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 }