Skip to content

Commit

Permalink
Add swarmctl commands to create, inspect, list and remove secrets.
Browse files Browse the repository at this point in the history
Signed-off-by: cyli <ying.li@docker.com>
  • Loading branch information
cyli committed Sep 27, 2016
1 parent 445b738 commit 930d873
Show file tree
Hide file tree
Showing 7 changed files with 293 additions and 0 deletions.
2 changes: 2 additions & 0 deletions cmd/swarmctl/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import (
"github.com/docker/swarmkit/cmd/swarmctl/cluster"
"github.com/docker/swarmkit/cmd/swarmctl/network"
"github.com/docker/swarmkit/cmd/swarmctl/node"
"github.com/docker/swarmkit/cmd/swarmctl/secrets"
"github.com/docker/swarmkit/cmd/swarmctl/service"
"github.com/docker/swarmkit/cmd/swarmctl/task"
"github.com/docker/swarmkit/version"
Expand Down Expand Up @@ -54,5 +55,6 @@ func init() {
version.Cmd,
network.Cmd,
cluster.Cmd,
secrets.Cmd,
)
}
21 changes: 21 additions & 0 deletions cmd/swarmctl/secrets/cmd.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
package secrets

import "github.com/spf13/cobra"

var (
// Cmd exposes the top-level service command.
Cmd = &cobra.Command{
Use: "secret",
Aliases: nil,
Short: "Secrets management",
}
)

func init() {
Cmd.AddCommand(
inspectCmd,
listCmd,
createCmd,
removeCmd,
)
}
43 changes: 43 additions & 0 deletions cmd/swarmctl/secrets/common.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
package secrets

import (
"fmt"

"github.com/docker/swarmkit/api"
"golang.org/x/net/context"
)

func getSecret(ctx context.Context, c api.ControlClient, input string) (*api.Secret, error) {
// not sure what it is, match by name or id prefix
resp, err := c.ListSecrets(ctx,
&api.ListSecretsRequest{
Filters: &api.ListSecretsRequest_Filters{
Names: []string{input},
IDPrefixes: []string{input},
},
},
)
if err != nil {
return nil, err
}

switch len(resp.Secrets) {
case 0:
return nil, fmt.Errorf("secret %s not found", input)
case 1:
return resp.Secrets[0], nil
default:
// ok, multiple matches. Prefer exact ID over exact name. If no exact matches, return an error
for _, s := range resp.Secrets {
if s.ID == input {
return s, nil
}
}
for _, s := range resp.Secrets {
if s.Spec.Annotations.Name == input {
return s, nil
}
}
return nil, fmt.Errorf("secret %s is ambiguous (%d matches found)", input, len(resp.Secrets))
}
}
44 changes: 44 additions & 0 deletions cmd/swarmctl/secrets/create.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
package secrets

import (
"errors"
"fmt"
"io/ioutil"
"os"

"github.com/docker/swarmkit/api"
"github.com/docker/swarmkit/cmd/swarmctl/common"
"github.com/spf13/cobra"
)

var createCmd = &cobra.Command{
Use: "create <secret name>",
Short: "Create a secret",
RunE: func(cmd *cobra.Command, args []string) error {
if len(args) != 1 {
return errors.New("create command takes a unique secret name as an argument, and accepts secret data via stdin")
}

secretData, err := ioutil.ReadAll(os.Stdin)
if err != nil {
return fmt.Errorf("Error reading content from STDIN: %v", err)
}

client, err := common.Dial(cmd)
if err != nil {
return err
}

spec := &api.SecretSpec{
Annotations: api.Annotations{Name: args[0]},
Data: secretData,
}

resp, err := client.CreateSecret(common.Context(cmd), &api.CreateSecretRequest{Spec: spec})
if err != nil {
return err
}
fmt.Println(resp.Secret.ID)
return nil
},
}
57 changes: 57 additions & 0 deletions cmd/swarmctl/secrets/inspect.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
package secrets

import (
"errors"
"fmt"
"os"
"text/tabwriter"
"time"

"github.com/docker/swarmkit/api"
"github.com/docker/swarmkit/cmd/swarmctl/common"
"github.com/spf13/cobra"
)

