Browse code

Move filters package to the API.

These filters are only use to interchange data between clients and daemons.
They don't belong to the parsers package.

Signed-off-by: David Calavera <david.calavera@gmail.com>

David Calavera authored on 2015/12/16 06:40:11
Showing 23 changed files
... ...
@@ -9,8 +9,8 @@ import (
9 9
 
10 10
 	"github.com/docker/docker/api/client/lib"
11 11
 	"github.com/docker/docker/api/types"
12
+	"github.com/docker/docker/api/types/filters"
12 13
 	"github.com/docker/docker/api/types/registry"
13
-	"github.com/docker/docker/pkg/parsers/filters"
14 14
 	"github.com/docker/docker/runconfig"
15 15
 )
16 16
 
... ...
@@ -2,11 +2,11 @@ package client
2 2
 
3 3
 import (
4 4
 	"github.com/docker/docker/api/types"
5
+	"github.com/docker/docker/api/types/filters"
5 6
 	Cli "github.com/docker/docker/cli"
6 7
 	"github.com/docker/docker/opts"
7 8
 	"github.com/docker/docker/pkg/jsonmessage"
8 9
 	flag "github.com/docker/docker/pkg/mflag"
9
-	"github.com/docker/docker/pkg/parsers/filters"
10 10
 )
11 11
 
12 12
 // CmdEvents prints a live stream of real time events from the server.
... ...
@@ -8,10 +8,10 @@ import (
8 8
 
9 9
 	"github.com/docker/distribution/reference"
10 10
 	"github.com/docker/docker/api/types"
11
+	"github.com/docker/docker/api/types/filters"
11 12
 	Cli "github.com/docker/docker/cli"
12 13
 	"github.com/docker/docker/opts"
13 14
 	flag "github.com/docker/docker/pkg/mflag"
14
-	"github.com/docker/docker/pkg/parsers/filters"
15 15
 	"github.com/docker/docker/pkg/stringid"
16 16
 	"github.com/docker/docker/pkg/units"
17 17
 )
... ...
@@ -6,7 +6,7 @@ import (
6 6
 	"strconv"
7 7
 
8 8
 	"github.com/docker/docker/api/types"
9
-	"github.com/docker/docker/pkg/parsers/filters"
9
+	"github.com/docker/docker/api/types/filters"
10 10
 )
11 11
 
12 12
 // ContainerList returns the list of containers in the docker host.
... ...
@@ -6,8 +6,8 @@ import (
6 6
 	"time"
7 7
 
8 8
 	"github.com/docker/docker/api/types"
9
+	"github.com/docker/docker/api/types/filters"
9 10
 	timetypes "github.com/docker/docker/api/types/time"
10
-	"github.com/docker/docker/pkg/parsers/filters"
11 11
 )
12 12
 
13 13
 // Events returns a stream of events in the daemon in a ReadCloser.
... ...
@@ -5,7 +5,7 @@ import (
5 5
 	"net/url"
6 6
 
7 7
 	"github.com/docker/docker/api/types"
8
-	"github.com/docker/docker/pkg/parsers/filters"
8
+	"github.com/docker/docker/api/types/filters"
9 9
 )
10 10
 
11 11
 // ImageList returns a list of images in the docker host.
... ...
@@ -6,7 +6,7 @@ import (
6 6
 	"net/url"
7 7
 
8 8
 	"github.com/docker/docker/api/types"
9
-	"github.com/docker/docker/pkg/parsers/filters"
9
+	"github.com/docker/docker/api/types/filters"
10 10
 )
11 11
 
12 12
 // VolumeList returns the volumes configured in the docker host.
... ...
@@ -3,10 +3,10 @@ package client
3 3
 import (
4 4
 	"github.com/docker/docker/api/client/ps"
5 5
 	"github.com/docker/docker/api/types"
6
+	"github.com/docker/docker/api/types/filters"
6 7
 	Cli "github.com/docker/docker/cli"
7 8
 	"github.com/docker/docker/opts"
8 9
 	flag "github.com/docker/docker/pkg/mflag"
9
-	"github.com/docker/docker/pkg/parsers/filters"
10 10
 )
11 11
 
12 12
 // CmdPs outputs a list of Docker containers.
... ...
@@ -5,10 +5,10 @@ import (
5 5
 	"text/tabwriter"
6 6
 
7 7
 	"github.com/docker/docker/api/types"
8
+	"github.com/docker/docker/api/types/filters"
8 9
 	Cli "github.com/docker/docker/cli"
9 10
 	"github.com/docker/docker/opts"
10 11
 	flag "github.com/docker/docker/pkg/mflag"
11
-	"github.com/docker/docker/pkg/parsers/filters"
12 12
 )
13 13
 
14 14
 // CmdVolume is the parent subcommand for all volume commands
... ...
@@ -5,8 +5,9 @@ import (
5 5
 	"path/filepath"
6 6
 	"testing"
7 7
 
8
-	"github.com/docker/docker/api/types"
9 8
 	"os"
9
+
10
+	"github.com/docker/docker/api/types"
10 11
 )
11 12
 
