From 156d99c28dede7cee8e3fb95243e4c45464fb7e5 Mon Sep 17 00:00:00 2001 From: Chris Laprun Date: Mon, 4 Nov 2024 19:22:42 +0100 Subject: [PATCH] feat: allow loading external CRDs from specified locations Locations are specified as comma-separated strings using the quarkus.operator-sdk.bundle.external-crd-locations property. Fixes #985 Signed-off-by: Chris Laprun --- .../bundle/deployment/BundleGenerator.java | 7 ++- .../bundle/deployment/BundleProcessor.java | 50 +++++++++++------ .../src/test/external-crds/external.crd.yml | 26 +++++++++ .../test/external-crds/v1beta1spec.crd.yml | 27 ++++++++++ .../operatorsdk/bundle/ExternalCRDsTest.java | 53 +++++++++++++++++++ .../bundle/MultipleOperatorsBundleTest.java | 4 +- .../sources/ReconcilerWithExternalCR.java | 17 ++++++ .../bundle/sources/V1Beta1CRD.java | 20 +++++++ .../operatorsdk/runtime/CRDInfos.java | 10 ++-- .../operatorsdk/runtime/CRDUtils.java | 34 +++++++++--- 10 files changed, 217 insertions(+), 31 deletions(-) create mode 100644 bundle-generator/deployment/src/test/external-crds/external.crd.yml create mode 100644 bundle-generator/deployment/src/test/external-crds/v1beta1spec.crd.yml create mode 100644 bundle-generator/deployment/src/test/java/io/quarkiverse/operatorsdk/bundle/ExternalCRDsTest.java create mode 100644 bundle-generator/deployment/src/test/java/io/quarkiverse/operatorsdk/bundle/sources/ReconcilerWithExternalCR.java create mode 100644 bundle-generator/deployment/src/test/java/io/quarkiverse/operatorsdk/bundle/sources/V1Beta1CRD.java 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 bbba53cb..e5b3bace 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 @@ -16,6 +16,7 @@ import io.quarkiverse.operatorsdk.runtime.BuildTimeOperatorConfiguration; import io.quarkiverse.operatorsdk.runtime.CRDGenerationInfo; import io.quarkiverse.operatorsdk.runtime.CRDInfo; +import io.quarkiverse.operatorsdk.runtime.CRDInfos; import io.quarkiverse.operatorsdk.runtime.Version; import io.quarkus.container.util.PathsUtil; @@ -50,7 +51,7 @@ private BundleGenerator() { public static List prepareGeneration(BundleGenerationConfiguration bundleConfiguration, BuildTimeOperatorConfiguration operatorConfiguration, Version version, Map> csvGroups, CRDGenerationInfo crds, - Path outputDirectory, String deploymentName) { + CRDInfos unownedCRDs, Path outputDirectory, String deploymentName) { List builders = new ArrayList<>(); final var mainSourcesRoot = PathsUtil.findMainSourcesRoot(outputDirectory); final var crdNameToInfoMappings = crds.getCrds().getCRDNameToInfoMappings(); @@ -76,6 +77,10 @@ public static List prepareGeneration(BundleGenerationConfigura if (!missing.isEmpty()) { log.warnv("Missing required CRD data for resources: {0} for bundle: {1}", missing, csvMetadata.bundleName); } + + // output non-generated CRDs + unownedCRDs.getCRDNameToInfoMappings().values() + .forEach(info -> builders.add(new CustomResourceManifestsBuilder(csvMetadata, info))); } return builders; 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 f1e0f930..a4999c3c 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 @@ -11,7 +11,9 @@ import java.util.List; import java.util.Map; import java.util.Optional; +import java.util.Set; import java.util.function.BooleanSupplier; +import java.util.function.Predicate; import org.jboss.jandex.AnnotationInstance; import org.jboss.jandex.AnnotationValue; @@ -38,12 +40,15 @@ import io.quarkiverse.operatorsdk.deployment.GeneratedCRDInfoBuildItem; import io.quarkiverse.operatorsdk.deployment.VersionBuildItem; import io.quarkiverse.operatorsdk.runtime.BuildTimeOperatorConfiguration; +import io.quarkiverse.operatorsdk.runtime.CRDInfo; import io.quarkiverse.operatorsdk.runtime.CRDInfos; +import io.quarkiverse.operatorsdk.runtime.CRDUtils; import io.quarkus.deployment.annotations.BuildProducer; import io.quarkus.deployment.annotations.BuildStep; import io.quarkus.deployment.builditem.ApplicationInfoBuildItem; import io.quarkus.deployment.builditem.CombinedIndexBuildItem; import io.quarkus.deployment.builditem.GeneratedFileSystemResourceBuildItem; +import io.quarkus.deployment.pkg.builditem.CurateOutcomeBuildItem; import io.quarkus.deployment.pkg.builditem.JarBuildItem; import io.quarkus.deployment.pkg.builditem.OutputTargetBuildItem; import io.quarkus.kubernetes.deployment.KubernetesConfig; @@ -58,6 +63,7 @@ public class BundleProcessor { private static final DotName CSV_METADATA = DotName.createSimple(CSVMetadata.class.getName()); private static final String BUNDLE = "bundle"; private static final String DEFAULT_PROVIDER_NAME = System.getProperty("user.name"); + private static final Set EMPTY_SET = Set.of(); private static ReconcilerAugmentedClassInfo augmentReconcilerInfo( ReconcilerAugmentedClassInfo reconcilerInfo) { @@ -110,24 +116,35 @@ private static String getBundleName(AnnotationInstance csvMetadata, String defau @BuildStep(onlyIf = IsGenerationEnabled.class) UnownedCRDInfoBuildItem unownedCRDInfo(BundleGenerationConfiguration bundleConfiguration, - OutputTargetBuildItem outputTarget) { + CurateOutcomeBuildItem appInfoBuildItem) { final Optional> maybeExternalCRDs = bundleConfiguration.externalCRDLocations(); if (maybeExternalCRDs.isPresent()) { - final var moduleRoot = outputTarget.getOutputDirectory().getParent(); + final var moduleRoot = appInfoBuildItem.getApplicationModel().getApplicationModule().getModuleDir().toPath(); final var crds = new CRDInfos(); - maybeExternalCRDs.get().forEach(crdLocation -> { - /* - * Path crdPath = toPath(crdLocation); - * CustomResourceDefinition crd = loadFrom(crdPath); - * new CRDInfo() - */ - }); + maybeExternalCRDs.get().stream() + .filter(Predicate.not(String::isBlank)) + .map(String::trim) + .forEach(crdLocation -> { + final var crdPath = moduleRoot.resolve(crdLocation); + final var crd = loadFrom(crdPath); + crds.addCRDInfoFor(crd.getCrdName(), crd.getCrdSpecVersion(), crd); + }); return new UnownedCRDInfoBuildItem(crds); } else { return new UnownedCRDInfoBuildItem(new CRDInfos()); } } + private CRDInfo loadFrom(Path crdPath) { + try { + final var crd = CRDUtils.loadFrom(crdPath); + final var crdName = crd.getMetadata().getName(); + return new CRDInfo(crdName, CRDUtils.DEFAULT_CRD_SPEC_VERSION, crdPath.toFile().getAbsolutePath(), EMPTY_SET); + } catch (IOException e) { + throw new RuntimeException(e); + } + } + @SuppressWarnings({ "unused" }) @BuildStep(onlyIf = IsGenerationEnabled.class) CSVMetadataBuildItem gatherCSVMetadata(KubernetesConfig kubernetesConfig, @@ -231,6 +248,7 @@ void generateBundle(ApplicationInfoBuildItem configuration, BuildTimeOperatorConfiguration operatorConfiguration, OutputTargetBuildItem outputTarget, CSVMetadataBuildItem csvMetadata, + UnownedCRDInfoBuildItem unownedCRDs, VersionBuildItem versionBuildItem, BuildProducer doneGeneratingCSV, GeneratedCRDInfoBuildItem generatedCustomResourcesDefinitions, @@ -282,22 +300,24 @@ void generateBundle(ApplicationInfoBuildItem configuration, final var generated = BundleGenerator.prepareGeneration(bundleConfiguration, operatorConfiguration, versionBuildItem.getVersion(), csvMetadata.getCsvGroups(), generatedCustomResourcesDefinitions.getCRDGenerationInfo(), + unownedCRDs.getCRDs(), outputTarget.getOutputDirectory(), deploymentName); generated.forEach(manifestBuilder -> { final var fileName = manifestBuilder.getFileName(); + final var name = manifestBuilder.getName(); try { generatedCSVs.produce( new GeneratedFileSystemResourceBuildItem( - Path.of(BUNDLE).resolve(manifestBuilder.getName()).resolve(fileName).toString(), + Path.of(BUNDLE).resolve(name).resolve(fileName).toString(), manifestBuilder.getManifestData(serviceAccounts, clusterRoleBindings, clusterRoles, roleBindings, roles, deployments))); - log.infov("Generating {0} for ''{1}'' controller -> {2}", + log.infov("Processing {0} for ''{1}'' controller -> {2}", manifestBuilder.getManifestType(), - manifestBuilder.getName(), - outputDir.resolve(manifestBuilder.getName()).resolve(fileName)); + name, + outputDir.resolve(name).resolve(fileName)); } catch (IOException e) { - log.errorv("Cannot generate {0} for ''{1}'' controller: {2}", - manifestBuilder.getManifestType(), manifestBuilder.getName(), e.getMessage()); + log.errorv("Cannot process {0} for ''{1}'' controller: {2}", + manifestBuilder.getManifestType(), name, e.getMessage()); } }); doneGeneratingCSV.produce(new GeneratedBundleBuildItem()); diff --git a/bundle-generator/deployment/src/test/external-crds/external.crd.yml b/bundle-generator/deployment/src/test/external-crds/external.crd.yml new file mode 100644 index 00000000..19576b17 --- /dev/null +++ b/bundle-generator/deployment/src/test/external-crds/external.crd.yml @@ -0,0 +1,26 @@ +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + name: externals.halkyon.io +spec: + conversion: + strategy: None + group: halkyon.io + names: + kind: External + listKind: ExternalList + plural: externals + singular: external + scope: Namespaced + versions: + - name: v1 + schema: + openAPIV3Schema: + properties: + spec: + type: object + status: + type: object + type: object + served: true + storage: true \ No newline at end of file diff --git a/bundle-generator/deployment/src/test/external-crds/v1beta1spec.crd.yml b/bundle-generator/deployment/src/test/external-crds/v1beta1spec.crd.yml new file mode 100644 index 00000000..98e1ec20 --- /dev/null +++ b/bundle-generator/deployment/src/test/external-crds/v1beta1spec.crd.yml @@ -0,0 +1,27 @@ +# Generated by Fabric8 CRDGenerator, manual edits might get overwritten! +apiVersion: apiextensions.k8s.io/v1beta1 +kind: CustomResourceDefinition +metadata: + name: v1beta1s.test.com +spec: + group: test.com + names: + kind: V1Beta1 + plural: v1beta1s + singular: v1beta1 + scope: Namespaced + subresources: + status: { } + validation: + openAPIV3Schema: + properties: + spec: + properties: + value: + type: string + type: object + type: object + versions: + - name: v1 + served: true + storage: true \ No newline at end of file diff --git a/bundle-generator/deployment/src/test/java/io/quarkiverse/operatorsdk/bundle/ExternalCRDsTest.java b/bundle-generator/deployment/src/test/java/io/quarkiverse/operatorsdk/bundle/ExternalCRDsTest.java new file mode 100644 index 00000000..4652edf0 --- /dev/null +++ b/bundle-generator/deployment/src/test/java/io/quarkiverse/operatorsdk/bundle/ExternalCRDsTest.java @@ -0,0 +1,53 @@ +package io.quarkiverse.operatorsdk.bundle; + +import static io.quarkiverse.operatorsdk.bundle.Utils.*; +import static io.quarkiverse.operatorsdk.bundle.Utils.getCRDNameFor; +import static org.junit.jupiter.api.Assertions.*; + +import java.io.IOException; +import java.nio.file.Files; + +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.RegisterExtension; + +import io.fabric8.kubernetes.api.model.HasMetadata; +import io.quarkiverse.operatorsdk.bundle.sources.External; +import io.quarkiverse.operatorsdk.bundle.sources.ExternalDependentResource; +import io.quarkiverse.operatorsdk.bundle.sources.First; +import io.quarkiverse.operatorsdk.bundle.sources.ReconcilerWithExternalCR; +import io.quarkiverse.operatorsdk.bundle.sources.V1Beta1CRD; +import io.quarkus.test.ProdBuildResults; +import io.quarkus.test.ProdModeTestResults; +import io.quarkus.test.QuarkusProdModeTest; + +public class ExternalCRDsTest { + + private static final String APP = "reconciler-with-external-crds"; + @RegisterExtension + static final QuarkusProdModeTest config = new QuarkusProdModeTest() + .setApplicationName(APP) + .withApplicationRoot((jar) -> jar + .addClasses(First.class, External.class, ExternalDependentResource.class, + ReconcilerWithExternalCR.class)) + .overrideConfigKey("quarkus.operator-sdk.bundle.external-crd-locations", + "src/test/external-crds/v1beta1spec.crd.yml, src/test/external-crds/external.crd.yml"); + + @ProdBuildResults + private ProdModeTestResults prodModeTestResults; + + @Test + public void shouldProcessExternalCRDsWhenPresentAndOutputWarningsAsNeeded() throws IOException { + final var bundle = prodModeTestResults.getBuildDir().resolve(BUNDLE); + assertTrue(Files.exists(bundle.resolve(APP))); + final var manifests = bundle.resolve(APP).resolve("manifests"); + assertFileExistsIn(manifests.resolve(getCRDNameFor(External.class)), manifests); + assertFileExistsIn(manifests.resolve(getCRDNameFor(V1Beta1CRD.class)), manifests); + + final var csv = getCSVFor(bundle, APP); + final var externalCRD = csv.getSpec().getCustomresourcedefinitions().getRequired().get(0); + assertEquals(HasMetadata.getFullResourceName(External.class), externalCRD.getName()); + final var v1beta1 = csv.getSpec().getCustomresourcedefinitions().getRequired().get(1); + assertEquals(HasMetadata.getFullResourceName(V1Beta1CRD.class), v1beta1.getName()); + } + +} 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 fd3f921b..49910b88 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 @@ -98,11 +98,11 @@ public void shouldWriteBundleForTheOperators() throws IOException { assertEquals(Third.DISPLAY, thirdCRD.getDisplayName()); assertEquals(Third.DESCRIPTION, thirdCRD.getDescription()); // CRDs should be alphabetically ordered - final var externalCRD = crds.getRequired().get(0); + final var externalCRD = crds.getRequired().get(1); assertEquals(HasMetadata.getFullResourceName(External.class), externalCRD.getName()); assertEquals(External.DISPLAY_NAME, externalCRD.getDisplayName()); assertEquals(External.DESCRIPTION, externalCRD.getDescription()); - assertEquals(HasMetadata.getFullResourceName(SecondExternal.class), crds.getRequired().get(1).getName()); + assertEquals(HasMetadata.getFullResourceName(SecondExternal.class), crds.getRequired().get(0).getName()); // should list native APIs as well final var spec = csv.getSpec(); final var nativeAPIs = spec.getNativeAPIs(); diff --git a/bundle-generator/deployment/src/test/java/io/quarkiverse/operatorsdk/bundle/sources/ReconcilerWithExternalCR.java b/bundle-generator/deployment/src/test/java/io/quarkiverse/operatorsdk/bundle/sources/ReconcilerWithExternalCR.java new file mode 100644 index 00000000..57e32cb2 --- /dev/null +++ b/bundle-generator/deployment/src/test/java/io/quarkiverse/operatorsdk/bundle/sources/ReconcilerWithExternalCR.java @@ -0,0 +1,17 @@ +package io.quarkiverse.operatorsdk.bundle.sources; + +import io.javaoperatorsdk.operator.api.reconciler.Context; +import io.javaoperatorsdk.operator.api.reconciler.ControllerConfiguration; +import io.javaoperatorsdk.operator.api.reconciler.Reconciler; +import io.javaoperatorsdk.operator.api.reconciler.UpdateControl; +import io.javaoperatorsdk.operator.api.reconciler.dependent.Dependent; +import io.quarkiverse.operatorsdk.annotations.CSVMetadata; + +@ControllerConfiguration(dependents = @Dependent(type = ExternalDependentResource.class)) +@CSVMetadata(requiredCRDs = @CSVMetadata.RequiredCRD(kind = V1Beta1CRD.KIND, name = V1Beta1CRD.CR_NAME, version = V1Beta1CRD.VERSION)) +public class ReconcilerWithExternalCR implements Reconciler { + @Override + public UpdateControl reconcile(First first, Context context) throws Exception { + return null; + } +} diff --git a/bundle-generator/deployment/src/test/java/io/quarkiverse/operatorsdk/bundle/sources/V1Beta1CRD.java b/bundle-generator/deployment/src/test/java/io/quarkiverse/operatorsdk/bundle/sources/V1Beta1CRD.java new file mode 100644 index 00000000..b5b60ce5 --- /dev/null +++ b/bundle-generator/deployment/src/test/java/io/quarkiverse/operatorsdk/bundle/sources/V1Beta1CRD.java @@ -0,0 +1,20 @@ +package io.quarkiverse.operatorsdk.bundle.sources; + +import io.fabric8.kubernetes.client.CustomResource; +import io.fabric8.kubernetes.model.annotation.Group; +import io.fabric8.kubernetes.model.annotation.Kind; +import io.fabric8.kubernetes.model.annotation.Version; + +@Group(V1Beta1CRD.GROUP) +@Version(V1Beta1CRD.VERSION) +@Kind(V1Beta1CRD.KIND) +public class V1Beta1CRD extends CustomResource { + + public static final String GROUP = "test.com"; + public static final String VERSION = "v2"; + public static final String KIND = "V1Beta1"; + public static final String CR_NAME = "v1beta1s." + GROUP; + + public record Spec(String value) { + } +} diff --git a/core/runtime/src/main/java/io/quarkiverse/operatorsdk/runtime/CRDInfos.java b/core/runtime/src/main/java/io/quarkiverse/operatorsdk/runtime/CRDInfos.java index 951a5814..482b3494 100644 --- a/core/runtime/src/main/java/io/quarkiverse/operatorsdk/runtime/CRDInfos.java +++ b/core/runtime/src/main/java/io/quarkiverse/operatorsdk/runtime/CRDInfos.java @@ -5,12 +5,8 @@ import java.util.function.Function; import java.util.stream.Collectors; -import io.fabric8.kubernetes.api.model.HasMetadata; -import io.fabric8.kubernetes.api.model.apiextensions.v1.CustomResourceDefinition; - public class CRDInfos { private final Map> infos; - private final static String CRD_SPEC_VERSION = HasMetadata.getVersion(CustomResourceDefinition.class); public CRDInfos() { this(new HashMap<>()); @@ -33,7 +29,7 @@ public Map getCRDNameToInfoMappings() { .values().stream() // only keep CRD v1 infos .flatMap(entry -> entry.values().stream() - .filter(crdInfo -> CRD_SPEC_VERSION.equals(crdInfo.getCrdSpecVersion()))) + .filter(crdInfo -> CRDUtils.DEFAULT_CRD_SPEC_VERSION.equals(crdInfo.getCrdSpecVersion()))) .collect(Collectors.toMap(CRDInfo::getCrdName, Function.identity())); } @@ -41,7 +37,7 @@ public Map> getExisting() { return infos; } - public void addCRDInfoFor(String crdName, String version, CRDInfo crdInfo) { - getOrCreateCRDSpecVersionToInfoMapping(crdName).put(version, crdInfo); + public void addCRDInfoFor(String crdName, String crdSpecVersion, CRDInfo crdInfo) { + getOrCreateCRDSpecVersionToInfoMapping(crdName).put(crdSpecVersion, crdInfo); } } diff --git a/core/runtime/src/main/java/io/quarkiverse/operatorsdk/runtime/CRDUtils.java b/core/runtime/src/main/java/io/quarkiverse/operatorsdk/runtime/CRDUtils.java index ee52b60c..bffba9eb 100644 --- a/core/runtime/src/main/java/io/quarkiverse/operatorsdk/runtime/CRDUtils.java +++ b/core/runtime/src/main/java/io/quarkiverse/operatorsdk/runtime/CRDUtils.java @@ -6,12 +6,18 @@ import org.jboss.logging.Logger; +import com.fasterxml.jackson.databind.ObjectMapper; + import io.fabric8.kubernetes.api.model.apiextensions.v1.CustomResourceDefinition; import io.fabric8.kubernetes.client.KubernetesClient; +import io.fabric8.kubernetes.client.utils.KubernetesSerialization; public final class CRDUtils { private static final Logger LOGGER = Logger.getLogger(CRDUtils.class.getName()); + private static final KubernetesSerialization SERIALIZATION = new KubernetesSerialization(new ObjectMapper(), false); + public final static String DEFAULT_CRD_SPEC_VERSION = "v1"; + public static final String V1BETA1_CRD_SPEC_VERSION = "v1beta1"; private CRDUtils() { } @@ -21,8 +27,7 @@ public static void applyCRD(KubernetesClient client, CRDGenerationInfo crdInfo, crdInfo.getCRDInfosFor(crdName).forEach((crdVersion, info) -> { final var filePath = Path.of(info.getFilePath()); try { - final var crd = client.getKubernetesSerialization() - .unmarshal(Files.newInputStream(filePath), getCRDClassFor(crdVersion)); + final var crd = loadFrom(filePath, client.getKubernetesSerialization(), getCRDClassFor(crdVersion)); apply(client, crdVersion, crd); LOGGER.infov("Applied {0} CRD named ''{1}'' from {2}", crdVersion, crdName, filePath); } catch (IOException ex) { @@ -35,9 +40,25 @@ public static void applyCRD(KubernetesClient client, CRDGenerationInfo crdInfo, } } + private static T loadFrom(Path crdPath, KubernetesSerialization serialization, Class crdClass) throws IOException { + serialization = serialization == null ? SERIALIZATION : serialization; + return serialization.unmarshal(Files.newInputStream(crdPath), crdClass); + } + + public static CustomResourceDefinition loadFrom(Path crdPath) throws IOException { + final var crd = loadFrom(crdPath, null, CustomResourceDefinition.class); + final var crdVersion = crd.getApiVersion().split("/")[1]; + if (!DEFAULT_CRD_SPEC_VERSION.equals(crdVersion)) { + LOGGER.warnv( + "CRD at {0} was loaded as a {1} CRD but is defined as using {2} CRD spec version. While things might still work as expected, we recommend that you only use CRDs using the {1} CRD spec version.", + crdPath, DEFAULT_CRD_SPEC_VERSION, crdVersion); + } + return crd; + } + private static void apply(KubernetesClient client, String v, Object crd) { switch (v) { - case "v1": + case DEFAULT_CRD_SPEC_VERSION: final var resource = client.apiextensions().v1().customResourceDefinitions() .resource((CustomResourceDefinition) crd); if (resource.get() != null) { @@ -46,7 +67,7 @@ private static void apply(KubernetesClient client, String v, Object crd) { resource.create(); } break; - case "v1beta1": + case V1BETA1_CRD_SPEC_VERSION: final var legacyResource = client.apiextensions().v1beta1().customResourceDefinitions() .resource((io.fabric8.kubernetes.api.model.apiextensions.v1beta1.CustomResourceDefinition) crd); if (legacyResource.get() != null) { @@ -62,8 +83,9 @@ private static void apply(KubernetesClient client, String v, Object crd) { private static Class getCRDClassFor(String v) { return switch (v) { - case "v1" -> CustomResourceDefinition.class; - case "v1beta1" -> io.fabric8.kubernetes.api.model.apiextensions.v1beta1.CustomResourceDefinition.class; + case DEFAULT_CRD_SPEC_VERSION -> CustomResourceDefinition.class; + case V1BETA1_CRD_SPEC_VERSION -> + io.fabric8.kubernetes.api.model.apiextensions.v1beta1.CustomResourceDefinition.class; default -> throw new IllegalArgumentException("Unknown CRD version: " + v); }; }