Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Unification of label and until filters across list/prune endpoints #9785

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
}
jmguzik marked this conversation as resolved.
Show resolved Hide resolved
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
}
})
}
}