From 383b1e59b222203e06b75a6a1a28cd8f8b749d4c Mon Sep 17 00:00:00 2001 From: Chris Laprun Date: Mon, 21 Mar 2022 21:39:39 +0100 Subject: [PATCH 01/15] fix: retrieve DependentResource based on name instead of class This causes issues if the class is proxied for any reason (e.g. because it's a CDI bean). --- .../io/javaoperatorsdk/operator/processing/Controller.java | 6 ++++-- 1 file changed, 4 insertions(+), 2 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 2a96965000..4073cd80c0 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 @@ -1,10 +1,13 @@ package io.javaoperatorsdk.operator.processing; +import java.util.Collections; import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; import java.util.Map; +import java.util.Map; import java.util.Optional; +import java.util.function.Function; import java.util.stream.Collectors; import org.slf4j.Logger; @@ -190,7 +193,7 @@ public String successTypeName(UpdateControl

result) { public UpdateControl

execute() throws Exception { initContextIfNeeded(resource, context); final var exceptions = new ArrayList(dependents.size()); - dependents.forEach((name, dependent) -> { + dependents.values().forEach((name, dependent) -> { try { final var reconcileResult = dependent.reconcile(resource, context); context.managedDependentResourceContext().setReconcileResult(name, reconcileResult); @@ -209,7 +212,6 @@ public UpdateControl

execute() throws Exception { .collect(Collectors.joining("\n")), exceptions); } - return reconciler.reconcile(resource, context); } }); From d0be9fad6c2f134d58a97dcd7a61433c958011c0 Mon Sep 17 00:00:00 2001 From: Chris Laprun Date: Wed, 23 Mar 2022 10:24:09 +0100 Subject: [PATCH 02/15] feat: record association of name and DependentResource in configuration --- .../io/javaoperatorsdk/operator/processing/Controller.java | 3 +-- 1 file changed, 1 insertion(+), 2 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 4073cd80c0..1fe2c86133 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 @@ -1,14 +1,13 @@ package io.javaoperatorsdk.operator.processing; import java.util.Collections; +import java.util.HashMap; import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; import java.util.Map; import java.util.Map; import java.util.Optional; -import java.util.function.Function; -import java.util.stream.Collectors; import org.slf4j.Logger; import org.slf4j.LoggerFactory; From 5ea7de2cfb8ede5b59f60897500413c8f743a629 Mon Sep 17 00:00:00 2001 From: Chris Laprun Date: Wed, 23 Mar 2022 17:07:37 +0100 Subject: [PATCH 03/15] feat: make it possible to retrieve actual dependent resource type --- .../javaoperatorsdk/operator/sample/MySQLSchemaReconciler.java | 1 + 1 file changed, 1 insertion(+) diff --git a/sample-operators/mysql-schema/src/main/java/io/javaoperatorsdk/operator/sample/MySQLSchemaReconciler.java b/sample-operators/mysql-schema/src/main/java/io/javaoperatorsdk/operator/sample/MySQLSchemaReconciler.java index 4d3b7402f9..c3801d77aa 100644 --- a/sample-operators/mysql-schema/src/main/java/io/javaoperatorsdk/operator/sample/MySQLSchemaReconciler.java +++ b/sample-operators/mysql-schema/src/main/java/io/javaoperatorsdk/operator/sample/MySQLSchemaReconciler.java @@ -11,6 +11,7 @@ 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.sample.dependent.SchemaDependentResource; import io.javaoperatorsdk.operator.sample.dependent.SecretDependentResource; From c40bd333509d313ce5995e93d23f322ebe20f369 Mon Sep 17 00:00:00 2001 From: Chris Laprun Date: Wed, 23 Mar 2022 22:08:01 +0100 Subject: [PATCH 04/15] fix: use proper name to retrieve SchemaDependentResource --- .../javaoperatorsdk/operator/sample/MySQLSchemaReconciler.java | 1 - 1 file changed, 1 deletion(-) diff --git a/sample-operators/mysql-schema/src/main/java/io/javaoperatorsdk/operator/sample/MySQLSchemaReconciler.java b/sample-operators/mysql-schema/src/main/java/io/javaoperatorsdk/operator/sample/MySQLSchemaReconciler.java index c3801d77aa..4d3b7402f9 100644 --- a/sample-operators/mysql-schema/src/main/java/io/javaoperatorsdk/operator/sample/MySQLSchemaReconciler.java +++ b/sample-operators/mysql-schema/src/main/java/io/javaoperatorsdk/operator/sample/MySQLSchemaReconciler.java @@ -11,7 +11,6 @@ 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.sample.dependent.SchemaDependentResource; import io.javaoperatorsdk.operator.sample.dependent.SecretDependentResource; From 78965426ac6084d8bcd03f37a4e526d830448443 Mon Sep 17 00:00:00 2001 From: Chris Laprun Date: Wed, 23 Mar 2022 23:13:10 +0100 Subject: [PATCH 05/15] feat: add simplified logging for reconcile logic --- .../processing/dependent/AbstractDependentResource.java | 7 +------ 1 file changed, 1 insertion(+), 6 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 8b1a634c08..e8e3b9b835 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 @@ -5,12 +5,7 @@ import io.fabric8.kubernetes.api.model.HasMetadata; import io.javaoperatorsdk.operator.api.reconciler.Context; -import io.javaoperatorsdk.operator.api.reconciler.dependent.Deleter; -import io.javaoperatorsdk.operator.api.reconciler.dependent.DependentResource; -import io.javaoperatorsdk.operator.api.reconciler.dependent.EventSourceProvider; -import io.javaoperatorsdk.operator.api.reconciler.dependent.RecentOperationCacheFiller; -import io.javaoperatorsdk.operator.api.reconciler.dependent.RecentOperationEventFilter; -import io.javaoperatorsdk.operator.api.reconciler.dependent.ReconcileResult; +import io.javaoperatorsdk.operator.api.reconciler.dependent.*; import io.javaoperatorsdk.operator.processing.event.ResourceID; public abstract class AbstractDependentResource From 2c722aa47236fd01ca95a0f810fc825789c60dad Mon Sep 17 00:00:00 2001 From: Chris Laprun Date: Thu, 24 Mar 2022 12:45:25 +0100 Subject: [PATCH 06/15] feat: EventSource are now registered with a name --- .../operator/processing/event/EventSourceManager.java | 1 + 1 file changed, 1 insertion(+) 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 86fb203f97..1fdb3c398f 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 @@ -6,6 +6,7 @@ import java.util.Set; import java.util.concurrent.locks.ReentrantLock; import java.util.stream.Collectors; +import java.util.stream.Stream; import org.slf4j.Logger; import org.slf4j.LoggerFactory; From 9fb1ab5ebcb8893867348d467cfc18e1b05fd21d Mon Sep 17 00:00:00 2001 From: Chris Laprun Date: Thu, 24 Mar 2022 13:54:05 +0100 Subject: [PATCH 07/15] feat: log dependent reconciliation result --- .../java/io/javaoperatorsdk/operator/processing/Controller.java | 2 ++ 1 file changed, 2 insertions(+) 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 1fe2c86133..e7daf6d403 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 @@ -8,6 +8,7 @@ import java.util.Map; import java.util.Map; import java.util.Optional; +import java.util.stream.Collectors; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -40,6 +41,7 @@ import io.javaoperatorsdk.operator.api.reconciler.dependent.Deleter; import io.javaoperatorsdk.operator.api.reconciler.dependent.DependentResource; import io.javaoperatorsdk.operator.api.reconciler.dependent.EventSourceProvider; +import io.javaoperatorsdk.operator.api.reconciler.dependent.ReconcileResult; import io.javaoperatorsdk.operator.api.reconciler.dependent.managed.DependentResourceConfigurator; import io.javaoperatorsdk.operator.api.reconciler.dependent.managed.KubernetesClientAware; import io.javaoperatorsdk.operator.api.reconciler.dependent.managed.ManagedDependentResourceException; From 3697f29c17bb08b18c77a5561c64d2250277f27f Mon Sep 17 00:00:00 2001 From: Chris Laprun Date: Fri, 25 Mar 2022 15:17:42 +0100 Subject: [PATCH 08/15] feat: aggregate dependent exceptions instead of using error result --- .../io/javaoperatorsdk/operator/processing/Controller.java | 5 ----- 1 file changed, 5 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 e7daf6d403..b9d0333bf4 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 @@ -1,14 +1,10 @@ package io.javaoperatorsdk.operator.processing; -import java.util.Collections; -import java.util.HashMap; import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; import java.util.Map; -import java.util.Map; import java.util.Optional; -import java.util.stream.Collectors; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -41,7 +37,6 @@ import io.javaoperatorsdk.operator.api.reconciler.dependent.Deleter; import io.javaoperatorsdk.operator.api.reconciler.dependent.DependentResource; import io.javaoperatorsdk.operator.api.reconciler.dependent.EventSourceProvider; -import io.javaoperatorsdk.operator.api.reconciler.dependent.ReconcileResult; import io.javaoperatorsdk.operator.api.reconciler.dependent.managed.DependentResourceConfigurator; import io.javaoperatorsdk.operator.api.reconciler.dependent.managed.KubernetesClientAware; import io.javaoperatorsdk.operator.api.reconciler.dependent.managed.ManagedDependentResourceException; From b6be8d3224519ab04374e0eeb65dc639293be7fc Mon Sep 17 00:00:00 2001 From: Chris Laprun Date: Fri, 25 Mar 2022 15:35:16 +0100 Subject: [PATCH 09/15] refactor: extract inner classes from EventSourceManager --- .../operator/processing/event/EventSourceManager.java | 1 - 1 file changed, 1 deletion(-) 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 1fdb3c398f..86fb203f97 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 @@ -6,7 +6,6 @@ import java.util.Set; import java.util.concurrent.locks.ReentrantLock; import java.util.stream.Collectors; -import java.util.stream.Stream; import org.slf4j.Logger; import org.slf4j.LoggerFactory; From 4f45652474dbdb90b0eb6e005592188f87c8fea0 Mon Sep 17 00:00:00 2001 From: Chris Laprun Date: Sun, 27 Mar 2022 00:25:37 +0100 Subject: [PATCH 10/15] refactor: improve error reporting, remove redundant logging --- .../java/io/javaoperatorsdk/operator/processing/Controller.java | 1 + 1 file changed, 1 insertion(+) 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 b9d0333bf4..80423474cc 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 @@ -5,6 +5,7 @@ import java.util.HashMap; import java.util.Map; import java.util.Optional; +import java.util.stream.Collectors; import org.slf4j.Logger; import org.slf4j.LoggerFactory; From c1c5e716b17088eaf8bc81ff1c4a97b3fb818760 Mon Sep 17 00:00:00 2001 From: Chris Laprun Date: Sat, 26 Mar 2022 17:14:06 +0100 Subject: [PATCH 11/15] feat: add a DependentResource implementation that's also an EventSource The benefit of this change is multi-fold: - simplifies AbstractDependentResource which doesn't have to handle cases it should not be concerned about - allows to share more code with AbstractSimpleDependentResource - enforces that the same name is used for the DependentResource and EventSource even in the standalone mode when using one of the provided implementations --- .../reconciler/EventSourceInitializer.java | 2 +- .../dependent/EventSourceProvider.java | 2 - .../dependent/AbstractDependentResource.java | 85 ++----------- ...actEventSourceHolderDependentResource.java | 114 ++++++++++++++++++ .../AbstractCachingDependentResource.java | 19 +-- .../AbstractSimpleDependentResource.java | 12 +- .../PerResourcePollingDependentResource.java | 8 +- .../external/PollingDependentResource.java | 8 +- .../KubernetesDependentResource.java | 41 ++----- 9 files changed, 152 insertions(+), 139 deletions(-) create mode 100644 operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/AbstractEventSourceHolderDependentResource.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 38f21683d8..4983db08b4 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 @@ -20,6 +20,6 @@ public interface EventSourceInitializer

{ * sources * @return list of event sources to register */ - Map prepareEventSources(EventSourceContext

context); + Map prepareEventSources(EventSourceContext

context); } 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 cc1514fac0..98190cb7ef 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 @@ -10,6 +10,4 @@ public interface EventSourceProvider

{ * @return the initiated event source. */ EventSource initEventSource(EventSourceContext

context); - - EventSource getEventSource(); } 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 e8e3b9b835..8c4ef11b1b 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 @@ -63,89 +63,20 @@ public ReconcileResult reconcile(P primary, Context

context) { protected R handleCreate(R desired, P primary, Context

context) { ResourceID resourceID = ResourceID.fromResource(primary); - R created = null; - try { - prepareEventFiltering(desired, resourceID); - created = creator.create(desired, primary, context); - cacheAfterCreate(resourceID, created); - return created; - } catch (RuntimeException e) { - cleanupAfterEventFiltering(desired, resourceID, created); - throw e; - } - } - - private void cleanupAfterEventFiltering(R desired, ResourceID resourceID, R created) { - if (isFilteringEventSource()) { - eventSourceAsRecentOperationEventFilter() - .cleanupOnCreateOrUpdateEventFiltering(resourceID, created); - } - } - - private void cacheAfterCreate(ResourceID resourceID, R created) { - if (isRecentOperationCacheFiller()) { - eventSourceAsRecentOperationCacheFiller().handleRecentResourceCreate(resourceID, created); - } + R created = creator.create(desired, primary, context); + cacheAfterCreate(resourceID, created); + return created; } - private void cacheAfterUpdate(R actual, ResourceID resourceID, R updated) { - if (isRecentOperationCacheFiller()) { - eventSourceAsRecentOperationCacheFiller().handleRecentResourceUpdate(resourceID, updated, - actual); - } - } + protected abstract void cacheAfterCreate(ResourceID resourceID, R created); - private void prepareEventFiltering(R desired, ResourceID resourceID) { - if (isFilteringEventSource()) { - eventSourceAsRecentOperationEventFilter().prepareForCreateOrUpdateEventFiltering(resourceID, - desired); - } - } + protected abstract void cacheAfterUpdate(R actual, ResourceID resourceID, R updated); protected R handleUpdate(R actual, R desired, P primary, Context

context) { ResourceID resourceID = ResourceID.fromResource(primary); - R updated = null; - try { - prepareEventFiltering(desired, resourceID); - updated = updater.update(actual, desired, primary, context); - cacheAfterUpdate(actual, resourceID, updated); - return updated; - } catch (RuntimeException e) { - cleanupAfterEventFiltering(desired, resourceID, updated); - throw e; - } - } - - @SuppressWarnings("unchecked") - private RecentOperationEventFilter eventSourceAsRecentOperationEventFilter() { - return (RecentOperationEventFilter) ((EventSourceProvider

) this).getEventSource(); - } - - @SuppressWarnings("unchecked") - private RecentOperationCacheFiller eventSourceAsRecentOperationCacheFiller() { - return (RecentOperationCacheFiller) ((EventSourceProvider

) this).getEventSource(); - } - - @SuppressWarnings("unchecked") - // this cannot be done in constructor since event source might be initialized later - protected boolean isFilteringEventSource() { - if (this instanceof EventSourceProvider) { - final var eventSource = ((EventSourceProvider

) this).getEventSource(); - return eventSource instanceof RecentOperationEventFilter; - } else { - return false; - } - } - - @SuppressWarnings("unchecked") - // this cannot be done in constructor since event source might be initialized later - protected boolean isRecentOperationCacheFiller() { - if (this instanceof EventSourceProvider) { - final var eventSource = ((EventSourceProvider

) this).getEventSource(); - return eventSource instanceof RecentOperationCacheFiller; - } else { - return false; - } + R updated = updater.update(actual, desired, primary, context); + cacheAfterUpdate(actual, resourceID, updated); + return updated; } protected R desired(P primary, Context

context) { 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 new file mode 100644 index 0000000000..7444db1912 --- /dev/null +++ b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/AbstractEventSourceHolderDependentResource.java @@ -0,0 +1,114 @@ +package io.javaoperatorsdk.operator.processing.dependent; + +import io.fabric8.kubernetes.api.model.HasMetadata; +import io.javaoperatorsdk.operator.OperatorException; +import io.javaoperatorsdk.operator.api.reconciler.Context; +import io.javaoperatorsdk.operator.api.reconciler.EventSourceContext; +import io.javaoperatorsdk.operator.api.reconciler.dependent.EventSourceProvider; +import io.javaoperatorsdk.operator.api.reconciler.dependent.RecentOperationCacheFiller; +import io.javaoperatorsdk.operator.api.reconciler.dependent.RecentOperationEventFilter; +import io.javaoperatorsdk.operator.processing.event.EventHandler; +import io.javaoperatorsdk.operator.processing.event.ResourceID; +import io.javaoperatorsdk.operator.processing.event.source.EventSource; + +public abstract class AbstractEventSourceHolderDependentResource + extends AbstractDependentResource implements EventSource, EventSourceProvider

{ + private T eventSource; + private boolean isFilteringEventSource; + private boolean isCacheFillerEventSource; + + @Override + public void start() throws OperatorException { + eventSource.start(); + } + + @Override + public void stop() throws OperatorException { + eventSource.stop(); + } + + public EventSource initEventSource(EventSourceContext

context) { + if (eventSource == null) { + eventSource = createEventSource(context); + isFilteringEventSource = eventSource instanceof RecentOperationEventFilter; + isCacheFillerEventSource = eventSource instanceof RecentOperationCacheFiller; + } + return this; + } + + protected abstract T createEventSource(EventSourceContext

context); + + protected void setEventSource(T eventSource) { + this.eventSource = eventSource; + } + + @Override + public void setEventHandler(EventHandler handler) { + eventSource.setEventHandler(handler); + } + + protected T eventSource() { + return eventSource; + } + + protected R handleCreate(R desired, P primary, Context

context) { + ResourceID resourceID = ResourceID.fromResource(primary); + R created = null; + try { + prepareEventFiltering(desired, resourceID); + created = super.handleCreate(desired, primary, context); + return created; + } catch (RuntimeException e) { + cleanupAfterEventFiltering(desired, resourceID, created); + throw e; + } + } + + protected R handleUpdate(R actual, R desired, P primary, Context

context) { + ResourceID resourceID = ResourceID.fromResource(primary); + R updated = null; + try { + prepareEventFiltering(desired, resourceID); + updated = super.handleUpdate(actual, desired, primary, context); + return updated; + } catch (RuntimeException e) { + cleanupAfterEventFiltering(desired, resourceID, updated); + throw e; + } + } + + + protected void cacheAfterCreate(ResourceID resourceID, R created) { + if (isCacheFillerEventSource) { + recentOperationCacheFiller().handleRecentResourceCreate(resourceID, created); + } + } + + protected void cacheAfterUpdate(R actual, ResourceID resourceID, R updated) { + if (isCacheFillerEventSource) { + recentOperationCacheFiller().handleRecentResourceUpdate(resourceID, updated, actual); + } + } + + private void prepareEventFiltering(R desired, ResourceID resourceID) { + if (isFilteringEventSource) { + recentOperationEventFilter().prepareForCreateOrUpdateEventFiltering(resourceID, desired); + } + } + + private void cleanupAfterEventFiltering(R desired, ResourceID resourceID, R created) { + if (isFilteringEventSource) { + recentOperationEventFilter().cleanupOnCreateOrUpdateEventFiltering(resourceID, created); + } + } + + @SuppressWarnings("unchecked") + private RecentOperationEventFilter recentOperationEventFilter() { + return (RecentOperationEventFilter) eventSource; + } + + @SuppressWarnings("unchecked") + private RecentOperationCacheFiller recentOperationCacheFiller() { + return (RecentOperationCacheFiller) 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 index b131850b1e..cd44012249 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 @@ -3,16 +3,12 @@ import java.util.Optional; import io.fabric8.kubernetes.api.model.HasMetadata; -import io.javaoperatorsdk.operator.api.reconciler.dependent.EventSourceProvider; -import io.javaoperatorsdk.operator.processing.dependent.AbstractDependentResource; +import io.javaoperatorsdk.operator.processing.dependent.AbstractEventSourceHolderDependentResource; import io.javaoperatorsdk.operator.processing.event.ExternalResourceCachingEventSource; -import io.javaoperatorsdk.operator.processing.event.source.EventSource; public abstract class AbstractCachingDependentResource - extends AbstractDependentResource - implements EventSourceProvider

{ - - protected ExternalResourceCachingEventSource eventSource; + extends + AbstractEventSourceHolderDependentResource> { private final Class resourceType; protected AbstractCachingDependentResource(Class resourceType) { @@ -20,12 +16,7 @@ protected AbstractCachingDependentResource(Class resourceType) { } public Optional fetchResource(P primaryResource) { - return eventSource.getAssociated(primaryResource); - } - - @Override - public EventSource getEventSource() { - return eventSource; + return eventSource().getAssociated(primaryResource); } @Override @@ -35,6 +26,6 @@ public Class resourceType() { @Override public Optional getResource(P primaryResource) { - return eventSource.getAssociated(primaryResource); + return eventSource().getAssociated(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 2b91e6d75f..afd15dcb6d 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 @@ -59,17 +59,13 @@ public final void delete(P primary, Context

context) { protected abstract void deleteResource(P primary, Context

context); @Override - protected R handleCreate(R desired, P primary, Context

context) { - var res = this.creator.create(desired, primary, context); - cache.put(ResourceID.fromResource(primary), res); - return res; + protected void cacheAfterCreate(ResourceID resourceID, R created) { + cache.put(resourceID, created); } @Override - protected R handleUpdate(R actual, R desired, P primary, Context

context) { - var res = updater.update(actual, desired, primary, context); - cache.put(ResourceID.fromResource(primary), res); - return res; + protected void cacheAfterUpdate(R actual, ResourceID resourceID, R updated) { + cache.put(resourceID, updated); } public Matcher.Result match(R actualResource, P primary, Context

context) { diff --git a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/external/PerResourcePollingDependentResource.java b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/external/PerResourcePollingDependentResource.java index 11b18832a7..b598073a21 100644 --- a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/external/PerResourcePollingDependentResource.java +++ b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/external/PerResourcePollingDependentResource.java @@ -2,7 +2,7 @@ import io.fabric8.kubernetes.api.model.HasMetadata; import io.javaoperatorsdk.operator.api.reconciler.EventSourceContext; -import io.javaoperatorsdk.operator.processing.event.source.EventSource; +import io.javaoperatorsdk.operator.processing.event.ExternalResourceCachingEventSource; import io.javaoperatorsdk.operator.processing.event.source.polling.PerResourcePollingEventSource; public abstract class PerResourcePollingDependentResource @@ -17,9 +17,9 @@ public PerResourcePollingDependentResource(Class resourceType, long pollingPe } @Override - public EventSource initEventSource(EventSourceContext

context) { - eventSource = new PerResourcePollingEventSource<>(this, context.getPrimaryCache(), + protected ExternalResourceCachingEventSource createEventSource( + EventSourceContext

context) { + return new PerResourcePollingEventSource<>(this, context.getPrimaryCache(), getPollingPeriod(), resourceType()); - return eventSource; } } diff --git a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/external/PollingDependentResource.java b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/external/PollingDependentResource.java index 58723a6f11..1cc6e22afe 100644 --- a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/external/PollingDependentResource.java +++ b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/external/PollingDependentResource.java @@ -5,8 +5,8 @@ import io.fabric8.kubernetes.api.model.HasMetadata; import io.javaoperatorsdk.operator.api.reconciler.EventSourceContext; +import io.javaoperatorsdk.operator.processing.event.ExternalResourceCachingEventSource; import io.javaoperatorsdk.operator.processing.event.ResourceID; -import io.javaoperatorsdk.operator.processing.event.source.EventSource; import io.javaoperatorsdk.operator.processing.event.source.polling.PollingEventSource; public abstract class PollingDependentResource @@ -21,8 +21,8 @@ public PollingDependentResource(Class resourceType, long pollingPeriod) { } @Override - public EventSource initEventSource(EventSourceContext

context) { - eventSource = new PollingEventSource<>(this, getPollingPeriod(), resourceType()); - return eventSource; + protected ExternalResourceCachingEventSource createEventSource( + EventSourceContext

context) { + return new PollingEventSource<>(this, getPollingPeriod(), resourceType()); } } 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 f77ec0b4aa..c96f993126 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 @@ -14,28 +14,25 @@ import io.javaoperatorsdk.operator.api.config.informer.InformerConfiguration; import io.javaoperatorsdk.operator.api.reconciler.Context; import io.javaoperatorsdk.operator.api.reconciler.EventSourceContext; -import io.javaoperatorsdk.operator.api.reconciler.dependent.EventSourceProvider; import io.javaoperatorsdk.operator.api.reconciler.dependent.managed.DependentResourceConfigurator; import io.javaoperatorsdk.operator.api.reconciler.dependent.managed.KubernetesClientAware; -import io.javaoperatorsdk.operator.processing.dependent.AbstractDependentResource; +import io.javaoperatorsdk.operator.processing.dependent.AbstractEventSourceHolderDependentResource; import io.javaoperatorsdk.operator.processing.dependent.Matcher; import io.javaoperatorsdk.operator.processing.dependent.Matcher.Result; import io.javaoperatorsdk.operator.processing.event.ResourceID; -import io.javaoperatorsdk.operator.processing.event.source.EventSource; import io.javaoperatorsdk.operator.processing.event.source.PrimaryToSecondaryMapper; import io.javaoperatorsdk.operator.processing.event.source.SecondaryToPrimaryMapper; import io.javaoperatorsdk.operator.processing.event.source.informer.InformerEventSource; import io.javaoperatorsdk.operator.processing.event.source.informer.Mappers; public abstract class KubernetesDependentResource - extends AbstractDependentResource - implements KubernetesClientAware, EventSourceProvider

, + extends AbstractEventSourceHolderDependentResource> + implements KubernetesClientAware, DependentResourceConfigurator { private static final Logger log = LoggerFactory.getLogger(KubernetesDependentResource.class); protected KubernetesClient client; - private InformerEventSource informerEventSource; private final Matcher matcher; private final ResourceUpdatePreProcessor processor; private final Class resourceType; @@ -80,12 +77,11 @@ private void configureWith(String labelSelector, Set namespaces) { * * @param informerEventSource informer to use* */ - public void configureWith( - InformerEventSource informerEventSource) { - this.informerEventSource = informerEventSource; - + public void configureWith(InformerEventSource informerEventSource) { + setEventSource(informerEventSource); } + @SuppressWarnings("unused") public R create(R target, P primary, Context

context) { return prepare(target, primary, "Creating").create(target); } @@ -121,19 +117,11 @@ protected NonNamespaceOperation, Resource> prepa } @Override - public EventSource initEventSource(EventSourceContext

context) { - if (informerEventSource == null) { - configureWith(null, context.getControllerConfiguration().getNamespaces()); - log.warn("Using default configuration for " + resourceType().getSimpleName() - + " KubernetesDependentResource, call configureWith to provide configuration"); - } - return informerEventSource; - } - - public KubernetesDependentResource setInformerEventSource( - InformerEventSource informerEventSource) { - this.informerEventSource = informerEventSource; - return this; + protected InformerEventSource createEventSource(EventSourceContext

context) { + configureWith(null, context.getControllerConfiguration().getNamespaces()); + log.warn("Using default configuration for " + resourceType().getSimpleName() + + " KubernetesDependentResource, call configureWith to provide configuration"); + return eventSource(); } protected boolean addOwnerReference() { @@ -147,7 +135,7 @@ public Class resourceType() { @Override public Optional getResource(P primaryResource) { - return informerEventSource.getAssociated(primaryResource); + return eventSource().getAssociated(primaryResource); } @Override @@ -164,9 +152,4 @@ public KubernetesClient getKubernetesClient() { protected R desired(P primary, Context

context) { return super.desired(primary, context); } - - @Override - public EventSource getEventSource() { - return informerEventSource; - } } From fb2a17d3d43a4ebb8d4161a98a412fc3e04c77f8 Mon Sep 17 00:00:00 2001 From: Chris Laprun Date: Sat, 26 Mar 2022 21:25:08 +0100 Subject: [PATCH 12/15] fix: improper javadoc --- .../operator/api/reconciler/EventSourceInitializer.java | 5 +++-- 1 file changed, 3 insertions(+), 2 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 4983db08b4..d540abacfe 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 @@ -14,11 +14,12 @@ public interface EventSourceInitializer

{ /** - * Prepares a list of {@link EventSource} implementations to be registered by the SDK. + * Prepares a map of {@link EventSource} implementations keyed by the name with which they need to + * be registered by the SDK. * * @param context a {@link EventSourceContext} providing access to information useful to event * sources - * @return list of event sources to register + * @return a map of event sources to register */ Map prepareEventSources(EventSourceContext

context); From 65eac3abea88c851cd2ac81b3daf6f5bef60f7e8 Mon Sep 17 00:00:00 2001 From: Chris Laprun Date: Sat, 26 Mar 2022 23:46:16 +0100 Subject: [PATCH 13/15] fix: also record interfaces even if event source was previously created --- .../AbstractEventSourceHolderDependentResource.java | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) 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 7444db1912..d104425140 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 @@ -28,11 +28,17 @@ public void stop() throws OperatorException { } public EventSource initEventSource(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 if (eventSource == null) { eventSource = createEventSource(context); - isFilteringEventSource = eventSource instanceof RecentOperationEventFilter; - isCacheFillerEventSource = eventSource instanceof RecentOperationCacheFiller; } + + // but we still need to record which interfaces the event source implements even if it has + // already been set before this method is called + isFilteringEventSource = eventSource instanceof RecentOperationEventFilter; + isCacheFillerEventSource = eventSource instanceof RecentOperationCacheFiller; return this; } From 9ad55d165eca225e9f5ed8254ffa5b81d1a9430c Mon Sep 17 00:00:00 2001 From: Chris Laprun Date: Sat, 26 Mar 2022 21:58:07 +0100 Subject: [PATCH 14/15] refactor: clean up --- .../api/reconciler/UpdateControl.java | 4 +- ...WebPageStandaloneDependentsReconciler.java | 56 ++++++++----------- 2 files changed, 25 insertions(+), 35 deletions(-) diff --git a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/reconciler/UpdateControl.java b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/reconciler/UpdateControl.java index 1762198286..eb44735a89 100644 --- a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/reconciler/UpdateControl.java +++ b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/reconciler/UpdateControl.java @@ -2,7 +2,6 @@ import io.fabric8.kubernetes.api.model.HasMetadata; -@SuppressWarnings("rawtypes") public class UpdateControl extends BaseControl> { private final T resource; @@ -32,8 +31,7 @@ public static UpdateControl updateResource(T customRe return new UpdateControl<>(customResource, false, true); } - public static UpdateControl updateStatus( - T customResource) { + public static UpdateControl updateStatus(T customResource) { return new UpdateControl<>(customResource, true, false); } 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 34c7686b45..8b19542dd5 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 @@ -5,9 +5,7 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import io.fabric8.kubernetes.api.model.ConfigMap; -import io.fabric8.kubernetes.api.model.Service; -import io.fabric8.kubernetes.api.model.apps.Deployment; +import io.fabric8.kubernetes.api.model.HasMetadata; import io.fabric8.kubernetes.client.KubernetesClient; import io.javaoperatorsdk.operator.api.reconciler.Context; import io.javaoperatorsdk.operator.api.reconciler.ControllerConfiguration; @@ -37,20 +35,26 @@ public class WebPageStandaloneDependentsReconciler private static final Logger log = LoggerFactory.getLogger(WebPageStandaloneDependentsReconciler.class); - private KubernetesDependentResource configMapDR; - private KubernetesDependentResource deploymentDR; - private KubernetesDependentResource serviceDR; + private final Map> dependentResources; public WebPageStandaloneDependentsReconciler(KubernetesClient kubernetesClient) { - createDependentResources(kubernetesClient); + dependentResources = Map.of( + "configmap", new ConfigMapDependentResource(), + "deployment", new DeploymentDependentResource(), + "service", new ServiceDependentResource()); + final var config = new KubernetesDependentResourceConfig() + .setLabelSelector(DEPENDENT_RESOURCE_LABEL_SELECTOR); + dependentResources.values().forEach(dr -> { + dr.setKubernetesClient(kubernetesClient); + dr.configureWith(config); + }); } @Override - public Map prepareEventSources(EventSourceContext context) { - return Map.of( - "configmap", configMapDR.initEventSource(context), - "deployment", deploymentDR.initEventSource(context), - "service", serviceDR.initEventSource(context)); + public Map prepareEventSources( + EventSourceContext context) { + dependentResources.values().forEach(dr -> dr.initEventSource(context)); + return dependentResources; } @Override @@ -58,35 +62,23 @@ public UpdateControl reconcile(WebPage webPage, Context contex throws Exception { simulateErrorIfRequested(webPage); - configMapDR.reconcile(webPage, context); - deploymentDR.reconcile(webPage, context); - serviceDR.reconcile(webPage, context); + dependentResources.values().forEach(dr -> dr.reconcile(webPage, context)); - webPage.setStatus( - createStatus(configMapDR.getResource(webPage).orElseThrow().getMetadata().getName())); + webPage.setStatus(createStatus(getConfigMapName(webPage))); return UpdateControl.updateStatus(webPage); } + private String getConfigMapName(WebPage webPage) { + return dependent("configmap").getResource(webPage).orElseThrow().getMetadata().getName(); + } + @Override public ErrorStatusUpdateControl updateErrorStatus( WebPage resource, Context retryInfo, Exception e) { return handleError(resource, e); } - private void createDependentResources(KubernetesClient client) { - this.configMapDR = new ConfigMapDependentResource(); - this.configMapDR.setKubernetesClient(client); - configMapDR.configureWith(new KubernetesDependentResourceConfig() - .setLabelSelector(DEPENDENT_RESOURCE_LABEL_SELECTOR)); - - this.deploymentDR = new DeploymentDependentResource(); - deploymentDR.setKubernetesClient(client); - deploymentDR.configureWith(new KubernetesDependentResourceConfig() - .setLabelSelector(DEPENDENT_RESOURCE_LABEL_SELECTOR)); - - this.serviceDR = new ServiceDependentResource(); - serviceDR.setKubernetesClient(client); - serviceDR.configureWith(new KubernetesDependentResourceConfig() - .setLabelSelector(DEPENDENT_RESOURCE_LABEL_SELECTOR)); + private KubernetesDependentResource dependent(String name) { + return dependentResources.get(name); } } From 03b6d0d0c3bc27c3cc831a67aeac177ebeb8eae4 Mon Sep 17 00:00:00 2001 From: Chris Laprun Date: Sun, 27 Mar 2022 20:51:30 +0200 Subject: [PATCH 15/15] revert: prepareEventSources should return map of EventSources --- .../operator/api/reconciler/EventSourceInitializer.java | 2 +- .../javaoperatorsdk/operator/processing/Controller.java | 2 +- .../sample/WebPageStandaloneDependentsReconciler.java | 9 +++++---- 3 files changed, 7 insertions(+), 6 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 d540abacfe..e539e10b3a 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 @@ -21,6 +21,6 @@ public interface EventSourceInitializer

{ * sources * @return a map of event sources to register */ - Map prepareEventSources(EventSourceContext

context); + Map prepareEventSources(EventSourceContext

context); } 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 80423474cc..f0fdd07b16 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 @@ -190,7 +190,7 @@ public String successTypeName(UpdateControl

result) { public UpdateControl

execute() throws Exception { initContextIfNeeded(resource, context); final var exceptions = new ArrayList(dependents.size()); - dependents.values().forEach((name, dependent) -> { + dependents.forEach((name, dependent) -> { try { final var reconcileResult = dependent.reconcile(resource, context); context.managedDependentResourceContext().setReconcileResult(name, reconcileResult); 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 8b19542dd5..4ac7dd103d 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 @@ -1,6 +1,8 @@ package io.javaoperatorsdk.operator.sample; import java.util.Map; +import java.util.Map.Entry; +import java.util.stream.Collectors; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -51,10 +53,9 @@ public WebPageStandaloneDependentsReconciler(KubernetesClient kubernetesClient) } @Override - public Map prepareEventSources( - EventSourceContext context) { - dependentResources.values().forEach(dr -> dr.initEventSource(context)); - return dependentResources; + public Map prepareEventSources(EventSourceContext context) { + return dependentResources.entrySet().stream() + .collect(Collectors.toUnmodifiableMap(Entry::getKey, Entry::getValue)); } @Override