From a1f5e078fa7215deafa935497a7706a0447ca5b1 Mon Sep 17 00:00:00 2001 From: Chris Laprun Date: Thu, 16 May 2024 16:18:07 +0200 Subject: [PATCH] feat: add factory methods to help with SSA Fixes #6012 --- CHANGELOG.md | 1 + .../kubernetes/api/model/HasMetadata.java | 25 ++++ .../kubernetes/api/model/HasMetadataTest.java | 116 +++++++++++++++--- 3 files changed, 128 insertions(+), 14 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 5958b2543ce..121e80fbfd4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -14,6 +14,7 @@ * Fix #5867: (java-generator) Add JsonFormat shape to date-time * 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 #6012: Add emptyClusterWideCopyOf and emptyCopyOf methods on HasMetadata #### Dependency Upgrade * Fix #5695: Upgrade Fabric8 Kubernetes Model to Kubernetes v1.30.0 diff --git a/kubernetes-model-generator/kubernetes-model-core/src/main/java/io/fabric8/kubernetes/api/model/HasMetadata.java b/kubernetes-model-generator/kubernetes-model-core/src/main/java/io/fabric8/kubernetes/api/model/HasMetadata.java index fd8f5e0e586..7072308cc83 100644 --- a/kubernetes-model-generator/kubernetes-model-core/src/main/java/io/fabric8/kubernetes/api/model/HasMetadata.java +++ b/kubernetes-model-generator/kubernetes-model-core/src/main/java/io/fabric8/kubernetes/api/model/HasMetadata.java @@ -48,6 +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); + 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"; ObjectMeta getMetadata(); @@ -493,4 +496,26 @@ default void removeOwnerReference(HasMetadata owner) { default Optional optionalMetadata() { return Optional.ofNullable(getMetadata()); } + + static T emptyCopyOf(T original, Supplier constructor) { + final T t = constructor.get(); + final ObjectMeta metadata = Objects.requireNonNull(original.getMetadata(), REQUIRES_NON_NULL_METADATA); + final String name = Objects.requireNonNull(metadata.getName(), REQUIRES_NON_NULL_NAME); + final String ns = Objects.requireNonNull(metadata.getNamespace(), REQUIRES_NON_NULL_NAMESPACE); + t.setMetadata(new ObjectMetaBuilder() + .withName(name) + .withNamespace(ns) + .build()); + return t; + } + + static T emptyClusterWideCopyOf(T original, Supplier constructor) { + final T t = constructor.get(); + final ObjectMeta metadata = Objects.requireNonNull(original.getMetadata(), REQUIRES_NON_NULL_METADATA); + final String name = Objects.requireNonNull(metadata.getName(), REQUIRES_NON_NULL_NAME); + t.setMetadata(new ObjectMetaBuilder() + .withName(name) + .build()); + return t; + } } diff --git a/kubernetes-model-generator/kubernetes-model-core/src/test/java/io/fabric8/kubernetes/api/model/HasMetadataTest.java b/kubernetes-model-generator/kubernetes-model-core/src/test/java/io/fabric8/kubernetes/api/model/HasMetadataTest.java index ec007b22a3e..357d3f95670 100644 --- a/kubernetes-model-generator/kubernetes-model-core/src/test/java/io/fabric8/kubernetes/api/model/HasMetadataTest.java +++ b/kubernetes-model-generator/kubernetes-model-core/src/test/java/io/fabric8/kubernetes/api/model/HasMetadataTest.java @@ -22,12 +22,7 @@ import java.util.Optional; -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertFalse; -import static org.junit.jupiter.api.Assertions.assertNotNull; -import static org.junit.jupiter.api.Assertions.assertNull; -import static org.junit.jupiter.api.Assertions.assertThrows; -import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.junit.jupiter.api.Assertions.*; class HasMetadataTest { @Test @@ -84,14 +79,14 @@ void invalidFinalizersShouldFail() { assertThrows(IllegalArgumentException.class, () -> hasMetadata.addFinalizer("fabric8.i/finalizer")); assertThrows(IllegalArgumentException.class, () -> hasMetadata.addFinalizer("fabric8./finalizer")); assertThrows(IllegalArgumentException.class, () -> hasMetadata.addFinalizer( - "this-label-is-too-long-12345678901234567890123456789012345678901234567890qwertyuiopasdfghjkl.io/finalizer")); + "this-label-is-too-long-12345678901234567890123456789012345678901234567890qwertyuiopasdfghjkl.io/finalizer")); assertThrows(IllegalArgumentException.class, () -> hasMetadata.addFinalizer( - "this.dns.name.is.way.way.too.long.more.than.255.characters.12345678901234567890.qwertyuiop.asdfghjkl.zxcvbnm.qwertyuiop.adfghjkl.zxcvbnm.mnbvcxz.lkjhgfdsa.poiuytrewq12345678901234567890.qwertyuiop.asdfghjkl.zxcvbnm.qwertyuiop.adfghjkl.zxcvbnm.mnbvcxz.lkjhgfdsa.poiuytrewq.io/finalizer")); + "this.dns.name.is.way.way.too.long.more.than.255.characters.12345678901234567890.qwertyuiop.asdfghjkl.zxcvbnm.qwertyuiop.adfghjkl.zxcvbnm.mnbvcxz.lkjhgfdsa.poiuytrewq12345678901234567890.qwertyuiop.asdfghjkl.zxcvbnm.qwertyuiop.adfghjkl.zxcvbnm.mnbvcxz.lkjhgfdsa.poiuytrewq.io/finalizer")); assertThrows(IllegalArgumentException.class, () -> hasMetadata.addFinalizer(".io/finalizer")); assertThrows(IllegalArgumentException.class, () -> hasMetadata.addFinalizer("fabric8.io/-finalizer")); assertThrows(IllegalArgumentException.class, () -> hasMetadata.addFinalizer("fabric8.io/finalizer-")); assertThrows(IllegalArgumentException.class, () -> hasMetadata - .addFinalizer("fabric8.io/finalizerreallyreallywaywaywaytooooooooooooooooooooolooooooooonnnnnnnnnnng")); + .addFinalizer("fabric8.io/finalizerreallyreallywaywaywaytooooooooooooooooooooolooooooooonnnnnnnnnnng")); } @Test @@ -106,7 +101,7 @@ public String getKind() { }; IllegalArgumentException iae = assertThrows(IllegalArgumentException.class, - () -> target.addOwnerReference((OwnerReference) null)); + () -> target.addOwnerReference((OwnerReference) null)); String msg = iae.getMessage(); assertTrue(msg.contains("null reference to '" + name + "' " + kind)); iae = assertThrows(IllegalArgumentException.class, () -> target.addOwnerReference((HasMetadata) null)); @@ -119,7 +114,7 @@ void addingNullOwnerReferenceShouldNotCrashEvenIfTargetDoesNotHaveMetadata() { final HasMetadata target = new Empty(); IllegalArgumentException iae = assertThrows(IllegalArgumentException.class, - () -> target.addOwnerReference((OwnerReference) null)); + () -> target.addOwnerReference((OwnerReference) null)); String msg = iae.getMessage(); assertTrue(msg.contains("null reference to unnamed " + target.getKind())); } @@ -156,11 +151,11 @@ void addingReferenceToOwnerWithMissingFieldsShouldFail() { HasMetadata owner = new InvalidOwner(); IllegalArgumentException iae = assertThrows(IllegalArgumentException.class, - () -> hasMetadata.addOwnerReference(owner)); + () -> hasMetadata.addOwnerReference(owner)); final String msg = iae.getMessage(); assertTrue( - msg.contains("uid") && msg.contains("apiVersion") && msg.contains("name") && msg.contains( - "kind")); + msg.contains("uid") && msg.contains("apiVersion") && msg.contains("name") && msg.contains( + "kind")); } @Test @@ -235,6 +230,99 @@ void addingOwnerReferenceToResourceInDifferentNamespaceShouldFail() { assertThrows(IllegalArgumentException.class, () -> namespaced1.addOwnerReference(namespaced2)); } + @Test + void testEmptyCopyOf() { + TestNamespacedHasMetadata original = new TestNamespacedHasMetadata(); + ObjectMeta originalMetadata = new ObjectMetaBuilder() + .withName("testName") + .withNamespace("testNamespace") + .withGeneration(100000L) + .build(); + original.setMetadata(originalMetadata); + + TestNamespacedHasMetadata copy = HasMetadata.emptyCopyOf(original, TestNamespacedHasMetadata::new); + + final ObjectMeta metadata = copy.getMetadata(); + assertEquals(originalMetadata.getName(), metadata.getName()); + assertEquals(originalMetadata.getNamespace(), metadata.getNamespace()); + assertNull(metadata.getGeneration()); + } + + @Test + void testEmptyClusterWideCopyOf() { + TestHasMetadata original = new TestHasMetadata(); + ObjectMeta originalMetadata = new ObjectMetaBuilder() + .withName("testName") + .withGeneration(100000L) + .build(); + original.setMetadata(originalMetadata); + + TestHasMetadata copy = HasMetadata.emptyClusterWideCopyOf(original, TestHasMetadata::new); + + final ObjectMeta metadata = copy.getMetadata(); + assertEquals(originalMetadata.getName(), metadata.getName()); + assertNull(metadata.getNamespace()); + assertNull(metadata.getGeneration()); + } + + @Test + void emptyCopyOfWithNullMetadataShouldFail() { + TestNamespacedHasMetadata original = new TestNamespacedHasMetadata(); + original.setMetadata(null); + + Exception exception = assertThrows(NullPointerException.class, () -> HasMetadata.emptyCopyOf(original, TestNamespacedHasMetadata::new)); + assertEquals(HasMetadata.REQUIRES_NON_NULL_METADATA, exception.getMessage()); + } + + @Test + void emptyClusterWideCopyOfWithNullMetadataShouldFail() { + TestHasMetadata original = new TestHasMetadata(); + original.setMetadata(null); + + Exception exception = assertThrows(NullPointerException.class, () -> HasMetadata.emptyClusterWideCopyOf(original, TestHasMetadata::new)); + assertEquals(HasMetadata.REQUIRES_NON_NULL_METADATA, exception.getMessage()); + } + + @Test + void emptyClusterWideCopyOfWithMissingNameShouldFail() { + TestNamespacedHasMetadata original = new TestNamespacedHasMetadata(); + ObjectMeta originalMetadata = new ObjectMetaBuilder() + .withNamespace("testNamespace") + .build(); + original.setMetadata(originalMetadata); + + Exception exception = assertThrows(NullPointerException.class, () -> HasMetadata.emptyClusterWideCopyOf(original, TestNamespacedHasMetadata::new)); + assertEquals(HasMetadata.REQUIRES_NON_NULL_NAME, exception.getMessage()); + } + + @Test + void canCreateNewNamespacedCopy() { + HasMetadata namespaced1 = new OwnerNamespaced(); + namespaced1.getMetadata().setNamespace("namespace1"); + } + + static class TestHasMetadata implements HasMetadata { + private ObjectMeta metadata; + + @Override + public ObjectMeta getMetadata() { + return metadata; + } + + @Override + public void setMetadata(ObjectMeta metadata) { + this.metadata = metadata; + } + + @Override + public void setApiVersion(String version) { + } + } + + static class TestNamespacedHasMetadata extends TestHasMetadata implements Namespaced { + // No additional fields or methods needed for this simple test implementation + } + @Group("fabric8.io") @Version("v1") private static class Woman implements HasMetadata {