diff --git a/docs/reference.md b/docs/reference.md index 3a9c8e6..d26b48e 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. +- **`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 (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 3166d97..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,6 +21,8 @@ public class DependentResourceSpec { private String resourceTemplate; + private Matcher matcher = Matcher.SSA; + private List dependsOn = new ArrayList<>(); @PreserveUnknownFields @@ -92,6 +94,14 @@ public void setClusterScoped(boolean clusterScoped) { this.clusterScoped = clusterScoped; } + public Matcher getMatcher() { + return matcher; + } + + public void setMatcher(Matcher matcher) { + this.matcher = matcher; + } + @Override public boolean equals(Object o) { if (this == o) @@ -101,7 +111,7 @@ public boolean equals(Object o) { DependentResourceSpec that = (DependentResourceSpec) o; return clusterScoped == that.clusterScoped && Objects.equals(name, that.name) && Objects.equals(resource, that.resource) - && Objects.equals(resourceTemplate, that.resourceTemplate) + && Objects.equals(resourceTemplate, that.resourceTemplate) && matcher == that.matcher && Objects.equals(dependsOn, that.dependsOn) && Objects.equals(readyPostCondition, that.readyPostCondition) && Objects.equals(condition, that.condition); @@ -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, 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 e325c64..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) { - super(genericTemplateHandler, desired, name, clusterScoped); + boolean clusterScoped, Matcher matcher) { + super(genericTemplateHandler, desired, name, clusterScoped, matcher); } public GCGenericDependentResource(GenericTemplateHandler genericTemplateHandler, - String desiredTemplate, String name, boolean clusterScoped) { - super(genericTemplateHandler, desiredTemplate, name, clusterScoped); + 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 6ac7d9d..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,15 +24,17 @@ public class GenericDependentResource private final String desiredTemplate; private final String name; private final boolean clusterScoped; + private final Matcher matcher; // optimize share between instances private final GenericTemplateHandler genericTemplateHandler; public GenericDependentResource(GenericTemplateHandler genericTemplateHandler, GenericKubernetesResource desired, String name, - boolean clusterScoped) { + boolean clusterScoped, Matcher matcher) { super(new GroupVersionKind(desired.getApiVersion(), desired.getKind())); this.desired = desired; + this.matcher = matcher; this.desiredTemplate = null; this.name = name; this.clusterScoped = clusterScoped; @@ -39,12 +42,13 @@ public GenericDependentResource(GenericTemplateHandler genericTemplateHandler, } public GenericDependentResource(GenericTemplateHandler genericTemplateHandler, - String desiredTemplate, String name, boolean clusterScoped) { + 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.matcher = matcher; this.desired = null; this.clusterScoped = clusterScoped; } @@ -75,6 +79,10 @@ public Result match(GenericKubernetesResource actualR && actualResource.getApiVersion().equals("apps/v1")) { return super.match(actualResource, primary, context); } - return Result.nonComputed(false); + 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 fd292af..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,16 +211,17 @@ private GenericDependentResource createDependentResource(DependentResourceSpec s return spec.getResourceTemplate() != null ? new GCGenericDependentResource(genericTemplateHandler, spec.getResourceTemplate(), spec.getName(), - spec.isClusterScoped()) + spec.isClusterScoped(), spec.getMatcher()) : new GCGenericDependentResource(genericTemplateHandler, spec.getResource(), spec.getName(), - spec.isClusterScoped()); + spec.isClusterScoped(), spec.getMatcher()); } else { return spec.getResourceTemplate() != null ? new GenericDependentResource(genericTemplateHandler, - spec.getResourceTemplate(), spec.getName(), spec.isClusterScoped()) + spec.getResourceTemplate(), spec.getName(), spec.isClusterScoped(), + spec.getMatcher()) : new GenericDependentResource(genericTemplateHandler, - spec.getResource(), spec.getName(), spec.isClusterScoped()); + 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"