Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -16,10 +16,12 @@
import io.ten1010.aipub.projectcontroller.domain.k8s.ImageRegistrySecretNameResolver;
import io.ten1010.aipub.projectcontroller.domain.k8s.K8sApiProvider;
import io.ten1010.aipub.projectcontroller.domain.k8s.KeyResolver;
import io.ten1010.aipub.projectcontroller.domain.k8s.LabelConstants;
import io.ten1010.aipub.projectcontroller.domain.k8s.NamespaceNameResolver;
import io.ten1010.aipub.projectcontroller.domain.k8s.ReconciliationService;
import io.ten1010.aipub.projectcontroller.domain.k8s.dto.V1alpha1Project;
import io.ten1010.aipub.projectcontroller.domain.k8s.util.K8sObjectUtils;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
Expand Down Expand Up @@ -103,15 +105,36 @@ protected Result reconcileInternal(Request request) throws ApiException {
deleteSecret(secretOpt.get());
return new Result(false);
}
Map<String, byte[]> reconciledData;
try {
reconciledData = this.reconciliationService.reconcileImageRegistrySecretData(
projectOpt.get());
} catch (ImageHubNotConnectedException e) {

Optional<String> currentRobotIdOpt = this.reconciliationService.resolveImageRegistryRobotId(
projectOpt.get());
if (currentRobotIdOpt.isEmpty()) {
return logImageHubNotConnectedAndRequeue(projName);
}
String currentRobotId = currentRobotIdOpt.get();
String existingRobotId = K8sObjectUtils.getAnnotations(secretOpt.get())
.get(LabelConstants.IMAGE_REGISTRY_ROBOT_ID_KEY);

Map<String, byte[]> reconciledData;
if (currentRobotId.equals(existingRobotId)) {
// robot 이 그대로면 비밀번호도 여전히 유효하다 — refreshSecret 을 또 부를 필요 없이
// 기존 Secret 데이터를 그대로 재사용한다. permission(bindingImageHubs) 변경은 robot
// 을 재생성하지 않으므로, 이 비교만으로 "재발급이 필요한가"를 판단할 수 있다.
reconciledData = secretOpt.get().getData();
} else {
try {
reconciledData = this.reconciliationService.reconcileImageRegistrySecretData(
projectOpt.get());
} catch (ImageHubNotConnectedException e) {
return logImageHubNotConnectedAndRequeue(projName);
}
}
Map<String, String> reconciledAnnotations = new HashMap<>(
K8sObjectUtils.getAnnotations(secretOpt.get()));
reconciledAnnotations.put(LabelConstants.IMAGE_REGISTRY_ROBOT_ID_KEY, currentRobotId);

return reconcileExistingSecret(secretOpt.get(), reconciledReferences, reconciledType,
reconciledData, reconciledLabels);
reconciledData, reconciledLabels, reconciledAnnotations);
}

if (!K8sObjectUtils.isTerminating(projectOpt.get())) {
Expand All @@ -124,8 +147,13 @@ protected Result reconcileInternal(Request request) throws ApiException {
}
Map<String, String> reconciledLabels = this.reconciliationService.reconcileSecretLabels(
namespaceOpt.get(), projectOpt.get());
Map<String, String> reconciledAnnotations = this.reconciliationService
.resolveImageRegistryRobotId(projectOpt.get())
.map(id -> Map.of(LabelConstants.IMAGE_REGISTRY_ROBOT_ID_KEY, id))
.orElse(Map.of());
return reconcileNoExistingSecret(request.getNamespace(), request.getName(),
reconciledReferences, reconciledType, reconciledData, reconciledLabels);
reconciledReferences, reconciledType, reconciledData, reconciledLabels,
reconciledAnnotations);
}

