Skip to content

Commit

Permalink
feat: name uniqueness validation
Browse files Browse the repository at this point in the history
Signed-off-by: Attila Mészáros <csviri@gmail.com>
  • Loading branch information
csviri committed Apr 16, 2024
1 parent 1d9de35 commit 71495c2
Show file tree
Hide file tree
Showing 6 changed files with 115 additions and 5 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
package io.csviri.operator.glue.customresource;

import io.javaoperatorsdk.operator.api.ObservedGenerationAwareStatus;

public class AbstractStatus extends ObservedGenerationAwareStatus {

private String errorMessage;

public String getErrorMessage() {
return errorMessage;
}

public void setErrorMessage(String errorMessage) {
this.errorMessage = errorMessage;
}

}
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
package io.csviri.operator.glue.customresource.glue;

import io.javaoperatorsdk.operator.api.ObservedGenerationAwareStatus;
import io.csviri.operator.glue.customresource.AbstractStatus;

public class GlueStatus extends ObservedGenerationAwareStatus {
public class GlueStatus extends AbstractStatus {

}
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
package io.csviri.operator.glue.customresource.operator;

import io.javaoperatorsdk.operator.api.ObservedGenerationAwareStatus;
import io.csviri.operator.glue.customresource.AbstractStatus;

public class ResourceFlowOperatorStatus extends AbstractStatus {

public class ResourceFlowOperatorStatus extends ObservedGenerationAwareStatus {

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
package io.csviri.operator.glue.reconciler;

import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.function.Consumer;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import io.csviri.operator.glue.GlueException;
import io.csviri.operator.glue.customresource.AbstractStatus;
import io.csviri.operator.glue.customresource.glue.DependentResourceSpec;
import io.csviri.operator.glue.customresource.glue.GlueSpec;
import io.csviri.operator.glue.customresource.glue.RelatedResourceSpec;
import io.fabric8.kubernetes.client.CustomResource;
import io.javaoperatorsdk.operator.api.reconciler.ErrorStatusUpdateControl;

import jakarta.inject.Singleton;

@Singleton
public class ValidationAndErrorHandler {
private static final Logger log = LoggerFactory.getLogger(ValidationAndErrorHandler.class);

public <T extends CustomResource<?, ? extends AbstractStatus>> ErrorStatusUpdateControl<T> updateStatusErrorMessage(
Exception e,
T resource) {
log.error("Error during reconciliation of resource. Name: {} namespace: {}, Kind: {}",
resource.getMetadata().getName(), resource.getMetadata().getNamespace(), resource.getKind(),
e);
if (e instanceof ValidationAndErrorHandler.NonUniqueNameException ex) {
resource.getStatus()
.setErrorMessage("Non unique names found: " + String.join(",", ex.getDuplicates()));
} else {
resource.getStatus().setErrorMessage("Error. See controller logs");
}
return ErrorStatusUpdateControl.updateStatus(resource);
}

public void checkIfNamesAreUnique(GlueSpec glueSpec) {
Set<String> seen = new HashSet<>();
List<String> duplicates = new ArrayList<>();

Consumer<String> deduplicate = n -> {
if (seen.contains(n)) {
duplicates.add(n);
} else {
seen.add(n);
}
};
glueSpec.getResources().stream().map(DependentResourceSpec::getName).forEach(deduplicate);
glueSpec.getRelatedResources().stream().map(RelatedResourceSpec::getName).forEach(deduplicate);

if (!duplicates.isEmpty()) {
throw new NonUniqueNameException(duplicates);
}

}

public static class NonUniqueNameException extends GlueException {

private final List<String> duplicates;

public NonUniqueNameException(List<String> duplicates) {
this.duplicates = duplicates;
}

public List<String> getDuplicates() {
return duplicates;
}
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,9 @@ public UpdateControl<Glue> reconcile(Glue primary,

log.debug("Reconciling glue. name: {} namespace: {}",
primary.getMetadata().getName(), primary.getMetadata().getNamespace());



registerRelatedResourceInformers(context, primary);
if (deletedGlueIfParentMarkedForDeletion(context, primary)) {
return UpdateControl.noUpdate();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
import io.csviri.operator.glue.customresource.glue.RelatedResourceSpec;
import io.csviri.operator.glue.customresource.operator.GlueOperator;
import io.csviri.operator.glue.customresource.operator.GlueOperatorSpec;
import io.csviri.operator.glue.reconciler.ValidationAndErrorHandler;
import io.fabric8.kubernetes.api.model.GenericKubernetesResource;
import io.fabric8.kubernetes.api.model.ObjectMetaBuilder;
import io.fabric8.kubernetes.client.utils.KubernetesResourceUtil;
Expand All @@ -23,17 +24,22 @@
import io.javaoperatorsdk.operator.processing.event.source.EventSource;
import io.javaoperatorsdk.operator.processing.event.source.informer.InformerEventSource;

import jakarta.inject.Inject;

@ControllerConfiguration
public class GlueOperatorReconciler
implements Reconciler<GlueOperator>, EventSourceInitializer<GlueOperator>,
Cleaner<GlueOperator> {
Cleaner<GlueOperator>, ErrorStatusHandler<GlueOperator> {

private static final Logger log = LoggerFactory.getLogger(GlueOperatorReconciler.class);

public static final String GLUE_LABEL_KEY = "foroperator";
public static final String GLUE_LABEL_VALUE = "true";
public static final String PARENT_RELATED_RESOURCE_NAME = "parent";

@Inject
ValidationAndErrorHandler validationAndErrorHandler;

private InformerEventSource<Glue, GlueOperator> resourceFlowEventSource;

@Override
Expand All @@ -43,6 +49,8 @@ public UpdateControl<GlueOperator> reconcile(GlueOperator glueOperator,
log.info("Reconciling GlueOperator {} in namespace: {}", glueOperator.getMetadata().getName(),
glueOperator.getMetadata().getNamespace());

validationAndErrorHandler.checkIfNamesAreUnique(glueOperator.getSpec());

var targetCREventSource = getOrRegisterCustomResourceEventSource(glueOperator, context);
targetCREventSource.list().forEach(cr -> {
var actualResourceFlow = resourceFlowEventSource
Expand Down Expand Up @@ -128,6 +136,12 @@ public Map<String, EventSource> prepareEventSources(
return EventSourceInitializer.nameEventSources(resourceFlowEventSource);
}

@Override
public ErrorStatusUpdateControl<GlueOperator> updateErrorStatus(GlueOperator resource,
Context<GlueOperator> context, Exception e) {
return validationAndErrorHandler.updateStatusErrorMessage(e, resource);
}

@Override
public DeleteControl cleanup(GlueOperator glueOperator,
Context<GlueOperator> context) {
Expand All @@ -141,4 +155,5 @@ private static String glueName(GenericKubernetesResource cr) {
return KubernetesResourceUtil.sanitizeName(cr.getMetadata().getName() + "-" + cr.getKind());
}


}

0 comments on commit 71495c2

Please sign in to comment.