diff --git a/cmd/clusterlint/main.go b/cmd/clusterlint/main.go index f1a1036e..46ad194a 100644 --- a/cmd/clusterlint/main.go +++ b/cmd/clusterlint/main.go @@ -103,12 +103,12 @@ func main() { Name: "C, ignore-checks", Usage: "run a specific check", }, - cli.StringFlag{ - Name: "n, namespace", + cli.StringSliceFlag{ + Name: "n, namespaces", Usage: "run checks in specific namespace", }, - cli.StringFlag{ - Name: "N, ignore-namespace", + cli.StringSliceFlag{ + Name: "N, ignore-namespaces", Usage: "run checks not in specific namespace", }, cli.StringFlag{ @@ -201,7 +201,7 @@ func runChecks(c *cli.Context) error { diagnosticFilter := checks.DiagnosticFilter{Severity: checks.Severity(c.String("level"))} - objectFilter, err := kube.NewObjectFilter(c.String("n"), c.String("N")) + objectFilter, err := kube.NewObjectFilter(c.StringSlice("n"), c.StringSlice("N")) if err != nil { return err } diff --git a/kube/object_filter.go b/kube/object_filter.go index d425f1b6..7dea1900 100644 --- a/kube/object_filter.go +++ b/kube/object_filter.go @@ -25,12 +25,12 @@ import ( // ObjectFilter stores k8s object's fields that needs to be included or excluded while running checks type ObjectFilter struct { - IncludeNamespace string - ExcludeNamespace string + IncludeNamespace []string + ExcludeNamespace []string } // NewObjectFilter is a constructor to initialize an instance of ObjectFilter -func NewObjectFilter(includeNamespace, excludeNamespace string) (ObjectFilter, error) { +func NewObjectFilter(includeNamespace, excludeNamespace []string) (ObjectFilter, error) { if len(includeNamespace) > 0 && len(excludeNamespace) > 0 { return ObjectFilter{}, fmt.Errorf("cannot specify both include and exclude namespace conditions") } @@ -42,11 +42,18 @@ func NewObjectFilter(includeNamespace, excludeNamespace string) (ObjectFilter, e // NamespaceOptions returns ListOptions for filtering by namespace func (f ObjectFilter) NamespaceOptions(opts metav1.ListOptions) metav1.ListOptions { + var selectors []fields.Selector if len(f.IncludeNamespace) > 0 { - opts.FieldSelector = fields.OneTermEqualSelector("metadata.namespace", f.IncludeNamespace).String() + for _, namespace := range f.IncludeNamespace { + selectors = append(selectors, fields.OneTermEqualSelector("metadata.namespace", namespace)) + } + } else if len(f.ExcludeNamespace) > 0 { + for _, namespace := range f.ExcludeNamespace { + selectors = append(selectors, fields.OneTermNotEqualSelector("metadata.namespace", namespace)) + } } - if len(f.ExcludeNamespace) > 0 { - opts.FieldSelector = fields.OneTermNotEqualSelector("metadata.namespace", f.ExcludeNamespace).String() + if len(selectors) > 0 { + opts.FieldSelector = fields.AndSelectors(selectors...).String() } return opts } diff --git a/kube/object_filter_test.go b/kube/object_filter_test.go index 37ace249..2207640d 100644 --- a/kube/object_filter_test.go +++ b/kube/object_filter_test.go @@ -27,24 +27,44 @@ import ( ) func TestNamespaceError(t *testing.T) { - _, err := NewObjectFilter("kube-system", "kube-system") + _, err := NewObjectFilter([]string{"namespace-1"}, []string{"namespace-2"}) assert.Error(t, err) assert.Equal(t, fmt.Errorf("cannot specify both include and exclude namespace conditions"), err) } func TestNamespaceOptions(t *testing.T) { - filter, err := NewObjectFilter("namespace-1", "") + filter, err := NewObjectFilter([]string{"namespace-1"}, []string{}) assert.NoError(t, err) assert.Equal(t, metav1.ListOptions{FieldSelector: fields.OneTermEqualSelector("metadata.namespace", "namespace-1").String()}, filter.NamespaceOptions(metav1.ListOptions{}), ) - filter, err = NewObjectFilter("", "namespace-2") + filter, err = NewObjectFilter([]string{}, []string{"namespace-2"}) assert.NoError(t, err) assert.Equal(t, metav1.ListOptions{FieldSelector: fields.OneTermNotEqualSelector("metadata.namespace", "namespace-2").String()}, filter.NamespaceOptions(metav1.ListOptions{}), ) + + filter, err = NewObjectFilter([]string{"namespace-1", "namespace-2"}, []string{}) + assert.NoError(t, err) + assert.Equal(t, + metav1.ListOptions{FieldSelector: fields.AndSelectors( + fields.OneTermEqualSelector("metadata.namespace", "namespace-1"), + fields.OneTermEqualSelector("metadata.namespace", "namespace-2"), + ).String()}, + filter.NamespaceOptions(metav1.ListOptions{}), + ) + + filter, err = NewObjectFilter([]string{}, []string{"namespace-3", "namespace-4"}) + assert.NoError(t, err) + assert.Equal(t, + metav1.ListOptions{FieldSelector: fields.AndSelectors( + fields.OneTermNotEqualSelector("metadata.namespace", "namespace-3"), + fields.OneTermNotEqualSelector("metadata.namespace", "namespace-4"), + ).String()}, + filter.NamespaceOptions(metav1.ListOptions{}), + ) }