Search in sources :

Example 1 with QuarkusControllerConfiguration

use of io.quarkiverse.operatorsdk.runtime.QuarkusControllerConfiguration in project quarkus-operator-sdk by quarkiverse.

the class AddRoleBindingsDecorator method visit.

@Override
public void visit(KubernetesListBuilder list) {
    final var serviceAccountName = getMandatoryDeploymentMetadata(list).getName();
    for (Entry<String, QuarkusControllerConfiguration> entry : configs.entrySet()) {
        String controllerName = entry.getKey();
        QuarkusControllerConfiguration config = entry.getValue();
        if (config.watchCurrentNamespace()) {
            // create a RoleBinding that will be applied in the current namespace if watching only the current NS
            list.addToItems(new RoleBindingBuilder().withNewMetadata().withName(controllerName + "-role-binding").endMetadata().withNewRoleRef(RBAC_AUTHORIZATION_GROUP, CLUSTER_ROLE, getClusterRoleName(controllerName)).addNewSubject(null, SERVICE_ACCOUNT, serviceAccountName, null).build());
        } else if (config.watchAllNamespaces()) {
            handleClusterRoleBinding(list, serviceAccountName, controllerName, controllerName + "-cluster-role-binding", "watch all namespaces", getClusterRoleName(controllerName));
        } else {
            config.getEffectiveNamespaces().forEach(ns -> list.addToItems(new RoleBindingBuilder().withNewMetadata().withName(controllerName + "-role-binding").withNamespace((String) ns).endMetadata().withNewRoleRef(RBAC_AUTHORIZATION_GROUP, CLUSTER_ROLE, getClusterRoleName(controllerName)).addNewSubject(null, SERVICE_ACCOUNT, serviceAccountName, null).build()));
        }
        // if we validate the CRDs, also create a binding for the CRD validating role
        if (validateCRDs) {
            final var crBindingName = controllerName + "-crd-validating-role-binding";
            handleClusterRoleBinding(list, serviceAccountName, controllerName, crBindingName, "validate CRDs", AddClusterRolesDecorator.JOSDK_CRD_VALIDATING_CLUSTER_ROLE);
        }
    }
}
Also used : AddClusterRolesDecorator.getClusterRoleName(io.quarkiverse.operatorsdk.deployment.AddClusterRolesDecorator.getClusterRoleName) ResourceProvidingDecorator(io.dekorate.kubernetes.decorator.ResourceProvidingDecorator) KubernetesListBuilder(io.fabric8.kubernetes.api.model.KubernetesListBuilder) RoleBindingBuilder(io.fabric8.kubernetes.api.model.rbac.RoleBindingBuilder) ConfigProvider(org.eclipse.microprofile.config.ConfigProvider) Map(java.util.Map) Entry(java.util.Map.Entry) ConcurrentHashMap(java.util.concurrent.ConcurrentHashMap) Optional(java.util.Optional) QuarkusControllerConfiguration(io.quarkiverse.operatorsdk.runtime.QuarkusControllerConfiguration) ClusterRoleBindingBuilder(io.fabric8.kubernetes.api.model.rbac.ClusterRoleBindingBuilder) ConcurrentMap(java.util.concurrent.ConcurrentMap) RoleBindingBuilder(io.fabric8.kubernetes.api.model.rbac.RoleBindingBuilder) ClusterRoleBindingBuilder(io.fabric8.kubernetes.api.model.rbac.ClusterRoleBindingBuilder) QuarkusControllerConfiguration(io.quarkiverse.operatorsdk.runtime.QuarkusControllerConfiguration)

Example 2 with QuarkusControllerConfiguration

use of io.quarkiverse.operatorsdk.runtime.QuarkusControllerConfiguration in project quarkus-operator-sdk by quarkiverse.

the class OperatorSDKProcessor method createConfigurationServiceAndOperator.

