mirror of
https://github.com/helm/helm.git
synced 2026-04-22 06:37:10 -04:00
Merge pull request #8532 from dm3ch/labels-selector
Adding filtering of helm releases for configmap and secret backends during list command
This commit is contained in:
commit
273d0364be
6 changed files with 79 additions and 0 deletions
|
|
@ -126,6 +126,7 @@ func newListCmd(cfg *action.Configuration, out io.Writer) *cobra.Command {
|
|||
f.IntVarP(&client.Limit, "max", "m", 256, "maximum number of releases to fetch")
|
||||
f.IntVar(&client.Offset, "offset", 0, "next release name in the list, used to offset from start value")
|
||||
f.StringVarP(&client.Filter, "filter", "f", "", "a regular expression (Perl compatible). Any releases that match the expression will be included in the results")
|
||||
f.StringVarP(&client.Selector, "selector", "l", "", "Selector (label query) to filter on, supports '=', '==', and '!='.(e.g. -l key1=value1,key2=value2). Works only for secret(default) and configmap storage backends.")
|
||||
bindOutputFlag(cmd, &outfmt)
|
||||
|
||||
return cmd
|
||||
|
|
|
|||
|
|
@ -20,6 +20,8 @@ import (
|
|||
"path"
|
||||
"regexp"
|
||||
|
||||
"k8s.io/apimachinery/pkg/labels"
|
||||
|
||||
"helm.sh/helm/v3/pkg/release"
|
||||
"helm.sh/helm/v3/pkg/releaseutil"
|
||||
)
|
||||
|
|
@ -126,6 +128,7 @@ type List struct {
|
|||
Deployed bool
|
||||
Failed bool
|
||||
Pending bool
|
||||
Selector string
|
||||
}
|
||||
|
||||
// NewList constructs a new *List
|
||||
|
|
@ -156,6 +159,7 @@ func (l *List) Run() ([]*release.Release, error) {
|
|||
if filter != nil && !filter.MatchString(rel.Name) {
|
||||
return false
|
||||
}
|
||||
|
||||
return true
|
||||
})
|
||||
|
||||
|
|
@ -178,6 +182,13 @@ func (l *List) Run() ([]*release.Release, error) {
|
|||
// latest releases, otherwise outdated entries can be returned
|
||||
results = l.filterStateMask(results)
|
||||
|
||||
// Skip anything that doesn't match the selector
|
||||
selectorObj, err := labels.Parse(l.Selector)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
results = l.filterSelector(results, selectorObj)
|
||||
|
||||
// Unfortunately, we have to sort before truncating, which can incur substantial overhead
|
||||
l.sort(results)
|
||||
|
||||
|
|
@ -260,6 +271,18 @@ func (l *List) filterStateMask(releases []*release.Release) []*release.Release {
|
|||
return desiredStateReleases
|
||||
}
|
||||
|
||||
func (l *List) filterSelector(releases []*release.Release, selector labels.Selector) []*release.Release {
|
||||
desiredStateReleases := make([]*release.Release, 0)
|
||||
|
||||
for _, rls := range releases {
|
||||
if selector.Matches(labels.Set(rls.Labels)) {
|
||||
desiredStateReleases = append(desiredStateReleases, rls)
|
||||
}
|
||||
}
|
||||
|
||||
return desiredStateReleases
|
||||
}
|
||||
|
||||
// SetStateMask calculates the state mask based on parameters.
|
||||
func (l *List) SetStateMask() {
|
||||
if l.All {
|
||||
|
|
|
|||
|
|
@ -320,3 +320,49 @@ func TestFilterLatestReleases(t *testing.T) {
|
|||
assert.ElementsMatch(t, expectedFilteredList, filteredList)
|
||||
})
|
||||
}
|
||||
|
||||
func TestSelectorList(t *testing.T) {
|
||||
r1 := releaseStub()
|
||||
r1.Name = "r1"
|
||||
r1.Version = 1
|
||||
r1.Labels = map[string]string{"key": "value1"}
|
||||
r2 := releaseStub()
|
||||
r2.Name = "r2"
|
||||
r2.Version = 1
|
||||
r2.Labels = map[string]string{"key": "value2"}
|
||||
r3 := releaseStub()
|
||||
r3.Name = "r3"
|
||||
r3.Version = 1
|
||||
r3.Labels = map[string]string{}
|
||||
|
||||
lister := newListFixture(t)
|
||||
for _, rel := range []*release.Release{r1, r2, r3} {
|
||||
if err := lister.cfg.Releases.Create(rel); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
t.Run("should fail selector parsing", func(t *testing.T) {
|
||||
is := assert.New(t)
|
||||
lister.Selector = "a?=b"
|
||||
|
||||
_, err := lister.Run()
|
||||
is.Error(err)
|
||||
})
|
||||
|
||||
t.Run("should select one release with matching label", func(t *testing.T) {
|
||||
lister.Selector = "key==value1"
|
||||
res, _ := lister.Run()
|
||||
|
||||
expectedFilteredList := []*release.Release{r1}
|
||||
assert.ElementsMatch(t, expectedFilteredList, res)
|
||||
})
|
||||
|
||||
t.Run("should select two releases with non matching label", func(t *testing.T) {
|
||||
lister.Selector = "key!=value1"
|
||||
res, _ := lister.Run()
|
||||
|
||||
expectedFilteredList := []*release.Release{r2, r3}
|
||||
assert.ElementsMatch(t, expectedFilteredList, res)
|
||||
})
|
||||
}
|
||||
|
|
|
|||
|
|
@ -37,6 +37,9 @@ type Release struct {
|
|||
Version int `json:"version,omitempty"`
|
||||
// Namespace is the kubernetes namespace of the release.
|
||||
Namespace string `json:"namespace,omitempty"`
|
||||
// Labels of the release.
|
||||
// Disabled encoding into Json cause labels are stored in storage driver metadata field.
|
||||
Labels map[string]string `json:"-"`
|
||||
}
|
||||
|
||||
// SetStatus is a helper for setting the status on a release.
|
||||
|
|
|
|||
|
|
@ -105,6 +105,9 @@ func (cfgmaps *ConfigMaps) List(filter func(*rspb.Release) bool) ([]*rspb.Releas
|
|||
cfgmaps.Log("list: failed to decode release: %v: %s", item, err)
|
||||
continue
|
||||
}
|
||||
|
||||
rls.Labels = item.ObjectMeta.Labels
|
||||
|
||||
if filter(rls) {
|
||||
results = append(results, rls)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -97,6 +97,9 @@ func (secrets *Secrets) List(filter func(*rspb.Release) bool) ([]*rspb.Release,
|
|||
secrets.Log("list: failed to decode release: %v: %s", item, err)
|
||||
continue
|
||||
}
|
||||
|
||||
rls.Labels = item.ObjectMeta.Labels
|
||||
|
||||
if filter(rls) {
|
||||
results = append(results, rls)
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue