Fixes for checking if kube objects should be updated

- Issue with passing an array to a method and assigning value to the
  array
- Deep dive into Ingress rules instead of an overall DeepEquals

Signed-off-by: Houston Putman <houston@apache.org>
diff --git a/controllers/solrcloud_controller.go b/controllers/solrcloud_controller.go
index 3200513..d4c23cf 100644
--- a/controllers/solrcloud_controller.go
+++ b/controllers/solrcloud_controller.go
@@ -112,11 +112,9 @@
 
 	newStatus := solr.SolrCloudStatus{}
 
-	busyBoxImage := *instance.Spec.BusyBoxImage
-
 	blockReconciliationOfStatefulSet := false
 
-	if err := reconcileZk(r, logger, instance, busyBoxImage, &newStatus); err != nil {
+	if err := reconcileZk(r, logger, instance, &newStatus); err != nil {
 		return requeueOrNot, err
 	}
 
@@ -127,18 +125,18 @@
 	}
 
 	// Check if the Common Service already exists
+	commonServiceLogger := logger.WithValues("service", commonService.Name)
 	foundCommonService := &corev1.Service{}
 	err = r.Get(context.TODO(), types.NamespacedName{Name: commonService.Name, Namespace: commonService.Namespace}, foundCommonService)
 	if err != nil && errors.IsNotFound(err) {
-		logger.Info("Creating Common Service", "service", commonService.Name)
+		commonServiceLogger.Info("Creating Common Service")
 		err = r.Create(context.TODO(), commonService)
-	} else if err == nil {
-		if util.CopyServiceFields(commonService, foundCommonService) {
-			// Update the found Service and write the result back if there are any changes
-			logger.Info("Updating Common Service", "service", commonService.Name)
-			err = r.Update(context.TODO(), foundCommonService)
-		}
-	} else {
+	} else if err == nil && util.CopyServiceFields(commonService, foundCommonService, commonServiceLogger) {
+		// Update the found Service and write the result back if there are any changes
+		commonServiceLogger.Info("Updating Common Service")
+		err = r.Update(context.TODO(), foundCommonService)
+	}
+	if err != nil {
 		return requeueOrNot, err
 	}
 
@@ -172,14 +170,15 @@
 		}
 
 		// Check if the HeadlessService already exists
+		headlessServiceLogger := logger.WithValues("service", headless.Name)
 		foundHeadless := &corev1.Service{}
 		err = r.Get(context.TODO(), types.NamespacedName{Name: headless.Name, Namespace: headless.Namespace}, foundHeadless)
 		if err != nil && errors.IsNotFound(err) {
-			logger.Info("Creating HeadlessService", "service", headless.Name)
+			headlessServiceLogger.Info("Creating HeadlessService")
 			err = r.Create(context.TODO(), headless)
-		} else if err == nil && util.CopyServiceFields(headless, foundHeadless) {
+		} else if err == nil && util.CopyServiceFields(headless, foundHeadless, headlessServiceLogger) {
 			// Update the found HeadlessService and write the result back if there are any changes
-			logger.Info("Updating HeadlessService", "service", headless.Name)
+			headlessServiceLogger.Info("Updating HeadlessService")
 			err = r.Update(context.TODO(), foundHeadless)
 		}
 		if err != nil {
@@ -226,15 +225,16 @@
 		}
 
 		// Check if the ConfigMap already exists
+		configMapLogger := logger.WithValues("configMap", configMap.Name)
 		foundConfigMap := &corev1.ConfigMap{}
 		err = r.Get(context.TODO(), types.NamespacedName{Name: configMap.Name, Namespace: configMap.Namespace}, foundConfigMap)
 		if err != nil && errors.IsNotFound(err) {
-			logger.Info("Creating ConfigMap", "namespace", configMap.Namespace, "name", configMap.Name)
+			configMapLogger.Info("Creating ConfigMap")
 			err = r.Create(context.TODO(), configMap)
 			solrXmlMd5 = fmt.Sprintf("%x", md5.Sum([]byte(configMap.Data["solr.xml"])))
-		} else if err == nil && util.CopyConfigMapFields(configMap, foundConfigMap) {
+		} else if err == nil && util.CopyConfigMapFields(configMap, foundConfigMap, configMapLogger) {
 			// Update the found ConfigMap and write the result back if there are any changes
-			logger.Info("Updating ConfigMap", "namespace", configMap.Namespace, "name", configMap.Name)
+			configMapLogger.Info("Updating ConfigMap")
 			err = r.Update(context.TODO(), foundConfigMap)
 			solrXmlMd5 = fmt.Sprintf("%x", md5.Sum([]byte(foundConfigMap.Data["solr.xml"])))
 		}
@@ -259,18 +259,19 @@
 		}
 
 		// Check if the StatefulSet already exists
