diff --git a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/config/AbstractConfigurationService.java b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/config/AbstractConfigurationService.java index 1a54dbafc7..f7ed42c577 100644 --- a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/config/AbstractConfigurationService.java +++ b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/config/AbstractConfigurationService.java @@ -10,6 +10,9 @@ import io.javaoperatorsdk.operator.ReconcilerUtils; import io.javaoperatorsdk.operator.api.reconciler.Reconciler; +/** + * An abstract implementation of {@link ConfigurationService} meant to ease custom implementations + */ @SuppressWarnings("rawtypes") public class AbstractConfigurationService implements ConfigurationService { private final Map configurations = new ConcurrentHashMap<>(); @@ -18,11 +21,11 @@ public class AbstractConfigurationService implements ConfigurationService { private Cloner cloner; private ExecutorServiceManager executorServiceManager; - public AbstractConfigurationService(Version version) { + protected AbstractConfigurationService(Version version) { this(version, null); } - public AbstractConfigurationService(Version version, Cloner cloner) { + protected AbstractConfigurationService(Version version, Cloner cloner) { this(version, cloner, null, null); } diff --git a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/config/ConfigurationService.java b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/config/ConfigurationService.java index ebc47fd488..942d770506 100644 --- a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/config/ConfigurationService.java +++ b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/config/ConfigurationService.java @@ -33,6 +33,68 @@ public interface ConfigurationService { Logger log = LoggerFactory.getLogger(ConfigurationService.class); int DEFAULT_MAX_CONCURRENT_REQUEST = 512; + /** + * The default numbers of concurrent reconciliations + */ + int DEFAULT_RECONCILIATION_THREADS_NUMBER = 50; + /** + * The default number of threads used to process dependent workflows + */ + int DEFAULT_WORKFLOW_EXECUTOR_THREAD_NUMBER = DEFAULT_RECONCILIATION_THREADS_NUMBER; + + /** + * Creates a new {@link ConfigurationService} instance used to configure an + * {@link io.javaoperatorsdk.operator.Operator} instance, starting from the specified base + * configuration and overriding specific aspects according to the provided + * {@link ConfigurationServiceOverrider} instance. + * + *

+ * NOTE: This overriding mechanism should only be used before creating + * your Operator instance as the configuration service is set at creation time and cannot be + * subsequently changed. As a result, overriding values this way after the Operator has been + * configured will not take effect. + *

+ * + * @param baseConfiguration the {@link ConfigurationService} to start from + * @param overrider the {@link ConfigurationServiceOverrider} used to change the values provided + * by the base configuration + * @return a new {@link ConfigurationService} starting from the configuration provided as base but + * with overridden values. + */ + static ConfigurationService newOverriddenConfigurationService( + ConfigurationService baseConfiguration, + Consumer overrider) { + if (overrider != null) { + final var toOverride = new ConfigurationServiceOverrider(baseConfiguration); + overrider.accept(toOverride); + return toOverride.build(); + } + return baseConfiguration; + } + + /** + * Creates a new {@link ConfigurationService} instance used to configure an + * {@link io.javaoperatorsdk.operator.Operator} instance, starting from the default configuration + * and overriding specific aspects according to the provided {@link ConfigurationServiceOverrider} + * instance. + * + *

+ * NOTE: This overriding mechanism should only be used before creating + * your Operator instance as the configuration service is set at creation time and cannot be + * subsequently changed. As a result, overriding values this way after the Operator has been + * configured will not take effect. + *

+ * + * @param overrider the {@link ConfigurationServiceOverrider} used to change the values provided + * by the default configuration + * @return a new {@link ConfigurationService} overriding the default values with the ones provided + * by the specified {@link ConfigurationServiceOverrider} + * @since 4.4.0 + */ + static ConfigurationService newOverriddenConfigurationService( + Consumer overrider) { + return newOverriddenConfigurationService(new BaseConfigurationService(), overrider); + } /** * Retrieves the configuration associated with the specified reconciler @@ -44,7 +106,6 @@ public interface ConfigurationService { */ ControllerConfiguration getConfigurationFor(Reconciler reconciler); - /** * Used to clone custom resources. * @@ -128,8 +189,6 @@ default boolean checkCRDAndValidateLocalModel() { return false; } - int DEFAULT_RECONCILIATION_THREADS_NUMBER = 50; - /** * The number of threads the operator can spin out to dispatch reconciliation requests to * reconcilers with the default executors @@ -140,8 +199,6 @@ default int concurrentReconciliationThreads() { return DEFAULT_RECONCILIATION_THREADS_NUMBER; } - int DEFAULT_WORKFLOW_EXECUTOR_THREAD_NUMBER = DEFAULT_RECONCILIATION_THREADS_NUMBER; - /** * Number of threads the operator can spin out to be used in the workflows with the default * executor. @@ -152,27 +209,64 @@ default int concurrentWorkflowExecutorThreads() { return DEFAULT_WORKFLOW_EXECUTOR_THREAD_NUMBER; } + /** + * Override to provide a custom {@link Metrics} implementation + * + * @return the {@link Metrics} implementation + */ default Metrics getMetrics() { return Metrics.NOOP; } + /** + * Override to provide a custom {@link ExecutorService} implementation to change how threads + * handle concurrent reconciliations + * + * @return the {@link ExecutorService} implementation to use for concurrent reconciliation + * processing + */ default ExecutorService getExecutorService() { return Executors.newFixedThreadPool(concurrentReconciliationThreads()); } + /** + * Override to provide a custom {@link ExecutorService} implementation to change how dependent + * workflows are processed in parallel + * + * @return the {@link ExecutorService} implementation to use for dependent workflow processing + */ default ExecutorService getWorkflowExecutorService() { return Executors.newFixedThreadPool(concurrentWorkflowExecutorThreads()); } + /** + * Determines whether the associated Kubernetes client should be closed when the associated + * {@link io.javaoperatorsdk.operator.Operator} is stopped. + * + * @return {@code true} if the Kubernetes should be closed on stop, {@code false} otherwise + */ default boolean closeClientOnStop() { return true; } + /** + * Override to provide a custom {@link DependentResourceFactory} implementation to change how + * {@link io.javaoperatorsdk.operator.api.reconciler.dependent.DependentResource} are instantiated + * + * @return the custom {@link DependentResourceFactory} implementation + */ @SuppressWarnings("rawtypes") default DependentResourceFactory dependentResourceFactory() { return DependentResourceFactory.DEFAULT; } + /** + * Retrieves the optional {@link LeaderElectionConfiguration} to specify how the associated + * {@link io.javaoperatorsdk.operator.Operator} handles leader election to ensure only one + * instance of the operator runs on the cluster at any given time + * + * @return the {@link LeaderElectionConfiguration} + */ default Optional getLeaderElectionConfiguration() { return Optional.empty(); } @@ -228,65 +322,23 @@ default Optional getInformerStoppedHandler() { }); } + /** + * Override to provide a custom {@link ManagedWorkflowFactory} implementation to change how + * {@link io.javaoperatorsdk.operator.processing.dependent.workflow.ManagedWorkflow} are + * instantiated + * + * @return the custom {@link ManagedWorkflowFactory} implementation + */ @SuppressWarnings("rawtypes") default ManagedWorkflowFactory getWorkflowFactory() { return ManagedWorkflowFactory.DEFAULT; } /** - * Creates a new {@link ConfigurationService} instance used to configure an - * {@link io.javaoperatorsdk.operator.Operator} instance, starting from the specified base - * configuration and overriding specific aspects according to the provided - * {@link ConfigurationServiceOverrider} instance. - * - *

- * NOTE: This overriding mechanism should only be used before creating - * your Operator instance as the configuration service is set at creation time and cannot be - * subsequently changed. As a result, overriding values this way after the Operator has been - * configured will not take effect. - *

- * - * @param baseConfiguration the {@link ConfigurationService} to start from - * @param overrider the {@link ConfigurationServiceOverrider} used to change the values provided - * by the base configuration - * @return a new {@link ConfigurationService} starting from the configuration provided as base but - * with overridden values. - */ - static ConfigurationService newOverriddenConfigurationService( - ConfigurationService baseConfiguration, - Consumer overrider) { - if (overrider != null) { - final var toOverride = new ConfigurationServiceOverrider(baseConfiguration); - overrider.accept(toOverride); - return toOverride.build(); - } - return baseConfiguration; - } - - /** - * Creates a new {@link ConfigurationService} instance used to configure an - * {@link io.javaoperatorsdk.operator.Operator} instance, starting from the default configuration - * and overriding specific aspects according to the provided {@link ConfigurationServiceOverrider} - * instance. - * - *

- * NOTE: This overriding mechanism should only be used before creating - * your Operator instance as the configuration service is set at creation time and cannot be - * subsequently changed. As a result, overriding values this way after the Operator has been - * configured will not take effect. - *

- * - * @param overrider the {@link ConfigurationServiceOverrider} used to change the values provided - * by the default configuration - * @return a new {@link ConfigurationService} overriding the default values with the ones provided - * by the specified {@link ConfigurationServiceOverrider} - * @since 4.4.0 + * Override to provide a custom {@link ExecutorServiceManager} implementation + * + * @return the custom {@link ExecutorServiceManager} implementation */ - static ConfigurationService newOverriddenConfigurationService( - Consumer overrider) { - return newOverriddenConfigurationService(new BaseConfigurationService(), overrider); - } - default ExecutorServiceManager getExecutorServiceManager() { return new ExecutorServiceManager(this); } 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 2b75b0d969..375afd4397 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 @@ -15,6 +15,18 @@ */ public interface DependentResource { + /** + * Computes a default name for the specified DependentResource class + * + * @param dependentResourceClass the DependentResource class for which we want to compute a + * default name + * @return the default name for the specified DependentResource class + */ + @SuppressWarnings("rawtypes") + static String defaultNameFor(Class dependentResourceClass) { + return dependentResourceClass.getName(); + } + /** * Reconciles the dependent resource given the desired primary state * @@ -65,21 +77,23 @@ default Optional getSecondaryResource(P primary, Context

context) { } /** - * Computes a default name for the specified DependentResource class + * Determines whether resources associated with this dependent need explicit handling when + * deleted, usually meaning that the dependent implements {@link Deleter} * - * @param dependentResourceClass the DependentResource class for which we want to compute a - * default name - * @return the default name for the specified DependentResource class + * @return {@code true} if explicit handling of resource deletion is needed, {@link false} + * otherwise */ - @SuppressWarnings("rawtypes") - static String defaultNameFor(Class dependentResourceClass) { - return dependentResourceClass.getName(); - } - default boolean isDeletable() { return this instanceof Deleter; } + + /** + * Retrieves the name identifying this DependentResource implementation, useful to refer to this + * in {@link io.javaoperatorsdk.operator.processing.dependent.workflow.Workflow} instances + * + * @return the name identifying this DependentResource implementation + */ default String name() { return defaultNameFor(getClass()); } diff --git a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/health/EventSourceHealthIndicator.java b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/health/EventSourceHealthIndicator.java index e44fcb5b72..2732c16707 100644 --- a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/health/EventSourceHealthIndicator.java +++ b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/health/EventSourceHealthIndicator.java @@ -2,5 +2,12 @@ public interface EventSourceHealthIndicator { + /** + * Retrieves the health status of an + * {@link io.javaoperatorsdk.operator.processing.event.source.EventSource} + * + * @return the health status + * @see Status + */ Status getStatus(); } diff --git a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/health/Status.java b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/health/Status.java index d3a300b7d8..272c360a87 100644 --- a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/health/Status.java +++ b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/health/Status.java @@ -1,5 +1,8 @@ package io.javaoperatorsdk.operator.health; +/** + * The health status of an {@link io.javaoperatorsdk.operator.processing.event.source.EventSource} + */ public enum Status { HEALTHY, UNHEALTHY, 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 24ea12ed0b..63223a01ff 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 @@ -16,6 +16,14 @@ import io.javaoperatorsdk.operator.processing.dependent.Matcher.Result; import io.javaoperatorsdk.operator.processing.event.ResourceID; +/** + * An abstract implementation of {@link DependentResource} to be used as base for custom + * implementations, providing, in particular, the core {@link #reconcile(HasMetadata, Context)} + * logic for dependents + * + * @param the dependent resource type + * @param

the associated primary resource type + */ @Ignore public abstract class AbstractDependentResource implements DependentResource, NameSetter { @@ -24,18 +32,16 @@ public abstract class AbstractDependentResource private final boolean creatable = this instanceof Creator; private final boolean updatable = this instanceof Updater; private final boolean deletable = this instanceof Deleter; - + private final DependentResourceReconciler dependentResourceReconciler; protected Creator creator; protected Updater updater; - private final DependentResourceReconciler dependentResourceReconciler; - protected String name; - @SuppressWarnings({"unchecked"}) protected AbstractDependentResource() { this(null); } + @SuppressWarnings("unchecked") protected AbstractDependentResource(String name) { creator = creatable ? (Creator) this : null; updater = updatable ? (Updater) this : null; @@ -120,7 +126,8 @@ public Optional getSecondaryResource(P primary, Context

context) { * secondary candidates for equality with the specified desired state, which might end up costly. * * @param secondaryResources to select the target resource from - * + * @param primary the primary resource + * @param context the context in which this method is called * @return the matching secondary resource or {@link Optional#empty()} if none matches * @throws IllegalStateException if more than one candidate is found, in which case some other * mechanism might be necessary to distinguish between candidate secondary resources 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 index 313d7115c9..a54027e4c3 100644 --- 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 @@ -12,6 +12,9 @@ * 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}. + * + * @param the dependent resource type + * @param

the primary resource type */ public interface BulkDependentResource { diff --git a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/DependentResourceWithExplicitState.java b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/DependentResourceWithExplicitState.java index 05206731db..4609b4a9c7 100644 --- a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/DependentResourceWithExplicitState.java +++ b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/DependentResourceWithExplicitState.java @@ -11,6 +11,10 @@ * are non Kubernetes resources which when created their ID is generated, so cannot be determined * based only on primary resources. In order to manage such dependent resource use this interface * for a resource that extends {@link AbstractExternalDependentResource}. + * + * @param the dependent resource type + * @param

the primary resource type + * @param the state type */ public interface DependentResourceWithExplicitState extends Creator, Deleter

{ diff --git a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/workflow/CRDPresentActivationCondition.java b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/workflow/CRDPresentActivationCondition.java index b3792bb9c7..5a60b63d41 100644 --- a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/workflow/CRDPresentActivationCondition.java +++ b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/workflow/CRDPresentActivationCondition.java @@ -16,7 +16,11 @@ * even used in multiple condition. By default, it checks CRD at most 10 times with a delay at least * 10 seconds. To fully customize CRD check trigger behavior you can extend this class and override * the {@link CRDPresentActivationCondition#shouldCheckStateNow(CRDCheckState)} method. - **/ + * + * @param the resource type associated with the CRD to check for presence + * @param

the primary resource type associated with the reconciler processing dependents + * associated with this condition + */ public class CRDPresentActivationCondition implements Condition { diff --git a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/source/EventSource.java b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/source/EventSource.java index 11b884bb73..850a9deb35 100644 --- a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/source/EventSource.java +++ b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/source/EventSource.java @@ -17,10 +17,18 @@ * Creates an event source to trigger your reconciler whenever something happens to a secondary or * external resource that should cause a reconciliation of the primary resource. EventSource * generalizes the concept of Informers and extends it to external (i.e. non Kubernetes) resources. + * + * @param the resource type that this EventSource is associated with + * @param

the primary resource type which reconciler needs to be triggered when events occur on + * resources of type R */ public interface EventSource extends LifecycleAware, EventSourceHealthIndicator { + static String generateName(EventSource eventSource) { + return eventSource.getClass().getName() + "@" + Integer.toHexString(eventSource.hashCode()); + } + /** * Sets the {@link EventHandler} that is linked to your reconciler when this EventSource is * registered. @@ -29,10 +37,21 @@ public interface EventSource */ void setEventHandler(EventHandler handler); + /** + * Retrieves the EventSource's name so that it can be referred to + * + * @return the EventSource's name + */ default String name() { return generateName(this); } + /** + * Retrieves the EventSource's starting priority + * + * @return the EventSource's starting priority + * @see EventSourceStartPriority + */ default EventSourceStartPriority priority() { return EventSourceStartPriority.DEFAULT; } @@ -44,6 +63,15 @@ default EventSourceStartPriority priority() { */ Class resourceType(); + /** + * Retrieves the optional unique secondary resource associated with the specified primary + * resource. Note that this operation will fail if multiple resources are associated with the + * specified primary resource. + * + * @param primary the primary resource for which the secondary resource is requested + * @return the secondary resource associated with the specified primary resource + * @throws IllegalStateException if multiple resources are associated with the primary one + */ default Optional getSecondaryResource(P primary) { var resources = getSecondaryResources(primary); if (resources.isEmpty()) { @@ -55,6 +83,13 @@ default Optional getSecondaryResource(P primary) { } } + /** + * Retrieves a potential empty set of resources tracked by this EventSource associated with the + * specified primary resource + * + * @param primary the primary resource for which the secondary resource is requested + * @return the set of secondary resources associated with the specified primary + */ Set getSecondaryResources(P primary); void setOnAddFilter(OnAddFilter onAddFilter); @@ -69,8 +104,4 @@ default Optional getSecondaryResource(P primary) { default Status getStatus() { return Status.UNKNOWN; } - - static String generateName(EventSource eventSource) { - return eventSource.getClass().getName() + "@" + Integer.toHexString(eventSource.hashCode()); - } } diff --git a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/source/EventSourceStartPriority.java b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/source/EventSourceStartPriority.java index d1d758bdb4..8284d611f2 100644 --- a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/source/EventSourceStartPriority.java +++ b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/source/EventSourceStartPriority.java @@ -1,17 +1,27 @@ package io.javaoperatorsdk.operator.processing.event.source; +/** + * Defines priority levels for {@link EventSource} implementation to ensure that some sources are + * started before others + */ public enum EventSourceStartPriority { /** * Event Sources with this priority are started and synced before the event source with DEFAULT - * priority. The use case to use this, if the event source holds an information regarding the - * state of a resource. For example a ConfigMap would store an ID of an external resource, in this - * case an event source that tracks the external resource might need this ID (event before the - * reconciliation) to check the state of the external resource. The only way to ensure that the ID - * is already cached is to start/sync related event source before the event source of the external - * resource. + * priority. This is needed if the event source holds information about another resource's state. + * In this situation, it is needed to initialize this event source before the one associated with + * resources which state is being tracked since that state information might be required to + * properly retrieve the other resources. + * + *

+ * For example a {@code ConfigMap} could store the identifier of a fictional external resource + * {@code A}. In this case, the event source tracking {@code A} resources might need the + * identifier from the {@code ConfigMap} to identify and check the state of {@code A} resources. + * This is usually needed before any reconciliation occurs and the only way to ensure the proper + * behavior in this case is to make sure that the event source tracking the {@code ConfigMaps} (in + * this example) is started/cache-synced before the event source for {@code A} resources gets + * started. + *

*/ RESOURCE_STATE_LOADER, DEFAULT - - }