Browse code

Add integration test for stack deploy with secrets.

Signed-off-by: Daniel Nephin <dnephin@docker.com>
(cherry picked from commit 6ec84ef76df30663d5728f903b314f4486587135)
Signed-off-by: Victor Vieux <vieux@docker.com>

Daniel Nephin authored on 2017/01/14 01:26:29
Showing 6 changed files
... ...
@@ -11,7 +11,8 @@ import (
11 11
 	"golang.org/x/net/context"
12 12
 )
13 13
 
14
-func getSecretsByNameOrIDPrefixes(ctx context.Context, client client.APIClient, terms []string) ([]swarm.Secret, error) {
14
+// GetSecretsByNameOrIDPrefixes returns secrets given a list of ids or names
15
+func GetSecretsByNameOrIDPrefixes(ctx context.Context, client client.APIClient, terms []string) ([]swarm.Secret, error) {
15 16
 	args := filters.NewArgs()
16 17
 	for _, n := range terms {
17 18
 		args.Add("names", n)
... ...
@@ -24,7 +25,7 @@ func getSecretsByNameOrIDPrefixes(ctx context.Context, client client.APIClient,
24 24
 }
25 25
 
26 26
 func getCliRequestedSecretIDs(ctx context.Context, client client.APIClient, terms []string) ([]string, error) {
27
-	secrets, err := getSecretsByNameOrIDPrefixes(ctx, client, terms)
27
+	secrets, err := GetSecretsByNameOrIDPrefixes(ctx, client, terms)
28 28
 	if err != nil {
29 29
 		return nil, err
30 30
 	}
... ...
@@ -1,7 +1,6 @@
1 1
 package stack
2 2
 
3 3
 import (
4
-	"errors"
5 4
 	"fmt"
6 5
 	"io/ioutil"
7 6
 	"os"
... ...
@@ -12,10 +11,12 @@ import (
12 12
 	"github.com/docker/docker/api/types/swarm"
13 13
 	"github.com/docker/docker/cli"
14 14
 	"github.com/docker/docker/cli/command"
15
+	secretcli "github.com/docker/docker/cli/command/secret"
15 16
 	"github.com/docker/docker/cli/compose/convert"
16 17
 	"github.com/docker/docker/cli/compose/loader"
17 18
 	composetypes "github.com/docker/docker/cli/compose/types"
18 19
 	dockerclient "github.com/docker/docker/client"
20
+	"github.com/pkg/errors"
19 21
 	"github.com/spf13/cobra"
20 22
 	"golang.org/x/net/context"
21 23
 )
... ...
@@ -225,9 +226,22 @@ func createSecrets(
225 225
 ) error {
226 226
 	client := dockerCli.Client()
227 227
 
228
-	for _, secret := range secrets {
229
-		fmt.Fprintf(dockerCli.Out(), "Creating secret %s\n", secret.Name)
230
-		_, err := client.SecretCreate(ctx, secret)
228
+	for _, secretSpec := range secrets {
229
+		// TODO: fix this after https://github.com/docker/docker/pull/29218
230
+		secrets, err := secretcli.GetSecretsByNameOrIDPrefixes(ctx, client, []string{secretSpec.Name})
231
+		switch {
232
+		case err != nil:
233
+			return err
234
+		case len(secrets) > 1:
235
+			return errors.Errorf("ambiguous secret name: %s", secretSpec.Name)
236
+		case len(secrets) == 0:
237
+			fmt.Fprintf(dockerCli.Out(), "Creating secret %s\n", secretSpec.Name)
238
+			_, err = client.SecretCreate(ctx, secretSpec)
239
+		default:
240
+			secret := secrets[0]
241
+			// Update secret to ensure that the local data hasn't changed
242
+			err = client.SecretUpdate(ctx, secret.ID, secret.Meta.Version, secretSpec)
243
+		}
231 244
 		if err != nil {
232 245
 			return err
233 246
 		}
... ...
@@ -7,6 +7,7 @@ import (
7 7
 	"github.com/docker/docker/api/types/network"
8 8
 	composetypes "github.com/docker/docker/cli/compose/types"
9 9
 	"github.com/docker/docker/pkg/testutil/assert"
10
+	"github.com/docker/docker/pkg/testutil/tempfile"
10 11
 )
11 12
 
12 13
 func TestNamespaceScope(t *testing.T) {
... ...
@@ -88,3 +89,34 @@ func TestNetworks(t *testing.T) {
88 88
 	assert.DeepEqual(t, networks, expected)
89 89
 	assert.DeepEqual(t, externals, []string{"special"})
90 90
 }
91
+
92
+func TestSecrets(t *testing.T) {
93
+	namespace := Namespace{name: "foo"}
94
+
95
+	secretText := "this is the first secret"
96
+	secretFile := tempfile.NewTempFile(t, "convert-secrets", secretText)
97
+	defer secretFile.Remove()
98
+
99
+	source := map[string]composetypes.SecretConfig{
100
+		"one": {
101
+			File:   secretFile.Name(),
102
+			Labels: map[string]string{"monster": "mash"},
103
+		},
104
+		"ext": {
105
+			External: composetypes.External{
106
+				External: true,
107
+			},
108
+		},
109
+	}
110
+
111
+	specs, err := Secrets(namespace, source)
112
+	assert.NilError(t, err)
113
+	assert.Equal(t, len(specs), 1)
114
+	secret := specs[0]
115
+	assert.Equal(t, secret.Name, "foo_one")
116
+	assert.DeepEqual(t, secret.Labels, map[string]string{
117
+		"monster":      "mash",
118
+		LabelNamespace: "foo",
119
+	})
120
+	assert.DeepEqual(t, secret.Data, []byte(secretText))
121
+}
... ...
@@ -1,15 +1,18 @@
1 1
 package main
2 2
 
3 3
 import (
4
+	"encoding/json"
4 5
 	"io/ioutil"
5 6
 	"os"
7
+	"sort"
6 8
 
9
+	"github.com/docker/docker/api/types/swarm"
10
+	"github.com/docker/docker/integration-cli/checker"
7 11
 	"github.com/docker/docker/pkg/integration/checker"
8 12
 	"github.com/go-check/check"
9 13
 )
10 14
 
11 15
 func (s *DockerSwarmSuite) TestStackRemove(c *check.C) {
12
-	testRequires(c, ExperimentalDaemon)
13 16
 	d := s.AddDaemon(c, true, true)
14 17
 
15 18
 	stackArgs := append([]string{"stack", "remove", "UNKNOWN_STACK"})
... ...
@@ -20,7 +23,6 @@ func (s *DockerSwarmSuite) TestStackRemove(c *check.C) {
20 20
 }
21 21
 
22 22
 func (s *DockerSwarmSuite) TestStackTasks(c *check.C) {
23
-	testRequires(c, ExperimentalDaemon)
24 23
 	d := s.AddDaemon(c, true, true)
25 24
 
26 25
 	stackArgs := append([]string{"stack", "ps", "UNKNOWN_STACK"})
... ...
@@ -31,7 +33,6 @@ func (s *DockerSwarmSuite) TestStackTasks(c *check.C) {
31 31
 }
32 32
 
33 33
 func (s *DockerSwarmSuite) TestStackServices(c *check.C) {
34
-	testRequires(c, ExperimentalDaemon)
35 34
 	d := s.AddDaemon(c, true, true)
36 35
 
37 36
 	stackArgs := append([]string{"stack", "services", "UNKNOWN_STACK"})
... ...
@@ -42,7 +43,6 @@ func (s *DockerSwarmSuite) TestStackServices(c *check.C) {
42 42
 }
43 43
 
44 44
 func (s *DockerSwarmSuite) TestStackDeployComposeFile(c *check.C) {
45
-	testRequires(c, ExperimentalDaemon)
46 45
 	d := s.AddDaemon(c, true, true)
47 46
 
48 47
 	testStackName := "testdeploy"
... ...
@@ -65,6 +65,43 @@ func (s *DockerSwarmSuite) TestStackDeployComposeFile(c *check.C) {
65 65
 	c.Assert(out, check.Equals, "NAME  SERVICES\n")
66 66
 }
67 67
 
68
+func (s *DockerSwarmSuite) TestStackDeployWithSecretsTwice(c *check.C) {
69
+	d := s.AddDaemon(c, true, true)
70
+
71
+	testStackName := "testdeploy"
72
+	stackArgs := []string{
73
+		"stack", "deploy",
74
+		"--compose-file", "fixtures/deploy/secrets.yaml",
75
+		testStackName,
76
+	}
77
+	out, err := d.Cmd(stackArgs...)
78
+	c.Assert(err, checker.IsNil, check.Commentf(out))
79
+
80
+	out, err = d.Cmd("service", "inspect", "--format", "{{ json .Spec.TaskTemplate.ContainerSpec.Secrets }}", "testdeploy_web")
81
+	c.Assert(err, checker.IsNil)
82
+
83
+	var refs []swarm.SecretReference
84
+	c.Assert(json.Unmarshal([]byte(out), &refs), checker.IsNil)
85
+	c.Assert(refs, checker.HasLen, 2)
86
+
87
+	sort.Sort(sortSecrets(refs))
88
+	c.Assert(refs[0].SecretName, checker.Equals, "testdeploy_special")
89
+	c.Assert(refs[0].File.Name, checker.Equals, "special")
90
+	c.Assert(refs[1].SecretName, checker.Equals, "testdeploy_super")
91
+	c.Assert(refs[1].File.Name, checker.Equals, "foo.txt")
92
+	c.Assert(refs[1].File.Mode, checker.Equals, os.FileMode(0400))
93
+
94
+	// Deploy again to ensure there are no errors when secret hasn't changed
95
+	out, err = d.Cmd(stackArgs...)
96
+	c.Assert(err, checker.IsNil, check.Commentf(out))
97
+}
98
+
99
+type sortSecrets []swarm.SecretReference
100
+
101
+func (s sortSecrets) Len() int           { return len(s) }
102
+func (s sortSecrets) Swap(i, j int)      { s[i], s[j] = s[j], s[i] }
103
+func (s sortSecrets) Less(i, j int) bool { return s[i].SecretName < s[j].SecretName }
104
+
68 105
 // testDAB is the DAB JSON used for testing.
69 106
 // TODO: Use template/text and substitute "Image" with the result of
70 107
 // `docker inspect --format '{{index .RepoDigests 0}}' busybox:latest`
71 108
new file mode 100644
... ...
@@ -0,0 +1,16 @@
0
+
1
+version: "3.1"
2
+services:
3
+  web:
4
+    image: busybox@sha256:e4f93f6ed15a0cdd342f5aae387886fba0ab98af0a102da6276eaf24d6e6ade0
5
+    command: top
6
+    secrets:
7
+      - special
8
+      - source: super
9
+        target: foo.txt
10
+        mode: 0400
11
+secrets:
12
+  special:
13
+    file: fixtures/secrets/default
14
+  super:
15
+    file: fixtures/secrets/default
0 16
new file mode 100644
... ...
@@ -0,0 +1 @@
0
+this is the secret