@BuildStep
ConfigurationServiceBuildItem createConfigurationServiceAndOperator(OutputTargetBuildItem outputTarget, CombinedIndexBuildItem combinedIndexBuildItem, BuildProducer<AdditionalBeanBuildItem> additionalBeans, BuildProducer<ReflectiveClassBuildItem> reflectionClasses, BuildProducer<ForceNonWeakReflectiveClassBuildItem> forcedReflectionClasses, BuildProducer<GeneratedCRDInfoBuildItem> generatedCRDInfo, LiveReloadBuildItem liveReload) {
    final CRDConfiguration crdConfig = buildTimeConfiguration.crd;
    final boolean validateCustomResources = ConfigurationUtils.shouldValidateCustomResources(buildTimeConfiguration.checkCRDAndValidateLocalModel, buildTimeConfiguration.crd.validate, log);
    // apply should imply generate: we cannot apply if we're not generating!
    final var crdGeneration = new CRDGeneration(crdConfig.generate || crdConfig.apply);
    final var index = combinedIndexBuildItem.getIndex();
    final List<QuarkusControllerConfiguration> controllerConfigs = ClassUtils.getKnownReconcilers(index, log).map(ci -> createControllerConfiguration(ci, additionalBeans, reflectionClasses, forcedReflectionClasses, index, crdGeneration, liveReload)).filter(Optional::isPresent).map(Optional::get).collect(Collectors.toList());
    // retrieve the known CRD information to make sure we always have a full view
    var storedCRDInfos = liveReload.getContextObject(ContextStoredCRDInfos.class);
    if (storedCRDInfos == null) {
        storedCRDInfos = new ContextStoredCRDInfos();
    }
    CRDGenerationInfo crdInfo = crdGeneration.generate(outputTarget, crdConfig, validateCustomResources, storedCRDInfos.getExisting());
    storedCRDInfos.putAll(crdInfo.getCrds());
    // record CRD generation info in context for future use
    liveReload.setContextObject(ContextStoredCRDInfos.class, storedCRDInfos);
    additionalBeans.produce(AdditionalBeanBuildItem.unremovableOf(OperatorProducer.class));
    // if the app doesn't provide a main class, add the AppEventListener
    if (index.getAllKnownImplementors(DotName.createSimple(QuarkusApplication.class.getName())).isEmpty()) {
        additionalBeans.produce(AdditionalBeanBuildItem.builder().addBeanClass(AppEventListener.class).setDefaultScope(DotName.createSimple(Singleton.class.getName())).setUnremovable().build());
    }
    generatedCRDInfo.produce(new GeneratedCRDInfoBuildItem(crdInfo));
    return new ConfigurationServiceBuildItem(Version.loadFromProperties(), controllerConfigs);
}
Also used : ConfigurationServiceRecorder(io.quarkiverse.operatorsdk.runtime.ConfigurationServiceRecorder) UnremovableBeanBuildItem(io.quarkus.arc.deployment.UnremovableBeanBuildItem) JandexUtil(io.quarkus.deployment.util.JandexUtil) RunTimeOperatorConfiguration(io.quarkiverse.operatorsdk.runtime.RunTimeOperatorConfiguration) Version(io.quarkiverse.operatorsdk.runtime.Version) ClassInfo(org.jboss.jandex.ClassInfo) CombinedIndexBuildItem(io.quarkus.deployment.builditem.CombinedIndexBuildItem) RECONCILER(io.quarkiverse.operatorsdk.common.Constants.RECONCILER) BuildProducer(io.quarkus.deployment.annotations.BuildProducer) MetricsCapabilityBuildItem(io.quarkus.deployment.metrics.MetricsCapabilityBuildItem) BooleanSupplier(java.util.function.BooleanSupplier) Reconciler(io.javaoperatorsdk.operator.api.reconciler.Reconciler) AdditionalBeanBuildItem(io.quarkus.arc.deployment.AdditionalBeanBuildItem) ObserverConfiguratorBuildItem(io.quarkus.arc.deployment.ObserverRegistrationPhaseBuildItem.ObserverConfiguratorBuildItem) BuildException(io.quarkus.builder.BuildException) FeatureBuildItem(io.quarkus.deployment.builditem.FeatureBuildItem) Operator(io.javaoperatorsdk.operator.Operator) ClassUtils.loadClass(io.quarkiverse.operatorsdk.common.ClassUtils.loadClass) ConfigurationUtils(io.quarkiverse.operatorsdk.common.ConfigurationUtils) CRDGenerationInfo(io.quarkiverse.operatorsdk.runtime.CRDGenerationInfo) Instance(javax.enterprise.inject.Instance) AnnotationValue(org.jboss.jandex.AnnotationValue) CUSTOM_RESOURCE(io.quarkiverse.operatorsdk.common.Constants.CUSTOM_RESOURCE) NoOpMetricsProvider(io.quarkiverse.operatorsdk.runtime.NoOpMetricsProvider) ObserverRegistrationPhaseBuildItem(io.quarkus.arc.deployment.ObserverRegistrationPhaseBuildItem) ExecutionTime(io.quarkus.deployment.annotations.ExecutionTime) ForceNonWeakReflectiveClassBuildItem(io.quarkus.deployment.builditem.nativeimage.ForceNonWeakReflectiveClassBuildItem) DecoratorBuildItem(io.quarkus.kubernetes.spi.DecoratorBuildItem) CDI(javax.enterprise.inject.spi.CDI) Collectors(java.util.stream.Collectors) HasMetadata(io.fabric8.kubernetes.api.model.HasMetadata) APPLICATION_SCOPED(io.quarkus.arc.processor.DotNames.APPLICATION_SCOPED) List(java.util.List) AnnotationInstance(org.jboss.jandex.AnnotationInstance) Annotation(java.lang.annotation.Annotation) Optional(java.util.Optional) QuarkusControllerConfiguration(io.quarkiverse.operatorsdk.runtime.QuarkusControllerConfiguration) ResultHandle(io.quarkus.gizmo.ResultHandle) CustomResource(io.fabric8.kubernetes.client.CustomResource) SyntheticBeanBuildItem(io.quarkus.arc.deployment.SyntheticBeanBuildItem) ReflectiveClassBuildItem(io.quarkus.deployment.builditem.nativeimage.ReflectiveClassBuildItem) Record(io.quarkus.deployment.annotations.Record) AppEventListener(io.quarkiverse.operatorsdk.runtime.AppEventListener) ClassUtils(io.quarkiverse.operatorsdk.common.ClassUtils) Logger(org.jboss.logging.Logger) MethodCreator(io.quarkus.gizmo.MethodCreator) DotName(org.jboss.jandex.DotName) HashMap(java.util.HashMap) Singleton(javax.inject.Singleton) MetricsFactory(io.quarkus.runtime.metrics.MetricsFactory) IndexDependencyBuildItem(io.quarkus.deployment.builditem.IndexDependencyBuildItem) OperatorProducer(io.quarkiverse.operatorsdk.runtime.OperatorProducer) BuildStep(io.quarkus.deployment.annotations.BuildStep) ResourceInfo(io.quarkiverse.operatorsdk.common.ResourceInfo) AssignableResultHandle(io.quarkus.gizmo.AssignableResultHandle) IndexView(org.jboss.jandex.IndexView) ConfigurationService(io.javaoperatorsdk.operator.api.config.ConfigurationService) MethodDescriptor(io.quarkus.gizmo.MethodDescriptor) BuildTimeOperatorConfiguration(io.quarkiverse.operatorsdk.runtime.BuildTimeOperatorConfiguration) ObjectMapper(com.fasterxml.jackson.databind.ObjectMapper) CONTROLLER_CONFIGURATION(io.quarkiverse.operatorsdk.common.Constants.CONTROLLER_CONFIGURATION) ReconcilerUtils(io.javaoperatorsdk.operator.ReconcilerUtils) ObserverConfigurator(io.quarkus.arc.processor.ObserverConfigurator) CRDConfiguration(io.quarkiverse.operatorsdk.runtime.CRDConfiguration) OutputTargetBuildItem(io.quarkus.deployment.pkg.builditem.OutputTargetBuildItem) DelayRegistrationUntil(io.quarkiverse.operatorsdk.runtime.DelayRegistrationUntil) QuarkusApplication(io.quarkus.runtime.QuarkusApplication) QuarkusConfigurationService(io.quarkiverse.operatorsdk.runtime.QuarkusConfigurationService) Collections(java.util.Collections) LiveReloadBuildItem(io.quarkus.deployment.builditem.LiveReloadBuildItem) Optional(java.util.Optional) QuarkusControllerConfiguration(io.quarkiverse.operatorsdk.runtime.QuarkusControllerConfiguration) QuarkusApplication(io.quarkus.runtime.QuarkusApplication) CRDConfiguration(io.quarkiverse.operatorsdk.runtime.CRDConfiguration) CRDGenerationInfo(io.quarkiverse.operatorsdk.runtime.CRDGenerationInfo) OperatorProducer(io.quarkiverse.operatorsdk.runtime.OperatorProducer) Singleton(javax.inject.Singleton) BuildStep(io.quarkus.deployment.annotations.BuildStep)

