From 860f11666a2e4b34c97223785b86df2c14804ba7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Attila=20M=C3=A9sz=C3=A1ros?= Date: Sun, 29 Dec 2024 21:40:10 +0100 Subject: [PATCH 1/3] feat: use SSA matcher flag for child resources MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Attila Mészáros --- .../glue/DependentResourceSpec.java | 16 +++++++++++++--- .../dependent/GCGenericDependentResource.java | 8 ++++---- .../glue/dependent/GenericDependentResource.java | 13 ++++++++++--- .../glue/reconciler/glue/GlueReconciler.java | 9 +++++---- 4 files changed, 32 insertions(+), 14 deletions(-) diff --git a/src/main/java/io/javaoperatorsdk/operator/glue/customresource/glue/DependentResourceSpec.java b/src/main/java/io/javaoperatorsdk/operator/glue/customresource/glue/DependentResourceSpec.java index 3166d97..8633f52 100644 --- a/src/main/java/io/javaoperatorsdk/operator/glue/customresource/glue/DependentResourceSpec.java +++ b/src/main/java/io/javaoperatorsdk/operator/glue/customresource/glue/DependentResourceSpec.java @@ -21,6 +21,8 @@ public class DependentResourceSpec { private String resourceTemplate; + private boolean useSSAMatcher = true; + private List dependsOn = new ArrayList<>(); @PreserveUnknownFields @@ -92,6 +94,14 @@ public void setClusterScoped(boolean clusterScoped) { this.clusterScoped = clusterScoped; } + public boolean isUseSSAMatcher() { + return useSSAMatcher; + } + + public void setUseSSAMatcher(boolean useSSAMatcher) { + this.useSSAMatcher = useSSAMatcher; + } + @Override public boolean equals(Object o) { if (this == o) @@ -99,8 +109,8 @@ public boolean equals(Object o) { if (o == null || getClass() != o.getClass()) return false; DependentResourceSpec that = (DependentResourceSpec) o; - return clusterScoped == that.clusterScoped && Objects.equals(name, that.name) - && Objects.equals(resource, that.resource) + return clusterScoped == that.clusterScoped && useSSAMatcher == that.useSSAMatcher + && Objects.equals(name, that.name) && Objects.equals(resource, that.resource) && Objects.equals(resourceTemplate, that.resourceTemplate) && Objects.equals(dependsOn, that.dependsOn) && Objects.equals(readyPostCondition, that.readyPostCondition) @@ -109,7 +119,7 @@ public boolean equals(Object o) { @Override public int hashCode() { - return Objects.hash(name, clusterScoped, resource, resourceTemplate, dependsOn, + return Objects.hash(name, clusterScoped, resource, resourceTemplate, useSSAMatcher, dependsOn, readyPostCondition, condition); } } diff --git a/src/main/java/io/javaoperatorsdk/operator/glue/dependent/GCGenericDependentResource.java b/src/main/java/io/javaoperatorsdk/operator/glue/dependent/GCGenericDependentResource.java index e325c64..7e802d6 100644 --- a/src/main/java/io/javaoperatorsdk/operator/glue/dependent/GCGenericDependentResource.java +++ b/src/main/java/io/javaoperatorsdk/operator/glue/dependent/GCGenericDependentResource.java @@ -10,12 +10,12 @@ public class GCGenericDependentResource extends GenericDependentResource public GCGenericDependentResource(GenericTemplateHandler genericTemplateHandler, GenericKubernetesResource desired, String name, - boolean clusterScoped) { - super(genericTemplateHandler, desired, name, clusterScoped); + boolean clusterScoped, boolean useSSAMatcher) { + super(genericTemplateHandler, desired, name, clusterScoped, useSSAMatcher); } public GCGenericDependentResource(GenericTemplateHandler genericTemplateHandler, - String desiredTemplate, String name, boolean clusterScoped) { - super(genericTemplateHandler, desiredTemplate, name, clusterScoped); + String desiredTemplate, String name, boolean clusterScoped, boolean useSSAMatcher) { + super(genericTemplateHandler, desiredTemplate, name, clusterScoped, useSSAMatcher); } } diff --git a/src/main/java/io/javaoperatorsdk/operator/glue/dependent/GenericDependentResource.java b/src/main/java/io/javaoperatorsdk/operator/glue/dependent/GenericDependentResource.java index 6ac7d9d..1e8f229 100644 --- a/src/main/java/io/javaoperatorsdk/operator/glue/dependent/GenericDependentResource.java +++ b/src/main/java/io/javaoperatorsdk/operator/glue/dependent/GenericDependentResource.java @@ -23,15 +23,17 @@ public class GenericDependentResource private final String desiredTemplate; private final String name; private final boolean clusterScoped; + private final boolean useSSAMatcher; // optimize share between instances private final GenericTemplateHandler genericTemplateHandler; public GenericDependentResource(GenericTemplateHandler genericTemplateHandler, GenericKubernetesResource desired, String name, - boolean clusterScoped) { + boolean clusterScoped, boolean useSSAMatcher) { super(new GroupVersionKind(desired.getApiVersion(), desired.getKind())); this.desired = desired; + this.useSSAMatcher = useSSAMatcher; this.desiredTemplate = null; this.name = name; this.clusterScoped = clusterScoped; @@ -39,12 +41,13 @@ public GenericDependentResource(GenericTemplateHandler genericTemplateHandler, } public GenericDependentResource(GenericTemplateHandler genericTemplateHandler, - String desiredTemplate, String name, boolean clusterScoped) { + String desiredTemplate, String name, boolean clusterScoped, boolean useSSAMatcher) { super(new GroupVersionKind(Utils.getApiVersionFromTemplate(desiredTemplate), Utils.getKindFromTemplate(desiredTemplate))); this.genericTemplateHandler = genericTemplateHandler; this.name = name; this.desiredTemplate = desiredTemplate; + this.useSSAMatcher = useSSAMatcher; this.desired = null; this.clusterScoped = clusterScoped; } @@ -75,6 +78,10 @@ public Result match(GenericKubernetesResource actualR && actualResource.getApiVersion().equals("apps/v1")) { return super.match(actualResource, primary, context); } - return Result.nonComputed(false); + if (useSSAMatcher) { + return super.match(actualResource, primary, context); + } else { + return Result.nonComputed(false); + } } } diff --git a/src/main/java/io/javaoperatorsdk/operator/glue/reconciler/glue/GlueReconciler.java b/src/main/java/io/javaoperatorsdk/operator/glue/reconciler/glue/GlueReconciler.java index fd292af..d31cc80 100644 --- a/src/main/java/io/javaoperatorsdk/operator/glue/reconciler/glue/GlueReconciler.java +++ b/src/main/java/io/javaoperatorsdk/operator/glue/reconciler/glue/GlueReconciler.java @@ -211,16 +211,17 @@ private GenericDependentResource createDependentResource(DependentResourceSpec s return spec.getResourceTemplate() != null ? new GCGenericDependentResource(genericTemplateHandler, spec.getResourceTemplate(), spec.getName(), - spec.isClusterScoped()) + spec.isClusterScoped(), spec.isUseSSAMatcher()) : new GCGenericDependentResource(genericTemplateHandler, spec.getResource(), spec.getName(), - spec.isClusterScoped()); + spec.isClusterScoped(), spec.isUseSSAMatcher()); } else { return spec.getResourceTemplate() != null ? new GenericDependentResource(genericTemplateHandler, - spec.getResourceTemplate(), spec.getName(), spec.isClusterScoped()) + spec.getResourceTemplate(), spec.getName(), spec.isClusterScoped(), + spec.isUseSSAMatcher()) : new GenericDependentResource(genericTemplateHandler, - spec.getResource(), spec.getName(), spec.isClusterScoped()); + spec.getResource(), spec.getName(), spec.isClusterScoped(), spec.isUseSSAMatcher()); } } From e2e8de233d87407105d9d93f32b6c559d4d6b79f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Attila=20M=C3=A9sz=C3=A1ros?= Date: Sun, 29 Dec 2024 21:55:00 +0100 Subject: [PATCH 2/3] 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 | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/docs/reference.md b/docs/reference.md index 3a9c8e6..a71f3bb 100644 --- a/docs/reference.md +++ b/docs/reference.md @@ -40,6 +40,10 @@ It has several attributes: (Same as `reconcilePrecondition` in Java Operator SDK) - **`readyPostCondition`** - condition to check if the resource is considered to be ready. If a resource is ready all the resources, which depend on it can proceed in reconciliation. +- **`useSSAMatcher`** - Match resources with Java Operator SDK Server Side Apply based matcher (default `true`). Matching resources + is makes the reconciliation much more efficient, since controller updates the resource only if truly changed. However, + it is not possible to match resources because of some characteristics of Kubernetes API (default values, value conversions, etc) + so you can always opt out the matching, and update the resource on every reconciliation. #### Built-in conditions From 3657acd9ef3bce640e202ace1ddf1e19a0aa70fa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Attila=20M=C3=A9sz=C3=A1ros?= Date: Wed, 1 Jan 2025 20:50:46 +0100 Subject: [PATCH 3/3] wip 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 | 4 ++-- .../glue/DependentResourceSpec.java | 18 +++++++++--------- .../glue/customresource/glue/Matcher.java | 5 +++++ .../dependent/GCGenericDependentResource.java | 9 +++++---- .../dependent/GenericDependentResource.java | 13 +++++++------ .../glue/reconciler/glue/GlueReconciler.java | 8 ++++---- .../operator/glue/GlueTest.java | 14 ++++++++++++++ src/test/resources/glue/SimpleNotUseSSA.yaml | 16 ++++++++++++++++ 8 files changed, 62 insertions(+), 25 deletions(-) create mode 100644 src/main/java/io/javaoperatorsdk/operator/glue/customresource/glue/Matcher.java create mode 100644 src/test/resources/glue/SimpleNotUseSSA.yaml diff --git a/docs/reference.md b/docs/reference.md index a71f3bb..d26b48e 100644 --- a/docs/reference.md +++ b/docs/reference.md @@ -40,10 +40,10 @@ It has several attributes: (Same as `reconcilePrecondition` in Java Operator SDK) - **`readyPostCondition`** - condition to check if the resource is considered to be ready. If a resource is ready all the resources, which depend on it can proceed in reconciliation. -- **`useSSAMatcher`** - Match resources with Java Operator SDK Server Side Apply based matcher (default `true`). Matching resources +- **`matcher`** - Match resources with Java Operator SDK Server Side Apply based matcher (default `SSA`). Matching resources is makes the reconciliation much more efficient, since controller updates the resource only if truly changed. However, it is not possible to match resources because of some characteristics of Kubernetes API (default values, value conversions, etc) - so you can always opt out the matching, and update the resource on every reconciliation. + so you can always opt out the matching (use value `NONE`), and update the resource on every reconciliation. #### Built-in conditions diff --git a/src/main/java/io/javaoperatorsdk/operator/glue/customresource/glue/DependentResourceSpec.java b/src/main/java/io/javaoperatorsdk/operator/glue/customresource/glue/DependentResourceSpec.java index 8633f52..ba929f5 100644 --- a/src/main/java/io/javaoperatorsdk/operator/glue/customresource/glue/DependentResourceSpec.java +++ b/src/main/java/io/javaoperatorsdk/operator/glue/customresource/glue/DependentResourceSpec.java @@ -21,7 +21,7 @@ public class DependentResourceSpec { private String resourceTemplate; - private boolean useSSAMatcher = true; + private Matcher matcher = Matcher.SSA; private List dependsOn = new ArrayList<>(); @@ -94,12 +94,12 @@ public void setClusterScoped(boolean clusterScoped) { this.clusterScoped = clusterScoped; } - public boolean isUseSSAMatcher() { - return useSSAMatcher; + public Matcher getMatcher() { + return matcher; } - public void setUseSSAMatcher(boolean useSSAMatcher) { - this.useSSAMatcher = useSSAMatcher; + public void setMatcher(Matcher matcher) { + this.matcher = matcher; } @Override @@ -109,9 +109,9 @@ public boolean equals(Object o) { if (o == null || getClass() != o.getClass()) return false; DependentResourceSpec that = (DependentResourceSpec) o; - return clusterScoped == that.clusterScoped && useSSAMatcher == that.useSSAMatcher - && Objects.equals(name, that.name) && Objects.equals(resource, that.resource) - && Objects.equals(resourceTemplate, that.resourceTemplate) + return clusterScoped == that.clusterScoped && Objects.equals(name, that.name) + && Objects.equals(resource, that.resource) + && Objects.equals(resourceTemplate, that.resourceTemplate) && matcher == that.matcher && Objects.equals(dependsOn, that.dependsOn) && Objects.equals(readyPostCondition, that.readyPostCondition) && Objects.equals(condition, that.condition); @@ -119,7 +119,7 @@ public boolean equals(Object o) { @Override public int hashCode() { - return Objects.hash(name, clusterScoped, resource, resourceTemplate, useSSAMatcher, dependsOn, + return Objects.hash(name, clusterScoped, resource, resourceTemplate, matcher, dependsOn, readyPostCondition, condition); } } diff --git a/src/main/java/io/javaoperatorsdk/operator/glue/customresource/glue/Matcher.java b/src/main/java/io/javaoperatorsdk/operator/glue/customresource/glue/Matcher.java new file mode 100644 index 0000000..6fcf982 --- /dev/null +++ b/src/main/java/io/javaoperatorsdk/operator/glue/customresource/glue/Matcher.java @@ -0,0 +1,5 @@ +package io.javaoperatorsdk.operator.glue.customresource.glue; + +public enum Matcher { + NONE, SSA +} diff --git a/src/main/java/io/javaoperatorsdk/operator/glue/dependent/GCGenericDependentResource.java b/src/main/java/io/javaoperatorsdk/operator/glue/dependent/GCGenericDependentResource.java index 7e802d6..9dbf369 100644 --- a/src/main/java/io/javaoperatorsdk/operator/glue/dependent/GCGenericDependentResource.java +++ b/src/main/java/io/javaoperatorsdk/operator/glue/dependent/GCGenericDependentResource.java @@ -3,6 +3,7 @@ import io.fabric8.kubernetes.api.model.GenericKubernetesResource; import io.javaoperatorsdk.operator.api.reconciler.dependent.GarbageCollected; import io.javaoperatorsdk.operator.glue.customresource.glue.Glue; +import io.javaoperatorsdk.operator.glue.customresource.glue.Matcher; import io.javaoperatorsdk.operator.glue.templating.GenericTemplateHandler; public class GCGenericDependentResource extends GenericDependentResource @@ -10,12 +11,12 @@ public class GCGenericDependentResource extends GenericDependentResource public GCGenericDependentResource(GenericTemplateHandler genericTemplateHandler, GenericKubernetesResource desired, String name, - boolean clusterScoped, boolean useSSAMatcher) { - super(genericTemplateHandler, desired, name, clusterScoped, useSSAMatcher); + boolean clusterScoped, Matcher matcher) { + super(genericTemplateHandler, desired, name, clusterScoped, matcher); } public GCGenericDependentResource(GenericTemplateHandler genericTemplateHandler, - String desiredTemplate, String name, boolean clusterScoped, boolean useSSAMatcher) { - super(genericTemplateHandler, desiredTemplate, name, clusterScoped, useSSAMatcher); + String desiredTemplate, String name, boolean clusterScoped, Matcher matcher) { + super(genericTemplateHandler, desiredTemplate, name, clusterScoped, matcher); } } diff --git a/src/main/java/io/javaoperatorsdk/operator/glue/dependent/GenericDependentResource.java b/src/main/java/io/javaoperatorsdk/operator/glue/dependent/GenericDependentResource.java index 1e8f229..679f52e 100644 --- a/src/main/java/io/javaoperatorsdk/operator/glue/dependent/GenericDependentResource.java +++ b/src/main/java/io/javaoperatorsdk/operator/glue/dependent/GenericDependentResource.java @@ -6,6 +6,7 @@ import io.javaoperatorsdk.operator.api.reconciler.dependent.Deleter; import io.javaoperatorsdk.operator.glue.Utils; import io.javaoperatorsdk.operator.glue.customresource.glue.Glue; +import io.javaoperatorsdk.operator.glue.customresource.glue.Matcher; import io.javaoperatorsdk.operator.glue.reconciler.glue.GlueReconciler; import io.javaoperatorsdk.operator.glue.templating.GenericTemplateHandler; import io.javaoperatorsdk.operator.processing.GroupVersionKind; @@ -23,17 +24,17 @@ public class GenericDependentResource private final String desiredTemplate; private final String name; private final boolean clusterScoped; - private final boolean useSSAMatcher; + private final Matcher matcher; // optimize share between instances private final GenericTemplateHandler genericTemplateHandler; public GenericDependentResource(GenericTemplateHandler genericTemplateHandler, GenericKubernetesResource desired, String name, - boolean clusterScoped, boolean useSSAMatcher) { + boolean clusterScoped, Matcher matcher) { super(new GroupVersionKind(desired.getApiVersion(), desired.getKind())); this.desired = desired; - this.useSSAMatcher = useSSAMatcher; + this.matcher = matcher; this.desiredTemplate = null; this.name = name; this.clusterScoped = clusterScoped; @@ -41,13 +42,13 @@ public GenericDependentResource(GenericTemplateHandler genericTemplateHandler, } public GenericDependentResource(GenericTemplateHandler genericTemplateHandler, - String desiredTemplate, String name, boolean clusterScoped, boolean useSSAMatcher) { + String desiredTemplate, String name, boolean clusterScoped, Matcher matcher) { super(new GroupVersionKind(Utils.getApiVersionFromTemplate(desiredTemplate), Utils.getKindFromTemplate(desiredTemplate))); this.genericTemplateHandler = genericTemplateHandler; this.name = name; this.desiredTemplate = desiredTemplate; - this.useSSAMatcher = useSSAMatcher; + this.matcher = matcher; this.desired = null; this.clusterScoped = clusterScoped; } @@ -78,7 +79,7 @@ public Result match(GenericKubernetesResource actualR && actualResource.getApiVersion().equals("apps/v1")) { return super.match(actualResource, primary, context); } - if (useSSAMatcher) { + if (Matcher.SSA.equals(matcher)) { return super.match(actualResource, primary, context); } else { return Result.nonComputed(false); diff --git a/src/main/java/io/javaoperatorsdk/operator/glue/reconciler/glue/GlueReconciler.java b/src/main/java/io/javaoperatorsdk/operator/glue/reconciler/glue/GlueReconciler.java index d31cc80..9a9571e 100644 --- a/src/main/java/io/javaoperatorsdk/operator/glue/reconciler/glue/GlueReconciler.java +++ b/src/main/java/io/javaoperatorsdk/operator/glue/reconciler/glue/GlueReconciler.java @@ -211,17 +211,17 @@ private GenericDependentResource createDependentResource(DependentResourceSpec s return spec.getResourceTemplate() != null ? new GCGenericDependentResource(genericTemplateHandler, spec.getResourceTemplate(), spec.getName(), - spec.isClusterScoped(), spec.isUseSSAMatcher()) + spec.isClusterScoped(), spec.getMatcher()) : new GCGenericDependentResource(genericTemplateHandler, spec.getResource(), spec.getName(), - spec.isClusterScoped(), spec.isUseSSAMatcher()); + spec.isClusterScoped(), spec.getMatcher()); } else { return spec.getResourceTemplate() != null ? new GenericDependentResource(genericTemplateHandler, spec.getResourceTemplate(), spec.getName(), spec.isClusterScoped(), - spec.isUseSSAMatcher()) + spec.getMatcher()) : new GenericDependentResource(genericTemplateHandler, - spec.getResource(), spec.getName(), spec.isClusterScoped(), spec.isUseSSAMatcher()); + spec.getResource(), spec.getName(), spec.isClusterScoped(), spec.getMatcher()); } } diff --git a/src/test/java/io/javaoperatorsdk/operator/glue/GlueTest.java b/src/test/java/io/javaoperatorsdk/operator/glue/GlueTest.java index 808171b..4ca0f66 100644 --- a/src/test/java/io/javaoperatorsdk/operator/glue/GlueTest.java +++ b/src/test/java/io/javaoperatorsdk/operator/glue/GlueTest.java @@ -22,6 +22,7 @@ import io.javaoperatorsdk.operator.glue.reconciler.ValidationAndErrorHandler; import io.quarkus.test.junit.QuarkusTest; +import static io.javaoperatorsdk.operator.glue.TestUtils.INITIAL_RECONCILE_WAIT_TIMEOUT; import static org.assertj.core.api.Assertions.assertThat; import static org.awaitility.Awaitility.await; @@ -354,6 +355,19 @@ void pathRelatedResourceStatus(String glueFileName) { }); } + @Test + void customizeMatcher() { + var glue = createGlue("/glue/SimpleNotUseSSA.yaml"); + + await().pollDelay(INITIAL_RECONCILE_WAIT_TIMEOUT).untilAsserted(() -> { + assertThat(get(ConfigMap.class, "simple-glue-no-ssa-configmap")).isNotNull(); + }); + delete(glue); + await().pollDelay(INITIAL_RECONCILE_WAIT_TIMEOUT).untilAsserted(() -> { + assertThat(get(ConfigMap.class, "simple-glue-no-ssa-configmap")).isNull(); + }); + } + private List testWorkflowList(int num) { List res = new ArrayList<>(); IntStream.range(0, num).forEach(index -> { diff --git a/src/test/resources/glue/SimpleNotUseSSA.yaml b/src/test/resources/glue/SimpleNotUseSSA.yaml new file mode 100644 index 0000000..cd2ca29 --- /dev/null +++ b/src/test/resources/glue/SimpleNotUseSSA.yaml @@ -0,0 +1,16 @@ +# Invalid GLUE, presents resources with non-unique name +apiVersion: io.javaoperatorsdk.operator.glue/v1beta1 +kind: Glue +metadata: + name: simple-glue-no-ssa +spec: + childResources: + - name: configMap + matcher: NONE + resource: + apiVersion: v1 + kind: ConfigMap + metadata: + name: simple-glue-no-ssa-configmap + data: + key: "value1"