github.com/openshift/moby-moby@v1.13.2-0.20170601211448-f5ec1e2936dc/cli/command/service/list.go (about) 1 package service 2 3 import ( 4 "fmt" 5 "io" 6 "text/tabwriter" 7 8 distreference "github.com/docker/distribution/reference" 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 out := dockerCli.Out() 53 54 services, err := client.ServiceList(ctx, types.ServiceListOptions{Filters: opts.filter.Value()}) 55 if err != nil { 56 return err 57 } 58 59 if len(services) > 0 && !opts.quiet { 60 // only non-empty services and not quiet, should we call TaskList and NodeList api 61 taskFilter := filters.NewArgs() 62 for _, service := range services { 63 taskFilter.Add("service", service.ID) 64 } 65 66 tasks, err := client.TaskList(ctx, types.TaskListOptions{Filters: taskFilter}) 67 if err != nil { 68 return err 69 } 70 71 nodes, err := client.NodeList(ctx, types.NodeListOptions{}) 72 if err != nil { 73 return err 74 } 75 76 PrintNotQuiet(out, services, nodes, tasks) 77 } else if !opts.quiet { 78 // no services and not quiet, print only one line with columns ID, NAME, MODE, REPLICAS... 79 PrintNotQuiet(out, services, []swarm.Node{}, []swarm.Task{}) 80 } else { 81 PrintQuiet(out, services) 82 } 83 84 return nil 85 } 86 87 // PrintNotQuiet shows service list in a non-quiet way. 88 // Besides this, command `docker stack services xxx` will call this, too. 89 func PrintNotQuiet(out io.Writer, services []swarm.Service, nodes []swarm.Node, tasks []swarm.Task) { 90 activeNodes := make(map[string]struct{}) 91 for _, n := range nodes { 92 if n.Status.State != swarm.NodeStateDown { 93 activeNodes[n.ID] = struct{}{} 94 } 95 } 96 97 running := map[string]int{} 98 tasksNoShutdown := map[string]int{} 99 100 for _, task := range tasks { 101 if task.DesiredState != swarm.TaskStateShutdown { 102 tasksNoShutdown[task.ServiceID]++ 103 } 104 105 if _, nodeActive := activeNodes[task.NodeID]; nodeActive && task.Status.State == swarm.TaskStateRunning { 106 running[task.ServiceID]++ 107 } 108 } 109 110 printTable(out, services, running, tasksNoShutdown) 111 } 112 113 func printTable(out io.Writer, services []swarm.Service, running, tasksNoShutdown map[string]int) { 114 writer := tabwriter.NewWriter(out, 0, 4, 2, ' ', 0) 115 116 // Ignore flushing errors 117 defer writer.Flush() 118 119 fmt.Fprintf(writer, listItemFmt, "ID", "NAME", "MODE", "REPLICAS", "IMAGE") 120 121 for _, service := range services { 122 mode := "" 123 replicas := "" 124 if service.Spec.Mode.Replicated != nil && service.Spec.Mode.Replicated.Replicas != nil { 125 mode = "replicated" 126 replicas = fmt.Sprintf("%d/%d", running[service.ID], *service.Spec.Mode.Replicated.Replicas) 127 } else if service.Spec.Mode.Global != nil { 128 mode = "global" 129 replicas = fmt.Sprintf("%d/%d", running[service.ID], tasksNoShutdown[service.ID]) 130 } 131 image := service.Spec.TaskTemplate.ContainerSpec.Image 132 ref, err := distreference.ParseNamed(image) 133 if err == nil { 134 // update image string for display 135 namedTagged, ok := ref.(distreference.NamedTagged) 136 if ok { 137 image = namedTagged.Name() + ":" + namedTagged.Tag() 138 } 139 } 140 141 fmt.Fprintf( 142 writer, 143 listItemFmt, 144 stringid.TruncateID(service.ID), 145 service.Spec.Name, 146 mode, 147 replicas, 148 image) 149 } 150 } 151 152 // PrintQuiet shows service list in a quiet way. 153 // Besides this, command `docker stack services xxx` will call this, too. 154 func PrintQuiet(out io.Writer, services []swarm.Service) { 155 for _, service := range services { 156 fmt.Fprintln(out, service.ID) 157 } 158 }