From 08fcdc7614aefa5e20872d1818d0f64496d7429e Mon Sep 17 00:00:00 2001 From: Timofey Sedov Date: Fri, 5 Sep 2025 15:47:18 +0300 Subject: [PATCH 1/2] Add regex support in filters --- internal/model/filters.go | 25 ++++++++++++++++++++++++- 1 file changed, 24 insertions(+), 1 deletion(-) diff --git a/internal/model/filters.go b/internal/model/filters.go index 83935e3e..5bc275ba 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,7 @@ type Namespaced interface { type Filters struct { includes []string excludes []string + regexpIncludes []*regexp.Regexp excludeClusterObjects bool kindFilter Filter componentFilter Filter @@ -39,11 +41,12 @@ 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, regexpIncludes []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(®expIncludes, "regexp", "r", nil, "include 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 +72,16 @@ func NewFilters(flags *pflag.FlagSet, includeAllFilters bool) func() (Filters, e includeClusterScopedObjects = false } } + + regexps := make([]*regexp.Regexp, 0, len(regexpIncludes)) + for _, re := range regexpIncludes { + regexps = append(regexps, regexp.MustCompile(re)) + } + return Filters{ includes: includes, excludes: excludes, + regexpIncludes: regexps, kindFilter: of, componentFilter: cf, namespaceFilter: nf, @@ -95,6 +105,16 @@ func (f Filters) GVKFilter(gvk schema.GroupVersionKind) bool { return f.kindFilter != nil && f.kindFilter.ShouldInclude(gvk.Kind) } +// RegexpFilter returns true if the name matches all regexpes. +func (f Filters) RegexpFilter(name string) bool { + for _, re := range f.regexpIncludes { + if !re.MatchString(name) { + 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 +123,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.RegexpFilter(o.GetName()) { + return false, nil + } if f.HasNamespaceFilters() && client == nil { return false, fmt.Errorf("no namespace metadata when namespace filters present") } From e13b2804a7672daeeaae64f346aa96f78dd1ed29 Mon Sep 17 00:00:00 2001 From: Timofey Sedov Date: Tue, 18 Nov 2025 01:01:23 +0300 Subject: [PATCH 2/2] Update regex filter --- internal/model/filters.go | 39 +++++++++++++++++++++++++++------------ 1 file changed, 27 insertions(+), 12 deletions(-) diff --git a/internal/model/filters.go b/internal/model/filters.go index 5bc275ba..bbcb0fe4 100644 --- a/internal/model/filters.go +++ b/internal/model/filters.go @@ -32,7 +32,8 @@ type Namespaced interface { type Filters struct { includes []string excludes []string - regexpIncludes []*regexp.Regexp + regexpObjectIncludes []*regexp.Regexp + regexpObjectExcludes []*regexp.Regexp excludeClusterObjects bool kindFilter Filter componentFilter Filter @@ -41,12 +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, regexpIncludes []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(®expIncludes, "regexp", "r", nil, "include k8s components matching this regexp") + 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") @@ -73,15 +75,20 @@ func NewFilters(flags *pflag.FlagSet, includeAllFilters bool) func() (Filters, e } } - regexps := make([]*regexp.Regexp, 0, len(regexpIncludes)) - for _, re := range regexpIncludes { - regexps = append(regexps, regexp.MustCompile(re)) + 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, - regexpIncludes: regexps, + regexpObjectIncludes: regexpIncludes, + regexpObjectExcludes: regexpExcludes, kindFilter: of, componentFilter: cf, namespaceFilter: nf, @@ -105,10 +112,18 @@ func (f Filters) GVKFilter(gvk schema.GroupVersionKind) bool { return f.kindFilter != nil && f.kindFilter.ShouldInclude(gvk.Kind) } -// RegexpFilter returns true if the name matches all regexpes. -func (f Filters) RegexpFilter(name string) bool { - for _, re := range f.regexpIncludes { - if !re.MatchString(name) { +// 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 } } @@ -123,7 +138,7 @@ 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.RegexpFilter(o.GetName()) { + if !f.ObjectFilter(o) { return false, nil } if f.HasNamespaceFilters() && client == nil {