From 539654c1a89f780f1cc8fdb2b5aa268f0f3ae042 Mon Sep 17 00:00:00 2001 From: Mathieu Champlon Date: Thu, 26 Apr 2018 11:13:14 +0200 Subject: [PATCH] Support multiple namespaces for docker stack ls Signed-off-by: Mathieu Champlon --- cli/command/stack/cmd.go | 3 -- cli/command/stack/deploy.go | 1 + cli/command/stack/kubernetes/cli.go | 8 +++- cli/command/stack/list.go | 61 +++++++++++++++++++++++------ cli/command/stack/options/opts.go | 1 + cli/command/stack/ps.go | 2 +- cli/command/stack/remove.go | 2 + cli/command/stack/services.go | 2 +- 8 files changed, 62 insertions(+), 18 deletions(-) diff --git a/cli/command/stack/cmd.go b/cli/command/stack/cmd.go index c71d43acd20c..c777126502d0 100644 --- a/cli/command/stack/cmd.go +++ b/cli/command/stack/cmd.go @@ -31,9 +31,6 @@ func NewStackCommand(dockerCli command.Cli) *cobra.Command { newServicesCommand(dockerCli), ) flags := cmd.PersistentFlags() - flags.String("namespace", "", "Kubernetes namespace to use") - flags.SetAnnotation("namespace", "kubernetes", nil) - flags.SetAnnotation("namespace", "experimentalCLI", nil) flags.String("kubeconfig", "", "Kubernetes config file") flags.SetAnnotation("kubeconfig", "kubernetes", nil) flags.SetAnnotation("kubeconfig", "experimentalCLI", nil) diff --git a/cli/command/stack/deploy.go b/cli/command/stack/deploy.go index 9aedcedab52d..38837c6f113a 100644 --- a/cli/command/stack/deploy.go +++ b/cli/command/stack/deploy.go @@ -49,5 +49,6 @@ func newDeployCommand(dockerCli command.Cli) *cobra.Command { `Query the registry to resolve image digest and supported platforms ("`+swarm.ResolveImageAlways+`"|"`+swarm.ResolveImageChanged+`"|"`+swarm.ResolveImageNever+`")`) flags.SetAnnotation("resolve-image", "version", []string{"1.30"}) flags.SetAnnotation("resolve-image", "swarm", nil) + kubernetes.AddNamespaceFlag(flags) return cmd } diff --git a/cli/command/stack/kubernetes/cli.go b/cli/command/stack/kubernetes/cli.go index 9749a41e91a5..018dd05e21f4 100644 --- a/cli/command/stack/kubernetes/cli.go +++ b/cli/command/stack/kubernetes/cli.go @@ -34,9 +34,15 @@ func NewOptions(flags *flag.FlagSet) Options { return opts } +// AddNamespaceFlag adds the namespace flag to the given flag set +func AddNamespaceFlag(flags *flag.FlagSet) { + flags.String("namespace", "", "Kubernetes namespace to use") + flags.SetAnnotation("namespace", "kubernetes", nil) + flags.SetAnnotation("namespace", "experimentalCLI", nil) +} + // WrapCli wraps command.Cli with kubernetes specifics func WrapCli(dockerCli command.Cli, opts Options) (*KubeCli, error) { - var err error cli := &KubeCli{ Cli: dockerCli, } diff --git a/cli/command/stack/list.go b/cli/command/stack/list.go index 3dcf0b03b8c5..f0351aa4b14c 100644 --- a/cli/command/stack/list.go +++ b/cli/command/stack/list.go @@ -28,6 +28,9 @@ func newListCommand(dockerCli command.Cli) *cobra.Command { flags := cmd.Flags() flags.StringVar(&opts.Format, "format", "", "Pretty-print stacks using a Go template") + flags.StringSliceVar(&opts.Namespaces, "namespace", []string{}, "Kubernetes namespaces to use") + flags.SetAnnotation("namespace", "kubernetes", nil) + flags.SetAnnotation("namespace", "experimentalCLI", nil) flags.BoolVarP(&opts.AllNamespaces, "all-namespaces", "", false, "List stacks among all Kubernetes namespaces") flags.SetAnnotation("all-namespaces", "kubernetes", nil) flags.SetAnnotation("all-namespaces", "experimentalCLI", nil) @@ -44,18 +47,26 @@ func runList(cmd *cobra.Command, dockerCli command.Cli, opts options.List) error stacks = append(stacks, ss...) } if dockerCli.ClientInfo().HasKubernetes() { - if dockerCli.ClientInfo().HasAll() && !cmd.Flags().Changed("namespace") { - opts.AllNamespaces = true + kopts := kubernetes.NewOptions(cmd.Flags()) + if opts.AllNamespaces || len(opts.Namespaces) == 0 { + if dockerCli.ClientInfo().HasAll() { + opts.AllNamespaces = true + } + ss, err := getStacks(dockerCli, opts, kopts) + if err != nil { + return err + } + stacks = append(stacks, ss...) + } else { + for _, nm := range removeDuplicates(opts.Namespaces) { + kopts.Namespace = nm + ss, err := getStacks(dockerCli, opts, kopts) + if err != nil { + return err + } + stacks = append(stacks, ss...) + } } - kli, err := kubernetes.WrapCli(dockerCli, kubernetes.NewOptions(cmd.Flags())) - if err != nil { - return err - } - ss, err := kubernetes.GetStacks(kli, opts) - if err != nil { - return err - } - stacks = append(stacks, ss...) } format := opts.Format if format == "" || format == formatter.TableFormatKey { @@ -69,7 +80,33 @@ func runList(cmd *cobra.Command, dockerCli command.Cli, opts options.List) error Format: formatter.Format(format), } sort.Slice(stacks, func(i, j int) bool { - return sortorder.NaturalLess(stacks[i].Name, stacks[j].Name) + return sortorder.NaturalLess(stacks[i].Name, stacks[j].Name) || + !sortorder.NaturalLess(stacks[j].Name, stacks[i].Name) && + sortorder.NaturalLess(stacks[j].Namespace, stacks[i].Namespace) }) return formatter.StackWrite(stackCtx, stacks) } + +func getStacks(dockerCli command.Cli, opts options.List, kopts kubernetes.Options) ([]*formatter.Stack, error) { + kli, err := kubernetes.WrapCli(dockerCli, kopts) + if err != nil { + return nil, err + } + ss, err := kubernetes.GetStacks(kli, opts) + if err != nil { + return nil, err + } + return ss, nil +} + +func removeDuplicates(namespaces []string) []string { + mnms := map[string]struct{}{} + for _, nm := range namespaces { + mnms[nm] = struct{}{} + } + namespaces = []string{} + for nm := range mnms { + namespaces = append(namespaces, nm) + } + return namespaces +} diff --git a/cli/command/stack/options/opts.go b/cli/command/stack/options/opts.go index f620fe54a9d5..afcecd9961ef 100644 --- a/cli/command/stack/options/opts.go +++ b/cli/command/stack/options/opts.go @@ -16,6 +16,7 @@ type Deploy struct { type List struct { Format string AllNamespaces bool + Namespaces []string } // PS holds docker stack ps options diff --git a/cli/command/stack/ps.go b/cli/command/stack/ps.go index 96cb9a7a1efb..be446c1f31ca 100644 --- a/cli/command/stack/ps.go +++ b/cli/command/stack/ps.go @@ -40,6 +40,6 @@ func newPsCommand(dockerCli command.Cli) *cobra.Command { flags.SetAnnotation("filter", "swarm", nil) flags.BoolVarP(&opts.Quiet, "quiet", "q", false, "Only display task IDs") flags.StringVar(&opts.Format, "format", "", "Pretty-print tasks using a Go template") - + kubernetes.AddNamespaceFlag(flags) return cmd } diff --git a/cli/command/stack/remove.go b/cli/command/stack/remove.go index 00242507d7b4..3f34cfc3072e 100644 --- a/cli/command/stack/remove.go +++ b/cli/command/stack/remove.go @@ -33,5 +33,7 @@ func newRemoveCommand(dockerCli command.Cli) *cobra.Command { } }, } + flags := cmd.Flags() + kubernetes.AddNamespaceFlag(flags) return cmd } diff --git a/cli/command/stack/services.go b/cli/command/stack/services.go index c218d99522ae..09e9310e8f7e 100644 --- a/cli/command/stack/services.go +++ b/cli/command/stack/services.go @@ -38,6 +38,6 @@ func newServicesCommand(dockerCli command.Cli) *cobra.Command { flags.StringVar(&opts.Format, "format", "", "Pretty-print services using a Go template") flags.VarP(&opts.Filter, "filter", "f", "Filter output based on conditions provided") flags.SetAnnotation("filter", "swarm", nil) - + kubernetes.AddNamespaceFlag(flags) return cmd }