Selaa lähdekoodia

Add format to secret ls

Signed-off-by: Boaz Shuster <ripcurld.github@gmail.com>
Boaz Shuster 8 vuotta sitten
vanhempi
commit
e281fe9c3a

+ 101 - 0
cli/command/formatter/secret.go

@@ -0,0 +1,101 @@
+package formatter
+
+import (
+	"fmt"
+	"strings"
+	"time"
+
+	"github.com/docker/docker/api/types/swarm"
+	units "github.com/docker/go-units"
+)
+
+const (
+	defaultSecretTableFormat = "table {{.ID}}\t{{.Name}}\t{{.CreatedAt}}\t{{.UpdatedAt}}"
+	secretIDHeader           = "ID"
+	secretNameHeader         = "NAME"
+	secretCreatedHeader      = "CREATED"
+	secretUpdatedHeader      = "UPDATED"
+)
+
+// NewSecretFormat returns a Format for rendering using a network Context
+func NewSecretFormat(source string, quiet bool) Format {
+	switch source {
+	case TableFormatKey:
+		if quiet {
+			return defaultQuietFormat
+		}
+		return defaultSecretTableFormat
+	}
+	return Format(source)
+}
+
+// SecretWrite writes the context
+func SecretWrite(ctx Context, secrets []swarm.Secret) error {
+	render := func(format func(subContext subContext) error) error {
+		for _, secret := range secrets {
+			secretCtx := &secretContext{s: secret}
+			if err := format(secretCtx); err != nil {
+				return err
+			}
+		}
+		return nil
+	}
+	return ctx.Write(newSecretContext(), render)
+}
+
+func newSecretContext() *secretContext {
+	sCtx := &secretContext{}
+
+	sCtx.header = map[string]string{
+		"ID":        secretIDHeader,
+		"Name":      nameHeader,
+		"CreatedAt": secretCreatedHeader,
+		"UpdatedAt": secretUpdatedHeader,
+		"Labels":    labelsHeader,
+	}
+	return sCtx
+}
+
+type secretContext struct {
+	HeaderContext
+	s swarm.Secret
+}
+
+func (c *secretContext) MarshalJSON() ([]byte, error) {
+	return marshalJSON(c)
+}
+
+func (c *secretContext) ID() string {
+	return c.s.ID
+}
+
+func (c *secretContext) Name() string {
+	return c.s.Spec.Annotations.Name
+}
+
+func (c *secretContext) CreatedAt() string {
+	return units.HumanDuration(time.Now().UTC().Sub(c.s.Meta.CreatedAt)) + " ago"
+}
+
+func (c *secretContext) UpdatedAt() string {
+	return units.HumanDuration(time.Now().UTC().Sub(c.s.Meta.UpdatedAt)) + " ago"
+}
+
+func (c *secretContext) Labels() string {
+	mapLabels := c.s.Spec.Annotations.Labels
+	if mapLabels == nil {
+		return ""
+	}
+	var joinLabels []string
+	for k, v := range mapLabels {
+		joinLabels = append(joinLabels, fmt.Sprintf("%s=%s", k, v))
+	}
+	return strings.Join(joinLabels, ",")
+}
+
+func (c *secretContext) Label(name string) string {
+	if c.s.Spec.Annotations.Labels == nil {
+		return ""
+	}
+	return c.s.Spec.Annotations.Labels[name]
+}

+ 63 - 0
cli/command/formatter/secret_test.go

