diff --git a/docs/documentation/v4-4-migration.md b/docs/documentation/v4-4-migration.md index 0f7d1929af..c198871f3f 100644 --- a/docs/documentation/v4-4-migration.md +++ b/docs/documentation/v4-4-migration.md @@ -77,7 +77,7 @@ the `KubernetesDependentResource`. The SSA based create/update can be combined with the legacy matcher, simply override the `match` method and use the [GenericKubernetesResourceMatcher](https://github.com/java-operator-sdk/java-operator-sdk/blob/main/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/kubernetes/GenericKubernetesResourceMatcher.java#L19-L19) -directly. +directly. See related [sample](https://github.com/java-operator-sdk/java-operator-sdk/blob/main/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/ssalegacymatcher/ServiceDependentResource.java#L39-L44). ### Migration from plain Update/Create to SSA Based Patch diff --git a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/kubernetes/GenericKubernetesResourceMatcher.java b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/kubernetes/GenericKubernetesResourceMatcher.java index 054dacef23..36935e08a0 100644 --- a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/kubernetes/GenericKubernetesResourceMatcher.java +++ b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/kubernetes/GenericKubernetesResourceMatcher.java @@ -19,7 +19,7 @@ public class GenericKubernetesResourceMatcher implements Matcher { - private static String SPEC = "/spec"; + private static final String SPEC = "/spec"; private static final String ADD = "add"; private static final String OP = "op"; public static final String METADATA_LABELS = "/metadata/labels"; diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/SSAWithLegacyMatcherIT.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/SSAWithLegacyMatcherIT.java new file mode 100644 index 0000000000..ba3444bfb8 --- /dev/null +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/SSAWithLegacyMatcherIT.java @@ -0,0 +1,53 @@ +package io.javaoperatorsdk.operator; + +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.RegisterExtension; + +import io.fabric8.kubernetes.api.model.ObjectMetaBuilder; +import io.fabric8.kubernetes.api.model.Service; +import io.javaoperatorsdk.operator.junit.LocallyRunOperatorExtension; +import io.javaoperatorsdk.operator.sample.ssalegacymatcher.SSALegacyMatcherCustomResource; +import io.javaoperatorsdk.operator.sample.ssalegacymatcher.SSALegacyMatcherReconciler; +import io.javaoperatorsdk.operator.sample.ssalegacymatcher.SSALegacyMatcherSpec; +import io.javaoperatorsdk.operator.sample.ssalegacymatcher.ServiceDependentResource; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.awaitility.Awaitility.await; + +public class SSAWithLegacyMatcherIT { + + public static final String TEST_RESOURCE_NAME = "test1"; + + @RegisterExtension + LocallyRunOperatorExtension extension = + LocallyRunOperatorExtension.builder().withReconciler(new SSALegacyMatcherReconciler()) + .build(); + + @Test + void matchesDependentWithLegacyMatcher() { + var resource = extension.create(testResource()); + + await().untilAsserted(() -> { + var service = extension.get(Service.class, TEST_RESOURCE_NAME); + assertThat(service).isNotNull(); + assertThat(ServiceDependentResource.createUpdateCount.get()).isEqualTo(1); + }); + + resource.getSpec().setValue("other_value"); + + await().untilAsserted(() -> { + assertThat(ServiceDependentResource.createUpdateCount.get()).isEqualTo(1); + }); + } + + SSALegacyMatcherCustomResource testResource() { + SSALegacyMatcherCustomResource res = new SSALegacyMatcherCustomResource(); + res.setMetadata(new ObjectMetaBuilder() + .withName(TEST_RESOURCE_NAME) + .build()); + res.setSpec(new SSALegacyMatcherSpec()); + res.getSpec().setValue("initial-value"); + return res; + } + +} diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/ssalegacymatcher/SSALegacyMatcherCustomResource.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/ssalegacymatcher/SSALegacyMatcherCustomResource.java new file mode 100644 index 0000000000..d68372bbeb --- /dev/null +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/ssalegacymatcher/SSALegacyMatcherCustomResource.java @@ -0,0 +1,16 @@ +package io.javaoperatorsdk.operator.sample.ssalegacymatcher; + +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("slm") +public class SSALegacyMatcherCustomResource + extends CustomResource + implements Namespaced { + +} diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/ssalegacymatcher/SSALegacyMatcherReconciler.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/ssalegacymatcher/SSALegacyMatcherReconciler.java new file mode 100644 index 0000000000..a513133670 --- /dev/null +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/ssalegacymatcher/SSALegacyMatcherReconciler.java @@ -0,0 +1,28 @@ +package io.javaoperatorsdk.operator.sample.ssalegacymatcher; + +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 = ServiceDependentResource.class)}) +public class SSALegacyMatcherReconciler + implements Reconciler { + + private final AtomicInteger numberOfExecutions = new AtomicInteger(0); + + @Override + public UpdateControl reconcile( + SSALegacyMatcherCustomResource resource, + Context context) { + numberOfExecutions.addAndGet(1); + return UpdateControl.noUpdate(); + } + + public int getNumberOfExecutions() { + return numberOfExecutions.get(); + } +} diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/ssalegacymatcher/SSALegacyMatcherSpec.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/ssalegacymatcher/SSALegacyMatcherSpec.java new file mode 100644 index 0000000000..6ee56aafd0 --- /dev/null +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/ssalegacymatcher/SSALegacyMatcherSpec.java @@ -0,0 +1,15 @@ +package io.javaoperatorsdk.operator.sample.ssalegacymatcher; + +public class SSALegacyMatcherSpec { + + private String value; + + public String getValue() { + return value; + } + + public SSALegacyMatcherSpec setValue(String value) { + this.value = value; + return this; + } +} diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/ssalegacymatcher/ServiceDependentResource.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/ssalegacymatcher/ServiceDependentResource.java new file mode 100644 index 0000000000..5230404ceb --- /dev/null +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/ssalegacymatcher/ServiceDependentResource.java @@ -0,0 +1,61 @@ +package io.javaoperatorsdk.operator.sample.ssalegacymatcher; + +import java.util.HashMap; +import java.util.Map; +import java.util.concurrent.atomic.AtomicInteger; + +import io.fabric8.kubernetes.api.model.Service; +import io.javaoperatorsdk.operator.SSAWithLegacyMatcherIT; +import io.javaoperatorsdk.operator.api.reconciler.Context; +import io.javaoperatorsdk.operator.processing.dependent.kubernetes.CRUDKubernetesDependentResource; +import io.javaoperatorsdk.operator.processing.dependent.kubernetes.GenericKubernetesResourceMatcher; +import io.javaoperatorsdk.operator.processing.dependent.kubernetes.KubernetesDependent; + +import static io.javaoperatorsdk.operator.ReconcilerUtils.loadYaml; + +@KubernetesDependent +public class ServiceDependentResource + extends CRUDKubernetesDependentResource { + + public static AtomicInteger createUpdateCount = new AtomicInteger(0); + + public ServiceDependentResource() { + super(Service.class); + } + + @Override + protected Service desired(SSALegacyMatcherCustomResource primary, + Context context) { + + Service service = loadYaml(Service.class, SSAWithLegacyMatcherIT.class, "service.yaml"); + service.getMetadata().setName(primary.getMetadata().getName()); + service.getMetadata().setNamespace(primary.getMetadata().getNamespace()); + Map labels = new HashMap<>(); + labels.put("app", "deployment-name"); + service.getSpec().setSelector(labels); + return service; + } + + @Override + public Result match(Service actualResource, SSALegacyMatcherCustomResource primary, + Context context) { + return GenericKubernetesResourceMatcher.match(this, actualResource, primary, context, + true, false, false); + } + + // override just to check the exec count + @Override + public Service update(Service actual, Service target, SSALegacyMatcherCustomResource primary, + Context context) { + createUpdateCount.addAndGet(1); + return super.update(actual, target, primary, context); + } + + // override just to check the exec count + @Override + public Service create(Service target, SSALegacyMatcherCustomResource primary, + Context context) { + createUpdateCount.addAndGet(1); + return super.create(target, primary, context); + } +}