diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/UnmodifiableDependentPartIT.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/UnmodifiableDependentPartIT.java new file mode 100644 index 0000000000..bc0e951d8a --- /dev/null +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/UnmodifiableDependentPartIT.java @@ -0,0 +1,63 @@ +package io.javaoperatorsdk.operator; + +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 io.javaoperatorsdk.operator.sample.unmodifiabledependentpart.UnmodifiableDependentPartCustomResource; +import io.javaoperatorsdk.operator.sample.unmodifiabledependentpart.UnmodifiableDependentPartReconciler; +import io.javaoperatorsdk.operator.sample.unmodifiabledependentpart.UnmodifiableDependentPartSpec; + +import static io.javaoperatorsdk.operator.sample.unmodifiabledependentpart.UnmodifiablePartConfigMapDependent.ACTUAL_DATA_KEY; +import static io.javaoperatorsdk.operator.sample.unmodifiabledependentpart.UnmodifiablePartConfigMapDependent.UNMODIFIABLE_INITIAL_DATA_KEY; +import static org.assertj.core.api.Assertions.assertThat; +import static org.awaitility.Awaitility.await; + +public class UnmodifiableDependentPartIT { + + public static final String TEST_RESOURCE_NAME = "test1"; + public static final String INITIAL_DATA = "initialData"; + public static final String UPDATED_DATA = "updatedData"; + + @RegisterExtension + LocallyRunOperatorExtension operator = + LocallyRunOperatorExtension.builder() + .withReconciler(UnmodifiableDependentPartReconciler.class) + .build(); + + @Test + void partConfigMapDataUnmodifiable() { + var resource = operator.create(testResource()); + + await().untilAsserted(() -> { + var cm = operator.get(ConfigMap.class, TEST_RESOURCE_NAME); + assertThat(cm).isNotNull(); + assertThat(cm.getData()).containsEntry(UNMODIFIABLE_INITIAL_DATA_KEY, INITIAL_DATA); + assertThat(cm.getData()).containsEntry(ACTUAL_DATA_KEY, INITIAL_DATA); + }); + + resource.getSpec().setData(UPDATED_DATA); + operator.replace(resource); + + await().untilAsserted(() -> { + var cm = operator.get(ConfigMap.class, TEST_RESOURCE_NAME); + assertThat(cm).isNotNull(); + assertThat(cm.getData()).containsEntry(UNMODIFIABLE_INITIAL_DATA_KEY, INITIAL_DATA); + assertThat(cm.getData()).containsEntry(ACTUAL_DATA_KEY, UPDATED_DATA); + }); + } + + + UnmodifiableDependentPartCustomResource testResource() { + var res = new UnmodifiableDependentPartCustomResource(); + res.setMetadata(new ObjectMetaBuilder() + .withName(TEST_RESOURCE_NAME) + .build()); + res.setSpec(new UnmodifiableDependentPartSpec()); + res.getSpec().setData(INITIAL_DATA); + return res; + } + +} diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/unmodifiabledependentpart/UnmodifiableDependentPartCustomResource.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/unmodifiabledependentpart/UnmodifiableDependentPartCustomResource.java new file mode 100644 index 0000000000..013fd62503 --- /dev/null +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/unmodifiabledependentpart/UnmodifiableDependentPartCustomResource.java @@ -0,0 +1,16 @@ +package io.javaoperatorsdk.operator.sample.unmodifiabledependentpart; + +import io.fabric8.kubernetes.api.model.Namespaced; +import io.fabric8.kubernetes.client.CustomResource; +import io.fabric8.kubernetes.model.annotation.Group; +import io.fabric8.kubernetes.model.annotation.ShortNames; +import io.fabric8.kubernetes.model.annotation.Version; + +@Group("sample.javaoperatorsdk") +@Version("v1") +@ShortNames("udp") +public class UnmodifiableDependentPartCustomResource + extends CustomResource + implements Namespaced { + +} diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/unmodifiabledependentpart/UnmodifiableDependentPartReconciler.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/unmodifiabledependentpart/UnmodifiableDependentPartReconciler.java new file mode 100644 index 0000000000..fd63a2cb12 --- /dev/null +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/unmodifiabledependentpart/UnmodifiableDependentPartReconciler.java @@ -0,0 +1,30 @@ +package io.javaoperatorsdk.operator.sample.unmodifiabledependentpart; + +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(type = UnmodifiablePartConfigMapDependent.class)}) +public class UnmodifiableDependentPartReconciler + implements Reconciler { + + private final AtomicInteger numberOfExecutions = new AtomicInteger(0); + + @Override + public UpdateControl reconcile( + UnmodifiableDependentPartCustomResource resource, + Context context) + throws InterruptedException { + numberOfExecutions.addAndGet(1); + return UpdateControl.noUpdate(); + } + + public int getNumberOfExecutions() { + return numberOfExecutions.get(); + } + +} diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/unmodifiabledependentpart/UnmodifiableDependentPartSpec.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/unmodifiabledependentpart/UnmodifiableDependentPartSpec.java new file mode 100644 index 0000000000..3f9c9b3460 --- /dev/null +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/unmodifiabledependentpart/UnmodifiableDependentPartSpec.java @@ -0,0 +1,15 @@ +package io.javaoperatorsdk.operator.sample.unmodifiabledependentpart; + +public class UnmodifiableDependentPartSpec { + + private String data; + + public String getData() { + return data; + } + + public UnmodifiableDependentPartSpec setData(String data) { + this.data = data; + return this; + } +} diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/unmodifiabledependentpart/UnmodifiablePartConfigMapDependent.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/unmodifiabledependentpart/UnmodifiablePartConfigMapDependent.java new file mode 100644 index 0000000000..a103e53162 --- /dev/null +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/unmodifiabledependentpart/UnmodifiablePartConfigMapDependent.java @@ -0,0 +1,38 @@ +package io.javaoperatorsdk.operator.sample.unmodifiabledependentpart; + +import java.util.Map; + +import io.fabric8.kubernetes.api.model.ConfigMap; +import io.fabric8.kubernetes.api.model.ConfigMapBuilder; +import io.fabric8.kubernetes.api.model.ObjectMetaBuilder; +import io.javaoperatorsdk.operator.api.reconciler.Context; +import io.javaoperatorsdk.operator.processing.dependent.kubernetes.CRUDKubernetesDependentResource; + +public class UnmodifiablePartConfigMapDependent + extends CRUDKubernetesDependentResource { + + public static final String UNMODIFIABLE_INITIAL_DATA_KEY = "initialDataKey"; + public static final String ACTUAL_DATA_KEY = "actualDataKey"; + + public UnmodifiablePartConfigMapDependent() { + super(ConfigMap.class); + } + + @Override + protected ConfigMap desired(UnmodifiableDependentPartCustomResource primary, + Context context) { + var actual = getSecondaryResource(primary, context); + ConfigMap res = new ConfigMapBuilder() + .withMetadata(new ObjectMetaBuilder() + .withName(primary.getMetadata().getName()) + .withNamespace(primary.getMetadata().getNamespace()) + .build()) + .build(); + res.setData(Map.of(ACTUAL_DATA_KEY, primary.getSpec().getData(), + // setting the old data if available + UNMODIFIABLE_INITIAL_DATA_KEY, + actual.map(cm -> cm.getData().get(UNMODIFIABLE_INITIAL_DATA_KEY)) + .orElse(primary.getSpec().getData()))); + return res; + } +}