list.go 2.2 KB

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