Example 3 with QuarkusControllerConfiguration

use of io.quarkiverse.operatorsdk.runtime.QuarkusControllerConfiguration in project quarkus-operator-sdk by quarkiverse.

the class OperatorSDKProcessor method createControllerConfiguration.

@SuppressWarnings("unchecked")
private Optional<QuarkusControllerConfiguration> createControllerConfiguration(ClassInfo info, BuildProducer<AdditionalBeanBuildItem> additionalBeans, BuildProducer<ReflectiveClassBuildItem> reflectionClasses, BuildProducer<ForceNonWeakReflectiveClassBuildItem> forcedReflectionClasses, IndexView index, CRDGeneration crdGeneration, LiveReloadBuildItem liveReload) {
    // first retrieve the target resource class name
    final var primaryType = JandexUtil.resolveTypeParameters(info.name(), RECONCILER, index).get(0);
    final var primaryTypeDN = primaryType.name();
    final var primaryTypeName = primaryTypeDN.toString();
    // retrieve the reconciler's name
    final var reconcilerClassName = info.name().toString();
    final var controllerAnnotation = info.classAnnotation(CONTROLLER_CONFIGURATION);
    final String name = ConfigurationUtils.getReconcilerName(reconcilerClassName, controllerAnnotation);
    // if we get CustomResource instead of a subclass, ignore the controller since we cannot do anything with it
    if (primaryTypeName == null || CUSTOM_RESOURCE.equals(primaryTypeDN)) {
        log.infov("Skipped processing of ''{0}'' controller as it's not parameterized with a CustomResource sub-class", reconcilerClassName);
        return Optional.empty();
    }
    // create Reconciler bean
    additionalBeans.produce(AdditionalBeanBuildItem.builder().addBeanClass(reconcilerClassName).setUnremovable().setDefaultScope(APPLICATION_SCOPED).build());
    // register target resource class for introspection
    registerForReflection(reflectionClasses, primaryTypeName);
    forcedReflectionClasses.produce(new ForceNonWeakReflectiveClassBuildItem(primaryTypeName));
    // register spec and status for introspection if we're targeting a CustomResource
    final var primaryCI = index.getClassByName(primaryTypeDN);
    boolean isCR = false;
    if (primaryCI == null) {
        log.warnv("''{0}'' has not been found in the Jandex index so it cannot be introspected. Assumed not to be a CustomResource implementation. If you believe this is wrong, please index your classes with Jandex.", primaryTypeDN);
    } else {
        try {
            isCR = JandexUtil.isSubclassOf(index, primaryCI, CUSTOM_RESOURCE);
        } catch (BuildException e) {
            log.errorv("Couldn't ascertain if ''{0}'' is a CustomResource subclass. Assumed not to be.", e);
        }
    }
    String specClassName = null;
    String statusClassName = null;
    if (isCR) {
        final var crParamTypes = JandexUtil.resolveTypeParameters(primaryTypeDN, CUSTOM_RESOURCE, index);
        specClassName = crParamTypes.get(0).name().toString();
        statusClassName = crParamTypes.get(1).name().toString();
        registerForReflection(reflectionClasses, specClassName);
        registerForReflection(reflectionClasses, statusClassName);
    }
    // now check if there's more work to do, depending on reloaded state
    Class<? extends HasMetadata> resourceClass = null;
    String resourceFullName = null;
    // check if we need to regenerate the CRD
    final var changeInformation = liveReload.getChangeInformation();
    if (isCR && crdGeneration.wantCRDGenerated()) {
        // check whether we already have generated CRDs
        var storedCRDInfos = liveReload.getContextObject(ContextStoredCRDInfos.class);
        // request CRD generation by default
        final boolean[] generateCurrent = { true };
        final var crClass = (Class<? extends CustomResource>) loadClass(primaryTypeName);
        resourceClass = crClass;
        resourceFullName = getFullResourceName(crClass);
        // When we have a live reload, check if we need to regenerate the associated CRD
        if (liveReload.isLiveReload() && storedCRDInfos != null) {
            final var finalCrdName = resourceFullName;
            final var crdInfos = storedCRDInfos.getCRDInfosFor(resourceFullName);
            // check for all CRD spec version requested
            buildTimeConfiguration.crd.versions.forEach(v -> {
                final var crd = crdInfos.get(v);
                // if we don't have any information about this CRD version, we need to generate the CRD
                if (crd == null) {
                    return;
                }
                // if dependent classes have been changed
                if (changeInformation != null) {
                    for (String changedClass : changeInformation.getChangedClasses()) {
                        if (crd.getDependentClassNames().contains(changedClass)) {
                            // a dependent class has been changed, so we'll need to generate the CRD
                            return;
                        }
                    }
                }
                // we've looked at all the changed classes and none have been changed for this CR/version: do not regenerate CRD
                generateCurrent[0] = false;
                log.infov("''{0}'' CRD generation was skipped for ''{1}'' because no changes impacting the CRD were detected", v, finalCrdName);
            });
        }
        // if we still need to generate the CRD, add the CR to the set to be generated
        if (generateCurrent[0]) {
            crdGeneration.withCustomResource(crClass, resourceFullName, name);
        }
    }
    // check if we need to regenerate the configuration for this controller
    QuarkusControllerConfiguration configuration = null;
    boolean regenerateConfig = true;
    var storedConfigurations = liveReload.getContextObject(ContextStoredControllerConfigurations.class);
    if (liveReload.isLiveReload() && storedConfigurations != null) {
        // check if we've already generated a configuration for this controller
        configuration = storedConfigurations.getConfigurations().get(reconcilerClassName);
        if (configuration != null) {
            /*
                 * A configuration needs to be regenerated if:
                 * - the ResourceController annotation has changed
                 * - the associated CustomResource metadata has changed
                 * - the configuration properties have changed as follows:
                 * + extension-wide properties affecting all controllers have changed
                 * + controller-specific properties have changed
                 *
                 * Here, we only perform a simplified check: either the class holding the ResourceController annotation has
                 * changed, or the associated CustomResource class or application.properties as a whole has changed. This
                 * could be optimized further if needed.
                 *
                 */
            final var changedClasses = changeInformation == null ? Collections.emptySet() : changeInformation.getChangedClasses();
            regenerateConfig = changedClasses.contains(reconcilerClassName) || changedClasses.contains(primaryTypeName) || liveReload.getChangedResources().contains("application.properties");
        }
    }
    if (regenerateConfig) {
        // extract the configuration from annotation and/or external configuration
        final var delayedRegistrationAnnotation = info.classAnnotation(DELAY_REGISTRATION);
        final var configExtractor = new BuildTimeHybridControllerConfiguration(buildTimeConfiguration, buildTimeConfiguration.controllers.get(name), controllerAnnotation, delayedRegistrationAnnotation);
        if (resourceFullName == null) {
            resourceClass = (Class<? extends HasMetadata>) loadClass(primaryTypeName);
            resourceFullName = getFullResourceName(resourceClass);
        }
        final var crVersion = HasMetadata.getVersion(resourceClass);
        // create the configuration
        configuration = new QuarkusControllerConfiguration(reconcilerClassName, name, resourceFullName, crVersion, configExtractor.generationAware(), primaryTypeName, configExtractor.delayedRegistration(), configExtractor.namespaces(name), getFinalizer(controllerAnnotation, resourceFullName), getLabelSelector(controllerAnnotation), Optional.ofNullable(specClassName), Optional.ofNullable(statusClassName));
        log.infov("Processed ''{0}'' reconciler named ''{1}'' for ''{2}'' resource (version ''{3}'')", reconcilerClassName, name, resourceFullName, HasMetadata.getApiVersion(resourceClass));
    } else {
        log.infov("Skipped configuration reload for ''{0}'' reconciler as no changes were detected", reconcilerClassName);
    }
    // store the configuration in the live reload context
    if (storedConfigurations == null) {
        storedConfigurations = new ContextStoredControllerConfigurations();
    }
    storedConfigurations.getConfigurations().put(reconcilerClassName, configuration);
    liveReload.setContextObject(ContextStoredControllerConfigurations.class, storedConfigurations);
    return Optional.of(configuration);
}
Also used : CustomResource(io.fabric8.kubernetes.client.CustomResource) ForceNonWeakReflectiveClassBuildItem(io.quarkus.deployment.builditem.nativeimage.ForceNonWeakReflectiveClassBuildItem) QuarkusControllerConfiguration(io.quarkiverse.operatorsdk.runtime.QuarkusControllerConfiguration) ClassUtils.loadClass(io.quarkiverse.operatorsdk.common.ClassUtils.loadClass) BuildException(io.quarkus.builder.BuildException)

