Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: add getPlural method on GroupVersionKind #2515

Merged
merged 4 commits into from
Aug 29, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -1,13 +1,18 @@
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;

public class GroupVersionKind {
private final String group;
private final String version;
private final String kind;
private final String apiVersion;
csviri marked this conversation as resolved.
Show resolved Hide resolved
protected final static Map<Class<? extends HasMetadata>, GroupVersionKind> CACHE =
new ConcurrentHashMap<>();

public GroupVersionKind(String apiVersion, String kind) {
this.kind = kind;
Expand All @@ -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<? extends HasMetadata> resourceClass) {
return CACHE.computeIfAbsent(resourceClass, GroupVersionKind::computeGVK);
}

private static GroupVersionKind computeGVK(Class<? extends HasMetadata> 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<? extends HasMetadata> 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() {
Expand All @@ -45,7 +56,7 @@ public String getKind() {
}

public String apiVersion() {
return group == null || group.isBlank() ? version : group + "/" + version;
return apiVersion;
}

@Override
Expand All @@ -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 + '\'' +
'}';
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,13 @@
public class GenericKubernetesDependentResource<P extends HasMetadata>
extends KubernetesDependentResource<GenericKubernetesResource, P> {

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;
}
Expand All @@ -20,7 +24,7 @@ protected InformerConfiguration.InformerConfigurationBuilder<GenericKubernetesRe
}

@SuppressWarnings("unused")
public GroupVersionKind getGroupVersionKind() {
public GroupVersionKindPlural getGroupVersionKind() {
return groupVersionKind;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
package io.javaoperatorsdk.operator.processing.dependent.kubernetes;

import java.util.Optional;

import io.fabric8.kubernetes.api.Pluralize;
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;

protected GroupVersionKindPlural(String group, String version, String kind, String plural) {
super(group, version, kind);
this.plural = plural;
}

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(),
plural != null ? plural
: (gvk instanceof GroupVersionKindPlural ? ((GroupVersionKindPlural) gvk).plural
: null));
}

/**
* 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);
}

/**
* 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<? extends HasMetadata> 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<String> 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()));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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 {

Expand All @@ -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");
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -66,16 +66,12 @@ private ConfigMap configMap(DynamicGenericEventSourceRegistrationCustomResource
return cm;
}

private GroupVersionKind gvkFor(Class<? extends HasMetadata> clazz) {
return new GroupVersionKind(HasMetadata.getApiVersion(clazz), HasMetadata.getKind(clazz));
}

private InformerEventSource<GenericKubernetesResource, DynamicGenericEventSourceRegistrationCustomResource> genericInformerFor(
Class<? extends HasMetadata> clazz,
Context<DynamicGenericEventSourceRegistrationCustomResource> context) {

return new InformerEventSource<>(
InformerConfiguration.from(gvkFor(clazz),
InformerConfiguration.from(GroupVersionKind.gvkFor(clazz),
context.eventSourceRetriever().eventSourceContextForDynamicRegistration()).build(),
context.eventSourceRetriever().eventSourceContextForDynamicRegistration());
}
Expand Down
Loading