You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@lucene.apache.org by ho...@apache.org on 2021/03/03 20:29:10 UTC

[lucene-solr-operator] branch main updated: Update the owner reference of resources, if the reference is not correct. (#230)

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/lucene-solr-operator.git


The following commit(s) were added to refs/heads/main by this push:
     new a71fe82  Update the owner reference of resources, if the reference is not correct. (#230)
a71fe82 is described below

commit a71fe82d1e7a9885669b8b2b166213b265d6890f
Author: Houston Putman <ho...@apache.org>
AuthorDate: Wed Mar 3 15:29:04 2021 -0500

    Update the owner reference of resources, if the reference is not correct. (#230)
    
    This addresses the situation when a resource, that a SolrCloud or SolrPrometheusExporter is supposed to control, has a different OwnerReference with controller= true. If this is found, then the controlling owner resource will be downgraded to controller=false, and a controlling OwnerReference will be created for either the SolrCloud or SolrPrometheusExporter.
---
 controllers/solrcloud_controller.go              | 131 ++++++++++++++---------
 controllers/solrprometheusexporter_controller.go |  52 +++++----
 controllers/util/common.go                       |  17 +++
 go.mod                                           |   1 +
 4 files changed, 133 insertions(+), 68 deletions(-)

diff --git a/controllers/solrcloud_controller.go b/controllers/solrcloud_controller.go
index 6e1b6d2..7051023 100644
--- a/controllers/solrcloud_controller.go
+++ b/controllers/solrcloud_controller.go
@@ -116,9 +116,6 @@ func (r *SolrCloudReconciler) Reconcile(req ctrl.Request) (ctrl.Result, error) {
 
 	// Generate Common Service
 	commonService := util.GenerateCommonService(instance)
-	if err := controllerutil.SetControllerReference(instance, commonService, r.scheme); err != nil {
-		return requeueOrNot, err
-	}
 
 	// Check if the Common Service already exists
 	commonServiceLogger := logger.WithValues("service", commonService.Name)
@@ -126,11 +123,19 @@ func (r *SolrCloudReconciler) Reconcile(req ctrl.Request) (ctrl.Result, error) {
 	err = r.Get(context.TODO(), types.NamespacedName{Name: commonService.Name, Namespace: commonService.Namespace}, foundCommonService)
 	if err != nil && errors.IsNotFound(err) {
 		commonServiceLogger.Info("Creating Common Service")
-		err = r.Create(context.TODO(), commonService)
-	} else if err == nil && util.CopyServiceFields(commonService, foundCommonService, commonServiceLogger) {
+		if err = controllerutil.SetControllerReference(instance, commonService, r.scheme); err == nil {
+			err = r.Create(context.TODO(), commonService)
+		}
+	} else if err == nil {
+		var needsUpdate bool
+		needsUpdate, err = util.OvertakeControllerRef(instance, foundCommonService, r.scheme)
+		needsUpdate = util.CopyServiceFields(commonService, foundCommonService, commonServiceLogger) || needsUpdate
+
 		// 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 needsUpdate && err == nil {
+			commonServiceLogger.Info("Updating Common Service")
+			err = r.Update(context.TODO(), foundCommonService)
+		}
 	}
 	if err != nil {
 		return requeueOrNot, err
@@ -161,21 +166,26 @@ func (r *SolrCloudReconciler) Reconcile(req ctrl.Request) (ctrl.Result, error) {
 	// Generate HeadlessService
 	if instance.UsesHeadlessService() {
 		headless := util.GenerateHeadlessService(instance)
-		if err := controllerutil.SetControllerReference(instance, headless, r.scheme); err != nil {
-			return requeueOrNot, err
-		}
 
 		// 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) {
-			headlessServiceLogger.Info("Creating HeadlessService")
-			err = r.Create(context.TODO(), headless)
-		} else if err == nil && util.CopyServiceFields(headless, foundHeadless, headlessServiceLogger) {
+			headlessServiceLogger.Info("Creating Headless Service")
+			if err = controllerutil.SetControllerReference(instance, headless, r.scheme); err == nil {
+				err = r.Create(context.TODO(), headless)
+			}
+		} else if err == nil {
+			var needsUpdate bool
+			needsUpdate, err = util.OvertakeControllerRef(instance, foundHeadless, r.scheme)
+			needsUpdate = util.CopyServiceFields(headless, foundHeadless, headlessServiceLogger) || needsUpdate
+
 			// Update the found HeadlessService and write the result back if there are any changes
-			headlessServiceLogger.Info("Updating HeadlessService")
-			err = r.Update(context.TODO(), foundHeadless)
+			if needsUpdate && err == nil {
+				headlessServiceLogger.Info("Updating Headless Service")
+				err = r.Update(context.TODO(), foundHeadless)
+			}
 		}
 		if err != nil {
 			return requeueOrNot, err
@@ -235,9 +245,6 @@ func (r *SolrCloudReconciler) Reconcile(req ctrl.Request) (ctrl.Result, error) {
 	if reconcileConfigInfo[util.SolrXmlFile] == "" {
 		// no user provided solr.xml, so create the default
 		configMap := util.GenerateConfigMap(instance)
-		if err := controllerutil.SetControllerReference(instance, configMap, r.scheme); err != nil {
-			return requeueOrNot, err
-		}
 
 		reconcileConfigInfo[util.SolrXmlMd5Annotation] = fmt.Sprintf("%x", md5.Sum([]byte(configMap.Data[util.SolrXmlFile])))
 		reconcileConfigInfo[util.SolrXmlFile] = configMap.Name
@@ -248,11 +255,19 @@ func (r *SolrCloudReconciler) Reconcile(req ctrl.Request) (ctrl.Result, error) {
 		err = r.Get(context.TODO(), types.NamespacedName{Name: configMap.Name, Namespace: configMap.Namespace}, foundConfigMap)
 		if err != nil && errors.IsNotFound(err) {
 			configMapLogger.Info("Creating ConfigMap")
-			err = r.Create(context.TODO(), configMap)
-		} else if err == nil && util.CopyConfigMapFields(configMap, foundConfigMap, configMapLogger) {
+			if err = controllerutil.SetControllerReference(instance, configMap, r.scheme); err == nil {
+				err = r.Create(context.TODO(), configMap)
+			}
+		} else if err == nil {
+			var needsUpdate bool
+			needsUpdate, err = util.OvertakeControllerRef(instance, foundConfigMap, r.scheme)
+			needsUpdate = util.CopyConfigMapFields(configMap, foundConfigMap, configMapLogger) || needsUpdate
+
 			// Update the found ConfigMap and write the result back if there are any changes
-			configMapLogger.Info("Updating ConfigMap")
-			err = r.Update(context.TODO(), foundConfigMap)
+			if needsUpdate && err == nil {
+				configMapLogger.Info("Updating ConfigMap")
+				err = r.Update(context.TODO(), foundConfigMap)
+			}
 		}
 		if err != nil {
 			return requeueOrNot, err
@@ -395,9 +410,6 @@ func (r *SolrCloudReconciler) Reconcile(req ctrl.Request) (ctrl.Result, error) {
 	if !blockReconciliationOfStatefulSet {
 		// Generate StatefulSet
 		statefulSet := util.GenerateStatefulSet(instance, &newStatus, hostNameIpMap, reconcileConfigInfo, needsPkcs12InitContainer, tlsCertMd5)
-		if err := controllerutil.SetControllerReference(instance, statefulSet, r.scheme); err != nil {
-			return requeueOrNot, err
-		}
 
 		// Check if the StatefulSet already exists
 		statefulSetLogger := logger.WithValues("statefulSet", statefulSet.Name)
@@ -405,18 +417,26 @@ func (r *SolrCloudReconciler) Reconcile(req ctrl.Request) (ctrl.Result, error) {
 		err = r.Get(context.TODO(), types.NamespacedName{Name: statefulSet.Name, Namespace: statefulSet.Namespace}, foundStatefulSet)
 		if err != nil && errors.IsNotFound(err) {
 			statefulSetLogger.Info("Creating StatefulSet")
-			err = r.Create(context.TODO(), statefulSet)
+			if err = controllerutil.SetControllerReference(instance, statefulSet, r.scheme); err == nil {
+				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, statefulSetLogger) {
-				// Update the found StatefulSet and write the result back if there are any changes
+			// Find which labels the PVCs will be using, to use for the finalizer
+			pvcLabelSelector = foundStatefulSet.Spec.Selector.MatchLabels
+
+			// Check to see if the StatefulSet needs an update
+			var needsUpdate bool
+			needsUpdate, err = util.OvertakeControllerRef(instance, foundStatefulSet, r.scheme)
+			needsUpdate = util.CopyStatefulSetFields(statefulSet, foundStatefulSet, statefulSetLogger) || needsUpdate
+
+			// Update the found StatefulSet and write the result back if there are any changes
+			if needsUpdate && err == nil {
 				statefulSetLogger.Info("Updating StatefulSet")
 				err = r.Update(context.TODO(), foundStatefulSet)
 			}
-			// Find which labels the PVCs will be using, to use for the finalizer
-			pvcLabelSelector = foundStatefulSet.Spec.Selector.MatchLabels
 		}
 		if err != nil {
 			return requeueOrNot, err
@@ -493,9 +513,6 @@ func (r *SolrCloudReconciler) Reconcile(req ctrl.Request) (ctrl.Result, error) {
 	if extAddressabilityOpts != nil && extAddressabilityOpts.Method == solr.Ingress {
 		// Generate Ingress
 		ingress := util.GenerateIngress(instance, solrNodeNames)
-		if err := controllerutil.SetControllerReference(instance, ingress, r.scheme); err != nil {
-			return requeueOrNot, err
-		}
 
 		// Check if the Ingress already exists
 		ingressLogger := logger.WithValues("ingress", ingress.Name)
@@ -503,11 +520,19 @@ func (r *SolrCloudReconciler) Reconcile(req ctrl.Request) (ctrl.Result, error) {
 		err = r.Get(context.TODO(), types.NamespacedName{Name: ingress.Name, Namespace: ingress.Namespace}, foundIngress)
 		if err != nil && errors.IsNotFound(err) {
 			ingressLogger.Info("Creating Ingress")
-			err = r.Create(context.TODO(), ingress)
-		} else if err == nil && util.CopyIngressFields(ingress, foundIngress, ingressLogger) {
+			if err = controllerutil.SetControllerReference(instance, ingress, r.scheme); err == nil {
+				err = r.Create(context.TODO(), ingress)
+			}
+		} else if err == nil {
+			var needsUpdate bool
+			needsUpdate, err = util.OvertakeControllerRef(instance, foundIngress, r.scheme)
+			needsUpdate = util.CopyIngressFields(ingress, foundIngress, ingressLogger) || needsUpdate
+
 			// Update the found Ingress and write the result back if there are any changes
-			ingressLogger.Info("Updating Ingress")
-			err = r.Update(context.TODO(), foundIngress)
+			if needsUpdate && err == nil {
+				ingressLogger.Info("Updating Ingress")
+				err = r.Update(context.TODO(), foundIngress)
+			}
 		}
 		if err != nil {
 			return requeueOrNot, err
@@ -651,9 +676,6 @@ func reconcileCloudStatus(r *SolrCloudReconciler, solrCloud *solr.SolrCloud, new
 func reconcileNodeService(r *SolrCloudReconciler, logger logr.Logger, instance *solr.SolrCloud, nodeName string) (err error, ip string) {
 	// Generate Node Service
 	service := util.GenerateNodeService(instance, nodeName)
-	if err := controllerutil.SetControllerReference(instance, service, r.scheme); err != nil {
-		return err, ip
-	}
 
 	// Check if the Node Service already exists
 	nodeServiceLogger := logger.WithValues("service", service.Name)
@@ -661,14 +683,22 @@ func reconcileNodeService(r *SolrCloudReconciler, logger logr.Logger, instance *
 	err = r.Get(context.TODO(), types.NamespacedName{Name: service.Name, Namespace: service.Namespace}, foundService)
 	if err != nil && errors.IsNotFound(err) {
 		nodeServiceLogger.Info("Creating Node Service")
-		err = r.Create(context.TODO(), service)
+		if err = controllerutil.SetControllerReference(instance, service, r.scheme); err == nil {
+			err = r.Create(context.TODO(), service)
+		}
 	} else if err == nil {
-		if util.CopyServiceFields(service, foundService, nodeServiceLogger) {
+		ip = foundService.Spec.ClusterIP
+
+		// Check to see if the Service needs an update
+		var needsUpdate bool
+		needsUpdate, err = util.OvertakeControllerRef(instance, foundService, r.scheme)
+		needsUpdate = util.CopyServiceFields(service, foundService, nodeServiceLogger) || needsUpdate
+
+		if needsUpdate && err == nil {
 			// 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
 	}
 	if err != nil {
 		return err, ip
@@ -689,9 +719,6 @@ func reconcileZk(r *SolrCloudReconciler, logger logr.Logger, instance *solr.Solr
 			return errors.NewBadRequest("Cannot create a Zookeeper Cluster, as the Solr Operator is not configured to use the Zookeeper CRD")
 		}
 		zkCluster := util.GenerateZookeeperCluster(instance, pzk)
-		if err := controllerutil.SetControllerReference(instance, zkCluster, r.scheme); err != nil {
-			return err
-		}
 
 		// Check if the ZookeeperCluster already exists
 		zkLogger := logger.WithValues("zookeeperCluster", zkCluster.Name)
@@ -699,10 +726,16 @@ func reconcileZk(r *SolrCloudReconciler, logger logr.Logger, instance *solr.Solr
 		err := r.Get(context.TODO(), types.NamespacedName{Name: zkCluster.Name, Namespace: zkCluster.Namespace}, foundZkCluster)
 		if err != nil && errors.IsNotFound(err) {
 			zkLogger.Info("Creating Zookeeer Cluster")
-			err = r.Create(context.TODO(), zkCluster)
+			if err = controllerutil.SetControllerReference(instance, zkCluster, r.scheme); err == nil {
+				err = r.Create(context.TODO(), zkCluster)
+			}
 		} else if err == nil {
-			if util.CopyZookeeperClusterFields(zkCluster, foundZkCluster, zkLogger) {
-				// Update the found ZookeeperCluster and write the result back if there are any changes
+			var needsUpdate bool
+			needsUpdate, err = util.OvertakeControllerRef(instance, foundZkCluster, r.scheme)
+			needsUpdate = util.CopyZookeeperClusterFields(zkCluster, foundZkCluster, zkLogger) || needsUpdate
+
+			// Update the found ZookeeperCluster and write the result back if there are any changes
+			if needsUpdate && err == nil {
 				zkLogger.Info("Updating Zookeeer Cluster")
 				err = r.Update(context.TODO(), foundZkCluster)
 			}
diff --git a/controllers/solrprometheusexporter_controller.go b/controllers/solrprometheusexporter_controller.go
index 8534ad2..0e396ea 100644
--- a/controllers/solrprometheusexporter_controller.go
+++ b/controllers/solrprometheusexporter_controller.go
@@ -111,9 +111,6 @@ func (r *SolrPrometheusExporterReconciler) Reconcile(req ctrl.Request) (ctrl.Res
 	if prometheusExporter.Spec.Config != "" {
 		// Generate ConfigMap
 		configMap := util.GenerateMetricsConfigMap(prometheusExporter)
-		if err := controllerutil.SetControllerReference(prometheusExporter, configMap, r.scheme); err != nil {
-			return ctrl.Result{}, err
-		}
 
 		// capture the MD5 for the default config XML, otherwise we already computed it above
 		if configXmlMd5 == "" {
@@ -126,11 +123,19 @@ func (r *SolrPrometheusExporterReconciler) Reconcile(req ctrl.Request) (ctrl.Res
 		err = r.Get(context.TODO(), types.NamespacedName{Name: configMap.Name, Namespace: configMap.Namespace}, foundConfigMap)
 		if err != nil && errors.IsNotFound(err) {
 			configMapLogger.Info("Creating ConfigMap")
-			err = r.Create(context.TODO(), configMap)
-		} else if err == nil && util.CopyConfigMapFields(configMap, foundConfigMap, configMapLogger) {
+			if err = controllerutil.SetControllerReference(prometheusExporter, configMap, r.scheme); err == nil {
+				err = r.Create(context.TODO(), configMap)
+			}
+		} else if err == nil {
+			var needsUpdate bool
+			needsUpdate, err = util.OvertakeControllerRef(prometheusExporter, foundConfigMap, r.scheme)
+			needsUpdate = util.CopyConfigMapFields(configMap, foundConfigMap, configMapLogger) || needsUpdate
+
 			// Update the found ConfigMap and write the result back if there are any changes
-			configMapLogger.Info("Updating ConfigMap")
-			err = r.Update(context.TODO(), foundConfigMap)
+			if needsUpdate && err == nil {
+				configMapLogger.Info("Updating ConfigMap")
+				err = r.Update(context.TODO(), foundConfigMap)
+			}
 		}
 		if err != nil {
 			return ctrl.Result{}, err
@@ -139,9 +144,6 @@ func (r *SolrPrometheusExporterReconciler) Reconcile(req ctrl.Request) (ctrl.Res
 
 	// Generate Metrics Service
 	metricsService := util.GenerateSolrMetricsService(prometheusExporter)
-	if err := controllerutil.SetControllerReference(prometheusExporter, metricsService, r.scheme); err != nil {
-		return ctrl.Result{}, err
-	}
 
 	// Check if the Metrics Service already exists
 	serviceLogger := logger.WithValues("service", metricsService.Name)
@@ -149,11 +151,19 @@ func (r *SolrPrometheusExporterReconciler) Reconcile(req ctrl.Request) (ctrl.Res
 	err = r.Get(context.TODO(), types.NamespacedName{Name: metricsService.Name, Namespace: metricsService.Namespace}, foundMetricsService)
 	if err != nil && errors.IsNotFound(err) {
 		serviceLogger.Info("Creating Service")
-		err = r.Create(context.TODO(), metricsService)
-	} else if err == nil && util.CopyServiceFields(metricsService, foundMetricsService, serviceLogger) {
+		if err = controllerutil.SetControllerReference(prometheusExporter, metricsService, r.scheme); err == nil {
+			err = r.Create(context.TODO(), metricsService)
+		}
+	} else if err == nil {
+		var needsUpdate bool
+		needsUpdate, err = util.OvertakeControllerRef(prometheusExporter, foundMetricsService, r.scheme)
+		needsUpdate = util.CopyServiceFields(metricsService, foundMetricsService, serviceLogger) || needsUpdate
+
 		// Update the found Metrics Service and write the result back if there are any changes
-		serviceLogger.Info("Updating Service")
-		err = r.Update(context.TODO(), foundMetricsService)
+		if needsUpdate && err == nil {
+			serviceLogger.Info("Updating Service")
+			err = r.Update(context.TODO(), foundMetricsService)
+		}
 	}
 	if err != nil {
 		return ctrl.Result{}, err
@@ -225,9 +235,6 @@ func (r *SolrPrometheusExporterReconciler) Reconcile(req ctrl.Request) (ctrl.Res
 	}
 
 	deploy := util.GenerateSolrPrometheusExporterDeployment(prometheusExporter, solrConnectionInfo, configXmlMd5, tlsClientOptions, basicAuthMd5)
-	if err := controllerutil.SetControllerReference(prometheusExporter, deploy, r.scheme); err != nil {
-		return ctrl.Result{}, err
-	}
 
 	ready := false
 	// Check if the Metrics Deployment already exists
@@ -236,9 +243,16 @@ func (r *SolrPrometheusExporterReconciler) Reconcile(req ctrl.Request) (ctrl.Res
 	err = r.Get(context.TODO(), types.NamespacedName{Name: deploy.Name, Namespace: deploy.Namespace}, foundDeploy)
 	if err != nil && errors.IsNotFound(err) {
 		deploymentLogger.Info("Creating Deployment")
-		err = r.Create(context.TODO(), deploy)
+		if err = controllerutil.SetControllerReference(prometheusExporter, deploy, r.scheme); err == nil {
+			err = r.Create(context.TODO(), deploy)
+		}
 	} else if err == nil {
-		if util.CopyDeploymentFields(deploy, foundDeploy, deploymentLogger) {
+		var needsUpdate bool
+		needsUpdate, err = util.OvertakeControllerRef(prometheusExporter, foundDeploy, r.scheme)
+		needsUpdate = util.CopyDeploymentFields(deploy, foundDeploy, deploymentLogger) || needsUpdate
+
+		// Update the found Metrics Service and write the result back if there are any changes
+		if needsUpdate && err == nil {
 			deploymentLogger.Info("Updating Deployment")
 			err = r.Update(context.TODO(), foundDeploy)
 		}
diff --git a/controllers/util/common.go b/controllers/util/common.go
index 7d4cf1b..ea06f45 100644
--- a/controllers/util/common.go
+++ b/controllers/util/common.go
@@ -23,7 +23,10 @@ import (
 	corev1 "k8s.io/api/core/v1"
 	netv1 "k8s.io/api/networking/v1beta1"
 	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
+	"k8s.io/apimachinery/pkg/runtime"
+	"k8s.io/utils/pointer"
 	"reflect"
+	"sigs.k8s.io/controller-runtime/pkg/controller/controllerutil"
 	"strconv"
 	"strings"
 )
@@ -536,3 +539,17 @@ func CopyPodContainers(fromPtr, toPtr *[]corev1.Container, basePath string, logg
 	}
 	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) {
+	if !metav1.IsControlledBy(controlled, owner) {
+		if otherController := metav1.GetControllerOfNoCopy(controlled); otherController != nil {
+			otherController.Controller = pointer.BoolPtr(false)
+			otherController.BlockOwnerDeletion = pointer.BoolPtr(false)
+		}
+		err = controllerutil.SetControllerReference(owner, controlled, scheme)
+		needsUpdate = true
+	}
+	return needsUpdate, err
+}
diff --git a/go.mod b/go.mod
index c462cbb..c416820 100644
--- a/go.mod
+++ b/go.mod
@@ -22,5 +22,6 @@ require (
 	k8s.io/apimachinery v0.19.0
 	k8s.io/client-go v0.19.0
 	k8s.io/klog/v2 v2.3.0 // indirect
+	k8s.io/utils v0.0.0-20200729134348-d5654de09c73
 	sigs.k8s.io/controller-runtime v0.6.2
 )