github.com/containerd/nerdctl@v1.7.7/cmd/nerdctl/container_list.go (about)

     1  /*
     2     Copyright The containerd Authors.
     3  
     4     Licensed under the Apache License, Version 2.0 (the "License");
     5     you may not use this file except in compliance with the License.
     6     You may obtain a copy of the License at
     7  
     8         http://www.apache.org/licenses/LICENSE-2.0
     9  
    10     Unless required by applicable law or agreed to in writing, software
    11     distributed under the License is distributed on an "AS IS" BASIS,
    12     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    13     See the License for the specific language governing permissions and
    14     limitations under the License.
    15  */
    16  
    17  package main
    18  
    19  import (
    20  	"bytes"
    21  	"errors"
    22  	"fmt"
    23  	"io"
    24  	"text/tabwriter"
    25  	"text/template"
    26  
    27  	"github.com/containerd/nerdctl/pkg/api/types"
    28  	"github.com/containerd/nerdctl/pkg/clientutil"
    29  	"github.com/containerd/nerdctl/pkg/cmd/container"
    30  	"github.com/containerd/nerdctl/pkg/formatter"
    31  
    32  	"github.com/spf13/cobra"
    33  )
    34  
    35  func newPsCommand() *cobra.Command {
    36  	var psCommand = &cobra.Command{
    37  		Use:           "ps",
    38  		Args:          cobra.NoArgs,
    39  		Short:         "List containers",
    40  		RunE:          psAction,
    41  		SilenceUsage:  true,
    42  		SilenceErrors: true,
    43  	}
    44  	psCommand.Flags().BoolP("all", "a", false, "Show all containers (default shows just running)")
    45  	psCommand.Flags().IntP("last", "n", -1, "Show n last created containers (includes all states)")
    46  	psCommand.Flags().BoolP("latest", "l", false, "Show the latest created container (includes all states)")
    47  	psCommand.Flags().Bool("no-trunc", false, "Don't truncate output")
    48  	psCommand.Flags().BoolP("quiet", "q", false, "Only display container IDs")
    49  	psCommand.Flags().BoolP("size", "s", false, "Display total file sizes")
    50  
    51  	// Alias "-f" is reserved for "--filter"
    52  	psCommand.Flags().String("format", "", "Format the output using the given Go template, e.g, '{{json .}}', 'wide'")
    53  	psCommand.RegisterFlagCompletionFunc("format", func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) {
    54  		return []string{"json", "table", "wide"}, cobra.ShellCompDirectiveNoFileComp
    55  	})
    56  	psCommand.Flags().StringSliceP("filter", "f", nil, "Filter matches containers based on given conditions")
    57  	return psCommand
    58  }
    59  
    60  func processOptions(cmd *cobra.Command) (types.ContainerListOptions, FormattingAndPrintingOptions, error) {
    61  	globalOptions, err := processRootCmdFlags(cmd)
    62  	if err != nil {
    63  		return types.ContainerListOptions{}, FormattingAndPrintingOptions{}, err
    64  	}
    65  	all, err := cmd.Flags().GetBool("all")
    66  	if err != nil {
    67  		return types.ContainerListOptions{}, FormattingAndPrintingOptions{}, err
    68  	}
    69  	latest, err := cmd.Flags().GetBool("latest")
    70  	if err != nil {
    71  		return types.ContainerListOptions{}, FormattingAndPrintingOptions{}, err
    72  	}
    73  
    74  	lastN, err := cmd.Flags().GetInt("last")
    75  	if err != nil {
    76  		return types.ContainerListOptions{}, FormattingAndPrintingOptions{}, err
    77  	}
    78  	if lastN == -1 && latest {
    79  		lastN = 1
    80  	}
    81  
    82  	filters, err := cmd.Flags().GetStringSlice("filter")
    83  	if err != nil {
    84  		return types.ContainerListOptions{}, FormattingAndPrintingOptions{}, err
    85  	}
    86  
    87  	noTrunc, err := cmd.Flags().GetBool("no-trunc")
    88  	if err != nil {
    89  		return types.ContainerListOptions{}, FormattingAndPrintingOptions{}, err
    90  	}
    91  	trunc := !noTrunc
    92  
    93  	quiet, err := cmd.Flags().GetBool("quiet")
    94  	if err != nil {
    95  		return types.ContainerListOptions{}, FormattingAndPrintingOptions{}, err
    96  	}
    97  	format, err := cmd.Flags().GetString("format")
    98  	if err != nil {
    99  		return types.ContainerListOptions{}, FormattingAndPrintingOptions{}, err
   100  	}
   101  
   102  	size := false
   103  	if !quiet {
   104  		size, err = cmd.Flags().GetBool("size")
   105  		if err != nil {
   106  			return types.ContainerListOptions{}, FormattingAndPrintingOptions{}, err
   107  		}
   108  	}
   109  
   110  	return types.ContainerListOptions{
   111  			GOptions: globalOptions,
   112  			All:      all,
   113  			LastN:    lastN,
   114  			Truncate: trunc,
   115  			Size:     size || (format == "wide" && !quiet),
   116  			Filters:  filters,
   117  		}, FormattingAndPrintingOptions{
   118  			Stdout: cmd.OutOrStdout(),
   119  			Quiet:  quiet,
   120  			Format: format,
   121  			Size:   size,
   122  		}, nil
   123  }
   124  
   125  func psAction(cmd *cobra.Command, args []string) error {
   126  	clOpts, fpOpts, err := processOptions(cmd)
   127  	if err != nil {
   128  		return err
   129  	}
   130  
   131  	client, ctx, cancel, err := clientutil.NewClient(cmd.Context(), clOpts.GOptions.Namespace, clOpts.GOptions.Address)
   132  	if err != nil {
   133  		return err
   134  	}
   135  	defer cancel()
   136  
   137  	containers, err := container.List(ctx, client, clOpts)
   138  	if err != nil {
   139  		return err
   140  	}
   141  
   142  	return formatAndPrintContainerInfo(containers, fpOpts)
   143  }
   144  
   145  // FormattingAndPrintingOptions specifies options for formatting and printing of `nerdctl (container) list`.
   146  type FormattingAndPrintingOptions struct {
   147  	Stdout io.Writer
   148  	// Only display container IDs.
   149  	Quiet bool
   150  	// Format the output using the given Go template (e.g., '{{json .}}', 'table', 'wide').
   151  	Format string
   152  	// Display total file sizes.
   153  	Size bool
   154  }
   155  
   156  func formatAndPrintContainerInfo(containers []container.ListItem, options FormattingAndPrintingOptions) error {
   157  	w := options.Stdout
   158  	var (
   159  		wide bool
   160  		tmpl *template.Template
   161  	)
   162  	switch options.Format {
   163  	case "", "table":
   164  		w = tabwriter.NewWriter(w, 4, 8, 4, ' ', 0)
   165  		if !options.Quiet {
   166  			printHeader := "CONTAINER ID\tIMAGE\tCOMMAND\tCREATED\tSTATUS\tPORTS\tNAMES"
   167  			if options.Size {
   168  				printHeader += "\tSIZE"
   169  			}
   170  			fmt.Fprintln(w, printHeader)
   171  		}
   172  	case "raw":
   173  		return errors.New("unsupported format: \"raw\"")
   174  	case "wide":
   175  		w = tabwriter.NewWriter(w, 4, 8, 4, ' ', 0)
   176  		if !options.Quiet {
   177  			fmt.Fprintln(w, "CONTAINER ID\tIMAGE\tCOMMAND\tCREATED\tSTATUS\tPORTS\tNAMES\tRUNTIME\tPLATFORM\tSIZE")
   178  			wide = true
   179  		}
   180  	default:
   181  		if options.Quiet {
   182  			return errors.New("format and quiet must not be specified together")
   183  		}
   184  		var err error
   185  		tmpl, err = formatter.ParseTemplate(options.Format)
   186  		if err != nil {
   187  			return err
   188  		}
   189  	}
   190  
   191  	for _, c := range containers {
   192  		if tmpl != nil {
   193  			var b bytes.Buffer
   194  			if err := tmpl.Execute(&b, &c); err != nil {
   195  				return err
   196  			}
   197  			if _, err := fmt.Fprintln(w, b.String()); err != nil {
   198  				return err
   199  			}
   200  		} else if options.Quiet {
   201  			if _, err := fmt.Fprintln(w, c.ID); err != nil {
   202  				return err
   203  			}
   204  		} else {
   205  			format := "%s\t%s\t%s\t%s\t%s\t%s\t%s"
   206  			args := []interface{}{
   207  				c.ID,
   208  				c.Image,
   209  				c.Command,
   210  				formatter.TimeSinceInHuman(c.CreatedAt),
   211  				c.Status,
   212  				c.Ports,
   213  				c.Names,
   214  			}
   215  			if wide {
   216  				format += "\t%s\t%s\t%s\n"
   217  				args = append(args, c.Runtime, c.Platform, c.Size)
   218  			} else if options.Size {
   219  				format += "\t%s\n"
   220  				args = append(args, c.Size)
   221  			} else {
   222  				format += "\n"
   223  			}
   224  			if _, err := fmt.Fprintf(w, format, args...); err != nil {
   225  				return err
   226  			}
   227  		}
   228  
   229  	}
   230  	if f, ok := w.(formatter.Flusher); ok {
   231  		return f.Flush()
   232  	}
   233  	return nil
   234  }