Unification of label filter across list/prune endpoints

Signed-off-by: Jakub Guzik <jakubmguzik@gmail.com>
This commit is contained in:
Jakub Guzik 2021-03-23 00:13:44 +01:00
parent 4d3e71ad28
commit 5eab1b0742
8 changed files with 111 additions and 105 deletions

View File

@ -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"
)
@ -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)
}
}

View File

@ -8,6 +8,7 @@ import (
"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"
@ -16,24 +17,12 @@ 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":

View File

@ -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
@ -260,7 +260,7 @@ 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)
if err != nil {
@ -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 {

View File

@ -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

View File

@ -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 {

View File

@ -5,6 +5,7 @@ import (
"strings"
"github.com/containers/podman/v3/libpod"
"github.com/containers/podman/v3/pkg/util"
"github.com/pkg/errors"
)
@ -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)

View File

@ -11,7 +11,7 @@ import (
"github.com/pkg/errors"
)
// ComputeUntilTimestamp extracts unitil timestamp from filters
// ComputeUntilTimestamp extracts until timestamp from filters
func ComputeUntilTimestamp(filter string, filterValues []string) (time.Time, error) {
invalid := time.Time{}
if len(filterValues) != 1 {
@ -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
}

77
pkg/util/filters_test.go Normal file
View File

@ -0,0 +1,77 @@
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)
}
})
}
}