diff --git a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/GroupVersionKind.java b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/GroupVersionKind.java index 9e5cbea14d..1f51a2d282 100644 --- a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/GroupVersionKind.java +++ b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/GroupVersionKind.java @@ -1,6 +1,8 @@ package io.javaoperatorsdk.operator.processing; +import java.util.Map; import java.util.Objects; +import java.util.concurrent.ConcurrentHashMap; import io.fabric8.kubernetes.api.model.HasMetadata; @@ -8,6 +10,9 @@ public class GroupVersionKind { private final String group; private final String version; private final String kind; + private final String apiVersion; + protected final static Map, GroupVersionKind> CACHE = + new ConcurrentHashMap<>(); public GroupVersionKind(String apiVersion, String kind) { this.kind = kind; @@ -19,17 +24,23 @@ public GroupVersionKind(String apiVersion, String kind) { this.group = groupAndVersion[0]; this.version = groupAndVersion[1]; } + this.apiVersion = apiVersion; + } + + public static GroupVersionKind gvkFor(Class resourceClass) { + return CACHE.computeIfAbsent(resourceClass, GroupVersionKind::computeGVK); + } + + private static GroupVersionKind computeGVK(Class rc) { + return new GroupVersionKind(HasMetadata.getGroup(rc), + HasMetadata.getVersion(rc), HasMetadata.getKind(rc)); } public GroupVersionKind(String group, String version, String kind) { this.group = group; this.version = version; this.kind = kind; - } - - public static GroupVersionKind gvkFor(Class resourceClass) { - return new GroupVersionKind(HasMetadata.getGroup(resourceClass), - HasMetadata.getVersion(resourceClass), HasMetadata.getKind(resourceClass)); + this.apiVersion = (group == null || group.isBlank()) ? version : group + "/" + version; } public String getGroup() { @@ -45,7 +56,7 @@ public String getKind() { } public String apiVersion() { - return group == null || group.isBlank() ? version : group + "/" + version; + return apiVersion; } @Override @@ -55,20 +66,18 @@ public boolean equals(Object o) { if (o == null || getClass() != o.getClass()) return false; GroupVersionKind that = (GroupVersionKind) o; - return Objects.equals(group, that.group) && Objects.equals(version, that.version) - && Objects.equals(kind, that.kind); + return Objects.equals(apiVersion, that.apiVersion) && Objects.equals(kind, that.kind); } @Override public int hashCode() { - return Objects.hash(group, version, kind); + return Objects.hash(apiVersion, kind); } @Override public String toString() { return "GroupVersionKind{" + - "group='" + group + '\'' + - ", version='" + version + '\'' + + "apiVersion='" + apiVersion + '\'' + ", kind='" + kind + '\'' + '}'; } diff --git a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/kubernetes/GenericKubernetesDependentResource.java b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/kubernetes/GenericKubernetesDependentResource.java index 57d3036804..266872bd38 100644 --- a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/kubernetes/GenericKubernetesDependentResource.java +++ b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/kubernetes/GenericKubernetesDependentResource.java @@ -8,9 +8,13 @@ public class GenericKubernetesDependentResource

extends KubernetesDependentResource { - private final GroupVersionKind groupVersionKind; + private final GroupVersionKindPlural groupVersionKind; public GenericKubernetesDependentResource(GroupVersionKind groupVersionKind) { + this(GroupVersionKindPlural.from(groupVersionKind)); + } + + public GenericKubernetesDependentResource(GroupVersionKindPlural groupVersionKind) { super(GenericKubernetesResource.class); this.groupVersionKind = groupVersionKind; } @@ -20,7 +24,7 @@ protected InformerConfiguration.InformerConfigurationBuilder resourceClass) { + final var gvk = GroupVersionKind.gvkFor(resourceClass); + return gvkWithPlural(gvk, HasMetadata.getPlural(resourceClass)); + } + + /** + * Retrieves the default plural form for the specified kind. + * + * @param kind the kind for which we want to get the default plural form + * @return the default plural form for the specified kind + */ + public static String getDefaultPluralFor(String kind) { + // todo: replace by Fabric8 version when available, see + // https://github.com/fabric8io/kubernetes-client/pull/6314 + return kind != null ? Pluralize.toPlural(kind.toLowerCase()) : null; + } + + /** + * Returns the plural form associated with the kind if it has been provided explicitly (either + * manually by the user, or determined from the associated resource class definition) + * + * @return {@link Optional#empty()} if the plural form was not provided explicitly, or the plural + * form if it was provided explicitly + */ + public Optional getPlural() { + return Optional.ofNullable(plural); + } + + /** + * Returns the plural form associated with the kind if it was provided or a default, computed form + * via {@link #getDefaultPluralFor(String)} (which should correspond to the actual plural form in + * most cases but might not always be correct, especially if the resource's creator defined an + * exotic plural form via the CRD. + * + * @return the plural form associated with the kind if provided or a default plural form otherwise + */ + @SuppressWarnings("unused") + public String getPluralOrDefault() { + return getPlural().orElse(getDefaultPluralFor(getKind())); + } +} diff --git a/operator-framework-core/src/test/java/io/javaoperatorsdk/operator/processing/GroupVersionKindTest.java b/operator-framework-core/src/test/java/io/javaoperatorsdk/operator/processing/GroupVersionKindTest.java index ad69cae800..a20233a6f0 100644 --- a/operator-framework-core/src/test/java/io/javaoperatorsdk/operator/processing/GroupVersionKindTest.java +++ b/operator-framework-core/src/test/java/io/javaoperatorsdk/operator/processing/GroupVersionKindTest.java @@ -2,8 +2,11 @@ import org.junit.jupiter.api.Test; +import io.fabric8.kubernetes.api.model.ConfigMap; +import io.fabric8.kubernetes.api.model.HasMetadata; +import io.javaoperatorsdk.operator.processing.dependent.kubernetes.GroupVersionKindPlural; + import static org.assertj.core.api.Assertions.assertThat; -import static org.junit.jupiter.api.Assertions.*; class GroupVersionKindTest { @@ -18,4 +21,39 @@ void testInitFromApiVersion() { assertThat(gvk.getVersion()).isEqualTo("v1"); } + @Test + void pluralShouldOnlyBeProvidedIfExplicitlySet() { + final var kind = "ConfigMap"; + var gvk = GroupVersionKindPlural.from(new GroupVersionKind("v1", kind)); + assertThat(gvk.getPlural()).isEmpty(); + assertThat(gvk.getPluralOrDefault()) + .isEqualTo(GroupVersionKindPlural.getDefaultPluralFor(kind)); + + gvk = GroupVersionKindPlural.from(GroupVersionKind.gvkFor(ConfigMap.class)); + assertThat(gvk.getPlural()).isEmpty(); + assertThat(gvk.getPluralOrDefault()).isEqualTo(HasMetadata.getPlural(ConfigMap.class)); + + gvk = GroupVersionKindPlural.gvkFor(ConfigMap.class); + assertThat(gvk.getPlural()).hasValue(HasMetadata.getPlural(ConfigMap.class)); + + gvk = GroupVersionKindPlural.from(gvk); + assertThat(gvk.getPlural()).hasValue(HasMetadata.getPlural(ConfigMap.class)); + } + + @Test + void pluralShouldBeEmptyIfNotProvided() { + final var kind = "MyKind"; + var gvk = + GroupVersionKindPlural.gvkWithPlural(new GroupVersionKind("josdk.io", "v1", kind), null); + assertThat(gvk.getPlural()).isEmpty(); + assertThat(gvk.getPluralOrDefault()) + .isEqualTo(GroupVersionKindPlural.getDefaultPluralFor(kind)); + } + + @Test + void pluralShouldOverrideDefaultComputedVersionIfProvided() { + var gvk = GroupVersionKindPlural.gvkWithPlural(new GroupVersionKind("josdk.io", "v1", "MyKind"), + "MyPlural"); + assertThat(gvk.getPlural()).hasValue("MyPlural"); + } } diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/dynamicgenericeventsourceregistration/DynamicGenericEventSourceRegistrationReconciler.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/dynamicgenericeventsourceregistration/DynamicGenericEventSourceRegistrationReconciler.java index 53dc3578bb..a1bc4a80f8 100644 --- a/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/dynamicgenericeventsourceregistration/DynamicGenericEventSourceRegistrationReconciler.java +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/dynamicgenericeventsourceregistration/DynamicGenericEventSourceRegistrationReconciler.java @@ -66,16 +66,12 @@ private ConfigMap configMap(DynamicGenericEventSourceRegistrationCustomResource return cm; } - private GroupVersionKind gvkFor(Class clazz) { - return new GroupVersionKind(HasMetadata.getApiVersion(clazz), HasMetadata.getKind(clazz)); - } - private InformerEventSource genericInformerFor( Class clazz, Context context) { return new InformerEventSource<>( - InformerConfiguration.from(gvkFor(clazz), + InformerConfiguration.from(GroupVersionKind.gvkFor(clazz), context.eventSourceRetriever().eventSourceContextForDynamicRegistration()).build(), context.eventSourceRetriever().eventSourceContextForDynamicRegistration()); }