github.1git.de/docker/cli@v26.1.3+incompatible/cli/command/container/list.go (about)

     1  package container
     2  
     3  import (
     4  	"context"
     5  	"io"
     6  
     7  	"github.com/docker/cli/cli"
     8  	"github.com/docker/cli/cli/command"
     9  	"github.com/docker/cli/cli/command/completion"
    10  	"github.com/docker/cli/cli/command/formatter"
    11  	flagsHelper "github.com/docker/cli/cli/flags"
    12  	"github.com/docker/cli/opts"
    13  	"github.com/docker/cli/templates"
    14  	"github.com/docker/docker/api/types/container"
    15  	"github.com/pkg/errors"
    16  	"github.com/spf13/cobra"
    17  )
    18  
    19  type psOptions struct {
    20  	quiet       bool
    21  	size        bool
    22  	sizeChanged bool
    23  	all         bool
    24  	noTrunc     bool
    25  	nLatest     bool
    26  	last        int
    27  	format      string
    28  	filter      opts.FilterOpt
    29  }
    30  
    31  // NewPsCommand creates a new cobra.Command for `docker ps`
    32  func NewPsCommand(dockerCLI command.Cli) *cobra.Command {
    33  	options := psOptions{filter: opts.NewFilterOpt()}
    34  
    35  	cmd := &cobra.Command{
    36  		Use:   "ps [OPTIONS]",
    37  		Short: "List containers",
    38  		Args:  cli.NoArgs,
    39  		RunE: func(cmd *cobra.Command, args []string) error {
    40  			options.sizeChanged = cmd.Flags().Changed("size")
    41  			return runPs(cmd.Context(), dockerCLI, &options)
    42  		},
    43  		Annotations: map[string]string{
    44  			"category-top": "3",
    45  			"aliases":      "docker container ls, docker container list, docker container ps, docker ps",
    46  		},
    47  		ValidArgsFunction: completion.NoComplete,
    48  	}
    49  
    50  	flags := cmd.Flags()
    51  
    52  	flags.BoolVarP(&options.quiet, "quiet", "q", false, "Only display container IDs")
    53  	flags.BoolVarP(&options.size, "size", "s", false, "Display total file sizes")
    54  	flags.BoolVarP(&options.all, "all", "a", false, "Show all containers (default shows just running)")
    55  	flags.BoolVar(&options.noTrunc, "no-trunc", false, "Don't truncate output")
    56  	flags.BoolVarP(&options.nLatest, "latest", "l", false, "Show the latest created container (includes all states)")
    57  	flags.IntVarP(&options.last, "last", "n", -1, "Show n last created containers (includes all states)")
    58  	flags.StringVar(&options.format, "format", "", flagsHelper.FormatHelp)
    59  	flags.VarP(&options.filter, "filter", "f", "Filter output based on conditions provided")
    60  
    61  	return cmd
    62  }
    63  
    64  func newListCommand(dockerCLI command.Cli) *cobra.Command {
    65  	cmd := *NewPsCommand(dockerCLI)
    66  	cmd.Aliases = []string{"ps", "list"}
    67  	cmd.Use = "ls [OPTIONS]"
    68  	return &cmd
    69  }
    70  
    71  func buildContainerListOptions(options *psOptions) (*container.ListOptions, error) {
    72  	listOptions := &container.ListOptions{
    73  		All:     options.all,
    74  		Limit:   options.last,
    75  		Size:    options.size,
    76  		Filters: options.filter.Value(),
    77  	}
    78  
    79  	if options.nLatest && options.last == -1 {
    80  		listOptions.Limit = 1
    81  	}
    82  
    83  	// always validate template when `--format` is used, for consistency
    84  	if len(options.format) > 0 {
    85  		tmpl, err := templates.NewParse("", options.format)
    86  		if err != nil {
    87  			return nil, errors.Wrap(err, "failed to parse template")
    88  		}
    89  
    90  		optionsProcessor := formatter.NewContainerContext()
    91  
    92  		// This shouldn't error out but swallowing the error makes it harder
    93  		// to track down if preProcessor issues come up.
    94  		if err := tmpl.Execute(io.Discard, optionsProcessor); err != nil {
    95  			return nil, errors.Wrap(err, "failed to execute template")
    96  		}
    97  
    98  		// if `size` was not explicitly set to false (with `--size=false`)
    99  		// and `--quiet` is not set, request size if the template requires it
   100  		if !options.quiet && !listOptions.Size && !options.sizeChanged {
   101  			// The --size option isn't set, but .Size may be used in the template.
   102  			// Parse and execute the given template to detect if the .Size field is
   103  			// used. If it is, then automatically enable the --size option. See #24696
   104  			//
   105  			// Only requesting container size information when needed is an optimization,
   106  			// because calculating the size is a costly operation.
   107  
   108  			if _, ok := optionsProcessor.FieldsUsed["Size"]; ok {
   109  				listOptions.Size = true
   110  			}
   111  		}
   112  	}
   113  
   114  	return listOptions, nil
   115  }
   116  
   117  func runPs(ctx context.Context, dockerCLI command.Cli, options *psOptions) error {
   118  	if len(options.format) == 0 {
   119  		// load custom psFormat from CLI config (if any)
   120  		options.format = dockerCLI.ConfigFile().PsFormat
   121  	} else if options.quiet {
   122  		_, _ = dockerCLI.Err().Write([]byte("WARNING: Ignoring custom format, because both --format and --quiet are set.\n"))
   123  	}
   124  
   125  	listOptions, err := buildContainerListOptions(options)
   126  	if err != nil {
   127  		return err
   128  	}
   129  
   130  	containers, err := dockerCLI.Client().ContainerList(ctx, *listOptions)
   131  	if err != nil {
   132  		return err
   133  	}
   134  
   135  	containerCtx := formatter.Context{
   136  		Output: dockerCLI.Out(),
   137  		Format: formatter.NewContainerFormat(options.format, options.quiet, listOptions.Size),
   138  		Trunc:  !options.noTrunc,
   139  	}
   140  	return formatter.ContainerWrite(containerCtx, containers)
   141  }