From 1f366453b6c743eb27db45d9be99ded9ca947daf Mon Sep 17 00:00:00 2001 From: Chris Laprun Date: Mon, 4 Mar 2024 16:33:02 +0100 Subject: [PATCH] fix: add proper default install mode derived from watched namespaces Fixes #839 Signed-off-by: Chris Laprun --- .../bundle/deployment/BundleGenerator.java | 5 ++- .../bundle/deployment/BundleProcessor.java | 7 +++- .../builders/CsvManifestsBuilder.java | 39 ++++++++++++++----- .../bundle/MultipleOperatorsBundleTest.java | 14 ++++++- .../bundle/sources/FirstReconciler.java | 2 +- 5 files changed, 50 insertions(+), 17 deletions(-) diff --git a/bundle-generator/deployment/src/main/java/io/quarkiverse/operatorsdk/bundle/deployment/BundleGenerator.java b/bundle-generator/deployment/src/main/java/io/quarkiverse/operatorsdk/bundle/deployment/BundleGenerator.java index 038eb9a6..c4ede555 100644 --- a/bundle-generator/deployment/src/main/java/io/quarkiverse/operatorsdk/bundle/deployment/BundleGenerator.java +++ b/bundle-generator/deployment/src/main/java/io/quarkiverse/operatorsdk/bundle/deployment/BundleGenerator.java @@ -15,6 +15,7 @@ import io.quarkiverse.operatorsdk.common.ReconcilerAugmentedClassInfo; import io.quarkiverse.operatorsdk.runtime.BuildTimeOperatorConfiguration; import io.quarkiverse.operatorsdk.runtime.CRDInfo; +import io.quarkiverse.operatorsdk.runtime.QuarkusControllerConfiguration; import io.quarkiverse.operatorsdk.runtime.Version; import io.quarkus.container.util.PathsUtil; @@ -49,7 +50,7 @@ private BundleGenerator() { public static List prepareGeneration(BundleGenerationConfiguration bundleConfiguration, BuildTimeOperatorConfiguration operatorConfiguration, Version version, Map> csvGroups, Map crds, - Path outputDirectory, String deploymentName) { + Path outputDirectory, String deploymentName, Map> controllerConfigs) { List builders = new ArrayList<>(); for (Map.Entry> entry : csvGroups.entrySet()) { final var csvMetadata = entry.getKey(); @@ -57,7 +58,7 @@ public static List prepareGeneration(BundleGenerationConfigura final var mainSourcesRoot = PathsUtil.findMainSourcesRoot(outputDirectory); final var csvBuilder = new CsvManifestsBuilder(csvMetadata, operatorConfiguration, entry.getValue(), - mainSourcesRoot != null ? mainSourcesRoot.getKey() : null, deploymentName); + mainSourcesRoot != null ? mainSourcesRoot.getKey() : null, deploymentName, controllerConfigs); builders.add(csvBuilder); builders.add(new AnnotationsManifestsBuilder(csvMetadata, labels)); builders.add(new BundleDockerfileManifestsBuilder(csvMetadata, labels)); diff --git a/bundle-generator/deployment/src/main/java/io/quarkiverse/operatorsdk/bundle/deployment/BundleProcessor.java b/bundle-generator/deployment/src/main/java/io/quarkiverse/operatorsdk/bundle/deployment/BundleProcessor.java index c58d1b95..cfd9ee53 100644 --- a/bundle-generator/deployment/src/main/java/io/quarkiverse/operatorsdk/bundle/deployment/BundleProcessor.java +++ b/bundle-generator/deployment/src/main/java/io/quarkiverse/operatorsdk/bundle/deployment/BundleProcessor.java @@ -32,6 +32,7 @@ import io.quarkiverse.operatorsdk.bundle.runtime.BundleGenerationConfiguration; import io.quarkiverse.operatorsdk.bundle.runtime.CSVMetadataHolder; import io.quarkiverse.operatorsdk.common.*; +import io.quarkiverse.operatorsdk.deployment.ControllerConfigurationsBuildItem; import io.quarkiverse.operatorsdk.deployment.GeneratedCRDInfoBuildItem; import io.quarkiverse.operatorsdk.deployment.VersionBuildItem; import io.quarkiverse.operatorsdk.runtime.BuildTimeOperatorConfiguration; @@ -193,7 +194,8 @@ void generateBundle(ApplicationInfoBuildItem configuration, BuildProducer doneGeneratingCSV, GeneratedCRDInfoBuildItem generatedCustomResourcesDefinitions, @SuppressWarnings("OptionalUsedAsFieldOrParameterType") Optional maybeGeneratedKubeResources, - BuildProducer generatedCSVs) { + BuildProducer generatedCSVs, + ControllerConfigurationsBuildItem controllerConfigurationsBuildItem) { final var crds = generatedCustomResourcesDefinitions.getCRDGenerationInfo().getCrds() .values().stream() .flatMap(entry -> entry.values().stream()) @@ -243,7 +245,8 @@ void generateBundle(ApplicationInfoBuildItem configuration, final var deploymentName = ResourceNameUtil.getResourceName(kubernetesConfig, configuration); final var generated = BundleGenerator.prepareGeneration(bundleConfiguration, operatorConfiguration, versionBuildItem.getVersion(), - csvMetadata.getCsvGroups(), crds, outputTarget.getOutputDirectory(), deploymentName); + csvMetadata.getCsvGroups(), crds, outputTarget.getOutputDirectory(), deploymentName, + controllerConfigurationsBuildItem.getControllerConfigs()); generated.forEach(manifestBuilder -> { final var fileName = manifestBuilder.getFileName(); try { diff --git a/bundle-generator/deployment/src/main/java/io/quarkiverse/operatorsdk/bundle/deployment/builders/CsvManifestsBuilder.java b/bundle-generator/deployment/src/main/java/io/quarkiverse/operatorsdk/bundle/deployment/builders/CsvManifestsBuilder.java index b4e6656e..e5a85bd4 100644 --- a/bundle-generator/deployment/src/main/java/io/quarkiverse/operatorsdk/bundle/deployment/builders/CsvManifestsBuilder.java +++ b/bundle-generator/deployment/src/main/java/io/quarkiverse/operatorsdk/bundle/deployment/builders/CsvManifestsBuilder.java @@ -23,12 +23,13 @@ import io.quarkiverse.operatorsdk.bundle.runtime.CSVMetadataHolder.RequiredCRD; import io.quarkiverse.operatorsdk.common.*; import io.quarkiverse.operatorsdk.runtime.BuildTimeOperatorConfiguration; +import io.quarkiverse.operatorsdk.runtime.QuarkusControllerConfiguration; public class CsvManifestsBuilder extends ManifestsBuilder { private static final Logger log = Logger.getLogger(CsvManifestsBuilder.class); - private static final String DEFAULT_INSTALL_MODE = "AllNamespaces"; + private static final String ALL_NAMESPACES = "AllNamespaces"; private static final String DEPLOYMENT = "deployment"; private static final String SERVICE_ACCOUNT_KIND = "ServiceAccount"; private static final String CLUSTER_ROLE_KIND = "ClusterRole"; @@ -37,6 +38,9 @@ public class CsvManifestsBuilder extends ManifestsBuilder { private static final Logger LOGGER = Logger.getLogger(CsvManifestsBuilder.class.getName()); private static final String IMAGE_PNG = "image/png"; public static final String OLM_TARGET_NAMESPACES = "metadata.annotations['olm.targetNamespaces']"; + public static final String OWN_NAMESPACE = "OwnNamespace"; + public static final String SINGLE_NAMESPACE = "SingleNamespace"; + public static final String MULTI_NAMESPACE = "MultiNamespace"; private ClusterServiceVersionBuilder csvBuilder; private final Set ownedCRs = new HashSet<>(); private final Set requiredCRs = new HashSet<>(); @@ -46,7 +50,7 @@ public class CsvManifestsBuilder extends ManifestsBuilder { public CsvManifestsBuilder(CSVMetadataHolder metadata, BuildTimeOperatorConfiguration operatorConfiguration, List controllers, - Path mainSourcesRoot, String deploymentName) { + Path mainSourcesRoot, String deploymentName, Map> controllerConfigs) { super(metadata); this.deploymentName = deploymentName; this.controllers = controllers; @@ -135,14 +139,6 @@ public CsvManifestsBuilder(CSVMetadataHolder metadata, BuildTimeOperatorConfigur } } - if (metadata.installModes == null || metadata.installModes.length == 0) { - csvSpecBuilder.addNewInstallMode(true, DEFAULT_INSTALL_MODE); - } else { - for (CSVMetadataHolder.InstallMode installMode : metadata.installModes) { - csvSpecBuilder.addNewInstallMode(installMode.supported, installMode.type); - } - } - // add owned and required CRD, also collect them final var nativeApis = new ArrayList(); controllers.forEach(raci -> { @@ -181,6 +177,29 @@ public CsvManifestsBuilder(CSVMetadataHolder metadata, BuildTimeOperatorConfigur } }); } + + // deal with install modes + // use watched namespaces information for default install mode + // fixme: multiple, incompatible controller configurations in the same bundle will result in inconsistent runs + final var config = controllerConfigs.get(raci.nameOrFailIfUnset()); + if (config.watchAllNamespaces()) { + csvSpecBuilder.withInstallModes(new InstallMode(true, ALL_NAMESPACES)); + } else if (config.watchCurrentNamespace()) { + csvSpecBuilder.withInstallModes(new InstallMode(true, OWN_NAMESPACE)); + } else { + final var namespaces = config.getNamespaces(); + if (namespaces.size() == 1) { + csvSpecBuilder.withInstallModes(new InstallMode(true, SINGLE_NAMESPACE)); + } else { + csvSpecBuilder.withInstallModes(new InstallMode(true, MULTI_NAMESPACE)); + } + } + // then process metadata + if (metadata.installModes != null) { + for (CSVMetadataHolder.InstallMode installMode : metadata.installModes) { + csvSpecBuilder.addNewInstallMode(installMode.supported, installMode.type); + } + } }); // add required CRDs from CSV metadata diff --git a/bundle-generator/deployment/src/test/java/io/quarkiverse/operatorsdk/bundle/MultipleOperatorsBundleTest.java b/bundle-generator/deployment/src/test/java/io/quarkiverse/operatorsdk/bundle/MultipleOperatorsBundleTest.java index a6ed788e..a4fe3b77 100644 --- a/bundle-generator/deployment/src/test/java/io/quarkiverse/operatorsdk/bundle/MultipleOperatorsBundleTest.java +++ b/bundle-generator/deployment/src/test/java/io/quarkiverse/operatorsdk/bundle/MultipleOperatorsBundleTest.java @@ -1,8 +1,7 @@ package io.quarkiverse.operatorsdk.bundle; import static io.quarkiverse.operatorsdk.bundle.Utils.*; -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.junit.jupiter.api.Assertions.*; import java.io.IOException; @@ -49,6 +48,13 @@ public void shouldWriteBundleForTheOperators() throws IOException { assertEquals(FirstReconciler.REPLACES, csv.getSpec().getReplaces()); var bundleMeta = getAnnotationsFor(bundle, "first-operator"); assertEquals(BUNDLE_PACKAGE, bundleMeta.getAnnotations().get("operators.operatorframework.io.bundle.package.v1")); + assertEquals(2, csv.getSpec().getInstallModes().size()); + var installMode = csv.getSpec().getInstallModes().get(0); + assertEquals("AllNamespaces", installMode.getType()); + assertTrue(installMode.getSupported()); + installMode = csv.getSpec().getInstallModes().get(1); + assertEquals("MultiNamespace", installMode.getType()); + assertFalse(installMode.getSupported()); checkBundleFor(bundle, "second-operator", Second.class); csv = getCSVFor(bundle, "second-operator"); @@ -59,6 +65,10 @@ public void shouldWriteBundleForTheOperators() throws IOException { .addToResources(SecondReconciler.RBAC_RULE_RES) .addToVerbs(SecondReconciler.RBAC_RULE_VERBS) .build())); + assertEquals(1, csv.getSpec().getInstallModes().size()); + installMode = csv.getSpec().getInstallModes().get(0); + assertEquals("SingleNamespace", installMode.getType()); + assertTrue(installMode.getSupported()); checkBundleFor(bundle, "third-operator", Third.class); // also check that external CRD is present diff --git a/bundle-generator/deployment/src/test/java/io/quarkiverse/operatorsdk/bundle/sources/FirstReconciler.java b/bundle-generator/deployment/src/test/java/io/quarkiverse/operatorsdk/bundle/sources/FirstReconciler.java index 0c344256..cda1f5e2 100644 --- a/bundle-generator/deployment/src/test/java/io/quarkiverse/operatorsdk/bundle/sources/FirstReconciler.java +++ b/bundle-generator/deployment/src/test/java/io/quarkiverse/operatorsdk/bundle/sources/FirstReconciler.java @@ -5,7 +5,7 @@ import io.javaoperatorsdk.operator.api.reconciler.UpdateControl; import io.quarkiverse.operatorsdk.annotations.CSVMetadata; -@CSVMetadata(bundleName = "first-operator", version = FirstReconciler.VERSION) +@CSVMetadata(bundleName = "first-operator", version = FirstReconciler.VERSION, installModes = @CSVMetadata.InstallMode(type = "MultiNamespace", supported = false)) public class FirstReconciler implements Reconciler { public static final String VERSION = "first-version";