Skip to content

Commit e0647d7

Browse files
authored
feat: add factory methods to help with SSA (6013)
feat: add factory methods to help with SSA Fixes #6012 --- chore: don't use wildcard imports Signed-off-by: Marc Nuri <[email protected]>
1 parent 52ccd57 commit e0647d7

File tree

4 files changed

+230
-94
lines changed

4 files changed

+230
-94
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
* Fix #5954: (crd-generator) Sort required properties to ensure deterministic output
1616
* Fix #5973: CacheImpl locking for reading indexes (Cache.byIndex|indexKeys|index) was reduced
1717
* Fix #5953: Made informer watch starting deterministic with respect to list processing
18+
* Fix #6012: Add convenience methods on HasMetadata to help with SSA
1819

1920
#### Dependency Upgrade
2021
* Fix #5695: Upgrade Fabric8 Kubernetes Model to Kubernetes v1.30.0

kubernetes-client-api/src/main/java/io/fabric8/kubernetes/client/utils/Utils.java

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -545,5 +545,4 @@ private static void schedule(Supplier<CompletableFuture<?>> runner, long delay,
545545
});
546546
}, delay, unit));
547547
}
548-
549548
}

kubernetes-model-generator/kubernetes-model-core/src/main/java/io/fabric8/kubernetes/api/model/HasMetadata.java