+		statefulSetLogger := logger.WithValues("statefulSet", statefulSet.Name)
 		foundStatefulSet := &appsv1.StatefulSet{}
 		err = r.Get(context.TODO(), types.NamespacedName{Name: statefulSet.Name, Namespace: statefulSet.Namespace}, foundStatefulSet)
 		if err != nil && errors.IsNotFound(err) {
-			logger.Info("Creating StatefulSet", "statefulSet", statefulSet.Name)
+			statefulSetLogger.Info("Creating StatefulSet")
 			err = r.Create(context.TODO(), statefulSet)
 			// Find which labels the PVCs will be using, to use for the finalizer
 			pvcLabelSelector = statefulSet.Spec.Selector.MatchLabels
 		} else if err == nil {
 			statefulSetStatus = foundStatefulSet.Status
-			if util.CopyStatefulSetFields(statefulSet, foundStatefulSet) {
+			if util.CopyStatefulSetFields(statefulSet, foundStatefulSet, statefulSetLogger) {
 				// Update the found StatefulSet and write the result back if there are any changes
-				logger.Info("Updating StatefulSet", "statefulSet", statefulSet.Name)
+				statefulSetLogger.Info("Updating StatefulSet")
 				err = r.Update(context.TODO(), foundStatefulSet)
 			}
 			// Find which labels the PVCs will be using, to use for the finalizer
@@ -341,14 +342,15 @@
 		}
 
 		// Check if the Ingress already exists
+		ingressLogger := logger.WithValues("ingress", ingress.Name)
 		foundIngress := &extv1.Ingress{}
 		err = r.Get(context.TODO(), types.NamespacedName{Name: ingress.Name, Namespace: ingress.Namespace}, foundIngress)
 		if err != nil && errors.IsNotFound(err) {
-			logger.Info("Creating Common Ingress", "ingress", ingress.Name)
+			ingressLogger.Info("Creating Ingress")
 			err = r.Create(context.TODO(), ingress)
-		} else if err == nil && util.CopyIngressFields(ingress, foundIngress) {
+		} else if err == nil && util.CopyIngressFields(ingress, foundIngress, ingressLogger) {
 			// Update the found Ingress and write the result back if there are any changes
-			logger.Info("Updating Common Ingress", "ingress", ingress.Name)
+			ingressLogger.Info("Updating Ingress")
 			err = r.Update(context.TODO(), foundIngress)
 		}
 		if err != nil {
@@ -481,16 +483,17 @@
 		return err, ip
 	}
 
-	// Check if the Ingress already exists
+	// Check if the Node Service already exists
+	nodeServiceLogger := logger.WithValues("service", service.Name)
 	foundService := &corev1.Service{}
 	err = r.Get(context.TODO(), types.NamespacedName{Name: service.Name, Namespace: service.Namespace}, foundService)
 	if err != nil && errors.IsNotFound(err) {
-		logger.Info("Creating Node Service", "service", service.Name)
+		nodeServiceLogger.Info("Creating Node Service")
 		err = r.Create(context.TODO(), service)
 	} else if err == nil {
-		if util.CopyServiceFields(service, foundService) {
-			// Update the found Ingress and write the result back if there are any changes
-			logger.Info("Updating Node Service", "service", service.Name)
+		if util.CopyServiceFields(service, foundService, nodeServiceLogger) {
+			// Update the found Node service because there are differences between our version and the existing version
+			nodeServiceLogger.Info("Updating Node Service")
 			err = r.Update(context.TODO(), foundService)
 		}
 		ip = foundService.Spec.ClusterIP
@@ -502,7 +505,7 @@
 	return nil, ip
 }
 
-func reconcileZk(r *SolrCloudReconciler, logger logr.Logger, instance *solr.SolrCloud, busyBoxImage solr.ContainerImage, newStatus *solr.SolrCloudStatus) error {
+func reconcileZk(r *SolrCloudReconciler, logger logr.Logger, instance *solr.SolrCloud, newStatus *solr.SolrCloudStatus) error {
 	zkRef := instance.Spec.ZookeeperRef
 
 	if zkRef.ConnectionInfo != nil {
@@ -519,15 +522,16 @@
 		}
 
 		// Check if the ZookeeperCluster already exists
+		zkLogger := logger.WithValues("zookeeperCluster", zkCluster.Name)
 		foundZkCluster := &zk.ZookeeperCluster{}
 		err := r.Get(context.TODO(), types.NamespacedName{Name: zkCluster.Name, Namespace: zkCluster.Namespace}, foundZkCluster)
 		if err != nil && errors.IsNotFound(err) {
-			logger.Info("Creating Zookeeer Cluster", "namespace", zkCluster.Namespace, "name", zkCluster.Name)
+			zkLogger.Info("Creating Zookeeer Cluster")
 			err = r.Create(context.TODO(), zkCluster)
 		} else if err == nil {
-			if util.CopyZookeeperClusterFields(zkCluster, foundZkCluster) {
+			if util.CopyZookeeperClusterFields(zkCluster, foundZkCluster, zkLogger) {
 				// Update the found ZookeeperCluster and write the result back if there are any changes
-				logger.Info("Updating Zookeeer Cluster", "namespace", zkCluster.Namespace, "name", zkCluster.Name)
+				zkLogger.Info("Updating Zookeeer Cluster")
 				err = r.Update(context.TODO(), foundZkCluster)
 			}
 			external := &foundZkCluster.Status.ExternalClientEndpoint
@@ -612,7 +616,6 @@
 		if err != nil {
 			return err
 		}
-		logger.Info("Checking for PVC Orphans in SolrCloud", "PVC Count", len(pvcList.Items), "ReadyReplicas Count", cloud.Status.ReadyReplicas)
 		if len(pvcList.Items) > int(*cloud.Spec.Replicas) {
 			if err != nil {
 				return err
diff --git a/controllers/solrprometheusexporter_controller.go b/controllers/solrprometheusexporter_controller.go
index 07afd4a..af2144f 100644
--- a/controllers/solrprometheusexporter_controller.go
+++ b/controllers/solrprometheusexporter_controller.go
@@ -52,7 +52,8 @@
 
 func (r *SolrPrometheusExporterReconciler) Reconcile(req ctrl.Request) (ctrl.Result, error) {
 	_ = context.Background()
-	_ = r.Log.WithValues("solrprometheusexporter", req.NamespacedName)
+
+	logger := r.Log.WithValues("namespace", req.Namespace, "solrPrometheusExporter", req.Name)
 
 	// Fetch the SolrPrometheusExporter instance
 	prometheusExporter := &solrv1beta1.SolrPrometheusExporter{}
@@ -69,7 +70,7 @@
 
 	changed := prometheusExporter.WithDefaults()
 	if changed {
-		r.Log.Info("Setting default settings for Solr PrometheusExporter", "namespace", prometheusExporter.Namespace, "name", prometheusExporter.Name)
+		logger.Info("Setting default settings for Solr PrometheusExporter")
 		if err := r.Update(context.TODO(), prometheusExporter); err != nil {
 			return ctrl.Result{}, err
 		}
@@ -84,14 +85,15 @@
 		}
 
 		// Check if the ConfigMap already exists
+		configMapLogger := logger.WithValues("configMap", configMap.Name)
 		foundConfigMap := &corev1.ConfigMap{}
 		err = r.Get(context.TODO(), types.NamespacedName{Name: configMap.Name, Namespace: configMap.Namespace}, foundConfigMap)
 		if err != nil && errors.IsNotFound(err) {
-			r.Log.Info("Creating PrometheusExporter ConfigMap", "namespace", configMap.Namespace, "name", configMap.Name)
+			configMapLogger.Info("Creating ConfigMap")
 			err = r.Create(context.TODO(), configMap)
-		} else if err == nil && util.CopyConfigMapFields(configMap, foundConfigMap) {
+		} else if err == nil && util.CopyConfigMapFields(configMap, foundConfigMap, configMapLogger) {
 			// Update the found ConfigMap and write the result back if there are any changes
-			r.Log.Info("Updating PrometheusExporter ConfigMap", "namespace", configMap.Namespace, "name", configMap.Name)
+			configMapLogger.Info("Updating ConfigMap")
 			err = r.Update(context.TODO(), foundConfigMap)
 		}
 		if err != nil {
@@ -106,14 +108,15 @@
 	}
 
 	// Check if the Metrics Service already exists
+	serviceLogger := logger.WithValues("service", metricsService.Name)
 	foundMetricsService := &corev1.Service{}
 	err = r.Get(context.TODO(), types.NamespacedName{Name: metricsService.Name, Namespace: metricsService.Namespace}, foundMetricsService)
 	if err != nil && errors.IsNotFound(err) {
-		r.Log.Info("Creating PrometheusExporter Service", "namespace", metricsService.Namespace, "name", metricsService.Name)
+		serviceLogger.Info("Creating Service")
 		err = r.Create(context.TODO(), metricsService)
-	} else if err == nil && util.CopyServiceFields(metricsService, foundMetricsService) {
+	} else if err == nil && util.CopyServiceFields(metricsService, foundMetricsService, serviceLogger) {
 		// Update the found Metrics Service and write the result back if there are any changes
-		r.Log.Info("Updating PrometheusExporter Service", "namespace", metricsService.Namespace, "name", metricsService.Name)
+		serviceLogger.Info("Updating Service")
 		err = r.Update(context.TODO(), foundMetricsService)
 	}
 	if err != nil {
@@ -131,27 +134,31 @@
 		return ctrl.Result{}, err
 	}
 
+	ready := false
+	// Check if the Metrics Deployment already exists
+	deploymentLogger := logger.WithValues("service", metricsService.Name)
 	foundDeploy := &appsv1.Deployment{}
 	err = r.Get(context.TODO(), types.NamespacedName{Name: deploy.Name, Namespace: deploy.Namespace}, foundDeploy)
 	if err != nil && errors.IsNotFound(err) {
-		r.Log.Info("Creating PrometheusExporter Deployment", "namespace", deploy.Namespace, "name", deploy.Name)
+		deploymentLogger.Info("Creating Deployment", "namespace", deploy.Namespace, "name", deploy.Name)
 		err = r.Create(context.TODO(), deploy)
 	} else if err == nil {
-		if util.CopyDeploymentFields(deploy, foundDeploy) {
-			r.Log.Info("Updating PrometheusExporter Deployment", "namespace", deploy.Namespace, "name", deploy.Name)
+		if util.CopyDeploymentFields(deploy, foundDeploy, deploymentLogger) {
+			deploymentLogger.Info("Updating Deployment", "namespace", deploy.Namespace, "name", deploy.Name)
 			err = r.Update(context.TODO(), foundDeploy)
-			if err != nil {
-				return ctrl.Result{}, err
-			}
 		}
-		ready := foundDeploy.Status.ReadyReplicas > 0
-
-		if ready != prometheusExporter.Status.Ready {
-			prometheusExporter.Status.Ready = ready
-			r.Log.Info("Updating status for solr-prometheus-exporter", "namespace", prometheusExporter.Namespace, "name", prometheusExporter.Name)
-			err = r.Status().Update(context.TODO(), prometheusExporter)
-		}
+		ready = foundDeploy.Status.ReadyReplicas > 0
 	}
+	if err != nil {
+		return ctrl.Result{}, err
+	}
+
+	if ready != prometheusExporter.Status.Ready {
+		prometheusExporter.Status.Ready = ready
+		logger.Info("Updating status for solr-prometheus-exporter", "namespace", prometheusExporter.Namespace, "name", prometheusExporter.Name)
+		err = r.Status().Update(context.TODO(), prometheusExporter)
+	}
+
 	return ctrl.Result{}, err
 }
 
diff --git a/controllers/util/common.go b/controllers/util/common.go
index a677c31..da0681d 100644
--- a/controllers/util/common.go
+++ b/controllers/util/common.go
@@ -17,6 +17,7 @@
 package util
 
 import (
+	"github.com/go-logr/logr"
 	appsv1 "k8s.io/api/apps/v1"
 	corev1 "k8s.io/api/core/v1"
 	extv1 "k8s.io/api/extensions/v1beta1"
@@ -29,14 +30,14 @@
 // CopyLabelsAndAnnotations copies the labels and annotations from one object to another.
 // Additional Labels and Annotations in the 'to' object will not be removed.
 // Returns true if there are updates required to the object.
-func CopyLabelsAndAnnotations(from, to *metav1.ObjectMeta) (requireUpdate bool) {
+func CopyLabelsAndAnnotations(from, to *metav1.ObjectMeta, logger logr.Logger) (requireUpdate bool) {
 	if len(to.Labels) == 0 && len(from.Labels) > 0 {
 		to.Labels = make(map[string]string, len(from.Labels))
 	}
 	for k, v := range from.Labels {
 		if to.Labels[k] != v {
 			requireUpdate = true
-			log.Info("Update Label", "label", k, "newValue", v, "oldValue", to.Labels[k])
+			logger.Info("Update Label", "label", k, "newValue", v, "oldValue", to.Labels[k])
 			to.Labels[k] = v
 		}
 	}
@@ -47,7 +48,7 @@
 	for k, v := range from.Annotations {
 		if to.Annotations[k] != v {
 			requireUpdate = true
-			log.Info("Update Annotation", "annotation", k, "newValue", v, "oldValue", to.Annotations[k])
+			logger.Info("Update Annotation", "annotation", k, "newValue", v, "oldValue", to.Annotations[k])
 			to.Annotations[k] = v
 		}
 	}
@@ -128,14 +129,15 @@
 }
 
 // CopyConfigMapFields copies the owned fields from one ConfigMap to another
-func CopyConfigMapFields(from, to *corev1.ConfigMap) bool {
-	requireUpdate := CopyLabelsAndAnnotations(&from.ObjectMeta, &to.ObjectMeta)
+func CopyConfigMapFields(from, to *corev1.ConfigMap, logger logr.Logger) bool {
+	logger = logger.WithValues("kind", "configMap")
+	requireUpdate := CopyLabelsAndAnnotations(&from.ObjectMeta, &to.ObjectMeta, logger)
 
 	// Don't copy the entire Spec, because we can't overwrite the clusterIp field
 
 	if !DeepEqualWithNils(to.Data, from.Data) {
 		requireUpdate = true
-		log.Info("Update required because:", "Data changed from", to.Data, "To:", from.Data)
+		logger.Info("Update required because field changed", "field", "Data", "from", to.Data, "to", from.Data)
 	}
 	to.Data = from.Data
 
@@ -143,32 +145,33 @@
 }
 
 // CopyServiceFields copies the owned fields from one Service to another
-func CopyServiceFields(from, to *corev1.Service) bool {
-	requireUpdate := CopyLabelsAndAnnotations(&from.ObjectMeta, &to.ObjectMeta)
+func CopyServiceFields(from, to *corev1.Service, logger logr.Logger) bool {
+	logger = logger.WithValues("kind", "service")
+	requireUpdate := CopyLabelsAndAnnotations(&from.ObjectMeta, &to.ObjectMeta, logger)
 
 	// Don't copy the entire Spec, because we can't overwrite the clusterIp field
 
 	if !DeepEqualWithNils(to.Spec.Selector, from.Spec.Selector) {
 		requireUpdate = true
-		log.Info("Update required because:", "Spec.Selector changed from", to.Spec.Selector, "To:", from.Spec.Selector)
+		logger.Info("Update required because field changed", "field", "Spec.Selector", "from", to.Spec.Selector, "to", from.Spec.Selector)
 	}
 	to.Spec.Selector = from.Spec.Selector
 
 	if !DeepEqualWithNils(to.Spec.Ports, from.Spec.Ports) {
 		requireUpdate = true
-		log.Info("Update required because:", "Spec.Ports changed from", to.Spec.Ports, "To:", from.Spec.Ports)
+		logger.Info("Update required because field changed", "field", "Spec.Ports", "from", to.Spec.Ports, "to", from.Spec.Ports)
 	}
 	to.Spec.Ports = from.Spec.Ports
 
 	if !DeepEqualWithNils(to.Spec.ExternalName, from.Spec.ExternalName) {
 		requireUpdate = true
-		log.Info("Update required because:", "Spec.ExternalName changed from", to.Spec.ExternalName, "To:", from.Spec.ExternalName)
+		logger.Info("Update required because field changed", "field", "Spec.ExternalName", "from", to.Spec.ExternalName, "to", from.Spec.ExternalName)
 	}
 	to.Spec.ExternalName = from.Spec.ExternalName
 
 	if !DeepEqualWithNils(to.Spec.PublishNotReadyAddresses, from.Spec.PublishNotReadyAddresses) {
 		requireUpdate = true
-		log.Info("Update required because:", "Spec.PublishNotReadyAddresses changed from", to.Spec.PublishNotReadyAddresses, "To:", from.Spec.PublishNotReadyAddresses)
+		logger.Info("Update required because field changed", "field", "Spec.PublishNotReadyAddresses", "from", to.Spec.PublishNotReadyAddresses, "to", from.Spec.PublishNotReadyAddresses)
 	}
 	to.Spec.PublishNotReadyAddresses = from.Spec.PublishNotReadyAddresses
 
@@ -176,32 +179,92 @@
 }
 
 // CopyIngressFields copies the owned fields from one Ingress to another
-func CopyIngressFields(from, to *extv1.Ingress) bool {
-	requireUpdate := CopyLabelsAndAnnotations(&from.ObjectMeta, &to.ObjectMeta)
+func CopyIngressFields(from, to *extv1.Ingress, logger logr.Logger) bool {
+	logger = logger.WithValues("kind", "ingress")
+	requireUpdate := CopyLabelsAndAnnotations(&from.ObjectMeta, &to.ObjectMeta, logger)
 
-	if !DeepEqualWithNils(to.Spec.Rules, from.Spec.Rules) {
+	if len(to.Spec.Rules) != len(from.Spec.Rules) {
 		requireUpdate = true
-		log.Info("Update required because:", "Spec.Rules changed from", to.Spec.Rules, "To:", from.Spec.Rules)
+		logger.Info("Update required because field changed", "field", "Spec.Rules", "from", to.Spec.Rules, "to", from.Spec.Rules)
+		to.Spec.Rules = from.Spec.Rules
+	} else {
+		for i := range from.Spec.Rules {
+			ruleBase := "Spec.Rules["+strconv.Itoa(i)+"]."
+			fromRule := &from.Spec.Rules[i]
+			toRule := &to.Spec.Rules[i]
+
+			if !DeepEqualWithNils(toRule.Host, fromRule.Host) {
+				requireUpdate = true
+				logger.Info("Update required because field changed", "field", ruleBase+"Host", "from", toRule.Host, "to", fromRule.Host)
+				toRule.Host = fromRule.Host
+			}
+
+			if fromRule.HTTP == nil || toRule.HTTP == nil {
+				requireUpdate = true
+				logger.Info("Update required because field changed", "field", ruleBase+"HTTP", "from", toRule.HTTP, "to", fromRule.HTTP)
+				toRule.HTTP = fromRule.HTTP
+			} else if len(fromRule.HTTP.Paths) != len(toRule.HTTP.Paths) {
+				requireUpdate = true
+				logger.Info("Update required because field changed", "field", ruleBase+"HTTP.Paths", "from", toRule.HTTP.Paths, "to", fromRule.HTTP.Paths)
+				toRule.HTTP.Paths = fromRule.HTTP.Paths
+			} else {
+				for j := range fromRule.HTTP.Paths {
+					pathBase := ruleBase+"HTTP.Paths["+strconv.Itoa(j)+"]."
+					fromPath := &fromRule.HTTP.Paths[j]
+					toPath := &toRule.HTTP.Paths[j]
+
+					if toPath.PathType != nil && !DeepEqualWithNils(toPath.PathType, fromPath.PathType) {
+						requireUpdate = true
+						logger.Info("Update required because field changed", "field", pathBase+"PathType", "from", toPath.PathType, "to", fromPath.PathType)
+						toPath.PathType = fromPath.PathType
+					}
+
+					if !DeepEqualWithNils(toPath.Path, fromPath.Path) {
+						requireUpdate = true
+						logger.Info("Update required because field changed", "field", pathBase+"Path", "from", toPath.Path, "to", fromPath.Path)
+						toPath.Path = fromPath.Path
+					}
+
+					if !DeepEqualWithNils(toPath.Backend.ServiceName, fromPath.Backend.ServiceName) {
+						requireUpdate = true
+						logger.Info("Update required because field changed", "field", pathBase+"Backend.ServiceName", "from", toPath.Backend.ServiceName, "to", fromPath.Backend.ServiceName)
+						toPath.Backend.ServiceName = fromPath.Backend.ServiceName
+					}
+
+					if !DeepEqualWithNils(toPath.Backend.ServicePort, fromPath.Backend.ServicePort) {
+						requireUpdate = true
+						logger.Info("Update required because field changed", "field", pathBase+"Backend.ServicePort", "from", toPath.Backend.ServicePort, "to", fromPath.Backend.ServicePort)
+						toPath.Backend.ServicePort = fromPath.Backend.ServicePort
+					}
+
+					if !DeepEqualWithNils(toPath.Backend.Resource, fromPath.Backend.Resource) {
+						requireUpdate = true
+						logger.Info("Update required because field changed", "field", pathBase+"Backend.Resource", "from", toPath.Backend.Resource, "to", fromPath.Backend.Resource)
+						toPath.Backend.Resource = fromPath.Backend.Resource
+					}
+				}
+			}
+		}
 	}
-	to.Spec.Rules = from.Spec.Rules
 
 	return requireUpdate
 }
 
 // CopyStatefulSetFields copies the owned fields from one StatefulSet to another
 // Returns true if the fields copied from don't match to.
-func CopyStatefulSetFields(from, to *appsv1.StatefulSet) bool {
-	requireUpdate := CopyLabelsAndAnnotations(&from.ObjectMeta, &to.ObjectMeta)
+func CopyStatefulSetFields(from, to *appsv1.StatefulSet, logger logr.Logger) bool {
+	logger = logger.WithValues("kind", "statefulSet")
+	requireUpdate := CopyLabelsAndAnnotations(&from.ObjectMeta, &to.ObjectMeta, logger)
 
 	if !DeepEqualWithNils(to.Spec.Replicas, from.Spec.Replicas) {
 		requireUpdate = true
-		log.Info("Update required because:", "Spec.Replicas changed from", to.Spec.Replicas, "To:", from.Spec.Replicas)
+		logger.Info("Update required because field changed", "field", "Spec.Replicas", "from", to.Spec.Replicas, "to", from.Spec.Replicas)
 		to.Spec.Replicas = from.Spec.Replicas
 	}
 
 	if !DeepEqualWithNils(to.Spec.UpdateStrategy, from.Spec.UpdateStrategy) {
 		requireUpdate = true
-		log.Info("Update required because:", "Spec.UpdateStrategy changed from", to.Spec.UpdateStrategy, "To:", from.Spec.UpdateStrategy)
+		logger.Info("Update required because field changed", "field", "Spec.UpdateStrategy", "from", to.Spec.UpdateStrategy, "to", from.Spec.UpdateStrategy)
 		to.Spec.UpdateStrategy = from.Spec.UpdateStrategy
 	}
 
@@ -210,13 +273,13 @@
 
 		if !DeepEqualWithNils(to.Spec.Selector, from.Spec.Selector) {
 			requireUpdate = true
-			log.Info("Update required because:", "Spec.Selector changed from", to.Spec.Selector, "To:", from.Spec.Selector)
+			logger.Info("Update required because field changed", "field", "Spec.Selector", "from", to.Spec.Selector, "to", from.Spec.Selector)
 			to.Spec.Selector = from.Spec.Selector
 		}
 
 		if !DeepEqualWithNils(to.Spec.PodManagementPolicy, from.Spec.PodManagementPolicy) {
 			requireUpdate = true
-			log.Info("Update required because:", "Spec.PodManagementPolicy changed from", to.Spec.PodManagementPolicy, "To:", from.Spec.PodManagementPolicy)
+			logger.Info("Update required because field changed", "field", "Spec.PodManagementPolicy", "from", to.Spec.PodManagementPolicy, "to", from.Spec.PodManagementPolicy)
 			to.Spec.PodManagementPolicy = from.Spec.PodManagementPolicy
 		}
 	*/
@@ -227,143 +290,153 @@
 
 		if len(from.Spec.VolumeClaimTemplates) > len(to.Spec.VolumeClaimTemplates) {
 			requireUpdate = true
-			log.Info("Update required because:", "Spec.VolumeClaimTemplates changed from", to.Spec.VolumeClaimTemplates, "To:", from.Spec.VolumeClaimTemplates)
+			logger.Info("Update required because field changed", "field", "Spec.VolumeClaimTemplates", "from", to.Spec.VolumeClaimTemplates, "to", from.Spec.VolumeClaimTemplates)
 			to.Spec.VolumeClaimTemplates = from.Spec.VolumeClaimTemplates
 		}
-		for i, fromVct := range from.Spec.VolumeClaimTemplates {
+		for i := range from.Spec.VolumeClaimTemplates {
+			vctBase := "Spec.VolumeClaimTemplates["+strconv.Itoa(i)+"]."
+			fromVct := &from.Spec.VolumeClaimTemplates[i]
+			toVct := &to.Spec.VolumeClaimTemplates[i]
 			if !DeepEqualWithNils(to.Spec.VolumeClaimTemplates[i].Name, fromVct.Name) {
 				requireUpdate = true
-				log.Info("Update required because:", "Spec.VolumeClaimTemplates["+strconv.Itoa(i)+"].Name changed from", to.Spec.VolumeClaimTemplates[i].Name, "To:", fromVct.Name)
-				to.Spec.VolumeClaimTemplates[i].Name = fromVct.Name
+				logger.Info("Update required because field changed", "field", vctBase+"Name", "from", toVct.Name, "to", fromVct.Name)
+				toVct.Name = fromVct.Name
 			}
 			if !DeepEqualWithNils(to.Spec.VolumeClaimTemplates[i].Labels, fromVct.Labels) {
 				requireUpdate = true
-				log.Info("Update required because:", "Spec.VolumeClaimTemplates["+strconv.Itoa(i)+"].Labels changed from", to.Spec.VolumeClaimTemplates[i].Labels, "To:", fromVct.Labels)
-				to.Spec.VolumeClaimTemplates[i].Labels = fromVct.Labels
+				logger.Info("Update required because field changed", "field", vctBase+"Labels", "from", toVct.Labels, "to", fromVct.Labels)
+				toVct.Labels = fromVct.Labels
 			}
 			if !DeepEqualWithNils(to.Spec.VolumeClaimTemplates[i].Annotations, fromVct.Annotations) {
 				requireUpdate = true
-				log.Info("Update required because:", "Spec.VolumeClaimTemplates["+strconv.Itoa(i)+"].Annotations changed from", to.Spec.VolumeClaimTemplates[i].Annotations, "To:", fromVct.Annotations)
-				to.Spec.VolumeClaimTemplates[i].Annotations = fromVct.Annotations
+				logger.Info("Update required because field changed", "field", vctBase+"Annotations", "from", toVct.Annotations, "to", fromVct.Annotations)
+				toVct.Annotations = fromVct.Annotations
 			}
 			if !DeepEqualWithNils(to.Spec.VolumeClaimTemplates[i].Spec, fromVct.Spec) {
 				requireUpdate = true
-				log.Info("Update required because:", "Spec.VolumeClaimTemplates["+strconv.Itoa(i)+"].Spec changed from", to.Spec.VolumeClaimTemplates[i].Spec, "To:", fromVct.Spec)
-				to.Spec.VolumeClaimTemplates[i].Spec = fromVct.Spec
+				logger.Info("Update required because field changed", "field", vctBase+"Spec", "from", toVct.Spec, "to", fromVct.Spec)
+				toVct.Spec = fromVct.Spec
 			}
 		}
 	*/
 
-	requireUpdate = requireUpdate || CopyPodTemplates(&from.Spec.Template, &to.Spec.Template, "Spec.Template")
+	requireUpdate = requireUpdate || CopyPodTemplates(&from.Spec.Template, &to.Spec.Template, "Spec.Template.", logger)
 
 	return requireUpdate
 }
 
 // CopyDeploymentFields copies the owned fields from one Deployment to another
 // Returns true if the fields copied from don't match to.
-func CopyDeploymentFields(from, to *appsv1.Deployment) bool {
-	requireUpdate := CopyLabelsAndAnnotations(&from.ObjectMeta, &to.ObjectMeta)
+func CopyDeploymentFields(from, to *appsv1.Deployment, logger logr.Logger) bool {
+	logger = logger.WithValues("kind", "deployment")
+	requireUpdate := CopyLabelsAndAnnotations(&from.ObjectMeta, &to.ObjectMeta, logger)
 
 	if !DeepEqualWithNils(to.Spec.Replicas, from.Spec.Replicas) {
 		requireUpdate = true
-		log.Info("Update required because:", "Spec.Replicas changed from", to.Spec.Replicas, "To:", from.Spec.Replicas)
+		logger.Info("Update required because field changed", "field", "Spec.Replicas", "from", to.Spec.Replicas, "to", from.Spec.Replicas)
 		to.Spec.Replicas = from.Spec.Replicas
 	}
 
 	if !DeepEqualWithNils(to.Spec.Selector, from.Spec.Selector) {
 		requireUpdate = true
-		log.Info("Update required because:", "Spec.Selector changed from", to.Spec.Selector, "To:", from.Spec.Selector)
+		logger.Info("Update required because field changed", "field", "Spec.Selector", "from", to.Spec.Selector, "to", from.Spec.Selector)
 		to.Spec.Selector = from.Spec.Selector
 	}
 
-	requireUpdate = requireUpdate || CopyPodTemplates(&from.Spec.Template, &to.Spec.Template, "Spec.Template")
+	requireUpdate = requireUpdate || CopyPodTemplates(&from.Spec.Template, &to.Spec.Template, "Spec.Template.", logger)
 
 	return requireUpdate
 }
 
-func CopyPodTemplates(from, to *corev1.PodTemplateSpec, basePath string) (requireUpdate bool) {
+func CopyPodTemplates(from, to *corev1.PodTemplateSpec, basePath string, logger logr.Logger) (requireUpdate bool) {
+	if basePath == "" {
+		logger = logger.WithValues("kind", "pod")
+	}
 	if !DeepEqualWithNils(to.Labels, from.Labels) {
 		requireUpdate = true
-		log.Info("Update required because:", basePath+".Labels changed from", to.Labels, "To:", from.Labels)
+		logger.Info("Update required because field changed", "field", basePath+"Labels", "from", to.Labels, "to", from.Labels)
 		to.Labels = from.Labels
 	}
 
 	if !DeepEqualWithNils(to.Annotations, from.Annotations) {
 		requireUpdate = true
-		log.Info("Update required because:", basePath+".Annotations changed from", to.Annotations, "To:", from.Annotations)
+		logger.Info("Update required because field changed", "field", basePath+"Annotations", "from", to.Annotations, "to", from.Annotations)
 		to.Annotations = from.Annotations
 	}
 
-	requireUpdate = requireUpdate || CopyPodContainers(from.Spec.Containers, to.Spec.Containers, basePath+".Spec.Containers")
+	requireUpdate = requireUpdate || CopyPodContainers(&from.Spec.Containers, &to.Spec.Containers, basePath+"Spec.Containers", logger)
 
-	requireUpdate = requireUpdate || CopyPodContainers(from.Spec.InitContainers, to.Spec.InitContainers, basePath+".Spec.InitContainers")
+	requireUpdate = requireUpdate || CopyPodContainers(&from.Spec.InitContainers, &to.Spec.InitContainers, basePath+"Spec.InitContainers", logger)
 
 	if !DeepEqualWithNils(to.Spec.HostAliases, from.Spec.HostAliases) {
 		requireUpdate = true
 		to.Spec.HostAliases = from.Spec.HostAliases
-		log.Info("Update required because:", basePath+".Spec.HostAliases changed from", to.Spec.HostAliases, "To:", from.Spec.HostAliases)
+		logger.Info("Update required because field changed", "field", basePath+"Spec.HostAliases", "from", to.Spec.HostAliases, "to", from.Spec.HostAliases)
 	}
 
 	if !DeepEqualWithNils(to.Spec.Volumes, from.Spec.Volumes) {
 		requireUpdate = true
 		to.Spec.Volumes = from.Spec.Volumes
-		log.Info("Update required because:", basePath+".Spec.Volumes changed from", to.Spec.Volumes, "To:", from.Spec.Volumes)
+		logger.Info("Update required because field changed", "field", basePath+"Spec.Volumes", "from", to.Spec.Volumes, "to", from.Spec.Volumes)
 	}
 
 	if !DeepEqualWithNils(to.Spec.ImagePullSecrets, from.Spec.ImagePullSecrets) {
 		requireUpdate = true
-		log.Info("Update required because:", basePath+".Spec.ImagePullSecrets changed from", to.Spec.ImagePullSecrets, "To:", from.Spec.ImagePullSecrets)
+		logger.Info("Update required because field changed", "field", basePath+"Spec.ImagePullSecrets", "from", to.Spec.ImagePullSecrets, "to", from.Spec.ImagePullSecrets)
 		to.Spec.ImagePullSecrets = from.Spec.ImagePullSecrets
 	}
 
 	if !DeepEqualWithNils(to.Spec.Affinity, from.Spec.Affinity) {
 		requireUpdate = true
-		log.Info("Update required because:", basePath+".Spec.Affinity changed from", to.Spec.Affinity, "To:", from.Spec.Affinity)
+		logger.Info("Update required because field changed", "field", basePath+"Spec.Affinity", "from", to.Spec.Affinity, "to", from.Spec.Affinity)
 		to.Spec.Affinity = from.Spec.Affinity
 	}
 
 	if !DeepEqualWithNils(to.Spec.SecurityContext, from.Spec.SecurityContext) {
 		requireUpdate = true
-		log.Info("Update required because:", basePath+".Spec.SecurityContext changed from", to.Spec.SecurityContext, "To:", from.Spec.SecurityContext)
+		logger.Info("Update required because field changed", "field", basePath+"Spec.SecurityContext", "from", to.Spec.SecurityContext, "to", from.Spec.SecurityContext)
 		to.Spec.SecurityContext = from.Spec.SecurityContext
 	}
 
 	if !DeepEqualWithNils(to.Spec.NodeSelector, from.Spec.NodeSelector) {
 		requireUpdate = true
-		log.Info("Update required because:", basePath+".Spec.NodeSelector changed from", to.Spec.NodeSelector, "To:", from.Spec.NodeSelector)
+		logger.Info("Update required because field changed", "field", basePath+"Spec.NodeSelector", "from", to.Spec.NodeSelector, "to", from.Spec.NodeSelector)
 		to.Spec.NodeSelector = from.Spec.NodeSelector
 	}
 
 	if !DeepEqualWithNils(to.Spec.Tolerations, from.Spec.Tolerations) {
 		requireUpdate = true
-		log.Info("Update required because:", basePath+".Spec.Tolerations changed from", to.Spec.Tolerations, "To:", from.Spec.Tolerations)
+		logger.Info("Update required because field changed", "field", basePath+"Spec.Tolerations", "from", to.Spec.Tolerations, "to", from.Spec.Tolerations)
 		to.Spec.Tolerations = from.Spec.Tolerations
 	}
 
 	if !DeepEqualWithNils(to.Spec.PriorityClassName, from.Spec.PriorityClassName) {
 		requireUpdate = true
-		log.Info("Update required because:", basePath+".Spec.PriorityClassName changed from", to.Spec.PriorityClassName, "To:", from.Spec.PriorityClassName)
+		logger.Info("Update required because field changed", "field", basePath+"Spec.PriorityClassName", "from", to.Spec.PriorityClassName, "to", from.Spec.PriorityClassName)
 		to.Spec.PriorityClassName = from.Spec.PriorityClassName
 	}
 
 	return requireUpdate
 }
 
-func CopyPodContainers(from, to []corev1.Container, basePath string) (requireUpdate bool) {
+func CopyPodContainers(fromPtr, toPtr *[]corev1.Container, basePath string, logger logr.Logger) (requireUpdate bool) {
+	to := *toPtr
+	from := *fromPtr
 	if len(to) < len(from) {
 		requireUpdate = true
-		to = from
+		*toPtr = from
 	} else {
 		for i := 0; i < len(from); i++ {
+			containerBasePath := basePath+"["+strconv.Itoa(i)+"]."
 			if !DeepEqualWithNils(to[i].Name, from[i].Name) {
 				requireUpdate = true
-				log.Info("Update required because:", basePath+".Containers["+strconv.Itoa(i)+")].Name changed from", to[i].Name, "To:", from[i].Name)
+				logger.Info("Update required because field changed", "field", containerBasePath+"Name", "from", to[i].Name, "to", from[i].Name)
 				to[i].Name = from[i].Name
 			}
 
 			if !DeepEqualWithNils(to[i].Image, from[i].Image) {
 				requireUpdate = true
-				log.Info("Update required because:", basePath+".Containers["+strconv.Itoa(i)+")].Image changed from", to[i].Image, "To:", from[i].Image)
+				logger.Info("Update required because field changed", "field", containerBasePath+"Image", "from", to[i].Image, "to", from[i].Image)
 				to[i].Image = from[i].Image
 			}
 
@@ -371,79 +444,79 @@
 				// Only request an update if the requestedPullPolicy is not empty
 				// Otherwise kubernetes will specify a defaultPollPolicy and the operator will endlessly recurse, trying to unset the default policy.
 				requireUpdate = true
-				log.Info("Update required because:", basePath+".Containers["+strconv.Itoa(i)+")].ImagePullPolicy changed from", to[i].ImagePullPolicy, "To:", from[i].ImagePullPolicy)
-				to[i].ImagePullPolicy = from[i].ImagePullPolicy
+				logger.Info("Update required because field changed", "field", containerBasePath+"ImagePullPolicy", "from", to[i].ImagePullPolicy, "to", from[i].ImagePullPolicy)
 			}
+			to[i].ImagePullPolicy = from[i].ImagePullPolicy
 
 			if !DeepEqualWithNils(to[i].Command, from[i].Command) {
 				requireUpdate = true
-				log.Info("Update required because:", basePath+".Containers["+strconv.Itoa(i)+")].Command changed from", to[i].Command, "To:", from[i].Command)
+				logger.Info("Update required because field changed", "field", containerBasePath+"Command", "from", to[i].Command, "to", from[i].Command)
 				to[i].Command = from[i].Command
 			}
 
 			if !DeepEqualWithNils(to[i].Args, from[i].Args) {
 				requireUpdate = true
-				log.Info("Update required because:", basePath+".Containers["+strconv.Itoa(i)+")].Args changed from", to[i].Args, "To:", from[i].Args)
+				logger.Info("Update required because field changed", "field", containerBasePath+"Args", "from", to[i].Args, "to", from[i].Args)
 				to[i].Args = from[i].Args
 			}
 
 			if !DeepEqualWithNils(to[i].Env, from[i].Env) {
 				requireUpdate = true
-				log.Info("Update required because:", basePath+".Containers["+strconv.Itoa(i)+")].Env changed from", to[i].Env, "To:", from[i].Env)
+				logger.Info("Update required because field changed", "field", containerBasePath+"Env", "from", to[i].Env, "to", from[i].Env)
 				to[i].Env = from[i].Env
 			}
 
 			if !DeepEqualWithNils(to[i].Resources, from[i].Resources) {
 				requireUpdate = true
-				log.Info("Update required because:", basePath+".Containers["+strconv.Itoa(i)+")].Resources changed from", to[i].Resources, "To:", from[i].Resources)
+				logger.Info("Update required because field changed", "field", containerBasePath+"Resources", "from", to[i].Resources, "to", from[i].Resources)
 				to[i].Resources = from[i].Resources
 			}
 
 			if !DeepEqualWithNils(to[i].VolumeMounts, from[i].VolumeMounts) {
 				requireUpdate = true
-				log.Info("Update required because:", basePath+".Containers["+strconv.Itoa(i)+")].VolumeMounts changed from", to[i].VolumeMounts, "To:", from[i].VolumeMounts)
+				logger.Info("Update required because field changed", "field", containerBasePath+"VolumeMounts", "from", to[i].VolumeMounts, "to", from[i].VolumeMounts)
 				to[i].VolumeMounts = from[i].VolumeMounts
 			}
 
 			if !DeepEqualWithNils(to[i].Ports, from[i].Ports) {
 				requireUpdate = true
-				log.Info("Update required because:", basePath+".Containers["+strconv.Itoa(i)+")].Ports changed from", to[i].Ports, "To:", from[i].Ports)
+				logger.Info("Update required because field changed", "field", containerBasePath+"Ports", "from", to[i].Ports, "to", from[i].Ports)
 				to[i].Ports = from[i].Ports
 			}
 
 			if !DeepEqualWithNils(to[i].Lifecycle, from[i].Lifecycle) {
 				requireUpdate = true
-				log.Info("Update required because:", basePath+".Containers["+strconv.Itoa(i)+")].Lifecycle changed from", to[i].Lifecycle, "To:", from[i].Lifecycle)
+				logger.Info("Update required because field changed", "field", containerBasePath+"Lifecycle", "from", to[i].Lifecycle, "to", from[i].Lifecycle)
 				to[i].Lifecycle = from[i].Lifecycle
 			}
 
 			if !DeepEqualWithNils(to[i].LivenessProbe, from[i].LivenessProbe) {
 				requireUpdate = true
-				log.Info("Update required because:", basePath+".Containers["+strconv.Itoa(i)+")].LivenessProbe changed from", to[i].LivenessProbe, "To:", from[i].LivenessProbe)
+				logger.Info("Update required because field changed", "field", containerBasePath+"LivenessProbe", "from", to[i].LivenessProbe, "to", from[i].LivenessProbe)
 				to[i].LivenessProbe = from[i].LivenessProbe
 			}
 
 			if !DeepEqualWithNils(to[i].ReadinessProbe, from[i].ReadinessProbe) {
 				requireUpdate = true
-				log.Info("Update required because:", basePath+".Containers["+strconv.Itoa(i)+")].ReadinessProbe changed from", to[i].ReadinessProbe, "To:", from[i].ReadinessProbe)
+				logger.Info("Update required because field changed", "field", containerBasePath+"ReadinessProbe", "from", to[i].ReadinessProbe, "to", from[i].ReadinessProbe)
 				to[i].LivenessProbe = from[i].ReadinessProbe
 			}
 
 			if !DeepEqualWithNils(to[i].StartupProbe, from[i].StartupProbe) {
 				requireUpdate = true
-				log.Info("Update required because:", basePath+".Containers["+strconv.Itoa(i)+")].StartupProbe changed from", to[i].StartupProbe, "To:", from[i].StartupProbe)
+				logger.Info("Update required because field changed", "field", containerBasePath+"StartupProbe", "from", to[i].StartupProbe, "to", from[i].StartupProbe)
 				to[i].StartupProbe = from[i].StartupProbe
 			}
 
 			if from[i].TerminationMessagePath != "" && !DeepEqualWithNils(to[i].TerminationMessagePath, from[i].TerminationMessagePath) {
 				requireUpdate = true
-				log.Info("Update required because:", basePath+".Containers["+strconv.Itoa(i)+")].TerminationMessagePath changed from", to[i].TerminationMessagePath, "To:", from[i].TerminationMessagePath)
+				logger.Info("Update required because field changed", "field", containerBasePath+"TerminationMessagePath", "from", to[i].TerminationMessagePath, "to", from[i].TerminationMessagePath)
 				to[i].TerminationMessagePath = from[i].TerminationMessagePath
 			}
 
 			if from[i].TerminationMessagePolicy != "" && !DeepEqualWithNils(to[i].TerminationMessagePolicy, from[i].TerminationMessagePolicy) {
 				requireUpdate = true
-				log.Info("Update required because:", basePath+".Containers["+strconv.Itoa(i)+")].TerminationMessagePolicy changed from", to[i].TerminationMessagePolicy, "To:", from[i].TerminationMessagePolicy)
+				logger.Info("Update required because field changed", "field", containerBasePath+"TerminationMessagePolicy", "from", to[i].TerminationMessagePolicy, "to", from[i].TerminationMessagePolicy)
 				to[i].TerminationMessagePolicy = from[i].TerminationMessagePolicy
 			}
 		}
diff --git a/controllers/util/zk_util.go b/controllers/util/zk_util.go
index cb7e494..352466a 100644
--- a/controllers/util/zk_util.go
+++ b/controllers/util/zk_util.go
@@ -18,6 +18,7 @@
 
 import (
 	solr "github.com/bloomberg/solr-operator/api/v1beta1"
+	"github.com/go-logr/logr"
 	zk "github.com/pravega/zookeeper-operator/pkg/apis/zookeeper/v1beta1"
 	corev1 "k8s.io/api/core/v1"
 	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
@@ -88,53 +89,66 @@
 
 // CopyZookeeperClusterFields copies the owned fields from one ZookeeperCluster to another
 // Returns true if the fields copied from don't match to.
-func CopyZookeeperClusterFields(from, to *zk.ZookeeperCluster) bool {
-	requireUpdate := CopyLabelsAndAnnotations(&from.ObjectMeta, &to.ObjectMeta)
+func CopyZookeeperClusterFields(from, to *zk.ZookeeperCluster, logger logr.Logger) bool {
+	logger = logger.WithValues("kind", "zookeeperCluster")
+	requireUpdate := CopyLabelsAndAnnotations(&from.ObjectMeta, &to.ObjectMeta, logger)
 
 	if !DeepEqualWithNils(to.Spec.Replicas, from.Spec.Replicas) {
-		log.Info("Updating Zk replicas")
+		logger.Info("Update required because field changed", "field", "Spec.Replicas", "from", to.Spec.Replicas, "to", from.Spec.Replicas)
 		requireUpdate = true
 	}
 	to.Spec.Replicas = from.Spec.Replicas
 
 	if !DeepEqualWithNils(to.Spec.Image.Repository, from.Spec.Image.Repository) {
-		log.Info("Updating Zk image repository")
+		logger.Info("Update required because field changed", "field", "Spec.Image.Repository", "from", to.Spec.Image.Repository, "to", from.Spec.Image.Repository)
 		requireUpdate = true
 	}
 	to.Spec.Image.Repository = from.Spec.Image.Repository
 
 	if !DeepEqualWithNils(to.Spec.Image.Tag, from.Spec.Image.Tag) {
-		log.Info("Updating Zk image tag")
+		logger.Info("Update required because field changed", "field", "Spec.Image.Tag", "from", to.Spec.Image.Tag, "to", from.Spec.Image.Tag)
 		requireUpdate = true
 	}
 	to.Spec.Image.Tag = from.Spec.Image.Tag
 
+	if !DeepEqualWithNils(to.Spec.Image.PullPolicy, from.Spec.Image.PullPolicy) {
+		logger.Info("Update required because field changed", "field", "Spec.Image.PullPolicy", "from", to.Spec.Image.PullPolicy, "to", from.Spec.Image.PullPolicy)
+		requireUpdate = true
+	}
+	to.Spec.Image.PullPolicy = from.Spec.Image.PullPolicy
+
 	if from.Spec.Persistence != nil {
 		if to.Spec.Persistence == nil {
-			log.Info("Updating Zk Persistence")
+			logger.Info("Update required because field changed", "field", "Spec.Persistence", "from", to.Spec.Persistence, "to", from.Spec.Persistence)
 			requireUpdate = true
 			to.Spec.Persistence = from.Spec.Persistence
 		} else {
 			if !DeepEqualWithNils(to.Spec.Persistence.PersistentVolumeClaimSpec.Resources.Requests, from.Spec.Persistence.PersistentVolumeClaimSpec.Resources.Requests) {
-				log.Info("Updating Zk Persistence PVC 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
+			}
+
 			if !DeepEqualWithNils(to.Spec.Persistence.PersistentVolumeClaimSpec.AccessModes, from.Spec.Persistence.PersistentVolumeClaimSpec.AccessModes) {
-				log.Info("Updating Zk Persistence PVC 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)
 				requireUpdate = true
 				to.Spec.Persistence.PersistentVolumeClaimSpec.AccessModes = from.Spec.Persistence.PersistentVolumeClaimSpec.AccessModes
 			}
 
 			if !DeepEqualWithNils(to.Spec.Persistence.PersistentVolumeClaimSpec.StorageClassName, from.Spec.Persistence.PersistentVolumeClaimSpec.StorageClassName) {
-				log.Info("Updating Zk Persistence PVC StorageClassName")
+				logger.Info("Update required because field changed", "field", "Spec.Persistence.PersistentVolumeClaimSpec.StorageClassName", "from", to.Spec.Persistence.PersistentVolumeClaimSpec.StorageClassName, "to", from.Spec.Persistence.PersistentVolumeClaimSpec.StorageClassName)
 				requireUpdate = true
 				to.Spec.Persistence.PersistentVolumeClaimSpec.StorageClassName = from.Spec.Persistence.PersistentVolumeClaimSpec.StorageClassName
 			}
 
 			if !DeepEqualWithNils(to.Spec.Persistence.VolumeReclaimPolicy, from.Spec.Persistence.VolumeReclaimPolicy) {
-				log.Info("Updating Zk Persistence VolumeReclaimPolicy")
+				logger.Info("Update required because field changed", "field", "Spec.Persistence.VolumeReclaimPolicy", "from", to.Spec.Persistence.VolumeReclaimPolicy, "to", from.Spec.Persistence.VolumeReclaimPolicy)
 				requireUpdate = true
 				to.Spec.Persistence.VolumeReclaimPolicy = from.Spec.Persistence.VolumeReclaimPolicy
 			}
@@ -148,21 +162,19 @@
 	}*/
 
 	if !DeepEqualWithNils(to.Spec.Pod.Resources, from.Spec.Pod.Resources) {
-		log.Info("Updating Zk 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
 	}
 
 	if !DeepEqualWithNils(to.Spec.Pod.Tolerations, from.Spec.Pod.Tolerations) {
-		log.Info("Updating Zk tolerations")
-		log.Info("Update required because:", "Spec.Pod.Tolerations canged from", to.Spec.Pod.Tolerations, "To:", from.Spec.Pod.Tolerations)
+		logger.Info("Update required because field changed", "field", "Spec.Pod.Tolerations", "from", to.Spec.Pod.Tolerations, "to", from.Spec.Pod.Tolerations)
 		requireUpdate = true
 		to.Spec.Pod.Tolerations = from.Spec.Pod.Tolerations
 	}
 
 	if !DeepEqualWithNils(to.Spec.Pod.NodeSelector, from.Spec.Pod.NodeSelector) {
-		log.Info("Updating Zk nodeSelector")
-		log.Info("Update required because:", "Spec.Pod.NodeSelector canged from", to.Spec.Pod.NodeSelector, "To:", from.Spec.Pod.NodeSelector)
+		logger.Info("Update required because field changed", "field", "Spec.Pod.NodeSelector", "from", to.Spec.Pod.NodeSelector, "to", from.Spec.Pod.NodeSelector)
 		requireUpdate = true
 		to.Spec.Pod.NodeSelector = from.Spec.Pod.NodeSelector
 	}
@@ -170,8 +182,7 @@
 	// The Zookeeper operator defaults the pod affinity, so we only want to require an update if the requested affinity is not null
 	// But always change it so that the change will be picked up if another change is done.
 	if !DeepEqualWithNils(to.Spec.Pod.Affinity, from.Spec.Pod.Affinity) && from.Spec.Pod.Affinity != nil {
-		log.Info("Updating Zk pod affinity")
-		log.Info("Update required because:", "Spec.Pod.Affinity canged from", to.Spec.Pod.Affinity, "To:", from.Spec.Pod.Affinity)
+		logger.Info("Update required because field changed", "field", "Spec.Pod.Affinity", "from", to.Spec.Pod.Affinity, "to", from.Spec.Pod.Affinity)
 		requireUpdate = true
 	}
 	to.Spec.Pod.Affinity = from.Spec.Pod.Affinity