From 37778929af2cd997a2777fb341f56120197de7fa Mon Sep 17 00:00:00 2001 From: csviri Date: Mon, 2 Jan 2023 10:48:23 +0100 Subject: [PATCH 1/5] feat: condition for bulk resources --- .../BulkDependentWithPreconditionIT.java | 39 +++++++++++++++++++ ...lkDependentWithPreconditionReconciler.java | 29 ++++++++++++++ .../bulkdependent/SamplePrecondition.java | 16 ++++++++ 3 files changed, 84 insertions(+) create mode 100644 operator-framework/src/test/java/io/javaoperatorsdk/operator/bulkdependent/BulkDependentWithPreconditionIT.java create mode 100644 operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/bulkdependent/ManagedBulkDependentWithPreconditionReconciler.java create mode 100644 operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/bulkdependent/SamplePrecondition.java diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/bulkdependent/BulkDependentWithPreconditionIT.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/bulkdependent/BulkDependentWithPreconditionIT.java new file mode 100644 index 0000000000..7848162d11 --- /dev/null +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/bulkdependent/BulkDependentWithPreconditionIT.java @@ -0,0 +1,39 @@ +package io.javaoperatorsdk.operator.bulkdependent; + +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.RegisterExtension; + +import io.javaoperatorsdk.operator.junit.LocallyRunOperatorExtension; +import io.javaoperatorsdk.operator.sample.bulkdependent.ManagedBulkDependentWithPreconditionReconciler; + +import static io.javaoperatorsdk.operator.bulkdependent.BulkDependentTestBase.INITIAL_NUMBER_OF_CONFIG_MAPS; +import static io.javaoperatorsdk.operator.bulkdependent.BulkDependentTestBase.testResource; +import static io.javaoperatorsdk.operator.sample.bulkdependent.ConfigMapDeleterBulkDependentResource.LABEL_KEY; +import static io.javaoperatorsdk.operator.sample.bulkdependent.ConfigMapDeleterBulkDependentResource.LABEL_VALUE; +import static org.assertj.core.api.Assertions.*; +import static org.awaitility.Awaitility.await; + +class BulkDependentWithPreconditionIT { + + @RegisterExtension + LocallyRunOperatorExtension extension = + LocallyRunOperatorExtension.builder() + .withReconciler(new ManagedBulkDependentWithPreconditionReconciler()) + .build(); + + @Test + void handlesBulkDependentWithPrecondition() { + var resource = testResource(); + extension.create(resource); + + await().untilAsserted(() -> { + var cms = extension.getKubernetesClient().configMaps().inNamespace(extension.getNamespace()) + .withLabel(LABEL_KEY, LABEL_VALUE) + .list().getItems(); + assertThat(cms).hasSize(INITIAL_NUMBER_OF_CONFIG_MAPS); + }); + } + + + +} diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/bulkdependent/ManagedBulkDependentWithPreconditionReconciler.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/bulkdependent/ManagedBulkDependentWithPreconditionReconciler.java new file mode 100644 index 0000000000..f9cfc2e5ee --- /dev/null +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/bulkdependent/ManagedBulkDependentWithPreconditionReconciler.java @@ -0,0 +1,29 @@ +package io.javaoperatorsdk.operator.sample.bulkdependent; + +import java.util.concurrent.atomic.AtomicInteger; + +import io.javaoperatorsdk.operator.api.reconciler.Context; +import io.javaoperatorsdk.operator.api.reconciler.ControllerConfiguration; +import io.javaoperatorsdk.operator.api.reconciler.Reconciler; +import io.javaoperatorsdk.operator.api.reconciler.UpdateControl; +import io.javaoperatorsdk.operator.api.reconciler.dependent.Dependent; + +@ControllerConfiguration(dependents = @Dependent(reconcilePrecondition = SamplePrecondition.class, + type = CRUDConfigMapBulkDependentResource.class)) +public class ManagedBulkDependentWithPreconditionReconciler + implements Reconciler { + + private final AtomicInteger numberOfExecutions = new AtomicInteger(0); + + @Override + public UpdateControl reconcile( + BulkDependentTestCustomResource resource, + Context context) throws Exception { + numberOfExecutions.incrementAndGet(); + return UpdateControl.noUpdate(); + } + + public int getNumberOfExecutions() { + return numberOfExecutions.get(); + } +} diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/bulkdependent/SamplePrecondition.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/bulkdependent/SamplePrecondition.java new file mode 100644 index 0000000000..1e3f9052c5 --- /dev/null +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/bulkdependent/SamplePrecondition.java @@ -0,0 +1,16 @@ +package io.javaoperatorsdk.operator.sample.bulkdependent; + +import io.fabric8.kubernetes.api.model.ConfigMap; +import io.javaoperatorsdk.operator.api.reconciler.Context; +import io.javaoperatorsdk.operator.processing.dependent.workflow.Condition; + +public class SamplePrecondition implements Condition { + + public static final String SKIP_RESOURCE_DATA = "skipThis"; + + @Override + public boolean isMet(BulkDependentTestCustomResource primary, ConfigMap secondary, + Context context) { + return !SKIP_RESOURCE_DATA.equals(primary.getSpec().getAdditionalData()); + } +} From 42a4e17f999ca527d947ea19f7d5a00d3992bb45 Mon Sep 17 00:00:00 2001 From: csviri Date: Mon, 2 Jan 2023 10:58:59 +0100 Subject: [PATCH 2/5] evaulation fix --- .../dependent/workflow/AbstractWorkflowExecutor.java | 8 ++++++-- .../operator/sample/bulkdependent/SamplePrecondition.java | 8 +++++--- 2 files changed, 11 insertions(+), 5 deletions(-) diff --git a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/workflow/AbstractWorkflowExecutor.java b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/workflow/AbstractWorkflowExecutor.java index 76db792468..2fea3eab00 100644 --- a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/workflow/AbstractWorkflowExecutor.java +++ b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/workflow/AbstractWorkflowExecutor.java @@ -9,6 +9,7 @@ import java.util.concurrent.Future; import java.util.stream.Collectors; +import io.javaoperatorsdk.operator.processing.dependent.BulkDependentResource; import org.slf4j.Logger; import io.fabric8.kubernetes.api.model.HasMetadata; @@ -101,9 +102,12 @@ protected synchronized void handleNodeExecutionFinish( protected boolean isConditionMet(Optional> condition, DependentResource dependentResource) { + var resources = dependentResource instanceof BulkDependentResource ? ((BulkDependentResource) + dependentResource).getSecondaryResources(primary,context) : + dependentResource.getSecondaryResource(primary, context).orElse(null); + return condition.map(c -> c.isMet(primary, - dependentResource.getSecondaryResource(primary, context).orElse(null), - context)) + (R) resources, context)) .orElse(true); } } diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/bulkdependent/SamplePrecondition.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/bulkdependent/SamplePrecondition.java index 1e3f9052c5..d9848943a0 100644 --- a/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/bulkdependent/SamplePrecondition.java +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/bulkdependent/SamplePrecondition.java @@ -4,13 +4,15 @@ import io.javaoperatorsdk.operator.api.reconciler.Context; import io.javaoperatorsdk.operator.processing.dependent.workflow.Condition; -public class SamplePrecondition implements Condition { +import java.util.Map; + +public class SamplePrecondition implements Condition, BulkDependentTestCustomResource> { public static final String SKIP_RESOURCE_DATA = "skipThis"; @Override - public boolean isMet(BulkDependentTestCustomResource primary, ConfigMap secondary, - Context context) { + public boolean isMet(BulkDependentTestCustomResource primary, Map secondary, + Context context) { return !SKIP_RESOURCE_DATA.equals(primary.getSpec().getAdditionalData()); } } From 623ced97f85c77baf7272b0adee08eef4e48a06b Mon Sep 17 00:00:00 2001 From: csviri Date: Mon, 2 Jan 2023 11:07:13 +0100 Subject: [PATCH 3/5] fix on impl --- .../workflow/AbstractWorkflowExecutor.java | 13 ++++++++----- .../sample/bulkdependent/SamplePrecondition.java | 9 +++++---- 2 files changed, 13 insertions(+), 9 deletions(-) diff --git a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/workflow/AbstractWorkflowExecutor.java b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/workflow/AbstractWorkflowExecutor.java index 2fea3eab00..8fa9a6fd12 100644 --- a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/workflow/AbstractWorkflowExecutor.java +++ b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/workflow/AbstractWorkflowExecutor.java @@ -9,12 +9,12 @@ import java.util.concurrent.Future; import java.util.stream.Collectors; -import io.javaoperatorsdk.operator.processing.dependent.BulkDependentResource; import org.slf4j.Logger; import io.fabric8.kubernetes.api.model.HasMetadata; import io.javaoperatorsdk.operator.api.reconciler.Context; import io.javaoperatorsdk.operator.api.reconciler.dependent.DependentResource; +import io.javaoperatorsdk.operator.processing.dependent.BulkDependentResource; @SuppressWarnings("rawtypes") public abstract class AbstractWorkflowExecutor

{ @@ -102,12 +102,15 @@ protected synchronized void handleNodeExecutionFinish( protected boolean isConditionMet(Optional> condition, DependentResource dependentResource) { - var resources = dependentResource instanceof BulkDependentResource ? ((BulkDependentResource) - dependentResource).getSecondaryResources(primary,context) : - dependentResource.getSecondaryResource(primary, context).orElse(null); + if (condition.isEmpty()) { + return true; + } + var resources = dependentResource instanceof BulkDependentResource + ? ((BulkDependentResource) dependentResource).getSecondaryResources(primary, context) + : dependentResource.getSecondaryResource(primary, context).orElse(null); return condition.map(c -> c.isMet(primary, - (R) resources, context)) + (R) resources, context)) .orElse(true); } } diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/bulkdependent/SamplePrecondition.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/bulkdependent/SamplePrecondition.java index d9848943a0..6209da090d 100644 --- a/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/bulkdependent/SamplePrecondition.java +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/bulkdependent/SamplePrecondition.java @@ -1,18 +1,19 @@ package io.javaoperatorsdk.operator.sample.bulkdependent; +import java.util.Map; + import io.fabric8.kubernetes.api.model.ConfigMap; import io.javaoperatorsdk.operator.api.reconciler.Context; import io.javaoperatorsdk.operator.processing.dependent.workflow.Condition; -import java.util.Map; - -public class SamplePrecondition implements Condition, BulkDependentTestCustomResource> { +public class SamplePrecondition + implements Condition, BulkDependentTestCustomResource> { public static final String SKIP_RESOURCE_DATA = "skipThis"; @Override public boolean isMet(BulkDependentTestCustomResource primary, Map secondary, - Context context) { + Context context) { return !SKIP_RESOURCE_DATA.equals(primary.getSpec().getAdditionalData()); } } From ad8759856cc59380ea4b0973478daa1140ec93e7 Mon Sep 17 00:00:00 2001 From: csviri Date: Tue, 3 Jan 2023 10:17:09 +0100 Subject: [PATCH 4/5] improved sample --- ...nIT.java => BulkDependentWithConditionIT.java} | 13 ++++++++++--- .../BulkDependentTestCustomResource.java | 2 +- .../bulkdependent/BulkDependentTestStatus.java | 15 +++++++++++++++ ...ulkDependentWithReadyConditionReconciler.java} | 13 ++++++++++--- ...Precondition.java => SampleBulkCondition.java} | 10 +++++++--- 5 files changed, 43 insertions(+), 10 deletions(-) rename operator-framework/src/test/java/io/javaoperatorsdk/operator/bulkdependent/{BulkDependentWithPreconditionIT.java => BulkDependentWithConditionIT.java} (73%) create mode 100644 operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/bulkdependent/BulkDependentTestStatus.java rename operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/bulkdependent/{ManagedBulkDependentWithPreconditionReconciler.java => ManagedBulkDependentWithReadyConditionReconciler.java} (67%) rename operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/bulkdependent/{SamplePrecondition.java => SampleBulkCondition.java} (60%) diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/bulkdependent/BulkDependentWithPreconditionIT.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/bulkdependent/BulkDependentWithConditionIT.java similarity index 73% rename from operator-framework/src/test/java/io/javaoperatorsdk/operator/bulkdependent/BulkDependentWithPreconditionIT.java rename to operator-framework/src/test/java/io/javaoperatorsdk/operator/bulkdependent/BulkDependentWithConditionIT.java index 7848162d11..b922bb73b4 100644 --- a/operator-framework/src/test/java/io/javaoperatorsdk/operator/bulkdependent/BulkDependentWithPreconditionIT.java +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/bulkdependent/BulkDependentWithConditionIT.java @@ -4,7 +4,8 @@ import org.junit.jupiter.api.extension.RegisterExtension; import io.javaoperatorsdk.operator.junit.LocallyRunOperatorExtension; -import io.javaoperatorsdk.operator.sample.bulkdependent.ManagedBulkDependentWithPreconditionReconciler; +import io.javaoperatorsdk.operator.sample.bulkdependent.BulkDependentTestCustomResource; +import io.javaoperatorsdk.operator.sample.bulkdependent.ManagedBulkDependentWithReadyConditionReconciler; import static io.javaoperatorsdk.operator.bulkdependent.BulkDependentTestBase.INITIAL_NUMBER_OF_CONFIG_MAPS; import static io.javaoperatorsdk.operator.bulkdependent.BulkDependentTestBase.testResource; @@ -13,12 +14,12 @@ import static org.assertj.core.api.Assertions.*; import static org.awaitility.Awaitility.await; -class BulkDependentWithPreconditionIT { +class BulkDependentWithConditionIT { @RegisterExtension LocallyRunOperatorExtension extension = LocallyRunOperatorExtension.builder() - .withReconciler(new ManagedBulkDependentWithPreconditionReconciler()) + .withReconciler(new ManagedBulkDependentWithReadyConditionReconciler()) .build(); @Test @@ -27,10 +28,16 @@ void handlesBulkDependentWithPrecondition() { extension.create(resource); await().untilAsserted(() -> { + var res = extension.get(BulkDependentTestCustomResource.class, + testResource().getMetadata().getName()); + assertThat(res.getStatus()).isNotNull(); + assertThat(res.getStatus().getReady()).isTrue(); + var cms = extension.getKubernetesClient().configMaps().inNamespace(extension.getNamespace()) .withLabel(LABEL_KEY, LABEL_VALUE) .list().getItems(); assertThat(cms).hasSize(INITIAL_NUMBER_OF_CONFIG_MAPS); + }); } diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/bulkdependent/BulkDependentTestCustomResource.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/bulkdependent/BulkDependentTestCustomResource.java index 68e6297f8c..a0af65d0d3 100644 --- a/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/bulkdependent/BulkDependentTestCustomResource.java +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/bulkdependent/BulkDependentTestCustomResource.java @@ -10,6 +10,6 @@ @Version("v1") @ShortNames("sbd") public class BulkDependentTestCustomResource - extends CustomResource + extends CustomResource implements Namespaced { } diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/bulkdependent/BulkDependentTestStatus.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/bulkdependent/BulkDependentTestStatus.java new file mode 100644 index 0000000000..cd16280ce9 --- /dev/null +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/bulkdependent/BulkDependentTestStatus.java @@ -0,0 +1,15 @@ +package io.javaoperatorsdk.operator.sample.bulkdependent; + +public class BulkDependentTestStatus { + + private Boolean ready; + + public Boolean getReady() { + return ready; + } + + public BulkDependentTestStatus setReady(boolean ready) { + this.ready = ready; + return this; + } +} diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/bulkdependent/ManagedBulkDependentWithPreconditionReconciler.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/bulkdependent/ManagedBulkDependentWithReadyConditionReconciler.java similarity index 67% rename from operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/bulkdependent/ManagedBulkDependentWithPreconditionReconciler.java rename to operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/bulkdependent/ManagedBulkDependentWithReadyConditionReconciler.java index f9cfc2e5ee..f250e16e56 100644 --- a/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/bulkdependent/ManagedBulkDependentWithPreconditionReconciler.java +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/bulkdependent/ManagedBulkDependentWithReadyConditionReconciler.java @@ -8,9 +8,9 @@ import io.javaoperatorsdk.operator.api.reconciler.UpdateControl; import io.javaoperatorsdk.operator.api.reconciler.dependent.Dependent; -@ControllerConfiguration(dependents = @Dependent(reconcilePrecondition = SamplePrecondition.class, +@ControllerConfiguration(dependents = @Dependent(readyPostcondition = SampleBulkCondition.class, type = CRUDConfigMapBulkDependentResource.class)) -public class ManagedBulkDependentWithPreconditionReconciler +public class ManagedBulkDependentWithReadyConditionReconciler implements Reconciler { private final AtomicInteger numberOfExecutions = new AtomicInteger(0); @@ -20,7 +20,14 @@ public UpdateControl reconcile( BulkDependentTestCustomResource resource, Context context) throws Exception { numberOfExecutions.incrementAndGet(); - return UpdateControl.noUpdate(); + + var ready = context.managedDependentResourceContext().getWorkflowReconcileResult() + .map(res -> res.allDependentResourcesReady()).orElseThrow(); + + resource.setStatus(new BulkDependentTestStatus()); + resource.getStatus().setReady(ready); + + return UpdateControl.patchStatus(resource); } public int getNumberOfExecutions() { diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/bulkdependent/SamplePrecondition.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/bulkdependent/SampleBulkCondition.java similarity index 60% rename from operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/bulkdependent/SamplePrecondition.java rename to operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/bulkdependent/SampleBulkCondition.java index 6209da090d..82b6a6d2d1 100644 --- a/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/bulkdependent/SamplePrecondition.java +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/bulkdependent/SampleBulkCondition.java @@ -6,14 +6,18 @@ import io.javaoperatorsdk.operator.api.reconciler.Context; import io.javaoperatorsdk.operator.processing.dependent.workflow.Condition; -public class SamplePrecondition +public class SampleBulkCondition implements Condition, BulkDependentTestCustomResource> { - public static final String SKIP_RESOURCE_DATA = "skipThis"; + // We use ConfigMaps here just to show how to check some properties of resources managed by a + // BulkDependentResource. In real life example this would be rather based on some status of those + // resources, like Pods. @Override public boolean isMet(BulkDependentTestCustomResource primary, Map secondary, Context context) { - return !SKIP_RESOURCE_DATA.equals(primary.getSpec().getAdditionalData()); + + return secondary.values().stream().allMatch(cm -> !cm.getData().isEmpty()); + } } From f11abfe150bfd995278c2b25fc080d8cffdc77ab Mon Sep 17 00:00:00 2001 From: csviri Date: Tue, 3 Jan 2023 10:36:31 +0100 Subject: [PATCH 5/5] docs --- docs/documentation/dependent-resources.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/docs/documentation/dependent-resources.md b/docs/documentation/dependent-resources.md index 04a203febe..30da9519e7 100644 --- a/docs/documentation/dependent-resources.md +++ b/docs/documentation/dependent-resources.md @@ -382,6 +382,8 @@ interface. Various examples are provided as [integration tests](https://github.com/java-operator-sdk/java-operator-sdk/tree/f5ffcfb6f546d79b4bab04ea503c8bad9d6acce6/operator-framework/src/test/java/io/javaoperatorsdk/operator/bulkdependent) . +To see how to add conditions on a bulk dependent resource [see integration test](https://github.com/java-operator-sdk/java-operator-sdk/blob/ad8759856cc59380ea4b0973478daa1140ec93e7/operator-framework/src/test/java/io/javaoperatorsdk/operator/bulkdependent/BulkDependentWithConditionIT.java). + ## Dependent Resources with Explicit State For cases when an external (non-Kubernetes) resource generates an ID during creation and from that point