All Downloads are FREE. Search and download functionalities are using the official Maven repository.

io.quarkiverse.operatorsdk.runtime.QuarkusControllerConfiguration Maven / Gradle / Ivy

The newest version!
package io.quarkiverse.operatorsdk.runtime;

import java.lang.annotation.Annotation;
import java.time.Duration;
import java.util.*;
import java.util.stream.Collectors;

import io.fabric8.kubernetes.api.model.HasMetadata;
import io.fabric8.kubernetes.api.model.rbac.PolicyRule;
import io.fabric8.kubernetes.api.model.rbac.RoleRef;
import io.fabric8.kubernetes.client.informers.cache.ItemStore;
import io.javaoperatorsdk.operator.ReconcilerUtils;
import io.javaoperatorsdk.operator.api.config.AnnotationConfigurable;
import io.javaoperatorsdk.operator.api.config.ConfigurationService;
import io.javaoperatorsdk.operator.api.config.ControllerConfiguration;
import io.javaoperatorsdk.operator.api.config.RetryConfiguration;
import io.javaoperatorsdk.operator.api.config.dependent.DependentResourceConfigurationProvider;
import io.javaoperatorsdk.operator.api.config.dependent.DependentResourceSpec;
import io.javaoperatorsdk.operator.api.reconciler.Reconciler;
import io.javaoperatorsdk.operator.processing.Controller;
import io.javaoperatorsdk.operator.processing.dependent.workflow.ManagedWorkflow;
import io.javaoperatorsdk.operator.processing.event.rate.LinearRateLimiter;
import io.javaoperatorsdk.operator.processing.event.rate.RateLimiter;
import io.javaoperatorsdk.operator.processing.event.source.controller.ResourceEventFilter;
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.OnUpdateFilter;
import io.javaoperatorsdk.operator.processing.retry.GenericRetry;
import io.javaoperatorsdk.operator.processing.retry.Retry;
import io.quarkiverse.operatorsdk.common.ClassLoadingUtils;
import io.quarkus.runtime.annotations.IgnoreProperty;
import io.quarkus.runtime.annotations.RecordableConstructor;

