github.com/olljanat/moby@v1.13.1/cli/command/stack/list.go (about)

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