@@ -0,0 +1,63 @@
+package formatter
+
+import (
+	"bytes"
+	"testing"
+	"time"
+
+	"github.com/docker/docker/api/types/swarm"
+	"github.com/docker/docker/pkg/testutil/assert"
+)
+
+func TestSecretContextFormatWrite(t *testing.T) {
+	// Check default output format (verbose and non-verbose mode) for table headers
+	cases := []struct {
+		context  Context
+		expected string
+	}{
+		// Errors
+		{
+			Context{Format: "{{InvalidFunction}}"},
+			`Template parsing error: template: :1: function "InvalidFunction" not defined
+`,
+		},
+		{
+			Context{Format: "{{nil}}"},
+			`Template parsing error: template: :1:2: executing "" at <nil>: nil is not a command
+`,
+		},
+		// 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
+`},
+		{Context{Format: NewSecretFormat("table {{.Name}}", true)},
+			`NAME
+passwords
+id_rsa
+`},
+		{Context{Format: NewSecretFormat("{{.ID}}-{{.Name}}", false)},
+			`1-passwords
+2-id_rsa
+`},
+	}
+
+	secrets := []swarm.Secret{
+		{ID: "1",
+			Meta: swarm.Meta{CreatedAt: time.Now(), UpdatedAt: time.Now()},
+			Spec: swarm.SecretSpec{Annotations: swarm.Annotations{Name: "passwords"}}},
+		{ID: "2",
+			Meta: swarm.Meta{CreatedAt: time.Now(), UpdatedAt: time.Now()},
+			Spec: swarm.SecretSpec{Annotations: swarm.Annotations{Name: "id_rsa"}}},
+	}
+	for _, testcase := range cases {
+		out := bytes.NewBufferString("")
+		testcase.context.Output = out
+		if err := SecretWrite(testcase.context, secrets); err != nil {
+			assert.Error(t, err, testcase.expected)
+		} else {
+			assert.Equal(t, out.String(), testcase.expected)
+		}
+	}
+}

+ 15 - 25
cli/command/secret/ls.go

@@ -1,20 +1,17 @@
 package secret
 package secret
 
 
 import (
 import (
-	"fmt"
-	"text/tabwriter"
-	"time"
-
 	"github.com/docker/docker/api/types"
 	"github.com/docker/docker/api/types"
 	"github.com/docker/docker/cli"
 	"github.com/docker/docker/cli"
 	"github.com/docker/docker/cli/command"
 	"github.com/docker/docker/cli/command"
-	"github.com/docker/go-units"
+	"github.com/docker/docker/cli/command/formatter"
 	"github.com/spf13/cobra"
 	"github.com/spf13/cobra"
 	"golang.org/x/net/context"
 	"golang.org/x/net/context"
 )
 )
 
 
 type listOptions struct {
 type listOptions struct {
-	quiet bool
+	quiet  bool
+	format string
 }
 }
 
 
 func newSecretListCommand(dockerCli *command.DockerCli) *cobra.Command {
 func newSecretListCommand(dockerCli *command.DockerCli) *cobra.Command {
@@ -32,6 +29,7 @@ func newSecretListCommand(dockerCli *command.DockerCli) *cobra.Command {
 
 
 	flags := cmd.Flags()
 	flags := cmd.Flags()
 	flags.BoolVarP(&opts.quiet, "quiet", "q", false, "Only display IDs")
 	flags.BoolVarP(&opts.quiet, "quiet", "q", false, "Only display IDs")
+	flags.StringVarP(&opts.format, "format", "", "", "Pretty-print secrets using a Go template")
 
 
 	return cmd
 	return cmd
 }
 }
@@ -44,25 +42,17 @@ func runSecretList(dockerCli *command.DockerCli, opts listOptions) error {
 	if err != nil {
 	if err != nil {
 		return err
 		return err
 	}
 	}
-
-	w := tabwriter.NewWriter(dockerCli.Out(), 20, 1, 3, ' ', 0)
-	if opts.quiet {
-		for _, s := range secrets {
-			fmt.Fprintf(w, "%s\n", s.ID)
-		}
-	} else {
-		fmt.Fprintf(w, "ID\tNAME\tCREATED\tUPDATED")
-		fmt.Fprintf(w, "\n")
-
-		for _, s := range secrets {
-			created := units.HumanDuration(time.Now().UTC().Sub(s.Meta.CreatedAt)) + " ago"
-			updated := units.HumanDuration(time.Now().UTC().Sub(s.Meta.UpdatedAt)) + " ago"
-
-			fmt.Fprintf(w, "%s\t%s\t%s\t%s\n", s.ID, s.Spec.Annotations.Name, created, updated)
+	format := opts.format
+	if len(format) == 0 {
+		if len(dockerCli.ConfigFile().SecretFormat) > 0 && !opts.quiet {
+			format = dockerCli.ConfigFile().SecretFormat
+		} else {
+			format = formatter.TableFormatKey
 		}
 		}
 	}
 	}
-
-	w.Flush()
-
-	return nil
+	secretCtx := formatter.Context{
+		Output: dockerCli.Out(),
+		Format: formatter.NewSecretFormat(format, opts.quiet),
+	}
+	return formatter.SecretWrite(secretCtx, secrets)
 }
 }

+ 1 - 0
cli/config/configfile/file.go

@@ -37,6 +37,7 @@ type ConfigFile struct {
 	ServiceInspectFormat string                      `json:"serviceInspectFormat,omitempty"`
 	ServiceInspectFormat string                      `json:"serviceInspectFormat,omitempty"`
 	ServicesFormat       string                      `json:"servicesFormat,omitempty"`
 	ServicesFormat       string                      `json:"servicesFormat,omitempty"`
 	TasksFormat          string                      `json:"tasksFormat,omitempty"`
 	TasksFormat          string                      `json:"tasksFormat,omitempty"`
+	SecretFormat         string                      `json:"secretFormat,omitempty"`
 }
 }
 
 
 // LegacyLoadFromReader reads the non-nested configuration data given and sets up the
 // LegacyLoadFromReader reads the non-nested configuration data given and sets up the

+ 9 - 0
docs/reference/commandline/cli.md

@@ -160,6 +160,14 @@ property is not set, the client falls back to the default table
 format. For a list of supported formatting directives, see
 format. For a list of supported formatting directives, see
 [**Formatting** section in the `docker stats` documentation](stats.md)
 [**Formatting** section in the `docker stats` documentation](stats.md)
 
 
+The property `secretFormat` specifies the default format for `docker
+secret ls` output. When the `--format` flag is not provided with the
+`docker secret ls` command, Docker's client uses this property. If this
+property is not set, the client falls back to the default table
+format. For a list of supported formatting directives, see
+[**Formatting** section in the `docker secret ls` documentation](secret_ls.md)
+
+
 The property `credsStore` specifies an external binary to serve as the default
 The property `credsStore` specifies an external binary to serve as the default
 credential store. When this property is set, `docker login` will attempt to
 credential store. When this property is set, `docker login` will attempt to
 store credentials in the binary specified by `docker-credential-<value>` which
 store credentials in the binary specified by `docker-credential-<value>` which
@@ -204,6 +212,7 @@ Following is a sample `config.json` file:
   "pluginsFormat": "table {{.ID}}\t{{.Name}}\t{{.Enabled}}",
   "pluginsFormat": "table {{.ID}}\t{{.Name}}\t{{.Enabled}}",
   "statsFormat": "table {{.Container}}\t{{.CPUPerc}}\t{{.MemUsage}}",
   "statsFormat": "table {{.Container}}\t{{.CPUPerc}}\t{{.MemUsage}}",
   "servicesFormat": "table {{.ID}}\t{{.Name}}\t{{.Mode}}",
   "servicesFormat": "table {{.ID}}\t{{.Name}}\t{{.Mode}}",
+  "secretFormat": "table {{.ID}}\t{{.Name}}\t{{.CreatedAt}}\t{{.UpdatedAt}}",
   "serviceInspectFormat": "pretty",
   "serviceInspectFormat": "pretty",
   "detachKeys": "ctrl-e,e",
   "detachKeys": "ctrl-e,e",
   "credsStore": "secretservice",
   "credsStore": "secretservice",

+ 44 - 0
docs/reference/commandline/secret_ls.md

@@ -25,6 +25,7 @@ Aliases:
 
 
 Options:
 Options:
   -q, --quiet          Only display IDs
   -q, --quiet          Only display IDs
+  -format string       Pretty-print secrets using a Go template
 ```
 ```
 
 
 ## Description
 ## Description
@@ -40,6 +41,49 @@ ID                          NAME                    CREATED
 mhv17xfe3gh6xc4rij5orpfds   secret.json             2016-10-27 23:25:43.909181089 +0000 UTC   2016-10-27 23:25:43.909181089 +0000 UTC
 mhv17xfe3gh6xc4rij5orpfds   secret.json             2016-10-27 23:25:43.909181089 +0000 UTC   2016-10-27 23:25:43.909181089 +0000 UTC
 ```
 ```
 
 
+### Format the output
+
+The formatting option (`--format`) pretty prints secrets output
+using a Go template.
+
+Valid placeholders for the Go template are listed below:
+
+| Placeholder  | Description                                                                          |
+| ------------ | ------------------------------------------------------------------------------------ |
+| `.ID`        | Secret ID                                                                            |
+| `.Name`      | Secret name                                                                          |
+| `.CreatedAt` | Time when the secret was created                                                     |
+| `.UpdatedAt` | Time when the secret was updated                                                     |
+| `.Labels`    | All labels assigned to the secret                                                    |
+| `.Label`     | Value of a specific label for this secret. For example `{{.Label "secret.ssh.key"}}` |
+
+When using the `--format` option, the `secret ls` command will either
+output the data exactly as the template declares or, when using the
+`table` directive, will include column headers as well.
+
+The following example uses a template without headers and outputs the
+`ID` and `Name` entries separated by a colon for all images:
+
+```bash
+$ docker secret ls --format "{{.ID}}: {{.Name}}"
+
+77af4d6b9913: secret-1
+b6fa739cedf5: secret-2
+78a85c484f71: secret-3
+```
+
+To list all secrets with their name and created date in a table format you
+can use:
+
+```bash
+$ docker secret ls --format "table {{.ID}}\t{{.Name}}\t{{.CreatedAt}}"
+
+ID                  NAME                      CREATED
+77af4d6b9913        secret-1                  5 minutes ago
+b6fa739cedf5        secret-2                  3 hours ago
+78a85c484f71        secret-3                  10 days ago
+```
+
 ## Related commands
 ## Related commands
 
 
 * [secret create](secret_create.md)
 * [secret create](secret_create.md)