@SuppressWarnings("rawtypes")
public class QuarkusControllerConfiguration implements ControllerConfiguration,
        DependentResourceConfigurationProvider {

    // we need to create this class because Quarkus cannot reference the default implementation that
    // JOSDK provides as it doesn't like lambdas at build time. The class also needs to be public
    // because otherwise Quarkus isn't able to access it… :(
    public final static class PassthroughResourceEventFilter implements ResourceEventFilter {

        @Override
        public boolean acceptChange(Controller controller, HasMetadata hasMetadata,
                HasMetadata p1) {
            return true;
        }
    }

    private final static ResourceEventFilter DEFAULT = new PassthroughResourceEventFilter();

    // Needed by Quarkus because LinearRateLimiter doesn't expose setters for byte recording
    public final static class DefaultRateLimiter extends LinearRateLimiter {

        public DefaultRateLimiter() {
            super();
        }

        @RecordableConstructor
        @SuppressWarnings("unused")
        public DefaultRateLimiter(Duration refreshPeriod, int limitForPeriod) {
            super(refreshPeriod, limitForPeriod);
        }
    }

    private final String associatedReconcilerClassName;
    private final String name;
    private final String resourceTypeName;
    private final String crVersion;
    private final boolean generationAware;
    private final boolean statusPresentAndNotVoid;
    private final Class resourceClass;
    private final Optional informerListLimit;
    private final ResourceEventFilter eventFilter;
    private final Optional> onAddFilter;
    private final Optional> onUpdateFilter;
    private final Optional> genericFilter;
    private final List additionalRBACRules;
    private final List additionalRBACRoleRefs;
    private final String fieldManager;
    private final Optional> itemStore;
    private Class retryConfigurationClass;
    private Class retryClass;
    private Class rateLimiterConfigurationClass;
    private Class rateLimiterClass;
    private Optional maxReconciliationInterval;
    private String finalizer;
    private Set namespaces;
    private boolean wereNamespacesSet;
    private RetryConfiguration retryConfiguration;
    private String labelSelector;
    private final Map> dependentsMetadata;
    private Retry retry;
    private RateLimiter rateLimiter;
    private ManagedWorkflow workflow;
    private ConfigurationService parent;

    @RecordableConstructor
    @SuppressWarnings("unchecked")
    public QuarkusControllerConfiguration(
            String associatedReconcilerClassName,
            String name,
            String resourceTypeName,
            String crVersion, boolean generationAware,
            Class resourceClass,
            Long nullableInformerListLimit,
            Set namespaces,
            boolean wereNamespacesSet,
            String finalizerName, String labelSelector,
            boolean statusPresentAndNotVoid, ResourceEventFilter eventFilter,
            Duration maxReconciliationInterval,
            OnAddFilter onAddFilter, OnUpdateFilter onUpdateFilter, GenericFilter genericFilter,
            Class retryClass, Class retryConfigurationClass,
            Class rateLimiterClass, Class rateLimiterConfigurationClass,
            Map> dependentsMetadata, ManagedWorkflow workflow,
            List additionalRBACRules, List additionalRBACRoleRefs, String fieldManager,
            ItemStore nullableItemStore) {
        this.associatedReconcilerClassName = associatedReconcilerClassName;
        this.name = name;
        this.resourceTypeName = resourceTypeName;
        this.crVersion = crVersion;
        this.generationAware = generationAware;
        this.resourceClass = resourceClass;
        this.informerListLimit = Optional.ofNullable(nullableInformerListLimit);
        this.additionalRBACRules = additionalRBACRules;
        this.additionalRBACRoleRefs = additionalRBACRoleRefs;
        this.dependentsMetadata = dependentsMetadata;
        this.workflow = workflow;
        this.retryConfiguration = ControllerConfiguration.super.getRetryConfiguration();
        setNamespaces(namespaces);
        this.wereNamespacesSet = wereNamespacesSet;
        setFinalizer(finalizerName);
        this.labelSelector = labelSelector;
        this.statusPresentAndNotVoid = statusPresentAndNotVoid;
        this.eventFilter = eventFilter != null ? eventFilter : DEFAULT;
        this.maxReconciliationInterval = maxReconciliationInterval != null ? Optional.of(maxReconciliationInterval)
                : ControllerConfiguration.super.maxReconciliationInterval();
        this.onAddFilter = Optional.ofNullable(onAddFilter);
        this.onUpdateFilter = Optional.ofNullable(onUpdateFilter);
        this.genericFilter = Optional.ofNullable(genericFilter);

        this.retryClass = retryClass;
        this.retry = GenericRetry.class.equals(retryClass) ? ControllerConfiguration.super.getRetry() : null;
        this.retryConfigurationClass = retryConfigurationClass;

        this.rateLimiterClass = rateLimiterClass;
        this.rateLimiter = DefaultRateLimiter.class.equals(rateLimiterClass) ? new DefaultRateLimiter() : null;
        this.rateLimiterConfigurationClass = rateLimiterConfigurationClass;

        this.fieldManager = fieldManager != null ? fieldManager : ControllerConfiguration.super.fieldManager();
        this.itemStore = Optional.ofNullable(nullableItemStore);
    }

    @Override
    @IgnoreProperty
    public ConfigurationService getConfigurationService() {
        return parent;
    }

    public void setParent(ConfigurationService parent) {
        this.parent = parent;
    }

    @Override
    public Class getResourceClass() {
        return resourceClass;
    }

    @Override
    public Optional getInformerListLimit() {
        return informerListLimit;
    }

    @SuppressWarnings("unused")
    // this is needed by Quarkus for the RecordableConstructor
    public Long getNullableInformerListLimit() {
        return informerListLimit.orElse(null);
    }

    @Override
    public String getName() {
        return name;
    }

    @Override
    public String getResourceTypeName() {
        return resourceTypeName;
    }

    @SuppressWarnings("unused")
    // this is needed by Quarkus for the RecordableConstructor
    public String getCrVersion() {
        return crVersion;
    }

    @Override
    public String getFinalizerName() {
        return finalizer;
    }

    public void setFinalizer(String finalizer) {
        this.finalizer = finalizer != null && !finalizer.isBlank() ? finalizer
                : ReconcilerUtils.getDefaultFinalizerName(resourceTypeName);
    }

    @Override
    public boolean isGenerationAware() {
        return generationAware;
    }

    @Override
    public String getAssociatedReconcilerClassName() {
        return associatedReconcilerClassName;
    }

    @Override
    public Set getNamespaces() {
        return namespaces;
    }

    @SuppressWarnings("unchecked")
    void setNamespaces(Collection namespaces) {
        if (!namespaces.equals(this.namespaces)) {
            this.namespaces = namespaces.stream().map(String::trim).collect(Collectors.toSet());
            wereNamespacesSet = true;
            // propagate namespace changes to the dependents' config if needed
            this.dependentsMetadata.forEach((name, spec) -> {
                final var config = spec.getDependentResourceConfig();
                if (config instanceof QuarkusKubernetesDependentResourceConfig qConfig) {
                    qConfig.setNamespaces(this.namespaces);
                }
            });
        }
    }

    public boolean isWereNamespacesSet() {
        return wereNamespacesSet;
    }

    @Override
    public RetryConfiguration getRetryConfiguration() {
        return retryConfiguration;
    }

    void setRetryConfiguration(RetryConfiguration retryConfiguration) {
        this.retryConfiguration = retryConfiguration != null ? retryConfiguration
                : ControllerConfiguration.super.getRetryConfiguration();
        // reset retry if needed
        if (retry == null || retry instanceof GenericRetry) {
            retry = GenericRetry.fromConfiguration(retryConfiguration);
        }
    }

    @IgnoreProperty
    @Override
    public Set getEffectiveNamespaces() {
        return ControllerConfiguration.super.getEffectiveNamespaces();
    }

    @Override
    public String getLabelSelector() {
        return labelSelector;
    }

    void setLabelSelector(String labelSelector) {
        this.labelSelector = labelSelector;
    }

    public boolean isStatusPresentAndNotVoid() {
        return statusPresentAndNotVoid;
    }

    public boolean areDependentsImpactedBy(Set changedClasses) {
        return dependentsMetadata.keySet().parallelStream().anyMatch(changedClasses::contains);
    }

    public boolean needsDependentBeansCreation() {
        return dependentsMetadata != null && !dependentsMetadata.isEmpty();
    }

    public ManagedWorkflow getWorkflow() {
        return workflow;
    }

    public void setWorkflow(ManagedWorkflow workflow) {
        this.workflow = workflow;
    }

    @Override
    public Object getConfigurationFor(DependentResourceSpec dependentResourceSpec) {
        return ((DependentResourceSpecMetadata) dependentResourceSpec).getDependentResourceConfig();
    }

    @Override
    public List getDependentResources() {
        return dependentsMetadata.values().parallelStream().collect(Collectors.toList());
    }

    @Override
    public Retry getRetry() {
        return retry;
    }

    @Override
    public RateLimiter getRateLimiter() {
        return rateLimiter;
    }

    @Override
    public ResourceEventFilter getEventFilter() {
        return eventFilter;
    }

    public Optional maxReconciliationInterval() {
        return maxReconciliationInterval;
    }

    // for Quarkus' RecordableConstructor
    @SuppressWarnings("unused")
    public Duration getMaxReconciliationInterval() {
        return maxReconciliationInterval.orElseThrow();
    }

    void setMaxReconciliationInterval(Duration duration) {
        maxReconciliationInterval = Optional.of(duration);
    }

    // for Quarkus' RecordableConstructor
    @SuppressWarnings("unused")
    public OnAddFilter getOnAddFilter() {
        return onAddFilter.orElse(null);
    }

    @Override
    public Optional> onAddFilter() {
        return onAddFilter;
    }

    // for Quarkus' RecordableConstructor
    @SuppressWarnings("unused")
    public OnUpdateFilter getOnUpdateFilter() {
        return onUpdateFilter.orElse(null);
    }

    @Override
    public Optional> onUpdateFilter() {
        return onUpdateFilter;
    }

    // for Quarkus' RecordableConstructor
    @SuppressWarnings("unused")
    public GenericFilter getGenericFilter() {
        return genericFilter.orElse(null);
    }

    @Override
    public Optional> genericFilter() {
        return genericFilter;
    }

    // for Quarkus' RecordableConstructor
    @SuppressWarnings("unused")
    public Class getRetryClass() {
        return retryClass;
    }

    // for Quarkus' RecordableConstructor
    @SuppressWarnings("unused")
    public Class getRateLimiterClass() {
        return rateLimiterClass;
    }

    // for Quarkus' RecordableConstructor
    @SuppressWarnings("unused")
    public Map> getDependentsMetadata() {
        return dependentsMetadata;
    }

    void initAnnotationConfigurables(Reconciler reconciler) {
        final Class reconcilerClass = reconciler.getClass();
        if (retryConfigurationClass != null) {
            if (retry == null) {
                retry = ClassLoadingUtils.instantiate(retryClass);
            }
            configure(reconcilerClass, retryConfigurationClass, (AnnotationConfigurable) retry);
            retryClass = null;
            retryConfigurationClass = null;
        }

        if (rateLimiterClass != null) {
            if (rateLimiter == null) {
                rateLimiter = ClassLoadingUtils.instantiate(rateLimiterClass);
            }
            configure(reconcilerClass, rateLimiterConfigurationClass, (AnnotationConfigurable) rateLimiter);
            rateLimiterClass = null;
            rateLimiterConfigurationClass = null;
        }
    }

    // Needed for the recordable constructor
    @SuppressWarnings("unused")
    public Class getRetryConfigurationClass() {
        return retryConfigurationClass;
    }

    // Needed for the recordable constructor
    @SuppressWarnings("unused")
    public Class getRateLimiterConfigurationClass() {
        return rateLimiterConfigurationClass;
    }

    @SuppressWarnings("unchecked")
    private void configure(Class reconcilerClass, Class configurationClass,
            AnnotationConfigurable configurable) {
        if (configurationClass != null) {
            var annotation = reconcilerClass.getAnnotation(configurationClass);
            if (annotation != null) {
                configurable.initFrom(annotation);
            }
        }
    }

    public List getAdditionalRBACRules() {
        return additionalRBACRules;
    }

    public List getAdditionalRBACRoleRefs() {
        return additionalRBACRoleRefs;
    }

    @SuppressWarnings("unused")
    // this is needed by Quarkus for the RecordableConstructor
    public String getFieldManager() {
        return fieldManager;
    }

    @Override
    public String fieldManager() {
        return fieldManager;
    }

    @Override
    public Optional> getItemStore() {
        return itemStore;
    }

    @SuppressWarnings("unused")
    // this is needed by Quarkus for the RecordableConstructor
    public ItemStore getNullableItemStore() {
        return itemStore.orElse(null);
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy