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);
}
}
}
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);
}
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);
}
Aggregations