+ implements io.javaoperatorsdk.operator.api.config.ControllerConfiguration
{
- protected final Reconciler reconciler;
private final ControllerConfiguration annotation;
private List resourceClass;
- public AnnotationControllerConfiguration(Reconciler reconciler) {
this.reconciler = reconciler;
this.annotation = reconciler.getClass().getAnnotation(ControllerConfiguration.class);
if (annotation == null) {
@@ -81,10 +90,10 @@ public Set getResourceClass() {
if (resourceClass == null) {
resourceClass =
- (Class ) Utils.getFirstTypeArgumentFromSuperClassOrInterface(reconciler.getClass(),
Reconciler.class);
}
return resourceClass;
@@ -102,16 +111,16 @@ public String getAssociatedReconcilerClassName() {
@SuppressWarnings("unchecked")
@Override
- public ResourceEventFilter getEventFilter() {
+ ResourceEventFilter answer = null;
- Class filter = filterType.getConstructor().newInstance();
if (answer == null) {
answer = filter;
@@ -141,17 +150,55 @@ public Optional PrimaryToSecondaryMapper getPrimaryToSecondaryMapper() {
return (PrimaryToSecondaryMapper ) primaryToSecondaryMapper;
@@ -61,6 +77,14 @@ public PrimaryToSecondaryMapper getPrimaryToSecondary
SecondaryToPrimaryMapper PrimaryToSecondaryMapper getPrimaryToSecondaryMapper();
@SuppressWarnings("unused")
@@ -71,6 +95,10 @@ class InformerConfigurationBuilder {
/**
- * Note that this method turns on automatic finalizer usage.
+ * This method turns on automatic finalizer usage.
*
* The implementation should delete the associated component(s). This method is called when an
* object is marked for deletion. After it's executed the custom resource finalizer is
diff --git a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/reconciler/ControllerConfiguration.java b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/reconciler/ControllerConfiguration.java
index a5e1459cb7..4c6621bb57 100644
--- a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/reconciler/ControllerConfiguration.java
+++ b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/reconciler/ControllerConfiguration.java
@@ -4,9 +4,15 @@
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
+import java.util.function.BiPredicate;
+import java.util.function.Predicate;
+import io.fabric8.kubernetes.api.model.HasMetadata;
import io.javaoperatorsdk.operator.api.reconciler.dependent.Dependent;
import io.javaoperatorsdk.operator.processing.event.source.controller.ResourceEventFilter;
+import io.javaoperatorsdk.operator.processing.event.source.filter.VoidGenericFilter;
+import io.javaoperatorsdk.operator.processing.event.source.filter.VoidOnAddFilter;
+import io.javaoperatorsdk.operator.processing.event.source.filter.VoidOnUpdateFilter;
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE})
@@ -51,15 +57,31 @@
String labelSelector() default Constants.NO_VALUE_SET;
/**
- *
- * Resource event filters only applies on events of the main custom resource. Not on events from
- * other event sources nor the periodic events.
- *
+ * Resource event filters only applies on events of the main custom resource. Not on
+ * events from other event sources nor the periodic events.
+ * implements Context {
private final Controller controller;
private final P primaryResource;
private final ControllerConfiguration controllerConfiguration;
- private final ManagedDependentResourceContext managedDependentResourceContext;
+ private final DefaultManagedDependentResourceContext defaultManagedDependentResourceContext;
public DefaultContext(RetryInfo retryInfo, Controller controller, P primaryResource) {
this.retryInfo = retryInfo;
this.controller = controller;
this.primaryResource = primaryResource;
this.controllerConfiguration = controller.getConfiguration();
- this.managedDependentResourceContext = new ManagedDependentResourceContext();
+ this.defaultManagedDependentResourceContext = new DefaultManagedDependentResourceContext();
}
@Override
@@ -52,8 +53,9 @@ public ControllerConfiguration getControllerConfiguration() {
return controllerConfiguration;
}
+ @Override
public ManagedDependentResourceContext managedDependentResourceContext() {
- return managedDependentResourceContext;
+ return defaultManagedDependentResourceContext;
}
public DefaultContext setRetryInfo(RetryInfo retryInfo) {
diff --git a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/reconciler/UpdateControl.java b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/reconciler/UpdateControl.java
index 62f9bc0cd2..12f392901d 100644
--- a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/reconciler/UpdateControl.java
+++ b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/reconciler/UpdateControl.java
@@ -3,15 +3,15 @@
import io.fabric8.kubernetes.api.model.HasMetadata;
import io.fabric8.kubernetes.client.CustomResource;
-public class UpdateControl extends BaseControl
+ * See this issue
+ * for more details.
+ */
+public interface GarbageCollected extends Deleter {
+
+}
diff --git a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/reconciler/dependent/VoidCondition.java b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/reconciler/dependent/VoidCondition.java
new file mode 100644
index 0000000000..7f4faf0ed1
--- /dev/null
+++ b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/reconciler/dependent/VoidCondition.java
@@ -0,0 +1,14 @@
+package io.javaoperatorsdk.operator.api.reconciler.dependent;
+
+import io.fabric8.kubernetes.api.model.HasMetadata;
+import io.javaoperatorsdk.operator.api.reconciler.Context;
+import io.javaoperatorsdk.operator.processing.dependent.workflow.Condition;
+
+/** Used as default value for Condition in annotations */
+@SuppressWarnings("rawtypes")
+public class VoidCondition implements Condition {
+ @Override
+ public boolean isMet(DependentResource dependentResource, HasMetadata primary, Context context) {
+ throw new IllegalStateException("This is a placeholder class, should not be called");
+ }
+}
diff --git a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/reconciler/dependent/managed/DefaultManagedDependentResourceContext.java b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/reconciler/dependent/managed/DefaultManagedDependentResourceContext.java
new file mode 100644
index 0000000000..5b1a21e5dd
--- /dev/null
+++ b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/reconciler/dependent/managed/DefaultManagedDependentResourceContext.java
@@ -0,0 +1,61 @@
+package io.javaoperatorsdk.operator.api.reconciler.dependent.managed;
+
+import java.util.Optional;
+import java.util.concurrent.ConcurrentHashMap;
+
+import io.javaoperatorsdk.operator.processing.dependent.workflow.WorkflowCleanupResult;
+import io.javaoperatorsdk.operator.processing.dependent.workflow.WorkflowReconcileResult;
+
+@SuppressWarnings("rawtypes")
+public class DefaultManagedDependentResourceContext implements ManagedDependentResourceContext {
+
+ private WorkflowReconcileResult workflowReconcileResult;
+ private WorkflowCleanupResult workflowCleanupResult;
+ private final ConcurrentHashMap attributes = new ConcurrentHashMap();
+
+ @Override
+ public
private final ControllerConfiguration configuration;
private final KubernetesClient kubernetesClient;
private final EventSourceManager eventSourceManager;
- private final LinkedHashMap managedWorkflow;
public Controller(Reconciler reconciler,
ControllerConfiguration configuration,
@@ -57,94 +64,10 @@ public Controller(Reconciler reconciler,
this.metrics = Optional.ofNullable(ConfigurationServiceProvider.instance().getMetrics())
.orElse(Metrics.NOOP);
contextInitializer = reconciler instanceof ContextInitializer;
-
- eventSourceManager = new EventSourceManager<>(this);
-
- final var hasDeleterHolder = new boolean[] {false};
- final var specs = configuration.getDependentResources();
- final var size = specs.size();
- if (size == 0) {
- dependents = new LinkedHashMap<>();
- } else {
- final Map context) {
- if (contextInitializer) {
- ((ContextInitializer ) reconciler).initContext(resource, context);
- }
- }
-
- @Override
- public DeleteControl cleanup(P resource, Context context) {
- try {
- return metrics
- .timeControllerExecution(
- new ControllerExecution<>() {
- @Override
- public String name() {
- return "cleanup";
- }
-
- @Override
- public String controllerName() {
- return configuration.getName();
- }
-
- @Override
- public String successTypeName(DeleteControl deleteControl) {
- return deleteControl.isRemoveFinalizer() ? "delete" : "finalizerNotRemoved";
- }
-
- @Override
- public DeleteControl execute() {
- initContextIfNeeded(resource, context);
- if (hasDeleterDependents) {
- dependents.values().stream()
- .filter(d -> d instanceof Deleter)
- .map(Deleter.class::cast)
- .forEach(deleter -> deleter.delete(resource, context));
- }
- if (isCleaner) {
- return ((Cleaner ) reconciler).cleanup(resource, context);
- } else {
- return DeleteControl.defaultDelete();
- }
- }
- });
- } catch (Exception e) {
- throw new OperatorException(e);
- }
+ managedWorkflow =
+ ManagedWorkflow.workflowFor(kubernetesClient, configuration.getDependentResources());
+ eventSourceManager = new EventSourceManager<>(this);
}
@Override
@@ -176,53 +99,84 @@ public String successTypeName(UpdateControl result) {
@Override
public UpdateControl execute() throws Exception {
initContextIfNeeded(resource, context);
- final var exceptions = new ArrayList context) {
+ try {
+ return metrics.timeControllerExecution(
+ new ControllerExecution<>() {
+ @Override
+ public String name() {
+ return "cleanup";
+ }
+
+ @Override
+ public String controllerName() {
+ return configuration.getName();
}
- i++;
- }
- return "";
- });
- return "\t\t- " + e.getMessage() + exceptionLocation.orElse("");
+
+ @Override
+ public String successTypeName(DeleteControl deleteControl) {
+ return deleteControl.isRemoveFinalizer() ? "delete" : "finalizerNotRemoved";
+ }
+
+ @Override
+ public DeleteControl execute() {
+ initContextIfNeeded(resource, context);
+ WorkflowCleanupResult workflowCleanupResult = null;
+ if (managedWorkflow.isCleaner()) {
+ workflowCleanupResult = managedWorkflow.cleanup(resource, context);
+ ((DefaultManagedDependentResourceContext) context.managedDependentResourceContext())
+ .setWorkflowCleanupResult(workflowCleanupResult);
+ workflowCleanupResult.throwAggregateExceptionIfErrorsPresent();
+ }
+ if (isCleaner) {
+ var cleanupResult = ((Cleaner ) reconciler).cleanup(resource, context);
+ if (!cleanupResult.isRemoveFinalizer()) {
+ return cleanupResult;
+ } else {
+ // this means there is no reschedule
+ return workflowCleanupResultToDefaultDelete(workflowCleanupResult);
+ }
+ } else {
+ return workflowCleanupResultToDefaultDelete(workflowCleanupResult);
+ }
+ }
+ });
+ } catch (Exception e) {
+ throw new OperatorException(e);
+ }
+ }
+
+ private DeleteControl workflowCleanupResultToDefaultDelete(
+ WorkflowCleanupResult workflowCleanupResult) {
+ if (workflowCleanupResult == null) {
+ return DeleteControl.defaultDelete();
+ } else {
+ return workflowCleanupResult.allPostConditionsMet() ? DeleteControl.defaultDelete()
+ : DeleteControl.noFinalizerRemoval();
+ }
+ }
+
+ private void initContextIfNeeded(P resource, Context context) {
+ if (contextInitializer) {
+ ((ContextInitializer ) reconciler).initContext(resource, context);
+ }
}
public void initAndRegisterEventSources(EventSourceContext context) {
- dependents.entrySet().stream()
+ managedWorkflow
+ .getDependentResourcesByName().entrySet().stream()
.filter(drEntry -> drEntry.getValue() instanceof EventSourceProvider)
.forEach(drEntry -> {
final var provider = (EventSourceProvider) drEntry.getValue();
@@ -372,6 +326,6 @@ public void stop() {
}
public boolean useFinalizer() {
- return isCleaner || hasDeleterDependents;
+ return isCleaner || managedWorkflow.isCleaner();
}
}
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 ac0fee99ee..5dbdba9358 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
@@ -6,7 +6,6 @@
import io.fabric8.kubernetes.api.model.HasMetadata;
import io.javaoperatorsdk.operator.api.reconciler.Context;
import io.javaoperatorsdk.operator.api.reconciler.Ignore;
-import io.javaoperatorsdk.operator.api.reconciler.dependent.Deleter;
import io.javaoperatorsdk.operator.api.reconciler.dependent.DependentResource;
import io.javaoperatorsdk.operator.api.reconciler.dependent.ReconcileResult;
import io.javaoperatorsdk.operator.processing.event.ResourceID;
@@ -18,7 +17,7 @@ public abstract class AbstractDependentResource Primary Resource
+ * @param the type of the associated primary resource
*/
@Ignore
public abstract class CRUDKubernetesDependentResource {
+ KubernetesDependentResource {
public CRUDKubernetesDependentResource(Class {
+
+ public CRUDNoGCKubernetesDependentResource(Class Primary Resource
- */
-@Ignore
-public abstract class CRUKubernetesDependentResource context) {
+ private void configureWith(KubernetesDependentResourceConfig context) {
+ var namespaces = config.namespaces();
if (namespaces.equals(Constants.SAME_AS_CONTROLLER_NAMESPACES_SET)) {
namespaces = context.getControllerConfiguration().getNamespaces();
}
- final SecondaryToPrimaryMapper context) {
}
public void delete(P primary, Context context) {
- if (!addOwnerReference()) {
- var resource = getSecondaryResource(primary);
- resource.ifPresent(r -> client.resource(r).delete());
- }
+ var resource = getSecondaryResource(primary);
+ resource.ifPresent(r -> client.resource(r).delete());
}
@SuppressWarnings("unchecked")
@@ -138,20 +153,21 @@ protected NonNamespaceOperation