From 930f2a3fd3e4c67ea8e20c3b9804fc644a9b89df Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bernhard=20Str=C3=A4hle?= Date: Sat, 30 Nov 2024 21:29:40 +0100 Subject: [PATCH 1/2] Fix processing private and package-private CustomResource classes --- .../crdv2/generator/CustomResourceInfo.java | 36 +++++++++++++++++- ...tecustomresources.sample.fabric8.io-v1.yml | 24 ++++++++++++ ...ercustomresources.sample.fabric8.io-v1.yml | 24 ++++++++++++ ...ercustomresources.sample.fabric8.io-v1.yml | 24 ++++++++++++ ...ercustomresources.sample.fabric8.io-v1.yml | 24 ++++++++++++ .../crd/maven/example/InnerResources.java | 38 +++++++++++++++++++ .../example/PackagePrivateCustomResource.java | 25 ++++++++++++ .../src/it/project-scan/verify.groovy | 6 ++- 8 files changed, 198 insertions(+), 3 deletions(-) create mode 100644 crd-generator/maven-plugin/src/it/project-scan/expected/packageprivatecustomresources.sample.fabric8.io-v1.yml create mode 100644 crd-generator/maven-plugin/src/it/project-scan/expected/packageprivateinnercustomresources.sample.fabric8.io-v1.yml create mode 100644 crd-generator/maven-plugin/src/it/project-scan/expected/privateinnercustomresources.sample.fabric8.io-v1.yml create mode 100644 crd-generator/maven-plugin/src/it/project-scan/expected/publicinnercustomresources.sample.fabric8.io-v1.yml create mode 100644 crd-generator/maven-plugin/src/it/project-scan/src/main/java/io/fabric8/crd/maven/example/InnerResources.java create mode 100644 crd-generator/maven-plugin/src/it/project-scan/src/main/java/io/fabric8/crd/maven/example/PackagePrivateCustomResource.java diff --git a/crd-generator/api-v2/src/main/java/io/fabric8/crdv2/generator/CustomResourceInfo.java b/crd-generator/api-v2/src/main/java/io/fabric8/crdv2/generator/CustomResourceInfo.java index 2c5eee7a8dc..baa816ea5a1 100644 --- a/crd-generator/api-v2/src/main/java/io/fabric8/crdv2/generator/CustomResourceInfo.java +++ b/crd-generator/api-v2/src/main/java/io/fabric8/crdv2/generator/CustomResourceInfo.java @@ -25,7 +25,10 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import java.lang.reflect.Constructor; import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Modifier; +import java.util.Arrays; import java.util.Map; import java.util.Objects; import java.util.Optional; @@ -165,7 +168,7 @@ public String[] labels() { public static CustomResourceInfo fromClass(Class customResource) { try { - final HasMetadata instance = customResource.getDeclaredConstructor().newInstance(); + final HasMetadata instance = createInstance(customResource); final String[] shortNames = CustomResource.getShortNames(customResource); final String[] categories = CustomResource.getCategories(customResource); @@ -203,11 +206,40 @@ public static CustomResourceInfo fromClass(Class customRe customResource.getCanonicalName(), specAndStatus.getSpecClassName(), specAndStatus.getStatusClassName(), toStringArray(instance.getMetadata().getAnnotations()), toStringArray(instance.getMetadata().getLabels())); - } catch (InstantiationException | IllegalAccessException | NoSuchMethodException | InvocationTargetException e) { + } catch (InstantiationException | IllegalAccessException | InvocationTargetException e) { throw KubernetesClientException.launderThrowable(e); } } + private static HasMetadata createInstance(Class customResource) + throws InvocationTargetException, InstantiationException, IllegalAccessException { + final Constructor defaultConstructor = Arrays.stream(customResource.getDeclaredConstructors()) + .filter(constructor -> constructor.getParameterCount() == 0) + .findFirst() + .orElseThrow(() -> new IllegalStateException( + "Cannot find default constructor for " + customResource.getCanonicalName())); + + if (Modifier.isPublic(customResource.getModifiers()) + && Modifier.isPublic(defaultConstructor.getModifiers())) { + return (HasMetadata) defaultConstructor.newInstance(); + } + + LOGGER.trace( + "Default constructor for CustomResource class {} is not accessible. Modifying accessibility...", + customResource.getCanonicalName()); + + boolean accessible = defaultConstructor.trySetAccessible(); + if (!accessible) { + LOGGER.warn( + "Default constructor for CustomResource class {} is not accessible.", + customResource.getCanonicalName()); + } else { + LOGGER.debug( + "Modified constructor for CustomResource class {} to make it accessible.", customResource.getCanonicalName()); + } + return (HasMetadata) defaultConstructor.newInstance(); + } + public static String[] toStringArray(Map map) { String[] res = new String[map.size()]; Set> entrySet = map.entrySet(); diff --git a/crd-generator/maven-plugin/src/it/project-scan/expected/packageprivatecustomresources.sample.fabric8.io-v1.yml b/crd-generator/maven-plugin/src/it/project-scan/expected/packageprivatecustomresources.sample.fabric8.io-v1.yml new file mode 100644 index 00000000000..a29c55fd761 --- /dev/null +++ b/crd-generator/maven-plugin/src/it/project-scan/expected/packageprivatecustomresources.sample.fabric8.io-v1.yml @@ -0,0 +1,24 @@ +# Generated by Fabric8 CRDGenerator, manual edits might get overwritten! +apiVersion: "apiextensions.k8s.io/v1" +kind: "CustomResourceDefinition" +metadata: + name: "packageprivatecustomresources.sample.fabric8.io" +spec: + group: "sample.fabric8.io" + names: + kind: "PackagePrivateCustomResource" + plural: "packageprivatecustomresources" + singular: "packageprivatecustomresource" + scope: "Cluster" + versions: + - name: "v1" + schema: + openAPIV3Schema: + properties: + spec: + type: "object" + status: + type: "object" + type: "object" + served: true + storage: true diff --git a/crd-generator/maven-plugin/src/it/project-scan/expected/packageprivateinnercustomresources.sample.fabric8.io-v1.yml b/crd-generator/maven-plugin/src/it/project-scan/expected/packageprivateinnercustomresources.sample.fabric8.io-v1.yml new file mode 100644 index 00000000000..5673b5bbc77 --- /dev/null +++ b/crd-generator/maven-plugin/src/it/project-scan/expected/packageprivateinnercustomresources.sample.fabric8.io-v1.yml @@ -0,0 +1,24 @@ +# Generated by Fabric8 CRDGenerator, manual edits might get overwritten! +apiVersion: "apiextensions.k8s.io/v1" +kind: "CustomResourceDefinition" +metadata: + name: "packageprivateinnercustomresources.sample.fabric8.io" +spec: + group: "sample.fabric8.io" + names: + kind: "PackagePrivateInnerCustomResource" + plural: "packageprivateinnercustomresources" + singular: "packageprivateinnercustomresource" + scope: "Cluster" + versions: + - name: "v1" + schema: + openAPIV3Schema: + properties: + spec: + type: "object" + status: + type: "object" + type: "object" + served: true + storage: true diff --git a/crd-generator/maven-plugin/src/it/project-scan/expected/privateinnercustomresources.sample.fabric8.io-v1.yml b/crd-generator/maven-plugin/src/it/project-scan/expected/privateinnercustomresources.sample.fabric8.io-v1.yml new file mode 100644 index 00000000000..486c87ff669 --- /dev/null +++ b/crd-generator/maven-plugin/src/it/project-scan/expected/privateinnercustomresources.sample.fabric8.io-v1.yml @@ -0,0 +1,24 @@ +# Generated by Fabric8 CRDGenerator, manual edits might get overwritten! +apiVersion: "apiextensions.k8s.io/v1" +kind: "CustomResourceDefinition" +metadata: + name: "privateinnercustomresources.sample.fabric8.io" +spec: + group: "sample.fabric8.io" + names: + kind: "PrivateInnerCustomResource" + plural: "privateinnercustomresources" + singular: "privateinnercustomresource" + scope: "Cluster" + versions: + - name: "v1" + schema: + openAPIV3Schema: + properties: + spec: + type: "object" + status: + type: "object" + type: "object" + served: true + storage: true diff --git a/crd-generator/maven-plugin/src/it/project-scan/expected/publicinnercustomresources.sample.fabric8.io-v1.yml b/crd-generator/maven-plugin/src/it/project-scan/expected/publicinnercustomresources.sample.fabric8.io-v1.yml new file mode 100644 index 00000000000..d5da9cdd739 --- /dev/null +++ b/crd-generator/maven-plugin/src/it/project-scan/expected/publicinnercustomresources.sample.fabric8.io-v1.yml @@ -0,0 +1,24 @@ +# Generated by Fabric8 CRDGenerator, manual edits might get overwritten! +apiVersion: "apiextensions.k8s.io/v1" +kind: "CustomResourceDefinition" +metadata: + name: "publicinnercustomresources.sample.fabric8.io" +spec: + group: "sample.fabric8.io" + names: + kind: "PublicInnerCustomResource" + plural: "publicinnercustomresources" + singular: "publicinnercustomresource" + scope: "Cluster" + versions: + - name: "v1" + schema: + openAPIV3Schema: + properties: + spec: + type: "object" + status: + type: "object" + type: "object" + served: true + storage: true diff --git a/crd-generator/maven-plugin/src/it/project-scan/src/main/java/io/fabric8/crd/maven/example/InnerResources.java b/crd-generator/maven-plugin/src/it/project-scan/src/main/java/io/fabric8/crd/maven/example/InnerResources.java new file mode 100644 index 00000000000..092a277092a --- /dev/null +++ b/crd-generator/maven-plugin/src/it/project-scan/src/main/java/io/fabric8/crd/maven/example/InnerResources.java @@ -0,0 +1,38 @@ +/* + * Copyright (C) 2015 Red Hat, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package io.fabric8.crd.maven.example; + +import io.fabric8.kubernetes.client.CustomResource; +import io.fabric8.kubernetes.model.annotation.Group; +import io.fabric8.kubernetes.model.annotation.Version; + +public class InnerResources { + + @Group("sample.fabric8.io") + @Version(value = "v1") + public static class PublicInnerCustomResource extends CustomResource { + } + + @Group("sample.fabric8.io") + @Version(value = "v1") + static class PackagePrivateInnerCustomResource extends CustomResource { + } + + @Group("sample.fabric8.io") + @Version(value = "v1") + private static class PrivateInnerCustomResource extends CustomResource { + } +} diff --git a/crd-generator/maven-plugin/src/it/project-scan/src/main/java/io/fabric8/crd/maven/example/PackagePrivateCustomResource.java b/crd-generator/maven-plugin/src/it/project-scan/src/main/java/io/fabric8/crd/maven/example/PackagePrivateCustomResource.java new file mode 100644 index 00000000000..b309b3174b8 --- /dev/null +++ b/crd-generator/maven-plugin/src/it/project-scan/src/main/java/io/fabric8/crd/maven/example/PackagePrivateCustomResource.java @@ -0,0 +1,25 @@ +/* + * Copyright (C) 2015 Red Hat, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package io.fabric8.crd.maven.example; + +import io.fabric8.kubernetes.client.CustomResource; +import io.fabric8.kubernetes.model.annotation.Group; +import io.fabric8.kubernetes.model.annotation.Version; + +@Group("sample.fabric8.io") +@Version(value = "v1") +class PackagePrivateCustomResource extends CustomResource { +} diff --git a/crd-generator/maven-plugin/src/it/project-scan/verify.groovy b/crd-generator/maven-plugin/src/it/project-scan/verify.groovy index ecee0776a70..885fb9c65cd 100644 --- a/crd-generator/maven-plugin/src/it/project-scan/verify.groovy +++ b/crd-generator/maven-plugin/src/it/project-scan/verify.groovy @@ -21,7 +21,11 @@ Path basedirPath = basedir.toPath(); [ - "multiples.sample.fabric8.io-v1" + "multiples.sample.fabric8.io-v1", + "packageprivatecustomresources.sample.fabric8.io-v1", + "packageprivateinnercustomresources.sample.fabric8.io-v1", + "privateinnercustomresources.sample.fabric8.io-v1", + "publicinnercustomresources.sample.fabric8.io-v1" ].each { Verify.verifyContentEquals( basedirPath.resolve(Paths.get("target", "classes", "META-INF", "fabric8", it + ".yml")), From 20588b09fd0893081911c62e8a97aa2bbff2fbc1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bernhard=20Str=C3=A4hle?= Date: Sun, 1 Dec 2024 11:26:19 +0100 Subject: [PATCH 2/2] Add tests to CustomResourceInfoTest --- .../generator/CustomResourceInfoTest.java | 45 ++++++++++++++++++- 1 file changed, 43 insertions(+), 2 deletions(-) diff --git a/crd-generator/api-v2/src/test/java/io/fabric8/crdv2/generator/CustomResourceInfoTest.java b/crd-generator/api-v2/src/test/java/io/fabric8/crdv2/generator/CustomResourceInfoTest.java index d7d908b95cd..ad441c0e1ce 100644 --- a/crd-generator/api-v2/src/test/java/io/fabric8/crdv2/generator/CustomResourceInfoTest.java +++ b/crd-generator/api-v2/src/test/java/io/fabric8/crdv2/generator/CustomResourceInfoTest.java @@ -27,6 +27,8 @@ import static org.junit.jupiter.api.Assertions.assertArrayEquals; import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertThrows; import static org.junit.jupiter.api.Assertions.assertTrue; public class CustomResourceInfoTest { @@ -44,12 +46,33 @@ public static class Status { @Version(VERSION) @ShortNames("s") @Categories("cat") - public static class ClusteredCR extends CustomResource { + public static class ClusteredCR extends + io.fabric8.kubernetes.client.CustomResource { } @Group(GROUP) @Version(VERSION) - public static class NamespacedCR extends CustomResource implements Namespaced { + public static class NamespacedCR extends io.fabric8.kubernetes.client.CustomResource + implements Namespaced { + } + + @Group(GROUP) + @Version(VERSION) + static class PackagePrivateInnerCustomResource extends + io.fabric8.kubernetes.client.CustomResource { + } + + @Group(GROUP) + @Version(VERSION) + private static class PrivateInnerCustomResource extends + io.fabric8.kubernetes.client.CustomResource { + } + + @Group(GROUP) + @Version(VERSION) + private static class NoDefaultConstructorCustomResource extends CustomResource { + NoDefaultConstructorCustomResource(String s) { + } } @Test @@ -92,4 +115,22 @@ void shouldProperlyCreateCustomResourceInfo() { assertTrue(info.storage()); assertEquals(HasMetadata.getKind(ClusteredCR.class), info.kind()); } + + @Test + void shouldCreateCustomResourceInfoFromPackagePrivateClass() { + CustomResourceInfo info = CustomResourceInfo.fromClass(PackagePrivateInnerCustomResource.class); + assertNotNull(info); + } + + @Test + void shouldCreateCustomResourceInfoFromPrivateClass() { + CustomResourceInfo info = CustomResourceInfo.fromClass(PrivateInnerCustomResource.class); + assertNotNull(info); + } + + @Test + void shouldFailForMissingDefaultConstructor() { + assertThrows(IllegalStateException.class, + () -> CustomResourceInfo.fromClass(NoDefaultConstructorCustomResource.class)); + } }