Search in sources :

Example 1 with BuildException

use of io.quarkus.builder.BuildException 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

CustomResource (io.fabric8.kubernetes.client.CustomResource)1 ClassUtils.loadClass (io.quarkiverse.operatorsdk.common.ClassUtils.loadClass)1 QuarkusControllerConfiguration (io.quarkiverse.operatorsdk.runtime.QuarkusControllerConfiguration)1 BuildException (io.quarkus.builder.BuildException)1 ForceNonWeakReflectiveClassBuildItem (io.quarkus.deployment.builditem.nativeimage.ForceNonWeakReflectiveClassBuildItem)1