diff --git a/cli/command/service/opts_test.go b/cli/command/service/opts_test.go index 3df3a4fd5d..85de3ae88a 100644 --- a/cli/command/service/opts_test.go +++ b/cli/command/service/opts_test.go @@ -1,13 +1,11 @@ package service import ( - "os" "reflect" "testing" "time" "github.com/docker/docker/api/types/container" - "github.com/docker/docker/opts" "github.com/docker/docker/pkg/testutil/assert" ) @@ -106,47 +104,3 @@ func TestHealthCheckOptionsToHealthConfigConflict(t *testing.T) { _, err := opt.toHealthConfig() assert.Error(t, err, "--no-healthcheck conflicts with --health-* options") } - -func TestSecretOptionsSimple(t *testing.T) { - var opt opts.SecretOpt - - testCase := "source=foo,target=testing" - assert.NilError(t, opt.Set(testCase)) - - reqs := opt.Value() - assert.Equal(t, len(reqs), 1) - req := reqs[0] - assert.Equal(t, req.Source, "foo") - assert.Equal(t, req.Target, "testing") -} - -func TestSecretOptionsCustomUidGid(t *testing.T) { - var opt opts.SecretOpt - - testCase := "source=foo,target=testing,uid=1000,gid=1001" - assert.NilError(t, opt.Set(testCase)) - - reqs := opt.Value() - assert.Equal(t, len(reqs), 1) - req := reqs[0] - assert.Equal(t, req.Source, "foo") - assert.Equal(t, req.Target, "testing") - assert.Equal(t, req.UID, "1000") - assert.Equal(t, req.GID, "1001") -} - -func TestSecretOptionsCustomMode(t *testing.T) { - var opt opts.SecretOpt - - testCase := "source=foo,target=testing,uid=1000,gid=1001,mode=0444" - assert.NilError(t, opt.Set(testCase)) - - reqs := opt.Value() - assert.Equal(t, len(reqs), 1) - req := reqs[0] - assert.Equal(t, req.Source, "foo") - assert.Equal(t, req.Target, "testing") - assert.Equal(t, req.UID, "1000") - assert.Equal(t, req.GID, "1001") - assert.Equal(t, req.Mode, os.FileMode(0444)) -} diff --git a/docs/reference/commandline/service_create.md b/docs/reference/commandline/service_create.md index e98c2cc8ae..8f33bf8c33 100644 --- a/docs/reference/commandline/service_create.md +++ b/docs/reference/commandline/service_create.md @@ -122,11 +122,22 @@ ID NAME MODE REPLICAS IMAGE ### Create a service with secrets Use the `--secret` flag to give a container access to a -[secret](secret_create.md). The following command will create a service -with two secrets named `ssh-key` and `app-key`: +[secret](secret_create.md). + +Create a service specifying a secret: ```bash -$ docker service create --name redis --secret source=ssh-key,target=ssh --secret source=app-key,target=app,uid=1000,gid=1001,mode=0400 redis:3.0.6 +$ docker service create --name redis --secret secret.json redis:3.0.6 +4cdgfyky7ozwh3htjfw0d12qv +``` + +Create a service specifying the secret, target, user/group ID and mode: + +```bash +$ docker service create --name redis \ + --secret source=ssh-key,target=ssh \ + --secret source=app-key,target=app,uid=1000,gid=1001,mode=0400 \ + redis:3.0.6 4cdgfyky7ozwh3htjfw0d12qv ``` diff --git a/integration-cli/docker_cli_service_create_test.go b/integration-cli/docker_cli_service_create_test.go index bac23b0eb1..7af6a5c6de 100644 --- a/integration-cli/docker_cli_service_create_test.go +++ b/integration-cli/docker_cli_service_create_test.go @@ -45,7 +45,37 @@ func (s *DockerSwarmSuite) TestServiceCreateMountVolume(c *check.C) { c.Assert(mounts[0].RW, checker.Equals, true) } -func (s *DockerSwarmSuite) TestServiceCreateWithSecret(c *check.C) { +func (s *DockerSwarmSuite) TestServiceCreateWithSecretSimple(c *check.C) { + d := s.AddDaemon(c, true, true) + + serviceName := "test-service-secret" + testName := "test_secret" + id := d.createSecret(c, swarm.SecretSpec{ + swarm.Annotations{ + Name: testName, + }, + []byte("TESTINGDATA"), + }) + c.Assert(id, checker.Not(checker.Equals), "", check.Commentf("secrets: %s", id)) + + out, err := d.Cmd("service", "create", "--name", serviceName, "--secret", testName, "busybox", "top") + c.Assert(err, checker.IsNil, check.Commentf(out)) + + out, err = d.Cmd("service", "inspect", "--format", "{{ json .Spec.TaskTemplate.ContainerSpec.Secrets }}", serviceName) + c.Assert(err, checker.IsNil) + + var refs []swarm.SecretReference + c.Assert(json.Unmarshal([]byte(out), &refs), checker.IsNil) + c.Assert(refs, checker.HasLen, 1) + + c.Assert(refs[0].SecretName, checker.Equals, testName) + c.Assert(refs[0].Target, checker.Not(checker.IsNil)) + c.Assert(refs[0].Target.Name, checker.Equals, testName) + c.Assert(refs[0].Target.UID, checker.Equals, "0") + c.Assert(refs[0].Target.GID, checker.Equals, "0") +} + +func (s *DockerSwarmSuite) TestServiceCreateWithSecretSourceTarget(c *check.C) { d := s.AddDaemon(c, true, true) serviceName := "test-service-secret" diff --git a/opts/secret.go b/opts/secret.go index 34ed42a680..9475d9f506 100644 --- a/opts/secret.go +++ b/opts/secret.go @@ -32,6 +32,14 @@ func (o *SecretOpt) Set(value string) error { Mode: 0444, } + // support a simple syntax of --secret foo + if len(fields) == 1 { + options.Source = fields[0] + options.Target = fields[0] + o.values = append(o.values, options) + return nil + } + for _, field := range fields { parts := strings.SplitN(field, "=", 2) key := strings.ToLower(parts[0]) @@ -62,7 +70,11 @@ func (o *SecretOpt) Set(value string) error { options.Mode = os.FileMode(m) default: - return fmt.Errorf("invalid field in secret request: %s", key) + if len(fields) == 1 && value == "" { + + } else { + return fmt.Errorf("invalid field in secret request: %s", key) + } } } diff --git a/opts/secret_test.go b/opts/secret_test.go new file mode 100644 index 0000000000..ce4418a0bc --- /dev/null +++ b/opts/secret_test.go @@ -0,0 +1,67 @@ +package opts + +import ( + "os" + "testing" + + "github.com/docker/docker/pkg/testutil/assert" +) + +func TestSecretOptionsSimple(t *testing.T) { + var opt SecretOpt + + testCase := "app-secret" + assert.NilError(t, opt.Set(testCase)) + + reqs := opt.Value() + assert.Equal(t, len(reqs), 1) + req := reqs[0] + assert.Equal(t, req.Source, "app-secret") + assert.Equal(t, req.Target, "app-secret") + assert.Equal(t, req.UID, "0") + assert.Equal(t, req.GID, "0") +} + +func TestSecretOptionsSourceTarget(t *testing.T) { + var opt SecretOpt + + testCase := "source=foo,target=testing" + assert.NilError(t, opt.Set(testCase)) + + reqs := opt.Value() + assert.Equal(t, len(reqs), 1) + req := reqs[0] + assert.Equal(t, req.Source, "foo") + assert.Equal(t, req.Target, "testing") +} + +func TestSecretOptionsCustomUidGid(t *testing.T) { + var opt SecretOpt + + testCase := "source=foo,target=testing,uid=1000,gid=1001" + assert.NilError(t, opt.Set(testCase)) + + reqs := opt.Value() + assert.Equal(t, len(reqs), 1) + req := reqs[0] + assert.Equal(t, req.Source, "foo") + assert.Equal(t, req.Target, "testing") + assert.Equal(t, req.UID, "1000") + assert.Equal(t, req.GID, "1001") +} + +func TestSecretOptionsCustomMode(t *testing.T) { + var opt SecretOpt + + testCase := "source=foo,target=testing,uid=1000,gid=1001,mode=0444" + assert.NilError(t, opt.Set(testCase)) + + reqs := opt.Value() + assert.Equal(t, len(reqs), 1) + req := reqs[0] + assert.Equal(t, req.Source, "foo") + assert.Equal(t, req.Target, "testing") + assert.Equal(t, req.UID, "1000") + assert.Equal(t, req.GID, "1001") + assert.Equal(t, req.Mode, os.FileMode(0444)) +}