From 3b9c4629c195375d0cdf32dfd51e48f6cacd1335 Mon Sep 17 00:00:00 2001
From: csviri <csviri@gmail.com>
Date: Tue, 31 Jan 2023 13:07:37 +0100
Subject: [PATCH 1/2] feat: sample of how to do unmodifiable part

---
 .../operator/UnmodifiableDependentPartIT.java | 63 +++++++++++++++++++
 ...modifiableDependentPartCustomResource.java | 16 +++++
 .../UnmodifiableDependentPartReconciler.java  | 30 +++++++++
 .../UnmodifiableDependentPartSpec.java        | 15 +++++
 .../UnmodifiablePartConfigMapDependent.java   | 38 +++++++++++
 5 files changed, 162 insertions(+)
 create mode 100644 operator-framework/src/test/java/io/javaoperatorsdk/operator/UnmodifiableDependentPartIT.java
 create mode 100644 operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/unmodifiabledependentpart/UnmodifiableDependentPartCustomResource.java
 create mode 100644 operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/unmodifiabledependentpart/UnmodifiableDependentPartReconciler.java
 create mode 100644 operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/unmodifiabledependentpart/UnmodifiableDependentPartSpec.java
 create mode 100644 operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/unmodifiabledependentpart/UnmodifiablePartConfigMapDependent.java

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..dc4a2ade9b
--- /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().get(UNMODIFIABLE_INITIAL_DATA_KEY)).isEqualTo(INITIAL_DATA);
+      assertThat(cm.getData().get(ACTUAL_DATA_KEY)).isEqualTo(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().get(UNMODIFIABLE_INITIAL_DATA_KEY)).isEqualTo(INITIAL_DATA);
+      assertThat(cm.getData().get(ACTUAL_DATA_KEY)).isEqualTo(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<UnmodifiableDependentPartSpec, Void>
+    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<UnmodifiableDependentPartCustomResource> {
+
+  private final AtomicInteger numberOfExecutions = new AtomicInteger(0);
+
+  @Override
+  public UpdateControl<UnmodifiableDependentPartCustomResource> reconcile(
+      UnmodifiableDependentPartCustomResource resource,
+      Context<UnmodifiableDependentPartCustomResource> 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<ConfigMap, UnmodifiableDependentPartCustomResource> {
+
+  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<UnmodifiableDependentPartCustomResource> 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;
+  }
+}

From c870680162050cafd22d5c3b094879e60752592a Mon Sep 17 00:00:00 2001
From: csviri <csviri@gmail.com>
Date: Tue, 31 Jan 2023 13:34:46 +0100
Subject: [PATCH 2/2] test improvements

---
 .../operator/UnmodifiableDependentPartIT.java             | 8 ++++----
 1 file changed, 4 insertions(+), 4 deletions(-)

diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/UnmodifiableDependentPartIT.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/UnmodifiableDependentPartIT.java
index dc4a2ade9b..bc0e951d8a 100644
--- a/operator-framework/src/test/java/io/javaoperatorsdk/operator/UnmodifiableDependentPartIT.java
+++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/UnmodifiableDependentPartIT.java
@@ -34,8 +34,8 @@ void partConfigMapDataUnmodifiable() {
     await().untilAsserted(() -> {
       var cm = operator.get(ConfigMap.class, TEST_RESOURCE_NAME);
       assertThat(cm).isNotNull();
-      assertThat(cm.getData().get(UNMODIFIABLE_INITIAL_DATA_KEY)).isEqualTo(INITIAL_DATA);
-      assertThat(cm.getData().get(ACTUAL_DATA_KEY)).isEqualTo(INITIAL_DATA);
+      assertThat(cm.getData()).containsEntry(UNMODIFIABLE_INITIAL_DATA_KEY, INITIAL_DATA);
+      assertThat(cm.getData()).containsEntry(ACTUAL_DATA_KEY, INITIAL_DATA);
     });
 
     resource.getSpec().setData(UPDATED_DATA);
@@ -44,8 +44,8 @@ void partConfigMapDataUnmodifiable() {
     await().untilAsserted(() -> {
       var cm = operator.get(ConfigMap.class, TEST_RESOURCE_NAME);
       assertThat(cm).isNotNull();
-      assertThat(cm.getData().get(UNMODIFIABLE_INITIAL_DATA_KEY)).isEqualTo(INITIAL_DATA);
-      assertThat(cm.getData().get(ACTUAL_DATA_KEY)).isEqualTo(UPDATED_DATA);
+      assertThat(cm.getData()).containsEntry(UNMODIFIABLE_INITIAL_DATA_KEY, INITIAL_DATA);
+      assertThat(cm.getData()).containsEntry(ACTUAL_DATA_KEY, UPDATED_DATA);
     });
   }