From f7fd7a5b9be2b77305db7e8df2b59790e8faed00 Mon Sep 17 00:00:00 2001 From: Sayed Soroush Hashemi Date: Mon, 12 Jun 2023 16:29:41 +0330 Subject: [PATCH 1/3] Add supported namespaces feature --- cmd/utils/flags.go | 25 ++++++++++++++----------- operator/redisfailover/config.go | 7 ++++--- operator/redisfailover/handler.go | 6 ++++++ 3 files changed, 24 insertions(+), 14 deletions(-) diff --git a/cmd/utils/flags.go b/cmd/utils/flags.go index 578fe0e62..99b2ad99d 100644 --- a/cmd/utils/flags.go +++ b/cmd/utils/flags.go @@ -11,14 +11,15 @@ import ( // CMDFlags are the flags used by the cmd // TODO: improve flags. type CMDFlags struct { - KubeConfig string - Development bool - ListenAddr string - MetricsPath string - K8sQueriesPerSecond int - K8sQueriesBurstable int - Concurrency int - LogLevel string + KubeConfig string + SupportedNamespacesRegex string + Development bool + ListenAddr string + MetricsPath string + K8sQueriesPerSecond int + K8sQueriesBurstable int + Concurrency int + LogLevel string } // Init initializes and parse the flags @@ -26,6 +27,7 @@ func (c *CMDFlags) Init() { kubehome := filepath.Join(homedir.HomeDir(), ".kube", "config") // register flags flag.StringVar(&c.KubeConfig, "kubeconfig", kubehome, "kubernetes configuration path, only used when development mode enabled") + flag.StringVar(&c.SupportedNamespacesRegex, "supported-namespaces-regex", ".*", "To limit the namespaces this operator looks into") flag.BoolVar(&c.Development, "development", false, "development flag will allow to run the operator outside a kubernetes cluster") flag.StringVar(&c.ListenAddr, "listen-address", ":9710", "Address to listen on for metrics.") flag.StringVar(&c.MetricsPath, "metrics-path", "/metrics", "Path to serve the metrics.") @@ -42,8 +44,9 @@ func (c *CMDFlags) Init() { // ToRedisOperatorConfig convert the flags to redisfailover config func (c *CMDFlags) ToRedisOperatorConfig() redisfailover.Config { return redisfailover.Config{ - ListenAddress: c.ListenAddr, - MetricsPath: c.MetricsPath, - Concurrency: c.Concurrency, + ListenAddress: c.ListenAddr, + MetricsPath: c.MetricsPath, + Concurrency: c.Concurrency, + SupportedNamespacesRegex: c.SupportedNamespacesRegex, } } diff --git a/operator/redisfailover/config.go b/operator/redisfailover/config.go index 97e316779..1cd2ba0d4 100644 --- a/operator/redisfailover/config.go +++ b/operator/redisfailover/config.go @@ -2,7 +2,8 @@ package redisfailover // Config is the configuration for the redis operator. type Config struct { - ListenAddress string - MetricsPath string - Concurrency int + ListenAddress string + MetricsPath string + Concurrency int + SupportedNamespacesRegex string } diff --git a/operator/redisfailover/handler.go b/operator/redisfailover/handler.go index 6a03afd56..9a9b28c25 100644 --- a/operator/redisfailover/handler.go +++ b/operator/redisfailover/handler.go @@ -59,6 +59,12 @@ func (r *RedisFailoverHandler) Handle(_ context.Context, obj runtime.Object) err return fmt.Errorf("can't handle the received object: not a redisfailover") } + if match, err := regexp.Match(r.config.SupportedNamespacesRegex, []byte(rf.Namespace)); err != nil { + return fmt.Errorf("can't check the Redisfailover's namespace against the target namespace regex: %w", err) + } else if !match { + return fmt.Errorf("the Redisfailover is in a not-supported namespace %s. Only supporting namespaces that match this regex %s", rf.Namespace, r.config.SupportedNamespacesRegex) + } + if err := rf.Validate(); err != nil { r.mClient.SetClusterError(rf.Namespace, rf.Name) return err From 89969ecc0c72efdc7870030723516a47866e8c5e Mon Sep 17 00:00:00 2001 From: Sayed Soroush Hashemi Date: Mon, 12 Jun 2023 17:54:28 +0330 Subject: [PATCH 2/3] Move the namespace filtering from handler to retriever --- operator/redisfailover/factory.go | 37 +++++++++++++++++++++++++++---- operator/redisfailover/handler.go | 6 ----- 2 files changed, 33 insertions(+), 10 deletions(-) diff --git a/operator/redisfailover/factory.go b/operator/redisfailover/factory.go index 2ebc67eb7..9d19fbecb 100644 --- a/operator/redisfailover/factory.go +++ b/operator/redisfailover/factory.go @@ -2,6 +2,7 @@ package redisfailover import ( "context" + "regexp" "time" "github.com/spotahome/kooper/v2/controller" @@ -13,6 +14,7 @@ import ( "k8s.io/client-go/kubernetes" "k8s.io/client-go/tools/cache" + redisfailoverv1 "github.com/spotahome/redis-operator/api/redisfailover/v1" "github.com/spotahome/redis-operator/log" "github.com/spotahome/redis-operator/metrics" rfservice "github.com/spotahome/redis-operator/operator/redisfailover/service" @@ -36,7 +38,7 @@ func New(cfg Config, k8sService k8s.Services, k8sClient kubernetes.Interface, lo // Create the handlers. rfHandler := NewRedisFailoverHandler(cfg, rfService, rfChecker, rfHealer, k8sService, kooperMetricsRecorder, logger) - rfRetriever := NewRedisFailoverRetriever(k8sService) + rfRetriever := NewRedisFailoverRetriever(cfg, k8sService) kooperLogger := kooperlogger{Logger: logger.WithField("operator", "redisfailover")} // Leader election service. @@ -58,13 +60,40 @@ func New(cfg Config, k8sService k8s.Services, k8sClient kubernetes.Interface, lo }) } -func NewRedisFailoverRetriever(cli k8s.Services) controller.Retriever { +func NewRedisFailoverRetriever(cfg Config, cli k8s.Services) controller.Retriever { + isNamespaceSupported := func(rf redisfailoverv1.RedisFailover) bool { + match, _ := regexp.Match(cfg.SupportedNamespacesRegex, []byte(rf.Namespace)) + return match + } + // check in the startup whether the regex compiles + return controller.MustRetrieverFromListerWatcher(&cache.ListWatch{ ListFunc: func(options metav1.ListOptions) (runtime.Object, error) { - return cli.ListRedisFailovers(context.Background(), "", options) + rfList, err := cli.ListRedisFailovers(context.Background(), "", options) + if err != nil { + return rfList, err + } + + targetRFList := make([]redisfailoverv1.RedisFailover, 0) + for _, rf := range rfList.Items { + if isNamespaceSupported(rf) { + targetRFList = append(targetRFList, rf) + } + } + rfList.Items = targetRFList + + return rfList, err }, WatchFunc: func(options metav1.ListOptions) (watch.Interface, error) { - return cli.WatchRedisFailovers(context.Background(), "", options) + watcher, err := cli.WatchRedisFailovers(context.Background(), "", options) + watcher = watch.Filter(watcher, func(event watch.Event) (watch.Event, bool) { + rf, ok := event.Object.(*redisfailoverv1.RedisFailover) + if !ok { + return event, false + } + return event, isNamespaceSupported(*rf) + }) + return watcher, err }, }) } diff --git a/operator/redisfailover/handler.go b/operator/redisfailover/handler.go index 9a9b28c25..6a03afd56 100644 --- a/operator/redisfailover/handler.go +++ b/operator/redisfailover/handler.go @@ -59,12 +59,6 @@ func (r *RedisFailoverHandler) Handle(_ context.Context, obj runtime.Object) err return fmt.Errorf("can't handle the received object: not a redisfailover") } - if match, err := regexp.Match(r.config.SupportedNamespacesRegex, []byte(rf.Namespace)); err != nil { - return fmt.Errorf("can't check the Redisfailover's namespace against the target namespace regex: %w", err) - } else if !match { - return fmt.Errorf("the Redisfailover is in a not-supported namespace %s. Only supporting namespaces that match this regex %s", rf.Namespace, r.config.SupportedNamespacesRegex) - } - if err := rf.Validate(); err != nil { r.mClient.SetClusterError(rf.Namespace, rf.Name) return err From a3d23de74bedb2f509b0b1673e7bdc578da6c6a1 Mon Sep 17 00:00:00 2001 From: Sayed Soroush Hashemi Date: Mon, 12 Jun 2023 17:55:29 +0330 Subject: [PATCH 3/3] Check if the supported namespace regex compiles at startup --- cmd/utils/flags.go | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/cmd/utils/flags.go b/cmd/utils/flags.go index 99b2ad99d..86927ed09 100644 --- a/cmd/utils/flags.go +++ b/cmd/utils/flags.go @@ -2,7 +2,9 @@ package utils import ( "flag" + "fmt" "path/filepath" + "regexp" "github.com/spotahome/redis-operator/operator/redisfailover" "k8s.io/client-go/util/homedir" @@ -39,6 +41,10 @@ func (c *CMDFlags) Init() { flag.StringVar(&c.LogLevel, "log-level", "info", "set log level") // Parse flags flag.Parse() + + if _, err := regexp.Compile(c.SupportedNamespacesRegex); err != nil { + panic(fmt.Errorf("supported namespaces Regex is not valid: %w", err)) + } } // ToRedisOperatorConfig convert the flags to redisfailover config