mirror of https://github.com/containers/podman.git
Unification of label filter across list/prune endpoints
Signed-off-by: Jakub Guzik <jakubmguzik@gmail.com>
This commit is contained in:
parent
4d3e71ad28
commit
5eab1b0742
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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":
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue