Browse code

filter flag: split out for separate --filter flags

adding tests and allowing for easy passing of filters.Args from client
to server.

Docker-DCO-1.1-Signed-off-by: Vincent Batts <vbatts@redhat.com> (github: vbatts)

Vincent Batts authored on 2014/05/20 05:31:15
Showing 5 changed files
... ...
@@ -1160,10 +1160,10 @@ func (cli *DockerCli) CmdImages(args ...string) error {
1160 1160
 
1161 1161
 	// Consolidate all filter flags, and sanity check them early.
1162 1162
 	// They'll get process in the daemon/server.
1163
-	imageFilters := map[string]string{}
1163
+	imageFilterArgs := filters.Args{}
1164 1164
 	for _, f := range flFilter.GetAll() {
1165 1165
 		var err error
1166
-		imageFilters, err = filters.ParseFlag(f, imageFilters)
1166
+		imageFilterArgs, err = filters.ParseFlag(f, imageFilterArgs)
1167 1167
 		if err != nil {
1168 1168
 			return err
1169 1169
 		}
... ...
@@ -1174,7 +1174,7 @@ func (cli *DockerCli) CmdImages(args ...string) error {
1174 1174
 	if *flViz || *flTree {
1175 1175
 		v := url.Values{
1176 1176
 			"all":     []string{"1"},
1177
-			"filters": []string{filters.ToParam(imageFilters)},
1177
+			"filters": []string{filters.ToParam(imageFilterArgs)},
1178 1178
 		}
1179 1179
 		body, _, err := readBody(cli.call("GET", "/images/json?"+v.Encode(), nil, false))
1180 1180
 		if err != nil {
... ...
@@ -1238,7 +1238,7 @@ func (cli *DockerCli) CmdImages(args ...string) error {
1238 1238
 		}
1239 1239
 	} else {
1240 1240
 		v := url.Values{
1241
-			"filters": []string{filters.ToParam(imageFilters)},
1241
+			"filters": []string{filters.ToParam(imageFilterArgs)},
1242 1242
 		}
1243 1243
 		if cmd.NArg() == 1 {
1244 1244
 			// FIXME rename this parameter, to not be confused with the filters flag
... ...
@@ -189,7 +189,7 @@ func getImagesJSON(eng *engine.Engine, version version.Version, w http.ResponseW
189 189
 	)
190 190
 
191 191
 	job.Setenv("filters", r.Form.Get("filters"))
192
-	// FIXME rename this parameter, to not be confused with the filters flag
192
+	// FIXME this parameter could just be a match filter
193 193
 	job.Setenv("filter", r.Form.Get("filter"))
194 194
 	job.Setenv("all", r.Form.Get("all"))
195 195
 
... ...
@@ -700,12 +700,19 @@ func (srv *Server) Images(job *engine.Job) engine.Status {
700 700
 		filt_tagged = true
701 701
 	)
702 702
 
703
-	imageFilters, err := filters.ParseFlag(job.Getenv("filters"), nil)
703
+	utils.Debugf("SUCH JOB: %#v", job)
704
+	utils.Debugf("SUCH ENV: %#v", *job.Env())
705
+	imageFilters, err := filters.FromParam(job.Getenv("filters"))
704 706
 	if err != nil {
705 707
 		return job.Error(err)
706 708
 	}
707
-	if i, ok := imageFilters["untagged"]; ok && strings.ToLower(i) == "true" {
708
-		filt_tagged = false
709
+	utils.Debugf("SUCH FILTERS: %#v", imageFilters)
710
+	if i, ok := imageFilters["untagged"]; ok {
711
+		for _, value := range i {
712
+			if strings.ToLower(value) == "true" {
713
+				filt_tagged = false
714
+			}
715
+		}
709 716
 	}
710 717
 
711 718
 	if job.GetenvBool("all") && !filt_tagged {
... ...
@@ -2,46 +2,51 @@ package filters
2 2
 
3 3
 import (
4 4
 	"errors"
5
+	"github.com/dotcloud/docker/pkg/beam/data"
5 6
 	"strings"
6 7
 )
7 8
 
9
+type Args map[string][]string
10
+
8 11
 /*
9 12
 Parse the argument to the filter flag. Like
10 13
 
11
-  `docker ps -f 'created=today;image.name=ubuntu*'`
12
-
13
-Filters delimited by ';', and expected to be 'name=value'
14
+  `docker ps -f 'created=today' -f 'image.name=ubuntu*'`
14 15
 
15 16
 If prev map is provided, then it is appended to, and returned. By default a new
16 17
 map is created.
17 18
 */
18
-func ParseFlag(arg string, prev map[string]string) (map[string]string, error) {
19
-	var filters map[string]string
20
-	if prev != nil {
21
-		filters = prev
22
-	} else {
23
-		filters = map[string]string{}
19
+func ParseFlag(arg string, prev Args) (Args, error) {
20
+	var filters Args = prev
21
+	if prev == nil {
22
+		filters = Args{}
24 23
 	}
25 24
 	if len(arg) == 0 {
26 25
 		return filters, nil
27 26
 	}
28 27
 
29
-	for _, chunk := range strings.Split(arg, ";") {
30
-		if !strings.Contains(chunk, "=") {
31
-			return filters, ErrorBadFormat
32
-		}
33
-		f := strings.SplitN(chunk, "=", 2)
34
-		filters[f[0]] = f[1]
28
+	if !strings.Contains(arg, "=") {
29
+		return filters, ErrorBadFormat
35 30
 	}
31
+
32
+	f := strings.SplitN(arg, "=", 2)
33
+	filters[f[0]] = append(filters[f[0]], f[1])
34
+
36 35
 	return filters, nil
37 36
 }
38 37
 
39 38
 var ErrorBadFormat = errors.New("bad format of filter (expected name=value)")
40 39
 
41
-func ToParam(f map[string]string) string {
42
-	fs := []string{}
43
-	for k, v := range f {
44
-		fs = append(fs, k+"="+v)
45
-	}
46
-	return strings.Join(fs, ";")
40
+/*
41
+packs the Args into an string for easy transport from client to server
42
+*/
43
+func ToParam(a Args) string {
44
+	return data.Encode(a)
45
+}
46
+
47
+/*
48
+unpacks the filter Args
49
+*/
50
+func FromParam(p string) (Args, error) {
51
+	return data.Decode(p)
47 52
 }
48 53
new file mode 100644
... ...
@@ -0,0 +1,60 @@
0
+package filters
1
+
2
+import (
3
+	"sort"
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 = Args{}
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["created"]) != 1 {
25
+		t.Errorf("failed to set this arg")
26
+	}
27
+	if len(args["image.name"]) != 2 {
28
+		t.Errorf("the args should have collapsed")
29
+	}
30
+}
31
+
32
+func TestParam(t *testing.T) {
33
+	a := Args{
34
+		"created":    []string{"today"},
35
+		"image.name": []string{"ubuntu*", "*untu"},
36
+	}
37
+
38
+	v := ToParam(a)
39
+	v1, err := FromParam(v)
40
+	if err != nil {
41
+		t.Errorf("%s", err)
42
+	}
43
+	for key, vals := range v1 {
44
+		if _, ok := a[key]; !ok {
45
+			t.Errorf("could not find key %s in original set", key)
46
+		}
47
+		sort.Strings(vals)
48
+		sort.Strings(a[key])
49
+		if len(vals) != len(a[key]) {
50
+			t.Errorf("value lengths ought to match")
51
+			continue
52
+		}
53
+		for i := range vals {
54
+			if vals[i] != a[key][i] {
55
+				t.Errorf("expected %s, but got %s", a[key][i], vals[i])
56
+			}
57
+		}
58
+	}
59
+}