Skip to content

Commit

Permalink
exclude filters and multiple kind/name filters (#3)
Browse files Browse the repository at this point in the history
  • Loading branch information
ryane authored Jul 6, 2019
1 parent 3d2093f commit 1e8a30b
Show file tree
Hide file tree
Showing 7 changed files with 329 additions and 67 deletions.
35 changes: 25 additions & 10 deletions cmd/root.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,12 +13,19 @@ import (
"github.com/spf13/cobra"
)

var GitCommit, Version string
var (
// GitCommit tracks the current git commit
GitCommit string
// Version tracks the current version
Version string
)

type root struct {
kind string
name string
filename string
includeKinds []string
includeNames []string
excludeKinds []string
excludeNames []string
filename string
}

func newRootCommand(args []string) *cobra.Command {
Expand All @@ -38,8 +45,10 @@ func newRootCommand(args []string) *cobra.Command {
}(),
}

rootCmd.Flags().StringVarP(&root.kind, "kind", "k", "", "Only include resources of kind")
rootCmd.Flags().StringVarP(&root.name, "name", "n", "", "Only include resources of name")
rootCmd.Flags().StringSliceVarP(&root.includeKinds, "kind", "k", []string{}, "Only include resources of kind")
rootCmd.Flags().StringSliceVarP(&root.includeNames, "name", "n", []string{}, "Only include resources with name")
rootCmd.Flags().StringSliceVarP(&root.excludeKinds, "exclude-kind", "K", []string{}, "Exclude resources of kind")
rootCmd.Flags().StringSliceVarP(&root.excludeNames, "exclude-name", "N", []string{}, "Exclude resources with name")
rootCmd.Flags().StringVarP(&root.filename, "filename", "f", "", "Read manifests from file")

rootCmd.SetVersionTemplate(`{{.Version}}`)
Expand Down Expand Up @@ -73,10 +82,15 @@ func (r *root) run() error {
}

// filter
filtered := filter.New(
filter.KindMatcher([]string{r.kind}),
filter.NameMatcher([]string{r.name}),
).Filter(results)
filters := []filter.Filter{}
filters = append(
filters,
filter.ExcludeNameFilter(r.excludeNames...),
filter.ExcludeKindFilter(r.excludeKinds...),
filter.NameFilter(r.includeNames...),
filter.KindFilter(r.includeKinds...),
)
filtered := filter.New(filters...).Filter(results)

// print
if err := printer.New().Print(filtered); err != nil {
Expand All @@ -86,6 +100,7 @@ func (r *root) run() error {
return nil
}

// Execute runs the root command
func Execute(args []string) {
if err := newRootCommand(args).Execute(); err != nil {
log.WithError(err).Error()
Expand Down
25 changes: 18 additions & 7 deletions pkg/filter/filter.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,23 +8,24 @@ type Filter interface {
Filter([]unstructured.Unstructured) []unstructured.Unstructured
}

type defaultFilter struct {
matchers []Matcher
type chainedFilter struct {
filters []Filter
}

type Matcher interface {
Match(unstructured.Unstructured) bool
Valid() bool
}

func New(matchers ...Matcher) Filter {
return &defaultFilter{matchers}
func New(filters ...Filter) Filter {
return &chainedFilter{filters}
}

func (f *defaultFilter) Filter(unstructureds []unstructured.Unstructured) []unstructured.Unstructured {
func (f *chainedFilter) Filter(unstructureds []unstructured.Unstructured) []unstructured.Unstructured {
filtered := unstructureds

for _, matcher := range f.matchers {
filtered = filter(filtered, matcher)
for _, filter := range f.filters {
filtered = filter.Filter(filtered)
}

return filtered
Expand All @@ -39,3 +40,13 @@ func filter(unstructureds []unstructured.Unstructured, matcher Matcher) []unstru
}
return filtered
}

func excludeFilter(unstructureds []unstructured.Unstructured, matcher Matcher) []unstructured.Unstructured {
filtered := []unstructured.Unstructured{}
for _, u := range unstructureds {
if !matcher.Match(u) {
filtered = append(filtered, u)
}
}
return filtered
}
109 changes: 99 additions & 10 deletions pkg/filter/filter_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,23 +6,112 @@ import (
"github.com/ryane/kfilt/pkg/filter"
)

type includeNames []string
type includeKinds []string
type excludeNames []string
type excludeKinds []string
type expectNames []string

func TestFilter(t *testing.T) {
tests := []struct {
names []string
kinds []string
expectNames []string
includeKinds includeKinds
includeNames includeNames
excludeKinds excludeKinds
excludeNames excludeNames
expectNames []string
}{
{[]string{"test-sa"}, []string{"Deployment"}, []string{}},
{[]string{"test-sa"}, []string{}, []string{"test-sa"}},
{[]string{"test-sa"}, []string{""}, []string{"test-sa"}},
{[]string{}, []string{"ServiceAccount"}, []string{"test-sa", "test-sa-2"}},
{[]string{"test-pod", "test-deployment"}, []string{"ServiceAccount"}, []string{}},
{
includeKinds{"Deployment", "Pod"},
includeNames{},
excludeKinds{},
excludeNames{},
expectNames{"test-pod", "test-deployment"},
},
{
includeKinds{"Deployment"},
includeNames{"test-sa"},
excludeKinds{},
excludeNames{},
expectNames{},
},
{
includeKinds{},
includeNames{"test-sa"},
excludeKinds{},
excludeNames{},
expectNames{"test-sa"},
},
{
includeKinds{},
includeNames{"test-sa", "test-sa-2"},
excludeKinds{},
excludeNames{},
expectNames{"test-sa", "test-sa-2"},
},
{
includeKinds{""},
includeNames{"test-sa"},
excludeKinds{},
excludeNames{},
expectNames{"test-sa"},
},
{
includeKinds{"ServiceAccount"},
includeNames{},
excludeKinds{},
excludeNames{},
expectNames{"test-sa", "test-sa-2"},
},
{
includeKinds{"ServiceAccount"},
includeNames{"test-pod", "test-deployment"},
excludeKinds{},
excludeNames{},
expectNames{},
},
{
includeKinds{"ServiceAccount"},
includeNames{},
excludeKinds{},
excludeNames{"test-sa"},
expectNames{"test-sa-2"},
},
{
includeKinds{},
includeNames{},
excludeKinds{"ServiceAccount"},
excludeNames{},
expectNames{"test-pod", "test-deployment"},
},
{
includeKinds{},
includeNames{},
excludeKinds{"ServiceAccount", "Deployment"},
excludeNames{},
expectNames{"test-pod"},
},
{
includeKinds{"ServiceAccount", "Deployment"},
includeNames{},
excludeKinds{"ServiceAccount"},
excludeNames{},
expectNames{"test-deployment"},
},
{
includeKinds{},
includeNames{"test-sa", "test-sa-2"},
excludeKinds{"ServiceAccount"},
excludeNames{},
expectNames{},
},
}

for _, test := range tests {
f := filter.New(
filter.KindMatcher(test.kinds),
filter.NameMatcher(test.names),
filter.ExcludeNameFilter(test.excludeNames...),
filter.ExcludeKindFilter(test.excludeKinds...),
filter.NameFilter(test.includeNames...),
filter.KindFilter(test.includeKinds...),
)

results := f.Filter(input)
Expand Down
39 changes: 34 additions & 5 deletions pkg/filter/kind_filter.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,36 @@ import (
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
)

type kindFilter struct {
matcher Matcher
}

func (f *kindFilter) Filter(unstructureds []unstructured.Unstructured) []unstructured.Unstructured {
if f.matcher.Valid() {
return filter(unstructureds, f.matcher)
}
return unstructureds
}

func KindFilter(kinds ...string) Filter {
return &kindFilter{KindMatcher(kinds)}
}

type excludeKindFilter struct {
matcher Matcher
}

func (f *excludeKindFilter) Filter(unstructureds []unstructured.Unstructured) []unstructured.Unstructured {
if f.matcher.Valid() {
return excludeFilter(unstructureds, f.matcher)
}
return unstructureds
}

func ExcludeKindFilter(kinds ...string) Filter {
return &excludeKindFilter{KindMatcher(kinds)}
}

type kindMatcher struct {
kinds []string
}
Expand All @@ -14,12 +44,11 @@ func KindMatcher(kinds []string) Matcher {
return &kindMatcher{validKinds(kinds)}
}

func (f *kindMatcher) Match(u unstructured.Unstructured) bool {
// no kinds specified so we just return a match
if len(f.kinds) == 0 {
return true
}
func (f *kindMatcher) Valid() bool {
return len(f.kinds) > 0
}

func (f *kindMatcher) Match(u unstructured.Unstructured) bool {
for _, kind := range f.kinds {
if strings.EqualFold(kind, u.GetKind()) {
return true
Expand Down
75 changes: 60 additions & 15 deletions pkg/filter/kind_filter_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,25 +4,36 @@ import (
"testing"

"github.com/ryane/kfilt/pkg/filter"
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
)

func TestKindFilterNil(t *testing.T) {
matcher := filter.KindMatcher(nil)

for _, u := range input {
if !matcher.Match(u) {
t.Errorf("expected match for %s", u.GetKind())
t.FailNow()
}
func TestKindMatcher(t *testing.T) {
var u = unstructured.Unstructured{
Object: map[string]interface{}{
"kind": "ServiceAccount",
"metadata": map[string]interface{}{
"name": "test-sa",
},
},
}
}

func TestKindFilterEmptyVals(t *testing.T) {
matcher := filter.KindMatcher([]string{" ", ""})
tests := []struct {
kinds []string
expected bool
}{
{[]string{"ServiceAccount"}, true},
{[]string{"serviceaccount"}, true},
{[]string{""}, false},
{[]string{" "}, false},
{[]string{}, false},
{[]string{"pod"}, false},
{[]string{"Deployment", "ServiceAccount"}, true},
}

for _, u := range input {
if !matcher.Match(u) {
t.Errorf("expected match for %s", u.GetKind())
for _, test := range tests {
matcher := filter.KindMatcher(test.kinds)
if result := matcher.Match(u); result != test.expected {
t.Errorf("expected %v for %v, got %v", test.expected, test.kinds, result)
t.FailNow()
}
}
Expand All @@ -34,14 +45,48 @@ func TestKindFilter(t *testing.T) {
expectNames []string
}{
{[]string{"ServiceAccount"}, []string{"test-sa", "test-sa-2"}},
{[]string{"ServiceAccount", "Deployment"}, []string{"test-sa", "test-sa-2", "test-deployment"}},
{[]string{"Deployment"}, []string{"test-deployment"}},
{[]string{"deployment"}, []string{"test-deployment"}},
{[]string{"Pod"}, []string{"test-pod"}},
{[]string{"ServiceAccount", "Deployment"}, []string{"test-sa", "test-sa-2", "test-deployment"}},
}

for _, test := range tests {
f := filter.New(filter.KindMatcher(test.kinds))
f := filter.KindFilter(test.kinds...)

results := f.Filter(input)
if len(results) != len(test.expectNames) {
t.Errorf("expected %d results, got %d", len(test.expectNames), len(results))
t.FailNow()
}

for i, u := range results {
name := u.GetName()
if name != test.expectNames[i] {
t.Errorf("expected %s, got %s", test.expectNames[i], name)
t.FailNow()
}
}
}
}

func TestExcludeKindFilter(t *testing.T) {
tests := []struct {
kinds []string
expectNames []string
}{
{[]string{}, []string{"test-sa", "test-sa-2", "test-pod", "test-deployment"}},
{[]string{"", " "}, []string{"test-sa", "test-sa-2", "test-pod", "test-deployment"}},
{[]string{"ServiceAccount"}, []string{"test-pod", "test-deployment"}},
{[]string{"Deployment"}, []string{"test-sa", "test-sa-2", "test-pod"}},
{[]string{"deployment"}, []string{"test-sa", "test-sa-2", "test-pod"}},
{[]string{"Pod"}, []string{"test-sa", "test-sa-2", "test-deployment"}},
{[]string{"ServiceAccount", "Deployment"}, []string{"test-pod"}},
}

for _, test := range tests {
f := filter.ExcludeKindFilter(test.kinds...)

results := f.Filter(input)
if len(results) != len(test.expectNames) {
Expand Down
Loading

0 comments on commit 1e8a30b

Please sign in to comment.