From 1ebd8a847bcc6bddbc9a0dacfe1b5ea09ce0e53b Mon Sep 17 00:00:00 2001 From: David Sondermann Date: Tue, 21 Oct 2025 13:24:50 +0200 Subject: [PATCH 1/2] test: update type parameters in SSABasedGenericKubernetesResourceMatcherTest for better type safety Signed-off-by: David Sondermann --- ...edGenericKubernetesResourceMatcherTest.java | 18 ++++++------------ 1 file changed, 6 insertions(+), 12 deletions(-) diff --git a/operator-framework-core/src/test/java/io/javaoperatorsdk/operator/processing/dependent/kubernetes/SSABasedGenericKubernetesResourceMatcherTest.java b/operator-framework-core/src/test/java/io/javaoperatorsdk/operator/processing/dependent/kubernetes/SSABasedGenericKubernetesResourceMatcherTest.java index c339e5ebf6..e441516d46 100644 --- a/operator-framework-core/src/test/java/io/javaoperatorsdk/operator/processing/dependent/kubernetes/SSABasedGenericKubernetesResourceMatcherTest.java +++ b/operator-framework-core/src/test/java/io/javaoperatorsdk/operator/processing/dependent/kubernetes/SSABasedGenericKubernetesResourceMatcherTest.java @@ -30,7 +30,7 @@ class SSABasedGenericKubernetesResourceMatcherTest { - private final Context mockedContext = mock(); + private final Context mockedContext = mock(); private final SSABasedGenericKubernetesResourceMatcher matcher = SSABasedGenericKubernetesResourceMatcher.getInstance(); @@ -325,8 +325,8 @@ void testSanitizeState_daemonSet_withResourceTypeMismatch() { void testCustomMatcher_returnsExpectedMatchBasedOnReadOnlyLabel(boolean readOnly) { var dr = new ConfigMapDR(); dr.configureWith( - new KubernetesDependentResourceConfigBuilder() - .withSSAMatcher(new ReadOnlyAwareMatcher()) + new KubernetesDependentResourceConfigBuilder() + .withSSAMatcher(new ReadOnlyAwareMatcher<>()) .build()); var desiredConfigMap = loadResource("configmap.empty-owner-reference-desired.yaml", ConfigMap.class); @@ -334,14 +334,8 @@ void testCustomMatcher_returnsExpectedMatchBasedOnReadOnlyLabel(boolean readOnly var actualConfigMap = loadResource("configmap.empty-owner-reference.yaml", ConfigMap.class); actualConfigMap.getMetadata().getLabels().put("readonly", Boolean.toString(readOnly)); - ConfigMap ignoredPrimary = null; - assertThat( - dr.match( - actualConfigMap, - desiredConfigMap, - ignoredPrimary, - (Context) mockedContext) - .matched()) + HasMetadata primary = mock(); + assertThat(dr.match(actualConfigMap, desiredConfigMap, primary, mockedContext).matched()) .isEqualTo(readOnly); } @@ -391,7 +385,7 @@ private static R loadResource(String fileName, Class clazz) { clazz, SSABasedGenericKubernetesResourceMatcherTest.class, fileName); } - private static class ConfigMapDR extends KubernetesDependentResource { + private static class ConfigMapDR extends KubernetesDependentResource { public ConfigMapDR() { super(ConfigMap.class); } From 5bc7ee9b3d0f6391bc66bb5cfc64dffca84c111d Mon Sep 17 00:00:00 2001 From: David Sondermann Date: Tue, 21 Oct 2025 13:25:15 +0200 Subject: [PATCH 2/2] fix: improve PodTemplateSpec sanitizer for GKE Autopilot compatibility Signed-off-by: David Sondermann --- .../kubernetes/PodTemplateSpecSanitizer.java | 1 - .../PodTemplateSpecSanitizerTest.java | 69 +++++++++++++++---- 2 files changed, 56 insertions(+), 14 deletions(-) diff --git a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/kubernetes/PodTemplateSpecSanitizer.java b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/kubernetes/PodTemplateSpecSanitizer.java index 962059961e..fd1dcff49c 100644 --- a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/kubernetes/PodTemplateSpecSanitizer.java +++ b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/kubernetes/PodTemplateSpecSanitizer.java @@ -124,7 +124,6 @@ private static void sanitizeQuantities( "resources", quantityPath)) .map(Map.class::cast) - .filter(m -> m.size() == desiredResource.size()) .ifPresent( m -> actualResource.forEach( diff --git a/operator-framework-core/src/test/java/io/javaoperatorsdk/operator/processing/dependent/kubernetes/PodTemplateSpecSanitizerTest.java b/operator-framework-core/src/test/java/io/javaoperatorsdk/operator/processing/dependent/kubernetes/PodTemplateSpecSanitizerTest.java index 091a1a666c..e7756b45bc 100644 --- a/operator-framework-core/src/test/java/io/javaoperatorsdk/operator/processing/dependent/kubernetes/PodTemplateSpecSanitizerTest.java +++ b/operator-framework-core/src/test/java/io/javaoperatorsdk/operator/processing/dependent/kubernetes/PodTemplateSpecSanitizerTest.java @@ -132,19 +132,6 @@ void testSanitizePodTemplateSpec_whenResourceIsNull_doNothing() { verifyNoInteractions(actualMap); } - @Test - void testSanitizeResourceRequirements_whenResourceSizeMismatch_doNothing() { - final var actualMap = - sanitizeRequestsAndLimits( - ContainerType.CONTAINER, - Map.of("cpu", new Quantity("2")), - Map.of(), - Map.of("cpu", new Quantity("4")), - Map.of("cpu", new Quantity("4"), "memory", new Quantity("4Gi"))); - assertContainerResources(actualMap, "requests").hasSize(1).containsEntry("cpu", "2"); - assertContainerResources(actualMap, "limits").hasSize(1).containsEntry("cpu", "4"); - } - @Test void testSanitizeResourceRequirements_whenResourceKeyMismatch_doNothing() { final var actualMap = @@ -187,6 +174,34 @@ void testSanitizePodTemplateSpec_whenResourcesHaveNumericalAmountMismatch_doNoth assertInitContainerResources(actualMap, "limits").hasSize(1).containsEntry("cpu", "2"); } + @Test + void + testSanitizePodTemplateSpec_whenResourcesHaveNumericalAmountMismatch_withEphemeralStorageAddedByOtherOperator_doNothing() { + // mimics an environment like GKE Autopilot that enforces ephemeral-storage requests and limits + final var actualMap = + sanitizeRequestsAndLimits( + ContainerType.INIT_CONTAINER, + Map.of( + "cpu", + new Quantity("2"), + "memory", + new Quantity("4Gi"), + "ephemeral-storage", + new Quantity("1Gi")), + Map.of("cpu", new Quantity("4"), "memory", new Quantity("4Ti")), + Map.of("cpu", new Quantity("2"), "ephemeral-storage", new Quantity("1Gi")), + Map.of("cpu", new Quantity("4000m"))); + assertInitContainerResources(actualMap, "requests") + .hasSize(3) + .containsEntry("cpu", "2") + .containsEntry("memory", "4Gi") + .containsEntry("ephemeral-storage", "1Gi"); + assertInitContainerResources(actualMap, "limits") + .hasSize(2) + .containsEntry("cpu", "2") + .containsEntry("ephemeral-storage", "1Gi"); + } + @Test void testSanitizeResourceRequirements_whenResourcesHaveAmountAndFormatMismatchWithSameNumericalAmount_thenSanitizeActualMap() { @@ -204,6 +219,34 @@ void testSanitizePodTemplateSpec_whenResourcesHaveNumericalAmountMismatch_doNoth assertContainerResources(actualMap, "limits").hasSize(1).containsEntry("cpu", "4000m"); } + @Test + void + testSanitizeResourceRequirements_whenResourcesHaveAmountAndFormatMismatchWithSameNumericalAmount_withEphemeralStorageAddedByOtherOperator_thenSanitizeActualMap() { + // mimics an environment like GKE Autopilot that enforces ephemeral-storage requests and limits + final var actualMap = + sanitizeRequestsAndLimits( + ContainerType.CONTAINER, + Map.of( + "cpu", + new Quantity("2"), + "memory", + new Quantity("4Gi"), + "ephemeral-storage", + new Quantity("1Gi")), + Map.of("cpu", new Quantity("2000m"), "memory", new Quantity("4096Mi")), + Map.of("cpu", new Quantity("4"), "ephemeral-storage", new Quantity("1Gi")), + Map.of("cpu", new Quantity("4000m"))); + assertContainerResources(actualMap, "requests") + .hasSize(3) + .containsEntry("cpu", "2000m") + .containsEntry("memory", "4096Mi") + .containsEntry("ephemeral-storage", "1Gi"); + assertContainerResources(actualMap, "limits") + .hasSize(2) + .containsEntry("cpu", "4000m") + .containsEntry("ephemeral-storage", "1Gi"); + } + @Test void testSanitizePodTemplateSpec_whenEnvVarsIsEmpty_doNothing() { final var template =