diff --git a/docs/documentation/dependent-resources.md b/docs/documentation/dependent-resources.md index 4f623ccdd1..db7a00fda8 100644 --- a/docs/documentation/dependent-resources.md +++ b/docs/documentation/dependent-resources.md @@ -301,20 +301,31 @@ tests [here](https://github.com/java-operator-sdk/java-operator-sdk/blob/main/op When dealing with multiple dependent resources of same type, the dependent resource implementation needs to know which specific resource should be targeted when reconciling a given dependent -resource, since there will be multiple instances of that type which could possibly be used, each -associated with the same primary resource. In order to do this, JOSDK relies on the -[resource discriminator](https://github.com/java-operator-sdk/java-operator-sdk/blob/main/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/reconciler/ResourceDiscriminator.java) -concept. Resource discriminators uniquely identify the target resource of a dependent resource. -In the managed Kubernetes dependent resources case, the discriminator can be declaratively set -using the `@KubernetesDependent` annotation: - -```java - -@KubernetesDependent(resourceDiscriminator = ConfigMap1Discriminator.class) -public class MultipleManagedDependentResourceConfigMap1 { -//... -} -``` +resource, since there could be multiple instances of that type which could possibly be used, each +associated with the same primary resource. In this situation, JOSDK automatically selects the appropriate secondary +resource matching the desired state associated with the primary resource. This makes sense because the desired +state computation already needs to be able to discriminate among multiple related secondary resources to tell JOSDK how +they should be reconciled. + +There might be casees, though, where it might be problematic to call the `desired` method several times (for example, because it is costly to do so), it is always possible to override this automated discrimination using several means: + +- Implement your own `getSecondaryResource` method on your `DependentResource` implementation from scratch. +- Override the `selectManagedSecondaryResource` method, if your `DependentResource` extends `AbstractDependentResource`. + This should be relatively simple to override this method to optimize the matching to your needs. You can see an + example of such an implementation in + the [`ExternalWithStateDependentResource`](https://github.com/operator-framework/java-operator-sdk/blob/6cd0f884a7c9b60c81bd2d52da54adbd64d6e118/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/externalstate/ExternalWithStateDependentResource.java#L43-L49) + class. +- Override the `managedSecondaryResourceID` method, if your `DependentResource` extends `KubernetesDependentResource`, + where it's very often possible to easily determine the `ResourceID` of the secondary resource. This would probably be + the easiest solution if you're working with Kubernetes resources. +- Configure + a [`ResourceDiscriminator`](https://github.com/java-operator-sdk/java-operator-sdk/blob/main/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/reconciler/ResourceDiscriminator.java) + implementation for your `DependentResource`. This was the approach that was used before JOSDK v5 but should not be + needed anymore as it is simpler and more efficient to override one the methods above instead of creating a separate + class. Discriminators can be declaratively set when using managed Kubernetes dependent resources via + the `resourceDiscriminator` field of the `@KubernetesDependent` annotation. + +### Sharing an Event Source Between Dependent Resources Dependent resources usually also provide event sources. When dealing with multiple dependents of the same type, one needs to decide whether these dependent resources should track the same @@ -330,10 +341,10 @@ would look as follows: useEventSourceWithName = "configMapSource") ``` -A sample is provided as an integration test both -for [managed](https://github.com/java-operator-sdk/java-operator-sdk/blob/main/operator-framework/src/test/java/io/javaoperatorsdk/operator/MultipleManagedDependentSameTypeIT.java) -and -for [standalone](https://github.com/java-operator-sdk/java-operator-sdk/blob/main/operator-framework/src/test/java/io/javaoperatorsdk/operator/MultipleDependentResourceIT.java) +A sample is provided as an integration test both: +for [managed](https://github.com/operator-framework/java-operator-sdk/blob/main/operator-framework/src/test/java/io/javaoperatorsdk/operator/MultipleManagedDependentNoDiscriminatorIT.java) + +For [standalone](https://github.com/operator-framework/java-operator-sdk/blob/main/operator-framework/src/test/java/io/javaoperatorsdk/operator/MultipleDependentResourceIT.java) cases. ## Bulk Dependent Resources diff --git a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/reconciler/dependent/DependentResource.java b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/reconciler/dependent/DependentResource.java index 98d700324d..eec011e5e8 100644 --- a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/reconciler/dependent/DependentResource.java +++ b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/reconciler/dependent/DependentResource.java @@ -49,6 +49,16 @@ default Optional> eventSource( return Optional.empty(); } + /** + * Retrieves the secondary resource (if it exists) associated with the specified primary resource + * for this DependentResource. + * + * @param primary the primary resource for which we want to retrieve the secondary resource + * associated with this DependentResource + * @param context the current {@link Context} in which the operation is called + * @return the secondary resource or {@link Optional#empty()} if it doesn't exist + * @throws IllegalStateException if more than one secondary is found to match the primary resource + */ default Optional getSecondaryResource(P primary, Context

context) { return Optional.empty(); } diff --git a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/AbstractDependentResource.java b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/AbstractDependentResource.java index aa2f0616ba..590affe617 100644 --- a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/AbstractDependentResource.java +++ b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/AbstractDependentResource.java @@ -1,6 +1,7 @@ package io.javaoperatorsdk.operator.processing.dependent; import java.util.Optional; +import java.util.Set; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -97,8 +98,39 @@ protected ReconcileResult reconcile(P primary, R actualResource, Context

c @Override public Optional getSecondaryResource(P primary, Context

context) { - return resourceDiscriminator == null ? context.getSecondaryResource(resourceType()) - : resourceDiscriminator.distinguish(resourceType(), primary, context); + if (resourceDiscriminator != null) { + return resourceDiscriminator.distinguish(resourceType(), primary, context); + } else { + var secondaryResources = context.getSecondaryResources(resourceType()); + if (secondaryResources.isEmpty()) { + return Optional.empty(); + } else { + return selectManagedSecondaryResource(secondaryResources, primary, context); + } + } + } + + /** + * Selects the actual secondary resource matching the desired state derived from the primary + * resource when several resources of the same type are found in the context. This method allows + * for optimized implementations in subclasses since this default implementation will check each + * secondary candidates for equality with the specified desired state, which might end up costly. + * + * @param secondaryResources to select the target resource from + * + * @return the matching secondary resource or {@link Optional#empty()} if none matches + * @throws IllegalStateException if more than one candidate is found, in which case some other + * mechanism might be necessary to distinguish between candidate secondary resources + */ + protected Optional selectManagedSecondaryResource(Set secondaryResources, P primary, + Context

context) { + R desired = desired(primary, context); + var targetResources = secondaryResources.stream().filter(r -> r.equals(desired)).toList(); + if (targetResources.size() > 1) { + throw new IllegalStateException( + "More than one secondary resource related to primary: " + targetResources); + } + return targetResources.isEmpty() ? Optional.empty() : Optional.of(targetResources.get(0)); } private void throwIfNull(R desired, P primary, String descriptor) { @@ -166,8 +198,7 @@ protected void handleDelete(P primary, R secondary, Context

context) { "handleDelete method must be implemented if Deleter trait is supported"); } - public void setResourceDiscriminator( - ResourceDiscriminator resourceDiscriminator) { + public void setResourceDiscriminator(ResourceDiscriminator resourceDiscriminator) { this.resourceDiscriminator = resourceDiscriminator; } diff --git a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/kubernetes/KubernetesDependentResource.java b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/kubernetes/KubernetesDependentResource.java index a190853b58..edc7491469 100644 --- a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/kubernetes/KubernetesDependentResource.java +++ b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/kubernetes/KubernetesDependentResource.java @@ -1,6 +1,7 @@ package io.javaoperatorsdk.operator.processing.dependent.kubernetes; import java.util.Map; +import java.util.Objects; import java.util.Optional; import java.util.Set; @@ -294,6 +295,29 @@ protected void addSecondaryToPrimaryMapperAnnotations(R desired, P primary, Stri } } + @Override + protected Optional selectManagedSecondaryResource(Set secondaryResources, P primary, + Context

context) { + ResourceID managedResourceID = managedSecondaryResourceID(primary, context); + return secondaryResources.stream() + .filter(r -> r.getMetadata().getName().equals(managedResourceID.getName()) && + Objects.equals(r.getMetadata().getNamespace(), + managedResourceID.getNamespace().orElse(null))) + .findFirst(); + } + + /** + * Override this method in order to optimize and not compute the desired when selecting the target + * secondary resource. Simply, a static ResourceID can be returned. + * + * @param primary resource + * @param context of current reconciliation + * @return id of the target managed resource + */ + protected ResourceID managedSecondaryResourceID(P primary, Context

context) { + return ResourceID.fromResource(desired(primary, context)); + } + protected boolean addOwnerReference() { return garbageCollected; } diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/ExternalStateBulkIT.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/ExternalStateBulkIT.java index 7452958b8c..a0cc9c5e8e 100644 --- a/operator-framework/src/test/java/io/javaoperatorsdk/operator/ExternalStateBulkIT.java +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/ExternalStateBulkIT.java @@ -34,7 +34,7 @@ class ExternalStateBulkIT { .build(); @Test - void reconcilesResourceWithPersistentState() throws InterruptedException { + void reconcilesResourceWithPersistentState() { var resource = operator.create(testResource()); assertResources(resource, INITIAL_TEST_DATA, INITIAL_BULK_SIZE); diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/MultipleDependentResourceIT.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/MultipleDependentResourceIT.java index d3e7f77fd5..224e9c487a 100644 --- a/operator-framework/src/test/java/io/javaoperatorsdk/operator/MultipleDependentResourceIT.java +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/MultipleDependentResourceIT.java @@ -1,7 +1,6 @@ package io.javaoperatorsdk.operator; import java.time.Duration; -import java.util.stream.IntStream; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.RegisterExtension; @@ -9,54 +8,73 @@ 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.multipledependentresource.MultipleDependentResourceConfigMap; import io.javaoperatorsdk.operator.sample.multipledependentresource.MultipleDependentResourceCustomResource; import io.javaoperatorsdk.operator.sample.multipledependentresource.MultipleDependentResourceReconciler; +import io.javaoperatorsdk.operator.sample.multipledependentresource.MultipleDependentResourceSpec; +import io.javaoperatorsdk.operator.sample.multipledrsametypenodiscriminator.*; +import static io.javaoperatorsdk.operator.sample.multipledependentresource.MultipleDependentResourceConfigMap.DATA_KEY; +import static io.javaoperatorsdk.operator.sample.multipledependentresource.MultipleDependentResourceConfigMap.getConfigMapName; +import static io.javaoperatorsdk.operator.sample.multipledependentresource.MultipleDependentResourceReconciler.FIRST_CONFIG_MAP_ID; +import static io.javaoperatorsdk.operator.sample.multipledependentresource.MultipleDependentResourceReconciler.SECOND_CONFIG_MAP_ID; import static org.assertj.core.api.Assertions.assertThat; import static org.awaitility.Awaitility.await; -class MultipleDependentResourceIT { +public class MultipleDependentResourceIT { + + public static final String CHANGED_VALUE = "changed value"; + public static final String INITIAL_VALUE = "initial value"; - public static final String TEST_RESOURCE_NAME = "multipledependentresource-testresource"; @RegisterExtension - LocallyRunOperatorExtension operator = + LocallyRunOperatorExtension extension = LocallyRunOperatorExtension.builder() - .withReconciler(MultipleDependentResourceReconciler.class) - .waitForNamespaceDeletion(true) + .withReconciler(new MultipleDependentResourceReconciler()) .build(); @Test - void twoConfigMapsHaveBeenCreated() { - MultipleDependentResourceCustomResource customResource = createTestCustomResource(); - operator.create(customResource); - - var reconciler = operator.getReconcilerOfType(MultipleDependentResourceReconciler.class); - - await().pollDelay(Duration.ofMillis(300)) - .until(() -> reconciler.getNumberOfExecutions() <= 1); - - IntStream.of(MultipleDependentResourceReconciler.FIRST_CONFIG_MAP_ID, - MultipleDependentResourceReconciler.SECOND_CONFIG_MAP_ID).forEach(configMapId -> { - ConfigMap configMap = - operator.get(ConfigMap.class, customResource.getConfigMapName(configMapId)); - assertThat(configMap).isNotNull(); - assertThat(configMap.getMetadata().getName()) - .isEqualTo(customResource.getConfigMapName(configMapId)); - assertThat(configMap.getData().get(MultipleDependentResourceConfigMap.DATA_KEY)) - .isEqualTo(String.valueOf(configMapId)); - }); - } + void handlesCRUDOperations() { + var res = extension.create(testResource()); + + await().untilAsserted(() -> { + var cm1 = extension.get(ConfigMap.class, getConfigMapName(FIRST_CONFIG_MAP_ID)); + var cm2 = extension.get(ConfigMap.class, getConfigMapName(SECOND_CONFIG_MAP_ID)); + + assertThat(cm1).isNotNull(); + assertThat(cm2).isNotNull(); + assertThat(cm1.getData()).containsEntry(DATA_KEY, INITIAL_VALUE); + assertThat(cm2.getData()).containsEntry(DATA_KEY, INITIAL_VALUE); + }); + + res.getSpec().setValue(CHANGED_VALUE); + res = extension.replace(res); + + await().untilAsserted(() -> { + var cm1 = extension.get(ConfigMap.class, getConfigMapName(FIRST_CONFIG_MAP_ID)); + var cm2 = extension.get(ConfigMap.class, getConfigMapName(SECOND_CONFIG_MAP_ID)); - public MultipleDependentResourceCustomResource createTestCustomResource() { - MultipleDependentResourceCustomResource resource = - new MultipleDependentResourceCustomResource(); - resource.setMetadata( - new ObjectMetaBuilder() - .withName(TEST_RESOURCE_NAME) - .withNamespace(operator.getNamespace()) - .build()); - return resource; + assertThat(cm1.getData()).containsEntry(DATA_KEY, CHANGED_VALUE); + assertThat(cm2.getData()).containsEntry(DATA_KEY, CHANGED_VALUE); + }); + + extension.delete(res); + + await().timeout(Duration.ofSeconds(120)).untilAsserted(() -> { + var cm1 = extension.get(ConfigMap.class, getConfigMapName(FIRST_CONFIG_MAP_ID)); + var cm2 = extension.get(ConfigMap.class, getConfigMapName(SECOND_CONFIG_MAP_ID)); + + assertThat(cm1).isNull(); + assertThat(cm2).isNull(); + }); } + MultipleDependentResourceCustomResource testResource() { + var res = new MultipleDependentResourceCustomResource(); + res.setMetadata(new ObjectMetaBuilder() + .withName("test1") + .build()); + res.setSpec(new MultipleDependentResourceSpec()); + res.getSpec().setValue(INITIAL_VALUE); + + return res; + } } diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/MultipleDependentResourceWithNoDiscriminatorIT.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/MultipleDependentResourceWithNoDiscriminatorIT.java new file mode 100644 index 0000000000..908cb58f79 --- /dev/null +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/MultipleDependentResourceWithNoDiscriminatorIT.java @@ -0,0 +1,65 @@ +package io.javaoperatorsdk.operator; + +import java.time.Duration; +import java.util.stream.IntStream; + +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.multipledependentresourcewithdiscriminator.MultipleDependentResourceConfigMap; +import io.javaoperatorsdk.operator.sample.multipledependentresourcewithdiscriminator.MultipleDependentResourceCustomResourceWithDiscriminator; +import io.javaoperatorsdk.operator.sample.multipledependentresourcewithdiscriminator.MultipleDependentResourceWithDiscriminatorReconciler; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.awaitility.Awaitility.await; + +class MultipleDependentResourceWithNoDiscriminatorIT { + + public static final String TEST_RESOURCE_NAME = "multipledependentresource-testresource"; + @RegisterExtension + LocallyRunOperatorExtension operator = + LocallyRunOperatorExtension.builder() + .withReconciler(MultipleDependentResourceWithDiscriminatorReconciler.class) + .waitForNamespaceDeletion(true) + .build(); + + @Test + void twoConfigMapsHaveBeenCreated() { + MultipleDependentResourceCustomResourceWithDiscriminator customResource = + createTestCustomResource(); + operator.create(customResource); + + var reconciler = + operator.getReconcilerOfType(MultipleDependentResourceWithDiscriminatorReconciler.class); + + await().pollDelay(Duration.ofMillis(300)) + .until(() -> reconciler.getNumberOfExecutions() <= 1); + + IntStream.of(MultipleDependentResourceWithDiscriminatorReconciler.FIRST_CONFIG_MAP_ID, + MultipleDependentResourceWithDiscriminatorReconciler.SECOND_CONFIG_MAP_ID) + .forEach(configMapId -> { + ConfigMap configMap = + operator.get(ConfigMap.class, customResource.getConfigMapName(configMapId)); + assertThat(configMap).isNotNull(); + assertThat(configMap.getMetadata().getName()) + .isEqualTo(customResource.getConfigMapName(configMapId)); + assertThat(configMap.getData().get(MultipleDependentResourceConfigMap.DATA_KEY)) + .isEqualTo(String.valueOf(configMapId)); + }); + } + + public MultipleDependentResourceCustomResourceWithDiscriminator createTestCustomResource() { + MultipleDependentResourceCustomResourceWithDiscriminator resource = + new MultipleDependentResourceCustomResourceWithDiscriminator(); + resource.setMetadata( + new ObjectMetaBuilder() + .withName(TEST_RESOURCE_NAME) + .withNamespace(operator.getNamespace()) + .build()); + return resource; + } + +} diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/MultipleManagedDependentNoDiscriminatorIT.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/MultipleManagedDependentNoDiscriminatorIT.java new file mode 100644 index 0000000000..361aa099af --- /dev/null +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/MultipleManagedDependentNoDiscriminatorIT.java @@ -0,0 +1,81 @@ +package io.javaoperatorsdk.operator; + +import java.time.Duration; + +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.multipledrsametypenodiscriminator.*; + +import static io.javaoperatorsdk.operator.sample.multipledrsametypenodiscriminator.MultipleManagedDependentSameTypeNoDiscriminatorReconciler.DATA_KEY; +import static org.assertj.core.api.Assertions.assertThat; +import static org.awaitility.Awaitility.await; + +public class MultipleManagedDependentNoDiscriminatorIT { + + public static final String RESOURCE_NAME = "test1"; + public static final String INITIAL_VALUE = "initial_value"; + public static final String CHANGED_VALUE = "changed_value"; + + @RegisterExtension + LocallyRunOperatorExtension extension = + LocallyRunOperatorExtension.builder() + .withReconciler(new MultipleManagedDependentSameTypeNoDiscriminatorReconciler()) + .build(); + + @Test + void handlesCRUDOperations() { + var res = extension.create(testResource()); + + await().untilAsserted(() -> { + var cm1 = extension.get(ConfigMap.class, + RESOURCE_NAME + MultipleManagedDependentNoDiscriminatorConfigMap1.NAME_SUFFIX); + var cm2 = extension.get(ConfigMap.class, + RESOURCE_NAME + MultipleManagedDependentNoDiscriminatorConfigMap2.NAME_SUFFIX); + + assertThat(cm1).isNotNull(); + assertThat(cm2).isNotNull(); + assertThat(cm1.getData()).containsEntry(DATA_KEY, INITIAL_VALUE); + assertThat(cm2.getData()).containsEntry(DATA_KEY, INITIAL_VALUE); + }); + + res.getSpec().setValue(CHANGED_VALUE); + res = extension.replace(res); + + await().untilAsserted(() -> { + var cm1 = extension.get(ConfigMap.class, + RESOURCE_NAME + MultipleManagedDependentNoDiscriminatorConfigMap1.NAME_SUFFIX); + var cm2 = extension.get(ConfigMap.class, + RESOURCE_NAME + MultipleManagedDependentNoDiscriminatorConfigMap2.NAME_SUFFIX); + + assertThat(cm1.getData()).containsEntry(DATA_KEY, CHANGED_VALUE); + assertThat(cm2.getData()).containsEntry(DATA_KEY, CHANGED_VALUE); + }); + + extension.delete(res); + + await().timeout(Duration.ofSeconds(60)).untilAsserted(() -> { + var cm1 = extension.get(ConfigMap.class, + RESOURCE_NAME + MultipleManagedDependentNoDiscriminatorConfigMap1.NAME_SUFFIX); + var cm2 = extension.get(ConfigMap.class, + RESOURCE_NAME + MultipleManagedDependentNoDiscriminatorConfigMap2.NAME_SUFFIX); + + assertThat(cm1).isNull(); + assertThat(cm2).isNull(); + }); + } + + MultipleManagedDependentNoDiscriminatorCustomResource testResource() { + var res = new MultipleManagedDependentNoDiscriminatorCustomResource(); + res.setMetadata(new ObjectMetaBuilder() + .withName(RESOURCE_NAME) + .build()); + res.setSpec(new MultipleManagedDependentNoDiscriminatorSpec()); + res.getSpec().setValue(INITIAL_VALUE); + return res; + } + +} diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/PrimaryIndexerIT.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/PrimaryIndexerIT.java index 16f223ce38..fb202de390 100644 --- a/operator-framework/src/test/java/io/javaoperatorsdk/operator/PrimaryIndexerIT.java +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/PrimaryIndexerIT.java @@ -13,12 +13,13 @@ import io.javaoperatorsdk.operator.sample.primaryindexer.PrimaryIndexerTestCustomResourceSpec; import io.javaoperatorsdk.operator.sample.primaryindexer.PrimaryIndexerTestReconciler; +import static io.javaoperatorsdk.operator.sample.primaryindexer.AbstractPrimaryIndexerTestReconciler.CONFIG_MAP_NAME; import static org.assertj.core.api.Assertions.assertThat; import static org.awaitility.Awaitility.await; class PrimaryIndexerIT { - public static final String CONFIG_MAP_NAME = "common-config-map"; + public static final String RESOURCE_NAME1 = "test1"; public static final String RESOURCE_NAME2 = "test2"; diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/PrimaryToSecondaryDependentIT.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/PrimaryToSecondaryDependentIT.java index eaa7e4410f..0e48b726e9 100644 --- a/operator-framework/src/test/java/io/javaoperatorsdk/operator/PrimaryToSecondaryDependentIT.java +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/PrimaryToSecondaryDependentIT.java @@ -14,6 +14,7 @@ import io.javaoperatorsdk.operator.sample.primarytosecondaydependent.PrimaryToSecondaryDependentReconciler; import io.javaoperatorsdk.operator.sample.primarytosecondaydependent.PrimaryToSecondaryDependentSpec; +import static io.javaoperatorsdk.operator.sample.primarytosecondaydependent.ConfigMapDependent.TEST_CONFIG_MAP_NAME; import static io.javaoperatorsdk.operator.sample.primarytosecondaydependent.ConfigMapReconcilePrecondition.DO_NOT_RECONCILE; import static io.javaoperatorsdk.operator.sample.primarytosecondaydependent.PrimaryToSecondaryDependentReconciler.DATA_KEY; import static org.assertj.core.api.Assertions.assertThat; @@ -21,7 +22,7 @@ class PrimaryToSecondaryDependentIT { - public static final String TEST_CONFIG_MAP_NAME = "testconfigmap"; + public static final String TEST_CR_NAME = "test1"; public static final String TEST_DATA = "testData"; public diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/externalstate/ExternalStateReconciler.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/externalstate/ExternalStateReconciler.java index 148309cad8..a9c3bb5a28 100644 --- a/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/externalstate/ExternalStateReconciler.java +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/externalstate/ExternalStateReconciler.java @@ -20,7 +20,7 @@ import io.javaoperatorsdk.operator.support.ExternalResource; import io.javaoperatorsdk.operator.support.TestExecutionInfoProvider; -@ControllerConfiguration() +@ControllerConfiguration public class ExternalStateReconciler implements Reconciler, Cleaner, TestExecutionInfoProvider { diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/externalstate/ExternalWithStateDependentResource.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/externalstate/ExternalWithStateDependentResource.java index c25113b406..0965c49636 100644 --- a/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/externalstate/ExternalWithStateDependentResource.java +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/externalstate/ExternalWithStateDependentResource.java @@ -34,16 +34,26 @@ public ExternalWithStateDependentResource() { @SuppressWarnings("unchecked") public Set fetchResources( ExternalStateCustomResource primaryResource) { - Optional configMapOptional = - getExternalStateEventSource().getSecondaryResource(primaryResource); - - return configMapOptional.map(configMap -> { - var id = configMap.getData().get(ID_KEY); + return getResourceID(primaryResource).map(id -> { var externalResource = externalService.read(id); return externalResource.map(Set::of).orElseGet(Collections::emptySet); }).orElseGet(Collections::emptySet); } + @Override + protected Optional selectManagedSecondaryResource( + Set secondaryResources, + ExternalStateCustomResource primary, Context context) { + var id = getResourceID(primary); + return id.flatMap(k -> secondaryResources.stream().filter(e -> e.getId().equals(k)).findAny()); + } + + private Optional getResourceID(ExternalStateCustomResource primaryResource) { + Optional configMapOptional = + getExternalStateEventSource().getSecondaryResource(primaryResource); + return configMapOptional.map(cm -> cm.getData().get(ID_KEY)); + } + @Override protected ExternalResource desired(ExternalStateCustomResource primary, Context context) { diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/multipledependentresource/MultipleDependentResourceConfigMap.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/multipledependentresource/MultipleDependentResourceConfigMap.java index 5c2e9974b5..39ed7f9f0b 100644 --- a/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/multipledependentresource/MultipleDependentResourceConfigMap.java +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/multipledependentresource/MultipleDependentResourceConfigMap.java @@ -1,6 +1,5 @@ package io.javaoperatorsdk.operator.sample.multipledependentresource; -import java.util.HashMap; import java.util.Map; import io.fabric8.kubernetes.api.model.ConfigMap; @@ -12,9 +11,9 @@ public class MultipleDependentResourceConfigMap extends CRUDKubernetesDependentResource { public static final String DATA_KEY = "key"; - private final int value; + private final String value; - public MultipleDependentResourceConfigMap(int value) { + public MultipleDependentResourceConfigMap(String value) { super(ConfigMap.class); this.value = value; } @@ -22,15 +21,17 @@ public MultipleDependentResourceConfigMap(int value) { @Override protected ConfigMap desired(MultipleDependentResourceCustomResource primary, Context context) { - Map data = new HashMap<>(); - data.put(DATA_KEY, String.valueOf(value)); return new ConfigMapBuilder() .withNewMetadata() - .withName(primary.getConfigMapName(value)) + .withName(getConfigMapName(value)) .withNamespace(primary.getMetadata().getNamespace()) .endMetadata() - .withData(data) + .withData(Map.of(DATA_KEY, primary.getSpec().getValue())) .build(); } + + public static String getConfigMapName(String id) { + return "configmap" + id; + } } diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/multipledependentresource/MultipleDependentResourceCustomResource.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/multipledependentresource/MultipleDependentResourceCustomResource.java index 55f0d60f2d..60ba494e8a 100644 --- a/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/multipledependentresource/MultipleDependentResourceCustomResource.java +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/multipledependentresource/MultipleDependentResourceCustomResource.java @@ -3,19 +3,13 @@ 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.Kind; import io.fabric8.kubernetes.model.annotation.ShortNames; import io.fabric8.kubernetes.model.annotation.Version; @Group("sample.javaoperatorsdk") @Version("v1") -@Kind("MultipleDependentResourceCustomResource") @ShortNames("mdr") public class MultipleDependentResourceCustomResource - extends CustomResource + extends CustomResource implements Namespaced { - - public String getConfigMapName(int id) { - return "configmap" + id; - } } diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/multipledependentresource/MultipleDependentResourceReconciler.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/multipledependentresource/MultipleDependentResourceReconciler.java index e75764bc77..4befd18d87 100644 --- a/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/multipledependentresource/MultipleDependentResourceReconciler.java +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/multipledependentresource/MultipleDependentResourceReconciler.java @@ -1,60 +1,37 @@ package io.javaoperatorsdk.operator.sample.multipledependentresource; import java.util.Map; -import java.util.concurrent.atomic.AtomicInteger; import io.fabric8.kubernetes.api.model.ConfigMap; import io.javaoperatorsdk.operator.api.config.informer.InformerConfiguration; import io.javaoperatorsdk.operator.api.reconciler.*; -import io.javaoperatorsdk.operator.processing.event.ResourceID; import io.javaoperatorsdk.operator.processing.event.source.EventSource; import io.javaoperatorsdk.operator.processing.event.source.informer.InformerEventSource; -import io.javaoperatorsdk.operator.support.TestExecutionInfoProvider; @ControllerConfiguration public class MultipleDependentResourceReconciler - implements Reconciler, - TestExecutionInfoProvider { + implements Reconciler { - public static final int FIRST_CONFIG_MAP_ID = 1; - public static final int SECOND_CONFIG_MAP_ID = 2; - private final AtomicInteger numberOfExecutions = new AtomicInteger(0); + public static final String FIRST_CONFIG_MAP_ID = "1"; + public static final String SECOND_CONFIG_MAP_ID = "2"; private final MultipleDependentResourceConfigMap firstDependentResourceConfigMap; private final MultipleDependentResourceConfigMap secondDependentResourceConfigMap; public MultipleDependentResourceReconciler() { firstDependentResourceConfigMap = new MultipleDependentResourceConfigMap(FIRST_CONFIG_MAP_ID); - secondDependentResourceConfigMap = new MultipleDependentResourceConfigMap(SECOND_CONFIG_MAP_ID); - - firstDependentResourceConfigMap - .setResourceDiscriminator( - new ResourceIDMatcherDiscriminator<>( - p -> new ResourceID(p.getConfigMapName(FIRST_CONFIG_MAP_ID), - p.getMetadata().getNamespace()))); - secondDependentResourceConfigMap - .setResourceDiscriminator( - new ResourceIDMatcherDiscriminator<>( - p -> new ResourceID(p.getConfigMapName(SECOND_CONFIG_MAP_ID), - p.getMetadata().getNamespace()))); } @Override public UpdateControl reconcile( MultipleDependentResourceCustomResource resource, Context context) { - numberOfExecutions.getAndIncrement(); firstDependentResourceConfigMap.reconcile(resource, context); secondDependentResourceConfigMap.reconcile(resource, context); return UpdateControl.noUpdate(); } - - public int getNumberOfExecutions() { - return numberOfExecutions.get(); - } - @Override public Map prepareEventSources( EventSourceContext context) { diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/multipledependentresource/MultipleDependentResourceSpec.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/multipledependentresource/MultipleDependentResourceSpec.java new file mode 100644 index 0000000000..def3fa9088 --- /dev/null +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/multipledependentresource/MultipleDependentResourceSpec.java @@ -0,0 +1,14 @@ +package io.javaoperatorsdk.operator.sample.multipledependentresource; + +public class MultipleDependentResourceSpec { + + private String value; + + public String getValue() { + return value; + } + + public void setValue(String value) { + this.value = value; + } +} diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/multipledependentresourcewithdiscriminator/MultipleDependentResourceConfigMap.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/multipledependentresourcewithdiscriminator/MultipleDependentResourceConfigMap.java new file mode 100644 index 0000000000..661e8e54be --- /dev/null +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/multipledependentresourcewithdiscriminator/MultipleDependentResourceConfigMap.java @@ -0,0 +1,37 @@ +package io.javaoperatorsdk.operator.sample.multipledependentresourcewithdiscriminator; + +import java.util.HashMap; +import java.util.Map; + +import io.fabric8.kubernetes.api.model.ConfigMap; +import io.fabric8.kubernetes.api.model.ConfigMapBuilder; +import io.javaoperatorsdk.operator.api.reconciler.Context; +import io.javaoperatorsdk.operator.processing.dependent.kubernetes.CRUDKubernetesDependentResource; + +public class MultipleDependentResourceConfigMap + extends + CRUDKubernetesDependentResource { + + public static final String DATA_KEY = "key"; + private final int value; + + public MultipleDependentResourceConfigMap(int value) { + super(ConfigMap.class); + this.value = value; + } + + @Override + protected ConfigMap desired(MultipleDependentResourceCustomResourceWithDiscriminator primary, + Context context) { + Map data = new HashMap<>(); + data.put(DATA_KEY, String.valueOf(value)); + + return new ConfigMapBuilder() + .withNewMetadata() + .withName(primary.getConfigMapName(value)) + .withNamespace(primary.getMetadata().getNamespace()) + .endMetadata() + .withData(data) + .build(); + } +} diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/multipledependentresourcewithdiscriminator/MultipleDependentResourceCustomResourceWithDiscriminator.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/multipledependentresourcewithdiscriminator/MultipleDependentResourceCustomResourceWithDiscriminator.java new file mode 100644 index 0000000000..7491d5e3ae --- /dev/null +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/multipledependentresourcewithdiscriminator/MultipleDependentResourceCustomResourceWithDiscriminator.java @@ -0,0 +1,19 @@ +package io.javaoperatorsdk.operator.sample.multipledependentresourcewithdiscriminator; + +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("mdwd") +public class MultipleDependentResourceCustomResourceWithDiscriminator + extends CustomResource + implements Namespaced { + + public String getConfigMapName(int id) { + return "configmap" + id; + } +} diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/multipledependentresourcewithdiscriminator/MultipleDependentResourceWithDiscriminatorReconciler.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/multipledependentresourcewithdiscriminator/MultipleDependentResourceWithDiscriminatorReconciler.java new file mode 100644 index 0000000000..aae9d23cc7 --- /dev/null +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/multipledependentresourcewithdiscriminator/MultipleDependentResourceWithDiscriminatorReconciler.java @@ -0,0 +1,68 @@ +package io.javaoperatorsdk.operator.sample.multipledependentresourcewithdiscriminator; + +import java.util.Map; +import java.util.concurrent.atomic.AtomicInteger; + +import io.fabric8.kubernetes.api.model.ConfigMap; +import io.javaoperatorsdk.operator.api.config.informer.InformerConfiguration; +import io.javaoperatorsdk.operator.api.reconciler.*; +import io.javaoperatorsdk.operator.processing.event.ResourceID; +import io.javaoperatorsdk.operator.processing.event.source.EventSource; +import io.javaoperatorsdk.operator.processing.event.source.informer.InformerEventSource; +import io.javaoperatorsdk.operator.support.TestExecutionInfoProvider; + +@ControllerConfiguration +public class MultipleDependentResourceWithDiscriminatorReconciler + implements Reconciler, + TestExecutionInfoProvider { + + public static final int FIRST_CONFIG_MAP_ID = 1; + public static final int SECOND_CONFIG_MAP_ID = 2; + private final AtomicInteger numberOfExecutions = new AtomicInteger(0); + + private final MultipleDependentResourceConfigMap firstDependentResourceConfigMap; + private final MultipleDependentResourceConfigMap secondDependentResourceConfigMap; + + public MultipleDependentResourceWithDiscriminatorReconciler() { + firstDependentResourceConfigMap = new MultipleDependentResourceConfigMap(FIRST_CONFIG_MAP_ID); + secondDependentResourceConfigMap = new MultipleDependentResourceConfigMap(SECOND_CONFIG_MAP_ID); + + firstDependentResourceConfigMap + .setResourceDiscriminator( + new ResourceIDMatcherDiscriminator<>( + p -> new ResourceID(p.getConfigMapName(FIRST_CONFIG_MAP_ID), + p.getMetadata().getNamespace()))); + secondDependentResourceConfigMap + .setResourceDiscriminator( + new ResourceIDMatcherDiscriminator<>( + p -> new ResourceID(p.getConfigMapName(SECOND_CONFIG_MAP_ID), + p.getMetadata().getNamespace()))); + } + + @Override + public UpdateControl reconcile( + MultipleDependentResourceCustomResourceWithDiscriminator resource, + Context context) { + numberOfExecutions.getAndIncrement(); + firstDependentResourceConfigMap.reconcile(resource, context); + secondDependentResourceConfigMap.reconcile(resource, context); + return UpdateControl.noUpdate(); + } + + + public int getNumberOfExecutions() { + return numberOfExecutions.get(); + } + + @Override + public Map prepareEventSources( + EventSourceContext context) { + InformerEventSource eventSource = + new InformerEventSource<>(InformerConfiguration.from(ConfigMap.class, context) + .build(), context); + firstDependentResourceConfigMap.configureWith(eventSource); + secondDependentResourceConfigMap.configureWith(eventSource); + + return EventSourceUtils.nameEventSources(eventSource); + } +} diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/multipledependentresourcewithdiscriminator/MultipleDependentResourceWithDiscriminatorStatus.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/multipledependentresourcewithdiscriminator/MultipleDependentResourceWithDiscriminatorStatus.java new file mode 100644 index 0000000000..84543b8a12 --- /dev/null +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/multipledependentresourcewithdiscriminator/MultipleDependentResourceWithDiscriminatorStatus.java @@ -0,0 +1,5 @@ +package io.javaoperatorsdk.operator.sample.multipledependentresourcewithdiscriminator; + +public class MultipleDependentResourceWithDiscriminatorStatus { + +} diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/multipledrsametypenodiscriminator/MultipleManagedDependentNoDiscriminatorConfigMap1.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/multipledrsametypenodiscriminator/MultipleManagedDependentNoDiscriminatorConfigMap1.java new file mode 100644 index 0000000000..6d0bc303df --- /dev/null +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/multipledrsametypenodiscriminator/MultipleManagedDependentNoDiscriminatorConfigMap1.java @@ -0,0 +1,38 @@ +package io.javaoperatorsdk.operator.sample.multipledrsametypenodiscriminator; + +import java.util.HashMap; +import java.util.Map; + +import io.fabric8.kubernetes.api.model.ConfigMap; +import io.fabric8.kubernetes.api.model.ConfigMapBuilder; +import io.javaoperatorsdk.operator.api.reconciler.Context; +import io.javaoperatorsdk.operator.processing.dependent.kubernetes.CRUDKubernetesDependentResource; +import io.javaoperatorsdk.operator.processing.dependent.kubernetes.KubernetesDependent; + +@KubernetesDependent +public class MultipleManagedDependentNoDiscriminatorConfigMap1 + extends + CRUDKubernetesDependentResource { + + public static final String NAME_SUFFIX = "-1"; + + public MultipleManagedDependentNoDiscriminatorConfigMap1() { + super(ConfigMap.class); + } + + @Override + protected ConfigMap desired(MultipleManagedDependentNoDiscriminatorCustomResource primary, + Context context) { + Map data = new HashMap<>(); + data.put(MultipleManagedDependentSameTypeNoDiscriminatorReconciler.DATA_KEY, + primary.getSpec().getValue()); + + return new ConfigMapBuilder() + .withNewMetadata() + .withName(primary.getMetadata().getName() + NAME_SUFFIX) + .withNamespace(primary.getMetadata().getNamespace()) + .endMetadata() + .withData(data) + .build(); + } +} diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/multipledrsametypenodiscriminator/MultipleManagedDependentNoDiscriminatorConfigMap2.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/multipledrsametypenodiscriminator/MultipleManagedDependentNoDiscriminatorConfigMap2.java new file mode 100644 index 0000000000..937291d83d --- /dev/null +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/multipledrsametypenodiscriminator/MultipleManagedDependentNoDiscriminatorConfigMap2.java @@ -0,0 +1,39 @@ +package io.javaoperatorsdk.operator.sample.multipledrsametypenodiscriminator; + +import java.util.HashMap; +import java.util.Map; + +import io.fabric8.kubernetes.api.model.ConfigMap; +import io.fabric8.kubernetes.api.model.ConfigMapBuilder; +import io.javaoperatorsdk.operator.api.reconciler.Context; +import io.javaoperatorsdk.operator.processing.dependent.kubernetes.CRUDKubernetesDependentResource; +import io.javaoperatorsdk.operator.processing.dependent.kubernetes.KubernetesDependent; + +import static io.javaoperatorsdk.operator.sample.multiplemanageddependentsametype.MultipleManagedDependentResourceReconciler.DATA_KEY; + +@KubernetesDependent +public class MultipleManagedDependentNoDiscriminatorConfigMap2 + extends + CRUDKubernetesDependentResource { + + public static final String NAME_SUFFIX = "-2"; + + public MultipleManagedDependentNoDiscriminatorConfigMap2() { + super(ConfigMap.class); + } + + @Override + protected ConfigMap desired(MultipleManagedDependentNoDiscriminatorCustomResource primary, + Context context) { + Map data = new HashMap<>(); + data.put(DATA_KEY, primary.getSpec().getValue()); + + return new ConfigMapBuilder() + .withNewMetadata() + .withName(primary.getMetadata().getName() + NAME_SUFFIX) + .withNamespace(primary.getMetadata().getNamespace()) + .endMetadata() + .withData(data) + .build(); + } +} diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/multipledrsametypenodiscriminator/MultipleManagedDependentNoDiscriminatorCustomResource.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/multipledrsametypenodiscriminator/MultipleManagedDependentNoDiscriminatorCustomResource.java new file mode 100644 index 0000000000..611d96f74e --- /dev/null +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/multipledrsametypenodiscriminator/MultipleManagedDependentNoDiscriminatorCustomResource.java @@ -0,0 +1,16 @@ +package io.javaoperatorsdk.operator.sample.multipledrsametypenodiscriminator; + +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("mnd") +public class MultipleManagedDependentNoDiscriminatorCustomResource + extends CustomResource + implements Namespaced { + +} diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/multipledrsametypenodiscriminator/MultipleManagedDependentNoDiscriminatorSpec.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/multipledrsametypenodiscriminator/MultipleManagedDependentNoDiscriminatorSpec.java new file mode 100644 index 0000000000..4ccc1d84f4 --- /dev/null +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/multipledrsametypenodiscriminator/MultipleManagedDependentNoDiscriminatorSpec.java @@ -0,0 +1,15 @@ +package io.javaoperatorsdk.operator.sample.multipledrsametypenodiscriminator; + +public class MultipleManagedDependentNoDiscriminatorSpec { + + private String value; + + public String getValue() { + return value; + } + + public MultipleManagedDependentNoDiscriminatorSpec setValue(String value) { + this.value = value; + return this; + } +} diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/multipledrsametypenodiscriminator/MultipleManagedDependentSameTypeNoDiscriminatorReconciler.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/multipledrsametypenodiscriminator/MultipleManagedDependentSameTypeNoDiscriminatorReconciler.java new file mode 100644 index 0000000000..0e446454da --- /dev/null +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/multipledrsametypenodiscriminator/MultipleManagedDependentSameTypeNoDiscriminatorReconciler.java @@ -0,0 +1,57 @@ +package io.javaoperatorsdk.operator.sample.multipledrsametypenodiscriminator; + +import java.util.Map; +import java.util.concurrent.atomic.AtomicInteger; + +import io.fabric8.kubernetes.api.model.ConfigMap; +import io.javaoperatorsdk.operator.api.config.informer.InformerConfiguration; +import io.javaoperatorsdk.operator.api.reconciler.*; +import io.javaoperatorsdk.operator.api.reconciler.dependent.Dependent; +import io.javaoperatorsdk.operator.processing.event.source.EventSource; +import io.javaoperatorsdk.operator.processing.event.source.informer.InformerEventSource; +import io.javaoperatorsdk.operator.support.TestExecutionInfoProvider; + +import static io.javaoperatorsdk.operator.sample.multiplemanageddependentsametype.MultipleManagedDependentResourceReconciler.CONFIG_MAP_EVENT_SOURCE; + +@Workflow(dependents = { + @Dependent(type = MultipleManagedDependentNoDiscriminatorConfigMap1.class, + useEventSourceWithName = CONFIG_MAP_EVENT_SOURCE), + @Dependent(type = MultipleManagedDependentNoDiscriminatorConfigMap2.class, + useEventSourceWithName = CONFIG_MAP_EVENT_SOURCE) +}) +@ControllerConfiguration +public class MultipleManagedDependentSameTypeNoDiscriminatorReconciler + implements Reconciler, + TestExecutionInfoProvider { + + public static final String CONFIG_MAP_EVENT_SOURCE = "ConfigMapEventSource"; + public static final String DATA_KEY = "key"; + + private final AtomicInteger numberOfExecutions = new AtomicInteger(0); + + public MultipleManagedDependentSameTypeNoDiscriminatorReconciler() {} + + @Override + public UpdateControl reconcile( + MultipleManagedDependentNoDiscriminatorCustomResource resource, + Context context) { + numberOfExecutions.getAndIncrement(); + + return UpdateControl.noUpdate(); + } + + + public int getNumberOfExecutions() { + return numberOfExecutions.get(); + } + + @Override + public Map prepareEventSources( + EventSourceContext context) { + InformerEventSource ies = + new InformerEventSource<>(InformerConfiguration.from(ConfigMap.class, context) + .build(), context); + + return Map.of(CONFIG_MAP_EVENT_SOURCE, ies); + } +} diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/primaryindexer/AbstractPrimaryIndexerTestReconciler.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/primaryindexer/AbstractPrimaryIndexerTestReconciler.java index 3b8fc43bfb..0fcdc8e39d 100644 --- a/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/primaryindexer/AbstractPrimaryIndexerTestReconciler.java +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/primaryindexer/AbstractPrimaryIndexerTestReconciler.java @@ -15,6 +15,8 @@ public class AbstractPrimaryIndexerTestReconciler implements Reconciler { + public static final String CONFIG_MAP_NAME = "common-config-map"; + private final Map numberOfExecutions = new ConcurrentHashMap<>(); protected static final String CONFIG_MAP_RELATION_INDEXER = "cm-indexer"; diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/primaryindexer/DependentPrimaryIndexerTestReconciler.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/primaryindexer/DependentPrimaryIndexerTestReconciler.java index 712635659c..513cc2cb5d 100644 --- a/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/primaryindexer/DependentPrimaryIndexerTestReconciler.java +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/primaryindexer/DependentPrimaryIndexerTestReconciler.java @@ -5,6 +5,9 @@ import java.util.stream.Collectors; 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.api.reconciler.ControllerConfiguration; import io.javaoperatorsdk.operator.api.reconciler.EventSourceContext; import io.javaoperatorsdk.operator.api.reconciler.Reconciler; @@ -32,6 +35,17 @@ public ReadOnlyConfigMapDependent() { super(ConfigMap.class); } + @Override + protected ConfigMap desired(PrimaryIndexerTestCustomResource primary, + Context context) { + return new ConfigMapBuilder() + .withMetadata(new ObjectMetaBuilder() + .withName(CONFIG_MAP_NAME) + .withNamespace(primary.getMetadata().getNamespace()) + .build()) + .build(); + } + @Override public Set toPrimaryResourceIDs(ConfigMap resource) { return cache.byIndex(CONFIG_MAP_RELATION_INDEXER, resource.getMetadata().getName()) diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/primarytosecondaydependent/ConfigMapDependent.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/primarytosecondaydependent/ConfigMapDependent.java index d08bc2131f..364e9088f7 100644 --- a/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/primarytosecondaydependent/ConfigMapDependent.java +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/primarytosecondaydependent/ConfigMapDependent.java @@ -1,12 +1,28 @@ package io.javaoperatorsdk.operator.sample.primarytosecondaydependent; 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.KubernetesDependentResource; public class ConfigMapDependent extends KubernetesDependentResource { + public static final String TEST_CONFIG_MAP_NAME = "testconfigmap"; + public ConfigMapDependent() { super(ConfigMap.class); } + + @Override + protected ConfigMap desired(PrimaryToSecondaryDependentCustomResource primary, + Context context) { + return new ConfigMapBuilder() + .withMetadata(new ObjectMetaBuilder() + .withName(TEST_CONFIG_MAP_NAME) + .withNamespace(primary.getMetadata().getNamespace()) + .build()) + .build(); + } } 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 index a103e53162..d373fe7d26 100644 --- 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 @@ -21,7 +21,7 @@ public UnmodifiablePartConfigMapDependent() { @Override protected ConfigMap desired(UnmodifiableDependentPartCustomResource primary, Context context) { - var actual = getSecondaryResource(primary, context); + var actual = context.getSecondaryResource(ConfigMap.class); ConfigMap res = new ConfigMapBuilder() .withMetadata(new ObjectMetaBuilder() .withName(primary.getMetadata().getName())