Browse code

Add network label filter support

This patch did following:

1) Make filter check logic same as `docker ps ` filters

Right now docker container logic work as following:
when same filter used like below:
-f name=jack -f name=tom
it would get all containers name is jack or tom(it is or logic)

when different filter used like below:

-f name=jack -f id=7d1
it would get all containers name is jack and id contains 7d1(it is and logic)

It would make sense in many user cases, but it did lack of compliate filter cases,
like "I want to get containers name is jack or id=7d1", it could work around use
(get id=7d1 containers' name and get name=jack containers, and then construct the
final containers, they could be done in user side use shell or rest API)

2) Fix one network filter bug which could include duplicate result
when use -f name= -f id=, it would get duplicate results

3) Make id filter same as container id filter, which means match any string.
not use prefix match.

It is for consistent match logic

Closes: #21417

Signed-off-by: Kai Qiang Wu(Kennan) <wkqwu@cn.ibm.com>

Kai Qiang Wu(Kennan) authored on 2016/03/24 23:31:19
Showing 6 changed files
... ...
@@ -2,8 +2,6 @@ package network
2 2
 
3 3
 import (
4 4
 	"fmt"
5
-	"regexp"
6
-	"strings"
7 5
 
8 6
 	"github.com/docker/docker/runconfig"
9 7
 	"github.com/docker/engine-api/types/filters"
... ...
@@ -13,27 +11,13 @@ import (
13 13
 type filterHandler func([]libnetwork.Network, string) ([]libnetwork.Network, error)
14 14
 
15 15
 var (
16
-	// supportedFilters predefined some supported filter handler function
17
-	supportedFilters = map[string]filterHandler{
18
-		"type": filterNetworkByType,
19
-		"name": filterNetworkByName,
20
-		"id":   filterNetworkByID,
16
+	// AcceptedFilters is an acceptable filters for validation
17
+	AcceptedFilters = map[string]bool{
18
+		"type":  true,
19
+		"name":  true,
20
+		"id":    true,
21
+		"label": true,
21 22
 	}
22
-
23
-	// AcceptedFilters is an acceptable filter flag list
24
-	// generated for validation. e.g.
25
-	// acceptedFilters = map[string]bool{
26
-	//     "type": true,
27
-	//     "name": true,
28
-	//     "id":   true,
29
-	// }
30
-	AcceptedFilters = func() map[string]bool {
31
-		ret := make(map[string]bool)
32
-		for k := range supportedFilters {
33
-			ret[k] = true
34
-		}
35
-		return ret
36
-	}()
37 23
 )
38 24
 
39 25
 func filterNetworkByType(nws []libnetwork.Network, netType string) (retNws []libnetwork.Network, err error) {
... ...
@@ -56,34 +40,6 @@ func filterNetworkByType(nws []libnetwork.Network, netType string) (retNws []lib
56 56
 	return retNws, nil
57 57
 }
58 58
 
59
-func filterNetworkByName(nws []libnetwork.Network, name string) (retNws []libnetwork.Network, err error) {
60
-	for _, nw := range nws {
61
-		// exact match (fast path)
62
-		if nw.Name() == name {
63
-			retNws = append(retNws, nw)
64
-			continue
65
-		}
66
-
67
-		// regexp match (slow path)
68
-		match, err := regexp.MatchString(name, nw.Name())
69
-		if err != nil || !match {
70
-			continue
71
-		} else {
72
-			retNws = append(retNws, nw)
73
-		}
74
-	}
75
-	return retNws, nil
76
-}
77
-
78
-func filterNetworkByID(nws []libnetwork.Network, id string) (retNws []libnetwork.Network, err error) {
79
-	for _, nw := range nws {
80
-		if strings.HasPrefix(nw.ID(), id) {
81
-			retNws = append(retNws, nw)
82
-		}
83
-	}
84
-	return retNws, nil
85
-}
86
-
87 59
 // FilterNetworks filters network list according to user specified filter
88 60
 // and returns user chosen networks
89 61
 func FilterNetworks(nws []libnetwork.Network, filter filters.Args) ([]libnetwork.Network, error) {
... ...
@@ -93,18 +49,40 @@ func FilterNetworks(nws []libnetwork.Network, filter filters.Args) ([]libnetwork
93 93
 	}
94 94
 
95 95
 	var displayNet []libnetwork.Network
96
-	for fkey, fhandler := range supportedFilters {
97
-		errFilter := filter.WalkValues(fkey, func(fval string) error {
98
-			passList, err := fhandler(nws, fval)
96
+	for _, nw := range nws {
97
+		if filter.Include("name") {
98
+			if !filter.Match("name", nw.Name()) {
99
+				continue
100
+			}
101
+		}
102
+		if filter.Include("id") {
103
+			if !filter.Match("id", nw.ID()) {
104
+				continue
105
+			}
106
+		}
107
+		if filter.Include("label") {
108
+			if !filter.MatchKVList("label", nw.Info().Labels()) {
109
+				continue
110
+			}
111
+		}
112
+		displayNet = append(displayNet, nw)
113
+	}
114
+
115
+	if filter.Include("type") {
116
+		var typeNet []libnetwork.Network
117
+		errFilter := filter.WalkValues("type", func(fval string) error {
118
+			passList, err := filterNetworkByType(displayNet, fval)
99 119
 			if err != nil {
100 120
 				return err
101 121
 			}
102
-			displayNet = append(displayNet, passList...)
122
+			typeNet = append(typeNet, passList...)
103 123
 			return nil
104 124
 		})
105 125
 		if errFilter != nil {
106 126
 			return nil, errFilter
107 127
 		}
128
+		displayNet = typeNet
108 129
 	}
130
+
109 131
 	return displayNet, nil
110 132
 }
... ...
@@ -118,6 +118,7 @@ This section lists each version from latest to oldest.  Each listing includes a
118 118
 
119 119
 * `POST /containers/create` now takes `StorageOpt` field.
120 120
 * `GET /info` now returns `SecurityOptions` field, showing if `apparmor`, `seccomp`, or `selinux` is supported.
121
+* `GET /networks` now supports filtering by `label`.
121 122
 
122 123
 ### v1.23 API changes
123 124
 
... ...
@@ -2947,8 +2947,9 @@ Content-Type: application/json
2947 2947
 Query Parameters:
2948 2948
 
2949 2949
 - **filters** - JSON encoded network list filter. The filter value is one of:
2950
-  -   `name=<network-name>` Matches all or part of a network name.
2951 2950
   -   `id=<network-id>` Matches all or part of a network id.
2951
+  -   `label=<key>` or `label=<key>=<value>` of a network label.
2952
+  -   `name=<network-name>` Matches all or part of a network name.
2952 2953
   -   `type=["custom"|"builtin"]` Filters networks by type. The `custom` keyword returns all user-defined networks.
2953 2954
 
2954 2955
 Status Codes:
... ...
@@ -52,32 +52,56 @@ Multiple filter flags are combined as an `OR` filter. For example,
52 52
 The currently supported filters are:
53 53
 
54 54
 * id (network's id)
55
+* label (`label=<key>` or `label=<key>=<value>`)
55 56
 * name (network's name)
56 57
 * type (custom|builtin)
57 58
 
58
-#### Type
59
+#### ID
59 60
 
60
-The `type` filter supports two values; `builtin` displays predefined networks
61
-(`bridge`, `none`, `host`), whereas `custom` displays user defined networks.
61
+The `id` filter matches on all or part of a network's ID.
62 62
 
63
-The following filter matches all user defined networks:
63
+The following filter matches all networks with an ID containing the
64
+`63d1ff1f77b0...` string.
64 65
 
65 66
 ```bash
66
-$ docker network ls --filter type=custom
67
+$ docker network ls --filter id=63d1ff1f77b07ca51070a8c227e962238358bd310bde1529cf62e6c307ade161
67 68
 NETWORK ID          NAME                DRIVER
68
-95e74588f40d        foo                 bridge
69 69
 63d1ff1f77b0        dev                 bridge
70 70
 ```
71 71
 
72
-By having this flag it allows for batch cleanup. For example, use this filter
73
-to delete all user defined networks:
72
+You can also filter for a substring in an ID as this shows:
74 73
 
75 74
 ```bash
76
-$ docker network rm `docker network ls --filter type=custom -q`
75
+$ docker network ls --filter id=95e74588f40d
76
+NETWORK ID          NAME                DRIVER
77
+95e74588f40d        foo                 bridge
78
+
79
+$ docker network ls --filter id=95e
80
+NETWORK ID          NAME                DRIVER
81
+95e74588f40d        foo                 bridge
77 82
 ```
78 83
 
79
-A warning will be issued when trying to remove a network that has containers
80
-attached.
84
+#### Label
85
+
86
+The `label` filter matches containers based on the presence of a `label` alone or a `label` and a
87
+value.
88
+
89
+The following filter matches networks with the `usage` label regardless of its value.
90
+
91
+```bash
92
+$ docker network ls -f "label=usage"
93
+NETWORK ID          NAME                DRIVER
94
+db9db329f835        test1               bridge              
95
+f6e212da9dfd        test2               bridge
96
+```
97
+
98
+The following filter matches containers with the `usage` label with the `prod` value.
99
+
100
+```bash
101
+$ docker network ls -f "label=usage=prod"
102
+NETWORK ID          NAME                DRIVER
103
+f6e212da9dfd        test2               bridge
104
+```
81 105
 
82 106
 #### Name
83 107
 
... ...
@@ -100,31 +124,30 @@ NETWORK ID          NAME                DRIVER
100 100
 06e7eef0a170        foobar              bridge
101 101
 ```
102 102
 
103
-#### ID
103
+#### Type
104 104
 
105
-The `id` filter matches on all or part of a network's ID.
105
+The `type` filter supports two values; `builtin` displays predefined networks
106
+(`bridge`, `none`, `host`), whereas `custom` displays user defined networks.
106 107
 
107
-The following filter matches all networks with an ID containing the
108
-`63d1ff1f77b0...` string.
108
+The following filter matches all user defined networks:
109 109
 
110 110
 ```bash
111
-$ docker network ls --filter id=63d1ff1f77b07ca51070a8c227e962238358bd310bde1529cf62e6c307ade161
111
+$ docker network ls --filter type=custom
112 112
 NETWORK ID          NAME                DRIVER
113
+95e74588f40d        foo                 bridge
113 114
 63d1ff1f77b0        dev                 bridge
114 115
 ```
115 116
 
116
-You can also filter for a substring in an ID as this shows:
117
+By having this flag it allows for batch cleanup. For example, use this filter
118
+to delete all user defined networks:
117 119
 
118 120
 ```bash
119
-$ docker network ls --filter id=95e74588f40d
120
-NETWORK ID          NAME                DRIVER
121
-95e74588f40d        foo                 bridge
122
-
123
-$ docker network ls --filter id=95e
124
-NETWORK ID          NAME                DRIVER
125
-95e74588f40d        foo                 bridge
121
+$ docker network rm `docker network ls --filter type=custom -q`
126 122
 ```
127 123
 
124
+A warning will be issued when trying to remove a network that has containers
125
+attached.
126
+
128 127
 ## Related information
129 128
 
130 129
 * [network disconnect ](network_disconnect.md)
... ...
@@ -308,16 +308,23 @@ func (s *DockerNetworkSuite) TestDockerNetworkRmPredefined(c *check.C) {
308 308
 }
309 309
 
310 310
 func (s *DockerNetworkSuite) TestDockerNetworkLsFilter(c *check.C) {
311
+	testNet := "testnet1"
312
+	testLabel := "foo"
313
+	testValue := "bar"
311 314
 	out, _ := dockerCmd(c, "network", "create", "dev")
312 315
 	defer func() {
313 316
 		dockerCmd(c, "network", "rm", "dev")
317
+		dockerCmd(c, "network", "rm", testNet)
314 318
 	}()
315 319
 	networkID := strings.TrimSpace(out)
316 320
 
317
-	// filter with partial ID and partial name
318
-	// only show 'bridge' and 'dev' network
319
-	out, _ = dockerCmd(c, "network", "ls", "-f", "id="+networkID[0:5], "-f", "name=dge")
320
-	assertNwList(c, out, []string{"bridge", "dev"})
321
+	// filter with partial ID
322
+	// only show 'dev' network
323
+	out, _ = dockerCmd(c, "network", "ls", "-f", "id="+networkID[0:5])
324
+	assertNwList(c, out, []string{"dev"})
325
+
326
+	out, _ = dockerCmd(c, "network", "ls", "-f", "name=dge")
327
+	assertNwList(c, out, []string{"bridge"})
321 328
 
322 329
 	// only show built-in network (bridge, none, host)
323 330
 	out, _ = dockerCmd(c, "network", "ls", "-f", "type=builtin")
... ...
@@ -331,6 +338,19 @@ func (s *DockerNetworkSuite) TestDockerNetworkLsFilter(c *check.C) {
331 331
 	// it should be equivalent of ls without option
332 332
 	out, _ = dockerCmd(c, "network", "ls", "-f", "type=custom", "-f", "type=builtin")
333 333
 	assertNwList(c, out, []string{"bridge", "dev", "host", "none"})
334
+
335
+	out, _ = dockerCmd(c, "network", "create", "--label", testLabel+"="+testValue, testNet)
336
+	assertNwIsAvailable(c, testNet)
337
+
338
+	out, _ = dockerCmd(c, "network", "ls", "-f", "label="+testLabel)
339
+	assertNwList(c, out, []string{testNet})
340
+
341
+	out, _ = dockerCmd(c, "network", "ls", "-f", "label="+testLabel+"="+testValue)
342
+	assertNwList(c, out, []string{testNet})
343
+
344
+	out, _ = dockerCmd(c, "network", "ls", "-f", "label=nonexistent")
345
+	outArr := strings.Split(strings.TrimSpace(out), "\n")
346
+	c.Assert(len(outArr), check.Equals, 1, check.Commentf("%s\n", out))
334 347
 }
335 348
 
336 349
 func (s *DockerNetworkSuite) TestDockerNetworkCreateDelete(c *check.C) {
... ...
@@ -47,32 +47,56 @@ Multiple filter flags are combined as an `OR` filter. For example,
47 47
 The currently supported filters are:
48 48
 
49 49
 * id (network's id)
50
+* label (`label=<key>` or `label=<key>=<value>`)
50 51
 * name (network's name)
51 52
 * type (custom|builtin)
52 53
 
53
-#### Type
54
+#### ID
54 55
 
55
-The `type` filter supports two values; `builtin` displays predefined networks
56
-(`bridge`, `none`, `host`), whereas `custom` displays user defined networks.
56
+The `id` filter matches on all or part of a network's ID.
57 57
 
58
-The following filter matches all user defined networks:
58
+The following filter matches all networks with an ID containing the
59
+`63d1ff1f77b0...` string.
59 60
 
60 61
 ```bash
61
-$ docker network ls --filter type=custom
62
+$ docker network ls --filter id=63d1ff1f77b07ca51070a8c227e962238358bd310bde1529cf62e6c307ade161
62 63
 NETWORK ID          NAME                DRIVER
63
-95e74588f40d        foo                 bridge
64 64
 63d1ff1f77b0        dev                 bridge
65 65
 ```
66 66
 
67
-By having this flag it allows for batch cleanup. For example, use this filter
68
-to delete all user defined networks:
67
+You can also filter for a substring in an ID as this shows:
69 68
 
70 69
 ```bash
71
-$ docker network rm `docker network ls --filter type=custom -q`
70
+$ docker network ls --filter id=95e74588f40d
71
+NETWORK ID          NAME                DRIVER
72
+95e74588f40d        foo                 bridge
73
+
74
+$ docker network ls --filter id=95e
75
+NETWORK ID          NAME                DRIVER
76
+95e74588f40d        foo                 bridge
72 77
 ```
73 78
 
74
-A warning will be issued when trying to remove a network that has containers
75
-attached.
79
+#### Label
80
+
81
+The `label` filter matches containers based on the presence of a `label` alone or a `label` and a
82
+value.
83
+
84
+The following filter matches networks with the `usage` label regardless of its value.
85
+
86
+```bash
87
+$ docker network ls -f "label=usage"
88
+NETWORK ID          NAME                DRIVER
89
+db9db329f835        test1               bridge              
90
+f6e212da9dfd        test2               bridge
91
+```
92
+
93
+The following filter matches containers with the `usage` label with the `prod` value.
94
+
95
+```bash
96
+$ docker network ls -f "label=usage=prod"
97
+NETWORK ID          NAME                DRIVER
98
+f6e212da9dfd        test2               bridge
99
+```
76 100
 
77 101
 #### Name
78 102
 
... ...
@@ -95,31 +119,30 @@ NETWORK ID          NAME                DRIVER
95 95
 06e7eef0a170        foobar              bridge
96 96
 ```
97 97
 
98
-#### ID
98
+#### Type
99 99
 
100
-The `id` filter matches on all or part of a network's ID.
100
+The `type` filter supports two values; `builtin` displays predefined networks
101
+(`bridge`, `none`, `host`), whereas `custom` displays user defined networks.
101 102
 
102
-The following filter matches all networks with an ID containing the
103
-`63d1ff1f77b0...` string.
103
+The following filter matches all user defined networks:
104 104
 
105 105
 ```bash
106
-$ docker network ls --filter id=63d1ff1f77b07ca51070a8c227e962238358bd310bde1529cf62e6c307ade161
106
+$ docker network ls --filter type=custom
107 107
 NETWORK ID          NAME                DRIVER
108
+95e74588f40d        foo                 bridge
108 109
 63d1ff1f77b0        dev                 bridge
109 110
 ```
110 111
 
111
-You can also filter for a substring in an ID as this shows:
112
+By having this flag it allows for batch cleanup. For example, use this filter
113
+to delete all user defined networks:
112 114
 
113 115
 ```bash
114
-$ docker network ls --filter id=95e74588f40d
115
-NETWORK ID          NAME                DRIVER
116
-95e74588f40d        foo                 bridge
117
-
118
-$ docker network ls --filter id=95e
119
-NETWORK ID          NAME                DRIVER
120
-95e74588f40d        foo                 bridge
116
+$ docker network rm `docker network ls --filter type=custom -q`
121 117
 ```
122 118
 
119
+A warning will be issued when trying to remove a network that has containers
120
+attached.
121
+
123 122
 # OPTIONS
124 123
 
125 124
 **-f**, **--filter**=*[]*