github.com/cilium/cilium@v1.16.2/operator/pkg/secretsync/secretsync_reconcile.go (about) 1 // SPDX-License-Identifier: Apache-2.0 2 // Copyright Authors of Cilium 3 4 package secretsync 5 6 import ( 7 "context" 8 9 "github.com/sirupsen/logrus" 10 corev1 "k8s.io/api/core/v1" 11 k8serrors "k8s.io/apimachinery/pkg/api/errors" 12 "k8s.io/apimachinery/pkg/types" 13 ctrl "sigs.k8s.io/controller-runtime" 14 "sigs.k8s.io/controller-runtime/pkg/client" 15 "sigs.k8s.io/controller-runtime/pkg/reconcile" 16 17 controllerruntime "github.com/cilium/cilium/operator/pkg/controller-runtime" 18 "github.com/cilium/cilium/pkg/logging/logfields" 19 ) 20 21 // Reconcile is part of the main kubernetes reconciliation loop which aims to 22 // move the current state of the cluster closer to the desired state. 23 // 24 // For more details, check Reconcile and its Result here: 25 // - https://pkg.go.dev/sigs.k8s.io/controller-runtime@v0.12.2/pkg/reconcile 26 func (r *secretSyncer) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) { 27 scopedLog := r.logger.WithFields(logrus.Fields{ 28 logfields.Controller: "secret-syncer", 29 logfields.Resource: req.NamespacedName, 30 }) 31 scopedLog.Info("Reconciling secret") 32 33 original := &corev1.Secret{} 34 if err := r.client.Get(ctx, req.NamespacedName, original); err != nil { 35 if k8serrors.IsNotFound(err) { 36 scopedLog.WithError(err).Debug("Unable to get Secret - either deleted or not yet available") 37 38 synced := false 39 // Check whether synced secret needs to be deleted from the registered secret namespaces. 40 for _, ns := range r.secretNamespaces { 41 // Check if there's an existing synced secret for the deleted Secret 42 deleted, err := r.cleanupSyncedSecret(ctx, req, scopedLog, ns) 43 if err != nil { 44 return controllerruntime.Fail(err) 45 } 46 47 synced = synced || deleted 48 } 49 50 scopedLog.WithField(logfields.Action, action(synced)).Info("Successfully reconciled Secret") 51 return controllerruntime.Success() 52 } 53 54 return controllerruntime.Fail(err) 55 } 56 57 cleanupNamespaces := map[string]struct{}{} 58 for _, ns := range r.secretNamespaces { 59 cleanupNamespaces[ns] = struct{}{} 60 } 61 62 synced := false 63 for _, reg := range r.registrations { 64 if reg.RefObjectCheckFunc(ctx, r.client, r.logger, original) || reg.IsDefaultSecret(original) { 65 desiredSync := desiredSyncSecret(reg.SecretsNamespace, original) 66 67 scopedLog.WithField("secretNamespace", reg.SecretsNamespace).Debug("Syncing secret") 68 if err := r.ensureSyncedSecret(ctx, desiredSync); err != nil { 69 return controllerruntime.Fail(err) 70 } 71 72 synced = true 73 delete(cleanupNamespaces, reg.SecretsNamespace) 74 } 75 } 76 77 // Check whether synced secret needs to be deleted from the secret namespaces 78 // where the secret is no longer referenced by any registration. 79 for ns := range cleanupNamespaces { 80 // Check if there's an existing synced secret that should be deleted 81 deleted, err := r.cleanupSyncedSecret(ctx, req, scopedLog, ns) 82 if err != nil { 83 return controllerruntime.Fail(err) 84 } 85 synced = synced || deleted 86 } 87 88 scopedLog.WithField(logfields.Action, action(synced)).Info("Successfully reconciled Secret") 89 return controllerruntime.Success() 90 } 91 92 func action(synced bool) string { 93 action := "ignored" 94 if synced { 95 action = "synced" 96 } 97 98 return action 99 } 100 101 func (r *secretSyncer) cleanupSyncedSecret(ctx context.Context, req reconcile.Request, scopedLog *logrus.Entry, ns string) (bool, error) { 102 syncSecret := &corev1.Secret{} 103 if err := r.client.Get(ctx, types.NamespacedName{Namespace: ns, Name: req.Namespace + "-" + req.Name}, syncSecret); err == nil { 104 // Try to delete existing synced secret 105 scopedLog.WithField("secretNamespace", ns).Debug("Delete synced secret") 106 if err := r.client.Delete(ctx, syncSecret); err != nil { 107 return true, err 108 } 109 110 return true, nil 111 } 112 113 return false, nil 114 } 115 116 func desiredSyncSecret(secretsNamespace string, original *corev1.Secret) *corev1.Secret { 117 s := &corev1.Secret{} 118 s.SetNamespace(secretsNamespace) 119 s.SetName(original.Namespace + "-" + original.Name) 120 s.SetAnnotations(original.GetAnnotations()) 121 s.SetLabels(original.GetLabels()) 122 if s.Labels == nil { 123 s.Labels = map[string]string{} 124 } 125 s.Labels[OwningSecretNamespace] = original.Namespace 126 s.Labels[OwningSecretName] = original.Name 127 s.Immutable = original.Immutable 128 s.Data = original.Data 129 s.StringData = original.StringData 130 s.Type = original.Type 131 132 return s 133 } 134 135 func (r *secretSyncer) ensureSyncedSecret(ctx context.Context, desired *corev1.Secret) error { 136 existing := &corev1.Secret{} 137 if err := r.client.Get(ctx, client.ObjectKeyFromObject(desired), existing); err != nil { 138 if k8serrors.IsNotFound(err) { 139 return r.client.Create(ctx, desired) 140 } 141 return err 142 } 143 144 temp := existing.DeepCopy() 145 temp.SetAnnotations(desired.GetAnnotations()) 146 temp.SetLabels(desired.GetLabels()) 147 temp.Immutable = desired.Immutable 148 temp.Data = desired.Data 149 temp.StringData = desired.StringData 150 temp.Type = desired.Type 151 152 return r.client.Patch(ctx, temp, client.MergeFrom(existing)) 153 }