From 593264e2c612ef2892c52004f9ef6d8db0db86e1 Mon Sep 17 00:00:00 2001 From: chaunceyjiang Date: Fri, 7 Apr 2023 14:27:28 +0800 Subject: [PATCH] Allows setting wildcards for SkippedPropagatingNamespaces Signed-off-by: chaunceyjiang --- .../app/controllermanager.go | 16 +-- cmd/controller-manager/app/options/options.go | 10 ++ .../app/options/validation.go | 9 +- pkg/controllers/context/context.go | 5 +- .../namespace/namespace_sync_controller.go | 9 +- pkg/detector/detector.go | 9 +- pkg/detector/detector_test.go | 127 ++++++++++++++++++ 7 files changed, 164 insertions(+), 21 deletions(-) create mode 100644 pkg/detector/detector_test.go diff --git a/cmd/controller-manager/app/controllermanager.go b/cmd/controller-manager/app/controllermanager.go index 08b9df6cdc69..a6f93aa3b156 100644 --- a/cmd/controller-manager/app/controllermanager.go +++ b/cmd/controller-manager/app/controllermanager.go @@ -411,14 +411,10 @@ func startWorkStatusController(ctx controllerscontext.Context) (enabled bool, er } func startNamespaceController(ctx controllerscontext.Context) (enabled bool, err error) { - skippedPropagatingNamespaces := map[string]struct{}{} - for _, ns := range ctx.Opts.SkippedPropagatingNamespaces { - skippedPropagatingNamespaces[ns] = struct{}{} - } namespaceSyncController := &namespace.Controller{ Client: ctx.Mgr.GetClient(), EventRecorder: ctx.Mgr.GetEventRecorderFor(namespace.ControllerName), - SkippedPropagatingNamespaces: skippedPropagatingNamespaces, + SkippedPropagatingNamespaces: ctx.Opts.SkippedPropagatingNamespaces, OverrideManager: ctx.OverrideManager, } if err := namespaceSyncController.SetupWithManager(ctx.Mgr); err != nil { @@ -543,11 +539,6 @@ func setupControllers(mgr controllerruntime.Manager, opts *options.Options, stop return } - skippedPropagatingNamespaces := map[string]struct{}{} - for _, ns := range opts.SkippedPropagatingNamespaces { - skippedPropagatingNamespaces[ns] = struct{}{} - } - controlPlaneInformerManager := genericmanager.NewSingleClusterInformerManager(dynamicClientSet, 0, stopChan) resourceInterpreter := resourceinterpreter.NewResourceInterpreter(controlPlaneInformerManager) @@ -564,12 +555,13 @@ func setupControllers(mgr controllerruntime.Manager, opts *options.Options, stop RESTMapper: mgr.GetRESTMapper(), DynamicClient: dynamicClientSet, SkippedResourceConfig: skippedResourceConfig, - SkippedPropagatingNamespaces: skippedPropagatingNamespaces, + SkippedPropagatingNamespaces: opts.SkippedNamespacesRegexps(), ResourceInterpreter: resourceInterpreter, EventRecorder: mgr.GetEventRecorderFor("resource-detector"), ConcurrentResourceTemplateSyncs: opts.ConcurrentResourceTemplateSyncs, RateLimiterOptions: opts.RateLimiterOpts, } + if err := mgr.Add(resourceDetector); err != nil { klog.Fatalf("Failed to setup resource detector: %v", err) } @@ -606,7 +598,7 @@ func setupControllers(mgr controllerruntime.Manager, opts *options.Options, stop ClusterCacheSyncTimeout: opts.ClusterCacheSyncTimeout, ClusterAPIQPS: opts.ClusterAPIQPS, ClusterAPIBurst: opts.ClusterAPIBurst, - SkippedPropagatingNamespaces: opts.SkippedPropagatingNamespaces, + SkippedPropagatingNamespaces: opts.SkippedNamespacesRegexps(), ConcurrentWorkSyncs: opts.ConcurrentWorkSyncs, EnableTaintManager: opts.EnableTaintManager, RateLimiterOptions: opts.RateLimiterOpts, diff --git a/cmd/controller-manager/app/options/options.go b/cmd/controller-manager/app/options/options.go index 2746fca20de3..210b92414e5f 100644 --- a/cmd/controller-manager/app/options/options.go +++ b/cmd/controller-manager/app/options/options.go @@ -2,6 +2,7 @@ package options import ( "fmt" + "regexp" "strings" "time" @@ -211,3 +212,12 @@ func (o *Options) AddFlags(flags *pflag.FlagSet, allControllers, disabledByDefau o.ProfileOpts.AddFlags(flags) features.FeatureGate.AddFlag(flags) } + +// SkippedNamespacesRegexps precompiled regular expressions +func (o *Options) SkippedNamespacesRegexps() []*regexp.Regexp { + skippedPropagatingNamespaces := make([]*regexp.Regexp, len(o.SkippedPropagatingNamespaces)) + for index, ns := range o.SkippedPropagatingNamespaces { + skippedPropagatingNamespaces[index] = regexp.MustCompile(fmt.Sprintf("^%s$", ns)) + } + return skippedPropagatingNamespaces +} diff --git a/cmd/controller-manager/app/options/validation.go b/cmd/controller-manager/app/options/validation.go index a9190b1ebaf4..b536e87f5392 100644 --- a/cmd/controller-manager/app/options/validation.go +++ b/cmd/controller-manager/app/options/validation.go @@ -1,6 +1,9 @@ package options import ( + "fmt" + "regexp" + "k8s.io/apimachinery/pkg/util/validation/field" "github.com/karmada-io/karmada/pkg/util" @@ -33,6 +36,10 @@ func (o *Options) Validate() field.ErrorList { if o.ClusterStartupGracePeriod.Duration <= 0 { errs = append(errs, field.Invalid(newPath.Child("ClusterStartupGracePeriod"), o.ClusterStartupGracePeriod, "must be greater than 0")) } - + for index, ns := range o.SkippedPropagatingNamespaces { + if _, err := regexp.Compile(fmt.Sprintf("^%s$", ns)); err != nil { + errs = append(errs, field.Invalid(newPath.Child("SkippedPropagatingNamespaces").Index(index), ns, "Invalid namespace regular expression")) + } + } return errs } diff --git a/pkg/controllers/context/context.go b/pkg/controllers/context/context.go index 992d28998c2e..3da9ea5bb0cd 100644 --- a/pkg/controllers/context/context.go +++ b/pkg/controllers/context/context.go @@ -1,6 +1,7 @@ package context import ( + "regexp" "time" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" @@ -52,8 +53,8 @@ type Options struct { ClusterAPIQPS float32 // ClusterAPIBurst is the burst to allow while talking with cluster kube-apiserver. ClusterAPIBurst int - // SkippedPropagatingNamespaces is a list of namespaces that will be skipped for propagating. - SkippedPropagatingNamespaces []string + // SkippedPropagatingNamespaces is a list of namespace regular expressions, matching namespaces will be skipped propagating. + SkippedPropagatingNamespaces []*regexp.Regexp // ClusterName is the name of cluster. ClusterName string // ConcurrentWorkSyncs is the number of Works that are allowed to sync concurrently. diff --git a/pkg/controllers/namespace/namespace_sync_controller.go b/pkg/controllers/namespace/namespace_sync_controller.go index 35b55d4cae02..768ae263c096 100644 --- a/pkg/controllers/namespace/namespace_sync_controller.go +++ b/pkg/controllers/namespace/namespace_sync_controller.go @@ -3,6 +3,7 @@ package namespace import ( "context" "fmt" + "regexp" "strings" corev1 "k8s.io/api/core/v1" @@ -41,7 +42,7 @@ const ( type Controller struct { client.Client // used to operate Work resources. EventRecorder record.EventRecorder - SkippedPropagatingNamespaces map[string]struct{} + SkippedPropagatingNamespaces []*regexp.Regexp OverrideManager overridemanager.OverrideManager } @@ -96,8 +97,10 @@ func (c *Controller) namespaceShouldBeSynced(namespace string) bool { return false } - if _, ok := c.SkippedPropagatingNamespaces[namespace]; ok { - return false + for _, nsRegexp := range c.SkippedPropagatingNamespaces { + if match := nsRegexp.MatchString(namespace); match { + return false + } } return true } diff --git a/pkg/detector/detector.go b/pkg/detector/detector.go index cbd06c2f8fac..90cf4eefe505 100644 --- a/pkg/detector/detector.go +++ b/pkg/detector/detector.go @@ -3,6 +3,7 @@ package detector import ( "context" "fmt" + "regexp" "sync" "time" @@ -55,7 +56,7 @@ type ResourceDetector struct { EventHandler cache.ResourceEventHandler Processor util.AsyncWorker SkippedResourceConfig *util.SkippedResourceConfig - SkippedPropagatingNamespaces map[string]struct{} + SkippedPropagatingNamespaces []*regexp.Regexp // ResourceInterpreter knows the details of resource structure. ResourceInterpreter resourceinterpreter.ResourceInterpreter EventRecorder record.EventRecorder @@ -253,8 +254,10 @@ func (d *ResourceDetector) EventFilter(obj interface{}) bool { } // if SkippedPropagatingNamespaces is set, skip object events in these namespaces. - if _, ok := d.SkippedPropagatingNamespaces[clusterWideKey.Namespace]; ok { - return false + for _, nsRegexp := range d.SkippedPropagatingNamespaces { + if match := nsRegexp.MatchString(clusterWideKey.Namespace); match { + return false + } } if unstructObj, ok := obj.(*unstructured.Unstructured); ok { diff --git a/pkg/detector/detector_test.go b/pkg/detector/detector_test.go new file mode 100644 index 000000000000..ff24b4b51b60 --- /dev/null +++ b/pkg/detector/detector_test.go @@ -0,0 +1,127 @@ +package detector + +import ( + "regexp" + "testing" + + "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" +) + +func BenchmarkEventFilterNoSkipNameSpaces(b *testing.B) { + dt := &ResourceDetector{} + dt.SkippedPropagatingNamespaces = nil + for i := 0; i < b.N; i++ { + dt.EventFilter(&unstructured.Unstructured{ + Object: map[string]interface{}{ + "apiVersion": "apps/v1", + "kind": "Deployment", + "metadata": map[string]interface{}{ + "name": "demo-deployment", + "namespace": "benchmark", + }, + "spec": map[string]interface{}{ + "replicas": 2, + }, + }, + }) + } +} + +func BenchmarkEventFilterNoMatchSkipNameSpaces(b *testing.B) { + dt := &ResourceDetector{} + dt.SkippedPropagatingNamespaces = append(dt.SkippedPropagatingNamespaces, regexp.MustCompile("^benchmark-.*$")) + for i := 0; i < b.N; i++ { + dt.EventFilter(&unstructured.Unstructured{ + Object: map[string]interface{}{ + "apiVersion": "apps/v1", + "kind": "Deployment", + "metadata": map[string]interface{}{ + "name": "demo-deployment", + "namespace": "benchmark", + }, + "spec": map[string]interface{}{ + "replicas": 2, + }, + }, + }) + } +} + +func BenchmarkEventFilterNoWildcards(b *testing.B) { + dt := &ResourceDetector{} + dt.SkippedPropagatingNamespaces = append(dt.SkippedPropagatingNamespaces, regexp.MustCompile("^benchmark$")) + for i := 0; i < b.N; i++ { + dt.EventFilter(&unstructured.Unstructured{ + Object: map[string]interface{}{ + "apiVersion": "apps/v1", + "kind": "Deployment", + "metadata": map[string]interface{}{ + "name": "demo-deployment", + "namespace": "benchmark-1", + }, + "spec": map[string]interface{}{ + "replicas": 2, + }, + }, + }) + } +} + +func BenchmarkEventFilterPrefixMatchSkipNameSpaces(b *testing.B) { + dt := &ResourceDetector{} + dt.SkippedPropagatingNamespaces = append(dt.SkippedPropagatingNamespaces, regexp.MustCompile("^benchmark-.*$")) + for i := 0; i < b.N; i++ { + dt.EventFilter(&unstructured.Unstructured{ + Object: map[string]interface{}{ + "apiVersion": "apps/v1", + "kind": "Deployment", + "metadata": map[string]interface{}{ + "name": "demo-deployment", + "namespace": "benchmark-1", + }, + "spec": map[string]interface{}{ + "replicas": 2, + }, + }, + }) + } +} +func BenchmarkEventFilterSuffixMatchSkipNameSpaces(b *testing.B) { + dt := &ResourceDetector{} + dt.SkippedPropagatingNamespaces = append(dt.SkippedPropagatingNamespaces, regexp.MustCompile("^.*-benchmark$")) + for i := 0; i < b.N; i++ { + dt.EventFilter(&unstructured.Unstructured{ + Object: map[string]interface{}{ + "apiVersion": "apps/v1", + "kind": "Deployment", + "metadata": map[string]interface{}{ + "name": "demo-deployment", + "namespace": "example-benchmark", + }, + "spec": map[string]interface{}{ + "replicas": 2, + }, + }, + }) + } +} + +func BenchmarkEventFilterMultiSkipNameSpaces(b *testing.B) { + dt := &ResourceDetector{} + dt.SkippedPropagatingNamespaces = append(dt.SkippedPropagatingNamespaces, regexp.MustCompile("^.*-benchmark$"), regexp.MustCompile("^benchmark-.*$")) + for i := 0; i < b.N; i++ { + dt.EventFilter(&unstructured.Unstructured{ + Object: map[string]interface{}{ + "apiVersion": "apps/v1", + "kind": "Deployment", + "metadata": map[string]interface{}{ + "name": "demo-deployment", + "namespace": "benchmark-1", + }, + "spec": map[string]interface{}{ + "replicas": 2, + }, + }, + }) + } +}