From b06f3f27a46bdab60e9a9eafaa7aa7c76ecca1fc Mon Sep 17 00:00:00 2001 From: Akihiro Suda Date: Thu, 23 Jun 2016 05:00:21 +0000 Subject: [PATCH] add `docker stack ls` Signed-off-by: Akihiro Suda --- command/stack/cmd_experimental.go | 1 + command/stack/list.go | 119 ++++++++++++++++++++++++++++++ command/stack/services.go | 4 - 3 files changed, 120 insertions(+), 4 deletions(-) create mode 100644 command/stack/list.go diff --git a/command/stack/cmd_experimental.go b/command/stack/cmd_experimental.go index d459e0a9a1..b32d925330 100644 --- a/command/stack/cmd_experimental.go +++ b/command/stack/cmd_experimental.go @@ -23,6 +23,7 @@ func NewStackCommand(dockerCli *command.DockerCli) *cobra.Command { cmd.AddCommand( newConfigCommand(dockerCli), newDeployCommand(dockerCli), + newListCommand(dockerCli), newRemoveCommand(dockerCli), newServicesCommand(dockerCli), newPsCommand(dockerCli), diff --git a/command/stack/list.go b/command/stack/list.go new file mode 100644 index 0000000000..9fe626d96d --- /dev/null +++ b/command/stack/list.go @@ -0,0 +1,119 @@ +// +build experimental + +package stack + +import ( + "fmt" + "io" + "strconv" + "text/tabwriter" + + "golang.org/x/net/context" + + "github.com/docker/docker/api/types" + "github.com/docker/docker/api/types/filters" + "github.com/docker/docker/cli" + "github.com/docker/docker/cli/command" + "github.com/docker/docker/client" + "github.com/spf13/cobra" +) + +const ( + listItemFmt = "%s\t%s\n" +) + +type listOptions struct { +} + +func newListCommand(dockerCli *command.DockerCli) *cobra.Command { + opts := listOptions{} + + cmd := &cobra.Command{ + Use: "ls", + Aliases: []string{"list"}, + Short: "List stacks", + Args: cli.NoArgs, + RunE: func(cmd *cobra.Command, args []string) error { + return runList(dockerCli, opts) + }, + } + + return cmd +} + +func runList(dockerCli *command.DockerCli, opts listOptions) error { + client := dockerCli.Client() + ctx := context.Background() + + stacks, err := getStacks(ctx, client) + if err != nil { + return err + } + + out := dockerCli.Out() + printTable(out, stacks) + return nil +} + +func printTable(out io.Writer, stacks []*stack) { + writer := tabwriter.NewWriter(out, 0, 4, 2, ' ', 0) + + // Ignore flushing errors + defer writer.Flush() + + fmt.Fprintf(writer, listItemFmt, "NAME", "SERVICES") + for _, stack := range stacks { + fmt.Fprintf( + writer, + listItemFmt, + stack.Name, + strconv.Itoa(stack.Services), + ) + } +} + +type stack struct { + // Name is the name of the stack + Name string + // Services is the number of the services + Services int +} + +func getStacks( + ctx context.Context, + apiclient client.APIClient, +) ([]*stack, error) { + + filter := filters.NewArgs() + filter.Add("label", labelNamespace) + + services, err := apiclient.ServiceList( + ctx, + types.ServiceListOptions{Filter: filter}) + if err != nil { + return nil, err + } + m := make(map[string]*stack, 0) + for _, service := range services { + labels := service.Spec.Labels + name, ok := labels[labelNamespace] + if !ok { + return nil, fmt.Errorf("cannot get label %s for service %s", + labelNamespace, service.ID) + } + ztack, ok := m[name] + if !ok { + m[name] = &stack{ + Name: name, + Services: 1, + } + } else { + ztack.Services++ + } + } + var stacks []*stack + for _, stack := range m { + stacks = append(stacks, stack) + } + return stacks, nil +} diff --git a/command/stack/services.go b/command/stack/services.go index 22906378d6..60f52c30c7 100644 --- a/command/stack/services.go +++ b/command/stack/services.go @@ -16,10 +16,6 @@ import ( "github.com/spf13/cobra" ) -const ( - listItemFmt = "%s\t%s\t%s\t%s\t%s\n" -) - type servicesOptions struct { quiet bool filter opts.FilterOpt