This is an automated email from the ASF dual-hosted git repository.

houston pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/solr-operator.git


The following commit(s) were added to refs/heads/main by this push:
     new cb30ca2  Fix resource checking, to not overwrite values that should be 
equal. (#398)
cb30ca2 is described below

commit cb30ca2f4a6ad8f31c2bac63a7c511ad22f279d7
Author: Houston Putman <[email protected]>
AuthorDate: Fri Feb 11 13:24:40 2022 -0500

    Fix resource checking, to not overwrite values that should be equal. (#398)
---
 controllers/util/common.go      | 52 +++++++++++++++++++---
 controllers/util/common_test.go | 95 +++++++++++++++++++++++++++++++++++++++++
 controllers/util/zk_util.go     | 18 +-------
 helm/solr-operator/Chart.yaml   |  7 +++
 4 files changed, 149 insertions(+), 23 deletions(-)

diff --git a/controllers/util/common.go b/controllers/util/common.go
index f5e8efc..5fb57f4 100644
--- a/controllers/util/common.go
+++ b/controllers/util/common.go
@@ -497,7 +497,7 @@ func CopyPodContainers(fromPtr, toPtr *[]corev1.Container, 
basePath string, logg
        from := *fromPtr
        if len(to) != len(from) {
                requireUpdate = true
-               logger.Info("Update required because field changed", "field", 
basePath+"Length", "from", len(to), "to", len(from))
+               logger.Info("Update required because field changed", "field", 
basePath+".Length", "from", len(to), "to", len(from))
                *toPtr = from
        } else {
                for i := 0; i < len(from); i++ {
@@ -540,11 +540,7 @@ func CopyPodContainers(fromPtr, toPtr *[]corev1.Container, 
basePath string, logg
                                to[i].Env = from[i].Env
                        }
 
-                       if !DeepEqualWithNils(to[i].Resources, 
from[i].Resources) {
-                               requireUpdate = true
-                               logger.Info("Update required because field 
changed", "field", containerBasePath+"Resources", "from", to[i].Resources, 
"to", from[i].Resources)
-                               to[i].Resources = from[i].Resources
-                       }
+                       requireUpdate = CopyResources(&from[i].Resources, 
&to[i].Resources, containerBasePath+"Resources.", logger) || requireUpdate
 
                        if !DeepEqualWithNils(to[i].VolumeMounts, 
from[i].VolumeMounts) {
                                requireUpdate = true
@@ -609,7 +605,7 @@ func CopyPodVolumes(fromPtr, toPtr *[]corev1.Volume, 
basePath string, logger log
        from := *fromPtr
        if len(to) != len(from) {
                requireUpdate = true
-               logger.Info("Update required because field changed", "field", 
basePath+"Length", "from", len(to), "to", len(from))
+               logger.Info("Update required because field changed", "field", 
basePath+".Length", "from", len(to), "to", len(from))
                *toPtr = from
        } else {
                for i := 0; i < len(from); i++ {
@@ -630,6 +626,48 @@ func CopyPodVolumes(fromPtr, toPtr *[]corev1.Volume, 
basePath string, logger log
        return requireUpdate
 }
 
+func CopyResources(from, to *corev1.ResourceRequirements, basePath string, 
logger logr.Logger) (requireUpdate bool) {
+
+       requireUpdate = CopyContainerResourceList(&from.Requests, &to.Requests, 
basePath+"Requests", logger) || requireUpdate
+
+       requireUpdate = CopyContainerResourceList(&from.Limits, &to.Limits, 
basePath+"Limits", logger) || requireUpdate
+
+       return requireUpdate
+}
+
+func CopyContainerResourceList(fromPtr, toPtr *corev1.ResourceList, basePath 
string, logger logr.Logger) (requireUpdate bool) {
+       to := *toPtr
+       from := *fromPtr
+       copyEntireMap := false
+       if len(to) != len(from) {
+               requireUpdate = true
+               logger.Info("Update required because field changed", "field", 
basePath+".Length", "from", len(to), "to", len(from))
+               copyEntireMap = true
+       } else {
+               for resourceName, newQuantity := range from {
+                       resourceBasePath := basePath + "[" + 
resourceName.String() + "]"
+                       if oldQuantity, isThere := to[resourceName]; isThere {
+                               if !oldQuantity.Equal(newQuantity) {
+                                       requireUpdate = true
+                                       logger.Info("Update required because 
field changed", "field", resourceBasePath, "from", oldQuantity, "to", 
newQuantity)
+                                       to[resourceName] = newQuantity
+                               }
+                       } else {
+                               // This means the keys of the resource maps 
(from & to) are not the same, therefore update the entire map.
+                               // We will still loop through the rest of the 
resources to print out any additional updates required,
+                               // otherwise the logging could miss resource 
quantity changes.
+                               requireUpdate = true
+                               logger.Info("Update required because field 
changed", "field", resourceBasePath, "from", nil, "to", newQuantity)
+                               copyEntireMap = true
+                       }
+               }
+       }
+       if copyEntireMap {
+               *toPtr = from
+       }
+       return requireUpdate
+}
+
 // OvertakeControllerRef makes sure that the controlled object has the owner 
as the controller ref.
 // If the object has a different controller, then that ref will be downgraded 
to an "owner" and the new controller ref will be added
 func OvertakeControllerRef(owner metav1.Object, controlled metav1.Object, 
scheme *runtime.Scheme) (needsUpdate bool, err error) {
diff --git a/controllers/util/common_test.go b/controllers/util/common_test.go
new file mode 100644
index 0000000..c968c73
--- /dev/null
+++ b/controllers/util/common_test.go
@@ -0,0 +1,95 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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.
+ */
+
+package util
+
+import (
+       "github.com/stretchr/testify/assert"
+       corev1 "k8s.io/api/core/v1"
+       "k8s.io/apimachinery/pkg/api/resource"
+       ctrl "sigs.k8s.io/controller-runtime"
+       "testing"
+)
+
+func TestCopyResources(t *testing.T) {
+       log := ctrl.Log
+
+       newResources := &corev1.ResourceRequirements{
+               Requests: map[corev1.ResourceName]resource.Quantity{
+                       corev1.ResourceCPU: *resource.NewQuantity(400, 
resource.DecimalSI),
+               },
+       }
+       baseResources := &corev1.ResourceRequirements{
+               Limits: map[corev1.ResourceName]resource.Quantity{},
+       }
+       assert.True(t, CopyResources(newResources, baseResources, "", log))
+       assert.NotNil(t, baseResources.Limits, "Limits should still exist but 
be empty after the copy. 'nil' does not overwrite an empty list/map")
+       assert.Len(t, baseResources.Limits, 0, "Incorrect length for resource 
limits")
+       assert.NotNil(t, baseResources.Requests, "Requests should exist after 
the copy")
+       assert.Len(t, baseResources.Requests, 1, "Incorrect length for resource 
requests")
+
+       newResources.Requests = map[corev1.ResourceName]resource.Quantity{
+               corev1.ResourceCPU: *resource.NewQuantity(400, 
resource.BinarySI),
+       }
+       assert.False(t, CopyResources(newResources, baseResources, "", log), 
"No change should be detected if only the format of a quantity changes")
+
+       baseResources.Limits = nil
+       newResources.Limits = map[corev1.ResourceName]resource.Quantity{}
+       assert.False(t, CopyResources(newResources, baseResources, "", log), 
"No change should be detected if a map goes from nil to empty")
+
+       newResources.Requests = map[corev1.ResourceName]resource.Quantity{
+               corev1.ResourceCPU: *resource.NewQuantity(600, 
resource.BinarySI),
+       }
+       assert.True(t, CopyResources(newResources, baseResources, "", log), "A 
change should be detected if a resource quantity changes")
+       assert.Len(t, baseResources.Requests, 1, "Incorrect length for resource 
requests")
+       assert.Contains(t, baseResources.Requests, corev1.ResourceCPU, "CPU 
should now be in the resource requests")
+       assert.Equal(t, resource.NewQuantity(600, resource.BinarySI), 
baseResources.Requests.Cpu(), "Incorrect value for request cpu")
+
+       newResources.Requests = map[corev1.ResourceName]resource.Quantity{
+               corev1.ResourceMemory: *resource.NewQuantity(400, 
resource.BinarySI),
+       }
+       assert.True(t, CopyResources(newResources, baseResources, "", log), "A 
change should be detected when completely changing fields, though staying at 
the same length")
+       assert.Len(t, baseResources.Requests, 1, "Incorrect length for resource 
requests")
+       assert.NotContains(t, baseResources.Requests, corev1.ResourceCPU, "CPU 
should no longer be in the resource requests")
+       assert.Contains(t, baseResources.Requests, corev1.ResourceMemory, 
"Memory should now be in the resource requests")
+       assert.Equal(t, resource.NewQuantity(400, resource.BinarySI), 
baseResources.Requests.Memory(), "Incorrect value for request memory")
+
+       newResources.Requests = map[corev1.ResourceName]resource.Quantity{
+               corev1.ResourceMemory: *resource.NewQuantity(600, 
resource.BinarySI),
+               corev1.ResourceCPU:    *resource.NewQuantity(500, 
resource.BinarySI),
+       }
+       assert.True(t, CopyResources(newResources, baseResources, "", log), "A 
change should be detected when adding resources")
+       assert.Len(t, baseResources.Requests, 2, "Incorrect length for resource 
requests")
+       assert.Contains(t, baseResources.Requests, corev1.ResourceCPU, "CPU 
should now be in the resource requests")
+       assert.Equal(t, resource.NewQuantity(500, resource.BinarySI), 
baseResources.Requests.Cpu(), "Incorrect value for request cpu")
+       assert.Contains(t, baseResources.Requests, corev1.ResourceMemory, 
"Memory should still be in the resource requests")
+       assert.Equal(t, resource.NewQuantity(600, resource.BinarySI), 
baseResources.Requests.Memory(), "Incorrect value for request memory")
+
+       newResources.Requests = map[corev1.ResourceName]resource.Quantity{
+               corev1.ResourceCPU: *resource.NewQuantity(500, 
resource.BinarySI),
+       }
+       assert.True(t, CopyResources(newResources, baseResources, "", log), "A 
change should be detected when removing resources")
+       assert.Len(t, baseResources.Requests, 1, "Incorrect length for resource 
requests")
+       assert.Contains(t, baseResources.Requests, corev1.ResourceCPU, "CPU 
should still be in the resource requests")
+       assert.Equal(t, resource.NewQuantity(500, resource.BinarySI), 
baseResources.Requests.Cpu(), "Incorrect value for request cpu")
+       assert.NotContains(t, baseResources.Requests, corev1.ResourceMemory, 
"Memory should no longer be in the resource requests")
+
+       newResources = &corev1.ResourceRequirements{}
+       assert.True(t, CopyResources(newResources, baseResources, "", log), "A 
change should be detected when removing everything")
+       assert.Len(t, baseResources.Requests, 0, "Incorrect length for resource 
requests")
+       assert.Nil(t, baseResources.Requests, "Resource requests should be nil")
+}
diff --git a/controllers/util/zk_util.go b/controllers/util/zk_util.go
index 980a42f..00a7b30 100644
--- a/controllers/util/zk_util.go
+++ b/controllers/util/zk_util.go
@@ -206,17 +206,7 @@ func CopyZookeeperClusterFields(from, to 
*zk_api.ZookeeperCluster, logger logr.L
                                requireUpdate = true
                                to.Spec.Persistence = from.Spec.Persistence
                        } else {
-                               if 
!DeepEqualWithNils(to.Spec.Persistence.PersistentVolumeClaimSpec.Resources.Requests,
 from.Spec.Persistence.PersistentVolumeClaimSpec.Resources.Requests) {
-                                       logger.Info("Update required because 
field changed", "field", 
"Spec.Persistence.PersistentVolumeClaimSpec.Resources.Requests", "from", 
to.Spec.Persistence.PersistentVolumeClaimSpec.Resources.Requests, "to", 
from.Spec.Persistence.PersistentVolumeClaimSpec.Resources.Requests)
-                                       requireUpdate = true
-                                       
to.Spec.Persistence.PersistentVolumeClaimSpec.Resources.Requests = 
from.Spec.Persistence.PersistentVolumeClaimSpec.Resources.Requests
-                               }
-
-                               if 
!DeepEqualWithNils(to.Spec.Persistence.PersistentVolumeClaimSpec.Resources.Limits,
 from.Spec.Persistence.PersistentVolumeClaimSpec.Resources.Limits) {
-                                       logger.Info("Update required because 
field changed", "field", 
"Spec.Persistence.PersistentVolumeClaimSpec.Resources.Limits", "from", 
to.Spec.Persistence.PersistentVolumeClaimSpec.Resources.Limits, "to", 
from.Spec.Persistence.PersistentVolumeClaimSpec.Resources.Limits)
-                                       requireUpdate = true
-                                       
to.Spec.Persistence.PersistentVolumeClaimSpec.Resources.Limits = 
from.Spec.Persistence.PersistentVolumeClaimSpec.Resources.Limits
-                               }
+                               requireUpdate = 
CopyResources(&from.Spec.Persistence.PersistentVolumeClaimSpec.Resources, 
&to.Spec.Persistence.PersistentVolumeClaimSpec.Resources, 
"Spec.Persistence.PersistentVolumeClaimSpec.Resources.", logger) || 
requireUpdate
 
                                if 
!DeepEqualWithNils(to.Spec.Persistence.PersistentVolumeClaimSpec.AccessModes, 
from.Spec.Persistence.PersistentVolumeClaimSpec.AccessModes) {
                                        logger.Info("Update required because 
field changed", "field", 
"Spec.Persistence.PersistentVolumeClaimSpec.AccessModes", "from", 
to.Spec.Persistence.PersistentVolumeClaimSpec.AccessModes, "to", 
from.Spec.Persistence.PersistentVolumeClaimSpec.AccessModes)
@@ -278,11 +268,7 @@ func CopyZookeeperClusterFields(from, to 
*zk_api.ZookeeperCluster, logger logr.L
                }
        }
 
-       if !DeepEqualWithNils(to.Spec.Pod.Resources, from.Spec.Pod.Resources) {
-               logger.Info("Update required because field changed", "field", 
"Spec.Pod.Resources", "from", to.Spec.Pod.Resources, "to", 
from.Spec.Pod.Resources)
-               requireUpdate = true
-               to.Spec.Pod.Resources = from.Spec.Pod.Resources
-       }
+       requireUpdate = CopyResources(&from.Spec.Pod.Resources, 
&to.Spec.Pod.Resources, "Spec.Pod.Resources.", logger) || requireUpdate
 
        if !DeepEqualWithNils(to.Spec.Pod.Env, from.Spec.Pod.Env) {
                logger.Info("Update required because field changed", "field", 
"Spec.Pod.Env", "from", to.Spec.Pod.Env, "to", from.Spec.Pod.Env)
diff --git a/helm/solr-operator/Chart.yaml b/helm/solr-operator/Chart.yaml
index 0baf77f..7aff0e1 100644
--- a/helm/solr-operator/Chart.yaml
+++ b/helm/solr-operator/Chart.yaml
@@ -84,6 +84,13 @@ annotations:
           url: https://github.com/apache/solr-operator/issues/391
         - name: Github PR
           url: https://github.com/apache/solr-operator/pull/394
+    - kind: fixed
+      description: Correctly check whether resource limits & requests have 
changed when updating children objects
+      links:
+        - name: Github Issue
+          url: https://github.com/apache/solr-operator/issues/393
+        - name: Github PR
+          url: https://github.com/apache/solr-operator/pull/398
   artifacthub.io/images: |
     - name: solr-operator
       image: apache/solr-operator:v0.6.0-prerelease

Reply via email to