Browse Source

Kick ClusterExternalSecret reconciliation when a Namespace is updated (#2486)

Signed-off-by: shuheiktgw <s-kitagawa@mercari.com>
Shuhei Kitagawa 2 years ago
parent
commit
ce52c98bef

+ 74 - 0
pkg/controllers/clusterexternalsecret/clusterexternalsecret_controller.go

@@ -16,6 +16,7 @@ package clusterexternalsecret
 
 import (
 	"context"
+	"reflect"
 	"sort"
 	"time"
 
@@ -23,6 +24,7 @@ import (
 	v1 "k8s.io/api/core/v1"
 	apierrors "k8s.io/apimachinery/pkg/api/errors"
 	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
+	"k8s.io/apimachinery/pkg/labels"
 	"k8s.io/apimachinery/pkg/runtime"
 	"k8s.io/apimachinery/pkg/types"
 	ctrl "sigs.k8s.io/controller-runtime"
@@ -30,6 +32,10 @@ import (
 	"sigs.k8s.io/controller-runtime/pkg/client"
 	"sigs.k8s.io/controller-runtime/pkg/controller"
 	"sigs.k8s.io/controller-runtime/pkg/controller/controllerutil"
+	"sigs.k8s.io/controller-runtime/pkg/event"
+	"sigs.k8s.io/controller-runtime/pkg/handler"
+	"sigs.k8s.io/controller-runtime/pkg/predicate"
+	"sigs.k8s.io/controller-runtime/pkg/reconcile"
 
 	esv1beta1 "github.com/external-secrets/external-secrets/apis/externalsecrets/v1beta1"
 	"github.com/external-secrets/external-secrets/pkg/controllers/clusterexternalsecret/cesmetrics"
@@ -265,5 +271,73 @@ func (r *Reconciler) SetupWithManager(mgr ctrl.Manager, opts controller.Options)
 		WithOptions(opts).
 		For(&esv1beta1.ClusterExternalSecret{}).
 		Owns(&esv1beta1.ExternalSecret{}, builder.OnlyMetadata).
+		Watches(
+			&v1.Namespace{},
+			handler.EnqueueRequestsFromMapFunc(r.findObjectsForNamespace),
+			builder.WithPredicates(namespacePredicate()),
+		).
 		Complete(r)
 }
+
+func (r *Reconciler) findObjectsForNamespace(ctx context.Context, namespace client.Object) []reconcile.Request {
+	namespaceLabels := labels.Set(namespace.GetLabels())
+
+	// Avoid consuming too much memory
+	const limit = 100
+	var requests []reconcile.Request
+	options := &client.ListOptions{Limit: limit}
+
+	for {
+		var clusterExternalSecrets esv1beta1.ClusterExternalSecretList
+		if err := r.List(ctx, &clusterExternalSecrets, options); err != nil {
+			r.Log.Error(err, errGetCES)
+			return []reconcile.Request{}
+		}
+
+		for i := range clusterExternalSecrets.Items {
+			clusterExternalSecret := &clusterExternalSecrets.Items[i]
+			labelSelector, err := metav1.LabelSelectorAsSelector(&clusterExternalSecret.Spec.NamespaceSelector)
+			if err != nil {
+				r.Log.Error(err, errConvertLabelSelector)
+				return []reconcile.Request{}
+			}
+
+			if labelSelector.Matches(namespaceLabels) {
+				requests = append(requests, reconcile.Request{
+					NamespacedName: types.NamespacedName{
+						Name:      clusterExternalSecret.GetName(),
+						Namespace: clusterExternalSecret.GetNamespace(),
+					},
+				})
+			}
+		}
+
+		if clusterExternalSecrets.Continue == "" {
+			break
+		}
+
+		options = &client.ListOptions{
+			Limit:    limit,
+			Continue: clusterExternalSecrets.Continue,
+		}
+	}
+
+	return requests
+}
+
+func namespacePredicate() predicate.Predicate {
+	return predicate.Funcs{
+		CreateFunc: func(e event.CreateEvent) bool {
+			return true
+		},
+		UpdateFunc: func(e event.UpdateEvent) bool {
+			if e.ObjectOld == nil || e.ObjectNew == nil {
+				return false
+			}
+			return !reflect.DeepEqual(e.ObjectOld.GetLabels(), e.ObjectNew.GetLabels())
+		},
+		DeleteFunc: func(deleteEvent event.DeleteEvent) bool {
+			return true
+		},
+	}
+}