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 extends Condition> condition,
+ protected T instantiateIfNotDefault(Class extends T> 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 extends DependentResource>
OnUpdateFilter extends HasMetadata> onUpdateFilter = null;
OnDeleteFilter extends HasMetadata> onDeleteFilter = null;
GenericFilter extends HasMetadata> genericFilter = null;
+ ResourceDiscriminator, ? extends HasMetadata> resourceDiscriminator = null;
if (kubeDependent != null) {
if (!Arrays.equals(KubernetesDependent.DEFAULT_NAMESPACES,
kubeDependent.namespaces())) {
@@ -297,7 +296,6 @@ private Object createKubernetesResourceConfig(Class extends DependentResource>
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 extends DependentResource>
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..eb947fa440
--- /dev/null
+++ b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/reconciler/ResourceDiscriminator.java
@@ -0,0 +1,10 @@
+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/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/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..6e937268d9 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;
@@ -15,25 +18,60 @@ public abstract class AbstractDependentResource
implements DependentResource {
private static final Logger log = LoggerFactory.getLogger(AbstractDependentResource.class);
- protected final boolean creatable = this instanceof Creator;
- protected final boolean updatable = this instanceof Updater;
+ private final boolean creatable = this instanceof Creator;
+ private final boolean updatable = this instanceof Updater;
+ private final boolean bulk = this instanceof BulkDependentResource;
protected Creator creator;
protected Updater updater;
+ private final BulkDependentResource bulkDependentResource;
+ private ResourceDiscriminator resourceDiscriminator;
+ private int currentCount;
@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 maybeActual = getSecondaryResource(primary);
+ if (bulk) {
+ final var count = bulkDependentResource.count(primary, context);
+ deleteBulkResourcesIfRequired(count, primary, context);
+ @SuppressWarnings("unchecked")
+ final ReconcileResult[] results = new ReconcileResult[count];
+ for (int i = 0; i < count; i++) {
+ results[i] = reconcileIndexAware(primary, i, context);
+ }
+ currentCount = count;
+ return ReconcileResult.aggregatedResult(results);
+ } else {
+ return reconcileIndexAware(primary, 0, context);
+ }
+ }
+
+ protected void deleteBulkResourcesIfRequired(int targetCount, P primary, Context context) {
+ if (targetCount >= currentCount) {
+ return;
+ }
+ for (int i = targetCount; i < currentCount; i++) {
+ var resource = bulkDependentResource.getSecondaryResource(primary, i, context);
+ var index = i;
+ resource.ifPresent(
+ r -> bulkDependentResource.deleteBulkResourceWithIndex(primary, r, index, context));
+ }
+ }
+
+ protected ReconcileResult reconcileIndexAware(P primary, int i, Context context) {
+ Optional maybeActual = bulk ? bulkDependentResource.getSecondaryResource(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);
@@ -42,9 +80,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 = bulkDependentResource.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);
@@ -62,6 +106,15 @@ 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);
+ }
+
private void throwIfNull(R desired, P primary, String descriptor) {
if (desired == null) {
throw new DependentResourceException(
@@ -87,7 +140,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
@@ -118,4 +171,37 @@ 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 R desired(P primary, int index, Context
context) {
+ throw new IllegalStateException("Must be implemented for bulk DependentResource creation");
+ }
+
+ public void delete(P primary, Context
context) {
+ if (bulk) {
+ deleteBulkResourcesIfRequired(0, primary, context);
+ } else {
+ handleDelete(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 resourceDiscriminator) {
+ this.resourceDiscriminator = resourceDiscriminator;
+ }
+
+ protected boolean isCreatable() {
+ return creatable;
+ }
+
+ protected boolean isUpdatable() {
+ return updatable;
+ }
+
+ protected boolean isBulk() {
+ return bulk;
+ }
}
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/BulkDependentResource.java b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/BulkDependentResource.java
new file mode 100644
index 0000000000..64a174e201
--- /dev/null
+++ b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/BulkDependentResource.java
@@ -0,0 +1,52 @@
+package io.javaoperatorsdk.operator.processing.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.dependent.Deleter;
+import io.javaoperatorsdk.operator.processing.dependent.Matcher.Result;
+
+/**
+ * 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);
+
+ /**
+ * 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);
+
+ Optional getSecondaryResource(P primary, int index, Context context);
+
+}
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..ee8f08a68d
--- /dev/null
+++ b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/BulkUpdater.java
@@ -0,0 +1,24 @@
+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) {
+ 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/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/GenericKubernetesResourceMatcher.java b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/kubernetes/GenericKubernetesResourceMatcher.java
index e294b1c938..359a434a39 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,16 +24,18 @@ private GenericKubernetesResourceMatcher(KubernetesDependentResource depen
static Matcher matcherFor(
Class resourceType, KubernetesDependentResource dependentResource) {
if (Secret.class.isAssignableFrom(resourceType)) {
- return (actual, primary, context) -> {
+ return (actualResource, primary, context) -> {
final var desired = dependentResource.desired(primary, context);
return Result.computed(
- ResourceComparators.compareSecretData((Secret) desired, (Secret) actual), desired);
+ ResourceComparators.compareSecretData((Secret) desired, (Secret) actualResource),
+ desired);
};
} else if (ConfigMap.class.isAssignableFrom(resourceType)) {
- return (actual, primary, context) -> {
+ return (actualResource, primary, context) -> {
final var desired = dependentResource.desired(primary, context);
return Result.computed(
- ResourceComparators.compareConfigMapData((ConfigMap) desired, (ConfigMap) actual),
+ ResourceComparators.compareConfigMapData((ConfigMap) desired,
+ (ConfigMap) actualResource),
desired);
};
} else {
@@ -43,32 +45,12 @@ 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);
+ public static Result match(
+ R desired, R actualResource, boolean considerMetadata) {
if (considerMetadata) {
final var desiredMetadata = desired.getMetadata();
final var actualMetadata = actualResource.getMetadata();
@@ -95,4 +77,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/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 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..44897dbf06 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,26 +122,33 @@ 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) {
return matcher.match(actualResource, primary, context);
}
- public void delete(P primary, Context
context) {
- var resource = getSecondaryResource(primary);
+ public Result match(R actualResource, P primary, int index, Context context) {
+ final var desired = desired(primary, index, context);
+ return GenericKubernetesResourceMatcher.match(desired, actualResource, false);
+ }
+
+ protected void handleDelete(P primary, Context
context) {
+ var resource = getSecondaryResource(primary, context);
resource.ifPresent(r -> client.resource(r).delete());
}
- @SuppressWarnings("unchecked")
- protected NonNamespaceOperation, Resource> prepare(R desired,
- P primary, String actionName) {
+ public void deleteBulkResourceWithIndex(P primary, R resource, int i, Context context) {
+ client.resource(resource).delete();
+ }
+
+ protected Resource prepare(R desired, P primary, String actionName) {
log.debug("{} target resource with type: {}, with id: {}",
actionName,
desired.getClass(),
@@ -154,8 +158,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
@@ -167,7 +170,10 @@ protected InformerEventSource createEventSource(EventSourceContext 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);
@@ -182,7 +188,7 @@ protected InformerEventSource createEventSource(EventSourceContext cont
}
private boolean useDefaultAnnotationsToIdentifyPrimary() {
- return !(this instanceof SecondaryToPrimaryMapper) && !garbageCollected && creatable;
+ return !(this instanceof SecondaryToPrimaryMapper) && !garbageCollected && isCreatable();
}
private void addDefaultSecondaryToPrimaryMapperAnnotations(R desired, P primary) {
@@ -203,16 +209,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;
@@ -228,6 +224,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);
}
@@ -235,5 +236,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..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
@@ -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,26 @@ 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;
+ }
+
+ 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/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..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
@@ -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);
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