github.com/verrazzano/verrazzano@v1.7.0/application-operator/controllers/clusters/multiclustersecret/controller.go (about)

     1  // Copyright (c) 2021, 2022, Oracle and/or its affiliates.
     2  // Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl.
     3  
     4  package multiclustersecret
     5  
     6  import (
     7  	"context"
     8  	"errors"
     9  
    10  	clustersv1alpha1 "github.com/verrazzano/verrazzano/application-operator/apis/clusters/v1alpha1"
    11  	"github.com/verrazzano/verrazzano/application-operator/constants"
    12  	"github.com/verrazzano/verrazzano/application-operator/controllers/clusters"
    13  	vzconst "github.com/verrazzano/verrazzano/pkg/constants"
    14  	vzlogInit "github.com/verrazzano/verrazzano/pkg/log"
    15  	vzlog2 "github.com/verrazzano/verrazzano/pkg/log/vzlog"
    16  	"go.uber.org/zap"
    17  	corev1 "k8s.io/api/core/v1"
    18  	"k8s.io/apimachinery/pkg/runtime"
    19  	"k8s.io/apimachinery/pkg/types"
    20  	ctrl "sigs.k8s.io/controller-runtime"
    21  	"sigs.k8s.io/controller-runtime/pkg/client"
    22  	"sigs.k8s.io/controller-runtime/pkg/controller/controllerutil"
    23  	"sigs.k8s.io/controller-runtime/pkg/reconcile"
    24  )
    25  
    26  // Reconciler reconciles a MultiClusterSecret object
    27  type Reconciler struct {
    28  	client.Client
    29  	Log          *zap.SugaredLogger
    30  	Scheme       *runtime.Scheme
    31  	AgentChannel chan clusters.StatusUpdateMessage
    32  }
    33  
    34  const (
    35  	finalizerName  = "multiclustersecret.verrazzano.io"
    36  	controllerName = "multiclustersecret"
    37  )
    38  
    39  // Reconcile reconciles a MultiClusterSecret resource. It fetches the embedded Secret, mutates it
    40  // based on the MultiClusterSecret, and updates the status of the MultiClusterSecret to reflect the
    41  // success or failure of the changes to the embedded Secret
    42  func (r *Reconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) {
    43  	if ctx == nil {
    44  		return ctrl.Result{}, errors.New("context cannot be nil")
    45  	}
    46  
    47  	// We do not want any resource to get reconciled if it is in namespace kube-system
    48  	// This is due to a bug found in OKE, it should not affect functionality of any vz operators
    49  	// If this is the case then return success
    50  	if req.Namespace == vzconst.KubeSystem {
    51  		log := zap.S().With(vzlogInit.FieldResourceNamespace, req.Namespace, vzlogInit.FieldResourceName, req.Name, vzlogInit.FieldController, controllerName)
    52  		log.Infof("Multi-cluster secret resource %v should not be reconciled in kube-system namespace, ignoring", req.NamespacedName)
    53  		return reconcile.Result{}, nil
    54  	}
    55  
    56  	var mcSecret clustersv1alpha1.MultiClusterSecret
    57  	err := r.fetchMultiClusterSecret(ctx, req.NamespacedName, &mcSecret)
    58  	if err != nil {
    59  		return clusters.IgnoreNotFoundWithLog(err, zap.S())
    60  	}
    61  	log, err := clusters.GetResourceLogger("mcsecret", req.NamespacedName, &mcSecret)
    62  	if err != nil {
    63  		zap.S().Errorf("Failed to create controller logger for multi-cluster secret resource: %v", err)
    64  		return clusters.NewRequeueWithDelay(), nil
    65  	}
    66  	log.Oncef("Reconciling multi-cluster secret resource %v, generation %v", req.NamespacedName, mcSecret.Generation)
    67  
    68  	res, err := r.doReconcile(ctx, mcSecret, log)
    69  	if clusters.ShouldRequeue(res) {
    70  		return res, nil
    71  	}
    72  	// Never return an error since it has already been logged and we don't want the
    73  	// controller runtime to log again (with stack trace).  Just re-queue if there is an error.
    74  	if err != nil {
    75  		return clusters.NewRequeueWithDelay(), nil
    76  	}
    77  
    78  	log.Oncef("Finished reconciling multi-cluster secret %v", req.NamespacedName)
    79  
    80  	return ctrl.Result{}, nil
    81  }
    82  
    83  // doReconcile performs the reconciliation operations for the MC secret
    84  func (r *Reconciler) doReconcile(ctx context.Context, mcSecret clustersv1alpha1.MultiClusterSecret, log vzlog2.VerrazzanoLogger) (ctrl.Result, error) {
    85  	// delete the wrapped resource since MC is being deleted
    86  	if !mcSecret.ObjectMeta.DeletionTimestamp.IsZero() {
    87  		err := clusters.DeleteAssociatedResource(ctx, r.Client, &mcSecret, finalizerName, &corev1.Secret{}, types.NamespacedName{Namespace: mcSecret.Namespace, Name: mcSecret.Name})
    88  		if err != nil {
    89  			log.Errorf("Failed to delete associated secret and finalizer: %v", err)
    90  		}
    91  		return ctrl.Result{}, err
    92  	}
    93  
    94  	oldState := clusters.SetEffectiveStateIfChanged(mcSecret.Spec.Placement, &mcSecret.Status)
    95  	if !clusters.IsPlacedInThisCluster(ctx, r, mcSecret.Spec.Placement) {
    96  		if oldState != mcSecret.Status.State {
    97  			// This must be done whether the resource is placed in this cluster or not, because we
    98  			// could be in an admin cluster and receive cluster level statuses from managed clusters,
    99  			// which can change our effective state
   100  			err := r.Status().Update(ctx, &mcSecret)
   101  			if err != nil {
   102  				return ctrl.Result{}, err
   103  			}
   104  		}
   105  		// if this mc secret is no longer placed on this cluster, remove the associated secret
   106  		err := clusters.DeleteAssociatedResource(ctx, r.Client, &mcSecret, finalizerName, &corev1.Secret{}, types.NamespacedName{Namespace: mcSecret.Namespace, Name: mcSecret.Name})
   107  		return ctrl.Result{}, err
   108  	}
   109  
   110  	log.Debug("MultiClusterSecret create or update with underlying secret",
   111  		"secret", mcSecret.Spec.Template.Metadata.Name,
   112  		"placement", mcSecret.Spec.Placement.Clusters[0].Name)
   113  	opResult, err := r.createOrUpdateSecret(ctx, mcSecret)
   114  
   115  	// Add our finalizer if not already added
   116  	if err == nil {
   117  		_, err = clusters.AddFinalizer(ctx, r.Client, &mcSecret, finalizerName)
   118  	}
   119  
   120  	ctrlResult, updateErr := r.updateStatus(ctx, &mcSecret, opResult, err)
   121  
   122  	// if an error occurred in createOrUpdate, return that error with a requeue
   123  	// even if update status succeeded
   124  	if err != nil {
   125  		res := ctrl.Result{Requeue: true, RequeueAfter: clusters.GetRandomRequeueDelay()}
   126  		return res, err
   127  	}
   128  
   129  	return ctrlResult, updateErr
   130  
   131  }
   132  
   133  func (r *Reconciler) updateStatus(ctx context.Context, mcSecret *clustersv1alpha1.MultiClusterSecret, opResult controllerutil.OperationResult, err error) (ctrl.Result, error) {
   134  	clusterName := clusters.GetClusterName(ctx, r.Client)
   135  	newCondition := clusters.GetConditionFromResult(err, opResult, "Secret")
   136  	return clusters.UpdateStatus(mcSecret, &mcSecret.Status, mcSecret.Spec.Placement, newCondition, clusterName,
   137  		r.AgentChannel, func() error { return r.Status().Update(ctx, mcSecret) })
   138  }
   139  
   140  // SetupWithManager registers our controller with the manager
   141  func (r *Reconciler) SetupWithManager(mgr ctrl.Manager) error {
   142  	return ctrl.NewControllerManagedBy(mgr).
   143  		For(&clustersv1alpha1.MultiClusterSecret{}).
   144  		Complete(r)
   145  }
   146  
   147  func (r *Reconciler) fetchMultiClusterSecret(ctx context.Context, name types.NamespacedName, mcSecretRef *clustersv1alpha1.MultiClusterSecret) error {
   148  	return r.Get(ctx, name, mcSecretRef)
   149  }
   150  
   151  func (r *Reconciler) createOrUpdateSecret(ctx context.Context, mcSecret clustersv1alpha1.MultiClusterSecret) (controllerutil.OperationResult, error) {
   152  	var secret corev1.Secret
   153  	secret.Namespace = mcSecret.Namespace
   154  	secret.Name = mcSecret.Name
   155  
   156  	return controllerutil.CreateOrUpdate(ctx, r.Client, &secret, func() error {
   157  		r.mutateSecret(mcSecret, &secret)
   158  		return nil
   159  	})
   160  
   161  }
   162  
   163  // mutateSecret mutates the corev1.Secret to reflect the contents of the parent MultiClusterSecret
   164  func (r *Reconciler) mutateSecret(mcSecret clustersv1alpha1.MultiClusterSecret, secret *corev1.Secret) {
   165  	secret.Type = mcSecret.Spec.Template.Type
   166  	secret.Data = mcSecret.Spec.Template.Data
   167  	secret.StringData = mcSecret.Spec.Template.StringData
   168  	secret.Labels = mcSecret.Spec.Template.Metadata.Labels
   169  	if secret.Labels == nil {
   170  		secret.Labels = map[string]string{}
   171  	}
   172  	secret.Labels[vzconst.VerrazzanoManagedLabelKey] = constants.LabelVerrazzanoManagedDefault
   173  	secret.Annotations = mcSecret.Spec.Template.Metadata.Annotations
   174  }