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..913b727eaa 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);
}
}
@@ -244,12 +242,17 @@ public List {
Optional 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 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/EventSourceInitializer.java b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/reconciler/EventSourceInitializer.java
index 9b3c7a67bd..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,10 +1,14 @@
package io.javaoperatorsdk.operator.api.reconciler;
+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.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 +43,22 @@ static Map 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 mapper;
+
+ public ResourceIDMatcherDiscriminator(Function mapper) {
+ this.mapper = mapper;
+ }
+
+ @Override
+ public Optional 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/Dependent.java b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/reconciler/dependent/Dependent.java
index 90ba701a6a..1a52cbcfc3 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
@@ -1,5 +1,6 @@
package io.javaoperatorsdk.operator.api.reconciler.dependent;
+import io.javaoperatorsdk.operator.api.reconciler.Constants;
import io.javaoperatorsdk.operator.processing.dependent.workflow.Condition;
import static io.javaoperatorsdk.operator.api.reconciler.Constants.NO_VALUE_SET;
@@ -57,4 +58,13 @@
* one can be
*/
String[] dependsOn() default {};
+
+ /**
+ * 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 event source name (if any) provided by the dependent resource should be used.
+ */
+ String useEventSourceWithName() 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 0923d19473..ea7759b8c4 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,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.processing.ResourceOwner;
+import io.javaoperatorsdk.operator.api.reconciler.EventSourceContext;
+import io.javaoperatorsdk.operator.processing.event.source.ResourceEventSource;
/**
* An interface to implement and provide dependent resource support.
@@ -10,7 +13,7 @@
* @param the associated primary resource type
*/
-public interface DependentResource context);
+ /**
+ * Retrieves the resource type associated with this DependentResource
+ *
+ * @return the resource type associated with this DependentResource
+ */
+ Class eventSourceContext) {
+ return Optional.empty();
+ }
+
+ default Optional 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/api/reconciler/dependent/EventSourceNotFoundException.java b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/reconciler/dependent/EventSourceNotFoundException.java
new file mode 100644
index 0000000000..26681e2ca6
--- /dev/null
+++ b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/reconciler/dependent/EventSourceNotFoundException.java
@@ -0,0 +1,16 @@
+package io.javaoperatorsdk.operator.api.reconciler.dependent;
+
+import io.javaoperatorsdk.operator.OperatorException;
+
+public class EventSourceNotFoundException extends OperatorException {
+
+ private String eventSourceName;
+
+ public EventSourceNotFoundException(String eventSourceName) {
+ this.eventSourceName = eventSourceName;
+ }
+
+ public String getEventSourceName() {
+ return eventSourceName;
+ }
+}
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/api/reconciler/dependent/EventSourceReferencer.java b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/reconciler/dependent/EventSourceReferencer.java
new file mode 100644
index 0000000000..13ac93e2bf
--- /dev/null
+++ b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/reconciler/dependent/EventSourceReferencer.java
@@ -0,0 +1,17 @@
+package io.javaoperatorsdk.operator.api.reconciler.dependent;
+
+import io.fabric8.kubernetes.api.model.HasMetadata;
+import io.javaoperatorsdk.operator.processing.event.EventSourceRetriever;
+
+public interface EventSourceReferencer {
+
+ default void useEventSourceWithName(String name) {}
+
+ /**
+ * Throws {@link EventSourceNotFoundException} an exception if the target event source to use is
+ * not found.
+ */
+ void resolveEventSource(EventSourceRetriever eventSourceRetriever)
+ throws EventSourceNotFoundException;
+
+}
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..66d982f01d 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 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);
- });
-
- // 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);
}
+
+ // register created event sources
+ final var dependentResourcesByName = managedWorkflow.getDependentResourcesByName();
+ final var size = dependentResourcesByName.size();
+ if (size > 0) {
+ dependentResourcesByName.forEach((key, value) -> {
+ if (value instanceof EventSourceProvider) {
+ final var provider = (EventSourceProvider) value;
+ final var source = provider.initEventSource(context);
+ eventSourceManager.registerEventSource(key, source);
+ } else {
+ Optional ) dr)
+ .resolveEventSource(eventSourceManager);
+ } catch (EventSourceNotFoundException e) {
+ unresolvable.computeIfAbsent(e.getEventSourceName(), s -> new ArrayList<>()).add(dr);
+ }
+ });
+ if (!unresolvable.isEmpty()) {
+ throw new IllegalStateException(
+ "Couldn't resolve referenced EventSources: " + unresolvable);
+ }
+ }
}
@Override
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 context) {
- var maybeActual = getSecondaryResource(primary);
+ return dependentResourceReconciler.reconcile(primary, context);
+ }
+
+ protected ReconcileResult context) {
if (creatable || updatable) {
- if (maybeActual.isEmpty()) {
+ if (resource == null) {
if (creatable) {
var desired = desired(primary, context);
throwIfNull(desired, primary, "Desired");
@@ -40,18 +53,19 @@ public ReconcileResult context) {
return ReconcileResult.resourceCreated(createdResource);
}
} else {
- final var actual = maybeActual.get();
if (updatable) {
- final var match = updater.match(actual, primary, context);
+ final Matcher.Result context) {
"Dependent {} is read-only, implement Creator and/or Updater interfaces to modify it",
getClass().getSimpleName());
}
- return ReconcileResult.noOperation(maybeActual.orElse(null));
+ return ReconcileResult.noOperation(resource);
+ }
+
+ public Result context) {
+ return updater.match(resource, primary, context);
+ }
+
+ @Override
+ public Optional context) {
+ return resourceDiscriminator == null ? context.getSecondaryResource(resourceType())
+ : resourceDiscriminator.distinguish(resourceType(), primary, context);
}
private void throwIfNull(R desired, P primary, String descriptor) {
@@ -87,7 +111,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
@@ -97,7 +121,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
@@ -118,4 +142,27 @@ 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 delete(P primary, Context context) {
+ dependentResourceReconciler.delete(primary, context);
+ }
+
+ protected void handleDelete(P primary, Context context) {
+ throw new IllegalStateException("delete method be implemented if Deleter trait is supported");
+ }
+
+ public void setResourceDiscriminator(
+ ResourceDiscriminator {
+ extends AbstractDependentResource {
private T eventSource;
+ private final Class context) {
+ public Optional 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.
// The filters are applied automatically only if event source is created automatically. So if an
// event source
// is shared between dependent resources this does not override the existing filters.
- if (eventSource == null) {
- eventSource = createEventSource(context);
+ if (eventSource == null && eventSourceNameToUse == null) {
+ setEventSource(createEventSource(context));
applyFilters();
}
+ return Optional.ofNullable(eventSource);
+ }
- isCacheFillerEventSource = eventSource instanceof RecentOperationCacheFiller;
- return eventSource;
+ @SuppressWarnings("unchecked")
+ @Override
+ public void resolveEventSource(EventSourceRetriever eventSourceRetriever) {
+ if (eventSourceNameToUse != null && eventSource == null) {
+ final var source =
+ eventSourceRetriever.getResourceEventSourceFor(resourceType(), eventSourceNameToUse);
+ if (source == null) {
+ throw new EventSourceNotFoundException(eventSourceNameToUse);
+ }
+ setEventSource((T) source);
+ }
+ }
+
+ /** To make this backwards compatible even for respect of overriding */
+ @SuppressWarnings("unchecked")
+ public T initEventSource(EventSourceContext context) {
+ return (T) eventSource(context).orElseThrow();
+ }
+
+ @Override
+ public void useEventSourceWithName(String name) {
+ this.eventSourceNameToUse = name;
+ }
+
+ @Override
+ public Class context);
protected void setEventSource(T eventSource) {
+ isCacheFillerEventSource = eventSource instanceof RecentOperationCacheFiller;
this.eventSource = eventSource;
}
@@ -55,8 +90,8 @@ protected void applyFilters() {
this.eventSource.setGenericFilter(genericFilter);
}
- protected T eventSource() {
- return eventSource;
+ public Optional {
+
+ /**
+ * Retrieves the Set of keys identifying the set of secondary resources associated with the
+ * specified primary resource.
+ *
+ * @param primary the primary resource with which we want to identify which secondary resources
+ * are associated
+ * @param context the {@link Context} associated with the current reconciliation
+ * @return a Set of identifiers allowing to associate a bulk secondary resource with the specified
+ * primary
+ */
+ Set context);
+
+ Map context);
+
+ R desired(P primary, String key, 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 key key of the resource
+ * @param context actual context
+ */
+ void deleteBulkResource(P primary, R resource, String key, 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 key key of the resource
+ * @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 context);
+
+}
diff --git a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/BulkDependentResourceReconciler.java b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/BulkDependentResourceReconciler.java
new file mode 100644
index 0000000000..11c7dbf80c
--- /dev/null
+++ b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/BulkDependentResourceReconciler.java
@@ -0,0 +1,97 @@
+package io.javaoperatorsdk.operator.processing.dependent;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+import io.fabric8.kubernetes.api.model.HasMetadata;
+import io.javaoperatorsdk.operator.api.reconciler.Context;
+import io.javaoperatorsdk.operator.api.reconciler.dependent.ReconcileResult;
+import io.javaoperatorsdk.operator.processing.dependent.Matcher.Result;
+import io.javaoperatorsdk.operator.processing.event.ResourceID;
+
+class BulkDependentResourceReconciler context) {
+ final var targetKeys = bulkDependentResource.desiredResourceKeys(primary, context);
+ Map context) {
+ var actualResources = bulkDependentResource.getSecondaryResources(primary, context);
+ deleteBulkResourcesIfRequired(Collections.emptySet(), actualResources, primary, context);
+ }
+
+ protected void deleteBulkResourcesIfRequired(Set context) {
+ actualResources.forEach((key, value) -> {
+ if (!expectedKeys.contains(key)) {
+ bulkDependentResource.deleteBulkResource(primary, value, key, context);
+ }
+ });
+ }
+
+ private static class BulkDependentResourceInstance context) {
+ return bulkDependentResource.desired(primary, key, context);
+ }
+
+ @Override
+ public Result context) {
+ return bulkDependentResource.match(resource, primary, key, context);
+ }
+
+ @Override
+ protected void onCreated(ResourceID primaryResourceId, R created) {
+ asAbstractDependentResource().onCreated(primaryResourceId, created);
+ }
+
+ @Override
+ protected void onUpdated(ResourceID primaryResourceId, R updated, R actual) {
+ asAbstractDependentResource().onUpdated(primaryResourceId, updated, actual);
+ }
+
+ @Override
+ public Class primary resource type
+ */
+public interface BulkUpdater context) {
+ if (!(this instanceof BulkDependentResource)) {
+ throw new IllegalStateException(
+ BulkUpdater.class.getSimpleName() + " interface should only be implemented by "
+ + BulkDependentResource.class.getSimpleName() + " implementations");
+ }
+ throw new IllegalStateException("This method should not be called from a "
+ + BulkDependentResource.class.getSimpleName() + " implementation");
+ }
+}
diff --git a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/DependentResourceReconciler.java b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/DependentResourceReconciler.java
new file mode 100644
index 0000000000..112905d926
--- /dev/null
+++ b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/DependentResourceReconciler.java
@@ -0,0 +1,12 @@
+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.dependent.ReconcileResult;
+
+interface DependentResourceReconciler context);
+
+ void delete(P primary, Context context);
+}
diff --git a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/SingleDependentResourceReconciler.java b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/SingleDependentResourceReconciler.java
new file mode 100644
index 0000000000..20b37b3c7e
--- /dev/null
+++ b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/SingleDependentResourceReconciler.java
@@ -0,0 +1,26 @@
+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.dependent.ReconcileResult;
+
+class SingleDependentResourceReconciler context) {
+ final var maybeActual = instance.getSecondaryResource(primary, context);
+ return instance.reconcile(primary, maybeActual.orElse(null), context);
+ }
+
+ @Override
+ public void delete(P primary, Context context) {
+ instance.handleDelete(primary, context);
+ }
+}
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 context) {
- var resourceId = ResourceID.fromResource(primary);
- Optional 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 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/GenericKubernetesResourceMatcher.java b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/kubernetes/GenericKubernetesResourceMatcher.java
index e294b1c938..9952763e6a 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
@@ -23,27 +23,52 @@ private GenericKubernetesResourceMatcher(KubernetesDependentResource context) {
- return match(dependentResource, actualResource, primary, context, false);
+ var desired = dependentResource.desired(primary, context);
+ return match(desired, actualResource, false);
+ }
+
+ public static context, boolean considerMetadata) {
final var desired = dependentResource.desired(primary, context);
- if (considerMetadata) {
- final var desiredMetadata = desired.getMetadata();
- final var actualMetadata = actualResource.getMetadata();
- final var matched =
- Objects.equals(desiredMetadata.getAnnotations(), actualMetadata.getAnnotations()) &&
- Objects.equals(desiredMetadata.getLabels(), actualMetadata.getLabels());
- if (!matched) {
- return Result.computed(false, desired);
- }
- }
-
- final var objectMapper = ConfigurationServiceProvider.instance().getObjectMapper();
-
- // reflection will be replaced by this:
- // https://github.com/fabric8io/kubernetes-client/issues/3816
- var desiredSpecNode = objectMapper.valueToTree(ReconcilerUtils.getSpec(desired));
- var actualSpecNode = objectMapper.valueToTree(ReconcilerUtils.getSpec(actualResource));
- var diffJsonPatch = JsonDiff.asJson(desiredSpecNode, actualSpecNode);
- for (int i = 0; i < diffJsonPatch.size(); i++) {
- String operation = diffJsonPatch.get(i).get("op").asText();
- if (!operation.equals("add")) {
- return Result.computed(false, desired);
- }
- }
- return Result.computed(true, desired);
+ return match(desired, actualResource, considerMetadata);
}
}
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..004a56b5f6 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,7 @@
* itself if no value is set
*/
Class extends GenericFilter> genericFilter() default GenericFilter.class;
+
+ Class extends ResourceDiscriminator> 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..79ac393bbe 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;
@@ -22,6 +19,7 @@
import io.javaoperatorsdk.operator.api.reconciler.dependent.managed.DependentResourceConfigurator;
import io.javaoperatorsdk.operator.api.reconciler.dependent.managed.KubernetesClientAware;
import io.javaoperatorsdk.operator.processing.dependent.AbstractEventSourceHolderDependentResource;
+import io.javaoperatorsdk.operator.processing.dependent.BulkDependentResource;
import io.javaoperatorsdk.operator.processing.dependent.Matcher;
import io.javaoperatorsdk.operator.processing.dependent.Matcher.Result;
import io.javaoperatorsdk.operator.processing.event.ResourceID;
@@ -34,20 +32,19 @@
public abstract class KubernetesDependentResource 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 context) {
return matcher.match(actualResource, primary, context);
}
- public void delete(P primary, Context context) {
- var resource = getSecondaryResource(primary);
+ @SuppressWarnings("unchecked")
+ public Result context) {
+ final var desired = ((BulkDependentResource context) {
+ var resource = getSecondaryResource(primary, context);
resource.ifPresent(r -> client.resource(r).delete());
}
- @SuppressWarnings("unchecked")
- protected NonNamespaceOperation context) {
+ client.resource(resource).delete();
+ }
+
+ protected Resource cont
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);
@@ -178,11 +192,11 @@ protected InformerEventSource
+ *
+ *
+ * @param eventSourceContext context of event source initialization
+ * @return an optional event source
+ */
+ default Optional