open-cluster-management.io/governance-policy-propagator@v0.13.0/controllers/propagator/rootpolicy_controller.go (about)

     1  // Copyright (c) 2021 Red Hat, Inc.
     2  // Copyright Contributors to the Open Cluster Management project
     3  
     4  package propagator
     5  
     6  import (
     7  	"context"
     8  	"sync"
     9  
    10  	k8serrors "k8s.io/apimachinery/pkg/api/errors"
    11  	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
    12  	ctrl "sigs.k8s.io/controller-runtime"
    13  	"sigs.k8s.io/controller-runtime/pkg/client"
    14  	"sigs.k8s.io/controller-runtime/pkg/event"
    15  	"sigs.k8s.io/controller-runtime/pkg/reconcile"
    16  
    17  	policiesv1 "open-cluster-management.io/governance-policy-propagator/api/v1"
    18  	"open-cluster-management.io/governance-policy-propagator/controllers/common"
    19  )
    20  
    21  const ControllerName string = "policy-propagator"
    22  
    23  var log = ctrl.Log.WithName(ControllerName)
    24  
    25  type RootPolicyReconciler struct {
    26  	Propagator
    27  }
    28  
    29  // Reconcile handles root policies, sending events to the replicated policy reconciler to ensure
    30  // that the desired policies are on the correct clusters. It also populates the status of the root
    31  // policy with placement information.
    32  func (r *RootPolicyReconciler) Reconcile(ctx context.Context, request ctrl.Request) (ctrl.Result, error) {
    33  	log := log.WithValues("Request.Namespace", request.Namespace, "Request.Name", request.Name)
    34  
    35  	log.V(3).Info("Acquiring the lock for the root policy")
    36  
    37  	lock, _ := r.RootPolicyLocks.LoadOrStore(request.NamespacedName, &sync.Mutex{})
    38  
    39  	lock.(*sync.Mutex).Lock()
    40  	defer lock.(*sync.Mutex).Unlock()
    41  
    42  	log.Info("Reconciling the policy")
    43  
    44  	// Fetch the Policy instance
    45  	instance := &policiesv1.Policy{}
    46  
    47  	err := r.Get(ctx, request.NamespacedName, instance)
    48  	if err != nil {
    49  		if k8serrors.IsNotFound(err) {
    50  			count, err := r.updateExistingReplicas(ctx, request.Namespace+"."+request.Name)
    51  			if err != nil {
    52  				log.Error(err, "Failed to send update events to replicated policies, requeueing")
    53  
    54  				return reconcile.Result{}, err
    55  			}
    56  
    57  			log.Info("Replicated policies sent for deletion", "count", count)
    58  
    59  			return reconcile.Result{}, nil
    60  		}
    61  
    62  		log.Error(err, "Failed to get the policy")
    63  
    64  		// Error reading the object - requeue the request.
    65  		return reconcile.Result{}, err
    66  	}
    67  
    68  	inClusterNs, err := common.IsInClusterNamespace(ctx, r.Client, instance.Namespace)
    69  	if err != nil {
    70  		log.Error(err, "Failed to determine if the policy is in a managed cluster namespace. Requeueing the request.")
    71  
    72  		return reconcile.Result{}, err
    73  	}
    74  
    75  	if !inClusterNs {
    76  		err := r.handleRootPolicy(ctx, instance)
    77  		if err != nil {
    78  			log.Error(err, "Failure during root policy handling")
    79  
    80  			propagationFailureMetric.WithLabelValues(instance.GetName(), instance.GetNamespace()).Inc()
    81  		}
    82  
    83  		log.Info("Reconciliation complete")
    84  
    85  		return reconcile.Result{}, err
    86  	}
    87  
    88  	log = log.WithValues("name", instance.GetName(), "namespace", instance.GetNamespace())
    89  
    90  	log.Info("The policy was found in the cluster namespace but doesn't belong to any root policy, deleting it")
    91  
    92  	err = r.Delete(ctx, instance)
    93  	if err != nil && !k8serrors.IsNotFound(err) {
    94  		log.Error(err, "Failed to delete the policy")
    95  
    96  		return reconcile.Result{}, err
    97  	}
    98  
    99  	return reconcile.Result{}, nil
   100  }
   101  
   102  // updateExistingReplicas lists all existing replicated policies for this root policy, and sends
   103  // events for each of them to the replicated policy reconciler. This will trigger updates on those
   104  // replicated policies, for example when the root policy spec changes, or when the replicated
   105  // policies might need to be deleted.
   106  func (r *RootPolicyReconciler) updateExistingReplicas(ctx context.Context, rootPolicyFullName string) (int, error) {
   107  	// Get all the replicated policies for this root policy
   108  	policyList := &policiesv1.PolicyList{}
   109  	opts := &client.ListOptions{}
   110  
   111  	matcher := client.MatchingLabels{common.RootPolicyLabel: rootPolicyFullName}
   112  	matcher.ApplyToList(opts)
   113  
   114  	err := r.List(ctx, policyList, opts)
   115  	if err != nil && !k8serrors.IsNotFound(err) {
   116  		return 0, err
   117  	}
   118  
   119  	for _, replicated := range policyList.Items {
   120  		simpleObj := &common.GuttedObject{
   121  			TypeMeta: metav1.TypeMeta{
   122  				Kind:       replicated.Kind,
   123  				APIVersion: replicated.APIVersion,
   124  			},
   125  			ObjectMeta: metav1.ObjectMeta{
   126  				Name:      replicated.Name,
   127  				Namespace: replicated.Namespace,
   128  			},
   129  		}
   130  
   131  		r.ReplicatedPolicyUpdates <- event.GenericEvent{Object: simpleObj}
   132  	}
   133  
   134  	return len(policyList.Items), nil
   135  }