github.com/cilium/cilium@v1.16.2/operator/pkg/secretsync/secretsync.go (about) 1 // SPDX-License-Identifier: Apache-2.0 2 // Copyright Authors of Cilium 3 4 package secretsync 5 6 import ( 7 "context" 8 "fmt" 9 10 "github.com/sirupsen/logrus" 11 corev1 "k8s.io/api/core/v1" 12 "k8s.io/apimachinery/pkg/types" 13 "k8s.io/utils/strings/slices" 14 ctrl "sigs.k8s.io/controller-runtime" 15 "sigs.k8s.io/controller-runtime/pkg/builder" 16 "sigs.k8s.io/controller-runtime/pkg/client" 17 "sigs.k8s.io/controller-runtime/pkg/event" 18 "sigs.k8s.io/controller-runtime/pkg/handler" 19 "sigs.k8s.io/controller-runtime/pkg/predicate" 20 "sigs.k8s.io/controller-runtime/pkg/reconcile" 21 ) 22 23 const ( 24 OwningSecretNamespace = "secretsync.cilium.io/owning-secret-namespace" 25 OwningSecretName = "secretsync.cilium.io/owning-secret-name" 26 ) 27 28 // secretSyncer syncs secrets to dedicated namespace. 29 type secretSyncer struct { 30 client client.Client 31 logger logrus.FieldLogger 32 33 registrations []*SecretSyncRegistration 34 secretNamespaces []string 35 } 36 37 type SecretSyncRegistration struct { 38 // RefObject defines the Kubernetes Object that is referencing a K8s Secret that needs to be synced. 39 RefObject client.Object 40 // RefObjectEnqueueFunc defines the mapping function from the reference object to the Secret. 41 RefObjectEnqueueFunc handler.EventHandler 42 // RefObjectCheckFunc defines a function that is called to check whether the given K8s Secret 43 // is still referenced by a reference object. 44 // Synced Secrets that origin from K8s Secrets that are no longer referenced by any registration are deleted. 45 RefObjectCheckFunc func(ctx context.Context, c client.Client, logger logrus.FieldLogger, obj *corev1.Secret) bool 46 // SecretsNamespace defines the name of the namespace in which the referenced K8s Secrets are to be synchronized. 47 SecretsNamespace string 48 // AdditionalWatches definites additional watches beside watching the directly referencing Kubernetes Object. 49 AdditionalWatches []AdditionalWatch 50 // DefaultSecret defines an optional reference to a TLS Secret that should be synced regardless of whether it's referenced or not. 51 DefaultSecret *DefaultSecret 52 } 53 54 type AdditionalWatch struct { 55 RefObject client.Object 56 RefObjectEnqueueFunc handler.EventHandler 57 RefObjectWatchOptions []builder.WatchesOption 58 } 59 60 type DefaultSecret struct { 61 Namespace string 62 Name string 63 } 64 65 func (r SecretSyncRegistration) String() string { 66 return fmt.Sprintf("%T -> %q", r.RefObject, r.SecretsNamespace) 67 } 68 69 func (r SecretSyncRegistration) IsDefaultSecret(secret *corev1.Secret) bool { 70 return r.DefaultSecret != nil && r.DefaultSecret.Namespace == secret.Namespace && r.DefaultSecret.Name == secret.Name 71 } 72 73 func NewSecretSyncReconciler(c client.Client, logger logrus.FieldLogger, registrations []*SecretSyncRegistration) *secretSyncer { 74 regs := []*SecretSyncRegistration{} 75 secretNamespaces := []string{} 76 for _, r := range registrations { 77 if r != nil { 78 regs = append(regs, r) 79 secretNamespaces = append(secretNamespaces, r.SecretsNamespace) 80 } 81 } 82 83 return &secretSyncer{ 84 client: c, 85 logger: logger, 86 87 registrations: regs, 88 secretNamespaces: secretNamespaces, 89 } 90 } 91 92 // SetupWithManager sets up the controller with the Manager. 93 func (r *secretSyncer) SetupWithManager(mgr ctrl.Manager) error { 94 r.logger.WithField("registrations", r.registrations).Info("Setting up Secret synchronization") 95 96 builder := ctrl.NewControllerManagedBy(mgr). 97 // Source Secrets outside of the secrets namespace 98 For(&corev1.Secret{}, r.notInSecretsNamespace()). 99 // Synced Secrets in the secrets namespace 100 Watches(&corev1.Secret{}, enqueueOwningSecretFromLabels(), r.deletedOrChangedInSecretsNamespace()) 101 102 for _, r := range r.registrations { 103 // Watch main object referencing TLS secrets 104 builder = builder.Watches(r.RefObject, r.RefObjectEnqueueFunc) 105 106 for _, a := range r.AdditionalWatches { 107 builder = builder.Watches(a.RefObject, a.RefObjectEnqueueFunc, a.RefObjectWatchOptions...) 108 } 109 } 110 111 return builder.Complete(r) 112 } 113 114 func (r *secretSyncer) notInSecretsNamespace() builder.Predicates { 115 return builder.WithPredicates(predicate.NewPredicateFuncs(func(object client.Object) bool { 116 return !slices.Contains(r.secretNamespaces, object.GetNamespace()) 117 })) 118 } 119 120 func enqueueOwningSecretFromLabels() handler.EventHandler { 121 return handler.EnqueueRequestsFromMapFunc(func(_ context.Context, o client.Object) []reconcile.Request { 122 labels := o.GetLabels() 123 124 if labels == nil { 125 return nil 126 } 127 128 owningSecretNamespace, owningSecretNamespacePresent := labels[OwningSecretNamespace] 129 owningSecretName, owningSecretNamePresent := labels[OwningSecretName] 130 131 if !owningSecretNamespacePresent || !owningSecretNamePresent { 132 return nil 133 } 134 135 return []reconcile.Request{ 136 { 137 NamespacedName: types.NamespacedName{ 138 Namespace: owningSecretNamespace, 139 Name: owningSecretName, 140 }, 141 }, 142 } 143 }) 144 } 145 146 func (r *secretSyncer) deletedOrChangedInSecretsNamespace() builder.Predicates { 147 return builder.WithPredicates(&deletedOrChangedInSecretsNamespaceStruct{ 148 secretNamespaces: r.secretNamespaces, 149 }) 150 } 151 152 func (r *secretSyncer) hasRegistrations() bool { 153 return len(r.registrations) > 0 154 } 155 156 var _ predicate.Predicate = &deletedOrChangedInSecretsNamespaceStruct{} 157 158 type deletedOrChangedInSecretsNamespaceStruct struct { 159 secretNamespaces []string 160 } 161 162 func (r *deletedOrChangedInSecretsNamespaceStruct) Create(event.CreateEvent) bool { 163 return false 164 } 165 166 func (r *deletedOrChangedInSecretsNamespaceStruct) Update(event event.UpdateEvent) bool { 167 return slices.Contains(r.secretNamespaces, event.ObjectOld.GetNamespace()) 168 } 169 170 func (r *deletedOrChangedInSecretsNamespaceStruct) Delete(event event.DeleteEvent) bool { 171 return slices.Contains(r.secretNamespaces, event.Object.GetNamespace()) 172 } 173 174 func (r *deletedOrChangedInSecretsNamespaceStruct) Generic(event.GenericEvent) bool { 175 return false 176 }