Skip to content

Commit

Permalink
feat: add factory methods to help with SSA
Browse files Browse the repository at this point in the history
Fixes #6012
  • Loading branch information
metacosm committed May 16, 2024
1 parent 522e795 commit a1f5e07
Show file tree
Hide file tree
Showing 3 changed files with 128 additions and 14 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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();

Expand Down Expand Up @@ -493,4 +496,26 @@ default void removeOwnerReference(HasMetadata owner) {
default Optional<ObjectMeta> optionalMetadata() {
return Optional.ofNullable(getMetadata());
}

static <T extends HasMetadata & Namespaced> T emptyCopyOf(T original, Supplier<T> 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 extends HasMetadata> T emptyClusterWideCopyOf(T original, Supplier<T> 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;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand All @@ -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));
Expand All @@ -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()));
}
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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 {
Expand Down

0 comments on commit a1f5e07

Please sign in to comment.