Lines changed: 131 additions & 93 deletions
Original file line numberDiff line numberDiff line change
@@ -48,10 +48,9 @@ public interface HasMetadata extends KubernetesResource {
4848
Pattern FINALIZER_NAME_MATCHER = Pattern.compile(
4949
"^((" + DNS_LABEL_REGEXP + "\\.)+" + DNS_LABEL_START + 2 + DNS_LABEL_END + ")/"
5050
+ DNS_LABEL_REGEXP);
51-
52-
ObjectMeta getMetadata();
53-
54-
void setMetadata(ObjectMeta metadata);
51+
String REQUIRES_NON_NULL_METADATA = "requires non-null metadata";
52+
String REQUIRES_NON_NULL_NAME = "requires non-null name";
53+
String REQUIRES_NON_NULL_NAMESPACE = "requires non-null namespace";
5554

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

68-
default String getKind() {
69-
return getKind(getClass());
70-
}
71-
7267
/**
7368
* Computes the {@code apiVersion} associated with this HasMetadata implementation. The value is derived from the
7469
* {@link Group} and {@link Version} annotations.
@@ -113,12 +108,6 @@ static String getVersion(Class<?> clazz) {
113108
return version != null ? version.value() : null;
114109
}
115110

116-
default String getApiVersion() {
117-
return getApiVersion(getClass());
118-
}
119-
120-
void setApiVersion(String version);
121-
122111
/**
123112
* Retrieves the plural form associated with the specified class if annotated with {@link
124113
* Plural} or computes a default value using the value returned by {@link #getSingular(Class)} as
@@ -133,11 +122,6 @@ static String getPlural(Class<?> clazz) {
133122
: Pluralize.toPlural(getSingular(clazz)));
134123
}
135124

136-
@JsonIgnore
137-
default String getPlural() {
138-
return getPlural(getClass());
139-
}
140-
141125
/**
142126
* Retrieves the singular form associated with the specified class as defined by the
143127
* {@link Singular} annotation or computes a default value (lower-cased version of the value
@@ -153,11 +137,6 @@ static String getSingular(Class<?> clazz) {
153137
.toLowerCase(Locale.ROOT);
154138
}
155139

156-
@JsonIgnore
157-
default String getSingular() {
158-
return getSingular(getClass());
159-
}
160-
161140
static String getFullResourceName(Class<?> clazz) {
162141
final String plural = getPlural(clazz);
163142
final String group = getGroup(clazz);
@@ -175,6 +154,98 @@ static String getFullResourceName(String plural, String group) {
175154
return group.isEmpty() ? plural : plural + "." + group;
176155
}
177156

157+
/**
158+
* Determines whether the specified finalizer is valid according to the
159+
* <a href=
160+
* 'https://kubernetes.io/docs/tasks/extend-kubernetes/custom-resources/custom-resource-definitions/#finalizers'>finalizer
161+
* specification</a>.
162+
*
163+
* @param finalizer the identifier of the finalizer which validity we want to check
164+
* @return {@code true} if the identifier is valid, {@code false} otherwise
165+
*/
166+
static boolean validateFinalizer(String finalizer) {
167+
if (finalizer == null) {
168+
return false;
169+
}
170+
final Matcher matcher = FINALIZER_NAME_MATCHER.matcher(finalizer);
171+
if (matcher.matches()) {
172+
final String group = matcher.group(1);
173+
return group.length() < 256;
174+
} else {
175+
return false;
176+
}
177+
}
178+
179+
/**
180+
* Sanitizes and validates the specified {@link OwnerReference}, presumably to add it
181+
*
182+
* @param ownerReference the {@link OwnerReference} to sanitize and validate
183+
* @return the sanitized and validated {@link OwnerReference} which should be used instead of the original one
184+
*/
185+
static OwnerReference sanitizeAndValidate(OwnerReference ownerReference) {
186+
// validate required fields are present
187+
final StringBuilder error = new StringBuilder(100);
188+
error.append("Owner is missing required field(s): ");
189+
final BiFunction<String, String, Optional<String>> trimmedFieldIfValid = (field, value) -> {
190+
boolean isError = false;
191+
if (value == null) {
192+
isError = true;
193+
} else {
194+
value = value.trim();
195+
if (value.isEmpty()) {
196+
isError = true;
197+
}
198+
}
199+
if (isError) {
200+
error.append(field).append(" ");
201+
return Optional.empty();
202+
} else {
203+
return Optional.of(value);
204+
}
205+
};
206+
final Supplier<IllegalArgumentException> exceptionSupplier = () -> new IllegalArgumentException(
207+
error.toString());
208+
209+
final Optional<String> uid = trimmedFieldIfValid.apply("uid", ownerReference.getUid());
210+
final Optional<String> apiVersion = trimmedFieldIfValid.apply("apiVersion",
211+
ownerReference.getApiVersion());
212+
final Optional<String> name = trimmedFieldIfValid.apply("name", ownerReference.getName());
213+
final Optional<String> kind = trimmedFieldIfValid.apply("kind", ownerReference.getKind());
214+
215+
// check that required values are present
216+
ownerReference = new OwnerReferenceBuilder(ownerReference)
217+
.withUid(uid.orElseThrow(exceptionSupplier))
218+
.withApiVersion(apiVersion.orElseThrow(exceptionSupplier))
219+
.withName(name.orElseThrow(exceptionSupplier))
220+
.withKind(kind.orElseThrow(exceptionSupplier))
221+
.build();
222+
return ownerReference;
223+
}
224+
225+
ObjectMeta getMetadata();
226+
227+
void setMetadata(ObjectMeta metadata);
228+
229+
default String getKind() {
230+
return getKind(getClass());
231+
}
232+
233+
default String getApiVersion() {
234+
return getApiVersion(getClass());
235+
}
236+
237+
void setApiVersion(String version);
238+
239+
@JsonIgnore
240+
default String getPlural() {
241+
return getPlural(getClass());
242+
}
243+
244+
@JsonIgnore
245+
default String getSingular() {
246+
return getSingular(getClass());
247+
}
248+
178249
@JsonIgnore
179250
@SuppressWarnings("unused")
180251
default String getFullResourceName() {
@@ -238,32 +309,9 @@ default boolean addFinalizer(String finalizer) {
238309
}
239310

240311
/**
241-
* Determines whether the specified finalizer is valid according to the
242-
* <a href=
243-
* 'https://kubernetes.io/docs/tasks/extend-kubernetes/custom-resources/custom-resource-definitions/#finalizers'>finalizer
244-
* specification</a>.
245-
*
246312
* @param finalizer the identifier of the finalizer which validity we want to check
247313
* @return {@code true} if the identifier is valid, {@code false} otherwise
248-
*/
249-
static boolean validateFinalizer(String finalizer) {
250-
if (finalizer == null) {
251-
return false;
252-
}
253-
final Matcher matcher = FINALIZER_NAME_MATCHER.matcher(finalizer);
254-
if (matcher.matches()) {
255-
final String group = matcher.group(1);
256-
return group.length() < 256;
257-
} else {
258-
return false;
259-
}
260-
}
261-
262-
/**
263314
* @see HasMetadata#validateFinalizer(String)
264-
*
265-
* @param finalizer the identifier of the finalizer which validity we want to check
266-
* @return {@code true} if the identifier is valid, {@code false} otherwise
267315
*/
268316
default boolean isFinalizerValid(String finalizer) {
269317
return HasMetadata.validateFinalizer(finalizer);
@@ -417,52 +465,6 @@ default OwnerReference addOwnerReference(OwnerReference ownerReference) {
417465
return ownerReference;
418466
}
419467

420-
/**
421-
* Sanitizes and validates the specified {@link OwnerReference}, presumably to add it
422-
*
423-
* @param ownerReference the {@link OwnerReference} to sanitize and validate
424-
* @return the sanitized and validated {@link OwnerReference} which should be used instead of the original one
425-
*/
426-
static OwnerReference sanitizeAndValidate(OwnerReference ownerReference) {
427-
// validate required fields are present
428-
final StringBuilder error = new StringBuilder(100);
429-
error.append("Owner is missing required field(s): ");
430-
final BiFunction<String, String, Optional<String>> trimmedFieldIfValid = (field, value) -> {
431-
boolean isError = false;
432-
if (value == null) {
433-
isError = true;
434-
} else {
435-
value = value.trim();
436-
if (value.isEmpty()) {
437-
isError = true;
438-
}
439-
}
440-
if (isError) {
441-
error.append(field).append(" ");
442-
return Optional.empty();
443-
} else {
444-
return Optional.of(value);
445-
}
446-
};
447-
final Supplier<IllegalArgumentException> exceptionSupplier = () -> new IllegalArgumentException(
448-
error.toString());
449-
450-
final Optional<String> uid = trimmedFieldIfValid.apply("uid", ownerReference.getUid());
451-
final Optional<String> apiVersion = trimmedFieldIfValid.apply("apiVersion",
452-
ownerReference.getApiVersion());
453-
final Optional<String> name = trimmedFieldIfValid.apply("name", ownerReference.getName());
454-
final Optional<String> kind = trimmedFieldIfValid.apply("kind", ownerReference.getKind());
455-
456-
// check that required values are present
457-
ownerReference = new OwnerReferenceBuilder(ownerReference)
458-
.withUid(uid.orElseThrow(exceptionSupplier))
459-
.withApiVersion(apiVersion.orElseThrow(exceptionSupplier))
460-
.withName(name.orElseThrow(exceptionSupplier))
461-
.withKind(kind.orElseThrow(exceptionSupplier))
462-
.build();
463-
return ownerReference;
464-
}
465-
466468
/**
467469
* Removes the {@link OwnerReference} identified by the specified UID if it's part of this {@code HasMetadata}'s owner
468470
* references
@@ -493,4 +495,40 @@ default void removeOwnerReference(HasMetadata owner) {
493495
default Optional<ObjectMeta> optionalMetadata() {
494496
return Optional.ofNullable(getMetadata());
495497
}
498+
499+
/**
500+
* Initializes this {@link ObjectMeta} field with name and namespace (if this instance represents a namespaced resource)
501+
* provided by the specified HasMetadata instance. This is a convenience method to avoid boilerplate, notably when using
502+
* Server-Side Apply, when creating a new instance with only some fields of the original one. Calls
503+
* {@link #setMetadata(ObjectMeta)} when done, if you want to further configure this instance's metadata, please use
504+
* {@link #initMetadataBuilderNameAndNamespaceFrom(HasMetadata)} instead, which <em>doesn't</em> sets the metadata, leaving it
505+
* up to the user once configuration is finished.
506+
*
507+
* @param original a HasMetadata instance from which to retrieve the name and namespace
508+
*/
509+
default void initNameAndNamespaceFrom(HasMetadata original) {
510+
Objects.requireNonNull(original);
511+
final ObjectMeta meta = initMetadataBuilderNameAndNamespaceFrom(original).build();
512+
setMetadata(meta);
513+
}
514+
515+
/**
516+
* Creates and initializes a new {@link ObjectMetaBuilder} with name and namespace (if the provided instance to initialize
517+
* from represents a namespaced resource) provided by the specified HasMetadata instance. This is a convenience method to
518+
* avoid boilerplate, notably when using Server-Side Apply, when creating a new instance with only some fields of the original
519+
* one. This method assumes that further configuration will occur on the newly created ObjectMetaBuilder.
520+
*
521+
* @param original an HasMetadata instance from which to retrieve the name and namespace
522+
* @return a new ObjectMetaBuilder instance initialized with the name and namespace (if needed) of the specified HasMetadata
523+
*/
524+
static ObjectMetaBuilder initMetadataBuilderNameAndNamespaceFrom(HasMetadata original) {
525+
Objects.requireNonNull(original);
526+
final ObjectMeta metadata = Objects.requireNonNull(original.getMetadata(), REQUIRES_NON_NULL_METADATA);
527+
final ObjectMetaBuilder metaBuilder = new ObjectMetaBuilder();
528+
metaBuilder.withName(Objects.requireNonNull(metadata.getName(), REQUIRES_NON_NULL_NAME));
529+
if (original instanceof Namespaced) {
530+
metaBuilder.withNamespace(Objects.requireNonNull(metadata.getNamespace(), REQUIRES_NON_NULL_NAMESPACE));
531+
}
532+
return metaBuilder;
533+
}
496534
}

0 commit comments

Comments
 (0)