From dc69973ff442935fa721f4544bab45658749b181 Mon Sep 17 00:00:00 2001 From: aananthraj Date: Fri, 2 Aug 2019 00:35:11 +0530 Subject: [PATCH] Add support for ignoring namespaces This Commit, - adds support for ignoring certain namespaces while watching all namespaces using `IgnoreNamespaceChecker` - replaces `Namespaces []string` with `Namespaces.Include []string` - adds `Namespaces.Ignore []string` ( optional / omitempty ) filed to ignore namespaces when Namespaces.Inculde contains "all" - adds unit test cases for isNamespaceIgnored() --- config.yaml | 90 +++++++++++++++---- deploy-all-in-one-tls.yaml | 86 ++++++++++++++---- deploy-all-in-one.yaml | 85 ++++++++++++++---- helm/botkube/values.yaml | 84 +++++++++++++---- pkg/config/config.go | 13 ++- pkg/controller/controller.go | 1 + .../ignore_namespace_checker.go | 63 +++++++++++++ .../ignore_namespace_checker_test.go | 29 ++++++ pkg/utils/utils.go | 4 +- 9 files changed, 378 insertions(+), 77 deletions(-) create mode 100644 pkg/filterengine/filters/ignore-namespace-filter/ignore_namespace_checker.go create mode 100644 pkg/filterengine/filters/ignore-namespace-filter/ignore_namespace_checker_test.go diff --git a/config.yaml b/config.yaml index 1a23ceb6bd..a11b72769e 100644 --- a/config.yaml +++ b/config.yaml @@ -1,92 +1,132 @@ ## Resources you want to watch resources: - name: pod # Name of the resources e.g pod, deployment, ingress, etc. - namespaces: # List of namespaces, "all" will watch all the namespaces - - all - events: # List of lifecycle events you want to receive, e.g create, update, delete, error OR all + namespaces: # List of namespaces, "all" will watch all the namespaces + include: + - all + ignore: # List of namespaces to be ignored (omitempty), used only with include: all + - # example : include [all], ignore [x,y,z] + events: # List of lifecycle events you want to receive, e.g create, update, delete, error OR all - create - delete - error - name: service namespaces: - - all + include: + - all + ignore: + - events: - create - delete - error - name: deployment namespaces: - - all + include: + - all + ignore: + - events: - create - delete + - update - error - name: statefulset namespaces: - - all + include: + - all + ignore: + - events: - create - delete - error - name: ingress namespaces: - - all + include: + - all + ignore: + - events: - create - delete - error - name: node namespaces: - - all + include: + - all + ignore: + - events: - create - delete - error - name: namespace namespaces: - - all + include: + - all + ignore: + - events: - create - delete - error - name: persistentvolume namespaces: - - all + include: + - all + ignore: + - events: - create - delete - error - name: persistentvolumeclaim namespaces: - - all + include: + - all + ignore: + - events: - create - delete - error - name: secret namespaces: - - all + include: + - all + ignore: + - events: - create - delete - error - name: configmap namespaces: - - all + include: + - all + ignore: + - events: - create - delete - error - name: daemonset namespaces: - - all + include: + - all + ignore: + - events: - create - delete - error - name: job namespaces: - - all + include: + - all + ignore: + - events: - create - delete @@ -94,28 +134,40 @@ resources: - update - name: role namespaces: - - all + include: + - all + ignore: + - events: - create - delete - error - name: rolebinding namespaces: - - all + include: + - all + ignore: + - events: - create - delete - error - name: clusterrolebindings namespaces: - - all + include: + - all + ignore: + - events: - create - delete - error - name: clusterrolebinding namespaces: - - all + include: + - all + ignore: + - events: - create - delete diff --git a/deploy-all-in-one-tls.yaml b/deploy-all-in-one-tls.yaml index e2b3581f99..0c03a53072 100644 --- a/deploy-all-in-one-tls.yaml +++ b/deploy-all-in-one-tls.yaml @@ -10,93 +10,132 @@ data: config.yaml: | ## Resources you want to watch resources: - - name: pod # Name of the resources e.g pod, deployment, ingress, etc. (Resource name must be in singular form) - namespaces: # List of namespaces, "all" will watch all the namespaces - - all - events: # List of lifecycle events you want to receive, e.g create, update, delete OR all + namespaces: + include: + - all + ignore: # List of namespaces to be ignored (omitempty), used only with include: all + - # example : include [all], ignore [x,y,z] + events: # List of lifecycle events you want to receive, e.g create, update, delete, error OR all - create - delete - error - name: service namespaces: - - all + include: + - all + ignore: + - events: - create - delete - error - name: deployment namespaces: - - all + include: + - all + ignore: + - events: - create - delete - error - name: statefulset namespaces: - - all + include: + - all + ignore: + - events: - create - delete - error - name: ingress namespaces: - - all + include: + - all + ignore: + - events: - create - delete - error - name: node namespaces: - - all + include: + - all + ignore: + - events: - create - delete - error - name: namespace namespaces: - - all + include: + - all + ignore: + - events: - create - delete - error - name: persistentvolume namespaces: - - all + include: + - all + ignore: + - events: - create - delete - error - name: persistentvolumeclaim namespaces: - - all + include: + - all + ignore: + - events: - create - delete - error - name: secret namespaces: - - all + include: + - all + ignore: + - events: - create - delete - error - name: configmap namespaces: - - all + include: + - all + ignore: + - events: - create - delete - error - name: daemonset namespaces: - - all + include: + - all + ignore: + - events: - create - delete + - update - error - name: job namespaces: - - all + include: + - all + ignore: + - events: - create - update @@ -104,14 +143,20 @@ data: - error - name: role namespaces: - - all + include: + - all + ignore: + - events: - create - delete - error - name: rolebinding namespaces: - - all + include: + - all + ignore: + - events: - create - delete @@ -125,7 +170,10 @@ data: - error - name: clusterrolebinding namespaces: - - all + include: + - all + ignore: + - events: - create - delete diff --git a/deploy-all-in-one.yaml b/deploy-all-in-one.yaml index 8b65e636a3..cab3976653 100644 --- a/deploy-all-in-one.yaml +++ b/deploy-all-in-one.yaml @@ -11,92 +11,132 @@ data: ## Resources you want to watch resources: - name: pod # Name of the resources e.g pod, deployment, ingress, etc. (Resource name must be in singular form) - namespaces: # List of namespaces, "all" will watch all the namespaces - - all - events: # List of lifecycle events you want to receive, e.g create, update, delete OR all + namespaces: + include: + - all + ignore: # List of namespaces to be ignored (omitempty), used only with include: all + - # example : include [all], ignore [x,y,z] + events: # List of lifecycle events you want to receive, e.g create, update, delete, error OR all - create - delete - error - name: service namespaces: - - all + include: + - all + ignore: + - events: - create - delete - error - name: deployment namespaces: - - all + include: + - all + ignore: + - events: - create - delete - error - name: statefulset namespaces: - - all + include: + - all + ignore: + - events: - create - delete - error - name: ingress namespaces: - - all + include: + - all + ignore: + - events: - create - delete - error - name: node namespaces: - - all + include: + - all + ignore: + - events: - create - delete - error - name: namespace namespaces: - - all + include: + - all + ignore: + - events: - create - delete - error - name: persistentvolume namespaces: - - all + include: + - all + ignore: + - events: - create - delete - error - name: persistentvolumeclaim namespaces: - - all + include: + - all + ignore: + - events: - create - delete - error - name: secret namespaces: - - all + include: + - all + ignore: + - events: - create - delete - error - name: configmap namespaces: - - all + include: + - all + ignore: + - events: - create - delete - error - name: daemonset namespaces: - - all + include: + - all + ignore: + - events: - create + - update - delete - error - name: job namespaces: - - all + include: + - all + ignore: + - events: - create - update @@ -104,14 +144,20 @@ data: - error - name: role namespaces: - - all + include: + - all + ignore: + - events: - create - delete - error - name: rolebinding namespaces: - - all + include: + - all + ignore: + - events: - create - delete @@ -125,7 +171,10 @@ data: - error - name: clusterrolebinding namespaces: - - all + include: + - all + ignore: + - events: - create - delete diff --git a/helm/botkube/values.yaml b/helm/botkube/values.yaml index 0b1a114b74..95767d5472 100644 --- a/helm/botkube/values.yaml +++ b/helm/botkube/values.yaml @@ -21,92 +21,131 @@ config: ## Resources you want to watch resources: - name: pod # Name of the resources e.g pod, deployment, ingress, etc. (Resource name must be in singular form) - namespaces: # List of namespaces, "all" will watch all the namespaces - - all - events: # List of lifecycle events you want to receive, e.g create, update, delete, error OR all + namespaces: + include: + - all + ignore: # List of namespaces to be ignored (omitempty), used only with include: all + - # example : include [all], ignore [x,y,z] + events: # List of lifecycle events you want to receive, e.g create, update, delete, error OR all - create - delete - error - name: service namespaces: - - all + include: + - all + ignore: + - events: - create - delete - error - name: deployment namespaces: - - all + include: + - all + ignore: + - events: - create - delete - error - name: statefulset namespaces: - - all + include: + - all + ignore: + - events: - create - delete - error - name: ingress namespaces: - - all + include: + - all + ignore: + - events: - create - delete - error - name: node namespaces: - - all + include: + - all + ignore: + - events: - create - delete - error - name: namespace namespaces: - - all + include: + - all + ignore: + - events: - create - delete - error - name: persistentvolume namespaces: - - all + include: + - all + ignore: + - events: - create - delete - error - name: persistentvolumeclaim namespaces: - - all + include: + - all + ignore: + - events: - create - delete - error - name: secret namespaces: - - all + include: + - all + ignore: + - events: - create - delete - error - name: configmap namespaces: - - all + include: + - all + ignore: + - events: - create - delete - error - name: daemonset namespaces: - - all + include: + - all + ignore: + - events: - create - delete - error - name: job namespaces: - - all + include: + - all + ignore: + - events: - create - delete @@ -114,14 +153,20 @@ config: - error - name: role namespaces: - - all + include: + - all + ignore: + - events: - create - delete - error - name: rolebinding namespaces: - - all + include: + - all + ignore: + - events: - create - delete @@ -135,7 +180,10 @@ config: - error - name: clusterrolebinding namespaces: - - all + include: + - all + ignore: + - events: - create - delete diff --git a/pkg/config/config.go b/pkg/config/config.go index 8f40cbd7f2..b428cf6a4a 100644 --- a/pkg/config/config.go +++ b/pkg/config/config.go @@ -53,10 +53,21 @@ type Config struct { // Resource contains resources to watch type Resource struct { Name string - Namespaces []string + Namespaces Namespaces Events []EventType } +// Namespaces contains namespaces to include and ignore +// Include contains a list of namespaces to be watched, +// - "all" to watch all the namespaces +// Ignore contains a list of namespaces to be ignored when all namespaces are included +// It is an optional (omitempty) field which is tandem with Include [all] +// example : include [all], ignore [x,y,z] +type Namespaces struct { + Include []string + Ignore []string `yaml:",omitempty"` +} + // Communications channels to send events to type Communications struct { Slack Slack diff --git a/pkg/controller/controller.go b/pkg/controller/controller.go index 2fd05b0a65..b1a7776f22 100644 --- a/pkg/controller/controller.go +++ b/pkg/controller/controller.go @@ -119,6 +119,7 @@ func sendEvent(obj interface{}, c *config.Config, kind string, eventType config. objectMeta := utils.GetObjectMetaData(obj) if !utils.AllowedEventKindsMap[utils.EventKind{Resource: kind, Namespace: "all", EventType: eventType}] && !utils.AllowedEventKindsMap[utils.EventKind{Resource: kind, Namespace: objectMeta.Namespace, EventType: eventType}] { + log.Logger.Debugf("Ignoring %s to %s/%v in %s namespaces", eventType, kind, objectMeta.Name, objectMeta.Namespace) return } log.Logger.Debugf("Processing %s to %s/%v in %s namespaces", eventType, kind, objectMeta.Name, objectMeta.Namespace) diff --git a/pkg/filterengine/filters/ignore-namespace-filter/ignore_namespace_checker.go b/pkg/filterengine/filters/ignore-namespace-filter/ignore_namespace_checker.go new file mode 100644 index 0000000000..6880a6f590 --- /dev/null +++ b/pkg/filterengine/filters/ignore-namespace-filter/ignore_namespace_checker.go @@ -0,0 +1,63 @@ +package filters + +import ( + "fmt" + "strings" + + "github.com/infracloudio/botkube/pkg/config" + "github.com/infracloudio/botkube/pkg/events" + "github.com/infracloudio/botkube/pkg/filterengine" + log "github.com/infracloudio/botkube/pkg/logging" +) + +// IgnoreNamespaceChecker ignore events from blocklisted namespaces +type IgnoreNamespaceChecker struct { + Description string +} + +// Register filter +func init() { + filterengine.DefaultFilterEngine.Register(IgnoreNamespaceChecker{ + Description: "Checks if event belongs to blocklisted namespaces and filter them.", + }) +} + +// Run filters and modifies event struct +func (f IgnoreNamespaceChecker) Run(object interface{}, event *events.Event) { + // load config.yaml + BotKubeConfig, err := config.New() + if err != nil { + log.Logger.Errorf(fmt.Sprintf("Error in loading configuration. Error:%s", err.Error())) + log.Logger.Debug("Skipping ignore namespace filter.") + } + if BotKubeConfig != nil { + for _, resource := range BotKubeConfig.Resources { + if resource.Name == strings.ToLower(event.Kind) { + // check if namespace to be ignored + if isNamespaceIgnored(resource.Namespaces, event.Namespace) { + event.Skip = true + } + } + } + + } + log.Logger.Debug("Ignore Namespaces filter successful!") +} + +// Describe filter +func (f IgnoreNamespaceChecker) Describe() string { + return f.Description +} + +// isNamespaceIgnored checks if a event to be ignored from user config +func isNamespaceIgnored(resourceNamespaces config.Namespaces, eventNamespace string) bool { + if len(resourceNamespaces.Include) == 1 && resourceNamespaces.Include[0] == "all" { + if len(resourceNamespaces.Ignore) > 0 { + ignoredNamespaces := fmt.Sprintf("%#v", resourceNamespaces.Ignore) + if strings.Contains(ignoredNamespaces, eventNamespace) { + return true + } + } + } + return false +} diff --git a/pkg/filterengine/filters/ignore-namespace-filter/ignore_namespace_checker_test.go b/pkg/filterengine/filters/ignore-namespace-filter/ignore_namespace_checker_test.go new file mode 100644 index 0000000000..97bc0d3d2f --- /dev/null +++ b/pkg/filterengine/filters/ignore-namespace-filter/ignore_namespace_checker_test.go @@ -0,0 +1,29 @@ +package filters + +import ( + "testing" + + "github.com/infracloudio/botkube/pkg/config" +) + +func TestIsNamespaceIgnored(t *testing.T) { + tests := map[string]struct { + Namespaces config.Namespaces + eventNamespace string + expected bool + }{ + `include all and ignore few --> watch all except ignored`: {config.Namespaces{Include: []string{"all"}, Ignore: []string{"demo", "abc"}}, "demo", true}, + `include all and ignore is "" --> watch all`: {config.Namespaces{Include: []string{"all"}, Ignore: []string{""}}, "demo", false}, + `include all and ignore is [] --> watch all`: {config.Namespaces{Include: []string{"all"}, Ignore: []string{}}, "demo", false}, + // utils.AllowedEventKindsMap inherently handles remaining test case + } + for name, test := range tests { + name, test := name, test + t.Run(name, func(t *testing.T) { + actual := isNamespaceIgnored(test.Namespaces, test.eventNamespace) + if actual != test.expected { + t.Errorf("expected: %+v != actual: %+v\n", test.expected, actual) + } + }) + } +} diff --git a/pkg/utils/utils.go b/pkg/utils/utils.go index 16ab261955..0dedb9153f 100644 --- a/pkg/utils/utils.go +++ b/pkg/utils/utils.go @@ -120,7 +120,7 @@ func InitInformerMap() { allEvents = true break } - for _, ns := range r.Namespaces { + for _, ns := range r.Namespaces.Include { AllowedEventKindsMap[EventKind{Resource: r.Name, Namespace: ns, EventType: e}] = true } } @@ -129,7 +129,7 @@ func InitInformerMap() { if allEvents { events := []config.EventType{config.CreateEvent, config.UpdateEvent, config.DeleteEvent, config.ErrorEvent} for _, ev := range events { - for _, ns := range r.Namespaces { + for _, ns := range r.Namespaces.Include { AllowedEventKindsMap[EventKind{Resource: r.Name, Namespace: ns, EventType: ev}] = true } }