2015-07-25 08:35:07 +00:00
|
|
|
// Package filters provides helper function to parse and handle command line
|
|
|
|
// filter, used for example in docker ps or docker images commands.
|
2014-07-29 00:23:38 +00:00
|
|
|
package filters
|
|
|
|
|
|
|
|
import (
|
|
|
|
"encoding/json"
|
|
|
|
"errors"
|
2014-10-13 06:12:44 +00:00
|
|
|
"regexp"
|
2014-07-29 00:23:38 +00:00
|
|
|
"strings"
|
|
|
|
)
|
|
|
|
|
2015-07-25 08:35:07 +00:00
|
|
|
// Args stores filter arguments as map key:{array of values}.
|
|
|
|
// It contains a aggregation of the list of arguments (which are in the form
|
|
|
|
// of -f 'key=value') based on the key, and store values for the same key
|
|
|
|
// in an slice.
|
|
|
|
// e.g given -f 'label=label1=1' -f 'label=label2=2' -f 'image.name=ubuntu'
|
|
|
|
// the args will be {'label': {'label1=1','label2=2'}, 'image.name', {'ubuntu'}}
|
2014-07-29 00:23:38 +00:00
|
|
|
type Args map[string][]string
|
|
|
|
|
2015-07-25 08:35:07 +00:00
|
|
|
// ParseFlag parses the argument to the filter flag. Like
|
2014-07-29 00:23:38 +00:00
|
|
|
//
|
|
|
|
// `docker ps -f 'created=today' -f 'image.name=ubuntu*'`
|
|
|
|
//
|
|
|
|
// If prev map is provided, then it is appended to, and returned. By default a new
|
|
|
|
// map is created.
|
|
|
|
func ParseFlag(arg string, prev Args) (Args, error) {
|
2015-07-25 08:35:07 +00:00
|
|
|
filters := prev
|
2014-07-29 00:23:38 +00:00
|
|
|
if prev == nil {
|
|
|
|
filters = Args{}
|
|
|
|
}
|
|
|
|
if len(arg) == 0 {
|
|
|
|
return filters, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
if !strings.Contains(arg, "=") {
|
2015-07-25 08:35:07 +00:00
|
|
|
return filters, ErrBadFormat
|
2014-07-29 00:23:38 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
f := strings.SplitN(arg, "=", 2)
|
2014-11-03 11:11:29 +00:00
|
|
|
name := strings.ToLower(strings.TrimSpace(f[0]))
|
|
|
|
value := strings.TrimSpace(f[1])
|
|
|
|
filters[name] = append(filters[name], value)
|
2014-07-29 00:23:38 +00:00
|
|
|
|
|
|
|
return filters, nil
|
|
|
|
}
|
|
|
|
|
2015-07-25 08:35:07 +00:00
|
|
|
// ErrBadFormat is an error returned in case of bad format for a filter.
|
|
|
|
var ErrBadFormat = errors.New("bad format of filter (expected name=value)")
|
2014-07-29 00:23:38 +00:00
|
|
|
|
2015-07-25 08:35:07 +00:00
|
|
|
// ToParam packs the Args into an string for easy transport from client to server.
|
2014-07-29 00:23:38 +00:00
|
|
|
func ToParam(a Args) (string, error) {
|
|
|
|
// this way we don't URL encode {}, just empty space
|
|
|
|
if len(a) == 0 {
|
|
|
|
return "", nil
|
|
|
|
}
|
|
|
|
|
|
|
|
buf, err := json.Marshal(a)
|
|
|
|
if err != nil {
|
|
|
|
return "", err
|
|
|
|
}
|
|
|
|
return string(buf), nil
|
|
|
|
}
|
|
|
|
|
2015-07-25 08:35:07 +00:00
|
|
|
// FromParam unpacks the filter Args.
|
2014-07-29 00:23:38 +00:00
|
|
|
func FromParam(p string) (Args, error) {
|
|
|
|
args := Args{}
|
|
|
|
if len(p) == 0 {
|
|
|
|
return args, nil
|
|
|
|
}
|
2015-04-21 22:47:51 +00:00
|
|
|
if err := json.NewDecoder(strings.NewReader(p)).Decode(&args); err != nil {
|
2014-07-29 00:23:38 +00:00
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
return args, nil
|
|
|
|
}
|
2014-10-13 06:12:44 +00:00
|
|
|
|
2015-07-25 08:35:07 +00:00
|
|
|
// MatchKVList returns true if the values for the specified field maches the ones
|
|
|
|
// from the sources.
|
|
|
|
// e.g. given Args are {'label': {'label1=1','label2=1'}, 'image.name', {'ubuntu'}},
|
|
|
|
// field is 'label' and sources are {'label':{'label1=1','label2=2','label3=3'}}
|
|
|
|
// it returns true.
|
2015-01-07 00:04:10 +00:00
|
|
|
func (filters Args) MatchKVList(field string, sources map[string]string) bool {
|
|
|
|
fieldValues := filters[field]
|
|
|
|
|
|
|
|
//do not filter if there is no filter set or cannot determine filter
|
|
|
|
if len(fieldValues) == 0 {
|
|
|
|
return true
|
|
|
|
}
|
|
|
|
|
|
|
|
if sources == nil || len(sources) == 0 {
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
|
|
|
|
outer:
|
|
|
|
for _, name2match := range fieldValues {
|
|
|
|
testKV := strings.SplitN(name2match, "=", 2)
|
|
|
|
|
|
|
|
for k, v := range sources {
|
|
|
|
if len(testKV) == 1 {
|
|
|
|
if k == testKV[0] {
|
|
|
|
continue outer
|
|
|
|
}
|
|
|
|
} else if k == testKV[0] && v == testKV[1] {
|
|
|
|
continue outer
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
|
|
|
|
return true
|
|
|
|
}
|
|
|
|
|
2015-07-25 08:35:07 +00:00
|
|
|
// Match returns true if the values for the specified field matches the source string
|
|
|
|
// e.g. given Args are {'label': {'label1=1','label2=1'}, 'image.name', {'ubuntu'}},
|
|
|
|
// field is 'image.name' and source is 'ubuntu'
|
|
|
|
// it returns true.
|
2014-10-13 06:12:44 +00:00
|
|
|
func (filters Args) Match(field, source string) bool {
|
|
|
|
fieldValues := filters[field]
|
|
|
|
|
|
|
|
//do not filter if there is no filter set or cannot determine filter
|
|
|
|
if len(fieldValues) == 0 {
|
|
|
|
return true
|
|
|
|
}
|
|
|
|
for _, name2match := range fieldValues {
|
|
|
|
match, err := regexp.MatchString(name2match, source)
|
|
|
|
if err != nil {
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
if match {
|
|
|
|
return true
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return false
|
|
|
|
}
|