Skip to content

Commit

Permalink
Plugable secret backend
Browse files Browse the repository at this point in the history
This commit extends SwarmKit secret management with pluggable secret
backends support.
Following previous commits:
1. moby/swarmkit@eebac27
2. moby/moby@08f7cf0
Added driver parameter to `docker secret` command.

Specifically:

1. `docker secret create [secret_name] --driver [driver_name]`
2.  Displaying the driver in
```
    $ docker secret ls
    $ docker secret inspect [secret_name]
    $ docker secret inspect [secret_name] -pretty
```

Signed-off-by: Liron Levin <[email protected]>
  • Loading branch information
Liron Levin committed Sep 7, 2017
1 parent af94015 commit 0ee9e05
Show file tree
Hide file tree
Showing 10 changed files with 115 additions and 36 deletions.
26 changes: 21 additions & 5 deletions cli/command/formatter/secret.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,19 +12,20 @@ import (
)

const (
defaultSecretTableFormat = "table {{.ID}}\t{{.Name}}\t{{.CreatedAt}}\t{{.UpdatedAt}}"
defaultSecretTableFormat = "table {{.ID}}\t{{.Name}}\t{{.Driver}}\t{{.CreatedAt}}\t{{.UpdatedAt}}"
secretIDHeader = "ID"
secretCreatedHeader = "CREATED"
secretUpdatedHeader = "UPDATED"
secretInspectPrettyTemplate Format = `ID: {{.ID}}
Name: {{.Name}}
secretInspectPrettyTemplate Format = `ID: {{.ID}}
Name: {{.Name}}
{{- if .Labels }}
Labels:
{{- range $k, $v := .Labels }}
- {{ $k }}{{if $v }}={{ $v }}{{ end }}
{{- end }}{{ end }}
Created at: {{.CreatedAt}}
Updated at: {{.UpdatedAt}}`
Driver: {{.Driver}}
Created at: {{.CreatedAt}}
Updated at: {{.UpdatedAt}}`
)

