From ad7dd1bb387c1e58cbf7de635120ea27547bce7b Mon Sep 17 00:00:00 2001 From: Lucy Linder Date: Thu, 6 Oct 2022 08:12:16 +0200 Subject: [PATCH 1/4] feat: demonstrate contract-first CRDs Modify the leader-election sample to use contract-first. That is, the CRDs is written in YAML and the java code is generated automatically using the java-generator-maven-plugin from fabric8. --- sample-operators/leader-election/README.md | 8 +++-- sample-operators/leader-election/pom.xml | 21 +++++++++--- .../LeaderElectionTestCustomResource.java | 15 --------- .../sample/LeaderElectionTestReconciler.java | 13 +++++--- .../sample/LeaderElectionTestStatus.java | 21 ------------ ...aderelection.sample.javaoperatorsdk-v1.yml | 32 +++++++++++++++++++ .../operator/sample/LeaderElectionE2E.java | 14 ++++---- 7 files changed, 70 insertions(+), 54 deletions(-) delete mode 100644 sample-operators/leader-election/src/main/java/io/javaoperatorsdk/operator/sample/LeaderElectionTestCustomResource.java delete mode 100644 sample-operators/leader-election/src/main/java/io/javaoperatorsdk/operator/sample/LeaderElectionTestStatus.java create mode 100644 sample-operators/leader-election/src/main/resources/kubernetes/leaderelection.sample.javaoperatorsdk-v1.yml diff --git a/sample-operators/leader-election/README.md b/sample-operators/leader-election/README.md index 2a9a369a45..d74bad0b35 100644 --- a/sample-operators/leader-election/README.md +++ b/sample-operators/leader-election/README.md @@ -1,6 +1,10 @@ # Leader Election E2E Test -The purpose of this module is to e2e test leader election feature. +The purpose of this module is to e2e test leader election feature and to demonstrate contract-first CRDs. The deployment is using directly pods in order to better control some aspects in test. -In real life this would be a Deployment. \ No newline at end of file +In real life this would be a Deployment. + +The custom resource definition (CRD) is defined in YAML in the folder `src/main/resources/kubernetes`. +Upon build, the [java-generator-maven-plugin](https://github.com/fabric8io/kubernetes-client/blob/master/doc/java-generation-from-CRD.md) +generates the Java code under `target/generated-sources/java`. diff --git a/sample-operators/leader-election/pom.xml b/sample-operators/leader-election/pom.xml index 87b346a818..bb417df67e 100644 --- a/sample-operators/leader-election/pom.xml +++ b/sample-operators/leader-election/pom.xml @@ -47,11 +47,6 @@ takes 1.21.1 - - io.fabric8 - crd-generator-apt - provided - org.awaitility awaitility @@ -88,6 +83,22 @@ maven-compiler-plugin 3.10.0 + + + io.fabric8 + java-generator-maven-plugin + ${fabric8-client.version} + + + + generate + + + + + src/main/resources/kubernetes + + diff --git a/sample-operators/leader-election/src/main/java/io/javaoperatorsdk/operator/sample/LeaderElectionTestCustomResource.java b/sample-operators/leader-election/src/main/java/io/javaoperatorsdk/operator/sample/LeaderElectionTestCustomResource.java deleted file mode 100644 index b440be4d18..0000000000 --- a/sample-operators/leader-election/src/main/java/io/javaoperatorsdk/operator/sample/LeaderElectionTestCustomResource.java +++ /dev/null @@ -1,15 +0,0 @@ -package io.javaoperatorsdk.operator.sample; - -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("le") -public class LeaderElectionTestCustomResource - extends CustomResource - implements Namespaced { -} diff --git a/sample-operators/leader-election/src/main/java/io/javaoperatorsdk/operator/sample/LeaderElectionTestReconciler.java b/sample-operators/leader-election/src/main/java/io/javaoperatorsdk/operator/sample/LeaderElectionTestReconciler.java index f6231d0a52..78d36d7aee 100644 --- a/sample-operators/leader-election/src/main/java/io/javaoperatorsdk/operator/sample/LeaderElectionTestReconciler.java +++ b/sample-operators/leader-election/src/main/java/io/javaoperatorsdk/operator/sample/LeaderElectionTestReconciler.java @@ -7,9 +7,12 @@ import io.javaoperatorsdk.operator.api.reconciler.Reconciler; import io.javaoperatorsdk.operator.api.reconciler.UpdateControl; +import javaoperatorsdk.sample.v1.LeaderElection; +import javaoperatorsdk.sample.v1.LeaderElectionStatus; + @ControllerConfiguration() public class LeaderElectionTestReconciler - implements Reconciler { + implements Reconciler { private final String reconcilerName; @@ -18,12 +21,12 @@ public LeaderElectionTestReconciler(String reconcilerName) { } @Override - public UpdateControl reconcile( - LeaderElectionTestCustomResource resource, - Context context) { + public UpdateControl reconcile( + LeaderElection resource, + Context context) { if (resource.getStatus() == null) { - resource.setStatus(new LeaderElectionTestStatus()); + resource.setStatus(new LeaderElectionStatus()); } resource.getStatus().getReconciledBy().add(reconcilerName); diff --git a/sample-operators/leader-election/src/main/java/io/javaoperatorsdk/operator/sample/LeaderElectionTestStatus.java b/sample-operators/leader-election/src/main/java/io/javaoperatorsdk/operator/sample/LeaderElectionTestStatus.java deleted file mode 100644 index 5a9e66e270..0000000000 --- a/sample-operators/leader-election/src/main/java/io/javaoperatorsdk/operator/sample/LeaderElectionTestStatus.java +++ /dev/null @@ -1,21 +0,0 @@ -package io.javaoperatorsdk.operator.sample; - -import java.util.ArrayList; -import java.util.List; - -public class LeaderElectionTestStatus { - - private List reconciledBy; - - public List getReconciledBy() { - if (reconciledBy == null) { - reconciledBy = new ArrayList<>(); - } - return reconciledBy; - } - - public LeaderElectionTestStatus setReconciledBy(List reconciledBy) { - this.reconciledBy = reconciledBy; - return this; - } -} diff --git a/sample-operators/leader-election/src/main/resources/kubernetes/leaderelection.sample.javaoperatorsdk-v1.yml b/sample-operators/leader-election/src/main/resources/kubernetes/leaderelection.sample.javaoperatorsdk-v1.yml new file mode 100644 index 0000000000..7871caf7dc --- /dev/null +++ b/sample-operators/leader-election/src/main/resources/kubernetes/leaderelection.sample.javaoperatorsdk-v1.yml @@ -0,0 +1,32 @@ +# Custom Resource Definition that will be used to generate the Java classes in target/generated-sources/java +# See https://github.com/fabric8io/kubernetes-client/blob/master/doc/java-generation-from-CRD.md +# The Java classes will then be used to recreate this CR in target/classes/META-INF/fabric8 +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + name: leaderelection.sample.javaoperatorsdk +spec: + group: sample.javaoperatorsdk + names: + kind: LeaderElection + singular: leaderelection + plural: leaderelection + shortNames: + - le + - les + scope: Namespaced + versions: + - name: v1 + schema: + openAPIV3Schema: + properties: + status: + properties: + reconciledBy: + items: + type: string + type: array + type: object + type: object + served: true + storage: true diff --git a/sample-operators/leader-election/src/test/java/io/javaoperatorsdk/operator/sample/LeaderElectionE2E.java b/sample-operators/leader-election/src/test/java/io/javaoperatorsdk/operator/sample/LeaderElectionE2E.java index 9b4eb736db..b5deec9058 100644 --- a/sample-operators/leader-election/src/test/java/io/javaoperatorsdk/operator/sample/LeaderElectionE2E.java +++ b/sample-operators/leader-election/src/test/java/io/javaoperatorsdk/operator/sample/LeaderElectionE2E.java @@ -27,6 +27,8 @@ import io.fabric8.kubernetes.client.KubernetesClient; import io.fabric8.kubernetes.client.KubernetesClientBuilder; +import javaoperatorsdk.sample.v1.LeaderElection; + import static io.javaoperatorsdk.operator.junit.AbstractOperatorExtension.CRD_READY_WAIT; import static org.assertj.core.api.Assertions.assertThat; import static org.awaitility.Awaitility.await; @@ -61,7 +63,7 @@ void otherInstancesTakesOverWhenSteppingDown(String yamlFilePrefix) { await().pollDelay(Duration.ofSeconds(MINIMAL_SECONDS_FOR_RENEWAL)) .atMost(Duration.ofSeconds(MAX_WAIT_SECONDS)) .untilAsserted(() -> { - var actualStatus = client.resources(LeaderElectionTestCustomResource.class) + var actualStatus = client.resources(LeaderElection.class) .inNamespace(namespace).withName(TEST_RESOURCE_NAME).get().getStatus(); assertThat(actualStatus).isNotNull(); @@ -71,14 +73,14 @@ void otherInstancesTakesOverWhenSteppingDown(String yamlFilePrefix) { client.pods().inNamespace(namespace).withName(OPERATOR_1_POD_NAME).delete(); - var actualListSize = client.resources(LeaderElectionTestCustomResource.class) + var actualListSize = client.resources(LeaderElection.class) .inNamespace(namespace).withName(TEST_RESOURCE_NAME).get().getStatus().getReconciledBy() .size(); await().pollDelay(Duration.ofSeconds(MINIMAL_SECONDS_FOR_RENEWAL)) .atMost(Duration.ofSeconds(240)) .untilAsserted(() -> { - var actualStatus = client.resources(LeaderElectionTestCustomResource.class) + var actualStatus = client.resources(LeaderElection.class) .inNamespace(namespace).withName(TEST_RESOURCE_NAME).get().getStatus(); assertThat(actualStatus).isNotNull(); @@ -87,7 +89,7 @@ void otherInstancesTakesOverWhenSteppingDown(String yamlFilePrefix) { }); assertReconciliations( - client.resources(LeaderElectionTestCustomResource.class).inNamespace(namespace) + client.resources(LeaderElection.class).inNamespace(namespace) .withName(TEST_RESOURCE_NAME).get().getStatus().getReconciledBy()); } @@ -104,7 +106,7 @@ private void assertReconciliations(List reconciledBy) { } private void applyCustomResource() { - var res = new LeaderElectionTestCustomResource(); + var res = new LeaderElection(); res.setMetadata(new ObjectMetaBuilder() .withName(TEST_RESOURCE_NAME) .withNamespace(namespace) @@ -150,7 +152,7 @@ private void deployOperatorsInOrder(String yamlFilePrefix) { void applyCRD() { String path = - "./target/classes/META-INF/fabric8/leaderelectiontestcustomresources.sample.javaoperatorsdk-v1.yml"; + "./src/main/resources/kubernetes/leaderelection.sample.javaoperatorsdk-v1.yml"; try (InputStream is = new FileInputStream(path)) { final var crd = client.load(is); crd.createOrReplace(); From 66d9973a19b49246e4b394b06331f0a7abcbb49e Mon Sep 17 00:00:00 2001 From: Lucy Linder Date: Thu, 6 Oct 2022 13:11:31 +0200 Subject: [PATCH 2/4] Fix custom resource name and plural --- .../kubernetes/leaderelection.sample.javaoperatorsdk-v1.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/sample-operators/leader-election/src/main/resources/kubernetes/leaderelection.sample.javaoperatorsdk-v1.yml b/sample-operators/leader-election/src/main/resources/kubernetes/leaderelection.sample.javaoperatorsdk-v1.yml index 7871caf7dc..bf3c90cb60 100644 --- a/sample-operators/leader-election/src/main/resources/kubernetes/leaderelection.sample.javaoperatorsdk-v1.yml +++ b/sample-operators/leader-election/src/main/resources/kubernetes/leaderelection.sample.javaoperatorsdk-v1.yml @@ -4,13 +4,13 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: - name: leaderelection.sample.javaoperatorsdk + name: leaderelections.sample.javaoperatorsdk spec: group: sample.javaoperatorsdk names: kind: LeaderElection singular: leaderelection - plural: leaderelection + plural: leaderelections shortNames: - le - les From f0a3751fb56aeb0d2606c42036294b28a4805019 Mon Sep 17 00:00:00 2001 From: Lucy Linder Date: Thu, 6 Oct 2022 17:27:56 +0200 Subject: [PATCH 3/4] Add status to CRD, ensure reconciledBy is not null, fix ClusterRole Lots of silly mistakes, sorry for that. --- .../leader-election/k8s/namespace-inferred-operator.yaml | 4 ++-- sample-operators/leader-election/k8s/operator.yaml | 4 ++-- .../operator/sample/LeaderElectionTestReconciler.java | 4 ++++ .../kubernetes/leaderelection.sample.javaoperatorsdk-v1.yml | 2 ++ 4 files changed, 10 insertions(+), 4 deletions(-) diff --git a/sample-operators/leader-election/k8s/namespace-inferred-operator.yaml b/sample-operators/leader-election/k8s/namespace-inferred-operator.yaml index 68e00d81ad..13724a911a 100644 --- a/sample-operators/leader-election/k8s/namespace-inferred-operator.yaml +++ b/sample-operators/leader-election/k8s/namespace-inferred-operator.yaml @@ -48,8 +48,8 @@ rules: - apiGroups: - "sample.javaoperatorsdk" resources: - - leaderelectiontestcustomresources - - leaderelectiontestcustomresources/status + - leaderelections + - leaderelections/status verbs: - '*' - apiGroups: diff --git a/sample-operators/leader-election/k8s/operator.yaml b/sample-operators/leader-election/k8s/operator.yaml index 00ed3e7273..9d289a2d6c 100644 --- a/sample-operators/leader-election/k8s/operator.yaml +++ b/sample-operators/leader-election/k8s/operator.yaml @@ -52,8 +52,8 @@ rules: - apiGroups: - "sample.javaoperatorsdk" resources: - - leaderelectiontestcustomresources - - leaderelectiontestcustomresources/status + - leaderelections + - leaderelections/status verbs: - '*' - apiGroups: diff --git a/sample-operators/leader-election/src/main/java/io/javaoperatorsdk/operator/sample/LeaderElectionTestReconciler.java b/sample-operators/leader-election/src/main/java/io/javaoperatorsdk/operator/sample/LeaderElectionTestReconciler.java index 78d36d7aee..4cd8627328 100644 --- a/sample-operators/leader-election/src/main/java/io/javaoperatorsdk/operator/sample/LeaderElectionTestReconciler.java +++ b/sample-operators/leader-election/src/main/java/io/javaoperatorsdk/operator/sample/LeaderElectionTestReconciler.java @@ -1,6 +1,7 @@ package io.javaoperatorsdk.operator.sample; import java.time.Duration; +import java.util.ArrayList; import io.javaoperatorsdk.operator.api.reconciler.Context; import io.javaoperatorsdk.operator.api.reconciler.ControllerConfiguration; @@ -28,6 +29,9 @@ public UpdateControl reconcile( if (resource.getStatus() == null) { resource.setStatus(new LeaderElectionStatus()); } + if (resource.getStatus().getReconciledBy() == null) { + resource.getStatus().setReconciledBy(new ArrayList<>()); + } resource.getStatus().getReconciledBy().add(reconcilerName); // update status is with optimistic locking diff --git a/sample-operators/leader-election/src/main/resources/kubernetes/leaderelection.sample.javaoperatorsdk-v1.yml b/sample-operators/leader-election/src/main/resources/kubernetes/leaderelection.sample.javaoperatorsdk-v1.yml index bf3c90cb60..cc9d8c3fc6 100644 --- a/sample-operators/leader-election/src/main/resources/kubernetes/leaderelection.sample.javaoperatorsdk-v1.yml +++ b/sample-operators/leader-election/src/main/resources/kubernetes/leaderelection.sample.javaoperatorsdk-v1.yml @@ -30,3 +30,5 @@ spec: type: object served: true storage: true + subresources: + status: {} From e30a7220a0e5016685ef764c74517fa99e852f28 Mon Sep 17 00:00:00 2001 From: Lucy Linder Date: Fri, 7 Oct 2022 11:42:48 +0200 Subject: [PATCH 4/4] Use plural form in CRD file name --- ...sdk-v1.yml => leaderelections.sample.javaoperatorsdk-v1.yml} | 0 .../io/javaoperatorsdk/operator/sample/LeaderElectionE2E.java | 2 +- 2 files changed, 1 insertion(+), 1 deletion(-) rename sample-operators/leader-election/src/main/resources/kubernetes/{leaderelection.sample.javaoperatorsdk-v1.yml => leaderelections.sample.javaoperatorsdk-v1.yml} (100%) diff --git a/sample-operators/leader-election/src/main/resources/kubernetes/leaderelection.sample.javaoperatorsdk-v1.yml b/sample-operators/leader-election/src/main/resources/kubernetes/leaderelections.sample.javaoperatorsdk-v1.yml similarity index 100% rename from sample-operators/leader-election/src/main/resources/kubernetes/leaderelection.sample.javaoperatorsdk-v1.yml rename to sample-operators/leader-election/src/main/resources/kubernetes/leaderelections.sample.javaoperatorsdk-v1.yml diff --git a/sample-operators/leader-election/src/test/java/io/javaoperatorsdk/operator/sample/LeaderElectionE2E.java b/sample-operators/leader-election/src/test/java/io/javaoperatorsdk/operator/sample/LeaderElectionE2E.java index b5deec9058..5512f54fc1 100644 --- a/sample-operators/leader-election/src/test/java/io/javaoperatorsdk/operator/sample/LeaderElectionE2E.java +++ b/sample-operators/leader-election/src/test/java/io/javaoperatorsdk/operator/sample/LeaderElectionE2E.java @@ -152,7 +152,7 @@ private void deployOperatorsInOrder(String yamlFilePrefix) { void applyCRD() { String path = - "./src/main/resources/kubernetes/leaderelection.sample.javaoperatorsdk-v1.yml"; + "./src/main/resources/kubernetes/leaderelections.sample.javaoperatorsdk-v1.yml"; try (InputStream is = new FileInputStream(path)) { final var crd = client.load(is); crd.createOrReplace();