list.go 3.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128
  1. package service
  2. import (
  3. "fmt"
  4. "github.com/docker/docker/api/types"
  5. "github.com/docker/docker/api/types/filters"
  6. "github.com/docker/docker/api/types/swarm"
  7. "github.com/docker/docker/cli"
  8. "github.com/docker/docker/cli/command"
  9. "github.com/docker/docker/cli/command/formatter"
  10. "github.com/docker/docker/opts"
  11. "github.com/spf13/cobra"
  12. "golang.org/x/net/context"
  13. )
  14. type listOptions struct {
  15. quiet bool
  16. format string
  17. filter opts.FilterOpt
  18. }
  19. func newListCommand(dockerCli *command.DockerCli) *cobra.Command {
  20. opts := listOptions{filter: opts.NewFilterOpt()}
  21. cmd := &cobra.Command{
  22. Use: "ls [OPTIONS]",
  23. Aliases: []string{"list"},
  24. Short: "List services",
  25. Args: cli.NoArgs,
  26. RunE: func(cmd *cobra.Command, args []string) error {
  27. return runList(dockerCli, opts)
  28. },
  29. }
  30. flags := cmd.Flags()
  31. flags.BoolVarP(&opts.quiet, "quiet", "q", false, "Only display IDs")
  32. flags.StringVar(&opts.format, "format", "", "Pretty-print services using a Go template")
  33. flags.VarP(&opts.filter, "filter", "f", "Filter output based on conditions provided")
  34. return cmd
  35. }
  36. func runList(dockerCli *command.DockerCli, opts listOptions) error {
  37. ctx := context.Background()
  38. client := dockerCli.Client()
  39. services, err := client.ServiceList(ctx, types.ServiceListOptions{Filters: opts.filter.Value()})
  40. if err != nil {
  41. return err
  42. }
  43. info := map[string]formatter.ServiceListInfo{}
  44. if len(services) > 0 && !opts.quiet {
  45. // only non-empty services and not quiet, should we call TaskList and NodeList api
  46. taskFilter := filters.NewArgs()
  47. for _, service := range services {
  48. taskFilter.Add("service", service.ID)
  49. }
  50. tasks, err := client.TaskList(ctx, types.TaskListOptions{Filters: taskFilter})
  51. if err != nil {
  52. return err
  53. }
  54. nodes, err := client.NodeList(ctx, types.NodeListOptions{})
  55. if err != nil {
  56. return err
  57. }
  58. info = GetServicesStatus(services, nodes, tasks)
  59. }
  60. format := opts.format
  61. if len(format) == 0 {
  62. if len(dockerCli.ConfigFile().ServicesFormat) > 0 && !opts.quiet {
  63. format = dockerCli.ConfigFile().ServicesFormat
  64. } else {
  65. format = formatter.TableFormatKey
  66. }
  67. }
  68. servicesCtx := formatter.Context{
  69. Output: dockerCli.Out(),
  70. Format: formatter.NewServiceListFormat(format, opts.quiet),
  71. }
  72. return formatter.ServiceListWrite(servicesCtx, services, info)
  73. }
  74. // GetServicesStatus returns a map of mode and replicas
  75. func GetServicesStatus(services []swarm.Service, nodes []swarm.Node, tasks []swarm.Task) map[string]formatter.ServiceListInfo {
  76. running := map[string]int{}
  77. tasksNoShutdown := map[string]int{}
  78. activeNodes := make(map[string]struct{})
  79. for _, n := range nodes {
  80. if n.Status.State != swarm.NodeStateDown {
  81. activeNodes[n.ID] = struct{}{}
  82. }
  83. }
  84. for _, task := range tasks {
  85. if task.DesiredState != swarm.TaskStateShutdown {
  86. tasksNoShutdown[task.ServiceID]++
  87. }
  88. if _, nodeActive := activeNodes[task.NodeID]; nodeActive && task.Status.State == swarm.TaskStateRunning {
  89. running[task.ServiceID]++
  90. }
  91. }
  92. info := map[string]formatter.ServiceListInfo{}
  93. for _, service := range services {
  94. info[service.ID] = formatter.ServiceListInfo{}
  95. if service.Spec.Mode.Replicated != nil && service.Spec.Mode.Replicated.Replicas != nil {
  96. info[service.ID] = formatter.ServiceListInfo{
  97. Mode: "replicated",
  98. Replicas: fmt.Sprintf("%d/%d", running[service.ID], *service.Spec.Mode.Replicated.Replicas),
  99. }
  100. } else if service.Spec.Mode.Global != nil {
  101. info[service.ID] = formatter.ServiceListInfo{
  102. Mode: "global",
  103. Replicas: fmt.Sprintf("%d/%d", running[service.ID], tasksNoShutdown[service.ID]),
  104. }
  105. }
  106. }
  107. return info
  108. }