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 f3fade4659..f1aeadd52a 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 @@ -15,15 +15,19 @@ */ package io.javaoperatorsdk.operator.api.reconciler; +import java.util.Map; import java.util.Optional; import java.util.Set; +import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ExecutorService; +import java.util.function.Function; import java.util.stream.Collectors; import java.util.stream.Stream; import io.fabric8.kubernetes.api.model.HasMetadata; import io.fabric8.kubernetes.client.KubernetesClient; import io.javaoperatorsdk.operator.api.config.ControllerConfiguration; +import io.javaoperatorsdk.operator.api.reconciler.dependent.DependentResource; import io.javaoperatorsdk.operator.api.reconciler.dependent.managed.DefaultManagedWorkflowAndDependentResourceContext; import io.javaoperatorsdk.operator.api.reconciler.dependent.managed.ManagedWorkflowAndDependentResourceContext; import io.javaoperatorsdk.operator.processing.Controller; @@ -41,6 +45,7 @@ public class DefaultContext
implements Context
{
defaultManagedDependentResourceContext;
private final boolean primaryResourceDeleted;
private final boolean primaryResourceFinalStateUnknown;
+ private final Map setRetryInfo(RetryInfo retryInfo) {
this.retryInfo = retryInfo;
return this;
}
+
+ @SuppressWarnings("unchecked")
+ public desiredStateComputer) {
+ return (R)
+ desiredStates.computeIfAbsent(
+ dependentResource, ignored -> desiredStateComputer.apply(getPrimaryResource()));
+ }
}
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 a7c5ce9e2d..8dc62b4ca7 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
@@ -23,6 +23,7 @@
import io.fabric8.kubernetes.api.model.HasMetadata;
import io.javaoperatorsdk.operator.api.reconciler.Context;
+import io.javaoperatorsdk.operator.api.reconciler.DefaultContext;
import io.javaoperatorsdk.operator.api.reconciler.Ignore;
import io.javaoperatorsdk.operator.api.reconciler.dependent.Deleter;
import io.javaoperatorsdk.operator.api.reconciler.dependent.DependentResource;
@@ -85,7 +86,7 @@ protected ReconcileResult c
if (creatable() || updatable()) {
if (actualResource == null) {
if (creatable) {
- var desired = desired(primary, context);
+ var desired = getOrComputeDesired(context);
throwIfNull(desired, primary, "Desired");
logForOperation("Creating", primary, desired);
var createdResource = handleCreate(desired, primary, context);
@@ -95,7 +96,8 @@ protected ReconcileResult c
if (updatable()) {
final Matcher.Result c
@Override
public Optional context) {
-
var secondaryResources = context.getSecondaryResources(resourceType());
if (secondaryResources.isEmpty()) {
return Optional.empty();
@@ -212,6 +213,27 @@ protected R desired(P primary, Context context) {
+ " updated");
}
+ /**
+ * Retrieves the desired state from the {@link Context} if it has already been computed or calls
+ * {@link #desired(HasMetadata, Context)} and stores its result in the context for further use.
+ * This ensures that {@code desired} is only called once per reconciliation to avoid unneeded
+ * processing and supports scenarios where idempotent computation of the desired state is not
+ * feasible.
+ *
+ * Note that this method should normally only be called by the SDK itself and exclusively (i.e.
+ * {@link #desired(HasMetadata, Context)} should not be called directly by the SDK) whenever the
+ * desired state is needed to ensure it is properly cached for the current reconciliation.
+ *
+ * @param context the {@link Context} in scope for the current reconciliation
+ * @return the desired state associated with this dependent resource based on the currently
+ * in-scope primary resource as found in the context
+ */
+ protected R getOrComputeDesired(Context context) {
+ assert context instanceof DefaultContext ;
+ DefaultContext defaultContext = (DefaultContext ) context;
+ return defaultContext.getOrComputeDesiredStateFor(this, p -> desired(p, defaultContext));
+ }
+
public void delete(P primary, Context context) {
dependentResourceReconciler.delete(primary, context);
}
diff --git a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/AbstractExternalDependentResource.java b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/AbstractExternalDependentResource.java
index e601e937cf..7b83a377c1 100644
--- a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/AbstractExternalDependentResource.java
+++ b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/AbstractExternalDependentResource.java
@@ -105,7 +105,7 @@ protected void handleExplicitStateCreation(P primary, R created, Context cont
@Override
public Matcher.Result context) {
- var desired = desired(primary, context);
+ var desired = getOrComputeDesired(context);
return Matcher.Result.computed(resource.equals(desired), desired);
}
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
index 5b3617c26c..23135f81b1 100644
--- 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
@@ -27,7 +27,6 @@
import io.javaoperatorsdk.operator.api.reconciler.Ignore;
import io.javaoperatorsdk.operator.api.reconciler.dependent.Deleter;
import io.javaoperatorsdk.operator.api.reconciler.dependent.ReconcileResult;
-import io.javaoperatorsdk.operator.processing.dependent.Matcher.Result;
class BulkDependentResourceReconciler context,
boolean labelsAndAnnotationsEquality,
String... ignorePaths) {
- final var desired = dependentResource.desired(primary, context);
+ final var desired = dependentResource.getOrComputeDesired(context);
return match(desired, actualResource, labelsAndAnnotationsEquality, context, ignorePaths);
}
@@ -150,7 +150,7 @@ public static context) {
@Override
public Result context) {
- final var desired = desired(primary, context);
+ final var desired = getOrComputeDesired(context);
return match(actualResource, desired, primary, context);
}
@@ -297,7 +297,7 @@ protected Optional context) {
- return ResourceID.fromResource(desired(primary, context));
+ return ResourceID.fromResource(getOrComputeDesired(context));
}
protected boolean addOwnerReference() {
@@ -305,8 +305,8 @@ protected boolean addOwnerReference() {
}
@Override
- protected R desired(P primary, Context context) {
- return super.desired(primary, context);
+ protected R getOrComputeDesired(Context context) {
+ return super.getOrComputeDesired(context);
}
@Override
diff --git a/operator-framework-core/src/test/java/io/javaoperatorsdk/operator/processing/dependent/AbstractDependentResourceTest.java b/operator-framework-core/src/test/java/io/javaoperatorsdk/operator/processing/dependent/AbstractDependentResourceTest.java
index bb9d6cf71e..1db69a1f9e 100644
--- a/operator-framework-core/src/test/java/io/javaoperatorsdk/operator/processing/dependent/AbstractDependentResourceTest.java
+++ b/operator-framework-core/src/test/java/io/javaoperatorsdk/operator/processing/dependent/AbstractDependentResourceTest.java
@@ -21,8 +21,10 @@
import org.junit.jupiter.api.Test;
import io.fabric8.kubernetes.api.model.ConfigMap;
+import io.fabric8.kubernetes.api.model.ConfigMapBuilder;
import io.fabric8.kubernetes.api.model.ObjectMetaBuilder;
import io.javaoperatorsdk.operator.api.reconciler.Context;
+import io.javaoperatorsdk.operator.api.reconciler.DefaultContext;
import io.javaoperatorsdk.operator.sample.simple.TestCustomResource;
import static org.junit.jupiter.api.Assertions.*;
@@ -31,6 +33,13 @@
class AbstractDependentResourceTest {
+ private static final TestCustomResource PRIMARY = new TestCustomResource();
+ private static final DefaultContext