sigs.k8s.io/cluster-api@v1.7.1/exp/addons/internal/controllers/clusterresourcesetbinding_controller.go (about)

     1  /*
     2  Copyright 2020 The Kubernetes Authors.
     3  
     4  Licensed under the Apache License, Version 2.0 (the "License");
     5  you may not use this file except in compliance with the License.
     6  You may obtain a copy of the License at
     7  
     8      http://www.apache.org/licenses/LICENSE-2.0
     9  
    10  Unless required by applicable law or agreed to in writing, software
    11  distributed under the License is distributed on an "AS IS" BASIS,
    12  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    13  See the License for the specific language governing permissions and
    14  limitations under the License.
    15  */
    16  
    17  package controllers
    18  
    19  import (
    20  	"context"
    21  
    22  	"github.com/pkg/errors"
    23  	apierrors "k8s.io/apimachinery/pkg/api/errors"
    24  	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
    25  	ctrl "sigs.k8s.io/controller-runtime"
    26  	"sigs.k8s.io/controller-runtime/pkg/client"
    27  	"sigs.k8s.io/controller-runtime/pkg/controller"
    28  	"sigs.k8s.io/controller-runtime/pkg/handler"
    29  	"sigs.k8s.io/controller-runtime/pkg/reconcile"
    30  
    31  	clusterv1 "sigs.k8s.io/cluster-api/api/v1beta1"
    32  	addonsv1 "sigs.k8s.io/cluster-api/exp/addons/api/v1beta1"
    33  	"sigs.k8s.io/cluster-api/feature"
    34  	"sigs.k8s.io/cluster-api/internal/hooks"
    35  	"sigs.k8s.io/cluster-api/util"
    36  	"sigs.k8s.io/cluster-api/util/patch"
    37  	"sigs.k8s.io/cluster-api/util/predicates"
    38  )
    39  
    40  // +kubebuilder:rbac:groups=addons.cluster.x-k8s.io,resources=*,verbs=get;list;watch;create;update;patch;delete
    41  
    42  // ClusterResourceSetBindingReconciler reconciles a ClusterResourceSetBinding object.
    43  type ClusterResourceSetBindingReconciler struct {
    44  	Client client.Client
    45  
    46  	// WatchFilterValue is the label value used to filter events prior to reconciliation.
    47  	WatchFilterValue string
    48  }
    49  
    50  func (r *ClusterResourceSetBindingReconciler) SetupWithManager(ctx context.Context, mgr ctrl.Manager, options controller.Options) error {
    51  	err := ctrl.NewControllerManagedBy(mgr).
    52  		For(&addonsv1.ClusterResourceSetBinding{}).
    53  		Watches(
    54  			&clusterv1.Cluster{},
    55  			handler.EnqueueRequestsFromMapFunc(r.clusterToClusterResourceSetBinding),
    56  		).
    57  		WithOptions(options).
    58  		WithEventFilter(predicates.ResourceNotPausedAndHasFilterLabel(ctrl.LoggerFrom(ctx), r.WatchFilterValue)).
    59  		Complete(r)
    60  	if err != nil {
    61  		return errors.Wrap(err, "failed setting up with a controller manager")
    62  	}
    63  
    64  	return nil
    65  }
    66  
    67  func (r *ClusterResourceSetBindingReconciler) Reconcile(ctx context.Context, req ctrl.Request) (_ ctrl.Result, reterr error) {
    68  	log := ctrl.LoggerFrom(ctx)
    69  
    70  	// Fetch the ClusterResourceSetBinding instance.
    71  	binding := &addonsv1.ClusterResourceSetBinding{}
    72  	if err := r.Client.Get(ctx, req.NamespacedName, binding); err != nil {
    73  		if apierrors.IsNotFound(err) {
    74  			// Object not found, return.  Created objects are automatically garbage collected.
    75  			// For additional cleanup logic use finalizers.
    76  			return ctrl.Result{}, nil
    77  		}
    78  		// Error reading the object - requeue the request.
    79  		return ctrl.Result{}, err
    80  	}
    81  	if err := r.updateClusterReference(ctx, binding); err != nil {
    82  		return ctrl.Result{}, err
    83  	}
    84  	cluster, err := util.GetClusterByName(ctx, r.Client, req.Namespace, binding.Spec.ClusterName)
    85  	if err != nil {
    86  		if apierrors.IsNotFound(err) {
    87  			// If the owner cluster is already deleted, delete its ClusterResourceSetBinding
    88  			log.Info("deleting ClusterResourceSetBinding because the owner Cluster no longer exists")
    89  			return ctrl.Result{}, r.Client.Delete(ctx, binding)
    90  		}
    91  		return ctrl.Result{}, err
    92  	}
    93  	// If the owner cluster is in deletion process, delete its ClusterResourceSetBinding
    94  	if !cluster.DeletionTimestamp.IsZero() {
    95  		if feature.Gates.Enabled(feature.RuntimeSDK) && feature.Gates.Enabled(feature.ClusterTopology) {
    96  			if cluster.Spec.Topology != nil && !hooks.IsOkToDelete(cluster) {
    97  				// If the Cluster is not yet ready to be deleted then do not delete the ClusterResourceSetBinding.
    98  				return ctrl.Result{}, nil
    99  			}
   100  		}
   101  		log.Info("deleting ClusterResourceSetBinding because the owner Cluster is currently being deleted")
   102  		return ctrl.Result{}, r.Client.Delete(ctx, binding)
   103  	}
   104  
   105  	return ctrl.Result{}, nil
   106  }
   107  
   108  // clusterToClusterResourceSetBinding is mapper function that maps clusters to ClusterResourceSetBinding.
   109  func (r *ClusterResourceSetBindingReconciler) clusterToClusterResourceSetBinding(_ context.Context, o client.Object) []ctrl.Request {
   110  	return []reconcile.Request{
   111  		{
   112  			NamespacedName: client.ObjectKey{
   113  				Namespace: o.GetNamespace(),
   114  				Name:      o.GetName(),
   115  			},
   116  		},
   117  	}
   118  }
   119  
   120  // updateClusterReference updates how the ClusterResourceSetBinding references the Cluster.
   121  // Before 1.4 cluster name was stored as an ownerReference. This function migrates the cluster name to the spec.clusterName and removes the Cluster OwnerReference.
   122  // Ref: https://github.com/kubernetes-sigs/cluster-api/issues/7669.
   123  func (r *ClusterResourceSetBindingReconciler) updateClusterReference(ctx context.Context, binding *addonsv1.ClusterResourceSetBinding) error {
   124  	patchHelper, err := patch.NewHelper(binding, r.Client)
   125  	if err != nil {
   126  		return err
   127  	}
   128  
   129  	// If the `.spec.clusterName` is not set, take the value from the ownerReference.
   130  	if binding.Spec.ClusterName == "" {
   131  		// Update the clusterName field of the existing ClusterResourceSetBindings with ownerReferences.
   132  		// More details please refer to: https://github.com/kubernetes-sigs/cluster-api/issues/7669.
   133  		clusterName, err := getClusterNameFromOwnerRef(binding.ObjectMeta)
   134  		if err != nil {
   135  			return err
   136  		}
   137  		binding.Spec.ClusterName = clusterName
   138  	}
   139  
   140  	// Remove the Cluster OwnerReference if it exists. This is a no-op if the OwnerReference does not exist.
   141  	// TODO: (killianmuldoon) This can be removed in CAPI v1beta2.
   142  	binding.OwnerReferences = util.RemoveOwnerRef(binding.OwnerReferences, metav1.OwnerReference{
   143  		APIVersion: clusterv1.GroupVersion.String(),
   144  		Kind:       "Cluster",
   145  		Name:       binding.Spec.ClusterName,
   146  	})
   147  
   148  	return patchHelper.Patch(ctx, binding)
   149  }