From a8c9b95c2a268414478479fbb30789f0b71533f0 Mon Sep 17 00:00:00 2001 From: Chris Laprun Date: Thu, 16 Oct 2025 11:15:54 +0200 Subject: [PATCH] wip Signed-off-by: Chris Laprun --- .../operator/processing/MockController.java | 20 ++++++++++ .../dependent/desiredonce/DesiredOnce.java | 18 +++++++++ .../desiredonce/DesiredOnceDependent.java | 35 +++++++++++++++++ .../dependent/desiredonce/DesiredOnceIT.java | 38 +++++++++++++++++++ .../desiredonce/DesiredOnceReconciler.java | 16 ++++++++ .../desiredonce/DesiredOnceSpec.java | 3 ++ 6 files changed, 130 insertions(+) create mode 100644 operator-framework-core/src/test/java/io/javaoperatorsdk/operator/processing/MockController.java create mode 100644 operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/desiredonce/DesiredOnce.java create mode 100644 operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/desiredonce/DesiredOnceDependent.java create mode 100644 operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/desiredonce/DesiredOnceIT.java create mode 100644 operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/desiredonce/DesiredOnceReconciler.java create mode 100644 operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/desiredonce/DesiredOnceSpec.java diff --git a/operator-framework-core/src/test/java/io/javaoperatorsdk/operator/processing/MockController.java b/operator-framework-core/src/test/java/io/javaoperatorsdk/operator/processing/MockController.java new file mode 100644 index 0000000000..d9a5250860 --- /dev/null +++ b/operator-framework-core/src/test/java/io/javaoperatorsdk/operator/processing/MockController.java @@ -0,0 +1,20 @@ +package io.javaoperatorsdk.operator.processing; + +import io.fabric8.kubernetes.api.model.HasMetadata; +import io.javaoperatorsdk.operator.MockKubernetesClient; +import io.javaoperatorsdk.operator.api.config.BaseConfigurationService; +import io.javaoperatorsdk.operator.api.config.MockControllerConfiguration; +import io.javaoperatorsdk.operator.api.reconciler.Reconciler; + +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +public class MockController { + public static

Controller

mockController(Class

primaryClass) { + Reconciler

reconciler = mock(Reconciler.class); + final var conf = MockControllerConfiguration.forResource(primaryClass); + final var configurationService = new BaseConfigurationService(); + when(conf.getConfigurationService()).thenReturn(configurationService); + return new Controller<>(reconciler, conf, MockKubernetesClient.client(primaryClass)); + } +} diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/desiredonce/DesiredOnce.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/desiredonce/DesiredOnce.java new file mode 100644 index 0000000000..e091a6a646 --- /dev/null +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/desiredonce/DesiredOnce.java @@ -0,0 +1,18 @@ +package io.javaoperatorsdk.operator.dependent.desiredonce; + +import io.fabric8.kubernetes.client.CustomResource; +import io.fabric8.kubernetes.model.annotation.Group; +import io.fabric8.kubernetes.model.annotation.Version; + +@Group("io.josdk") +@Version("v1") +public class DesiredOnce extends CustomResource { + static final String KEY = "key"; + static final String VALUE = "value"; + + public DesiredOnce() {} + + public DesiredOnce(String value) { + this.spec = new DesiredOnceSpec(value); + } +} diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/desiredonce/DesiredOnceDependent.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/desiredonce/DesiredOnceDependent.java new file mode 100644 index 0000000000..c904b49658 --- /dev/null +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/desiredonce/DesiredOnceDependent.java @@ -0,0 +1,35 @@ +package io.javaoperatorsdk.operator.dependent.desiredonce; + +import java.util.Map; + +import io.fabric8.kubernetes.api.model.ConfigMap; +import io.fabric8.kubernetes.api.model.ConfigMapBuilder; +import io.javaoperatorsdk.operator.api.config.informer.Informer; +import io.javaoperatorsdk.operator.api.reconciler.Context; +import io.javaoperatorsdk.operator.processing.dependent.kubernetes.CRUDKubernetesDependentResource; +import io.javaoperatorsdk.operator.processing.dependent.kubernetes.KubernetesDependent; + +@KubernetesDependent(informer = @Informer(labelSelector = "desiredonce=true")) +public class DesiredOnceDependent extends CRUDKubernetesDependentResource { + private boolean desiredAlreadyCalled; + + @Override + protected ConfigMap desired(DesiredOnce primary, Context context) { + if (desiredAlreadyCalled) { + throw new IllegalStateException("desired should have only been called once"); + } + desiredAlreadyCalled = true; + return new ConfigMapBuilder() + .editOrNewMetadata() + .withName(getName(primary)) + .withNamespace(primary.getMetadata().getNamespace()) + .withLabels(Map.of("desiredonce", "true")) + .endMetadata() + .addToData(DesiredOnce.KEY, primary.getSpec().value()) + .build(); + } + + static String getName(DesiredOnce primary) { + return primary.getMetadata().getName() + "cm"; + } +} diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/desiredonce/DesiredOnceIT.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/desiredonce/DesiredOnceIT.java new file mode 100644 index 0000000000..658bf4184f --- /dev/null +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/desiredonce/DesiredOnceIT.java @@ -0,0 +1,38 @@ +package io.javaoperatorsdk.operator.dependent.desiredonce; + +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.RegisterExtension; + +import io.fabric8.kubernetes.api.model.ConfigMap; +import io.fabric8.kubernetes.api.model.ObjectMetaBuilder; +import io.javaoperatorsdk.operator.junit.LocallyRunOperatorExtension; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.awaitility.Awaitility.await; + +class DesiredOnceIT { + + @RegisterExtension + LocallyRunOperatorExtension operator = + LocallyRunOperatorExtension.builder().withReconciler(DesiredOnceReconciler.class).build(); + + @Test + public void checkThatDesiredIsOnlyCalledOncePerReconciliation() { + var resource = operator.create(testResource()); + + await() + .untilAsserted( + () -> { + var cm = operator.get(ConfigMap.class, DesiredOnceDependent.getName(resource)); + assertThat(cm).isNotNull(); + assertThat(cm.getData().get(DesiredOnce.KEY)).isEqualTo(DesiredOnce.VALUE); + }); + } + + private DesiredOnce testResource() { + var res = new DesiredOnce(DesiredOnce.VALUE); + res.setMetadata( + new ObjectMetaBuilder().withName("test").withNamespace(operator.getNamespace()).build()); + return res; + } +} diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/desiredonce/DesiredOnceReconciler.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/desiredonce/DesiredOnceReconciler.java new file mode 100644 index 0000000000..5e7844322b --- /dev/null +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/desiredonce/DesiredOnceReconciler.java @@ -0,0 +1,16 @@ +package io.javaoperatorsdk.operator.dependent.desiredonce; + +import io.javaoperatorsdk.operator.api.reconciler.Context; +import io.javaoperatorsdk.operator.api.reconciler.Reconciler; +import io.javaoperatorsdk.operator.api.reconciler.UpdateControl; +import io.javaoperatorsdk.operator.api.reconciler.Workflow; +import io.javaoperatorsdk.operator.api.reconciler.dependent.Dependent; + +@Workflow(dependents = @Dependent(type = DesiredOnceDependent.class)) +public class DesiredOnceReconciler implements Reconciler { + @Override + public UpdateControl reconcile(DesiredOnce resource, Context context) + throws Exception { + return UpdateControl.noUpdate(); + } +} diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/desiredonce/DesiredOnceSpec.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/desiredonce/DesiredOnceSpec.java new file mode 100644 index 0000000000..ae31b526b2 --- /dev/null +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/desiredonce/DesiredOnceSpec.java @@ -0,0 +1,3 @@ +package io.javaoperatorsdk.operator.dependent.desiredonce; + +public record DesiredOnceSpec(String value) {}