list.go 2.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113
  1. package stack
  2. import (
  3. "fmt"
  4. "io"
  5. "strconv"
  6. "text/tabwriter"
  7. "golang.org/x/net/context"
  8. "github.com/docker/docker/api/types"
  9. "github.com/docker/docker/cli"
  10. "github.com/docker/docker/cli/command"
  11. "github.com/docker/docker/cli/compose/convert"
  12. "github.com/docker/docker/client"
  13. "github.com/spf13/cobra"
  14. )
  15. const (
  16. listItemFmt = "%s\t%s\n"
  17. )
  18. type listOptions struct {
  19. }
  20. func newListCommand(dockerCli *command.DockerCli) *cobra.Command {
  21. opts := listOptions{}
  22. cmd := &cobra.Command{
  23. Use: "ls",
  24. Aliases: []string{"list"},
  25. Short: "List stacks",
  26. Args: cli.NoArgs,
  27. RunE: func(cmd *cobra.Command, args []string) error {
  28. return runList(dockerCli, opts)
  29. },
  30. }
  31. return cmd
  32. }
  33. func runList(dockerCli *command.DockerCli, opts listOptions) error {
  34. client := dockerCli.Client()
  35. ctx := context.Background()
  36. stacks, err := getStacks(ctx, client)
  37. if err != nil {
  38. return err
  39. }
  40. out := dockerCli.Out()
  41. printTable(out, stacks)
  42. return nil
  43. }
  44. func printTable(out io.Writer, stacks []*stack) {
  45. writer := tabwriter.NewWriter(out, 0, 4, 2, ' ', 0)
  46. // Ignore flushing errors
  47. defer writer.Flush()
  48. fmt.Fprintf(writer, listItemFmt, "NAME", "SERVICES")
  49. for _, stack := range stacks {
  50. fmt.Fprintf(
  51. writer,
  52. listItemFmt,
  53. stack.Name,
  54. strconv.Itoa(stack.Services),
  55. )
  56. }
  57. }
  58. type stack struct {
  59. // Name is the name of the stack
  60. Name string
  61. // Services is the number of the services
  62. Services int
  63. }
  64. func getStacks(
  65. ctx context.Context,
  66. apiclient client.APIClient,
  67. ) ([]*stack, error) {
  68. services, err := apiclient.ServiceList(
  69. ctx,
  70. types.ServiceListOptions{Filters: getAllStacksFilter()})
  71. if err != nil {
  72. return nil, err
  73. }
  74. m := make(map[string]*stack, 0)
  75. for _, service := range services {
  76. labels := service.Spec.Labels
  77. name, ok := labels[convert.LabelNamespace]
  78. if !ok {
  79. return nil, fmt.Errorf("cannot get label %s for service %s",
  80. convert.LabelNamespace, service.ID)
  81. }
  82. ztack, ok := m[name]
  83. if !ok {
  84. m[name] = &stack{
  85. Name: name,
  86. Services: 1,
  87. }
  88. } else {
  89. ztack.Services++
  90. }
  91. }
  92. var stacks []*stack
  93. for _, stack := range m {
  94. stacks = append(stacks, stack)
  95. }
  96. return stacks, nil
  97. }