diff --git a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/kubernetes/SSABasedGenericKubernetesResourceMatcher.java b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/kubernetes/SSABasedGenericKubernetesResourceMatcher.java index d31e4f6e7e..1945609079 100644 --- a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/kubernetes/SSABasedGenericKubernetesResourceMatcher.java +++ b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/kubernetes/SSABasedGenericKubernetesResourceMatcher.java @@ -231,6 +231,7 @@ protected void sanitizeState(R actual, R desired, Map actualMap) } } + @SuppressWarnings("unchecked") private static void sanitizeVolumeClaimTemplates( Map actualMap, StatefulSetSpec actualSpec, StatefulSetSpec desiredSpec) { int claims = desiredSpec.getVolumeClaimTemplates().size(); @@ -251,6 +252,18 @@ private static void sanitizeVolumeClaimTemplates( .map(Map.class::cast) .ifPresent(m -> m.remove("status")); } + var desiredResources = claim.getSpec().getResources(); + var actualResources = actualSpec.getVolumeClaimTemplates().get(i).getSpec().getResources(); + if (desiredResources != null && actualResources != null) { + Optional.ofNullable( + GenericKubernetesResource.get( + actualMap, "spec", "volumeClaimTemplates", i, "spec", "resources", "requests")) + .map(Map.class::cast) + .ifPresent( + m -> + QuantitySanitizer.sanitizeQuantities( + m, actualResources.getRequests(), desiredResources.getRequests())); + } } } 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 fda7c4fad3..eb7e54f492 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 @@ -253,7 +253,8 @@ void withFinalizer() { strings = { "sample-sts-volumeclaimtemplates-desired.yaml", "sample-sts-volumeclaimtemplates-desired-with-status.yaml", - "sample-sts-volumeclaimtemplates-desired-with-volumemode.yaml" + "sample-sts-volumeclaimtemplates-desired-with-volumemode.yaml", + "sample-sts-volumeclaimtemplates-desired-with-equivalent-storage.yaml" }) void testSanitizeState_statefulSetWithVolumeClaims(String desiredResourceFileName) { var desiredStatefulSet = loadResource(desiredResourceFileName, StatefulSet.class); diff --git a/operator-framework-core/src/test/resources/io/javaoperatorsdk/operator/processing/dependent/kubernetes/sample-sts-volumeclaimtemplates-desired-with-equivalent-storage.yaml b/operator-framework-core/src/test/resources/io/javaoperatorsdk/operator/processing/dependent/kubernetes/sample-sts-volumeclaimtemplates-desired-with-equivalent-storage.yaml new file mode 100644 index 0000000000..f8daf9d100 --- /dev/null +++ b/operator-framework-core/src/test/resources/io/javaoperatorsdk/operator/processing/dependent/kubernetes/sample-sts-volumeclaimtemplates-desired-with-equivalent-storage.yaml @@ -0,0 +1,50 @@ +# +# Copyright Java Operator SDK Authors +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +# desired StatefulSet with a VolumeClaimTemplate with a numerically equal, non-canonical storage quantity +apiVersion: apps/v1 +kind: StatefulSet +metadata: + name: "test" +spec: + replicas: 1 + selector: + matchLabels: + app: test-app + serviceName: "nginx-service" + template: + metadata: + labels: + app: test-app + spec: + containers: + - name: nginx + image: nginx:1.17.0 + ports: + - containerPort: 80 + volumeMounts: + - name: persistent-storage + mountPath: /usr/share/nginx/html + volumeClaimTemplates: + - metadata: + name: persistent-storage + spec: + accessModes: + - ReadWriteOnce + resources: + requests: + storage: 1024Mi + storageClassName: standard diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/statefulsetdesiredsanitizer/StatefulSetDesiredSanitizerDependentResource.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/statefulsetdesiredsanitizer/StatefulSetDesiredSanitizerDependentResource.java index e4bcaac460..2c40bedc25 100644 --- a/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/statefulsetdesiredsanitizer/StatefulSetDesiredSanitizerDependentResource.java +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/statefulsetdesiredsanitizer/StatefulSetDesiredSanitizerDependentResource.java @@ -16,6 +16,7 @@ package io.javaoperatorsdk.operator.dependent.statefulsetdesiredsanitizer; import io.fabric8.kubernetes.api.model.ObjectMetaBuilder; +import io.fabric8.kubernetes.api.model.Quantity; import io.fabric8.kubernetes.api.model.apps.StatefulSet; import io.javaoperatorsdk.operator.ReconcilerUtilsInternal; import io.javaoperatorsdk.operator.api.reconciler.Context; @@ -39,6 +40,15 @@ protected StatefulSet desired( .withName(primary.getMetadata().getName()) .withNamespace(primary.getMetadata().getNamespace()) .build()); + // the API server stores 1024Mi as 1Gi + template + .getSpec() + .getVolumeClaimTemplates() + .get(0) + .getSpec() + .getResources() + .getRequests() + .put("storage", new Quantity("1024Mi")); return template; }