From 18e337d619f6b1d2b860e5fd5b23a919c46f1259 Mon Sep 17 00:00:00 2001 From: Chris Laprun Date: Fri, 23 Aug 2024 18:49:36 +0200 Subject: [PATCH 1/4] feat: add getPlural method on GroupVersionKind Signed-off-by: Chris Laprun --- .../operator/processing/GroupVersionKind.java | 25 +++++++++++++++---- ...ericEventSourceRegistrationReconciler.java | 6 +---- .../ConfigMapGenericKubernetesDependent.java | 3 ++- .../ConfigMapGenericKubernetesDependent.java | 3 ++- 4 files changed, 25 insertions(+), 12 deletions(-) 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..93a7395f8d 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 @@ -2,12 +2,15 @@ import java.util.Objects; +import io.fabric8.kubernetes.api.Pluralize; import io.fabric8.kubernetes.api.model.HasMetadata; public class GroupVersionKind { private final String group; private final String version; private final String kind; + private final String plural; + private final String apiVersion; public GroupVersionKind(String apiVersion, String kind) { this.kind = kind; @@ -19,17 +22,26 @@ public GroupVersionKind(String apiVersion, String kind) { this.group = groupAndVersion[0]; this.version = groupAndVersion[1]; } + this.plural = Pluralize.toPlural(kind); + this.apiVersion = apiVersion; } public GroupVersionKind(String group, String version, String kind) { + this(group, version, kind, null); + } + + public GroupVersionKind(String group, String version, String kind, String plural) { this.group = group; this.version = version; this.kind = kind; + this.plural = plural != null ? plural : Pluralize.toPlural(kind); + this.apiVersion = (group == null || group.isBlank()) ? version : group + "/" + version; } public static GroupVersionKind gvkFor(Class resourceClass) { return new GroupVersionKind(HasMetadata.getGroup(resourceClass), - HasMetadata.getVersion(resourceClass), HasMetadata.getKind(resourceClass)); + HasMetadata.getVersion(resourceClass), HasMetadata.getKind(resourceClass), + HasMetadata.getPlural(resourceClass)); } public String getGroup() { @@ -44,8 +56,12 @@ public String getKind() { return kind; } + public String getPlural() { + return plural; + } + public String apiVersion() { - return group == null || group.isBlank() ? version : group + "/" + version; + return apiVersion; } @Override @@ -55,13 +71,12 @@ 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 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()); } diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/generickubernetesresource/generickubernetesdependentresourcemanaged/ConfigMapGenericKubernetesDependent.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/generickubernetesresource/generickubernetesdependentresourcemanaged/ConfigMapGenericKubernetesDependent.java index d5ab470cfb..5d9dfd35da 100644 --- a/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/generickubernetesresource/generickubernetesdependentresourcemanaged/ConfigMapGenericKubernetesDependent.java +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/generickubernetesresource/generickubernetesdependentresourcemanaged/ConfigMapGenericKubernetesDependent.java @@ -4,6 +4,7 @@ import java.io.InputStream; import java.util.Map; +import io.fabric8.kubernetes.api.model.ConfigMap; import io.fabric8.kubernetes.api.model.GenericKubernetesResource; import io.javaoperatorsdk.operator.api.reconciler.Context; import io.javaoperatorsdk.operator.api.reconciler.dependent.GarbageCollected; @@ -24,7 +25,7 @@ public class ConfigMapGenericKubernetesDependent extends public static final String KEY = "key"; public ConfigMapGenericKubernetesDependent() { - super(new GroupVersionKind("", VERSION, KIND)); + super(GroupVersionKind.gvkFor(ConfigMap.class)); } @Override diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/generickubernetesresource/generickubernetesdependentstandalone/ConfigMapGenericKubernetesDependent.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/generickubernetesresource/generickubernetesdependentstandalone/ConfigMapGenericKubernetesDependent.java index 4efc968ef0..47ea8b0605 100644 --- a/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/generickubernetesresource/generickubernetesdependentstandalone/ConfigMapGenericKubernetesDependent.java +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/generickubernetesresource/generickubernetesdependentstandalone/ConfigMapGenericKubernetesDependent.java @@ -4,6 +4,7 @@ import java.io.InputStream; import java.util.Map; +import io.fabric8.kubernetes.api.model.ConfigMap; import io.fabric8.kubernetes.api.model.GenericKubernetesResource; import io.javaoperatorsdk.operator.api.reconciler.Context; import io.javaoperatorsdk.operator.api.reconciler.dependent.GarbageCollected; @@ -24,7 +25,7 @@ public class ConfigMapGenericKubernetesDependent extends public static final String KEY = "key"; public ConfigMapGenericKubernetesDependent() { - super(new GroupVersionKind("", VERSION, KIND)); + super(GroupVersionKind.gvkFor(ConfigMap.class)); } @Override From 313a5f0573e122396483a257fa7600fa15e3fdfd Mon Sep 17 00:00:00 2001 From: Chris Laprun Date: Mon, 26 Aug 2024 13:32:35 +0200 Subject: [PATCH 2/4] fix: make getPlural return Optional to show whether plural is known Signed-off-by: Chris Laprun --- .../operator/processing/GroupVersionKind.java | 17 +++++++++++++---- .../processing/GroupVersionKindTest.java | 12 ++++++++++++ 2 files changed, 25 insertions(+), 4 deletions(-) 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 93a7395f8d..5a3fdddae6 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,7 @@ package io.javaoperatorsdk.operator.processing; import java.util.Objects; +import java.util.Optional; import io.fabric8.kubernetes.api.Pluralize; import io.fabric8.kubernetes.api.model.HasMetadata; @@ -22,7 +23,7 @@ public GroupVersionKind(String apiVersion, String kind) { this.group = groupAndVersion[0]; this.version = groupAndVersion[1]; } - this.plural = Pluralize.toPlural(kind); + this.plural = null; this.apiVersion = apiVersion; } @@ -34,7 +35,7 @@ public GroupVersionKind(String group, String version, String kind, String plural this.group = group; this.version = version; this.kind = kind; - this.plural = plural != null ? plural : Pluralize.toPlural(kind); + this.plural = plural; this.apiVersion = (group == null || group.isBlank()) ? version : group + "/" + version; } @@ -56,8 +57,16 @@ public String getKind() { return kind; } - public String getPlural() { - return plural; + /** + * 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 (in which case, + * it could be approximated by using {@link Pluralize#toPlural(String)} on the kind), or + * the plural form if it was provided explicitly + */ + public Optional getPlural() { + return Optional.ofNullable(plural); } public String apiVersion() { 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..14bb7d7251 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,6 +2,9 @@ import org.junit.jupiter.api.Test; +import io.fabric8.kubernetes.api.model.ConfigMap; +import io.fabric8.kubernetes.api.model.HasMetadata; + import static org.assertj.core.api.Assertions.assertThat; import static org.junit.jupiter.api.Assertions.*; @@ -18,4 +21,13 @@ void testInitFromApiVersion() { assertThat(gvk.getVersion()).isEqualTo("v1"); } + @Test + void pluralShouldOnlyBeProvidedIfExplicitlySet() { + var gvk = new GroupVersionKind("v1", "ConfigMap"); + assertThat(gvk.getPlural()).isEmpty(); + + gvk = GroupVersionKind.gvkFor(ConfigMap.class); + assertThat(gvk.getPlural()).hasValue(HasMetadata.getPlural(ConfigMap.class)); + } + } From 240fdbaab290c87076daeb4de3e17e6ba432c929 Mon Sep 17 00:00:00 2001 From: Chris Laprun Date: Wed, 28 Aug 2024 14:05:26 +0200 Subject: [PATCH 3/4] refactor: add GroupVersionKindPlural class, used by GenericKubernetesDependentResource Signed-off-by: Chris Laprun --- .../operator/processing/GroupVersionKind.java | 41 +++++-------- .../GenericKubernetesDependentResource.java | 8 ++- .../kubernetes/GroupVersionKindPlural.java | 58 +++++++++++++++++++ .../processing/GroupVersionKindTest.java | 11 ++-- 4 files changed, 81 insertions(+), 37 deletions(-) create mode 100644 operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/kubernetes/GroupVersionKindPlural.java 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 5a3fdddae6..07e90d3c4b 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,18 +1,20 @@ package io.javaoperatorsdk.operator.processing; import java.util.Objects; -import java.util.Optional; -import io.fabric8.kubernetes.api.Pluralize; import io.fabric8.kubernetes.api.model.HasMetadata; +import io.javaoperatorsdk.operator.processing.dependent.kubernetes.GroupVersionKindPlural; public class GroupVersionKind { private final String group; private final String version; private final String kind; - private final String plural; private final String apiVersion; + /** + * @deprecated Use {@link GroupVersionKindPlural#gvkFor(String, String)} instead + */ + @Deprecated(forRemoval = true) public GroupVersionKind(String apiVersion, String kind) { this.kind = kind; String[] groupAndVersion = apiVersion.split("/"); @@ -23,28 +25,24 @@ public GroupVersionKind(String apiVersion, String kind) { this.group = groupAndVersion[0]; this.version = groupAndVersion[1]; } - this.plural = null; this.apiVersion = apiVersion; } - public GroupVersionKind(String group, String version, String kind) { - this(group, version, kind, null); + public static GroupVersionKind gvkFor(Class resourceClass) { + return GroupVersionKindPlural.gvkFor(resourceClass); } - public GroupVersionKind(String group, String version, String kind, String plural) { + /** + * @deprecated Use {@link GroupVersionKindPlural#gvkFor(String, String, String)} instead + */ + @Deprecated(forRemoval = true) + public GroupVersionKind(String group, String version, String kind) { this.group = group; this.version = version; this.kind = kind; - this.plural = plural; this.apiVersion = (group == null || group.isBlank()) ? version : group + "/" + version; } - public static GroupVersionKind gvkFor(Class resourceClass) { - return new GroupVersionKind(HasMetadata.getGroup(resourceClass), - HasMetadata.getVersion(resourceClass), HasMetadata.getKind(resourceClass), - HasMetadata.getPlural(resourceClass)); - } - public String getGroup() { return group; } @@ -57,18 +55,6 @@ public String getKind() { return kind; } - /** - * 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 (in which case, - * it could be approximated by using {@link Pluralize#toPlural(String)} on the kind), or - * the plural form if it was provided explicitly - */ - public Optional getPlural() { - return Optional.ofNullable(plural); - } - public String apiVersion() { return apiVersion; } @@ -91,8 +77,7 @@ public int hashCode() { @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..df94983baa 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(new GroupVersionKindPlural(groupVersionKind)); + } + + public GenericKubernetesDependentResource(GroupVersionKindPlural groupVersionKind) { super(GenericKubernetesResource.class); this.groupVersionKind = groupVersionKind; } @@ -20,7 +24,7 @@ protected InformerConfiguration.InformerConfigurationBuilder resourceClass) { + return new GroupVersionKindPlural(HasMetadata.getGroup(resourceClass), + HasMetadata.getVersion(resourceClass), HasMetadata.getKind(resourceClass), + HasMetadata.getPlural(resourceClass)); + } + + /** + * 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 Pluralize#toPlural(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 + */ + public String getPluralOrDefault() { + return getPlural().orElse(Pluralize.toPlural(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 14bb7d7251..a0b3b2cc8c 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 @@ -4,29 +4,26 @@ 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 { @Test void testInitFromApiVersion() { - var gvk = new GroupVersionKind("v1", "ConfigMap"); + var gvk = GroupVersionKindPlural.gvkFor("v1", "ConfigMap"); assertThat(gvk.getGroup()).isNull(); assertThat(gvk.getVersion()).isEqualTo("v1"); - gvk = new GroupVersionKind("apps/v1", "Deployment"); + gvk = GroupVersionKindPlural.gvkFor("apps/v1", "Deployment"); assertThat(gvk.getGroup()).isEqualTo("apps"); assertThat(gvk.getVersion()).isEqualTo("v1"); } @Test void pluralShouldOnlyBeProvidedIfExplicitlySet() { - var gvk = new GroupVersionKind("v1", "ConfigMap"); - assertThat(gvk.getPlural()).isEmpty(); - - gvk = GroupVersionKind.gvkFor(ConfigMap.class); + final var gvk = GroupVersionKindPlural.gvkFor(ConfigMap.class); assertThat(gvk.getPlural()).hasValue(HasMetadata.getPlural(ConfigMap.class)); } From 4756be30650559ba478246c5c98cc7c1e38d28b3 Mon Sep 17 00:00:00 2001 From: Chris Laprun Date: Thu, 29 Aug 2024 16:42:10 +0200 Subject: [PATCH 4/4] refactor: clean up, cache already resolved GVKs Signed-off-by: Chris Laprun --- .../operator/processing/GroupVersionKind.java | 20 ++--- .../GenericKubernetesDependentResource.java | 2 +- .../kubernetes/GroupVersionKindPlural.java | 77 ++++++++++++++++--- .../processing/GroupVersionKindTest.java | 35 ++++++++- .../ConfigMapGenericKubernetesDependent.java | 3 +- .../ConfigMapGenericKubernetesDependent.java | 3 +- 6 files changed, 110 insertions(+), 30 deletions(-) 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 07e90d3c4b..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,20 +1,19 @@ 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; -import io.javaoperatorsdk.operator.processing.dependent.kubernetes.GroupVersionKindPlural; 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<>(); - /** - * @deprecated Use {@link GroupVersionKindPlural#gvkFor(String, String)} instead - */ - @Deprecated(forRemoval = true) public GroupVersionKind(String apiVersion, String kind) { this.kind = kind; String[] groupAndVersion = apiVersion.split("/"); @@ -29,13 +28,14 @@ public GroupVersionKind(String apiVersion, String kind) { } public static GroupVersionKind gvkFor(Class resourceClass) { - return GroupVersionKindPlural.gvkFor(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)); } - /** - * @deprecated Use {@link GroupVersionKindPlural#gvkFor(String, String, String)} instead - */ - @Deprecated(forRemoval = true) public GroupVersionKind(String group, String version, String kind) { this.group = group; this.version = version; 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 df94983baa..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 @@ -11,7 +11,7 @@ public class GenericKubernetesDependentResource

private final GroupVersionKindPlural groupVersionKind; public GenericKubernetesDependentResource(GroupVersionKind groupVersionKind) { - this(new GroupVersionKindPlural(groupVersionKind)); + this(GroupVersionKindPlural.from(groupVersionKind)); } public GenericKubernetesDependentResource(GroupVersionKindPlural groupVersionKind) { diff --git a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/kubernetes/GroupVersionKindPlural.java b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/kubernetes/GroupVersionKindPlural.java index 4d156286b0..3354ccedae 100644 --- a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/kubernetes/GroupVersionKindPlural.java +++ b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/kubernetes/GroupVersionKindPlural.java @@ -6,31 +6,83 @@ import io.fabric8.kubernetes.api.model.HasMetadata; import io.javaoperatorsdk.operator.processing.GroupVersionKind; +/** + * An extension of {@link GroupVersionKind} that also records the associated plural form which is + * useful when dealing with Kubernetes RBACs. Downstream projects might leverage that information. + */ public class GroupVersionKindPlural extends GroupVersionKind { private final String plural; - public GroupVersionKindPlural(String group, String version, String kind, String plural) { + protected GroupVersionKindPlural(String group, String version, String kind, String plural) { super(group, version, kind); this.plural = plural; } - public GroupVersionKindPlural(GroupVersionKind gvk) { + protected GroupVersionKindPlural(String apiVersion, String kind, String plural) { + super(apiVersion, kind); + this.plural = plural; + } + + protected GroupVersionKindPlural(GroupVersionKind gvk, String plural) { this(gvk.getGroup(), gvk.getVersion(), gvk.getKind(), - gvk instanceof GroupVersionKindPlural ? ((GroupVersionKindPlural) gvk).plural : null); + plural != null ? plural + : (gvk instanceof GroupVersionKindPlural ? ((GroupVersionKindPlural) gvk).plural + : null)); } - public static GroupVersionKind gvkFor(String group, String version, String kind) { - return new GroupVersionKind(group, version, kind); + /** + * Creates a new GroupVersionKindPlural from the specified {@link GroupVersionKind}. + * + * @param gvk a {@link GroupVersionKind} from which to create a new GroupVersionKindPlural object + * @return a new GroupVersionKindPlural object matching the specified {@link GroupVersionKind} + */ + public static GroupVersionKindPlural from(GroupVersionKind gvk) { + return gvk instanceof GroupVersionKindPlural ? ((GroupVersionKindPlural) gvk) + : gvkWithPlural(gvk, null); } - public static GroupVersionKind gvkFor(String apiVersion, String kind) { - return new GroupVersionKind(apiVersion, kind); + /** + * Creates a new GroupVersionKindPlural based on the specified {@link GroupVersionKind} instance + * but specifying a plural form to use as well. + * + * @param gvk the base {@link GroupVersionKind} from which to derive a new GroupVersionKindPlural + * @param plural the plural form to use for the new instance or {@code null} if the default plural + * form is desired. Note that the specified plural form will override any existing plural + * form for the specified {@link GroupVersionKind} (in particular, if the specified + * {@link GroupVersionKind} was already an instance of GroupVersionKindPlural, its plural + * form will only be considered in the new instance if the specified plural form is + * {@code null} + * @return a new GroupVersionKindPlural derived from the specified {@link GroupVersionKind} and + * plural form + */ + public static GroupVersionKindPlural gvkWithPlural(GroupVersionKind gvk, String plural) { + return new GroupVersionKindPlural(gvk, plural); } + /** + * Creates a new GroupVersionKindPlural instance extracting the information from the specified + * {@link HasMetadata} implementation + * + * @param resourceClass the {@link HasMetadata} from which group, version, kind and plural form + * are extracted + * @return a new GroupVersionKindPlural instance based on the specified {@link HasMetadata} + * implementation + */ public static GroupVersionKindPlural gvkFor(Class resourceClass) { - return new GroupVersionKindPlural(HasMetadata.getGroup(resourceClass), - HasMetadata.getVersion(resourceClass), HasMetadata.getKind(resourceClass), - HasMetadata.getPlural(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; } /** @@ -46,13 +98,14 @@ public Optional getPlural() { /** * Returns the plural form associated with the kind if it was provided or a default, computed form - * via {@link Pluralize#toPlural(String)} (which should correspond to the actual plural form in + * 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(Pluralize.toPlural(getKind())); + 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 a0b3b2cc8c..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 @@ -12,19 +12,48 @@ class GroupVersionKindTest { @Test void testInitFromApiVersion() { - var gvk = GroupVersionKindPlural.gvkFor("v1", "ConfigMap"); + var gvk = new GroupVersionKind("v1", "ConfigMap"); assertThat(gvk.getGroup()).isNull(); assertThat(gvk.getVersion()).isEqualTo("v1"); - gvk = GroupVersionKindPlural.gvkFor("apps/v1", "Deployment"); + gvk = new GroupVersionKind("apps/v1", "Deployment"); assertThat(gvk.getGroup()).isEqualTo("apps"); assertThat(gvk.getVersion()).isEqualTo("v1"); } @Test void pluralShouldOnlyBeProvidedIfExplicitlySet() { - final var gvk = GroupVersionKindPlural.gvkFor(ConfigMap.class); + 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/generickubernetesresource/generickubernetesdependentresourcemanaged/ConfigMapGenericKubernetesDependent.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/generickubernetesresource/generickubernetesdependentresourcemanaged/ConfigMapGenericKubernetesDependent.java index 5d9dfd35da..d5ab470cfb 100644 --- a/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/generickubernetesresource/generickubernetesdependentresourcemanaged/ConfigMapGenericKubernetesDependent.java +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/generickubernetesresource/generickubernetesdependentresourcemanaged/ConfigMapGenericKubernetesDependent.java @@ -4,7 +4,6 @@ import java.io.InputStream; import java.util.Map; -import io.fabric8.kubernetes.api.model.ConfigMap; import io.fabric8.kubernetes.api.model.GenericKubernetesResource; import io.javaoperatorsdk.operator.api.reconciler.Context; import io.javaoperatorsdk.operator.api.reconciler.dependent.GarbageCollected; @@ -25,7 +24,7 @@ public class ConfigMapGenericKubernetesDependent extends public static final String KEY = "key"; public ConfigMapGenericKubernetesDependent() { - super(GroupVersionKind.gvkFor(ConfigMap.class)); + super(new GroupVersionKind("", VERSION, KIND)); } @Override diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/generickubernetesresource/generickubernetesdependentstandalone/ConfigMapGenericKubernetesDependent.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/generickubernetesresource/generickubernetesdependentstandalone/ConfigMapGenericKubernetesDependent.java index 47ea8b0605..4efc968ef0 100644 --- a/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/generickubernetesresource/generickubernetesdependentstandalone/ConfigMapGenericKubernetesDependent.java +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/generickubernetesresource/generickubernetesdependentstandalone/ConfigMapGenericKubernetesDependent.java @@ -4,7 +4,6 @@ import java.io.InputStream; import java.util.Map; -import io.fabric8.kubernetes.api.model.ConfigMap; import io.fabric8.kubernetes.api.model.GenericKubernetesResource; import io.javaoperatorsdk.operator.api.reconciler.Context; import io.javaoperatorsdk.operator.api.reconciler.dependent.GarbageCollected; @@ -25,7 +24,7 @@ public class ConfigMapGenericKubernetesDependent extends public static final String KEY = "key"; public ConfigMapGenericKubernetesDependent() { - super(GroupVersionKind.gvkFor(ConfigMap.class)); + super(new GroupVersionKind("", VERSION, KIND)); } @Override