Skip to content

Commit

Permalink
Merge pull request #9785 from jmguzik/unification-of-label-filter
Browse files Browse the repository at this point in the history
Unification of label and until filters across list/prune endpoints
  • Loading branch information
openshift-merge-robot authored Mar 24, 2021
2 parents 860de13 + 914218c commit 0cb3066
Show file tree
Hide file tree
Showing 8 changed files with 152 additions and 117 deletions.
14 changes: 3 additions & 11 deletions libpod/image/filters.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import (
"time"

"github.com/containers/podman/v3/pkg/inspect"
"github.com/containers/podman/v3/pkg/util"
"github.com/pkg/errors"
"github.com/sirupsen/logrus"
)
Expand Down Expand Up @@ -78,23 +79,14 @@ func ReadOnlyFilter(readOnly bool) ResultFilter {
}

// LabelFilter allows you to filter by images labels key and/or value
func LabelFilter(ctx context.Context, labelfilter string) ResultFilter {
func LabelFilter(ctx context.Context, filter string) ResultFilter {
// We need to handle both label=key and label=key=value
return func(i *Image) bool {
var value string
splitFilter := strings.SplitN(labelfilter, "=", 2)
key := splitFilter[0]
if len(splitFilter) > 1 {
value = splitFilter[1]
}
labels, err := i.Labels(ctx)
if err != nil {
return false
}
if len(strings.TrimSpace(labels[key])) > 0 && len(strings.TrimSpace(value)) == 0 {
return true
}
return labels[key] == value
return util.MatchLabelFilters([]string{filter}, labels)
}
}

Expand Down
24 changes: 3 additions & 21 deletions libpod/image/prune.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,10 @@ package image
import (
"context"
"strings"
"time"

"github.com/containers/podman/v3/libpod/events"
"github.com/containers/podman/v3/pkg/domain/entities/reports"
"github.com/containers/podman/v3/pkg/timetype"
"github.com/containers/podman/v3/pkg/util"
"github.com/containers/storage"
"github.com/pkg/errors"
"github.com/sirupsen/logrus"
Expand All @@ -16,36 +15,19 @@ import (
func generatePruneFilterFuncs(filter, filterValue string) (ImageFilter, error) {
switch filter {
case "label":
var filterArray = strings.SplitN(filterValue, "=", 2)
var filterKey = filterArray[0]
if len(filterArray) > 1 {
filterValue = filterArray[1]
} else {
filterValue = ""
}
return func(i *Image) bool {
labels, err := i.Labels(context.Background())
if err != nil {
return false
}
for labelKey, labelValue := range labels {
if labelKey == filterKey && (filterValue == "" || labelValue == filterValue) {
return true
}
}
return false
return util.MatchLabelFilters([]string{filterValue}, labels)
}, nil

case "until":
ts, err := timetype.GetTimestamp(filterValue, time.Now())
if err != nil {
return nil, err
}
seconds, nanoseconds, err := timetype.ParseTimestamps(ts, 0)
until, err := util.ComputeUntilTimestamp([]string{filterValue})
if err != nil {
return nil, err
}
until := time.Unix(seconds, nanoseconds)
return func(i *Image) bool {
if !until.IsZero() && i.Created().After((until)) {
return true
Expand Down
29 changes: 3 additions & 26 deletions libpod/network/netconflist.go
Original file line number Diff line number Diff line change
Expand Up @@ -225,7 +225,7 @@ func IfPassesFilter(netconf *libcni.NetworkConfigList, filters map[string][]stri

case "label":
// matches all labels
result = matchPruneLabelFilters(netconf, filterValues)
result = util.MatchLabelFilters(filterValues, GetNetworkLabels(netconf))

case "driver":
// matches only for the DefaultNetworkDriver
Expand Down Expand Up @@ -260,9 +260,9 @@ func IfPassesPruneFilter(config *config.Config, netconf *libcni.NetworkConfigLis
for key, filterValues := range f {
switch strings.ToLower(key) {
case "label":
return matchPruneLabelFilters(netconf, filterValues), nil
return util.MatchLabelFilters(filterValues, GetNetworkLabels(netconf)), nil
case "until":
until, err := util.ComputeUntilTimestamp(key, filterValues)
until, err := util.ComputeUntilTimestamp(filterValues)
if err != nil {
return false, err
}
Expand All @@ -280,29 +280,6 @@ func IfPassesPruneFilter(config *config.Config, netconf *libcni.NetworkConfigLis
return false, nil
}

func matchPruneLabelFilters(netconf *libcni.NetworkConfigList, filterValues []string) bool {
labels := GetNetworkLabels(netconf)
result := true
outer:
for _, filterValue := range filterValues {
filterArray := strings.SplitN(filterValue, "=", 2)
filterKey := filterArray[0]
if len(filterArray) > 1 {
filterValue = filterArray[1]
} else {
filterValue = ""
}
for labelKey, labelValue := range labels {
if labelKey == filterKey && (filterValue == "" || labelValue == filterValue) {
result = true
continue outer
}
}
result = false
}
return result
}

func getCreatedTimestamp(config *config.Config, netconf *libcni.NetworkConfigList) (*time.Time, error) {
networkConfigPath, err := GetCNIConfigPathByNameOrID(config, netconf.Name)
if err != nil {
Expand Down
24 changes: 2 additions & 22 deletions pkg/domain/filters/containers.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,27 +23,7 @@ func GenerateContainerFilterFuncs(filter string, filterValues []string, r *libpo
case "label":
// we have to match that all given labels exits on that container
return func(c *libpod.Container) bool {
labels := c.Labels()
for _, filterValue := range filterValues {
matched := false
filterArray := strings.SplitN(filterValue, "=", 2)
filterKey := filterArray[0]
if len(filterArray) > 1 {
filterValue = filterArray[1]
} else {
filterValue = ""
}
for labelKey, labelValue := range labels {
if labelKey == filterKey && (filterValue == "" || labelValue == filterValue) {
matched = true
break
}
}
if !matched {
return false
}
}
return true
return util.MatchLabelFilters(filterValues, c.Labels())
}, nil
case "name":
// we only have to match one name
Expand Down Expand Up @@ -185,7 +165,7 @@ func GenerateContainerFilterFuncs(filter string, filterValues []string, r *libpo
return false
}, nil
case "until":
until, err := util.ComputeUntilTimestamp(filter, filterValues)
until, err := util.ComputeUntilTimestamp(filterValues)
if err != nil {
return nil, err
}
Expand Down
21 changes: 1 addition & 20 deletions pkg/domain/filters/pods.go
Original file line number Diff line number Diff line change
Expand Up @@ -114,26 +114,7 @@ func GeneratePodFilterFunc(filter string, filterValues []string) (
case "label":
return func(p *libpod.Pod) bool {
labels := p.Labels()
for _, filterValue := range filterValues {
matched := false
filterArray := strings.SplitN(filterValue, "=", 2)
filterKey := filterArray[0]
if len(filterArray) > 1 {
filterValue = filterArray[1]
} else {
filterValue = ""
}
for labelKey, labelValue := range labels {
if labelKey == filterKey && (filterValue == "" || labelValue == filterValue) {
matched = true
break
}
}
if !matched {
return false
}
}
return true
return util.MatchLabelFilters(filterValues, labels)
}, nil
case "network":
return func(p *libpod.Pod) bool {
Expand Down
17 changes: 3 additions & 14 deletions pkg/domain/filters/volumes.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import (
"strings"

"github.com/containers/podman/v3/libpod"
"github.com/containers/podman/v3/pkg/util"
"github.com/pkg/errors"
)

Expand All @@ -29,21 +30,9 @@ func GenerateVolumeFilters(filters url.Values) ([]libpod.VolumeFilter, error) {
return v.Scope() == scopeVal
})
case "label":
filterArray := strings.SplitN(val, "=", 2)
filterKey := filterArray[0]
var filterVal string
if len(filterArray) > 1 {
filterVal = filterArray[1]
} else {
filterVal = ""
}
filter := val
vf = append(vf, func(v *libpod.Volume) bool {
for labelKey, labelValue := range v.Labels() {
if labelKey == filterKey && (filterVal == "" || labelValue == filterVal) {
return true
}
}
return false
return util.MatchLabelFilters([]string{filter}, v.Labels())
})
case "opt":
filterArray := strings.SplitN(val, "=", 2)
Expand Down
27 changes: 24 additions & 3 deletions pkg/util/filters.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,11 +11,11 @@ import (
"github.com/pkg/errors"
)

// ComputeUntilTimestamp extracts unitil timestamp from filters
func ComputeUntilTimestamp(filter string, filterValues []string) (time.Time, error) {
// ComputeUntilTimestamp extracts until timestamp from filters
func ComputeUntilTimestamp(filterValues []string) (time.Time, error) {
invalid := time.Time{}
if len(filterValues) != 1 {
return invalid, errors.Errorf("specify exactly one timestamp for %s", filter)
return invalid, errors.Errorf("specify exactly one timestamp for until")
}
ts, err := timetype.GetTimestamp(filterValues[0], time.Now())
if err != nil {
Expand Down Expand Up @@ -93,3 +93,24 @@ func PrepareFilters(r *http.Request) (*map[string][]string, error) {
}
return &filterMap, nil
}

// MatchLabelFilters matches labels and returs true if they are valid
func MatchLabelFilters(filterValues []string, labels map[string]string) bool {
outer:
for _, filterValue := range filterValues {
filterArray := strings.SplitN(filterValue, "=", 2)
filterKey := filterArray[0]
if len(filterArray) > 1 {
filterValue = filterArray[1]
} else {
filterValue = ""
}
for labelKey, labelValue := range labels {
if labelKey == filterKey && (filterValue == "" || labelValue == filterValue) {
continue outer
}
}
return false
}
return true
}
113 changes: 113 additions & 0 deletions pkg/util/filters_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@
package util

import (
"testing"
)

func TestMatchLabelFilters(t *testing.T) {
testLabels := map[string]string{
"label1": "",
"label2": "test",
"label3": "",
}
type args struct {
filterValues []string
labels map[string]string
}
tests := []struct {
name string
args args
want bool
}{
{
name: "Match when all filters the same as labels",
args: args{
filterValues: []string{"label1", "label3", "label2=test"},
labels: testLabels,
},
want: true,
},
{
name: "Match when filter value not provided in args",
args: args{
filterValues: []string{"label2"},
labels: testLabels,
},
want: true,
},
{
name: "Match when no filter value is given",
args: args{
filterValues: []string{"label2="},
labels: testLabels,
},
want: true,
},
{
name: "Do not match when filter value differs",
args: args{
filterValues: []string{"label2=differs"},
labels: testLabels,
},
want: false,
},
{
name: "Do not match when filter value not listed in labels",
args: args{
filterValues: []string{"label1=xyz"},
labels: testLabels,
},
want: false,
},
{
name: "Do not match when one from many not ok",
args: args{
filterValues: []string{"label1=xyz", "invalid=valid"},
labels: testLabels,
},
want: false,
},
}
for _, tt := range tests {
tt := tt
t.Run(tt.name, func(t *testing.T) {
if got := MatchLabelFilters(tt.args.filterValues, tt.args.labels); got != tt.want {
t.Errorf("MatchLabelFilters() = %v, want %v", got, tt.want)
}
})
}
}

func TestComputeUntilTimestamp(t *testing.T) {
tests := []struct {
name string
args []string
wantErr bool
}{
{
name: "Return error when more values in list",
args: []string{"5h", "6s"},
wantErr: true,
},
{
name: "Return error when invalid time",
args: []string{"invalidTime"},
wantErr: true,
},
{
name: "Do not return error when correct time format supplied",
args: []string{"44m"},
wantErr: false,
},
}
for _, tt := range tests {
tt := tt
t.Run(tt.name, func(t *testing.T) {
_, err := ComputeUntilTimestamp(tt.args)
if (err != nil) != tt.wantErr {
t.Errorf("ComputeUntilTimestamp() error = %v, wantErr %v", err, tt.wantErr)
return
}
})
}
}

0 comments on commit 0cb3066

Please sign in to comment.