// NewSecretFormat returns a Format for rendering using a secret Context
Expand Down Expand Up @@ -61,6 +62,7 @@ func newSecretContext() *secretContext {
sCtx.header = map[string]string{
"ID": secretIDHeader,
"Name": nameHeader,
"Driver": driverHeader,
"CreatedAt": secretCreatedHeader,
"UpdatedAt": secretUpdatedHeader,
"Labels": labelsHeader,
Expand Down Expand Up @@ -89,6 +91,13 @@ func (c *secretContext) CreatedAt() string {
return units.HumanDuration(time.Now().UTC().Sub(c.s.Meta.CreatedAt)) + " ago"
}

func (c *secretContext) Driver() string {
if c.s.Spec.Driver == nil {
return ""
}
return c.s.Spec.Driver.Name
}

func (c *secretContext) UpdatedAt() string {
return units.HumanDuration(time.Now().UTC().Sub(c.s.Meta.UpdatedAt)) + " ago"
}
Expand Down Expand Up @@ -153,6 +162,13 @@ func (ctx *secretInspectContext) Labels() map[string]string {
return ctx.Secret.Spec.Labels
}

func (ctx *secretInspectContext) Driver() string {
if ctx.Secret.Spec.Driver == nil {
return ""
}
return ctx.Secret.Spec.Driver.Name
}

func (ctx *secretInspectContext) CreatedAt() string {
return command.PrettyPrint(ctx.Secret.CreatedAt)
}
Expand Down
6 changes: 3 additions & 3 deletions cli/command/formatter/secret_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,9 +28,9 @@ func TestSecretContextFormatWrite(t *testing.T) {
},
// Table format
{Context{Format: NewSecretFormat("table", false)},
`ID NAME CREATED UPDATED
1 passwords Less than a second ago Less than a second ago
2 id_rsa Less than a second ago Less than a second ago
`ID NAME DRIVER CREATED UPDATED
1 passwords Less than a second ago Less than a second ago
2 id_rsa Less than a second ago Less than a second ago
`},
{Context{Format: NewSecretFormat("table {{.Name}}", true)},
`NAME
Expand Down
49 changes: 36 additions & 13 deletions cli/command/secret/create.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import (

type createOptions struct {
name string
driver string
file string
labels opts.ListOpts
}
Expand All @@ -27,17 +28,21 @@ func newSecretCreateCommand(dockerCli command.Cli) *cobra.Command {
}

cmd := &cobra.Command{
Use: "create [OPTIONS] SECRET file|-",
Use: "create [OPTIONS] SECRET [file|-]",
Short: "Create a secret from a file or STDIN as content",
Args: cli.ExactArgs(2),
Args: cli.RequiresRangeArgs(1, 2),
RunE: func(cmd *cobra.Command, args []string) error {
options.name = args[0]
options.file = args[1]
if len(args) == 2 {
options.file = args[1]
}
return runSecretCreate(dockerCli, options)
},
}
flags := cmd.Flags()
flags.VarP(&options.labels, "label", "l", "Secret labels")
flags.StringVarP(&options.driver, "driver", "d", "", "Secret driver")
flags.SetAnnotation("driver", "version", []string{"1.31"})

return cmd
}
Expand All @@ -46,28 +51,26 @@ func runSecretCreate(dockerCli command.Cli, options createOptions) error {
client := dockerCli.Client()
ctx := context.Background()

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()
if options.driver != "" && options.file != "" {
return errors.Errorf("When using secret driver secret data must be empty")
}

secretData, err := ioutil.ReadAll(in)
secretData, err := readSecretData(dockerCli.In(), options.file)
if err != nil {
return errors.Errorf("Error reading content from %q: %v", options.file, err)
}

spec := swarm.SecretSpec{
Annotations: swarm.Annotations{
Name: options.name,
Labels: opts.ConvertKVStringsToMap(options.labels.GetAll()),
},
Data: secretData,
}
if options.driver != "" {
spec.Driver = &swarm.Driver{
Name: options.driver,
}
}

r, err := client.SecretCreate(ctx, spec)
if err != nil {
Expand All @@ -77,3 +80,23 @@ func runSecretCreate(dockerCli command.Cli, options createOptions) error {
fmt.Fprintln(dockerCli.Out(), r.ID)
return nil
}

func readSecretData(in io.ReadCloser, file string) ([]byte, error) {
// Read secret value from external driver
if file == "" {
return nil, nil
}
if file != "-" {
var err error
in, err = system.OpenSequential(file)
if err != nil {
return nil, err
}
defer in.Close()
}
data, err := ioutil.ReadAll(in)
if err != nil {
return nil, err
}
return data, nil
}
38 changes: 33 additions & 5 deletions cli/command/secret/create_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,12 +24,11 @@ func TestSecretCreateErrors(t *testing.T) {
secretCreateFunc func(swarm.SecretSpec) (types.SecretCreateResponse, error)
expectedError string
}{
{
args: []string{"too_few"},
expectedError: "requires exactly 2 arguments",
},
{args: []string{"too", "many", "arguments"},
expectedError: "requires exactly 2 arguments",
expectedError: "requires at least 1 and at most 2 arguments",
},
{args: []string{"create", "--driver", "driver", "-"},
expectedError: "secret data must be empty",
},
{
args: []string{"name", filepath.Join("testdata", secretDataFile)},
Expand Down Expand Up @@ -75,6 +74,35 @@ func TestSecretCreateWithName(t *testing.T) {
assert.Equal(t, "ID-"+name, strings.TrimSpace(cli.OutBuffer().String()))
}

func TestSecretCreateWithDriver(t *testing.T) {
expectedDriver := &swarm.Driver{
Name: "secret-driver",
}
name := "foo"

cli := test.NewFakeCli(&fakeClient{
secretCreateFunc: func(spec swarm.SecretSpec) (types.SecretCreateResponse, error) {
if spec.Name != name {
return types.SecretCreateResponse{}, errors.Errorf("expected name %q, got %q", name, spec.Name)
}

if !reflect.DeepEqual(spec.Driver.Name, expectedDriver.Name) {
return types.SecretCreateResponse{}, errors.Errorf("expected driver %v, got %v", expectedDriver, spec.Labels)
}

return types.SecretCreateResponse{
ID: "ID-" + spec.Name,
}, nil
},
})

cmd := newSecretCreateCommand(cli)
cmd.SetArgs([]string{name})
cmd.Flags().Set("driver", expectedDriver.Name)
assert.NoError(t, cmd.Execute())
assert.Equal(t, "ID-"+name, strings.TrimSpace(cli.OutBuffer().String()))
}

func TestSecretCreateWithLabels(t *testing.T) {
expectedLabels := map[string]string{
"lbl1": "Label-foo",
Expand Down
1 change: 1 addition & 0 deletions cli/command/secret/inspect_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -154,6 +154,7 @@ func TestSecretInspectPretty(t *testing.T) {
}),
SecretID("secretID"),
SecretName("secretName"),
SecretDriver("driver"),
SecretCreatedAt(time.Time{}),
SecretUpdatedAt(time.Time{}),
), []byte{}, nil
Expand Down
1 change: 1 addition & 0 deletions cli/command/secret/ls_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,7 @@ func TestSecretList(t *testing.T) {
SecretVersion(swarm.Version{Index: 11}),
SecretCreatedAt(time.Now().Add(-2*time.Hour)),
SecretUpdatedAt(time.Now().Add(-1*time.Hour)),
SecretDriver("driver"),
),
}, nil
},
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
ID: secretID
Name: secretName
ID: secretID
Name: secretName
Labels:
- lbl1=value1
Created at: 0001-01-01 00:00:00 +0000 utc
Updated at: 0001-01-01 00:00:00 +0000 utc
Driver: driver
Created at: 0001-01-01 00:00:00 +0000 utc
Updated at: 0001-01-01 00:00:00 +0000 utc
6 changes: 3 additions & 3 deletions cli/command/secret/testdata/secret-list-with-filter.golden
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
ID NAME CREATED UPDATED
ID-foo foo 2 hours ago About an hour ago
ID-bar bar 2 hours ago About an hour ago
ID NAME DRIVER CREATED UPDATED
ID-foo foo 2 hours ago About an hour ago
ID-bar bar 2 hours ago About an hour ago
6 changes: 3 additions & 3 deletions cli/command/secret/testdata/secret-list.golden
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
ID NAME CREATED UPDATED
ID-foo foo 2 hours ago About an hour ago
ID-bar bar 2 hours ago About an hour ago
ID NAME DRIVER CREATED UPDATED
ID-foo foo 2 hours ago About an hour ago
ID-bar bar driver 2 hours ago About an hour ago
9 changes: 9 additions & 0 deletions internal/test/builders/secret.go
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,15 @@ func SecretName(name string) func(secret *swarm.Secret) {
}
}

// SecretDriver sets the secret's driver name
func SecretDriver(driver string) func(secret *swarm.Secret) {
return func(secret *swarm.Secret) {
secret.Spec.Driver = &swarm.Driver{
Name: driver,
}
}
}

// SecretID sets the secret's ID
func SecretID(ID string) func(secret *swarm.Secret) {
return func(secret *swarm.Secret) {
Expand Down

0 comments on commit 0ee9e05

Please sign in to comment.