Przeglądaj źródła

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 8 lat temu
rodzic
commit
1fed3349fb

+ 3 - 2
cli/command/secret/utils.go

@@ -11,7 +11,8 @@ import (
 	"golang.org/x/net/context"
 )
 
-func getSecretsByNameOrIDPrefixes(ctx context.Context, client client.APIClient, terms []string) ([]swarm.Secret, error) {
+// GetSecretsByNameOrIDPrefixes returns secrets given a list of ids or names
+func GetSecretsByNameOrIDPrefixes(ctx context.Context, client client.APIClient, terms []string) ([]swarm.Secret, error) {
 	args := filters.NewArgs()
 	for _, n := range terms {
 		args.Add("names", n)
@@ -24,7 +25,7 @@ func getSecretsByNameOrIDPrefixes(ctx context.Context, client client.APIClient,
 }
 
 func getCliRequestedSecretIDs(ctx context.Context, client client.APIClient, terms []string) ([]string, error) {
-	secrets, err := getSecretsByNameOrIDPrefixes(ctx, client, terms)
+	secrets, err := GetSecretsByNameOrIDPrefixes(ctx, client, terms)
 	if err != nil {
 		return nil, err
 	}

+ 18 - 4
cli/command/stack/deploy.go

@@ -1,7 +1,6 @@
 package stack
 
 import (
-	"errors"
 	"fmt"
 	"io/ioutil"
 	"os"
@@ -12,10 +11,12 @@ import (
 	"github.com/docker/docker/api/types/swarm"
 	"github.com/docker/docker/cli"
 	"github.com/docker/docker/cli/command"
+	secretcli "github.com/docker/docker/cli/command/secret"
 	"github.com/docker/docker/cli/compose/convert"
 	"github.com/docker/docker/cli/compose/loader"
 	composetypes "github.com/docker/docker/cli/compose/types"
 	dockerclient "github.com/docker/docker/client"
+	"github.com/pkg/errors"
 	"github.com/spf13/cobra"
 	"golang.org/x/net/context"
 )
@@ -225,9 +226,22 @@ func createSecrets(
 ) error {
 	client := dockerCli.Client()
 
-	for _, secret := range secrets {
-		fmt.Fprintf(dockerCli.Out(), "Creating secret %s\n", secret.Name)
-		_, err := client.SecretCreate(ctx, secret)
+	for _, secretSpec := range secrets {
+		// TODO: fix this after https://github.com/docker/docker/pull/29218
+		secrets, err := secretcli.GetSecretsByNameOrIDPrefixes(ctx, client, []string{secretSpec.Name})
+		switch {
+		case err != nil:
+			return err
+		case len(secrets) > 1:
+			return errors.Errorf("ambiguous secret name: %s", secretSpec.Name)
+		case len(secrets) == 0:
+			fmt.Fprintf(dockerCli.Out(), "Creating secret %s\n", secretSpec.Name)
+			_, err = client.SecretCreate(ctx, secretSpec)
+		default:
+			secret := secrets[0]
+			// Update secret to ensure that the local data hasn't changed
+			err = client.SecretUpdate(ctx, secret.ID, secret.Meta.Version, secretSpec)
+		}
 		if err != nil {
 			return err
 		}

+ 32 - 0
cli/compose/convert/compose_test.go

@@ -7,6 +7,7 @@ import (
 	"github.com/docker/docker/api/types/network"
 	composetypes "github.com/docker/docker/cli/compose/types"
 	"github.com/docker/docker/pkg/testutil/assert"
+	"github.com/docker/docker/pkg/testutil/tempfile"
 )
 
 func TestNamespaceScope(t *testing.T) {
@@ -88,3 +89,34 @@ func TestNetworks(t *testing.T) {
 	assert.DeepEqual(t, networks, expected)
 	assert.DeepEqual(t, externals, []string{"special"})
 }
+
+func TestSecrets(t *testing.T) {
+	namespace := Namespace{name: "foo"}
+
+	secretText := "this is the first secret"
+	secretFile := tempfile.NewTempFile(t, "convert-secrets", secretText)
+	defer secretFile.Remove()
+
+	source := map[string]composetypes.SecretConfig{
+		"one": {
+			File:   secretFile.Name(),
+			Labels: map[string]string{"monster": "mash"},
+		},
+		"ext": {
+			External: composetypes.External{
+				External: true,
+			},
+		},
+	}
+
+	specs, err := Secrets(namespace, source)
+	assert.NilError(t, err)
+	assert.Equal(t, len(specs), 1)
+	secret := specs[0]
+	assert.Equal(t, secret.Name, "foo_one")
+	assert.DeepEqual(t, secret.Labels, map[string]string{
+		"monster":      "mash",
+		LabelNamespace: "foo",
+	})
+	assert.DeepEqual(t, secret.Data, []byte(secretText))
+}

+ 41 - 4
integration-cli/docker_cli_stack_test.go

@@ -1,15 +1,18 @@
 package main
 
 import (
+	"encoding/json"
 	"io/ioutil"
 	"os"
+	"sort"
 
+	"github.com/docker/docker/api/types/swarm"
+	"github.com/docker/docker/integration-cli/checker"
 	"github.com/docker/docker/pkg/integration/checker"
 	"github.com/go-check/check"
 )
 
 func (s *DockerSwarmSuite) TestStackRemove(c *check.C) {
-	testRequires(c, ExperimentalDaemon)
 	d := s.AddDaemon(c, true, true)
 
 	stackArgs := append([]string{"stack", "remove", "UNKNOWN_STACK"})
@@ -20,7 +23,6 @@ func (s *DockerSwarmSuite) TestStackRemove(c *check.C) {
 }
 
 func (s *DockerSwarmSuite) TestStackTasks(c *check.C) {
-	testRequires(c, ExperimentalDaemon)
 	d := s.AddDaemon(c, true, true)
 
 	stackArgs := append([]string{"stack", "ps", "UNKNOWN_STACK"})
@@ -31,7 +33,6 @@ func (s *DockerSwarmSuite) TestStackTasks(c *check.C) {
 }
 
 func (s *DockerSwarmSuite) TestStackServices(c *check.C) {
-	testRequires(c, ExperimentalDaemon)
 	d := s.AddDaemon(c, true, true)
 
 	stackArgs := append([]string{"stack", "services", "UNKNOWN_STACK"})
@@ -42,7 +43,6 @@ func (s *DockerSwarmSuite) TestStackServices(c *check.C) {
 }
 
 func (s *DockerSwarmSuite) TestStackDeployComposeFile(c *check.C) {
-	testRequires(c, ExperimentalDaemon)
 	d := s.AddDaemon(c, true, true)
 
 	testStackName := "testdeploy"
@@ -65,6 +65,43 @@ func (s *DockerSwarmSuite) TestStackDeployComposeFile(c *check.C) {
 	c.Assert(out, check.Equals, "NAME  SERVICES\n")
 }
 
+func (s *DockerSwarmSuite) TestStackDeployWithSecretsTwice(c *check.C) {
+	d := s.AddDaemon(c, true, true)
+
+	testStackName := "testdeploy"
+	stackArgs := []string{
+		"stack", "deploy",
+		"--compose-file", "fixtures/deploy/secrets.yaml",
+		testStackName,
+	}
+	out, err := d.Cmd(stackArgs...)
+	c.Assert(err, checker.IsNil, check.Commentf(out))
+
+	out, err = d.Cmd("service", "inspect", "--format", "{{ json .Spec.TaskTemplate.ContainerSpec.Secrets }}", "testdeploy_web")
+	c.Assert(err, checker.IsNil)
+
+	var refs []swarm.SecretReference
+	c.Assert(json.Unmarshal([]byte(out), &refs), checker.IsNil)
+	c.Assert(refs, checker.HasLen, 2)
+
+	sort.Sort(sortSecrets(refs))
+	c.Assert(refs[0].SecretName, checker.Equals, "testdeploy_special")
+	c.Assert(refs[0].File.Name, checker.Equals, "special")
+	c.Assert(refs[1].SecretName, checker.Equals, "testdeploy_super")
+	c.Assert(refs[1].File.Name, checker.Equals, "foo.txt")
+	c.Assert(refs[1].File.Mode, checker.Equals, os.FileMode(0400))
+
+	// Deploy again to ensure there are no errors when secret hasn't changed
+	out, err = d.Cmd(stackArgs...)
+	c.Assert(err, checker.IsNil, check.Commentf(out))
+}
+
+type sortSecrets []swarm.SecretReference
+
+func (s sortSecrets) Len() int           { return len(s) }
+func (s sortSecrets) Swap(i, j int)      { s[i], s[j] = s[j], s[i] }
+func (s sortSecrets) Less(i, j int) bool { return s[i].SecretName < s[j].SecretName }
+
 // testDAB is the DAB JSON used for testing.
 // TODO: Use template/text and substitute "Image" with the result of
 // `docker inspect --format '{{index .RepoDigests 0}}' busybox:latest`

+ 16 - 0
integration-cli/fixtures/deploy/secrets.yaml

@@ -0,0 +1,16 @@
+
+version: "3.1"
+services:
+  web:
+    image: busybox@sha256:e4f93f6ed15a0cdd342f5aae387886fba0ab98af0a102da6276eaf24d6e6ade0
+    command: top
+    secrets:
+      - special
+      - source: super
+        target: foo.txt
+        mode: 0400
+secrets:
+  special:
+    file: fixtures/secrets/default
+  super:
+    file: fixtures/secrets/default

+ 1 - 0
integration-cli/fixtures/secrets/default

@@ -0,0 +1 @@
+this is the secret