From 5da6bdc066abca8b3f3b2b1f224d78013bfc0f1e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Attila=20M=C3=A9sz=C3=A1ros?= Date: Thu, 2 May 2024 14:57:16 +0200 Subject: [PATCH 1/9] cluster scoped parent MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Attila Mészáros --- .../operator/glue/GlueOperatorTest.java | 6 +++++ .../sample/secretcopy/secret-to-copy.yaml | 9 ++++++++ .../secretcopy/secret.copy.operator.yaml | 23 +++++++++++++++++++ 3 files changed, 38 insertions(+) create mode 100644 src/test/resources/sample/secretcopy/secret-to-copy.yaml create mode 100644 src/test/resources/sample/secretcopy/secret.copy.operator.yaml diff --git a/src/test/java/io/csviri/operator/glue/GlueOperatorTest.java b/src/test/java/io/csviri/operator/glue/GlueOperatorTest.java index a8ac01e..e70ed1b 100644 --- a/src/test/java/io/csviri/operator/glue/GlueOperatorTest.java +++ b/src/test/java/io/csviri/operator/glue/GlueOperatorTest.java @@ -189,6 +189,12 @@ void parentWithLabelSelector() { } + @Test + void secretCopySample() { + + } + + GlueOperator testWorkflowOperator() { var wo = new GlueOperator(); wo.setMetadata(new ObjectMetaBuilder() diff --git a/src/test/resources/sample/secretcopy/secret-to-copy.yaml b/src/test/resources/sample/secretcopy/secret-to-copy.yaml new file mode 100644 index 0000000..27f0cff --- /dev/null +++ b/src/test/resources/sample/secretcopy/secret-to-copy.yaml @@ -0,0 +1,9 @@ +apiVersion: v1 +kind: Secret +metadata: + name: secret-to-copy + namespace: default +type: Opaque +data: + password: dG9wc2VjcmV0 + diff --git a/src/test/resources/sample/secretcopy/secret.copy.operator.yaml b/src/test/resources/sample/secretcopy/secret.copy.operator.yaml new file mode 100644 index 0000000..6ef5fde --- /dev/null +++ b/src/test/resources/sample/secretcopy/secret.copy.operator.yaml @@ -0,0 +1,23 @@ +apiVersion: io.csviri.operator.glue/v1beta1 +kind: GlueOperator +metadata: + name: webpage-operator +spec: + parent: + apiVersion: v1 + kind: Namespace + childResources: + - name: secret + resource: + apiVersion: v1 + kind: Secret + metadata: + name: copied-secret + type: Opaque + data: + shared-password: {sharedsecret.data.password} + relatedResources: + - name: sharedsecret + apiVersion: v1 + kind: Secret + resourceNames: [ "secret-to-copy" ] From e7836e581dd433da9a6d2d01efd4da7c538f305f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Attila=20M=C3=A9sz=C3=A1ros?= Date: Thu, 2 May 2024 15:08:58 +0200 Subject: [PATCH 2/9] progress MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Attila Mészáros --- .../customresource/operator/GlueMetadata.java | 40 +++++++++++++++++++ .../operator/GlueOperatorSpec.java | 15 +++++-- .../glue/customresource/operator/Parent.java | 18 +++++++++ 3 files changed, 70 insertions(+), 3 deletions(-) create mode 100644 src/main/java/io/csviri/operator/glue/customresource/operator/GlueMetadata.java diff --git a/src/main/java/io/csviri/operator/glue/customresource/operator/GlueMetadata.java b/src/main/java/io/csviri/operator/glue/customresource/operator/GlueMetadata.java new file mode 100644 index 0000000..d6479af --- /dev/null +++ b/src/main/java/io/csviri/operator/glue/customresource/operator/GlueMetadata.java @@ -0,0 +1,40 @@ +package io.csviri.operator.glue.customresource.operator; + +import java.util.Objects; + +public class GlueMetadata { + + String name; + String namespace; + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public String getNamespace() { + return namespace; + } + + public void setNamespace(String namespace) { + this.namespace = namespace; + } + + @Override + public boolean equals(Object o) { + if (this == o) + return true; + if (o == null || getClass() != o.getClass()) + return false; + GlueMetadata that = (GlueMetadata) o; + return Objects.equals(name, that.name) && Objects.equals(namespace, that.namespace); + } + + @Override + public int hashCode() { + return Objects.hash(name, namespace); + } +} diff --git a/src/main/java/io/csviri/operator/glue/customresource/operator/GlueOperatorSpec.java b/src/main/java/io/csviri/operator/glue/customresource/operator/GlueOperatorSpec.java index 2cc23dd..f485e63 100644 --- a/src/main/java/io/csviri/operator/glue/customresource/operator/GlueOperatorSpec.java +++ b/src/main/java/io/csviri/operator/glue/customresource/operator/GlueOperatorSpec.java @@ -10,6 +10,8 @@ public class GlueOperatorSpec extends GlueSpec { @Required private Parent parent; + private GlueMetadata glueMetadata; + public Parent getParent() { return parent; } @@ -19,6 +21,14 @@ public GlueOperatorSpec setParent(Parent parent) { return this; } + public GlueMetadata getGlueMetadata() { + return glueMetadata; + } + + public void setGlueMetadata(GlueMetadata glueMetadata) { + this.glueMetadata = glueMetadata; + } + @Override public boolean equals(Object o) { if (this == o) @@ -28,12 +38,11 @@ public boolean equals(Object o) { if (!super.equals(o)) return false; GlueOperatorSpec that = (GlueOperatorSpec) o; - return Objects.equals(parent, that.parent); + return Objects.equals(parent, that.parent) && Objects.equals(glueMetadata, that.glueMetadata); } @Override public int hashCode() { - return Objects.hash(super.hashCode(), parent); + return Objects.hash(super.hashCode(), parent, glueMetadata); } - } diff --git a/src/main/java/io/csviri/operator/glue/customresource/operator/Parent.java b/src/main/java/io/csviri/operator/glue/customresource/operator/Parent.java index 45a6f5e..fde35ad 100644 --- a/src/main/java/io/csviri/operator/glue/customresource/operator/Parent.java +++ b/src/main/java/io/csviri/operator/glue/customresource/operator/Parent.java @@ -1,5 +1,7 @@ package io.csviri.operator.glue.customresource.operator; +import java.util.Objects; + public class Parent { private String apiVersion; @@ -38,4 +40,20 @@ public String getLabelSelector() { public void setLabelSelector(String labelSelector) { this.labelSelector = labelSelector; } + + @Override + public boolean equals(Object o) { + if (this == o) + return true; + if (o == null || getClass() != o.getClass()) + return false; + Parent parent = (Parent) o; + return Objects.equals(apiVersion, parent.apiVersion) && Objects.equals(kind, parent.kind) + && Objects.equals(labelSelector, parent.labelSelector); + } + + @Override + public int hashCode() { + return Objects.hash(apiVersion, kind, labelSelector); + } } From eb044293c1fd0ad31f28453e09791ab676bdbcdc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Attila=20M=C3=A9sz=C3=A1ros?= Date: Fri, 3 May 2024 10:54:48 +0200 Subject: [PATCH 3/9] wip MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Attila Mészáros --- .../glue/reconciler/glue/GlueReconciler.java | 1 - .../operator/GlueOperatorReconciler.java | 25 ++++++++++++++----- 2 files changed, 19 insertions(+), 7 deletions(-) diff --git a/src/main/java/io/csviri/operator/glue/reconciler/glue/GlueReconciler.java b/src/main/java/io/csviri/operator/glue/reconciler/glue/GlueReconciler.java index 65513b5..f6a8515 100644 --- a/src/main/java/io/csviri/operator/glue/reconciler/glue/GlueReconciler.java +++ b/src/main/java/io/csviri/operator/glue/reconciler/glue/GlueReconciler.java @@ -131,7 +131,6 @@ private void registerRelatedResourceInformers(Context context, // todo test private void cleanupRemovedResourcesFromWorkflow(Context context, Glue primary) { - context.getSecondaryResources(GenericKubernetesResource.class).forEach(r -> { String dependentName = r.getMetadata().getAnnotations().get(DEPENDENT_NAME_ANNOTATION_KEY); // dependent name is null for related resources diff --git a/src/main/java/io/csviri/operator/glue/reconciler/operator/GlueOperatorReconciler.java b/src/main/java/io/csviri/operator/glue/reconciler/operator/GlueOperatorReconciler.java index 5fb51ae..a4dcd25 100644 --- a/src/main/java/io/csviri/operator/glue/reconciler/operator/GlueOperatorReconciler.java +++ b/src/main/java/io/csviri/operator/glue/reconciler/operator/GlueOperatorReconciler.java @@ -16,6 +16,7 @@ import io.csviri.operator.glue.customresource.operator.ResourceFlowOperatorStatus; import io.csviri.operator.glue.reconciler.ValidationAndErrorHandler; import io.fabric8.kubernetes.api.model.GenericKubernetesResource; +import io.fabric8.kubernetes.api.model.ObjectMeta; import io.fabric8.kubernetes.api.model.ObjectMetaBuilder; import io.fabric8.kubernetes.client.utils.KubernetesResourceUtil; import io.javaoperatorsdk.operator.api.config.informer.InformerConfiguration; @@ -94,12 +95,10 @@ private Glue createGlue(GenericKubernetesResource targetParentResource, GlueOperator glueOperator) { var glue = new Glue(); - glue.setMetadata(new ObjectMetaBuilder() - .withName( - glueName(targetParentResource.getMetadata().getName(), targetParentResource.getKind())) - .withNamespace(targetParentResource.getMetadata().getNamespace()) - .withLabels(Map.of(FOR_GLUE_OPERATOR_LABEL_KEY, FOR_GLUE_OPERATOR_LABEL_VALUE)) - .build()); + ObjectMeta glueMetadata = glueMetadata(glueOperator, targetParentResource); + + + glue.setMetadata(glueMetadata); glue.setSpec(toWorkflowSpec(glueOperator.getSpec())); if (!defaultGlueLabels.isEmpty()) { @@ -120,6 +119,20 @@ private Glue createGlue(GenericKubernetesResource targetParentResource, return glue; } + private ObjectMeta glueMetadata(GlueOperator glueOperator, + GenericKubernetesResource targetParentResource) { + + ObjectMetaBuilder objectMetaBuilder = new ObjectMetaBuilder(); + + objectMetaBuilder.withName( + glueName(targetParentResource.getMetadata().getName(), targetParentResource.getKind())) + .withNamespace(targetParentResource.getMetadata().getNamespace()); + + objectMetaBuilder + .withLabels(Map.of(FOR_GLUE_OPERATOR_LABEL_KEY, FOR_GLUE_OPERATOR_LABEL_VALUE)); + return objectMetaBuilder.build(); + } + private GlueSpec toWorkflowSpec(GlueOperatorSpec spec) { var res = new GlueSpec(); res.setChildResources(new ArrayList<>(spec.getChildResources())); From 37a26f523e1e37b88d44a0e95d5c71ed389cc367 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Attila=20M=C3=A9sz=C3=A1ros?= Date: Fri, 3 May 2024 13:00:04 +0200 Subject: [PATCH 4/9] wip MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Attila Mészáros --- .../customresource/operator/GlueMetadata.java | 4 +- .../dependent/GCGenericDependentResource.java | 11 ++++-- .../dependent/GenericDependentResource.java | 10 +++-- .../glue/reconciler/glue/GlueReconciler.java | 23 +++++++----- .../operator/GlueOperatorReconciler.java | 37 ++++++++++++++----- .../templating/GenericTemplateHandler.java | 25 ++++++++++--- .../operator/glue/GlueOperatorTest.java | 5 ++- .../io/csviri/operator/glue/TestUtils.java | 2 +- .../secretcopy/secret.copy.operator.yaml | 3 ++ 9 files changed, 84 insertions(+), 36 deletions(-) diff --git a/src/main/java/io/csviri/operator/glue/customresource/operator/GlueMetadata.java b/src/main/java/io/csviri/operator/glue/customresource/operator/GlueMetadata.java index d6479af..011565c 100644 --- a/src/main/java/io/csviri/operator/glue/customresource/operator/GlueMetadata.java +++ b/src/main/java/io/csviri/operator/glue/customresource/operator/GlueMetadata.java @@ -4,8 +4,8 @@ public class GlueMetadata { - String name; - String namespace; + private String name; + private String namespace; public String getName() { return name; diff --git a/src/main/java/io/csviri/operator/glue/dependent/GCGenericDependentResource.java b/src/main/java/io/csviri/operator/glue/dependent/GCGenericDependentResource.java index 408bb76..021f513 100644 --- a/src/main/java/io/csviri/operator/glue/dependent/GCGenericDependentResource.java +++ b/src/main/java/io/csviri/operator/glue/dependent/GCGenericDependentResource.java @@ -1,18 +1,21 @@ package io.csviri.operator.glue.dependent; import io.csviri.operator.glue.customresource.glue.Glue; +import io.csviri.operator.glue.templating.GenericTemplateHandler; import io.fabric8.kubernetes.api.model.GenericKubernetesResource; import io.javaoperatorsdk.operator.api.reconciler.dependent.GarbageCollected; public class GCGenericDependentResource extends GenericDependentResource implements GarbageCollected { - public GCGenericDependentResource(GenericKubernetesResource desired, String name, + public GCGenericDependentResource(GenericTemplateHandler genericTemplateHandler, + GenericKubernetesResource desired, String name, boolean clusterScoped) { - super(desired, name, clusterScoped); + super(genericTemplateHandler, desired, name, clusterScoped); } - public GCGenericDependentResource(String desiredTemplate, String name, boolean clusterScoped) { - super(desiredTemplate, name, clusterScoped); + public GCGenericDependentResource(GenericTemplateHandler genericTemplateHandler, + String desiredTemplate, String name, boolean clusterScoped) { + super(genericTemplateHandler, desiredTemplate, name, clusterScoped); } } diff --git a/src/main/java/io/csviri/operator/glue/dependent/GenericDependentResource.java b/src/main/java/io/csviri/operator/glue/dependent/GenericDependentResource.java index 8cfb09f..3f72a94 100644 --- a/src/main/java/io/csviri/operator/glue/dependent/GenericDependentResource.java +++ b/src/main/java/io/csviri/operator/glue/dependent/GenericDependentResource.java @@ -25,20 +25,24 @@ public class GenericDependentResource private final boolean clusterScoped; // optimize share between instances - private final GenericTemplateHandler genericTemplateHandler = new GenericTemplateHandler(); + private final GenericTemplateHandler genericTemplateHandler; - public GenericDependentResource(GenericKubernetesResource desired, String name, + public GenericDependentResource(GenericTemplateHandler genericTemplateHandler, + GenericKubernetesResource desired, String name, boolean clusterScoped) { super(new GroupVersionKind(desired.getApiVersion(), desired.getKind())); this.desired = desired; this.desiredTemplate = null; this.name = name; this.clusterScoped = clusterScoped; + this.genericTemplateHandler = genericTemplateHandler; } - public GenericDependentResource(String desiredTemplate, String name, boolean clusterScoped) { + public GenericDependentResource(GenericTemplateHandler genericTemplateHandler, + String desiredTemplate, String name, boolean clusterScoped) { super(new GroupVersionKind(Utils.getApiVersionFromTemplate(desiredTemplate), Utils.getKindFromTemplate(desiredTemplate))); + this.genericTemplateHandler = genericTemplateHandler; this.name = name; this.desiredTemplate = desiredTemplate; this.desired = null; diff --git a/src/main/java/io/csviri/operator/glue/reconciler/glue/GlueReconciler.java b/src/main/java/io/csviri/operator/glue/reconciler/glue/GlueReconciler.java index f6a8515..d410a97 100644 --- a/src/main/java/io/csviri/operator/glue/reconciler/glue/GlueReconciler.java +++ b/src/main/java/io/csviri/operator/glue/reconciler/glue/GlueReconciler.java @@ -49,13 +49,14 @@ public class GlueReconciler implements Reconciler, Cleaner, ErrorSta private final KubernetesResourceDeletedCondition deletePostCondition = new KubernetesResourceDeletedCondition(); - private final GenericTemplateHandler genericTemplateHandler = new GenericTemplateHandler(); - + private final GenericTemplateHandler genericTemplateHandler; public GlueReconciler(ValidationAndErrorHandler validationAndErrorHandler, - InformerRegister informerRegister) { + InformerRegister informerRegister, + GenericTemplateHandler genericTemplateHandler) { this.validationAndErrorHandler = validationAndErrorHandler; this.informerRegister = informerRegister; + this.genericTemplateHandler = genericTemplateHandler; } /** @@ -199,21 +200,23 @@ private void createAndAddDependentToWorkflow(Glue primary, Context context .ifPresent(c -> builder.withReconcilePrecondition(toCondition(c))); } - private static GenericDependentResource createDependentResource(DependentResourceSpec spec, + private GenericDependentResource createDependentResource(DependentResourceSpec spec, boolean leafDependent, Boolean resourceInSameNamespaceAsPrimary) { if (leafDependent && resourceInSameNamespaceAsPrimary && !spec.isClusterScoped()) { return spec.getResourceTemplate() != null - ? new GCGenericDependentResource(spec.getResourceTemplate(), spec.getName(), + ? new GCGenericDependentResource(genericTemplateHandler, spec.getResourceTemplate(), + spec.getName(), spec.isClusterScoped()) - : new GCGenericDependentResource(spec.getResource(), spec.getName(), + : new GCGenericDependentResource(genericTemplateHandler, spec.getResource(), + spec.getName(), spec.isClusterScoped()); } else { return spec.getResourceTemplate() != null - ? new GenericDependentResource(spec.getResourceTemplate(), spec.getName(), - spec.isClusterScoped()) - : new GenericDependentResource(spec.getResource(), spec.getName(), - spec.isClusterScoped()); + ? new GenericDependentResource(genericTemplateHandler, + spec.getResourceTemplate(), spec.getName(), spec.isClusterScoped()) + : new GenericDependentResource(genericTemplateHandler, + spec.getResource(), spec.getName(), spec.isClusterScoped()); } } diff --git a/src/main/java/io/csviri/operator/glue/reconciler/operator/GlueOperatorReconciler.java b/src/main/java/io/csviri/operator/glue/reconciler/operator/GlueOperatorReconciler.java index a4dcd25..1475f64 100644 --- a/src/main/java/io/csviri/operator/glue/reconciler/operator/GlueOperatorReconciler.java +++ b/src/main/java/io/csviri/operator/glue/reconciler/operator/GlueOperatorReconciler.java @@ -15,6 +15,7 @@ import io.csviri.operator.glue.customresource.operator.GlueOperatorSpec; import io.csviri.operator.glue.customresource.operator.ResourceFlowOperatorStatus; import io.csviri.operator.glue.reconciler.ValidationAndErrorHandler; +import io.csviri.operator.glue.templating.GenericTemplateHandler; import io.fabric8.kubernetes.api.model.GenericKubernetesResource; import io.fabric8.kubernetes.api.model.ObjectMeta; import io.fabric8.kubernetes.api.model.ObjectMetaBuilder; @@ -27,7 +28,6 @@ import io.javaoperatorsdk.operator.processing.event.source.informer.InformerEventSource; import jakarta.annotation.PostConstruct; -import jakarta.inject.Inject; import static io.csviri.operator.glue.reconciler.glue.GlueReconciler.GLUE_RECONCILER_NAME; @@ -43,19 +43,25 @@ public class GlueOperatorReconciler public static final String PARENT_RELATED_RESOURCE_NAME = "parent"; public static final String GLUE_OPERATOR_RECONCILER_NAME = "glue-operator"; - @Inject - ValidationAndErrorHandler validationAndErrorHandler; - @ConfigProperty(name = "quarkus.operator-sdk.controllers." + GLUE_RECONCILER_NAME + ".selector") Optional glueLabelSelector; - @Inject - ControllerConfig controllerConfig; + private final ControllerConfig controllerConfig; + private final ValidationAndErrorHandler validationAndErrorHandler; + private final GenericTemplateHandler genericTemplateHandler; private Map defaultGlueLabels; private InformerEventSource glueEventSource; + public GlueOperatorReconciler(ControllerConfig controllerConfig, + ValidationAndErrorHandler validationAndErrorHandler, + GenericTemplateHandler genericTemplateHandler) { + this.controllerConfig = controllerConfig; + this.validationAndErrorHandler = validationAndErrorHandler; + this.genericTemplateHandler = genericTemplateHandler; + } + @PostConstruct void init() { defaultGlueLabels = initDefaultLabelsToAddToGlue(); @@ -120,13 +126,24 @@ private Glue createGlue(GenericKubernetesResource targetParentResource, } private ObjectMeta glueMetadata(GlueOperator glueOperator, - GenericKubernetesResource targetParentResource) { + GenericKubernetesResource parent) { ObjectMetaBuilder objectMetaBuilder = new ObjectMetaBuilder(); - objectMetaBuilder.withName( - glueName(targetParentResource.getMetadata().getName(), targetParentResource.getKind())) - .withNamespace(targetParentResource.getMetadata().getNamespace()); + var glueMeta = glueOperator.getSpec().getGlueMetadata(); + if (glueMeta != null) { + // todo optimize + var data = Map.of(PARENT_RELATED_RESOURCE_NAME, parent); + var glueName = genericTemplateHandler.processInputAndTemplate(data, glueMeta.getName()); + var glueNamespace = + genericTemplateHandler.processInputAndTemplate(data, glueMeta.getNamespace()); + objectMetaBuilder.withName(glueName); + objectMetaBuilder.withName(glueNamespace); + } else { + objectMetaBuilder.withName( + glueName(parent.getMetadata().getName(), parent.getKind())) + .withNamespace(parent.getMetadata().getNamespace()); + } objectMetaBuilder .withLabels(Map.of(FOR_GLUE_OPERATOR_LABEL_KEY, FOR_GLUE_OPERATOR_LABEL_VALUE)); diff --git a/src/main/java/io/csviri/operator/glue/templating/GenericTemplateHandler.java b/src/main/java/io/csviri/operator/glue/templating/GenericTemplateHandler.java index b257014..b8860cc 100644 --- a/src/main/java/io/csviri/operator/glue/templating/GenericTemplateHandler.java +++ b/src/main/java/io/csviri/operator/glue/templating/GenericTemplateHandler.java @@ -5,12 +5,16 @@ import io.csviri.operator.glue.Utils; import io.csviri.operator.glue.customresource.glue.Glue; +import io.fabric8.kubernetes.api.model.GenericKubernetesResource; import io.javaoperatorsdk.operator.api.reconciler.Context; import io.quarkus.qute.Engine; import io.quarkus.qute.Template; import com.fasterxml.jackson.databind.ObjectMapper; +import jakarta.inject.Singleton; + +@Singleton public class GenericTemplateHandler { public static final String WORKFLOW_METADATA_KEY = "glueMetadata"; @@ -18,18 +22,29 @@ public class GenericTemplateHandler { private static final ObjectMapper objectMapper = new ObjectMapper(); private static final Engine engine = Engine.builder().addDefaults().build(); + public String processTemplate(Map> data, String template) { + Template parsedTemplate = engine.parse(template); + return parsedTemplate.data(data).render(); + } + + public String processInputAndTemplate(Map data, + String template) { + Map> res = new HashMap<>(); + data.forEach((key, value) -> res.put(key, + value == null ? null : objectMapper.convertValue(value, Map.class))); + return processTemplate(res, template); + } + public String processTemplate(String template, Glue primary, Context context) { - Template hello = engine.parse(template); var data = createDataWithResources(primary, context); - return hello.data(data).render(); + return processTemplate(data, template); } - @SuppressWarnings("rawtypes") - private static Map createDataWithResources(Glue primary, + private static Map> createDataWithResources(Glue primary, Context context) { - Map res = new HashMap<>(); + Map> res = new HashMap<>(); var actualResourcesByName = Utils.getActualResourcesByNameInWorkflow(context, primary); actualResourcesByName.forEach((key, value) -> res.put(key, diff --git a/src/test/java/io/csviri/operator/glue/GlueOperatorTest.java b/src/test/java/io/csviri/operator/glue/GlueOperatorTest.java index e70ed1b..c3a5f0b 100644 --- a/src/test/java/io/csviri/operator/glue/GlueOperatorTest.java +++ b/src/test/java/io/csviri/operator/glue/GlueOperatorTest.java @@ -5,6 +5,7 @@ import java.util.stream.IntStream; import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.Test; import io.csviri.operator.glue.customresource.TestCustomResource; @@ -189,9 +190,11 @@ void parentWithLabelSelector() { } + @Disabled @Test void secretCopySample() { - + create(TestUtils + .loadGlueOperator("/glueoperator/ParentLabelSelector.yaml")); } diff --git a/src/test/java/io/csviri/operator/glue/TestUtils.java b/src/test/java/io/csviri/operator/glue/TestUtils.java index f5efe86..ae3dcd7 100644 --- a/src/test/java/io/csviri/operator/glue/TestUtils.java +++ b/src/test/java/io/csviri/operator/glue/TestUtils.java @@ -24,7 +24,7 @@ public class TestUtils { - public static final Duration GC_WAIT_TIMEOUT = Duration.ofSeconds(90); + public static final Duration GC_WAIT_TIMEOUT = Duration.ofSeconds(120); public static final Duration INITIAL_RECONCILE_WAIT_TIMEOUT = Duration.ofMillis(150); public static final int CRD_READY_WAIT = 1000; diff --git a/src/test/resources/sample/secretcopy/secret.copy.operator.yaml b/src/test/resources/sample/secretcopy/secret.copy.operator.yaml index 6ef5fde..7512758 100644 --- a/src/test/resources/sample/secretcopy/secret.copy.operator.yaml +++ b/src/test/resources/sample/secretcopy/secret.copy.operator.yaml @@ -6,6 +6,9 @@ spec: parent: apiVersion: v1 kind: Namespace + glueMetadata: + name: copied-secret-glue + namespace: {parent.metadata.name} childResources: - name: secret resource: From cdb3e027cfd44b2bedef45cc2d493ee745d719d4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Attila=20M=C3=A9sz=C3=A1ros?= Date: Fri, 3 May 2024 14:23:09 +0200 Subject: [PATCH 5/9] wip MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Attila Mészáros --- .../operator/glue/GlueOperatorTest.java | 20 +++++++++++++++++-- ...perator.yaml => secret-copy.operator.yaml} | 0 2 files changed, 18 insertions(+), 2 deletions(-) rename src/test/resources/sample/secretcopy/{secret.copy.operator.yaml => secret-copy.operator.yaml} (100%) diff --git a/src/test/java/io/csviri/operator/glue/GlueOperatorTest.java b/src/test/java/io/csviri/operator/glue/GlueOperatorTest.java index c3a5f0b..19b41c6 100644 --- a/src/test/java/io/csviri/operator/glue/GlueOperatorTest.java +++ b/src/test/java/io/csviri/operator/glue/GlueOperatorTest.java @@ -17,6 +17,8 @@ import io.csviri.operator.glue.reconciler.ValidationAndErrorHandler; import io.fabric8.kubernetes.api.model.ConfigMap; import io.fabric8.kubernetes.api.model.ObjectMetaBuilder; +import io.fabric8.kubernetes.api.model.Secret; +import io.fabric8.kubernetes.client.dsl.NonDeletingOperation; import io.quarkus.test.junit.QuarkusTest; import static io.csviri.operator.glue.TestData.*; @@ -193,8 +195,22 @@ void parentWithLabelSelector() { @Disabled @Test void secretCopySample() { - create(TestUtils - .loadGlueOperator("/glueoperator/ParentLabelSelector.yaml")); + var secret = TestUtils.load("/sample/secretcopy/secret-to-copy.yaml", Secret.class); + client.resource(secret).createOr(NonDeletingOperation::update); + + var go = create(TestUtils + .loadGlueOperator("/sample/secretcopy/secret-copy.operator.yaml")); + + await().untilAsserted(() -> { + var namespaces = client.namespaces().list().getItems(); + namespaces.forEach(ns -> { + var copiedSecret = + client.secrets().inNamespace(ns.getMetadata().getName()).withName("copied-secret"); + assertThat(copiedSecret).isNotNull(); + }); + }); + + } diff --git a/src/test/resources/sample/secretcopy/secret.copy.operator.yaml b/src/test/resources/sample/secretcopy/secret-copy.operator.yaml similarity index 100% rename from src/test/resources/sample/secretcopy/secret.copy.operator.yaml rename to src/test/resources/sample/secretcopy/secret-copy.operator.yaml From 037c1f8996fa2c65cc9fe478394efeceaac24fed Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Attila=20M=C3=A9sz=C3=A1ros?= Date: Fri, 3 May 2024 18:07:32 +0200 Subject: [PATCH 6/9] wip MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Attila Mészáros --- .../glue/customresource/operator/Parent.java | 16 +++++++++++++--- .../operator/GlueOperatorReconciler.java | 2 +- .../csviri/operator/glue/GlueOperatorTest.java | 18 +++++++++++++----- .../secretcopy/secret-copy.operator.yaml | 3 ++- 4 files changed, 29 insertions(+), 10 deletions(-) diff --git a/src/main/java/io/csviri/operator/glue/customresource/operator/Parent.java b/src/main/java/io/csviri/operator/glue/customresource/operator/Parent.java index fde35ad..54fa936 100644 --- a/src/main/java/io/csviri/operator/glue/customresource/operator/Parent.java +++ b/src/main/java/io/csviri/operator/glue/customresource/operator/Parent.java @@ -6,8 +6,10 @@ public class Parent { private String apiVersion; private String kind; + private boolean clusterScoped = false; private String labelSelector; + public Parent() {} public Parent(String apiVersion, String kind) { @@ -41,6 +43,14 @@ public void setLabelSelector(String labelSelector) { this.labelSelector = labelSelector; } + public boolean isClusterScoped() { + return clusterScoped; + } + + public void setClusterScoped(boolean clusterScoped) { + this.clusterScoped = clusterScoped; + } + @Override public boolean equals(Object o) { if (this == o) @@ -48,12 +58,12 @@ public boolean equals(Object o) { if (o == null || getClass() != o.getClass()) return false; Parent parent = (Parent) o; - return Objects.equals(apiVersion, parent.apiVersion) && Objects.equals(kind, parent.kind) - && Objects.equals(labelSelector, parent.labelSelector); + return clusterScoped == parent.clusterScoped && Objects.equals(apiVersion, parent.apiVersion) + && Objects.equals(kind, parent.kind) && Objects.equals(labelSelector, parent.labelSelector); } @Override public int hashCode() { - return Objects.hash(apiVersion, kind, labelSelector); + return Objects.hash(apiVersion, kind, clusterScoped, labelSelector); } } diff --git a/src/main/java/io/csviri/operator/glue/reconciler/operator/GlueOperatorReconciler.java b/src/main/java/io/csviri/operator/glue/reconciler/operator/GlueOperatorReconciler.java index 1475f64..2d8bef5 100644 --- a/src/main/java/io/csviri/operator/glue/reconciler/operator/GlueOperatorReconciler.java +++ b/src/main/java/io/csviri/operator/glue/reconciler/operator/GlueOperatorReconciler.java @@ -118,9 +118,9 @@ private Glue createGlue(GenericKubernetesResource targetParentResource, parentRelatedSpec.setKind(parent.getKind()); parentRelatedSpec.setResourceNames(List.of(targetParentResource.getMetadata().getName())); parentRelatedSpec.setNamespace(targetParentResource.getMetadata().getNamespace()); + parentRelatedSpec.setClusterScoped(glueOperator.getSpec().getParent().isClusterScoped()); glue.getSpec().getRelatedResources().add(parentRelatedSpec); - glue.addOwnerReference(targetParentResource); return glue; } diff --git a/src/test/java/io/csviri/operator/glue/GlueOperatorTest.java b/src/test/java/io/csviri/operator/glue/GlueOperatorTest.java index 19b41c6..9dcf207 100644 --- a/src/test/java/io/csviri/operator/glue/GlueOperatorTest.java +++ b/src/test/java/io/csviri/operator/glue/GlueOperatorTest.java @@ -5,7 +5,6 @@ import java.util.stream.IntStream; import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.Test; import io.csviri.operator.glue.customresource.TestCustomResource; @@ -191,8 +190,6 @@ void parentWithLabelSelector() { }); } - - @Disabled @Test void secretCopySample() { var secret = TestUtils.load("/sample/secretcopy/secret-to-copy.yaml", Secret.class); @@ -205,12 +202,23 @@ void secretCopySample() { var namespaces = client.namespaces().list().getItems(); namespaces.forEach(ns -> { var copiedSecret = - client.secrets().inNamespace(ns.getMetadata().getName()).withName("copied-secret"); + client.secrets().inNamespace(ns.getMetadata().getName()).withName("copied-secret") + .get(); assertThat(copiedSecret).isNotNull(); + assertThat(copiedSecret.getData().get("shared-password")) + .isEqualTo(secret.getData().get("password")); }); }); - + delete(go); + await().untilAsserted(() -> { + var namespaces = client.namespaces().list().getItems(); + namespaces.forEach(ns -> { + var copiedSecret = + client.secrets().inNamespace(ns.getMetadata().getName()).withName("copied-secret"); + assertThat(copiedSecret).isNull(); + }); + }); } diff --git a/src/test/resources/sample/secretcopy/secret-copy.operator.yaml b/src/test/resources/sample/secretcopy/secret-copy.operator.yaml index 7512758..388abc6 100644 --- a/src/test/resources/sample/secretcopy/secret-copy.operator.yaml +++ b/src/test/resources/sample/secretcopy/secret-copy.operator.yaml @@ -8,7 +8,7 @@ spec: kind: Namespace glueMetadata: name: copied-secret-glue - namespace: {parent.metadata.name} + namespace: "{parent.metadata.name}" childResources: - name: secret resource: @@ -23,4 +23,5 @@ spec: - name: sharedsecret apiVersion: v1 kind: Secret + namespace: default resourceNames: [ "secret-to-copy" ] From 6392b75f2e1466b18c3e739d95b20fcdd8c7907f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Attila=20M=C3=A9sz=C3=A1ros?= Date: Mon, 13 May 2024 13:59:01 +0200 Subject: [PATCH 7/9] working integration test MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Attila Mészáros --- .../operator/GlueOperatorReconciler.java | 4 ++-- .../operator/glue/GlueOperatorTest.java | 20 +++++++++++++------ .../secretcopy/secret-copy.operator.yaml | 5 +++-- 3 files changed, 19 insertions(+), 10 deletions(-) diff --git a/src/main/java/io/csviri/operator/glue/reconciler/operator/GlueOperatorReconciler.java b/src/main/java/io/csviri/operator/glue/reconciler/operator/GlueOperatorReconciler.java index 2d8bef5..63d2e89 100644 --- a/src/main/java/io/csviri/operator/glue/reconciler/operator/GlueOperatorReconciler.java +++ b/src/main/java/io/csviri/operator/glue/reconciler/operator/GlueOperatorReconciler.java @@ -132,13 +132,13 @@ private ObjectMeta glueMetadata(GlueOperator glueOperator, var glueMeta = glueOperator.getSpec().getGlueMetadata(); if (glueMeta != null) { - // todo optimize + // optimize var data = Map.of(PARENT_RELATED_RESOURCE_NAME, parent); var glueName = genericTemplateHandler.processInputAndTemplate(data, glueMeta.getName()); var glueNamespace = genericTemplateHandler.processInputAndTemplate(data, glueMeta.getNamespace()); objectMetaBuilder.withName(glueName); - objectMetaBuilder.withName(glueNamespace); + objectMetaBuilder.withNamespace(glueNamespace); } else { objectMetaBuilder.withName( glueName(parent.getMetadata().getName(), parent.getKind())) diff --git a/src/test/java/io/csviri/operator/glue/GlueOperatorTest.java b/src/test/java/io/csviri/operator/glue/GlueOperatorTest.java index 9dcf207..ac40c59 100644 --- a/src/test/java/io/csviri/operator/glue/GlueOperatorTest.java +++ b/src/test/java/io/csviri/operator/glue/GlueOperatorTest.java @@ -10,6 +10,7 @@ import io.csviri.operator.glue.customresource.TestCustomResource; import io.csviri.operator.glue.customresource.TestCustomResource2; import io.csviri.operator.glue.customresource.glue.DependentResourceSpec; +import io.csviri.operator.glue.customresource.glue.Glue; import io.csviri.operator.glue.customresource.operator.GlueOperator; import io.csviri.operator.glue.customresource.operator.GlueOperatorSpec; import io.csviri.operator.glue.customresource.operator.Parent; @@ -29,6 +30,8 @@ @QuarkusTest class GlueOperatorTest extends TestBase { + public static final String COPIED_SECRET_NAME = "copied-secret"; + @BeforeEach void applyCRD() { TestUtils.applyTestCrd(client, TestCustomResource.class, TestCustomResource2.class); @@ -202,7 +205,7 @@ void secretCopySample() { var namespaces = client.namespaces().list().getItems(); namespaces.forEach(ns -> { var copiedSecret = - client.secrets().inNamespace(ns.getMetadata().getName()).withName("copied-secret") + client.secrets().inNamespace(ns.getMetadata().getName()).withName(COPIED_SECRET_NAME) .get(); assertThat(copiedSecret).isNotNull(); assertThat(copiedSecret.getData().get("shared-password")) @@ -211,12 +214,17 @@ void secretCopySample() { }); delete(go); + client.namespaces().list().getItems().forEach(ns -> { + client.resources(Glue.class) + .inNamespace(ns.getMetadata().getName()).withName("copied-secret-glue").delete(); + client.secrets() + .inNamespace(ns.getMetadata().getName()).withName(COPIED_SECRET_NAME).delete(); + }); await().untilAsserted(() -> { - var namespaces = client.namespaces().list().getItems(); - namespaces.forEach(ns -> { - var copiedSecret = - client.secrets().inNamespace(ns.getMetadata().getName()).withName("copied-secret"); - assertThat(copiedSecret).isNull(); + client.namespaces().list().getItems().forEach(ns -> { + var g = client.resources(Glue.class) + .inNamespace(ns.getMetadata().getName()).withName("copied-glue-secret").get(); + assertThat(g).isNull(); }); }); } diff --git a/src/test/resources/sample/secretcopy/secret-copy.operator.yaml b/src/test/resources/sample/secretcopy/secret-copy.operator.yaml index 388abc6..86ee9e5 100644 --- a/src/test/resources/sample/secretcopy/secret-copy.operator.yaml +++ b/src/test/resources/sample/secretcopy/secret-copy.operator.yaml @@ -1,11 +1,12 @@ apiVersion: io.csviri.operator.glue/v1beta1 kind: GlueOperator metadata: - name: webpage-operator + name: secret-copy-operator spec: parent: apiVersion: v1 kind: Namespace + clusterScoped: true glueMetadata: name: copied-secret-glue namespace: "{parent.metadata.name}" @@ -18,7 +19,7 @@ spec: name: copied-secret type: Opaque data: - shared-password: {sharedsecret.data.password} + shared-password: "{sharedsecret.data.password}" relatedResources: - name: sharedsecret apiVersion: v1 From 94755b2d24c3dbed4e14cca5d769e72b2f4df95f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Attila=20M=C3=A9sz=C3=A1ros?= Date: Mon, 13 May 2024 14:15:30 +0200 Subject: [PATCH 8/9] docs update MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Attila Mészáros --- docs/reference.md | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/docs/reference.md b/docs/reference.md index 1eada14..7b21441 100644 --- a/docs/reference.md +++ b/docs/reference.md @@ -84,12 +84,17 @@ The `DependentResource` implementation of JOSDK makes all kinds of optimizations ## [GlueOperator resource](https://github.com/csviri/kubernetes-glue-operator/releases/latest/download/glueoperators.glue-v1.yml) -The specs of `GlueOperator` are almost identical to `Glue`, it just adds one additional attribute **`parent`**, -which has the following sub-attributes: - - **`apiVersion`** and **`kind`** - specifies the resources to reconciler according to the spec. - Targets are usually custom resources but not necessarily, it also works with built-in Kubernetes - resources. - - **`labelSelector`** - an optional label selector for the target resources +The specs of `GlueOperator` are almost identical to `Glue`, it just adds some additional attributes: + + - **`parent`** - specifies the resources handled by the operator. Targets are usually custom resources but not necessarily, + it also works with built-in Kubernetes resources. With the following sub-attributes: + - **`apiVersion`** and **`kind`** - of the target custom resources. + - **`labelSelector`** - optional label selector for the target resources. + - **`clusterScoped`** - optional boolean value, if the parent resource is cluster scoped. Default is `false`. + - **`glueMetadata`** - you can customize the `Glue` resource created for each parent resource. + This is especially important when the parent is a cluster scoped resource. Using this + you can specify the **`name`** and **`namespace`** of the created `Glue`. + See usage on the sample [secret-copy-operator](https://github.com/csviri/kubernetes-glue-operator/blob/main/src/test/resources/sample/secretcopy/secret-copy.operator.yaml#L10-L12). See minimal `GlueOperator` [here](https://github.com/csviri/kubernetes-glue-operator/blob/main/src/test/resources/glueoperator/SimpleGlueOperator.yaml). From 3583d5cde757708c35887475689c2be2e6d4f7cb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Attila=20M=C3=A9sz=C3=A1ros?= Date: Mon, 13 May 2024 14:17:18 +0200 Subject: [PATCH 9/9] docs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Attila Mészáros --- docs/reference.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/reference.md b/docs/reference.md index 7b21441..bf28179 100644 --- a/docs/reference.md +++ b/docs/reference.md @@ -91,9 +91,9 @@ The specs of `GlueOperator` are almost identical to `Glue`, it just adds some ad - **`apiVersion`** and **`kind`** - of the target custom resources. - **`labelSelector`** - optional label selector for the target resources. - **`clusterScoped`** - optional boolean value, if the parent resource is cluster scoped. Default is `false`. - - **`glueMetadata`** - you can customize the `Glue` resource created for each parent resource. - This is especially important when the parent is a cluster scoped resource. Using this - you can specify the **`name`** and **`namespace`** of the created `Glue`. + - **`glueMetadata`** - optionally, you can customize the `Glue` resource created for each parent resource. + This is especially important when the parent is a cluster scoped resource - in that case it is mandatory to set. + Using this you can specify the **`name`** and **`namespace`** of the created `Glue`. See usage on the sample [secret-copy-operator](https://github.com/csviri/kubernetes-glue-operator/blob/main/src/test/resources/sample/secretcopy/secret-copy.operator.yaml#L10-L12). See minimal `GlueOperator` [here](https://github.com/csviri/kubernetes-glue-operator/blob/main/src/test/resources/glueoperator/SimpleGlueOperator.yaml).