Merge pull request #3373 from chaunceyjiang/wildcards

feat: Allows setting wildcards for SkippedPropagatingNamespaces
This commit is contained in:
karmada-bot 2023-04-19 17:18:11 +08:00 committed by GitHub
commit 507e66f6c6
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 164 additions and 21 deletions

View File

@ -411,14 +411,10 @@ func startWorkStatusController(ctx controllerscontext.Context) (enabled bool, er
} }
func startNamespaceController(ctx controllerscontext.Context) (enabled bool, err error) { 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{ namespaceSyncController := &namespace.Controller{
Client: ctx.Mgr.GetClient(), Client: ctx.Mgr.GetClient(),
EventRecorder: ctx.Mgr.GetEventRecorderFor(namespace.ControllerName), EventRecorder: ctx.Mgr.GetEventRecorderFor(namespace.ControllerName),
SkippedPropagatingNamespaces: skippedPropagatingNamespaces, SkippedPropagatingNamespaces: ctx.Opts.SkippedPropagatingNamespaces,
OverrideManager: ctx.OverrideManager, OverrideManager: ctx.OverrideManager,
} }
if err := namespaceSyncController.SetupWithManager(ctx.Mgr); err != nil { if err := namespaceSyncController.SetupWithManager(ctx.Mgr); err != nil {
@ -543,11 +539,6 @@ func setupControllers(mgr controllerruntime.Manager, opts *options.Options, stop
return return
} }
skippedPropagatingNamespaces := map[string]struct{}{}
for _, ns := range opts.SkippedPropagatingNamespaces {
skippedPropagatingNamespaces[ns] = struct{}{}
}
controlPlaneInformerManager := genericmanager.NewSingleClusterInformerManager(dynamicClientSet, 0, stopChan) controlPlaneInformerManager := genericmanager.NewSingleClusterInformerManager(dynamicClientSet, 0, stopChan)
resourceInterpreter := resourceinterpreter.NewResourceInterpreter(controlPlaneInformerManager) resourceInterpreter := resourceinterpreter.NewResourceInterpreter(controlPlaneInformerManager)
@ -564,12 +555,13 @@ func setupControllers(mgr controllerruntime.Manager, opts *options.Options, stop
RESTMapper: mgr.GetRESTMapper(), RESTMapper: mgr.GetRESTMapper(),
DynamicClient: dynamicClientSet, DynamicClient: dynamicClientSet,
SkippedResourceConfig: skippedResourceConfig, SkippedResourceConfig: skippedResourceConfig,
SkippedPropagatingNamespaces: skippedPropagatingNamespaces, SkippedPropagatingNamespaces: opts.SkippedNamespacesRegexps(),
ResourceInterpreter: resourceInterpreter, ResourceInterpreter: resourceInterpreter,
EventRecorder: mgr.GetEventRecorderFor("resource-detector"), EventRecorder: mgr.GetEventRecorderFor("resource-detector"),
ConcurrentResourceTemplateSyncs: opts.ConcurrentResourceTemplateSyncs, ConcurrentResourceTemplateSyncs: opts.ConcurrentResourceTemplateSyncs,
RateLimiterOptions: opts.RateLimiterOpts, RateLimiterOptions: opts.RateLimiterOpts,
} }
if err := mgr.Add(resourceDetector); err != nil { if err := mgr.Add(resourceDetector); err != nil {
klog.Fatalf("Failed to setup resource detector: %v", err) 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, ClusterCacheSyncTimeout: opts.ClusterCacheSyncTimeout,
ClusterAPIQPS: opts.ClusterAPIQPS, ClusterAPIQPS: opts.ClusterAPIQPS,
ClusterAPIBurst: opts.ClusterAPIBurst, ClusterAPIBurst: opts.ClusterAPIBurst,
SkippedPropagatingNamespaces: opts.SkippedPropagatingNamespaces, SkippedPropagatingNamespaces: opts.SkippedNamespacesRegexps(),
ConcurrentWorkSyncs: opts.ConcurrentWorkSyncs, ConcurrentWorkSyncs: opts.ConcurrentWorkSyncs,
EnableTaintManager: opts.EnableTaintManager, EnableTaintManager: opts.EnableTaintManager,
RateLimiterOptions: opts.RateLimiterOpts, RateLimiterOptions: opts.RateLimiterOpts,

View File

@ -2,6 +2,7 @@ package options
import ( import (
"fmt" "fmt"
"regexp"
"strings" "strings"
"time" "time"
@ -211,3 +212,12 @@ func (o *Options) AddFlags(flags *pflag.FlagSet, allControllers, disabledByDefau
o.ProfileOpts.AddFlags(flags) o.ProfileOpts.AddFlags(flags)
features.FeatureGate.AddFlag(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
}

View File

@ -1,6 +1,9 @@
package options package options
import ( import (
"fmt"
"regexp"
"k8s.io/apimachinery/pkg/util/validation/field" "k8s.io/apimachinery/pkg/util/validation/field"
"github.com/karmada-io/karmada/pkg/util" "github.com/karmada-io/karmada/pkg/util"
@ -33,6 +36,10 @@ func (o *Options) Validate() field.ErrorList {
if o.ClusterStartupGracePeriod.Duration <= 0 { if o.ClusterStartupGracePeriod.Duration <= 0 {
errs = append(errs, field.Invalid(newPath.Child("ClusterStartupGracePeriod"), o.ClusterStartupGracePeriod, "must be greater than 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 return errs
} }

View File

@ -1,6 +1,7 @@
package context package context
import ( import (
"regexp"
"time" "time"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
@ -52,8 +53,8 @@ type Options struct {
ClusterAPIQPS float32 ClusterAPIQPS float32
// ClusterAPIBurst is the burst to allow while talking with cluster kube-apiserver. // ClusterAPIBurst is the burst to allow while talking with cluster kube-apiserver.
ClusterAPIBurst int ClusterAPIBurst int
// SkippedPropagatingNamespaces is a list of namespaces that will be skipped for propagating. // SkippedPropagatingNamespaces is a list of namespace regular expressions, matching namespaces will be skipped propagating.
SkippedPropagatingNamespaces []string SkippedPropagatingNamespaces []*regexp.Regexp
// ClusterName is the name of cluster. // ClusterName is the name of cluster.
ClusterName string ClusterName string
// ConcurrentWorkSyncs is the number of Works that are allowed to sync concurrently. // ConcurrentWorkSyncs is the number of Works that are allowed to sync concurrently.

View File

@ -3,6 +3,7 @@ package namespace
import ( import (
"context" "context"
"fmt" "fmt"
"regexp"
"strings" "strings"
corev1 "k8s.io/api/core/v1" corev1 "k8s.io/api/core/v1"
@ -41,7 +42,7 @@ const (
type Controller struct { type Controller struct {
client.Client // used to operate Work resources. client.Client // used to operate Work resources.
EventRecorder record.EventRecorder EventRecorder record.EventRecorder
SkippedPropagatingNamespaces map[string]struct{} SkippedPropagatingNamespaces []*regexp.Regexp
OverrideManager overridemanager.OverrideManager OverrideManager overridemanager.OverrideManager
} }
@ -96,8 +97,10 @@ func (c *Controller) namespaceShouldBeSynced(namespace string) bool {
return false return false
} }
if _, ok := c.SkippedPropagatingNamespaces[namespace]; ok { for _, nsRegexp := range c.SkippedPropagatingNamespaces {
return false if match := nsRegexp.MatchString(namespace); match {
return false
}
} }
return true return true
} }

View File

@ -3,6 +3,7 @@ package detector
import ( import (
"context" "context"
"fmt" "fmt"
"regexp"
"sync" "sync"
"time" "time"
@ -55,7 +56,7 @@ type ResourceDetector struct {
EventHandler cache.ResourceEventHandler EventHandler cache.ResourceEventHandler
Processor util.AsyncWorker Processor util.AsyncWorker
SkippedResourceConfig *util.SkippedResourceConfig SkippedResourceConfig *util.SkippedResourceConfig
SkippedPropagatingNamespaces map[string]struct{} SkippedPropagatingNamespaces []*regexp.Regexp
// ResourceInterpreter knows the details of resource structure. // ResourceInterpreter knows the details of resource structure.
ResourceInterpreter resourceinterpreter.ResourceInterpreter ResourceInterpreter resourceinterpreter.ResourceInterpreter
EventRecorder record.EventRecorder 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 SkippedPropagatingNamespaces is set, skip object events in these namespaces.
if _, ok := d.SkippedPropagatingNamespaces[clusterWideKey.Namespace]; ok { for _, nsRegexp := range d.SkippedPropagatingNamespaces {
return false if match := nsRegexp.MatchString(clusterWideKey.Namespace); match {
return false
}
} }
if unstructObj, ok := obj.(*unstructured.Unstructured); ok { if unstructObj, ok := obj.(*unstructured.Unstructured); ok {

View File

@ -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,
},
},
})
}
}