From e39a8f6bcf6c6288450a507fa59ced088d99a2f6 Mon Sep 17 00:00:00 2001 From: ctrought <65360454+ctrought@users.noreply.github.com> Date: Fri, 31 Mar 2023 14:43:33 -0400 Subject: [PATCH] refactor: use Namespace controller for namespaceSelector Signed-off-by: Craig Trought --- internal/pkg/cmd/reloader.go | 2 +- internal/pkg/controller/controller.go | 75 ++++++++++++++++++--------- pkg/kube/resourcemapper.go | 1 + 3 files changed, 52 insertions(+), 26 deletions(-) diff --git a/internal/pkg/cmd/reloader.go b/internal/pkg/cmd/reloader.go index b297eca..a0ed7ea 100644 --- a/internal/pkg/cmd/reloader.go +++ b/internal/pkg/cmd/reloader.go @@ -148,7 +148,7 @@ func startReloader(cmd *cobra.Command, args []string) { var controllers []*controller.Controller for k := range kube.ResourceMap { - if ignoredResourcesList.Contains(k) { + if ignoredResourcesList.Contains(k) || (len(namespaceLabelSelector) == 0 && k == "namespaces") { continue } diff --git a/internal/pkg/controller/controller.go b/internal/pkg/controller/controller.go index f1934ff..a0e5a66 100644 --- a/internal/pkg/controller/controller.go +++ b/internal/pkg/controller/controller.go @@ -1,7 +1,6 @@ package controller import ( - "context" "fmt" "time" @@ -14,6 +13,7 @@ import ( v1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/fields" + "k8s.io/apimachinery/pkg/labels" "k8s.io/apimachinery/pkg/util/runtime" "k8s.io/apimachinery/pkg/util/wait" "k8s.io/client-go/kubernetes" @@ -22,6 +22,7 @@ import ( "k8s.io/client-go/tools/record" "k8s.io/client-go/util/workqueue" "k8s.io/kubectl/pkg/scheme" + "k8s.io/utils/strings/slices" ) // Controller for checking events @@ -41,6 +42,7 @@ type Controller struct { // controllerInitialized flag determines whether controlled is being initialized var secretControllerInitialized bool = false var configmapControllerInitialized bool = false +var selectedNamespacesCache []string // NewController for initializing a Controller func NewController( @@ -65,7 +67,17 @@ func NewController( recorder := eventBroadcaster.NewRecorder(scheme.Scheme, v1.EventSource{Component: fmt.Sprintf("reloader-%s", resource)}) queue := workqueue.NewRateLimitingQueue(workqueue.DefaultControllerRateLimiter()) - listWatcher := cache.NewListWatchFromClient(client.CoreV1().RESTClient(), resource, namespace, fields.Everything()) + + optionsModifier := func(options *metav1.ListOptions) { + if resource == "namespaces" { + labelSelector := metav1.LabelSelector{MatchLabels: c.namespaceSelector} + options.LabelSelector = labels.Set(labelSelector.MatchLabels).String() + } else { + options.FieldSelector = fields.Everything().String() + } + } + + listWatcher := cache.NewFilteredListWatchFromClient(client.CoreV1().RESTClient(), resource, namespace, optionsModifier) indexer, informer := cache.NewIndexerInformer(listWatcher, kube.ResourceMap[resource], 0, cache.ResourceEventHandlerFuncs{ AddFunc: c.Add, @@ -84,8 +96,15 @@ func NewController( // Add function to add a new object to the queue in case of creating a resource func (c *Controller) Add(obj interface{}) { + + switch object := obj.(type) { + case *v1.Namespace: + c.addSelectedNamespaceToCache(object) + return + } + if options.ReloadOnCreate == "true" { - if !c.resourceInIgnoredNamespace(obj) && c.resourceInNamespaceSelector(obj) && secretControllerInitialized && configmapControllerInitialized { + if !c.resourceInIgnoredNamespace(obj) && c.resourceInSelectedNamespaces(obj) && secretControllerInitialized && configmapControllerInitialized { c.queue.Add(handler.ResourceCreatedHandler{ Resource: obj, Collectors: c.collectors, @@ -105,45 +124,45 @@ func (c *Controller) resourceInIgnoredNamespace(raw interface{}) bool { return false } -func (c *Controller) resourceInNamespaceSelector(raw interface{}) bool { +func (c *Controller) resourceInSelectedNamespaces(raw interface{}) bool { if len(c.namespaceSelector) == 0 { return true } switch object := raw.(type) { case *v1.ConfigMap: - return c.matchLabels(object.ObjectMeta.Namespace) + if slices.Contains(selectedNamespacesCache, object.GetNamespace()) { + return true + } case *v1.Secret: - return c.matchLabels(object.ObjectMeta.Namespace) + if slices.Contains(selectedNamespacesCache, object.GetNamespace()) { + return true + } } - return true + return false } -func (c *Controller) matchLabels(resourceNamespace string) bool { - namespace, err := c.client.CoreV1().Namespaces().Get(context.Background(), resourceNamespace, metav1.GetOptions{}) - if err != nil { - logrus.Warn(err) - return false - } +func (c *Controller) addSelectedNamespaceToCache(namespace *v1.Namespace) { + selectedNamespacesCache = append(selectedNamespacesCache, namespace.GetName()) +} - for selectorKey, selectorVal := range c.namespaceSelector { - - namespaceLabelVal, namespaceLabelKeyExists := namespace.ObjectMeta.Labels[selectorKey] - - if namespaceLabelKeyExists && selectorVal == "*" { - continue - } - - if !namespaceLabelKeyExists || selectorVal != namespaceLabelVal { - return false +func (c *Controller) removeSelectedNamespaceFromCache(namespace *v1.Namespace) { + for i, v := range selectedNamespacesCache { + if v == namespace.GetName() { + selectedNamespacesCache = append(selectedNamespacesCache[:i], selectedNamespacesCache[i+1:]...) + return } } - return true } // Update function to add an old object and a new object to the queue in case of updating a resource func (c *Controller) Update(old interface{}, new interface{}) { - if !c.resourceInIgnoredNamespace(new) && c.resourceInNamespaceSelector(new) { + switch new.(type) { + case *v1.Namespace: + return + } + + if !c.resourceInIgnoredNamespace(new) && c.resourceInSelectedNamespaces(new) { c.queue.Add(handler.ResourceUpdatedHandler{ Resource: new, OldResource: old, @@ -155,6 +174,12 @@ func (c *Controller) Update(old interface{}, new interface{}) { // Delete function to add an object to the queue in case of deleting a resource func (c *Controller) Delete(old interface{}) { + switch object := old.(type) { + case *v1.Namespace: + c.removeSelectedNamespaceFromCache(object) + return + } + // Todo: Any future delete event can be handled here } diff --git a/pkg/kube/resourcemapper.go b/pkg/kube/resourcemapper.go index 2d82ca2..fb42e61 100644 --- a/pkg/kube/resourcemapper.go +++ b/pkg/kube/resourcemapper.go @@ -9,4 +9,5 @@ import ( var ResourceMap = map[string]runtime.Object{ "configMaps": &v1.ConfigMap{}, "secrets": &v1.Secret{}, + "namespaces": &v1.Namespace{}, }