12 13
 type ports struct {
... ...
@@ -10,9 +10,9 @@ import (
10 10
 	"github.com/Sirupsen/logrus"
11 11
 	"github.com/docker/docker/api/server/httputils"
12 12
 	"github.com/docker/docker/api/types"
13
+	"github.com/docker/docker/api/types/filters"
13 14
 	"github.com/docker/docker/api/types/network"
14 15
 	"github.com/docker/docker/daemon"
15
-	"github.com/docker/docker/pkg/parsers/filters"
16 16
 	"github.com/docker/docker/runconfig"
17 17
 	"github.com/docker/libnetwork"
18 18
 )
... ...
@@ -2,8 +2,8 @@ package system
2 2
 
3 3
 import (
4 4
 	"github.com/docker/docker/api/types"
5
+	"github.com/docker/docker/api/types/filters"
5 6
 	"github.com/docker/docker/pkg/jsonmessage"
6
-	"github.com/docker/docker/pkg/parsers/filters"
7 7
 )
8 8
 
9 9
 // Backend is the methods that need to be implemented to provide
... ...
@@ -9,10 +9,10 @@ import (
9 9
 	"github.com/docker/docker/api"
10 10
 	"github.com/docker/docker/api/server/httputils"
11 11
 	"github.com/docker/docker/api/types"
12
+	"github.com/docker/docker/api/types/filters"
12 13
 	timetypes "github.com/docker/docker/api/types/time"
13 14
 	"github.com/docker/docker/pkg/ioutils"
14 15
 	"github.com/docker/docker/pkg/jsonmessage"
15
-	"github.com/docker/docker/pkg/parsers/filters"
16 16
 	"golang.org/x/net/context"
17 17
 )
18 18
 
... ...
@@ -5,7 +5,7 @@ import (
5 5
 	"io"
6 6
 	"net"
7 7
 
8
-	"github.com/docker/docker/pkg/parsers/filters"
8
+	"github.com/docker/docker/api/types/filters"
9 9
 	"github.com/docker/docker/pkg/ulimit"
10 10
 	"github.com/docker/docker/runconfig"
11 11
 )
12 12
new file mode 100644
... ...
@@ -0,0 +1,241 @@
0
+// Package filters provides helper function to parse and handle command line
1
+// filter, used for example in docker ps or docker images commands.
2
+package filters
3
+
4
+import (
5
+	"encoding/json"
6
+	"errors"
7
+	"fmt"
8
+	"regexp"
9
+	"strings"
10
+)
11
+
12
+// Args stores filter arguments as map key:{array of values}.
13
+// It contains a aggregation of the list of arguments (which are in the form
14
+// of -f 'key=value') based on the key, and store values for the same key
15
+// in an slice.
16
+// e.g given -f 'label=label1=1' -f 'label=label2=2' -f 'image.name=ubuntu'
17
+// the args will be {'label': {'label1=1','label2=2'}, 'image.name', {'ubuntu'}}
18
+type Args struct {
19
+	fields map[string]map[string]bool
20
+}
21
+
22
+// NewArgs initializes a new Args struct.
23
+func NewArgs() Args {
24
+	return Args{fields: map[string]map[string]bool{}}
25
+}
26
+
27
+// ParseFlag parses the argument to the filter flag. Like
28
+//
29
+//   `docker ps -f 'created=today' -f 'image.name=ubuntu*'`
30
+//
31
+// If prev map is provided, then it is appended to, and returned. By default a new
32
+// map is created.
33
+func ParseFlag(arg string, prev Args) (Args, error) {
34
+	filters := prev
35
+	if len(arg) == 0 {
36
+		return filters, nil
37
+	}
38
+
39
+	if !strings.Contains(arg, "=") {
40
+		return filters, ErrBadFormat
41
+	}
42
+
43
+	f := strings.SplitN(arg, "=", 2)
44
+
45
+	name := strings.ToLower(strings.TrimSpace(f[0]))
46
+	value := strings.TrimSpace(f[1])
47
+
48
+	filters.Add(name, value)
49
+
50
+	return filters, nil
51
+}
52
+
53
+// ErrBadFormat is an error returned in case of bad format for a filter.
54
+var ErrBadFormat = errors.New("bad format of filter (expected name=value)")
55
+
56
+// ToParam packs the Args into an string for easy transport from client to server.
57
+func ToParam(a Args) (string, error) {
58
+	// this way we don't URL encode {}, just empty space
59
+	if a.Len() == 0 {
60
+		return "", nil
61
+	}
62
+
63
+	buf, err := json.Marshal(a.fields)
64
+	if err != nil {
65
+		return "", err
66
+	}
67
+	return string(buf), nil
68
+}
69
+
70
+// FromParam unpacks the filter Args.
71
+func FromParam(p string) (Args, error) {
72
+	if len(p) == 0 {
73
+		return NewArgs(), nil
74
+	}
75
+
76
+	r := strings.NewReader(p)
77
+	d := json.NewDecoder(r)
78
+
79
+	m := map[string]map[string]bool{}
80
+	if err := d.Decode(&m); err != nil {
81
+		r.Seek(0, 0)
82
+
83
+		// Allow parsing old arguments in slice format.
84
+		// Because other libraries might be sending them in this format.
85
+		deprecated := map[string][]string{}
86
+		if deprecatedErr := d.Decode(&deprecated); deprecatedErr == nil {
87
+			m = deprecatedArgs(deprecated)
88
+		} else {
89
+			return NewArgs(), err
90
+		}
91
+	}
92
+	return Args{m}, nil
93
+}
94
+
95
+// Get returns the list of values associates with a field.
96
+// It returns a slice of strings to keep backwards compatibility with old code.
97
+func (filters Args) Get(field string) []string {
98
+	values := filters.fields[field]
99
+	if values == nil {
100
+		return make([]string, 0)
101
+	}
102
+	slice := make([]string, 0, len(values))
103
+	for key := range values {
104
+		slice = append(slice, key)
105
+	}
106
+	return slice
107
+}
108
+
109
+// Add adds a new value to a filter field.
110
+func (filters Args) Add(name, value string) {
111
+	if _, ok := filters.fields[name]; ok {
112
+		filters.fields[name][value] = true
113
+	} else {
114
+		filters.fields[name] = map[string]bool{value: true}
115
+	}
116
+}
117
+
118
+// Del removes a value from a filter field.
119
+func (filters Args) Del(name, value string) {
120
+	if _, ok := filters.fields[name]; ok {
121
+		delete(filters.fields[name], value)
122
+	}
123
+}
124
+
125
+// Len returns the number of fields in the arguments.
126
+func (filters Args) Len() int {
127
+	return len(filters.fields)
128
+}
129
+
130
+// MatchKVList returns true if the values for the specified field matches the ones
131
+// from the sources.
132
+// e.g. given Args are {'label': {'label1=1','label2=1'}, 'image.name', {'ubuntu'}},
133
+//      field is 'label' and sources are {'label1': '1', 'label2': '2'}
134
+//      it returns true.
135
+func (filters Args) MatchKVList(field string, sources map[string]string) bool {
136
+	fieldValues := filters.fields[field]
137
+
138
+	//do not filter if there is no filter set or cannot determine filter
139
+	if len(fieldValues) == 0 {
140
+		return true
141
+	}
142
+
143
+	if sources == nil || len(sources) == 0 {
144
+		return false
145
+	}
146
+
147
+	for name2match := range fieldValues {
148
+		testKV := strings.SplitN(name2match, "=", 2)
149
+
150
+		v, ok := sources[testKV[0]]
151
+		if !ok {
152
+			return false
153
+		}
154
+		if len(testKV) == 2 && testKV[1] != v {
155
+			return false
156
+		}
157
+	}
158
+
159
+	return true
160
+}
161
+
162
+// Match returns true if the values for the specified field matches the source string
163
+// e.g. given Args are {'label': {'label1=1','label2=1'}, 'image.name', {'ubuntu'}},
164
+//      field is 'image.name' and source is 'ubuntu'
165
+//      it returns true.
166
+func (filters Args) Match(field, source string) bool {
167
+	if filters.ExactMatch(field, source) {
168
+		return true
169
+	}
170
+
171
+	fieldValues := filters.fields[field]
172
+	for name2match := range fieldValues {
173
+		match, err := regexp.MatchString(name2match, source)
174
+		if err != nil {
175
+			continue
176
+		}
177
+		if match {
178
+			return true
179
+		}
180
+	}
181
+	return false
182
+}
183
+
184
+// ExactMatch returns true if the source matches exactly one of the filters.
185
+func (filters Args) ExactMatch(field, source string) bool {
186
+	fieldValues, ok := filters.fields[field]
187
+	//do not filter if there is no filter set or cannot determine filter
188
+	if !ok || len(fieldValues) == 0 {
189
+		return true
190
+	}
191
+
192
+	// try to march full name value to avoid O(N) regular expression matching
193
+	if fieldValues[source] {
194
+		return true
195
+	}
196
+	return false
197
+}
198
+
199
+// Include returns true if the name of the field to filter is in the filters.
200
+func (filters Args) Include(field string) bool {
201
+	_, ok := filters.fields[field]
202
+	return ok
203
+}
204
+
205
+// Validate ensures that all the fields in the filter are valid.
206
+// It returns an error as soon as it finds an invalid field.
207
+func (filters Args) Validate(accepted map[string]bool) error {
208
+	for name := range filters.fields {
209
+		if !accepted[name] {
210
+			return fmt.Errorf("Invalid filter '%s'", name)
211
+		}
212
+	}
213
+	return nil
214
+}
215
+
216
+// WalkValues iterates over the list of filtered values for a field.
217
+// It stops the iteration if it finds an error and it returns that error.
218
+func (filters Args) WalkValues(field string, op func(value string) error) error {
219
+	if _, ok := filters.fields[field]; !ok {
220
+		return nil
221
+	}
222
+	for v := range filters.fields[field] {
223
+		if err := op(v); err != nil {
224
+			return err
225
+		}
226
+	}
227
+	return nil
228
+}
229
+
230
+func deprecatedArgs(d map[string][]string) map[string]map[string]bool {
231
+	m := map[string]map[string]bool{}
232
+	for k, v := range d {
233
+		values := map[string]bool{}
234
+		for _, vv := range v {
235
+			values[vv] = true
236
+		}
237
+		m[k] = values
238
+	}
239
+	return m
240
+}
0 241
new file mode 100644
... ...
@@ -0,0 +1,351 @@
0
+package filters
1
+
2
+import (
3
+	"fmt"
4
+	"testing"
5
+)
6
+
7
+func TestParseArgs(t *testing.T) {
8
+	// equivalent of `docker ps -f 'created=today' -f 'image.name=ubuntu*' -f 'image.name=*untu'`
9
+	flagArgs := []string{
10
+		"created=today",
11
+		"image.name=ubuntu*",
12
+		"image.name=*untu",
13
+	}
14
+	var (
15
+		args = NewArgs()
16
+		err  error
17
+	)
18
+	for i := range flagArgs {
19
+		args, err = ParseFlag(flagArgs[i], args)
20
+		if err != nil {
21
+			t.Errorf("failed to parse %s: %s", flagArgs[i], err)
22
+		}
23
+	}
24
+	if len(args.Get("created")) != 1 {
25
+		t.Errorf("failed to set this arg")
26
+	}
27
+	if len(args.Get("image.name")) != 2 {
28
+		t.Errorf("the args should have collapsed")
29
+	}
30
+}
31
+
32
+func TestParseArgsEdgeCase(t *testing.T) {
33
+	var filters Args
34
+	args, err := ParseFlag("", filters)
35
+	if err != nil {
36
+		t.Fatal(err)
37
+	}
38
+	if args.Len() != 0 {
39
+		t.Fatalf("Expected an empty Args (map), got %v", args)
40
+	}
41
+	if args, err = ParseFlag("anything", args); err == nil || err != ErrBadFormat {
42
+		t.Fatalf("Expected ErrBadFormat, got %v", err)
43
+	}
44
+}
45
+
46
+func TestToParam(t *testing.T) {
47
+	fields := map[string]map[string]bool{
48
+		"created":    {"today": true},
49
+		"image.name": {"ubuntu*": true, "*untu": true},
50
+	}
51
+	a := Args{fields: fields}
52
+
53
+	_, err := ToParam(a)
54
+	if err != nil {
55
+		t.Errorf("failed to marshal the filters: %s", err)
56
+	}
57
+}
58
+
59
+func TestFromParam(t *testing.T) {
60
+	invalids := []string{
61
+		"anything",
62
+		"['a','list']",
63
+		"{'key': 'value'}",
64
+		`{"key": "value"}`,
65
+	}
66
+	valid := map[*Args][]string{
67
+		&Args{fields: map[string]map[string]bool{"key": {"value": true}}}: {
68
+			`{"key": ["value"]}`,
69
+			`{"key": {"value": true}}`,
70
+		},
71
+		&Args{fields: map[string]map[string]bool{"key": {"value1": true, "value2": true}}}: {
72
+			`{"key": ["value1", "value2"]}`,
73
+			`{"key": {"value1": true, "value2": true}}`,
74
+		},
75
+		&Args{fields: map[string]map[string]bool{"key1": {"value1": true}, "key2": {"value2": true}}}: {
76
+			`{"key1": ["value1"], "key2": ["value2"]}`,
77
+			`{"key1": {"value1": true}, "key2": {"value2": true}}`,
78
+		},
79
+	}
80
+
81
+	for _, invalid := range invalids {
82
+		if _, err := FromParam(invalid); err == nil {
83
+			t.Fatalf("Expected an error with %v, got nothing", invalid)
84
+		}
85
+	}
86
+
87
+	for expectedArgs, matchers := range valid {
88
+		for _, json := range matchers {
89
+			args, err := FromParam(json)
90
+			if err != nil {
91
+				t.Fatal(err)
92
+			}
93
+			if args.Len() != expectedArgs.Len() {
94
+				t.Fatalf("Expected %v, go %v", expectedArgs, args)
95
+			}
96
+			for key, expectedValues := range expectedArgs.fields {
97
+				values := args.Get(key)
98
+
99
+				if len(values) != len(expectedValues) {
100
+					t.Fatalf("Expected %v, go %v", expectedArgs, args)
101
+				}
102
+
103
+				for _, v := range values {
104
+					if !expectedValues[v] {
105
+						t.Fatalf("Expected %v, go %v", expectedArgs, args)
106
+					}
107
+				}
108
+			}
109
+		}
110
+	}
111
+}
112
+
113
+func TestEmpty(t *testing.T) {
114
+	a := Args{}
115
+	v, err := ToParam(a)
116
+	if err != nil {
117
+		t.Errorf("failed to marshal the filters: %s", err)
118
+	}
119
+	v1, err := FromParam(v)
120
+	if err != nil {
121
+		t.Errorf("%s", err)
122
+	}
123
+	if a.Len() != v1.Len() {
124
+		t.Errorf("these should both be empty sets")
125
+	}
126
+}
127
+
128
+func TestArgsMatchKVListEmptySources(t *testing.T) {
129
+	args := NewArgs()
130
+	if !args.MatchKVList("created", map[string]string{}) {
131
+		t.Fatalf("Expected true for (%v,created), got true", args)
132
+	}
133
+
134
+	args = Args{map[string]map[string]bool{"created": {"today": true}}}
135
+	if args.MatchKVList("created", map[string]string{}) {
136
+		t.Fatalf("Expected false for (%v,created), got true", args)
137
+	}
138
+}
139
+
140
+func TestArgsMatchKVList(t *testing.T) {
141
+	// Not empty sources
142
+	sources := map[string]string{
143
+		"key1": "value1",
144
+		"key2": "value2",
145
+		"key3": "value3",
146
+	}
147
+
148
+	matches := map[*Args]string{
149
+		&Args{}: "field",
150
+		&Args{map[string]map[string]bool{
151
+			"created": map[string]bool{"today": true},
152
+			"labels":  map[string]bool{"key1": true}},
153
+		}: "labels",
154
+		&Args{map[string]map[string]bool{
155
+			"created": map[string]bool{"today": true},
156
+			"labels":  map[string]bool{"key1=value1": true}},
157
+		}: "labels",
158
+	}
159
+
160
+	for args, field := range matches {
161
+		if args.MatchKVList(field, sources) != true {
162
+			t.Fatalf("Expected true for %v on %v, got false", sources, args)
163
+		}
164
+	}
165
+
166
+	differs := map[*Args]string{
167
+		&Args{map[string]map[string]bool{
168
+			"created": map[string]bool{"today": true}},
169
+		}: "created",
170
+		&Args{map[string]map[string]bool{
171
+			"created": map[string]bool{"today": true},
172
+			"labels":  map[string]bool{"key4": true}},
173
+		}: "labels",
174
+		&Args{map[string]map[string]bool{
175
+			"created": map[string]bool{"today": true},
176
+			"labels":  map[string]bool{"key1=value3": true}},
177
+		}: "labels",
178
+	}
179
+
180
+	for args, field := range differs {
181
+		if args.MatchKVList(field, sources) != false {
182
+			t.Fatalf("Expected false for %v on %v, got true", sources, args)
183
+		}
184
+	}
185
+}
186
+
187
+func TestArgsMatch(t *testing.T) {
188
+	source := "today"
189
+
190
+	matches := map[*Args]string{
191
+		&Args{}: "field",
192
+		&Args{map[string]map[string]bool{
193
+			"created": map[string]bool{"today": true}},
194
+		}: "today",
195
+		&Args{map[string]map[string]bool{
196
+			"created": map[string]bool{"to*": true}},
197
+		}: "created",
198
+		&Args{map[string]map[string]bool{
199
+			"created": map[string]bool{"to(.*)": true}},
200
+		}: "created",
201
+		&Args{map[string]map[string]bool{
202
+			"created": map[string]bool{"tod": true}},
203
+		}: "created",
204
+		&Args{map[string]map[string]bool{
205
+			"created": map[string]bool{"anyting": true, "to*": true}},
206
+		}: "created",
207
+	}
208
+
209
+	for args, field := range matches {
210
+		if args.Match(field, source) != true {
211
+			t.Fatalf("Expected true for %v on %v, got false", source, args)
212
+		}
213
+	}
214
+
215
+	differs := map[*Args]string{
216
+		&Args{map[string]map[string]bool{
217
+			"created": map[string]bool{"tomorrow": true}},
218
+		}: "created",
219
+		&Args{map[string]map[string]bool{
220
+			"created": map[string]bool{"to(day": true}},
221
+		}: "created",
222
+		&Args{map[string]map[string]bool{
223
+			"created": map[string]bool{"tom(.*)": true}},
224
+		}: "created",
225
+		&Args{map[string]map[string]bool{
226
+			"created": map[string]bool{"tom": true}},
227
+		}: "created",
228
+		&Args{map[string]map[string]bool{
229
+			"created": map[string]bool{"today1": true},
230
+			"labels":  map[string]bool{"today": true}},
231
+		}: "created",
232
+	}
233
+
234
+	for args, field := range differs {
235
+		if args.Match(field, source) != false {
236
+			t.Fatalf("Expected false for %v on %v, got true", source, args)
237
+		}
238
+	}
239
+}
240
+
241
+func TestAdd(t *testing.T) {
242
+	f := NewArgs()
243
+	f.Add("status", "running")
244
+	v := f.fields["status"]
245
+	if len(v) != 1 || !v["running"] {
246
+		t.Fatalf("Expected to include a running status, got %v", v)
247
+	}
248
+
249
+	f.Add("status", "paused")
250
+	if len(v) != 2 || !v["paused"] {
251
+		t.Fatalf("Expected to include a paused status, got %v", v)
252
+	}
253
+}
254
+
255
+func TestDel(t *testing.T) {
256
+	f := NewArgs()
257
+	f.Add("status", "running")
258
+	f.Del("status", "running")
259
+	v := f.fields["status"]
260
+	if v["running"] {
261
+		t.Fatalf("Expected to not include a running status filter, got true")
262
+	}
263
+}
264
+
265
+func TestLen(t *testing.T) {
266
+	f := NewArgs()
267
+	if f.Len() != 0 {
268
+		t.Fatalf("Expected to not include any field")
269
+	}
270
+	f.Add("status", "running")
271
+	if f.Len() != 1 {
272
+		t.Fatalf("Expected to include one field")
273
+	}
274
+}
275
+
276
+func TestExactMatch(t *testing.T) {
277
+	f := NewArgs()
278
+
279
+	if !f.ExactMatch("status", "running") {
280
+		t.Fatalf("Expected to match `running` when there are no filters, got false")
281
+	}
282
+
283
+	f.Add("status", "running")
284
+	f.Add("status", "pause*")
285
+
286
+	if !f.ExactMatch("status", "running") {
287
+		t.Fatalf("Expected to match `running` with one of the filters, got false")
288
+	}
289
+
290
+	if f.ExactMatch("status", "paused") {
291
+		t.Fatalf("Expected to not match `paused` with one of the filters, got true")
292
+	}
293
+}
294
+
295
+func TestInclude(t *testing.T) {
296
+	f := NewArgs()
297
+	if f.Include("status") {
298
+		t.Fatalf("Expected to not include a status key, got true")
299
+	}
300
+	f.Add("status", "running")
301
+	if !f.Include("status") {
302
+		t.Fatalf("Expected to include a status key, got false")
303
+	}
304
+}
305
+
306
+func TestValidate(t *testing.T) {
307
+	f := NewArgs()
308
+	f.Add("status", "running")
309
+
310
+	valid := map[string]bool{
311
+		"status":   true,
312
+		"dangling": true,
313
+	}
314
+
315
+	if err := f.Validate(valid); err != nil {
316
+		t.Fatal(err)
317
+	}
318
+
319
+	f.Add("bogus", "running")
320
+	if err := f.Validate(valid); err == nil {
321
+		t.Fatalf("Expected to return an error, got nil")
322
+	}
323
+}
324
+
325
+func TestWalkValues(t *testing.T) {
326
+	f := NewArgs()
327
+	f.Add("status", "running")
328
+	f.Add("status", "paused")
329
+
330
+	f.WalkValues("status", func(value string) error {
331
+		if value != "running" && value != "paused" {
332
+			t.Fatalf("Unexpected value %s", value)
333
+		}
334
+		return nil
335
+	})
336
+
337
+	err := f.WalkValues("status", func(value string) error {
338
+		return fmt.Errorf("return")
339
+	})
340
+	if err == nil {
341
+		t.Fatalf("Expected to get an error, got nil")
342
+	}
343
+
344
+	err = f.WalkValues("foo", func(value string) error {
345
+		return fmt.Errorf("return")
346
+	})
347
+	if err != nil {
348
+		t.Fatalf("Expected to not iterate when the field doesn't exist, got %v", err)
349
+	}
350
+}
... ...
@@ -22,6 +22,7 @@ import (
22 22
 	"github.com/docker/distribution/reference"
23 23
 	"github.com/docker/docker/api"
24 24
 	"github.com/docker/docker/api/types"
25
+	"github.com/docker/docker/api/types/filters"
25 26
 	registrytypes "github.com/docker/docker/api/types/registry"
26 27
 	"github.com/docker/docker/container"
27 28
 	"github.com/docker/docker/daemon/events"
... ...
@@ -49,7 +50,6 @@ import (
49 49
 	"github.com/docker/docker/pkg/mount"
50 50
 	"github.com/docker/docker/pkg/namesgenerator"
51 51
 	"github.com/docker/docker/pkg/nat"
52
-	"github.com/docker/docker/pkg/parsers/filters"
53 52
 	"github.com/docker/docker/pkg/progress"
54 53
 	"github.com/docker/docker/pkg/signal"
55 54
 	"github.com/docker/docker/pkg/streamformatter"
... ...
@@ -2,8 +2,8 @@ package events
2 2
 
3 3
 import (
4 4
 	"github.com/docker/distribution/reference"
5
+	"github.com/docker/docker/api/types/filters"
5 6
 	"github.com/docker/docker/pkg/jsonmessage"
6
-	"github.com/docker/docker/pkg/parsers/filters"
7 7
 )
8 8
 
9 9
 // Filter can filter out docker events from a stream
... ...
@@ -7,9 +7,9 @@ import (
7 7
 
8 8
 	"github.com/docker/distribution/reference"
9 9
 	"github.com/docker/docker/api/types"
10
+	"github.com/docker/docker/api/types/filters"
10 11
 	"github.com/docker/docker/image"
11 12
 	"github.com/docker/docker/layer"
12
-	"github.com/docker/docker/pkg/parsers/filters"
13 13
 )
14 14
 
15 15
 var acceptedImageFilterTags = map[string]bool{
... ...
@@ -31,7 +31,7 @@ func (daemon *Daemon) Map() map[image.ID]*image.Image {
31 31
 }
32 32
 
33 33
 // Images returns a filtered list of images. filterArgs is a JSON-encoded set
34
-// of filter arguments which will be interpreted by pkg/parsers/filters.
34
+// of filter arguments which will be interpreted by api/types/filters.
35 35
 // filter is a shell glob string applied to repository names. The argument
36 36
 // named all controls whether all images in the graph are filtered, or just
37 37
 // the heads.
... ...
@@ -8,11 +8,11 @@ import (
8 8
 
9 9
 	"github.com/Sirupsen/logrus"
10 10
 	"github.com/docker/docker/api/types"
11
+	"github.com/docker/docker/api/types/filters"
11 12
 	"github.com/docker/docker/container"
12 13
 	"github.com/docker/docker/image"
13 14
 	"github.com/docker/docker/pkg/graphdb"
14 15
 	"github.com/docker/docker/pkg/nat"
15
-	"github.com/docker/docker/pkg/parsers/filters"
16 16
 )
17 17
 
18 18
 // iterationAction represents possible outcomes happening during the container iteration.
... ...
@@ -9,9 +9,9 @@ import (
9 9
 	"strings"
10 10
 
11 11
 	"github.com/docker/docker/api/types"
12
+	"github.com/docker/docker/api/types/filters"
12 13
 	"github.com/docker/docker/api/types/network"
13 14
 	"github.com/docker/docker/pkg/integration/checker"
14
-	"github.com/docker/docker/pkg/parsers/filters"
15 15
 	"github.com/go-check/check"
16 16
 )
17 17
 
18 18
deleted file mode 100644
... ...
@@ -1,241 +0,0 @@
1
-// Package filters provides helper function to parse and handle command line
2
-// filter, used for example in docker ps or docker images commands.
3
-package filters
4
-
5
-import (
6
-	"encoding/json"
7
-	"errors"
8
-	"fmt"
9
-	"regexp"
10
-	"strings"
11
-)
12
-
13
-// Args stores filter arguments as map key:{array of values}.
14
-// It contains a aggregation of the list of arguments (which are in the form
15
-// of -f 'key=value') based on the key, and store values for the same key
16
-// in an slice.
17
-// e.g given -f 'label=label1=1' -f 'label=label2=2' -f 'image.name=ubuntu'
18
-// the args will be {'label': {'label1=1','label2=2'}, 'image.name', {'ubuntu'}}
19
-type Args struct {
20
-	fields map[string]map[string]bool
21
-}
22
-
23
-// NewArgs initializes a new Args struct.
24
-func NewArgs() Args {
25
-	return Args{fields: map[string]map[string]bool{}}
26
-}
27
-
28
-// ParseFlag parses the argument to the filter flag. Like
29
-//
30
-//   `docker ps -f 'created=today' -f 'image.name=ubuntu*'`
31
-//
32
-// If prev map is provided, then it is appended to, and returned. By default a new
33
-// map is created.
34
-func ParseFlag(arg string, prev Args) (Args, error) {
35
-	filters := prev
36
-	if len(arg) == 0 {
37
-		return filters, nil
38
-	}
39
-
40
-	if !strings.Contains(arg, "=") {
41
-		return filters, ErrBadFormat
42
-	}
43
-
44
-	f := strings.SplitN(arg, "=", 2)
45
-
46
-	name := strings.ToLower(strings.TrimSpace(f[0]))
47
-	value := strings.TrimSpace(f[1])
48
-
49
-	filters.Add(name, value)
50
-
51
-	return filters, nil
52
-}
53
-
54
-// ErrBadFormat is an error returned in case of bad format for a filter.
55
-var ErrBadFormat = errors.New("bad format of filter (expected name=value)")
56
-
57
-// ToParam packs the Args into an string for easy transport from client to server.
58
-func ToParam(a Args) (string, error) {
59
-	// this way we don't URL encode {}, just empty space
60
-	if a.Len() == 0 {
61
-		return "", nil
62
-	}
63
-
64
-	buf, err := json.Marshal(a.fields)
65
-	if err != nil {
66
-		return "", err
67
-	}
68
-	return string(buf), nil
69
-}
70
-
71
-// FromParam unpacks the filter Args.
72
-func FromParam(p string) (Args, error) {
73
-	if len(p) == 0 {
74
-		return NewArgs(), nil
75
-	}
76
-
77
-	r := strings.NewReader(p)
78
-	d := json.NewDecoder(r)
79
-
80
-	m := map[string]map[string]bool{}
81
-	if err := d.Decode(&m); err != nil {
82
-		r.Seek(0, 0)
83
-
84
-		// Allow parsing old arguments in slice format.
85
-		// Because other libraries might be sending them in this format.
86
-		deprecated := map[string][]string{}
87
-		if deprecatedErr := d.Decode(&deprecated); deprecatedErr == nil {
88
-			m = deprecatedArgs(deprecated)
89
-		} else {
90
-			return NewArgs(), err
91
-		}
92
-	}
93
-	return Args{m}, nil
94
-}
95
-
96
-// Get returns the list of values associates with a field.
97
-// It returns a slice of strings to keep backwards compatibility with old code.
98
-func (filters Args) Get(field string) []string {
99
-	values := filters.fields[field]
100
-	if values == nil {
101
-		return make([]string, 0)
102
-	}
103
-	slice := make([]string, 0, len(values))
104
-	for key := range values {
105
-		slice = append(slice, key)
106
-	}
107
-	return slice
108
-}
109
-
110
-// Add adds a new value to a filter field.
111
-func (filters Args) Add(name, value string) {
112
-	if _, ok := filters.fields[name]; ok {
113
-		filters.fields[name][value] = true
114
-	} else {
115
-		filters.fields[name] = map[string]bool{value: true}
116
-	}
117
-}
118
-
119
-// Del removes a value from a filter field.
120
-func (filters Args) Del(name, value string) {
121
-	if _, ok := filters.fields[name]; ok {
122
-		delete(filters.fields[name], value)
123
-	}
124
-}
125
-
126
-// Len returns the number of fields in the arguments.
127
-func (filters Args) Len() int {
128
-	return len(filters.fields)
129
-}
130
-
131
-// MatchKVList returns true if the values for the specified field matches the ones
132
-// from the sources.
133
-// e.g. given Args are {'label': {'label1=1','label2=1'}, 'image.name', {'ubuntu'}},
134
-//      field is 'label' and sources are {'label1': '1', 'label2': '2'}
135
-//      it returns true.
136
-func (filters Args) MatchKVList(field string, sources map[string]string) bool {
137
-	fieldValues := filters.fields[field]
138
-
139
-	//do not filter if there is no filter set or cannot determine filter
140
-	if len(fieldValues) == 0 {
141
-		return true
142
-	}
143
-
144
-	if sources == nil || len(sources) == 0 {
145
-		return false
146
-	}
147
-
148
-	for name2match := range fieldValues {
149
-		testKV := strings.SplitN(name2match, "=", 2)
150
-
151
-		v, ok := sources[testKV[0]]
152
-		if !ok {
153
-			return false
154
-		}
155
-		if len(testKV) == 2 && testKV[1] != v {
156
-			return false
157
-		}
158
-	}
159
-
160
-	return true
161
-}
162
-
163
-// Match returns true if the values for the specified field matches the source string
164
-// e.g. given Args are {'label': {'label1=1','label2=1'}, 'image.name', {'ubuntu'}},
165
-//      field is 'image.name' and source is 'ubuntu'
166
-//      it returns true.
167
-func (filters Args) Match(field, source string) bool {
168
-	if filters.ExactMatch(field, source) {
169
-		return true
170
-	}
171
-
172
-	fieldValues := filters.fields[field]
173
-	for name2match := range fieldValues {
174
-		match, err := regexp.MatchString(name2match, source)
175
-		if err != nil {
176
-			continue
177
-		}
178
-		if match {
179
-			return true
180
-		}
181
-	}
182
-	return false
183
-}
184
-
185
-// ExactMatch returns true if the source matches exactly one of the filters.
186
-func (filters Args) ExactMatch(field, source string) bool {
187
-	fieldValues, ok := filters.fields[field]
188
-	//do not filter if there is no filter set or cannot determine filter
189
-	if !ok || len(fieldValues) == 0 {
190
-		return true
191
-	}
192
-
193
-	// try to march full name value to avoid O(N) regular expression matching
194
-	if fieldValues[source] {
195
-		return true
196
-	}
197
-	return false
198
-}
199
-
200
-// Include returns true if the name of the field to filter is in the filters.
201
-func (filters Args) Include(field string) bool {
202
-	_, ok := filters.fields[field]
203
-	return ok
204
-}
205
-
206
-// Validate ensures that all the fields in the filter are valid.
207
-// It returns an error as soon as it finds an invalid field.
208
-func (filters Args) Validate(accepted map[string]bool) error {
209
-	for name := range filters.fields {
210
-		if !accepted[name] {
211
-			return fmt.Errorf("Invalid filter '%s'", name)
212
-		}
213
-	}
214
-	return nil
215
-}
216
-
217
-// WalkValues iterates over the list of filtered values for a field.
218
-// It stops the iteration if it finds an error and it returns that error.
219
-func (filters Args) WalkValues(field string, op func(value string) error) error {
220
-	if _, ok := filters.fields[field]; !ok {
221
-		return nil
222
-	}
223
-	for v := range filters.fields[field] {
224
-		if err := op(v); err != nil {
225
-			return err
226
-		}
227
-	}
228
-	return nil
229
-}
230
-
231
-func deprecatedArgs(d map[string][]string) map[string]map[string]bool {
232
-	m := map[string]map[string]bool{}
233
-	for k, v := range d {
234
-		values := map[string]bool{}
235
-		for _, vv := range v {
236
-			values[vv] = true
237
-		}
238
-		m[k] = values
239
-	}
240
-	return m
241
-}
242 1
deleted file mode 100644
... ...
@@ -1,351 +0,0 @@
1
-package filters
2
-
3
-import (
4
-	"fmt"
5
-	"testing"
6
-)
7
-
8
-func TestParseArgs(t *testing.T) {
9
-	// equivalent of `docker ps -f 'created=today' -f 'image.name=ubuntu*' -f 'image.name=*untu'`
10
-	flagArgs := []string{
11
-		"created=today",
12
-		"image.name=ubuntu*",
13
-		"image.name=*untu",
14
-	}
15
-	var (
16
-		args = NewArgs()
17
-		err  error
18
-	)
19
-	for i := range flagArgs {
20
-		args, err = ParseFlag(flagArgs[i], args)
21
-		if err != nil {
22
-			t.Errorf("failed to parse %s: %s", flagArgs[i], err)
23
-		}
24
-	}
25
-	if len(args.Get("created")) != 1 {
26
-		t.Errorf("failed to set this arg")
27
-	}
28
-	if len(args.Get("image.name")) != 2 {
29
-		t.Errorf("the args should have collapsed")
30
-	}
31
-}
32
-
33
-func TestParseArgsEdgeCase(t *testing.T) {
34
-	var filters Args
35
-	args, err := ParseFlag("", filters)
36
-	if err != nil {
37
-		t.Fatal(err)
38
-	}
39
-	if args.Len() != 0 {
40
-		t.Fatalf("Expected an empty Args (map), got %v", args)
41
-	}
42
-	if args, err = ParseFlag("anything", args); err == nil || err != ErrBadFormat {
43
-		t.Fatalf("Expected ErrBadFormat, got %v", err)
44
-	}
45
-}
46
-
47
-func TestToParam(t *testing.T) {
48
-	fields := map[string]map[string]bool{
49
-		"created":    {"today": true},
50
-		"image.name": {"ubuntu*": true, "*untu": true},
51
-	}
52
-	a := Args{fields: fields}
53
-
54
-	_, err := ToParam(a)
55
-	if err != nil {
56
-		t.Errorf("failed to marshal the filters: %s", err)
57
-	}
58
-}
59
-
60
-func TestFromParam(t *testing.T) {
61
-	invalids := []string{
62
-		"anything",
63
-		"['a','list']",
64
-		"{'key': 'value'}",
65
-		`{"key": "value"}`,
66
-	}
67
-	valid := map[*Args][]string{
68
-		&Args{fields: map[string]map[string]bool{"key": {"value": true}}}: {
69
-			`{"key": ["value"]}`,
70
-			`{"key": {"value": true}}`,
71
-		},
72
-		&Args{fields: map[string]map[string]bool{"key": {"value1": true, "value2": true}}}: {
73
-			`{"key": ["value1", "value2"]}`,
74
-			`{"key": {"value1": true, "value2": true}}`,
75
-		},
76
-		&Args{fields: map[string]map[string]bool{"key1": {"value1": true}, "key2": {"value2": true}}}: {
77
-			`{"key1": ["value1"], "key2": ["value2"]}`,
78
-			`{"key1": {"value1": true}, "key2": {"value2": true}}`,
79
-		},
80
-	}
81
-
82
-	for _, invalid := range invalids {
83
-		if _, err := FromParam(invalid); err == nil {
84
-			t.Fatalf("Expected an error with %v, got nothing", invalid)
85
-		}
86
-	}
87
-
88
-	for expectedArgs, matchers := range valid {
89
-		for _, json := range matchers {
90
-			args, err := FromParam(json)
91
-			if err != nil {
92
-				t.Fatal(err)
93
-			}
94
-			if args.Len() != expectedArgs.Len() {
95
-				t.Fatalf("Expected %v, go %v", expectedArgs, args)
96
-			}
97
-			for key, expectedValues := range expectedArgs.fields {
98
-				values := args.Get(key)
99
-
100
-				if len(values) != len(expectedValues) {
101
-					t.Fatalf("Expected %v, go %v", expectedArgs, args)
102
-				}
103
-
104
-				for _, v := range values {
105
-					if !expectedValues[v] {
106
-						t.Fatalf("Expected %v, go %v", expectedArgs, args)
107
-					}
108
-				}
109
-			}
110
-		}
111
-	}
112
-}
113
-
114
-func TestEmpty(t *testing.T) {
115
-	a := Args{}
116
-	v, err := ToParam(a)
117
-	if err != nil {
118
-		t.Errorf("failed to marshal the filters: %s", err)
119
-	}
120
-	v1, err := FromParam(v)
121
-	if err != nil {
122
-		t.Errorf("%s", err)
123
-	}
124
-	if a.Len() != v1.Len() {
125
-		t.Errorf("these should both be empty sets")
126
-	}
127
-}
128
-
129
-func TestArgsMatchKVListEmptySources(t *testing.T) {
130
-	args := NewArgs()
131
-	if !args.MatchKVList("created", map[string]string{}) {
132
-		t.Fatalf("Expected true for (%v,created), got true", args)
133
-	}
134
-
135
-	args = Args{map[string]map[string]bool{"created": {"today": true}}}
136
-	if args.MatchKVList("created", map[string]string{}) {
137
-		t.Fatalf("Expected false for (%v,created), got true", args)
138
-	}
139
-}
140
-
141
-func TestArgsMatchKVList(t *testing.T) {
142
-	// Not empty sources
143
-	sources := map[string]string{
144
-		"key1": "value1",
145
-		"key2": "value2",
146
-		"key3": "value3",
147
-	}
148
-
149
-	matches := map[*Args]string{
150
-		&Args{}: "field",
151
-		&Args{map[string]map[string]bool{
152
-			"created": map[string]bool{"today": true},
153
-			"labels":  map[string]bool{"key1": true}},
154
-		}: "labels",
155
-		&Args{map[string]map[string]bool{
156
-			"created": map[string]bool{"today": true},
157
-			"labels":  map[string]bool{"key1=value1": true}},
158
-		}: "labels",
159
-	}
160
-
161
-	for args, field := range matches {
162
-		if args.MatchKVList(field, sources) != true {
163
-			t.Fatalf("Expected true for %v on %v, got false", sources, args)
164
-		}
165
-	}
166
-
167
-	differs := map[*Args]string{
168
-		&Args{map[string]map[string]bool{
169
-			"created": map[string]bool{"today": true}},
170
-		}: "created",
171
-		&Args{map[string]map[string]bool{
172
-			"created": map[string]bool{"today": true},
173
-			"labels":  map[string]bool{"key4": true}},
174
-		}: "labels",
175
-		&Args{map[string]map[string]bool{
176
-			"created": map[string]bool{"today": true},
177
-			"labels":  map[string]bool{"key1=value3": true}},
178
-		}: "labels",
179
-	}
180
-
181
-	for args, field := range differs {
182
-		if args.MatchKVList(field, sources) != false {
183
-			t.Fatalf("Expected false for %v on %v, got true", sources, args)
184
-		}
185
-	}
186
-}
187
-
188
-func TestArgsMatch(t *testing.T) {
189
-	source := "today"
190
-
191
-	matches := map[*Args]string{
192
-		&Args{}: "field",
193
-		&Args{map[string]map[string]bool{
194
-			"created": map[string]bool{"today": true}},
195
-		}: "today",
196
-		&Args{map[string]map[string]bool{
197
-			"created": map[string]bool{"to*": true}},
198
-		}: "created",
199
-		&Args{map[string]map[string]bool{
200
-			"created": map[string]bool{"to(.*)": true}},
201
-		}: "created",
202
-		&Args{map[string]map[string]bool{
203
-			"created": map[string]bool{"tod": true}},
204
-		}: "created",
205
-		&Args{map[string]map[string]bool{
206
-			"created": map[string]bool{"anyting": true, "to*": true}},
207
-		}: "created",
208
-	}
209
-
210
-	for args, field := range matches {
211
-		if args.Match(field, source) != true {
212
-			t.Fatalf("Expected true for %v on %v, got false", source, args)
213
-		}
214
-	}
215
-
216
-	differs := map[*Args]string{
217
-		&Args{map[string]map[string]bool{
218
-			"created": map[string]bool{"tomorrow": true}},
219
-		}: "created",
220
-		&Args{map[string]map[string]bool{
221
-			"created": map[string]bool{"to(day": true}},
222
-		}: "created",
223
-		&Args{map[string]map[string]bool{
224
-			"created": map[string]bool{"tom(.*)": true}},
225
-		}: "created",
226
-		&Args{map[string]map[string]bool{
227
-			"created": map[string]bool{"tom": true}},
228
-		}: "created",
229
-		&Args{map[string]map[string]bool{
230
-			"created": map[string]bool{"today1": true},
231
-			"labels":  map[string]bool{"today": true}},
232
-		}: "created",
233
-	}
234
-
235
-	for args, field := range differs {
236
-		if args.Match(field, source) != false {
237
-			t.Fatalf("Expected false for %v on %v, got true", source, args)
238
-		}
239
-	}
240
-}
241
-
242
-func TestAdd(t *testing.T) {
243
-	f := NewArgs()
244
-	f.Add("status", "running")
245
-	v := f.fields["status"]
246
-	if len(v) != 1 || !v["running"] {
247
-		t.Fatalf("Expected to include a running status, got %v", v)
248
-	}
249
-
250
-	f.Add("status", "paused")
251
-	if len(v) != 2 || !v["paused"] {
252
-		t.Fatalf("Expected to include a paused status, got %v", v)
253
-	}
254
-}
255
-
256
-func TestDel(t *testing.T) {
257
-	f := NewArgs()
258
-	f.Add("status", "running")
259
-	f.Del("status", "running")
260
-	v := f.fields["status"]
261
-	if v["running"] {
262
-		t.Fatalf("Expected to not include a running status filter, got true")
263
-	}
264
-}
265
-
266
-func TestLen(t *testing.T) {
267
-	f := NewArgs()
268
-	if f.Len() != 0 {
269
-		t.Fatalf("Expected to not include any field")
270
-	}
271
-	f.Add("status", "running")
272
-	if f.Len() != 1 {
273
-		t.Fatalf("Expected to include one field")
274
-	}
275
-}
276
-
277
-func TestExactMatch(t *testing.T) {
278
-	f := NewArgs()
279
-
280
-	if !f.ExactMatch("status", "running") {
281
-		t.Fatalf("Expected to match `running` when there are no filters, got false")
282
-	}
283
-
284
-	f.Add("status", "running")
285
-	f.Add("status", "pause*")
286
-
287
-	if !f.ExactMatch("status", "running") {
288
-		t.Fatalf("Expected to match `running` with one of the filters, got false")
289
-	}
290
-
291
-	if f.ExactMatch("status", "paused") {
292
-		t.Fatalf("Expected to not match `paused` with one of the filters, got true")
293
-	}
294
-}
295
-
296
-func TestInclude(t *testing.T) {
297
-	f := NewArgs()
298
-	if f.Include("status") {
299
-		t.Fatalf("Expected to not include a status key, got true")
300
-	}
301
-	f.Add("status", "running")
302
-	if !f.Include("status") {
303
-		t.Fatalf("Expected to include a status key, got false")
304
-	}
305
-}
306
-
307
-func TestValidate(t *testing.T) {
308
-	f := NewArgs()
309
-	f.Add("status", "running")
310
-
311
-	valid := map[string]bool{
312
-		"status":   true,
313
-		"dangling": true,
314
-	}
315
-
316
-	if err := f.Validate(valid); err != nil {
317
-		t.Fatal(err)
318
-	}
319
-
320
-	f.Add("bogus", "running")
321
-	if err := f.Validate(valid); err == nil {
322
-		t.Fatalf("Expected to return an error, got nil")
323
-	}
324
-}
325
-
326
-func TestWalkValues(t *testing.T) {
327
-	f := NewArgs()
328
-	f.Add("status", "running")
329
-	f.Add("status", "paused")
330
-
331
-	f.WalkValues("status", func(value string) error {
332
-		if value != "running" && value != "paused" {
333
-			t.Fatalf("Unexpected value %s", value)
334
-		}
335
-		return nil
336
-	})
337
-
338
-	err := f.WalkValues("status", func(value string) error {
339
-		return fmt.Errorf("return")
340
-	})
341
-	if err == nil {
342
-		t.Fatalf("Expected to get an error, got nil")
343
-	}
344
-
345
-	err = f.WalkValues("foo", func(value string) error {
346
-		return fmt.Errorf("return")
347
-	})
348
-	if err != nil {
349
-		t.Fatalf("Expected to not iterate when the field doesn't exist, got %v", err)
350
-	}
351
-}