@@ -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