Aggregations

QuarkusControllerConfiguration (io.quarkiverse.operatorsdk.runtime.QuarkusControllerConfiguration)3 CustomResource (io.fabric8.kubernetes.client.CustomResource)2 ClassUtils.loadClass (io.quarkiverse.operatorsdk.common.ClassUtils.loadClass)2 BuildException (io.quarkus.builder.BuildException)2 Optional (java.util.Optional)2 ObjectMapper (com.fasterxml.jackson.databind.ObjectMapper)1 ResourceProvidingDecorator (io.dekorate.kubernetes.decorator.ResourceProvidingDecorator)1 HasMetadata (io.fabric8.kubernetes.api.model.HasMetadata)1 KubernetesListBuilder (io.fabric8.kubernetes.api.model.KubernetesListBuilder)1 ClusterRoleBindingBuilder (io.fabric8.kubernetes.api.model.rbac.ClusterRoleBindingBuilder)1 RoleBindingBuilder (io.fabric8.kubernetes.api.model.rbac.RoleBindingBuilder)1 Operator (io.javaoperatorsdk.operator.Operator)1 ReconcilerUtils (io.javaoperatorsdk.operator.ReconcilerUtils)1 ConfigurationService (io.javaoperatorsdk.operator.api.config.ConfigurationService)1 Reconciler (io.javaoperatorsdk.operator.api.reconciler.Reconciler)1 ClassUtils (io.quarkiverse.operatorsdk.common.ClassUtils)1 ConfigurationUtils (io.quarkiverse.operatorsdk.common.ConfigurationUtils)1 CONTROLLER_CONFIGURATION (io.quarkiverse.operatorsdk.common.Constants.CONTROLLER_CONFIGURATION)1 CUSTOM_RESOURCE (io.quarkiverse.operatorsdk.common.Constants.CUSTOM_RESOURCE)1 RECONCILER (io.quarkiverse.operatorsdk.common.Constants.RECONCILER)1