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 }