diff --git a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/reconciler/BaseControl.java b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/reconciler/BaseControl.java index a5cdb85257..15e705dd8c 100644 --- a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/reconciler/BaseControl.java +++ b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/reconciler/BaseControl.java @@ -3,10 +3,17 @@ import java.time.Duration; import java.util.Optional; import java.util.concurrent.TimeUnit; +import java.util.function.BiPredicate; -public abstract class BaseControl> { +import io.fabric8.kubernetes.api.model.HasMetadata; +import io.javaoperatorsdk.operator.api.reconciler.expectation.Expectation; +import io.javaoperatorsdk.operator.api.reconciler.expectation.ExpectationAdapter; +import io.javaoperatorsdk.operator.api.reconciler.expectation.ExpectationContext; + +public abstract class BaseControl, P extends HasMetadata> { private Long scheduleDelay = null; + private Expectation

expectation; public T rescheduleAfter(long delay) { rescheduleAfter(Duration.ofMillis(delay)); @@ -25,4 +32,16 @@ public T rescheduleAfter(long delay, TimeUnit timeUnit) { public Optional getScheduleDelay() { return Optional.ofNullable(scheduleDelay); } + + public void expect(Expectation

expectation) { + this.expectation = expectation; + } + + public void expect(BiPredicate> expectation, Duration timeout) { + this.expectation = new ExpectationAdapter<>(expectation, timeout); + } + + public Expectation

getExpectation() { + return expectation; + } } diff --git a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/reconciler/CacheAware.java b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/reconciler/CacheAware.java new file mode 100644 index 0000000000..b29732214f --- /dev/null +++ b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/reconciler/CacheAware.java @@ -0,0 +1,44 @@ +package io.javaoperatorsdk.operator.api.reconciler; + +import java.util.Optional; +import java.util.Set; +import java.util.stream.Stream; + +import io.fabric8.kubernetes.api.model.HasMetadata; +import io.javaoperatorsdk.operator.api.config.ControllerConfiguration; +import io.javaoperatorsdk.operator.processing.event.EventSourceRetriever; +import io.javaoperatorsdk.operator.processing.event.source.IndexerResourceCache; + +public interface CacheAware

{ + default Optional getSecondaryResource(Class expectedType) { + return getSecondaryResource(expectedType, null); + } + + Set getSecondaryResources(Class expectedType); + + default Stream getSecondaryResourcesAsStream(Class expectedType) { + return getSecondaryResources(expectedType).stream(); + } + + Optional getSecondaryResource(Class expectedType, String eventSourceName); + + ControllerConfiguration

getControllerConfiguration(); + + /** + * Retrieves the primary resource. + * + * @return the primary resource associated with the current reconciliation + */ + P getPrimaryResource(); + + /** + * Retrieves the primary resource cache. + * + * @return the {@link IndexerResourceCache} associated with the associated {@link Reconciler} for + * this context + */ + @SuppressWarnings("unused") + IndexedResourceCache

getPrimaryCache(); + + EventSourceRetriever

eventSourceRetriever(); +} 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 f47deb9734..ddc30224a0 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 @@ -1,35 +1,18 @@ package io.javaoperatorsdk.operator.api.reconciler; import java.util.Optional; -import java.util.Set; import java.util.concurrent.ExecutorService; -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.managed.ManagedWorkflowAndDependentResourceContext; -import io.javaoperatorsdk.operator.processing.event.EventSourceRetriever; +import io.javaoperatorsdk.operator.api.reconciler.expectation.ExpectationResult; import io.javaoperatorsdk.operator.processing.event.source.IndexerResourceCache; -public interface Context

{ +public interface Context

extends CacheAware

{ Optional getRetryInfo(); - default Optional getSecondaryResource(Class expectedType) { - return getSecondaryResource(expectedType, null); - } - - Set getSecondaryResources(Class expectedType); - - default Stream getSecondaryResourcesAsStream(Class expectedType) { - return getSecondaryResources(expectedType).stream(); - } - - Optional getSecondaryResource(Class expectedType, String eventSourceName); - - ControllerConfiguration

getControllerConfiguration(); - /** * Retrieve the {@link ManagedWorkflowAndDependentResourceContext} used to interact with {@link * io.javaoperatorsdk.operator.api.reconciler.dependent.DependentResource}s and associated {@link @@ -39,8 +22,6 @@ default Stream getSecondaryResourcesAsStream(Class expectedType) { */ ManagedWorkflowAndDependentResourceContext managedWorkflowAndDependentResourceContext(); - EventSourceRetriever

eventSourceRetriever(); - KubernetesClient getClient(); /** ExecutorService initialized by framework for workflows. Used for workflow standalone mode. */ @@ -72,4 +53,6 @@ default Stream getSecondaryResourcesAsStream(Class expectedType) { * @return {@code true} is another reconciliation is already scheduled, {@code false} otherwise */ boolean isNextReconciliationImminent(); + + Optional> expectationResult(); } 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 2acf8d13ca..2a90f7de18 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 @@ -11,6 +11,7 @@ import io.javaoperatorsdk.operator.api.config.ControllerConfiguration; import io.javaoperatorsdk.operator.api.reconciler.dependent.managed.DefaultManagedWorkflowAndDependentResourceContext; import io.javaoperatorsdk.operator.api.reconciler.dependent.managed.ManagedWorkflowAndDependentResourceContext; +import io.javaoperatorsdk.operator.api.reconciler.expectation.ExpectationResult; import io.javaoperatorsdk.operator.processing.Controller; import io.javaoperatorsdk.operator.processing.event.EventSourceRetriever; import io.javaoperatorsdk.operator.processing.event.NoEventSourceForClassException; @@ -119,6 +120,11 @@ public boolean isNextReconciliationImminent() { .isNextReconciliationImminent(ResourceID.fromResource(primaryResource)); } + @Override + public Optional> expectationResult() { + return Optional.empty(); + } + public DefaultContext

setRetryInfo(RetryInfo retryInfo) { this.retryInfo = retryInfo; return this; diff --git a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/reconciler/DeleteControl.java b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/reconciler/DeleteControl.java index 7160e70830..a7f9104d6d 100644 --- a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/reconciler/DeleteControl.java +++ b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/reconciler/DeleteControl.java @@ -1,6 +1,8 @@ package io.javaoperatorsdk.operator.api.reconciler; -public class DeleteControl extends BaseControl { +import io.fabric8.kubernetes.api.model.HasMetadata; + +public class DeleteControl

extends BaseControl, P> { private final boolean removeFinalizer; @@ -27,7 +29,7 @@ public static DeleteControl defaultDelete() { * * @return delete control that will not remove finalizer. */ - public static DeleteControl noFinalizerRemoval() { + public static DeleteControl noFinalizerRemoval() { return new DeleteControl(false); } @@ -36,7 +38,7 @@ public boolean isRemoveFinalizer() { } @Override - public DeleteControl rescheduleAfter(long delay) { + public DeleteControl

rescheduleAfter(long delay) { if (removeFinalizer) { throw new IllegalStateException("Cannot reschedule cleanup if removing finalizer"); } diff --git a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/reconciler/ErrorStatusUpdateControl.java b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/reconciler/ErrorStatusUpdateControl.java index e9073d613c..9b21d60883 100644 --- a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/reconciler/ErrorStatusUpdateControl.java +++ b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/reconciler/ErrorStatusUpdateControl.java @@ -6,7 +6,7 @@ import io.fabric8.kubernetes.api.model.HasMetadata; public class ErrorStatusUpdateControl

- extends BaseControl> { + extends BaseControl, P> { private final P resource; private boolean noRetry = false; 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 1b5eefd7ff..f3ebeb76d9 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 @@ -5,7 +5,7 @@ import io.fabric8.kubernetes.api.model.HasMetadata; import io.fabric8.kubernetes.client.CustomResource; -public class UpdateControl

extends BaseControl> { +public class UpdateControl

extends BaseControl, P> { private final P resource; private final boolean patchResource; diff --git a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/reconciler/expectation/AbstractExpectation.java b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/reconciler/expectation/AbstractExpectation.java new file mode 100644 index 0000000000..4821df4236 --- /dev/null +++ b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/reconciler/expectation/AbstractExpectation.java @@ -0,0 +1,22 @@ +package io.javaoperatorsdk.operator.api.reconciler.expectation; + +import java.time.Duration; + +import io.fabric8.kubernetes.api.model.HasMetadata; + +public abstract class AbstractExpectation

implements Expectation

{ + + protected final Duration timeout; + + protected AbstractExpectation(Duration timeout) { + this.timeout = timeout; + } + + @Override + public abstract boolean isFulfilled(P primary, ExpectationContext

context); + + @Override + public Duration timeout() { + return timeout; + } +} diff --git a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/reconciler/expectation/Expectation.java b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/reconciler/expectation/Expectation.java new file mode 100644 index 0000000000..47a97bd188 --- /dev/null +++ b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/reconciler/expectation/Expectation.java @@ -0,0 +1,12 @@ +package io.javaoperatorsdk.operator.api.reconciler.expectation; + +import java.time.Duration; + +import io.fabric8.kubernetes.api.model.HasMetadata; + +public interface Expectation

{ + + boolean isFulfilled(P primary, ExpectationContext

context); + + Duration timeout(); +} diff --git a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/reconciler/expectation/ExpectationAdapter.java b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/reconciler/expectation/ExpectationAdapter.java new file mode 100644 index 0000000000..ca0707fc31 --- /dev/null +++ b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/reconciler/expectation/ExpectationAdapter.java @@ -0,0 +1,21 @@ +package io.javaoperatorsdk.operator.api.reconciler.expectation; + +import java.time.Duration; +import java.util.function.BiPredicate; + +import io.fabric8.kubernetes.api.model.HasMetadata; + +public class ExpectationAdapter

extends AbstractExpectation

{ + + private final BiPredicate> expectation; + + public ExpectationAdapter(BiPredicate> expectation, Duration timeout) { + super(timeout); + this.expectation = expectation; + } + + @Override + public boolean isFulfilled(P primary, ExpectationContext

context) { + return expectation.test(primary, context); + } +} diff --git a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/reconciler/expectation/ExpectationContext.java b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/reconciler/expectation/ExpectationContext.java new file mode 100644 index 0000000000..614a58dd41 --- /dev/null +++ b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/reconciler/expectation/ExpectationContext.java @@ -0,0 +1,6 @@ +package io.javaoperatorsdk.operator.api.reconciler.expectation; + +import io.fabric8.kubernetes.api.model.HasMetadata; +import io.javaoperatorsdk.operator.api.reconciler.CacheAware; + +public interface ExpectationContext

extends CacheAware

{} diff --git a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/reconciler/expectation/ExpectationResult.java b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/reconciler/expectation/ExpectationResult.java new file mode 100644 index 0000000000..d8908eacd1 --- /dev/null +++ b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/reconciler/expectation/ExpectationResult.java @@ -0,0 +1,22 @@ +package io.javaoperatorsdk.operator.api.reconciler.expectation; + +import io.fabric8.kubernetes.api.model.HasMetadata; + +public class ExpectationResult

{ + + private ExpectationStatus status; + + private Expectation

expectation; + + public ExpectationResult(ExpectationStatus status) { + this.status = status; + } + + public ExpectationStatus getStatus() { + return status; + } + + public Expectation

getExpectation() { + return expectation; + } +} diff --git a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/reconciler/expectation/ExpectationStatus.java b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/reconciler/expectation/ExpectationStatus.java new file mode 100644 index 0000000000..5ced5093da --- /dev/null +++ b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/reconciler/expectation/ExpectationStatus.java @@ -0,0 +1,6 @@ +package io.javaoperatorsdk.operator.api.reconciler.expectation; + +public enum ExpectationStatus { + TIMEOUT, + FULFILLED; +} diff --git a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/ReconciliationDispatcher.java b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/ReconciliationDispatcher.java index c4b161ef27..f1bd59f2a2 100644 --- a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/ReconciliationDispatcher.java +++ b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/ReconciliationDispatcher.java @@ -250,7 +250,7 @@ private PostExecutionControl

createPostExecutionControl( } private void updatePostExecutionControlWithReschedule( - PostExecutionControl

postExecutionControl, BaseControl baseControl) { + PostExecutionControl

postExecutionControl, BaseControl baseControl) { baseControl.getScheduleDelay().ifPresent(postExecutionControl::withReSchedule); }