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 factory methods to help with SSA #6013

Merged
merged 2 commits into from
May 28, 2024
Merged
Show file tree
Hide file tree
Changes from 1 commit
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
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
* Fix #5954: (crd-generator) Sort required properties to ensure deterministic output
* Fix #5973: CacheImpl locking for reading indexes (Cache.byIndex|indexKeys|index) was reduced
* Fix #5953: Made informer watch starting deterministic with respect to list processing
* Fix #6012: Add convenience methods on HasMetadata to help with SSA

#### Dependency Upgrade
* Fix #5695: Upgrade Fabric8 Kubernetes Model to Kubernetes v1.30.0
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -545,5 +545,4 @@ private static void schedule(Supplier<CompletableFuture<?>> runner, long delay,
});
}, delay, unit));
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -48,10 +48,9 @@ public interface HasMetadata extends KubernetesResource {
Pattern FINALIZER_NAME_MATCHER = Pattern.compile(
"^((" + DNS_LABEL_REGEXP + "\\.)+" + DNS_LABEL_START + 2 + DNS_LABEL_END + ")/"
+ DNS_LABEL_REGEXP);

ObjectMeta getMetadata();

void setMetadata(ObjectMeta metadata);
String REQUIRES_NON_NULL_METADATA = "requires non-null metadata";
String REQUIRES_NON_NULL_NAME = "requires non-null name";
String REQUIRES_NON_NULL_NAMESPACE = "requires non-null namespace";

/**
* Retrieves the kind associated with the specified HasMetadata implementation. If the implementation is annotated with
Expand All @@ -65,10 +64,6 @@ static String getKind(Class<?> clazz) {
return kind != null ? kind.value() : clazz.getSimpleName();
}

default String getKind() {
return getKind(getClass());
}

/**
* Computes the {@code apiVersion} associated with this HasMetadata implementation. The value is derived from the
* {@link Group} and {@link Version} annotations.
Expand Down Expand Up @@ -113,12 +108,6 @@ static String getVersion(Class<?> clazz) {
return version != null ? version.value() : null;
}

default String getApiVersion() {
return getApiVersion(getClass());
}

void setApiVersion(String version);

/**
* Retrieves the plural form associated with the specified class if annotated with {@link
* Plural} or computes a default value using the value returned by {@link #getSingular(Class)} as
Expand All @@ -133,11 +122,6 @@ static String getPlural(Class<?> clazz) {
: Pluralize.toPlural(getSingular(clazz)));
}

@JsonIgnore
default String getPlural() {
return getPlural(getClass());
}

/**
* Retrieves the singular form associated with the specified class as defined by the
* {@link Singular} annotation or computes a default value (lower-cased version of the value
Expand All @@ -153,11 +137,6 @@ static String getSingular(Class<?> clazz) {
.toLowerCase(Locale.ROOT);
}

@JsonIgnore
default String getSingular() {
return getSingular(getClass());
}

static String getFullResourceName(Class<?> clazz) {
final String plural = getPlural(clazz);
final String group = getGroup(clazz);
Expand All @@ -175,6 +154,98 @@ static String getFullResourceName(String plural, String group) {
return group.isEmpty() ? plural : plural + "." + group;
}

/**
* Determines whether the specified finalizer is valid according to the
* <a href=
* 'https://kubernetes.io/docs/tasks/extend-kubernetes/custom-resources/custom-resource-definitions/#finalizers'>finalizer
* specification</a>.
*
* @param finalizer the identifier of the finalizer which validity we want to check
* @return {@code true} if the identifier is valid, {@code false} otherwise
*/
static boolean validateFinalizer(String finalizer) {
if (finalizer == null) {
return false;
}
final Matcher matcher = FINALIZER_NAME_MATCHER.matcher(finalizer);
if (matcher.matches()) {
final String group = matcher.group(1);
return group.length() < 256;
} else {
return false;
}
}

/**
* Sanitizes and validates the specified {@link OwnerReference}, presumably to add it
*
* @param ownerReference the {@link OwnerReference} to sanitize and validate
* @return the sanitized and validated {@link OwnerReference} which should be used instead of the original one
*/
static OwnerReference sanitizeAndValidate(OwnerReference ownerReference) {
// validate required fields are present
final StringBuilder error = new StringBuilder(100);
error.append("Owner is missing required field(s): ");
final BiFunction<String, String, Optional<String>> trimmedFieldIfValid = (field, value) -> {
boolean isError = false;
if (value == null) {
isError = true;
} else {
value = value.trim();
if (value.isEmpty()) {
isError = true;
}
}
if (isError) {
error.append(field).append(" ");
return Optional.empty();
} else {
return Optional.of(value);
}
};
final Supplier<IllegalArgumentException> exceptionSupplier = () -> new IllegalArgumentException(
error.toString());

final Optional<String> uid = trimmedFieldIfValid.apply("uid", ownerReference.getUid());
final Optional<String> apiVersion = trimmedFieldIfValid.apply("apiVersion",
ownerReference.getApiVersion());
final Optional<String> name = trimmedFieldIfValid.apply("name", ownerReference.getName());
final Optional<String> kind = trimmedFieldIfValid.apply("kind", ownerReference.getKind());

// check that required values are present
ownerReference = new OwnerReferenceBuilder(ownerReference)
.withUid(uid.orElseThrow(exceptionSupplier))
.withApiVersion(apiVersion.orElseThrow(exceptionSupplier))
.withName(name.orElseThrow(exceptionSupplier))
.withKind(kind.orElseThrow(exceptionSupplier))
.build();
return ownerReference;
}

ObjectMeta getMetadata();

void setMetadata(ObjectMeta metadata);

default String getKind() {
return getKind(getClass());
}

default String getApiVersion() {
return getApiVersion(getClass());
}

void setApiVersion(String version);

@JsonIgnore
default String getPlural() {
return getPlural(getClass());
}

@JsonIgnore
default String getSingular() {
return getSingular(getClass());
}

@JsonIgnore
@SuppressWarnings("unused")
default String getFullResourceName() {
Expand Down Expand Up @@ -238,32 +309,9 @@ default boolean addFinalizer(String finalizer) {
}

/**
* Determines whether the specified finalizer is valid according to the
* <a href=
* 'https://kubernetes.io/docs/tasks/extend-kubernetes/custom-resources/custom-resource-definitions/#finalizers'>finalizer
* specification</a>.
*
* @param finalizer the identifier of the finalizer which validity we want to check
* @return {@code true} if the identifier is valid, {@code false} otherwise
*/
static boolean validateFinalizer(String finalizer) {
if (finalizer == null) {
return false;
}
final Matcher matcher = FINALIZER_NAME_MATCHER.matcher(finalizer);
if (matcher.matches()) {
final String group = matcher.group(1);
return group.length() < 256;
} else {
return false;
}
}

/**
* @see HasMetadata#validateFinalizer(String)
*
* @param finalizer the identifier of the finalizer which validity we want to check
* @return {@code true} if the identifier is valid, {@code false} otherwise
*/
default boolean isFinalizerValid(String finalizer) {
return HasMetadata.validateFinalizer(finalizer);
Expand Down Expand Up @@ -417,52 +465,6 @@ default OwnerReference addOwnerReference(OwnerReference ownerReference) {
return ownerReference;
}

/**
* Sanitizes and validates the specified {@link OwnerReference}, presumably to add it
*
* @param ownerReference the {@link OwnerReference} to sanitize and validate
* @return the sanitized and validated {@link OwnerReference} which should be used instead of the original one
*/
static OwnerReference sanitizeAndValidate(OwnerReference ownerReference) {
// validate required fields are present
final StringBuilder error = new StringBuilder(100);
error.append("Owner is missing required field(s): ");
final BiFunction<String, String, Optional<String>> trimmedFieldIfValid = (field, value) -> {
boolean isError = false;
if (value == null) {
isError = true;
} else {
value = value.trim();
if (value.isEmpty()) {
isError = true;
}
}
if (isError) {
error.append(field).append(" ");
return Optional.empty();
} else {
return Optional.of(value);
}
};
final Supplier<IllegalArgumentException> exceptionSupplier = () -> new IllegalArgumentException(
error.toString());

final Optional<String> uid = trimmedFieldIfValid.apply("uid", ownerReference.getUid());
final Optional<String> apiVersion = trimmedFieldIfValid.apply("apiVersion",
ownerReference.getApiVersion());
final Optional<String> name = trimmedFieldIfValid.apply("name", ownerReference.getName());
final Optional<String> kind = trimmedFieldIfValid.apply("kind", ownerReference.getKind());

// check that required values are present
ownerReference = new OwnerReferenceBuilder(ownerReference)
.withUid(uid.orElseThrow(exceptionSupplier))
.withApiVersion(apiVersion.orElseThrow(exceptionSupplier))
.withName(name.orElseThrow(exceptionSupplier))
.withKind(kind.orElseThrow(exceptionSupplier))
.build();
return ownerReference;
}

/**
* Removes the {@link OwnerReference} identified by the specified UID if it's part of this {@code HasMetadata}'s owner
* references
Expand Down Expand Up @@ -493,4 +495,40 @@ default void removeOwnerReference(HasMetadata owner) {
default Optional<ObjectMeta> optionalMetadata() {
return Optional.ofNullable(getMetadata());
}

/**
* Initializes this {@link ObjectMeta} field with name and namespace (if this instance represents a namespaced resource)
* provided by the specified HasMetadata instance. This is a convenience method to avoid boilerplate, notably when using
* Server-Side Apply, when creating a new instance with only some fields of the original one. Calls
* {@link #setMetadata(ObjectMeta)} when done, if you want to further configure this instance's metadata, please use
* {@link #initMetadataBuilderNameAndNamespaceFrom(HasMetadata)} instead, which <em>doesn't</em> sets the metadata, leaving it
* up to the user once configuration is finished.
*
* @param original a HasMetadata instance from which to retrieve the name and namespace
*/
default void initNameAndNamespaceFrom(HasMetadata original) {
metacosm marked this conversation as resolved.
Show resolved Hide resolved
Objects.requireNonNull(original);
final ObjectMeta meta = initMetadataBuilderNameAndNamespaceFrom(original).build();
setMetadata(meta);
}

/**
* Creates and initializes a new {@link ObjectMetaBuilder} with name and namespace (if the provided instance to initialize
* from represents a namespaced resource) provided by the specified HasMetadata instance. This is a convenience method to
* avoid boilerplate, notably when using Server-Side Apply, when creating a new instance with only some fields of the original
* one. This method assumes that further configuration will occur on the newly created ObjectMetaBuilder.
*
* @param original an HasMetadata instance from which to retrieve the name and namespace
* @return a new ObjectMetaBuilder instance initialized with the name and namespace (if needed) of the specified HasMetadata
*/
static ObjectMetaBuilder initMetadataBuilderNameAndNamespaceFrom(HasMetadata original) {
Objects.requireNonNull(original);
final ObjectMeta metadata = Objects.requireNonNull(original.getMetadata(), REQUIRES_NON_NULL_METADATA);
final ObjectMetaBuilder metaBuilder = new ObjectMetaBuilder();
metaBuilder.withName(Objects.requireNonNull(metadata.getName(), REQUIRES_NON_NULL_NAME));
if (original instanceof Namespaced) {
metaBuilder.withNamespace(Objects.requireNonNull(metadata.getNamespace(), REQUIRES_NON_NULL_NAMESPACE));
}
return metaBuilder;
}
}
Loading
Loading