diff --git a/internal/model/filters.go b/internal/model/filters.go index 83935e3e..bbcb0fe4 100644 --- a/internal/model/filters.go +++ b/internal/model/filters.go @@ -16,6 +16,7 @@ package model import ( "fmt" + "regexp" "github.com/pkg/errors" "github.com/spf13/pflag" @@ -31,6 +32,8 @@ type Namespaced interface { type Filters struct { includes []string excludes []string + regexpObjectIncludes []*regexp.Regexp + regexpObjectExcludes []*regexp.Regexp excludeClusterObjects bool kindFilter Filter componentFilter Filter @@ -39,11 +42,13 @@ type Filters struct { // NewFilters sets up options in the supplied flags and returns a function to return filters. func NewFilters(flags *pflag.FlagSet, includeAllFilters bool) func() (Filters, error) { - var includes, excludes, kindIncludes, kindExcludes, nsIncludes, nsExcludes []string + var includes, excludes, kindIncludes, kindExcludes, nsIncludes, nsExcludes, regexpObjectIncludes, regexpObjectExcludes []string var includeClusterScopedObjects bool flags.StringArrayVarP(&includes, "component", "c", nil, "include just this component") flags.StringArrayVarP(&excludes, "exclude-component", "C", nil, "exclude this component") + flags.StringArrayVarP(®expObjectIncludes, "object", "t", nil, "include k8s components matching this regexp") + flags.StringArrayVarP(®expObjectExcludes, "exclude-object", "T", nil, "exclude k8s components matching this regexp") if includeAllFilters { flags.StringArrayVarP(&kindIncludes, "kind", "k", nil, "include objects with this kind") flags.StringArrayVarP(&kindExcludes, "exclude-kind", "K", nil, "exclude objects with this kind") @@ -69,9 +74,21 @@ func NewFilters(flags *pflag.FlagSet, includeAllFilters bool) func() (Filters, e includeClusterScopedObjects = false } } + + regexpIncludes := make([]*regexp.Regexp, 0, len(regexpObjectIncludes)) + regexpExcludes := make([]*regexp.Regexp, 0, len(regexpObjectExcludes)) + for _, re := range regexpObjectIncludes { + regexpIncludes = append(regexpIncludes, regexp.MustCompile(fmt.Sprintf(`(?i)^%s$`, re))) + } + for _, re := range regexpObjectExcludes { + regexpExcludes = append(regexpExcludes, regexp.MustCompile(fmt.Sprintf(`(?i)^%s$`, re))) + } + return Filters{ includes: includes, excludes: excludes, + regexpObjectIncludes: regexpIncludes, + regexpObjectExcludes: regexpExcludes, kindFilter: of, componentFilter: cf, namespaceFilter: nf, @@ -95,6 +112,24 @@ func (f Filters) GVKFilter(gvk schema.GroupVersionKind) bool { return f.kindFilter != nil && f.kindFilter.ShouldInclude(gvk.Kind) } +// ObjectFilter returns true if the name matches all include regexes and does not match all exclude regexes +// This code was inspired and adapted from grafana tanka +// https://github.com/grafana/tanka/blob/a6a63ac17f713d5fd64bb6d7972bc84c6b5902a6/pkg/process/filter.go#L74 +func (f Filters) ObjectFilter(object K8sQbecMeta) bool { + kindName := object.GetKind() + "/" + object.GetName() + for _, re := range f.regexpObjectIncludes { + if !re.MatchString(kindName) { + return false + } + } + for _, re := range f.regexpObjectExcludes { + if re.MatchString(kindName) { + return false + } + } + return true +} + // HasNamespaceFilters returns true if filters based on namespace scope are in effect. func (f Filters) HasNamespaceFilters() bool { return (f.namespaceFilter != nil && f.namespaceFilter.HasFilters()) || f.excludeClusterObjects @@ -103,6 +138,9 @@ func (f Filters) HasNamespaceFilters() bool { // Match returns true if the current filters match the supplied object. The client can be nil // if namespace scope filters are not in effect. func (f Filters) Match(o K8sQbecMeta, client Namespaced, defaultNS string) (bool, error) { + if !f.ObjectFilter(o) { + return false, nil + } if f.HasNamespaceFilters() && client == nil { return false, fmt.Errorf("no namespace metadata when namespace filters present") }