github.com/vieux/docker@v0.6.3-0.20161004191708-e097c2a938c7/cli/command/service/list.go (about) 1 package service 2 3 import ( 4 "fmt" 5 "io" 6 "strings" 7 "text/tabwriter" 8 9 "github.com/docker/docker/api/types" 10 "github.com/docker/docker/api/types/filters" 11 "github.com/docker/docker/api/types/swarm" 12 "github.com/docker/docker/cli" 13 "github.com/docker/docker/cli/command" 14 "github.com/docker/docker/opts" 15 "github.com/docker/docker/pkg/stringid" 16 "github.com/spf13/cobra" 17 "golang.org/x/net/context" 18 ) 19 20 const ( 21 listItemFmt = "%s\t%s\t%s\t%s\t%s\n" 22 ) 23 24 type listOptions struct { 25 quiet bool 26 filter opts.FilterOpt 27 } 28 29 func newListCommand(dockerCli *command.DockerCli) *cobra.Command { 30 opts := listOptions{filter: opts.NewFilterOpt()} 31 32 cmd := &cobra.Command{ 33 Use: "ls [OPTIONS]", 34 Aliases: []string{"list"}, 35 Short: "List services", 36 Args: cli.NoArgs, 37 RunE: func(cmd *cobra.Command, args []string) error { 38 return runList(dockerCli, opts) 39 }, 40 } 41 42 flags := cmd.Flags() 43 flags.BoolVarP(&opts.quiet, "quiet", "q", false, "Only display IDs") 44 flags.VarP(&opts.filter, "filter", "f", "Filter output based on conditions provided") 45 46 return cmd 47 } 48 49 func runList(dockerCli *command.DockerCli, opts listOptions) error { 50 ctx := context.Background() 51 client := dockerCli.Client() 52 53 services, err := client.ServiceList(ctx, types.ServiceListOptions{Filter: opts.filter.Value()}) 54 if err != nil { 55 return err 56 } 57 58 out := dockerCli.Out() 59 if opts.quiet { 60 PrintQuiet(out, services) 61 } else { 62 taskFilter := filters.NewArgs() 63 for _, service := range services { 64 taskFilter.Add("service", service.ID) 65 } 66 67 tasks, err := client.TaskList(ctx, types.TaskListOptions{Filter: taskFilter}) 68 if err != nil { 69 return err 70 } 71 72 nodes, err := client.NodeList(ctx, types.NodeListOptions{}) 73 if err != nil { 74 return err 75 } 76 77 PrintNotQuiet(out, services, nodes, tasks) 78 } 79 return nil 80 } 81 82 // PrintNotQuiet shows service list in a non-quiet way. 83 // Besides this, command `docker stack services xxx` will call this, too. 84 func PrintNotQuiet(out io.Writer, services []swarm.Service, nodes []swarm.Node, tasks []swarm.Task) { 85 activeNodes := make(map[string]struct{}) 86 for _, n := range nodes { 87 if n.Status.State != swarm.NodeStateDown { 88 activeNodes[n.ID] = struct{}{} 89 } 90 } 91 92 running := map[string]int{} 93 for _, task := range tasks { 94 if _, nodeActive := activeNodes[task.NodeID]; nodeActive && task.Status.State == "running" { 95 running[task.ServiceID]++ 96 } 97 } 98 99 printTable(out, services, running) 100 } 101 102 func printTable(out io.Writer, services []swarm.Service, running map[string]int) { 103 writer := tabwriter.NewWriter(out, 0, 4, 2, ' ', 0) 104 105 // Ignore flushing errors 106 defer writer.Flush() 107 108 fmt.Fprintf(writer, listItemFmt, "ID", "NAME", "REPLICAS", "IMAGE", "COMMAND") 109 for _, service := range services { 110 replicas := "" 111 if service.Spec.Mode.Replicated != nil && service.Spec.Mode.Replicated.Replicas != nil { 112 replicas = fmt.Sprintf("%d/%d", running[service.ID], *service.Spec.Mode.Replicated.Replicas) 113 } else if service.Spec.Mode.Global != nil { 114 replicas = "global" 115 } 116 fmt.Fprintf( 117 writer, 118 listItemFmt, 119 stringid.TruncateID(service.ID), 120 service.Spec.Name, 121 replicas, 122 service.Spec.TaskTemplate.ContainerSpec.Image, 123 strings.Join(service.Spec.TaskTemplate.ContainerSpec.Args, " ")) 124 } 125 } 126 127 // PrintQuiet shows service list in a quiet way. 128 // Besides this, command `docker stack services xxx` will call this, too. 129 func PrintQuiet(out io.Writer, services []swarm.Service) { 130 for _, service := range services { 131 fmt.Fprintln(out, service.ID) 132 } 133 }