From 6e42e5330249c7618705fb22df267ba924ad5c7f Mon Sep 17 00:00:00 2001 From: csviri Date: Wed, 27 Jul 2022 13:26:40 +0200 Subject: [PATCH 01/67] feat: improvements on caching and dependent resources --- .../operator/api/reconciler/Context.java | 1 + .../api/reconciler/dependent/DependentResource.java | 9 ++++++++- .../dependent/AbstractDependentResource.java | 9 ++++++++- .../external/AbstractCachingDependentResource.java | 6 ------ .../external/AbstractSimpleDependentResource.java | 2 +- .../kubernetes/KubernetesDependentResource.java | 8 ++------ .../config/ControllerConfigurationOverriderTest.java | 5 ----- .../dependent/AbstractDependentResourceTest.java | 4 ++-- .../dependent/EmptyTestDependentResource.java | 7 ------- .../external/AbstractSimpleDependentResourceTest.java | 4 ++-- .../workflow/AbstractWorkflowExecutorTest.java | 11 ----------- .../StandaloneDependentTestReconciler.java | 2 +- .../sample/WebPageDependentsWorkflowReconciler.java | 2 +- .../sample/WebPageStandaloneDependentsReconciler.java | 2 +- 14 files changed, 27 insertions(+), 45 deletions(-) diff --git a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/reconciler/Context.java b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/reconciler/Context.java index 845810c8a1..fbe83181af 100644 --- a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/reconciler/Context.java +++ b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/reconciler/Context.java @@ -17,6 +17,7 @@ default Optional getSecondaryResource(Class expectedType) { Set getSecondaryResources(Class expectedType); + @Deprecated(forRemoval = true) Optional getSecondaryResource(Class expectedType, String eventSourceName); ControllerConfiguration

getControllerConfiguration(); 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 0923d19473..1c73cac176 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 @@ -10,7 +10,7 @@ * @param the dependent resource type * @param

the associated primary resource type */ -public interface DependentResource extends ResourceOwner { +public interface DependentResource { /** * Reconciles the dependent resource given the desired primary state @@ -21,6 +21,13 @@ public interface DependentResource extends ResourceOwn */ ReconcileResult reconcile(P primary, Context

context); + /** + * Retrieves the resource type associated with this ResourceOwner + * + * @return the resource type associated with this ResourceOwner + */ + Class resourceType(); + /** * Computes a default name for the specified DependentResource class * 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 5dbdba9358..730add6f01 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,5 +1,7 @@ package io.javaoperatorsdk.operator.processing.dependent; +import java.util.Optional; + import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -27,9 +29,10 @@ public AbstractDependentResource() { updater = updatable ? (Updater) this : null; } + @SuppressWarnings("unchecked") @Override public ReconcileResult reconcile(P primary, Context

context) { - var maybeActual = getSecondaryResource(primary); + Optional maybeActual = getSecondaryResource(primary, context); if (creatable || updatable) { if (maybeActual.isEmpty()) { if (creatable) { @@ -62,6 +65,10 @@ public ReconcileResult reconcile(P primary, Context

context) { return ReconcileResult.noOperation(maybeActual.orElse(null)); } + protected Optional getSecondaryResource(P primary, Context

context) { + return context.getSecondaryResource(resourceType()); + } + private void throwIfNull(R desired, P primary, String descriptor) { if (desired == null) { throw new DependentResourceException( diff --git a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/external/AbstractCachingDependentResource.java b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/external/AbstractCachingDependentResource.java index 242625bc5d..e7d07a5513 100644 --- a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/external/AbstractCachingDependentResource.java +++ b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/external/AbstractCachingDependentResource.java @@ -1,7 +1,5 @@ package io.javaoperatorsdk.operator.processing.dependent.external; -import java.util.Optional; - import io.fabric8.kubernetes.api.model.HasMetadata; import io.javaoperatorsdk.operator.api.reconciler.Ignore; import io.javaoperatorsdk.operator.processing.dependent.AbstractEventSourceHolderDependentResource; @@ -22,8 +20,4 @@ public Class resourceType() { return resourceType; } - @Override - public Optional getSecondaryResource(P primaryResource) { - return eventSource().getSecondaryResource(primaryResource); - } } diff --git a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/external/AbstractSimpleDependentResource.java b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/external/AbstractSimpleDependentResource.java index 0675db5f1a..748452c30c 100644 --- a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/external/AbstractSimpleDependentResource.java +++ b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/external/AbstractSimpleDependentResource.java @@ -33,7 +33,7 @@ public AbstractSimpleDependentResource(UpdatableCache cache) { } @Override - public Optional getSecondaryResource(HasMetadata primaryResource) { + public Optional getSecondaryResource(P primaryResource, Context

context) { return cache.get(ResourceID.fromResource(primaryResource)); } 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 930e5fd5b4..3de659ca92 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,7 +1,6 @@ package io.javaoperatorsdk.operator.processing.dependent.kubernetes; import java.util.HashMap; -import java.util.Optional; import java.util.Set; import org.slf4j.Logger; @@ -138,7 +137,7 @@ public Result match(R actualResource, P primary, Context

context) { } public void delete(P primary, Context

context) { - var resource = getSecondaryResource(primary); + var resource = context.getSecondaryResource(primary.getClass()); resource.ifPresent(r -> client.resource(r).delete()); } @@ -208,10 +207,7 @@ public Class resourceType() { return resourceType; } - @Override - public Optional getSecondaryResource(P primaryResource) { - return eventSource().getSecondaryResource(primaryResource); - } + @Override public void setKubernetesClient(KubernetesClient kubernetesClient) { diff --git a/operator-framework-core/src/test/java/io/javaoperatorsdk/operator/api/config/ControllerConfigurationOverriderTest.java b/operator-framework-core/src/test/java/io/javaoperatorsdk/operator/api/config/ControllerConfigurationOverriderTest.java index 127cd535b1..6e486b5347 100644 --- a/operator-framework-core/src/test/java/io/javaoperatorsdk/operator/api/config/ControllerConfigurationOverriderTest.java +++ b/operator-framework-core/src/test/java/io/javaoperatorsdk/operator/api/config/ControllerConfigurationOverriderTest.java @@ -1,6 +1,5 @@ package io.javaoperatorsdk.operator.api.config; -import java.util.Optional; import java.util.Set; import org.junit.jupiter.api.Test; @@ -85,10 +84,6 @@ public Class resourceType() { return Object.class; } - @Override - public Optional getSecondaryResource(ConfigMap primary) { - return Optional.empty(); - } } } diff --git a/operator-framework-core/src/test/java/io/javaoperatorsdk/operator/processing/dependent/AbstractDependentResourceTest.java b/operator-framework-core/src/test/java/io/javaoperatorsdk/operator/processing/dependent/AbstractDependentResourceTest.java index 4edd3dfb4c..004cb5d159 100644 --- a/operator-framework-core/src/test/java/io/javaoperatorsdk/operator/processing/dependent/AbstractDependentResourceTest.java +++ b/operator-framework-core/src/test/java/io/javaoperatorsdk/operator/processing/dependent/AbstractDependentResourceTest.java @@ -16,7 +16,6 @@ class AbstractDependentResourceTest { - @Test void throwsExceptionIfDesiredIsNullOnCreate() { TestDependentResource testDependentResource = new TestDependentResource(); @@ -80,7 +79,8 @@ public Class resourceType() { } @Override - public Optional getSecondaryResource(TestCustomResource primary) { + protected Optional getSecondaryResource(TestCustomResource primary, + Context context) { return Optional.ofNullable(secondary); } diff --git a/operator-framework-core/src/test/java/io/javaoperatorsdk/operator/processing/dependent/EmptyTestDependentResource.java b/operator-framework-core/src/test/java/io/javaoperatorsdk/operator/processing/dependent/EmptyTestDependentResource.java index aa75849051..dab1bc0132 100644 --- a/operator-framework-core/src/test/java/io/javaoperatorsdk/operator/processing/dependent/EmptyTestDependentResource.java +++ b/operator-framework-core/src/test/java/io/javaoperatorsdk/operator/processing/dependent/EmptyTestDependentResource.java @@ -1,7 +1,5 @@ package io.javaoperatorsdk.operator.processing.dependent; -import java.util.Optional; - import io.fabric8.kubernetes.api.model.apps.Deployment; import io.javaoperatorsdk.operator.api.reconciler.Context; import io.javaoperatorsdk.operator.api.reconciler.dependent.DependentResource; @@ -17,11 +15,6 @@ public ReconcileResult reconcile(TestCustomResource primary, return null; } - @Override - public Optional getSecondaryResource(TestCustomResource primaryResource) { - return Optional.empty(); - } - @Override public Class resourceType() { return Deployment.class; diff --git a/operator-framework-core/src/test/java/io/javaoperatorsdk/operator/processing/dependent/external/AbstractSimpleDependentResourceTest.java b/operator-framework-core/src/test/java/io/javaoperatorsdk/operator/processing/dependent/external/AbstractSimpleDependentResourceTest.java index e020fd27a3..520f44365f 100644 --- a/operator-framework-core/src/test/java/io/javaoperatorsdk/operator/processing/dependent/external/AbstractSimpleDependentResourceTest.java +++ b/operator-framework-core/src/test/java/io/javaoperatorsdk/operator/processing/dependent/external/AbstractSimpleDependentResourceTest.java @@ -45,14 +45,14 @@ void getsTheResourceFromSupplyIfReconciling() { simpleDependentResource.reconcile(TestUtils.testCustomResource1(), null); verify(supplierMock, times(1)).get(); - assertThat(simpleDependentResource.getSecondaryResource(TestUtils.testCustomResource1())) + assertThat(simpleDependentResource.getSecondaryResource(TestUtils.testCustomResource1(), null)) .isPresent() .isEqualTo(Optional.of(SampleExternalResource.testResource1())); } @Test void getResourceReadsTheResourceFromCache() { - simpleDependentResource.getSecondaryResource(TestUtils.testCustomResource1()); + simpleDependentResource.getSecondaryResource(TestUtils.testCustomResource1(), null); verify(supplierMock, times(0)).get(); verify(updatableCacheMock, times(1)).get(any()); diff --git a/operator-framework-core/src/test/java/io/javaoperatorsdk/operator/processing/dependent/workflow/AbstractWorkflowExecutorTest.java b/operator-framework-core/src/test/java/io/javaoperatorsdk/operator/processing/dependent/workflow/AbstractWorkflowExecutorTest.java index 8dfab3fa20..9c10c06cc0 100644 --- a/operator-framework-core/src/test/java/io/javaoperatorsdk/operator/processing/dependent/workflow/AbstractWorkflowExecutorTest.java +++ b/operator-framework-core/src/test/java/io/javaoperatorsdk/operator/processing/dependent/workflow/AbstractWorkflowExecutorTest.java @@ -3,7 +3,6 @@ import java.util.ArrayList; import java.util.Collections; import java.util.List; -import java.util.Optional; import io.javaoperatorsdk.operator.api.reconciler.Context; import io.javaoperatorsdk.operator.api.reconciler.dependent.Deleter; @@ -49,11 +48,6 @@ public Class resourceType() { return String.class; } - @Override - public Optional getSecondaryResource(TestCustomResource primary) { - return Optional.of(VALUE); - } - @Override public String toString() { return name; @@ -113,11 +107,6 @@ public Class resourceType() { return String.class; } - @Override - public Optional getSecondaryResource(TestCustomResource primary) { - return Optional.of(VALUE); - } - @Override public String toString() { return name; diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/standalonedependent/StandaloneDependentTestReconciler.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/standalonedependent/StandaloneDependentTestReconciler.java index af6b2d7e25..2dace670ba 100644 --- a/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/standalonedependent/StandaloneDependentTestReconciler.java +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/standalonedependent/StandaloneDependentTestReconciler.java @@ -47,7 +47,7 @@ public UpdateControl reconcile( StandaloneDependentTestCustomResource primary, Context context) { deploymentDependent.reconcile(primary, context); - Optional deployment = deploymentDependent.getSecondaryResource(primary); + Optional deployment = context.getSecondaryResource(Deployment.class); if (deployment.isEmpty()) { throw new IllegalStateException("Resource should not be empty after reconcile."); } diff --git a/sample-operators/webpage/src/main/java/io/javaoperatorsdk/operator/sample/WebPageDependentsWorkflowReconciler.java b/sample-operators/webpage/src/main/java/io/javaoperatorsdk/operator/sample/WebPageDependentsWorkflowReconciler.java index 6986180b89..06080be67b 100644 --- a/sample-operators/webpage/src/main/java/io/javaoperatorsdk/operator/sample/WebPageDependentsWorkflowReconciler.java +++ b/sample-operators/webpage/src/main/java/io/javaoperatorsdk/operator/sample/WebPageDependentsWorkflowReconciler.java @@ -69,7 +69,7 @@ public UpdateControl reconcile(WebPage webPage, Context contex webPage.setStatus( createStatus( - configMapDR.getSecondaryResource(webPage).orElseThrow().getMetadata().getName())); + context.getSecondaryResource(ConfigMap.class).orElseThrow().getMetadata().getName())); return UpdateControl.patchStatus(webPage); } diff --git a/sample-operators/webpage/src/main/java/io/javaoperatorsdk/operator/sample/WebPageStandaloneDependentsReconciler.java b/sample-operators/webpage/src/main/java/io/javaoperatorsdk/operator/sample/WebPageStandaloneDependentsReconciler.java index 0bb692a4d0..b99e130135 100644 --- a/sample-operators/webpage/src/main/java/io/javaoperatorsdk/operator/sample/WebPageStandaloneDependentsReconciler.java +++ b/sample-operators/webpage/src/main/java/io/javaoperatorsdk/operator/sample/WebPageStandaloneDependentsReconciler.java @@ -65,7 +65,7 @@ public UpdateControl reconcile(WebPage webPage, Context contex webPage.setStatus( createStatus( - configMapDR.getSecondaryResource(webPage).orElseThrow().getMetadata().getName())); + context.getSecondaryResource(ConfigMap.class).orElseThrow().getMetadata().getName())); return UpdateControl.patchStatus(webPage); } From 7e3119ca69c4bb6b5baed3e287e4a2b7266472ce Mon Sep 17 00:00:00 2001 From: csviri Date: Wed, 27 Jul 2022 14:26:13 +0200 Subject: [PATCH 02/67] wip --- .../operator/api/reconciler/Context.java | 11 +++++--- .../api/reconciler/DefaultContext.java | 7 +++++ .../api/reconciler/ResourceDiscriminator.java | 14 ++++++++++ .../reconciler/ResourceListDiscriminator.java | 19 ++++++++++++++ .../dependent/DependentResource.java | 1 - .../operator/processing/ResourceOwner.java | 26 ------------------- .../processing/event/EventSourceManager.java | 20 +++++++++----- .../event/EventSourceRetriever.java | 18 +++++++++++++ .../processing/event/EventSources.java | 5 ++-- .../event/source/ResourceEventSource.java | 11 +++++--- .../WebPageDependentsWorkflowReconciler.java | 2 -- 11 files changed, 89 insertions(+), 45 deletions(-) create mode 100644 operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/reconciler/ResourceDiscriminator.java create mode 100644 operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/reconciler/ResourceListDiscriminator.java delete mode 100644 operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/ResourceOwner.java create mode 100644 operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/EventSourceRetriever.java diff --git a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/reconciler/Context.java b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/reconciler/Context.java index fbe83181af..a26e7c46fb 100644 --- a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/reconciler/Context.java +++ b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/reconciler/Context.java @@ -11,14 +11,17 @@ public interface Context

{ Optional getRetryInfo(); - default Optional getSecondaryResource(Class expectedType) { - return getSecondaryResource(expectedType, null); + default Optional getSecondaryResource(Class expectedType) { + return getSecondaryResource(expectedType, (String) null); } - Set getSecondaryResources(Class expectedType); + Set getSecondaryResources(Class expectedType); @Deprecated(forRemoval = true) - Optional getSecondaryResource(Class expectedType, String eventSourceName); + Optional getSecondaryResource(Class expectedType, String eventSourceName); + + Optional getSecondaryResource(Class expectedType, + ResourceDiscriminator discriminator); ControllerConfiguration

getControllerConfiguration(); diff --git a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/reconciler/DefaultContext.java b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/reconciler/DefaultContext.java index afb37a8c53..3ad03516ae 100644 --- a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/reconciler/DefaultContext.java +++ b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/reconciler/DefaultContext.java @@ -47,6 +47,13 @@ public Optional getSecondaryResource(Class expectedType, String eventS .getSecondaryResource(primaryResource); } + // todo implement + @Override + public Optional getSecondaryResource(Class expectedType, + ResourceDiscriminator discriminator) { + return Optional.empty(); + } + @Override public ControllerConfiguration

getControllerConfiguration() { return controllerConfiguration; diff --git a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/reconciler/ResourceDiscriminator.java b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/reconciler/ResourceDiscriminator.java new file mode 100644 index 0000000000..3ff1dc58a2 --- /dev/null +++ b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/reconciler/ResourceDiscriminator.java @@ -0,0 +1,14 @@ +package io.javaoperatorsdk.operator.api.reconciler; + +import java.util.Optional; + +import io.fabric8.kubernetes.api.model.HasMetadata; +import io.javaoperatorsdk.operator.processing.event.EventSourceRetriever; + +// todo is discriminator a good name? it not just discriminates but also reads from cache +public interface ResourceDiscriminator { + + Optional distinguish(Class resource, Context

context, + EventSourceRetriever

eventSourceManager); + +} diff --git a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/reconciler/ResourceListDiscriminator.java b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/reconciler/ResourceListDiscriminator.java new file mode 100644 index 0000000000..da6f173268 --- /dev/null +++ b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/reconciler/ResourceListDiscriminator.java @@ -0,0 +1,19 @@ +package io.javaoperatorsdk.operator.api.reconciler; + +import java.util.Optional; +import java.util.Set; + +import io.fabric8.kubernetes.api.model.HasMetadata; +import io.javaoperatorsdk.operator.processing.event.EventSourceRetriever; + +public abstract class ResourceListDiscriminator + implements ResourceDiscriminator { + @Override + public Optional distinguish(Class resource, Context

context, + EventSourceRetriever

eventSourceManager) { + var resources = context.getSecondaryResources(resource); + return distinguish(resources); + } + + abstract Optional distinguish(Set resourceList); +} 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 1c73cac176..3bd9bf8061 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 @@ -2,7 +2,6 @@ import io.fabric8.kubernetes.api.model.HasMetadata; import io.javaoperatorsdk.operator.api.reconciler.Context; -import io.javaoperatorsdk.operator.processing.ResourceOwner; /** * An interface to implement and provide dependent resource support. diff --git a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/ResourceOwner.java b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/ResourceOwner.java deleted file mode 100644 index f9c02a8a33..0000000000 --- a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/ResourceOwner.java +++ /dev/null @@ -1,26 +0,0 @@ -package io.javaoperatorsdk.operator.processing; - -import java.util.Optional; - -import io.fabric8.kubernetes.api.model.HasMetadata; - -public interface ResourceOwner { - - /** - * Retrieves the resource type associated with this ResourceOwner - * - * @return the resource type associated with this ResourceOwner - */ - Class resourceType(); - - /** - * Retrieves the resource associated with the specified primary one, returning the actual state of - * the resource. Typically, this state might come from a local cache, updated after - * reconciliation. - * - * @param primary the primary resource for which we want to retrieve the secondary resource - * @return an {@link Optional} containing the secondary resource or {@link Optional#empty()} if it - * doesn't exist - */ - Optional getSecondaryResource(P primary); -} diff --git a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/EventSourceManager.java b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/EventSourceManager.java index 3f1e5e848d..6e4305b82a 100644 --- a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/EventSourceManager.java +++ b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/EventSourceManager.java @@ -23,7 +23,8 @@ import io.javaoperatorsdk.operator.processing.event.source.controller.ResourceAction; import io.javaoperatorsdk.operator.processing.event.source.timer.TimerEventSource; -public class EventSourceManager

implements LifecycleAware { +public class EventSourceManager

+ implements LifecycleAware, EventSourceRetriever

{ private static final Logger log = LoggerFactory.getLogger(EventSourceManager.class); @@ -171,17 +172,24 @@ public ControllerResourceEventSource

getControllerResourceEventSource() { return eventSources.controllerResourceEventSource(); } - ResourceEventSource getResourceEventSourceFor( - Class dependentType) { + @Override + public ResourceEventSource getResourceEventSourceFor( + Class dependentType) { return getResourceEventSourceFor(dependentType, null); } - public List> getEventSourcesFor(Class dependentType) { + public List> getResourceEventSourcesFor(Class dependentType) { return eventSources.getEventSources(dependentType); } - public ResourceEventSource getResourceEventSourceFor( - Class dependentType, String qualifier) { + @Deprecated + public List> getEventSourcesFor(Class dependentType) { + return eventSources.getEventSources(dependentType); + } + + @Override + public ResourceEventSource getResourceEventSourceFor( + Class dependentType, String qualifier) { Objects.requireNonNull(dependentType, "dependentType is Mandatory"); return eventSources.get(dependentType, qualifier); } diff --git a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/EventSourceRetriever.java b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/EventSourceRetriever.java new file mode 100644 index 0000000000..a31d8902b0 --- /dev/null +++ b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/EventSourceRetriever.java @@ -0,0 +1,18 @@ +package io.javaoperatorsdk.operator.processing.event; + +import java.util.List; + +import io.fabric8.kubernetes.api.model.HasMetadata; +import io.javaoperatorsdk.operator.processing.event.source.ResourceEventSource; + +public interface EventSourceRetriever

{ + + ResourceEventSource getResourceEventSourceFor( + Class dependentType); + + ResourceEventSource getResourceEventSourceFor( + Class dependentType, String qualifier); + + List> getResourceEventSourcesFor(Class dependentType); + +} diff --git a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/EventSources.java b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/EventSources.java index 17de7ff947..e4fabe7ff8 100644 --- a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/EventSources.java +++ b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/EventSources.java @@ -8,7 +8,6 @@ import io.fabric8.kubernetes.api.model.HasMetadata; import io.javaoperatorsdk.operator.processing.Controller; -import io.javaoperatorsdk.operator.processing.ResourceOwner; import io.javaoperatorsdk.operator.processing.event.source.EventSource; import io.javaoperatorsdk.operator.processing.event.source.ResourceEventSource; import io.javaoperatorsdk.operator.processing.event.source.controller.ControllerResourceEventSource; @@ -86,8 +85,8 @@ public void add(String name, EventSource eventSource) { @SuppressWarnings("rawtypes") private Class getResourceType(EventSource source) { - return source instanceof ResourceOwner - ? ((ResourceOwner) source).resourceType() + return source instanceof ResourceEventSource + ? ((ResourceEventSource) source).resourceType() : source.getClass(); } diff --git a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/source/ResourceEventSource.java b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/source/ResourceEventSource.java index bf6b9c0fd3..d58ea11e48 100644 --- a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/source/ResourceEventSource.java +++ b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/source/ResourceEventSource.java @@ -4,14 +4,19 @@ import java.util.Set; import io.fabric8.kubernetes.api.model.HasMetadata; -import io.javaoperatorsdk.operator.processing.ResourceOwner; import io.javaoperatorsdk.operator.processing.event.source.filter.GenericFilter; import io.javaoperatorsdk.operator.processing.event.source.filter.OnAddFilter; import io.javaoperatorsdk.operator.processing.event.source.filter.OnDeleteFilter; import io.javaoperatorsdk.operator.processing.event.source.filter.OnUpdateFilter; -public interface ResourceEventSource extends EventSource, - ResourceOwner { +public interface ResourceEventSource extends EventSource { + + /** + * Retrieves the resource type associated with this ResourceOwner + * + * @return the resource type associated with this ResourceOwner + */ + Class resourceType(); default Optional getSecondaryResource(P primary) { var resources = getSecondaryResources(primary); diff --git a/sample-operators/webpage/src/main/java/io/javaoperatorsdk/operator/sample/WebPageDependentsWorkflowReconciler.java b/sample-operators/webpage/src/main/java/io/javaoperatorsdk/operator/sample/WebPageDependentsWorkflowReconciler.java index 06080be67b..6b81a9d6e3 100644 --- a/sample-operators/webpage/src/main/java/io/javaoperatorsdk/operator/sample/WebPageDependentsWorkflowReconciler.java +++ b/sample-operators/webpage/src/main/java/io/javaoperatorsdk/operator/sample/WebPageDependentsWorkflowReconciler.java @@ -93,6 +93,4 @@ private void initDependentResources(KubernetesClient client) { }); } - - } From ffc150b32eb2e347061aca906231066eb3675a41 Mon Sep 17 00:00:00 2001 From: csviri Date: Wed, 27 Jul 2022 16:05:27 +0200 Subject: [PATCH 03/67] fix --- .../dependent/kubernetes/KubernetesDependentResource.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 3de659ca92..9b37d3b880 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 @@ -137,7 +137,7 @@ public Result match(R actualResource, P primary, Context

context) { } public void delete(P primary, Context

context) { - var resource = context.getSecondaryResource(primary.getClass()); + var resource = context.getSecondaryResource(resourceType()); resource.ifPresent(r -> client.resource(r).delete()); } From 7b16504d5967028c2a1589002ba0a22d39495ba5 Mon Sep 17 00:00:00 2001 From: csviri Date: Thu, 28 Jul 2022 13:11:18 +0200 Subject: [PATCH 04/67] kubernetes dependent resource configuration --- .../AnnotationControllerConfiguration.java | 11 +++++++---- .../reconciler/VoidResourceDiscriminator.java | 16 ++++++++++++++++ .../dependent/AbstractDependentResource.java | 19 ++++++++++++++++++- .../kubernetes/KubernetesDependent.java | 5 +++++ .../KubernetesDependentResource.java | 1 + .../KubernetesDependentResourceConfig.java | 19 +++++++++++++------ 6 files changed, 60 insertions(+), 11 deletions(-) create mode 100644 operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/reconciler/VoidResourceDiscriminator.java diff --git a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/config/AnnotationControllerConfiguration.java b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/config/AnnotationControllerConfiguration.java index b5e3fffcf0..59dc80869a 100644 --- a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/config/AnnotationControllerConfiguration.java +++ b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/config/AnnotationControllerConfiguration.java @@ -17,15 +17,13 @@ import io.javaoperatorsdk.operator.OperatorException; import io.javaoperatorsdk.operator.ReconcilerUtils; import io.javaoperatorsdk.operator.api.config.dependent.DependentResourceSpec; -import io.javaoperatorsdk.operator.api.reconciler.Constants; +import io.javaoperatorsdk.operator.api.reconciler.*; import io.javaoperatorsdk.operator.api.reconciler.ControllerConfiguration; -import io.javaoperatorsdk.operator.api.reconciler.Reconciler; import io.javaoperatorsdk.operator.api.reconciler.dependent.Dependent; import io.javaoperatorsdk.operator.api.reconciler.dependent.DependentResource; import io.javaoperatorsdk.operator.processing.dependent.kubernetes.KubernetesDependent; import io.javaoperatorsdk.operator.processing.dependent.kubernetes.KubernetesDependentResource; import io.javaoperatorsdk.operator.processing.dependent.kubernetes.KubernetesDependentResourceConfig; -import io.javaoperatorsdk.operator.processing.dependent.workflow.Condition; import io.javaoperatorsdk.operator.processing.event.rate.RateLimiter; import io.javaoperatorsdk.operator.processing.event.source.controller.ResourceEventFilter; import io.javaoperatorsdk.operator.processing.event.source.controller.ResourceEventFilters; @@ -287,6 +285,7 @@ private Object createKubernetesResourceConfig(Class OnUpdateFilter onUpdateFilter = null; OnDeleteFilter onDeleteFilter = null; GenericFilter genericFilter = null; + ResourceDiscriminator resourceDiscriminator = null; if (kubeDependent != null) { if (!Arrays.equals(KubernetesDependent.DEFAULT_NAMESPACES, kubeDependent.namespaces())) { @@ -311,10 +310,14 @@ private Object createKubernetesResourceConfig(Class genericFilter = createFilter(kubeDependent.genericFilter(), GenericFilter.class, context) .orElse(null); + + resourceDiscriminator = instantiateIfNotVoid(kubeDependent.resourceDiscriminator(), + VoidResourceDiscriminator.class); } config = - new KubernetesDependentResourceConfig(namespaces, labelSelector, configuredNS, onAddFilter, + new KubernetesDependentResourceConfig(namespaces, labelSelector, configuredNS, + resourceDiscriminator, onAddFilter, onUpdateFilter, onDeleteFilter, genericFilter); return config; diff --git a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/reconciler/VoidResourceDiscriminator.java b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/reconciler/VoidResourceDiscriminator.java new file mode 100644 index 0000000000..0caec9361d --- /dev/null +++ b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/reconciler/VoidResourceDiscriminator.java @@ -0,0 +1,16 @@ +package io.javaoperatorsdk.operator.api.reconciler; + +import java.util.Optional; + +import io.fabric8.kubernetes.api.model.HasMetadata; +import io.javaoperatorsdk.operator.processing.event.EventSourceRetriever; + +public class VoidResourceDiscriminator + implements ResourceDiscriminator { + + @Override + public Optional distinguish(Class resource, Context

context, + EventSourceRetriever

eventSourceManager) { + throw new UnsupportedOperationException(); + } +} 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 730add6f01..9e12f25db0 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 @@ -8,6 +8,7 @@ import io.fabric8.kubernetes.api.model.HasMetadata; import io.javaoperatorsdk.operator.api.reconciler.Context; import io.javaoperatorsdk.operator.api.reconciler.Ignore; +import io.javaoperatorsdk.operator.api.reconciler.ResourceDiscriminator; import io.javaoperatorsdk.operator.api.reconciler.dependent.DependentResource; import io.javaoperatorsdk.operator.api.reconciler.dependent.ReconcileResult; import io.javaoperatorsdk.operator.processing.event.ResourceID; @@ -23,6 +24,8 @@ public abstract class AbstractDependentResource protected Creator creator; protected Updater updater; + private ResourceDiscriminator resourceDiscriminator; + @SuppressWarnings("unchecked") public AbstractDependentResource() { creator = creatable ? (Creator) this : null; @@ -66,7 +69,11 @@ public ReconcileResult reconcile(P primary, Context

context) { } protected Optional getSecondaryResource(P primary, Context

context) { - return context.getSecondaryResource(resourceType()); + if (resourceDiscriminator == null) { + return context.getSecondaryResource(resourceType()); + } else { + return context.getSecondaryResource(resourceType(), resourceDiscriminator); + } } private void throwIfNull(R desired, P primary, String descriptor) { @@ -125,4 +132,14 @@ protected R desired(P primary, Context

context) { throw new IllegalStateException( "desired method must be implemented if this DependentResource can be created and/or updated"); } + + protected ResourceDiscriminator getResourceDiscriminator() { + return resourceDiscriminator; + } + + protected AbstractDependentResource setResourceDiscriminator( + ResourceDiscriminator resourceDiscriminator) { + this.resourceDiscriminator = resourceDiscriminator; + return this; + } } diff --git a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/kubernetes/KubernetesDependent.java b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/kubernetes/KubernetesDependent.java index f66ff95373..6453a1b061 100644 --- a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/kubernetes/KubernetesDependent.java +++ b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/kubernetes/KubernetesDependent.java @@ -6,6 +6,9 @@ import java.lang.annotation.Target; import io.javaoperatorsdk.operator.api.reconciler.Constants; +import io.javaoperatorsdk.operator.api.reconciler.ResourceDiscriminator; +import io.javaoperatorsdk.operator.api.reconciler.VoidResourceDiscriminator; +import io.javaoperatorsdk.operator.processing.event.source.filter.*; import io.javaoperatorsdk.operator.processing.event.source.filter.GenericFilter; import io.javaoperatorsdk.operator.processing.event.source.filter.OnAddFilter; import io.javaoperatorsdk.operator.processing.event.source.filter.OnDeleteFilter; @@ -68,4 +71,6 @@ * itself if no value is set */ Class genericFilter() default GenericFilter.class; + + Class resourceDiscriminator() default VoidResourceDiscriminator.class; } 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 9b37d3b880..e6d358ce12 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 @@ -166,6 +166,7 @@ protected InformerEventSource createEventSource(EventSourceContext

cont onUpdateFilter = kubernetesDependentResourceConfig.onUpdateFilter(); onDeleteFilter = kubernetesDependentResourceConfig.onDeleteFilter(); genericFilter = kubernetesDependentResourceConfig.genericFilter(); + setResourceDiscriminator(kubernetesDependentResourceConfig.getResourceDiscriminator()); configureWith(kubernetesDependentResourceConfig.labelSelector(), kubernetesDependentResourceConfig.namespaces(), diff --git a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/kubernetes/KubernetesDependentResourceConfig.java b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/kubernetes/KubernetesDependentResourceConfig.java index e4a75b165a..fbd4eebd9a 100644 --- a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/kubernetes/KubernetesDependentResourceConfig.java +++ b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/kubernetes/KubernetesDependentResourceConfig.java @@ -3,6 +3,7 @@ import java.util.Set; import io.javaoperatorsdk.operator.api.reconciler.Constants; +import io.javaoperatorsdk.operator.api.reconciler.ResourceDiscriminator; import io.javaoperatorsdk.operator.processing.event.source.filter.GenericFilter; import io.javaoperatorsdk.operator.processing.event.source.filter.OnAddFilter; import io.javaoperatorsdk.operator.processing.event.source.filter.OnDeleteFilter; @@ -15,7 +16,7 @@ public class KubernetesDependentResourceConfig { private Set namespaces = Constants.SAME_AS_CONTROLLER_NAMESPACES_SET; private String labelSelector = NO_VALUE_SET; private boolean namespacesWereConfigured = false; - + private ResourceDiscriminator resourceDiscriminator; private OnAddFilter onAddFilter; @@ -29,7 +30,8 @@ public KubernetesDependentResourceConfig() {} @SuppressWarnings("rawtypes") public KubernetesDependentResourceConfig(Set namespaces, String labelSelector, - boolean configuredNS, OnAddFilter onAddFilter, + boolean configuredNS, ResourceDiscriminator resourceDiscriminator, + OnAddFilter onAddFilter, OnUpdateFilter onUpdateFilter, OnDeleteFilter onDeleteFilter, GenericFilter genericFilter) { this.namespaces = namespaces; @@ -42,7 +44,7 @@ public KubernetesDependentResourceConfig(Set namespaces, String labelSel } public KubernetesDependentResourceConfig(Set namespaces, String labelSelector) { - this(namespaces, labelSelector, true, null, null, null, null); + this(namespaces, labelSelector, true, null, null, null, null, null); } public KubernetesDependentResourceConfig setNamespaces(Set namespaces) { @@ -73,17 +75,22 @@ public OnAddFilter onAddFilter() { return onAddFilter; } - @SuppressWarnings("rawtypes") - public OnUpdateFilter onUpdateFilter() { + + public OnUpdateFilter onUpdateFilter() { return onUpdateFilter; } @SuppressWarnings("rawtypes") - public OnDeleteFilter onDeleteFilter() { + public OnDeleteFilter onDeleteFilter() { return onDeleteFilter; } public GenericFilter genericFilter() { return genericFilter; } + + @SuppressWarnings("rawtypes") + public ResourceDiscriminator getResourceDiscriminator() { + return resourceDiscriminator; + } } From 7b442bcf12444304246cd88f781331d134352878 Mon Sep 17 00:00:00 2001 From: csviri Date: Thu, 28 Jul 2022 14:58:09 +0200 Subject: [PATCH 05/67] IT fix --- .../operator/api/reconciler/DefaultContext.java | 4 ++-- .../api/reconciler/ResourceDiscriminator.java | 2 +- .../reconciler/ResourceListDiscriminator.java | 7 ++++--- .../reconciler/VoidResourceDiscriminator.java | 2 +- .../dependent/AbstractDependentResource.java | 1 + .../KubernetesDependentResourceConfig.java | 1 + .../ConfigMapDependentResource1.java | 16 +++++++++++++++- .../ConfigMapDependentResource2.java | 16 +++++++++++++++- 8 files changed, 40 insertions(+), 9 deletions(-) diff --git a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/reconciler/DefaultContext.java b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/reconciler/DefaultContext.java index 3ad03516ae..0d5bc22018 100644 --- a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/reconciler/DefaultContext.java +++ b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/reconciler/DefaultContext.java @@ -47,11 +47,11 @@ public Optional getSecondaryResource(Class expectedType, String eventS .getSecondaryResource(primaryResource); } - // todo implement @Override public Optional getSecondaryResource(Class expectedType, ResourceDiscriminator discriminator) { - return Optional.empty(); + return discriminator.distinguish(expectedType, primaryResource, this, + controller.getEventSourceManager()); } @Override diff --git a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/reconciler/ResourceDiscriminator.java b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/reconciler/ResourceDiscriminator.java index 3ff1dc58a2..2fe69c3804 100644 --- a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/reconciler/ResourceDiscriminator.java +++ b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/reconciler/ResourceDiscriminator.java @@ -8,7 +8,7 @@ // todo is discriminator a good name? it not just discriminates but also reads from cache public interface ResourceDiscriminator { - Optional distinguish(Class resource, Context

context, + Optional distinguish(Class resource, P primary, Context

context, EventSourceRetriever

eventSourceManager); } diff --git a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/reconciler/ResourceListDiscriminator.java b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/reconciler/ResourceListDiscriminator.java index da6f173268..fbb86dbd04 100644 --- a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/reconciler/ResourceListDiscriminator.java +++ b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/reconciler/ResourceListDiscriminator.java @@ -6,14 +6,15 @@ import io.fabric8.kubernetes.api.model.HasMetadata; import io.javaoperatorsdk.operator.processing.event.EventSourceRetriever; +// todo this requires rather a matcher (name+namespace) as input public abstract class ResourceListDiscriminator implements ResourceDiscriminator { @Override - public Optional distinguish(Class resource, Context

context, + public Optional distinguish(Class resource, P primary, Context

context, EventSourceRetriever

eventSourceManager) { var resources = context.getSecondaryResources(resource); - return distinguish(resources); + return distinguish(primary, resources); } - abstract Optional distinguish(Set resourceList); + protected abstract Optional distinguish(P primary, Set resourceList); } diff --git a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/reconciler/VoidResourceDiscriminator.java b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/reconciler/VoidResourceDiscriminator.java index 0caec9361d..3a627df109 100644 --- a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/reconciler/VoidResourceDiscriminator.java +++ b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/reconciler/VoidResourceDiscriminator.java @@ -9,7 +9,7 @@ public class VoidResourceDiscriminator implements ResourceDiscriminator { @Override - public Optional distinguish(Class resource, Context

context, + public Optional distinguish(Class resource, P primary, Context

context, EventSourceRetriever

eventSourceManager) { throw new UnsupportedOperationException(); } 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 9e12f25db0..130b6237e3 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 @@ -24,6 +24,7 @@ public abstract class AbstractDependentResource protected Creator creator; protected Updater updater; + // todo discuss, rather implement this as interface? private ResourceDiscriminator resourceDiscriminator; @SuppressWarnings("unchecked") diff --git a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/kubernetes/KubernetesDependentResourceConfig.java b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/kubernetes/KubernetesDependentResourceConfig.java index fbd4eebd9a..93b399631b 100644 --- a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/kubernetes/KubernetesDependentResourceConfig.java +++ b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/kubernetes/KubernetesDependentResourceConfig.java @@ -41,6 +41,7 @@ public KubernetesDependentResourceConfig(Set namespaces, String labelSel this.onUpdateFilter = onUpdateFilter; this.onDeleteFilter = onDeleteFilter; this.genericFilter = genericFilter; + this.resourceDiscriminator = resourceDiscriminator; } public KubernetesDependentResourceConfig(Set namespaces, String labelSelector) { diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/orderedmanageddependent/ConfigMapDependentResource1.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/orderedmanageddependent/ConfigMapDependentResource1.java index 14530cf17e..d9364f0d83 100644 --- a/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/orderedmanageddependent/ConfigMapDependentResource1.java +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/orderedmanageddependent/ConfigMapDependentResource1.java @@ -2,15 +2,19 @@ import java.util.HashMap; import java.util.Map; +import java.util.Optional; +import java.util.Set; import io.fabric8.kubernetes.api.model.ConfigMap; import io.fabric8.kubernetes.api.model.ObjectMeta; import io.javaoperatorsdk.operator.api.reconciler.Context; +import io.javaoperatorsdk.operator.api.reconciler.ResourceListDiscriminator; import io.javaoperatorsdk.operator.api.reconciler.dependent.ReconcileResult; import io.javaoperatorsdk.operator.processing.dependent.kubernetes.CRUDKubernetesDependentResource; import io.javaoperatorsdk.operator.processing.dependent.kubernetes.KubernetesDependent; -@KubernetesDependent(labelSelector = "dependent = cm1") +@KubernetesDependent(labelSelector = "dependent = cm1", + resourceDiscriminator = ConfigMapDependentResource1.CM1ResourceDiscriminator.class) public class ConfigMapDependentResource1 extends CRUDKubernetesDependentResource { @@ -42,4 +46,14 @@ protected ConfigMap desired(OrderedManagedDependentCustomResource primary, return configMap; } + public static class CM1ResourceDiscriminator + extends ResourceListDiscriminator { + @Override + protected Optional distinguish(OrderedManagedDependentCustomResource primary, + Set resourceList) { + return resourceList.stream().filter(cm -> cm.getMetadata().getName() + .equals(primary.getMetadata().getName() + "1")).findFirst(); + } + } + } diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/orderedmanageddependent/ConfigMapDependentResource2.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/orderedmanageddependent/ConfigMapDependentResource2.java index 35ae69586e..6ad23d0ae9 100644 --- a/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/orderedmanageddependent/ConfigMapDependentResource2.java +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/orderedmanageddependent/ConfigMapDependentResource2.java @@ -2,15 +2,19 @@ import java.util.HashMap; import java.util.Map; +import java.util.Optional; +import java.util.Set; import io.fabric8.kubernetes.api.model.ConfigMap; import io.fabric8.kubernetes.api.model.ObjectMeta; import io.javaoperatorsdk.operator.api.reconciler.Context; +import io.javaoperatorsdk.operator.api.reconciler.ResourceListDiscriminator; import io.javaoperatorsdk.operator.api.reconciler.dependent.ReconcileResult; import io.javaoperatorsdk.operator.processing.dependent.kubernetes.CRUDKubernetesDependentResource; import io.javaoperatorsdk.operator.processing.dependent.kubernetes.KubernetesDependent; -@KubernetesDependent(labelSelector = "dependent = cm2") +@KubernetesDependent(labelSelector = "dependent = cm2", + resourceDiscriminator = ConfigMapDependentResource2.CM2ResourceDiscriminator.class) public class ConfigMapDependentResource2 extends CRUDKubernetesDependentResource { @@ -42,4 +46,14 @@ protected ConfigMap desired(OrderedManagedDependentCustomResource primary, return configMap; } + public static class CM2ResourceDiscriminator + extends ResourceListDiscriminator { + @Override + protected Optional distinguish(OrderedManagedDependentCustomResource primary, + Set resourceList) { + return resourceList.stream().filter(cm -> cm.getMetadata().getName() + .equals(primary.getMetadata().getName() + "2")).findFirst(); + } + } + } From f5fd95725ddca6811ebdf97d11394390fdf0efd9 Mon Sep 17 00:00:00 2001 From: csviri Date: Fri, 29 Jul 2022 09:31:48 +0200 Subject: [PATCH 06/67] fixed ITs --- .../ResourceIDMatcherDiscriminator.java | 30 +++++++++++++++++++ .../reconciler/ResourceListDiscriminator.java | 20 ------------- .../KubernetesDependentResourceConfig.java | 6 ++++ .../MultipleDependentResourceReconciler.java | 23 +++++++++----- .../ConfigMapDependentResource1.java | 14 ++++----- .../ConfigMapDependentResource2.java | 14 ++++----- 6 files changed, 61 insertions(+), 46 deletions(-) create mode 100644 operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/reconciler/ResourceIDMatcherDiscriminator.java delete mode 100644 operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/reconciler/ResourceListDiscriminator.java diff --git a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/reconciler/ResourceIDMatcherDiscriminator.java b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/reconciler/ResourceIDMatcherDiscriminator.java new file mode 100644 index 0000000000..0d06222e45 --- /dev/null +++ b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/reconciler/ResourceIDMatcherDiscriminator.java @@ -0,0 +1,30 @@ +package io.javaoperatorsdk.operator.api.reconciler; + +import java.util.Optional; +import java.util.function.Function; + +import io.fabric8.kubernetes.api.model.HasMetadata; +import io.javaoperatorsdk.operator.processing.event.EventSourceRetriever; +import io.javaoperatorsdk.operator.processing.event.ResourceID; + +public class ResourceIDMatcherDiscriminator + implements ResourceDiscriminator { + + private final Function mapper; + + public ResourceIDMatcherDiscriminator(Function mapper) { + this.mapper = mapper; + } + + @Override + public Optional distinguish(Class resource, P primary, Context

context, + EventSourceRetriever

eventSourceManager) { + var resourceID = mapper.apply(primary); + return context.getSecondaryResources(resource).stream() + .filter(r -> r.getMetadata().getName() + .equals(resourceID.getName()) && + resourceID.getNamespace().map(ns -> ns.equals(r.getMetadata().getNamespace())) + .orElse(true)) + .findFirst(); + } +} diff --git a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/reconciler/ResourceListDiscriminator.java b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/reconciler/ResourceListDiscriminator.java deleted file mode 100644 index fbb86dbd04..0000000000 --- a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/reconciler/ResourceListDiscriminator.java +++ /dev/null @@ -1,20 +0,0 @@ -package io.javaoperatorsdk.operator.api.reconciler; - -import java.util.Optional; -import java.util.Set; - -import io.fabric8.kubernetes.api.model.HasMetadata; -import io.javaoperatorsdk.operator.processing.event.EventSourceRetriever; - -// todo this requires rather a matcher (name+namespace) as input -public abstract class ResourceListDiscriminator - implements ResourceDiscriminator { - @Override - public Optional distinguish(Class resource, P primary, Context

context, - EventSourceRetriever

eventSourceManager) { - var resources = context.getSecondaryResources(resource); - return distinguish(primary, resources); - } - - protected abstract Optional distinguish(P primary, Set resourceList); -} diff --git a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/kubernetes/KubernetesDependentResourceConfig.java b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/kubernetes/KubernetesDependentResourceConfig.java index 93b399631b..a185071221 100644 --- a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/kubernetes/KubernetesDependentResourceConfig.java +++ b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/kubernetes/KubernetesDependentResourceConfig.java @@ -94,4 +94,10 @@ public GenericFilter genericFilter() { public ResourceDiscriminator getResourceDiscriminator() { return resourceDiscriminator; } + + public

KubernetesDependentResourceConfig setResourceDiscriminator( + ResourceDiscriminator resourceDiscriminator) { + this.resourceDiscriminator = resourceDiscriminator; + return this; + } } 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 8cdbb81eba..69c1ba0b04 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 @@ -3,15 +3,12 @@ import java.util.Map; import java.util.concurrent.atomic.AtomicInteger; +import io.fabric8.kubernetes.api.model.ConfigMap; import io.fabric8.kubernetes.client.KubernetesClient; -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.EventSourceInitializer; -import io.javaoperatorsdk.operator.api.reconciler.Reconciler; -import io.javaoperatorsdk.operator.api.reconciler.UpdateControl; +import io.javaoperatorsdk.operator.api.reconciler.*; import io.javaoperatorsdk.operator.junit.KubernetesClientAware; import io.javaoperatorsdk.operator.processing.dependent.kubernetes.KubernetesDependentResourceConfig; +import io.javaoperatorsdk.operator.processing.event.ResourceID; import io.javaoperatorsdk.operator.processing.event.source.EventSource; import io.javaoperatorsdk.operator.support.TestExecutionInfoProvider; @@ -32,14 +29,24 @@ public class MultipleDependentResourceReconciler public MultipleDependentResourceReconciler() { firstDependentResourceConfigMap = new MultipleDependentResourceConfigMap(FIRST_CONFIG_MAP_ID); + secondDependentResourceConfigMap = new MultipleDependentResourceConfigMap(SECOND_CONFIG_MAP_ID); firstDependentResourceConfigMap.configureWith( new KubernetesDependentResourceConfig() - .setLabelSelector(getLabelSelector(FIRST_CONFIG_MAP_ID))); + .setLabelSelector(getLabelSelector(FIRST_CONFIG_MAP_ID)) + .setResourceDiscriminator( + new ResourceIDMatcherDiscriminator( + p -> new ResourceID(p.getConfigMapName(FIRST_CONFIG_MAP_ID), + p.getMetadata().getNamespace())))); + secondDependentResourceConfigMap.configureWith( new KubernetesDependentResourceConfig() - .setLabelSelector(getLabelSelector(SECOND_CONFIG_MAP_ID))); + .setLabelSelector(getLabelSelector(SECOND_CONFIG_MAP_ID)) + .setResourceDiscriminator( + new ResourceIDMatcherDiscriminator( + p -> new ResourceID(p.getConfigMapName(SECOND_CONFIG_MAP_ID), + p.getMetadata().getNamespace())))); } private String getLabelSelector(int resourceId) { diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/orderedmanageddependent/ConfigMapDependentResource1.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/orderedmanageddependent/ConfigMapDependentResource1.java index d9364f0d83..bf8d60d9c4 100644 --- a/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/orderedmanageddependent/ConfigMapDependentResource1.java +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/orderedmanageddependent/ConfigMapDependentResource1.java @@ -2,16 +2,15 @@ import java.util.HashMap; import java.util.Map; -import java.util.Optional; -import java.util.Set; import io.fabric8.kubernetes.api.model.ConfigMap; import io.fabric8.kubernetes.api.model.ObjectMeta; import io.javaoperatorsdk.operator.api.reconciler.Context; -import io.javaoperatorsdk.operator.api.reconciler.ResourceListDiscriminator; +import io.javaoperatorsdk.operator.api.reconciler.ResourceIDMatcherDiscriminator; import io.javaoperatorsdk.operator.api.reconciler.dependent.ReconcileResult; import io.javaoperatorsdk.operator.processing.dependent.kubernetes.CRUDKubernetesDependentResource; import io.javaoperatorsdk.operator.processing.dependent.kubernetes.KubernetesDependent; +import io.javaoperatorsdk.operator.processing.event.ResourceID; @KubernetesDependent(labelSelector = "dependent = cm1", resourceDiscriminator = ConfigMapDependentResource1.CM1ResourceDiscriminator.class) @@ -47,12 +46,9 @@ protected ConfigMap desired(OrderedManagedDependentCustomResource primary, } public static class CM1ResourceDiscriminator - extends ResourceListDiscriminator { - @Override - protected Optional distinguish(OrderedManagedDependentCustomResource primary, - Set resourceList) { - return resourceList.stream().filter(cm -> cm.getMetadata().getName() - .equals(primary.getMetadata().getName() + "1")).findFirst(); + extends ResourceIDMatcherDiscriminator { + public CM1ResourceDiscriminator() { + super(p -> new ResourceID(p.getMetadata().getName() + "1", p.getMetadata().getNamespace())); } } diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/orderedmanageddependent/ConfigMapDependentResource2.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/orderedmanageddependent/ConfigMapDependentResource2.java index 6ad23d0ae9..2b17d615b9 100644 --- a/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/orderedmanageddependent/ConfigMapDependentResource2.java +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/orderedmanageddependent/ConfigMapDependentResource2.java @@ -2,16 +2,15 @@ import java.util.HashMap; import java.util.Map; -import java.util.Optional; -import java.util.Set; import io.fabric8.kubernetes.api.model.ConfigMap; import io.fabric8.kubernetes.api.model.ObjectMeta; import io.javaoperatorsdk.operator.api.reconciler.Context; -import io.javaoperatorsdk.operator.api.reconciler.ResourceListDiscriminator; +import io.javaoperatorsdk.operator.api.reconciler.ResourceIDMatcherDiscriminator; import io.javaoperatorsdk.operator.api.reconciler.dependent.ReconcileResult; import io.javaoperatorsdk.operator.processing.dependent.kubernetes.CRUDKubernetesDependentResource; import io.javaoperatorsdk.operator.processing.dependent.kubernetes.KubernetesDependent; +import io.javaoperatorsdk.operator.processing.event.ResourceID; @KubernetesDependent(labelSelector = "dependent = cm2", resourceDiscriminator = ConfigMapDependentResource2.CM2ResourceDiscriminator.class) @@ -47,12 +46,9 @@ protected ConfigMap desired(OrderedManagedDependentCustomResource primary, } public static class CM2ResourceDiscriminator - extends ResourceListDiscriminator { - @Override - protected Optional distinguish(OrderedManagedDependentCustomResource primary, - Set resourceList) { - return resourceList.stream().filter(cm -> cm.getMetadata().getName() - .equals(primary.getMetadata().getName() + "2")).findFirst(); + extends ResourceIDMatcherDiscriminator { + public CM2ResourceDiscriminator() { + super(p -> new ResourceID(p.getMetadata().getName() + "2", p.getMetadata().getNamespace())); } } From d9d4a95a38a99addb5a3f5282c55e00065a6fdc0 Mon Sep 17 00:00:00 2001 From: csviri Date: Fri, 29 Jul 2022 13:48:16 +0200 Subject: [PATCH 07/67] index based discriminator --- .../api/reconciler/ResourceDiscriminator.java | 2 +- .../ResourceIDMatcherDiscriminator.java | 2 +- .../reconciler/VoidResourceDiscriminator.java | 2 +- .../dependent/AbstractDependentResource.java | 5 +- .../KubernetesDependentResource.java | 18 +-- .../operator/IndexDiscriminatorIT.java | 77 +++++++++++ .../IndexDiscriminator.java | 42 ++++++ .../IndexDiscriminatorTestCustomResource.java | 16 +++ .../IndexDiscriminatorTestDRConfigMap.java | 38 ++++++ .../IndexDiscriminatorTestReconciler.java | 120 ++++++++++++++++++ .../IndexDiscriminatorTestSpec.java | 15 +++ .../IndexDiscriminatorTestStatus.java | 5 + .../MultipleDependentResourceConfigMap.java | 1 - .../MultipleDependentResourceReconciler.java | 7 - 14 files changed, 329 insertions(+), 21 deletions(-) create mode 100644 operator-framework/src/test/java/io/javaoperatorsdk/operator/IndexDiscriminatorIT.java create mode 100644 operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/indexdiscriminator/IndexDiscriminator.java create mode 100644 operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/indexdiscriminator/IndexDiscriminatorTestCustomResource.java create mode 100644 operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/indexdiscriminator/IndexDiscriminatorTestDRConfigMap.java create mode 100644 operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/indexdiscriminator/IndexDiscriminatorTestReconciler.java create mode 100644 operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/indexdiscriminator/IndexDiscriminatorTestSpec.java create mode 100644 operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/indexdiscriminator/IndexDiscriminatorTestStatus.java diff --git a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/reconciler/ResourceDiscriminator.java b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/reconciler/ResourceDiscriminator.java index 2fe69c3804..0505fd0b57 100644 --- a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/reconciler/ResourceDiscriminator.java +++ b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/reconciler/ResourceDiscriminator.java @@ -9,6 +9,6 @@ public interface ResourceDiscriminator { Optional distinguish(Class resource, P primary, Context

context, - EventSourceRetriever

eventSourceManager); + EventSourceRetriever

eventSourceRetriever); } diff --git a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/reconciler/ResourceIDMatcherDiscriminator.java b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/reconciler/ResourceIDMatcherDiscriminator.java index 0d06222e45..3df1956bf9 100644 --- a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/reconciler/ResourceIDMatcherDiscriminator.java +++ b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/reconciler/ResourceIDMatcherDiscriminator.java @@ -18,7 +18,7 @@ public ResourceIDMatcherDiscriminator(Function mapper) { @Override public Optional distinguish(Class resource, P primary, Context

context, - EventSourceRetriever

eventSourceManager) { + EventSourceRetriever

eventSourceRetriever) { var resourceID = mapper.apply(primary); return context.getSecondaryResources(resource).stream() .filter(r -> r.getMetadata().getName() diff --git a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/reconciler/VoidResourceDiscriminator.java b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/reconciler/VoidResourceDiscriminator.java index 3a627df109..a02c0c6a95 100644 --- a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/reconciler/VoidResourceDiscriminator.java +++ b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/reconciler/VoidResourceDiscriminator.java @@ -10,7 +10,7 @@ public class VoidResourceDiscriminator @Override public Optional distinguish(Class resource, P primary, Context

context, - EventSourceRetriever

eventSourceManager) { + EventSourceRetriever

eventSourceRetriever) { throw new UnsupportedOperationException(); } } 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 130b6237e3..5ecd52cc35 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 @@ -134,11 +134,12 @@ protected R desired(P primary, Context

context) { "desired method must be implemented if this DependentResource can be created and/or updated"); } - protected ResourceDiscriminator getResourceDiscriminator() { + // todo review & refactor configuration to cover all cases + public ResourceDiscriminator getResourceDiscriminator() { return resourceDiscriminator; } - protected AbstractDependentResource setResourceDiscriminator( + public AbstractDependentResource setResourceDiscriminator( ResourceDiscriminator resourceDiscriminator) { this.resourceDiscriminator = resourceDiscriminator; return this; 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 e6d358ce12..5c8e7f35f5 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 @@ -67,13 +67,15 @@ private void configureWith(String labelSelector, Set namespaces, namespaces = context.getControllerConfiguration().getNamespaces(); } - var ic = InformerConfiguration.from(resourceType()) - .withLabelSelector(labelSelector) - .withSecondaryToPrimaryMapper(getSecondaryToPrimaryMapper()) - .withNamespaces(namespaces, inheritNamespacesOnChange) - .build(); - - configureWith(new InformerEventSource<>(ic, context)); + if (eventSource() == null) { + var ic = InformerConfiguration.from(resourceType()) + .withLabelSelector(labelSelector) + .withSecondaryToPrimaryMapper(getSecondaryToPrimaryMapper()) + .withNamespaces(namespaces, inheritNamespacesOnChange) + .build(); + + configureWith(new InformerEventSource<>(ic, context)); + } } @SuppressWarnings("unchecked") @@ -137,7 +139,7 @@ public Result match(R actualResource, P primary, Context

context) { } public void delete(P primary, Context

context) { - var resource = context.getSecondaryResource(resourceType()); + var resource = getSecondaryResource(primary, context); resource.ifPresent(r -> client.resource(r).delete()); } diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/IndexDiscriminatorIT.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/IndexDiscriminatorIT.java new file mode 100644 index 0000000000..fe5b63de8a --- /dev/null +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/IndexDiscriminatorIT.java @@ -0,0 +1,77 @@ +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.indexdiscriminator.IndexDiscriminatorTestCustomResource; +import io.javaoperatorsdk.operator.sample.indexdiscriminator.IndexDiscriminatorTestReconciler; +import io.javaoperatorsdk.operator.sample.indexdiscriminator.IndexDiscriminatorTestSpec; + +import static io.javaoperatorsdk.operator.sample.indexdiscriminator.IndexDiscriminatorTestDRConfigMap.DATA_KEY; +import static io.javaoperatorsdk.operator.sample.indexdiscriminator.IndexDiscriminatorTestReconciler.FIRST_CONFIG_MAP_SUFFIX_1; +import static io.javaoperatorsdk.operator.sample.indexdiscriminator.IndexDiscriminatorTestReconciler.FIRST_CONFIG_MAP_SUFFIX_2; +import static org.assertj.core.api.Assertions.assertThat; +import static org.awaitility.Awaitility.await; + +class IndexDiscriminatorIT { + + public static final String TEST_RESOURCE_1 = "test1"; + public static final String CHANGED_SPEC_VALUE = "otherValue"; + @RegisterExtension + LocallyRunOperatorExtension operator = + LocallyRunOperatorExtension.builder().withReconciler(IndexDiscriminatorTestReconciler.class) + .build(); + + @Test + void resourcesFoundAndReconciled() { + var res = operator.create(createTestCustomResource()); + var reconciler = operator.getReconcilerOfType(IndexDiscriminatorTestReconciler.class); + + await().untilAsserted(() -> { + assertThat(reconciler.getNumberOfExecutions()).isEqualTo(1); + assertThat(operator.get(ConfigMap.class, TEST_RESOURCE_1 + FIRST_CONFIG_MAP_SUFFIX_1)) + .isNotNull(); + assertThat(operator.get(ConfigMap.class, TEST_RESOURCE_1 + FIRST_CONFIG_MAP_SUFFIX_2)) + .isNotNull(); + }); + + res.getSpec().setValue(CHANGED_SPEC_VALUE); + res = operator.replace(res); + + await().untilAsserted(() -> { + assertThat(reconciler.getNumberOfExecutions()).isEqualTo(2); + var cm1 = operator.get(ConfigMap.class, TEST_RESOURCE_1 + FIRST_CONFIG_MAP_SUFFIX_1); + var cm2 = operator.get(ConfigMap.class, TEST_RESOURCE_1 + FIRST_CONFIG_MAP_SUFFIX_2); + assertThat(cm1).isNotNull(); + assertThat(cm2).isNotNull(); + assertThat(cm1.getData().get(DATA_KEY)).isEqualTo(CHANGED_SPEC_VALUE); + assertThat(cm2.getData().get(DATA_KEY)).isEqualTo(CHANGED_SPEC_VALUE); + }); + + operator.delete(res); + + await().untilAsserted(() -> { + var cm1 = operator.get(ConfigMap.class, TEST_RESOURCE_1 + FIRST_CONFIG_MAP_SUFFIX_1); + var cm2 = operator.get(ConfigMap.class, TEST_RESOURCE_1 + FIRST_CONFIG_MAP_SUFFIX_2); + assertThat(cm1).isNull(); + assertThat(cm2).isNull(); + }); + } + + public IndexDiscriminatorTestCustomResource createTestCustomResource() { + IndexDiscriminatorTestCustomResource resource = + new IndexDiscriminatorTestCustomResource(); + resource.setMetadata( + new ObjectMetaBuilder() + .withName(TEST_RESOURCE_1) + .withNamespace(operator.getNamespace()) + .build()); + resource.setSpec(new IndexDiscriminatorTestSpec()); + resource.getSpec().setValue("default"); + return resource; + } + +} diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/indexdiscriminator/IndexDiscriminator.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/indexdiscriminator/IndexDiscriminator.java new file mode 100644 index 0000000000..6cf72519a1 --- /dev/null +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/indexdiscriminator/IndexDiscriminator.java @@ -0,0 +1,42 @@ +package io.javaoperatorsdk.operator.sample.indexdiscriminator; + +import java.util.Optional; + +import io.fabric8.kubernetes.api.model.ConfigMap; +import io.javaoperatorsdk.operator.api.reconciler.Context; +import io.javaoperatorsdk.operator.api.reconciler.ResourceDiscriminator; +import io.javaoperatorsdk.operator.processing.event.EventSourceRetriever; +import io.javaoperatorsdk.operator.processing.event.source.informer.InformerEventSource; + +import static io.javaoperatorsdk.operator.sample.indexdiscriminator.IndexDiscriminatorTestReconciler.configMapKeyFromPrimary; + +public class IndexDiscriminator + implements ResourceDiscriminator { + + private final String indexName; + private final String nameSuffix; + + public IndexDiscriminator(String indexName, String nameSuffix) { + this.indexName = indexName; + this.nameSuffix = nameSuffix; + } + + @Override + public Optional distinguish(Class resource, + IndexDiscriminatorTestCustomResource primary, + Context context, + EventSourceRetriever eventSourceRetriever) { + + InformerEventSource eventSource = + (InformerEventSource) eventSourceRetriever + .getResourceEventSourceFor(ConfigMap.class); + var resources = eventSource.byIndex(indexName, configMapKeyFromPrimary(primary, nameSuffix)); + if (resources.isEmpty()) { + return Optional.empty(); + } else if (resources.size() > 1) { + throw new IllegalStateException("more than one resource"); + } else { + return Optional.of(resources.get(0)); + } + } +} diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/indexdiscriminator/IndexDiscriminatorTestCustomResource.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/indexdiscriminator/IndexDiscriminatorTestCustomResource.java new file mode 100644 index 0000000000..729b1d80eb --- /dev/null +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/indexdiscriminator/IndexDiscriminatorTestCustomResource.java @@ -0,0 +1,16 @@ +package io.javaoperatorsdk.operator.sample.indexdiscriminator; + +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("idt") +public class IndexDiscriminatorTestCustomResource + extends CustomResource + implements Namespaced { + +} diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/indexdiscriminator/IndexDiscriminatorTestDRConfigMap.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/indexdiscriminator/IndexDiscriminatorTestDRConfigMap.java new file mode 100644 index 0000000000..88dc40f55c --- /dev/null +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/indexdiscriminator/IndexDiscriminatorTestDRConfigMap.java @@ -0,0 +1,38 @@ +package io.javaoperatorsdk.operator.sample.indexdiscriminator; + +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.CRUDNoGCKubernetesDependentResource; +import io.javaoperatorsdk.operator.processing.dependent.kubernetes.KubernetesDependent; + +@KubernetesDependent +public class IndexDiscriminatorTestDRConfigMap + extends CRUDNoGCKubernetesDependentResource { + + public static final String DATA_KEY = "key"; + private final String suffix; + + public IndexDiscriminatorTestDRConfigMap(String value) { + super(ConfigMap.class); + this.suffix = value; + } + + @Override + protected ConfigMap desired(IndexDiscriminatorTestCustomResource primary, + Context context) { + Map data = new HashMap<>(); + data.put(DATA_KEY, primary.getSpec().getValue()); + + return new ConfigMapBuilder() + .withNewMetadata() + .withName(primary.getMetadata().getName() + suffix) + .withNamespace(primary.getMetadata().getNamespace()) + .endMetadata() + .withData(data) + .build(); + } +} diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/indexdiscriminator/IndexDiscriminatorTestReconciler.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/indexdiscriminator/IndexDiscriminatorTestReconciler.java new file mode 100644 index 0000000000..0b0af2a1cc --- /dev/null +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/indexdiscriminator/IndexDiscriminatorTestReconciler.java @@ -0,0 +1,120 @@ +package io.javaoperatorsdk.operator.sample.indexdiscriminator; + +import java.util.Collections; +import java.util.List; +import java.util.Map; +import java.util.concurrent.atomic.AtomicInteger; + +import io.fabric8.kubernetes.api.model.ConfigMap; +import io.fabric8.kubernetes.client.KubernetesClient; +import io.javaoperatorsdk.operator.api.config.informer.InformerConfiguration; +import io.javaoperatorsdk.operator.api.reconciler.*; +import io.javaoperatorsdk.operator.junit.KubernetesClientAware; +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 IndexDiscriminatorTestReconciler + implements Reconciler, + Cleaner, + TestExecutionInfoProvider, EventSourceInitializer, + KubernetesClientAware { + + public static final String FIRST_CONFIG_MAP_SUFFIX_1 = "-1"; + public static final String FIRST_CONFIG_MAP_SUFFIX_2 = "-2"; + public static final String CONFIG_MAP_INDEX_1 = "CONFIG_MAP_INDEX1"; + public static final String CONFIG_MAP_INDEX_2 = "CONFIG_MAP_INDEX2"; + + private final AtomicInteger numberOfExecutions = new AtomicInteger(0); + + private final IndexDiscriminatorTestDRConfigMap firstDependentResourceConfigMap; + private final IndexDiscriminatorTestDRConfigMap secondDependentResourceConfigMap; + private KubernetesClient client; + + public IndexDiscriminatorTestReconciler() { + firstDependentResourceConfigMap = + new IndexDiscriminatorTestDRConfigMap(FIRST_CONFIG_MAP_SUFFIX_1); + secondDependentResourceConfigMap = + new IndexDiscriminatorTestDRConfigMap(FIRST_CONFIG_MAP_SUFFIX_2); + } + + @Override + public UpdateControl reconcile( + IndexDiscriminatorTestCustomResource 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); + + eventSource.addIndexer(CONFIG_MAP_INDEX_1, cm -> { + if (cm.getMetadata().getName().endsWith(FIRST_CONFIG_MAP_SUFFIX_1)) { + return List.of(configMapKey(cm)); + } else { + return Collections.emptyList(); + } + }); + eventSource.addIndexer(CONFIG_MAP_INDEX_2, cm -> { + if (cm.getMetadata().getName().endsWith(FIRST_CONFIG_MAP_SUFFIX_2)) { + return List.of(configMapKey(cm)); + } else { + return Collections.emptyList(); + } + }); + + firstDependentResourceConfigMap.configureWith(eventSource); + secondDependentResourceConfigMap.configureWith(eventSource); + + firstDependentResourceConfigMap + .setResourceDiscriminator( + new IndexDiscriminator(CONFIG_MAP_INDEX_1, FIRST_CONFIG_MAP_SUFFIX_1)); + secondDependentResourceConfigMap + .setResourceDiscriminator( + new IndexDiscriminator(CONFIG_MAP_INDEX_2, FIRST_CONFIG_MAP_SUFFIX_2)); + return EventSourceInitializer.nameEventSources(eventSource); + } + + @Override + public KubernetesClient getKubernetesClient() { + return client; + } + + @Override + public void setKubernetesClient(KubernetesClient kubernetesClient) { + this.client = kubernetesClient; + firstDependentResourceConfigMap.setKubernetesClient(kubernetesClient); + secondDependentResourceConfigMap.setKubernetesClient(kubernetesClient); + } + + public static String configMapKey(ConfigMap configMap) { + return configMap.getMetadata().getName() + "#" + configMap.getMetadata().getNamespace(); + } + + public static String configMapKeyFromPrimary(IndexDiscriminatorTestCustomResource primary, + String nameSuffix) { + return primary.getMetadata().getName() + nameSuffix + "#" + + primary.getMetadata().getNamespace(); + } + + @Override + public DeleteControl cleanup(IndexDiscriminatorTestCustomResource resource, + Context context) { + firstDependentResourceConfigMap.delete(resource, context); + secondDependentResourceConfigMap.delete(resource, context); + return DeleteControl.defaultDelete(); + } +} diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/indexdiscriminator/IndexDiscriminatorTestSpec.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/indexdiscriminator/IndexDiscriminatorTestSpec.java new file mode 100644 index 0000000000..fcedd48abe --- /dev/null +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/indexdiscriminator/IndexDiscriminatorTestSpec.java @@ -0,0 +1,15 @@ +package io.javaoperatorsdk.operator.sample.indexdiscriminator; + +public class IndexDiscriminatorTestSpec { + + private String value; + + public String getValue() { + return value; + } + + public IndexDiscriminatorTestSpec setValue(String value) { + this.value = value; + return this; + } +} diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/indexdiscriminator/IndexDiscriminatorTestStatus.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/indexdiscriminator/IndexDiscriminatorTestStatus.java new file mode 100644 index 0000000000..d31c86e8de --- /dev/null +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/indexdiscriminator/IndexDiscriminatorTestStatus.java @@ -0,0 +1,5 @@ +package io.javaoperatorsdk.operator.sample.indexdiscriminator; + +public class IndexDiscriminatorTestStatus { + +} 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 4cdc2e457d..1adbfb9f95 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 @@ -31,7 +31,6 @@ protected ConfigMap desired(MultipleDependentResourceCustomResource primary, .withNewMetadata() .withName(primary.getConfigMapName(value)) .withNamespace(primary.getMetadata().getNamespace()) - .withLabels(Map.of(MultipleDependentResourceReconciler.LABEL, String.valueOf(value))) .endMetadata() .withData(data) .build(); 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 69c1ba0b04..2891129a93 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 @@ -20,7 +20,6 @@ public class MultipleDependentResourceReconciler public static final int FIRST_CONFIG_MAP_ID = 1; public static final int SECOND_CONFIG_MAP_ID = 2; - public static final String LABEL = "multipledependentresource"; private final AtomicInteger numberOfExecutions = new AtomicInteger(0); private final MultipleDependentResourceConfigMap firstDependentResourceConfigMap; @@ -34,7 +33,6 @@ public MultipleDependentResourceReconciler() { firstDependentResourceConfigMap.configureWith( new KubernetesDependentResourceConfig() - .setLabelSelector(getLabelSelector(FIRST_CONFIG_MAP_ID)) .setResourceDiscriminator( new ResourceIDMatcherDiscriminator( p -> new ResourceID(p.getConfigMapName(FIRST_CONFIG_MAP_ID), @@ -42,17 +40,12 @@ public MultipleDependentResourceReconciler() { secondDependentResourceConfigMap.configureWith( new KubernetesDependentResourceConfig() - .setLabelSelector(getLabelSelector(SECOND_CONFIG_MAP_ID)) .setResourceDiscriminator( new ResourceIDMatcherDiscriminator( p -> new ResourceID(p.getConfigMapName(SECOND_CONFIG_MAP_ID), p.getMetadata().getNamespace())))); } - private String getLabelSelector(int resourceId) { - return LABEL + "=" + resourceId; - } - @Override public UpdateControl reconcile( MultipleDependentResourceCustomResource resource, From 924f162082a26e27771d455ad946cc90d8abc402 Mon Sep 17 00:00:00 2001 From: csviri Date: Fri, 29 Jul 2022 14:13:03 +0200 Subject: [PATCH 08/67] IT fix --- .../KubernetesDependentResource.java | 17 +++++---- .../MultipleDependentResourceReconciler.java | 36 ++++++++++--------- 2 files changed, 27 insertions(+), 26 deletions(-) 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 5c8e7f35f5..d9eae04da8 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 @@ -67,15 +67,14 @@ private void configureWith(String labelSelector, Set namespaces, namespaces = context.getControllerConfiguration().getNamespaces(); } - if (eventSource() == null) { - var ic = InformerConfiguration.from(resourceType()) - .withLabelSelector(labelSelector) - .withSecondaryToPrimaryMapper(getSecondaryToPrimaryMapper()) - .withNamespaces(namespaces, inheritNamespacesOnChange) - .build(); - - configureWith(new InformerEventSource<>(ic, context)); - } + var ic = InformerConfiguration.from(resourceType()) + .withLabelSelector(labelSelector) + .withSecondaryToPrimaryMapper(getSecondaryToPrimaryMapper()) + .withNamespaces(namespaces, inheritNamespacesOnChange) + .build(); + + configureWith(new InformerEventSource<>(ic, context)); + } @SuppressWarnings("unchecked") 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 2891129a93..49f5ee64c1 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 @@ -5,11 +5,12 @@ import io.fabric8.kubernetes.api.model.ConfigMap; import io.fabric8.kubernetes.client.KubernetesClient; +import io.javaoperatorsdk.operator.api.config.informer.InformerConfiguration; import io.javaoperatorsdk.operator.api.reconciler.*; import io.javaoperatorsdk.operator.junit.KubernetesClientAware; -import io.javaoperatorsdk.operator.processing.dependent.kubernetes.KubernetesDependentResourceConfig; 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 @@ -31,19 +32,16 @@ public MultipleDependentResourceReconciler() { secondDependentResourceConfigMap = new MultipleDependentResourceConfigMap(SECOND_CONFIG_MAP_ID); - firstDependentResourceConfigMap.configureWith( - new KubernetesDependentResourceConfig() - .setResourceDiscriminator( - new ResourceIDMatcherDiscriminator( - p -> new ResourceID(p.getConfigMapName(FIRST_CONFIG_MAP_ID), - p.getMetadata().getNamespace())))); - - secondDependentResourceConfigMap.configureWith( - new KubernetesDependentResourceConfig() - .setResourceDiscriminator( - new ResourceIDMatcherDiscriminator( - p -> new ResourceID(p.getConfigMapName(SECOND_CONFIG_MAP_ID), - p.getMetadata().getNamespace())))); + 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 @@ -64,9 +62,13 @@ public int getNumberOfExecutions() { @Override public Map prepareEventSources( EventSourceContext context) { - return EventSourceInitializer.nameEventSources( - firstDependentResourceConfigMap.initEventSource(context), - secondDependentResourceConfigMap.initEventSource(context)); + InformerEventSource eventSource = + new InformerEventSource<>(InformerConfiguration.from(ConfigMap.class, context) + .build(), context); + firstDependentResourceConfigMap.configureWith(eventSource); + secondDependentResourceConfigMap.configureWith(eventSource); + + return EventSourceInitializer.nameEventSources(eventSource); } @Override From 7eb4eb73501e70a44e8b2017455b697a401e8332 Mon Sep 17 00:00:00 2001 From: csviri Date: Fri, 29 Jul 2022 16:03:01 +0200 Subject: [PATCH 09/67] wip --- .../operator/api/reconciler/ResourceDiscriminator.java | 1 + 1 file changed, 1 insertion(+) diff --git a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/reconciler/ResourceDiscriminator.java b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/reconciler/ResourceDiscriminator.java index 0505fd0b57..97130a27d5 100644 --- a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/reconciler/ResourceDiscriminator.java +++ b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/reconciler/ResourceDiscriminator.java @@ -6,6 +6,7 @@ import io.javaoperatorsdk.operator.processing.event.EventSourceRetriever; // todo is discriminator a good name? it not just discriminates but also reads from cache +// todo discuss a List version of this (for reconciler but also for batch processing?) public interface ResourceDiscriminator { Optional distinguish(Class resource, P primary, Context

context, From 1f6b8b957ad52249547acf44c44a939123bd8d62 Mon Sep 17 00:00:00 2001 From: csviri Date: Fri, 26 Aug 2022 10:15:34 +0200 Subject: [PATCH 10/67] fixes from rebase from next --- .../config/AnnotationControllerConfiguration.java | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/config/AnnotationControllerConfiguration.java b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/config/AnnotationControllerConfiguration.java index 59dc80869a..2f1e6149df 100644 --- a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/config/AnnotationControllerConfiguration.java +++ b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/config/AnnotationControllerConfiguration.java @@ -24,6 +24,7 @@ import io.javaoperatorsdk.operator.processing.dependent.kubernetes.KubernetesDependent; import io.javaoperatorsdk.operator.processing.dependent.kubernetes.KubernetesDependentResource; import io.javaoperatorsdk.operator.processing.dependent.kubernetes.KubernetesDependentResourceConfig; +import io.javaoperatorsdk.operator.processing.dependent.workflow.Condition; import io.javaoperatorsdk.operator.processing.event.rate.RateLimiter; import io.javaoperatorsdk.operator.processing.event.source.controller.ResourceEventFilter; import io.javaoperatorsdk.operator.processing.event.source.controller.ResourceEventFilters; @@ -311,8 +312,8 @@ private Object createKubernetesResourceConfig(Class createFilter(kubeDependent.genericFilter(), GenericFilter.class, context) .orElse(null); - resourceDiscriminator = instantiateIfNotVoid(kubeDependent.resourceDiscriminator(), - VoidResourceDiscriminator.class); + resourceDiscriminator = + instantiateDiscriminatorIfNotVoid(kubeDependent.resourceDiscriminator()); } config = @@ -323,6 +324,15 @@ private Object createKubernetesResourceConfig(Class return config; } + @SuppressWarnings({"unchecked"}) + private ResourceDiscriminator instantiateDiscriminatorIfNotVoid( + Class discriminator) { + if (discriminator != VoidResourceDiscriminator.class) { + return instantiateAndConfigureIfNeeded(discriminator, ResourceDiscriminator.class); + } + return null; + } + public static T valueOrDefault( ControllerConfiguration controllerConfiguration, Function mapper, From c3595570404bb33a6df41a2c135d89e48a085ddd Mon Sep 17 00:00:00 2001 From: csviri Date: Mon, 5 Sep 2022 15:04:49 +0200 Subject: [PATCH 11/67] fix after rebase --- .../AnnotationControllerConfiguration.java | 8 ++-- .../workflow/WorkflowCleanupExecutor.java | 5 +- .../workflow/WorkflowReconcileExecutor.java | 12 +++-- .../workflow/WorkflowCleanupExecutorTest.java | 11 +++-- .../WorkflowReconcileExecutorTest.java | 46 ++++++++++--------- 5 files changed, 49 insertions(+), 33 deletions(-) diff --git a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/config/AnnotationControllerConfiguration.java b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/config/AnnotationControllerConfiguration.java index 2f1e6149df..1475cf58bc 100644 --- a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/config/AnnotationControllerConfiguration.java +++ b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/config/AnnotationControllerConfiguration.java @@ -45,13 +45,14 @@ public class AnnotationControllerConfiguration

private static final String KUBE_DEPENDENT_NAME = KubernetesDependent.class.getSimpleName(); protected final Reconciler

reconciler; - private final ControllerConfiguration annotation; + private final io.javaoperatorsdk.operator.api.reconciler.ControllerConfiguration annotation; private List specs; private Class

resourceClass; public AnnotationControllerConfiguration(Reconciler

reconciler) { this.reconciler = reconciler; - this.annotation = reconciler.getClass().getAnnotation(ControllerConfiguration.class); + this.annotation = reconciler.getClass() + .getAnnotation(io.javaoperatorsdk.operator.api.reconciler.ControllerConfiguration.class); if (annotation == null) { throw new OperatorException( "Missing mandatory @" + ControllerConfiguration.class.getSimpleName() + @@ -328,7 +329,8 @@ private Object createKubernetesResourceConfig(Class private ResourceDiscriminator instantiateDiscriminatorIfNotVoid( Class discriminator) { if (discriminator != VoidResourceDiscriminator.class) { - return instantiateAndConfigureIfNeeded(discriminator, ResourceDiscriminator.class); + return instantiateAndConfigureIfNeeded(discriminator, ResourceDiscriminator.class, + CONTROLLER_CONFIG_ANNOTATION); } return null; } diff --git a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/workflow/WorkflowCleanupExecutor.java b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/workflow/WorkflowCleanupExecutor.java index 98c6789869..a3c6319511 100644 --- a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/workflow/WorkflowCleanupExecutor.java +++ b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/workflow/WorkflowCleanupExecutor.java @@ -106,7 +106,10 @@ public void run() { } boolean deletePostConditionMet = deletePostCondition.map(c -> c.isMet(primary, - dependentResourceNode.getDependentResource().getSecondaryResource(primary) + // todo pass also discriminator + context + .getSecondaryResource( + dependentResourceNode.getDependentResource().resourceType()) .orElse(null), context)).orElse(true); diff --git a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/workflow/WorkflowReconcileExecutor.java b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/workflow/WorkflowReconcileExecutor.java index 14028cb980..6b2f7fb82b 100644 --- a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/workflow/WorkflowReconcileExecutor.java +++ b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/workflow/WorkflowReconcileExecutor.java @@ -86,7 +86,9 @@ private synchronized void handleReconcile(DependentResourceNode depend boolean reconcileConditionMet = dependentResourceNode.getReconcilePrecondition() .map(rc -> rc.isMet(primary, - dependentResourceNode.getDependentResource().getSecondaryResource(primary).orElse(null), + context + .getSecondaryResource(dependentResourceNode.getDependentResource().resourceType()) + .orElse(null), context)) .orElse(true); @@ -169,7 +171,9 @@ public void run() { reconciled.add(dependentResourceNode); boolean ready = dependentResourceNode.getReadyPostcondition() .map(rc -> rc.isMet(primary, - dependentResourceNode.getDependentResource().getSecondaryResource(primary) + context + .getSecondaryResource( + dependentResourceNode.getDependentResource().resourceType()) .orElse(null), context)) .orElse(true); @@ -210,8 +214,8 @@ public void run() { } boolean deletePostConditionMet = deletePostCondition.map(c -> c.isMet(primary, - dependentResourceNode.getDependentResource().getSecondaryResource(primary) - .orElse(null), + context.getSecondaryResource( + dependentResourceNode.getDependentResource().resourceType()).orElse(null), context)).orElse(true); if (deletePostConditionMet) { alreadyVisited.add(dependentResourceNode); diff --git a/operator-framework-core/src/test/java/io/javaoperatorsdk/operator/processing/dependent/workflow/WorkflowCleanupExecutorTest.java b/operator-framework-core/src/test/java/io/javaoperatorsdk/operator/processing/dependent/workflow/WorkflowCleanupExecutorTest.java index 7c1c5d6ff6..56bd876687 100644 --- a/operator-framework-core/src/test/java/io/javaoperatorsdk/operator/processing/dependent/workflow/WorkflowCleanupExecutorTest.java +++ b/operator-framework-core/src/test/java/io/javaoperatorsdk/operator/processing/dependent/workflow/WorkflowCleanupExecutorTest.java @@ -4,17 +4,20 @@ import org.junit.jupiter.api.Test; import io.javaoperatorsdk.operator.AggregatedOperatorException; +import io.javaoperatorsdk.operator.api.reconciler.Context; import io.javaoperatorsdk.operator.processing.dependent.workflow.builder.WorkflowBuilder; import io.javaoperatorsdk.operator.sample.simple.TestCustomResource; import static io.javaoperatorsdk.operator.processing.dependent.workflow.ExecutionAssert.assertThat; import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.mockito.Mockito.mock; class WorkflowCleanupExecutorTest extends AbstractWorkflowExecutorTest { protected TestDeleterDependent dd1 = new TestDeleterDependent("DR_DELETER_1"); protected TestDeleterDependent dd2 = new TestDeleterDependent("DR_DELETER_2"); protected TestDeleterDependent dd3 = new TestDeleterDependent("DR_DELETER_3"); + Context mockContext = mock(Context.class); @Test void cleanUpDiamondWorkflow() { @@ -45,7 +48,7 @@ void dontDeleteIfDependentErrored() { .withThrowExceptionFurther(false) .build(); - var res = workflow.cleanup(new TestCustomResource(), null); + var res = workflow.cleanup(new TestCustomResource(), mockContext); assertThrows(AggregatedOperatorException.class, res::throwAggregateExceptionIfErrorsPresent); @@ -64,7 +67,7 @@ void cleanupConditionTrivialCase() { .addDependentResource(dd2).dependsOn(dd1).withDeletePostcondition(noMetDeletePostCondition) .build(); - var res = workflow.cleanup(new TestCustomResource(), null); + var res = workflow.cleanup(new TestCustomResource(), mockContext); assertThat(executionHistory).deleted(dd2).notReconciled(dd1); Assertions.assertThat(res.getDeleteCalledOnDependents()).containsExactlyInAnyOrder(dd2); @@ -79,7 +82,7 @@ void cleanupConditionMet() { .addDependentResource(dd2).dependsOn(dd1).withDeletePostcondition(metDeletePostCondition) .build(); - var res = workflow.cleanup(new TestCustomResource(), null); + var res = workflow.cleanup(new TestCustomResource(), mockContext); assertThat(executionHistory).deleted(dd2, dd1); @@ -99,7 +102,7 @@ void cleanupConditionDiamondWorkflow() { .addDependentResource(dd4).dependsOn(dd2, dd3) .build(); - var res = workflow.cleanup(new TestCustomResource(), null); + var res = workflow.cleanup(new TestCustomResource(), mockContext); assertThat(executionHistory) .reconciledInOrder(dd4, dd2) diff --git a/operator-framework-core/src/test/java/io/javaoperatorsdk/operator/processing/dependent/workflow/WorkflowReconcileExecutorTest.java b/operator-framework-core/src/test/java/io/javaoperatorsdk/operator/processing/dependent/workflow/WorkflowReconcileExecutorTest.java index fa9d757b67..873cf66cb6 100644 --- a/operator-framework-core/src/test/java/io/javaoperatorsdk/operator/processing/dependent/workflow/WorkflowReconcileExecutorTest.java +++ b/operator-framework-core/src/test/java/io/javaoperatorsdk/operator/processing/dependent/workflow/WorkflowReconcileExecutorTest.java @@ -4,11 +4,13 @@ import org.junit.jupiter.api.Test; import io.javaoperatorsdk.operator.AggregatedOperatorException; +import io.javaoperatorsdk.operator.api.reconciler.Context; import io.javaoperatorsdk.operator.processing.dependent.workflow.builder.WorkflowBuilder; import io.javaoperatorsdk.operator.sample.simple.TestCustomResource; import static io.javaoperatorsdk.operator.processing.dependent.workflow.ExecutionAssert.assertThat; import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.mockito.Mockito.mock; @SuppressWarnings("rawtypes") class WorkflowReconcileExecutorTest extends AbstractWorkflowExecutorTest { @@ -21,6 +23,8 @@ class WorkflowReconcileExecutorTest extends AbstractWorkflowExecutorTest { private final Condition notMetReadyCondition = (primary, secondary, context) -> false; + Context mockContext = mock(Context.class); + @Test void reconcileTopLevelResources() { var workflow = new WorkflowBuilder() @@ -28,7 +32,7 @@ void reconcileTopLevelResources() { .addDependentResource(dr2) .build(); - var res = workflow.reconcile(new TestCustomResource(), null); + var res = workflow.reconcile(new TestCustomResource(), mockContext); assertThat(executionHistory).reconciled(dr1, dr2); Assertions.assertThat(res.getErroredDependents()).isEmpty(); @@ -42,7 +46,7 @@ void reconciliationWithSimpleDependsOn() { .addDependentResource(dr2).dependsOn(dr1) .build(); - var res = workflow.reconcile(new TestCustomResource(), null); + var res = workflow.reconcile(new TestCustomResource(), mockContext); Assertions.assertThat(res.getErroredDependents()).isEmpty(); assertThat(executionHistory).reconciledInOrder(dr1, dr2); @@ -61,7 +65,7 @@ void reconciliationWithTwoTheDependsOns() { .addDependentResource(dr3).dependsOn(dr1) .build(); - var res = workflow.reconcile(new TestCustomResource(), null); + var res = workflow.reconcile(new TestCustomResource(), mockContext); Assertions.assertThat(res.getErroredDependents()).isEmpty(); assertThat(executionHistory) @@ -83,7 +87,7 @@ void diamondShareWorkflowReconcile() { .addDependentResource(dr4).dependsOn(dr3).dependsOn(dr2) .build(); - var res = workflow.reconcile(new TestCustomResource(), null); + var res = workflow.reconcile(new TestCustomResource(), mockContext); Assertions.assertThat(res.getErroredDependents()).isEmpty(); assertThat(executionHistory) @@ -103,7 +107,7 @@ void exceptionHandlingSimpleCases() { .withThrowExceptionFurther(false) .build(); - var res = workflow.reconcile(new TestCustomResource(), null); + var res = workflow.reconcile(new TestCustomResource(), mockContext); assertThrows(AggregatedOperatorException.class, res::throwAggregateExceptionIfErrorsPresent); @@ -123,7 +127,7 @@ void dependentsOnErroredResourceNotReconciled() { .withThrowExceptionFurther(false) .build(); - var res = workflow.reconcile(new TestCustomResource(), null); + var res = workflow.reconcile(new TestCustomResource(), mockContext); assertThrows(AggregatedOperatorException.class, res::throwAggregateExceptionIfErrorsPresent); @@ -145,7 +149,7 @@ void oneBranchErrorsOtherCompletes() { .withThrowExceptionFurther(false) .build(); - var res = workflow.reconcile(new TestCustomResource(), null); + var res = workflow.reconcile(new TestCustomResource(), mockContext); assertThrows(AggregatedOperatorException.class, res::throwAggregateExceptionIfErrorsPresent); @@ -164,7 +168,7 @@ void onlyOneDependsOnErroredResourceNotReconciled() { .withThrowExceptionFurther(false) .build(); - var res = workflow.reconcile(new TestCustomResource(), null); + var res = workflow.reconcile(new TestCustomResource(), mockContext); assertThrows(AggregatedOperatorException.class, res::throwAggregateExceptionIfErrorsPresent); @@ -182,7 +186,7 @@ void simpleReconcileCondition() { .addDependentResource(drDeleter).withReconcilePrecondition(not_met_reconcile_condition) .build(); - var res = workflow.reconcile(new TestCustomResource(), null); + var res = workflow.reconcile(new TestCustomResource(), mockContext); assertThat(executionHistory).notReconciled(dr1).reconciled(dr2).deleted(drDeleter); Assertions.assertThat(res.getErroredDependents()).isEmpty(); @@ -200,7 +204,7 @@ void triangleOnceConditionNotMet() { .dependsOn(dr1) .build(); - var res = workflow.reconcile(new TestCustomResource(), null); + var res = workflow.reconcile(new TestCustomResource(), mockContext); assertThat(executionHistory).reconciledInOrder(dr1, dr2).deleted(drDeleter); Assertions.assertThat(res.getErroredDependents()).isEmpty(); @@ -222,7 +226,7 @@ void reconcileConditionTransitiveDelete() { .withReconcilePrecondition(met_reconcile_condition) .build(); - var res = workflow.reconcile(new TestCustomResource(), null); + var res = workflow.reconcile(new TestCustomResource(), mockContext); Assertions.assertThat(res.getErroredDependents()).isEmpty(); assertThat(executionHistory).notReconciled(dr2); @@ -246,7 +250,7 @@ void reconcileConditionAlsoErrorDependsOn() { .withThrowExceptionFurther(false) .build(); - var res = workflow.reconcile(new TestCustomResource(), null); + var res = workflow.reconcile(new TestCustomResource(), mockContext); assertThrows(AggregatedOperatorException.class, res::throwAggregateExceptionIfErrorsPresent); @@ -267,7 +271,7 @@ void oneDependsOnConditionNotMet() { .addDependentResource(drDeleter).dependsOn(dr1, dr2) .build(); - var res = workflow.reconcile(new TestCustomResource(), null); + var res = workflow.reconcile(new TestCustomResource(), mockContext); Assertions.assertThat(res.getErroredDependents()).isEmpty(); @@ -287,7 +291,7 @@ void deletedIfReconcileConditionNotMet() { .addDependentResource(drDeleter2).dependsOn(dr1, drDeleter) .build(); - var res = workflow.reconcile(new TestCustomResource(), null); + var res = workflow.reconcile(new TestCustomResource(), mockContext); assertThat(executionHistory) .reconciledInOrder(dr1, drDeleter2, drDeleter) @@ -313,7 +317,7 @@ void deleteDoneInReverseOrder() { .addDependentResource(drDeleter4).dependsOn(drDeleter3) .build(); - var res = workflow.reconcile(new TestCustomResource(), null); + var res = workflow.reconcile(new TestCustomResource(), mockContext); assertThat(executionHistory) .reconciledInOrder(dr1, drDeleter4, drDeleter3, drDeleter) @@ -339,7 +343,7 @@ void diamondDeleteWithPostConditionInMiddle() { .addDependentResource(drDeleter4).dependsOn(drDeleter3, drDeleter2) .build(); - var res = workflow.reconcile(new TestCustomResource(), null); + var res = workflow.reconcile(new TestCustomResource(), mockContext); assertThat(executionHistory).notReconciled(drDeleter) .reconciledInOrder(drDeleter4, drDeleter2) @@ -363,7 +367,7 @@ void diamondDeleteErrorInMiddle() { .withThrowExceptionFurther(false) .build(); - var res = workflow.reconcile(new TestCustomResource(), null); + var res = workflow.reconcile(new TestCustomResource(), mockContext); assertThat(executionHistory) .notReconciled(drDeleter, drError) @@ -381,7 +385,7 @@ void readyConditionTrivialCase() { .addDependentResource(dr2).dependsOn(dr1) .build(); - var res = workflow.reconcile(new TestCustomResource(), null); + var res = workflow.reconcile(new TestCustomResource(), mockContext); assertThat(executionHistory).reconciledInOrder(dr1, dr2); @@ -397,7 +401,7 @@ void readyConditionNotMetTrivialCase() { .addDependentResource(dr2).dependsOn(dr1) .build(); - var res = workflow.reconcile(new TestCustomResource(), null); + var res = workflow.reconcile(new TestCustomResource(), mockContext); assertThat(executionHistory).reconciled(dr1).notReconciled(dr2); @@ -417,7 +421,7 @@ void readyConditionNotMetInOneParent() { .addDependentResource(dr3).dependsOn(dr1, dr2) .build(); - var res = workflow.reconcile(new TestCustomResource(), null); + var res = workflow.reconcile(new TestCustomResource(), mockContext); assertThat(executionHistory).reconciled(dr1, dr2).notReconciled(dr3); Assertions.assertThat(res.getErroredDependents()).isEmpty(); @@ -437,7 +441,7 @@ void diamondShareWithReadyCondition() { .addDependentResource(dr4).dependsOn(dr2, dr3) .build(); - var res = workflow.reconcile(new TestCustomResource(), null); + var res = workflow.reconcile(new TestCustomResource(), mockContext); Assertions.assertThat(res.getErroredDependents()).isEmpty(); assertThat(executionHistory).reconciledInOrder(dr1, dr2) From e9c8c7e7c646f57522d9c07dc454c51b9eb19c8a Mon Sep 17 00:00:00 2001 From: csviri Date: Tue, 6 Sep 2022 16:46:08 +0200 Subject: [PATCH 12/67] event source provider to context --- .../javaoperatorsdk/operator/api/reconciler/Context.java | 3 +++ .../operator/api/reconciler/DefaultContext.java | 9 +++++++-- .../operator/api/reconciler/ResourceDiscriminator.java | 4 +--- .../api/reconciler/ResourceIDMatcherDiscriminator.java | 4 +--- .../api/reconciler/VoidResourceDiscriminator.java | 4 +--- .../sample/indexdiscriminator/IndexDiscriminator.java | 7 +++---- 6 files changed, 16 insertions(+), 15 deletions(-) diff --git a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/reconciler/Context.java b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/reconciler/Context.java index a26e7c46fb..2e4fb98e6f 100644 --- a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/reconciler/Context.java +++ b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/reconciler/Context.java @@ -6,6 +6,7 @@ import io.fabric8.kubernetes.api.model.HasMetadata; import io.javaoperatorsdk.operator.api.config.ControllerConfiguration; import io.javaoperatorsdk.operator.api.reconciler.dependent.managed.ManagedDependentResourceContext; +import io.javaoperatorsdk.operator.processing.event.EventSourceRetriever; public interface Context

{ @@ -26,4 +27,6 @@ Optional getSecondaryResource(Class expectedType, ControllerConfiguration

getControllerConfiguration(); ManagedDependentResourceContext managedDependentResourceContext(); + + EventSourceRetriever

eventSourceRetriever(); } diff --git a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/reconciler/DefaultContext.java b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/reconciler/DefaultContext.java index 0d5bc22018..cb7f4ae63b 100644 --- a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/reconciler/DefaultContext.java +++ b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/reconciler/DefaultContext.java @@ -9,6 +9,7 @@ import io.javaoperatorsdk.operator.api.reconciler.dependent.managed.DefaultManagedDependentResourceContext; import io.javaoperatorsdk.operator.api.reconciler.dependent.managed.ManagedDependentResourceContext; import io.javaoperatorsdk.operator.processing.Controller; +import io.javaoperatorsdk.operator.processing.event.EventSourceRetriever; public class DefaultContext

implements Context

{ @@ -50,8 +51,7 @@ public Optional getSecondaryResource(Class expectedType, String eventS @Override public Optional getSecondaryResource(Class expectedType, ResourceDiscriminator discriminator) { - return discriminator.distinguish(expectedType, primaryResource, this, - controller.getEventSourceManager()); + return discriminator.distinguish(expectedType, primaryResource, this); } @Override @@ -64,6 +64,11 @@ public ManagedDependentResourceContext managedDependentResourceContext() { return defaultManagedDependentResourceContext; } + @Override + public EventSourceRetriever

eventSourceRetriever() { + return controller.getEventSourceManager(); + } + public DefaultContext

setRetryInfo(RetryInfo retryInfo) { this.retryInfo = retryInfo; return this; diff --git a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/reconciler/ResourceDiscriminator.java b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/reconciler/ResourceDiscriminator.java index 97130a27d5..5af8239901 100644 --- a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/reconciler/ResourceDiscriminator.java +++ b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/reconciler/ResourceDiscriminator.java @@ -3,13 +3,11 @@ import java.util.Optional; import io.fabric8.kubernetes.api.model.HasMetadata; -import io.javaoperatorsdk.operator.processing.event.EventSourceRetriever; // todo is discriminator a good name? it not just discriminates but also reads from cache // todo discuss a List version of this (for reconciler but also for batch processing?) public interface ResourceDiscriminator { - Optional distinguish(Class resource, P primary, Context

context, - EventSourceRetriever

eventSourceRetriever); + Optional distinguish(Class resource, P primary, Context

context); } diff --git a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/reconciler/ResourceIDMatcherDiscriminator.java b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/reconciler/ResourceIDMatcherDiscriminator.java index 3df1956bf9..ef0852c900 100644 --- a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/reconciler/ResourceIDMatcherDiscriminator.java +++ b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/reconciler/ResourceIDMatcherDiscriminator.java @@ -4,7 +4,6 @@ import java.util.function.Function; import io.fabric8.kubernetes.api.model.HasMetadata; -import io.javaoperatorsdk.operator.processing.event.EventSourceRetriever; import io.javaoperatorsdk.operator.processing.event.ResourceID; public class ResourceIDMatcherDiscriminator @@ -17,8 +16,7 @@ public ResourceIDMatcherDiscriminator(Function mapper) { } @Override - public Optional distinguish(Class resource, P primary, Context

context, - EventSourceRetriever

eventSourceRetriever) { + public Optional distinguish(Class resource, P primary, Context

context) { var resourceID = mapper.apply(primary); return context.getSecondaryResources(resource).stream() .filter(r -> r.getMetadata().getName() diff --git a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/reconciler/VoidResourceDiscriminator.java b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/reconciler/VoidResourceDiscriminator.java index a02c0c6a95..0a5fd1f965 100644 --- a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/reconciler/VoidResourceDiscriminator.java +++ b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/reconciler/VoidResourceDiscriminator.java @@ -3,14 +3,12 @@ import java.util.Optional; import io.fabric8.kubernetes.api.model.HasMetadata; -import io.javaoperatorsdk.operator.processing.event.EventSourceRetriever; public class VoidResourceDiscriminator implements ResourceDiscriminator { @Override - public Optional distinguish(Class resource, P primary, Context

context, - EventSourceRetriever

eventSourceRetriever) { + public Optional distinguish(Class resource, P primary, Context

context) { throw new UnsupportedOperationException(); } } diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/indexdiscriminator/IndexDiscriminator.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/indexdiscriminator/IndexDiscriminator.java index 6cf72519a1..eb6e193479 100644 --- a/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/indexdiscriminator/IndexDiscriminator.java +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/indexdiscriminator/IndexDiscriminator.java @@ -5,7 +5,6 @@ import io.fabric8.kubernetes.api.model.ConfigMap; import io.javaoperatorsdk.operator.api.reconciler.Context; import io.javaoperatorsdk.operator.api.reconciler.ResourceDiscriminator; -import io.javaoperatorsdk.operator.processing.event.EventSourceRetriever; import io.javaoperatorsdk.operator.processing.event.source.informer.InformerEventSource; import static io.javaoperatorsdk.operator.sample.indexdiscriminator.IndexDiscriminatorTestReconciler.configMapKeyFromPrimary; @@ -24,11 +23,11 @@ public IndexDiscriminator(String indexName, String nameSuffix) { @Override public Optional distinguish(Class resource, IndexDiscriminatorTestCustomResource primary, - Context context, - EventSourceRetriever eventSourceRetriever) { + Context context) { InformerEventSource eventSource = - (InformerEventSource) eventSourceRetriever + (InformerEventSource) context + .eventSourceRetriever() .getResourceEventSourceFor(ConfigMap.class); var resources = eventSource.byIndex(indexName, configMapKeyFromPrimary(primary, nameSuffix)); if (resources.isEmpty()) { From 2042fd88a6141cadbb8fa49d793525d29a1164f0 Mon Sep 17 00:00:00 2001 From: csviri Date: Fri, 16 Sep 2022 09:48:32 +0200 Subject: [PATCH 13/67] todo fixes --- .../api/reconciler/ResourceDiscriminator.java | 2 -- .../dependent/AbstractDependentResource.java | 13 +++----- .../workflow/WorkflowCleanupExecutor.java | 33 ++++++++++++++----- .../workflow/WorkflowReconcileExecutor.java | 26 ++++++++++++--- 4 files changed, 52 insertions(+), 22 deletions(-) diff --git a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/reconciler/ResourceDiscriminator.java b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/reconciler/ResourceDiscriminator.java index 5af8239901..072e7d8078 100644 --- a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/reconciler/ResourceDiscriminator.java +++ b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/reconciler/ResourceDiscriminator.java @@ -4,8 +4,6 @@ import io.fabric8.kubernetes.api.model.HasMetadata; -// todo is discriminator a good name? it not just discriminates but also reads from cache -// todo discuss a List version of this (for reconciler but also for batch processing?) public interface ResourceDiscriminator { Optional distinguish(Class resource, P primary, Context

context); 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 5ecd52cc35..ec553864e6 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 @@ -24,7 +24,6 @@ public abstract class AbstractDependentResource protected Creator creator; protected Updater updater; - // todo discuss, rather implement this as interface? private ResourceDiscriminator resourceDiscriminator; @SuppressWarnings("unchecked") @@ -134,14 +133,12 @@ protected R desired(P primary, Context

context) { "desired method must be implemented if this DependentResource can be created and/or updated"); } - // todo review & refactor configuration to cover all cases - public ResourceDiscriminator getResourceDiscriminator() { - return resourceDiscriminator; - } - - public AbstractDependentResource setResourceDiscriminator( + public void setResourceDiscriminator( ResourceDiscriminator resourceDiscriminator) { this.resourceDiscriminator = resourceDiscriminator; - return this; + } + + public ResourceDiscriminator getResourceDiscriminator() { + return resourceDiscriminator; } } diff --git a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/workflow/WorkflowCleanupExecutor.java b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/workflow/WorkflowCleanupExecutor.java index a3c6319511..5d4f46b9f6 100644 --- a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/workflow/WorkflowCleanupExecutor.java +++ b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/workflow/WorkflowCleanupExecutor.java @@ -13,8 +13,10 @@ import io.fabric8.kubernetes.api.model.HasMetadata; import io.javaoperatorsdk.operator.api.reconciler.Context; +import io.javaoperatorsdk.operator.api.reconciler.ResourceDiscriminator; import io.javaoperatorsdk.operator.api.reconciler.dependent.Deleter; import io.javaoperatorsdk.operator.api.reconciler.dependent.GarbageCollected; +import io.javaoperatorsdk.operator.processing.dependent.AbstractDependentResource; @SuppressWarnings("rawtypes") public class WorkflowCleanupExecutor

{ @@ -105,14 +107,10 @@ public void run() { deleteCalled.add(dependentResourceNode); } boolean deletePostConditionMet = - deletePostCondition.map(c -> c.isMet(primary, - // todo pass also discriminator - context - .getSecondaryResource( - dependentResourceNode.getDependentResource().resourceType()) - .orElse(null), - context)).orElse(true); - + deletePostCondition + .map(c -> c.isMet(primary, getSecondaryResource(dependentResourceNode), + context)) + .orElse(true); if (deletePostConditionMet) { alreadyVisited.add(dependentResourceNode); handleDependentCleaned(dependentResourceNode); @@ -130,6 +128,25 @@ public void run() { } } + @SuppressWarnings("unchecked") + private R getSecondaryResource(DependentResourceNode dependentResourceNode) { + if (dependentResourceNode.getDependentResource() instanceof AbstractDependentResource && + ((AbstractDependentResource) dependentResourceNode.getDependentResource()) + .getResourceDiscriminator() != null) { + ResourceDiscriminator discriminator = + ((AbstractDependentResource) dependentResourceNode.getDependentResource()) + .getResourceDiscriminator(); + return context + .getSecondaryResource(dependentResourceNode.getDependentResource().resourceType(), + discriminator) + .orElse(null); + } else { + return context + .getSecondaryResource(dependentResourceNode.getDependentResource().resourceType()) + .orElse(null); + } + } + private synchronized void handleDependentCleaned( DependentResourceNode dependentResourceNode) { var dependOns = dependentResourceNode.getDependsOn(); diff --git a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/workflow/WorkflowReconcileExecutor.java b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/workflow/WorkflowReconcileExecutor.java index 6b2f7fb82b..735604d4a9 100644 --- a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/workflow/WorkflowReconcileExecutor.java +++ b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/workflow/WorkflowReconcileExecutor.java @@ -13,10 +13,12 @@ import io.fabric8.kubernetes.api.model.HasMetadata; import io.javaoperatorsdk.operator.api.reconciler.Context; +import io.javaoperatorsdk.operator.api.reconciler.ResourceDiscriminator; import io.javaoperatorsdk.operator.api.reconciler.dependent.Deleter; import io.javaoperatorsdk.operator.api.reconciler.dependent.DependentResource; import io.javaoperatorsdk.operator.api.reconciler.dependent.GarbageCollected; import io.javaoperatorsdk.operator.api.reconciler.dependent.ReconcileResult; +import io.javaoperatorsdk.operator.processing.dependent.AbstractDependentResource; import io.javaoperatorsdk.operator.processing.event.ResourceID; @SuppressWarnings({"rawtypes", "unchecked"}) @@ -85,10 +87,7 @@ private synchronized void handleReconcile(DependentResourceNode depend } boolean reconcileConditionMet = dependentResourceNode.getReconcilePrecondition() - .map(rc -> rc.isMet(primary, - context - .getSecondaryResource(dependentResourceNode.getDependentResource().resourceType()) - .orElse(null), + .map(rc -> rc.isMet(primary, getSecondaryResource(dependentResourceNode), context)) .orElse(true); @@ -102,6 +101,25 @@ private synchronized void handleReconcile(DependentResourceNode depend } } + @SuppressWarnings("unchecked") + private R getSecondaryResource(DependentResourceNode dependentResourceNode) { + if (dependentResourceNode.getDependentResource() instanceof AbstractDependentResource && + ((AbstractDependentResource) dependentResourceNode.getDependentResource()) + .getResourceDiscriminator() != null) { + ResourceDiscriminator discriminator = + ((AbstractDependentResource) dependentResourceNode.getDependentResource()) + .getResourceDiscriminator(); + return context + .getSecondaryResource(dependentResourceNode.getDependentResource().resourceType(), + discriminator) + .orElse(null); + } else { + return context + .getSecondaryResource(dependentResourceNode.getDependentResource().resourceType()) + .orElse(null); + } + } + private synchronized void handleDelete(DependentResourceNode dependentResourceNode) { log.debug("Submitting for delete: {}", dependentResourceNode); From 8f04146b4ef694675c88a38b7e84b535b473c63c Mon Sep 17 00:00:00 2001 From: csviri Date: Tue, 27 Sep 2022 09:22:15 +0200 Subject: [PATCH 14/67] remove void discriminator --- .../config/AnnotationControllerConfiguration.java | 2 +- .../api/reconciler/VoidResourceDiscriminator.java | 14 -------------- .../dependent/kubernetes/KubernetesDependent.java | 3 +-- 3 files changed, 2 insertions(+), 17 deletions(-) delete mode 100644 operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/reconciler/VoidResourceDiscriminator.java diff --git a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/config/AnnotationControllerConfiguration.java b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/config/AnnotationControllerConfiguration.java index 1475cf58bc..db3e8de3f0 100644 --- a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/config/AnnotationControllerConfiguration.java +++ b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/config/AnnotationControllerConfiguration.java @@ -328,7 +328,7 @@ private Object createKubernetesResourceConfig(Class @SuppressWarnings({"unchecked"}) private ResourceDiscriminator instantiateDiscriminatorIfNotVoid( Class discriminator) { - if (discriminator != VoidResourceDiscriminator.class) { + if (discriminator != ResourceDiscriminator.class) { return instantiateAndConfigureIfNeeded(discriminator, ResourceDiscriminator.class, CONTROLLER_CONFIG_ANNOTATION); } diff --git a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/reconciler/VoidResourceDiscriminator.java b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/reconciler/VoidResourceDiscriminator.java deleted file mode 100644 index 0a5fd1f965..0000000000 --- a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/reconciler/VoidResourceDiscriminator.java +++ /dev/null @@ -1,14 +0,0 @@ -package io.javaoperatorsdk.operator.api.reconciler; - -import java.util.Optional; - -import io.fabric8.kubernetes.api.model.HasMetadata; - -public class VoidResourceDiscriminator - implements ResourceDiscriminator { - - @Override - public Optional distinguish(Class resource, P primary, Context

context) { - throw new UnsupportedOperationException(); - } -} diff --git a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/kubernetes/KubernetesDependent.java b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/kubernetes/KubernetesDependent.java index 6453a1b061..603f4ae62e 100644 --- a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/kubernetes/KubernetesDependent.java +++ b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/kubernetes/KubernetesDependent.java @@ -7,7 +7,6 @@ import io.javaoperatorsdk.operator.api.reconciler.Constants; import io.javaoperatorsdk.operator.api.reconciler.ResourceDiscriminator; -import io.javaoperatorsdk.operator.api.reconciler.VoidResourceDiscriminator; import io.javaoperatorsdk.operator.processing.event.source.filter.*; import io.javaoperatorsdk.operator.processing.event.source.filter.GenericFilter; import io.javaoperatorsdk.operator.processing.event.source.filter.OnAddFilter; @@ -72,5 +71,5 @@ */ Class genericFilter() default GenericFilter.class; - Class resourceDiscriminator() default VoidResourceDiscriminator.class; + Class resourceDiscriminator() default ResourceDiscriminator.class; } From bdd2d9621173307fd442dff3f2b873fa3ca3ceee Mon Sep 17 00:00:00 2001 From: csviri Date: Tue, 27 Sep 2022 09:46:31 +0200 Subject: [PATCH 15/67] rebase on next --- .../processing/dependent/workflow/WorkflowReconcileExecutor.java | 1 + 1 file changed, 1 insertion(+) diff --git a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/workflow/WorkflowReconcileExecutor.java b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/workflow/WorkflowReconcileExecutor.java index 735604d4a9..46c47c01ab 100644 --- a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/workflow/WorkflowReconcileExecutor.java +++ b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/workflow/WorkflowReconcileExecutor.java @@ -187,6 +187,7 @@ public void run() { ReconcileResult reconcileResult = dependentResource.reconcile(primary, context); reconcileResults.put(dependentResource, reconcileResult); reconciled.add(dependentResourceNode); + boolean ready = dependentResourceNode.getReadyPostcondition() .map(rc -> rc.isMet(primary, context From 378f463a6a8b9be36da470b9cbde65afb173254e Mon Sep 17 00:00:00 2001 From: csviri Date: Thu, 8 Sep 2022 17:29:42 +0200 Subject: [PATCH 16/67] fix: bulk creation of dependent resource directly in abstract resource --- .../dependent/AbstractDependentResource.java | 98 ++++++++++++++++--- .../BulkResourceDiscriminatorFactory.java | 10 ++ .../processing/dependent/Updater.java | 9 ++ 3 files changed, 106 insertions(+), 11 deletions(-) create mode 100644 operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/BulkResourceDiscriminatorFactory.java 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 ec553864e6..39486a7396 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,5 +1,7 @@ package io.javaoperatorsdk.operator.processing.dependent; +import java.util.ArrayList; +import java.util.List; import java.util.Optional; import org.slf4j.Logger; @@ -24,7 +26,9 @@ public abstract class AbstractDependentResource protected Creator creator; protected Updater updater; - private ResourceDiscriminator resourceDiscriminator; + protected List> resourceDiscriminator = new ArrayList<>(1); + // used just for bulk creation + protected BulkResourceDiscriminatorFactory bulkResourceDiscriminatorFactory; @SuppressWarnings("unchecked") public AbstractDependentResource() { @@ -32,14 +36,44 @@ public AbstractDependentResource() { updater = updatable ? (Updater) this : null; } - @SuppressWarnings("unchecked") @Override public ReconcileResult reconcile(P primary, Context

context) { - Optional maybeActual = getSecondaryResource(primary, context); + var count = count(primary, context); + if (isBulkResourceCreation(primary, context)) { + initDiscriminators(count); + } + for (int i = 0; i < count; i++) { + reconcileWithIndex(primary, i, context); + } + // todo result + return null; + } + + private void initDiscriminators(int count) { + if (resourceDiscriminator.size() == count) { + return; + } + if (resourceDiscriminator.size() < count) { + for (int i = resourceDiscriminator.size() - 1; i < count; i++) { + resourceDiscriminator.add(bulkResourceDiscriminatorFactory.createResourceDiscriminator(i)); + } + } + if (resourceDiscriminator.size() < count) { + for (int i = resourceDiscriminator.size() - 1; i < count; i++) { + resourceDiscriminator.add(bulkResourceDiscriminatorFactory.createResourceDiscriminator(i)); + } + } + if (resourceDiscriminator.size() > count) { + resourceDiscriminator.subList(count, resourceDiscriminator.size()).clear(); + } + } + + protected ReconcileResult reconcileWithIndex(P primary, int i, Context

context) { + Optional maybeActual = getSecondaryResource(primary, i, context); if (creatable || updatable) { if (maybeActual.isEmpty()) { if (creatable) { - var desired = desired(primary, context); + var desired = desired(primary, i, context); throwIfNull(desired, primary, "Desired"); logForOperation("Creating", primary, desired); var createdResource = handleCreate(desired, primary, context); @@ -48,7 +82,8 @@ public ReconcileResult reconcile(P primary, Context

context) { } else { final var actual = maybeActual.get(); if (updatable) { - final var match = updater.match(actual, primary, context); + // todo simplify matcher? + final var match = updater.match(actual, primary, i, context); if (!match.matched()) { final var desired = match.computedDesired().orElse(desired(primary, context)); throwIfNull(desired, primary, "Desired"); @@ -68,12 +103,25 @@ public ReconcileResult reconcile(P primary, Context

context) { return ReconcileResult.noOperation(maybeActual.orElse(null)); } + // todo check protected Optional getSecondaryResource(P primary, Context

context) { - if (resourceDiscriminator == null) { + if (resourceDiscriminator.isEmpty()) { return context.getSecondaryResource(resourceType()); } else { - return context.getSecondaryResource(resourceType(), resourceDiscriminator); + return context.getSecondaryResource(resourceType(), resourceDiscriminator.get(0)); + } + } + + protected Optional getSecondaryResource(P primary, int index, Context

context) { + if (index > 0 && resourceDiscriminator.isEmpty()) { + throw new IllegalStateException( + "Handling resources in bulk bot no resource discriminators set."); + } + if (!isBulkResourceCreation(primary, context)) { + return getSecondaryResource(primary, context); } + + return context.getSecondaryResource(resourceType(), resourceDiscriminator.get(index)); } private void throwIfNull(R desired, P primary, String descriptor) { @@ -111,7 +159,7 @@ protected R handleCreate(R desired, P primary, Context

context) { protected abstract void onCreated(ResourceID primaryResourceId, R created); /** - * Allows sub-classes to perform additional processing on the updated resource if needed. + * Allows subclasses to perform additional processing on the updated resource if needed. * * @param primaryResourceId the {@link ResourceID} of the primary resource associated with the * newly updated resource @@ -133,12 +181,40 @@ protected R desired(P primary, Context

context) { "desired method must be implemented if this DependentResource can be created and/or updated"); } - public void setResourceDiscriminator( + protected R desired(P primary, int index, Context

context) { + if (!isBulkResourceCreation(primary, context)) { + return desired(primary, context); + } else { + throw new IllegalStateException( + "desired() with index method must be implemented for bulk DependentResource creation"); + } + } + + public AbstractDependentResource setResourceDiscriminator( ResourceDiscriminator resourceDiscriminator) { - this.resourceDiscriminator = resourceDiscriminator; + this.resourceDiscriminator.add(resourceDiscriminator); + return this; } public ResourceDiscriminator getResourceDiscriminator() { - return resourceDiscriminator; + return resourceDiscriminator.get(0); + } + + protected int count(P primary, Context

context) { + return 1; + } + + protected boolean isBulkResourceCreation(P primary, Context

context) { + return false; + } + + public BulkResourceDiscriminatorFactory getBulkResourceDiscriminatorFactory() { + return bulkResourceDiscriminatorFactory; + } + + public AbstractDependentResource setBulkResourceDiscriminatorFactory( + BulkResourceDiscriminatorFactory bulkResourceDiscriminatorFactory) { + this.bulkResourceDiscriminatorFactory = bulkResourceDiscriminatorFactory; + return this; } } diff --git a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/BulkResourceDiscriminatorFactory.java b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/BulkResourceDiscriminatorFactory.java new file mode 100644 index 0000000000..8b9b6e968d --- /dev/null +++ b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/BulkResourceDiscriminatorFactory.java @@ -0,0 +1,10 @@ +package io.javaoperatorsdk.operator.processing.dependent; + +import io.fabric8.kubernetes.api.model.HasMetadata; +import io.javaoperatorsdk.operator.api.reconciler.ResourceDiscriminator; + +public interface BulkResourceDiscriminatorFactory { + + ResourceDiscriminator createResourceDiscriminator(int index); + +} diff --git a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/Updater.java b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/Updater.java index 828f9ad785..19480f253b 100644 --- a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/Updater.java +++ b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/Updater.java @@ -8,4 +8,13 @@ public interface Updater { R update(R actual, R desired, P primary, Context

context); Result match(R actualResource, P primary, Context

context); + + // todo change to simple desired matching? + default Result match(R actualResource, P primary, int index, Context

context) { + if (index == 0) { + return match(actualResource, primary, context); + } else { + throw new IllegalStateException("Implement this"); + } + } } From 210034a3d11452da61ae3e55bde2657bae862c18 Mon Sep 17 00:00:00 2001 From: csviri Date: Fri, 9 Sep 2022 08:43:44 +0200 Subject: [PATCH 17/67] wip --- .../operator/config/runtime/StandaloneBulkDependentIT.java | 4 ++++ 1 file changed, 4 insertions(+) create mode 100644 operator-framework/src/test/java/io/javaoperatorsdk/operator/config/runtime/StandaloneBulkDependentIT.java diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/config/runtime/StandaloneBulkDependentIT.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/config/runtime/StandaloneBulkDependentIT.java new file mode 100644 index 0000000000..ef27c9c2e1 --- /dev/null +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/config/runtime/StandaloneBulkDependentIT.java @@ -0,0 +1,4 @@ +package io.javaoperatorsdk.operator.config.runtime; + +public class StandaloneBulkDependentIT { +} From 7a56e1c9d024257d6a1bdc842e81bdbe370a81b7 Mon Sep 17 00:00:00 2001 From: csviri Date: Fri, 9 Sep 2022 11:13:17 +0200 Subject: [PATCH 18/67] wip --- .../javaoperatorsdk/operator/StandaloneBulkDependentIT.java | 4 ++++ .../operator/config/runtime/StandaloneBulkDependentIT.java | 4 ---- 2 files changed, 4 insertions(+), 4 deletions(-) create mode 100644 operator-framework/src/test/java/io/javaoperatorsdk/operator/StandaloneBulkDependentIT.java delete mode 100644 operator-framework/src/test/java/io/javaoperatorsdk/operator/config/runtime/StandaloneBulkDependentIT.java diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/StandaloneBulkDependentIT.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/StandaloneBulkDependentIT.java new file mode 100644 index 0000000000..0ee8ef49e2 --- /dev/null +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/StandaloneBulkDependentIT.java @@ -0,0 +1,4 @@ +package io.javaoperatorsdk.operator; + +public class StandaloneBulkDependentIT { +} diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/config/runtime/StandaloneBulkDependentIT.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/config/runtime/StandaloneBulkDependentIT.java deleted file mode 100644 index ef27c9c2e1..0000000000 --- a/operator-framework/src/test/java/io/javaoperatorsdk/operator/config/runtime/StandaloneBulkDependentIT.java +++ /dev/null @@ -1,4 +0,0 @@ -package io.javaoperatorsdk.operator.config.runtime; - -public class StandaloneBulkDependentIT { -} From bc4709ce43f9cfa35fe47d1866e3481646da8c09 Mon Sep 17 00:00:00 2001 From: csviri Date: Fri, 9 Sep 2022 11:18:54 +0200 Subject: [PATCH 19/67] wip to start IT --- .../io/javaoperatorsdk/operator/StandaloneBulkDependentIT.java | 1 + 1 file changed, 1 insertion(+) diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/StandaloneBulkDependentIT.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/StandaloneBulkDependentIT.java index 0ee8ef49e2..56ac867aa8 100644 --- a/operator-framework/src/test/java/io/javaoperatorsdk/operator/StandaloneBulkDependentIT.java +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/StandaloneBulkDependentIT.java @@ -1,4 +1,5 @@ package io.javaoperatorsdk.operator; public class StandaloneBulkDependentIT { + } From 2e96cd9a75cfb5f07ae602f3d7a332175ab404a3 Mon Sep 17 00:00:00 2001 From: csviri Date: Fri, 9 Sep 2022 16:09:49 +0200 Subject: [PATCH 20/67] fixes, progress --- .../reconciler/dependent/ReconcileResult.java | 39 +++++++---- .../dependent/AbstractDependentResource.java | 18 +++-- .../processing/dependent/Updater.java | 6 +- .../KubernetesDependentResource.java | 6 +- .../operator/StandaloneBulkDependentIT.java | 47 ++++++++++++- .../ConfigMapBulkDependentResource.java | 69 +++++++++++++++++++ .../StandaloneBulkDependentReconciler.java | 58 ++++++++++++++++ ...daloneBulkDependentTestCustomResource.java | 15 ++++ .../StandaloneBulkDependentTestSpec.java | 15 ++++ 9 files changed, 246 insertions(+), 27 deletions(-) create mode 100644 operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/standalonebulkdependent/ConfigMapBulkDependentResource.java create mode 100644 operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/standalonebulkdependent/StandaloneBulkDependentReconciler.java create mode 100644 operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/standalonebulkdependent/StandaloneBulkDependentTestCustomResource.java create mode 100644 operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/standalonebulkdependent/StandaloneBulkDependentTestSpec.java diff --git a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/reconciler/dependent/ReconcileResult.java b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/reconciler/dependent/ReconcileResult.java index c83da1c8ea..65b6e8adfd 100644 --- a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/reconciler/dependent/ReconcileResult.java +++ b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/reconciler/dependent/ReconcileResult.java @@ -1,14 +1,16 @@ package io.javaoperatorsdk.operator.api.reconciler.dependent; -import java.util.Optional; +import java.util.*; +import java.util.stream.Collectors; import io.fabric8.kubernetes.api.model.HasMetadata; import io.javaoperatorsdk.operator.processing.event.ResourceID; public class ReconcileResult { - private final R resource; - private final Operation operation; + private Map resourceOperations = new HashMap<>(1); + + public ReconcileResult() {} public static ReconcileResult resourceCreated(T resource) { return new ReconcileResult<>(resource, Operation.CREATED); @@ -24,23 +26,34 @@ public static ReconcileResult noOperation(T resource) { @Override public String toString() { - return getResource() - .map(r -> r instanceof HasMetadata ? ResourceID.fromResource((HasMetadata) r) : r) - .orElse("no resource") - + " -> " + operation; + return resourceOperations.entrySet().stream().collect(Collectors.toMap( + e -> e instanceof HasMetadata ? ResourceID.fromResource((HasMetadata) e) : e, + Map.Entry::getValue)) + .toString(); } private ReconcileResult(R resource, Operation operation) { - this.resource = resource; - this.operation = operation; + resourceOperations.put(resource, operation); + } + + public Optional getSingleResource() { + return resourceOperations.entrySet().stream().findFirst().map(Map.Entry::getKey); } - public Optional getResource() { - return Optional.ofNullable(resource); + public Operation getSingleOperation() { + return resourceOperations.entrySet().stream().findFirst().map(Map.Entry::getValue) + .orElseThrow(); } - public Operation getOperation() { - return operation; + public Map getResourceOperations() { + return resourceOperations; + } + + public void addReconcileResult(ReconcileResult result) { + result.getSingleResource().ifPresent(r -> { + resourceOperations.put(r, result.getSingleOperation()); + }); + } public enum Operation { 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 39486a7396..c3c661e093 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 @@ -42,11 +42,12 @@ public ReconcileResult reconcile(P primary, Context

context) { if (isBulkResourceCreation(primary, context)) { initDiscriminators(count); } + ReconcileResult result = new ReconcileResult<>(); for (int i = 0; i < count; i++) { - reconcileWithIndex(primary, i, context); + var res = reconcileWithIndex(primary, i, context); + result.addReconcileResult(res); } - // todo result - return null; + return result; } private void initDiscriminators(int count) { @@ -83,7 +84,12 @@ protected ReconcileResult reconcileWithIndex(P primary, int i, Context

con final var actual = maybeActual.get(); if (updatable) { // todo simplify matcher? - final var match = updater.match(actual, primary, i, context); + final Matcher.Result match; + if (isBulkResourceCreation(primary, context)) { + match = updater.match(actual, primary, i, context); + } else { + match = updater.match(actual, primary, context); + } if (!match.matched()) { final var desired = match.computedDesired().orElse(desired(primary, context)); throwIfNull(desired, primary, "Desired"); @@ -192,7 +198,9 @@ protected R desired(P primary, int index, Context

context) { public AbstractDependentResource setResourceDiscriminator( ResourceDiscriminator resourceDiscriminator) { - this.resourceDiscriminator.add(resourceDiscriminator); + if (resourceDiscriminator != null) { + this.resourceDiscriminator.add(resourceDiscriminator); + } return this; } diff --git a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/Updater.java b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/Updater.java index 19480f253b..dd36be0492 100644 --- a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/Updater.java +++ b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/Updater.java @@ -11,10 +11,6 @@ public interface Updater { // todo change to simple desired matching? default Result match(R actualResource, P primary, int index, Context

context) { - if (index == 0) { - return match(actualResource, primary, context); - } else { - throw new IllegalStateException("Implement this"); - } + throw new IllegalStateException("Implement this for bulk matching"); } } 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 d9eae04da8..298a9b3735 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 @@ -167,8 +167,10 @@ protected InformerEventSource createEventSource(EventSourceContext

cont onUpdateFilter = kubernetesDependentResourceConfig.onUpdateFilter(); onDeleteFilter = kubernetesDependentResourceConfig.onDeleteFilter(); genericFilter = kubernetesDependentResourceConfig.genericFilter(); - setResourceDiscriminator(kubernetesDependentResourceConfig.getResourceDiscriminator()); - + var discriminator = kubernetesDependentResourceConfig.getResourceDiscriminator(); + if (discriminator != null) { + setResourceDiscriminator(discriminator); + } configureWith(kubernetesDependentResourceConfig.labelSelector(), kubernetesDependentResourceConfig.namespaces(), !kubernetesDependentResourceConfig.wereNamespacesConfigured(), context); diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/StandaloneBulkDependentIT.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/StandaloneBulkDependentIT.java index 56ac867aa8..006dc9dd53 100644 --- a/operator-framework/src/test/java/io/javaoperatorsdk/operator/StandaloneBulkDependentIT.java +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/StandaloneBulkDependentIT.java @@ -1,5 +1,48 @@ package io.javaoperatorsdk.operator; -public class StandaloneBulkDependentIT { - +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.RegisterExtension; + +import io.fabric8.kubernetes.api.model.ObjectMeta; +import io.javaoperatorsdk.operator.junit.LocallyRunOperatorExtension; +import io.javaoperatorsdk.operator.sample.standalonebulkdependent.StandaloneBulkDependentReconciler; +import io.javaoperatorsdk.operator.sample.standalonebulkdependent.StandaloneBulkDependentTestCustomResource; +import io.javaoperatorsdk.operator.sample.standalonebulkdependent.StandaloneBulkDependentTestSpec; + +import static io.javaoperatorsdk.operator.sample.standalonebulkdependent.ConfigMapBulkDependentResource.LABEL_KEY; +import static io.javaoperatorsdk.operator.sample.standalonebulkdependent.ConfigMapBulkDependentResource.LABEL_VALUE; +import static org.assertj.core.api.Assertions.assertThat; +import static org.awaitility.Awaitility.await; + +class StandaloneBulkDependentIT { + + public static final String TEST_RESOURCE_NAME = "test"; + public static final int NUMBER_OF_CONFIG_MAPS = 3; + + @RegisterExtension + LocallyRunOperatorExtension operator = + LocallyRunOperatorExtension.builder().withReconciler(new StandaloneBulkDependentReconciler()) + .build(); + + @Test + void managesBulkConfigMaps() { + operator.create(testResource()); + + await().untilAsserted(() -> { + var cms = operator.getKubernetesClient().configMaps().inNamespace(operator.getNamespace()) + .withLabel(LABEL_KEY, LABEL_VALUE) + .list().getItems(); + assertThat(cms).hasSize(NUMBER_OF_CONFIG_MAPS); + }); + } + + private StandaloneBulkDependentTestCustomResource testResource() { + StandaloneBulkDependentTestCustomResource cr = new StandaloneBulkDependentTestCustomResource(); + cr.setMetadata(new ObjectMeta()); + cr.getMetadata().setName(TEST_RESOURCE_NAME); + cr.setSpec(new StandaloneBulkDependentTestSpec()); + cr.getSpec().setNumberOfResources(NUMBER_OF_CONFIG_MAPS); + return cr; + } + } diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/standalonebulkdependent/ConfigMapBulkDependentResource.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/standalonebulkdependent/ConfigMapBulkDependentResource.java new file mode 100644 index 0000000000..3df6c7223b --- /dev/null +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/standalonebulkdependent/ConfigMapBulkDependentResource.java @@ -0,0 +1,69 @@ +package io.javaoperatorsdk.operator.sample.standalonebulkdependent; + +import java.util.Map; +import java.util.Optional; +import java.util.stream.Collectors; + +import io.fabric8.kubernetes.api.model.ConfigMap; +import io.fabric8.kubernetes.api.model.ObjectMetaBuilder; +import io.javaoperatorsdk.operator.api.reconciler.Context; +import io.javaoperatorsdk.operator.api.reconciler.ResourceDiscriminator; +import io.javaoperatorsdk.operator.processing.dependent.BulkResourceDiscriminatorFactory; +import io.javaoperatorsdk.operator.processing.dependent.kubernetes.CRUDKubernetesDependentResource; + +public class ConfigMapBulkDependentResource + extends CRUDKubernetesDependentResource { + + public static final String LABEL_KEY = "bulk"; + public static final String LABEL_VALUE = "true"; + + public ConfigMapBulkDependentResource() { + super(ConfigMap.class); + setBulkResourceDiscriminatorFactory( + new BulkResourceDiscriminatorFactory() { + @Override + public ResourceDiscriminator createResourceDiscriminator( + int index) { + return (resource, primary, context) -> { + var resources = context.getSecondaryResources(resource).stream() + .filter(r -> r.getMetadata().getName().endsWith("-" + index)) + .collect(Collectors.toList()); + if (resources.isEmpty()) { + return Optional.empty(); + } else if (resources.size() > 1) { + throw new IllegalStateException("More than one resource found for index:" + index); + } else { + return Optional.of(resources.get(0)); + } + }; + } + }); + } + + @Override + protected ConfigMap desired(StandaloneBulkDependentTestCustomResource primary, + int index, Context context) { + ConfigMap configMap = new ConfigMap(); + configMap.setMetadata(new ObjectMetaBuilder() + .withName(primary.getMetadata().getName() + "-" + index) + .withNamespace(primary.getMetadata().getNamespace()) + .withLabels(Map.of(LABEL_KEY, LABEL_VALUE)) + .build()); + configMap.setData(Map.of("number", "" + index)); + return configMap; + } + + @Override + protected boolean isBulkResourceCreation(StandaloneBulkDependentTestCustomResource primary, + Context context) { + return true; + } + + @Override + protected int count(StandaloneBulkDependentTestCustomResource primary, + Context context) { + return primary.getSpec().getNumberOfResources(); + } + + +} diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/standalonebulkdependent/StandaloneBulkDependentReconciler.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/standalonebulkdependent/StandaloneBulkDependentReconciler.java new file mode 100644 index 0000000000..bf2b3cbf09 --- /dev/null +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/standalonebulkdependent/StandaloneBulkDependentReconciler.java @@ -0,0 +1,58 @@ +package io.javaoperatorsdk.operator.sample.standalonebulkdependent; + +import java.util.Map; +import java.util.concurrent.atomic.AtomicInteger; + +import io.fabric8.kubernetes.client.KubernetesClient; +import io.javaoperatorsdk.operator.api.reconciler.*; +import io.javaoperatorsdk.operator.junit.KubernetesClientAware; +import io.javaoperatorsdk.operator.processing.event.source.EventSource; +import io.javaoperatorsdk.operator.support.TestExecutionInfoProvider; + +@ControllerConfiguration +public class StandaloneBulkDependentReconciler + implements Reconciler, TestExecutionInfoProvider, + EventSourceInitializer, KubernetesClientAware { + + private final AtomicInteger numberOfExecutions = new AtomicInteger(0); + + private ConfigMapBulkDependentResource dependent; + private KubernetesClient kubernetesClient; + + public StandaloneBulkDependentReconciler() { + dependent = new ConfigMapBulkDependentResource(); + } + + @Override + public UpdateControl reconcile( + StandaloneBulkDependentTestCustomResource resource, + Context context) { + numberOfExecutions.addAndGet(1); + + dependent.reconcile(resource, context); + + return UpdateControl.noUpdate(); + } + + public int getNumberOfExecutions() { + return numberOfExecutions.get(); + } + + @Override + public Map prepareEventSources( + EventSourceContext context) { + return EventSourceInitializer + .nameEventSources(dependent.initEventSource(context)); + } + + @Override + public KubernetesClient getKubernetesClient() { + return kubernetesClient; + } + + @Override + public void setKubernetesClient(KubernetesClient kubernetesClient) { + this.kubernetesClient = kubernetesClient; + dependent.setKubernetesClient(kubernetesClient); + } +} diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/standalonebulkdependent/StandaloneBulkDependentTestCustomResource.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/standalonebulkdependent/StandaloneBulkDependentTestCustomResource.java new file mode 100644 index 0000000000..4ae13f945b --- /dev/null +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/standalonebulkdependent/StandaloneBulkDependentTestCustomResource.java @@ -0,0 +1,15 @@ +package io.javaoperatorsdk.operator.sample.standalonebulkdependent; + +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("sbd") +public class StandaloneBulkDependentTestCustomResource + extends CustomResource + implements Namespaced { +} diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/standalonebulkdependent/StandaloneBulkDependentTestSpec.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/standalonebulkdependent/StandaloneBulkDependentTestSpec.java new file mode 100644 index 0000000000..521da411c3 --- /dev/null +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/standalonebulkdependent/StandaloneBulkDependentTestSpec.java @@ -0,0 +1,15 @@ +package io.javaoperatorsdk.operator.sample.standalonebulkdependent; + +public class StandaloneBulkDependentTestSpec { + + private Integer numberOfResources; + + public Integer getNumberOfResources() { + return numberOfResources; + } + + public StandaloneBulkDependentTestSpec setNumberOfResources(Integer numberOfResources) { + this.numberOfResources = numberOfResources; + return this; + } +} From 6892077fd1381af12957dd9c94fb0eb02fef1e9d Mon Sep 17 00:00:00 2001 From: csviri Date: Mon, 12 Sep 2022 10:54:20 +0200 Subject: [PATCH 21/67] wp --- .../dependent/AbstractDependentResource.java | 16 ++++++-- .../ConfigMapBulkDependentResource.java | 38 ++++++------------- 2 files changed, 24 insertions(+), 30 deletions(-) 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 c3c661e093..8e513c9ce2 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 @@ -38,7 +38,7 @@ public AbstractDependentResource() { @Override public ReconcileResult reconcile(P primary, Context

context) { - var count = count(primary, context); + var count = count(primary, context).orElse(1); if (isBulkResourceCreation(primary, context)) { initDiscriminators(count); } @@ -208,12 +208,20 @@ public ResourceDiscriminator getResourceDiscriminator() { return resourceDiscriminator.get(0); } - protected int count(P primary, Context

context) { - return 1; + /** + * @param primary resource + * @param context actual context + * @return empty optional if it's not a bulk resource management, number of instances otherwise. + */ + protected Optional count(P primary, Context

context) { + return Optional.empty(); } + /** + * Override in case the count() is a more heavy + */ protected boolean isBulkResourceCreation(P primary, Context

context) { - return false; + return count(primary, context).isPresent(); } public BulkResourceDiscriminatorFactory getBulkResourceDiscriminatorFactory() { diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/standalonebulkdependent/ConfigMapBulkDependentResource.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/standalonebulkdependent/ConfigMapBulkDependentResource.java index 3df6c7223b..7d13f67b50 100644 --- a/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/standalonebulkdependent/ConfigMapBulkDependentResource.java +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/standalonebulkdependent/ConfigMapBulkDependentResource.java @@ -7,8 +7,6 @@ import io.fabric8.kubernetes.api.model.ConfigMap; import io.fabric8.kubernetes.api.model.ObjectMetaBuilder; import io.javaoperatorsdk.operator.api.reconciler.Context; -import io.javaoperatorsdk.operator.api.reconciler.ResourceDiscriminator; -import io.javaoperatorsdk.operator.processing.dependent.BulkResourceDiscriminatorFactory; import io.javaoperatorsdk.operator.processing.dependent.kubernetes.CRUDKubernetesDependentResource; public class ConfigMapBulkDependentResource @@ -20,22 +18,16 @@ public class ConfigMapBulkDependentResource public ConfigMapBulkDependentResource() { super(ConfigMap.class); setBulkResourceDiscriminatorFactory( - new BulkResourceDiscriminatorFactory() { - @Override - public ResourceDiscriminator createResourceDiscriminator( - int index) { - return (resource, primary, context) -> { - var resources = context.getSecondaryResources(resource).stream() - .filter(r -> r.getMetadata().getName().endsWith("-" + index)) - .collect(Collectors.toList()); - if (resources.isEmpty()) { - return Optional.empty(); - } else if (resources.size() > 1) { - throw new IllegalStateException("More than one resource found for index:" + index); - } else { - return Optional.of(resources.get(0)); - } - }; + index -> (resource, primary, context) -> { + var resources = context.getSecondaryResources(resource).stream() + .filter(r -> r.getMetadata().getName().endsWith("-" + index)) + .collect(Collectors.toList()); + if (resources.isEmpty()) { + return Optional.empty(); + } else if (resources.size() > 1) { + throw new IllegalStateException("More than one resource found for index:" + index); + } else { + return Optional.of(resources.get(0)); } }); } @@ -54,15 +46,9 @@ protected ConfigMap desired(StandaloneBulkDependentTestCustomResource primary, } @Override - protected boolean isBulkResourceCreation(StandaloneBulkDependentTestCustomResource primary, + protected Optional count(StandaloneBulkDependentTestCustomResource primary, Context context) { - return true; - } - - @Override - protected int count(StandaloneBulkDependentTestCustomResource primary, - Context context) { - return primary.getSpec().getNumberOfResources(); + return Optional.of(primary.getSpec().getNumberOfResources()); } From 9d8055bb691938b2cfdf1ffc12b3b730e49940e3 Mon Sep 17 00:00:00 2001 From: csviri Date: Mon, 12 Sep 2022 12:58:39 +0200 Subject: [PATCH 22/67] matcher --- .../dependent/AbstractDependentResource.java | 3 +- .../dependent/DesiredEqualsMatcher.java | 6 + .../processing/dependent/Matcher.java | 15 +++ .../processing/dependent/Updater.java | 1 - .../GenericKubernetesResourceMatcher.java | 103 ++++++++++++------ .../KubernetesDependentResource.java | 5 + 6 files changed, 97 insertions(+), 36 deletions(-) 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 8e513c9ce2..359a0da35f 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 @@ -83,7 +83,6 @@ protected ReconcileResult reconcileWithIndex(P primary, int i, Context

con } else { final var actual = maybeActual.get(); if (updatable) { - // todo simplify matcher? final Matcher.Result match; if (isBulkResourceCreation(primary, context)) { match = updater.match(actual, primary, i, context); @@ -155,7 +154,7 @@ protected R handleCreate(R desired, P primary, Context

context) { } /** - * Allows sub-classes to perform additional processing (e.g. caching) on the created resource if + * Allows subclasses to perform additional processing (e.g. caching) on the created resource if * needed. * * @param primaryResourceId the {@link ResourceID} of the primary resource associated with the diff --git a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/DesiredEqualsMatcher.java b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/DesiredEqualsMatcher.java index 459d7951d6..1d3b34a47b 100644 --- a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/DesiredEqualsMatcher.java +++ b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/DesiredEqualsMatcher.java @@ -16,4 +16,10 @@ public Result match(R actualResource, P primary, Context

context) { var desired = abstractDependentResource.desired(primary, context); return Result.computed(actualResource.equals(desired), desired); } + + @Override + public Result match(R actualResource, P primary, int index, Context

context) { + var desired = abstractDependentResource.desired(primary, index, context); + return Result.computed(actualResource.equals(desired), desired); + } } diff --git a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/Matcher.java b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/Matcher.java index 750fe89cbf..835f76ab3a 100644 --- a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/Matcher.java +++ b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/Matcher.java @@ -95,4 +95,19 @@ public Optional computedDesired() { * {@link Result#computed(boolean, Object)}) */ Result match(R actualResource, P primary, Context

context); + + /** + * Determines whether the specified secondary resource matches the desired state with target index + * of a bulk resource as defined from the specified primary resource, given the specified + * {@link Context}. + * + * @param actualResource the resource we want to determine whether it's matching the desired state + * @param primary the primary resource from which the desired state is inferred + * @param context the context in which the resource is being matched + * @return a {@link Result} encapsulating whether the resource matched its desired state and this + * associated state if it was computed as part of the matching process. Use the static + * convenience methods ({@link Result#nonComputed(boolean)} and + * {@link Result#computed(boolean, Object)}) + */ + Result match(R actualResource, P primary, int index, Context

context); } diff --git a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/Updater.java b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/Updater.java index dd36be0492..06b3cb52f6 100644 --- a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/Updater.java +++ b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/Updater.java @@ -9,7 +9,6 @@ public interface Updater { Result match(R actualResource, P primary, Context

context); - // todo change to simple desired matching? default Result match(R actualResource, P primary, int index, Context

context) { throw new IllegalStateException("Implement this for bulk matching"); } 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 e294b1c938..56b5bc7a59 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 @@ -24,17 +24,42 @@ private GenericKubernetesResourceMatcher(KubernetesDependentResource depen static Matcher matcherFor( Class resourceType, KubernetesDependentResource dependentResource) { if (Secret.class.isAssignableFrom(resourceType)) { - return (actual, primary, context) -> { - final var desired = dependentResource.desired(primary, context); - return Result.computed( - ResourceComparators.compareSecretData((Secret) desired, (Secret) actual), desired); + return new Matcher<>() { + @Override + public Result match(R actualResource, P primary, Context

context) { + final var desired = dependentResource.desired(primary, context); + return Result.computed( + ResourceComparators.compareSecretData((Secret) desired, (Secret) actualResource), + desired); + } + + @Override + public Result match(R actualResource, P primary, int index, Context

context) { + final var desired = dependentResource.desired(primary, context); + return Result.computed( + ResourceComparators.compareSecretData((Secret) desired, (Secret) actualResource), + desired); + } }; } else if (ConfigMap.class.isAssignableFrom(resourceType)) { - return (actual, primary, context) -> { - final var desired = dependentResource.desired(primary, context); - return Result.computed( - ResourceComparators.compareConfigMapData((ConfigMap) desired, (ConfigMap) actual), - desired); + return new Matcher<>() { + @Override + public Result match(R actualResource, P primary, Context

context) { + final var desired = dependentResource.desired(primary, context); + return Result.computed( + ResourceComparators.compareConfigMapData((ConfigMap) desired, + (ConfigMap) actualResource), + desired); + } + + @Override + public Result match(R actualResource, P primary, int index, Context

context) { + final var desired = dependentResource.desired(primary, index, context); + return Result.computed( + ResourceComparators.compareConfigMapData((ConfigMap) desired, + (ConfigMap) actualResource), + desired); + } }; } else { return new GenericKubernetesResourceMatcher(dependentResource); @@ -43,32 +68,18 @@ static Matcher matcherFor( @Override public Result match(R actualResource, P primary, Context

context) { - return match(dependentResource, actualResource, primary, context, false); + var desired = dependentResource.desired(primary, context); + return match(desired, actualResource, false); } - /** - * Determines whether the specified actual resource matches the desired state defined by the - * specified {@link KubernetesDependentResource} based on the observed state of the associated - * specified primary resource. - * - * @param dependentResource the {@link KubernetesDependentResource} implementation used to - * computed the desired state associated with the specified primary resource - * @param actualResource the observed dependent resource for which we want to determine whether it - * matches the desired state or not - * @param primary the primary resource from which we want to compute the desired state - * @param context the {@link Context} instance within which this method is called - * @param considerMetadata {@code true} to consider the metadata of the actual resource when - * determining if it matches the desired state, {@code false} if matching should occur only - * considering the spec of the resources - * @return a {@link io.javaoperatorsdk.operator.processing.dependent.Matcher.Result} object - * @param the type of resource we want to determine whether they match or not - * @param

the type of primary resources associated with the secondary resources we want to - * match - */ - public static Result match( - KubernetesDependentResource dependentResource, R actualResource, P primary, - Context

context, boolean considerMetadata) { - final var desired = dependentResource.desired(primary, context); + @Override + public Result match(R actualResource, P primary, int index, Context

context) { + var desired = dependentResource.desired(primary, index, context); + return match(desired, actualResource, false); + } + + public static Result match( + R desired, R actualResource, boolean considerMetadata) { if (considerMetadata) { final var desiredMetadata = desired.getMetadata(); final var actualMetadata = actualResource.getMetadata(); @@ -95,4 +106,30 @@ public static Result match( } return Result.computed(true, desired); } + + /** + * Determines whether the specified actual resource matches the desired state defined by the + * specified {@link KubernetesDependentResource} based on the observed state of the associated + * specified primary resource. + * + * @param dependentResource the {@link KubernetesDependentResource} implementation used to + * computed the desired state associated with the specified primary resource + * @param actualResource the observed dependent resource for which we want to determine whether it + * matches the desired state or not + * @param primary the primary resource from which we want to compute the desired state + * @param context the {@link Context} instance within which this method is called + * @param considerMetadata {@code true} to consider the metadata of the actual resource when + * determining if it matches the desired state, {@code false} if matching should occur only + * considering the spec of the resources + * @return a {@link io.javaoperatorsdk.operator.processing.dependent.Matcher.Result} object + * @param the type of resource we want to determine whether they match or not + * @param

the type of primary resources associated with the secondary resources we want to + * match + */ + public static Result match( + KubernetesDependentResource dependentResource, R actualResource, P primary, + Context

context, boolean considerMetadata) { + final var desired = dependentResource.desired(primary, context); + return match(desired, actualResource, considerMetadata); + } } 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 298a9b3735..1b81746f8f 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 @@ -228,6 +228,11 @@ protected R desired(P primary, Context

context) { return super.desired(primary, context); } + @Override + protected R desired(P primary, int index, Context

context) { + return super.desired(primary, index, context); + } + private void prepareEventFiltering(R desired, ResourceID resourceID) { eventSource().prepareForCreateOrUpdateEventFiltering(resourceID, desired); } From e222735f3370d8e49f9aba3fe96f863aae471546 Mon Sep 17 00:00:00 2001 From: csviri Date: Mon, 12 Sep 2022 15:43:56 +0200 Subject: [PATCH 23/67] test passes --- .../dependent/AbstractDependentResource.java | 48 +++++++++++++------ .../KubernetesDependentResource.java | 9 ++++ .../operator/StandaloneBulkDependentIT.java | 20 +++++++- .../ConfigMapBulkDependentResource.java | 5 ++ 4 files changed, 67 insertions(+), 15 deletions(-) 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 359a0da35f..89a5c9e767 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 @@ -40,27 +40,41 @@ public AbstractDependentResource() { public ReconcileResult reconcile(P primary, Context

context) { var count = count(primary, context).orElse(1); if (isBulkResourceCreation(primary, context)) { - initDiscriminators(count); + cleanupBulkResourcesIfRequired(count, resourceDiscriminator.size(), primary, context); + adjustDiscriminators(count); } ReconcileResult result = new ReconcileResult<>(); for (int i = 0; i < count; i++) { - var res = reconcileWithIndex(primary, i, context); + var res = reconcileIndexAware(primary, i, context); result.addReconcileResult(res); } return result; } - private void initDiscriminators(int count) { - if (resourceDiscriminator.size() == count) { + private void cleanupBulkResourcesIfRequired(int targetCount, int actualCount, P primary, + Context

context) { + if (targetCount >= actualCount) { return; } - if (resourceDiscriminator.size() < count) { - for (int i = resourceDiscriminator.size() - 1; i < count; i++) { - resourceDiscriminator.add(bulkResourceDiscriminatorFactory.createResourceDiscriminator(i)); - } + for (int i = targetCount; i < actualCount; i++) { + var resource = getSecondaryResourceIndexAware(primary, i, context); + var index = i; + resource.ifPresent(r -> { + deleteBulkResourceWithIndex(primary, r, index, context); + }); + } + } + + protected void deleteBulkResourceWithIndex(P primary, R resource, int i, Context

context) { + throw new IllegalStateException("Implement if handling bulk resources."); + } + + private void adjustDiscriminators(int count) { + if (resourceDiscriminator.size() == count) { + return; } if (resourceDiscriminator.size() < count) { - for (int i = resourceDiscriminator.size() - 1; i < count; i++) { + for (int i = resourceDiscriminator.size(); i < count; i++) { resourceDiscriminator.add(bulkResourceDiscriminatorFactory.createResourceDiscriminator(i)); } } @@ -69,12 +83,12 @@ private void initDiscriminators(int count) { } } - protected ReconcileResult reconcileWithIndex(P primary, int i, Context

context) { - Optional maybeActual = getSecondaryResource(primary, i, context); + protected ReconcileResult reconcileIndexAware(P primary, int i, Context

context) { + Optional maybeActual = getSecondaryResourceIndexAware(primary, i, context); if (creatable || updatable) { if (maybeActual.isEmpty()) { if (creatable) { - var desired = desired(primary, i, context); + var desired = desiredIndexAware(primary, i, context); throwIfNull(desired, primary, "Desired"); logForOperation("Creating", primary, desired); var createdResource = handleCreate(desired, primary, context); @@ -90,7 +104,8 @@ protected ReconcileResult reconcileWithIndex(P primary, int i, Context

con match = updater.match(actual, primary, context); } if (!match.matched()) { - final var desired = match.computedDesired().orElse(desired(primary, context)); + final var desired = + match.computedDesired().orElse(desiredIndexAware(primary, i, context)); throwIfNull(desired, primary, "Desired"); logForOperation("Updating", primary, desired); var updatedResource = handleUpdate(actual, desired, primary, context); @@ -108,6 +123,11 @@ protected ReconcileResult reconcileWithIndex(P primary, int i, Context

con return ReconcileResult.noOperation(maybeActual.orElse(null)); } + private R desiredIndexAware(P primary, int i, Context

context) { + return isBulkResourceCreation(primary, context) ? desired(primary, i, context) + : desired(primary, context); + } + // todo check protected Optional getSecondaryResource(P primary, Context

context) { if (resourceDiscriminator.isEmpty()) { @@ -117,7 +137,7 @@ protected Optional getSecondaryResource(P primary, Context

context) { } } - protected Optional getSecondaryResource(P primary, int index, Context

context) { + protected Optional getSecondaryResourceIndexAware(P primary, int index, Context

context) { if (index > 0 && resourceDiscriminator.isEmpty()) { throw new IllegalStateException( "Handling resources in bulk bot no resource discriminators set."); 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 1b81746f8f..fda4773f31 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 @@ -137,11 +137,20 @@ public Result match(R actualResource, P primary, Context

context) { return matcher.match(actualResource, primary, context); } + public Result match(R actualResource, P primary, int index, Context

context) { + return matcher.match(actualResource, primary, index, context); + } + public void delete(P primary, Context

context) { var resource = getSecondaryResource(primary, context); resource.ifPresent(r -> client.resource(r).delete()); } + @Override + protected void deleteBulkResourceWithIndex(P primary, R resource, int i, Context

context) { + client.resource(resource).delete(); + } + @SuppressWarnings("unchecked") protected NonNamespaceOperation, Resource> prepare(R desired, P primary, String actionName) { diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/StandaloneBulkDependentIT.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/StandaloneBulkDependentIT.java index 006dc9dd53..6e02c838c9 100644 --- a/operator-framework/src/test/java/io/javaoperatorsdk/operator/StandaloneBulkDependentIT.java +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/StandaloneBulkDependentIT.java @@ -27,15 +27,33 @@ class StandaloneBulkDependentIT { @Test void managesBulkConfigMaps() { operator.create(testResource()); + assertNumberOfConfigMaps(3); + updateSpecWithNumber(1); + assertNumberOfConfigMaps(1); + + updateSpecWithNumber(5); + assertNumberOfConfigMaps(5); + + operator.delete(testResource()); + assertNumberOfConfigMaps(0); + } + + void assertNumberOfConfigMaps(int n) { await().untilAsserted(() -> { var cms = operator.getKubernetesClient().configMaps().inNamespace(operator.getNamespace()) .withLabel(LABEL_KEY, LABEL_VALUE) .list().getItems(); - assertThat(cms).hasSize(NUMBER_OF_CONFIG_MAPS); + assertThat(cms).hasSize(n); }); } + private void updateSpecWithNumber(int n) { + var resource = testResource(); + resource.getSpec().setNumberOfResources(n); + operator.replace(resource); + } + private StandaloneBulkDependentTestCustomResource testResource() { StandaloneBulkDependentTestCustomResource cr = new StandaloneBulkDependentTestCustomResource(); cr.setMetadata(new ObjectMeta()); diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/standalonebulkdependent/ConfigMapBulkDependentResource.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/standalonebulkdependent/ConfigMapBulkDependentResource.java index 7d13f67b50..9f6a82ba40 100644 --- a/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/standalonebulkdependent/ConfigMapBulkDependentResource.java +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/standalonebulkdependent/ConfigMapBulkDependentResource.java @@ -4,6 +4,9 @@ import java.util.Optional; import java.util.stream.Collectors; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + import io.fabric8.kubernetes.api.model.ConfigMap; import io.fabric8.kubernetes.api.model.ObjectMetaBuilder; import io.javaoperatorsdk.operator.api.reconciler.Context; @@ -12,6 +15,8 @@ public class ConfigMapBulkDependentResource extends CRUDKubernetesDependentResource { + private final static Logger log = LoggerFactory.getLogger(ConfigMapBulkDependentResource.class); + public static final String LABEL_KEY = "bulk"; public static final String LABEL_VALUE = "true"; From e55592d9dd9a6d8c57c9bbded720230a71713d17 Mon Sep 17 00:00:00 2001 From: csviri Date: Tue, 13 Sep 2022 13:02:26 +0200 Subject: [PATCH 24/67] bulk dependent resource to an interface --- .../dependent/AbstractDependentResource.java | 58 +++++-------------- .../dependent/BulkDependentResource.java | 18 ++++++ .../KubernetesDependentResource.java | 3 +- .../ConfigMapBulkDependentResource.java | 42 ++++++++------ 4 files changed, 56 insertions(+), 65 deletions(-) create mode 100644 operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/BulkDependentResource.java 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 89a5c9e767..08bf2ba3b2 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 @@ -22,24 +22,25 @@ public abstract class AbstractDependentResource protected final boolean creatable = this instanceof Creator; protected final boolean updatable = this instanceof Updater; + protected final boolean bulk = this instanceof BulkDependentResource; protected Creator creator; protected Updater updater; + protected BulkDependentResource bulkDependentResource; protected List> resourceDiscriminator = new ArrayList<>(1); - // used just for bulk creation - protected BulkResourceDiscriminatorFactory bulkResourceDiscriminatorFactory; @SuppressWarnings("unchecked") public AbstractDependentResource() { creator = creatable ? (Creator) this : null; updater = updatable ? (Updater) this : null; + bulkDependentResource = bulk ? (BulkDependentResource) this : null; } @Override public ReconcileResult reconcile(P primary, Context

context) { - var count = count(primary, context).orElse(1); - if (isBulkResourceCreation(primary, context)) { + var count = bulk ? bulkDependentResource.count(primary, context) : 1; + if (bulk) { cleanupBulkResourcesIfRequired(count, resourceDiscriminator.size(), primary, context); adjustDiscriminators(count); } @@ -59,23 +60,19 @@ private void cleanupBulkResourcesIfRequired(int targetCount, int actualCount, P for (int i = targetCount; i < actualCount; i++) { var resource = getSecondaryResourceIndexAware(primary, i, context); var index = i; - resource.ifPresent(r -> { - deleteBulkResourceWithIndex(primary, r, index, context); - }); + resource.ifPresent( + r -> bulkDependentResource.deleteBulkResourceWithIndex(primary, r, index, context)); } } - protected void deleteBulkResourceWithIndex(P primary, R resource, int i, Context

context) { - throw new IllegalStateException("Implement if handling bulk resources."); - } - private void adjustDiscriminators(int count) { if (resourceDiscriminator.size() == count) { return; } if (resourceDiscriminator.size() < count) { for (int i = resourceDiscriminator.size(); i < count; i++) { - resourceDiscriminator.add(bulkResourceDiscriminatorFactory.createResourceDiscriminator(i)); + resourceDiscriminator.add(bulkDependentResource.bulkResourceDiscriminatorFactory() + .createResourceDiscriminator(i)); } } if (resourceDiscriminator.size() > count) { @@ -98,7 +95,7 @@ protected ReconcileResult reconcileIndexAware(P primary, int i, Context

co final var actual = maybeActual.get(); if (updatable) { final Matcher.Result match; - if (isBulkResourceCreation(primary, context)) { + if (bulk) { match = updater.match(actual, primary, i, context); } else { match = updater.match(actual, primary, context); @@ -124,7 +121,7 @@ protected ReconcileResult reconcileIndexAware(P primary, int i, Context

co } private R desiredIndexAware(P primary, int i, Context

context) { - return isBulkResourceCreation(primary, context) ? desired(primary, i, context) + return bulk ? desired(primary, i, context) : desired(primary, context); } @@ -142,7 +139,7 @@ protected Optional getSecondaryResourceIndexAware(P primary, int index, Conte throw new IllegalStateException( "Handling resources in bulk bot no resource discriminators set."); } - if (!isBulkResourceCreation(primary, context)) { + if (!bulk) { return getSecondaryResource(primary, context); } @@ -207,7 +204,7 @@ protected R desired(P primary, Context

context) { } protected R desired(P primary, int index, Context

context) { - if (!isBulkResourceCreation(primary, context)) { + if (!bulk) { return desired(primary, context); } else { throw new IllegalStateException( @@ -223,33 +220,4 @@ public AbstractDependentResource setResourceDiscriminator( return this; } - public ResourceDiscriminator getResourceDiscriminator() { - return resourceDiscriminator.get(0); - } - - /** - * @param primary resource - * @param context actual context - * @return empty optional if it's not a bulk resource management, number of instances otherwise. - */ - protected Optional count(P primary, Context

context) { - return Optional.empty(); - } - - /** - * Override in case the count() is a more heavy - */ - protected boolean isBulkResourceCreation(P primary, Context

context) { - return count(primary, context).isPresent(); - } - - public BulkResourceDiscriminatorFactory getBulkResourceDiscriminatorFactory() { - return bulkResourceDiscriminatorFactory; - } - - public AbstractDependentResource setBulkResourceDiscriminatorFactory( - BulkResourceDiscriminatorFactory bulkResourceDiscriminatorFactory) { - this.bulkResourceDiscriminatorFactory = bulkResourceDiscriminatorFactory; - return this; - } } diff --git a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/BulkDependentResource.java b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/BulkDependentResource.java new file mode 100644 index 0000000000..df5c2b920b --- /dev/null +++ b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/BulkDependentResource.java @@ -0,0 +1,18 @@ +package io.javaoperatorsdk.operator.processing.dependent; + +import io.fabric8.kubernetes.api.model.HasMetadata; +import io.javaoperatorsdk.operator.api.reconciler.Context; + +public interface BulkDependentResource { + + int count(P primary, Context

context); + + default R desired(P primary, int index, Context

context) { + throw new IllegalStateException("Implement if the dependent resource is a creator or updater"); + } + + void deleteBulkResourceWithIndex(P primary, R resource, int i, Context

context); + + BulkResourceDiscriminatorFactory bulkResourceDiscriminatorFactory(); + +} 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 fda4773f31..6299ca7ad8 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 @@ -146,8 +146,7 @@ public void delete(P primary, Context

context) { resource.ifPresent(r -> client.resource(r).delete()); } - @Override - protected void deleteBulkResourceWithIndex(P primary, R resource, int i, Context

context) { + public void deleteBulkResourceWithIndex(P primary, R resource, int i, Context

context) { client.resource(resource).delete(); } diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/standalonebulkdependent/ConfigMapBulkDependentResource.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/standalonebulkdependent/ConfigMapBulkDependentResource.java index 9f6a82ba40..89b69edb41 100644 --- a/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/standalonebulkdependent/ConfigMapBulkDependentResource.java +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/standalonebulkdependent/ConfigMapBulkDependentResource.java @@ -10,35 +10,38 @@ import io.fabric8.kubernetes.api.model.ConfigMap; import io.fabric8.kubernetes.api.model.ObjectMetaBuilder; import io.javaoperatorsdk.operator.api.reconciler.Context; +import io.javaoperatorsdk.operator.processing.dependent.BulkDependentResource; +import io.javaoperatorsdk.operator.processing.dependent.BulkResourceDiscriminatorFactory; import io.javaoperatorsdk.operator.processing.dependent.kubernetes.CRUDKubernetesDependentResource; public class ConfigMapBulkDependentResource - extends CRUDKubernetesDependentResource { + extends CRUDKubernetesDependentResource + implements BulkDependentResource { private final static Logger log = LoggerFactory.getLogger(ConfigMapBulkDependentResource.class); public static final String LABEL_KEY = "bulk"; public static final String LABEL_VALUE = "true"; + private BulkResourceDiscriminatorFactory factory = + index -> (resource, primary, context) -> { + var resources = context.getSecondaryResources(resource).stream() + .filter(r -> r.getMetadata().getName().endsWith("-" + index)) + .collect(Collectors.toList()); + if (resources.isEmpty()) { + return Optional.empty(); + } else if (resources.size() > 1) { + throw new IllegalStateException("More than one resource found for index:" + index); + } else { + return Optional.of(resources.get(0)); + } + }; public ConfigMapBulkDependentResource() { super(ConfigMap.class); - setBulkResourceDiscriminatorFactory( - index -> (resource, primary, context) -> { - var resources = context.getSecondaryResources(resource).stream() - .filter(r -> r.getMetadata().getName().endsWith("-" + index)) - .collect(Collectors.toList()); - if (resources.isEmpty()) { - return Optional.empty(); - } else if (resources.size() > 1) { - throw new IllegalStateException("More than one resource found for index:" + index); - } else { - return Optional.of(resources.get(0)); - } - }); } @Override - protected ConfigMap desired(StandaloneBulkDependentTestCustomResource primary, + public ConfigMap desired(StandaloneBulkDependentTestCustomResource primary, int index, Context context) { ConfigMap configMap = new ConfigMap(); configMap.setMetadata(new ObjectMetaBuilder() @@ -51,10 +54,13 @@ protected ConfigMap desired(StandaloneBulkDependentTestCustomResource primary, } @Override - protected Optional count(StandaloneBulkDependentTestCustomResource primary, + public int count(StandaloneBulkDependentTestCustomResource primary, Context context) { - return Optional.of(primary.getSpec().getNumberOfResources()); + return primary.getSpec().getNumberOfResources(); } - + @Override + public BulkResourceDiscriminatorFactory bulkResourceDiscriminatorFactory() { + return factory; + } } From b0a722775d14319808dce90c8ffd821700881d9e Mon Sep 17 00:00:00 2001 From: csviri Date: Wed, 14 Sep 2022 16:20:45 +0200 Subject: [PATCH 25/67] wip --- .../dependent/kubernetes/KubernetesDependentResource.java | 1 + .../io/javaoperatorsdk/operator/StandaloneBulkDependentIT.java | 3 ++- 2 files changed, 3 insertions(+), 1 deletion(-) 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 6299ca7ad8..c049630c95 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 @@ -141,6 +141,7 @@ public Result match(R actualResource, P primary, int index, Context

contex return matcher.match(actualResource, primary, index, context); } + // todo delete bulk support public void delete(P primary, Context

context) { var resource = getSecondaryResource(primary, context); resource.ifPresent(r -> client.resource(r).delete()); diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/StandaloneBulkDependentIT.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/StandaloneBulkDependentIT.java index 6e02c838c9..4e7c8ae489 100644 --- a/operator-framework/src/test/java/io/javaoperatorsdk/operator/StandaloneBulkDependentIT.java +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/StandaloneBulkDependentIT.java @@ -44,7 +44,8 @@ void assertNumberOfConfigMaps(int n) { var cms = operator.getKubernetesClient().configMaps().inNamespace(operator.getNamespace()) .withLabel(LABEL_KEY, LABEL_VALUE) .list().getItems(); - assertThat(cms).hasSize(n); + assertThat(cms).withFailMessage("Number of items is still: " + cms.size()) + .hasSize(n); }); } From 88f0053d03883799f4ded1bf94559c6c7a07a7ad Mon Sep 17 00:00:00 2001 From: csviri Date: Wed, 14 Sep 2022 16:22:54 +0200 Subject: [PATCH 26/67] test improvement --- .../operator/StandaloneBulkDependentIT.java | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/StandaloneBulkDependentIT.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/StandaloneBulkDependentIT.java index 4e7c8ae489..c2d709ee8a 100644 --- a/operator-framework/src/test/java/io/javaoperatorsdk/operator/StandaloneBulkDependentIT.java +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/StandaloneBulkDependentIT.java @@ -1,5 +1,7 @@ package io.javaoperatorsdk.operator; +import java.time.Duration; + import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.RegisterExtension; @@ -40,13 +42,14 @@ void managesBulkConfigMaps() { } void assertNumberOfConfigMaps(int n) { - await().untilAsserted(() -> { - var cms = operator.getKubernetesClient().configMaps().inNamespace(operator.getNamespace()) - .withLabel(LABEL_KEY, LABEL_VALUE) - .list().getItems(); - assertThat(cms).withFailMessage("Number of items is still: " + cms.size()) - .hasSize(n); - }); + await().atMost(Duration.ofSeconds(20)) + .untilAsserted(() -> { + var cms = operator.getKubernetesClient().configMaps().inNamespace(operator.getNamespace()) + .withLabel(LABEL_KEY, LABEL_VALUE) + .list().getItems(); + assertThat(cms).withFailMessage("Number of items is still: " + cms.size()) + .hasSize(n); + }); } private void updateSpecWithNumber(int n) { From fe3344603743ccece92f708f54289e175aeee1f2 Mon Sep 17 00:00:00 2001 From: csviri Date: Wed, 14 Sep 2022 17:34:36 +0200 Subject: [PATCH 27/67] note --- .../operator/processing/dependent/AbstractDependentResource.java | 1 + 1 file changed, 1 insertion(+) 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 08bf2ba3b2..c08936bc3b 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 @@ -41,6 +41,7 @@ public AbstractDependentResource() { public ReconcileResult reconcile(P primary, Context

context) { var count = bulk ? bulkDependentResource.count(primary, context) : 1; if (bulk) { + //todo do this just if it is deleter? cleanupBulkResourcesIfRequired(count, resourceDiscriminator.size(), primary, context); adjustDiscriminators(count); } From 65705d48ec621a7fa598c2fc71dadaedcde91720 Mon Sep 17 00:00:00 2001 From: csviri Date: Thu, 15 Sep 2022 15:26:32 +0200 Subject: [PATCH 28/67] wip --- .../dependent/AbstractDependentResource.java | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) 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 c08936bc3b..1e665a30bc 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 @@ -41,7 +41,7 @@ public AbstractDependentResource() { public ReconcileResult reconcile(P primary, Context

context) { var count = bulk ? bulkDependentResource.count(primary, context) : 1; if (bulk) { - //todo do this just if it is deleter? + // todo do this just if it is deleter? cleanupBulkResourcesIfRequired(count, resourceDiscriminator.size(), primary, context); adjustDiscriminators(count); } @@ -205,12 +205,8 @@ protected R desired(P primary, Context

context) { } protected R desired(P primary, int index, Context

context) { - if (!bulk) { - return desired(primary, context); - } else { - throw new IllegalStateException( - "desired() with index method must be implemented for bulk DependentResource creation"); - } + throw new IllegalStateException( + "Must be implemented for bulk DependentResource creation"); } public AbstractDependentResource setResourceDiscriminator( From 58c4096a1232ae9e3e35ad758d40af84169b48a7 Mon Sep 17 00:00:00 2001 From: csviri Date: Tue, 20 Sep 2022 14:59:08 +0200 Subject: [PATCH 29/67] rebase on next --- .../processing/dependent/AbstractDependentResource.java | 8 ++++++++ 1 file changed, 8 insertions(+) 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 1e665a30bc..320c95b383 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 @@ -217,4 +217,12 @@ public AbstractDependentResource setResourceDiscriminator( return this; } + public ResourceDiscriminator getResourceDiscriminator() { + if (this.resourceDiscriminator.isEmpty()) { + return null; + } else { + return this.resourceDiscriminator.get(0); + } + } + } From 6a4109bb12d5e3d2fb08ecfde1eb69a3d125cf91 Mon Sep 17 00:00:00 2001 From: csviri Date: Tue, 20 Sep 2022 15:12:04 +0200 Subject: [PATCH 30/67] increates test timeout --- .../io/javaoperatorsdk/operator/StandaloneBulkDependentIT.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/StandaloneBulkDependentIT.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/StandaloneBulkDependentIT.java index c2d709ee8a..7928c44f1a 100644 --- a/operator-framework/src/test/java/io/javaoperatorsdk/operator/StandaloneBulkDependentIT.java +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/StandaloneBulkDependentIT.java @@ -42,7 +42,7 @@ void managesBulkConfigMaps() { } void assertNumberOfConfigMaps(int n) { - await().atMost(Duration.ofSeconds(20)) + await().atMost(Duration.ofSeconds(30)) .untilAsserted(() -> { var cms = operator.getKubernetesClient().configMaps().inNamespace(operator.getNamespace()) .withLabel(LABEL_KEY, LABEL_VALUE) From 5a3ac2d5757feaf539e4a86ee4acb3caa45c839a Mon Sep 17 00:00:00 2001 From: csviri Date: Tue, 20 Sep 2022 15:36:12 +0200 Subject: [PATCH 31/67] comment --- .../io/javaoperatorsdk/operator/StandaloneBulkDependentIT.java | 1 + 1 file changed, 1 insertion(+) diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/StandaloneBulkDependentIT.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/StandaloneBulkDependentIT.java index 7928c44f1a..30e3c82549 100644 --- a/operator-framework/src/test/java/io/javaoperatorsdk/operator/StandaloneBulkDependentIT.java +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/StandaloneBulkDependentIT.java @@ -42,6 +42,7 @@ void managesBulkConfigMaps() { } void assertNumberOfConfigMaps(int n) { + // this test was failing with a lower timeout on GitHub, probably the garbage collection was slower there. await().atMost(Duration.ofSeconds(30)) .untilAsserted(() -> { var cms = operator.getKubernetesClient().configMaps().inNamespace(operator.getNamespace()) From 50c6a0f56007b45fc234e5b16aa726cc4f0f32b1 Mon Sep 17 00:00:00 2001 From: csviri Date: Tue, 20 Sep 2022 16:12:30 +0200 Subject: [PATCH 32/67] fix format --- .../operator/StandaloneBulkDependentIT.java | 13 +++++++------ .../ConfigMapBulkDependentResource.java | 2 +- .../StandaloneBulkDependentReconciler.java | 2 +- .../StandaloneBulkDependentTestCustomResource.java | 2 +- .../StandaloneBulkDependentTestSpec.java | 2 +- 5 files changed, 11 insertions(+), 10 deletions(-) rename operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/{standalonebulkdependent => bulkdependent}/ConfigMapBulkDependentResource.java (97%) rename operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/{standalonebulkdependent => bulkdependent}/StandaloneBulkDependentReconciler.java (96%) rename operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/{standalonebulkdependent => bulkdependent}/StandaloneBulkDependentTestCustomResource.java (87%) rename operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/{standalonebulkdependent => bulkdependent}/StandaloneBulkDependentTestSpec.java (82%) diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/StandaloneBulkDependentIT.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/StandaloneBulkDependentIT.java index 30e3c82549..c30988d00d 100644 --- a/operator-framework/src/test/java/io/javaoperatorsdk/operator/StandaloneBulkDependentIT.java +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/StandaloneBulkDependentIT.java @@ -7,12 +7,12 @@ import io.fabric8.kubernetes.api.model.ObjectMeta; import io.javaoperatorsdk.operator.junit.LocallyRunOperatorExtension; -import io.javaoperatorsdk.operator.sample.standalonebulkdependent.StandaloneBulkDependentReconciler; -import io.javaoperatorsdk.operator.sample.standalonebulkdependent.StandaloneBulkDependentTestCustomResource; -import io.javaoperatorsdk.operator.sample.standalonebulkdependent.StandaloneBulkDependentTestSpec; +import io.javaoperatorsdk.operator.sample.bulkdependent.StandaloneBulkDependentReconciler; +import io.javaoperatorsdk.operator.sample.bulkdependent.StandaloneBulkDependentTestCustomResource; +import io.javaoperatorsdk.operator.sample.bulkdependent.StandaloneBulkDependentTestSpec; -import static io.javaoperatorsdk.operator.sample.standalonebulkdependent.ConfigMapBulkDependentResource.LABEL_KEY; -import static io.javaoperatorsdk.operator.sample.standalonebulkdependent.ConfigMapBulkDependentResource.LABEL_VALUE; +import static io.javaoperatorsdk.operator.sample.bulkdependent.ConfigMapBulkDependentResource.LABEL_KEY; +import static io.javaoperatorsdk.operator.sample.bulkdependent.ConfigMapBulkDependentResource.LABEL_VALUE; import static org.assertj.core.api.Assertions.assertThat; import static org.awaitility.Awaitility.await; @@ -42,7 +42,8 @@ void managesBulkConfigMaps() { } void assertNumberOfConfigMaps(int n) { - // this test was failing with a lower timeout on GitHub, probably the garbage collection was slower there. + // this test was failing with a lower timeout on GitHub, probably the garbage collection was + // slower there. await().atMost(Duration.ofSeconds(30)) .untilAsserted(() -> { var cms = operator.getKubernetesClient().configMaps().inNamespace(operator.getNamespace()) diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/standalonebulkdependent/ConfigMapBulkDependentResource.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/bulkdependent/ConfigMapBulkDependentResource.java similarity index 97% rename from operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/standalonebulkdependent/ConfigMapBulkDependentResource.java rename to operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/bulkdependent/ConfigMapBulkDependentResource.java index 89b69edb41..6daa108624 100644 --- a/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/standalonebulkdependent/ConfigMapBulkDependentResource.java +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/bulkdependent/ConfigMapBulkDependentResource.java @@ -1,4 +1,4 @@ -package io.javaoperatorsdk.operator.sample.standalonebulkdependent; +package io.javaoperatorsdk.operator.sample.bulkdependent; import java.util.Map; import java.util.Optional; diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/standalonebulkdependent/StandaloneBulkDependentReconciler.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/bulkdependent/StandaloneBulkDependentReconciler.java similarity index 96% rename from operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/standalonebulkdependent/StandaloneBulkDependentReconciler.java rename to operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/bulkdependent/StandaloneBulkDependentReconciler.java index bf2b3cbf09..adf3b7e517 100644 --- a/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/standalonebulkdependent/StandaloneBulkDependentReconciler.java +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/bulkdependent/StandaloneBulkDependentReconciler.java @@ -1,4 +1,4 @@ -package io.javaoperatorsdk.operator.sample.standalonebulkdependent; +package io.javaoperatorsdk.operator.sample.bulkdependent; import java.util.Map; import java.util.concurrent.atomic.AtomicInteger; diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/standalonebulkdependent/StandaloneBulkDependentTestCustomResource.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/bulkdependent/StandaloneBulkDependentTestCustomResource.java similarity index 87% rename from operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/standalonebulkdependent/StandaloneBulkDependentTestCustomResource.java rename to operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/bulkdependent/StandaloneBulkDependentTestCustomResource.java index 4ae13f945b..8fc9559a17 100644 --- a/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/standalonebulkdependent/StandaloneBulkDependentTestCustomResource.java +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/bulkdependent/StandaloneBulkDependentTestCustomResource.java @@ -1,4 +1,4 @@ -package io.javaoperatorsdk.operator.sample.standalonebulkdependent; +package io.javaoperatorsdk.operator.sample.bulkdependent; import io.fabric8.kubernetes.api.model.Namespaced; import io.fabric8.kubernetes.client.CustomResource; diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/standalonebulkdependent/StandaloneBulkDependentTestSpec.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/bulkdependent/StandaloneBulkDependentTestSpec.java similarity index 82% rename from operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/standalonebulkdependent/StandaloneBulkDependentTestSpec.java rename to operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/bulkdependent/StandaloneBulkDependentTestSpec.java index 521da411c3..5e48beae5b 100644 --- a/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/standalonebulkdependent/StandaloneBulkDependentTestSpec.java +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/bulkdependent/StandaloneBulkDependentTestSpec.java @@ -1,4 +1,4 @@ -package io.javaoperatorsdk.operator.sample.standalonebulkdependent; +package io.javaoperatorsdk.operator.sample.bulkdependent; public class StandaloneBulkDependentTestSpec { From b9553967f7d536713e4053a1efb4a1f37d23ae62 Mon Sep 17 00:00:00 2001 From: csviri Date: Tue, 20 Sep 2022 16:23:31 +0200 Subject: [PATCH 33/67] wip --- .../ManagedBulkDependentReconciler.java | 25 +++++++++++++++++++ 1 file changed, 25 insertions(+) create mode 100644 operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/bulkdependent/ManagedBulkDependentReconciler.java diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/bulkdependent/ManagedBulkDependentReconciler.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/bulkdependent/ManagedBulkDependentReconciler.java new file mode 100644 index 0000000000..aa5ab84446 --- /dev/null +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/bulkdependent/ManagedBulkDependentReconciler.java @@ -0,0 +1,25 @@ +package io.javaoperatorsdk.operator.sample.bulkdependent; + +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; + +@ControllerConfiguration +public class ManagedBulkDependentReconciler + implements Reconciler { + + private final AtomicInteger numberOfExecutions = new AtomicInteger(0); + + @Override + public UpdateControl reconcile( + StandaloneBulkDependentTestCustomResource resource, + Context context) throws Exception { + + numberOfExecutions.addAndGet(1); + + return UpdateControl.noUpdate(); + } +} From 069290d882097d3d862ab704dc1712404d64a25b Mon Sep 17 00:00:00 2001 From: csviri Date: Wed, 21 Sep 2022 11:17:40 +0200 Subject: [PATCH 34/67] delete, other improvements --- docs/assets/js/uikit.js | 2 +- .../dependent/AbstractDependentResource.java | 6 ++-- .../dependent/BulkDependentResource.java | 28 ++++++++++++++++--- .../KubernetesDependentResource.java | 22 +++++++-------- .../operator/StandaloneBulkDependentIT.java | 1 + .../ManagedBulkDependentReconciler.java | 3 +- 6 files changed, 41 insertions(+), 21 deletions(-) diff --git a/docs/assets/js/uikit.js b/docs/assets/js/uikit.js index 5b1acf9275..665c8c0522 100644 --- a/docs/assets/js/uikit.js +++ b/docs/assets/js/uikit.js @@ -8214,7 +8214,7 @@ updateAria: function(toggled) { attr(this.$el, 'aria-expanded', isBoolean(toggled) - ? toggled + ? toggled// todo delete bulk support : isToggled(this.target, this.cls) ); } 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 320c95b383..bd70435aa4 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 @@ -34,6 +34,7 @@ public abstract class AbstractDependentResource public AbstractDependentResource() { creator = creatable ? (Creator) this : null; updater = updatable ? (Updater) this : null; + bulkDependentResource = bulk ? (BulkDependentResource) this : null; } @@ -41,8 +42,7 @@ public AbstractDependentResource() { public ReconcileResult reconcile(P primary, Context

context) { var count = bulk ? bulkDependentResource.count(primary, context) : 1; if (bulk) { - // todo do this just if it is deleter? - cleanupBulkResourcesIfRequired(count, resourceDiscriminator.size(), primary, context); + deleteBulkResourcesIfRequired(count, resourceDiscriminator.size(), primary, context); adjustDiscriminators(count); } ReconcileResult result = new ReconcileResult<>(); @@ -53,7 +53,7 @@ public ReconcileResult reconcile(P primary, Context

context) { return result; } - private void cleanupBulkResourcesIfRequired(int targetCount, int actualCount, P primary, + protected void deleteBulkResourcesIfRequired(int targetCount, int actualCount, P primary, Context

context) { if (targetCount >= actualCount) { return; diff --git a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/BulkDependentResource.java b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/BulkDependentResource.java index df5c2b920b..c89af4b481 100644 --- a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/BulkDependentResource.java +++ b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/BulkDependentResource.java @@ -2,17 +2,37 @@ import io.fabric8.kubernetes.api.model.HasMetadata; import io.javaoperatorsdk.operator.api.reconciler.Context; +import io.javaoperatorsdk.operator.api.reconciler.dependent.Deleter; -public interface BulkDependentResource { +/** + * Manages dynamic number of resources created for a primary resource. Since the point of a bulk + * dependent resource is to manage the number of secondary resources dynamically it implement + * {@link Creator} and {@link Deleter} interfaces out of the box. A concrete dependent resource can + * implement additionally also {@link Updater}. + */ +public interface BulkDependentResource extends Creator, Deleter

{ + /** + * @return number of resources to create + */ int count(P primary, Context

context); - default R desired(P primary, int index, Context

context) { - throw new IllegalStateException("Implement if the dependent resource is a creator or updater"); - } + R desired(P primary, int index, Context

context); + /** + * Used to delete resource if the desired count is lower than the actual count of a resource. + * + * @param primary resource + * @param resource actual resource from the cache for the index + * @param i index of the resource + * @param context actual context + */ void deleteBulkResourceWithIndex(P primary, R resource, int i, Context

context); + /** + * @return a discriminator factor that helps to create a discriminator for a certain resource with + * an index + */ BulkResourceDiscriminatorFactory bulkResourceDiscriminatorFactory(); } 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 c049630c95..e9a609d5c1 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 @@ -7,9 +7,7 @@ import org.slf4j.LoggerFactory; import io.fabric8.kubernetes.api.model.HasMetadata; -import io.fabric8.kubernetes.api.model.KubernetesResourceList; import io.fabric8.kubernetes.client.KubernetesClient; -import io.fabric8.kubernetes.client.dsl.NonNamespaceOperation; import io.fabric8.kubernetes.client.dsl.Resource; import io.javaoperatorsdk.operator.OperatorException; import io.javaoperatorsdk.operator.api.config.informer.InformerConfiguration; @@ -125,12 +123,12 @@ protected R handleUpdate(R actual, R desired, P primary, Context

context) { @SuppressWarnings("unused") public R create(R target, P primary, Context

context) { - return prepare(target, primary, "Creating").create(target); + return prepare(target, primary, "Creating").create(); } public R update(R actual, R target, P primary, Context

context) { var updatedActual = processor.replaceSpecOnActual(actual, target, context); - return prepare(target, primary, "Updating").replace(updatedActual); + return prepare(updatedActual, primary, "Updating").replace(); } public Result match(R actualResource, P primary, Context

context) { @@ -141,10 +139,13 @@ public Result match(R actualResource, P primary, int index, Context

contex return matcher.match(actualResource, primary, index, context); } - // todo delete bulk support public void delete(P primary, Context

context) { - var resource = getSecondaryResource(primary, context); - resource.ifPresent(r -> client.resource(r).delete()); + if (bulk) { + deleteBulkResourcesIfRequired(0, resourceDiscriminator.size(), primary, context); + } else { + var resource = getSecondaryResource(primary, context); + resource.ifPresent(r -> client.resource(r).delete()); + } } public void deleteBulkResourceWithIndex(P primary, R resource, int i, Context

context) { @@ -152,7 +153,7 @@ public void deleteBulkResourceWithIndex(P primary, R resource, int i, Context

} @SuppressWarnings("unchecked") - protected NonNamespaceOperation, Resource> prepare(R desired, + protected Resource prepare(R desired, P primary, String actionName) { log.debug("{} target resource with type: {}, with id: {}", actionName, @@ -163,8 +164,7 @@ protected NonNamespaceOperation, Resource> prepa } else if (useDefaultAnnotationsToIdentifyPrimary()) { addDefaultSecondaryToPrimaryMapperAnnotations(desired, primary); } - Class targetClass = (Class) desired.getClass(); - return client.resources(targetClass).inNamespace(desired.getMetadata().getNamespace()); + return client.resource(desired).inNamespace(desired.getMetadata().getNamespace()); } @Override @@ -220,8 +220,6 @@ public Class resourceType() { return resourceType; } - - @Override public void setKubernetesClient(KubernetesClient kubernetesClient) { this.client = kubernetesClient; diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/StandaloneBulkDependentIT.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/StandaloneBulkDependentIT.java index c30988d00d..5d9f2e643d 100644 --- a/operator-framework/src/test/java/io/javaoperatorsdk/operator/StandaloneBulkDependentIT.java +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/StandaloneBulkDependentIT.java @@ -26,6 +26,7 @@ class StandaloneBulkDependentIT { LocallyRunOperatorExtension.builder().withReconciler(new StandaloneBulkDependentReconciler()) .build(); + // TODO update, deleter - no GC test, external bulk resource @Test void managesBulkConfigMaps() { operator.create(testResource()); diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/bulkdependent/ManagedBulkDependentReconciler.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/bulkdependent/ManagedBulkDependentReconciler.java index aa5ab84446..466e4cb159 100644 --- a/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/bulkdependent/ManagedBulkDependentReconciler.java +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/bulkdependent/ManagedBulkDependentReconciler.java @@ -6,8 +6,9 @@ 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 +@ControllerConfiguration(dependents = @Dependent(type = ConfigMapBulkDependentResource.class)) public class ManagedBulkDependentReconciler implements Reconciler { From 018e8c569552eb8debf410178cf7c4d07956dd50 Mon Sep 17 00:00:00 2001 From: csviri Date: Wed, 21 Sep 2022 13:36:39 +0200 Subject: [PATCH 35/67] manage tests, refactored ITs --- .../operator/BulkDependentTestBase.java | 68 +++++++++++++++++++ .../operator/ManagedBulkDependentIT.java | 20 ++++++ .../operator/StandaloneBulkDependentIT.java | 64 ++--------------- ...a => BulkDependentTestCustomResource.java} | 4 +- ...stSpec.java => BulkDependentTestSpec.java} | 4 +- .../ConfigMapBulkDependentResource.java | 16 ++--- .../ManagedBulkDependentReconciler.java | 8 +-- .../StandaloneBulkDependentReconciler.java | 12 ++-- 8 files changed, 115 insertions(+), 81 deletions(-) create mode 100644 operator-framework/src/test/java/io/javaoperatorsdk/operator/BulkDependentTestBase.java create mode 100644 operator-framework/src/test/java/io/javaoperatorsdk/operator/ManagedBulkDependentIT.java rename operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/bulkdependent/{StandaloneBulkDependentTestCustomResource.java => BulkDependentTestCustomResource.java} (77%) rename operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/bulkdependent/{StandaloneBulkDependentTestSpec.java => BulkDependentTestSpec.java} (64%) diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/BulkDependentTestBase.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/BulkDependentTestBase.java new file mode 100644 index 0000000000..697cd2fc9b --- /dev/null +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/BulkDependentTestBase.java @@ -0,0 +1,68 @@ +package io.javaoperatorsdk.operator; + +import java.time.Duration; + +import org.junit.jupiter.api.Test; + +import io.fabric8.kubernetes.api.model.ObjectMeta; +import io.javaoperatorsdk.operator.junit.LocallyRunOperatorExtension; +import io.javaoperatorsdk.operator.sample.bulkdependent.BulkDependentTestCustomResource; +import io.javaoperatorsdk.operator.sample.bulkdependent.BulkDependentTestSpec; + +import static io.javaoperatorsdk.operator.sample.bulkdependent.ConfigMapBulkDependentResource.LABEL_KEY; +import static io.javaoperatorsdk.operator.sample.bulkdependent.ConfigMapBulkDependentResource.LABEL_VALUE; +import static org.assertj.core.api.Assertions.assertThat; +import static org.awaitility.Awaitility.await; + +public abstract class BulkDependentTestBase { + + public static final String TEST_RESOURCE_NAME = "test"; + public static final int INITIAL_NUMBER_OF_CONFIG_MAPS = 3; + + @Test + public void managesBulkConfigMaps() { + extension().create(testResource()); + assertNumberOfConfigMaps(3); + + updateSpecWithNumber(1); + assertNumberOfConfigMaps(1); + + updateSpecWithNumber(5); + assertNumberOfConfigMaps(5); + + extension().delete(testResource()); + assertNumberOfConfigMaps(0); + } + + private void assertNumberOfConfigMaps(int n) { + // this test was failing with a lower timeout on GitHub, probably the garbage collection was + // slower there. + await().atMost(Duration.ofSeconds(30)) + .untilAsserted(() -> { + var cms = + extension().getKubernetesClient().configMaps().inNamespace(extension().getNamespace()) + .withLabel(LABEL_KEY, LABEL_VALUE) + .list().getItems(); + assertThat(cms).withFailMessage("Number of items is still: " + cms.size()) + .hasSize(n); + }); + } + + private BulkDependentTestCustomResource testResource() { + BulkDependentTestCustomResource cr = new BulkDependentTestCustomResource(); + cr.setMetadata(new ObjectMeta()); + cr.getMetadata().setName(TEST_RESOURCE_NAME); + cr.setSpec(new BulkDependentTestSpec()); + cr.getSpec().setNumberOfResources(INITIAL_NUMBER_OF_CONFIG_MAPS); + return cr; + } + + + private void updateSpecWithNumber(int n) { + var resource = testResource(); + resource.getSpec().setNumberOfResources(n); + extension().replace(resource); + } + + abstract LocallyRunOperatorExtension extension(); +} diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/ManagedBulkDependentIT.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/ManagedBulkDependentIT.java new file mode 100644 index 0000000000..4c38714dbe --- /dev/null +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/ManagedBulkDependentIT.java @@ -0,0 +1,20 @@ +package io.javaoperatorsdk.operator; + +import org.junit.jupiter.api.extension.RegisterExtension; + +import io.javaoperatorsdk.operator.junit.LocallyRunOperatorExtension; +import io.javaoperatorsdk.operator.sample.bulkdependent.ManagedBulkDependentReconciler; + +class ManagedBulkDependentIT extends BulkDependentTestBase { + + @RegisterExtension + LocallyRunOperatorExtension extension = + LocallyRunOperatorExtension.builder().withReconciler(new ManagedBulkDependentReconciler()) + .build(); + + + @Override + LocallyRunOperatorExtension extension() { + return extension; + } +} diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/StandaloneBulkDependentIT.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/StandaloneBulkDependentIT.java index 5d9f2e643d..b0f999f2a4 100644 --- a/operator-framework/src/test/java/io/javaoperatorsdk/operator/StandaloneBulkDependentIT.java +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/StandaloneBulkDependentIT.java @@ -1,73 +1,19 @@ 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.ObjectMeta; import io.javaoperatorsdk.operator.junit.LocallyRunOperatorExtension; import io.javaoperatorsdk.operator.sample.bulkdependent.StandaloneBulkDependentReconciler; -import io.javaoperatorsdk.operator.sample.bulkdependent.StandaloneBulkDependentTestCustomResource; -import io.javaoperatorsdk.operator.sample.bulkdependent.StandaloneBulkDependentTestSpec; - -import static io.javaoperatorsdk.operator.sample.bulkdependent.ConfigMapBulkDependentResource.LABEL_KEY; -import static io.javaoperatorsdk.operator.sample.bulkdependent.ConfigMapBulkDependentResource.LABEL_VALUE; -import static org.assertj.core.api.Assertions.assertThat; -import static org.awaitility.Awaitility.await; -class StandaloneBulkDependentIT { - - public static final String TEST_RESOURCE_NAME = "test"; - public static final int NUMBER_OF_CONFIG_MAPS = 3; +class StandaloneBulkDependentIT extends BulkDependentTestBase { @RegisterExtension - LocallyRunOperatorExtension operator = + LocallyRunOperatorExtension extension = LocallyRunOperatorExtension.builder().withReconciler(new StandaloneBulkDependentReconciler()) .build(); - // TODO update, deleter - no GC test, external bulk resource - @Test - void managesBulkConfigMaps() { - operator.create(testResource()); - assertNumberOfConfigMaps(3); - - updateSpecWithNumber(1); - assertNumberOfConfigMaps(1); - - updateSpecWithNumber(5); - assertNumberOfConfigMaps(5); - - operator.delete(testResource()); - assertNumberOfConfigMaps(0); + @Override + LocallyRunOperatorExtension extension() { + return extension; } - - void assertNumberOfConfigMaps(int n) { - // this test was failing with a lower timeout on GitHub, probably the garbage collection was - // slower there. - await().atMost(Duration.ofSeconds(30)) - .untilAsserted(() -> { - var cms = operator.getKubernetesClient().configMaps().inNamespace(operator.getNamespace()) - .withLabel(LABEL_KEY, LABEL_VALUE) - .list().getItems(); - assertThat(cms).withFailMessage("Number of items is still: " + cms.size()) - .hasSize(n); - }); - } - - private void updateSpecWithNumber(int n) { - var resource = testResource(); - resource.getSpec().setNumberOfResources(n); - operator.replace(resource); - } - - private StandaloneBulkDependentTestCustomResource testResource() { - StandaloneBulkDependentTestCustomResource cr = new StandaloneBulkDependentTestCustomResource(); - cr.setMetadata(new ObjectMeta()); - cr.getMetadata().setName(TEST_RESOURCE_NAME); - cr.setSpec(new StandaloneBulkDependentTestSpec()); - cr.getSpec().setNumberOfResources(NUMBER_OF_CONFIG_MAPS); - return cr; - } - } diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/bulkdependent/StandaloneBulkDependentTestCustomResource.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/bulkdependent/BulkDependentTestCustomResource.java similarity index 77% rename from operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/bulkdependent/StandaloneBulkDependentTestCustomResource.java rename to operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/bulkdependent/BulkDependentTestCustomResource.java index 8fc9559a17..68e6297f8c 100644 --- a/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/bulkdependent/StandaloneBulkDependentTestCustomResource.java +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/bulkdependent/BulkDependentTestCustomResource.java @@ -9,7 +9,7 @@ @Group("sample.javaoperatorsdk") @Version("v1") @ShortNames("sbd") -public class StandaloneBulkDependentTestCustomResource - extends CustomResource +public class BulkDependentTestCustomResource + extends CustomResource implements Namespaced { } diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/bulkdependent/StandaloneBulkDependentTestSpec.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/bulkdependent/BulkDependentTestSpec.java similarity index 64% rename from operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/bulkdependent/StandaloneBulkDependentTestSpec.java rename to operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/bulkdependent/BulkDependentTestSpec.java index 5e48beae5b..62b8b0fceb 100644 --- a/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/bulkdependent/StandaloneBulkDependentTestSpec.java +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/bulkdependent/BulkDependentTestSpec.java @@ -1,6 +1,6 @@ package io.javaoperatorsdk.operator.sample.bulkdependent; -public class StandaloneBulkDependentTestSpec { +public class BulkDependentTestSpec { private Integer numberOfResources; @@ -8,7 +8,7 @@ public Integer getNumberOfResources() { return numberOfResources; } - public StandaloneBulkDependentTestSpec setNumberOfResources(Integer numberOfResources) { + public BulkDependentTestSpec setNumberOfResources(Integer numberOfResources) { this.numberOfResources = numberOfResources; return this; } diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/bulkdependent/ConfigMapBulkDependentResource.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/bulkdependent/ConfigMapBulkDependentResource.java index 6daa108624..c404f1b4dc 100644 --- a/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/bulkdependent/ConfigMapBulkDependentResource.java +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/bulkdependent/ConfigMapBulkDependentResource.java @@ -15,14 +15,14 @@ import io.javaoperatorsdk.operator.processing.dependent.kubernetes.CRUDKubernetesDependentResource; public class ConfigMapBulkDependentResource - extends CRUDKubernetesDependentResource - implements BulkDependentResource { + extends CRUDKubernetesDependentResource + implements BulkDependentResource { private final static Logger log = LoggerFactory.getLogger(ConfigMapBulkDependentResource.class); public static final String LABEL_KEY = "bulk"; public static final String LABEL_VALUE = "true"; - private BulkResourceDiscriminatorFactory factory = + private BulkResourceDiscriminatorFactory factory = index -> (resource, primary, context) -> { var resources = context.getSecondaryResources(resource).stream() .filter(r -> r.getMetadata().getName().endsWith("-" + index)) @@ -41,8 +41,8 @@ public ConfigMapBulkDependentResource() { } @Override - public ConfigMap desired(StandaloneBulkDependentTestCustomResource primary, - int index, Context context) { + public ConfigMap desired(BulkDependentTestCustomResource primary, + int index, Context context) { ConfigMap configMap = new ConfigMap(); configMap.setMetadata(new ObjectMetaBuilder() .withName(primary.getMetadata().getName() + "-" + index) @@ -54,13 +54,13 @@ public ConfigMap desired(StandaloneBulkDependentTestCustomResource primary, } @Override - public int count(StandaloneBulkDependentTestCustomResource primary, - Context context) { + public int count(BulkDependentTestCustomResource primary, + Context context) { return primary.getSpec().getNumberOfResources(); } @Override - public BulkResourceDiscriminatorFactory bulkResourceDiscriminatorFactory() { + public BulkResourceDiscriminatorFactory bulkResourceDiscriminatorFactory() { return factory; } } diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/bulkdependent/ManagedBulkDependentReconciler.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/bulkdependent/ManagedBulkDependentReconciler.java index 466e4cb159..4d22a6300a 100644 --- a/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/bulkdependent/ManagedBulkDependentReconciler.java +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/bulkdependent/ManagedBulkDependentReconciler.java @@ -10,14 +10,14 @@ @ControllerConfiguration(dependents = @Dependent(type = ConfigMapBulkDependentResource.class)) public class ManagedBulkDependentReconciler - implements Reconciler { + implements Reconciler { private final AtomicInteger numberOfExecutions = new AtomicInteger(0); @Override - public UpdateControl reconcile( - StandaloneBulkDependentTestCustomResource resource, - Context context) throws Exception { + public UpdateControl reconcile( + BulkDependentTestCustomResource resource, + Context context) throws Exception { numberOfExecutions.addAndGet(1); diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/bulkdependent/StandaloneBulkDependentReconciler.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/bulkdependent/StandaloneBulkDependentReconciler.java index adf3b7e517..db6dca8590 100644 --- a/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/bulkdependent/StandaloneBulkDependentReconciler.java +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/bulkdependent/StandaloneBulkDependentReconciler.java @@ -11,8 +11,8 @@ @ControllerConfiguration public class StandaloneBulkDependentReconciler - implements Reconciler, TestExecutionInfoProvider, - EventSourceInitializer, KubernetesClientAware { + implements Reconciler, TestExecutionInfoProvider, + EventSourceInitializer, KubernetesClientAware { private final AtomicInteger numberOfExecutions = new AtomicInteger(0); @@ -24,9 +24,9 @@ public StandaloneBulkDependentReconciler() { } @Override - public UpdateControl reconcile( - StandaloneBulkDependentTestCustomResource resource, - Context context) { + public UpdateControl reconcile( + BulkDependentTestCustomResource resource, + Context context) { numberOfExecutions.addAndGet(1); dependent.reconcile(resource, context); @@ -40,7 +40,7 @@ public int getNumberOfExecutions() { @Override public Map prepareEventSources( - EventSourceContext context) { + EventSourceContext context) { return EventSourceInitializer .nameEventSources(dependent.initEventSource(context)); } From a596b003cc208a60ccc5a79136ecadc770cfe436 Mon Sep 17 00:00:00 2001 From: csviri Date: Wed, 21 Sep 2022 14:50:30 +0200 Subject: [PATCH 36/67] additionl IT --- .../operator/BulkDependentDeleterIT.java | 19 ++++++++++ .../operator/BulkDependentTestBase.java | 37 ++++++++++++++++++- .../bulkdependent/BulkDependentTestSpec.java | 10 +++++ .../CRUDConfigMapBulkDependentResource.java | 7 ++++ ...onfigMapDeleterBulkDependentResource.java} | 28 +++++++++----- .../ManagedBulkDependentReconciler.java | 3 +- .../ManagedDeleterBulkReconciler.java | 20 ++++++++++ .../StandaloneBulkDependentReconciler.java | 4 +- 8 files changed, 112 insertions(+), 16 deletions(-) create mode 100644 operator-framework/src/test/java/io/javaoperatorsdk/operator/BulkDependentDeleterIT.java create mode 100644 operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/bulkdependent/CRUDConfigMapBulkDependentResource.java rename operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/bulkdependent/{ConfigMapBulkDependentResource.java => ConfigMapDeleterBulkDependentResource.java} (68%) create mode 100644 operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/bulkdependent/ManagedDeleterBulkReconciler.java diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/BulkDependentDeleterIT.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/BulkDependentDeleterIT.java new file mode 100644 index 0000000000..dce6df8b98 --- /dev/null +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/BulkDependentDeleterIT.java @@ -0,0 +1,19 @@ +package io.javaoperatorsdk.operator; + +import org.junit.jupiter.api.extension.RegisterExtension; + +import io.javaoperatorsdk.operator.junit.LocallyRunOperatorExtension; +import io.javaoperatorsdk.operator.sample.bulkdependent.ManagedDeleterBulkReconciler; + +public class BulkDependentDeleterIT extends BulkDependentTestBase { + + @RegisterExtension + LocallyRunOperatorExtension extension = + LocallyRunOperatorExtension.builder().withReconciler(new ManagedDeleterBulkReconciler()) + .build(); + + @Override + LocallyRunOperatorExtension extension() { + return extension; + } +} diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/BulkDependentTestBase.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/BulkDependentTestBase.java index 697cd2fc9b..0a5cfee161 100644 --- a/operator-framework/src/test/java/io/javaoperatorsdk/operator/BulkDependentTestBase.java +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/BulkDependentTestBase.java @@ -8,9 +8,10 @@ import io.javaoperatorsdk.operator.junit.LocallyRunOperatorExtension; import io.javaoperatorsdk.operator.sample.bulkdependent.BulkDependentTestCustomResource; import io.javaoperatorsdk.operator.sample.bulkdependent.BulkDependentTestSpec; +import io.javaoperatorsdk.operator.sample.bulkdependent.ConfigMapDeleterBulkDependentResource; -import static io.javaoperatorsdk.operator.sample.bulkdependent.ConfigMapBulkDependentResource.LABEL_KEY; -import static io.javaoperatorsdk.operator.sample.bulkdependent.ConfigMapBulkDependentResource.LABEL_VALUE; +import static io.javaoperatorsdk.operator.sample.bulkdependent.ConfigMapDeleterBulkDependentResource.LABEL_KEY; +import static io.javaoperatorsdk.operator.sample.bulkdependent.ConfigMapDeleterBulkDependentResource.LABEL_VALUE; import static org.assertj.core.api.Assertions.assertThat; import static org.awaitility.Awaitility.await; @@ -18,6 +19,8 @@ public abstract class BulkDependentTestBase { public static final String TEST_RESOURCE_NAME = "test"; public static final int INITIAL_NUMBER_OF_CONFIG_MAPS = 3; + public static final String INITIAL_ADDITIONAL_DATA = "initialData"; + public static final String NEW_VERSION_OF_ADDITIONAL_DATA = "newVersionOfAdditionalData"; @Test public void managesBulkConfigMaps() { @@ -34,6 +37,16 @@ public void managesBulkConfigMaps() { assertNumberOfConfigMaps(0); } + @Test + public void updatesData() { + extension().create(testResource()); + assertNumberOfConfigMaps(3); + assertAdditionalDataOnConfigMaps(INITIAL_ADDITIONAL_DATA); + + updateSpecWithNewAdditionalData(NEW_VERSION_OF_ADDITIONAL_DATA); + assertAdditionalDataOnConfigMaps(NEW_VERSION_OF_ADDITIONAL_DATA); + } + private void assertNumberOfConfigMaps(int n) { // this test was failing with a lower timeout on GitHub, probably the garbage collection was // slower there. @@ -48,15 +61,35 @@ private void assertNumberOfConfigMaps(int n) { }); } + private void assertAdditionalDataOnConfigMaps(String expectedValue) { + await().atMost(Duration.ofSeconds(30)) + .untilAsserted(() -> { + var cms = + extension().getKubernetesClient().configMaps().inNamespace(extension().getNamespace()) + .withLabel(LABEL_KEY, LABEL_VALUE) + .list().getItems(); + cms.forEach(cm -> { + assertThat(cm.getData().get(ConfigMapDeleterBulkDependentResource.ADDITIONAL_DATA_KEY)) + .isEqualTo(expectedValue); + }); + }); + } + private BulkDependentTestCustomResource testResource() { BulkDependentTestCustomResource cr = new BulkDependentTestCustomResource(); cr.setMetadata(new ObjectMeta()); cr.getMetadata().setName(TEST_RESOURCE_NAME); cr.setSpec(new BulkDependentTestSpec()); cr.getSpec().setNumberOfResources(INITIAL_NUMBER_OF_CONFIG_MAPS); + cr.getSpec().setAdditionalData(INITIAL_ADDITIONAL_DATA); return cr; } + private void updateSpecWithNewAdditionalData(String data) { + var resource = testResource(); + resource.getSpec().setAdditionalData(data); + extension().replace(resource); + } private void updateSpecWithNumber(int n) { var resource = testResource(); diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/bulkdependent/BulkDependentTestSpec.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/bulkdependent/BulkDependentTestSpec.java index 62b8b0fceb..5266950b41 100644 --- a/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/bulkdependent/BulkDependentTestSpec.java +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/bulkdependent/BulkDependentTestSpec.java @@ -3,6 +3,7 @@ public class BulkDependentTestSpec { private Integer numberOfResources; + private String additionalData; public Integer getNumberOfResources() { return numberOfResources; @@ -12,4 +13,13 @@ public BulkDependentTestSpec setNumberOfResources(Integer numberOfResources) { this.numberOfResources = numberOfResources; return this; } + + public BulkDependentTestSpec setAdditionalData(String additionalData) { + this.additionalData = additionalData; + return this; + } + + public String getAdditionalData() { + return additionalData; + } } diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/bulkdependent/CRUDConfigMapBulkDependentResource.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/bulkdependent/CRUDConfigMapBulkDependentResource.java new file mode 100644 index 0000000000..83cec0bb69 --- /dev/null +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/bulkdependent/CRUDConfigMapBulkDependentResource.java @@ -0,0 +1,7 @@ +package io.javaoperatorsdk.operator.sample.bulkdependent; + +import io.javaoperatorsdk.operator.api.reconciler.dependent.GarbageCollected; + +public class CRUDConfigMapBulkDependentResource extends ConfigMapDeleterBulkDependentResource + implements GarbageCollected { +} diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/bulkdependent/ConfigMapBulkDependentResource.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/bulkdependent/ConfigMapDeleterBulkDependentResource.java similarity index 68% rename from operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/bulkdependent/ConfigMapBulkDependentResource.java rename to operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/bulkdependent/ConfigMapDeleterBulkDependentResource.java index c404f1b4dc..eab06ac61d 100644 --- a/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/bulkdependent/ConfigMapBulkDependentResource.java +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/bulkdependent/ConfigMapDeleterBulkDependentResource.java @@ -4,24 +4,31 @@ import java.util.Optional; import java.util.stream.Collectors; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - import io.fabric8.kubernetes.api.model.ConfigMap; import io.fabric8.kubernetes.api.model.ObjectMetaBuilder; import io.javaoperatorsdk.operator.api.reconciler.Context; +import io.javaoperatorsdk.operator.api.reconciler.dependent.Deleter; import io.javaoperatorsdk.operator.processing.dependent.BulkDependentResource; import io.javaoperatorsdk.operator.processing.dependent.BulkResourceDiscriminatorFactory; +import io.javaoperatorsdk.operator.processing.dependent.Creator; +import io.javaoperatorsdk.operator.processing.dependent.Updater; import io.javaoperatorsdk.operator.processing.dependent.kubernetes.CRUDKubernetesDependentResource; +import io.javaoperatorsdk.operator.processing.dependent.kubernetes.KubernetesDependentResource; -public class ConfigMapBulkDependentResource - extends CRUDKubernetesDependentResource - implements BulkDependentResource { - - private final static Logger log = LoggerFactory.getLogger(ConfigMapBulkDependentResource.class); +/** + * Not using CRUDKubernetesDependentResource so the delete functionality can be tested. + */ +public class ConfigMapDeleterBulkDependentResource + extends + KubernetesDependentResource + implements Creator, + Updater, + Deleter, + BulkDependentResource { public static final String LABEL_KEY = "bulk"; public static final String LABEL_VALUE = "true"; + public static final String ADDITIONAL_DATA_KEY = "additionalData"; private BulkResourceDiscriminatorFactory factory = index -> (resource, primary, context) -> { var resources = context.getSecondaryResources(resource).stream() @@ -36,7 +43,7 @@ public class ConfigMapBulkDependentResource } }; - public ConfigMapBulkDependentResource() { + public ConfigMapDeleterBulkDependentResource() { super(ConfigMap.class); } @@ -49,7 +56,8 @@ public ConfigMap desired(BulkDependentTestCustomResource primary, .withNamespace(primary.getMetadata().getNamespace()) .withLabels(Map.of(LABEL_KEY, LABEL_VALUE)) .build()); - configMap.setData(Map.of("number", "" + index)); + configMap.setData( + Map.of("number", "" + index, ADDITIONAL_DATA_KEY, primary.getSpec().getAdditionalData())); return configMap; } diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/bulkdependent/ManagedBulkDependentReconciler.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/bulkdependent/ManagedBulkDependentReconciler.java index 4d22a6300a..3b2acd942e 100644 --- a/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/bulkdependent/ManagedBulkDependentReconciler.java +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/bulkdependent/ManagedBulkDependentReconciler.java @@ -8,7 +8,7 @@ import io.javaoperatorsdk.operator.api.reconciler.UpdateControl; import io.javaoperatorsdk.operator.api.reconciler.dependent.Dependent; -@ControllerConfiguration(dependents = @Dependent(type = ConfigMapBulkDependentResource.class)) +@ControllerConfiguration(dependents = @Dependent(type = CRUDConfigMapBulkDependentResource.class)) public class ManagedBulkDependentReconciler implements Reconciler { @@ -20,7 +20,6 @@ public UpdateControl reconcile( Context context) throws Exception { numberOfExecutions.addAndGet(1); - return UpdateControl.noUpdate(); } } diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/bulkdependent/ManagedDeleterBulkReconciler.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/bulkdependent/ManagedDeleterBulkReconciler.java new file mode 100644 index 0000000000..e759bdd200 --- /dev/null +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/bulkdependent/ManagedDeleterBulkReconciler.java @@ -0,0 +1,20 @@ +package io.javaoperatorsdk.operator.sample.bulkdependent; + +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 = ConfigMapDeleterBulkDependentResource.class)) +public class ManagedDeleterBulkReconciler implements Reconciler { + @Override + public UpdateControl reconcile( + BulkDependentTestCustomResource resource, + Context context) + throws Exception { + + return UpdateControl.noUpdate(); + } +} diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/bulkdependent/StandaloneBulkDependentReconciler.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/bulkdependent/StandaloneBulkDependentReconciler.java index db6dca8590..4033583340 100644 --- a/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/bulkdependent/StandaloneBulkDependentReconciler.java +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/bulkdependent/StandaloneBulkDependentReconciler.java @@ -16,11 +16,11 @@ public class StandaloneBulkDependentReconciler private final AtomicInteger numberOfExecutions = new AtomicInteger(0); - private ConfigMapBulkDependentResource dependent; + private ConfigMapDeleterBulkDependentResource dependent; private KubernetesClient kubernetesClient; public StandaloneBulkDependentReconciler() { - dependent = new ConfigMapBulkDependentResource(); + dependent = new CRUDConfigMapBulkDependentResource(); } @Override From bd6b9e7981236924a5780307f4229239836a9e3c Mon Sep 17 00:00:00 2001 From: csviri Date: Wed, 21 Sep 2022 17:06:00 +0200 Subject: [PATCH 37/67] external resource --- .../dependent/AbstractDependentResource.java | 6 +- .../processing/dependent/BulkUpdater.java | 13 +++ .../KubernetesDependentResource.java | 2 +- .../ExternalBulkDependentResource.java | 104 ++++++++++++++++++ .../external/ExternalResource.java | 37 +++++++ .../external/ExternalServiceMock.java | 39 +++++++ 6 files changed, 199 insertions(+), 2 deletions(-) create mode 100644 operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/BulkUpdater.java create mode 100644 operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/bulkdependent/external/ExternalBulkDependentResource.java create mode 100644 operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/bulkdependent/external/ExternalResource.java create mode 100644 operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/bulkdependent/external/ExternalServiceMock.java 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 bd70435aa4..e1a4c6ec21 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 @@ -42,7 +42,7 @@ public AbstractDependentResource() { public ReconcileResult reconcile(P primary, Context

context) { var count = bulk ? bulkDependentResource.count(primary, context) : 1; if (bulk) { - deleteBulkResourcesIfRequired(count, resourceDiscriminator.size(), primary, context); + deleteBulkResourcesIfRequired(count, lastKnownBulkSize(), primary, context); adjustDiscriminators(count); } ReconcileResult result = new ReconcileResult<>(); @@ -225,4 +225,8 @@ public ResourceDiscriminator getResourceDiscriminator() { } } + protected int lastKnownBulkSize() { + return resourceDiscriminator.size(); + } + } diff --git a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/BulkUpdater.java b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/BulkUpdater.java new file mode 100644 index 0000000000..d5ad2957d2 --- /dev/null +++ b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/BulkUpdater.java @@ -0,0 +1,13 @@ +package io.javaoperatorsdk.operator.processing.dependent; + +import io.fabric8.kubernetes.api.model.HasMetadata; +import io.javaoperatorsdk.operator.api.reconciler.Context; + +public interface BulkUpdater extends Updater { + + default Matcher.Result match(R actualResource, P primary, Context

context) { + throw new IllegalStateException(); + } + + Matcher.Result match(R actualResource, P primary, int index, Context

context); +} 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 e9a609d5c1..26a7313f08 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 @@ -141,7 +141,7 @@ public Result match(R actualResource, P primary, int index, Context

contex public void delete(P primary, Context

context) { if (bulk) { - deleteBulkResourcesIfRequired(0, resourceDiscriminator.size(), primary, context); + deleteBulkResourcesIfRequired(0, lastKnownBulkSize(), primary, context); } else { var resource = getSecondaryResource(primary, context); resource.ifPresent(r -> client.resource(r).delete()); diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/bulkdependent/external/ExternalBulkDependentResource.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/bulkdependent/external/ExternalBulkDependentResource.java new file mode 100644 index 0000000000..fc95bebdb7 --- /dev/null +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/bulkdependent/external/ExternalBulkDependentResource.java @@ -0,0 +1,104 @@ +package io.javaoperatorsdk.operator.sample.bulkdependent.external; + +import java.util.HashMap; +import java.util.Map; +import java.util.Optional; +import java.util.Set; +import java.util.stream.Collectors; + +import io.javaoperatorsdk.operator.api.reconciler.Context; +import io.javaoperatorsdk.operator.processing.dependent.*; +import io.javaoperatorsdk.operator.processing.dependent.external.PollingDependentResource; +import io.javaoperatorsdk.operator.processing.event.ResourceID; +import io.javaoperatorsdk.operator.processing.event.source.CacheKeyMapper; +import io.javaoperatorsdk.operator.sample.bulkdependent.BulkDependentTestCustomResource; + +public class ExternalBulkDependentResource + extends PollingDependentResource + implements BulkDependentResource, + BulkUpdater { + + public static final String EXTERNAL_RESOURCE_NAME_DELIMITER = "#"; + + private ExternalServiceMock externalServiceMock = ExternalServiceMock.getInstance(); + + public ExternalBulkDependentResource() { + super(ExternalResource.class, CacheKeyMapper.singleResourceCacheKeyMapper()); + } + + @Override + public Map> fetchResources() { + // todo + Map> result = new HashMap<>(); + var resources = externalServiceMock.listResources(); + + return result; + } + + @Override + public void delete(BulkDependentTestCustomResource primary, + Context context) { + deleteBulkResourcesIfRequired(0, lastKnownBulkSize(), primary, context); + } + + @Override + public int count(BulkDependentTestCustomResource primary, + Context context) { + return primary.getSpec().getNumberOfResources(); + } + + @Override + public void deleteBulkResourceWithIndex(BulkDependentTestCustomResource primary, + ExternalResource resource, int i, Context context) { + externalServiceMock.delete(resource.getId()); + } + + @Override + public BulkResourceDiscriminatorFactory bulkResourceDiscriminatorFactory() { + return index -> (resource, primary, context) -> { + ExternalResource collect = context.getSecondaryResources(resource).stream() + .filter(r -> r.getId().endsWith(EXTERNAL_RESOURCE_NAME_DELIMITER + index)) + .collect(Collectors.collectingAndThen( + Collectors.toList(), + list -> { + if (list.size() > 1) { + throw new IllegalStateException("Found more than 1 object: " + list); + } + return list.get(0); + })); + return Optional.ofNullable(collect); + }; + } + + @Override + public ExternalResource desired(BulkDependentTestCustomResource primary, int index, + Context context) { + return new ExternalResource(toExternalResourceId(primary, index), "" + index); + } + + @Override + public ExternalResource create(ExternalResource desired, BulkDependentTestCustomResource primary, + Context context) { + return externalServiceMock.create(desired); + } + + @Override + public ExternalResource update(ExternalResource actual, ExternalResource desired, + BulkDependentTestCustomResource primary, Context context) { + return externalServiceMock.update(desired); + } + + @Override + public Matcher.Result match(ExternalResource actualResource, + BulkDependentTestCustomResource primary, + int index, Context context) { + var desired = desired(primary, index, context); + return Matcher.Result.computed(desired.equals(actualResource), desired); + } + + private static String toExternalResourceId(BulkDependentTestCustomResource primary, int i) { + return primary.getMetadata().getName() + EXTERNAL_RESOURCE_NAME_DELIMITER + + primary.getMetadata().getNamespace() + + EXTERNAL_RESOURCE_NAME_DELIMITER + i; + } +} diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/bulkdependent/external/ExternalResource.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/bulkdependent/external/ExternalResource.java new file mode 100644 index 0000000000..935fd99e47 --- /dev/null +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/bulkdependent/external/ExternalResource.java @@ -0,0 +1,37 @@ +package io.javaoperatorsdk.operator.sample.bulkdependent.external; + +import java.util.Objects; + +public class ExternalResource { + + private String id; + private String data; + + public ExternalResource(String id, String data) { + this.id = id; + this.data = data; + } + + public String getId() { + return id; + } + + public String getData() { + return data; + } + + @Override + public boolean equals(Object o) { + if (this == o) + return true; + if (o == null || getClass() != o.getClass()) + return false; + ExternalResource that = (ExternalResource) o; + return Objects.equals(id, that.id) && Objects.equals(data, that.data); + } + + @Override + public int hashCode() { + return Objects.hash(id, data); + } +} diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/bulkdependent/external/ExternalServiceMock.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/bulkdependent/external/ExternalServiceMock.java new file mode 100644 index 0000000000..e73062ccf2 --- /dev/null +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/bulkdependent/external/ExternalServiceMock.java @@ -0,0 +1,39 @@ +package io.javaoperatorsdk.operator.sample.bulkdependent.external; + +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.concurrent.ConcurrentHashMap; + +public class ExternalServiceMock { + + private static ExternalServiceMock serviceMock = new ExternalServiceMock(); + + private Map resourceMap = new ConcurrentHashMap<>(); + + public ExternalResource create(ExternalResource externalResource) { + resourceMap.put(externalResource.getId(), externalResource); + return externalResource; + } + + public Optional read(String id) { + return Optional.ofNullable(resourceMap.get(id)); + } + + public ExternalResource update(ExternalResource externalResource) { + return resourceMap.put(externalResource.getId(), externalResource); + } + + public Optional delete(String id) { + return Optional.ofNullable(resourceMap.remove(id)); + } + + public List listResources() { + return new ArrayList<>(resourceMap.values()); + } + + public static ExternalServiceMock getInstance() { + return serviceMock; + } +} From 50a50b94a6c74664f632bd1ad0d9ad9eeded2f4c Mon Sep 17 00:00:00 2001 From: csviri Date: Thu, 22 Sep 2022 14:44:08 +0200 Subject: [PATCH 38/67] external resource IT --- .../BulkDependentDeleterIT.java | 2 +- .../BulkDependentTestBase.java | 17 +++++- .../BulkExternalDependentIT.java | 56 +++++++++++++++++++ .../ManagedBulkDependentIT.java | 2 +- .../StandaloneBulkDependentIT.java | 2 +- .../ExternalBulkDependentResource.java | 35 ++++++------ .../ExternalBulkResourceReconciler.java | 19 +++++++ 7 files changed, 109 insertions(+), 24 deletions(-) rename operator-framework/src/test/java/io/javaoperatorsdk/operator/{ => bulkdependent}/BulkDependentDeleterIT.java (91%) rename operator-framework/src/test/java/io/javaoperatorsdk/operator/{ => bulkdependent}/BulkDependentTestBase.java (86%) create mode 100644 operator-framework/src/test/java/io/javaoperatorsdk/operator/bulkdependent/BulkExternalDependentIT.java rename operator-framework/src/test/java/io/javaoperatorsdk/operator/{ => bulkdependent}/ManagedBulkDependentIT.java (91%) rename operator-framework/src/test/java/io/javaoperatorsdk/operator/{ => bulkdependent}/StandaloneBulkDependentIT.java (91%) create mode 100644 operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/bulkdependent/external/ExternalBulkResourceReconciler.java diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/BulkDependentDeleterIT.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/bulkdependent/BulkDependentDeleterIT.java similarity index 91% rename from operator-framework/src/test/java/io/javaoperatorsdk/operator/BulkDependentDeleterIT.java rename to operator-framework/src/test/java/io/javaoperatorsdk/operator/bulkdependent/BulkDependentDeleterIT.java index dce6df8b98..a934bdd1f3 100644 --- a/operator-framework/src/test/java/io/javaoperatorsdk/operator/BulkDependentDeleterIT.java +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/bulkdependent/BulkDependentDeleterIT.java @@ -1,4 +1,4 @@ -package io.javaoperatorsdk.operator; +package io.javaoperatorsdk.operator.bulkdependent; import org.junit.jupiter.api.extension.RegisterExtension; diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/BulkDependentTestBase.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/bulkdependent/BulkDependentTestBase.java similarity index 86% rename from operator-framework/src/test/java/io/javaoperatorsdk/operator/BulkDependentTestBase.java rename to operator-framework/src/test/java/io/javaoperatorsdk/operator/bulkdependent/BulkDependentTestBase.java index 0a5cfee161..605731623c 100644 --- a/operator-framework/src/test/java/io/javaoperatorsdk/operator/BulkDependentTestBase.java +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/bulkdependent/BulkDependentTestBase.java @@ -1,4 +1,4 @@ -package io.javaoperatorsdk.operator; +package io.javaoperatorsdk.operator.bulkdependent; import java.time.Duration; @@ -75,7 +75,7 @@ private void assertAdditionalDataOnConfigMaps(String expectedValue) { }); } - private BulkDependentTestCustomResource testResource() { + public static BulkDependentTestCustomResource testResource() { BulkDependentTestCustomResource cr = new BulkDependentTestCustomResource(); cr.setMetadata(new ObjectMeta()); cr.getMetadata().setName(TEST_RESOURCE_NAME); @@ -91,11 +91,24 @@ private void updateSpecWithNewAdditionalData(String data) { extension().replace(resource); } + public static void updateSpecWithNewAdditionalData(LocallyRunOperatorExtension extension, + String data) { + var resource = testResource(); + resource.getSpec().setAdditionalData(data); + extension.replace(resource); + } + private void updateSpecWithNumber(int n) { var resource = testResource(); resource.getSpec().setNumberOfResources(n); extension().replace(resource); } + public static void updateSpecWithNumber(LocallyRunOperatorExtension extension, int n) { + var resource = testResource(); + resource.getSpec().setNumberOfResources(n); + extension.replace(resource); + } + abstract LocallyRunOperatorExtension extension(); } diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/bulkdependent/BulkExternalDependentIT.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/bulkdependent/BulkExternalDependentIT.java new file mode 100644 index 0000000000..29f66e8205 --- /dev/null +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/bulkdependent/BulkExternalDependentIT.java @@ -0,0 +1,56 @@ +package io.javaoperatorsdk.operator.bulkdependent; + +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.RegisterExtension; + +import io.javaoperatorsdk.operator.junit.LocallyRunOperatorExtension; +import io.javaoperatorsdk.operator.sample.bulkdependent.external.ExternalBulkResourceReconciler; +import io.javaoperatorsdk.operator.sample.bulkdependent.external.ExternalServiceMock; + +import static io.javaoperatorsdk.operator.bulkdependent.BulkDependentTestBase.*; +import static org.assertj.core.api.Assertions.assertThat; +import static org.awaitility.Awaitility.await; + +class BulkExternalDependentIT { + + @RegisterExtension + LocallyRunOperatorExtension extension = + LocallyRunOperatorExtension.builder().withReconciler(new ExternalBulkResourceReconciler()) + .build(); + + ExternalServiceMock externalServiceMock = ExternalServiceMock.getInstance(); + + @Test + void managesExternalBulkResources() { + extension.create(testResource()); + assertResourceNumberAndData(3, INITIAL_ADDITIONAL_DATA); + + updateSpecWithNumber(extension, 1); + assertResourceNumberAndData(1, INITIAL_ADDITIONAL_DATA); + + updateSpecWithNumber(extension, 5); + assertResourceNumberAndData(5, INITIAL_ADDITIONAL_DATA); + + extension.delete(testResource()); + assertResourceNumberAndData(0, INITIAL_ADDITIONAL_DATA); + } + + + @Test + void handlesResourceUpdates() { + extension.create(testResource()); + assertResourceNumberAndData(3, INITIAL_ADDITIONAL_DATA); + + updateSpecWithNewAdditionalData(extension, NEW_VERSION_OF_ADDITIONAL_DATA); + assertResourceNumberAndData(3, NEW_VERSION_OF_ADDITIONAL_DATA); + } + + private void assertResourceNumberAndData(int n, String data) { + await().untilAsserted(() -> { + var resources = externalServiceMock.listResources(); + assertThat(resources).hasSize(n); + assertThat(resources).allMatch(r -> r.getData().equals(data)); + }); + } + +} diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/ManagedBulkDependentIT.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/bulkdependent/ManagedBulkDependentIT.java similarity index 91% rename from operator-framework/src/test/java/io/javaoperatorsdk/operator/ManagedBulkDependentIT.java rename to operator-framework/src/test/java/io/javaoperatorsdk/operator/bulkdependent/ManagedBulkDependentIT.java index 4c38714dbe..7f074ac8f5 100644 --- a/operator-framework/src/test/java/io/javaoperatorsdk/operator/ManagedBulkDependentIT.java +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/bulkdependent/ManagedBulkDependentIT.java @@ -1,4 +1,4 @@ -package io.javaoperatorsdk.operator; +package io.javaoperatorsdk.operator.bulkdependent; import org.junit.jupiter.api.extension.RegisterExtension; diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/StandaloneBulkDependentIT.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/bulkdependent/StandaloneBulkDependentIT.java similarity index 91% rename from operator-framework/src/test/java/io/javaoperatorsdk/operator/StandaloneBulkDependentIT.java rename to operator-framework/src/test/java/io/javaoperatorsdk/operator/bulkdependent/StandaloneBulkDependentIT.java index b0f999f2a4..683cc1662b 100644 --- a/operator-framework/src/test/java/io/javaoperatorsdk/operator/StandaloneBulkDependentIT.java +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/bulkdependent/StandaloneBulkDependentIT.java @@ -1,4 +1,4 @@ -package io.javaoperatorsdk.operator; +package io.javaoperatorsdk.operator.bulkdependent; import org.junit.jupiter.api.extension.RegisterExtension; diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/bulkdependent/external/ExternalBulkDependentResource.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/bulkdependent/external/ExternalBulkDependentResource.java index fc95bebdb7..0638c658c2 100644 --- a/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/bulkdependent/external/ExternalBulkDependentResource.java +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/bulkdependent/external/ExternalBulkDependentResource.java @@ -1,16 +1,12 @@ package io.javaoperatorsdk.operator.sample.bulkdependent.external; -import java.util.HashMap; -import java.util.Map; -import java.util.Optional; -import java.util.Set; +import java.util.*; import java.util.stream.Collectors; import io.javaoperatorsdk.operator.api.reconciler.Context; import io.javaoperatorsdk.operator.processing.dependent.*; import io.javaoperatorsdk.operator.processing.dependent.external.PollingDependentResource; import io.javaoperatorsdk.operator.processing.event.ResourceID; -import io.javaoperatorsdk.operator.processing.event.source.CacheKeyMapper; import io.javaoperatorsdk.operator.sample.bulkdependent.BulkDependentTestCustomResource; public class ExternalBulkDependentResource @@ -23,15 +19,18 @@ public class ExternalBulkDependentResource private ExternalServiceMock externalServiceMock = ExternalServiceMock.getInstance(); public ExternalBulkDependentResource() { - super(ExternalResource.class, CacheKeyMapper.singleResourceCacheKeyMapper()); + super(ExternalResource.class, ExternalResource::getId); } @Override public Map> fetchResources() { - // todo Map> result = new HashMap<>(); var resources = externalServiceMock.listResources(); - + resources.stream().forEach(er -> { + var resourceID = toResourceID(er); + result.putIfAbsent(resourceID, new HashSet<>()); + result.get(resourceID).add(er); + }); return result; } @@ -56,24 +55,17 @@ public void deleteBulkResourceWithIndex(BulkDependentTestCustomResource primary, @Override public BulkResourceDiscriminatorFactory bulkResourceDiscriminatorFactory() { return index -> (resource, primary, context) -> { - ExternalResource collect = context.getSecondaryResources(resource).stream() + return context.getSecondaryResources(resource).stream() .filter(r -> r.getId().endsWith(EXTERNAL_RESOURCE_NAME_DELIMITER + index)) - .collect(Collectors.collectingAndThen( - Collectors.toList(), - list -> { - if (list.size() > 1) { - throw new IllegalStateException("Found more than 1 object: " + list); - } - return list.get(0); - })); - return Optional.ofNullable(collect); + .collect(Collectors.toList()).stream().findFirst(); }; } @Override public ExternalResource desired(BulkDependentTestCustomResource primary, int index, Context context) { - return new ExternalResource(toExternalResourceId(primary, index), "" + index); + return new ExternalResource(toExternalResourceId(primary, index), + primary.getSpec().getAdditionalData()); } @Override @@ -101,4 +93,9 @@ private static String toExternalResourceId(BulkDependentTestCustomResource prima primary.getMetadata().getNamespace() + EXTERNAL_RESOURCE_NAME_DELIMITER + i; } + + private ResourceID toResourceID(ExternalResource externalResource) { + var parts = externalResource.getId().split(EXTERNAL_RESOURCE_NAME_DELIMITER); + return new ResourceID(parts[0], parts[1]); + } } diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/bulkdependent/external/ExternalBulkResourceReconciler.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/bulkdependent/external/ExternalBulkResourceReconciler.java new file mode 100644 index 0000000000..2543422d74 --- /dev/null +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/bulkdependent/external/ExternalBulkResourceReconciler.java @@ -0,0 +1,19 @@ +package io.javaoperatorsdk.operator.sample.bulkdependent.external; + +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; +import io.javaoperatorsdk.operator.sample.bulkdependent.BulkDependentTestCustomResource; + +@ControllerConfiguration(dependents = @Dependent(type = ExternalBulkDependentResource.class)) +public class ExternalBulkResourceReconciler implements Reconciler { + + @Override + public UpdateControl reconcile( + BulkDependentTestCustomResource resource, Context context) + throws Exception { + return UpdateControl.noUpdate(); + } +} From 3d07b2e1c7eb1e321e5b95e6aa0b8f481e583abe Mon Sep 17 00:00:00 2001 From: csviri Date: Thu, 22 Sep 2022 14:55:47 +0200 Subject: [PATCH 39/67] docs --- .../dependent/AbstractDependentResource.java | 12 ++---------- .../operator/processing/dependent/BulkUpdater.java | 7 +++++++ 2 files changed, 9 insertions(+), 10 deletions(-) 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 e1a4c6ec21..1d16b79222 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 @@ -82,7 +82,8 @@ private void adjustDiscriminators(int count) { } protected ReconcileResult reconcileIndexAware(P primary, int i, Context

context) { - Optional maybeActual = getSecondaryResourceIndexAware(primary, i, context); + Optional maybeActual = bulk ? getSecondaryResourceIndexAware(primary, i, context) + : getSecondaryResource(primary, context); if (creatable || updatable) { if (maybeActual.isEmpty()) { if (creatable) { @@ -126,7 +127,6 @@ private R desiredIndexAware(P primary, int i, Context

context) { : desired(primary, context); } - // todo check protected Optional getSecondaryResource(P primary, Context

context) { if (resourceDiscriminator.isEmpty()) { return context.getSecondaryResource(resourceType()); @@ -136,14 +136,6 @@ protected Optional getSecondaryResource(P primary, Context

context) { } protected Optional getSecondaryResourceIndexAware(P primary, int index, Context

context) { - if (index > 0 && resourceDiscriminator.isEmpty()) { - throw new IllegalStateException( - "Handling resources in bulk bot no resource discriminators set."); - } - if (!bulk) { - return getSecondaryResource(primary, context); - } - return context.getSecondaryResource(resourceType(), resourceDiscriminator.get(index)); } diff --git a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/BulkUpdater.java b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/BulkUpdater.java index d5ad2957d2..9c00b47d0c 100644 --- a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/BulkUpdater.java +++ b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/BulkUpdater.java @@ -3,6 +3,13 @@ import io.fabric8.kubernetes.api.model.HasMetadata; import io.javaoperatorsdk.operator.api.reconciler.Context; +/** + * Helper for the Bulk Dependent Resources to make it more explicit that bulk needs to only + * implement the index aware match method. + * + * @param secondary resource type + * @param

primary resource type + */ public interface BulkUpdater extends Updater { default Matcher.Result match(R actualResource, P primary, Context

context) { From fe3bd1cdca400fedf524ebd293fd9b54155b79d7 Mon Sep 17 00:00:00 2001 From: csviri Date: Fri, 16 Sep 2022 16:09:32 +0200 Subject: [PATCH 40/67] feat: optional event source from dependent resources --- .../reconciler/EventSourceInitializer.java | 15 ++ .../dependent/DependentResource.java | 9 ++ .../dependent/EventSourceProvider.java | 5 + .../operator/processing/Controller.java | 17 +- ...actEventSourceHolderDependentResource.java | 39 +++-- .../AbstractSimpleDependentResource.java | 81 ---------- .../KubernetesDependentResource.java | 8 +- .../AbstractSimpleDependentResourceTest.java | 149 ------------------ ...endentGarbageCollectionTestReconciler.java | 5 +- ...DependentPrimaryIndexerTestReconciler.java | 7 +- .../StandaloneDependentTestReconciler.java | 4 +- .../dependent/SchemaDependentResource.java | 1 - .../WebPageDependentsWorkflowReconciler.java | 6 +- ...WebPageStandaloneDependentsReconciler.java | 5 +- 14 files changed, 89 insertions(+), 262 deletions(-) delete mode 100644 operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/external/AbstractSimpleDependentResource.java delete mode 100644 operator-framework-core/src/test/java/io/javaoperatorsdk/operator/processing/dependent/external/AbstractSimpleDependentResourceTest.java diff --git a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/reconciler/EventSourceInitializer.java b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/reconciler/EventSourceInitializer.java index 9b3c7a67bd..0062e59b73 100644 --- a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/reconciler/EventSourceInitializer.java +++ b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/reconciler/EventSourceInitializer.java @@ -2,9 +2,12 @@ import java.util.HashMap; import java.util.Map; +import java.util.Optional; import io.fabric8.kubernetes.api.model.HasMetadata; +import io.javaoperatorsdk.operator.api.reconciler.dependent.DependentResource; import io.javaoperatorsdk.operator.processing.event.source.EventSource; +import io.javaoperatorsdk.operator.processing.event.source.ResourceEventSource; /** * An interface that a {@link Reconciler} can implement to have the SDK register the provided @@ -39,6 +42,18 @@ static Map nameEventSources(EventSource... eventSources) { return eventSourceMap; } + @SuppressWarnings("unchecked,rawtypes") + static Map nameEventSourcesFromDependentResource( + EventSourceContext context, DependentResource... dependentResources) { + + Map eventSourceMap = new HashMap<>(dependentResources.length); + for (DependentResource dependentResource : dependentResources) { + Optional es = dependentResource.provideEventSource(context); + es.ifPresent(e -> eventSourceMap.put(generateNameFor(e), e)); + } + return eventSourceMap; + } + /** * This is for the use case when the event sources are not access explicitly by name in the * reconciler. 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 3bd9bf8061..e99be3218a 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 @@ -1,7 +1,11 @@ package io.javaoperatorsdk.operator.api.reconciler.dependent; +import java.util.Optional; + import io.fabric8.kubernetes.api.model.HasMetadata; import io.javaoperatorsdk.operator.api.reconciler.Context; +import io.javaoperatorsdk.operator.api.reconciler.EventSourceContext; +import io.javaoperatorsdk.operator.processing.event.source.ResourceEventSource; /** * An interface to implement and provide dependent resource support. @@ -27,6 +31,11 @@ public interface DependentResource { */ Class resourceType(); + default Optional> provideEventSource( + EventSourceContext

eventSourceContext) { + return Optional.empty(); + } + /** * Computes a default name for the specified DependentResource class * diff --git a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/reconciler/dependent/EventSourceProvider.java b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/reconciler/dependent/EventSourceProvider.java index 98190cb7ef..c83af1270a 100644 --- a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/reconciler/dependent/EventSourceProvider.java +++ b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/reconciler/dependent/EventSourceProvider.java @@ -4,6 +4,11 @@ import io.javaoperatorsdk.operator.api.reconciler.EventSourceContext; import io.javaoperatorsdk.operator.processing.event.source.EventSource; +/** + * @deprecated now event source related methods are directly on {@link DependentResource} + * @param

primary resource + */ +@Deprecated(forRemoval = true) public interface EventSourceProvider

{ /** * @param context - event source context where the event source is initialized diff --git a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/Controller.java b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/Controller.java index 92b70e722d..33c2176324 100644 --- a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/Controller.java +++ b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/Controller.java @@ -39,6 +39,7 @@ import io.javaoperatorsdk.operator.processing.event.EventProcessor; import io.javaoperatorsdk.operator.processing.event.EventSourceManager; import io.javaoperatorsdk.operator.processing.event.ResourceID; +import io.javaoperatorsdk.operator.processing.event.source.ResourceEventSource; import static io.javaoperatorsdk.operator.api.reconciler.Constants.WATCH_CURRENT_NAMESPACE; @@ -209,13 +210,19 @@ private void initContextIfNeeded(P resource, Context

context) { public void initAndRegisterEventSources(EventSourceContext

context) { managedWorkflow .getDependentResourcesByName().entrySet().stream() - .filter(drEntry -> drEntry.getValue() instanceof EventSourceProvider) .forEach(drEntry -> { - final var provider = (EventSourceProvider) drEntry.getValue(); - final var source = provider.initEventSource(context); - eventSourceManager.registerEventSource(drEntry.getKey(), source); + if (drEntry.getValue() instanceof EventSourceProvider) { + final var provider = (EventSourceProvider) drEntry.getValue(); + final var source = provider.initEventSource(context); + eventSourceManager.registerEventSource(drEntry.getKey(), source); + } else { + Optional eventSource = + drEntry.getValue().provideEventSource(context); + eventSource.ifPresent(es -> { + eventSourceManager.registerEventSource(drEntry.getKey(), es); + }); + } }); - // add manually defined event sources if (reconciler instanceof EventSourceInitializer) { final var provider = (EventSourceInitializer

) this.reconciler; diff --git a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/AbstractEventSourceHolderDependentResource.java b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/AbstractEventSourceHolderDependentResource.java index 0ceba16826..f882a86e44 100644 --- a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/AbstractEventSourceHolderDependentResource.java +++ b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/AbstractEventSourceHolderDependentResource.java @@ -1,12 +1,12 @@ package io.javaoperatorsdk.operator.processing.dependent; +import java.util.Optional; + import io.fabric8.kubernetes.api.model.HasMetadata; import io.javaoperatorsdk.operator.api.reconciler.EventSourceContext; import io.javaoperatorsdk.operator.api.reconciler.Ignore; -import io.javaoperatorsdk.operator.api.reconciler.dependent.EventSourceProvider; import io.javaoperatorsdk.operator.api.reconciler.dependent.RecentOperationCacheFiller; import io.javaoperatorsdk.operator.processing.event.ResourceID; -import io.javaoperatorsdk.operator.processing.event.source.EventSource; import io.javaoperatorsdk.operator.processing.event.source.ResourceEventSource; import io.javaoperatorsdk.operator.processing.event.source.filter.GenericFilter; import io.javaoperatorsdk.operator.processing.event.source.filter.OnAddFilter; @@ -15,8 +15,7 @@ @Ignore public abstract class AbstractEventSourceHolderDependentResource> - extends AbstractDependentResource - implements EventSourceProvider

{ + extends AbstractDependentResource { private T eventSource; private boolean isCacheFillerEventSource; @@ -25,8 +24,20 @@ public abstract class AbstractEventSourceHolderDependentResource onDeleteFilter; protected GenericFilter genericFilter; + /** + * Even a dependent resource holds an event source, it might not provide it. For example if event + * sources are shared between multiple dependent resources. Typically + * {@link io.javaoperatorsdk.operator.processing.dependent.kubernetes.KubernetesDependentResource} + * needs to be aware of event source even if the actual state of the resource is not read directly + * from the event source. + */ + private boolean provideEventSource = true; + - public EventSource initEventSource(EventSourceContext

context) { + public Optional> provideEventSource(EventSourceContext

context) { + if (!provideEventSource) { + return Optional.empty(); + } // some sub-classes (e.g. KubernetesDependentResource) can have their event source created // before this method is called in the managed case, so only create the event source if it // hasn't already been set. @@ -34,17 +45,21 @@ public EventSource initEventSource(EventSourceContext

context) { // event source // is shared between dependent resources this does not override the existing filters. if (eventSource == null) { - eventSource = createEventSource(context); + setEventSource(createEventSource(context)); applyFilters(); } + return Optional.of(eventSource); + } - isCacheFillerEventSource = eventSource instanceof RecentOperationCacheFiller; - return eventSource; + /** To make this backwards compatible even for respect of overriding */ + public T initEventSource(EventSourceContext

context) { + return (T) provideEventSource(context).orElseThrow(); } protected abstract T createEventSource(EventSourceContext

context); protected void setEventSource(T eventSource) { + isCacheFillerEventSource = eventSource instanceof RecentOperationCacheFiller; this.eventSource = eventSource; } @@ -55,8 +70,8 @@ protected void applyFilters() { this.eventSource.setGenericFilter(genericFilter); } - protected T eventSource() { - return eventSource; + public Optional> eventSource() { + return Optional.ofNullable(eventSource); } protected void onCreated(ResourceID primaryResourceId, R created) { @@ -87,4 +102,8 @@ public void setOnUpdateFilter(OnUpdateFilter onUpdateFilter) { public void setOnDeleteFilter(OnDeleteFilter onDeleteFilter) { this.onDeleteFilter = onDeleteFilter; } + + public void setProvideEventSource(boolean provideEventSource) { + this.provideEventSource = provideEventSource; + } } diff --git a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/external/AbstractSimpleDependentResource.java b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/external/AbstractSimpleDependentResource.java deleted file mode 100644 index 748452c30c..0000000000 --- a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/external/AbstractSimpleDependentResource.java +++ /dev/null @@ -1,81 +0,0 @@ -package io.javaoperatorsdk.operator.processing.dependent.external; - -import java.util.Optional; - -import io.fabric8.kubernetes.api.model.HasMetadata; -import io.javaoperatorsdk.operator.api.reconciler.Context; -import io.javaoperatorsdk.operator.api.reconciler.Ignore; -import io.javaoperatorsdk.operator.api.reconciler.dependent.ReconcileResult; -import io.javaoperatorsdk.operator.processing.dependent.AbstractDependentResource; -import io.javaoperatorsdk.operator.processing.dependent.DesiredEqualsMatcher; -import io.javaoperatorsdk.operator.processing.dependent.Matcher; -import io.javaoperatorsdk.operator.processing.event.ResourceID; -import io.javaoperatorsdk.operator.processing.event.source.ConcurrentHashMapCache; -import io.javaoperatorsdk.operator.processing.event.source.UpdatableCache; - -/** A base class for external dependent resources that don't have an event source. */ -@Ignore -public abstract class AbstractSimpleDependentResource - extends AbstractDependentResource { - - // cache serves only to keep the resource readable again until next reconciliation when the - // new resource is read again. - protected final UpdatableCache cache; - protected Matcher matcher; - - public AbstractSimpleDependentResource() { - this(new ConcurrentHashMapCache<>()); - } - - public AbstractSimpleDependentResource(UpdatableCache cache) { - this.cache = cache; - initMatcher(); - } - - @Override - public Optional getSecondaryResource(P primaryResource, Context

context) { - return cache.get(ResourceID.fromResource(primaryResource)); - } - - /** - * Actually read the resource from the target API - * - * @param primaryResource the primary associated resource - * @return fetched resource if present - **/ - public abstract Optional fetchResource(HasMetadata primaryResource); - - @Override - public ReconcileResult reconcile(P primary, Context

context) { - var resourceId = ResourceID.fromResource(primary); - Optional resource = fetchResource(primary); - resource.ifPresentOrElse(r -> cache.put(resourceId, r), () -> cache.remove(resourceId)); - return super.reconcile(primary, context); - } - - public final void delete(P primary, Context

context) { - deleteResource(primary, context); - cache.remove(ResourceID.fromResource(primary)); - } - - protected abstract void deleteResource(P primary, Context

context); - - @Override - protected void onCreated(ResourceID primaryResourceId, R created) { - cache.put(primaryResourceId, created); - } - - @Override - protected void onUpdated(ResourceID primaryResourceId, R updated, R actual) { - cache.put(primaryResourceId, updated); - } - - public Matcher.Result match(R actualResource, P primary, Context

context) { - return matcher.match(actualResource, primary, context); - } - - protected void initMatcher() { - matcher = new DesiredEqualsMatcher<>(this); - } - -} 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 26a7313f08..c438109379 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 @@ -190,7 +190,7 @@ protected InformerEventSource createEventSource(EventSourceContext

cont "Using default configuration for {} KubernetesDependentResource, call configureWith to provide configuration", resourceType().getSimpleName()); } - return eventSource(); + return (InformerEventSource) eventSource().orElseThrow(); } private boolean useDefaultAnnotationsToIdentifyPrimary() { @@ -241,11 +241,13 @@ protected R desired(P primary, int index, Context

context) { } private void prepareEventFiltering(R desired, ResourceID resourceID) { - eventSource().prepareForCreateOrUpdateEventFiltering(resourceID, desired); + ((InformerEventSource) eventSource().orElseThrow()) + .prepareForCreateOrUpdateEventFiltering(resourceID, desired); } private void cleanupAfterEventFiltering(ResourceID resourceID) { - eventSource().cleanupOnCreateOrUpdateEventFiltering(resourceID); + ((InformerEventSource) eventSource().orElseThrow()) + .cleanupOnCreateOrUpdateEventFiltering(resourceID); } } diff --git a/operator-framework-core/src/test/java/io/javaoperatorsdk/operator/processing/dependent/external/AbstractSimpleDependentResourceTest.java b/operator-framework-core/src/test/java/io/javaoperatorsdk/operator/processing/dependent/external/AbstractSimpleDependentResourceTest.java deleted file mode 100644 index 520f44365f..0000000000 --- a/operator-framework-core/src/test/java/io/javaoperatorsdk/operator/processing/dependent/external/AbstractSimpleDependentResourceTest.java +++ /dev/null @@ -1,149 +0,0 @@ -package io.javaoperatorsdk.operator.processing.dependent.external; - -import java.util.Optional; -import java.util.function.Supplier; - -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; - -import io.fabric8.kubernetes.api.model.HasMetadata; -import io.javaoperatorsdk.operator.TestUtils; -import io.javaoperatorsdk.operator.api.reconciler.Context; -import io.javaoperatorsdk.operator.api.reconciler.dependent.Deleter; -import io.javaoperatorsdk.operator.processing.dependent.Creator; -import io.javaoperatorsdk.operator.processing.dependent.Updater; -import io.javaoperatorsdk.operator.processing.event.ResourceID; -import io.javaoperatorsdk.operator.processing.event.source.SampleExternalResource; -import io.javaoperatorsdk.operator.processing.event.source.UpdatableCache; -import io.javaoperatorsdk.operator.sample.simple.TestCustomResource; - -import static org.assertj.core.api.Assertions.assertThat; -import static org.mockito.Mockito.any; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.times; -import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.when; - -@SuppressWarnings("unchecked") -class AbstractSimpleDependentResourceTest { - - UpdatableCache updatableCacheMock = mock(UpdatableCache.class); - Supplier supplierMock = mock(Supplier.class); - - SimpleDependentResource simpleDependentResource = - new SimpleDependentResource(updatableCacheMock, supplierMock); - - @BeforeEach - void setup() { - when(supplierMock.get()).thenReturn(SampleExternalResource.testResource1()); - } - - @Test - void getsTheResourceFromSupplyIfReconciling() { - simpleDependentResource = new SimpleDependentResource(supplierMock); - - simpleDependentResource.reconcile(TestUtils.testCustomResource1(), null); - - verify(supplierMock, times(1)).get(); - assertThat(simpleDependentResource.getSecondaryResource(TestUtils.testCustomResource1(), null)) - .isPresent() - .isEqualTo(Optional.of(SampleExternalResource.testResource1())); - } - - @Test - void getResourceReadsTheResourceFromCache() { - simpleDependentResource.getSecondaryResource(TestUtils.testCustomResource1(), null); - - verify(supplierMock, times(0)).get(); - verify(updatableCacheMock, times(1)).get(any()); - } - - @Test - void createPutsNewResourceToTheCache() { - when(supplierMock.get()).thenReturn(null); - when(updatableCacheMock.get(any())).thenReturn(Optional.empty()); - - simpleDependentResource.reconcile(TestUtils.testCustomResource1(), null); - - verify(updatableCacheMock, times(1)).put(any(), any()); - } - - @Test - void updatePutsNewResourceToCache() { - var actual = SampleExternalResource.testResource1(); - actual.setValue("changedValue"); - when(supplierMock.get()).thenReturn(actual); - when(updatableCacheMock.get(any())).thenReturn(Optional.of(actual)); - - simpleDependentResource.reconcile(TestUtils.testCustomResource1(), null); - - verify(updatableCacheMock, times(1)) - .put(ResourceID.fromResource(TestUtils.testCustomResource1()), actual); - - verify(updatableCacheMock, times(1)) - .put( - ResourceID.fromResource(TestUtils.testCustomResource1()), - SampleExternalResource.testResource1()); - } - - @Test - void deleteRemovesResourceFromCache() { - simpleDependentResource.delete(TestUtils.testCustomResource1(), null); - verify(updatableCacheMock, times(1)).remove(any()); - } - - private static class SimpleDependentResource - extends AbstractSimpleDependentResource - implements Creator, - Updater, - Deleter { - - private final Supplier supplier; - - public SimpleDependentResource(Supplier supplier) { - this.supplier = supplier; - } - - public SimpleDependentResource( - UpdatableCache cache, Supplier supplier) { - super(cache); - this.supplier = supplier; - } - - @Override - public Optional fetchResource(HasMetadata primaryResource) { - return Optional.ofNullable(supplier.get()); - } - - @Override - protected void deleteResource(TestCustomResource primary, - Context context) {} - - @Override - public SampleExternalResource create( - SampleExternalResource desired, TestCustomResource primary, - Context context) { - return SampleExternalResource.testResource1(); - } - - @Override - public SampleExternalResource update( - SampleExternalResource actual, - SampleExternalResource desired, - TestCustomResource primary, - Context context) { - return SampleExternalResource.testResource1(); - } - - @Override - protected SampleExternalResource desired(TestCustomResource primary, - Context context) { - return SampleExternalResource.testResource1(); - } - - @Override - public Class resourceType() { - return SampleExternalResource.class; - } - } -} diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/kubernetesdependentgarbagecollection/DependentGarbageCollectionTestReconciler.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/kubernetesdependentgarbagecollection/DependentGarbageCollectionTestReconciler.java index e9b947a83b..47d97920b5 100644 --- a/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/kubernetesdependentgarbagecollection/DependentGarbageCollectionTestReconciler.java +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/kubernetesdependentgarbagecollection/DependentGarbageCollectionTestReconciler.java @@ -1,5 +1,6 @@ package io.javaoperatorsdk.operator.sample.kubernetesdependentgarbagecollection; +import java.util.Collections; import java.util.Map; import io.fabric8.kubernetes.api.model.ConfigMap; @@ -32,8 +33,8 @@ public DependentGarbageCollectionTestReconciler() { @Override public Map prepareEventSources( EventSourceContext context) { - return EventSourceInitializer - .nameEventSources(configMapDependent.initEventSource(context)); + return configMapDependent.provideEventSource(context).map(es -> EventSourceInitializer + .nameEventSources()).orElse(Collections.emptyMap()); } @Override 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 59b6594846..cb2a4e2e6c 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 @@ -1,5 +1,6 @@ package io.javaoperatorsdk.operator.sample.primaryindexer; +import java.util.Optional; import java.util.Set; import java.util.stream.Collectors; @@ -10,8 +11,8 @@ import io.javaoperatorsdk.operator.api.reconciler.dependent.Dependent; import io.javaoperatorsdk.operator.processing.dependent.kubernetes.KubernetesDependentResource; import io.javaoperatorsdk.operator.processing.event.ResourceID; -import io.javaoperatorsdk.operator.processing.event.source.EventSource; import io.javaoperatorsdk.operator.processing.event.source.IndexerResourceCache; +import io.javaoperatorsdk.operator.processing.event.source.ResourceEventSource; import io.javaoperatorsdk.operator.processing.event.source.SecondaryToPrimaryMapper; @ControllerConfiguration(dependents = @Dependent( @@ -38,11 +39,11 @@ public Set toPrimaryResourceIDs(ConfigMap dependentResource) { } @Override - public EventSource initEventSource( + public Optional> provideEventSource( EventSourceContext context) { cache = context.getPrimaryCache(); cache.addIndexer(CONFIG_MAP_RELATION_INDEXER, indexer); - return super.initEventSource(context); + return super.provideEventSource(context); } } } diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/standalonedependent/StandaloneDependentTestReconciler.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/standalonedependent/StandaloneDependentTestReconciler.java index 2dace670ba..70b119c2ab 100644 --- a/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/standalonedependent/StandaloneDependentTestReconciler.java +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/standalonedependent/StandaloneDependentTestReconciler.java @@ -38,8 +38,8 @@ public StandaloneDependentTestReconciler() { @Override public Map prepareEventSources( EventSourceContext context) { - return EventSourceInitializer - .nameEventSources(deploymentDependent.initEventSource(context)); + return EventSourceInitializer.nameEventSourcesFromDependentResource(context, + deploymentDependent); } @Override diff --git a/sample-operators/mysql-schema/src/main/java/io/javaoperatorsdk/operator/sample/dependent/SchemaDependentResource.java b/sample-operators/mysql-schema/src/main/java/io/javaoperatorsdk/operator/sample/dependent/SchemaDependentResource.java index 48e3f37abe..060b3d7502 100644 --- a/sample-operators/mysql-schema/src/main/java/io/javaoperatorsdk/operator/sample/dependent/SchemaDependentResource.java +++ b/sample-operators/mysql-schema/src/main/java/io/javaoperatorsdk/operator/sample/dependent/SchemaDependentResource.java @@ -97,5 +97,4 @@ public Set fetchResources(MySQLSchema primaryResource) { throw new RuntimeException("Error while trying read Schema", e); } } - } diff --git a/sample-operators/webpage/src/main/java/io/javaoperatorsdk/operator/sample/WebPageDependentsWorkflowReconciler.java b/sample-operators/webpage/src/main/java/io/javaoperatorsdk/operator/sample/WebPageDependentsWorkflowReconciler.java index 6b81a9d6e3..0eeba42083 100644 --- a/sample-operators/webpage/src/main/java/io/javaoperatorsdk/operator/sample/WebPageDependentsWorkflowReconciler.java +++ b/sample-operators/webpage/src/main/java/io/javaoperatorsdk/operator/sample/WebPageDependentsWorkflowReconciler.java @@ -55,9 +55,9 @@ public WebPageDependentsWorkflowReconciler(KubernetesClient kubernetesClient) { @Override public Map prepareEventSources(EventSourceContext context) { - return EventSourceInitializer.nameEventSources(configMapDR.initEventSource(context), - deploymentDR.initEventSource(context), serviceDR.initEventSource(context), - ingressDR.initEventSource(context)); + return EventSourceInitializer.nameEventSourcesFromDependentResource(context, configMapDR, + deploymentDR, serviceDR, + ingressDR); } @Override diff --git a/sample-operators/webpage/src/main/java/io/javaoperatorsdk/operator/sample/WebPageStandaloneDependentsReconciler.java b/sample-operators/webpage/src/main/java/io/javaoperatorsdk/operator/sample/WebPageStandaloneDependentsReconciler.java index b99e130135..15262acb48 100644 --- a/sample-operators/webpage/src/main/java/io/javaoperatorsdk/operator/sample/WebPageStandaloneDependentsReconciler.java +++ b/sample-operators/webpage/src/main/java/io/javaoperatorsdk/operator/sample/WebPageStandaloneDependentsReconciler.java @@ -40,9 +40,8 @@ public WebPageStandaloneDependentsReconciler(KubernetesClient kubernetesClient) @Override public Map prepareEventSources(EventSourceContext context) { - return EventSourceInitializer.nameEventSources(configMapDR.initEventSource(context), - deploymentDR.initEventSource(context), serviceDR.initEventSource(context), - ingressDR.initEventSource(context)); + return EventSourceInitializer.nameEventSourcesFromDependentResource(context, configMapDR, + deploymentDR, serviceDR, ingressDR); } @Override From efd232401990a9861871249265e7a7f7fe119cbf Mon Sep 17 00:00:00 2001 From: csviri Date: Thu, 22 Sep 2022 16:09:23 +0200 Subject: [PATCH 41/67] fix test --- .../DependentGarbageCollectionTestReconciler.java | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/kubernetesdependentgarbagecollection/DependentGarbageCollectionTestReconciler.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/kubernetesdependentgarbagecollection/DependentGarbageCollectionTestReconciler.java index 47d97920b5..c20d573a03 100644 --- a/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/kubernetesdependentgarbagecollection/DependentGarbageCollectionTestReconciler.java +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/kubernetesdependentgarbagecollection/DependentGarbageCollectionTestReconciler.java @@ -1,6 +1,5 @@ package io.javaoperatorsdk.operator.sample.kubernetesdependentgarbagecollection; -import java.util.Collections; import java.util.Map; import io.fabric8.kubernetes.api.model.ConfigMap; @@ -33,8 +32,8 @@ public DependentGarbageCollectionTestReconciler() { @Override public Map prepareEventSources( EventSourceContext context) { - return configMapDependent.provideEventSource(context).map(es -> EventSourceInitializer - .nameEventSources()).orElse(Collections.emptyMap()); + return EventSourceInitializer.nameEventSourcesFromDependentResource(context, + configMapDependent); } @Override From ab1e980a51915a7bfa8e075f61fbda2746a4b0c9 Mon Sep 17 00:00:00 2001 From: csviri Date: Fri, 23 Sep 2022 14:12:37 +0200 Subject: [PATCH 42/67] wip --- .../AnnotationControllerConfiguration.java | 3 ++- .../ControllerConfigurationOverrider.java | 4 ++-- .../dependent/DependentResourceSpec.java | 10 ++++++++- .../reconciler/EventSourceInitializer.java | 2 +- .../api/reconciler/dependent/Dependent.java | 9 ++++++++ .../dependent/DependentResource.java | 22 ++++++++++++++++++- .../operator/processing/Controller.java | 2 +- .../dependent/AbstractDependentResource.java | 20 +++++++++++++++++ ...actEventSourceHolderDependentResource.java | 22 +++---------------- .../ControllerConfigurationOverriderTest.java | 7 +++--- .../AbstractDependentResourceTest.java | 8 +++++++ .../dependent/EmptyTestDependentResource.java | 3 +++ .../AbstractWorkflowExecutorTest.java | 6 +++++ .../workflow/ManagedWorkflowTestUtils.java | 3 ++- ...DependentPrimaryIndexerTestReconciler.java | 4 ++-- 15 files changed, 92 insertions(+), 33 deletions(-) diff --git a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/config/AnnotationControllerConfiguration.java b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/config/AnnotationControllerConfiguration.java index db3e8de3f0..74bef0862a 100644 --- a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/config/AnnotationControllerConfiguration.java +++ b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/config/AnnotationControllerConfiguration.java @@ -249,7 +249,8 @@ public List getDependentResources() { Set.of(dependent.dependsOn()), instantiateConditionIfNotDefault(dependent.readyPostcondition(), context), instantiateConditionIfNotDefault(dependent.reconcilePrecondition(), context), - instantiateConditionIfNotDefault(dependent.deletePostcondition(), context)); + instantiateConditionIfNotDefault(dependent.deletePostcondition(), context), + dependent.provideEventSource()); specsMap.put(name, spec); } diff --git a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/config/ControllerConfigurationOverrider.java b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/config/ControllerConfigurationOverrider.java index e9cae2dccf..ee153c879a 100644 --- a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/config/ControllerConfigurationOverrider.java +++ b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/config/ControllerConfigurationOverrider.java @@ -174,7 +174,7 @@ private void replaceConfig(String name, Object newConfig, DependentResourceSpec< namedDependentResourceSpecs.put(name, new DependentResourceSpec<>(current.getDependentResourceClass(), newConfig, name, current.getDependsOn(), current.getReadyCondition(), current.getReconcileCondition(), - current.getDeletePostCondition())); + current.getDeletePostCondition(), current.provideEventSource())); } @SuppressWarnings("unchecked") @@ -220,7 +220,7 @@ public ControllerConfiguration build() { KubernetesDependentResourceConfig c) { return new DependentResourceSpec(spec.getDependentResourceClass(), c.setNamespaces(namespaces), name, spec.getDependsOn(), spec.getReadyCondition(), - spec.getReconcileCondition(), spec.getDeletePostCondition()); + spec.getReconcileCondition(), spec.getDeletePostCondition(), spec.provideEventSource()); } public static ControllerConfigurationOverrider override( diff --git a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/config/dependent/DependentResourceSpec.java b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/config/dependent/DependentResourceSpec.java index f146d127d0..71476cc2d3 100644 --- a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/config/dependent/DependentResourceSpec.java +++ b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/config/dependent/DependentResourceSpec.java @@ -23,9 +23,12 @@ public class DependentResourceSpec, C> { private final Condition deletePostCondition; + private final boolean provideEventSource; + public DependentResourceSpec(Class dependentResourceClass, C dependentResourceConfig, String name, Set dependsOn, Condition readyCondition, - Condition reconcileCondition, Condition deletePostCondition) { + Condition reconcileCondition, Condition deletePostCondition, + boolean provideEventSource) { this.dependentResourceClass = dependentResourceClass; this.dependentResourceConfig = dependentResourceConfig; this.name = name; @@ -33,6 +36,7 @@ public DependentResourceSpec(Class dependentResourceClass, C dependentResourc this.readyCondition = readyCondition; this.reconcileCondition = reconcileCondition; this.deletePostCondition = deletePostCondition; + this.provideEventSource = provideEventSource; } public Class getDependentResourceClass() { @@ -89,4 +93,8 @@ public Condition getReconcileCondition() { public Condition getDeletePostCondition() { return deletePostCondition; } + + public boolean provideEventSource() { + return provideEventSource; + } } diff --git a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/reconciler/EventSourceInitializer.java b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/reconciler/EventSourceInitializer.java index 0062e59b73..96fa85ee6a 100644 --- a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/reconciler/EventSourceInitializer.java +++ b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/reconciler/EventSourceInitializer.java @@ -48,7 +48,7 @@ static Map nameEventSourcesFromDepe Map eventSourceMap = new HashMap<>(dependentResources.length); for (DependentResource dependentResource : dependentResources) { - Optional es = dependentResource.provideEventSource(context); + Optional es = dependentResource.eventSource(context); es.ifPresent(e -> eventSourceMap.put(generateNameFor(e), e)); } return eventSourceMap; diff --git a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/reconciler/dependent/Dependent.java b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/reconciler/dependent/Dependent.java index 90ba701a6a..00d2ad1882 100644 --- a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/reconciler/dependent/Dependent.java +++ b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/reconciler/dependent/Dependent.java @@ -57,4 +57,13 @@ * one can be */ String[] dependsOn() default {}; + + /** + * Setting this to false means that the event source provided by the dependent resource won't be + * used. This is helpful if more dependent resources created for the same type, and want to share + * a common event source. In that case an event source needs to be explicitly registered. + * + * @return if the event source (if any) provided by the dependent resource should be used or not. + */ + boolean provideEventSource() default true; } 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 e99be3218a..65bec0dad7 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 @@ -31,11 +31,31 @@ public interface DependentResource { */ Class resourceType(); - default Optional> provideEventSource( + /** + * Dependent resources are designed to by default provide event sources. There are cases where it + * might not: + *

    + *
  • If an event source is shared between multiple dependent resources. In this case only one or + * none of the dependent resources sharing the event source should provide one.
  • + *
  • Some special implementation of an event source. That just execute some action might not + * provide one.
  • + *
+ * + * @param eventSourceContext context of event source initialization + * @return an optional event source + */ + default Optional> eventSource( EventSourceContext

eventSourceContext) { return Optional.empty(); } + // todo should be a setter? + /** + * Calling this method, instructs the implementation to not provide an event source, even if it + * normally does. + */ + void doNotProvideEventSource(); + /** * Computes a default name for the specified DependentResource class * diff --git a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/Controller.java b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/Controller.java index 33c2176324..3582e6f614 100644 --- a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/Controller.java +++ b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/Controller.java @@ -217,7 +217,7 @@ public void initAndRegisterEventSources(EventSourceContext

context) { eventSourceManager.registerEventSource(drEntry.getKey(), source); } else { Optional eventSource = - drEntry.getValue().provideEventSource(context); + drEntry.getValue().eventSource(context); eventSource.ifPresent(es -> { eventSourceManager.registerEventSource(drEntry.getKey(), es); }); 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 1d16b79222..a91d5b399e 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 @@ -9,11 +9,13 @@ import io.fabric8.kubernetes.api.model.HasMetadata; import io.javaoperatorsdk.operator.api.reconciler.Context; +import io.javaoperatorsdk.operator.api.reconciler.EventSourceContext; import io.javaoperatorsdk.operator.api.reconciler.Ignore; import io.javaoperatorsdk.operator.api.reconciler.ResourceDiscriminator; import io.javaoperatorsdk.operator.api.reconciler.dependent.DependentResource; import io.javaoperatorsdk.operator.api.reconciler.dependent.ReconcileResult; import io.javaoperatorsdk.operator.processing.event.ResourceID; +import io.javaoperatorsdk.operator.processing.event.source.ResourceEventSource; @Ignore public abstract class AbstractDependentResource @@ -27,6 +29,7 @@ public abstract class AbstractDependentResource protected Creator creator; protected Updater updater; protected BulkDependentResource bulkDependentResource; + protected boolean returnEventSource = true; protected List> resourceDiscriminator = new ArrayList<>(1); @@ -38,6 +41,23 @@ public AbstractDependentResource() { bulkDependentResource = bulk ? (BulkDependentResource) this : null; } + @Override + public void doNotProvideEventSource() { + this.returnEventSource = false; + } + + @Override + public Optional> eventSource(EventSourceContext

eventSourceContext) { + if (!returnEventSource) { + return Optional.empty(); + } else { + return Optional.of(provideEventSource(eventSourceContext)); + } + } + + protected abstract ResourceEventSource provideEventSource( + EventSourceContext

eventSourceContext); + @Override public ReconcileResult reconcile(P primary, Context

context) { var count = bulk ? bulkDependentResource.count(primary, context) : 1; diff --git a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/AbstractEventSourceHolderDependentResource.java b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/AbstractEventSourceHolderDependentResource.java index f882a86e44..a35e1b4d33 100644 --- a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/AbstractEventSourceHolderDependentResource.java +++ b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/AbstractEventSourceHolderDependentResource.java @@ -24,20 +24,7 @@ public abstract class AbstractEventSourceHolderDependentResource onDeleteFilter; protected GenericFilter genericFilter; - /** - * Even a dependent resource holds an event source, it might not provide it. For example if event - * sources are shared between multiple dependent resources. Typically - * {@link io.javaoperatorsdk.operator.processing.dependent.kubernetes.KubernetesDependentResource} - * needs to be aware of event source even if the actual state of the resource is not read directly - * from the event source. - */ - private boolean provideEventSource = true; - - - public Optional> provideEventSource(EventSourceContext

context) { - if (!provideEventSource) { - return Optional.empty(); - } + public ResourceEventSource provideEventSource(EventSourceContext

context) { // some sub-classes (e.g. KubernetesDependentResource) can have their event source created // before this method is called in the managed case, so only create the event source if it // hasn't already been set. @@ -48,12 +35,12 @@ public Optional> provideEventSource(EventSourceContext setEventSource(createEventSource(context)); applyFilters(); } - return Optional.of(eventSource); + return eventSource; } /** To make this backwards compatible even for respect of overriding */ public T initEventSource(EventSourceContext

context) { - return (T) provideEventSource(context).orElseThrow(); + return (T) eventSource(context).orElseThrow(); } protected abstract T createEventSource(EventSourceContext

context); @@ -103,7 +90,4 @@ public void setOnDeleteFilter(OnDeleteFilter onDeleteFilter) { this.onDeleteFilter = onDeleteFilter; } - public void setProvideEventSource(boolean provideEventSource) { - this.provideEventSource = provideEventSource; - } } diff --git a/operator-framework-core/src/test/java/io/javaoperatorsdk/operator/api/config/ControllerConfigurationOverriderTest.java b/operator-framework-core/src/test/java/io/javaoperatorsdk/operator/api/config/ControllerConfigurationOverriderTest.java index 6e486b5347..312d307cc1 100644 --- a/operator-framework-core/src/test/java/io/javaoperatorsdk/operator/api/config/ControllerConfigurationOverriderTest.java +++ b/operator-framework-core/src/test/java/io/javaoperatorsdk/operator/api/config/ControllerConfigurationOverriderTest.java @@ -5,11 +5,8 @@ import org.junit.jupiter.api.Test; import io.fabric8.kubernetes.api.model.ConfigMap; -import io.javaoperatorsdk.operator.api.reconciler.Constants; -import io.javaoperatorsdk.operator.api.reconciler.Context; +import io.javaoperatorsdk.operator.api.reconciler.*; 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; import io.javaoperatorsdk.operator.api.reconciler.dependent.DependentResource; import io.javaoperatorsdk.operator.api.reconciler.dependent.ReconcileResult; @@ -84,6 +81,8 @@ public Class resourceType() { return Object.class; } + @Override + public void doNotProvideEventSource() {} } } diff --git a/operator-framework-core/src/test/java/io/javaoperatorsdk/operator/processing/dependent/AbstractDependentResourceTest.java b/operator-framework-core/src/test/java/io/javaoperatorsdk/operator/processing/dependent/AbstractDependentResourceTest.java index 004cb5d159..190e45adfb 100644 --- a/operator-framework-core/src/test/java/io/javaoperatorsdk/operator/processing/dependent/AbstractDependentResourceTest.java +++ b/operator-framework-core/src/test/java/io/javaoperatorsdk/operator/processing/dependent/AbstractDependentResourceTest.java @@ -7,7 +7,9 @@ import io.fabric8.kubernetes.api.model.ConfigMap; import io.fabric8.kubernetes.api.model.ObjectMetaBuilder; import io.javaoperatorsdk.operator.api.reconciler.Context; +import io.javaoperatorsdk.operator.api.reconciler.EventSourceContext; import io.javaoperatorsdk.operator.processing.event.ResourceID; +import io.javaoperatorsdk.operator.processing.event.source.ResourceEventSource; import io.javaoperatorsdk.operator.sample.simple.TestCustomResource; import static org.junit.jupiter.api.Assertions.*; @@ -78,6 +80,12 @@ public Class resourceType() { return ConfigMap.class; } + @Override + protected ResourceEventSource provideEventSource( + EventSourceContext eventSourceContext) { + return null; + } + @Override protected Optional getSecondaryResource(TestCustomResource primary, Context context) { diff --git a/operator-framework-core/src/test/java/io/javaoperatorsdk/operator/processing/dependent/EmptyTestDependentResource.java b/operator-framework-core/src/test/java/io/javaoperatorsdk/operator/processing/dependent/EmptyTestDependentResource.java index dab1bc0132..dd42a80392 100644 --- a/operator-framework-core/src/test/java/io/javaoperatorsdk/operator/processing/dependent/EmptyTestDependentResource.java +++ b/operator-framework-core/src/test/java/io/javaoperatorsdk/operator/processing/dependent/EmptyTestDependentResource.java @@ -20,5 +20,8 @@ public Class resourceType() { return Deployment.class; } + @Override + public void doNotProvideEventSource() {} + } diff --git a/operator-framework-core/src/test/java/io/javaoperatorsdk/operator/processing/dependent/workflow/AbstractWorkflowExecutorTest.java b/operator-framework-core/src/test/java/io/javaoperatorsdk/operator/processing/dependent/workflow/AbstractWorkflowExecutorTest.java index 9c10c06cc0..2ecb6bde24 100644 --- a/operator-framework-core/src/test/java/io/javaoperatorsdk/operator/processing/dependent/workflow/AbstractWorkflowExecutorTest.java +++ b/operator-framework-core/src/test/java/io/javaoperatorsdk/operator/processing/dependent/workflow/AbstractWorkflowExecutorTest.java @@ -48,6 +48,9 @@ public Class resourceType() { return String.class; } + @Override + public void doNotProvideEventSource() {} + @Override public String toString() { return name; @@ -107,6 +110,9 @@ public Class resourceType() { return String.class; } + @Override + public void doNotProvideEventSource() {} + @Override public String toString() { return name; diff --git a/operator-framework-core/src/test/java/io/javaoperatorsdk/operator/processing/dependent/workflow/ManagedWorkflowTestUtils.java b/operator-framework-core/src/test/java/io/javaoperatorsdk/operator/processing/dependent/workflow/ManagedWorkflowTestUtils.java index 2332778e2e..72a3886963 100644 --- a/operator-framework-core/src/test/java/io/javaoperatorsdk/operator/processing/dependent/workflow/ManagedWorkflowTestUtils.java +++ b/operator-framework-core/src/test/java/io/javaoperatorsdk/operator/processing/dependent/workflow/ManagedWorkflowTestUtils.java @@ -11,7 +11,8 @@ public class ManagedWorkflowTestUtils { @SuppressWarnings("unchecked") public static DependentResourceSpec createDRS(String name, String... dependOns) { return new DependentResourceSpec(EmptyTestDependentResource.class, - null, name, Set.of(dependOns), null, null, null); + null, name, Set.of(dependOns), null, null, null, + true); } } 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 cb2a4e2e6c..6d79d4ee56 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 @@ -39,11 +39,11 @@ public Set toPrimaryResourceIDs(ConfigMap dependentResource) { } @Override - public Optional> provideEventSource( + public Optional> eventSource( EventSourceContext context) { cache = context.getPrimaryCache(); cache.addIndexer(CONFIG_MAP_RELATION_INDEXER, indexer); - return super.provideEventSource(context); + return super.eventSource(context); } } } From 34850d194aedb2d5a8b359ad04ad13d83e5e6526 Mon Sep 17 00:00:00 2001 From: csviri Date: Fri, 23 Sep 2022 15:13:45 +0200 Subject: [PATCH 43/67] wip --- .../processing/dependent/kubernetes/KubernetesDependent.java | 2 ++ 1 file changed, 2 insertions(+) diff --git a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/kubernetes/KubernetesDependent.java b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/kubernetes/KubernetesDependent.java index 603f4ae62e..a259f97f74 100644 --- a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/kubernetes/KubernetesDependent.java +++ b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/kubernetes/KubernetesDependent.java @@ -72,4 +72,6 @@ Class genericFilter() default GenericFilter.class; Class resourceDiscriminator() default ResourceDiscriminator.class; + + String eventSourceNameToUse() default NO_VALUE_SET; } From fc618d7dde8bb9ee61a97cf9a7df6dc1fedcdb4f Mon Sep 17 00:00:00 2001 From: csviri Date: Mon, 26 Sep 2022 14:42:32 +0200 Subject: [PATCH 44/67] wip --- .../AnnotationControllerConfiguration.java | 5 +++-- .../dependent/EventSourceAware.java | 10 ++++++++++ .../operator/processing/Controller.java | 16 ++++++++++------ ...actEventSourceHolderDependentResource.java | 19 ++++++++++++++++++- .../kubernetes/KubernetesDependent.java | 2 +- .../KubernetesDependentResource.java | 19 +++++++++++++------ .../KubernetesDependentResourceConfig.java | 14 ++++++++------ .../workflow/ManagedWorkflowSupport.java | 4 +++- .../event/EventSourceRetriever.java | 2 +- .../dependent/SchemaDependentResource.java | 4 +--- 10 files changed, 68 insertions(+), 27 deletions(-) create mode 100644 operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/reconciler/dependent/EventSourceAware.java diff --git a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/config/AnnotationControllerConfiguration.java b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/config/AnnotationControllerConfiguration.java index 74bef0862a..501d0a4dad 100644 --- a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/config/AnnotationControllerConfiguration.java +++ b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/config/AnnotationControllerConfiguration.java @@ -289,13 +289,13 @@ private Object createKubernetesResourceConfig(Class OnDeleteFilter onDeleteFilter = null; GenericFilter genericFilter = null; ResourceDiscriminator resourceDiscriminator = null; + String eventSourceNameToUse = null; if (kubeDependent != null) { if (!Arrays.equals(KubernetesDependent.DEFAULT_NAMESPACES, kubeDependent.namespaces())) { namespaces = Set.of(kubeDependent.namespaces()); configuredNS = true; } - final var fromAnnotation = kubeDependent.labelSelector(); labelSelector = Constants.NO_VALUE_SET.equals(fromAnnotation) ? null : fromAnnotation; @@ -316,12 +316,13 @@ private Object createKubernetesResourceConfig(Class resourceDiscriminator = instantiateDiscriminatorIfNotVoid(kubeDependent.resourceDiscriminator()); + eventSourceNameToUse = kubeDependent.eventSourceToUse(); } config = new KubernetesDependentResourceConfig(namespaces, labelSelector, configuredNS, resourceDiscriminator, onAddFilter, - onUpdateFilter, onDeleteFilter, genericFilter); + onUpdateFilter, onDeleteFilter, genericFilter, eventSourceNameToUse); return config; } diff --git a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/reconciler/dependent/EventSourceAware.java b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/reconciler/dependent/EventSourceAware.java new file mode 100644 index 0000000000..09b13d90c5 --- /dev/null +++ b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/reconciler/dependent/EventSourceAware.java @@ -0,0 +1,10 @@ +package io.javaoperatorsdk.operator.api.reconciler.dependent; + +import io.fabric8.kubernetes.api.model.HasMetadata; +import io.javaoperatorsdk.operator.processing.event.EventSourceRetriever; + +public interface EventSourceAware

{ + + void selectEventSources(EventSourceRetriever

eventSourceRetriever); + +} diff --git a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/Controller.java b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/Controller.java index 3582e6f614..57ce0a3a5e 100644 --- a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/Controller.java +++ b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/Controller.java @@ -32,6 +32,7 @@ import io.javaoperatorsdk.operator.api.reconciler.Ignore; import io.javaoperatorsdk.operator.api.reconciler.Reconciler; import io.javaoperatorsdk.operator.api.reconciler.UpdateControl; +import io.javaoperatorsdk.operator.api.reconciler.dependent.EventSourceAware; import io.javaoperatorsdk.operator.api.reconciler.dependent.EventSourceProvider; import io.javaoperatorsdk.operator.api.reconciler.dependent.managed.DefaultManagedDependentResourceContext; import io.javaoperatorsdk.operator.processing.dependent.workflow.ManagedWorkflow; @@ -208,6 +209,11 @@ private void initContextIfNeeded(P resource, Context

context) { } public void initAndRegisterEventSources(EventSourceContext

context) { + if (reconciler instanceof EventSourceInitializer) { + final var provider = (EventSourceInitializer

) this.reconciler; + final var ownSources = provider.prepareEventSources(context); + ownSources.forEach(eventSourceManager::registerEventSource); + } managedWorkflow .getDependentResourcesByName().entrySet().stream() .forEach(drEntry -> { @@ -223,12 +229,10 @@ public void initAndRegisterEventSources(EventSourceContext

context) { }); } }); - // add manually defined event sources - if (reconciler instanceof EventSourceInitializer) { - final var provider = (EventSourceInitializer

) this.reconciler; - final var ownSources = provider.prepareEventSources(context); - ownSources.forEach(eventSourceManager::registerEventSource); - } + managedWorkflow.getDependentResourcesByName().entrySet().stream().map(Map.Entry::getValue) + .filter(EventSourceAware.class::isInstance) + .forEach(dr -> ((EventSourceAware) dr) + .selectEventSources(eventSourceManager)); } @Override diff --git a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/AbstractEventSourceHolderDependentResource.java b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/AbstractEventSourceHolderDependentResource.java index a35e1b4d33..06a9dbb67b 100644 --- a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/AbstractEventSourceHolderDependentResource.java +++ b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/AbstractEventSourceHolderDependentResource.java @@ -5,7 +5,9 @@ import io.fabric8.kubernetes.api.model.HasMetadata; import io.javaoperatorsdk.operator.api.reconciler.EventSourceContext; import io.javaoperatorsdk.operator.api.reconciler.Ignore; +import io.javaoperatorsdk.operator.api.reconciler.dependent.EventSourceAware; import io.javaoperatorsdk.operator.api.reconciler.dependent.RecentOperationCacheFiller; +import io.javaoperatorsdk.operator.processing.event.EventSourceRetriever; import io.javaoperatorsdk.operator.processing.event.ResourceID; import io.javaoperatorsdk.operator.processing.event.source.ResourceEventSource; import io.javaoperatorsdk.operator.processing.event.source.filter.GenericFilter; @@ -15,7 +17,7 @@ @Ignore public abstract class AbstractEventSourceHolderDependentResource> - extends AbstractDependentResource { + extends AbstractDependentResource implements EventSourceAware

{ private T eventSource; private boolean isCacheFillerEventSource; @@ -23,6 +25,7 @@ public abstract class AbstractEventSourceHolderDependentResource onUpdateFilter; protected OnDeleteFilter onDeleteFilter; protected GenericFilter genericFilter; + protected String eventSourceToUse; public ResourceEventSource provideEventSource(EventSourceContext

context) { // some sub-classes (e.g. KubernetesDependentResource) can have their event source created @@ -38,6 +41,15 @@ public ResourceEventSource provideEventSource(EventSourceContext

contex return eventSource; } + @SuppressWarnings("unchecked") + @Override + public void selectEventSources(EventSourceRetriever

eventSourceRetriever) { + if (eventSourceToUse != null) { + eventSource = + (T) eventSourceRetriever.getResourceEventSourceFor(resourceType(), eventSourceToUse); + } + } + /** To make this backwards compatible even for respect of overriding */ public T initEventSource(EventSourceContext

context) { return (T) eventSource(context).orElseThrow(); @@ -90,4 +102,9 @@ public void setOnDeleteFilter(OnDeleteFilter onDeleteFilter) { this.onDeleteFilter = onDeleteFilter; } + public AbstractEventSourceHolderDependentResource setEventSourceToUse( + String eventSourceToUse) { + this.eventSourceToUse = eventSourceToUse; + return this; + } } diff --git a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/kubernetes/KubernetesDependent.java b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/kubernetes/KubernetesDependent.java index a259f97f74..2ccd4da82a 100644 --- a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/kubernetes/KubernetesDependent.java +++ b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/kubernetes/KubernetesDependent.java @@ -73,5 +73,5 @@ Class resourceDiscriminator() default ResourceDiscriminator.class; - String eventSourceNameToUse() default NO_VALUE_SET; + String eventSourceToUse() default NO_VALUE_SET; } 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 c438109379..2d524eac2d 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 @@ -31,7 +31,7 @@ public abstract class KubernetesDependentResource extends AbstractEventSourceHolderDependentResource> implements KubernetesClientAware, - DependentResourceConfigurator { + DependentResourceConfigurator> { private static final Logger log = LoggerFactory.getLogger(KubernetesDependentResource.class); @@ -53,9 +53,18 @@ public KubernetesDependentResource(Class resourceType) { : GenericResourceUpdatePreProcessor.processorFor(resourceType); } + @SuppressWarnings("unchecked") @Override - public void configureWith(KubernetesDependentResourceConfig config) { + public void configureWith(KubernetesDependentResourceConfig config) { this.kubernetesDependentResourceConfig = config; + var discriminator = kubernetesDependentResourceConfig.getResourceDiscriminator(); + if (discriminator != null) { + setResourceDiscriminator(discriminator); + } + config.getEventSourceToUse().ifPresent(n -> { + doNotProvideEventSource(); + setEventSourceToUse(n); + }); } private void configureWith(String labelSelector, Set namespaces, @@ -172,14 +181,12 @@ protected Resource prepare(R desired, protected InformerEventSource createEventSource(EventSourceContext

context) { if (kubernetesDependentResourceConfig != null) { // sets the filters for the dependent resource, which are applied by parent class + onAddFilter = kubernetesDependentResourceConfig.onAddFilter(); onUpdateFilter = kubernetesDependentResourceConfig.onUpdateFilter(); onDeleteFilter = kubernetesDependentResourceConfig.onDeleteFilter(); genericFilter = kubernetesDependentResourceConfig.genericFilter(); - var discriminator = kubernetesDependentResourceConfig.getResourceDiscriminator(); - if (discriminator != null) { - setResourceDiscriminator(discriminator); - } + configureWith(kubernetesDependentResourceConfig.labelSelector(), kubernetesDependentResourceConfig.namespaces(), !kubernetesDependentResourceConfig.wereNamespacesConfigured(), context); diff --git a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/kubernetes/KubernetesDependentResourceConfig.java b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/kubernetes/KubernetesDependentResourceConfig.java index a185071221..fc4122cd31 100644 --- a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/kubernetes/KubernetesDependentResourceConfig.java +++ b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/kubernetes/KubernetesDependentResourceConfig.java @@ -1,5 +1,6 @@ package io.javaoperatorsdk.operator.processing.dependent.kubernetes; +import java.util.Optional; import java.util.Set; import io.javaoperatorsdk.operator.api.reconciler.Constants; @@ -17,6 +18,7 @@ public class KubernetesDependentResourceConfig { private String labelSelector = NO_VALUE_SET; private boolean namespacesWereConfigured = false; private ResourceDiscriminator resourceDiscriminator; + private String eventSourceToUse; private OnAddFilter onAddFilter; @@ -33,7 +35,7 @@ public KubernetesDependentResourceConfig(Set namespaces, String labelSel boolean configuredNS, ResourceDiscriminator resourceDiscriminator, OnAddFilter onAddFilter, OnUpdateFilter onUpdateFilter, - OnDeleteFilter onDeleteFilter, GenericFilter genericFilter) { + OnDeleteFilter onDeleteFilter, GenericFilter genericFilter, String eventSourceToUse) { this.namespaces = namespaces; this.labelSelector = labelSelector; this.namespacesWereConfigured = configuredNS; @@ -42,10 +44,12 @@ public KubernetesDependentResourceConfig(Set namespaces, String labelSel this.onDeleteFilter = onDeleteFilter; this.genericFilter = genericFilter; this.resourceDiscriminator = resourceDiscriminator; + this.eventSourceToUse = eventSourceToUse; } public KubernetesDependentResourceConfig(Set namespaces, String labelSelector) { - this(namespaces, labelSelector, true, null, null, null, null, null); + this(namespaces, labelSelector, true, null, null, null, + null, null, null); } public KubernetesDependentResourceConfig setNamespaces(Set namespaces) { @@ -95,9 +99,7 @@ public ResourceDiscriminator getResourceDiscriminator() { return resourceDiscriminator; } - public

KubernetesDependentResourceConfig setResourceDiscriminator( - ResourceDiscriminator resourceDiscriminator) { - this.resourceDiscriminator = resourceDiscriminator; - return this; + public Optional getEventSourceToUse() { + return Optional.ofNullable(eventSourceToUse); } } diff --git a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/workflow/ManagedWorkflowSupport.java b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/workflow/ManagedWorkflowSupport.java index aad9475518..8ca56960b5 100644 --- a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/workflow/ManagedWorkflowSupport.java +++ b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/workflow/ManagedWorkflowSupport.java @@ -81,7 +81,9 @@ public DependentResource createAndConfigureFrom(DependentResourceSpec spec, if (dependentResource instanceof KubernetesClientAware) { ((KubernetesClientAware) dependentResource).setKubernetesClient(client); } - + if (!spec.provideEventSource()) { + dependentResource.doNotProvideEventSource(); + } if (dependentResource instanceof DependentResourceConfigurator) { final var configurator = (DependentResourceConfigurator) dependentResource; spec.getDependentResourceConfiguration().ifPresent(configurator::configureWith); diff --git a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/EventSourceRetriever.java b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/EventSourceRetriever.java index a31d8902b0..421092fb6a 100644 --- a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/EventSourceRetriever.java +++ b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/EventSourceRetriever.java @@ -11,7 +11,7 @@ ResourceEventSource getResourceEventSourceFor( Class dependentType); ResourceEventSource getResourceEventSourceFor( - Class dependentType, String qualifier); + Class dependentType, String name); List> getResourceEventSourcesFor(Class dependentType); diff --git a/sample-operators/mysql-schema/src/main/java/io/javaoperatorsdk/operator/sample/dependent/SchemaDependentResource.java b/sample-operators/mysql-schema/src/main/java/io/javaoperatorsdk/operator/sample/dependent/SchemaDependentResource.java index 060b3d7502..c262822d21 100644 --- a/sample-operators/mysql-schema/src/main/java/io/javaoperatorsdk/operator/sample/dependent/SchemaDependentResource.java +++ b/sample-operators/mysql-schema/src/main/java/io/javaoperatorsdk/operator/sample/dependent/SchemaDependentResource.java @@ -11,7 +11,6 @@ import io.fabric8.kubernetes.api.model.Secret; import io.javaoperatorsdk.operator.api.reconciler.Context; import io.javaoperatorsdk.operator.api.reconciler.dependent.Deleter; -import io.javaoperatorsdk.operator.api.reconciler.dependent.EventSourceProvider; import io.javaoperatorsdk.operator.api.reconciler.dependent.managed.DependentResourceConfigurator; import io.javaoperatorsdk.operator.processing.dependent.Creator; import io.javaoperatorsdk.operator.processing.dependent.external.PerResourcePollingDependentResource; @@ -26,8 +25,7 @@ public class SchemaDependentResource extends PerResourcePollingDependentResource - implements EventSourceProvider, - DependentResourceConfigurator, + implements DependentResourceConfigurator, Creator, Deleter { public static final String NAME = "schema"; From 72ef5b63c3a14d409f042c1f8bbff0fdc667f304 Mon Sep 17 00:00:00 2001 From: csviri Date: Mon, 26 Sep 2022 18:35:05 +0200 Subject: [PATCH 45/67] wip --- .../operator/MultipleManagedDependentSameTypeIT.java | 9 +++++++++ 1 file changed, 9 insertions(+) create mode 100644 operator-framework/src/test/java/io/javaoperatorsdk/operator/MultipleManagedDependentSameTypeIT.java diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/MultipleManagedDependentSameTypeIT.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/MultipleManagedDependentSameTypeIT.java new file mode 100644 index 0000000000..480b2c50d7 --- /dev/null +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/MultipleManagedDependentSameTypeIT.java @@ -0,0 +1,9 @@ +package io.javaoperatorsdk.operator; + +public class MultipleManagedDependentSameTypeIT { + + + + + +} From 5927d96e9a9fbfc7779c816201756ed63c327677 Mon Sep 17 00:00:00 2001 From: csviri Date: Tue, 27 Sep 2022 10:11:57 +0200 Subject: [PATCH 46/67] format --- .../operator/MultipleManagedDependentSameTypeIT.java | 4 ---- 1 file changed, 4 deletions(-) diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/MultipleManagedDependentSameTypeIT.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/MultipleManagedDependentSameTypeIT.java index 480b2c50d7..3ff1bf156f 100644 --- a/operator-framework/src/test/java/io/javaoperatorsdk/operator/MultipleManagedDependentSameTypeIT.java +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/MultipleManagedDependentSameTypeIT.java @@ -2,8 +2,4 @@ public class MultipleManagedDependentSameTypeIT { - - - - } From 6c87c40aabfd38feda20cc37e4fb06f074a3cd75 Mon Sep 17 00:00:00 2001 From: csviri Date: Tue, 27 Sep 2022 10:21:45 +0200 Subject: [PATCH 47/67] fix --- .../AnnotationControllerConfiguration.java | 3 +- ...ipleManagedDependentResourceConfigMap.java | 39 ++++++++++++++++ ...anagedDependentResourceCustomResource.java | 19 ++++++++ ...pleManagedDependentResourceReconciler.java | 44 +++++++++++++++++++ 4 files changed, 104 insertions(+), 1 deletion(-) create mode 100644 operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/multiplemanageddependent/MultipleManagedDependentResourceConfigMap.java create mode 100644 operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/multiplemanageddependent/MultipleManagedDependentResourceCustomResource.java create mode 100644 operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/multiplemanageddependent/MultipleManagedDependentResourceReconciler.java diff --git a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/config/AnnotationControllerConfiguration.java b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/config/AnnotationControllerConfiguration.java index 501d0a4dad..fb16ff419b 100644 --- a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/config/AnnotationControllerConfiguration.java +++ b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/config/AnnotationControllerConfiguration.java @@ -316,7 +316,8 @@ private Object createKubernetesResourceConfig(Class resourceDiscriminator = instantiateDiscriminatorIfNotVoid(kubeDependent.resourceDiscriminator()); - eventSourceNameToUse = kubeDependent.eventSourceToUse(); + eventSourceNameToUse = Constants.NO_VALUE_SET.equals(kubeDependent.eventSourceToUse()) ? null + : kubeDependent.eventSourceToUse(); } config = diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/multiplemanageddependent/MultipleManagedDependentResourceConfigMap.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/multiplemanageddependent/MultipleManagedDependentResourceConfigMap.java new file mode 100644 index 0000000000..e2d88c7948 --- /dev/null +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/multiplemanageddependent/MultipleManagedDependentResourceConfigMap.java @@ -0,0 +1,39 @@ +package io.javaoperatorsdk.operator.sample.multiplemanageddependent; + +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 MultipleManagedDependentResourceConfigMap + extends + CRUDKubernetesDependentResource { + + public static final String DATA_KEY = "key"; + private final int value; + + public MultipleManagedDependentResourceConfigMap(int value) { + super(ConfigMap.class); + this.value = value; + } + + @Override + protected ConfigMap desired(MultipleManagedDependentResourceCustomResource 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/multiplemanageddependent/MultipleManagedDependentResourceCustomResource.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/multiplemanageddependent/MultipleManagedDependentResourceCustomResource.java new file mode 100644 index 0000000000..fae9d4c05d --- /dev/null +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/multiplemanageddependent/MultipleManagedDependentResourceCustomResource.java @@ -0,0 +1,19 @@ +package io.javaoperatorsdk.operator.sample.multiplemanageddependent; + +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("mdr") +public class MultipleManagedDependentResourceCustomResource + extends CustomResource + implements Namespaced { + + public String getConfigMapName(int id) { + return "configmap" + id; + } +} diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/multiplemanageddependent/MultipleManagedDependentResourceReconciler.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/multiplemanageddependent/MultipleManagedDependentResourceReconciler.java new file mode 100644 index 0000000000..35ef1e84b3 --- /dev/null +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/multiplemanageddependent/MultipleManagedDependentResourceReconciler.java @@ -0,0 +1,44 @@ +package io.javaoperatorsdk.operator.sample.multiplemanageddependent; + +import java.util.Map; +import java.util.concurrent.atomic.AtomicInteger; + +import io.fabric8.kubernetes.client.KubernetesClient; +import io.javaoperatorsdk.operator.api.reconciler.*; +import io.javaoperatorsdk.operator.processing.event.source.EventSource; +import io.javaoperatorsdk.operator.support.TestExecutionInfoProvider; + +@ControllerConfiguration +public class MultipleManagedDependentResourceReconciler + implements Reconciler, + TestExecutionInfoProvider, + EventSourceInitializer { + + 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 KubernetesClient client; + + public MultipleManagedDependentResourceReconciler() {} + + @Override + public UpdateControl reconcile( + MultipleManagedDependentResourceCustomResource resource, + Context context) { + numberOfExecutions.getAndIncrement(); + + return UpdateControl.noUpdate(); + } + + + public int getNumberOfExecutions() { + return numberOfExecutions.get(); + } + + @Override + public Map prepareEventSources( + EventSourceContext context) { + return null; + } +} From b9101394a74ea9cafa766fc0df4bbb2113380d96 Mon Sep 17 00:00:00 2001 From: csviri Date: Tue, 27 Sep 2022 12:26:09 +0200 Subject: [PATCH 48/67] IT skeleton --- .../MultipleManagedDependentSameTypeIT.java | 55 ++++++++++++++++++- .../MultipleDependentResourceConfigMap.java | 2 - .../ConfigMap1Discriminator.java | 26 +++++++++ .../ConfigMap2Discriminator.java | 26 +++++++++ ...leManagedDependentResourceConfigMap1.java} | 17 +++--- ...pleManagedDependentResourceConfigMap2.java | 41 ++++++++++++++ ...anagedDependentResourceCustomResource.java | 7 +-- ...pleManagedDependentResourceReconciler.java | 23 +++++--- .../MultipleManagedDependentResourceSpec.java | 15 +++++ 9 files changed, 189 insertions(+), 23 deletions(-) create mode 100644 operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/multiplemanageddependent/ConfigMap1Discriminator.java create mode 100644 operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/multiplemanageddependent/ConfigMap2Discriminator.java rename operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/multiplemanageddependent/{MultipleManagedDependentResourceConfigMap.java => MultipleManagedDependentResourceConfigMap1.java} (63%) create mode 100644 operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/multiplemanageddependent/MultipleManagedDependentResourceConfigMap2.java create mode 100644 operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/multiplemanageddependent/MultipleManagedDependentResourceSpec.java diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/MultipleManagedDependentSameTypeIT.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/MultipleManagedDependentSameTypeIT.java index 3ff1bf156f..00b76c44cc 100644 --- a/operator-framework/src/test/java/io/javaoperatorsdk/operator/MultipleManagedDependentSameTypeIT.java +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/MultipleManagedDependentSameTypeIT.java @@ -1,5 +1,58 @@ package io.javaoperatorsdk.operator; -public class MultipleManagedDependentSameTypeIT { +import java.util.stream.Collectors; + +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.RegisterExtension; + +import io.fabric8.kubernetes.api.model.ObjectMetaBuilder; +import io.javaoperatorsdk.operator.junit.LocallyRunOperatorExtension; +import io.javaoperatorsdk.operator.sample.multiplemanageddependent.MultipleManagedDependentResourceCustomResource; +import io.javaoperatorsdk.operator.sample.multiplemanageddependent.MultipleManagedDependentResourceReconciler; +import io.javaoperatorsdk.operator.sample.multiplemanageddependent.MultipleManagedDependentResourceSpec; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.awaitility.Awaitility.await; + +class MultipleManagedDependentSameTypeIT { + + public static final String TEST_RESOURCE_NAME = "test1"; + public static final String DEFAULT_SPEC_VALUE = "val1"; + public static final String UPDATED_SPEC_VALUE = "val2"; + + @RegisterExtension + LocallyRunOperatorExtension operator = + LocallyRunOperatorExtension.builder() + .withReconciler(new MultipleManagedDependentResourceReconciler()) + .build(); + + + @Test + void handlesCrudOperations() { + operator.create(testResource()); + + assertConfigMapsCreated(DEFAULT_SPEC_VALUE); + } + + private void assertConfigMapsCreated(String expectedData) { + await().untilAsserted(() -> { + var maps = operator.getKubernetesClient().configMaps() + .inNamespace(operator.getNamespace()).list().getItems().stream() + .filter(cm -> cm.getMetadata().getName().startsWith(TEST_RESOURCE_NAME)) + .collect(Collectors.toList()); + assertThat(maps).hasSize(2); + }); + } + + private MultipleManagedDependentResourceCustomResource testResource() { + var res = new MultipleManagedDependentResourceCustomResource(); + res.setMetadata(new ObjectMetaBuilder() + .withName(TEST_RESOURCE_NAME) + .build()); + + res.setSpec(new MultipleManagedDependentResourceSpec()); + res.getSpec().setValue(DEFAULT_SPEC_VALUE); + return res; + } } 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 1adbfb9f95..5c2e9974b5 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 @@ -7,9 +7,7 @@ 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 MultipleDependentResourceConfigMap extends CRUDKubernetesDependentResource { diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/multiplemanageddependent/ConfigMap1Discriminator.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/multiplemanageddependent/ConfigMap1Discriminator.java new file mode 100644 index 0000000000..975687f7dc --- /dev/null +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/multiplemanageddependent/ConfigMap1Discriminator.java @@ -0,0 +1,26 @@ +package io.javaoperatorsdk.operator.sample.multiplemanageddependent; + +import java.util.Optional; + +import io.fabric8.kubernetes.api.model.ConfigMap; +import io.javaoperatorsdk.operator.api.reconciler.Context; +import io.javaoperatorsdk.operator.api.reconciler.ResourceDiscriminator; +import io.javaoperatorsdk.operator.processing.event.ResourceID; +import io.javaoperatorsdk.operator.processing.event.source.informer.InformerEventSource; + +import static io.javaoperatorsdk.operator.sample.multiplemanageddependent.MultipleManagedDependentResourceConfigMap1.NAME_SUFFIX; + +public class ConfigMap1Discriminator + implements ResourceDiscriminator { + @Override + public Optional distinguish(Class resource, + MultipleManagedDependentResourceCustomResource primary, + Context context) { + InformerEventSource ies = + (InformerEventSource) context + .eventSourceRetriever().getResourceEventSourceFor(ConfigMap.class); + + return ies.get(new ResourceID(primary.getMetadata().getName() + NAME_SUFFIX, + primary.getMetadata().getNamespace())); + } +} diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/multiplemanageddependent/ConfigMap2Discriminator.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/multiplemanageddependent/ConfigMap2Discriminator.java new file mode 100644 index 0000000000..b15b01491c --- /dev/null +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/multiplemanageddependent/ConfigMap2Discriminator.java @@ -0,0 +1,26 @@ +package io.javaoperatorsdk.operator.sample.multiplemanageddependent; + +import java.util.Optional; + +import io.fabric8.kubernetes.api.model.ConfigMap; +import io.javaoperatorsdk.operator.api.reconciler.Context; +import io.javaoperatorsdk.operator.api.reconciler.ResourceDiscriminator; +import io.javaoperatorsdk.operator.processing.event.ResourceID; +import io.javaoperatorsdk.operator.processing.event.source.informer.InformerEventSource; + +import static io.javaoperatorsdk.operator.sample.multiplemanageddependent.MultipleManagedDependentResourceConfigMap2.NAME_SUFFIX; + +public class ConfigMap2Discriminator + implements ResourceDiscriminator { + @Override + public Optional distinguish(Class resource, + MultipleManagedDependentResourceCustomResource primary, + Context context) { + InformerEventSource ies = + (InformerEventSource) context + .eventSourceRetriever().getResourceEventSourceFor(ConfigMap.class); + + return ies.get(new ResourceID(primary.getMetadata().getName() + NAME_SUFFIX, + primary.getMetadata().getNamespace())); + } +} diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/multiplemanageddependent/MultipleManagedDependentResourceConfigMap.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/multiplemanageddependent/MultipleManagedDependentResourceConfigMap1.java similarity index 63% rename from operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/multiplemanageddependent/MultipleManagedDependentResourceConfigMap.java rename to operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/multiplemanageddependent/MultipleManagedDependentResourceConfigMap1.java index e2d88c7948..7e489b3896 100644 --- a/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/multiplemanageddependent/MultipleManagedDependentResourceConfigMap.java +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/multiplemanageddependent/MultipleManagedDependentResourceConfigMap1.java @@ -9,28 +9,29 @@ import io.javaoperatorsdk.operator.processing.dependent.kubernetes.CRUDKubernetesDependentResource; import io.javaoperatorsdk.operator.processing.dependent.kubernetes.KubernetesDependent; -@KubernetesDependent -public class MultipleManagedDependentResourceConfigMap +import static io.javaoperatorsdk.operator.sample.multiplemanageddependent.MultipleManagedDependentResourceReconciler.CONFIG_MAP_EVENT_SOURCE; + +@KubernetesDependent(eventSourceToUse = CONFIG_MAP_EVENT_SOURCE, + resourceDiscriminator = ConfigMap1Discriminator.class) +public class MultipleManagedDependentResourceConfigMap1 extends CRUDKubernetesDependentResource { - public static final String DATA_KEY = "key"; - private final int value; + public static final String NAME_SUFFIX = "-1"; - public MultipleManagedDependentResourceConfigMap(int value) { + public MultipleManagedDependentResourceConfigMap1() { super(ConfigMap.class); - this.value = value; } @Override protected ConfigMap desired(MultipleManagedDependentResourceCustomResource primary, Context context) { Map data = new HashMap<>(); - data.put(DATA_KEY, String.valueOf(value)); + data.put(MultipleManagedDependentResourceReconciler.DATA_KEY, primary.getSpec().getValue()); return new ConfigMapBuilder() .withNewMetadata() - .withName(primary.getConfigMapName(value)) + .withName(primary.getMetadata().getName() + NAME_SUFFIX) .withNamespace(primary.getMetadata().getNamespace()) .endMetadata() .withData(data) diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/multiplemanageddependent/MultipleManagedDependentResourceConfigMap2.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/multiplemanageddependent/MultipleManagedDependentResourceConfigMap2.java new file mode 100644 index 0000000000..a48b05e83d --- /dev/null +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/multiplemanageddependent/MultipleManagedDependentResourceConfigMap2.java @@ -0,0 +1,41 @@ +package io.javaoperatorsdk.operator.sample.multiplemanageddependent; + +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.multiplemanageddependent.MultipleManagedDependentResourceReconciler.CONFIG_MAP_EVENT_SOURCE; +import static io.javaoperatorsdk.operator.sample.multiplemanageddependent.MultipleManagedDependentResourceReconciler.DATA_KEY; + +@KubernetesDependent(eventSourceToUse = CONFIG_MAP_EVENT_SOURCE, + resourceDiscriminator = ConfigMap2Discriminator.class) +public class MultipleManagedDependentResourceConfigMap2 + extends + CRUDKubernetesDependentResource { + + public static final String NAME_SUFFIX = "-2"; + + public MultipleManagedDependentResourceConfigMap2() { + super(ConfigMap.class); + } + + @Override + protected ConfigMap desired(MultipleManagedDependentResourceCustomResource 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/multiplemanageddependent/MultipleManagedDependentResourceCustomResource.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/multiplemanageddependent/MultipleManagedDependentResourceCustomResource.java index fae9d4c05d..f10abbc6f5 100644 --- a/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/multiplemanageddependent/MultipleManagedDependentResourceCustomResource.java +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/multiplemanageddependent/MultipleManagedDependentResourceCustomResource.java @@ -8,12 +8,9 @@ @Group("sample.javaoperatorsdk") @Version("v1") -@ShortNames("mdr") +@ShortNames("mmd") public class MultipleManagedDependentResourceCustomResource - 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/multiplemanageddependent/MultipleManagedDependentResourceReconciler.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/multiplemanageddependent/MultipleManagedDependentResourceReconciler.java index 35ef1e84b3..b94c514578 100644 --- a/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/multiplemanageddependent/MultipleManagedDependentResourceReconciler.java +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/multiplemanageddependent/MultipleManagedDependentResourceReconciler.java @@ -3,22 +3,27 @@ import java.util.Map; import java.util.concurrent.atomic.AtomicInteger; -import io.fabric8.kubernetes.client.KubernetesClient; +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; -@ControllerConfiguration +@ControllerConfiguration(dependents = { + @Dependent(type = MultipleManagedDependentResourceConfigMap1.class), + @Dependent(type = MultipleManagedDependentResourceConfigMap2.class) +}) public class MultipleManagedDependentResourceReconciler implements Reconciler, TestExecutionInfoProvider, EventSourceInitializer { - 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 CONFIG_MAP_EVENT_SOURCE = "ConfigMapEventSource"; + public static final String DATA_KEY = "key"; - private KubernetesClient client; + private final AtomicInteger numberOfExecutions = new AtomicInteger(0); public MultipleManagedDependentResourceReconciler() {} @@ -39,6 +44,10 @@ public int getNumberOfExecutions() { @Override public Map prepareEventSources( EventSourceContext context) { - return null; + 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/multiplemanageddependent/MultipleManagedDependentResourceSpec.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/multiplemanageddependent/MultipleManagedDependentResourceSpec.java new file mode 100644 index 0000000000..cc388b767f --- /dev/null +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/multiplemanageddependent/MultipleManagedDependentResourceSpec.java @@ -0,0 +1,15 @@ +package io.javaoperatorsdk.operator.sample.multiplemanageddependent; + +public class MultipleManagedDependentResourceSpec { + + private String value; + + public String getValue() { + return value; + } + + public MultipleManagedDependentResourceSpec setValue(String value) { + this.value = value; + return this; + } +} From ef67469115d71dbfb0c4b5695b35b16bfc92dfa3 Mon Sep 17 00:00:00 2001 From: csviri Date: Tue, 27 Sep 2022 12:31:59 +0200 Subject: [PATCH 49/67] IT managed dependent resourc --- .../MultipleManagedDependentSameTypeIT.java | 27 ++++++++++++++++--- 1 file changed, 23 insertions(+), 4 deletions(-) diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/MultipleManagedDependentSameTypeIT.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/MultipleManagedDependentSameTypeIT.java index 00b76c44cc..072f3325ac 100644 --- a/operator-framework/src/test/java/io/javaoperatorsdk/operator/MultipleManagedDependentSameTypeIT.java +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/MultipleManagedDependentSameTypeIT.java @@ -11,14 +11,15 @@ import io.javaoperatorsdk.operator.sample.multiplemanageddependent.MultipleManagedDependentResourceReconciler; import io.javaoperatorsdk.operator.sample.multiplemanageddependent.MultipleManagedDependentResourceSpec; +import static io.javaoperatorsdk.operator.sample.multiplemanageddependent.MultipleManagedDependentResourceReconciler.DATA_KEY; import static org.assertj.core.api.Assertions.assertThat; import static org.awaitility.Awaitility.await; class MultipleManagedDependentSameTypeIT { public static final String TEST_RESOURCE_NAME = "test1"; - public static final String DEFAULT_SPEC_VALUE = "val1"; - public static final String UPDATED_SPEC_VALUE = "val2"; + public static final String DEFAULT_SPEC_VALUE = "val"; + public static final String UPDATED_SPEC_VALUE = "updated-val"; @RegisterExtension LocallyRunOperatorExtension operator = @@ -30,17 +31,35 @@ class MultipleManagedDependentSameTypeIT { @Test void handlesCrudOperations() { operator.create(testResource()); + assertConfigMapsPresent(DEFAULT_SPEC_VALUE); - assertConfigMapsCreated(DEFAULT_SPEC_VALUE); + var updatedResource = testResource(); + updatedResource.getSpec().setValue(UPDATED_SPEC_VALUE); + operator.replace(updatedResource); + assertConfigMapsPresent(UPDATED_SPEC_VALUE); + + operator.delete(testResource()); + assertConfigMapsDeleted(); } - private void assertConfigMapsCreated(String expectedData) { + private void assertConfigMapsPresent(String expectedData) { await().untilAsserted(() -> { var maps = operator.getKubernetesClient().configMaps() .inNamespace(operator.getNamespace()).list().getItems().stream() .filter(cm -> cm.getMetadata().getName().startsWith(TEST_RESOURCE_NAME)) .collect(Collectors.toList()); assertThat(maps).hasSize(2); + assertThat(maps).allMatch(cm -> cm.getData().get(DATA_KEY).equals(expectedData)); + }); + } + + private void assertConfigMapsDeleted() { + await().untilAsserted(() -> { + var maps = operator.getKubernetesClient().configMaps() + .inNamespace(operator.getNamespace()).list().getItems().stream() + .filter(cm -> cm.getMetadata().getName().startsWith(TEST_RESOURCE_NAME)) + .collect(Collectors.toList()); + assertThat(maps).hasSize(0); }); } From d80feed26cc97f93ad19716c5f8289f3ae45bf6f Mon Sep 17 00:00:00 2001 From: csviri Date: Tue, 27 Sep 2022 12:50:24 +0200 Subject: [PATCH 50/67] IT improvement --- .../javaoperatorsdk/operator/IntegrationTestConstants.java | 7 +++++++ .../operator/MultipleManagedDependentSameTypeIT.java | 5 ++++- 2 files changed, 11 insertions(+), 1 deletion(-) create mode 100644 operator-framework/src/test/java/io/javaoperatorsdk/operator/IntegrationTestConstants.java diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/IntegrationTestConstants.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/IntegrationTestConstants.java new file mode 100644 index 0000000000..1191e6a121 --- /dev/null +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/IntegrationTestConstants.java @@ -0,0 +1,7 @@ +package io.javaoperatorsdk.operator; + +public class IntegrationTestConstants { + + public static final int GARBAGE_COLLECTION_TIMEOUT_SECONDS = 30; + +} diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/MultipleManagedDependentSameTypeIT.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/MultipleManagedDependentSameTypeIT.java index 072f3325ac..def9286914 100644 --- a/operator-framework/src/test/java/io/javaoperatorsdk/operator/MultipleManagedDependentSameTypeIT.java +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/MultipleManagedDependentSameTypeIT.java @@ -1,5 +1,6 @@ package io.javaoperatorsdk.operator; +import java.time.Duration; import java.util.stream.Collectors; import org.junit.jupiter.api.Test; @@ -11,6 +12,7 @@ import io.javaoperatorsdk.operator.sample.multiplemanageddependent.MultipleManagedDependentResourceReconciler; import io.javaoperatorsdk.operator.sample.multiplemanageddependent.MultipleManagedDependentResourceSpec; +import static io.javaoperatorsdk.operator.IntegrationTestConstants.GARBAGE_COLLECTION_TIMEOUT_SECONDS; import static io.javaoperatorsdk.operator.sample.multiplemanageddependent.MultipleManagedDependentResourceReconciler.DATA_KEY; import static org.assertj.core.api.Assertions.assertThat; import static org.awaitility.Awaitility.await; @@ -20,6 +22,7 @@ class MultipleManagedDependentSameTypeIT { public static final String TEST_RESOURCE_NAME = "test1"; public static final String DEFAULT_SPEC_VALUE = "val"; public static final String UPDATED_SPEC_VALUE = "updated-val"; + public static final int SECONDS = 30; @RegisterExtension LocallyRunOperatorExtension operator = @@ -54,7 +57,7 @@ private void assertConfigMapsPresent(String expectedData) { } private void assertConfigMapsDeleted() { - await().untilAsserted(() -> { + await().atMost(Duration.ofSeconds(GARBAGE_COLLECTION_TIMEOUT_SECONDS)).untilAsserted(() -> { var maps = operator.getKubernetesClient().configMaps() .inNamespace(operator.getNamespace()).list().getItems().stream() .filter(cm -> cm.getMetadata().getName().startsWith(TEST_RESOURCE_NAME)) From a6bee16ca380c1646c3834d1dcad78eaffbaff7f Mon Sep 17 00:00:00 2001 From: csviri Date: Tue, 27 Sep 2022 15:12:43 +0200 Subject: [PATCH 51/67] wip external resource handling --- ...ubernetesDependentGarbageCollectionIT.java | 9 +-- .../MultipleManagedDependentSameTypeIT.java | 8 +-- ...pleManagedExternalDependentSameTypeIT.java | 56 ++++++++++++++++ .../BulkExternalDependentIT.java | 2 +- .../ExternalBulkDependentResource.java | 19 ++---- .../external/ExternalResource.java | 37 ---------- .../ConfigMap1Discriminator.java | 4 +- .../ConfigMap2Discriminator.java | 4 +- ...pleManagedDependentResourceConfigMap1.java | 4 +- ...pleManagedDependentResourceConfigMap2.java | 6 +- ...anagedDependentResourceCustomResource.java | 2 +- ...pleManagedDependentResourceReconciler.java | 2 +- .../MultipleManagedDependentResourceSpec.java | 2 +- .../AbstractExternalDependentResource.java | 63 +++++++++++++++++ .../ExternalDependentResource1.java | 20 ++++++ .../ExternalDependentResource2.java | 20 ++++++ .../ExternalResourceDiscriminator.java | 25 +++++++ ...ternalDependentResourceCustomResource.java | 17 +++++ ...edExternalDependentResourceReconciler.java | 67 +++++++++++++++++++ .../operator/support/ExternalResource.java | 58 ++++++++++++++++ .../ExternalServiceMock.java | 2 +- 21 files changed, 356 insertions(+), 71 deletions(-) create mode 100644 operator-framework/src/test/java/io/javaoperatorsdk/operator/MultipleManagedExternalDependentSameTypeIT.java delete mode 100644 operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/bulkdependent/external/ExternalResource.java rename operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/{multiplemanageddependent => multiplemanageddependentsametype}/ConfigMap1Discriminator.java (93%) rename operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/{multiplemanageddependent => multiplemanageddependentsametype}/ConfigMap2Discriminator.java (93%) rename operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/{multiplemanageddependent => multiplemanageddependentsametype}/MultipleManagedDependentResourceConfigMap1.java (93%) rename operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/{multiplemanageddependent => multiplemanageddependentsametype}/MultipleManagedDependentResourceConfigMap2.java (90%) rename operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/{multiplemanageddependent => multiplemanageddependentsametype}/MultipleManagedDependentResourceCustomResource.java (97%) rename operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/{multiplemanageddependent => multiplemanageddependentsametype}/MultipleManagedDependentResourceReconciler.java (99%) rename operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/{multiplemanageddependent => multiplemanageddependentsametype}/MultipleManagedDependentResourceSpec.java (96%) create mode 100644 operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/multiplemanagedexternaldependenttype/AbstractExternalDependentResource.java create mode 100644 operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/multiplemanagedexternaldependenttype/ExternalDependentResource1.java create mode 100644 operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/multiplemanagedexternaldependenttype/ExternalDependentResource2.java create mode 100644 operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/multiplemanagedexternaldependenttype/ExternalResourceDiscriminator.java create mode 100644 operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/multiplemanagedexternaldependenttype/MultipleManagedExternalDependentResourceCustomResource.java create mode 100644 operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/multiplemanagedexternaldependenttype/MultipleManagedExternalDependentResourceReconciler.java create mode 100644 operator-framework/src/test/java/io/javaoperatorsdk/operator/support/ExternalResource.java rename operator-framework/src/test/java/io/javaoperatorsdk/operator/{sample/bulkdependent/external => support}/ExternalServiceMock.java (94%) diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/KubernetesDependentGarbageCollectionIT.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/KubernetesDependentGarbageCollectionIT.java index dc3f080ab7..c32f328d76 100644 --- a/operator-framework/src/test/java/io/javaoperatorsdk/operator/KubernetesDependentGarbageCollectionIT.java +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/KubernetesDependentGarbageCollectionIT.java @@ -43,10 +43,11 @@ void resourceSecondaryResourceIsGarbageCollected() { operator.delete(createdResources); - await().atMost(Duration.ofSeconds(30)).untilAsserted(() -> { - ConfigMap cm = operator.get(ConfigMap.class, TEST_RESOURCE_NAME); - assertThat(cm).isNull(); - }); + await().atMost(Duration.ofSeconds(IntegrationTestConstants.GARBAGE_COLLECTION_TIMEOUT_SECONDS)) + .untilAsserted(() -> { + ConfigMap cm = operator.get(ConfigMap.class, TEST_RESOURCE_NAME); + assertThat(cm).isNull(); + }); } @Test diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/MultipleManagedDependentSameTypeIT.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/MultipleManagedDependentSameTypeIT.java index def9286914..32fe0eaec4 100644 --- a/operator-framework/src/test/java/io/javaoperatorsdk/operator/MultipleManagedDependentSameTypeIT.java +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/MultipleManagedDependentSameTypeIT.java @@ -8,12 +8,12 @@ import io.fabric8.kubernetes.api.model.ObjectMetaBuilder; import io.javaoperatorsdk.operator.junit.LocallyRunOperatorExtension; -import io.javaoperatorsdk.operator.sample.multiplemanageddependent.MultipleManagedDependentResourceCustomResource; -import io.javaoperatorsdk.operator.sample.multiplemanageddependent.MultipleManagedDependentResourceReconciler; -import io.javaoperatorsdk.operator.sample.multiplemanageddependent.MultipleManagedDependentResourceSpec; +import io.javaoperatorsdk.operator.sample.multiplemanageddependentsametype.MultipleManagedDependentResourceCustomResource; +import io.javaoperatorsdk.operator.sample.multiplemanageddependentsametype.MultipleManagedDependentResourceReconciler; +import io.javaoperatorsdk.operator.sample.multiplemanageddependentsametype.MultipleManagedDependentResourceSpec; import static io.javaoperatorsdk.operator.IntegrationTestConstants.GARBAGE_COLLECTION_TIMEOUT_SECONDS; -import static io.javaoperatorsdk.operator.sample.multiplemanageddependent.MultipleManagedDependentResourceReconciler.DATA_KEY; +import static io.javaoperatorsdk.operator.sample.multiplemanageddependentsametype.MultipleManagedDependentResourceReconciler.DATA_KEY; import static org.assertj.core.api.Assertions.assertThat; import static org.awaitility.Awaitility.await; diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/MultipleManagedExternalDependentSameTypeIT.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/MultipleManagedExternalDependentSameTypeIT.java new file mode 100644 index 0000000000..65fc5b404c --- /dev/null +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/MultipleManagedExternalDependentSameTypeIT.java @@ -0,0 +1,56 @@ +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.javaoperatorsdk.operator.junit.LocallyRunOperatorExtension; +import io.javaoperatorsdk.operator.sample.multiplemanageddependentsametype.MultipleManagedDependentResourceSpec; +import io.javaoperatorsdk.operator.sample.multiplemanagedexternaldependenttype.MultipleManagedExternalDependentResourceCustomResource; +import io.javaoperatorsdk.operator.sample.multiplemanagedexternaldependenttype.MultipleManagedExternalDependentResourceReconciler; +import io.javaoperatorsdk.operator.support.ExternalServiceMock; + +import static io.javaoperatorsdk.operator.MultipleManagedDependentSameTypeIT.DEFAULT_SPEC_VALUE; +import static org.assertj.core.api.Assertions.assertThat; +import static org.awaitility.Awaitility.await; + +class MultipleManagedExternalDependentSameTypeIT { + + @RegisterExtension + LocallyRunOperatorExtension operator = + LocallyRunOperatorExtension.builder() + .withReconciler(new MultipleManagedExternalDependentResourceReconciler()) + .build(); + + public static final String TEST_RESOURCE_NAME = "test1"; + public static final String DEFAULT_SPEC_VALUE = "val"; + public static final String UPDATED_SPEC_VALUE = "updated-val"; + + protected ExternalServiceMock externalServiceMock = ExternalServiceMock.getInstance(); + + @Test + void handlesExternalCrudOperations() { + operator.create(testResource()); + + assertResourceCreatedWithData(); + } + + private void assertResourceCreatedWithData() { + await().untilAsserted(() -> { + var resources = externalServiceMock.listResources(); + assertThat(resources).hasSize(2); + }); + } + + private MultipleManagedExternalDependentResourceCustomResource testResource() { + var res = new MultipleManagedExternalDependentResourceCustomResource(); + res.setMetadata(new ObjectMetaBuilder() + .withName(TEST_RESOURCE_NAME) + .build()); + + res.setSpec(new MultipleManagedDependentResourceSpec()); + res.getSpec().setValue(DEFAULT_SPEC_VALUE); + return res; + } + +} diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/bulkdependent/BulkExternalDependentIT.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/bulkdependent/BulkExternalDependentIT.java index 29f66e8205..38f0cc2316 100644 --- a/operator-framework/src/test/java/io/javaoperatorsdk/operator/bulkdependent/BulkExternalDependentIT.java +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/bulkdependent/BulkExternalDependentIT.java @@ -5,7 +5,7 @@ import io.javaoperatorsdk.operator.junit.LocallyRunOperatorExtension; import io.javaoperatorsdk.operator.sample.bulkdependent.external.ExternalBulkResourceReconciler; -import io.javaoperatorsdk.operator.sample.bulkdependent.external.ExternalServiceMock; +import io.javaoperatorsdk.operator.support.ExternalServiceMock; import static io.javaoperatorsdk.operator.bulkdependent.BulkDependentTestBase.*; import static org.assertj.core.api.Assertions.assertThat; diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/bulkdependent/external/ExternalBulkDependentResource.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/bulkdependent/external/ExternalBulkDependentResource.java index 0638c658c2..7055a58721 100644 --- a/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/bulkdependent/external/ExternalBulkDependentResource.java +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/bulkdependent/external/ExternalBulkDependentResource.java @@ -8,13 +8,18 @@ import io.javaoperatorsdk.operator.processing.dependent.external.PollingDependentResource; import io.javaoperatorsdk.operator.processing.event.ResourceID; import io.javaoperatorsdk.operator.sample.bulkdependent.BulkDependentTestCustomResource; +import io.javaoperatorsdk.operator.support.ExternalResource; +import io.javaoperatorsdk.operator.support.ExternalServiceMock; + +import static io.javaoperatorsdk.operator.support.ExternalResource.EXTERNAL_RESOURCE_NAME_DELIMITER; +import static io.javaoperatorsdk.operator.support.ExternalResource.toExternalResourceId; public class ExternalBulkDependentResource extends PollingDependentResource implements BulkDependentResource, BulkUpdater { - public static final String EXTERNAL_RESOURCE_NAME_DELIMITER = "#"; + private ExternalServiceMock externalServiceMock = ExternalServiceMock.getInstance(); @@ -27,7 +32,7 @@ public Map> fetchResources() { Map> result = new HashMap<>(); var resources = externalServiceMock.listResources(); resources.stream().forEach(er -> { - var resourceID = toResourceID(er); + var resourceID = er.toResourceID(); result.putIfAbsent(resourceID, new HashSet<>()); result.get(resourceID).add(er); }); @@ -88,14 +93,4 @@ public Matcher.Result match(ExternalResource actualResource, return Matcher.Result.computed(desired.equals(actualResource), desired); } - private static String toExternalResourceId(BulkDependentTestCustomResource primary, int i) { - return primary.getMetadata().getName() + EXTERNAL_RESOURCE_NAME_DELIMITER + - primary.getMetadata().getNamespace() + - EXTERNAL_RESOURCE_NAME_DELIMITER + i; - } - - private ResourceID toResourceID(ExternalResource externalResource) { - var parts = externalResource.getId().split(EXTERNAL_RESOURCE_NAME_DELIMITER); - return new ResourceID(parts[0], parts[1]); - } } diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/bulkdependent/external/ExternalResource.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/bulkdependent/external/ExternalResource.java deleted file mode 100644 index 935fd99e47..0000000000 --- a/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/bulkdependent/external/ExternalResource.java +++ /dev/null @@ -1,37 +0,0 @@ -package io.javaoperatorsdk.operator.sample.bulkdependent.external; - -import java.util.Objects; - -public class ExternalResource { - - private String id; - private String data; - - public ExternalResource(String id, String data) { - this.id = id; - this.data = data; - } - - public String getId() { - return id; - } - - public String getData() { - return data; - } - - @Override - public boolean equals(Object o) { - if (this == o) - return true; - if (o == null || getClass() != o.getClass()) - return false; - ExternalResource that = (ExternalResource) o; - return Objects.equals(id, that.id) && Objects.equals(data, that.data); - } - - @Override - public int hashCode() { - return Objects.hash(id, data); - } -} diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/multiplemanageddependent/ConfigMap1Discriminator.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/multiplemanageddependentsametype/ConfigMap1Discriminator.java similarity index 93% rename from operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/multiplemanageddependent/ConfigMap1Discriminator.java rename to operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/multiplemanageddependentsametype/ConfigMap1Discriminator.java index 975687f7dc..cc20dfa45e 100644 --- a/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/multiplemanageddependent/ConfigMap1Discriminator.java +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/multiplemanageddependentsametype/ConfigMap1Discriminator.java @@ -1,4 +1,4 @@ -package io.javaoperatorsdk.operator.sample.multiplemanageddependent; +package io.javaoperatorsdk.operator.sample.multiplemanageddependentsametype; import java.util.Optional; @@ -8,7 +8,7 @@ import io.javaoperatorsdk.operator.processing.event.ResourceID; import io.javaoperatorsdk.operator.processing.event.source.informer.InformerEventSource; -import static io.javaoperatorsdk.operator.sample.multiplemanageddependent.MultipleManagedDependentResourceConfigMap1.NAME_SUFFIX; +import static io.javaoperatorsdk.operator.sample.multiplemanageddependentsametype.MultipleManagedDependentResourceConfigMap1.NAME_SUFFIX; public class ConfigMap1Discriminator implements ResourceDiscriminator { diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/multiplemanageddependent/ConfigMap2Discriminator.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/multiplemanageddependentsametype/ConfigMap2Discriminator.java similarity index 93% rename from operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/multiplemanageddependent/ConfigMap2Discriminator.java rename to operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/multiplemanageddependentsametype/ConfigMap2Discriminator.java index b15b01491c..8bda6afcee 100644 --- a/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/multiplemanageddependent/ConfigMap2Discriminator.java +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/multiplemanageddependentsametype/ConfigMap2Discriminator.java @@ -1,4 +1,4 @@ -package io.javaoperatorsdk.operator.sample.multiplemanageddependent; +package io.javaoperatorsdk.operator.sample.multiplemanageddependentsametype; import java.util.Optional; @@ -8,7 +8,7 @@ import io.javaoperatorsdk.operator.processing.event.ResourceID; import io.javaoperatorsdk.operator.processing.event.source.informer.InformerEventSource; -import static io.javaoperatorsdk.operator.sample.multiplemanageddependent.MultipleManagedDependentResourceConfigMap2.NAME_SUFFIX; +import static io.javaoperatorsdk.operator.sample.multiplemanageddependentsametype.MultipleManagedDependentResourceConfigMap2.NAME_SUFFIX; public class ConfigMap2Discriminator implements ResourceDiscriminator { diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/multiplemanageddependent/MultipleManagedDependentResourceConfigMap1.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/multiplemanageddependentsametype/MultipleManagedDependentResourceConfigMap1.java similarity index 93% rename from operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/multiplemanageddependent/MultipleManagedDependentResourceConfigMap1.java rename to operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/multiplemanageddependentsametype/MultipleManagedDependentResourceConfigMap1.java index 7e489b3896..0d450c1871 100644 --- a/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/multiplemanageddependent/MultipleManagedDependentResourceConfigMap1.java +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/multiplemanageddependentsametype/MultipleManagedDependentResourceConfigMap1.java @@ -1,4 +1,4 @@ -package io.javaoperatorsdk.operator.sample.multiplemanageddependent; +package io.javaoperatorsdk.operator.sample.multiplemanageddependentsametype; import java.util.HashMap; import java.util.Map; @@ -9,7 +9,7 @@ import io.javaoperatorsdk.operator.processing.dependent.kubernetes.CRUDKubernetesDependentResource; import io.javaoperatorsdk.operator.processing.dependent.kubernetes.KubernetesDependent; -import static io.javaoperatorsdk.operator.sample.multiplemanageddependent.MultipleManagedDependentResourceReconciler.CONFIG_MAP_EVENT_SOURCE; +import static io.javaoperatorsdk.operator.sample.multiplemanageddependentsametype.MultipleManagedDependentResourceReconciler.CONFIG_MAP_EVENT_SOURCE; @KubernetesDependent(eventSourceToUse = CONFIG_MAP_EVENT_SOURCE, resourceDiscriminator = ConfigMap1Discriminator.class) diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/multiplemanageddependent/MultipleManagedDependentResourceConfigMap2.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/multiplemanageddependentsametype/MultipleManagedDependentResourceConfigMap2.java similarity index 90% rename from operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/multiplemanageddependent/MultipleManagedDependentResourceConfigMap2.java rename to operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/multiplemanageddependentsametype/MultipleManagedDependentResourceConfigMap2.java index a48b05e83d..11902fc518 100644 --- a/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/multiplemanageddependent/MultipleManagedDependentResourceConfigMap2.java +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/multiplemanageddependentsametype/MultipleManagedDependentResourceConfigMap2.java @@ -1,4 +1,4 @@ -package io.javaoperatorsdk.operator.sample.multiplemanageddependent; +package io.javaoperatorsdk.operator.sample.multiplemanageddependentsametype; import java.util.HashMap; import java.util.Map; @@ -9,8 +9,8 @@ import io.javaoperatorsdk.operator.processing.dependent.kubernetes.CRUDKubernetesDependentResource; import io.javaoperatorsdk.operator.processing.dependent.kubernetes.KubernetesDependent; -import static io.javaoperatorsdk.operator.sample.multiplemanageddependent.MultipleManagedDependentResourceReconciler.CONFIG_MAP_EVENT_SOURCE; -import static io.javaoperatorsdk.operator.sample.multiplemanageddependent.MultipleManagedDependentResourceReconciler.DATA_KEY; +import static io.javaoperatorsdk.operator.sample.multiplemanageddependentsametype.MultipleManagedDependentResourceReconciler.CONFIG_MAP_EVENT_SOURCE; +import static io.javaoperatorsdk.operator.sample.multiplemanageddependentsametype.MultipleManagedDependentResourceReconciler.DATA_KEY; @KubernetesDependent(eventSourceToUse = CONFIG_MAP_EVENT_SOURCE, resourceDiscriminator = ConfigMap2Discriminator.class) diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/multiplemanageddependent/MultipleManagedDependentResourceCustomResource.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/multiplemanageddependentsametype/MultipleManagedDependentResourceCustomResource.java similarity index 97% rename from operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/multiplemanageddependent/MultipleManagedDependentResourceCustomResource.java rename to operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/multiplemanageddependentsametype/MultipleManagedDependentResourceCustomResource.java index f10abbc6f5..44564727a2 100644 --- a/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/multiplemanageddependent/MultipleManagedDependentResourceCustomResource.java +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/multiplemanageddependentsametype/MultipleManagedDependentResourceCustomResource.java @@ -1,4 +1,4 @@ -package io.javaoperatorsdk.operator.sample.multiplemanageddependent; +package io.javaoperatorsdk.operator.sample.multiplemanageddependentsametype; import io.fabric8.kubernetes.api.model.Namespaced; import io.fabric8.kubernetes.client.CustomResource; diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/multiplemanageddependent/MultipleManagedDependentResourceReconciler.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/multiplemanageddependentsametype/MultipleManagedDependentResourceReconciler.java similarity index 99% rename from operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/multiplemanageddependent/MultipleManagedDependentResourceReconciler.java rename to operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/multiplemanageddependentsametype/MultipleManagedDependentResourceReconciler.java index b94c514578..edfa7ad2bd 100644 --- a/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/multiplemanageddependent/MultipleManagedDependentResourceReconciler.java +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/multiplemanageddependentsametype/MultipleManagedDependentResourceReconciler.java @@ -1,4 +1,4 @@ -package io.javaoperatorsdk.operator.sample.multiplemanageddependent; +package io.javaoperatorsdk.operator.sample.multiplemanageddependentsametype; import java.util.Map; import java.util.concurrent.atomic.AtomicInteger; diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/multiplemanageddependent/MultipleManagedDependentResourceSpec.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/multiplemanageddependentsametype/MultipleManagedDependentResourceSpec.java similarity index 96% rename from operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/multiplemanageddependent/MultipleManagedDependentResourceSpec.java rename to operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/multiplemanageddependentsametype/MultipleManagedDependentResourceSpec.java index cc388b767f..cdd524e03e 100644 --- a/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/multiplemanageddependent/MultipleManagedDependentResourceSpec.java +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/multiplemanageddependentsametype/MultipleManagedDependentResourceSpec.java @@ -1,4 +1,4 @@ -package io.javaoperatorsdk.operator.sample.multiplemanageddependent; +package io.javaoperatorsdk.operator.sample.multiplemanageddependentsametype; public class MultipleManagedDependentResourceSpec { diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/multiplemanagedexternaldependenttype/AbstractExternalDependentResource.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/multiplemanagedexternaldependenttype/AbstractExternalDependentResource.java new file mode 100644 index 0000000000..2c27e7e14a --- /dev/null +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/multiplemanagedexternaldependenttype/AbstractExternalDependentResource.java @@ -0,0 +1,63 @@ +package io.javaoperatorsdk.operator.sample.multiplemanagedexternaldependenttype; + +import java.util.Map; +import java.util.Set; + +import io.javaoperatorsdk.operator.api.reconciler.Context; +import io.javaoperatorsdk.operator.api.reconciler.dependent.Deleter; +import io.javaoperatorsdk.operator.processing.dependent.Creator; +import io.javaoperatorsdk.operator.processing.dependent.Matcher; +import io.javaoperatorsdk.operator.processing.dependent.Updater; +import io.javaoperatorsdk.operator.processing.dependent.external.PollingDependentResource; +import io.javaoperatorsdk.operator.processing.event.ResourceID; +import io.javaoperatorsdk.operator.support.ExternalResource; +import io.javaoperatorsdk.operator.support.ExternalServiceMock; + +public abstract class AbstractExternalDependentResource extends + PollingDependentResource + implements Creator, + Updater, + Deleter { + + protected ExternalServiceMock externalServiceMock = ExternalServiceMock.getInstance(); + + public AbstractExternalDependentResource() { + super(ExternalResource.class, ExternalResource::getId); + } + + @Override + public Map> fetchResources() { + throw new IllegalStateException("Should not be called"); + } + + @Override + public ExternalResource create(ExternalResource desired, + MultipleManagedExternalDependentResourceCustomResource primary, + Context context) { + return externalServiceMock.create(desired); + } + + @Override + public ExternalResource update(ExternalResource actual, + ExternalResource desired, + MultipleManagedExternalDependentResourceCustomResource primary, + Context context) { + return externalServiceMock.update(desired); + } + + @Override + public Matcher.Result match(ExternalResource actualResource, + MultipleManagedExternalDependentResourceCustomResource primary, + Context context) { + var desired = desired(primary, context); + return Matcher.Result.computed(actualResource.equals(desired), desired); + } + + @Override + public void delete(MultipleManagedExternalDependentResourceCustomResource primary, + Context context) { + externalServiceMock.delete(ExternalResource.toExternalResourceId(primary)); + } + + +} diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/multiplemanagedexternaldependenttype/ExternalDependentResource1.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/multiplemanagedexternaldependenttype/ExternalDependentResource1.java new file mode 100644 index 0000000000..3764c1e94a --- /dev/null +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/multiplemanagedexternaldependenttype/ExternalDependentResource1.java @@ -0,0 +1,20 @@ +package io.javaoperatorsdk.operator.sample.multiplemanagedexternaldependenttype; + +import io.javaoperatorsdk.operator.api.reconciler.Context; +import io.javaoperatorsdk.operator.support.ExternalResource; + +public class ExternalDependentResource1 extends AbstractExternalDependentResource { + + public static final String SUFFIX = "-1"; + + public ExternalDependentResource1() { + setResourceDiscriminator(new ExternalResourceDiscriminator(SUFFIX)); + } + + @Override + protected ExternalResource desired(MultipleManagedExternalDependentResourceCustomResource primary, + Context context) { + return new ExternalResource(ExternalResource.toExternalResourceId(primary) + SUFFIX, + primary.getSpec().getValue()); + } +} diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/multiplemanagedexternaldependenttype/ExternalDependentResource2.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/multiplemanagedexternaldependenttype/ExternalDependentResource2.java new file mode 100644 index 0000000000..b8866a25f9 --- /dev/null +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/multiplemanagedexternaldependenttype/ExternalDependentResource2.java @@ -0,0 +1,20 @@ +package io.javaoperatorsdk.operator.sample.multiplemanagedexternaldependenttype; + +import io.javaoperatorsdk.operator.api.reconciler.Context; +import io.javaoperatorsdk.operator.support.ExternalResource; + +public class ExternalDependentResource2 extends AbstractExternalDependentResource { + + public static final String SUFFIX = "-2"; + + public ExternalDependentResource2() { + setResourceDiscriminator(new ExternalResourceDiscriminator(SUFFIX)); + } + + @Override + protected ExternalResource desired(MultipleManagedExternalDependentResourceCustomResource primary, + Context context) { + return new ExternalResource(ExternalResource.toExternalResourceId(primary) + SUFFIX, + primary.getSpec().getValue()); + } +} diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/multiplemanagedexternaldependenttype/ExternalResourceDiscriminator.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/multiplemanagedexternaldependenttype/ExternalResourceDiscriminator.java new file mode 100644 index 0000000000..ba265455de --- /dev/null +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/multiplemanagedexternaldependenttype/ExternalResourceDiscriminator.java @@ -0,0 +1,25 @@ +package io.javaoperatorsdk.operator.sample.multiplemanagedexternaldependenttype; + +import java.util.Optional; + +import io.javaoperatorsdk.operator.api.reconciler.Context; +import io.javaoperatorsdk.operator.api.reconciler.ResourceDiscriminator; +import io.javaoperatorsdk.operator.support.ExternalResource; + +public class ExternalResourceDiscriminator implements + ResourceDiscriminator { + + private String suffix; + + public ExternalResourceDiscriminator(String suffix) { + this.suffix = suffix; + } + + @Override + public Optional distinguish(Class resource, + MultipleManagedExternalDependentResourceCustomResource primary, + Context context) { + var resources = context.getSecondaryResources(ExternalResource.class); + return resources.stream().filter(r -> r.getId().endsWith(suffix)).findFirst(); + } +} diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/multiplemanagedexternaldependenttype/MultipleManagedExternalDependentResourceCustomResource.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/multiplemanagedexternaldependenttype/MultipleManagedExternalDependentResourceCustomResource.java new file mode 100644 index 0000000000..af14325128 --- /dev/null +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/multiplemanagedexternaldependenttype/MultipleManagedExternalDependentResourceCustomResource.java @@ -0,0 +1,17 @@ +package io.javaoperatorsdk.operator.sample.multiplemanagedexternaldependenttype; + +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; +import io.javaoperatorsdk.operator.sample.multiplemanageddependentsametype.MultipleManagedDependentResourceSpec; + +@Group("sample.javaoperatorsdk") +@Version("v1") +@ShortNames("mmd") +public class MultipleManagedExternalDependentResourceCustomResource + extends CustomResource + implements Namespaced { + +} diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/multiplemanagedexternaldependenttype/MultipleManagedExternalDependentResourceReconciler.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/multiplemanagedexternaldependenttype/MultipleManagedExternalDependentResourceReconciler.java new file mode 100644 index 0000000000..83bdf1dac2 --- /dev/null +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/multiplemanagedexternaldependenttype/MultipleManagedExternalDependentResourceReconciler.java @@ -0,0 +1,67 @@ +package io.javaoperatorsdk.operator.sample.multiplemanagedexternaldependenttype; + +import java.util.HashMap; +import java.util.HashSet; +import java.util.Map; +import java.util.Set; +import java.util.concurrent.atomic.AtomicInteger; + +import io.javaoperatorsdk.operator.api.reconciler.*; +import io.javaoperatorsdk.operator.api.reconciler.dependent.Dependent; +import io.javaoperatorsdk.operator.processing.event.ResourceID; +import io.javaoperatorsdk.operator.processing.event.source.EventSource; +import io.javaoperatorsdk.operator.processing.event.source.polling.PollingEventSource; +import io.javaoperatorsdk.operator.support.ExternalResource; +import io.javaoperatorsdk.operator.support.ExternalServiceMock; +import io.javaoperatorsdk.operator.support.TestExecutionInfoProvider; + +@ControllerConfiguration(dependents = { + @Dependent(type = ExternalDependentResource1.class, provideEventSource = false), + @Dependent(type = ExternalDependentResource2.class, provideEventSource = false) +}) +public class MultipleManagedExternalDependentResourceReconciler + implements Reconciler, + TestExecutionInfoProvider, + EventSourceInitializer { + + public static final String CONFIG_MAP_EVENT_SOURCE = "ConfigMapEventSource"; + protected ExternalServiceMock externalServiceMock = ExternalServiceMock.getInstance(); + + private final AtomicInteger numberOfExecutions = new AtomicInteger(0); + + + public MultipleManagedExternalDependentResourceReconciler() {} + + @Override + public UpdateControl reconcile( + MultipleManagedExternalDependentResourceCustomResource resource, + Context context) { + numberOfExecutions.getAndIncrement(); + + return UpdateControl.noUpdate(); + } + + + public int getNumberOfExecutions() { + return numberOfExecutions.get(); + } + + @Override + public Map prepareEventSources( + EventSourceContext context) { + + PollingEventSource pollingEventSource = + new PollingEventSource<>(() -> { + var lists = externalServiceMock.listResources(); + Map> res = new HashMap<>(); + lists.forEach(er -> { + var resourceId = er.toResourceID(); + res.computeIfAbsent(resourceId, rid -> new HashSet<>()); + res.get(resourceId).add(er); + }); + return res; + }, 1000L, ExternalResource.class, ExternalResource::getId); + + return Map.of(CONFIG_MAP_EVENT_SOURCE, pollingEventSource); + } +} diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/support/ExternalResource.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/support/ExternalResource.java new file mode 100644 index 0000000000..cb8d4b74e5 --- /dev/null +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/support/ExternalResource.java @@ -0,0 +1,58 @@ +package io.javaoperatorsdk.operator.support; + +import java.util.Objects; + +import io.fabric8.kubernetes.api.model.HasMetadata; +import io.javaoperatorsdk.operator.processing.event.ResourceID; + +public class ExternalResource { + + public static final String EXTERNAL_RESOURCE_NAME_DELIMITER = "#"; + + private String id; + private String data; + + public ExternalResource(String id, String data) { + this.id = id; + this.data = data; + } + + public String getId() { + return id; + } + + public String getData() { + return data; + } + + @Override + public boolean equals(Object o) { + if (this == o) + return true; + if (o == null || getClass() != o.getClass()) + return false; + ExternalResource that = (ExternalResource) o; + return Objects.equals(id, that.id) && Objects.equals(data, that.data); + } + + @Override + public int hashCode() { + return Objects.hash(id, data); + } + + public ResourceID toResourceID() { + var parts = getId().split(EXTERNAL_RESOURCE_NAME_DELIMITER); + return new ResourceID(parts[0], parts[1]); + } + + public static String toExternalResourceId(HasMetadata primary, int i) { + return primary.getMetadata().getName() + EXTERNAL_RESOURCE_NAME_DELIMITER + + primary.getMetadata().getNamespace() + + EXTERNAL_RESOURCE_NAME_DELIMITER + i; + } + + public static String toExternalResourceId(HasMetadata primary) { + return primary.getMetadata().getName() + EXTERNAL_RESOURCE_NAME_DELIMITER + + primary.getMetadata().getNamespace(); + } +} diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/bulkdependent/external/ExternalServiceMock.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/support/ExternalServiceMock.java similarity index 94% rename from operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/bulkdependent/external/ExternalServiceMock.java rename to operator-framework/src/test/java/io/javaoperatorsdk/operator/support/ExternalServiceMock.java index e73062ccf2..eea26637fc 100644 --- a/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/bulkdependent/external/ExternalServiceMock.java +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/support/ExternalServiceMock.java @@ -1,4 +1,4 @@ -package io.javaoperatorsdk.operator.sample.bulkdependent.external; +package io.javaoperatorsdk.operator.support; import java.util.ArrayList; import java.util.List; From 94dff834d9b987940ded509cc7be26d8853a1234 Mon Sep 17 00:00:00 2001 From: csviri Date: Tue, 27 Sep 2022 15:29:07 +0200 Subject: [PATCH 52/67] fix shortname --- .../operator/MultipleManagedExternalDependentSameTypeIT.java | 2 ++ .../MultipleManagedExternalDependentResourceCustomResource.java | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/MultipleManagedExternalDependentSameTypeIT.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/MultipleManagedExternalDependentSameTypeIT.java index 65fc5b404c..4e62797865 100644 --- a/operator-framework/src/test/java/io/javaoperatorsdk/operator/MultipleManagedExternalDependentSameTypeIT.java +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/MultipleManagedExternalDependentSameTypeIT.java @@ -33,6 +33,8 @@ void handlesExternalCrudOperations() { operator.create(testResource()); assertResourceCreatedWithData(); + + // todo check issues with update } private void assertResourceCreatedWithData() { diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/multiplemanagedexternaldependenttype/MultipleManagedExternalDependentResourceCustomResource.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/multiplemanagedexternaldependenttype/MultipleManagedExternalDependentResourceCustomResource.java index af14325128..c989a5c96c 100644 --- a/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/multiplemanagedexternaldependenttype/MultipleManagedExternalDependentResourceCustomResource.java +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/multiplemanagedexternaldependenttype/MultipleManagedExternalDependentResourceCustomResource.java @@ -9,7 +9,7 @@ @Group("sample.javaoperatorsdk") @Version("v1") -@ShortNames("mmd") +@ShortNames("mme") public class MultipleManagedExternalDependentResourceCustomResource extends CustomResource implements Namespaced { From 5f6356e59ee949c46442283ec1c227cf8e11307b Mon Sep 17 00:00:00 2001 From: csviri Date: Tue, 27 Sep 2022 17:20:18 +0200 Subject: [PATCH 53/67] IT --- ...pleManagedExternalDependentSameTypeIT.java | 19 ++++++++++++++++--- .../AbstractExternalDependentResource.java | 14 +++++++++++++- .../ExternalDependentResource1.java | 9 ++------- .../ExternalDependentResource2.java | 9 ++------- 4 files changed, 33 insertions(+), 18 deletions(-) diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/MultipleManagedExternalDependentSameTypeIT.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/MultipleManagedExternalDependentSameTypeIT.java index 4e62797865..ec2b7a394a 100644 --- a/operator-framework/src/test/java/io/javaoperatorsdk/operator/MultipleManagedExternalDependentSameTypeIT.java +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/MultipleManagedExternalDependentSameTypeIT.java @@ -31,16 +31,29 @@ class MultipleManagedExternalDependentSameTypeIT { @Test void handlesExternalCrudOperations() { operator.create(testResource()); + assertResourceCreatedWithData(DEFAULT_SPEC_VALUE); - assertResourceCreatedWithData(); + var updatedResource = testResource(); + updatedResource.getSpec().setValue(UPDATED_SPEC_VALUE); + operator.replace(updatedResource); + assertResourceCreatedWithData(UPDATED_SPEC_VALUE); - // todo check issues with update + operator.delete(testResource()); + assertExternalResourceDeleted(); } - private void assertResourceCreatedWithData() { + private void assertExternalResourceDeleted() { + await().untilAsserted(() -> { + var resources = externalServiceMock.listResources(); + assertThat(resources).hasSize(0); + }); + } + + private void assertResourceCreatedWithData(String expectedData) { await().untilAsserted(() -> { var resources = externalServiceMock.listResources(); assertThat(resources).hasSize(2); + assertThat(resources).allMatch(er -> er.getData().equals(expectedData)); }); } diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/multiplemanagedexternaldependenttype/AbstractExternalDependentResource.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/multiplemanagedexternaldependenttype/AbstractExternalDependentResource.java index 2c27e7e14a..f7d3c91dd3 100644 --- a/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/multiplemanagedexternaldependenttype/AbstractExternalDependentResource.java +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/multiplemanagedexternaldependenttype/AbstractExternalDependentResource.java @@ -56,8 +56,20 @@ public Matcher.Result match(ExternalResource actualResource, @Override public void delete(MultipleManagedExternalDependentResourceCustomResource primary, Context context) { - externalServiceMock.delete(ExternalResource.toExternalResourceId(primary)); + externalServiceMock.delete(toExternalResourceID(primary)); } + protected ExternalResource desired(MultipleManagedExternalDependentResourceCustomResource primary, + Context context) { + return new ExternalResource(toExternalResourceID(primary), + primary.getSpec().getValue()); + } + + protected String toExternalResourceID( + MultipleManagedExternalDependentResourceCustomResource primary) { + return ExternalResource.toExternalResourceId(primary) + resourceIDSuffix(); + } + + protected abstract String resourceIDSuffix(); } diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/multiplemanagedexternaldependenttype/ExternalDependentResource1.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/multiplemanagedexternaldependenttype/ExternalDependentResource1.java index 3764c1e94a..cfe67a3796 100644 --- a/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/multiplemanagedexternaldependenttype/ExternalDependentResource1.java +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/multiplemanagedexternaldependenttype/ExternalDependentResource1.java @@ -1,8 +1,5 @@ package io.javaoperatorsdk.operator.sample.multiplemanagedexternaldependenttype; -import io.javaoperatorsdk.operator.api.reconciler.Context; -import io.javaoperatorsdk.operator.support.ExternalResource; - public class ExternalDependentResource1 extends AbstractExternalDependentResource { public static final String SUFFIX = "-1"; @@ -12,9 +9,7 @@ public ExternalDependentResource1() { } @Override - protected ExternalResource desired(MultipleManagedExternalDependentResourceCustomResource primary, - Context context) { - return new ExternalResource(ExternalResource.toExternalResourceId(primary) + SUFFIX, - primary.getSpec().getValue()); + protected String resourceIDSuffix() { + return SUFFIX; } } diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/multiplemanagedexternaldependenttype/ExternalDependentResource2.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/multiplemanagedexternaldependenttype/ExternalDependentResource2.java index b8866a25f9..29bb237e1a 100644 --- a/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/multiplemanagedexternaldependenttype/ExternalDependentResource2.java +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/multiplemanagedexternaldependenttype/ExternalDependentResource2.java @@ -1,8 +1,5 @@ package io.javaoperatorsdk.operator.sample.multiplemanagedexternaldependenttype; -import io.javaoperatorsdk.operator.api.reconciler.Context; -import io.javaoperatorsdk.operator.support.ExternalResource; - public class ExternalDependentResource2 extends AbstractExternalDependentResource { public static final String SUFFIX = "-2"; @@ -12,9 +9,7 @@ public ExternalDependentResource2() { } @Override - protected ExternalResource desired(MultipleManagedExternalDependentResourceCustomResource primary, - Context context) { - return new ExternalResource(ExternalResource.toExternalResourceId(primary) + SUFFIX, - primary.getSpec().getValue()); + protected String resourceIDSuffix() { + return SUFFIX; } } From a4885726f9f1c87530791b6de063730c43f04bf8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Attila=20M=C3=A9sz=C3=A1ros?= Date: Wed, 28 Sep 2022 10:43:44 +0200 Subject: [PATCH 54/67] feat: decouple event source from cache + list discriminator (#1378) --- .../AnnotationControllerConfiguration.java | 29 +++-- .../operator/api/reconciler/Context.java | 15 ++- .../api/reconciler/DefaultContext.java | 12 ++ .../api/reconciler/ResourceDiscriminator.java | 11 ++ .../ResourceIDMatcherDiscriminator.java | 25 ++++ .../dependent/DependentResource.java | 16 ++- .../operator/processing/ResourceOwner.java | 26 ---- .../dependent/AbstractDependentResource.java | 21 ++- ...actEventSourceHolderDependentResource.java | 9 ++ .../AbstractCachingDependentResource.java | 29 ----- .../AbstractPollingDependentResource.java | 6 +- .../AbstractSimpleDependentResource.java | 2 +- .../kubernetes/KubernetesDependent.java | 4 + .../KubernetesDependentResource.java | 34 ++--- .../KubernetesDependentResourceConfig.java | 22 ++-- .../workflow/DependentResourceNode.java | 5 + .../workflow/WorkflowCleanupExecutor.java | 11 +- .../workflow/WorkflowReconcileExecutor.java | 12 +- .../processing/event/EventProcessor.java | 11 +- .../processing/event/EventSourceManager.java | 19 ++- .../event/EventSourceRetriever.java | 18 +++ .../processing/event/EventSources.java | 5 +- .../operator/processing/event/ResourceID.java | 6 + .../event/source/ResourceEventSource.java | 11 +- .../informer/ManagedInformerEventSource.java | 4 +- .../ControllerConfigurationOverriderTest.java | 5 - .../AbstractDependentResourceTest.java | 4 +- .../dependent/EmptyTestDependentResource.java | 7 - .../AbstractSimpleDependentResourceTest.java | 4 +- .../AbstractWorkflowExecutorTest.java | 11 -- .../workflow/WorkflowCleanupExecutorTest.java | 11 +- .../WorkflowReconcileExecutorTest.java | 46 ++++--- .../operator/IndexDiscriminatorIT.java | 77 +++++++++++ .../IndexDiscriminator.java | 41 ++++++ .../IndexDiscriminatorTestCustomResource.java | 16 +++ .../IndexDiscriminatorTestDRConfigMap.java | 38 ++++++ .../IndexDiscriminatorTestReconciler.java | 120 ++++++++++++++++++ .../IndexDiscriminatorTestSpec.java | 15 +++ .../IndexDiscriminatorTestStatus.java | 5 + .../MultipleDependentResourceConfigMap.java | 1 - .../MultipleDependentResourceReconciler.java | 44 ++++--- .../ConfigMapDependentResource1.java | 12 +- .../ConfigMapDependentResource2.java | 12 +- ...ourcePollingEventSourceTestReconciler.java | 3 +- .../StandaloneDependentTestReconciler.java | 2 +- .../WebPageDependentsWorkflowReconciler.java | 4 +- ...WebPageStandaloneDependentsReconciler.java | 2 +- 47 files changed, 622 insertions(+), 221 deletions(-) create mode 100644 operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/reconciler/ResourceDiscriminator.java create mode 100644 operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/reconciler/ResourceIDMatcherDiscriminator.java delete mode 100644 operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/ResourceOwner.java delete mode 100644 operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/external/AbstractCachingDependentResource.java create mode 100644 operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/EventSourceRetriever.java create mode 100644 operator-framework/src/test/java/io/javaoperatorsdk/operator/IndexDiscriminatorIT.java create mode 100644 operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/indexdiscriminator/IndexDiscriminator.java create mode 100644 operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/indexdiscriminator/IndexDiscriminatorTestCustomResource.java create mode 100644 operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/indexdiscriminator/IndexDiscriminatorTestDRConfigMap.java create mode 100644 operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/indexdiscriminator/IndexDiscriminatorTestReconciler.java create mode 100644 operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/indexdiscriminator/IndexDiscriminatorTestSpec.java create mode 100644 operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/indexdiscriminator/IndexDiscriminatorTestStatus.java diff --git a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/config/AnnotationControllerConfiguration.java b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/config/AnnotationControllerConfiguration.java index b5e3fffcf0..dbd09a32cc 100644 --- a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/config/AnnotationControllerConfiguration.java +++ b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/config/AnnotationControllerConfiguration.java @@ -17,9 +17,8 @@ import io.javaoperatorsdk.operator.OperatorException; import io.javaoperatorsdk.operator.ReconcilerUtils; import io.javaoperatorsdk.operator.api.config.dependent.DependentResourceSpec; -import io.javaoperatorsdk.operator.api.reconciler.Constants; +import io.javaoperatorsdk.operator.api.reconciler.*; import io.javaoperatorsdk.operator.api.reconciler.ControllerConfiguration; -import io.javaoperatorsdk.operator.api.reconciler.Reconciler; import io.javaoperatorsdk.operator.api.reconciler.dependent.Dependent; import io.javaoperatorsdk.operator.api.reconciler.dependent.DependentResource; import io.javaoperatorsdk.operator.processing.dependent.kubernetes.KubernetesDependent; @@ -54,9 +53,8 @@ public AnnotationControllerConfiguration(Reconciler

reconciler) { this.reconciler = reconciler; this.annotation = reconciler.getClass().getAnnotation(ControllerConfiguration.class); if (annotation == null) { - throw new OperatorException( - "Missing mandatory @" + ControllerConfiguration.class.getSimpleName() + - " annotation for reconciler: " + reconciler); + throw new OperatorException("Missing mandatory @" + CONTROLLER_CONFIG_ANNOTATION + + " annotation for reconciler: " + reconciler); } } @@ -247,9 +245,9 @@ public List getDependentResources() { final var context = "DependentResource of type '" + dependentType.getName() + "'"; spec = new DependentResourceSpec(dependentType, config, name, Set.of(dependent.dependsOn()), - instantiateConditionIfNotDefault(dependent.readyPostcondition(), context), - instantiateConditionIfNotDefault(dependent.reconcilePrecondition(), context), - instantiateConditionIfNotDefault(dependent.deletePostcondition(), context)); + instantiateIfNotDefault(dependent.readyPostcondition(), Condition.class, context), + instantiateIfNotDefault(dependent.reconcilePrecondition(), Condition.class, context), + instantiateIfNotDefault(dependent.deletePostcondition(), Condition.class, context)); specsMap.put(name, spec); } @@ -258,10 +256,10 @@ public List getDependentResources() { return specs; } - protected Condition instantiateConditionIfNotDefault(Class condition, + protected T instantiateIfNotDefault(Class toInstantiate, Class defaultClass, String context) { - if (condition != Condition.class) { - return instantiateAndConfigureIfNeeded(condition, Condition.class, context); + if (!defaultClass.equals(toInstantiate)) { + return instantiateAndConfigureIfNeeded(toInstantiate, defaultClass, context); } return null; } @@ -287,6 +285,7 @@ private Object createKubernetesResourceConfig(Class OnUpdateFilter onUpdateFilter = null; OnDeleteFilter onDeleteFilter = null; GenericFilter genericFilter = null; + ResourceDiscriminator resourceDiscriminator = null; if (kubeDependent != null) { if (!Arrays.equals(KubernetesDependent.DEFAULT_NAMESPACES, kubeDependent.namespaces())) { @@ -297,7 +296,6 @@ private Object createKubernetesResourceConfig(Class final var fromAnnotation = kubeDependent.labelSelector(); labelSelector = Constants.NO_VALUE_SET.equals(fromAnnotation) ? null : fromAnnotation; - final var context = KUBE_DEPENDENT_NAME + " annotation on " + dependentType.getName() + " DependentResource"; onAddFilter = createFilter(kubeDependent.onAddFilter(), OnAddFilter.class, context) @@ -311,10 +309,15 @@ private Object createKubernetesResourceConfig(Class genericFilter = createFilter(kubeDependent.genericFilter(), GenericFilter.class, context) .orElse(null); + + resourceDiscriminator = + instantiateIfNotDefault(kubeDependent.resourceDiscriminator(), + ResourceDiscriminator.class, context); } config = - new KubernetesDependentResourceConfig(namespaces, labelSelector, configuredNS, onAddFilter, + new KubernetesDependentResourceConfig(namespaces, labelSelector, configuredNS, + resourceDiscriminator, onAddFilter, onUpdateFilter, onDeleteFilter, genericFilter); return config; diff --git a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/reconciler/Context.java b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/reconciler/Context.java index 845810c8a1..2e4fb98e6f 100644 --- a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/reconciler/Context.java +++ b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/reconciler/Context.java @@ -6,20 +6,27 @@ import io.fabric8.kubernetes.api.model.HasMetadata; import io.javaoperatorsdk.operator.api.config.ControllerConfiguration; import io.javaoperatorsdk.operator.api.reconciler.dependent.managed.ManagedDependentResourceContext; +import io.javaoperatorsdk.operator.processing.event.EventSourceRetriever; public interface Context

{ Optional getRetryInfo(); - default Optional getSecondaryResource(Class expectedType) { - return getSecondaryResource(expectedType, null); + default Optional getSecondaryResource(Class expectedType) { + return getSecondaryResource(expectedType, (String) null); } - Set getSecondaryResources(Class expectedType); + Set getSecondaryResources(Class expectedType); - Optional getSecondaryResource(Class expectedType, String eventSourceName); + @Deprecated(forRemoval = true) + Optional getSecondaryResource(Class expectedType, String eventSourceName); + + Optional getSecondaryResource(Class expectedType, + ResourceDiscriminator discriminator); ControllerConfiguration

getControllerConfiguration(); ManagedDependentResourceContext managedDependentResourceContext(); + + EventSourceRetriever

eventSourceRetriever(); } diff --git a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/reconciler/DefaultContext.java b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/reconciler/DefaultContext.java index afb37a8c53..cb7f4ae63b 100644 --- a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/reconciler/DefaultContext.java +++ b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/reconciler/DefaultContext.java @@ -9,6 +9,7 @@ import io.javaoperatorsdk.operator.api.reconciler.dependent.managed.DefaultManagedDependentResourceContext; import io.javaoperatorsdk.operator.api.reconciler.dependent.managed.ManagedDependentResourceContext; import io.javaoperatorsdk.operator.processing.Controller; +import io.javaoperatorsdk.operator.processing.event.EventSourceRetriever; public class DefaultContext

implements Context

{ @@ -47,6 +48,12 @@ public Optional getSecondaryResource(Class expectedType, String eventS .getSecondaryResource(primaryResource); } + @Override + public Optional getSecondaryResource(Class expectedType, + ResourceDiscriminator discriminator) { + return discriminator.distinguish(expectedType, primaryResource, this); + } + @Override public ControllerConfiguration

getControllerConfiguration() { return controllerConfiguration; @@ -57,6 +64,11 @@ public ManagedDependentResourceContext managedDependentResourceContext() { return defaultManagedDependentResourceContext; } + @Override + public EventSourceRetriever

eventSourceRetriever() { + return controller.getEventSourceManager(); + } + public DefaultContext

setRetryInfo(RetryInfo retryInfo) { this.retryInfo = retryInfo; return this; diff --git a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/reconciler/ResourceDiscriminator.java b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/reconciler/ResourceDiscriminator.java new file mode 100644 index 0000000000..072e7d8078 --- /dev/null +++ b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/reconciler/ResourceDiscriminator.java @@ -0,0 +1,11 @@ +package io.javaoperatorsdk.operator.api.reconciler; + +import java.util.Optional; + +import io.fabric8.kubernetes.api.model.HasMetadata; + +public interface ResourceDiscriminator { + + Optional distinguish(Class resource, P primary, Context

context); + +} diff --git a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/reconciler/ResourceIDMatcherDiscriminator.java b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/reconciler/ResourceIDMatcherDiscriminator.java new file mode 100644 index 0000000000..f28633252a --- /dev/null +++ b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/reconciler/ResourceIDMatcherDiscriminator.java @@ -0,0 +1,25 @@ +package io.javaoperatorsdk.operator.api.reconciler; + +import java.util.Optional; +import java.util.function.Function; + +import io.fabric8.kubernetes.api.model.HasMetadata; +import io.javaoperatorsdk.operator.processing.event.ResourceID; + +public class ResourceIDMatcherDiscriminator + implements ResourceDiscriminator { + + private final Function mapper; + + public ResourceIDMatcherDiscriminator(Function mapper) { + this.mapper = mapper; + } + + @Override + public Optional distinguish(Class resource, P primary, Context

context) { + var resourceID = mapper.apply(primary); + return context.getSecondaryResources(resource).stream() + .filter(resourceID::isSameResource) + .findFirst(); + } +} 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 0923d19473..8d31778488 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 @@ -1,8 +1,9 @@ package io.javaoperatorsdk.operator.api.reconciler.dependent; +import java.util.Optional; + import io.fabric8.kubernetes.api.model.HasMetadata; import io.javaoperatorsdk.operator.api.reconciler.Context; -import io.javaoperatorsdk.operator.processing.ResourceOwner; /** * An interface to implement and provide dependent resource support. @@ -10,7 +11,7 @@ * @param the dependent resource type * @param

the associated primary resource type */ -public interface DependentResource extends ResourceOwner { +public interface DependentResource { /** * Reconciles the dependent resource given the desired primary state @@ -21,6 +22,17 @@ public interface DependentResource extends ResourceOwn */ ReconcileResult reconcile(P primary, Context

context); + /** + * Retrieves the resource type associated with this DependentResource + * + * @return the resource type associated with this DependentResource + */ + Class resourceType(); + + default Optional getSecondaryResource(P primary, Context

context) { + return Optional.empty(); + } + /** * Computes a default name for the specified DependentResource class * diff --git a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/ResourceOwner.java b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/ResourceOwner.java deleted file mode 100644 index f9c02a8a33..0000000000 --- a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/ResourceOwner.java +++ /dev/null @@ -1,26 +0,0 @@ -package io.javaoperatorsdk.operator.processing; - -import java.util.Optional; - -import io.fabric8.kubernetes.api.model.HasMetadata; - -public interface ResourceOwner { - - /** - * Retrieves the resource type associated with this ResourceOwner - * - * @return the resource type associated with this ResourceOwner - */ - Class resourceType(); - - /** - * Retrieves the resource associated with the specified primary one, returning the actual state of - * the resource. Typically, this state might come from a local cache, updated after - * reconciliation. - * - * @param primary the primary resource for which we want to retrieve the secondary resource - * @return an {@link Optional} containing the secondary resource or {@link Optional#empty()} if it - * doesn't exist - */ - Optional getSecondaryResource(P primary); -} 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 5dbdba9358..1abfb3df4b 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,11 +1,14 @@ package io.javaoperatorsdk.operator.processing.dependent; +import java.util.Optional; + import org.slf4j.Logger; import org.slf4j.LoggerFactory; import io.fabric8.kubernetes.api.model.HasMetadata; import io.javaoperatorsdk.operator.api.reconciler.Context; import io.javaoperatorsdk.operator.api.reconciler.Ignore; +import io.javaoperatorsdk.operator.api.reconciler.ResourceDiscriminator; import io.javaoperatorsdk.operator.api.reconciler.dependent.DependentResource; import io.javaoperatorsdk.operator.api.reconciler.dependent.ReconcileResult; import io.javaoperatorsdk.operator.processing.event.ResourceID; @@ -21,6 +24,8 @@ public abstract class AbstractDependentResource protected Creator creator; protected Updater updater; + private ResourceDiscriminator resourceDiscriminator; + @SuppressWarnings("unchecked") public AbstractDependentResource() { creator = creatable ? (Creator) this : null; @@ -29,7 +34,7 @@ public AbstractDependentResource() { @Override public ReconcileResult reconcile(P primary, Context

context) { - var maybeActual = getSecondaryResource(primary); + Optional maybeActual = getSecondaryResource(primary, context); if (creatable || updatable) { if (maybeActual.isEmpty()) { if (creatable) { @@ -62,6 +67,11 @@ public ReconcileResult reconcile(P primary, Context

context) { return ReconcileResult.noOperation(maybeActual.orElse(null)); } + public Optional getSecondaryResource(P primary, Context

context) { + return resourceDiscriminator == null ? context.getSecondaryResource(resourceType()) + : resourceDiscriminator.distinguish(resourceType(), primary, context); + } + private void throwIfNull(R desired, P primary, String descriptor) { if (desired == null) { throw new DependentResourceException( @@ -118,4 +128,13 @@ protected R desired(P primary, Context

context) { throw new IllegalStateException( "desired method must be implemented if this DependentResource can be created and/or updated"); } + + public void setResourceDiscriminator( + ResourceDiscriminator resourceDiscriminator) { + this.resourceDiscriminator = resourceDiscriminator; + } + + public ResourceDiscriminator getResourceDiscriminator() { + return resourceDiscriminator; + } } diff --git a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/AbstractEventSourceHolderDependentResource.java b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/AbstractEventSourceHolderDependentResource.java index 0ceba16826..be0db98393 100644 --- a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/AbstractEventSourceHolderDependentResource.java +++ b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/AbstractEventSourceHolderDependentResource.java @@ -19,12 +19,16 @@ public abstract class AbstractEventSourceHolderDependentResource { private T eventSource; + private final Class resourceType; private boolean isCacheFillerEventSource; protected OnAddFilter onAddFilter; protected OnUpdateFilter onUpdateFilter; protected OnDeleteFilter onDeleteFilter; protected GenericFilter genericFilter; + protected AbstractEventSourceHolderDependentResource(Class resourceType) { + this.resourceType = resourceType; + } public EventSource initEventSource(EventSourceContext

context) { // some sub-classes (e.g. KubernetesDependentResource) can have their event source created @@ -42,6 +46,11 @@ public EventSource initEventSource(EventSourceContext

context) { return eventSource; } + @Override + public Class resourceType() { + return resourceType; + } + protected abstract T createEventSource(EventSourceContext

context); protected void setEventSource(T eventSource) { diff --git a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/external/AbstractCachingDependentResource.java b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/external/AbstractCachingDependentResource.java deleted file mode 100644 index 242625bc5d..0000000000 --- a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/external/AbstractCachingDependentResource.java +++ /dev/null @@ -1,29 +0,0 @@ -package io.javaoperatorsdk.operator.processing.dependent.external; - -import java.util.Optional; - -import io.fabric8.kubernetes.api.model.HasMetadata; -import io.javaoperatorsdk.operator.api.reconciler.Ignore; -import io.javaoperatorsdk.operator.processing.dependent.AbstractEventSourceHolderDependentResource; -import io.javaoperatorsdk.operator.processing.event.source.ExternalResourceCachingEventSource; - -@Ignore -public abstract class AbstractCachingDependentResource - extends - AbstractEventSourceHolderDependentResource> { - private final Class resourceType; - - protected AbstractCachingDependentResource(Class resourceType) { - this.resourceType = resourceType; - } - - @Override - public Class resourceType() { - return resourceType; - } - - @Override - public Optional getSecondaryResource(P primaryResource) { - return eventSource().getSecondaryResource(primaryResource); - } -} diff --git a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/external/AbstractPollingDependentResource.java b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/external/AbstractPollingDependentResource.java index 6bab6f48cf..2ccba025a7 100644 --- a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/external/AbstractPollingDependentResource.java +++ b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/external/AbstractPollingDependentResource.java @@ -2,11 +2,15 @@ import io.fabric8.kubernetes.api.model.HasMetadata; import io.javaoperatorsdk.operator.api.reconciler.Ignore; +import io.javaoperatorsdk.operator.processing.dependent.AbstractEventSourceHolderDependentResource; import io.javaoperatorsdk.operator.processing.event.source.CacheKeyMapper; +import io.javaoperatorsdk.operator.processing.event.source.ExternalResourceCachingEventSource; @Ignore public abstract class AbstractPollingDependentResource - extends AbstractCachingDependentResource implements CacheKeyMapper { + extends + AbstractEventSourceHolderDependentResource> + implements CacheKeyMapper { public static final int DEFAULT_POLLING_PERIOD = 5000; private long pollingPeriod; diff --git a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/external/AbstractSimpleDependentResource.java b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/external/AbstractSimpleDependentResource.java index 0675db5f1a..748452c30c 100644 --- a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/external/AbstractSimpleDependentResource.java +++ b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/external/AbstractSimpleDependentResource.java @@ -33,7 +33,7 @@ public AbstractSimpleDependentResource(UpdatableCache cache) { } @Override - public Optional getSecondaryResource(HasMetadata primaryResource) { + public Optional getSecondaryResource(P primaryResource, Context

context) { return cache.get(ResourceID.fromResource(primaryResource)); } diff --git a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/kubernetes/KubernetesDependent.java b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/kubernetes/KubernetesDependent.java index f66ff95373..603f4ae62e 100644 --- a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/kubernetes/KubernetesDependent.java +++ b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/kubernetes/KubernetesDependent.java @@ -6,6 +6,8 @@ import java.lang.annotation.Target; import io.javaoperatorsdk.operator.api.reconciler.Constants; +import io.javaoperatorsdk.operator.api.reconciler.ResourceDiscriminator; +import io.javaoperatorsdk.operator.processing.event.source.filter.*; import io.javaoperatorsdk.operator.processing.event.source.filter.GenericFilter; import io.javaoperatorsdk.operator.processing.event.source.filter.OnAddFilter; import io.javaoperatorsdk.operator.processing.event.source.filter.OnDeleteFilter; @@ -68,4 +70,6 @@ * itself if no value is set */ Class genericFilter() default GenericFilter.class; + + Class resourceDiscriminator() default ResourceDiscriminator.class; } 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 930e5fd5b4..328a061e6b 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,16 +1,13 @@ package io.javaoperatorsdk.operator.processing.dependent.kubernetes; import java.util.HashMap; -import java.util.Optional; import java.util.Set; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import io.fabric8.kubernetes.api.model.HasMetadata; -import io.fabric8.kubernetes.api.model.KubernetesResourceList; import io.fabric8.kubernetes.client.KubernetesClient; -import io.fabric8.kubernetes.client.dsl.NonNamespaceOperation; import io.fabric8.kubernetes.client.dsl.Resource; import io.javaoperatorsdk.operator.OperatorException; import io.javaoperatorsdk.operator.api.config.informer.InformerConfiguration; @@ -41,13 +38,12 @@ public abstract class KubernetesDependentResource matcher; private final ResourceUpdatePreProcessor processor; - private final Class resourceType; private final boolean garbageCollected = this instanceof GarbageCollected; private KubernetesDependentResourceConfig kubernetesDependentResourceConfig; @SuppressWarnings("unchecked") public KubernetesDependentResource(Class resourceType) { - this.resourceType = resourceType; + super(resourceType); matcher = this instanceof Matcher ? (Matcher) this : GenericKubernetesResourceMatcher.matcherFor(resourceType, this); @@ -75,6 +71,7 @@ private void configureWith(String labelSelector, Set namespaces, .build(); configureWith(new InformerEventSource<>(ic, context)); + } @SuppressWarnings("unchecked") @@ -94,7 +91,7 @@ private SecondaryToPrimaryMapper getSecondaryToPrimaryMapper() { /** * Use to share informers between event more resources. * - * @param informerEventSource informer to use* + * @param informerEventSource informer to use */ public void configureWith(InformerEventSource informerEventSource) { setEventSource(informerEventSource); @@ -125,12 +122,12 @@ protected R handleUpdate(R actual, R desired, P primary, Context

context) { @SuppressWarnings("unused") public R create(R target, P primary, Context

context) { - return prepare(target, primary, "Creating").create(target); + return prepare(target, primary, "Creating").create(); } public R update(R actual, R target, P primary, Context

context) { var updatedActual = processor.replaceSpecOnActual(actual, target, context); - return prepare(target, primary, "Updating").replace(updatedActual); + return prepare(updatedActual, primary, "Updating").replace(); } public Result match(R actualResource, P primary, Context

context) { @@ -138,13 +135,11 @@ public Result match(R actualResource, P primary, Context

context) { } public void delete(P primary, Context

context) { - var resource = getSecondaryResource(primary); - resource.ifPresent(r -> client.resource(r).delete()); + getSecondaryResource(primary, context).ifPresent(r -> client.resource(r).delete()); } @SuppressWarnings("unchecked") - protected NonNamespaceOperation, Resource> prepare(R desired, - P primary, String actionName) { + protected Resource prepare(R desired, P primary, String actionName) { log.debug("{} target resource with type: {}, with id: {}", actionName, desired.getClass(), @@ -155,7 +150,8 @@ protected NonNamespaceOperation, Resource> prepa addDefaultSecondaryToPrimaryMapperAnnotations(desired, primary); } Class targetClass = (Class) desired.getClass(); - return client.resources(targetClass).inNamespace(desired.getMetadata().getNamespace()); + return client.resources(targetClass).inNamespace(desired.getMetadata().getNamespace()) + .resource(desired); } @Override @@ -167,6 +163,7 @@ protected InformerEventSource createEventSource(EventSourceContext

cont onUpdateFilter = kubernetesDependentResourceConfig.onUpdateFilter(); onDeleteFilter = kubernetesDependentResourceConfig.onDeleteFilter(); genericFilter = kubernetesDependentResourceConfig.genericFilter(); + setResourceDiscriminator(kubernetesDependentResourceConfig.getResourceDiscriminator()); configureWith(kubernetesDependentResourceConfig.labelSelector(), kubernetesDependentResourceConfig.namespaces(), @@ -203,16 +200,6 @@ protected boolean addOwnerReference() { return garbageCollected; } - @Override - public Class resourceType() { - return resourceType; - } - - @Override - public Optional getSecondaryResource(P primaryResource) { - return eventSource().getSecondaryResource(primaryResource); - } - @Override public void setKubernetesClient(KubernetesClient kubernetesClient) { this.client = kubernetesClient; @@ -235,5 +222,4 @@ private void prepareEventFiltering(R desired, ResourceID resourceID) { private void cleanupAfterEventFiltering(ResourceID resourceID) { eventSource().cleanupOnCreateOrUpdateEventFiltering(resourceID); } - } diff --git a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/kubernetes/KubernetesDependentResourceConfig.java b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/kubernetes/KubernetesDependentResourceConfig.java index e4a75b165a..e2a2c0f684 100644 --- a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/kubernetes/KubernetesDependentResourceConfig.java +++ b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/kubernetes/KubernetesDependentResourceConfig.java @@ -3,6 +3,7 @@ import java.util.Set; import io.javaoperatorsdk.operator.api.reconciler.Constants; +import io.javaoperatorsdk.operator.api.reconciler.ResourceDiscriminator; import io.javaoperatorsdk.operator.processing.event.source.filter.GenericFilter; import io.javaoperatorsdk.operator.processing.event.source.filter.OnAddFilter; import io.javaoperatorsdk.operator.processing.event.source.filter.OnDeleteFilter; @@ -15,7 +16,7 @@ public class KubernetesDependentResourceConfig { private Set namespaces = Constants.SAME_AS_CONTROLLER_NAMESPACES_SET; private String labelSelector = NO_VALUE_SET; private boolean namespacesWereConfigured = false; - + private ResourceDiscriminator resourceDiscriminator; private OnAddFilter onAddFilter; @@ -27,9 +28,9 @@ public class KubernetesDependentResourceConfig { public KubernetesDependentResourceConfig() {} - @SuppressWarnings("rawtypes") public KubernetesDependentResourceConfig(Set namespaces, String labelSelector, - boolean configuredNS, OnAddFilter onAddFilter, + boolean configuredNS, ResourceDiscriminator resourceDiscriminator, + OnAddFilter onAddFilter, OnUpdateFilter onUpdateFilter, OnDeleteFilter onDeleteFilter, GenericFilter genericFilter) { this.namespaces = namespaces; @@ -39,10 +40,11 @@ public KubernetesDependentResourceConfig(Set namespaces, String labelSel this.onUpdateFilter = onUpdateFilter; this.onDeleteFilter = onDeleteFilter; this.genericFilter = genericFilter; + this.resourceDiscriminator = resourceDiscriminator; } public KubernetesDependentResourceConfig(Set namespaces, String labelSelector) { - this(namespaces, labelSelector, true, null, null, null, null); + this(namespaces, labelSelector, true, null, null, null, null, null); } public KubernetesDependentResourceConfig setNamespaces(Set namespaces) { @@ -73,17 +75,21 @@ public OnAddFilter onAddFilter() { return onAddFilter; } - @SuppressWarnings("rawtypes") - public OnUpdateFilter onUpdateFilter() { + + public OnUpdateFilter onUpdateFilter() { return onUpdateFilter; } - @SuppressWarnings("rawtypes") - public OnDeleteFilter onDeleteFilter() { + public OnDeleteFilter onDeleteFilter() { return onDeleteFilter; } public GenericFilter genericFilter() { return genericFilter; } + + @SuppressWarnings("rawtypes") + public ResourceDiscriminator getResourceDiscriminator() { + return resourceDiscriminator; + } } diff --git a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/workflow/DependentResourceNode.java b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/workflow/DependentResourceNode.java index e317145916..32cef6c68e 100644 --- a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/workflow/DependentResourceNode.java +++ b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/workflow/DependentResourceNode.java @@ -5,6 +5,7 @@ import java.util.Optional; import io.fabric8.kubernetes.api.model.HasMetadata; +import io.javaoperatorsdk.operator.api.reconciler.Context; import io.javaoperatorsdk.operator.api.reconciler.dependent.DependentResource; @SuppressWarnings("rawtypes") @@ -85,4 +86,8 @@ public DependentResourceNode setReadyPostcondition(Condition readyPo public List getParents() { return parents; } + + protected R getSecondaryResource(P primary, Context

context) { + return getDependentResource().getSecondaryResource(primary, context).orElse(null); + } } diff --git a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/workflow/WorkflowCleanupExecutor.java b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/workflow/WorkflowCleanupExecutor.java index 98c6789869..45541b91d6 100644 --- a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/workflow/WorkflowCleanupExecutor.java +++ b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/workflow/WorkflowCleanupExecutor.java @@ -104,12 +104,10 @@ public void run() { ((Deleter

) dependentResourceNode.getDependentResource()).delete(primary, context); deleteCalled.add(dependentResourceNode); } - boolean deletePostConditionMet = - deletePostCondition.map(c -> c.isMet(primary, - dependentResourceNode.getDependentResource().getSecondaryResource(primary) - .orElse(null), - context)).orElse(true); - + boolean deletePostConditionMet = deletePostCondition + .map(c -> c.isMet(primary, dependentResourceNode.getSecondaryResource(primary, context), + context)) + .orElse(true); if (deletePostConditionMet) { alreadyVisited.add(dependentResourceNode); handleDependentCleaned(dependentResourceNode); @@ -127,6 +125,7 @@ public void run() { } } + private synchronized void handleDependentCleaned( DependentResourceNode dependentResourceNode) { var dependOns = dependentResourceNode.getDependsOn(); diff --git a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/workflow/WorkflowReconcileExecutor.java b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/workflow/WorkflowReconcileExecutor.java index 14028cb980..33ecd12f77 100644 --- a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/workflow/WorkflowReconcileExecutor.java +++ b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/workflow/WorkflowReconcileExecutor.java @@ -85,8 +85,7 @@ private synchronized void handleReconcile(DependentResourceNode depend } boolean reconcileConditionMet = dependentResourceNode.getReconcilePrecondition() - .map(rc -> rc.isMet(primary, - dependentResourceNode.getDependentResource().getSecondaryResource(primary).orElse(null), + .map(rc -> rc.isMet(primary, dependentResourceNode.getSecondaryResource(primary, context), context)) .orElse(true); @@ -167,9 +166,12 @@ public void run() { ReconcileResult reconcileResult = dependentResource.reconcile(primary, context); reconcileResults.put(dependentResource, reconcileResult); reconciled.add(dependentResourceNode); + boolean ready = dependentResourceNode.getReadyPostcondition() .map(rc -> rc.isMet(primary, - dependentResourceNode.getDependentResource().getSecondaryResource(primary) + context + .getSecondaryResource( + dependentResourceNode.getDependentResource().resourceType()) .orElse(null), context)) .orElse(true); @@ -210,8 +212,8 @@ public void run() { } boolean deletePostConditionMet = deletePostCondition.map(c -> c.isMet(primary, - dependentResourceNode.getDependentResource().getSecondaryResource(primary) - .orElse(null), + context.getSecondaryResource( + dependentResourceNode.getDependentResource().resourceType()).orElse(null), context)).orElse(true); if (deletePostConditionMet) { alreadyVisited.add(dependentResourceNode); diff --git a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/EventProcessor.java b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/EventProcessor.java index 58bf5ee00a..e0280796b2 100644 --- a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/EventProcessor.java +++ b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/EventProcessor.java @@ -135,7 +135,8 @@ private void handleMarkedEventForResource(ResourceState state) { private void submitReconciliationExecution(ResourceState state) { try { boolean controllerUnderExecution = isControllerUnderExecution(state); - Optional maybeLatest = cache.get(state.getId()); + final var resourceID = state.getId(); + Optional maybeLatest = cache.get(resourceID); maybeLatest.ifPresent(MDCUtils::addResourceInfo); if (!controllerUnderExecution && maybeLatest.isPresent()) { var rateLimit = state.getRateLimit(); @@ -145,24 +146,24 @@ private void submitReconciliationExecution(ResourceState state) { } var rateLimiterPermission = rateLimiter.isLimited(rateLimit); if (rateLimiterPermission.isPresent()) { - handleRateLimitedSubmission(state.getId(), rateLimiterPermission.get()); + handleRateLimitedSubmission(resourceID, rateLimiterPermission.get()); return; } state.setUnderProcessing(true); final var latest = maybeLatest.get(); ExecutionScope executionScope = new ExecutionScope<>(latest, state.getRetry()); state.unMarkEventReceived(); - metrics.reconcileCustomResource(state.getId(), state.getRetry(), metricsMetadata); + metrics.reconcileCustomResource(resourceID, state.getRetry(), metricsMetadata); log.debug("Executing events for custom resource. Scope: {}", executionScope); executor.execute(new ReconcilerExecutor(executionScope)); } else { log.debug( "Skipping executing controller for resource id: {}. Controller in execution: {}. Latest Resource present: {}", - state, + resourceID, controllerUnderExecution, maybeLatest.isPresent()); if (maybeLatest.isEmpty()) { - log.debug("no custom resource found in cache for ResourceID: {}", state); + log.debug("no custom resource found in cache for resource id: {}", resourceID); } } } finally { diff --git a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/EventSourceManager.java b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/EventSourceManager.java index 3f1e5e848d..fc68e6e413 100644 --- a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/EventSourceManager.java +++ b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/EventSourceManager.java @@ -23,7 +23,8 @@ import io.javaoperatorsdk.operator.processing.event.source.controller.ResourceAction; import io.javaoperatorsdk.operator.processing.event.source.timer.TimerEventSource; -public class EventSourceManager

implements LifecycleAware { +public class EventSourceManager

+ implements LifecycleAware, EventSourceRetriever

{ private static final Logger log = LoggerFactory.getLogger(EventSourceManager.class); @@ -171,17 +172,23 @@ public ControllerResourceEventSource

getControllerResourceEventSource() { return eventSources.controllerResourceEventSource(); } - ResourceEventSource getResourceEventSourceFor( - Class dependentType) { + @Override + public ResourceEventSource getResourceEventSourceFor(Class dependentType) { return getResourceEventSourceFor(dependentType, null); } - public List> getEventSourcesFor(Class dependentType) { + public List> getResourceEventSourcesFor(Class dependentType) { return eventSources.getEventSources(dependentType); } - public ResourceEventSource getResourceEventSourceFor( - Class dependentType, String qualifier) { + @Deprecated + public List> getEventSourcesFor(Class dependentType) { + return eventSources.getEventSources(dependentType); + } + + @Override + public ResourceEventSource getResourceEventSourceFor( + Class dependentType, String qualifier) { Objects.requireNonNull(dependentType, "dependentType is Mandatory"); return eventSources.get(dependentType, qualifier); } diff --git a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/EventSourceRetriever.java b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/EventSourceRetriever.java new file mode 100644 index 0000000000..a31d8902b0 --- /dev/null +++ b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/EventSourceRetriever.java @@ -0,0 +1,18 @@ +package io.javaoperatorsdk.operator.processing.event; + +import java.util.List; + +import io.fabric8.kubernetes.api.model.HasMetadata; +import io.javaoperatorsdk.operator.processing.event.source.ResourceEventSource; + +public interface EventSourceRetriever

{ + + ResourceEventSource getResourceEventSourceFor( + Class dependentType); + + ResourceEventSource getResourceEventSourceFor( + Class dependentType, String qualifier); + + List> getResourceEventSourcesFor(Class dependentType); + +} diff --git a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/EventSources.java b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/EventSources.java index 17de7ff947..e4fabe7ff8 100644 --- a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/EventSources.java +++ b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/EventSources.java @@ -8,7 +8,6 @@ import io.fabric8.kubernetes.api.model.HasMetadata; import io.javaoperatorsdk.operator.processing.Controller; -import io.javaoperatorsdk.operator.processing.ResourceOwner; import io.javaoperatorsdk.operator.processing.event.source.EventSource; import io.javaoperatorsdk.operator.processing.event.source.ResourceEventSource; import io.javaoperatorsdk.operator.processing.event.source.controller.ControllerResourceEventSource; @@ -86,8 +85,8 @@ public void add(String name, EventSource eventSource) { @SuppressWarnings("rawtypes") private Class getResourceType(EventSource source) { - return source instanceof ResourceOwner - ? ((ResourceOwner) source).resourceType() + return source instanceof ResourceEventSource + ? ((ResourceEventSource) source).resourceType() : source.getClass(); } diff --git a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/ResourceID.java b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/ResourceID.java index 7baeab6a4b..b6c2d976e3 100644 --- a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/ResourceID.java +++ b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/ResourceID.java @@ -54,6 +54,12 @@ public boolean equals(Object o) { that.namespace); } + public boolean isSameResource(HasMetadata hasMetadata) { + final var metadata = hasMetadata.getMetadata(); + return getName().equals(metadata.getName()) && + getNamespace().map(ns -> ns.equals(metadata.getNamespace())).orElse(true); + } + @Override public int hashCode() { return Objects.hash(name, namespace); diff --git a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/source/ResourceEventSource.java b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/source/ResourceEventSource.java index bf6b9c0fd3..38b5d7007f 100644 --- a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/source/ResourceEventSource.java +++ b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/source/ResourceEventSource.java @@ -4,14 +4,19 @@ import java.util.Set; import io.fabric8.kubernetes.api.model.HasMetadata; -import io.javaoperatorsdk.operator.processing.ResourceOwner; import io.javaoperatorsdk.operator.processing.event.source.filter.GenericFilter; import io.javaoperatorsdk.operator.processing.event.source.filter.OnAddFilter; import io.javaoperatorsdk.operator.processing.event.source.filter.OnDeleteFilter; import io.javaoperatorsdk.operator.processing.event.source.filter.OnUpdateFilter; -public interface ResourceEventSource extends EventSource, - ResourceOwner { +public interface ResourceEventSource extends EventSource { + + /** + * Retrieves the resource type associated with this ResourceEventSource + * + * @return the resource type associated with this ResourceEventSource + */ + Class resourceType(); default Optional getSecondaryResource(P primary) { var resources = getSecondaryResources(primary); diff --git a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/source/informer/ManagedInformerEventSource.java b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/source/informer/ManagedInformerEventSource.java index 6ebd63a7eb..cc9af59094 100644 --- a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/source/informer/ManagedInformerEventSource.java +++ b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/source/informer/ManagedInformerEventSource.java @@ -93,10 +93,10 @@ public void handleRecentResourceCreate(ResourceID resourceID, R resource) { public Optional get(ResourceID resourceID) { Optional resource = temporaryResourceCache.getResourceFromCache(resourceID); if (resource.isPresent()) { - log.debug("Resource found in temporal cache for Resource ID: {}", resourceID); + log.debug("Resource found in temporary cache for Resource ID: {}", resourceID); return resource; } else { - log.debug("Resource not found in temporal cache reading it from informer cache," + + log.debug("Resource not found in temporary cache reading it from informer cache," + " for Resource ID: {}", resourceID); return cache.get(resourceID); } diff --git a/operator-framework-core/src/test/java/io/javaoperatorsdk/operator/api/config/ControllerConfigurationOverriderTest.java b/operator-framework-core/src/test/java/io/javaoperatorsdk/operator/api/config/ControllerConfigurationOverriderTest.java index 127cd535b1..6e486b5347 100644 --- a/operator-framework-core/src/test/java/io/javaoperatorsdk/operator/api/config/ControllerConfigurationOverriderTest.java +++ b/operator-framework-core/src/test/java/io/javaoperatorsdk/operator/api/config/ControllerConfigurationOverriderTest.java @@ -1,6 +1,5 @@ package io.javaoperatorsdk.operator.api.config; -import java.util.Optional; import java.util.Set; import org.junit.jupiter.api.Test; @@ -85,10 +84,6 @@ public Class resourceType() { return Object.class; } - @Override - public Optional getSecondaryResource(ConfigMap primary) { - return Optional.empty(); - } } } diff --git a/operator-framework-core/src/test/java/io/javaoperatorsdk/operator/processing/dependent/AbstractDependentResourceTest.java b/operator-framework-core/src/test/java/io/javaoperatorsdk/operator/processing/dependent/AbstractDependentResourceTest.java index 4edd3dfb4c..b93abd45c0 100644 --- a/operator-framework-core/src/test/java/io/javaoperatorsdk/operator/processing/dependent/AbstractDependentResourceTest.java +++ b/operator-framework-core/src/test/java/io/javaoperatorsdk/operator/processing/dependent/AbstractDependentResourceTest.java @@ -16,7 +16,6 @@ class AbstractDependentResourceTest { - @Test void throwsExceptionIfDesiredIsNullOnCreate() { TestDependentResource testDependentResource = new TestDependentResource(); @@ -80,7 +79,8 @@ public Class resourceType() { } @Override - public Optional getSecondaryResource(TestCustomResource primary) { + public Optional getSecondaryResource(TestCustomResource primary, + Context context) { return Optional.ofNullable(secondary); } diff --git a/operator-framework-core/src/test/java/io/javaoperatorsdk/operator/processing/dependent/EmptyTestDependentResource.java b/operator-framework-core/src/test/java/io/javaoperatorsdk/operator/processing/dependent/EmptyTestDependentResource.java index aa75849051..dab1bc0132 100644 --- a/operator-framework-core/src/test/java/io/javaoperatorsdk/operator/processing/dependent/EmptyTestDependentResource.java +++ b/operator-framework-core/src/test/java/io/javaoperatorsdk/operator/processing/dependent/EmptyTestDependentResource.java @@ -1,7 +1,5 @@ package io.javaoperatorsdk.operator.processing.dependent; -import java.util.Optional; - import io.fabric8.kubernetes.api.model.apps.Deployment; import io.javaoperatorsdk.operator.api.reconciler.Context; import io.javaoperatorsdk.operator.api.reconciler.dependent.DependentResource; @@ -17,11 +15,6 @@ public ReconcileResult reconcile(TestCustomResource primary, return null; } - @Override - public Optional getSecondaryResource(TestCustomResource primaryResource) { - return Optional.empty(); - } - @Override public Class resourceType() { return Deployment.class; diff --git a/operator-framework-core/src/test/java/io/javaoperatorsdk/operator/processing/dependent/external/AbstractSimpleDependentResourceTest.java b/operator-framework-core/src/test/java/io/javaoperatorsdk/operator/processing/dependent/external/AbstractSimpleDependentResourceTest.java index e020fd27a3..520f44365f 100644 --- a/operator-framework-core/src/test/java/io/javaoperatorsdk/operator/processing/dependent/external/AbstractSimpleDependentResourceTest.java +++ b/operator-framework-core/src/test/java/io/javaoperatorsdk/operator/processing/dependent/external/AbstractSimpleDependentResourceTest.java @@ -45,14 +45,14 @@ void getsTheResourceFromSupplyIfReconciling() { simpleDependentResource.reconcile(TestUtils.testCustomResource1(), null); verify(supplierMock, times(1)).get(); - assertThat(simpleDependentResource.getSecondaryResource(TestUtils.testCustomResource1())) + assertThat(simpleDependentResource.getSecondaryResource(TestUtils.testCustomResource1(), null)) .isPresent() .isEqualTo(Optional.of(SampleExternalResource.testResource1())); } @Test void getResourceReadsTheResourceFromCache() { - simpleDependentResource.getSecondaryResource(TestUtils.testCustomResource1()); + simpleDependentResource.getSecondaryResource(TestUtils.testCustomResource1(), null); verify(supplierMock, times(0)).get(); verify(updatableCacheMock, times(1)).get(any()); diff --git a/operator-framework-core/src/test/java/io/javaoperatorsdk/operator/processing/dependent/workflow/AbstractWorkflowExecutorTest.java b/operator-framework-core/src/test/java/io/javaoperatorsdk/operator/processing/dependent/workflow/AbstractWorkflowExecutorTest.java index 8dfab3fa20..9c10c06cc0 100644 --- a/operator-framework-core/src/test/java/io/javaoperatorsdk/operator/processing/dependent/workflow/AbstractWorkflowExecutorTest.java +++ b/operator-framework-core/src/test/java/io/javaoperatorsdk/operator/processing/dependent/workflow/AbstractWorkflowExecutorTest.java @@ -3,7 +3,6 @@ import java.util.ArrayList; import java.util.Collections; import java.util.List; -import java.util.Optional; import io.javaoperatorsdk.operator.api.reconciler.Context; import io.javaoperatorsdk.operator.api.reconciler.dependent.Deleter; @@ -49,11 +48,6 @@ public Class resourceType() { return String.class; } - @Override - public Optional getSecondaryResource(TestCustomResource primary) { - return Optional.of(VALUE); - } - @Override public String toString() { return name; @@ -113,11 +107,6 @@ public Class resourceType() { return String.class; } - @Override - public Optional getSecondaryResource(TestCustomResource primary) { - return Optional.of(VALUE); - } - @Override public String toString() { return name; diff --git a/operator-framework-core/src/test/java/io/javaoperatorsdk/operator/processing/dependent/workflow/WorkflowCleanupExecutorTest.java b/operator-framework-core/src/test/java/io/javaoperatorsdk/operator/processing/dependent/workflow/WorkflowCleanupExecutorTest.java index 7c1c5d6ff6..56bd876687 100644 --- a/operator-framework-core/src/test/java/io/javaoperatorsdk/operator/processing/dependent/workflow/WorkflowCleanupExecutorTest.java +++ b/operator-framework-core/src/test/java/io/javaoperatorsdk/operator/processing/dependent/workflow/WorkflowCleanupExecutorTest.java @@ -4,17 +4,20 @@ import org.junit.jupiter.api.Test; import io.javaoperatorsdk.operator.AggregatedOperatorException; +import io.javaoperatorsdk.operator.api.reconciler.Context; import io.javaoperatorsdk.operator.processing.dependent.workflow.builder.WorkflowBuilder; import io.javaoperatorsdk.operator.sample.simple.TestCustomResource; import static io.javaoperatorsdk.operator.processing.dependent.workflow.ExecutionAssert.assertThat; import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.mockito.Mockito.mock; class WorkflowCleanupExecutorTest extends AbstractWorkflowExecutorTest { protected TestDeleterDependent dd1 = new TestDeleterDependent("DR_DELETER_1"); protected TestDeleterDependent dd2 = new TestDeleterDependent("DR_DELETER_2"); protected TestDeleterDependent dd3 = new TestDeleterDependent("DR_DELETER_3"); + Context mockContext = mock(Context.class); @Test void cleanUpDiamondWorkflow() { @@ -45,7 +48,7 @@ void dontDeleteIfDependentErrored() { .withThrowExceptionFurther(false) .build(); - var res = workflow.cleanup(new TestCustomResource(), null); + var res = workflow.cleanup(new TestCustomResource(), mockContext); assertThrows(AggregatedOperatorException.class, res::throwAggregateExceptionIfErrorsPresent); @@ -64,7 +67,7 @@ void cleanupConditionTrivialCase() { .addDependentResource(dd2).dependsOn(dd1).withDeletePostcondition(noMetDeletePostCondition) .build(); - var res = workflow.cleanup(new TestCustomResource(), null); + var res = workflow.cleanup(new TestCustomResource(), mockContext); assertThat(executionHistory).deleted(dd2).notReconciled(dd1); Assertions.assertThat(res.getDeleteCalledOnDependents()).containsExactlyInAnyOrder(dd2); @@ -79,7 +82,7 @@ void cleanupConditionMet() { .addDependentResource(dd2).dependsOn(dd1).withDeletePostcondition(metDeletePostCondition) .build(); - var res = workflow.cleanup(new TestCustomResource(), null); + var res = workflow.cleanup(new TestCustomResource(), mockContext); assertThat(executionHistory).deleted(dd2, dd1); @@ -99,7 +102,7 @@ void cleanupConditionDiamondWorkflow() { .addDependentResource(dd4).dependsOn(dd2, dd3) .build(); - var res = workflow.cleanup(new TestCustomResource(), null); + var res = workflow.cleanup(new TestCustomResource(), mockContext); assertThat(executionHistory) .reconciledInOrder(dd4, dd2) diff --git a/operator-framework-core/src/test/java/io/javaoperatorsdk/operator/processing/dependent/workflow/WorkflowReconcileExecutorTest.java b/operator-framework-core/src/test/java/io/javaoperatorsdk/operator/processing/dependent/workflow/WorkflowReconcileExecutorTest.java index fa9d757b67..873cf66cb6 100644 --- a/operator-framework-core/src/test/java/io/javaoperatorsdk/operator/processing/dependent/workflow/WorkflowReconcileExecutorTest.java +++ b/operator-framework-core/src/test/java/io/javaoperatorsdk/operator/processing/dependent/workflow/WorkflowReconcileExecutorTest.java @@ -4,11 +4,13 @@ import org.junit.jupiter.api.Test; import io.javaoperatorsdk.operator.AggregatedOperatorException; +import io.javaoperatorsdk.operator.api.reconciler.Context; import io.javaoperatorsdk.operator.processing.dependent.workflow.builder.WorkflowBuilder; import io.javaoperatorsdk.operator.sample.simple.TestCustomResource; import static io.javaoperatorsdk.operator.processing.dependent.workflow.ExecutionAssert.assertThat; import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.mockito.Mockito.mock; @SuppressWarnings("rawtypes") class WorkflowReconcileExecutorTest extends AbstractWorkflowExecutorTest { @@ -21,6 +23,8 @@ class WorkflowReconcileExecutorTest extends AbstractWorkflowExecutorTest { private final Condition notMetReadyCondition = (primary, secondary, context) -> false; + Context mockContext = mock(Context.class); + @Test void reconcileTopLevelResources() { var workflow = new WorkflowBuilder() @@ -28,7 +32,7 @@ void reconcileTopLevelResources() { .addDependentResource(dr2) .build(); - var res = workflow.reconcile(new TestCustomResource(), null); + var res = workflow.reconcile(new TestCustomResource(), mockContext); assertThat(executionHistory).reconciled(dr1, dr2); Assertions.assertThat(res.getErroredDependents()).isEmpty(); @@ -42,7 +46,7 @@ void reconciliationWithSimpleDependsOn() { .addDependentResource(dr2).dependsOn(dr1) .build(); - var res = workflow.reconcile(new TestCustomResource(), null); + var res = workflow.reconcile(new TestCustomResource(), mockContext); Assertions.assertThat(res.getErroredDependents()).isEmpty(); assertThat(executionHistory).reconciledInOrder(dr1, dr2); @@ -61,7 +65,7 @@ void reconciliationWithTwoTheDependsOns() { .addDependentResource(dr3).dependsOn(dr1) .build(); - var res = workflow.reconcile(new TestCustomResource(), null); + var res = workflow.reconcile(new TestCustomResource(), mockContext); Assertions.assertThat(res.getErroredDependents()).isEmpty(); assertThat(executionHistory) @@ -83,7 +87,7 @@ void diamondShareWorkflowReconcile() { .addDependentResource(dr4).dependsOn(dr3).dependsOn(dr2) .build(); - var res = workflow.reconcile(new TestCustomResource(), null); + var res = workflow.reconcile(new TestCustomResource(), mockContext); Assertions.assertThat(res.getErroredDependents()).isEmpty(); assertThat(executionHistory) @@ -103,7 +107,7 @@ void exceptionHandlingSimpleCases() { .withThrowExceptionFurther(false) .build(); - var res = workflow.reconcile(new TestCustomResource(), null); + var res = workflow.reconcile(new TestCustomResource(), mockContext); assertThrows(AggregatedOperatorException.class, res::throwAggregateExceptionIfErrorsPresent); @@ -123,7 +127,7 @@ void dependentsOnErroredResourceNotReconciled() { .withThrowExceptionFurther(false) .build(); - var res = workflow.reconcile(new TestCustomResource(), null); + var res = workflow.reconcile(new TestCustomResource(), mockContext); assertThrows(AggregatedOperatorException.class, res::throwAggregateExceptionIfErrorsPresent); @@ -145,7 +149,7 @@ void oneBranchErrorsOtherCompletes() { .withThrowExceptionFurther(false) .build(); - var res = workflow.reconcile(new TestCustomResource(), null); + var res = workflow.reconcile(new TestCustomResource(), mockContext); assertThrows(AggregatedOperatorException.class, res::throwAggregateExceptionIfErrorsPresent); @@ -164,7 +168,7 @@ void onlyOneDependsOnErroredResourceNotReconciled() { .withThrowExceptionFurther(false) .build(); - var res = workflow.reconcile(new TestCustomResource(), null); + var res = workflow.reconcile(new TestCustomResource(), mockContext); assertThrows(AggregatedOperatorException.class, res::throwAggregateExceptionIfErrorsPresent); @@ -182,7 +186,7 @@ void simpleReconcileCondition() { .addDependentResource(drDeleter).withReconcilePrecondition(not_met_reconcile_condition) .build(); - var res = workflow.reconcile(new TestCustomResource(), null); + var res = workflow.reconcile(new TestCustomResource(), mockContext); assertThat(executionHistory).notReconciled(dr1).reconciled(dr2).deleted(drDeleter); Assertions.assertThat(res.getErroredDependents()).isEmpty(); @@ -200,7 +204,7 @@ void triangleOnceConditionNotMet() { .dependsOn(dr1) .build(); - var res = workflow.reconcile(new TestCustomResource(), null); + var res = workflow.reconcile(new TestCustomResource(), mockContext); assertThat(executionHistory).reconciledInOrder(dr1, dr2).deleted(drDeleter); Assertions.assertThat(res.getErroredDependents()).isEmpty(); @@ -222,7 +226,7 @@ void reconcileConditionTransitiveDelete() { .withReconcilePrecondition(met_reconcile_condition) .build(); - var res = workflow.reconcile(new TestCustomResource(), null); + var res = workflow.reconcile(new TestCustomResource(), mockContext); Assertions.assertThat(res.getErroredDependents()).isEmpty(); assertThat(executionHistory).notReconciled(dr2); @@ -246,7 +250,7 @@ void reconcileConditionAlsoErrorDependsOn() { .withThrowExceptionFurther(false) .build(); - var res = workflow.reconcile(new TestCustomResource(), null); + var res = workflow.reconcile(new TestCustomResource(), mockContext); assertThrows(AggregatedOperatorException.class, res::throwAggregateExceptionIfErrorsPresent); @@ -267,7 +271,7 @@ void oneDependsOnConditionNotMet() { .addDependentResource(drDeleter).dependsOn(dr1, dr2) .build(); - var res = workflow.reconcile(new TestCustomResource(), null); + var res = workflow.reconcile(new TestCustomResource(), mockContext); Assertions.assertThat(res.getErroredDependents()).isEmpty(); @@ -287,7 +291,7 @@ void deletedIfReconcileConditionNotMet() { .addDependentResource(drDeleter2).dependsOn(dr1, drDeleter) .build(); - var res = workflow.reconcile(new TestCustomResource(), null); + var res = workflow.reconcile(new TestCustomResource(), mockContext); assertThat(executionHistory) .reconciledInOrder(dr1, drDeleter2, drDeleter) @@ -313,7 +317,7 @@ void deleteDoneInReverseOrder() { .addDependentResource(drDeleter4).dependsOn(drDeleter3) .build(); - var res = workflow.reconcile(new TestCustomResource(), null); + var res = workflow.reconcile(new TestCustomResource(), mockContext); assertThat(executionHistory) .reconciledInOrder(dr1, drDeleter4, drDeleter3, drDeleter) @@ -339,7 +343,7 @@ void diamondDeleteWithPostConditionInMiddle() { .addDependentResource(drDeleter4).dependsOn(drDeleter3, drDeleter2) .build(); - var res = workflow.reconcile(new TestCustomResource(), null); + var res = workflow.reconcile(new TestCustomResource(), mockContext); assertThat(executionHistory).notReconciled(drDeleter) .reconciledInOrder(drDeleter4, drDeleter2) @@ -363,7 +367,7 @@ void diamondDeleteErrorInMiddle() { .withThrowExceptionFurther(false) .build(); - var res = workflow.reconcile(new TestCustomResource(), null); + var res = workflow.reconcile(new TestCustomResource(), mockContext); assertThat(executionHistory) .notReconciled(drDeleter, drError) @@ -381,7 +385,7 @@ void readyConditionTrivialCase() { .addDependentResource(dr2).dependsOn(dr1) .build(); - var res = workflow.reconcile(new TestCustomResource(), null); + var res = workflow.reconcile(new TestCustomResource(), mockContext); assertThat(executionHistory).reconciledInOrder(dr1, dr2); @@ -397,7 +401,7 @@ void readyConditionNotMetTrivialCase() { .addDependentResource(dr2).dependsOn(dr1) .build(); - var res = workflow.reconcile(new TestCustomResource(), null); + var res = workflow.reconcile(new TestCustomResource(), mockContext); assertThat(executionHistory).reconciled(dr1).notReconciled(dr2); @@ -417,7 +421,7 @@ void readyConditionNotMetInOneParent() { .addDependentResource(dr3).dependsOn(dr1, dr2) .build(); - var res = workflow.reconcile(new TestCustomResource(), null); + var res = workflow.reconcile(new TestCustomResource(), mockContext); assertThat(executionHistory).reconciled(dr1, dr2).notReconciled(dr3); Assertions.assertThat(res.getErroredDependents()).isEmpty(); @@ -437,7 +441,7 @@ void diamondShareWithReadyCondition() { .addDependentResource(dr4).dependsOn(dr2, dr3) .build(); - var res = workflow.reconcile(new TestCustomResource(), null); + var res = workflow.reconcile(new TestCustomResource(), mockContext); Assertions.assertThat(res.getErroredDependents()).isEmpty(); assertThat(executionHistory).reconciledInOrder(dr1, dr2) diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/IndexDiscriminatorIT.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/IndexDiscriminatorIT.java new file mode 100644 index 0000000000..fe5b63de8a --- /dev/null +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/IndexDiscriminatorIT.java @@ -0,0 +1,77 @@ +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.indexdiscriminator.IndexDiscriminatorTestCustomResource; +import io.javaoperatorsdk.operator.sample.indexdiscriminator.IndexDiscriminatorTestReconciler; +import io.javaoperatorsdk.operator.sample.indexdiscriminator.IndexDiscriminatorTestSpec; + +import static io.javaoperatorsdk.operator.sample.indexdiscriminator.IndexDiscriminatorTestDRConfigMap.DATA_KEY; +import static io.javaoperatorsdk.operator.sample.indexdiscriminator.IndexDiscriminatorTestReconciler.FIRST_CONFIG_MAP_SUFFIX_1; +import static io.javaoperatorsdk.operator.sample.indexdiscriminator.IndexDiscriminatorTestReconciler.FIRST_CONFIG_MAP_SUFFIX_2; +import static org.assertj.core.api.Assertions.assertThat; +import static org.awaitility.Awaitility.await; + +class IndexDiscriminatorIT { + + public static final String TEST_RESOURCE_1 = "test1"; + public static final String CHANGED_SPEC_VALUE = "otherValue"; + @RegisterExtension + LocallyRunOperatorExtension operator = + LocallyRunOperatorExtension.builder().withReconciler(IndexDiscriminatorTestReconciler.class) + .build(); + + @Test + void resourcesFoundAndReconciled() { + var res = operator.create(createTestCustomResource()); + var reconciler = operator.getReconcilerOfType(IndexDiscriminatorTestReconciler.class); + + await().untilAsserted(() -> { + assertThat(reconciler.getNumberOfExecutions()).isEqualTo(1); + assertThat(operator.get(ConfigMap.class, TEST_RESOURCE_1 + FIRST_CONFIG_MAP_SUFFIX_1)) + .isNotNull(); + assertThat(operator.get(ConfigMap.class, TEST_RESOURCE_1 + FIRST_CONFIG_MAP_SUFFIX_2)) + .isNotNull(); + }); + + res.getSpec().setValue(CHANGED_SPEC_VALUE); + res = operator.replace(res); + + await().untilAsserted(() -> { + assertThat(reconciler.getNumberOfExecutions()).isEqualTo(2); + var cm1 = operator.get(ConfigMap.class, TEST_RESOURCE_1 + FIRST_CONFIG_MAP_SUFFIX_1); + var cm2 = operator.get(ConfigMap.class, TEST_RESOURCE_1 + FIRST_CONFIG_MAP_SUFFIX_2); + assertThat(cm1).isNotNull(); + assertThat(cm2).isNotNull(); + assertThat(cm1.getData().get(DATA_KEY)).isEqualTo(CHANGED_SPEC_VALUE); + assertThat(cm2.getData().get(DATA_KEY)).isEqualTo(CHANGED_SPEC_VALUE); + }); + + operator.delete(res); + + await().untilAsserted(() -> { + var cm1 = operator.get(ConfigMap.class, TEST_RESOURCE_1 + FIRST_CONFIG_MAP_SUFFIX_1); + var cm2 = operator.get(ConfigMap.class, TEST_RESOURCE_1 + FIRST_CONFIG_MAP_SUFFIX_2); + assertThat(cm1).isNull(); + assertThat(cm2).isNull(); + }); + } + + public IndexDiscriminatorTestCustomResource createTestCustomResource() { + IndexDiscriminatorTestCustomResource resource = + new IndexDiscriminatorTestCustomResource(); + resource.setMetadata( + new ObjectMetaBuilder() + .withName(TEST_RESOURCE_1) + .withNamespace(operator.getNamespace()) + .build()); + resource.setSpec(new IndexDiscriminatorTestSpec()); + resource.getSpec().setValue("default"); + return resource; + } + +} diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/indexdiscriminator/IndexDiscriminator.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/indexdiscriminator/IndexDiscriminator.java new file mode 100644 index 0000000000..eb6e193479 --- /dev/null +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/indexdiscriminator/IndexDiscriminator.java @@ -0,0 +1,41 @@ +package io.javaoperatorsdk.operator.sample.indexdiscriminator; + +import java.util.Optional; + +import io.fabric8.kubernetes.api.model.ConfigMap; +import io.javaoperatorsdk.operator.api.reconciler.Context; +import io.javaoperatorsdk.operator.api.reconciler.ResourceDiscriminator; +import io.javaoperatorsdk.operator.processing.event.source.informer.InformerEventSource; + +import static io.javaoperatorsdk.operator.sample.indexdiscriminator.IndexDiscriminatorTestReconciler.configMapKeyFromPrimary; + +public class IndexDiscriminator + implements ResourceDiscriminator { + + private final String indexName; + private final String nameSuffix; + + public IndexDiscriminator(String indexName, String nameSuffix) { + this.indexName = indexName; + this.nameSuffix = nameSuffix; + } + + @Override + public Optional distinguish(Class resource, + IndexDiscriminatorTestCustomResource primary, + Context context) { + + InformerEventSource eventSource = + (InformerEventSource) context + .eventSourceRetriever() + .getResourceEventSourceFor(ConfigMap.class); + var resources = eventSource.byIndex(indexName, configMapKeyFromPrimary(primary, nameSuffix)); + if (resources.isEmpty()) { + return Optional.empty(); + } else if (resources.size() > 1) { + throw new IllegalStateException("more than one resource"); + } else { + return Optional.of(resources.get(0)); + } + } +} diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/indexdiscriminator/IndexDiscriminatorTestCustomResource.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/indexdiscriminator/IndexDiscriminatorTestCustomResource.java new file mode 100644 index 0000000000..729b1d80eb --- /dev/null +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/indexdiscriminator/IndexDiscriminatorTestCustomResource.java @@ -0,0 +1,16 @@ +package io.javaoperatorsdk.operator.sample.indexdiscriminator; + +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("idt") +public class IndexDiscriminatorTestCustomResource + extends CustomResource + implements Namespaced { + +} diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/indexdiscriminator/IndexDiscriminatorTestDRConfigMap.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/indexdiscriminator/IndexDiscriminatorTestDRConfigMap.java new file mode 100644 index 0000000000..88dc40f55c --- /dev/null +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/indexdiscriminator/IndexDiscriminatorTestDRConfigMap.java @@ -0,0 +1,38 @@ +package io.javaoperatorsdk.operator.sample.indexdiscriminator; + +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.CRUDNoGCKubernetesDependentResource; +import io.javaoperatorsdk.operator.processing.dependent.kubernetes.KubernetesDependent; + +@KubernetesDependent +public class IndexDiscriminatorTestDRConfigMap + extends CRUDNoGCKubernetesDependentResource { + + public static final String DATA_KEY = "key"; + private final String suffix; + + public IndexDiscriminatorTestDRConfigMap(String value) { + super(ConfigMap.class); + this.suffix = value; + } + + @Override + protected ConfigMap desired(IndexDiscriminatorTestCustomResource primary, + Context context) { + Map data = new HashMap<>(); + data.put(DATA_KEY, primary.getSpec().getValue()); + + return new ConfigMapBuilder() + .withNewMetadata() + .withName(primary.getMetadata().getName() + suffix) + .withNamespace(primary.getMetadata().getNamespace()) + .endMetadata() + .withData(data) + .build(); + } +} diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/indexdiscriminator/IndexDiscriminatorTestReconciler.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/indexdiscriminator/IndexDiscriminatorTestReconciler.java new file mode 100644 index 0000000000..0b0af2a1cc --- /dev/null +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/indexdiscriminator/IndexDiscriminatorTestReconciler.java @@ -0,0 +1,120 @@ +package io.javaoperatorsdk.operator.sample.indexdiscriminator; + +import java.util.Collections; +import java.util.List; +import java.util.Map; +import java.util.concurrent.atomic.AtomicInteger; + +import io.fabric8.kubernetes.api.model.ConfigMap; +import io.fabric8.kubernetes.client.KubernetesClient; +import io.javaoperatorsdk.operator.api.config.informer.InformerConfiguration; +import io.javaoperatorsdk.operator.api.reconciler.*; +import io.javaoperatorsdk.operator.junit.KubernetesClientAware; +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 IndexDiscriminatorTestReconciler + implements Reconciler, + Cleaner, + TestExecutionInfoProvider, EventSourceInitializer, + KubernetesClientAware { + + public static final String FIRST_CONFIG_MAP_SUFFIX_1 = "-1"; + public static final String FIRST_CONFIG_MAP_SUFFIX_2 = "-2"; + public static final String CONFIG_MAP_INDEX_1 = "CONFIG_MAP_INDEX1"; + public static final String CONFIG_MAP_INDEX_2 = "CONFIG_MAP_INDEX2"; + + private final AtomicInteger numberOfExecutions = new AtomicInteger(0); + + private final IndexDiscriminatorTestDRConfigMap firstDependentResourceConfigMap; + private final IndexDiscriminatorTestDRConfigMap secondDependentResourceConfigMap; + private KubernetesClient client; + + public IndexDiscriminatorTestReconciler() { + firstDependentResourceConfigMap = + new IndexDiscriminatorTestDRConfigMap(FIRST_CONFIG_MAP_SUFFIX_1); + secondDependentResourceConfigMap = + new IndexDiscriminatorTestDRConfigMap(FIRST_CONFIG_MAP_SUFFIX_2); + } + + @Override + public UpdateControl reconcile( + IndexDiscriminatorTestCustomResource 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); + + eventSource.addIndexer(CONFIG_MAP_INDEX_1, cm -> { + if (cm.getMetadata().getName().endsWith(FIRST_CONFIG_MAP_SUFFIX_1)) { + return List.of(configMapKey(cm)); + } else { + return Collections.emptyList(); + } + }); + eventSource.addIndexer(CONFIG_MAP_INDEX_2, cm -> { + if (cm.getMetadata().getName().endsWith(FIRST_CONFIG_MAP_SUFFIX_2)) { + return List.of(configMapKey(cm)); + } else { + return Collections.emptyList(); + } + }); + + firstDependentResourceConfigMap.configureWith(eventSource); + secondDependentResourceConfigMap.configureWith(eventSource); + + firstDependentResourceConfigMap + .setResourceDiscriminator( + new IndexDiscriminator(CONFIG_MAP_INDEX_1, FIRST_CONFIG_MAP_SUFFIX_1)); + secondDependentResourceConfigMap + .setResourceDiscriminator( + new IndexDiscriminator(CONFIG_MAP_INDEX_2, FIRST_CONFIG_MAP_SUFFIX_2)); + return EventSourceInitializer.nameEventSources(eventSource); + } + + @Override + public KubernetesClient getKubernetesClient() { + return client; + } + + @Override + public void setKubernetesClient(KubernetesClient kubernetesClient) { + this.client = kubernetesClient; + firstDependentResourceConfigMap.setKubernetesClient(kubernetesClient); + secondDependentResourceConfigMap.setKubernetesClient(kubernetesClient); + } + + public static String configMapKey(ConfigMap configMap) { + return configMap.getMetadata().getName() + "#" + configMap.getMetadata().getNamespace(); + } + + public static String configMapKeyFromPrimary(IndexDiscriminatorTestCustomResource primary, + String nameSuffix) { + return primary.getMetadata().getName() + nameSuffix + "#" + + primary.getMetadata().getNamespace(); + } + + @Override + public DeleteControl cleanup(IndexDiscriminatorTestCustomResource resource, + Context context) { + firstDependentResourceConfigMap.delete(resource, context); + secondDependentResourceConfigMap.delete(resource, context); + return DeleteControl.defaultDelete(); + } +} diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/indexdiscriminator/IndexDiscriminatorTestSpec.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/indexdiscriminator/IndexDiscriminatorTestSpec.java new file mode 100644 index 0000000000..fcedd48abe --- /dev/null +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/indexdiscriminator/IndexDiscriminatorTestSpec.java @@ -0,0 +1,15 @@ +package io.javaoperatorsdk.operator.sample.indexdiscriminator; + +public class IndexDiscriminatorTestSpec { + + private String value; + + public String getValue() { + return value; + } + + public IndexDiscriminatorTestSpec setValue(String value) { + this.value = value; + return this; + } +} diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/indexdiscriminator/IndexDiscriminatorTestStatus.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/indexdiscriminator/IndexDiscriminatorTestStatus.java new file mode 100644 index 0000000000..d31c86e8de --- /dev/null +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/indexdiscriminator/IndexDiscriminatorTestStatus.java @@ -0,0 +1,5 @@ +package io.javaoperatorsdk.operator.sample.indexdiscriminator; + +public class IndexDiscriminatorTestStatus { + +} 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 4cdc2e457d..1adbfb9f95 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 @@ -31,7 +31,6 @@ protected ConfigMap desired(MultipleDependentResourceCustomResource primary, .withNewMetadata() .withName(primary.getConfigMapName(value)) .withNamespace(primary.getMetadata().getNamespace()) - .withLabels(Map.of(MultipleDependentResourceReconciler.LABEL, String.valueOf(value))) .endMetadata() .withData(data) .build(); 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 8cdbb81eba..49f5ee64c1 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 @@ -3,16 +3,14 @@ import java.util.Map; import java.util.concurrent.atomic.AtomicInteger; +import io.fabric8.kubernetes.api.model.ConfigMap; import io.fabric8.kubernetes.client.KubernetesClient; -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.EventSourceInitializer; -import io.javaoperatorsdk.operator.api.reconciler.Reconciler; -import io.javaoperatorsdk.operator.api.reconciler.UpdateControl; +import io.javaoperatorsdk.operator.api.config.informer.InformerConfiguration; +import io.javaoperatorsdk.operator.api.reconciler.*; import io.javaoperatorsdk.operator.junit.KubernetesClientAware; -import io.javaoperatorsdk.operator.processing.dependent.kubernetes.KubernetesDependentResourceConfig; +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 @@ -23,7 +21,6 @@ public class MultipleDependentResourceReconciler public static final int FIRST_CONFIG_MAP_ID = 1; public static final int SECOND_CONFIG_MAP_ID = 2; - public static final String LABEL = "multipledependentresource"; private final AtomicInteger numberOfExecutions = new AtomicInteger(0); private final MultipleDependentResourceConfigMap firstDependentResourceConfigMap; @@ -32,18 +29,19 @@ public class MultipleDependentResourceReconciler public MultipleDependentResourceReconciler() { firstDependentResourceConfigMap = new MultipleDependentResourceConfigMap(FIRST_CONFIG_MAP_ID); - secondDependentResourceConfigMap = new MultipleDependentResourceConfigMap(SECOND_CONFIG_MAP_ID); - firstDependentResourceConfigMap.configureWith( - new KubernetesDependentResourceConfig() - .setLabelSelector(getLabelSelector(FIRST_CONFIG_MAP_ID))); - secondDependentResourceConfigMap.configureWith( - new KubernetesDependentResourceConfig() - .setLabelSelector(getLabelSelector(SECOND_CONFIG_MAP_ID))); - } + secondDependentResourceConfigMap = new MultipleDependentResourceConfigMap(SECOND_CONFIG_MAP_ID); - private String getLabelSelector(int resourceId) { - return LABEL + "=" + resourceId; + 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 @@ -64,9 +62,13 @@ public int getNumberOfExecutions() { @Override public Map prepareEventSources( EventSourceContext context) { - return EventSourceInitializer.nameEventSources( - firstDependentResourceConfigMap.initEventSource(context), - secondDependentResourceConfigMap.initEventSource(context)); + InformerEventSource eventSource = + new InformerEventSource<>(InformerConfiguration.from(ConfigMap.class, context) + .build(), context); + firstDependentResourceConfigMap.configureWith(eventSource); + secondDependentResourceConfigMap.configureWith(eventSource); + + return EventSourceInitializer.nameEventSources(eventSource); } @Override diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/orderedmanageddependent/ConfigMapDependentResource1.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/orderedmanageddependent/ConfigMapDependentResource1.java index 14530cf17e..bf8d60d9c4 100644 --- a/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/orderedmanageddependent/ConfigMapDependentResource1.java +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/orderedmanageddependent/ConfigMapDependentResource1.java @@ -6,11 +6,14 @@ import io.fabric8.kubernetes.api.model.ConfigMap; import io.fabric8.kubernetes.api.model.ObjectMeta; import io.javaoperatorsdk.operator.api.reconciler.Context; +import io.javaoperatorsdk.operator.api.reconciler.ResourceIDMatcherDiscriminator; import io.javaoperatorsdk.operator.api.reconciler.dependent.ReconcileResult; import io.javaoperatorsdk.operator.processing.dependent.kubernetes.CRUDKubernetesDependentResource; import io.javaoperatorsdk.operator.processing.dependent.kubernetes.KubernetesDependent; +import io.javaoperatorsdk.operator.processing.event.ResourceID; -@KubernetesDependent(labelSelector = "dependent = cm1") +@KubernetesDependent(labelSelector = "dependent = cm1", + resourceDiscriminator = ConfigMapDependentResource1.CM1ResourceDiscriminator.class) public class ConfigMapDependentResource1 extends CRUDKubernetesDependentResource { @@ -42,4 +45,11 @@ protected ConfigMap desired(OrderedManagedDependentCustomResource primary, return configMap; } + public static class CM1ResourceDiscriminator + extends ResourceIDMatcherDiscriminator { + public CM1ResourceDiscriminator() { + super(p -> new ResourceID(p.getMetadata().getName() + "1", p.getMetadata().getNamespace())); + } + } + } diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/orderedmanageddependent/ConfigMapDependentResource2.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/orderedmanageddependent/ConfigMapDependentResource2.java index 35ae69586e..2b17d615b9 100644 --- a/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/orderedmanageddependent/ConfigMapDependentResource2.java +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/orderedmanageddependent/ConfigMapDependentResource2.java @@ -6,11 +6,14 @@ import io.fabric8.kubernetes.api.model.ConfigMap; import io.fabric8.kubernetes.api.model.ObjectMeta; import io.javaoperatorsdk.operator.api.reconciler.Context; +import io.javaoperatorsdk.operator.api.reconciler.ResourceIDMatcherDiscriminator; import io.javaoperatorsdk.operator.api.reconciler.dependent.ReconcileResult; import io.javaoperatorsdk.operator.processing.dependent.kubernetes.CRUDKubernetesDependentResource; import io.javaoperatorsdk.operator.processing.dependent.kubernetes.KubernetesDependent; +import io.javaoperatorsdk.operator.processing.event.ResourceID; -@KubernetesDependent(labelSelector = "dependent = cm2") +@KubernetesDependent(labelSelector = "dependent = cm2", + resourceDiscriminator = ConfigMapDependentResource2.CM2ResourceDiscriminator.class) public class ConfigMapDependentResource2 extends CRUDKubernetesDependentResource { @@ -42,4 +45,11 @@ protected ConfigMap desired(OrderedManagedDependentCustomResource primary, return configMap; } + public static class CM2ResourceDiscriminator + extends ResourceIDMatcherDiscriminator { + public CM2ResourceDiscriminator() { + super(p -> new ResourceID(p.getMetadata().getName() + "2", p.getMetadata().getNamespace())); + } + } + } diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/perresourceeventsource/PerResourcePollingEventSourceTestReconciler.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/perresourceeventsource/PerResourcePollingEventSourceTestReconciler.java index dec730d536..ecc33afcb3 100644 --- a/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/perresourceeventsource/PerResourcePollingEventSourceTestReconciler.java +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/perresourceeventsource/PerResourcePollingEventSourceTestReconciler.java @@ -57,7 +57,8 @@ public void setKubernetesClient(KubernetesClient kubernetesClient) { } public int getNumberOfExecutions(String name) { - return numberOfExecutions.get(name); + var num = numberOfExecutions.get(name); + return num == null ? 0 : num; } public int getNumberOfFetchExecution(String name) { diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/standalonedependent/StandaloneDependentTestReconciler.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/standalonedependent/StandaloneDependentTestReconciler.java index af6b2d7e25..2dace670ba 100644 --- a/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/standalonedependent/StandaloneDependentTestReconciler.java +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/standalonedependent/StandaloneDependentTestReconciler.java @@ -47,7 +47,7 @@ public UpdateControl reconcile( StandaloneDependentTestCustomResource primary, Context context) { deploymentDependent.reconcile(primary, context); - Optional deployment = deploymentDependent.getSecondaryResource(primary); + Optional deployment = context.getSecondaryResource(Deployment.class); if (deployment.isEmpty()) { throw new IllegalStateException("Resource should not be empty after reconcile."); } diff --git a/sample-operators/webpage/src/main/java/io/javaoperatorsdk/operator/sample/WebPageDependentsWorkflowReconciler.java b/sample-operators/webpage/src/main/java/io/javaoperatorsdk/operator/sample/WebPageDependentsWorkflowReconciler.java index 6986180b89..6b81a9d6e3 100644 --- a/sample-operators/webpage/src/main/java/io/javaoperatorsdk/operator/sample/WebPageDependentsWorkflowReconciler.java +++ b/sample-operators/webpage/src/main/java/io/javaoperatorsdk/operator/sample/WebPageDependentsWorkflowReconciler.java @@ -69,7 +69,7 @@ public UpdateControl reconcile(WebPage webPage, Context contex webPage.setStatus( createStatus( - configMapDR.getSecondaryResource(webPage).orElseThrow().getMetadata().getName())); + context.getSecondaryResource(ConfigMap.class).orElseThrow().getMetadata().getName())); return UpdateControl.patchStatus(webPage); } @@ -93,6 +93,4 @@ private void initDependentResources(KubernetesClient client) { }); } - - } diff --git a/sample-operators/webpage/src/main/java/io/javaoperatorsdk/operator/sample/WebPageStandaloneDependentsReconciler.java b/sample-operators/webpage/src/main/java/io/javaoperatorsdk/operator/sample/WebPageStandaloneDependentsReconciler.java index 0bb692a4d0..b99e130135 100644 --- a/sample-operators/webpage/src/main/java/io/javaoperatorsdk/operator/sample/WebPageStandaloneDependentsReconciler.java +++ b/sample-operators/webpage/src/main/java/io/javaoperatorsdk/operator/sample/WebPageStandaloneDependentsReconciler.java @@ -65,7 +65,7 @@ public UpdateControl reconcile(WebPage webPage, Context contex webPage.setStatus( createStatus( - configMapDR.getSecondaryResource(webPage).orElseThrow().getMetadata().getName())); + context.getSecondaryResource(ConfigMap.class).orElseThrow().getMetadata().getName())); return UpdateControl.patchStatus(webPage); } From 7c66c05f6358f9fe5256c753ad262e004099b186 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Attila=20M=C3=A9sz=C3=A1ros?= Date: Thu, 29 Sep 2022 16:31:43 +0200 Subject: [PATCH 55/67] bulk dependent resources (#1448) --- .../api/reconciler/ResourceDiscriminator.java | 1 - .../reconciler/dependent/ReconcileResult.java | 50 ++++++-- .../dependent/AbstractDependentResource.java | 104 ++++++++++++++-- .../dependent/BulkDependentResource.java | 35 ++++++ .../processing/dependent/BulkUpdater.java | 20 +++ .../dependent/DesiredEqualsMatcher.java | 6 + .../processing/dependent/Matcher.java | 15 +++ .../processing/dependent/Updater.java | 4 + .../GenericKubernetesResourceMatcher.java | 103 +++++++++++----- .../KubernetesDependentResource.java | 30 ++++- .../KubernetesDependentResourceConfig.java | 7 +- .../workflow/WorkflowCleanupExecutor.java | 1 - .../junit/LocallyRunOperatorExtension.java | 8 +- .../bulkdependent/BulkDependentDeleterIT.java | 19 +++ .../bulkdependent/BulkDependentTestBase.java | 114 ++++++++++++++++++ .../BulkExternalDependentIT.java | 56 +++++++++ .../bulkdependent/ManagedBulkDependentIT.java | 20 +++ .../StandaloneBulkDependentIT.java | 19 +++ .../BulkDependentTestCustomResource.java | 15 +++ .../bulkdependent/BulkDependentTestSpec.java | 25 ++++ .../CRUDConfigMapBulkDependentResource.java | 7 ++ ...ConfigMapDeleterBulkDependentResource.java | 72 +++++++++++ .../ManagedBulkDependentReconciler.java | 25 ++++ .../ManagedDeleterBulkReconciler.java | 20 +++ .../StandaloneBulkDependentReconciler.java | 58 +++++++++ .../ExternalBulkDependentResource.java | 101 ++++++++++++++++ .../ExternalBulkResourceReconciler.java | 19 +++ .../external/ExternalResource.java | 37 ++++++ .../external/ExternalServiceMock.java | 39 ++++++ .../MultipleDependentResourceReconciler.java | 1 - 30 files changed, 963 insertions(+), 68 deletions(-) create mode 100644 operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/BulkDependentResource.java create mode 100644 operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/BulkUpdater.java create mode 100644 operator-framework/src/test/java/io/javaoperatorsdk/operator/bulkdependent/BulkDependentDeleterIT.java create mode 100644 operator-framework/src/test/java/io/javaoperatorsdk/operator/bulkdependent/BulkDependentTestBase.java create mode 100644 operator-framework/src/test/java/io/javaoperatorsdk/operator/bulkdependent/BulkExternalDependentIT.java create mode 100644 operator-framework/src/test/java/io/javaoperatorsdk/operator/bulkdependent/ManagedBulkDependentIT.java create mode 100644 operator-framework/src/test/java/io/javaoperatorsdk/operator/bulkdependent/StandaloneBulkDependentIT.java create mode 100644 operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/bulkdependent/BulkDependentTestCustomResource.java create mode 100644 operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/bulkdependent/BulkDependentTestSpec.java create mode 100644 operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/bulkdependent/CRUDConfigMapBulkDependentResource.java create mode 100644 operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/bulkdependent/ConfigMapDeleterBulkDependentResource.java create mode 100644 operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/bulkdependent/ManagedBulkDependentReconciler.java create mode 100644 operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/bulkdependent/ManagedDeleterBulkReconciler.java create mode 100644 operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/bulkdependent/StandaloneBulkDependentReconciler.java create mode 100644 operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/bulkdependent/external/ExternalBulkDependentResource.java create mode 100644 operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/bulkdependent/external/ExternalBulkResourceReconciler.java create mode 100644 operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/bulkdependent/external/ExternalResource.java create mode 100644 operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/bulkdependent/external/ExternalServiceMock.java diff --git a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/reconciler/ResourceDiscriminator.java b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/reconciler/ResourceDiscriminator.java index 072e7d8078..eb947fa440 100644 --- a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/reconciler/ResourceDiscriminator.java +++ b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/reconciler/ResourceDiscriminator.java @@ -7,5 +7,4 @@ public interface ResourceDiscriminator { Optional distinguish(Class resource, P primary, Context

context); - } diff --git a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/reconciler/dependent/ReconcileResult.java b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/reconciler/dependent/ReconcileResult.java index c83da1c8ea..468e14e8ea 100644 --- a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/reconciler/dependent/ReconcileResult.java +++ b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/reconciler/dependent/ReconcileResult.java @@ -1,14 +1,14 @@ package io.javaoperatorsdk.operator.api.reconciler.dependent; -import java.util.Optional; +import java.util.*; +import java.util.stream.Collectors; import io.fabric8.kubernetes.api.model.HasMetadata; import io.javaoperatorsdk.operator.processing.event.ResourceID; public class ReconcileResult { - private final R resource; - private final Operation operation; + private final Map resourceOperations; public static ReconcileResult resourceCreated(T resource) { return new ReconcileResult<>(resource, Operation.CREATED); @@ -22,25 +22,49 @@ public static ReconcileResult noOperation(T resource) { return new ReconcileResult<>(resource, Operation.NONE); } + @SafeVarargs + public static ReconcileResult aggregatedResult(ReconcileResult... results) { + if (results == null) { + throw new IllegalArgumentException("Should provide results to aggregate"); + } + if (results.length == 1) { + return results[0]; + } + final Map operations = new HashMap<>(results.length); + for (ReconcileResult res : results) { + res.getSingleResource().ifPresent(r -> operations.put(r, res.getSingleOperation())); + } + return new ReconcileResult<>(operations); + } + @Override public String toString() { - return getResource() - .map(r -> r instanceof HasMetadata ? ResourceID.fromResource((HasMetadata) r) : r) - .orElse("no resource") - + " -> " + operation; + return resourceOperations.entrySet().stream().collect(Collectors.toMap( + e -> e instanceof HasMetadata ? ResourceID.fromResource((HasMetadata) e) : e, + Map.Entry::getValue)) + .toString(); } private ReconcileResult(R resource, Operation operation) { - this.resource = resource; - this.operation = operation; + resourceOperations = resource != null ? Map.of(resource, operation) : Collections.emptyMap(); + } + + private ReconcileResult(Map operations) { + resourceOperations = Collections.unmodifiableMap(operations); + } + + public Optional getSingleResource() { + return resourceOperations.entrySet().stream().findFirst().map(Map.Entry::getKey); } - public Optional getResource() { - return Optional.ofNullable(resource); + public Operation getSingleOperation() { + return resourceOperations.entrySet().stream().findFirst().map(Map.Entry::getValue) + .orElseThrow(); } - public Operation getOperation() { - return operation; + @SuppressWarnings("unused") + public Map getResourceOperations() { + return resourceOperations; } public enum Operation { 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 1abfb3df4b..078fc60b66 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,5 +1,7 @@ package io.javaoperatorsdk.operator.processing.dependent; +import java.util.ArrayList; +import java.util.List; import java.util.Optional; import org.slf4j.Logger; @@ -20,25 +22,73 @@ public abstract class AbstractDependentResource protected final boolean creatable = this instanceof Creator; protected final boolean updatable = this instanceof Updater; + protected final boolean bulk = this instanceof BulkDependentResource; protected Creator creator; protected Updater updater; + protected BulkDependentResource bulkDependentResource; - private ResourceDiscriminator resourceDiscriminator; + private final List> resourceDiscriminator = new ArrayList<>(1); @SuppressWarnings("unchecked") public AbstractDependentResource() { creator = creatable ? (Creator) this : null; updater = updatable ? (Updater) this : null; + + bulkDependentResource = bulk ? (BulkDependentResource) this : null; } @Override public ReconcileResult reconcile(P primary, Context

context) { - Optional maybeActual = getSecondaryResource(primary, context); + if (bulk) { + final var count = bulkDependentResource.count(primary, context); + deleteBulkResourcesIfRequired(count, lastKnownBulkSize(), primary, context); + adjustDiscriminators(count); + @SuppressWarnings("unchecked") + final ReconcileResult[] results = new ReconcileResult[count]; + for (int i = 0; i < count; i++) { + results[i] = reconcileIndexAware(primary, i, context); + } + return ReconcileResult.aggregatedResult(results); + } else { + return reconcileIndexAware(primary, 0, context); + } + } + + protected void deleteBulkResourcesIfRequired(int targetCount, int actualCount, P primary, + Context

context) { + if (targetCount >= actualCount) { + return; + } + for (int i = targetCount; i < actualCount; i++) { + var resource = getSecondaryResourceIndexAware(primary, i, context); + var index = i; + resource.ifPresent( + r -> bulkDependentResource.deleteBulkResourceWithIndex(primary, r, index, context)); + } + } + + private void adjustDiscriminators(int count) { + if (resourceDiscriminator.size() == count) { + return; + } + if (resourceDiscriminator.size() < count) { + for (int i = resourceDiscriminator.size(); i < count; i++) { + resourceDiscriminator.add(bulkDependentResource.getResourceDiscriminator(i)); + } + } + if (resourceDiscriminator.size() > count) { + resourceDiscriminator.subList(count, resourceDiscriminator.size()).clear(); + } + } + + protected ReconcileResult reconcileIndexAware(P primary, int i, Context

context) { + Optional maybeActual = bulk ? getSecondaryResourceIndexAware(primary, i, context) + : getSecondaryResource(primary, context); if (creatable || updatable) { if (maybeActual.isEmpty()) { if (creatable) { - var desired = desired(primary, context); + var desired = desiredIndexAware(primary, i, context); throwIfNull(desired, primary, "Desired"); logForOperation("Creating", primary, desired); var createdResource = handleCreate(desired, primary, context); @@ -47,9 +97,15 @@ public ReconcileResult reconcile(P primary, Context

context) { } else { final var actual = maybeActual.get(); if (updatable) { - final var match = updater.match(actual, primary, context); + final Matcher.Result match; + if (bulk) { + match = updater.match(actual, primary, i, context); + } else { + match = updater.match(actual, primary, context); + } if (!match.matched()) { - final var desired = match.computedDesired().orElse(desired(primary, context)); + final var desired = + match.computedDesired().orElse(desiredIndexAware(primary, i, context)); throwIfNull(desired, primary, "Desired"); logForOperation("Updating", primary, desired); var updatedResource = handleUpdate(actual, desired, primary, context); @@ -67,9 +123,18 @@ public ReconcileResult reconcile(P primary, Context

context) { return ReconcileResult.noOperation(maybeActual.orElse(null)); } + private R desiredIndexAware(P primary, int i, Context

context) { + return bulk ? desired(primary, i, context) + : desired(primary, context); + } + public Optional getSecondaryResource(P primary, Context

context) { - return resourceDiscriminator == null ? context.getSecondaryResource(resourceType()) - : resourceDiscriminator.distinguish(resourceType(), primary, context); + return resourceDiscriminator.isEmpty() ? context.getSecondaryResource(resourceType()) + : resourceDiscriminator.get(0).distinguish(resourceType(), primary, context); + } + + protected Optional getSecondaryResourceIndexAware(P primary, int index, Context

context) { + return context.getSecondaryResource(resourceType(), resourceDiscriminator.get(index)); } private void throwIfNull(R desired, P primary, String descriptor) { @@ -97,7 +162,7 @@ protected R handleCreate(R desired, P primary, Context

context) { } /** - * Allows sub-classes to perform additional processing (e.g. caching) on the created resource if + * Allows subclasses to perform additional processing (e.g. caching) on the created resource if * needed. * * @param primaryResourceId the {@link ResourceID} of the primary resource associated with the @@ -129,12 +194,29 @@ protected R desired(P primary, Context

context) { "desired method must be implemented if this DependentResource can be created and/or updated"); } - public void setResourceDiscriminator( + protected R desired(P primary, int index, Context

context) { + throw new IllegalStateException( + "Must be implemented for bulk DependentResource creation"); + } + + public AbstractDependentResource setResourceDiscriminator( ResourceDiscriminator resourceDiscriminator) { - this.resourceDiscriminator = resourceDiscriminator; + if (resourceDiscriminator != null) { + this.resourceDiscriminator.add(resourceDiscriminator); + } + return this; } public ResourceDiscriminator getResourceDiscriminator() { - return resourceDiscriminator; + if (this.resourceDiscriminator.isEmpty()) { + return null; + } else { + return this.resourceDiscriminator.get(0); + } } + + protected int lastKnownBulkSize() { + return resourceDiscriminator.size(); + } + } diff --git a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/BulkDependentResource.java b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/BulkDependentResource.java new file mode 100644 index 0000000000..1f2688f5cb --- /dev/null +++ b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/BulkDependentResource.java @@ -0,0 +1,35 @@ +package io.javaoperatorsdk.operator.processing.dependent; + +import io.fabric8.kubernetes.api.model.HasMetadata; +import io.javaoperatorsdk.operator.api.reconciler.Context; +import io.javaoperatorsdk.operator.api.reconciler.ResourceDiscriminator; +import io.javaoperatorsdk.operator.api.reconciler.dependent.Deleter; + +/** + * Manages dynamic number of resources created for a primary resource. Since the point of a bulk + * dependent resource is to manage the number of secondary resources dynamically it implement + * {@link Creator} and {@link Deleter} interfaces out of the box. A concrete dependent resource can + * implement additionally also {@link Updater}. + */ +public interface BulkDependentResource extends Creator, Deleter

{ + + /** + * @return number of resources to create + */ + int count(P primary, Context

context); + + R desired(P primary, int index, Context

context); + + /** + * Used to delete resource if the desired count is lower than the actual count of a resource. + * + * @param primary resource + * @param resource actual resource from the cache for the index + * @param i index of the resource + * @param context actual context + */ + void deleteBulkResourceWithIndex(P primary, R resource, int i, Context

context); + + ResourceDiscriminator getResourceDiscriminator(int index); + +} diff --git a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/BulkUpdater.java b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/BulkUpdater.java new file mode 100644 index 0000000000..9c00b47d0c --- /dev/null +++ b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/BulkUpdater.java @@ -0,0 +1,20 @@ +package io.javaoperatorsdk.operator.processing.dependent; + +import io.fabric8.kubernetes.api.model.HasMetadata; +import io.javaoperatorsdk.operator.api.reconciler.Context; + +/** + * Helper for the Bulk Dependent Resources to make it more explicit that bulk needs to only + * implement the index aware match method. + * + * @param secondary resource type + * @param

primary resource type + */ +public interface BulkUpdater extends Updater { + + default Matcher.Result match(R actualResource, P primary, Context

context) { + throw new IllegalStateException(); + } + + Matcher.Result match(R actualResource, P primary, int index, Context

context); +} diff --git a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/DesiredEqualsMatcher.java b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/DesiredEqualsMatcher.java index 459d7951d6..1d3b34a47b 100644 --- a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/DesiredEqualsMatcher.java +++ b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/DesiredEqualsMatcher.java @@ -16,4 +16,10 @@ public Result match(R actualResource, P primary, Context

context) { var desired = abstractDependentResource.desired(primary, context); return Result.computed(actualResource.equals(desired), desired); } + + @Override + public Result match(R actualResource, P primary, int index, Context

context) { + var desired = abstractDependentResource.desired(primary, index, context); + return Result.computed(actualResource.equals(desired), desired); + } } diff --git a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/Matcher.java b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/Matcher.java index 750fe89cbf..835f76ab3a 100644 --- a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/Matcher.java +++ b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/Matcher.java @@ -95,4 +95,19 @@ public Optional computedDesired() { * {@link Result#computed(boolean, Object)}) */ Result match(R actualResource, P primary, Context

context); + + /** + * Determines whether the specified secondary resource matches the desired state with target index + * of a bulk resource as defined from the specified primary resource, given the specified + * {@link Context}. + * + * @param actualResource the resource we want to determine whether it's matching the desired state + * @param primary the primary resource from which the desired state is inferred + * @param context the context in which the resource is being matched + * @return a {@link Result} encapsulating whether the resource matched its desired state and this + * associated state if it was computed as part of the matching process. Use the static + * convenience methods ({@link Result#nonComputed(boolean)} and + * {@link Result#computed(boolean, Object)}) + */ + Result match(R actualResource, P primary, int index, Context

context); } diff --git a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/Updater.java b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/Updater.java index 828f9ad785..06b3cb52f6 100644 --- a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/Updater.java +++ b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/Updater.java @@ -8,4 +8,8 @@ public interface Updater { R update(R actual, R desired, P primary, Context

context); Result match(R actualResource, P primary, Context

context); + + default Result match(R actualResource, P primary, int index, Context

context) { + throw new IllegalStateException("Implement this for bulk matching"); + } } 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 e294b1c938..bb066b5b24 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 @@ -24,17 +24,42 @@ private GenericKubernetesResourceMatcher(KubernetesDependentResource depen static Matcher matcherFor( Class resourceType, KubernetesDependentResource dependentResource) { if (Secret.class.isAssignableFrom(resourceType)) { - return (actual, primary, context) -> { - final var desired = dependentResource.desired(primary, context); - return Result.computed( - ResourceComparators.compareSecretData((Secret) desired, (Secret) actual), desired); + return new Matcher<>() { + @Override + public Result match(R actualResource, P primary, Context

context) { + final var desired = dependentResource.desired(primary, context); + return Result.computed( + ResourceComparators.compareSecretData((Secret) desired, (Secret) actualResource), + desired); + } + + @Override + public Result match(R actualResource, P primary, int index, Context

context) { + final var desired = dependentResource.desired(primary, index, context); + return Result.computed( + ResourceComparators.compareSecretData((Secret) desired, (Secret) actualResource), + desired); + } }; } else if (ConfigMap.class.isAssignableFrom(resourceType)) { - return (actual, primary, context) -> { - final var desired = dependentResource.desired(primary, context); - return Result.computed( - ResourceComparators.compareConfigMapData((ConfigMap) desired, (ConfigMap) actual), - desired); + return new Matcher<>() { + @Override + public Result match(R actualResource, P primary, Context

context) { + final var desired = dependentResource.desired(primary, context); + return Result.computed( + ResourceComparators.compareConfigMapData((ConfigMap) desired, + (ConfigMap) actualResource), + desired); + } + + @Override + public Result match(R actualResource, P primary, int index, Context

context) { + final var desired = dependentResource.desired(primary, index, context); + return Result.computed( + ResourceComparators.compareConfigMapData((ConfigMap) desired, + (ConfigMap) actualResource), + desired); + } }; } else { return new GenericKubernetesResourceMatcher(dependentResource); @@ -43,32 +68,18 @@ static Matcher matcherFor( @Override public Result match(R actualResource, P primary, Context

context) { - return match(dependentResource, actualResource, primary, context, false); + var desired = dependentResource.desired(primary, context); + return match(desired, actualResource, false); } - /** - * Determines whether the specified actual resource matches the desired state defined by the - * specified {@link KubernetesDependentResource} based on the observed state of the associated - * specified primary resource. - * - * @param dependentResource the {@link KubernetesDependentResource} implementation used to - * computed the desired state associated with the specified primary resource - * @param actualResource the observed dependent resource for which we want to determine whether it - * matches the desired state or not - * @param primary the primary resource from which we want to compute the desired state - * @param context the {@link Context} instance within which this method is called - * @param considerMetadata {@code true} to consider the metadata of the actual resource when - * determining if it matches the desired state, {@code false} if matching should occur only - * considering the spec of the resources - * @return a {@link io.javaoperatorsdk.operator.processing.dependent.Matcher.Result} object - * @param the type of resource we want to determine whether they match or not - * @param

the type of primary resources associated with the secondary resources we want to - * match - */ - public static Result match( - KubernetesDependentResource dependentResource, R actualResource, P primary, - Context

context, boolean considerMetadata) { - final var desired = dependentResource.desired(primary, context); + @Override + public Result match(R actualResource, P primary, int index, Context

context) { + var desired = dependentResource.desired(primary, index, context); + return match(desired, actualResource, false); + } + + public static Result match( + R desired, R actualResource, boolean considerMetadata) { if (considerMetadata) { final var desiredMetadata = desired.getMetadata(); final var actualMetadata = actualResource.getMetadata(); @@ -95,4 +106,30 @@ public static Result match( } return Result.computed(true, desired); } + + /** + * Determines whether the specified actual resource matches the desired state defined by the + * specified {@link KubernetesDependentResource} based on the observed state of the associated + * specified primary resource. + * + * @param dependentResource the {@link KubernetesDependentResource} implementation used to + * computed the desired state associated with the specified primary resource + * @param actualResource the observed dependent resource for which we want to determine whether it + * matches the desired state or not + * @param primary the primary resource from which we want to compute the desired state + * @param context the {@link Context} instance within which this method is called + * @param considerMetadata {@code true} to consider the metadata of the actual resource when + * determining if it matches the desired state, {@code false} if matching should occur only + * considering the spec of the resources + * @return a {@link io.javaoperatorsdk.operator.processing.dependent.Matcher.Result} object + * @param the type of resource we want to determine whether they match or not + * @param

the type of primary resources associated with the secondary resources we want to + * match + */ + public static Result match( + KubernetesDependentResource dependentResource, R actualResource, P primary, + Context

context, boolean considerMetadata) { + final var desired = dependentResource.desired(primary, context); + return match(desired, actualResource, considerMetadata); + } } 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 328a061e6b..3738a2e7d2 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 @@ -134,8 +134,21 @@ public Result match(R actualResource, P primary, Context

context) { return matcher.match(actualResource, primary, context); } + public Result match(R actualResource, P primary, int index, Context

context) { + return matcher.match(actualResource, primary, index, context); + } + public void delete(P primary, Context

context) { - getSecondaryResource(primary, context).ifPresent(r -> client.resource(r).delete()); + if (bulk) { + deleteBulkResourcesIfRequired(0, lastKnownBulkSize(), primary, context); + } else { + var resource = getSecondaryResource(primary, context); + resource.ifPresent(r -> client.resource(r).delete()); + } + } + + public void deleteBulkResourceWithIndex(P primary, R resource, int i, Context

context) { + client.resource(resource).delete(); } @SuppressWarnings("unchecked") @@ -149,9 +162,7 @@ protected Resource prepare(R desired, P primary, String actionName) { } else if (useDefaultAnnotationsToIdentifyPrimary()) { addDefaultSecondaryToPrimaryMapperAnnotations(desired, primary); } - Class targetClass = (Class) desired.getClass(); - return client.resources(targetClass).inNamespace(desired.getMetadata().getNamespace()) - .resource(desired); + return client.resource(desired).inNamespace(desired.getMetadata().getNamespace()); } @Override @@ -163,8 +174,10 @@ protected InformerEventSource createEventSource(EventSourceContext

cont onUpdateFilter = kubernetesDependentResourceConfig.onUpdateFilter(); onDeleteFilter = kubernetesDependentResourceConfig.onDeleteFilter(); genericFilter = kubernetesDependentResourceConfig.genericFilter(); - setResourceDiscriminator(kubernetesDependentResourceConfig.getResourceDiscriminator()); - + var discriminator = kubernetesDependentResourceConfig.getResourceDiscriminator(); + if (discriminator != null) { + setResourceDiscriminator(discriminator); + } configureWith(kubernetesDependentResourceConfig.labelSelector(), kubernetesDependentResourceConfig.namespaces(), !kubernetesDependentResourceConfig.wereNamespacesConfigured(), context); @@ -215,6 +228,11 @@ protected R desired(P primary, Context

context) { return super.desired(primary, context); } + @Override + protected R desired(P primary, int index, Context

context) { + return super.desired(primary, index, context); + } + private void prepareEventFiltering(R desired, ResourceID resourceID) { eventSource().prepareForCreateOrUpdateEventFiltering(resourceID, desired); } diff --git a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/kubernetes/KubernetesDependentResourceConfig.java b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/kubernetes/KubernetesDependentResourceConfig.java index e2a2c0f684..c7674dd1a7 100644 --- a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/kubernetes/KubernetesDependentResourceConfig.java +++ b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/kubernetes/KubernetesDependentResourceConfig.java @@ -75,7 +75,6 @@ public OnAddFilter onAddFilter() { return onAddFilter; } - public OnUpdateFilter onUpdateFilter() { return onUpdateFilter; } @@ -92,4 +91,10 @@ public GenericFilter genericFilter() { public ResourceDiscriminator getResourceDiscriminator() { return resourceDiscriminator; } + + public

KubernetesDependentResourceConfig setResourceDiscriminator( + ResourceDiscriminator resourceDiscriminator) { + this.resourceDiscriminator = resourceDiscriminator; + return this; + } } diff --git a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/workflow/WorkflowCleanupExecutor.java b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/workflow/WorkflowCleanupExecutor.java index 45541b91d6..ac08d2d874 100644 --- a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/workflow/WorkflowCleanupExecutor.java +++ b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/workflow/WorkflowCleanupExecutor.java @@ -125,7 +125,6 @@ public void run() { } } - private synchronized void handleDependentCleaned( DependentResourceNode dependentResourceNode) { var dependOns = dependentResourceNode.getDependsOn(); diff --git a/operator-framework-junit5/src/main/java/io/javaoperatorsdk/operator/junit/LocallyRunOperatorExtension.java b/operator-framework-junit5/src/main/java/io/javaoperatorsdk/operator/junit/LocallyRunOperatorExtension.java index e2f4234453..f7627a5555 100644 --- a/operator-framework-junit5/src/main/java/io/javaoperatorsdk/operator/junit/LocallyRunOperatorExtension.java +++ b/operator-framework-junit5/src/main/java/io/javaoperatorsdk/operator/junit/LocallyRunOperatorExtension.java @@ -134,7 +134,10 @@ protected void before(ExtensionContext context) { ref.controllerConfigurationOverrider.accept(oconfig); } - applyCrd(config.getResourceTypeName()); + // only try to apply a CRD for the reconciler if it is associated to a CR + if (CustomResource.class.isAssignableFrom(config.getResourceClass())) { + applyCrd(config.getResourceTypeName()); + } if (ref.reconciler instanceof KubernetesClientAware) { ((KubernetesClientAware) ref.reconciler).setKubernetesClient(kubernetesClient); @@ -151,6 +154,9 @@ protected void before(ExtensionContext context) { private void applyCrd(String resourceTypeName) { String path = "/META-INF/fabric8/" + resourceTypeName + "-v1.yml"; try (InputStream is = getClass().getResourceAsStream(path)) { + if (is == null) { + throw new IllegalStateException("Cannot find CRD at " + path); + } final var crd = getKubernetesClient().load(is); crd.createOrReplace(); Thread.sleep(CRD_READY_WAIT); // readiness is not applicable for CRD, just wait a little diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/bulkdependent/BulkDependentDeleterIT.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/bulkdependent/BulkDependentDeleterIT.java new file mode 100644 index 0000000000..a934bdd1f3 --- /dev/null +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/bulkdependent/BulkDependentDeleterIT.java @@ -0,0 +1,19 @@ +package io.javaoperatorsdk.operator.bulkdependent; + +import org.junit.jupiter.api.extension.RegisterExtension; + +import io.javaoperatorsdk.operator.junit.LocallyRunOperatorExtension; +import io.javaoperatorsdk.operator.sample.bulkdependent.ManagedDeleterBulkReconciler; + +public class BulkDependentDeleterIT extends BulkDependentTestBase { + + @RegisterExtension + LocallyRunOperatorExtension extension = + LocallyRunOperatorExtension.builder().withReconciler(new ManagedDeleterBulkReconciler()) + .build(); + + @Override + LocallyRunOperatorExtension extension() { + return extension; + } +} diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/bulkdependent/BulkDependentTestBase.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/bulkdependent/BulkDependentTestBase.java new file mode 100644 index 0000000000..605731623c --- /dev/null +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/bulkdependent/BulkDependentTestBase.java @@ -0,0 +1,114 @@ +package io.javaoperatorsdk.operator.bulkdependent; + +import java.time.Duration; + +import org.junit.jupiter.api.Test; + +import io.fabric8.kubernetes.api.model.ObjectMeta; +import io.javaoperatorsdk.operator.junit.LocallyRunOperatorExtension; +import io.javaoperatorsdk.operator.sample.bulkdependent.BulkDependentTestCustomResource; +import io.javaoperatorsdk.operator.sample.bulkdependent.BulkDependentTestSpec; +import io.javaoperatorsdk.operator.sample.bulkdependent.ConfigMapDeleterBulkDependentResource; + +import static io.javaoperatorsdk.operator.sample.bulkdependent.ConfigMapDeleterBulkDependentResource.LABEL_KEY; +import static io.javaoperatorsdk.operator.sample.bulkdependent.ConfigMapDeleterBulkDependentResource.LABEL_VALUE; +import static org.assertj.core.api.Assertions.assertThat; +import static org.awaitility.Awaitility.await; + +public abstract class BulkDependentTestBase { + + public static final String TEST_RESOURCE_NAME = "test"; + public static final int INITIAL_NUMBER_OF_CONFIG_MAPS = 3; + public static final String INITIAL_ADDITIONAL_DATA = "initialData"; + public static final String NEW_VERSION_OF_ADDITIONAL_DATA = "newVersionOfAdditionalData"; + + @Test + public void managesBulkConfigMaps() { + extension().create(testResource()); + assertNumberOfConfigMaps(3); + + updateSpecWithNumber(1); + assertNumberOfConfigMaps(1); + + updateSpecWithNumber(5); + assertNumberOfConfigMaps(5); + + extension().delete(testResource()); + assertNumberOfConfigMaps(0); + } + + @Test + public void updatesData() { + extension().create(testResource()); + assertNumberOfConfigMaps(3); + assertAdditionalDataOnConfigMaps(INITIAL_ADDITIONAL_DATA); + + updateSpecWithNewAdditionalData(NEW_VERSION_OF_ADDITIONAL_DATA); + assertAdditionalDataOnConfigMaps(NEW_VERSION_OF_ADDITIONAL_DATA); + } + + private void assertNumberOfConfigMaps(int n) { + // this test was failing with a lower timeout on GitHub, probably the garbage collection was + // slower there. + await().atMost(Duration.ofSeconds(30)) + .untilAsserted(() -> { + var cms = + extension().getKubernetesClient().configMaps().inNamespace(extension().getNamespace()) + .withLabel(LABEL_KEY, LABEL_VALUE) + .list().getItems(); + assertThat(cms).withFailMessage("Number of items is still: " + cms.size()) + .hasSize(n); + }); + } + + private void assertAdditionalDataOnConfigMaps(String expectedValue) { + await().atMost(Duration.ofSeconds(30)) + .untilAsserted(() -> { + var cms = + extension().getKubernetesClient().configMaps().inNamespace(extension().getNamespace()) + .withLabel(LABEL_KEY, LABEL_VALUE) + .list().getItems(); + cms.forEach(cm -> { + assertThat(cm.getData().get(ConfigMapDeleterBulkDependentResource.ADDITIONAL_DATA_KEY)) + .isEqualTo(expectedValue); + }); + }); + } + + public static BulkDependentTestCustomResource testResource() { + BulkDependentTestCustomResource cr = new BulkDependentTestCustomResource(); + cr.setMetadata(new ObjectMeta()); + cr.getMetadata().setName(TEST_RESOURCE_NAME); + cr.setSpec(new BulkDependentTestSpec()); + cr.getSpec().setNumberOfResources(INITIAL_NUMBER_OF_CONFIG_MAPS); + cr.getSpec().setAdditionalData(INITIAL_ADDITIONAL_DATA); + return cr; + } + + private void updateSpecWithNewAdditionalData(String data) { + var resource = testResource(); + resource.getSpec().setAdditionalData(data); + extension().replace(resource); + } + + public static void updateSpecWithNewAdditionalData(LocallyRunOperatorExtension extension, + String data) { + var resource = testResource(); + resource.getSpec().setAdditionalData(data); + extension.replace(resource); + } + + private void updateSpecWithNumber(int n) { + var resource = testResource(); + resource.getSpec().setNumberOfResources(n); + extension().replace(resource); + } + + public static void updateSpecWithNumber(LocallyRunOperatorExtension extension, int n) { + var resource = testResource(); + resource.getSpec().setNumberOfResources(n); + extension.replace(resource); + } + + abstract LocallyRunOperatorExtension extension(); +} diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/bulkdependent/BulkExternalDependentIT.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/bulkdependent/BulkExternalDependentIT.java new file mode 100644 index 0000000000..29f66e8205 --- /dev/null +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/bulkdependent/BulkExternalDependentIT.java @@ -0,0 +1,56 @@ +package io.javaoperatorsdk.operator.bulkdependent; + +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.RegisterExtension; + +import io.javaoperatorsdk.operator.junit.LocallyRunOperatorExtension; +import io.javaoperatorsdk.operator.sample.bulkdependent.external.ExternalBulkResourceReconciler; +import io.javaoperatorsdk.operator.sample.bulkdependent.external.ExternalServiceMock; + +import static io.javaoperatorsdk.operator.bulkdependent.BulkDependentTestBase.*; +import static org.assertj.core.api.Assertions.assertThat; +import static org.awaitility.Awaitility.await; + +class BulkExternalDependentIT { + + @RegisterExtension + LocallyRunOperatorExtension extension = + LocallyRunOperatorExtension.builder().withReconciler(new ExternalBulkResourceReconciler()) + .build(); + + ExternalServiceMock externalServiceMock = ExternalServiceMock.getInstance(); + + @Test + void managesExternalBulkResources() { + extension.create(testResource()); + assertResourceNumberAndData(3, INITIAL_ADDITIONAL_DATA); + + updateSpecWithNumber(extension, 1); + assertResourceNumberAndData(1, INITIAL_ADDITIONAL_DATA); + + updateSpecWithNumber(extension, 5); + assertResourceNumberAndData(5, INITIAL_ADDITIONAL_DATA); + + extension.delete(testResource()); + assertResourceNumberAndData(0, INITIAL_ADDITIONAL_DATA); + } + + + @Test + void handlesResourceUpdates() { + extension.create(testResource()); + assertResourceNumberAndData(3, INITIAL_ADDITIONAL_DATA); + + updateSpecWithNewAdditionalData(extension, NEW_VERSION_OF_ADDITIONAL_DATA); + assertResourceNumberAndData(3, NEW_VERSION_OF_ADDITIONAL_DATA); + } + + private void assertResourceNumberAndData(int n, String data) { + await().untilAsserted(() -> { + var resources = externalServiceMock.listResources(); + assertThat(resources).hasSize(n); + assertThat(resources).allMatch(r -> r.getData().equals(data)); + }); + } + +} diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/bulkdependent/ManagedBulkDependentIT.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/bulkdependent/ManagedBulkDependentIT.java new file mode 100644 index 0000000000..7f074ac8f5 --- /dev/null +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/bulkdependent/ManagedBulkDependentIT.java @@ -0,0 +1,20 @@ +package io.javaoperatorsdk.operator.bulkdependent; + +import org.junit.jupiter.api.extension.RegisterExtension; + +import io.javaoperatorsdk.operator.junit.LocallyRunOperatorExtension; +import io.javaoperatorsdk.operator.sample.bulkdependent.ManagedBulkDependentReconciler; + +class ManagedBulkDependentIT extends BulkDependentTestBase { + + @RegisterExtension + LocallyRunOperatorExtension extension = + LocallyRunOperatorExtension.builder().withReconciler(new ManagedBulkDependentReconciler()) + .build(); + + + @Override + LocallyRunOperatorExtension extension() { + return extension; + } +} diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/bulkdependent/StandaloneBulkDependentIT.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/bulkdependent/StandaloneBulkDependentIT.java new file mode 100644 index 0000000000..683cc1662b --- /dev/null +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/bulkdependent/StandaloneBulkDependentIT.java @@ -0,0 +1,19 @@ +package io.javaoperatorsdk.operator.bulkdependent; + +import org.junit.jupiter.api.extension.RegisterExtension; + +import io.javaoperatorsdk.operator.junit.LocallyRunOperatorExtension; +import io.javaoperatorsdk.operator.sample.bulkdependent.StandaloneBulkDependentReconciler; + +class StandaloneBulkDependentIT extends BulkDependentTestBase { + + @RegisterExtension + LocallyRunOperatorExtension extension = + LocallyRunOperatorExtension.builder().withReconciler(new StandaloneBulkDependentReconciler()) + .build(); + + @Override + LocallyRunOperatorExtension extension() { + return extension; + } +} diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/bulkdependent/BulkDependentTestCustomResource.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/bulkdependent/BulkDependentTestCustomResource.java new file mode 100644 index 0000000000..68e6297f8c --- /dev/null +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/bulkdependent/BulkDependentTestCustomResource.java @@ -0,0 +1,15 @@ +package io.javaoperatorsdk.operator.sample.bulkdependent; + +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("sbd") +public class BulkDependentTestCustomResource + extends CustomResource + implements Namespaced { +} diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/bulkdependent/BulkDependentTestSpec.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/bulkdependent/BulkDependentTestSpec.java new file mode 100644 index 0000000000..5266950b41 --- /dev/null +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/bulkdependent/BulkDependentTestSpec.java @@ -0,0 +1,25 @@ +package io.javaoperatorsdk.operator.sample.bulkdependent; + +public class BulkDependentTestSpec { + + private Integer numberOfResources; + private String additionalData; + + public Integer getNumberOfResources() { + return numberOfResources; + } + + public BulkDependentTestSpec setNumberOfResources(Integer numberOfResources) { + this.numberOfResources = numberOfResources; + return this; + } + + public BulkDependentTestSpec setAdditionalData(String additionalData) { + this.additionalData = additionalData; + return this; + } + + public String getAdditionalData() { + return additionalData; + } +} diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/bulkdependent/CRUDConfigMapBulkDependentResource.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/bulkdependent/CRUDConfigMapBulkDependentResource.java new file mode 100644 index 0000000000..83cec0bb69 --- /dev/null +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/bulkdependent/CRUDConfigMapBulkDependentResource.java @@ -0,0 +1,7 @@ +package io.javaoperatorsdk.operator.sample.bulkdependent; + +import io.javaoperatorsdk.operator.api.reconciler.dependent.GarbageCollected; + +public class CRUDConfigMapBulkDependentResource extends ConfigMapDeleterBulkDependentResource + implements GarbageCollected { +} diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/bulkdependent/ConfigMapDeleterBulkDependentResource.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/bulkdependent/ConfigMapDeleterBulkDependentResource.java new file mode 100644 index 0000000000..a7fbd9cb98 --- /dev/null +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/bulkdependent/ConfigMapDeleterBulkDependentResource.java @@ -0,0 +1,72 @@ +package io.javaoperatorsdk.operator.sample.bulkdependent; + +import java.util.Map; +import java.util.Optional; +import java.util.stream.Collectors; + +import io.fabric8.kubernetes.api.model.ConfigMap; +import io.fabric8.kubernetes.api.model.ObjectMetaBuilder; +import io.javaoperatorsdk.operator.api.reconciler.Context; +import io.javaoperatorsdk.operator.api.reconciler.ResourceDiscriminator; +import io.javaoperatorsdk.operator.api.reconciler.dependent.Deleter; +import io.javaoperatorsdk.operator.processing.dependent.BulkDependentResource; +import io.javaoperatorsdk.operator.processing.dependent.Creator; +import io.javaoperatorsdk.operator.processing.dependent.Updater; +import io.javaoperatorsdk.operator.processing.dependent.kubernetes.KubernetesDependentResource; + +/** + * Not using CRUDKubernetesDependentResource so the delete functionality can be tested. + */ +public class ConfigMapDeleterBulkDependentResource + extends + KubernetesDependentResource + implements Creator, + Updater, + Deleter, + BulkDependentResource { + + public static final String LABEL_KEY = "bulk"; + public static final String LABEL_VALUE = "true"; + public static final String ADDITIONAL_DATA_KEY = "additionalData"; + + public ConfigMapDeleterBulkDependentResource() { + super(ConfigMap.class); + } + + @Override + public ConfigMap desired(BulkDependentTestCustomResource primary, + int index, Context context) { + ConfigMap configMap = new ConfigMap(); + configMap.setMetadata(new ObjectMetaBuilder() + .withName(primary.getMetadata().getName() + "-" + index) + .withNamespace(primary.getMetadata().getNamespace()) + .withLabels(Map.of(LABEL_KEY, LABEL_VALUE)) + .build()); + configMap.setData( + Map.of("number", "" + index, ADDITIONAL_DATA_KEY, primary.getSpec().getAdditionalData())); + return configMap; + } + + @Override + public int count(BulkDependentTestCustomResource primary, + Context context) { + return primary.getSpec().getNumberOfResources(); + } + + @Override + public ResourceDiscriminator getResourceDiscriminator( + int index) { + return (resource, primary, context) -> { + var resources = context.getSecondaryResources(resource).stream() + .filter(r -> r.getMetadata().getName().endsWith("-" + index)) + .collect(Collectors.toList()); + if (resources.isEmpty()) { + return Optional.empty(); + } else if (resources.size() > 1) { + throw new IllegalStateException("More than one resource found for index:" + index); + } else { + return Optional.of(resources.get(0)); + } + }; + } +} diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/bulkdependent/ManagedBulkDependentReconciler.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/bulkdependent/ManagedBulkDependentReconciler.java new file mode 100644 index 0000000000..3b2acd942e --- /dev/null +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/bulkdependent/ManagedBulkDependentReconciler.java @@ -0,0 +1,25 @@ +package io.javaoperatorsdk.operator.sample.bulkdependent; + +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 = CRUDConfigMapBulkDependentResource.class)) +public class ManagedBulkDependentReconciler + implements Reconciler { + + private final AtomicInteger numberOfExecutions = new AtomicInteger(0); + + @Override + public UpdateControl reconcile( + BulkDependentTestCustomResource resource, + Context context) throws Exception { + + numberOfExecutions.addAndGet(1); + return UpdateControl.noUpdate(); + } +} diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/bulkdependent/ManagedDeleterBulkReconciler.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/bulkdependent/ManagedDeleterBulkReconciler.java new file mode 100644 index 0000000000..e759bdd200 --- /dev/null +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/bulkdependent/ManagedDeleterBulkReconciler.java @@ -0,0 +1,20 @@ +package io.javaoperatorsdk.operator.sample.bulkdependent; + +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 = ConfigMapDeleterBulkDependentResource.class)) +public class ManagedDeleterBulkReconciler implements Reconciler { + @Override + public UpdateControl reconcile( + BulkDependentTestCustomResource resource, + Context context) + throws Exception { + + return UpdateControl.noUpdate(); + } +} diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/bulkdependent/StandaloneBulkDependentReconciler.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/bulkdependent/StandaloneBulkDependentReconciler.java new file mode 100644 index 0000000000..4033583340 --- /dev/null +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/bulkdependent/StandaloneBulkDependentReconciler.java @@ -0,0 +1,58 @@ +package io.javaoperatorsdk.operator.sample.bulkdependent; + +import java.util.Map; +import java.util.concurrent.atomic.AtomicInteger; + +import io.fabric8.kubernetes.client.KubernetesClient; +import io.javaoperatorsdk.operator.api.reconciler.*; +import io.javaoperatorsdk.operator.junit.KubernetesClientAware; +import io.javaoperatorsdk.operator.processing.event.source.EventSource; +import io.javaoperatorsdk.operator.support.TestExecutionInfoProvider; + +@ControllerConfiguration +public class StandaloneBulkDependentReconciler + implements Reconciler, TestExecutionInfoProvider, + EventSourceInitializer, KubernetesClientAware { + + private final AtomicInteger numberOfExecutions = new AtomicInteger(0); + + private ConfigMapDeleterBulkDependentResource dependent; + private KubernetesClient kubernetesClient; + + public StandaloneBulkDependentReconciler() { + dependent = new CRUDConfigMapBulkDependentResource(); + } + + @Override + public UpdateControl reconcile( + BulkDependentTestCustomResource resource, + Context context) { + numberOfExecutions.addAndGet(1); + + dependent.reconcile(resource, context); + + return UpdateControl.noUpdate(); + } + + public int getNumberOfExecutions() { + return numberOfExecutions.get(); + } + + @Override + public Map prepareEventSources( + EventSourceContext context) { + return EventSourceInitializer + .nameEventSources(dependent.initEventSource(context)); + } + + @Override + public KubernetesClient getKubernetesClient() { + return kubernetesClient; + } + + @Override + public void setKubernetesClient(KubernetesClient kubernetesClient) { + this.kubernetesClient = kubernetesClient; + dependent.setKubernetesClient(kubernetesClient); + } +} diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/bulkdependent/external/ExternalBulkDependentResource.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/bulkdependent/external/ExternalBulkDependentResource.java new file mode 100644 index 0000000000..110626a923 --- /dev/null +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/bulkdependent/external/ExternalBulkDependentResource.java @@ -0,0 +1,101 @@ +package io.javaoperatorsdk.operator.sample.bulkdependent.external; + +import java.util.*; +import java.util.stream.Collectors; + +import io.javaoperatorsdk.operator.api.reconciler.Context; +import io.javaoperatorsdk.operator.api.reconciler.ResourceDiscriminator; +import io.javaoperatorsdk.operator.processing.dependent.*; +import io.javaoperatorsdk.operator.processing.dependent.external.PollingDependentResource; +import io.javaoperatorsdk.operator.processing.event.ResourceID; +import io.javaoperatorsdk.operator.sample.bulkdependent.BulkDependentTestCustomResource; + +public class ExternalBulkDependentResource + extends PollingDependentResource + implements BulkDependentResource, + BulkUpdater { + + public static final String EXTERNAL_RESOURCE_NAME_DELIMITER = "#"; + + private final ExternalServiceMock externalServiceMock = ExternalServiceMock.getInstance(); + + public ExternalBulkDependentResource() { + super(ExternalResource.class, ExternalResource::getId); + } + + @Override + public Map> fetchResources() { + Map> result = new HashMap<>(); + var resources = externalServiceMock.listResources(); + resources.forEach(er -> { + var resourceID = toResourceID(er); + result.putIfAbsent(resourceID, new HashSet<>()); + result.get(resourceID).add(er); + }); + return result; + } + + @Override + public void delete(BulkDependentTestCustomResource primary, + Context context) { + deleteBulkResourcesIfRequired(0, lastKnownBulkSize(), primary, context); + } + + @Override + public int count(BulkDependentTestCustomResource primary, + Context context) { + return primary.getSpec().getNumberOfResources(); + } + + @Override + public void deleteBulkResourceWithIndex(BulkDependentTestCustomResource primary, + ExternalResource resource, int i, Context context) { + externalServiceMock.delete(resource.getId()); + } + + @Override + public ExternalResource desired(BulkDependentTestCustomResource primary, int index, + Context context) { + return new ExternalResource(toExternalResourceId(primary, index), + primary.getSpec().getAdditionalData()); + } + + @Override + public ExternalResource create(ExternalResource desired, BulkDependentTestCustomResource primary, + Context context) { + return externalServiceMock.create(desired); + } + + @Override + public ExternalResource update(ExternalResource actual, ExternalResource desired, + BulkDependentTestCustomResource primary, Context context) { + return externalServiceMock.update(desired); + } + + @Override + public Matcher.Result match(ExternalResource actualResource, + BulkDependentTestCustomResource primary, + int index, Context context) { + var desired = desired(primary, index, context); + return Matcher.Result.computed(desired.equals(actualResource), desired); + } + + private static String toExternalResourceId(BulkDependentTestCustomResource primary, int i) { + return primary.getMetadata().getName() + EXTERNAL_RESOURCE_NAME_DELIMITER + + primary.getMetadata().getNamespace() + + EXTERNAL_RESOURCE_NAME_DELIMITER + i; + } + + private ResourceID toResourceID(ExternalResource externalResource) { + var parts = externalResource.getId().split(EXTERNAL_RESOURCE_NAME_DELIMITER); + return new ResourceID(parts[0], parts[1]); + } + + @Override + public ResourceDiscriminator getResourceDiscriminator( + int index) { + return (resource, primary, context) -> context.getSecondaryResources(resource).stream() + .filter(r -> r.getId().endsWith(EXTERNAL_RESOURCE_NAME_DELIMITER + index)) + .collect(Collectors.toList()).stream().findFirst(); + } +} diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/bulkdependent/external/ExternalBulkResourceReconciler.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/bulkdependent/external/ExternalBulkResourceReconciler.java new file mode 100644 index 0000000000..2543422d74 --- /dev/null +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/bulkdependent/external/ExternalBulkResourceReconciler.java @@ -0,0 +1,19 @@ +package io.javaoperatorsdk.operator.sample.bulkdependent.external; + +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; +import io.javaoperatorsdk.operator.sample.bulkdependent.BulkDependentTestCustomResource; + +@ControllerConfiguration(dependents = @Dependent(type = ExternalBulkDependentResource.class)) +public class ExternalBulkResourceReconciler implements Reconciler { + + @Override + public UpdateControl reconcile( + BulkDependentTestCustomResource resource, Context context) + throws Exception { + return UpdateControl.noUpdate(); + } +} diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/bulkdependent/external/ExternalResource.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/bulkdependent/external/ExternalResource.java new file mode 100644 index 0000000000..935fd99e47 --- /dev/null +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/bulkdependent/external/ExternalResource.java @@ -0,0 +1,37 @@ +package io.javaoperatorsdk.operator.sample.bulkdependent.external; + +import java.util.Objects; + +public class ExternalResource { + + private String id; + private String data; + + public ExternalResource(String id, String data) { + this.id = id; + this.data = data; + } + + public String getId() { + return id; + } + + public String getData() { + return data; + } + + @Override + public boolean equals(Object o) { + if (this == o) + return true; + if (o == null || getClass() != o.getClass()) + return false; + ExternalResource that = (ExternalResource) o; + return Objects.equals(id, that.id) && Objects.equals(data, that.data); + } + + @Override + public int hashCode() { + return Objects.hash(id, data); + } +} diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/bulkdependent/external/ExternalServiceMock.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/bulkdependent/external/ExternalServiceMock.java new file mode 100644 index 0000000000..e73062ccf2 --- /dev/null +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/bulkdependent/external/ExternalServiceMock.java @@ -0,0 +1,39 @@ +package io.javaoperatorsdk.operator.sample.bulkdependent.external; + +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.concurrent.ConcurrentHashMap; + +public class ExternalServiceMock { + + private static ExternalServiceMock serviceMock = new ExternalServiceMock(); + + private Map resourceMap = new ConcurrentHashMap<>(); + + public ExternalResource create(ExternalResource externalResource) { + resourceMap.put(externalResource.getId(), externalResource); + return externalResource; + } + + public Optional read(String id) { + return Optional.ofNullable(resourceMap.get(id)); + } + + public ExternalResource update(ExternalResource externalResource) { + return resourceMap.put(externalResource.getId(), externalResource); + } + + public Optional delete(String id) { + return Optional.ofNullable(resourceMap.remove(id)); + } + + public List listResources() { + return new ArrayList<>(resourceMap.values()); + } + + public static ExternalServiceMock getInstance() { + return serviceMock; + } +} 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 49f5ee64c1..0994e6b9b0 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 @@ -29,7 +29,6 @@ public class MultipleDependentResourceReconciler public MultipleDependentResourceReconciler() { firstDependentResourceConfigMap = new MultipleDependentResourceConfigMap(FIRST_CONFIG_MAP_ID); - secondDependentResourceConfigMap = new MultipleDependentResourceConfigMap(SECOND_CONFIG_MAP_ID); firstDependentResourceConfigMap From 66882bc6b58114cde03fd9e42b45ad89d3818324 Mon Sep 17 00:00:00 2001 From: csviri Date: Thu, 29 Sep 2022 17:08:47 +0200 Subject: [PATCH 56/67] fixes after merge --- .../api/reconciler/dependent/DependentResource.java | 6 ------ .../dependent/workflow/WorkflowCleanupExecutor.java | 2 -- .../dependent/workflow/WorkflowReconcileExecutor.java | 2 -- .../processing/dependent/AbstractDependentResourceTest.java | 2 +- 4 files changed, 1 insertion(+), 11 deletions(-) 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 43d9027984..1e92b7a5b3 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 @@ -56,12 +56,6 @@ default Optional> eventSource( */ void doNotProvideEventSource(); - /** - * Retrieves the resource type associated with this DependentResource - * - * @return the resource type associated with this DependentResource - */ - Class resourceType(); default Optional getSecondaryResource(P primary, Context

context) { return Optional.empty(); diff --git a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/workflow/WorkflowCleanupExecutor.java b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/workflow/WorkflowCleanupExecutor.java index 1a69f5979e..45541b91d6 100644 --- a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/workflow/WorkflowCleanupExecutor.java +++ b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/workflow/WorkflowCleanupExecutor.java @@ -13,10 +13,8 @@ import io.fabric8.kubernetes.api.model.HasMetadata; import io.javaoperatorsdk.operator.api.reconciler.Context; -import io.javaoperatorsdk.operator.api.reconciler.ResourceDiscriminator; import io.javaoperatorsdk.operator.api.reconciler.dependent.Deleter; import io.javaoperatorsdk.operator.api.reconciler.dependent.GarbageCollected; -import io.javaoperatorsdk.operator.processing.dependent.AbstractDependentResource; @SuppressWarnings("rawtypes") public class WorkflowCleanupExecutor

{ diff --git a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/workflow/WorkflowReconcileExecutor.java b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/workflow/WorkflowReconcileExecutor.java index 906651ffac..33ecd12f77 100644 --- a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/workflow/WorkflowReconcileExecutor.java +++ b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/workflow/WorkflowReconcileExecutor.java @@ -13,12 +13,10 @@ import io.fabric8.kubernetes.api.model.HasMetadata; import io.javaoperatorsdk.operator.api.reconciler.Context; -import io.javaoperatorsdk.operator.api.reconciler.ResourceDiscriminator; import io.javaoperatorsdk.operator.api.reconciler.dependent.Deleter; import io.javaoperatorsdk.operator.api.reconciler.dependent.DependentResource; import io.javaoperatorsdk.operator.api.reconciler.dependent.GarbageCollected; import io.javaoperatorsdk.operator.api.reconciler.dependent.ReconcileResult; -import io.javaoperatorsdk.operator.processing.dependent.AbstractDependentResource; import io.javaoperatorsdk.operator.processing.event.ResourceID; @SuppressWarnings({"rawtypes", "unchecked"}) diff --git a/operator-framework-core/src/test/java/io/javaoperatorsdk/operator/processing/dependent/AbstractDependentResourceTest.java b/operator-framework-core/src/test/java/io/javaoperatorsdk/operator/processing/dependent/AbstractDependentResourceTest.java index 190e45adfb..0fd85c5afc 100644 --- a/operator-framework-core/src/test/java/io/javaoperatorsdk/operator/processing/dependent/AbstractDependentResourceTest.java +++ b/operator-framework-core/src/test/java/io/javaoperatorsdk/operator/processing/dependent/AbstractDependentResourceTest.java @@ -87,7 +87,7 @@ protected ResourceEventSource provideEventSource( } @Override - protected Optional getSecondaryResource(TestCustomResource primary, + public Optional getSecondaryResource(TestCustomResource primary, Context context) { return Optional.ofNullable(secondary); } From 4792f60a6c916d073733538e58f32454ab085bf7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Attila=20M=C3=A9sz=C3=A1ros?= Date: Wed, 28 Sep 2022 10:43:44 +0200 Subject: [PATCH 57/67] feat: decouple event source from cache + list discriminator (#1378) --- .../AnnotationControllerConfiguration.java | 29 +++-- .../operator/api/reconciler/Context.java | 15 ++- .../api/reconciler/DefaultContext.java | 12 ++ .../api/reconciler/ResourceDiscriminator.java | 11 ++ .../ResourceIDMatcherDiscriminator.java | 25 ++++ .../dependent/DependentResource.java | 16 ++- .../operator/processing/ResourceOwner.java | 26 ---- .../dependent/AbstractDependentResource.java | 21 ++- ...actEventSourceHolderDependentResource.java | 9 ++ .../AbstractCachingDependentResource.java | 29 ----- .../AbstractPollingDependentResource.java | 6 +- .../AbstractSimpleDependentResource.java | 2 +- .../kubernetes/KubernetesDependent.java | 4 + .../KubernetesDependentResource.java | 34 ++--- .../KubernetesDependentResourceConfig.java | 22 ++-- .../workflow/DependentResourceNode.java | 5 + .../workflow/WorkflowCleanupExecutor.java | 11 +- .../workflow/WorkflowReconcileExecutor.java | 12 +- .../processing/event/EventProcessor.java | 11 +- .../processing/event/EventSourceManager.java | 19 ++- .../event/EventSourceRetriever.java | 18 +++ .../processing/event/EventSources.java | 5 +- .../operator/processing/event/ResourceID.java | 6 + .../event/source/ResourceEventSource.java | 11 +- .../informer/ManagedInformerEventSource.java | 4 +- .../ControllerConfigurationOverriderTest.java | 5 - .../AbstractDependentResourceTest.java | 4 +- .../dependent/EmptyTestDependentResource.java | 7 - .../AbstractSimpleDependentResourceTest.java | 4 +- .../AbstractWorkflowExecutorTest.java | 11 -- .../workflow/WorkflowCleanupExecutorTest.java | 11 +- .../WorkflowReconcileExecutorTest.java | 46 ++++--- .../operator/IndexDiscriminatorIT.java | 77 +++++++++++ .../IndexDiscriminator.java | 41 ++++++ .../IndexDiscriminatorTestCustomResource.java | 16 +++ .../IndexDiscriminatorTestDRConfigMap.java | 38 ++++++ .../IndexDiscriminatorTestReconciler.java | 120 ++++++++++++++++++ .../IndexDiscriminatorTestSpec.java | 15 +++ .../IndexDiscriminatorTestStatus.java | 5 + .../MultipleDependentResourceConfigMap.java | 1 - .../MultipleDependentResourceReconciler.java | 44 ++++--- .../ConfigMapDependentResource1.java | 12 +- .../ConfigMapDependentResource2.java | 12 +- .../StandaloneDependentTestReconciler.java | 2 +- .../WebPageDependentsWorkflowReconciler.java | 4 +- ...WebPageStandaloneDependentsReconciler.java | 2 +- 46 files changed, 620 insertions(+), 220 deletions(-) create mode 100644 operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/reconciler/ResourceDiscriminator.java create mode 100644 operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/reconciler/ResourceIDMatcherDiscriminator.java delete mode 100644 operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/ResourceOwner.java delete mode 100644 operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/external/AbstractCachingDependentResource.java create mode 100644 operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/EventSourceRetriever.java create mode 100644 operator-framework/src/test/java/io/javaoperatorsdk/operator/IndexDiscriminatorIT.java create mode 100644 operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/indexdiscriminator/IndexDiscriminator.java create mode 100644 operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/indexdiscriminator/IndexDiscriminatorTestCustomResource.java create mode 100644 operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/indexdiscriminator/IndexDiscriminatorTestDRConfigMap.java create mode 100644 operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/indexdiscriminator/IndexDiscriminatorTestReconciler.java create mode 100644 operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/indexdiscriminator/IndexDiscriminatorTestSpec.java create mode 100644 operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/indexdiscriminator/IndexDiscriminatorTestStatus.java diff --git a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/config/AnnotationControllerConfiguration.java b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/config/AnnotationControllerConfiguration.java index b5e3fffcf0..dbd09a32cc 100644 --- a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/config/AnnotationControllerConfiguration.java +++ b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/config/AnnotationControllerConfiguration.java @@ -17,9 +17,8 @@ import io.javaoperatorsdk.operator.OperatorException; import io.javaoperatorsdk.operator.ReconcilerUtils; import io.javaoperatorsdk.operator.api.config.dependent.DependentResourceSpec; -import io.javaoperatorsdk.operator.api.reconciler.Constants; +import io.javaoperatorsdk.operator.api.reconciler.*; import io.javaoperatorsdk.operator.api.reconciler.ControllerConfiguration; -import io.javaoperatorsdk.operator.api.reconciler.Reconciler; import io.javaoperatorsdk.operator.api.reconciler.dependent.Dependent; import io.javaoperatorsdk.operator.api.reconciler.dependent.DependentResource; import io.javaoperatorsdk.operator.processing.dependent.kubernetes.KubernetesDependent; @@ -54,9 +53,8 @@ public AnnotationControllerConfiguration(Reconciler

reconciler) { this.reconciler = reconciler; this.annotation = reconciler.getClass().getAnnotation(ControllerConfiguration.class); if (annotation == null) { - throw new OperatorException( - "Missing mandatory @" + ControllerConfiguration.class.getSimpleName() + - " annotation for reconciler: " + reconciler); + throw new OperatorException("Missing mandatory @" + CONTROLLER_CONFIG_ANNOTATION + + " annotation for reconciler: " + reconciler); } } @@ -247,9 +245,9 @@ public List getDependentResources() { final var context = "DependentResource of type '" + dependentType.getName() + "'"; spec = new DependentResourceSpec(dependentType, config, name, Set.of(dependent.dependsOn()), - instantiateConditionIfNotDefault(dependent.readyPostcondition(), context), - instantiateConditionIfNotDefault(dependent.reconcilePrecondition(), context), - instantiateConditionIfNotDefault(dependent.deletePostcondition(), context)); + instantiateIfNotDefault(dependent.readyPostcondition(), Condition.class, context), + instantiateIfNotDefault(dependent.reconcilePrecondition(), Condition.class, context), + instantiateIfNotDefault(dependent.deletePostcondition(), Condition.class, context)); specsMap.put(name, spec); } @@ -258,10 +256,10 @@ public List getDependentResources() { return specs; } - protected Condition instantiateConditionIfNotDefault(Class condition, + protected T instantiateIfNotDefault(Class toInstantiate, Class defaultClass, String context) { - if (condition != Condition.class) { - return instantiateAndConfigureIfNeeded(condition, Condition.class, context); + if (!defaultClass.equals(toInstantiate)) { + return instantiateAndConfigureIfNeeded(toInstantiate, defaultClass, context); } return null; } @@ -287,6 +285,7 @@ private Object createKubernetesResourceConfig(Class OnUpdateFilter onUpdateFilter = null; OnDeleteFilter onDeleteFilter = null; GenericFilter genericFilter = null; + ResourceDiscriminator resourceDiscriminator = null; if (kubeDependent != null) { if (!Arrays.equals(KubernetesDependent.DEFAULT_NAMESPACES, kubeDependent.namespaces())) { @@ -297,7 +296,6 @@ private Object createKubernetesResourceConfig(Class final var fromAnnotation = kubeDependent.labelSelector(); labelSelector = Constants.NO_VALUE_SET.equals(fromAnnotation) ? null : fromAnnotation; - final var context = KUBE_DEPENDENT_NAME + " annotation on " + dependentType.getName() + " DependentResource"; onAddFilter = createFilter(kubeDependent.onAddFilter(), OnAddFilter.class, context) @@ -311,10 +309,15 @@ private Object createKubernetesResourceConfig(Class genericFilter = createFilter(kubeDependent.genericFilter(), GenericFilter.class, context) .orElse(null); + + resourceDiscriminator = + instantiateIfNotDefault(kubeDependent.resourceDiscriminator(), + ResourceDiscriminator.class, context); } config = - new KubernetesDependentResourceConfig(namespaces, labelSelector, configuredNS, onAddFilter, + new KubernetesDependentResourceConfig(namespaces, labelSelector, configuredNS, + resourceDiscriminator, onAddFilter, onUpdateFilter, onDeleteFilter, genericFilter); return config; diff --git a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/reconciler/Context.java b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/reconciler/Context.java index 845810c8a1..2e4fb98e6f 100644 --- a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/reconciler/Context.java +++ b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/reconciler/Context.java @@ -6,20 +6,27 @@ import io.fabric8.kubernetes.api.model.HasMetadata; import io.javaoperatorsdk.operator.api.config.ControllerConfiguration; import io.javaoperatorsdk.operator.api.reconciler.dependent.managed.ManagedDependentResourceContext; +import io.javaoperatorsdk.operator.processing.event.EventSourceRetriever; public interface Context

{ Optional getRetryInfo(); - default Optional getSecondaryResource(Class expectedType) { - return getSecondaryResource(expectedType, null); + default Optional getSecondaryResource(Class expectedType) { + return getSecondaryResource(expectedType, (String) null); } - Set getSecondaryResources(Class expectedType); + Set getSecondaryResources(Class expectedType); - Optional getSecondaryResource(Class expectedType, String eventSourceName); + @Deprecated(forRemoval = true) + Optional getSecondaryResource(Class expectedType, String eventSourceName); + + Optional getSecondaryResource(Class expectedType, + ResourceDiscriminator discriminator); ControllerConfiguration

getControllerConfiguration(); ManagedDependentResourceContext managedDependentResourceContext(); + + EventSourceRetriever

eventSourceRetriever(); } diff --git a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/reconciler/DefaultContext.java b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/reconciler/DefaultContext.java index afb37a8c53..cb7f4ae63b 100644 --- a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/reconciler/DefaultContext.java +++ b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/reconciler/DefaultContext.java @@ -9,6 +9,7 @@ import io.javaoperatorsdk.operator.api.reconciler.dependent.managed.DefaultManagedDependentResourceContext; import io.javaoperatorsdk.operator.api.reconciler.dependent.managed.ManagedDependentResourceContext; import io.javaoperatorsdk.operator.processing.Controller; +import io.javaoperatorsdk.operator.processing.event.EventSourceRetriever; public class DefaultContext

implements Context

{ @@ -47,6 +48,12 @@ public Optional getSecondaryResource(Class expectedType, String eventS .getSecondaryResource(primaryResource); } + @Override + public Optional getSecondaryResource(Class expectedType, + ResourceDiscriminator discriminator) { + return discriminator.distinguish(expectedType, primaryResource, this); + } + @Override public ControllerConfiguration

getControllerConfiguration() { return controllerConfiguration; @@ -57,6 +64,11 @@ public ManagedDependentResourceContext managedDependentResourceContext() { return defaultManagedDependentResourceContext; } + @Override + public EventSourceRetriever

eventSourceRetriever() { + return controller.getEventSourceManager(); + } + public DefaultContext

setRetryInfo(RetryInfo retryInfo) { this.retryInfo = retryInfo; return this; diff --git a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/reconciler/ResourceDiscriminator.java b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/reconciler/ResourceDiscriminator.java new file mode 100644 index 0000000000..072e7d8078 --- /dev/null +++ b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/reconciler/ResourceDiscriminator.java @@ -0,0 +1,11 @@ +package io.javaoperatorsdk.operator.api.reconciler; + +import java.util.Optional; + +import io.fabric8.kubernetes.api.model.HasMetadata; + +public interface ResourceDiscriminator { + + Optional distinguish(Class resource, P primary, Context

context); + +} diff --git a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/reconciler/ResourceIDMatcherDiscriminator.java b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/reconciler/ResourceIDMatcherDiscriminator.java new file mode 100644 index 0000000000..f28633252a --- /dev/null +++ b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/reconciler/ResourceIDMatcherDiscriminator.java @@ -0,0 +1,25 @@ +package io.javaoperatorsdk.operator.api.reconciler; + +import java.util.Optional; +import java.util.function.Function; + +import io.fabric8.kubernetes.api.model.HasMetadata; +import io.javaoperatorsdk.operator.processing.event.ResourceID; + +public class ResourceIDMatcherDiscriminator + implements ResourceDiscriminator { + + private final Function mapper; + + public ResourceIDMatcherDiscriminator(Function mapper) { + this.mapper = mapper; + } + + @Override + public Optional distinguish(Class resource, P primary, Context

context) { + var resourceID = mapper.apply(primary); + return context.getSecondaryResources(resource).stream() + .filter(resourceID::isSameResource) + .findFirst(); + } +} 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 0923d19473..8d31778488 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 @@ -1,8 +1,9 @@ package io.javaoperatorsdk.operator.api.reconciler.dependent; +import java.util.Optional; + import io.fabric8.kubernetes.api.model.HasMetadata; import io.javaoperatorsdk.operator.api.reconciler.Context; -import io.javaoperatorsdk.operator.processing.ResourceOwner; /** * An interface to implement and provide dependent resource support. @@ -10,7 +11,7 @@ * @param the dependent resource type * @param

the associated primary resource type */ -public interface DependentResource extends ResourceOwner { +public interface DependentResource { /** * Reconciles the dependent resource given the desired primary state @@ -21,6 +22,17 @@ public interface DependentResource extends ResourceOwn */ ReconcileResult reconcile(P primary, Context

context); + /** + * Retrieves the resource type associated with this DependentResource + * + * @return the resource type associated with this DependentResource + */ + Class resourceType(); + + default Optional getSecondaryResource(P primary, Context

context) { + return Optional.empty(); + } + /** * Computes a default name for the specified DependentResource class * diff --git a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/ResourceOwner.java b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/ResourceOwner.java deleted file mode 100644 index f9c02a8a33..0000000000 --- a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/ResourceOwner.java +++ /dev/null @@ -1,26 +0,0 @@ -package io.javaoperatorsdk.operator.processing; - -import java.util.Optional; - -import io.fabric8.kubernetes.api.model.HasMetadata; - -public interface ResourceOwner { - - /** - * Retrieves the resource type associated with this ResourceOwner - * - * @return the resource type associated with this ResourceOwner - */ - Class resourceType(); - - /** - * Retrieves the resource associated with the specified primary one, returning the actual state of - * the resource. Typically, this state might come from a local cache, updated after - * reconciliation. - * - * @param primary the primary resource for which we want to retrieve the secondary resource - * @return an {@link Optional} containing the secondary resource or {@link Optional#empty()} if it - * doesn't exist - */ - Optional getSecondaryResource(P primary); -} 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 5dbdba9358..1abfb3df4b 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,11 +1,14 @@ package io.javaoperatorsdk.operator.processing.dependent; +import java.util.Optional; + import org.slf4j.Logger; import org.slf4j.LoggerFactory; import io.fabric8.kubernetes.api.model.HasMetadata; import io.javaoperatorsdk.operator.api.reconciler.Context; import io.javaoperatorsdk.operator.api.reconciler.Ignore; +import io.javaoperatorsdk.operator.api.reconciler.ResourceDiscriminator; import io.javaoperatorsdk.operator.api.reconciler.dependent.DependentResource; import io.javaoperatorsdk.operator.api.reconciler.dependent.ReconcileResult; import io.javaoperatorsdk.operator.processing.event.ResourceID; @@ -21,6 +24,8 @@ public abstract class AbstractDependentResource protected Creator creator; protected Updater updater; + private ResourceDiscriminator resourceDiscriminator; + @SuppressWarnings("unchecked") public AbstractDependentResource() { creator = creatable ? (Creator) this : null; @@ -29,7 +34,7 @@ public AbstractDependentResource() { @Override public ReconcileResult reconcile(P primary, Context

context) { - var maybeActual = getSecondaryResource(primary); + Optional maybeActual = getSecondaryResource(primary, context); if (creatable || updatable) { if (maybeActual.isEmpty()) { if (creatable) { @@ -62,6 +67,11 @@ public ReconcileResult reconcile(P primary, Context

context) { return ReconcileResult.noOperation(maybeActual.orElse(null)); } + public Optional getSecondaryResource(P primary, Context

context) { + return resourceDiscriminator == null ? context.getSecondaryResource(resourceType()) + : resourceDiscriminator.distinguish(resourceType(), primary, context); + } + private void throwIfNull(R desired, P primary, String descriptor) { if (desired == null) { throw new DependentResourceException( @@ -118,4 +128,13 @@ protected R desired(P primary, Context

context) { throw new IllegalStateException( "desired method must be implemented if this DependentResource can be created and/or updated"); } + + public void setResourceDiscriminator( + ResourceDiscriminator resourceDiscriminator) { + this.resourceDiscriminator = resourceDiscriminator; + } + + public ResourceDiscriminator getResourceDiscriminator() { + return resourceDiscriminator; + } } diff --git a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/AbstractEventSourceHolderDependentResource.java b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/AbstractEventSourceHolderDependentResource.java index 0ceba16826..be0db98393 100644 --- a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/AbstractEventSourceHolderDependentResource.java +++ b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/AbstractEventSourceHolderDependentResource.java @@ -19,12 +19,16 @@ public abstract class AbstractEventSourceHolderDependentResource { private T eventSource; + private final Class resourceType; private boolean isCacheFillerEventSource; protected OnAddFilter onAddFilter; protected OnUpdateFilter onUpdateFilter; protected OnDeleteFilter onDeleteFilter; protected GenericFilter genericFilter; + protected AbstractEventSourceHolderDependentResource(Class resourceType) { + this.resourceType = resourceType; + } public EventSource initEventSource(EventSourceContext

context) { // some sub-classes (e.g. KubernetesDependentResource) can have their event source created @@ -42,6 +46,11 @@ public EventSource initEventSource(EventSourceContext

context) { return eventSource; } + @Override + public Class resourceType() { + return resourceType; + } + protected abstract T createEventSource(EventSourceContext

context); protected void setEventSource(T eventSource) { diff --git a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/external/AbstractCachingDependentResource.java b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/external/AbstractCachingDependentResource.java deleted file mode 100644 index 242625bc5d..0000000000 --- a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/external/AbstractCachingDependentResource.java +++ /dev/null @@ -1,29 +0,0 @@ -package io.javaoperatorsdk.operator.processing.dependent.external; - -import java.util.Optional; - -import io.fabric8.kubernetes.api.model.HasMetadata; -import io.javaoperatorsdk.operator.api.reconciler.Ignore; -import io.javaoperatorsdk.operator.processing.dependent.AbstractEventSourceHolderDependentResource; -import io.javaoperatorsdk.operator.processing.event.source.ExternalResourceCachingEventSource; - -@Ignore -public abstract class AbstractCachingDependentResource - extends - AbstractEventSourceHolderDependentResource> { - private final Class resourceType; - - protected AbstractCachingDependentResource(Class resourceType) { - this.resourceType = resourceType; - } - - @Override - public Class resourceType() { - return resourceType; - } - - @Override - public Optional getSecondaryResource(P primaryResource) { - return eventSource().getSecondaryResource(primaryResource); - } -} diff --git a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/external/AbstractPollingDependentResource.java b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/external/AbstractPollingDependentResource.java index 6bab6f48cf..2ccba025a7 100644 --- a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/external/AbstractPollingDependentResource.java +++ b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/external/AbstractPollingDependentResource.java @@ -2,11 +2,15 @@ import io.fabric8.kubernetes.api.model.HasMetadata; import io.javaoperatorsdk.operator.api.reconciler.Ignore; +import io.javaoperatorsdk.operator.processing.dependent.AbstractEventSourceHolderDependentResource; import io.javaoperatorsdk.operator.processing.event.source.CacheKeyMapper; +import io.javaoperatorsdk.operator.processing.event.source.ExternalResourceCachingEventSource; @Ignore public abstract class AbstractPollingDependentResource - extends AbstractCachingDependentResource implements CacheKeyMapper { + extends + AbstractEventSourceHolderDependentResource> + implements CacheKeyMapper { public static final int DEFAULT_POLLING_PERIOD = 5000; private long pollingPeriod; diff --git a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/external/AbstractSimpleDependentResource.java b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/external/AbstractSimpleDependentResource.java index 0675db5f1a..748452c30c 100644 --- a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/external/AbstractSimpleDependentResource.java +++ b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/external/AbstractSimpleDependentResource.java @@ -33,7 +33,7 @@ public AbstractSimpleDependentResource(UpdatableCache cache) { } @Override - public Optional getSecondaryResource(HasMetadata primaryResource) { + public Optional getSecondaryResource(P primaryResource, Context

context) { return cache.get(ResourceID.fromResource(primaryResource)); } diff --git a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/kubernetes/KubernetesDependent.java b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/kubernetes/KubernetesDependent.java index f66ff95373..603f4ae62e 100644 --- a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/kubernetes/KubernetesDependent.java +++ b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/kubernetes/KubernetesDependent.java @@ -6,6 +6,8 @@ import java.lang.annotation.Target; import io.javaoperatorsdk.operator.api.reconciler.Constants; +import io.javaoperatorsdk.operator.api.reconciler.ResourceDiscriminator; +import io.javaoperatorsdk.operator.processing.event.source.filter.*; import io.javaoperatorsdk.operator.processing.event.source.filter.GenericFilter; import io.javaoperatorsdk.operator.processing.event.source.filter.OnAddFilter; import io.javaoperatorsdk.operator.processing.event.source.filter.OnDeleteFilter; @@ -68,4 +70,6 @@ * itself if no value is set */ Class genericFilter() default GenericFilter.class; + + Class resourceDiscriminator() default ResourceDiscriminator.class; } 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 930e5fd5b4..328a061e6b 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,16 +1,13 @@ package io.javaoperatorsdk.operator.processing.dependent.kubernetes; import java.util.HashMap; -import java.util.Optional; import java.util.Set; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import io.fabric8.kubernetes.api.model.HasMetadata; -import io.fabric8.kubernetes.api.model.KubernetesResourceList; import io.fabric8.kubernetes.client.KubernetesClient; -import io.fabric8.kubernetes.client.dsl.NonNamespaceOperation; import io.fabric8.kubernetes.client.dsl.Resource; import io.javaoperatorsdk.operator.OperatorException; import io.javaoperatorsdk.operator.api.config.informer.InformerConfiguration; @@ -41,13 +38,12 @@ public abstract class KubernetesDependentResource matcher; private final ResourceUpdatePreProcessor processor; - private final Class resourceType; private final boolean garbageCollected = this instanceof GarbageCollected; private KubernetesDependentResourceConfig kubernetesDependentResourceConfig; @SuppressWarnings("unchecked") public KubernetesDependentResource(Class resourceType) { - this.resourceType = resourceType; + super(resourceType); matcher = this instanceof Matcher ? (Matcher) this : GenericKubernetesResourceMatcher.matcherFor(resourceType, this); @@ -75,6 +71,7 @@ private void configureWith(String labelSelector, Set namespaces, .build(); configureWith(new InformerEventSource<>(ic, context)); + } @SuppressWarnings("unchecked") @@ -94,7 +91,7 @@ private SecondaryToPrimaryMapper getSecondaryToPrimaryMapper() { /** * Use to share informers between event more resources. * - * @param informerEventSource informer to use* + * @param informerEventSource informer to use */ public void configureWith(InformerEventSource informerEventSource) { setEventSource(informerEventSource); @@ -125,12 +122,12 @@ protected R handleUpdate(R actual, R desired, P primary, Context

context) { @SuppressWarnings("unused") public R create(R target, P primary, Context

context) { - return prepare(target, primary, "Creating").create(target); + return prepare(target, primary, "Creating").create(); } public R update(R actual, R target, P primary, Context

context) { var updatedActual = processor.replaceSpecOnActual(actual, target, context); - return prepare(target, primary, "Updating").replace(updatedActual); + return prepare(updatedActual, primary, "Updating").replace(); } public Result match(R actualResource, P primary, Context

context) { @@ -138,13 +135,11 @@ public Result match(R actualResource, P primary, Context

context) { } public void delete(P primary, Context

context) { - var resource = getSecondaryResource(primary); - resource.ifPresent(r -> client.resource(r).delete()); + getSecondaryResource(primary, context).ifPresent(r -> client.resource(r).delete()); } @SuppressWarnings("unchecked") - protected NonNamespaceOperation, Resource> prepare(R desired, - P primary, String actionName) { + protected Resource prepare(R desired, P primary, String actionName) { log.debug("{} target resource with type: {}, with id: {}", actionName, desired.getClass(), @@ -155,7 +150,8 @@ protected NonNamespaceOperation, Resource> prepa addDefaultSecondaryToPrimaryMapperAnnotations(desired, primary); } Class targetClass = (Class) desired.getClass(); - return client.resources(targetClass).inNamespace(desired.getMetadata().getNamespace()); + return client.resources(targetClass).inNamespace(desired.getMetadata().getNamespace()) + .resource(desired); } @Override @@ -167,6 +163,7 @@ protected InformerEventSource createEventSource(EventSourceContext

cont onUpdateFilter = kubernetesDependentResourceConfig.onUpdateFilter(); onDeleteFilter = kubernetesDependentResourceConfig.onDeleteFilter(); genericFilter = kubernetesDependentResourceConfig.genericFilter(); + setResourceDiscriminator(kubernetesDependentResourceConfig.getResourceDiscriminator()); configureWith(kubernetesDependentResourceConfig.labelSelector(), kubernetesDependentResourceConfig.namespaces(), @@ -203,16 +200,6 @@ protected boolean addOwnerReference() { return garbageCollected; } - @Override - public Class resourceType() { - return resourceType; - } - - @Override - public Optional getSecondaryResource(P primaryResource) { - return eventSource().getSecondaryResource(primaryResource); - } - @Override public void setKubernetesClient(KubernetesClient kubernetesClient) { this.client = kubernetesClient; @@ -235,5 +222,4 @@ private void prepareEventFiltering(R desired, ResourceID resourceID) { private void cleanupAfterEventFiltering(ResourceID resourceID) { eventSource().cleanupOnCreateOrUpdateEventFiltering(resourceID); } - } diff --git a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/kubernetes/KubernetesDependentResourceConfig.java b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/kubernetes/KubernetesDependentResourceConfig.java index e4a75b165a..e2a2c0f684 100644 --- a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/kubernetes/KubernetesDependentResourceConfig.java +++ b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/kubernetes/KubernetesDependentResourceConfig.java @@ -3,6 +3,7 @@ import java.util.Set; import io.javaoperatorsdk.operator.api.reconciler.Constants; +import io.javaoperatorsdk.operator.api.reconciler.ResourceDiscriminator; import io.javaoperatorsdk.operator.processing.event.source.filter.GenericFilter; import io.javaoperatorsdk.operator.processing.event.source.filter.OnAddFilter; import io.javaoperatorsdk.operator.processing.event.source.filter.OnDeleteFilter; @@ -15,7 +16,7 @@ public class KubernetesDependentResourceConfig { private Set namespaces = Constants.SAME_AS_CONTROLLER_NAMESPACES_SET; private String labelSelector = NO_VALUE_SET; private boolean namespacesWereConfigured = false; - + private ResourceDiscriminator resourceDiscriminator; private OnAddFilter onAddFilter; @@ -27,9 +28,9 @@ public class KubernetesDependentResourceConfig { public KubernetesDependentResourceConfig() {} - @SuppressWarnings("rawtypes") public KubernetesDependentResourceConfig(Set namespaces, String labelSelector, - boolean configuredNS, OnAddFilter onAddFilter, + boolean configuredNS, ResourceDiscriminator resourceDiscriminator, + OnAddFilter onAddFilter, OnUpdateFilter onUpdateFilter, OnDeleteFilter onDeleteFilter, GenericFilter genericFilter) { this.namespaces = namespaces; @@ -39,10 +40,11 @@ public KubernetesDependentResourceConfig(Set namespaces, String labelSel this.onUpdateFilter = onUpdateFilter; this.onDeleteFilter = onDeleteFilter; this.genericFilter = genericFilter; + this.resourceDiscriminator = resourceDiscriminator; } public KubernetesDependentResourceConfig(Set namespaces, String labelSelector) { - this(namespaces, labelSelector, true, null, null, null, null); + this(namespaces, labelSelector, true, null, null, null, null, null); } public KubernetesDependentResourceConfig setNamespaces(Set namespaces) { @@ -73,17 +75,21 @@ public OnAddFilter onAddFilter() { return onAddFilter; } - @SuppressWarnings("rawtypes") - public OnUpdateFilter onUpdateFilter() { + + public OnUpdateFilter onUpdateFilter() { return onUpdateFilter; } - @SuppressWarnings("rawtypes") - public OnDeleteFilter onDeleteFilter() { + public OnDeleteFilter onDeleteFilter() { return onDeleteFilter; } public GenericFilter genericFilter() { return genericFilter; } + + @SuppressWarnings("rawtypes") + public ResourceDiscriminator getResourceDiscriminator() { + return resourceDiscriminator; + } } diff --git a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/workflow/DependentResourceNode.java b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/workflow/DependentResourceNode.java index e317145916..32cef6c68e 100644 --- a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/workflow/DependentResourceNode.java +++ b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/workflow/DependentResourceNode.java @@ -5,6 +5,7 @@ import java.util.Optional; import io.fabric8.kubernetes.api.model.HasMetadata; +import io.javaoperatorsdk.operator.api.reconciler.Context; import io.javaoperatorsdk.operator.api.reconciler.dependent.DependentResource; @SuppressWarnings("rawtypes") @@ -85,4 +86,8 @@ public DependentResourceNode setReadyPostcondition(Condition readyPo public List getParents() { return parents; } + + protected R getSecondaryResource(P primary, Context

context) { + return getDependentResource().getSecondaryResource(primary, context).orElse(null); + } } diff --git a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/workflow/WorkflowCleanupExecutor.java b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/workflow/WorkflowCleanupExecutor.java index 98c6789869..45541b91d6 100644 --- a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/workflow/WorkflowCleanupExecutor.java +++ b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/workflow/WorkflowCleanupExecutor.java @@ -104,12 +104,10 @@ public void run() { ((Deleter

) dependentResourceNode.getDependentResource()).delete(primary, context); deleteCalled.add(dependentResourceNode); } - boolean deletePostConditionMet = - deletePostCondition.map(c -> c.isMet(primary, - dependentResourceNode.getDependentResource().getSecondaryResource(primary) - .orElse(null), - context)).orElse(true); - + boolean deletePostConditionMet = deletePostCondition + .map(c -> c.isMet(primary, dependentResourceNode.getSecondaryResource(primary, context), + context)) + .orElse(true); if (deletePostConditionMet) { alreadyVisited.add(dependentResourceNode); handleDependentCleaned(dependentResourceNode); @@ -127,6 +125,7 @@ public void run() { } } + private synchronized void handleDependentCleaned( DependentResourceNode dependentResourceNode) { var dependOns = dependentResourceNode.getDependsOn(); diff --git a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/workflow/WorkflowReconcileExecutor.java b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/workflow/WorkflowReconcileExecutor.java index 14028cb980..33ecd12f77 100644 --- a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/workflow/WorkflowReconcileExecutor.java +++ b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/workflow/WorkflowReconcileExecutor.java @@ -85,8 +85,7 @@ private synchronized void handleReconcile(DependentResourceNode depend } boolean reconcileConditionMet = dependentResourceNode.getReconcilePrecondition() - .map(rc -> rc.isMet(primary, - dependentResourceNode.getDependentResource().getSecondaryResource(primary).orElse(null), + .map(rc -> rc.isMet(primary, dependentResourceNode.getSecondaryResource(primary, context), context)) .orElse(true); @@ -167,9 +166,12 @@ public void run() { ReconcileResult reconcileResult = dependentResource.reconcile(primary, context); reconcileResults.put(dependentResource, reconcileResult); reconciled.add(dependentResourceNode); + boolean ready = dependentResourceNode.getReadyPostcondition() .map(rc -> rc.isMet(primary, - dependentResourceNode.getDependentResource().getSecondaryResource(primary) + context + .getSecondaryResource( + dependentResourceNode.getDependentResource().resourceType()) .orElse(null), context)) .orElse(true); @@ -210,8 +212,8 @@ public void run() { } boolean deletePostConditionMet = deletePostCondition.map(c -> c.isMet(primary, - dependentResourceNode.getDependentResource().getSecondaryResource(primary) - .orElse(null), + context.getSecondaryResource( + dependentResourceNode.getDependentResource().resourceType()).orElse(null), context)).orElse(true); if (deletePostConditionMet) { alreadyVisited.add(dependentResourceNode); diff --git a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/EventProcessor.java b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/EventProcessor.java index 58bf5ee00a..e0280796b2 100644 --- a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/EventProcessor.java +++ b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/EventProcessor.java @@ -135,7 +135,8 @@ private void handleMarkedEventForResource(ResourceState state) { private void submitReconciliationExecution(ResourceState state) { try { boolean controllerUnderExecution = isControllerUnderExecution(state); - Optional maybeLatest = cache.get(state.getId()); + final var resourceID = state.getId(); + Optional maybeLatest = cache.get(resourceID); maybeLatest.ifPresent(MDCUtils::addResourceInfo); if (!controllerUnderExecution && maybeLatest.isPresent()) { var rateLimit = state.getRateLimit(); @@ -145,24 +146,24 @@ private void submitReconciliationExecution(ResourceState state) { } var rateLimiterPermission = rateLimiter.isLimited(rateLimit); if (rateLimiterPermission.isPresent()) { - handleRateLimitedSubmission(state.getId(), rateLimiterPermission.get()); + handleRateLimitedSubmission(resourceID, rateLimiterPermission.get()); return; } state.setUnderProcessing(true); final var latest = maybeLatest.get(); ExecutionScope executionScope = new ExecutionScope<>(latest, state.getRetry()); state.unMarkEventReceived(); - metrics.reconcileCustomResource(state.getId(), state.getRetry(), metricsMetadata); + metrics.reconcileCustomResource(resourceID, state.getRetry(), metricsMetadata); log.debug("Executing events for custom resource. Scope: {}", executionScope); executor.execute(new ReconcilerExecutor(executionScope)); } else { log.debug( "Skipping executing controller for resource id: {}. Controller in execution: {}. Latest Resource present: {}", - state, + resourceID, controllerUnderExecution, maybeLatest.isPresent()); if (maybeLatest.isEmpty()) { - log.debug("no custom resource found in cache for ResourceID: {}", state); + log.debug("no custom resource found in cache for resource id: {}", resourceID); } } } finally { diff --git a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/EventSourceManager.java b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/EventSourceManager.java index 3f1e5e848d..fc68e6e413 100644 --- a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/EventSourceManager.java +++ b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/EventSourceManager.java @@ -23,7 +23,8 @@ import io.javaoperatorsdk.operator.processing.event.source.controller.ResourceAction; import io.javaoperatorsdk.operator.processing.event.source.timer.TimerEventSource; -public class EventSourceManager

implements LifecycleAware { +public class EventSourceManager

+ implements LifecycleAware, EventSourceRetriever

{ private static final Logger log = LoggerFactory.getLogger(EventSourceManager.class); @@ -171,17 +172,23 @@ public ControllerResourceEventSource

getControllerResourceEventSource() { return eventSources.controllerResourceEventSource(); } - ResourceEventSource getResourceEventSourceFor( - Class dependentType) { + @Override + public ResourceEventSource getResourceEventSourceFor(Class dependentType) { return getResourceEventSourceFor(dependentType, null); } - public List> getEventSourcesFor(Class dependentType) { + public List> getResourceEventSourcesFor(Class dependentType) { return eventSources.getEventSources(dependentType); } - public ResourceEventSource getResourceEventSourceFor( - Class dependentType, String qualifier) { + @Deprecated + public List> getEventSourcesFor(Class dependentType) { + return eventSources.getEventSources(dependentType); + } + + @Override + public ResourceEventSource getResourceEventSourceFor( + Class dependentType, String qualifier) { Objects.requireNonNull(dependentType, "dependentType is Mandatory"); return eventSources.get(dependentType, qualifier); } diff --git a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/EventSourceRetriever.java b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/EventSourceRetriever.java new file mode 100644 index 0000000000..a31d8902b0 --- /dev/null +++ b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/EventSourceRetriever.java @@ -0,0 +1,18 @@ +package io.javaoperatorsdk.operator.processing.event; + +import java.util.List; + +import io.fabric8.kubernetes.api.model.HasMetadata; +import io.javaoperatorsdk.operator.processing.event.source.ResourceEventSource; + +public interface EventSourceRetriever

{ + + ResourceEventSource getResourceEventSourceFor( + Class dependentType); + + ResourceEventSource getResourceEventSourceFor( + Class dependentType, String qualifier); + + List> getResourceEventSourcesFor(Class dependentType); + +} diff --git a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/EventSources.java b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/EventSources.java index 17de7ff947..e4fabe7ff8 100644 --- a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/EventSources.java +++ b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/EventSources.java @@ -8,7 +8,6 @@ import io.fabric8.kubernetes.api.model.HasMetadata; import io.javaoperatorsdk.operator.processing.Controller; -import io.javaoperatorsdk.operator.processing.ResourceOwner; import io.javaoperatorsdk.operator.processing.event.source.EventSource; import io.javaoperatorsdk.operator.processing.event.source.ResourceEventSource; import io.javaoperatorsdk.operator.processing.event.source.controller.ControllerResourceEventSource; @@ -86,8 +85,8 @@ public void add(String name, EventSource eventSource) { @SuppressWarnings("rawtypes") private Class getResourceType(EventSource source) { - return source instanceof ResourceOwner - ? ((ResourceOwner) source).resourceType() + return source instanceof ResourceEventSource + ? ((ResourceEventSource) source).resourceType() : source.getClass(); } diff --git a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/ResourceID.java b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/ResourceID.java index 7baeab6a4b..b6c2d976e3 100644 --- a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/ResourceID.java +++ b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/ResourceID.java @@ -54,6 +54,12 @@ public boolean equals(Object o) { that.namespace); } + public boolean isSameResource(HasMetadata hasMetadata) { + final var metadata = hasMetadata.getMetadata(); + return getName().equals(metadata.getName()) && + getNamespace().map(ns -> ns.equals(metadata.getNamespace())).orElse(true); + } + @Override public int hashCode() { return Objects.hash(name, namespace); diff --git a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/source/ResourceEventSource.java b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/source/ResourceEventSource.java index bf6b9c0fd3..38b5d7007f 100644 --- a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/source/ResourceEventSource.java +++ b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/source/ResourceEventSource.java @@ -4,14 +4,19 @@ import java.util.Set; import io.fabric8.kubernetes.api.model.HasMetadata; -import io.javaoperatorsdk.operator.processing.ResourceOwner; import io.javaoperatorsdk.operator.processing.event.source.filter.GenericFilter; import io.javaoperatorsdk.operator.processing.event.source.filter.OnAddFilter; import io.javaoperatorsdk.operator.processing.event.source.filter.OnDeleteFilter; import io.javaoperatorsdk.operator.processing.event.source.filter.OnUpdateFilter; -public interface ResourceEventSource extends EventSource, - ResourceOwner { +public interface ResourceEventSource extends EventSource { + + /** + * Retrieves the resource type associated with this ResourceEventSource + * + * @return the resource type associated with this ResourceEventSource + */ + Class resourceType(); default Optional getSecondaryResource(P primary) { var resources = getSecondaryResources(primary); diff --git a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/source/informer/ManagedInformerEventSource.java b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/source/informer/ManagedInformerEventSource.java index 6ebd63a7eb..cc9af59094 100644 --- a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/source/informer/ManagedInformerEventSource.java +++ b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/source/informer/ManagedInformerEventSource.java @@ -93,10 +93,10 @@ public void handleRecentResourceCreate(ResourceID resourceID, R resource) { public Optional get(ResourceID resourceID) { Optional resource = temporaryResourceCache.getResourceFromCache(resourceID); if (resource.isPresent()) { - log.debug("Resource found in temporal cache for Resource ID: {}", resourceID); + log.debug("Resource found in temporary cache for Resource ID: {}", resourceID); return resource; } else { - log.debug("Resource not found in temporal cache reading it from informer cache," + + log.debug("Resource not found in temporary cache reading it from informer cache," + " for Resource ID: {}", resourceID); return cache.get(resourceID); } diff --git a/operator-framework-core/src/test/java/io/javaoperatorsdk/operator/api/config/ControllerConfigurationOverriderTest.java b/operator-framework-core/src/test/java/io/javaoperatorsdk/operator/api/config/ControllerConfigurationOverriderTest.java index 127cd535b1..6e486b5347 100644 --- a/operator-framework-core/src/test/java/io/javaoperatorsdk/operator/api/config/ControllerConfigurationOverriderTest.java +++ b/operator-framework-core/src/test/java/io/javaoperatorsdk/operator/api/config/ControllerConfigurationOverriderTest.java @@ -1,6 +1,5 @@ package io.javaoperatorsdk.operator.api.config; -import java.util.Optional; import java.util.Set; import org.junit.jupiter.api.Test; @@ -85,10 +84,6 @@ public Class resourceType() { return Object.class; } - @Override - public Optional getSecondaryResource(ConfigMap primary) { - return Optional.empty(); - } } } diff --git a/operator-framework-core/src/test/java/io/javaoperatorsdk/operator/processing/dependent/AbstractDependentResourceTest.java b/operator-framework-core/src/test/java/io/javaoperatorsdk/operator/processing/dependent/AbstractDependentResourceTest.java index 4edd3dfb4c..b93abd45c0 100644 --- a/operator-framework-core/src/test/java/io/javaoperatorsdk/operator/processing/dependent/AbstractDependentResourceTest.java +++ b/operator-framework-core/src/test/java/io/javaoperatorsdk/operator/processing/dependent/AbstractDependentResourceTest.java @@ -16,7 +16,6 @@ class AbstractDependentResourceTest { - @Test void throwsExceptionIfDesiredIsNullOnCreate() { TestDependentResource testDependentResource = new TestDependentResource(); @@ -80,7 +79,8 @@ public Class resourceType() { } @Override - public Optional getSecondaryResource(TestCustomResource primary) { + public Optional getSecondaryResource(TestCustomResource primary, + Context context) { return Optional.ofNullable(secondary); } diff --git a/operator-framework-core/src/test/java/io/javaoperatorsdk/operator/processing/dependent/EmptyTestDependentResource.java b/operator-framework-core/src/test/java/io/javaoperatorsdk/operator/processing/dependent/EmptyTestDependentResource.java index aa75849051..dab1bc0132 100644 --- a/operator-framework-core/src/test/java/io/javaoperatorsdk/operator/processing/dependent/EmptyTestDependentResource.java +++ b/operator-framework-core/src/test/java/io/javaoperatorsdk/operator/processing/dependent/EmptyTestDependentResource.java @@ -1,7 +1,5 @@ package io.javaoperatorsdk.operator.processing.dependent; -import java.util.Optional; - import io.fabric8.kubernetes.api.model.apps.Deployment; import io.javaoperatorsdk.operator.api.reconciler.Context; import io.javaoperatorsdk.operator.api.reconciler.dependent.DependentResource; @@ -17,11 +15,6 @@ public ReconcileResult reconcile(TestCustomResource primary, return null; } - @Override - public Optional getSecondaryResource(TestCustomResource primaryResource) { - return Optional.empty(); - } - @Override public Class resourceType() { return Deployment.class; diff --git a/operator-framework-core/src/test/java/io/javaoperatorsdk/operator/processing/dependent/external/AbstractSimpleDependentResourceTest.java b/operator-framework-core/src/test/java/io/javaoperatorsdk/operator/processing/dependent/external/AbstractSimpleDependentResourceTest.java index e020fd27a3..520f44365f 100644 --- a/operator-framework-core/src/test/java/io/javaoperatorsdk/operator/processing/dependent/external/AbstractSimpleDependentResourceTest.java +++ b/operator-framework-core/src/test/java/io/javaoperatorsdk/operator/processing/dependent/external/AbstractSimpleDependentResourceTest.java @@ -45,14 +45,14 @@ void getsTheResourceFromSupplyIfReconciling() { simpleDependentResource.reconcile(TestUtils.testCustomResource1(), null); verify(supplierMock, times(1)).get(); - assertThat(simpleDependentResource.getSecondaryResource(TestUtils.testCustomResource1())) + assertThat(simpleDependentResource.getSecondaryResource(TestUtils.testCustomResource1(), null)) .isPresent() .isEqualTo(Optional.of(SampleExternalResource.testResource1())); } @Test void getResourceReadsTheResourceFromCache() { - simpleDependentResource.getSecondaryResource(TestUtils.testCustomResource1()); + simpleDependentResource.getSecondaryResource(TestUtils.testCustomResource1(), null); verify(supplierMock, times(0)).get(); verify(updatableCacheMock, times(1)).get(any()); diff --git a/operator-framework-core/src/test/java/io/javaoperatorsdk/operator/processing/dependent/workflow/AbstractWorkflowExecutorTest.java b/operator-framework-core/src/test/java/io/javaoperatorsdk/operator/processing/dependent/workflow/AbstractWorkflowExecutorTest.java index 8dfab3fa20..9c10c06cc0 100644 --- a/operator-framework-core/src/test/java/io/javaoperatorsdk/operator/processing/dependent/workflow/AbstractWorkflowExecutorTest.java +++ b/operator-framework-core/src/test/java/io/javaoperatorsdk/operator/processing/dependent/workflow/AbstractWorkflowExecutorTest.java @@ -3,7 +3,6 @@ import java.util.ArrayList; import java.util.Collections; import java.util.List; -import java.util.Optional; import io.javaoperatorsdk.operator.api.reconciler.Context; import io.javaoperatorsdk.operator.api.reconciler.dependent.Deleter; @@ -49,11 +48,6 @@ public Class resourceType() { return String.class; } - @Override - public Optional getSecondaryResource(TestCustomResource primary) { - return Optional.of(VALUE); - } - @Override public String toString() { return name; @@ -113,11 +107,6 @@ public Class resourceType() { return String.class; } - @Override - public Optional getSecondaryResource(TestCustomResource primary) { - return Optional.of(VALUE); - } - @Override public String toString() { return name; diff --git a/operator-framework-core/src/test/java/io/javaoperatorsdk/operator/processing/dependent/workflow/WorkflowCleanupExecutorTest.java b/operator-framework-core/src/test/java/io/javaoperatorsdk/operator/processing/dependent/workflow/WorkflowCleanupExecutorTest.java index 7c1c5d6ff6..56bd876687 100644 --- a/operator-framework-core/src/test/java/io/javaoperatorsdk/operator/processing/dependent/workflow/WorkflowCleanupExecutorTest.java +++ b/operator-framework-core/src/test/java/io/javaoperatorsdk/operator/processing/dependent/workflow/WorkflowCleanupExecutorTest.java @@ -4,17 +4,20 @@ import org.junit.jupiter.api.Test; import io.javaoperatorsdk.operator.AggregatedOperatorException; +import io.javaoperatorsdk.operator.api.reconciler.Context; import io.javaoperatorsdk.operator.processing.dependent.workflow.builder.WorkflowBuilder; import io.javaoperatorsdk.operator.sample.simple.TestCustomResource; import static io.javaoperatorsdk.operator.processing.dependent.workflow.ExecutionAssert.assertThat; import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.mockito.Mockito.mock; class WorkflowCleanupExecutorTest extends AbstractWorkflowExecutorTest { protected TestDeleterDependent dd1 = new TestDeleterDependent("DR_DELETER_1"); protected TestDeleterDependent dd2 = new TestDeleterDependent("DR_DELETER_2"); protected TestDeleterDependent dd3 = new TestDeleterDependent("DR_DELETER_3"); + Context mockContext = mock(Context.class); @Test void cleanUpDiamondWorkflow() { @@ -45,7 +48,7 @@ void dontDeleteIfDependentErrored() { .withThrowExceptionFurther(false) .build(); - var res = workflow.cleanup(new TestCustomResource(), null); + var res = workflow.cleanup(new TestCustomResource(), mockContext); assertThrows(AggregatedOperatorException.class, res::throwAggregateExceptionIfErrorsPresent); @@ -64,7 +67,7 @@ void cleanupConditionTrivialCase() { .addDependentResource(dd2).dependsOn(dd1).withDeletePostcondition(noMetDeletePostCondition) .build(); - var res = workflow.cleanup(new TestCustomResource(), null); + var res = workflow.cleanup(new TestCustomResource(), mockContext); assertThat(executionHistory).deleted(dd2).notReconciled(dd1); Assertions.assertThat(res.getDeleteCalledOnDependents()).containsExactlyInAnyOrder(dd2); @@ -79,7 +82,7 @@ void cleanupConditionMet() { .addDependentResource(dd2).dependsOn(dd1).withDeletePostcondition(metDeletePostCondition) .build(); - var res = workflow.cleanup(new TestCustomResource(), null); + var res = workflow.cleanup(new TestCustomResource(), mockContext); assertThat(executionHistory).deleted(dd2, dd1); @@ -99,7 +102,7 @@ void cleanupConditionDiamondWorkflow() { .addDependentResource(dd4).dependsOn(dd2, dd3) .build(); - var res = workflow.cleanup(new TestCustomResource(), null); + var res = workflow.cleanup(new TestCustomResource(), mockContext); assertThat(executionHistory) .reconciledInOrder(dd4, dd2) diff --git a/operator-framework-core/src/test/java/io/javaoperatorsdk/operator/processing/dependent/workflow/WorkflowReconcileExecutorTest.java b/operator-framework-core/src/test/java/io/javaoperatorsdk/operator/processing/dependent/workflow/WorkflowReconcileExecutorTest.java index fa9d757b67..873cf66cb6 100644 --- a/operator-framework-core/src/test/java/io/javaoperatorsdk/operator/processing/dependent/workflow/WorkflowReconcileExecutorTest.java +++ b/operator-framework-core/src/test/java/io/javaoperatorsdk/operator/processing/dependent/workflow/WorkflowReconcileExecutorTest.java @@ -4,11 +4,13 @@ import org.junit.jupiter.api.Test; import io.javaoperatorsdk.operator.AggregatedOperatorException; +import io.javaoperatorsdk.operator.api.reconciler.Context; import io.javaoperatorsdk.operator.processing.dependent.workflow.builder.WorkflowBuilder; import io.javaoperatorsdk.operator.sample.simple.TestCustomResource; import static io.javaoperatorsdk.operator.processing.dependent.workflow.ExecutionAssert.assertThat; import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.mockito.Mockito.mock; @SuppressWarnings("rawtypes") class WorkflowReconcileExecutorTest extends AbstractWorkflowExecutorTest { @@ -21,6 +23,8 @@ class WorkflowReconcileExecutorTest extends AbstractWorkflowExecutorTest { private final Condition notMetReadyCondition = (primary, secondary, context) -> false; + Context mockContext = mock(Context.class); + @Test void reconcileTopLevelResources() { var workflow = new WorkflowBuilder() @@ -28,7 +32,7 @@ void reconcileTopLevelResources() { .addDependentResource(dr2) .build(); - var res = workflow.reconcile(new TestCustomResource(), null); + var res = workflow.reconcile(new TestCustomResource(), mockContext); assertThat(executionHistory).reconciled(dr1, dr2); Assertions.assertThat(res.getErroredDependents()).isEmpty(); @@ -42,7 +46,7 @@ void reconciliationWithSimpleDependsOn() { .addDependentResource(dr2).dependsOn(dr1) .build(); - var res = workflow.reconcile(new TestCustomResource(), null); + var res = workflow.reconcile(new TestCustomResource(), mockContext); Assertions.assertThat(res.getErroredDependents()).isEmpty(); assertThat(executionHistory).reconciledInOrder(dr1, dr2); @@ -61,7 +65,7 @@ void reconciliationWithTwoTheDependsOns() { .addDependentResource(dr3).dependsOn(dr1) .build(); - var res = workflow.reconcile(new TestCustomResource(), null); + var res = workflow.reconcile(new TestCustomResource(), mockContext); Assertions.assertThat(res.getErroredDependents()).isEmpty(); assertThat(executionHistory) @@ -83,7 +87,7 @@ void diamondShareWorkflowReconcile() { .addDependentResource(dr4).dependsOn(dr3).dependsOn(dr2) .build(); - var res = workflow.reconcile(new TestCustomResource(), null); + var res = workflow.reconcile(new TestCustomResource(), mockContext); Assertions.assertThat(res.getErroredDependents()).isEmpty(); assertThat(executionHistory) @@ -103,7 +107,7 @@ void exceptionHandlingSimpleCases() { .withThrowExceptionFurther(false) .build(); - var res = workflow.reconcile(new TestCustomResource(), null); + var res = workflow.reconcile(new TestCustomResource(), mockContext); assertThrows(AggregatedOperatorException.class, res::throwAggregateExceptionIfErrorsPresent); @@ -123,7 +127,7 @@ void dependentsOnErroredResourceNotReconciled() { .withThrowExceptionFurther(false) .build(); - var res = workflow.reconcile(new TestCustomResource(), null); + var res = workflow.reconcile(new TestCustomResource(), mockContext); assertThrows(AggregatedOperatorException.class, res::throwAggregateExceptionIfErrorsPresent); @@ -145,7 +149,7 @@ void oneBranchErrorsOtherCompletes() { .withThrowExceptionFurther(false) .build(); - var res = workflow.reconcile(new TestCustomResource(), null); + var res = workflow.reconcile(new TestCustomResource(), mockContext); assertThrows(AggregatedOperatorException.class, res::throwAggregateExceptionIfErrorsPresent); @@ -164,7 +168,7 @@ void onlyOneDependsOnErroredResourceNotReconciled() { .withThrowExceptionFurther(false) .build(); - var res = workflow.reconcile(new TestCustomResource(), null); + var res = workflow.reconcile(new TestCustomResource(), mockContext); assertThrows(AggregatedOperatorException.class, res::throwAggregateExceptionIfErrorsPresent); @@ -182,7 +186,7 @@ void simpleReconcileCondition() { .addDependentResource(drDeleter).withReconcilePrecondition(not_met_reconcile_condition) .build(); - var res = workflow.reconcile(new TestCustomResource(), null); + var res = workflow.reconcile(new TestCustomResource(), mockContext); assertThat(executionHistory).notReconciled(dr1).reconciled(dr2).deleted(drDeleter); Assertions.assertThat(res.getErroredDependents()).isEmpty(); @@ -200,7 +204,7 @@ void triangleOnceConditionNotMet() { .dependsOn(dr1) .build(); - var res = workflow.reconcile(new TestCustomResource(), null); + var res = workflow.reconcile(new TestCustomResource(), mockContext); assertThat(executionHistory).reconciledInOrder(dr1, dr2).deleted(drDeleter); Assertions.assertThat(res.getErroredDependents()).isEmpty(); @@ -222,7 +226,7 @@ void reconcileConditionTransitiveDelete() { .withReconcilePrecondition(met_reconcile_condition) .build(); - var res = workflow.reconcile(new TestCustomResource(), null); + var res = workflow.reconcile(new TestCustomResource(), mockContext); Assertions.assertThat(res.getErroredDependents()).isEmpty(); assertThat(executionHistory).notReconciled(dr2); @@ -246,7 +250,7 @@ void reconcileConditionAlsoErrorDependsOn() { .withThrowExceptionFurther(false) .build(); - var res = workflow.reconcile(new TestCustomResource(), null); + var res = workflow.reconcile(new TestCustomResource(), mockContext); assertThrows(AggregatedOperatorException.class, res::throwAggregateExceptionIfErrorsPresent); @@ -267,7 +271,7 @@ void oneDependsOnConditionNotMet() { .addDependentResource(drDeleter).dependsOn(dr1, dr2) .build(); - var res = workflow.reconcile(new TestCustomResource(), null); + var res = workflow.reconcile(new TestCustomResource(), mockContext); Assertions.assertThat(res.getErroredDependents()).isEmpty(); @@ -287,7 +291,7 @@ void deletedIfReconcileConditionNotMet() { .addDependentResource(drDeleter2).dependsOn(dr1, drDeleter) .build(); - var res = workflow.reconcile(new TestCustomResource(), null); + var res = workflow.reconcile(new TestCustomResource(), mockContext); assertThat(executionHistory) .reconciledInOrder(dr1, drDeleter2, drDeleter) @@ -313,7 +317,7 @@ void deleteDoneInReverseOrder() { .addDependentResource(drDeleter4).dependsOn(drDeleter3) .build(); - var res = workflow.reconcile(new TestCustomResource(), null); + var res = workflow.reconcile(new TestCustomResource(), mockContext); assertThat(executionHistory) .reconciledInOrder(dr1, drDeleter4, drDeleter3, drDeleter) @@ -339,7 +343,7 @@ void diamondDeleteWithPostConditionInMiddle() { .addDependentResource(drDeleter4).dependsOn(drDeleter3, drDeleter2) .build(); - var res = workflow.reconcile(new TestCustomResource(), null); + var res = workflow.reconcile(new TestCustomResource(), mockContext); assertThat(executionHistory).notReconciled(drDeleter) .reconciledInOrder(drDeleter4, drDeleter2) @@ -363,7 +367,7 @@ void diamondDeleteErrorInMiddle() { .withThrowExceptionFurther(false) .build(); - var res = workflow.reconcile(new TestCustomResource(), null); + var res = workflow.reconcile(new TestCustomResource(), mockContext); assertThat(executionHistory) .notReconciled(drDeleter, drError) @@ -381,7 +385,7 @@ void readyConditionTrivialCase() { .addDependentResource(dr2).dependsOn(dr1) .build(); - var res = workflow.reconcile(new TestCustomResource(), null); + var res = workflow.reconcile(new TestCustomResource(), mockContext); assertThat(executionHistory).reconciledInOrder(dr1, dr2); @@ -397,7 +401,7 @@ void readyConditionNotMetTrivialCase() { .addDependentResource(dr2).dependsOn(dr1) .build(); - var res = workflow.reconcile(new TestCustomResource(), null); + var res = workflow.reconcile(new TestCustomResource(), mockContext); assertThat(executionHistory).reconciled(dr1).notReconciled(dr2); @@ -417,7 +421,7 @@ void readyConditionNotMetInOneParent() { .addDependentResource(dr3).dependsOn(dr1, dr2) .build(); - var res = workflow.reconcile(new TestCustomResource(), null); + var res = workflow.reconcile(new TestCustomResource(), mockContext); assertThat(executionHistory).reconciled(dr1, dr2).notReconciled(dr3); Assertions.assertThat(res.getErroredDependents()).isEmpty(); @@ -437,7 +441,7 @@ void diamondShareWithReadyCondition() { .addDependentResource(dr4).dependsOn(dr2, dr3) .build(); - var res = workflow.reconcile(new TestCustomResource(), null); + var res = workflow.reconcile(new TestCustomResource(), mockContext); Assertions.assertThat(res.getErroredDependents()).isEmpty(); assertThat(executionHistory).reconciledInOrder(dr1, dr2) diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/IndexDiscriminatorIT.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/IndexDiscriminatorIT.java new file mode 100644 index 0000000000..fe5b63de8a --- /dev/null +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/IndexDiscriminatorIT.java @@ -0,0 +1,77 @@ +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.indexdiscriminator.IndexDiscriminatorTestCustomResource; +import io.javaoperatorsdk.operator.sample.indexdiscriminator.IndexDiscriminatorTestReconciler; +import io.javaoperatorsdk.operator.sample.indexdiscriminator.IndexDiscriminatorTestSpec; + +import static io.javaoperatorsdk.operator.sample.indexdiscriminator.IndexDiscriminatorTestDRConfigMap.DATA_KEY; +import static io.javaoperatorsdk.operator.sample.indexdiscriminator.IndexDiscriminatorTestReconciler.FIRST_CONFIG_MAP_SUFFIX_1; +import static io.javaoperatorsdk.operator.sample.indexdiscriminator.IndexDiscriminatorTestReconciler.FIRST_CONFIG_MAP_SUFFIX_2; +import static org.assertj.core.api.Assertions.assertThat; +import static org.awaitility.Awaitility.await; + +class IndexDiscriminatorIT { + + public static final String TEST_RESOURCE_1 = "test1"; + public static final String CHANGED_SPEC_VALUE = "otherValue"; + @RegisterExtension + LocallyRunOperatorExtension operator = + LocallyRunOperatorExtension.builder().withReconciler(IndexDiscriminatorTestReconciler.class) + .build(); + + @Test + void resourcesFoundAndReconciled() { + var res = operator.create(createTestCustomResource()); + var reconciler = operator.getReconcilerOfType(IndexDiscriminatorTestReconciler.class); + + await().untilAsserted(() -> { + assertThat(reconciler.getNumberOfExecutions()).isEqualTo(1); + assertThat(operator.get(ConfigMap.class, TEST_RESOURCE_1 + FIRST_CONFIG_MAP_SUFFIX_1)) + .isNotNull(); + assertThat(operator.get(ConfigMap.class, TEST_RESOURCE_1 + FIRST_CONFIG_MAP_SUFFIX_2)) + .isNotNull(); + }); + + res.getSpec().setValue(CHANGED_SPEC_VALUE); + res = operator.replace(res); + + await().untilAsserted(() -> { + assertThat(reconciler.getNumberOfExecutions()).isEqualTo(2); + var cm1 = operator.get(ConfigMap.class, TEST_RESOURCE_1 + FIRST_CONFIG_MAP_SUFFIX_1); + var cm2 = operator.get(ConfigMap.class, TEST_RESOURCE_1 + FIRST_CONFIG_MAP_SUFFIX_2); + assertThat(cm1).isNotNull(); + assertThat(cm2).isNotNull(); + assertThat(cm1.getData().get(DATA_KEY)).isEqualTo(CHANGED_SPEC_VALUE); + assertThat(cm2.getData().get(DATA_KEY)).isEqualTo(CHANGED_SPEC_VALUE); + }); + + operator.delete(res); + + await().untilAsserted(() -> { + var cm1 = operator.get(ConfigMap.class, TEST_RESOURCE_1 + FIRST_CONFIG_MAP_SUFFIX_1); + var cm2 = operator.get(ConfigMap.class, TEST_RESOURCE_1 + FIRST_CONFIG_MAP_SUFFIX_2); + assertThat(cm1).isNull(); + assertThat(cm2).isNull(); + }); + } + + public IndexDiscriminatorTestCustomResource createTestCustomResource() { + IndexDiscriminatorTestCustomResource resource = + new IndexDiscriminatorTestCustomResource(); + resource.setMetadata( + new ObjectMetaBuilder() + .withName(TEST_RESOURCE_1) + .withNamespace(operator.getNamespace()) + .build()); + resource.setSpec(new IndexDiscriminatorTestSpec()); + resource.getSpec().setValue("default"); + return resource; + } + +} diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/indexdiscriminator/IndexDiscriminator.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/indexdiscriminator/IndexDiscriminator.java new file mode 100644 index 0000000000..eb6e193479 --- /dev/null +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/indexdiscriminator/IndexDiscriminator.java @@ -0,0 +1,41 @@ +package io.javaoperatorsdk.operator.sample.indexdiscriminator; + +import java.util.Optional; + +import io.fabric8.kubernetes.api.model.ConfigMap; +import io.javaoperatorsdk.operator.api.reconciler.Context; +import io.javaoperatorsdk.operator.api.reconciler.ResourceDiscriminator; +import io.javaoperatorsdk.operator.processing.event.source.informer.InformerEventSource; + +import static io.javaoperatorsdk.operator.sample.indexdiscriminator.IndexDiscriminatorTestReconciler.configMapKeyFromPrimary; + +public class IndexDiscriminator + implements ResourceDiscriminator { + + private final String indexName; + private final String nameSuffix; + + public IndexDiscriminator(String indexName, String nameSuffix) { + this.indexName = indexName; + this.nameSuffix = nameSuffix; + } + + @Override + public Optional distinguish(Class resource, + IndexDiscriminatorTestCustomResource primary, + Context context) { + + InformerEventSource eventSource = + (InformerEventSource) context + .eventSourceRetriever() + .getResourceEventSourceFor(ConfigMap.class); + var resources = eventSource.byIndex(indexName, configMapKeyFromPrimary(primary, nameSuffix)); + if (resources.isEmpty()) { + return Optional.empty(); + } else if (resources.size() > 1) { + throw new IllegalStateException("more than one resource"); + } else { + return Optional.of(resources.get(0)); + } + } +} diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/indexdiscriminator/IndexDiscriminatorTestCustomResource.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/indexdiscriminator/IndexDiscriminatorTestCustomResource.java new file mode 100644 index 0000000000..729b1d80eb --- /dev/null +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/indexdiscriminator/IndexDiscriminatorTestCustomResource.java @@ -0,0 +1,16 @@ +package io.javaoperatorsdk.operator.sample.indexdiscriminator; + +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("idt") +public class IndexDiscriminatorTestCustomResource + extends CustomResource + implements Namespaced { + +} diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/indexdiscriminator/IndexDiscriminatorTestDRConfigMap.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/indexdiscriminator/IndexDiscriminatorTestDRConfigMap.java new file mode 100644 index 0000000000..88dc40f55c --- /dev/null +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/indexdiscriminator/IndexDiscriminatorTestDRConfigMap.java @@ -0,0 +1,38 @@ +package io.javaoperatorsdk.operator.sample.indexdiscriminator; + +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.CRUDNoGCKubernetesDependentResource; +import io.javaoperatorsdk.operator.processing.dependent.kubernetes.KubernetesDependent; + +@KubernetesDependent +public class IndexDiscriminatorTestDRConfigMap + extends CRUDNoGCKubernetesDependentResource { + + public static final String DATA_KEY = "key"; + private final String suffix; + + public IndexDiscriminatorTestDRConfigMap(String value) { + super(ConfigMap.class); + this.suffix = value; + } + + @Override + protected ConfigMap desired(IndexDiscriminatorTestCustomResource primary, + Context context) { + Map data = new HashMap<>(); + data.put(DATA_KEY, primary.getSpec().getValue()); + + return new ConfigMapBuilder() + .withNewMetadata() + .withName(primary.getMetadata().getName() + suffix) + .withNamespace(primary.getMetadata().getNamespace()) + .endMetadata() + .withData(data) + .build(); + } +} diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/indexdiscriminator/IndexDiscriminatorTestReconciler.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/indexdiscriminator/IndexDiscriminatorTestReconciler.java new file mode 100644 index 0000000000..0b0af2a1cc --- /dev/null +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/indexdiscriminator/IndexDiscriminatorTestReconciler.java @@ -0,0 +1,120 @@ +package io.javaoperatorsdk.operator.sample.indexdiscriminator; + +import java.util.Collections; +import java.util.List; +import java.util.Map; +import java.util.concurrent.atomic.AtomicInteger; + +import io.fabric8.kubernetes.api.model.ConfigMap; +import io.fabric8.kubernetes.client.KubernetesClient; +import io.javaoperatorsdk.operator.api.config.informer.InformerConfiguration; +import io.javaoperatorsdk.operator.api.reconciler.*; +import io.javaoperatorsdk.operator.junit.KubernetesClientAware; +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 IndexDiscriminatorTestReconciler + implements Reconciler, + Cleaner, + TestExecutionInfoProvider, EventSourceInitializer, + KubernetesClientAware { + + public static final String FIRST_CONFIG_MAP_SUFFIX_1 = "-1"; + public static final String FIRST_CONFIG_MAP_SUFFIX_2 = "-2"; + public static final String CONFIG_MAP_INDEX_1 = "CONFIG_MAP_INDEX1"; + public static final String CONFIG_MAP_INDEX_2 = "CONFIG_MAP_INDEX2"; + + private final AtomicInteger numberOfExecutions = new AtomicInteger(0); + + private final IndexDiscriminatorTestDRConfigMap firstDependentResourceConfigMap; + private final IndexDiscriminatorTestDRConfigMap secondDependentResourceConfigMap; + private KubernetesClient client; + + public IndexDiscriminatorTestReconciler() { + firstDependentResourceConfigMap = + new IndexDiscriminatorTestDRConfigMap(FIRST_CONFIG_MAP_SUFFIX_1); + secondDependentResourceConfigMap = + new IndexDiscriminatorTestDRConfigMap(FIRST_CONFIG_MAP_SUFFIX_2); + } + + @Override + public UpdateControl reconcile( + IndexDiscriminatorTestCustomResource 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); + + eventSource.addIndexer(CONFIG_MAP_INDEX_1, cm -> { + if (cm.getMetadata().getName().endsWith(FIRST_CONFIG_MAP_SUFFIX_1)) { + return List.of(configMapKey(cm)); + } else { + return Collections.emptyList(); + } + }); + eventSource.addIndexer(CONFIG_MAP_INDEX_2, cm -> { + if (cm.getMetadata().getName().endsWith(FIRST_CONFIG_MAP_SUFFIX_2)) { + return List.of(configMapKey(cm)); + } else { + return Collections.emptyList(); + } + }); + + firstDependentResourceConfigMap.configureWith(eventSource); + secondDependentResourceConfigMap.configureWith(eventSource); + + firstDependentResourceConfigMap + .setResourceDiscriminator( + new IndexDiscriminator(CONFIG_MAP_INDEX_1, FIRST_CONFIG_MAP_SUFFIX_1)); + secondDependentResourceConfigMap + .setResourceDiscriminator( + new IndexDiscriminator(CONFIG_MAP_INDEX_2, FIRST_CONFIG_MAP_SUFFIX_2)); + return EventSourceInitializer.nameEventSources(eventSource); + } + + @Override + public KubernetesClient getKubernetesClient() { + return client; + } + + @Override + public void setKubernetesClient(KubernetesClient kubernetesClient) { + this.client = kubernetesClient; + firstDependentResourceConfigMap.setKubernetesClient(kubernetesClient); + secondDependentResourceConfigMap.setKubernetesClient(kubernetesClient); + } + + public static String configMapKey(ConfigMap configMap) { + return configMap.getMetadata().getName() + "#" + configMap.getMetadata().getNamespace(); + } + + public static String configMapKeyFromPrimary(IndexDiscriminatorTestCustomResource primary, + String nameSuffix) { + return primary.getMetadata().getName() + nameSuffix + "#" + + primary.getMetadata().getNamespace(); + } + + @Override + public DeleteControl cleanup(IndexDiscriminatorTestCustomResource resource, + Context context) { + firstDependentResourceConfigMap.delete(resource, context); + secondDependentResourceConfigMap.delete(resource, context); + return DeleteControl.defaultDelete(); + } +} diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/indexdiscriminator/IndexDiscriminatorTestSpec.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/indexdiscriminator/IndexDiscriminatorTestSpec.java new file mode 100644 index 0000000000..fcedd48abe --- /dev/null +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/indexdiscriminator/IndexDiscriminatorTestSpec.java @@ -0,0 +1,15 @@ +package io.javaoperatorsdk.operator.sample.indexdiscriminator; + +public class IndexDiscriminatorTestSpec { + + private String value; + + public String getValue() { + return value; + } + + public IndexDiscriminatorTestSpec setValue(String value) { + this.value = value; + return this; + } +} diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/indexdiscriminator/IndexDiscriminatorTestStatus.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/indexdiscriminator/IndexDiscriminatorTestStatus.java new file mode 100644 index 0000000000..d31c86e8de --- /dev/null +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/indexdiscriminator/IndexDiscriminatorTestStatus.java @@ -0,0 +1,5 @@ +package io.javaoperatorsdk.operator.sample.indexdiscriminator; + +public class IndexDiscriminatorTestStatus { + +} 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 4cdc2e457d..1adbfb9f95 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 @@ -31,7 +31,6 @@ protected ConfigMap desired(MultipleDependentResourceCustomResource primary, .withNewMetadata() .withName(primary.getConfigMapName(value)) .withNamespace(primary.getMetadata().getNamespace()) - .withLabels(Map.of(MultipleDependentResourceReconciler.LABEL, String.valueOf(value))) .endMetadata() .withData(data) .build(); 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 8cdbb81eba..49f5ee64c1 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 @@ -3,16 +3,14 @@ import java.util.Map; import java.util.concurrent.atomic.AtomicInteger; +import io.fabric8.kubernetes.api.model.ConfigMap; import io.fabric8.kubernetes.client.KubernetesClient; -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.EventSourceInitializer; -import io.javaoperatorsdk.operator.api.reconciler.Reconciler; -import io.javaoperatorsdk.operator.api.reconciler.UpdateControl; +import io.javaoperatorsdk.operator.api.config.informer.InformerConfiguration; +import io.javaoperatorsdk.operator.api.reconciler.*; import io.javaoperatorsdk.operator.junit.KubernetesClientAware; -import io.javaoperatorsdk.operator.processing.dependent.kubernetes.KubernetesDependentResourceConfig; +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 @@ -23,7 +21,6 @@ public class MultipleDependentResourceReconciler public static final int FIRST_CONFIG_MAP_ID = 1; public static final int SECOND_CONFIG_MAP_ID = 2; - public static final String LABEL = "multipledependentresource"; private final AtomicInteger numberOfExecutions = new AtomicInteger(0); private final MultipleDependentResourceConfigMap firstDependentResourceConfigMap; @@ -32,18 +29,19 @@ public class MultipleDependentResourceReconciler public MultipleDependentResourceReconciler() { firstDependentResourceConfigMap = new MultipleDependentResourceConfigMap(FIRST_CONFIG_MAP_ID); - secondDependentResourceConfigMap = new MultipleDependentResourceConfigMap(SECOND_CONFIG_MAP_ID); - firstDependentResourceConfigMap.configureWith( - new KubernetesDependentResourceConfig() - .setLabelSelector(getLabelSelector(FIRST_CONFIG_MAP_ID))); - secondDependentResourceConfigMap.configureWith( - new KubernetesDependentResourceConfig() - .setLabelSelector(getLabelSelector(SECOND_CONFIG_MAP_ID))); - } + secondDependentResourceConfigMap = new MultipleDependentResourceConfigMap(SECOND_CONFIG_MAP_ID); - private String getLabelSelector(int resourceId) { - return LABEL + "=" + resourceId; + 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 @@ -64,9 +62,13 @@ public int getNumberOfExecutions() { @Override public Map prepareEventSources( EventSourceContext context) { - return EventSourceInitializer.nameEventSources( - firstDependentResourceConfigMap.initEventSource(context), - secondDependentResourceConfigMap.initEventSource(context)); + InformerEventSource eventSource = + new InformerEventSource<>(InformerConfiguration.from(ConfigMap.class, context) + .build(), context); + firstDependentResourceConfigMap.configureWith(eventSource); + secondDependentResourceConfigMap.configureWith(eventSource); + + return EventSourceInitializer.nameEventSources(eventSource); } @Override diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/orderedmanageddependent/ConfigMapDependentResource1.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/orderedmanageddependent/ConfigMapDependentResource1.java index 14530cf17e..bf8d60d9c4 100644 --- a/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/orderedmanageddependent/ConfigMapDependentResource1.java +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/orderedmanageddependent/ConfigMapDependentResource1.java @@ -6,11 +6,14 @@ import io.fabric8.kubernetes.api.model.ConfigMap; import io.fabric8.kubernetes.api.model.ObjectMeta; import io.javaoperatorsdk.operator.api.reconciler.Context; +import io.javaoperatorsdk.operator.api.reconciler.ResourceIDMatcherDiscriminator; import io.javaoperatorsdk.operator.api.reconciler.dependent.ReconcileResult; import io.javaoperatorsdk.operator.processing.dependent.kubernetes.CRUDKubernetesDependentResource; import io.javaoperatorsdk.operator.processing.dependent.kubernetes.KubernetesDependent; +import io.javaoperatorsdk.operator.processing.event.ResourceID; -@KubernetesDependent(labelSelector = "dependent = cm1") +@KubernetesDependent(labelSelector = "dependent = cm1", + resourceDiscriminator = ConfigMapDependentResource1.CM1ResourceDiscriminator.class) public class ConfigMapDependentResource1 extends CRUDKubernetesDependentResource { @@ -42,4 +45,11 @@ protected ConfigMap desired(OrderedManagedDependentCustomResource primary, return configMap; } + public static class CM1ResourceDiscriminator + extends ResourceIDMatcherDiscriminator { + public CM1ResourceDiscriminator() { + super(p -> new ResourceID(p.getMetadata().getName() + "1", p.getMetadata().getNamespace())); + } + } + } diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/orderedmanageddependent/ConfigMapDependentResource2.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/orderedmanageddependent/ConfigMapDependentResource2.java index 35ae69586e..2b17d615b9 100644 --- a/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/orderedmanageddependent/ConfigMapDependentResource2.java +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/orderedmanageddependent/ConfigMapDependentResource2.java @@ -6,11 +6,14 @@ import io.fabric8.kubernetes.api.model.ConfigMap; import io.fabric8.kubernetes.api.model.ObjectMeta; import io.javaoperatorsdk.operator.api.reconciler.Context; +import io.javaoperatorsdk.operator.api.reconciler.ResourceIDMatcherDiscriminator; import io.javaoperatorsdk.operator.api.reconciler.dependent.ReconcileResult; import io.javaoperatorsdk.operator.processing.dependent.kubernetes.CRUDKubernetesDependentResource; import io.javaoperatorsdk.operator.processing.dependent.kubernetes.KubernetesDependent; +import io.javaoperatorsdk.operator.processing.event.ResourceID; -@KubernetesDependent(labelSelector = "dependent = cm2") +@KubernetesDependent(labelSelector = "dependent = cm2", + resourceDiscriminator = ConfigMapDependentResource2.CM2ResourceDiscriminator.class) public class ConfigMapDependentResource2 extends CRUDKubernetesDependentResource { @@ -42,4 +45,11 @@ protected ConfigMap desired(OrderedManagedDependentCustomResource primary, return configMap; } + public static class CM2ResourceDiscriminator + extends ResourceIDMatcherDiscriminator { + public CM2ResourceDiscriminator() { + super(p -> new ResourceID(p.getMetadata().getName() + "2", p.getMetadata().getNamespace())); + } + } + } diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/standalonedependent/StandaloneDependentTestReconciler.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/standalonedependent/StandaloneDependentTestReconciler.java index af6b2d7e25..2dace670ba 100644 --- a/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/standalonedependent/StandaloneDependentTestReconciler.java +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/standalonedependent/StandaloneDependentTestReconciler.java @@ -47,7 +47,7 @@ public UpdateControl reconcile( StandaloneDependentTestCustomResource primary, Context context) { deploymentDependent.reconcile(primary, context); - Optional deployment = deploymentDependent.getSecondaryResource(primary); + Optional deployment = context.getSecondaryResource(Deployment.class); if (deployment.isEmpty()) { throw new IllegalStateException("Resource should not be empty after reconcile."); } diff --git a/sample-operators/webpage/src/main/java/io/javaoperatorsdk/operator/sample/WebPageDependentsWorkflowReconciler.java b/sample-operators/webpage/src/main/java/io/javaoperatorsdk/operator/sample/WebPageDependentsWorkflowReconciler.java index 6986180b89..6b81a9d6e3 100644 --- a/sample-operators/webpage/src/main/java/io/javaoperatorsdk/operator/sample/WebPageDependentsWorkflowReconciler.java +++ b/sample-operators/webpage/src/main/java/io/javaoperatorsdk/operator/sample/WebPageDependentsWorkflowReconciler.java @@ -69,7 +69,7 @@ public UpdateControl reconcile(WebPage webPage, Context contex webPage.setStatus( createStatus( - configMapDR.getSecondaryResource(webPage).orElseThrow().getMetadata().getName())); + context.getSecondaryResource(ConfigMap.class).orElseThrow().getMetadata().getName())); return UpdateControl.patchStatus(webPage); } @@ -93,6 +93,4 @@ private void initDependentResources(KubernetesClient client) { }); } - - } diff --git a/sample-operators/webpage/src/main/java/io/javaoperatorsdk/operator/sample/WebPageStandaloneDependentsReconciler.java b/sample-operators/webpage/src/main/java/io/javaoperatorsdk/operator/sample/WebPageStandaloneDependentsReconciler.java index 0bb692a4d0..b99e130135 100644 --- a/sample-operators/webpage/src/main/java/io/javaoperatorsdk/operator/sample/WebPageStandaloneDependentsReconciler.java +++ b/sample-operators/webpage/src/main/java/io/javaoperatorsdk/operator/sample/WebPageStandaloneDependentsReconciler.java @@ -65,7 +65,7 @@ public UpdateControl reconcile(WebPage webPage, Context contex webPage.setStatus( createStatus( - configMapDR.getSecondaryResource(webPage).orElseThrow().getMetadata().getName())); + context.getSecondaryResource(ConfigMap.class).orElseThrow().getMetadata().getName())); return UpdateControl.patchStatus(webPage); } From 99f4b10f677423c30a7d973fe3beb278fdcc525b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Attila=20M=C3=A9sz=C3=A1ros?= Date: Thu, 29 Sep 2022 16:31:43 +0200 Subject: [PATCH 58/67] bulk dependent resources (#1448) --- .../api/reconciler/ResourceDiscriminator.java | 1 - .../reconciler/dependent/ReconcileResult.java | 50 ++++++-- .../dependent/AbstractDependentResource.java | 104 ++++++++++++++-- .../dependent/BulkDependentResource.java | 35 ++++++ .../processing/dependent/BulkUpdater.java | 20 +++ .../dependent/DesiredEqualsMatcher.java | 6 + .../processing/dependent/Matcher.java | 15 +++ .../processing/dependent/Updater.java | 4 + .../GenericKubernetesResourceMatcher.java | 103 +++++++++++----- .../KubernetesDependentResource.java | 30 ++++- .../KubernetesDependentResourceConfig.java | 7 +- .../workflow/WorkflowCleanupExecutor.java | 1 - .../bulkdependent/BulkDependentDeleterIT.java | 19 +++ .../bulkdependent/BulkDependentTestBase.java | 114 ++++++++++++++++++ .../BulkExternalDependentIT.java | 56 +++++++++ .../bulkdependent/ManagedBulkDependentIT.java | 20 +++ .../StandaloneBulkDependentIT.java | 19 +++ .../BulkDependentTestCustomResource.java | 15 +++ .../bulkdependent/BulkDependentTestSpec.java | 25 ++++ .../CRUDConfigMapBulkDependentResource.java | 7 ++ ...ConfigMapDeleterBulkDependentResource.java | 72 +++++++++++ .../ManagedBulkDependentReconciler.java | 25 ++++ .../ManagedDeleterBulkReconciler.java | 20 +++ .../StandaloneBulkDependentReconciler.java | 58 +++++++++ .../ExternalBulkDependentResource.java | 101 ++++++++++++++++ .../ExternalBulkResourceReconciler.java | 19 +++ .../external/ExternalResource.java | 37 ++++++ .../external/ExternalServiceMock.java | 39 ++++++ .../MultipleDependentResourceReconciler.java | 1 - 29 files changed, 956 insertions(+), 67 deletions(-) create mode 100644 operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/BulkDependentResource.java create mode 100644 operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/BulkUpdater.java create mode 100644 operator-framework/src/test/java/io/javaoperatorsdk/operator/bulkdependent/BulkDependentDeleterIT.java create mode 100644 operator-framework/src/test/java/io/javaoperatorsdk/operator/bulkdependent/BulkDependentTestBase.java create mode 100644 operator-framework/src/test/java/io/javaoperatorsdk/operator/bulkdependent/BulkExternalDependentIT.java create mode 100644 operator-framework/src/test/java/io/javaoperatorsdk/operator/bulkdependent/ManagedBulkDependentIT.java create mode 100644 operator-framework/src/test/java/io/javaoperatorsdk/operator/bulkdependent/StandaloneBulkDependentIT.java create mode 100644 operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/bulkdependent/BulkDependentTestCustomResource.java create mode 100644 operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/bulkdependent/BulkDependentTestSpec.java create mode 100644 operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/bulkdependent/CRUDConfigMapBulkDependentResource.java create mode 100644 operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/bulkdependent/ConfigMapDeleterBulkDependentResource.java create mode 100644 operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/bulkdependent/ManagedBulkDependentReconciler.java create mode 100644 operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/bulkdependent/ManagedDeleterBulkReconciler.java create mode 100644 operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/bulkdependent/StandaloneBulkDependentReconciler.java create mode 100644 operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/bulkdependent/external/ExternalBulkDependentResource.java create mode 100644 operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/bulkdependent/external/ExternalBulkResourceReconciler.java create mode 100644 operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/bulkdependent/external/ExternalResource.java create mode 100644 operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/bulkdependent/external/ExternalServiceMock.java diff --git a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/reconciler/ResourceDiscriminator.java b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/reconciler/ResourceDiscriminator.java index 072e7d8078..eb947fa440 100644 --- a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/reconciler/ResourceDiscriminator.java +++ b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/reconciler/ResourceDiscriminator.java @@ -7,5 +7,4 @@ public interface ResourceDiscriminator { Optional distinguish(Class resource, P primary, Context

context); - } diff --git a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/reconciler/dependent/ReconcileResult.java b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/reconciler/dependent/ReconcileResult.java index c83da1c8ea..468e14e8ea 100644 --- a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/reconciler/dependent/ReconcileResult.java +++ b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/reconciler/dependent/ReconcileResult.java @@ -1,14 +1,14 @@ package io.javaoperatorsdk.operator.api.reconciler.dependent; -import java.util.Optional; +import java.util.*; +import java.util.stream.Collectors; import io.fabric8.kubernetes.api.model.HasMetadata; import io.javaoperatorsdk.operator.processing.event.ResourceID; public class ReconcileResult { - private final R resource; - private final Operation operation; + private final Map resourceOperations; public static ReconcileResult resourceCreated(T resource) { return new ReconcileResult<>(resource, Operation.CREATED); @@ -22,25 +22,49 @@ public static ReconcileResult noOperation(T resource) { return new ReconcileResult<>(resource, Operation.NONE); } + @SafeVarargs + public static ReconcileResult aggregatedResult(ReconcileResult... results) { + if (results == null) { + throw new IllegalArgumentException("Should provide results to aggregate"); + } + if (results.length == 1) { + return results[0]; + } + final Map operations = new HashMap<>(results.length); + for (ReconcileResult res : results) { + res.getSingleResource().ifPresent(r -> operations.put(r, res.getSingleOperation())); + } + return new ReconcileResult<>(operations); + } + @Override public String toString() { - return getResource() - .map(r -> r instanceof HasMetadata ? ResourceID.fromResource((HasMetadata) r) : r) - .orElse("no resource") - + " -> " + operation; + return resourceOperations.entrySet().stream().collect(Collectors.toMap( + e -> e instanceof HasMetadata ? ResourceID.fromResource((HasMetadata) e) : e, + Map.Entry::getValue)) + .toString(); } private ReconcileResult(R resource, Operation operation) { - this.resource = resource; - this.operation = operation; + resourceOperations = resource != null ? Map.of(resource, operation) : Collections.emptyMap(); + } + + private ReconcileResult(Map operations) { + resourceOperations = Collections.unmodifiableMap(operations); + } + + public Optional getSingleResource() { + return resourceOperations.entrySet().stream().findFirst().map(Map.Entry::getKey); } - public Optional getResource() { - return Optional.ofNullable(resource); + public Operation getSingleOperation() { + return resourceOperations.entrySet().stream().findFirst().map(Map.Entry::getValue) + .orElseThrow(); } - public Operation getOperation() { - return operation; + @SuppressWarnings("unused") + public Map getResourceOperations() { + return resourceOperations; } public enum Operation { 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 1abfb3df4b..078fc60b66 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,5 +1,7 @@ package io.javaoperatorsdk.operator.processing.dependent; +import java.util.ArrayList; +import java.util.List; import java.util.Optional; import org.slf4j.Logger; @@ -20,25 +22,73 @@ public abstract class AbstractDependentResource protected final boolean creatable = this instanceof Creator; protected final boolean updatable = this instanceof Updater; + protected final boolean bulk = this instanceof BulkDependentResource; protected Creator creator; protected Updater updater; + protected BulkDependentResource bulkDependentResource; - private ResourceDiscriminator resourceDiscriminator; + private final List> resourceDiscriminator = new ArrayList<>(1); @SuppressWarnings("unchecked") public AbstractDependentResource() { creator = creatable ? (Creator) this : null; updater = updatable ? (Updater) this : null; + + bulkDependentResource = bulk ? (BulkDependentResource) this : null; } @Override public ReconcileResult reconcile(P primary, Context

context) { - Optional maybeActual = getSecondaryResource(primary, context); + if (bulk) { + final var count = bulkDependentResource.count(primary, context); + deleteBulkResourcesIfRequired(count, lastKnownBulkSize(), primary, context); + adjustDiscriminators(count); + @SuppressWarnings("unchecked") + final ReconcileResult[] results = new ReconcileResult[count]; + for (int i = 0; i < count; i++) { + results[i] = reconcileIndexAware(primary, i, context); + } + return ReconcileResult.aggregatedResult(results); + } else { + return reconcileIndexAware(primary, 0, context); + } + } + + protected void deleteBulkResourcesIfRequired(int targetCount, int actualCount, P primary, + Context

context) { + if (targetCount >= actualCount) { + return; + } + for (int i = targetCount; i < actualCount; i++) { + var resource = getSecondaryResourceIndexAware(primary, i, context); + var index = i; + resource.ifPresent( + r -> bulkDependentResource.deleteBulkResourceWithIndex(primary, r, index, context)); + } + } + + private void adjustDiscriminators(int count) { + if (resourceDiscriminator.size() == count) { + return; + } + if (resourceDiscriminator.size() < count) { + for (int i = resourceDiscriminator.size(); i < count; i++) { + resourceDiscriminator.add(bulkDependentResource.getResourceDiscriminator(i)); + } + } + if (resourceDiscriminator.size() > count) { + resourceDiscriminator.subList(count, resourceDiscriminator.size()).clear(); + } + } + + protected ReconcileResult reconcileIndexAware(P primary, int i, Context

context) { + Optional maybeActual = bulk ? getSecondaryResourceIndexAware(primary, i, context) + : getSecondaryResource(primary, context); if (creatable || updatable) { if (maybeActual.isEmpty()) { if (creatable) { - var desired = desired(primary, context); + var desired = desiredIndexAware(primary, i, context); throwIfNull(desired, primary, "Desired"); logForOperation("Creating", primary, desired); var createdResource = handleCreate(desired, primary, context); @@ -47,9 +97,15 @@ public ReconcileResult reconcile(P primary, Context

context) { } else { final var actual = maybeActual.get(); if (updatable) { - final var match = updater.match(actual, primary, context); + final Matcher.Result match; + if (bulk) { + match = updater.match(actual, primary, i, context); + } else { + match = updater.match(actual, primary, context); + } if (!match.matched()) { - final var desired = match.computedDesired().orElse(desired(primary, context)); + final var desired = + match.computedDesired().orElse(desiredIndexAware(primary, i, context)); throwIfNull(desired, primary, "Desired"); logForOperation("Updating", primary, desired); var updatedResource = handleUpdate(actual, desired, primary, context); @@ -67,9 +123,18 @@ public ReconcileResult reconcile(P primary, Context

context) { return ReconcileResult.noOperation(maybeActual.orElse(null)); } + private R desiredIndexAware(P primary, int i, Context

context) { + return bulk ? desired(primary, i, context) + : desired(primary, context); + } + public Optional getSecondaryResource(P primary, Context

context) { - return resourceDiscriminator == null ? context.getSecondaryResource(resourceType()) - : resourceDiscriminator.distinguish(resourceType(), primary, context); + return resourceDiscriminator.isEmpty() ? context.getSecondaryResource(resourceType()) + : resourceDiscriminator.get(0).distinguish(resourceType(), primary, context); + } + + protected Optional getSecondaryResourceIndexAware(P primary, int index, Context

context) { + return context.getSecondaryResource(resourceType(), resourceDiscriminator.get(index)); } private void throwIfNull(R desired, P primary, String descriptor) { @@ -97,7 +162,7 @@ protected R handleCreate(R desired, P primary, Context

context) { } /** - * Allows sub-classes to perform additional processing (e.g. caching) on the created resource if + * Allows subclasses to perform additional processing (e.g. caching) on the created resource if * needed. * * @param primaryResourceId the {@link ResourceID} of the primary resource associated with the @@ -129,12 +194,29 @@ protected R desired(P primary, Context

context) { "desired method must be implemented if this DependentResource can be created and/or updated"); } - public void setResourceDiscriminator( + protected R desired(P primary, int index, Context

context) { + throw new IllegalStateException( + "Must be implemented for bulk DependentResource creation"); + } + + public AbstractDependentResource setResourceDiscriminator( ResourceDiscriminator resourceDiscriminator) { - this.resourceDiscriminator = resourceDiscriminator; + if (resourceDiscriminator != null) { + this.resourceDiscriminator.add(resourceDiscriminator); + } + return this; } public ResourceDiscriminator getResourceDiscriminator() { - return resourceDiscriminator; + if (this.resourceDiscriminator.isEmpty()) { + return null; + } else { + return this.resourceDiscriminator.get(0); + } } + + protected int lastKnownBulkSize() { + return resourceDiscriminator.size(); + } + } diff --git a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/BulkDependentResource.java b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/BulkDependentResource.java new file mode 100644 index 0000000000..1f2688f5cb --- /dev/null +++ b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/BulkDependentResource.java @@ -0,0 +1,35 @@ +package io.javaoperatorsdk.operator.processing.dependent; + +import io.fabric8.kubernetes.api.model.HasMetadata; +import io.javaoperatorsdk.operator.api.reconciler.Context; +import io.javaoperatorsdk.operator.api.reconciler.ResourceDiscriminator; +import io.javaoperatorsdk.operator.api.reconciler.dependent.Deleter; + +/** + * Manages dynamic number of resources created for a primary resource. Since the point of a bulk + * dependent resource is to manage the number of secondary resources dynamically it implement + * {@link Creator} and {@link Deleter} interfaces out of the box. A concrete dependent resource can + * implement additionally also {@link Updater}. + */ +public interface BulkDependentResource extends Creator, Deleter

{ + + /** + * @return number of resources to create + */ + int count(P primary, Context

context); + + R desired(P primary, int index, Context

context); + + /** + * Used to delete resource if the desired count is lower than the actual count of a resource. + * + * @param primary resource + * @param resource actual resource from the cache for the index + * @param i index of the resource + * @param context actual context + */ + void deleteBulkResourceWithIndex(P primary, R resource, int i, Context

context); + + ResourceDiscriminator getResourceDiscriminator(int index); + +} diff --git a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/BulkUpdater.java b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/BulkUpdater.java new file mode 100644 index 0000000000..9c00b47d0c --- /dev/null +++ b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/BulkUpdater.java @@ -0,0 +1,20 @@ +package io.javaoperatorsdk.operator.processing.dependent; + +import io.fabric8.kubernetes.api.model.HasMetadata; +import io.javaoperatorsdk.operator.api.reconciler.Context; + +/** + * Helper for the Bulk Dependent Resources to make it more explicit that bulk needs to only + * implement the index aware match method. + * + * @param secondary resource type + * @param

primary resource type + */ +public interface BulkUpdater extends Updater { + + default Matcher.Result match(R actualResource, P primary, Context

context) { + throw new IllegalStateException(); + } + + Matcher.Result match(R actualResource, P primary, int index, Context

context); +} diff --git a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/DesiredEqualsMatcher.java b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/DesiredEqualsMatcher.java index 459d7951d6..1d3b34a47b 100644 --- a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/DesiredEqualsMatcher.java +++ b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/DesiredEqualsMatcher.java @@ -16,4 +16,10 @@ public Result match(R actualResource, P primary, Context

context) { var desired = abstractDependentResource.desired(primary, context); return Result.computed(actualResource.equals(desired), desired); } + + @Override + public Result match(R actualResource, P primary, int index, Context

context) { + var desired = abstractDependentResource.desired(primary, index, context); + return Result.computed(actualResource.equals(desired), desired); + } } diff --git a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/Matcher.java b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/Matcher.java index 750fe89cbf..835f76ab3a 100644 --- a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/Matcher.java +++ b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/Matcher.java @@ -95,4 +95,19 @@ public Optional computedDesired() { * {@link Result#computed(boolean, Object)}) */ Result match(R actualResource, P primary, Context

context); + + /** + * Determines whether the specified secondary resource matches the desired state with target index + * of a bulk resource as defined from the specified primary resource, given the specified + * {@link Context}. + * + * @param actualResource the resource we want to determine whether it's matching the desired state + * @param primary the primary resource from which the desired state is inferred + * @param context the context in which the resource is being matched + * @return a {@link Result} encapsulating whether the resource matched its desired state and this + * associated state if it was computed as part of the matching process. Use the static + * convenience methods ({@link Result#nonComputed(boolean)} and + * {@link Result#computed(boolean, Object)}) + */ + Result match(R actualResource, P primary, int index, Context

context); } diff --git a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/Updater.java b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/Updater.java index 828f9ad785..06b3cb52f6 100644 --- a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/Updater.java +++ b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/Updater.java @@ -8,4 +8,8 @@ public interface Updater { R update(R actual, R desired, P primary, Context

context); Result match(R actualResource, P primary, Context

context); + + default Result match(R actualResource, P primary, int index, Context

context) { + throw new IllegalStateException("Implement this for bulk matching"); + } } 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 e294b1c938..bb066b5b24 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 @@ -24,17 +24,42 @@ private GenericKubernetesResourceMatcher(KubernetesDependentResource depen static Matcher matcherFor( Class resourceType, KubernetesDependentResource dependentResource) { if (Secret.class.isAssignableFrom(resourceType)) { - return (actual, primary, context) -> { - final var desired = dependentResource.desired(primary, context); - return Result.computed( - ResourceComparators.compareSecretData((Secret) desired, (Secret) actual), desired); + return new Matcher<>() { + @Override + public Result match(R actualResource, P primary, Context

context) { + final var desired = dependentResource.desired(primary, context); + return Result.computed( + ResourceComparators.compareSecretData((Secret) desired, (Secret) actualResource), + desired); + } + + @Override + public Result match(R actualResource, P primary, int index, Context

context) { + final var desired = dependentResource.desired(primary, index, context); + return Result.computed( + ResourceComparators.compareSecretData((Secret) desired, (Secret) actualResource), + desired); + } }; } else if (ConfigMap.class.isAssignableFrom(resourceType)) { - return (actual, primary, context) -> { - final var desired = dependentResource.desired(primary, context); - return Result.computed( - ResourceComparators.compareConfigMapData((ConfigMap) desired, (ConfigMap) actual), - desired); + return new Matcher<>() { + @Override + public Result match(R actualResource, P primary, Context

context) { + final var desired = dependentResource.desired(primary, context); + return Result.computed( + ResourceComparators.compareConfigMapData((ConfigMap) desired, + (ConfigMap) actualResource), + desired); + } + + @Override + public Result match(R actualResource, P primary, int index, Context

context) { + final var desired = dependentResource.desired(primary, index, context); + return Result.computed( + ResourceComparators.compareConfigMapData((ConfigMap) desired, + (ConfigMap) actualResource), + desired); + } }; } else { return new GenericKubernetesResourceMatcher(dependentResource); @@ -43,32 +68,18 @@ static Matcher matcherFor( @Override public Result match(R actualResource, P primary, Context

context) { - return match(dependentResource, actualResource, primary, context, false); + var desired = dependentResource.desired(primary, context); + return match(desired, actualResource, false); } - /** - * Determines whether the specified actual resource matches the desired state defined by the - * specified {@link KubernetesDependentResource} based on the observed state of the associated - * specified primary resource. - * - * @param dependentResource the {@link KubernetesDependentResource} implementation used to - * computed the desired state associated with the specified primary resource - * @param actualResource the observed dependent resource for which we want to determine whether it - * matches the desired state or not - * @param primary the primary resource from which we want to compute the desired state - * @param context the {@link Context} instance within which this method is called - * @param considerMetadata {@code true} to consider the metadata of the actual resource when - * determining if it matches the desired state, {@code false} if matching should occur only - * considering the spec of the resources - * @return a {@link io.javaoperatorsdk.operator.processing.dependent.Matcher.Result} object - * @param the type of resource we want to determine whether they match or not - * @param

the type of primary resources associated with the secondary resources we want to - * match - */ - public static Result match( - KubernetesDependentResource dependentResource, R actualResource, P primary, - Context

context, boolean considerMetadata) { - final var desired = dependentResource.desired(primary, context); + @Override + public Result match(R actualResource, P primary, int index, Context

context) { + var desired = dependentResource.desired(primary, index, context); + return match(desired, actualResource, false); + } + + public static Result match( + R desired, R actualResource, boolean considerMetadata) { if (considerMetadata) { final var desiredMetadata = desired.getMetadata(); final var actualMetadata = actualResource.getMetadata(); @@ -95,4 +106,30 @@ public static Result match( } return Result.computed(true, desired); } + + /** + * Determines whether the specified actual resource matches the desired state defined by the + * specified {@link KubernetesDependentResource} based on the observed state of the associated + * specified primary resource. + * + * @param dependentResource the {@link KubernetesDependentResource} implementation used to + * computed the desired state associated with the specified primary resource + * @param actualResource the observed dependent resource for which we want to determine whether it + * matches the desired state or not + * @param primary the primary resource from which we want to compute the desired state + * @param context the {@link Context} instance within which this method is called + * @param considerMetadata {@code true} to consider the metadata of the actual resource when + * determining if it matches the desired state, {@code false} if matching should occur only + * considering the spec of the resources + * @return a {@link io.javaoperatorsdk.operator.processing.dependent.Matcher.Result} object + * @param the type of resource we want to determine whether they match or not + * @param

the type of primary resources associated with the secondary resources we want to + * match + */ + public static Result match( + KubernetesDependentResource dependentResource, R actualResource, P primary, + Context

context, boolean considerMetadata) { + final var desired = dependentResource.desired(primary, context); + return match(desired, actualResource, considerMetadata); + } } 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 328a061e6b..3738a2e7d2 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 @@ -134,8 +134,21 @@ public Result match(R actualResource, P primary, Context

context) { return matcher.match(actualResource, primary, context); } + public Result match(R actualResource, P primary, int index, Context

context) { + return matcher.match(actualResource, primary, index, context); + } + public void delete(P primary, Context

context) { - getSecondaryResource(primary, context).ifPresent(r -> client.resource(r).delete()); + if (bulk) { + deleteBulkResourcesIfRequired(0, lastKnownBulkSize(), primary, context); + } else { + var resource = getSecondaryResource(primary, context); + resource.ifPresent(r -> client.resource(r).delete()); + } + } + + public void deleteBulkResourceWithIndex(P primary, R resource, int i, Context

context) { + client.resource(resource).delete(); } @SuppressWarnings("unchecked") @@ -149,9 +162,7 @@ protected Resource prepare(R desired, P primary, String actionName) { } else if (useDefaultAnnotationsToIdentifyPrimary()) { addDefaultSecondaryToPrimaryMapperAnnotations(desired, primary); } - Class targetClass = (Class) desired.getClass(); - return client.resources(targetClass).inNamespace(desired.getMetadata().getNamespace()) - .resource(desired); + return client.resource(desired).inNamespace(desired.getMetadata().getNamespace()); } @Override @@ -163,8 +174,10 @@ protected InformerEventSource createEventSource(EventSourceContext

cont onUpdateFilter = kubernetesDependentResourceConfig.onUpdateFilter(); onDeleteFilter = kubernetesDependentResourceConfig.onDeleteFilter(); genericFilter = kubernetesDependentResourceConfig.genericFilter(); - setResourceDiscriminator(kubernetesDependentResourceConfig.getResourceDiscriminator()); - + var discriminator = kubernetesDependentResourceConfig.getResourceDiscriminator(); + if (discriminator != null) { + setResourceDiscriminator(discriminator); + } configureWith(kubernetesDependentResourceConfig.labelSelector(), kubernetesDependentResourceConfig.namespaces(), !kubernetesDependentResourceConfig.wereNamespacesConfigured(), context); @@ -215,6 +228,11 @@ protected R desired(P primary, Context

context) { return super.desired(primary, context); } + @Override + protected R desired(P primary, int index, Context

context) { + return super.desired(primary, index, context); + } + private void prepareEventFiltering(R desired, ResourceID resourceID) { eventSource().prepareForCreateOrUpdateEventFiltering(resourceID, desired); } diff --git a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/kubernetes/KubernetesDependentResourceConfig.java b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/kubernetes/KubernetesDependentResourceConfig.java index e2a2c0f684..c7674dd1a7 100644 --- a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/kubernetes/KubernetesDependentResourceConfig.java +++ b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/kubernetes/KubernetesDependentResourceConfig.java @@ -75,7 +75,6 @@ public OnAddFilter onAddFilter() { return onAddFilter; } - public OnUpdateFilter onUpdateFilter() { return onUpdateFilter; } @@ -92,4 +91,10 @@ public GenericFilter genericFilter() { public ResourceDiscriminator getResourceDiscriminator() { return resourceDiscriminator; } + + public

KubernetesDependentResourceConfig setResourceDiscriminator( + ResourceDiscriminator resourceDiscriminator) { + this.resourceDiscriminator = resourceDiscriminator; + return this; + } } diff --git a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/workflow/WorkflowCleanupExecutor.java b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/workflow/WorkflowCleanupExecutor.java index 45541b91d6..ac08d2d874 100644 --- a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/workflow/WorkflowCleanupExecutor.java +++ b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/workflow/WorkflowCleanupExecutor.java @@ -125,7 +125,6 @@ public void run() { } } - private synchronized void handleDependentCleaned( DependentResourceNode dependentResourceNode) { var dependOns = dependentResourceNode.getDependsOn(); diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/bulkdependent/BulkDependentDeleterIT.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/bulkdependent/BulkDependentDeleterIT.java new file mode 100644 index 0000000000..a934bdd1f3 --- /dev/null +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/bulkdependent/BulkDependentDeleterIT.java @@ -0,0 +1,19 @@ +package io.javaoperatorsdk.operator.bulkdependent; + +import org.junit.jupiter.api.extension.RegisterExtension; + +import io.javaoperatorsdk.operator.junit.LocallyRunOperatorExtension; +import io.javaoperatorsdk.operator.sample.bulkdependent.ManagedDeleterBulkReconciler; + +public class BulkDependentDeleterIT extends BulkDependentTestBase { + + @RegisterExtension + LocallyRunOperatorExtension extension = + LocallyRunOperatorExtension.builder().withReconciler(new ManagedDeleterBulkReconciler()) + .build(); + + @Override + LocallyRunOperatorExtension extension() { + return extension; + } +} diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/bulkdependent/BulkDependentTestBase.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/bulkdependent/BulkDependentTestBase.java new file mode 100644 index 0000000000..605731623c --- /dev/null +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/bulkdependent/BulkDependentTestBase.java @@ -0,0 +1,114 @@ +package io.javaoperatorsdk.operator.bulkdependent; + +import java.time.Duration; + +import org.junit.jupiter.api.Test; + +import io.fabric8.kubernetes.api.model.ObjectMeta; +import io.javaoperatorsdk.operator.junit.LocallyRunOperatorExtension; +import io.javaoperatorsdk.operator.sample.bulkdependent.BulkDependentTestCustomResource; +import io.javaoperatorsdk.operator.sample.bulkdependent.BulkDependentTestSpec; +import io.javaoperatorsdk.operator.sample.bulkdependent.ConfigMapDeleterBulkDependentResource; + +import static io.javaoperatorsdk.operator.sample.bulkdependent.ConfigMapDeleterBulkDependentResource.LABEL_KEY; +import static io.javaoperatorsdk.operator.sample.bulkdependent.ConfigMapDeleterBulkDependentResource.LABEL_VALUE; +import static org.assertj.core.api.Assertions.assertThat; +import static org.awaitility.Awaitility.await; + +public abstract class BulkDependentTestBase { + + public static final String TEST_RESOURCE_NAME = "test"; + public static final int INITIAL_NUMBER_OF_CONFIG_MAPS = 3; + public static final String INITIAL_ADDITIONAL_DATA = "initialData"; + public static final String NEW_VERSION_OF_ADDITIONAL_DATA = "newVersionOfAdditionalData"; + + @Test + public void managesBulkConfigMaps() { + extension().create(testResource()); + assertNumberOfConfigMaps(3); + + updateSpecWithNumber(1); + assertNumberOfConfigMaps(1); + + updateSpecWithNumber(5); + assertNumberOfConfigMaps(5); + + extension().delete(testResource()); + assertNumberOfConfigMaps(0); + } + + @Test + public void updatesData() { + extension().create(testResource()); + assertNumberOfConfigMaps(3); + assertAdditionalDataOnConfigMaps(INITIAL_ADDITIONAL_DATA); + + updateSpecWithNewAdditionalData(NEW_VERSION_OF_ADDITIONAL_DATA); + assertAdditionalDataOnConfigMaps(NEW_VERSION_OF_ADDITIONAL_DATA); + } + + private void assertNumberOfConfigMaps(int n) { + // this test was failing with a lower timeout on GitHub, probably the garbage collection was + // slower there. + await().atMost(Duration.ofSeconds(30)) + .untilAsserted(() -> { + var cms = + extension().getKubernetesClient().configMaps().inNamespace(extension().getNamespace()) + .withLabel(LABEL_KEY, LABEL_VALUE) + .list().getItems(); + assertThat(cms).withFailMessage("Number of items is still: " + cms.size()) + .hasSize(n); + }); + } + + private void assertAdditionalDataOnConfigMaps(String expectedValue) { + await().atMost(Duration.ofSeconds(30)) + .untilAsserted(() -> { + var cms = + extension().getKubernetesClient().configMaps().inNamespace(extension().getNamespace()) + .withLabel(LABEL_KEY, LABEL_VALUE) + .list().getItems(); + cms.forEach(cm -> { + assertThat(cm.getData().get(ConfigMapDeleterBulkDependentResource.ADDITIONAL_DATA_KEY)) + .isEqualTo(expectedValue); + }); + }); + } + + public static BulkDependentTestCustomResource testResource() { + BulkDependentTestCustomResource cr = new BulkDependentTestCustomResource(); + cr.setMetadata(new ObjectMeta()); + cr.getMetadata().setName(TEST_RESOURCE_NAME); + cr.setSpec(new BulkDependentTestSpec()); + cr.getSpec().setNumberOfResources(INITIAL_NUMBER_OF_CONFIG_MAPS); + cr.getSpec().setAdditionalData(INITIAL_ADDITIONAL_DATA); + return cr; + } + + private void updateSpecWithNewAdditionalData(String data) { + var resource = testResource(); + resource.getSpec().setAdditionalData(data); + extension().replace(resource); + } + + public static void updateSpecWithNewAdditionalData(LocallyRunOperatorExtension extension, + String data) { + var resource = testResource(); + resource.getSpec().setAdditionalData(data); + extension.replace(resource); + } + + private void updateSpecWithNumber(int n) { + var resource = testResource(); + resource.getSpec().setNumberOfResources(n); + extension().replace(resource); + } + + public static void updateSpecWithNumber(LocallyRunOperatorExtension extension, int n) { + var resource = testResource(); + resource.getSpec().setNumberOfResources(n); + extension.replace(resource); + } + + abstract LocallyRunOperatorExtension extension(); +} diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/bulkdependent/BulkExternalDependentIT.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/bulkdependent/BulkExternalDependentIT.java new file mode 100644 index 0000000000..29f66e8205 --- /dev/null +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/bulkdependent/BulkExternalDependentIT.java @@ -0,0 +1,56 @@ +package io.javaoperatorsdk.operator.bulkdependent; + +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.RegisterExtension; + +import io.javaoperatorsdk.operator.junit.LocallyRunOperatorExtension; +import io.javaoperatorsdk.operator.sample.bulkdependent.external.ExternalBulkResourceReconciler; +import io.javaoperatorsdk.operator.sample.bulkdependent.external.ExternalServiceMock; + +import static io.javaoperatorsdk.operator.bulkdependent.BulkDependentTestBase.*; +import static org.assertj.core.api.Assertions.assertThat; +import static org.awaitility.Awaitility.await; + +class BulkExternalDependentIT { + + @RegisterExtension + LocallyRunOperatorExtension extension = + LocallyRunOperatorExtension.builder().withReconciler(new ExternalBulkResourceReconciler()) + .build(); + + ExternalServiceMock externalServiceMock = ExternalServiceMock.getInstance(); + + @Test + void managesExternalBulkResources() { + extension.create(testResource()); + assertResourceNumberAndData(3, INITIAL_ADDITIONAL_DATA); + + updateSpecWithNumber(extension, 1); + assertResourceNumberAndData(1, INITIAL_ADDITIONAL_DATA); + + updateSpecWithNumber(extension, 5); + assertResourceNumberAndData(5, INITIAL_ADDITIONAL_DATA); + + extension.delete(testResource()); + assertResourceNumberAndData(0, INITIAL_ADDITIONAL_DATA); + } + + + @Test + void handlesResourceUpdates() { + extension.create(testResource()); + assertResourceNumberAndData(3, INITIAL_ADDITIONAL_DATA); + + updateSpecWithNewAdditionalData(extension, NEW_VERSION_OF_ADDITIONAL_DATA); + assertResourceNumberAndData(3, NEW_VERSION_OF_ADDITIONAL_DATA); + } + + private void assertResourceNumberAndData(int n, String data) { + await().untilAsserted(() -> { + var resources = externalServiceMock.listResources(); + assertThat(resources).hasSize(n); + assertThat(resources).allMatch(r -> r.getData().equals(data)); + }); + } + +} diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/bulkdependent/ManagedBulkDependentIT.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/bulkdependent/ManagedBulkDependentIT.java new file mode 100644 index 0000000000..7f074ac8f5 --- /dev/null +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/bulkdependent/ManagedBulkDependentIT.java @@ -0,0 +1,20 @@ +package io.javaoperatorsdk.operator.bulkdependent; + +import org.junit.jupiter.api.extension.RegisterExtension; + +import io.javaoperatorsdk.operator.junit.LocallyRunOperatorExtension; +import io.javaoperatorsdk.operator.sample.bulkdependent.ManagedBulkDependentReconciler; + +class ManagedBulkDependentIT extends BulkDependentTestBase { + + @RegisterExtension + LocallyRunOperatorExtension extension = + LocallyRunOperatorExtension.builder().withReconciler(new ManagedBulkDependentReconciler()) + .build(); + + + @Override + LocallyRunOperatorExtension extension() { + return extension; + } +} diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/bulkdependent/StandaloneBulkDependentIT.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/bulkdependent/StandaloneBulkDependentIT.java new file mode 100644 index 0000000000..683cc1662b --- /dev/null +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/bulkdependent/StandaloneBulkDependentIT.java @@ -0,0 +1,19 @@ +package io.javaoperatorsdk.operator.bulkdependent; + +import org.junit.jupiter.api.extension.RegisterExtension; + +import io.javaoperatorsdk.operator.junit.LocallyRunOperatorExtension; +import io.javaoperatorsdk.operator.sample.bulkdependent.StandaloneBulkDependentReconciler; + +class StandaloneBulkDependentIT extends BulkDependentTestBase { + + @RegisterExtension + LocallyRunOperatorExtension extension = + LocallyRunOperatorExtension.builder().withReconciler(new StandaloneBulkDependentReconciler()) + .build(); + + @Override + LocallyRunOperatorExtension extension() { + return extension; + } +} diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/bulkdependent/BulkDependentTestCustomResource.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/bulkdependent/BulkDependentTestCustomResource.java new file mode 100644 index 0000000000..68e6297f8c --- /dev/null +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/bulkdependent/BulkDependentTestCustomResource.java @@ -0,0 +1,15 @@ +package io.javaoperatorsdk.operator.sample.bulkdependent; + +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("sbd") +public class BulkDependentTestCustomResource + extends CustomResource + implements Namespaced { +} diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/bulkdependent/BulkDependentTestSpec.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/bulkdependent/BulkDependentTestSpec.java new file mode 100644 index 0000000000..5266950b41 --- /dev/null +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/bulkdependent/BulkDependentTestSpec.java @@ -0,0 +1,25 @@ +package io.javaoperatorsdk.operator.sample.bulkdependent; + +public class BulkDependentTestSpec { + + private Integer numberOfResources; + private String additionalData; + + public Integer getNumberOfResources() { + return numberOfResources; + } + + public BulkDependentTestSpec setNumberOfResources(Integer numberOfResources) { + this.numberOfResources = numberOfResources; + return this; + } + + public BulkDependentTestSpec setAdditionalData(String additionalData) { + this.additionalData = additionalData; + return this; + } + + public String getAdditionalData() { + return additionalData; + } +} diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/bulkdependent/CRUDConfigMapBulkDependentResource.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/bulkdependent/CRUDConfigMapBulkDependentResource.java new file mode 100644 index 0000000000..83cec0bb69 --- /dev/null +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/bulkdependent/CRUDConfigMapBulkDependentResource.java @@ -0,0 +1,7 @@ +package io.javaoperatorsdk.operator.sample.bulkdependent; + +import io.javaoperatorsdk.operator.api.reconciler.dependent.GarbageCollected; + +public class CRUDConfigMapBulkDependentResource extends ConfigMapDeleterBulkDependentResource + implements GarbageCollected { +} diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/bulkdependent/ConfigMapDeleterBulkDependentResource.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/bulkdependent/ConfigMapDeleterBulkDependentResource.java new file mode 100644 index 0000000000..a7fbd9cb98 --- /dev/null +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/bulkdependent/ConfigMapDeleterBulkDependentResource.java @@ -0,0 +1,72 @@ +package io.javaoperatorsdk.operator.sample.bulkdependent; + +import java.util.Map; +import java.util.Optional; +import java.util.stream.Collectors; + +import io.fabric8.kubernetes.api.model.ConfigMap; +import io.fabric8.kubernetes.api.model.ObjectMetaBuilder; +import io.javaoperatorsdk.operator.api.reconciler.Context; +import io.javaoperatorsdk.operator.api.reconciler.ResourceDiscriminator; +import io.javaoperatorsdk.operator.api.reconciler.dependent.Deleter; +import io.javaoperatorsdk.operator.processing.dependent.BulkDependentResource; +import io.javaoperatorsdk.operator.processing.dependent.Creator; +import io.javaoperatorsdk.operator.processing.dependent.Updater; +import io.javaoperatorsdk.operator.processing.dependent.kubernetes.KubernetesDependentResource; + +/** + * Not using CRUDKubernetesDependentResource so the delete functionality can be tested. + */ +public class ConfigMapDeleterBulkDependentResource + extends + KubernetesDependentResource + implements Creator, + Updater, + Deleter, + BulkDependentResource { + + public static final String LABEL_KEY = "bulk"; + public static final String LABEL_VALUE = "true"; + public static final String ADDITIONAL_DATA_KEY = "additionalData"; + + public ConfigMapDeleterBulkDependentResource() { + super(ConfigMap.class); + } + + @Override + public ConfigMap desired(BulkDependentTestCustomResource primary, + int index, Context context) { + ConfigMap configMap = new ConfigMap(); + configMap.setMetadata(new ObjectMetaBuilder() + .withName(primary.getMetadata().getName() + "-" + index) + .withNamespace(primary.getMetadata().getNamespace()) + .withLabels(Map.of(LABEL_KEY, LABEL_VALUE)) + .build()); + configMap.setData( + Map.of("number", "" + index, ADDITIONAL_DATA_KEY, primary.getSpec().getAdditionalData())); + return configMap; + } + + @Override + public int count(BulkDependentTestCustomResource primary, + Context context) { + return primary.getSpec().getNumberOfResources(); + } + + @Override + public ResourceDiscriminator getResourceDiscriminator( + int index) { + return (resource, primary, context) -> { + var resources = context.getSecondaryResources(resource).stream() + .filter(r -> r.getMetadata().getName().endsWith("-" + index)) + .collect(Collectors.toList()); + if (resources.isEmpty()) { + return Optional.empty(); + } else if (resources.size() > 1) { + throw new IllegalStateException("More than one resource found for index:" + index); + } else { + return Optional.of(resources.get(0)); + } + }; + } +} diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/bulkdependent/ManagedBulkDependentReconciler.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/bulkdependent/ManagedBulkDependentReconciler.java new file mode 100644 index 0000000000..3b2acd942e --- /dev/null +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/bulkdependent/ManagedBulkDependentReconciler.java @@ -0,0 +1,25 @@ +package io.javaoperatorsdk.operator.sample.bulkdependent; + +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 = CRUDConfigMapBulkDependentResource.class)) +public class ManagedBulkDependentReconciler + implements Reconciler { + + private final AtomicInteger numberOfExecutions = new AtomicInteger(0); + + @Override + public UpdateControl reconcile( + BulkDependentTestCustomResource resource, + Context context) throws Exception { + + numberOfExecutions.addAndGet(1); + return UpdateControl.noUpdate(); + } +} diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/bulkdependent/ManagedDeleterBulkReconciler.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/bulkdependent/ManagedDeleterBulkReconciler.java new file mode 100644 index 0000000000..e759bdd200 --- /dev/null +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/bulkdependent/ManagedDeleterBulkReconciler.java @@ -0,0 +1,20 @@ +package io.javaoperatorsdk.operator.sample.bulkdependent; + +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 = ConfigMapDeleterBulkDependentResource.class)) +public class ManagedDeleterBulkReconciler implements Reconciler { + @Override + public UpdateControl reconcile( + BulkDependentTestCustomResource resource, + Context context) + throws Exception { + + return UpdateControl.noUpdate(); + } +} diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/bulkdependent/StandaloneBulkDependentReconciler.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/bulkdependent/StandaloneBulkDependentReconciler.java new file mode 100644 index 0000000000..4033583340 --- /dev/null +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/bulkdependent/StandaloneBulkDependentReconciler.java @@ -0,0 +1,58 @@ +package io.javaoperatorsdk.operator.sample.bulkdependent; + +import java.util.Map; +import java.util.concurrent.atomic.AtomicInteger; + +import io.fabric8.kubernetes.client.KubernetesClient; +import io.javaoperatorsdk.operator.api.reconciler.*; +import io.javaoperatorsdk.operator.junit.KubernetesClientAware; +import io.javaoperatorsdk.operator.processing.event.source.EventSource; +import io.javaoperatorsdk.operator.support.TestExecutionInfoProvider; + +@ControllerConfiguration +public class StandaloneBulkDependentReconciler + implements Reconciler, TestExecutionInfoProvider, + EventSourceInitializer, KubernetesClientAware { + + private final AtomicInteger numberOfExecutions = new AtomicInteger(0); + + private ConfigMapDeleterBulkDependentResource dependent; + private KubernetesClient kubernetesClient; + + public StandaloneBulkDependentReconciler() { + dependent = new CRUDConfigMapBulkDependentResource(); + } + + @Override + public UpdateControl reconcile( + BulkDependentTestCustomResource resource, + Context context) { + numberOfExecutions.addAndGet(1); + + dependent.reconcile(resource, context); + + return UpdateControl.noUpdate(); + } + + public int getNumberOfExecutions() { + return numberOfExecutions.get(); + } + + @Override + public Map prepareEventSources( + EventSourceContext context) { + return EventSourceInitializer + .nameEventSources(dependent.initEventSource(context)); + } + + @Override + public KubernetesClient getKubernetesClient() { + return kubernetesClient; + } + + @Override + public void setKubernetesClient(KubernetesClient kubernetesClient) { + this.kubernetesClient = kubernetesClient; + dependent.setKubernetesClient(kubernetesClient); + } +} diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/bulkdependent/external/ExternalBulkDependentResource.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/bulkdependent/external/ExternalBulkDependentResource.java new file mode 100644 index 0000000000..110626a923 --- /dev/null +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/bulkdependent/external/ExternalBulkDependentResource.java @@ -0,0 +1,101 @@ +package io.javaoperatorsdk.operator.sample.bulkdependent.external; + +import java.util.*; +import java.util.stream.Collectors; + +import io.javaoperatorsdk.operator.api.reconciler.Context; +import io.javaoperatorsdk.operator.api.reconciler.ResourceDiscriminator; +import io.javaoperatorsdk.operator.processing.dependent.*; +import io.javaoperatorsdk.operator.processing.dependent.external.PollingDependentResource; +import io.javaoperatorsdk.operator.processing.event.ResourceID; +import io.javaoperatorsdk.operator.sample.bulkdependent.BulkDependentTestCustomResource; + +public class ExternalBulkDependentResource + extends PollingDependentResource + implements BulkDependentResource, + BulkUpdater { + + public static final String EXTERNAL_RESOURCE_NAME_DELIMITER = "#"; + + private final ExternalServiceMock externalServiceMock = ExternalServiceMock.getInstance(); + + public ExternalBulkDependentResource() { + super(ExternalResource.class, ExternalResource::getId); + } + + @Override + public Map> fetchResources() { + Map> result = new HashMap<>(); + var resources = externalServiceMock.listResources(); + resources.forEach(er -> { + var resourceID = toResourceID(er); + result.putIfAbsent(resourceID, new HashSet<>()); + result.get(resourceID).add(er); + }); + return result; + } + + @Override + public void delete(BulkDependentTestCustomResource primary, + Context context) { + deleteBulkResourcesIfRequired(0, lastKnownBulkSize(), primary, context); + } + + @Override + public int count(BulkDependentTestCustomResource primary, + Context context) { + return primary.getSpec().getNumberOfResources(); + } + + @Override + public void deleteBulkResourceWithIndex(BulkDependentTestCustomResource primary, + ExternalResource resource, int i, Context context) { + externalServiceMock.delete(resource.getId()); + } + + @Override + public ExternalResource desired(BulkDependentTestCustomResource primary, int index, + Context context) { + return new ExternalResource(toExternalResourceId(primary, index), + primary.getSpec().getAdditionalData()); + } + + @Override + public ExternalResource create(ExternalResource desired, BulkDependentTestCustomResource primary, + Context context) { + return externalServiceMock.create(desired); + } + + @Override + public ExternalResource update(ExternalResource actual, ExternalResource desired, + BulkDependentTestCustomResource primary, Context context) { + return externalServiceMock.update(desired); + } + + @Override + public Matcher.Result match(ExternalResource actualResource, + BulkDependentTestCustomResource primary, + int index, Context context) { + var desired = desired(primary, index, context); + return Matcher.Result.computed(desired.equals(actualResource), desired); + } + + private static String toExternalResourceId(BulkDependentTestCustomResource primary, int i) { + return primary.getMetadata().getName() + EXTERNAL_RESOURCE_NAME_DELIMITER + + primary.getMetadata().getNamespace() + + EXTERNAL_RESOURCE_NAME_DELIMITER + i; + } + + private ResourceID toResourceID(ExternalResource externalResource) { + var parts = externalResource.getId().split(EXTERNAL_RESOURCE_NAME_DELIMITER); + return new ResourceID(parts[0], parts[1]); + } + + @Override + public ResourceDiscriminator getResourceDiscriminator( + int index) { + return (resource, primary, context) -> context.getSecondaryResources(resource).stream() + .filter(r -> r.getId().endsWith(EXTERNAL_RESOURCE_NAME_DELIMITER + index)) + .collect(Collectors.toList()).stream().findFirst(); + } +} diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/bulkdependent/external/ExternalBulkResourceReconciler.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/bulkdependent/external/ExternalBulkResourceReconciler.java new file mode 100644 index 0000000000..2543422d74 --- /dev/null +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/bulkdependent/external/ExternalBulkResourceReconciler.java @@ -0,0 +1,19 @@ +package io.javaoperatorsdk.operator.sample.bulkdependent.external; + +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; +import io.javaoperatorsdk.operator.sample.bulkdependent.BulkDependentTestCustomResource; + +@ControllerConfiguration(dependents = @Dependent(type = ExternalBulkDependentResource.class)) +public class ExternalBulkResourceReconciler implements Reconciler { + + @Override + public UpdateControl reconcile( + BulkDependentTestCustomResource resource, Context context) + throws Exception { + return UpdateControl.noUpdate(); + } +} diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/bulkdependent/external/ExternalResource.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/bulkdependent/external/ExternalResource.java new file mode 100644 index 0000000000..935fd99e47 --- /dev/null +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/bulkdependent/external/ExternalResource.java @@ -0,0 +1,37 @@ +package io.javaoperatorsdk.operator.sample.bulkdependent.external; + +import java.util.Objects; + +public class ExternalResource { + + private String id; + private String data; + + public ExternalResource(String id, String data) { + this.id = id; + this.data = data; + } + + public String getId() { + return id; + } + + public String getData() { + return data; + } + + @Override + public boolean equals(Object o) { + if (this == o) + return true; + if (o == null || getClass() != o.getClass()) + return false; + ExternalResource that = (ExternalResource) o; + return Objects.equals(id, that.id) && Objects.equals(data, that.data); + } + + @Override + public int hashCode() { + return Objects.hash(id, data); + } +} diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/bulkdependent/external/ExternalServiceMock.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/bulkdependent/external/ExternalServiceMock.java new file mode 100644 index 0000000000..e73062ccf2 --- /dev/null +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/bulkdependent/external/ExternalServiceMock.java @@ -0,0 +1,39 @@ +package io.javaoperatorsdk.operator.sample.bulkdependent.external; + +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.concurrent.ConcurrentHashMap; + +public class ExternalServiceMock { + + private static ExternalServiceMock serviceMock = new ExternalServiceMock(); + + private Map resourceMap = new ConcurrentHashMap<>(); + + public ExternalResource create(ExternalResource externalResource) { + resourceMap.put(externalResource.getId(), externalResource); + return externalResource; + } + + public Optional read(String id) { + return Optional.ofNullable(resourceMap.get(id)); + } + + public ExternalResource update(ExternalResource externalResource) { + return resourceMap.put(externalResource.getId(), externalResource); + } + + public Optional delete(String id) { + return Optional.ofNullable(resourceMap.remove(id)); + } + + public List listResources() { + return new ArrayList<>(resourceMap.values()); + } + + public static ExternalServiceMock getInstance() { + return serviceMock; + } +} 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 49f5ee64c1..0994e6b9b0 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 @@ -29,7 +29,6 @@ public class MultipleDependentResourceReconciler public MultipleDependentResourceReconciler() { firstDependentResourceConfigMap = new MultipleDependentResourceConfigMap(FIRST_CONFIG_MAP_ID); - secondDependentResourceConfigMap = new MultipleDependentResourceConfigMap(SECOND_CONFIG_MAP_ID); firstDependentResourceConfigMap From ba64ed8a5176d35dae00d010dc09ef48f4110e1f Mon Sep 17 00:00:00 2001 From: csviri Date: Mon, 3 Oct 2022 10:17:43 +0200 Subject: [PATCH 59/67] fixes after merge --- .../api/config/AnnotationControllerConfiguration.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/config/AnnotationControllerConfiguration.java b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/config/AnnotationControllerConfiguration.java index 37e0fb101b..75d622e30b 100644 --- a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/config/AnnotationControllerConfiguration.java +++ b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/config/AnnotationControllerConfiguration.java @@ -313,8 +313,8 @@ private Object createKubernetesResourceConfig(Class .orElse(null); resourceDiscriminator = - instantiateIfNotDefault(kubeDependent.resourceDiscriminator(), - ResourceDiscriminator.class, context); + instantiateIfNotDefault(kubeDependent.resourceDiscriminator(), + ResourceDiscriminator.class, context); eventSourceNameToUse = Constants.NO_VALUE_SET.equals(kubeDependent.eventSourceToUse()) ? null : kubeDependent.eventSourceToUse(); } From 5a912a59afb989e8d8337b116c1d2febbda3f7d0 Mon Sep 17 00:00:00 2001 From: csviri Date: Mon, 3 Oct 2022 14:35:32 +0200 Subject: [PATCH 60/67] controller fix --- .../operator/processing/Controller.java | 40 ++++++++++--------- 1 file changed, 21 insertions(+), 19 deletions(-) diff --git a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/Controller.java b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/Controller.java index 57ce0a3a5e..fade121b9e 100644 --- a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/Controller.java +++ b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/Controller.java @@ -213,26 +213,28 @@ public void initAndRegisterEventSources(EventSourceContext

context) { final var provider = (EventSourceInitializer

) this.reconciler; final var ownSources = provider.prepareEventSources(context); ownSources.forEach(eventSourceManager::registerEventSource); + } else { + managedWorkflow + .getDependentResourcesByName().entrySet().stream() + .forEach(drEntry -> { + if (drEntry.getValue() instanceof EventSourceProvider) { + final var provider = (EventSourceProvider) drEntry.getValue(); + final var source = provider.initEventSource(context); + eventSourceManager.registerEventSource(drEntry.getKey(), source); + } else { + Optional eventSource = + drEntry.getValue().eventSource(context); + eventSource.ifPresent(es -> { + eventSourceManager.registerEventSource(drEntry.getKey(), es); + }); + } + }); + managedWorkflow.getDependentResourcesByName().entrySet().stream().map(Map.Entry::getValue) + .filter(EventSourceAware.class::isInstance) + .forEach(dr -> ((EventSourceAware) dr) + .selectEventSources(eventSourceManager)); } - managedWorkflow - .getDependentResourcesByName().entrySet().stream() - .forEach(drEntry -> { - if (drEntry.getValue() instanceof EventSourceProvider) { - final var provider = (EventSourceProvider) drEntry.getValue(); - final var source = provider.initEventSource(context); - eventSourceManager.registerEventSource(drEntry.getKey(), source); - } else { - Optional eventSource = - drEntry.getValue().eventSource(context); - eventSource.ifPresent(es -> { - eventSourceManager.registerEventSource(drEntry.getKey(), es); - }); - } - }); - managedWorkflow.getDependentResourcesByName().entrySet().stream().map(Map.Entry::getValue) - .filter(EventSourceAware.class::isInstance) - .forEach(dr -> ((EventSourceAware) dr) - .selectEventSources(eventSourceManager)); + } @Override From 4576677cb2f01f84f66ebabd958b0f9b0bd14605 Mon Sep 17 00:00:00 2001 From: csviri Date: Mon, 3 Oct 2022 14:46:51 +0200 Subject: [PATCH 61/67] merge fixes --- docs/assets/js/uikit.js | 2 +- .../config/AnnotationControllerConfiguration.java | 15 ++------------- .../reconciler/dependent/DependentResource.java | 4 ++-- 3 files changed, 5 insertions(+), 16 deletions(-) diff --git a/docs/assets/js/uikit.js b/docs/assets/js/uikit.js index 665c8c0522..5b1acf9275 100644 --- a/docs/assets/js/uikit.js +++ b/docs/assets/js/uikit.js @@ -8214,7 +8214,7 @@ updateAria: function(toggled) { attr(this.$el, 'aria-expanded', isBoolean(toggled) - ? toggled// todo delete bulk support + ? toggled : isToggled(this.target, this.cls) ); } diff --git a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/config/AnnotationControllerConfiguration.java b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/config/AnnotationControllerConfiguration.java index 75d622e30b..fa2b5c1927 100644 --- a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/config/AnnotationControllerConfiguration.java +++ b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/config/AnnotationControllerConfiguration.java @@ -45,14 +45,13 @@ public class AnnotationControllerConfiguration

private static final String KUBE_DEPENDENT_NAME = KubernetesDependent.class.getSimpleName(); protected final Reconciler

reconciler; - private final io.javaoperatorsdk.operator.api.reconciler.ControllerConfiguration annotation; + private final ControllerConfiguration annotation; private List specs; private Class

resourceClass; public AnnotationControllerConfiguration(Reconciler

reconciler) { this.reconciler = reconciler; - this.annotation = reconciler.getClass() - .getAnnotation(io.javaoperatorsdk.operator.api.reconciler.ControllerConfiguration.class); + this.annotation = reconciler.getClass().getAnnotation(ControllerConfiguration.class); if (annotation == null) { throw new OperatorException("Missing mandatory @" + CONTROLLER_CONFIG_ANNOTATION + " annotation for reconciler: " + reconciler); @@ -327,16 +326,6 @@ private Object createKubernetesResourceConfig(Class return config; } - @SuppressWarnings({"unchecked"}) - private ResourceDiscriminator instantiateDiscriminatorIfNotVoid( - Class discriminator) { - if (discriminator != ResourceDiscriminator.class) { - return instantiateAndConfigureIfNeeded(discriminator, ResourceDiscriminator.class, - CONTROLLER_CONFIG_ANNOTATION); - } - return null; - } - public static T valueOrDefault( ControllerConfiguration controllerConfiguration, Function mapper, 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 1e92b7a5b3..6c769533e8 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 @@ -25,9 +25,9 @@ public interface DependentResource { ReconcileResult reconcile(P primary, Context

context); /** - * Retrieves the resource type associated with this ResourceOwner + * Retrieves the resource type associated with this DependentResource * - * @return the resource type associated with this ResourceOwner + * @return the resource type associated with this DependentResource */ Class resourceType(); From 55c37968d78057b413288f747d63010794d3fab9 Mon Sep 17 00:00:00 2001 From: csviri Date: Mon, 3 Oct 2022 15:09:50 +0200 Subject: [PATCH 62/67] automatic event source selection --- .../dependent/AbstractDependentResource.java | 5 ++++- .../AbstractEventSourceHolderDependentResource.java | 10 +++++++--- 2 files changed, 11 insertions(+), 4 deletions(-) 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 64ac7d7a5c..fe7717e846 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 @@ -29,7 +29,7 @@ public abstract class AbstractDependentResource protected Creator creator; protected Updater updater; protected BulkDependentResource bulkDependentResource; - protected boolean returnEventSource = true; + private boolean returnEventSource = true; protected List> resourceDiscriminator = new ArrayList<>(1); @@ -239,4 +239,7 @@ protected int lastKnownBulkSize() { return resourceDiscriminator.size(); } + protected boolean getReturnEventSource() { + return returnEventSource; + } } diff --git a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/AbstractEventSourceHolderDependentResource.java b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/AbstractEventSourceHolderDependentResource.java index ffbb0e7123..d7eb41a3db 100644 --- a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/AbstractEventSourceHolderDependentResource.java +++ b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/AbstractEventSourceHolderDependentResource.java @@ -50,9 +50,13 @@ public ResourceEventSource provideEventSource(EventSourceContext

contex @SuppressWarnings("unchecked") @Override public void selectEventSources(EventSourceRetriever

eventSourceRetriever) { - if (eventSourceToUse != null) { - eventSource = - (T) eventSourceRetriever.getResourceEventSourceFor(resourceType(), eventSourceToUse); + if (!getReturnEventSource()) { + if (eventSourceToUse != null) { + eventSource = + (T) eventSourceRetriever.getResourceEventSourceFor(resourceType(), eventSourceToUse); + } else { + eventSource = (T) eventSourceRetriever.getResourceEventSourceFor(resourceType()); + } } } From 964cf5af1a349b6365f9bf19dd92219e30e298e1 Mon Sep 17 00:00:00 2001 From: csviri Date: Mon, 3 Oct 2022 15:35:33 +0200 Subject: [PATCH 63/67] fix --- .../operator/processing/Controller.java | 40 +++++++++---------- 1 file changed, 19 insertions(+), 21 deletions(-) diff --git a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/Controller.java b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/Controller.java index fade121b9e..57ce0a3a5e 100644 --- a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/Controller.java +++ b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/Controller.java @@ -213,28 +213,26 @@ public void initAndRegisterEventSources(EventSourceContext

context) { final var provider = (EventSourceInitializer

) this.reconciler; final var ownSources = provider.prepareEventSources(context); ownSources.forEach(eventSourceManager::registerEventSource); - } else { - managedWorkflow - .getDependentResourcesByName().entrySet().stream() - .forEach(drEntry -> { - if (drEntry.getValue() instanceof EventSourceProvider) { - final var provider = (EventSourceProvider) drEntry.getValue(); - final var source = provider.initEventSource(context); - eventSourceManager.registerEventSource(drEntry.getKey(), source); - } else { - Optional eventSource = - drEntry.getValue().eventSource(context); - eventSource.ifPresent(es -> { - eventSourceManager.registerEventSource(drEntry.getKey(), es); - }); - } - }); - managedWorkflow.getDependentResourcesByName().entrySet().stream().map(Map.Entry::getValue) - .filter(EventSourceAware.class::isInstance) - .forEach(dr -> ((EventSourceAware) dr) - .selectEventSources(eventSourceManager)); } - + managedWorkflow + .getDependentResourcesByName().entrySet().stream() + .forEach(drEntry -> { + if (drEntry.getValue() instanceof EventSourceProvider) { + final var provider = (EventSourceProvider) drEntry.getValue(); + final var source = provider.initEventSource(context); + eventSourceManager.registerEventSource(drEntry.getKey(), source); + } else { + Optional eventSource = + drEntry.getValue().eventSource(context); + eventSource.ifPresent(es -> { + eventSourceManager.registerEventSource(drEntry.getKey(), es); + }); + } + }); + managedWorkflow.getDependentResourcesByName().entrySet().stream().map(Map.Entry::getValue) + .filter(EventSourceAware.class::isInstance) + .forEach(dr -> ((EventSourceAware) dr) + .selectEventSources(eventSourceManager)); } @Override From 9bad8941addef321a6341880cc255963d67c46eb Mon Sep 17 00:00:00 2001 From: csviri Date: Mon, 3 Oct 2022 16:08:23 +0200 Subject: [PATCH 64/67] proper setting event source --- .../api/reconciler/dependent/DependentResource.java | 1 - .../AbstractEventSourceHolderDependentResource.java | 6 +++--- .../MultipleManagedExternalDependentResourceReconciler.java | 3 --- 3 files changed, 3 insertions(+), 7 deletions(-) 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 6c769533e8..d098137b46 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,7 +49,6 @@ default Optional> eventSource( return Optional.empty(); } - // todo should be a setter? /** * Calling this method, instructs the implementation to not provide an event source, even if it * normally does. diff --git a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/AbstractEventSourceHolderDependentResource.java b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/AbstractEventSourceHolderDependentResource.java index d7eb41a3db..a90017897a 100644 --- a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/AbstractEventSourceHolderDependentResource.java +++ b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/AbstractEventSourceHolderDependentResource.java @@ -52,10 +52,10 @@ public ResourceEventSource provideEventSource(EventSourceContext

contex public void selectEventSources(EventSourceRetriever

eventSourceRetriever) { if (!getReturnEventSource()) { if (eventSourceToUse != null) { - eventSource = - (T) eventSourceRetriever.getResourceEventSourceFor(resourceType(), eventSourceToUse); + setEventSource( + (T) eventSourceRetriever.getResourceEventSourceFor(resourceType(), eventSourceToUse)); } else { - eventSource = (T) eventSourceRetriever.getResourceEventSourceFor(resourceType()); + setEventSource((T) eventSourceRetriever.getResourceEventSourceFor(resourceType())); } } } diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/multiplemanagedexternaldependenttype/MultipleManagedExternalDependentResourceReconciler.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/multiplemanagedexternaldependenttype/MultipleManagedExternalDependentResourceReconciler.java index 83bdf1dac2..573df92287 100644 --- a/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/multiplemanagedexternaldependenttype/MultipleManagedExternalDependentResourceReconciler.java +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/multiplemanagedexternaldependenttype/MultipleManagedExternalDependentResourceReconciler.java @@ -26,10 +26,8 @@ public class MultipleManagedExternalDependentResourceReconciler public static final String CONFIG_MAP_EVENT_SOURCE = "ConfigMapEventSource"; protected ExternalServiceMock externalServiceMock = ExternalServiceMock.getInstance(); - private final AtomicInteger numberOfExecutions = new AtomicInteger(0); - public MultipleManagedExternalDependentResourceReconciler() {} @Override @@ -41,7 +39,6 @@ public UpdateControl rec return UpdateControl.noUpdate(); } - public int getNumberOfExecutions() { return numberOfExecutions.get(); } From b86b6f72dde525a3b41d1b0331cc4a6fed73ad5f Mon Sep 17 00:00:00 2001 From: Chris Laprun Date: Mon, 3 Oct 2022 18:23:04 +0200 Subject: [PATCH 65/67] fix: avoid NPE if no dependent resources are passed --- .../api/reconciler/EventSourceInitializer.java | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/reconciler/EventSourceInitializer.java b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/reconciler/EventSourceInitializer.java index 96fa85ee6a..017418ea35 100644 --- a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/reconciler/EventSourceInitializer.java +++ b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/reconciler/EventSourceInitializer.java @@ -1,5 +1,6 @@ package io.javaoperatorsdk.operator.api.reconciler; +import java.util.Collections; import java.util.HashMap; import java.util.Map; import java.util.Optional; @@ -46,12 +47,16 @@ static Map nameEventSources(EventSource... eventSources) { static Map nameEventSourcesFromDependentResource( EventSourceContext context, DependentResource... dependentResources) { - Map eventSourceMap = new HashMap<>(dependentResources.length); - for (DependentResource dependentResource : dependentResources) { - Optional es = dependentResource.eventSource(context); - es.ifPresent(e -> eventSourceMap.put(generateNameFor(e), e)); + if (dependentResources != null) { + Map eventSourceMap = new HashMap<>(dependentResources.length); + for (DependentResource dependentResource : dependentResources) { + Optional es = dependentResource.eventSource(context); + es.ifPresent(e -> eventSourceMap.put(generateNameFor(e), e)); + } + return eventSourceMap; + } else { + return Collections.emptyMap(); } - return eventSourceMap; } /** From b99847d6bbbbe2cb1c8a884bff97fd2e06b3cc37 Mon Sep 17 00:00:00 2001 From: Chris Laprun Date: Mon, 3 Oct 2022 21:12:06 +0200 Subject: [PATCH 66/67] refactor: simplify handling of reused event sources DependentResources that provide an EventSource should implement EventSourceAware. If they want to reuse an already created event source instead of providing their own, they need to select the appropriate event source identified by the name specified by the useEventSourceNamed method. --- .../AnnotationControllerConfiguration.java | 7 ++-- .../ControllerConfigurationOverrider.java | 5 ++- .../dependent/DependentResourceSpec.java | 10 ++--- .../api/reconciler/EventSourceContext.java | 14 +++++-- .../reconciler/EventSourceInitializer.java | 13 +++---- .../api/reconciler/dependent/Dependent.java | 2 +- .../dependent/DependentResource.java | 27 ------------- .../dependent/EventSourceAware.java | 32 +++++++++++++-- .../operator/processing/Controller.java | 39 ++++++++----------- .../dependent/AbstractDependentResource.java | 24 ------------ ...actEventSourceHolderDependentResource.java | 39 ++++++++----------- .../KubernetesDependentResource.java | 5 --- .../KubernetesDependentResourceConfig.java | 13 +------ .../workflow/ManagedWorkflowSupport.java | 9 +++-- .../ControllerConfigurationOverriderTest.java | 3 -- .../AbstractDependentResourceTest.java | 8 ---- .../dependent/EmptyTestDependentResource.java | 3 -- .../AbstractWorkflowExecutorTest.java | 6 --- .../workflow/ManagedWorkflowTestUtils.java | 2 +- .../ExternalResourceDiscriminator.java | 2 +- ...edExternalDependentResourceReconciler.java | 27 ++++++------- 21 files changed, 115 insertions(+), 175 deletions(-) diff --git a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/config/AnnotationControllerConfiguration.java b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/config/AnnotationControllerConfiguration.java index fa2b5c1927..ab13a6d5d5 100644 --- a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/config/AnnotationControllerConfiguration.java +++ b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/config/AnnotationControllerConfiguration.java @@ -248,7 +248,8 @@ public List getDependentResources() { instantiateIfNotDefault(dependent.readyPostcondition(), Condition.class, context), instantiateIfNotDefault(dependent.reconcilePrecondition(), Condition.class, context), instantiateIfNotDefault(dependent.deletePostcondition(), Condition.class, context), - dependent.provideEventSource()); + Constants.NO_VALUE_SET.equals(dependent.eventSource()) ? null + : dependent.eventSource()); specsMap.put(name, spec); } @@ -314,14 +315,12 @@ private Object createKubernetesResourceConfig(Class resourceDiscriminator = instantiateIfNotDefault(kubeDependent.resourceDiscriminator(), ResourceDiscriminator.class, context); - eventSourceNameToUse = Constants.NO_VALUE_SET.equals(kubeDependent.eventSourceToUse()) ? null - : kubeDependent.eventSourceToUse(); } config = new KubernetesDependentResourceConfig(namespaces, labelSelector, configuredNS, resourceDiscriminator, onAddFilter, - onUpdateFilter, onDeleteFilter, genericFilter, eventSourceNameToUse); + onUpdateFilter, onDeleteFilter, genericFilter); return config; } diff --git a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/config/ControllerConfigurationOverrider.java b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/config/ControllerConfigurationOverrider.java index ee153c879a..454bd4ca49 100644 --- a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/config/ControllerConfigurationOverrider.java +++ b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/config/ControllerConfigurationOverrider.java @@ -174,7 +174,7 @@ private void replaceConfig(String name, Object newConfig, DependentResourceSpec< namedDependentResourceSpecs.put(name, new DependentResourceSpec<>(current.getDependentResourceClass(), newConfig, name, current.getDependsOn(), current.getReadyCondition(), current.getReconcileCondition(), - current.getDeletePostCondition(), current.provideEventSource())); + current.getDeletePostCondition(), current.getEventSourceName().orElse(null))); } @SuppressWarnings("unchecked") @@ -220,7 +220,8 @@ public ControllerConfiguration build() { KubernetesDependentResourceConfig c) { return new DependentResourceSpec(spec.getDependentResourceClass(), c.setNamespaces(namespaces), name, spec.getDependsOn(), spec.getReadyCondition(), - spec.getReconcileCondition(), spec.getDeletePostCondition(), spec.provideEventSource()); + spec.getReconcileCondition(), spec.getDeletePostCondition(), + (String) spec.getEventSourceName().orElse(null)); } public static ControllerConfigurationOverrider override( diff --git a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/config/dependent/DependentResourceSpec.java b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/config/dependent/DependentResourceSpec.java index 71476cc2d3..fadd0a9816 100644 --- a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/config/dependent/DependentResourceSpec.java +++ b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/config/dependent/DependentResourceSpec.java @@ -23,12 +23,12 @@ public class DependentResourceSpec, C> { private final Condition deletePostCondition; - private final boolean provideEventSource; + private final String eventSourceName; public DependentResourceSpec(Class dependentResourceClass, C dependentResourceConfig, String name, Set dependsOn, Condition readyCondition, Condition reconcileCondition, Condition deletePostCondition, - boolean provideEventSource) { + String eventSourceName) { this.dependentResourceClass = dependentResourceClass; this.dependentResourceConfig = dependentResourceConfig; this.name = name; @@ -36,7 +36,7 @@ public DependentResourceSpec(Class dependentResourceClass, C dependentResourc this.readyCondition = readyCondition; this.reconcileCondition = reconcileCondition; this.deletePostCondition = deletePostCondition; - this.provideEventSource = provideEventSource; + this.eventSourceName = eventSourceName; } public Class getDependentResourceClass() { @@ -94,7 +94,7 @@ public Condition getDeletePostCondition() { return deletePostCondition; } - public boolean provideEventSource() { - return provideEventSource; + public Optional getEventSourceName() { + return Optional.ofNullable(eventSourceName); } } diff --git a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/reconciler/EventSourceContext.java b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/reconciler/EventSourceContext.java index e8062e9651..2c1e82ba4c 100644 --- a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/reconciler/EventSourceContext.java +++ b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/reconciler/EventSourceContext.java @@ -3,6 +3,8 @@ import io.fabric8.kubernetes.api.model.HasMetadata; import io.fabric8.kubernetes.client.KubernetesClient; import io.javaoperatorsdk.operator.api.config.ControllerConfiguration; +import io.javaoperatorsdk.operator.processing.event.EventSourceManager; +import io.javaoperatorsdk.operator.processing.event.EventSourceRetriever; import io.javaoperatorsdk.operator.processing.event.source.EventSource; import io.javaoperatorsdk.operator.processing.event.source.IndexerResourceCache; @@ -13,14 +15,14 @@ */ public class EventSourceContext

{ - private final IndexerResourceCache

primaryCache; + private final EventSourceManager

eventSourceManager; private final ControllerConfiguration

controllerConfiguration; private final KubernetesClient client; - public EventSourceContext(IndexerResourceCache

primaryCache, + public EventSourceContext(EventSourceManager

eventSourceManager, ControllerConfiguration

controllerConfiguration, KubernetesClient client) { - this.primaryCache = primaryCache; + this.eventSourceManager = eventSourceManager; this.controllerConfiguration = controllerConfiguration; this.client = client; } @@ -31,7 +33,7 @@ public EventSourceContext(IndexerResourceCache

primaryCache, * @return the primary resource cache */ public IndexerResourceCache

getPrimaryCache() { - return primaryCache; + return eventSourceManager.getControllerResourceEventSource(); } /** @@ -54,4 +56,8 @@ public ControllerConfiguration

getControllerConfiguration() { public KubernetesClient getClient() { return client; } + + public EventSourceRetriever

getEventSourceRetriever() { + return eventSourceManager; + } } diff --git a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/reconciler/EventSourceInitializer.java b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/reconciler/EventSourceInitializer.java index 017418ea35..c49332b468 100644 --- a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/reconciler/EventSourceInitializer.java +++ b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/reconciler/EventSourceInitializer.java @@ -1,14 +1,14 @@ package io.javaoperatorsdk.operator.api.reconciler; +import java.util.Arrays; import java.util.Collections; import java.util.HashMap; import java.util.Map; -import java.util.Optional; import io.fabric8.kubernetes.api.model.HasMetadata; import io.javaoperatorsdk.operator.api.reconciler.dependent.DependentResource; +import io.javaoperatorsdk.operator.api.reconciler.dependent.EventSourceAware; import io.javaoperatorsdk.operator.processing.event.source.EventSource; -import io.javaoperatorsdk.operator.processing.event.source.ResourceEventSource; /** * An interface that a {@link Reconciler} can implement to have the SDK register the provided @@ -46,13 +46,12 @@ static Map nameEventSources(EventSource... eventSources) { @SuppressWarnings("unchecked,rawtypes") static Map nameEventSourcesFromDependentResource( EventSourceContext context, DependentResource... dependentResources) { - if (dependentResources != null) { Map eventSourceMap = new HashMap<>(dependentResources.length); - for (DependentResource dependentResource : dependentResources) { - Optional es = dependentResource.eventSource(context); - es.ifPresent(e -> eventSourceMap.put(generateNameFor(e), e)); - } + Arrays.stream(dependentResources) + .filter(EventSourceAware.class::isInstance) + .forEach(esa -> ((EventSourceAware) esa).eventSource(context) + .ifPresent(es -> eventSourceMap.put(generateNameFor(es), es))); return eventSourceMap; } else { return Collections.emptyMap(); diff --git a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/reconciler/dependent/Dependent.java b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/reconciler/dependent/Dependent.java index 00d2ad1882..06ec90eba5 100644 --- a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/reconciler/dependent/Dependent.java +++ b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/reconciler/dependent/Dependent.java @@ -65,5 +65,5 @@ * * @return if the event source (if any) provided by the dependent resource should be used or not. */ - boolean provideEventSource() default true; + String eventSource() default NO_VALUE_SET; } 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 d098137b46..8d31778488 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 @@ -4,8 +4,6 @@ import io.fabric8.kubernetes.api.model.HasMetadata; import io.javaoperatorsdk.operator.api.reconciler.Context; -import io.javaoperatorsdk.operator.api.reconciler.EventSourceContext; -import io.javaoperatorsdk.operator.processing.event.source.ResourceEventSource; /** * An interface to implement and provide dependent resource support. @@ -31,31 +29,6 @@ public interface DependentResource { */ Class resourceType(); - /** - * Dependent resources are designed to by default provide event sources. There are cases where it - * might not: - *

    - *
  • If an event source is shared between multiple dependent resources. In this case only one or - * none of the dependent resources sharing the event source should provide one.
  • - *
  • Some special implementation of an event source. That just execute some action might not - * provide one.
  • - *
- * - * @param eventSourceContext context of event source initialization - * @return an optional event source - */ - default Optional> eventSource( - EventSourceContext

eventSourceContext) { - return Optional.empty(); - } - - /** - * Calling this method, instructs the implementation to not provide an event source, even if it - * normally does. - */ - void doNotProvideEventSource(); - - default Optional getSecondaryResource(P primary, Context

context) { return Optional.empty(); } diff --git a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/reconciler/dependent/EventSourceAware.java b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/reconciler/dependent/EventSourceAware.java index 09b13d90c5..c4f4fbcc4f 100644 --- a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/reconciler/dependent/EventSourceAware.java +++ b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/reconciler/dependent/EventSourceAware.java @@ -1,10 +1,36 @@ package io.javaoperatorsdk.operator.api.reconciler.dependent; +import java.util.Optional; + import io.fabric8.kubernetes.api.model.HasMetadata; -import io.javaoperatorsdk.operator.processing.event.EventSourceRetriever; +import io.javaoperatorsdk.operator.api.reconciler.EventSourceContext; +import io.javaoperatorsdk.operator.processing.event.source.ResourceEventSource; + +public interface EventSourceAware { + + /** + * Dependent resources are designed to by default provide event sources. There are cases where it + * might not: + *

    + *
  • If an event source is shared between multiple dependent resources. In this case only one or + * none of the dependent resources sharing the event source should provide one.
  • + *
  • Some special implementation of an event source. That just execute some action might not + * provide one.
  • + *
+ * + * @param context context of event source initialization + * @return an optional event source + */ + default Optional> eventSource(EventSourceContext

context) { + return getUsedEventSourceName().map( + name -> context.getEventSourceRetriever().getResourceEventSourceFor(resourceType(), name)); + } -public interface EventSourceAware

{ + Class resourceType(); - void selectEventSources(EventSourceRetriever

eventSourceRetriever); + void useEventSourceNamed(String eventSourceName); + default Optional getUsedEventSourceName() { + return Optional.empty(); + } } diff --git a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/Controller.java b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/Controller.java index 57ce0a3a5e..f5ee0cd269 100644 --- a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/Controller.java +++ b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/Controller.java @@ -40,7 +40,6 @@ import io.javaoperatorsdk.operator.processing.event.EventProcessor; import io.javaoperatorsdk.operator.processing.event.EventSourceManager; import io.javaoperatorsdk.operator.processing.event.ResourceID; -import io.javaoperatorsdk.operator.processing.event.source.ResourceEventSource; import static io.javaoperatorsdk.operator.api.reconciler.Constants.WATCH_CURRENT_NAMESPACE; @@ -214,25 +213,21 @@ public void initAndRegisterEventSources(EventSourceContext

context) { final var ownSources = provider.prepareEventSources(context); ownSources.forEach(eventSourceManager::registerEventSource); } - managedWorkflow - .getDependentResourcesByName().entrySet().stream() - .forEach(drEntry -> { - if (drEntry.getValue() instanceof EventSourceProvider) { - final var provider = (EventSourceProvider) drEntry.getValue(); - final var source = provider.initEventSource(context); - eventSourceManager.registerEventSource(drEntry.getKey(), source); - } else { - Optional eventSource = - drEntry.getValue().eventSource(context); - eventSource.ifPresent(es -> { - eventSourceManager.registerEventSource(drEntry.getKey(), es); - }); - } - }); - managedWorkflow.getDependentResourcesByName().entrySet().stream().map(Map.Entry::getValue) - .filter(EventSourceAware.class::isInstance) - .forEach(dr -> ((EventSourceAware) dr) - .selectEventSources(eventSourceManager)); + managedWorkflow.getDependentResourcesByName().entrySet().stream().filter(entry -> { + final var value = entry.getValue(); + return value instanceof EventSourceProvider || value instanceof EventSourceAware; + }).forEach(entry -> { + final var value = entry.getValue(); + final var key = entry.getKey(); + if (value instanceof EventSourceProvider) { + final var provider = (EventSourceProvider) value; + final var source = provider.initEventSource(context); + eventSourceManager.registerEventSource(key, source); + } else { + ((EventSourceAware) value).eventSource(context) + .ifPresent(es -> eventSourceManager.registerEventSource(key, es)); + } + }); } @Override @@ -299,8 +294,8 @@ public synchronized void start(boolean startEventProcessor) throws OperatorExcep try { // check that the custom resource is known by the cluster if configured that way validateCRDWithLocalModelIfRequired(resClass, controllerName, crdName, specVersion); - final var context = new EventSourceContext<>( - eventSourceManager.getControllerResourceEventSource(), configuration, kubernetesClient); + final var context = + new EventSourceContext<>(eventSourceManager, configuration, kubernetesClient); initAndRegisterEventSources(context); eventSourceManager.start(); 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 fe7717e846..e3d399b67a 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 @@ -9,13 +9,11 @@ import io.fabric8.kubernetes.api.model.HasMetadata; import io.javaoperatorsdk.operator.api.reconciler.Context; -import io.javaoperatorsdk.operator.api.reconciler.EventSourceContext; import io.javaoperatorsdk.operator.api.reconciler.Ignore; import io.javaoperatorsdk.operator.api.reconciler.ResourceDiscriminator; import io.javaoperatorsdk.operator.api.reconciler.dependent.DependentResource; import io.javaoperatorsdk.operator.api.reconciler.dependent.ReconcileResult; import io.javaoperatorsdk.operator.processing.event.ResourceID; -import io.javaoperatorsdk.operator.processing.event.source.ResourceEventSource; @Ignore public abstract class AbstractDependentResource @@ -29,7 +27,6 @@ public abstract class AbstractDependentResource protected Creator creator; protected Updater updater; protected BulkDependentResource bulkDependentResource; - private boolean returnEventSource = true; protected List> resourceDiscriminator = new ArrayList<>(1); @@ -41,23 +38,6 @@ public AbstractDependentResource() { bulkDependentResource = bulk ? (BulkDependentResource) this : null; } - @Override - public void doNotProvideEventSource() { - this.returnEventSource = false; - } - - @Override - public Optional> eventSource(EventSourceContext

eventSourceContext) { - if (!returnEventSource) { - return Optional.empty(); - } else { - return Optional.of(provideEventSource(eventSourceContext)); - } - } - - protected abstract ResourceEventSource provideEventSource( - EventSourceContext

eventSourceContext); - @Override public ReconcileResult reconcile(P primary, Context

context) { if (bulk) { @@ -238,8 +218,4 @@ public ResourceDiscriminator getResourceDiscriminator() { protected int lastKnownBulkSize() { return resourceDiscriminator.size(); } - - protected boolean getReturnEventSource() { - return returnEventSource; - } } diff --git a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/AbstractEventSourceHolderDependentResource.java b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/AbstractEventSourceHolderDependentResource.java index a90017897a..06532200ae 100644 --- a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/AbstractEventSourceHolderDependentResource.java +++ b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/AbstractEventSourceHolderDependentResource.java @@ -7,7 +7,6 @@ import io.javaoperatorsdk.operator.api.reconciler.Ignore; import io.javaoperatorsdk.operator.api.reconciler.dependent.EventSourceAware; import io.javaoperatorsdk.operator.api.reconciler.dependent.RecentOperationCacheFiller; -import io.javaoperatorsdk.operator.processing.event.EventSourceRetriever; import io.javaoperatorsdk.operator.processing.event.ResourceID; import io.javaoperatorsdk.operator.processing.event.source.ResourceEventSource; import io.javaoperatorsdk.operator.processing.event.source.filter.GenericFilter; @@ -17,7 +16,7 @@ @Ignore public abstract class AbstractEventSourceHolderDependentResource> - extends AbstractDependentResource implements EventSourceAware

{ + extends AbstractDependentResource implements EventSourceAware { private T eventSource; private final Class resourceType; @@ -33,7 +32,9 @@ protected AbstractEventSourceHolderDependentResource(Class resourceType) { } - public ResourceEventSource provideEventSource(EventSourceContext

context) { + @SuppressWarnings("unchecked") + @Override + public Optional> eventSource(EventSourceContext

context) { // some sub-classes (e.g. KubernetesDependentResource) can have their event source created // before this method is called in the managed case, so only create the event source if it // hasn't already been set. @@ -41,26 +42,16 @@ public ResourceEventSource provideEventSource(EventSourceContext

contex // event source // is shared between dependent resources this does not override the existing filters. if (eventSource == null) { - setEventSource(createEventSource(context)); + T localEventSource = + (T) EventSourceAware.super.eventSource(context).orElse(createEventSource(context)); + setEventSource(localEventSource); applyFilters(); } - return eventSource; - } - - @SuppressWarnings("unchecked") - @Override - public void selectEventSources(EventSourceRetriever

eventSourceRetriever) { - if (!getReturnEventSource()) { - if (eventSourceToUse != null) { - setEventSource( - (T) eventSourceRetriever.getResourceEventSourceFor(resourceType(), eventSourceToUse)); - } else { - setEventSource((T) eventSourceRetriever.getResourceEventSourceFor(resourceType())); - } - } + return Optional.of(eventSource); } /** To make this backwards compatible even for respect of overriding */ + @SuppressWarnings("unchecked") public T initEventSource(EventSourceContext

context) { return (T) eventSource(context).orElseThrow(); } @@ -117,9 +108,13 @@ public void setOnDeleteFilter(OnDeleteFilter onDeleteFilter) { this.onDeleteFilter = onDeleteFilter; } - public AbstractEventSourceHolderDependentResource setEventSourceToUse( - String eventSourceToUse) { - this.eventSourceToUse = eventSourceToUse; - return this; + @Override + public void useEventSourceNamed(String eventSourceName) { + this.eventSourceToUse = eventSourceName; + } + + @Override + public Optional getUsedEventSourceName() { + return Optional.ofNullable(eventSourceToUse); } } 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 37d2246a58..df195fad29 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 @@ -60,10 +60,6 @@ public void configureWith(KubernetesDependentResourceConfig config) { if (discriminator != null) { setResourceDiscriminator(discriminator); } - config.getEventSourceToUse().ifPresent(n -> { - doNotProvideEventSource(); - setEventSourceToUse(n); - }); } private void configureWith(String labelSelector, Set namespaces, @@ -160,7 +156,6 @@ public void deleteBulkResourceWithIndex(P primary, R resource, int i, Context

client.resource(resource).delete(); } - @SuppressWarnings("unchecked") protected Resource prepare(R desired, P primary, String actionName) { log.debug("{} target resource with type: {}, with id: {}", diff --git a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/kubernetes/KubernetesDependentResourceConfig.java b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/kubernetes/KubernetesDependentResourceConfig.java index 8d092fdfa5..188b805fc4 100644 --- a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/kubernetes/KubernetesDependentResourceConfig.java +++ b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/kubernetes/KubernetesDependentResourceConfig.java @@ -1,6 +1,5 @@ package io.javaoperatorsdk.operator.processing.dependent.kubernetes; -import java.util.Optional; import java.util.Set; import io.javaoperatorsdk.operator.api.reconciler.Constants; @@ -18,8 +17,6 @@ public class KubernetesDependentResourceConfig { private String labelSelector = NO_VALUE_SET; private boolean namespacesWereConfigured = false; private ResourceDiscriminator resourceDiscriminator; - private String eventSourceToUse; - private OnAddFilter onAddFilter; private OnUpdateFilter onUpdateFilter; @@ -34,7 +31,7 @@ public KubernetesDependentResourceConfig(Set namespaces, String labelSel boolean configuredNS, ResourceDiscriminator resourceDiscriminator, OnAddFilter onAddFilter, OnUpdateFilter onUpdateFilter, - OnDeleteFilter onDeleteFilter, GenericFilter genericFilter, String eventSourceToUse) { + OnDeleteFilter onDeleteFilter, GenericFilter genericFilter) { this.namespaces = namespaces; this.labelSelector = labelSelector; this.namespacesWereConfigured = configuredNS; @@ -43,12 +40,10 @@ public KubernetesDependentResourceConfig(Set namespaces, String labelSel this.onDeleteFilter = onDeleteFilter; this.genericFilter = genericFilter; this.resourceDiscriminator = resourceDiscriminator; - this.eventSourceToUse = eventSourceToUse; } public KubernetesDependentResourceConfig(Set namespaces, String labelSelector) { - this(namespaces, labelSelector, true, null, null, null, - null, null, null); + this(namespaces, labelSelector, true, null, null, null, null, null); } public KubernetesDependentResourceConfig setNamespaces(Set namespaces) { @@ -96,8 +91,4 @@ public GenericFilter genericFilter() { public ResourceDiscriminator getResourceDiscriminator() { return resourceDiscriminator; } - - public Optional getEventSourceToUse() { - return Optional.ofNullable(eventSourceToUse); - } } diff --git a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/workflow/ManagedWorkflowSupport.java b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/workflow/ManagedWorkflowSupport.java index 8ca56960b5..b34d58af74 100644 --- a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/workflow/ManagedWorkflowSupport.java +++ b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/workflow/ManagedWorkflowSupport.java @@ -15,6 +15,7 @@ import io.javaoperatorsdk.operator.api.config.ConfigurationServiceProvider; import io.javaoperatorsdk.operator.api.config.dependent.DependentResourceSpec; import io.javaoperatorsdk.operator.api.reconciler.dependent.DependentResource; +import io.javaoperatorsdk.operator.api.reconciler.dependent.EventSourceAware; import io.javaoperatorsdk.operator.api.reconciler.dependent.managed.DependentResourceConfigurator; import io.javaoperatorsdk.operator.api.reconciler.dependent.managed.KubernetesClientAware; import io.javaoperatorsdk.operator.processing.dependent.workflow.builder.WorkflowBuilder; @@ -78,12 +79,14 @@ public DependentResource createAndConfigureFrom(DependentResourceSpec spec, final var dependentResource = ConfigurationServiceProvider.instance().dependentResourceFactory().createFrom(spec); + if (dependentResource instanceof EventSourceAware) { + EventSourceAware eventSourceAware = (EventSourceAware) dependentResource; + spec.getEventSourceName().ifPresent(n -> eventSourceAware.useEventSourceNamed((String) n)); + } + if (dependentResource instanceof KubernetesClientAware) { ((KubernetesClientAware) dependentResource).setKubernetesClient(client); } - if (!spec.provideEventSource()) { - dependentResource.doNotProvideEventSource(); - } if (dependentResource instanceof DependentResourceConfigurator) { final var configurator = (DependentResourceConfigurator) dependentResource; spec.getDependentResourceConfiguration().ifPresent(configurator::configureWith); diff --git a/operator-framework-core/src/test/java/io/javaoperatorsdk/operator/api/config/ControllerConfigurationOverriderTest.java b/operator-framework-core/src/test/java/io/javaoperatorsdk/operator/api/config/ControllerConfigurationOverriderTest.java index 312d307cc1..4dd6938728 100644 --- a/operator-framework-core/src/test/java/io/javaoperatorsdk/operator/api/config/ControllerConfigurationOverriderTest.java +++ b/operator-framework-core/src/test/java/io/javaoperatorsdk/operator/api/config/ControllerConfigurationOverriderTest.java @@ -80,9 +80,6 @@ public ReconcileResult reconcile(ConfigMap primary, Context c public Class resourceType() { return Object.class; } - - @Override - public void doNotProvideEventSource() {} } } diff --git a/operator-framework-core/src/test/java/io/javaoperatorsdk/operator/processing/dependent/AbstractDependentResourceTest.java b/operator-framework-core/src/test/java/io/javaoperatorsdk/operator/processing/dependent/AbstractDependentResourceTest.java index 0fd85c5afc..b93abd45c0 100644 --- a/operator-framework-core/src/test/java/io/javaoperatorsdk/operator/processing/dependent/AbstractDependentResourceTest.java +++ b/operator-framework-core/src/test/java/io/javaoperatorsdk/operator/processing/dependent/AbstractDependentResourceTest.java @@ -7,9 +7,7 @@ import io.fabric8.kubernetes.api.model.ConfigMap; import io.fabric8.kubernetes.api.model.ObjectMetaBuilder; import io.javaoperatorsdk.operator.api.reconciler.Context; -import io.javaoperatorsdk.operator.api.reconciler.EventSourceContext; import io.javaoperatorsdk.operator.processing.event.ResourceID; -import io.javaoperatorsdk.operator.processing.event.source.ResourceEventSource; import io.javaoperatorsdk.operator.sample.simple.TestCustomResource; import static org.junit.jupiter.api.Assertions.*; @@ -80,12 +78,6 @@ public Class resourceType() { return ConfigMap.class; } - @Override - protected ResourceEventSource provideEventSource( - EventSourceContext eventSourceContext) { - return null; - } - @Override public Optional getSecondaryResource(TestCustomResource primary, Context context) { diff --git a/operator-framework-core/src/test/java/io/javaoperatorsdk/operator/processing/dependent/EmptyTestDependentResource.java b/operator-framework-core/src/test/java/io/javaoperatorsdk/operator/processing/dependent/EmptyTestDependentResource.java index dd42a80392..dab1bc0132 100644 --- a/operator-framework-core/src/test/java/io/javaoperatorsdk/operator/processing/dependent/EmptyTestDependentResource.java +++ b/operator-framework-core/src/test/java/io/javaoperatorsdk/operator/processing/dependent/EmptyTestDependentResource.java @@ -20,8 +20,5 @@ public Class resourceType() { return Deployment.class; } - @Override - public void doNotProvideEventSource() {} - } diff --git a/operator-framework-core/src/test/java/io/javaoperatorsdk/operator/processing/dependent/workflow/AbstractWorkflowExecutorTest.java b/operator-framework-core/src/test/java/io/javaoperatorsdk/operator/processing/dependent/workflow/AbstractWorkflowExecutorTest.java index 2ecb6bde24..9c10c06cc0 100644 --- a/operator-framework-core/src/test/java/io/javaoperatorsdk/operator/processing/dependent/workflow/AbstractWorkflowExecutorTest.java +++ b/operator-framework-core/src/test/java/io/javaoperatorsdk/operator/processing/dependent/workflow/AbstractWorkflowExecutorTest.java @@ -48,9 +48,6 @@ public Class resourceType() { return String.class; } - @Override - public void doNotProvideEventSource() {} - @Override public String toString() { return name; @@ -110,9 +107,6 @@ public Class resourceType() { return String.class; } - @Override - public void doNotProvideEventSource() {} - @Override public String toString() { return name; diff --git a/operator-framework-core/src/test/java/io/javaoperatorsdk/operator/processing/dependent/workflow/ManagedWorkflowTestUtils.java b/operator-framework-core/src/test/java/io/javaoperatorsdk/operator/processing/dependent/workflow/ManagedWorkflowTestUtils.java index 72a3886963..e3b97afb3c 100644 --- a/operator-framework-core/src/test/java/io/javaoperatorsdk/operator/processing/dependent/workflow/ManagedWorkflowTestUtils.java +++ b/operator-framework-core/src/test/java/io/javaoperatorsdk/operator/processing/dependent/workflow/ManagedWorkflowTestUtils.java @@ -12,7 +12,7 @@ public class ManagedWorkflowTestUtils { public static DependentResourceSpec createDRS(String name, String... dependOns) { return new DependentResourceSpec(EmptyTestDependentResource.class, null, name, Set.of(dependOns), null, null, null, - true); + null); } } diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/multiplemanagedexternaldependenttype/ExternalResourceDiscriminator.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/multiplemanagedexternaldependenttype/ExternalResourceDiscriminator.java index ba265455de..5a394113c1 100644 --- a/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/multiplemanagedexternaldependenttype/ExternalResourceDiscriminator.java +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/multiplemanagedexternaldependenttype/ExternalResourceDiscriminator.java @@ -9,7 +9,7 @@ public class ExternalResourceDiscriminator implements ResourceDiscriminator { - private String suffix; + private final String suffix; public ExternalResourceDiscriminator(String suffix) { this.suffix = suffix; diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/multiplemanagedexternaldependenttype/MultipleManagedExternalDependentResourceReconciler.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/multiplemanagedexternaldependenttype/MultipleManagedExternalDependentResourceReconciler.java index 573df92287..28b94ac715 100644 --- a/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/multiplemanagedexternaldependenttype/MultipleManagedExternalDependentResourceReconciler.java +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/multiplemanagedexternaldependenttype/MultipleManagedExternalDependentResourceReconciler.java @@ -16,8 +16,10 @@ import io.javaoperatorsdk.operator.support.TestExecutionInfoProvider; @ControllerConfiguration(dependents = { - @Dependent(type = ExternalDependentResource1.class, provideEventSource = false), - @Dependent(type = ExternalDependentResource2.class, provideEventSource = false) + @Dependent(type = ExternalDependentResource1.class, + eventSource = MultipleManagedExternalDependentResourceReconciler.CONFIG_MAP_EVENT_SOURCE), + @Dependent(type = ExternalDependentResource2.class, + eventSource = MultipleManagedExternalDependentResourceReconciler.CONFIG_MAP_EVENT_SOURCE) }) public class MultipleManagedExternalDependentResourceReconciler implements Reconciler, @@ -47,17 +49,16 @@ public int getNumberOfExecutions() { public Map prepareEventSources( EventSourceContext context) { - PollingEventSource pollingEventSource = - new PollingEventSource<>(() -> { - var lists = externalServiceMock.listResources(); - Map> res = new HashMap<>(); - lists.forEach(er -> { - var resourceId = er.toResourceID(); - res.computeIfAbsent(resourceId, rid -> new HashSet<>()); - res.get(resourceId).add(er); - }); - return res; - }, 1000L, ExternalResource.class, ExternalResource::getId); + final var pollingEventSource = new PollingEventSource<>(() -> { + var lists = externalServiceMock.listResources(); + Map> res = new HashMap<>(); + lists.forEach(er -> { + var resourceId = er.toResourceID(); + res.computeIfAbsent(resourceId, rid -> new HashSet<>()); + res.get(resourceId).add(er); + }); + return res; + }, 1000L, ExternalResource.class, ExternalResource::getId); return Map.of(CONFIG_MAP_EVENT_SOURCE, pollingEventSource); } From 6fa1b5d0262a2e934ab319e7a6a856916cab6ea3 Mon Sep 17 00:00:00 2001 From: csviri Date: Tue, 4 Oct 2022 09:46:04 +0200 Subject: [PATCH 67/67] docs: javadoc fix --- .../operator/api/reconciler/dependent/Dependent.java | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/reconciler/dependent/Dependent.java b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/reconciler/dependent/Dependent.java index 06ec90eba5..2f2f1f1e0d 100644 --- a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/reconciler/dependent/Dependent.java +++ b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/reconciler/dependent/Dependent.java @@ -59,11 +59,11 @@ String[] dependsOn() default {}; /** - * Setting this to false means that the event source provided by the dependent resource won't be - * used. This is helpful if more dependent resources created for the same type, and want to share - * a common event source. In that case an event source needs to be explicitly registered. + * Setting here a name of the event source means that dependent resource will use an event source + * registered with that name. So won't create one. This is helpful if more dependent resources + * created for the same type, and want to share a common event source. * - * @return if the event source (if any) provided by the dependent resource should be used or not. + * @return event source name (if any) provided by the dependent resource should be used. */ String eventSource() default NO_VALUE_SET; }