From c6f0b7f448fac4d037d00f944a7908c60c04dff2 Mon Sep 17 00:00:00 2001 From: Yong Tang Date: Sat, 19 Nov 2016 17:41:11 -0800 Subject: [PATCH] Add `--file` flag for `docker secret create` command This fix tries to address the issue raised in 28581 and 28927 where it is not possible to create a secret from a file (only through STDIN). This fix add a flag `--file` to `docker secret create` so that it is possible to create a secret from a file with: ``` docker secret create --file secret.in secret.name ``` or ``` echo TEST | docker secret create --file - secret.name ``` Related docs has been updated. An integration test has been added to cover the changes. This fix fixes 28581. This fix is related to 28927. Signed-off-by: Yong Tang --- cli/command/secret/create.go | 25 +++++++++++--- docs/reference/commandline/secret_create.md | 27 +++++++++++---- .../docker_cli_secret_create_test.go | 34 +++++++++++++++++++ 3 files changed, 75 insertions(+), 11 deletions(-) diff --git a/cli/command/secret/create.go b/cli/command/secret/create.go index 381a931415..5d4dc34d12 100644 --- a/cli/command/secret/create.go +++ b/cli/command/secret/create.go @@ -2,13 +2,14 @@ package secret import ( "fmt" + "io" "io/ioutil" - "os" "github.com/docker/docker/api/types/swarm" "github.com/docker/docker/cli" "github.com/docker/docker/cli/command" "github.com/docker/docker/opts" + "github.com/docker/docker/pkg/system" runconfigopts "github.com/docker/docker/runconfig/opts" "github.com/spf13/cobra" "golang.org/x/net/context" @@ -16,6 +17,7 @@ import ( type createOptions struct { name string + file string labels opts.ListOpts } @@ -26,7 +28,7 @@ func newSecretCreateCommand(dockerCli *command.DockerCli) *cobra.Command { cmd := &cobra.Command{ Use: "create [OPTIONS] SECRET", - Short: "Create a secret using stdin as content", + Short: "Create a secret from a file or STDIN as content", Args: cli.ExactArgs(1), RunE: func(cmd *cobra.Command, args []string) error { createOpts.name = args[0] @@ -35,6 +37,7 @@ func newSecretCreateCommand(dockerCli *command.DockerCli) *cobra.Command { } flags := cmd.Flags() flags.VarP(&createOpts.labels, "label", "l", "Secret labels") + flags.StringVarP(&createOpts.file, "file", "f", "", "Read from a file or STDIN ('-')") return cmd } @@ -43,9 +46,23 @@ func runSecretCreate(dockerCli *command.DockerCli, options createOptions) error client := dockerCli.Client() ctx := context.Background() - secretData, err := ioutil.ReadAll(os.Stdin) + if options.file == "" { + return fmt.Errorf("Please specify either a file name or STDIN ('-') with --file") + } + + var in io.Reader = dockerCli.In() + if options.file != "-" { + file, err := system.OpenSequential(options.file) + if err != nil { + return err + } + in = file + defer file.Close() + } + + secretData, err := ioutil.ReadAll(in) if err != nil { - return fmt.Errorf("Error reading content from STDIN: %v", err) + return fmt.Errorf("Error reading content from %q: %v", options.file, err) } spec := swarm.SecretSpec{ diff --git a/docs/reference/commandline/secret_create.md b/docs/reference/commandline/secret_create.md index 952ad26b2c..ef2641a91c 100644 --- a/docs/reference/commandline/secret_create.md +++ b/docs/reference/commandline/secret_create.md @@ -16,15 +16,17 @@ keywords: ["secret, create"] # secret create ```Markdown -Usage: docker secret create [OPTIONS] SECRET +Usage: docker secret create [OPTIONS] SECRET + +Create a secret from a file or STDIN as content -Create a secret using stdin as content Options: - --help Print usage - -l, --label list Secret labels (default []) + -f, --file string Read from a file or STDIN ('-') + --help Print usage + -l, --label list Secret labels (default []) ``` -Creates a secret using standard input for the secret content. You must run this +Creates a secret using standard input or from a file for the secret content. You must run this command on a manager node. ## Examples @@ -32,7 +34,18 @@ command on a manager node. ### Create a secret ```bash -$ cat secret.json | docker secret create secret.json +$ cat secret.json | docker secret create -f - secret.json +mhv17xfe3gh6xc4rij5orpfds + +$ docker secret ls +ID NAME CREATED UPDATED SIZE +mhv17xfe3gh6xc4rij5orpfds secret.json 2016-10-27 23:25:43.909181089 +0000 UTC 2016-10-27 23:25:43.909181089 +0000 UTC 1679 +``` + +### Create a secret with a file + +```bash +$ docker secret create --file secret.in secret.json mhv17xfe3gh6xc4rij5orpfds $ docker secret ls @@ -43,7 +56,7 @@ mhv17xfe3gh6xc4rij5orpfds secret.json 2016-10-27 23:25:43.90918108 ### Create a secret with labels ```bash -$ cat secret.json | docker secret create secret.json --label env=dev --label rev=20161102 +$ cat secret.json | docker secret create secret.json -f - --label env=dev --label rev=20161102 jtn7g6aukl5ky7nr9gvwafoxh $ docker secret inspect secret.json diff --git a/integration-cli/docker_cli_secret_create_test.go b/integration-cli/docker_cli_secret_create_test.go index fdfc793e16..38d292eec6 100644 --- a/integration-cli/docker_cli_secret_create_test.go +++ b/integration-cli/docker_cli_secret_create_test.go @@ -3,6 +3,10 @@ package main import ( + "io/ioutil" + "os" + "strings" + "github.com/docker/docker/api/types/swarm" "github.com/docker/docker/pkg/integration/checker" "github.com/go-check/check" @@ -104,3 +108,33 @@ func (s *DockerSwarmSuite) TestSecretCreateResolve(c *check.C) { c.Assert(out, checker.Not(checker.Contains), id) c.Assert(out, checker.Not(checker.Contains), fake) } + +func (s *DockerSwarmSuite) TestSecretCreateWithFile(c *check.C) { + d := s.AddDaemon(c, true, true) + + testFile, err := ioutil.TempFile("", "secretCreateTest") + c.Assert(err, checker.IsNil, check.Commentf("failed to create temporary file")) + defer os.Remove(testFile.Name()) + + testData := "TESTINGDATA" + _, err = testFile.Write([]byte(testData)) + c.Assert(err, checker.IsNil, check.Commentf("failed to write to temporary file")) + + testName := "test_secret" + out, err := d.Cmd("secret", "create", "--file", testFile.Name(), testName) + c.Assert(err, checker.IsNil) + c.Assert(strings.TrimSpace(out), checker.Not(checker.Equals), "", check.Commentf(out)) + + id := strings.TrimSpace(out) + secret := d.GetSecret(c, id) + c.Assert(secret.Spec.Name, checker.Equals, testName) + + testName = "test_secret_2" + out, err = d.Cmd("secret", "create", testName, "-f", testFile.Name()) + c.Assert(err, checker.IsNil) + c.Assert(strings.TrimSpace(out), checker.Not(checker.Equals), "", check.Commentf(out)) + + id = strings.TrimSpace(out) + secret = d.GetSecret(c, id) + c.Assert(secret.Spec.Name, checker.Equals, testName) +}