func printSecretSummary(secret *api.Secret) {
w := tabwriter.NewWriter(os.Stdout, 8, 8, 8, ' ', 0)
defer w.Flush()

common.FprintfIfNotEmpty(w, "ID\t: %s\n", secret.ID)
common.FprintfIfNotEmpty(w, "Name\t: %s\n", secret.Spec.Annotations.Name)
if len(secret.Spec.Annotations.Labels) > 0 {
fmt.Fprintln(w, "Labels\t")
for k, v := range secret.Spec.Annotations.Labels {
fmt.Fprintf(w, " %s\t: %s\n", k, v)
}
}
common.FprintfIfNotEmpty(w, "Digest\t: %s\n", secret.Digest)
common.FprintfIfNotEmpty(w, "Size\t: %d\n", secret.SecretSize)

created := time.Unix(int64(secret.Meta.CreatedAt.Seconds), int64(secret.Meta.CreatedAt.Nanos))
common.FprintfIfNotEmpty(w, "Created\t: %s\n", created.Format(time.RFC822))
}

var (
inspectCmd = &cobra.Command{
Use: "inspect <secret ID or name>",
Short: "Inspect a secret",
RunE: func(cmd *cobra.Command, args []string) error {
if len(args) != 1 {
return errors.New("inspect command takes a single secret ID or name")
}

client, err := common.Dial(cmd)
if err != nil {
return err
}

secret, err := getSecret(common.Context(cmd), client, args[0])
if err != nil {
return err
}

printSecretSummary(secret)
return nil
},
}
)
88 changes: 88 additions & 0 deletions cmd/swarmctl/secrets/list.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
package secrets

import (
"errors"
"fmt"
"os"
"sort"
"text/tabwriter"
"time"

"github.com/docker/swarmkit/api"
"github.com/docker/swarmkit/cmd/swarmctl/common"
"github.com/dustin/go-humanize"
"github.com/spf13/cobra"
)

type secretSorter []*api.Secret

func (k secretSorter) Len() int { return len(k) }
func (k secretSorter) Swap(i, j int) { k[i], k[j] = k[j], k[i] }
func (k secretSorter) Less(i, j int) bool {
iTime := time.Unix(k[i].Meta.CreatedAt.Seconds, int64(k[i].Meta.CreatedAt.Nanos))
jTime := time.Unix(k[j].Meta.CreatedAt.Seconds, int64(k[j].Meta.CreatedAt.Nanos))
return jTime.Before(iTime)
}

var (
listCmd = &cobra.Command{
Use: "ls",
Short: "List secrets",
RunE: func(cmd *cobra.Command, args []string) error {
if len(args) != 0 {
return errors.New("ls command takes no arguments")
}

flags := cmd.Flags()
quiet, err := flags.GetBool("quiet")
if err != nil {
return err
}

client, err := common.Dial(cmd)
if err != nil {
return err
}

resp, err := client.ListSecrets(common.Context(cmd), &api.ListSecretsRequest{})
if err != nil {
return err
}

var output func(*api.Secret)

if !quiet {
w := tabwriter.NewWriter(os.Stdout, 0, 4, 4, ' ', 0)
defer func() {
// Ignore flushing errors - there's nothing we can do.
_ = w.Flush()
}()
common.PrintHeader(w, "ID", "Name", "Created", "Digest", "Size")
output = func(s *api.Secret) {
created := time.Unix(int64(s.Meta.CreatedAt.Seconds), int64(s.Meta.CreatedAt.Nanos))
fmt.Fprintf(w, "%s\t%s\t%s\t%s\t%d\n",
s.ID,
s.Spec.Annotations.Name,
humanize.Time(created),
s.Digest,
s.SecretSize,
)
}

} else {
output = func(s *api.Secret) { fmt.Println(s.ID) }
}

sorted := secretSorter(resp.Secrets)
sort.Sort(sorted)
for _, s := range sorted {
output(s)
}
return nil
},
}
)

func init() {
listCmd.Flags().BoolP("quiet", "q", false, "Only display secret names")
}
38 changes: 38 additions & 0 deletions cmd/swarmctl/secrets/remove.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
package secrets

import (
"errors"
"fmt"

"github.com/docker/swarmkit/api"
"github.com/docker/swarmkit/cmd/swarmctl/common"
"github.com/spf13/cobra"
)

var removeCmd = &cobra.Command{
Use: "remove <secret ID or name>",
Short: "Remove a secret",
Aliases: []string{"rm"},
RunE: func(cmd *cobra.Command, args []string) error {
if len(args) == 0 {
return errors.New("remove command takes a single secret ID or name")
}

client, err := common.Dial(cmd)
if err != nil {
return err
}

secret, err := getSecret(common.Context(cmd), client, args[0])
if err != nil {
return err
}

_, err = client.RemoveSecret(common.Context(cmd), &api.RemoveSecretRequest{SecretID: secret.ID})
if err != nil {
return err
}
fmt.Println(secret.ID)
return nil
},
}

0 comments on commit 930d873

Please sign in to comment.