Browse code

Merge pull request #30810 from allencloud/make-secret-ls-support-filter

make secret ls support filters in CLI

Sebastiaan van Stijn authored on 2017/03/28 20:43:19
Showing 6 changed files
... ...
@@ -7984,6 +7984,9 @@ paths:
7984 7984
           description: |
7985 7985
             A JSON encoded value of the filters (a `map[string][]string`) to process on the secrets list. Available filters:
7986 7986
 
7987
+            - `id=<secret id>`
7988
+            - `label=<key> or label=<key>=value`
7989
+            - `name=<secret name>`
7987 7990
             - `names=<secret name>`
7988 7991
       tags: ["Secret"]
7989 7992
   /secrets/create:
... ...
@@ -5,6 +5,7 @@ import (
5 5
 	"github.com/docker/docker/cli"
6 6
 	"github.com/docker/docker/cli/command"
7 7
 	"github.com/docker/docker/cli/command/formatter"
8
+	"github.com/docker/docker/opts"
8 9
 	"github.com/spf13/cobra"
9 10
 	"golang.org/x/net/context"
10 11
 )
... ...
@@ -12,10 +13,11 @@ import (
12 12
 type listOptions struct {
13 13
 	quiet  bool
14 14
 	format string
15
+	filter opts.FilterOpt
15 16
 }
16 17
 
17 18
 func newSecretListCommand(dockerCli *command.DockerCli) *cobra.Command {
18
-	opts := listOptions{}
19
+	opts := listOptions{filter: opts.NewFilterOpt()}
19 20
 
20 21
 	cmd := &cobra.Command{
21 22
 		Use:     "ls [OPTIONS]",
... ...
@@ -30,6 +32,7 @@ func newSecretListCommand(dockerCli *command.DockerCli) *cobra.Command {
30 30
 	flags := cmd.Flags()
31 31
 	flags.BoolVarP(&opts.quiet, "quiet", "q", false, "Only display IDs")
32 32
 	flags.StringVarP(&opts.format, "format", "", "", "Pretty-print secrets using a Go template")
33
+	flags.VarP(&opts.filter, "filter", "f", "Filter output based on conditions provided")
33 34
 
34 35
 	return cmd
35 36
 }
... ...
@@ -38,7 +41,7 @@ func runSecretList(dockerCli *command.DockerCli, opts listOptions) error {
38 38
 	client := dockerCli.Client()
39 39
 	ctx := context.Background()
40 40
 
41
-	secrets, err := client.SecretList(ctx, types.SecretListOptions{})
41
+	secrets, err := client.SecretList(ctx, types.SecretListOptions{Filters: opts.filter.Value()})
42 42
 	if err != nil {
43 43
 		return err
44 44
 	}
... ...
@@ -26,7 +26,7 @@ func ParseSecrets(client client.SecretAPIClient, requestedSecrets []*swarmtypes.
26 26
 
27 27
 	args := filters.NewArgs()
28 28
 	for _, s := range secretRefs {
29
-		args.Add("names", s.SecretName)
29
+		args.Add("name", s.SecretName)
30 30
 	}
31 31
 
32 32
 	secrets, err := client.SecretList(ctx, types.SecretListOptions{
33 33
new file mode 100644
... ...
@@ -0,0 +1,57 @@
0
+package cluster
1
+
2
+import (
3
+	"testing"
4
+
5
+	"github.com/docker/docker/api/types/filters"
6
+)
7
+
8
+func TestNewListSecretsFilters(t *testing.T) {
9
+	validNameFilter := filters.NewArgs()
10
+	validNameFilter.Add("name", "test_name")
11
+
12
+	validIDFilter := filters.NewArgs()
13
+	validIDFilter.Add("id", "7c9009d6720f6de3b492f5")
14
+
15
+	validLabelFilter := filters.NewArgs()
16
+	validLabelFilter.Add("label", "type=test")
17
+	validLabelFilter.Add("label", "storage=ssd")
18
+	validLabelFilter.Add("label", "memory")
19
+
20
+	validNamesFilter := filters.NewArgs()
21
+	validNamesFilter.Add("names", "test_name")
22
+
23
+	validAllFilter := filters.NewArgs()
24
+	validAllFilter.Add("name", "nodeName")
25
+	validAllFilter.Add("id", "7c9009d6720f6de3b492f5")
26
+	validAllFilter.Add("label", "type=test")
27
+	validAllFilter.Add("label", "memory")
28
+	validAllFilter.Add("names", "test_name")
29
+
30
+	validFilters := []filters.Args{
31
+		validNameFilter,
32
+		validIDFilter,
33
+		validLabelFilter,
34
+		validNamesFilter,
35
+		validAllFilter,
36
+	}
37
+
38
+	invalidTypeFilter := filters.NewArgs()
39
+	invalidTypeFilter.Add("nonexist", "aaaa")
40
+
41
+	invalidFilters := []filters.Args{
42
+		invalidTypeFilter,
43
+	}
44
+
45
+	for _, filter := range validFilters {
46
+		if _, err := newListSecretsFilters(filter); err != nil {
47
+			t.Fatalf("Should get no error, got %v", err)
48
+		}
49
+	}
50
+
51
+	for _, filter := range invalidFilters {
52
+		if _, err := newListSecretsFilters(filter); err == nil {
53
+			t.Fatalf("Should get an error for filter %s, while got nil", filter)
54
+		}
55
+	}
56
+}
... ...
@@ -24,8 +24,10 @@ Aliases:
24 24
   ls, list
25 25
 
26 26
 Options:
27
-  -q, --quiet          Only display IDs
28
-  -format string       Pretty-print secrets using a Go template
27
+  -f, --filter filter   Filter output based on conditions provided
28
+      --format string   Pretty-print secrets using a Go template
29
+      --help            Print usage
30
+  -q, --quiet           Only display IDs
29 31
 ```
30 32
 
31 33
 ## Description
... ...
@@ -39,8 +41,70 @@ For detailed information about using secrets, refer to [manage sensitive data wi
39 39
 ```bash
40 40
 $ docker secret ls
41 41
 
42
-ID                          NAME                CREATED             UPDATED
43
-eo7jnzguqgtpdah3cm5srfb97   my_secret           11 minutes ago      11 minutes ago
42
+ID                          NAME                        CREATED             UPDATED
43
+6697bflskwj1998km1gnnjr38   q5s5570vtvnimefos1fyeo2u2   6 weeks ago         6 weeks ago
44
+9u9hk4br2ej0wgngkga6rp4hq   my_secret                   5 weeks ago         5 weeks ago
45
+mem02h8n73mybpgqjf0kfi1n0   test_secret                 3 seconds ago       3 seconds ago
46
+```
47
+
48
+### Filtering
49
+
50
+The filtering flag (`-f` or `--filter`) format is a `key=value` pair. If there is more
51
+than one filter, then pass multiple flags (e.g., `--filter "foo=bar" --filter "bif=baz"`)
52
+
53
+The currently supported filters are:
54
+
55
+* [id](secret_ls.md#id) (secret's ID)
56
+* [label](secret_ls.md#label) (`label=<key>` or `label=<key>=<value>`)
57
+* [name](secret_ls.md#name) (secret's name)
58
+
59
+#### id
60
+
61
+The `id` filter matches all or prefix of a secret's id.
62
+
63
+```bash
64
+$ docker secret ls -f "id=6697bflskwj1998km1gnnjr38"
65
+
66
+ID                          NAME                        CREATED             UPDATED
67
+6697bflskwj1998km1gnnjr38   q5s5570vtvnimefos1fyeo2u2   6 weeks ago         6 weeks ago
68
+```
69
+
70
+#### label
71
+
72
+The `label` filter matches secrets based on the presence of a `label` alone or
73
+a `label` and a value.
74
+
75
+The following filter matches all secrets with a `project` label regardless of
76
+its value:
77
+
78
+```bash
79
+$ docker secret ls --filter label=project
80
+
81
+ID                          NAME                        CREATED             UPDATED
82
+mem02h8n73mybpgqjf0kfi1n0   test_secret                 About an hour ago   About an hour ago
83
+```
84
+
85
+The following filter matches only services with the `project` label with the
86
+`project-a` value.
87
+
88
+```bash
89
+$ docker service ls --filter label=project=test
90
+
91
+ID                          NAME                        CREATED             UPDATED
92
+mem02h8n73mybpgqjf0kfi1n0   test_secret                 About an hour ago   About an hour ago
93
+```
94
+
95
+#### name
96
+
97
+The `name` filter matches on all or prefix of a secret's name.
98
+
99
+The following filter matches secret with a name containing a prefix of `test`.
100
+
101
+```bash
102
+$ docker secret ls --filter name=test_secret
103
+
104
+ID                          NAME                        CREATED             UPDATED
105
+mem02h8n73mybpgqjf0kfi1n0   test_secret                 About an hour ago   About an hour ago
44 106
 ```
45 107
 
46 108
 ### Format the output
47 109
new file mode 100644
... ...
@@ -0,0 +1,125 @@
0
+// +build !windows
1
+
2
+package main
3
+
4
+import (
5
+	"strings"
6
+
7
+	"github.com/docker/docker/api/types/swarm"
8
+	"github.com/docker/docker/integration-cli/checker"
9
+	"github.com/go-check/check"
10
+)
11
+
12
+func (s *DockerSwarmSuite) TestSecretList(c *check.C) {
13
+	d := s.AddDaemon(c, true, true)
14
+
15
+	testName0 := "test0"
16
+	testName1 := "test1"
17
+
18
+	// create secret test0
19
+	id0 := d.CreateSecret(c, swarm.SecretSpec{
20
+		Annotations: swarm.Annotations{
21
+			Name:   testName0,
22
+			Labels: map[string]string{"type": "test"},
23
+		},
24
+		Data: []byte("TESTINGDATA0"),
25
+	})
26
+	c.Assert(id0, checker.Not(checker.Equals), "", check.Commentf("secrets: %s", id0))
27
+
28
+	secret := d.GetSecret(c, id0)
29
+	c.Assert(secret.Spec.Name, checker.Equals, testName0)
30
+
31
+	// create secret test1
32
+	id1 := d.CreateSecret(c, swarm.SecretSpec{
33
+		Annotations: swarm.Annotations{
34
+			Name:   testName1,
35
+			Labels: map[string]string{"type": "production"},
36
+		},
37
+		Data: []byte("TESTINGDATA1"),
38
+	})
39
+	c.Assert(id1, checker.Not(checker.Equals), "", check.Commentf("secrets: %s", id1))
40
+
41
+	secret = d.GetSecret(c, id1)
42
+	c.Assert(secret.Spec.Name, checker.Equals, testName1)
43
+
44
+	// test by command `docker secret ls`
45
+	out, err := d.Cmd("secret", "ls")
46
+	c.Assert(err, checker.IsNil, check.Commentf(out))
47
+	c.Assert(strings.TrimSpace(out), checker.Contains, testName0)
48
+	c.Assert(strings.TrimSpace(out), checker.Contains, testName1)
49
+
50
+	// test filter by name `docker secret ls --filter name=xxx`
51
+	args := []string{
52
+		"secret",
53
+		"ls",
54
+		"--filter",
55
+		"name=test0",
56
+	}
57
+	out, err = d.Cmd(args...)
58
+	c.Assert(err, checker.IsNil, check.Commentf(out))
59
+
60
+	c.Assert(strings.TrimSpace(out), checker.Contains, testName0)
61
+	c.Assert(strings.TrimSpace(out), checker.Not(checker.Contains), testName1)
62
+
63
+	// test filter by id `docker secret ls --filter id=xxx`
64
+	args = []string{
65
+		"secret",
66
+		"ls",
67
+		"--filter",
68
+		"id=" + id1,
69
+	}
70
+	out, err = d.Cmd(args...)
71
+	c.Assert(err, checker.IsNil, check.Commentf(out))
72
+
73
+	c.Assert(strings.TrimSpace(out), checker.Not(checker.Contains), testName0)
74
+	c.Assert(strings.TrimSpace(out), checker.Contains, testName1)
75
+
76
+	// test filter by label `docker secret ls --filter label=xxx`
77
+	args = []string{
78
+		"secret",
79
+		"ls",
80
+		"--filter",
81
+		"label=type",
82
+	}
83
+	out, err = d.Cmd(args...)
84
+	c.Assert(err, checker.IsNil, check.Commentf(out))
85
+
86
+	c.Assert(strings.TrimSpace(out), checker.Contains, testName0)
87
+	c.Assert(strings.TrimSpace(out), checker.Contains, testName1)
88
+
89
+	args = []string{
90
+		"secret",
91
+		"ls",
92
+		"--filter",
93
+		"label=type=test",
94
+	}
95
+	out, err = d.Cmd(args...)
96
+	c.Assert(err, checker.IsNil, check.Commentf(out))
97
+
98
+	c.Assert(strings.TrimSpace(out), checker.Contains, testName0)
99
+	c.Assert(strings.TrimSpace(out), checker.Not(checker.Contains), testName1)
100
+
101
+	args = []string{
102
+		"secret",
103
+		"ls",
104
+		"--filter",
105
+		"label=type=production",
106
+	}
107
+	out, err = d.Cmd(args...)
108
+	c.Assert(err, checker.IsNil, check.Commentf(out))
109
+
110
+	c.Assert(strings.TrimSpace(out), checker.Not(checker.Contains), testName0)
111
+	c.Assert(strings.TrimSpace(out), checker.Contains, testName1)
112
+
113
+	// test invalid filter `docker secret ls --filter noexisttype=xxx`
114
+	args = []string{
115
+		"secret",
116
+		"ls",
117
+		"--filter",
118
+		"noexisttype=test0",
119
+	}
120
+	out, err = d.Cmd(args...)
121
+	c.Assert(err, checker.NotNil, check.Commentf(out))
122
+
123
+	c.Assert(strings.TrimSpace(out), checker.Contains, "Error response from daemon: Invalid filter 'noexisttype'")
124
+}