return new Result(false);
Expand All @@ -143,13 +171,15 @@ private Result reconcileNoExistingSecret(
List<V1OwnerReference> reconciledReferences,
String reconciledType,
Map<String, byte[]> reconciledData,
Map<String, String> reconciledLabels) throws ApiException {
Map<String, String> reconciledLabels,
Map<String, String> reconciledAnnotations) throws ApiException {
V1Secret secret = new V1SecretBuilder()
.withNewMetadata()
.withNamespace(namespace)
.withName(objName)
.withOwnerReferences(reconciledReferences)
.withLabels(reconciledLabels)
.withAnnotations(reconciledAnnotations)
.endMetadata()
.withType(reconciledType)
.withData(reconciledData)
Expand All @@ -164,17 +194,20 @@ private Result reconcileExistingSecret(
List<V1OwnerReference> reconciledReferences,
String reconciledType,
Map<String, byte[]> reconciledData,
Map<String, String> reconciledLabels) throws ApiException {
Map<String, String> reconciledLabels,
Map<String, String> reconciledAnnotations) throws ApiException {
if (Set.copyOf(K8sObjectUtils.getOwnerReferences(existing))
.equals(Set.copyOf(reconciledReferences)) &&
Objects.equals(existing.getType(), reconciledType) &&
Objects.equals(existing.getData(), reconciledData)) {
Objects.equals(existing.getData(), reconciledData) &&
Objects.equals(K8sObjectUtils.getAnnotations(existing), reconciledAnnotations)) {
return new Result(false);
}
V1Secret edited = new V1SecretBuilder(existing)
.editMetadata()
.withOwnerReferences(reconciledReferences)
.withLabels(reconciledLabels)
.withAnnotations(reconciledAnnotations)
.endMetadata()
.withType(reconciledType)
.withData(reconciledData)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,13 @@ public Map<String, Object> resolve(V1alpha1Project project) {
return json;
}

@Override
public Optional<String> resolveImageRegistryRobotId(V1alpha1Project project) {
String username = this.imageRegistryRobotUsernameResolver.resolve(
K8sObjectUtils.getName(project));
return findByUsername(username).map(ImageRegistryRobot::getId);
}

private String getPassword(ImageRegistryRobot robot) {
Objects.requireNonNull(robot.getId());
ImageRegistryRobotSecret secret = this.imageRegistryRobotService.refreshSecret(robot.getId());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

import io.ten1010.aipub.projectcontroller.domain.k8s.dto.V1alpha1Project;
import java.util.Map;
import java.util.Optional;

public class DefaultDockerConfigJsonResolver implements DockerConfigJsonResolver {

Expand All @@ -10,4 +11,9 @@ public Map<String, Object> resolve(V1alpha1Project project) {
return Map.of();
}

@Override
public Optional<String> resolveImageRegistryRobotId(V1alpha1Project project) {
return Optional.empty();
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,16 @@

import io.ten1010.aipub.projectcontroller.domain.k8s.dto.V1alpha1Project;
import java.util.Map;
import java.util.Optional;

public interface DockerConfigJsonResolver {

Map<String, Object> resolve(V1alpha1Project project);

/**
* project 에 연결된 image registry robot 의 id 를 조회한다. secret 을 새로 발급받지 않고
* "지금 이 프로젝트의 robot 이 마지막으로 발급했던 robot 과 같은가"만 값싸게 확인하기 위한 용도.
*/
Optional<String> resolveImageRegistryRobotId(V1alpha1Project project);

}
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@ public final class LabelConstants {
ProjectApiConstants.AIPUB_GROUP + "/" + "workload-name";
public static final String WORKLOAD_KIND_KEY =
ProjectApiConstants.AIPUB_GROUP + "/" + "workload-kind";
public static final String IMAGE_REGISTRY_ROBOT_ID_KEY =
ProjectApiConstants.PROJECT_GROUP + "/" + "image-registry-robot-id";

private LabelConstants() {
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -914,6 +914,15 @@ public Map<String, byte[]> reconcileImageRegistrySecretData(V1alpha1Project proj
}
}

/**
* project 에 연결된 image registry robot 의 id 를 조회한다(secret 재발급 없이). 이미 반영된
* Secret 의 {@link LabelConstants#IMAGE_REGISTRY_ROBOT_ID_KEY} annotation 과 비교해서, robot
* 이 그대로인지(=비밀번호도 그대로 유효한지) 판단하는 용도로 쓰인다.
*/
public Optional<String> resolveImageRegistryRobotId(V1alpha1Project project) {
return this.dockerConfigJsonResolver.resolveImageRegistryRobotId(project);
}

public Map<String, String> reconcileNodeLabels(V1Node existing) {
Map<String, String> existingLabels = K8sObjectUtils.getLabels(existing);
Map<String, String> reconciled = new HashMap<>(existingLabels);
Expand Down