1
0
Fork 0
mirror of https://github.com/moby/moby.git synced 2022-11-09 12:21:53 -05:00
moby--moby/api/client/service/list.go
Aaron Lehmann 614ad95fbb service ls: Show tasks from nodes that are not down, not only "ready" nodes
Currently, the counter only shows tasks on READY nodes. It's more
correct to also count nodes in the other states except DOWN, because
the tasks assocated with those nodes are still assumed to be running for
orchestration purposes. One example of when this could matter is during
a leader failover when agents are in the process of reconnecting.

Signed-off-by: Aaron Lehmann <aaron.lehmann@docker.com>
2016-07-28 11:44:28 -07:00

133 lines
3.4 KiB
Go

package service
import (
"fmt"
"io"
"strings"
"text/tabwriter"
"github.com/docker/docker/api/client"
"github.com/docker/docker/cli"
"github.com/docker/docker/opts"
"github.com/docker/docker/pkg/stringid"
"github.com/docker/engine-api/types"
"github.com/docker/engine-api/types/filters"
"github.com/docker/engine-api/types/swarm"
"github.com/spf13/cobra"
"golang.org/x/net/context"
)
const (
listItemFmt = "%s\t%s\t%s\t%s\t%s\n"
)
type listOptions struct {
quiet bool
filter opts.FilterOpt
}
func newListCommand(dockerCli *client.DockerCli) *cobra.Command {
opts := listOptions{filter: opts.NewFilterOpt()}
cmd := &cobra.Command{
Use: "ls [OPTIONS]",
Aliases: []string{"list"},
Short: "List services",
Args: cli.NoArgs,
RunE: func(cmd *cobra.Command, args []string) error {
return runList(dockerCli, opts)
},
}
flags := cmd.Flags()
flags.BoolVarP(&opts.quiet, "quiet", "q", false, "Only display IDs")
flags.VarP(&opts.filter, "filter", "f", "Filter output based on conditions provided")
return cmd
}
func runList(dockerCli *client.DockerCli, opts listOptions) error {
ctx := context.Background()
client := dockerCli.Client()
services, err := client.ServiceList(ctx, types.ServiceListOptions{Filter: opts.filter.Value()})
if err != nil {
return err
}
out := dockerCli.Out()
if opts.quiet {
PrintQuiet(out, services)
} else {
taskFilter := filters.NewArgs()
for _, service := range services {
taskFilter.Add("service", service.ID)
}
tasks, err := client.TaskList(ctx, types.TaskListOptions{Filter: taskFilter})
if err != nil {
return err
}
nodes, err := client.NodeList(ctx, types.NodeListOptions{})
if err != nil {
return err
}
PrintNotQuiet(out, services, nodes, tasks)
}
return nil
}
// PrintNotQuiet shows service list in a non-quiet way.
// Besides this, command `docker stack services xxx` will call this, too.
func PrintNotQuiet(out io.Writer, services []swarm.Service, nodes []swarm.Node, tasks []swarm.Task) {
activeNodes := make(map[string]struct{})
for _, n := range nodes {
if n.Status.State != swarm.NodeStateDown {
activeNodes[n.ID] = struct{}{}
}
}
running := map[string]int{}
for _, task := range tasks {
if _, nodeActive := activeNodes[task.NodeID]; nodeActive && task.Status.State == "running" {
running[task.ServiceID]++
}
}
printTable(out, services, running)
}
func printTable(out io.Writer, services []swarm.Service, running map[string]int) {
writer := tabwriter.NewWriter(out, 0, 4, 2, ' ', 0)
// Ignore flushing errors
defer writer.Flush()
fmt.Fprintf(writer, listItemFmt, "ID", "NAME", "REPLICAS", "IMAGE", "COMMAND")
for _, service := range services {
replicas := ""
if service.Spec.Mode.Replicated != nil && service.Spec.Mode.Replicated.Replicas != nil {
replicas = fmt.Sprintf("%d/%d", running[service.ID], *service.Spec.Mode.Replicated.Replicas)
} else if service.Spec.Mode.Global != nil {
replicas = "global"
}
fmt.Fprintf(
writer,
listItemFmt,
stringid.TruncateID(service.ID),
service.Spec.Name,
replicas,
service.Spec.TaskTemplate.ContainerSpec.Image,
strings.Join(service.Spec.TaskTemplate.ContainerSpec.Args, " "))
}
}
// PrintQuiet shows service list in a quiet way.
// Besides this, command `docker stack services xxx` will call this, too.
func PrintQuiet(out io.Writer, services []swarm.Service) {
for _, service := range services {
fmt.Fprintln(out, service.ID)
}
}