Skip to content

Commit 65f389f

Browse files
committed
Improve crictl inspect[pi] commands to allow filtering
The commands now allow filtering based on various fields or just to inspect all pods, containers or images. Signed-off-by: Sascha Grunert <[email protected]>
1 parent 8fa1e83 commit 65f389f

File tree

3 files changed

+191
-40
lines changed

3 files changed

+191
-40
lines changed

Diff for: cmd/crictl/container.go

+76-8
Original file line numberDiff line numberDiff line change
@@ -508,19 +508,78 @@ var containerStatusCommand = &cli.Command{
508508
Name: "template",
509509
Usage: "The template string is only used when output is go-template; The Template format is golang template",
510510
},
511+
&cli.StringFlag{
512+
Name: "name",
513+
Usage: "Filter by container name regular expression pattern",
514+
},
515+
&cli.StringFlag{
516+
Name: "pod",
517+
Aliases: []string{"p"},
518+
Usage: "Filter by pod id",
519+
},
520+
&cli.StringFlag{
521+
Name: "image",
522+
Usage: "Filter by container image",
523+
},
524+
&cli.StringFlag{
525+
Name: "state",
526+
Aliases: []string{"s"},
527+
Usage: "Filter by container state",
528+
},
529+
&cli.StringSliceFlag{
530+
Name: "label",
531+
Usage: "Filter by key=value label",
532+
},
533+
&cli.BoolFlag{
534+
Name: "latest",
535+
Aliases: []string{"l"},
536+
Usage: "Show the most recently created container (includes all states)",
537+
},
538+
&cli.IntFlag{
539+
Name: "last",
540+
Aliases: []string{"n"},
541+
Usage: "Show last n recently created containers (includes all states). Set 0 for unlimited.",
542+
},
511543
},
512544
Action: func(c *cli.Context) error {
513-
if c.NArg() == 0 {
514-
return errors.New("ID cannot be empty")
515-
}
516545
runtimeClient, err := getRuntimeService(c, 0)
517546
if err != nil {
518547
return err
519548
}
520549

550+
ids := c.Args().Slice()
551+
552+
if len(ids) == 0 {
553+
opts := &listOptions{
554+
nameRegexp: c.String("name"),
555+
podID: c.String("pod"),
556+
image: c.String("image"),
557+
state: c.String("state"),
558+
latest: c.Bool("latest"),
559+
last: c.Int("last"),
560+
}
561+
opts.labels, err = parseLabelStringSlice(c.StringSlice("label"))
562+
if err != nil {
563+
return err
564+
}
565+
566+
ctrs, err := ListContainers(runtimeClient, opts)
567+
if err != nil {
568+
return fmt.Errorf("listing containers: %w", err)
569+
}
570+
for _, ctr := range ctrs {
571+
ids = append(ids, ctr.GetId())
572+
}
573+
}
574+
575+
if len(ids) == 0 {
576+
logrus.Error("No IDs provided or nothing found per filter")
577+
return cli.ShowSubcommandHelp(c)
578+
}
579+
521580
if err := containerStatus(
522581
runtimeClient,
523-
c.Args().Slice(),
582+
ids,
524583
c.String("output"),
525584
c.String("template"),
526585
c.Bool("quiet"),
@@ -644,7 +703,7 @@ var listContainersCommand = &cli.Command{
644703
return err
645704
}
646705

647-
if err = ListContainers(runtimeClient, imageClient, opts); err != nil {
706+
if err = OutputContainers(runtimeClient, imageClient, opts); err != nil {
648707
return fmt.Errorf("listing containers: %w", err)
649708
}
650709
return nil
@@ -1091,7 +1150,7 @@ func outputContainerStatusTable(r *pb.ContainerStatusResponse, verbose bool) {
10911150

10921151
// ListContainers sends a ListContainerRequest to the server, and parses
10931152
// the returned ListContainerResponse.
1094-
func ListContainers(runtimeClient internalapi.RuntimeService, imageClient internalapi.ImageManagerService, opts *listOptions) error {
1153+
func ListContainers(runtimeClient internalapi.RuntimeService, opts *listOptions) ([]*pb.Container, error) {
10951154
filter := &pb.ContainerFilter{}
10961155
if opts.id != "" {
10971156
filter.Id = opts.id
@@ -1135,9 +1194,18 @@ func ListContainers(runtimeClient internalapi.RuntimeService, imageClient intern
11351194
})
11361195
logrus.Debugf("ListContainerResponse: %v", r)
11371196
if err != nil {
1138-
return err
1197+
return nil, fmt.Errorf("call list containers RPC: %w", err)
1198+
}
1199+
return getContainersList(r, opts), nil
1200+
}
1201+
1202+
// OutputContainers sends a ListContainerRequest to the server, and parses
1203+
// the returned ListContainerResponse for output.
1204+
func OutputContainers(runtimeClient internalapi.RuntimeService, imageClient internalapi.ImageManagerService, opts *listOptions) error {
1205+
r, err := ListContainers(runtimeClient, opts)
1206+
if err != nil {
1207+
return fmt.Errorf("list containers: %w", err)
11391208
}
1140-
r = getContainersList(r, opts)
11411209

11421210
switch opts.output {
11431211
case outputTypeJSON:

Diff for: cmd/crictl/image.go

+40-18
Original file line numberDiff line numberDiff line change
@@ -195,20 +195,11 @@ var listImageCommand = &cli.Command{
195195
return err
196196
}
197197

198-
r, err := ListImages(imageClient, c.Args().First())
198+
r, err := ListImages(imageClient, c.Args().First(), c.StringSlice("filter"))
199199
if err != nil {
200200
return fmt.Errorf("listing images: %w", err)
201201
}
202202

203-
sort.Sort(imageByRef(r.Images))
204-
205-
if len(c.StringSlice("filter")) > 0 && len(r.Images) > 0 {
206-
r.Images, err = filterImagesList(r.Images, c.StringSlice("filter"))
207-
if err != nil {
208-
return fmt.Errorf("listing images: %w", err)
209-
}
210-
}
211-
212203
switch c.String("output") {
213204
case outputTypeJSON:
214205
return outputProtobufObjAsJSON(r)
@@ -307,11 +298,17 @@ var imageStatusCommand = &cli.Command{
307298
Name: "template",
308299
Usage: "The template string is only used when output is go-template; The Template format is golang template",
309300
},
301+
&cli.StringFlag{
302+
Name: "name",
303+
Usage: "Filter by image name",
304+
},
305+
&cli.StringSliceFlag{
306+
Name: "filter",
307+
Aliases: []string{"f"},
308+
Usage: "Filter output based on provided conditions.\nAvailable filters: \n* dangling=(boolean - true or false)\n* reference=/regular expression/\n* before=<image-name>[:<tag>]|<image id>|<image@digest>\n* since=<image-name>[:<tag>]|<image id>|<image@digest>\nMultiple filters can be combined together.",
309+
},
310310
},
311311
Action: func(c *cli.Context) error {
312-
if c.NArg() == 0 {
313-
return cli.ShowSubcommandHelp(c)
314-
}
315312
imageClient, err := getImageService(c)
316313
if err != nil {
317314
return err
@@ -324,10 +321,25 @@ var imageStatusCommand = &cli.Command{
324321
}
325322
tmplStr := c.String("template")
326323

327-
statuses := []statusData{}
328-
for i := range c.NArg() {
329-
id := c.Args().Get(i)
324+
ids := c.Args().Slice()
325+
326+
if len(ids) == 0 {
327+
r, err := ListImages(imageClient, c.String("name"), c.StringSlice("filter"))
328+
if err != nil {
329+
return fmt.Errorf("listing images: %w", err)
330+
}
331+
for _, img := range r.GetImages() {
332+
ids = append(ids, img.GetId())
333+
}
334+
}
330335

336+
if len(ids) == 0 {
337+
logrus.Error("No IDs provided or nothing found per filter")
338+
return cli.ShowSubcommandHelp(c)
339+
}
340+
341+
statuses := []statusData{}
342+
for _, id := range ids {
331343
r, err := ImageStatus(imageClient, id, verbose)
332344
if err != nil {
333345
return fmt.Errorf("image status for %q request: %w", id, err)
@@ -688,8 +700,8 @@ func PullImageWithSandbox(client internalapi.ImageManagerService, image string,
688700

689701
// ListImages sends a ListImagesRequest to the server, and parses
690702
// the returned ListImagesResponse.
691-
func ListImages(client internalapi.ImageManagerService, image string) (*pb.ListImagesResponse, error) {
692-
request := &pb.ListImagesRequest{Filter: &pb.ImageFilter{Image: &pb.ImageSpec{Image: image}}}
703+
func ListImages(client internalapi.ImageManagerService, nameFilter string, conditionFilters []string) (*pb.ListImagesResponse, error) {
704+
request := &pb.ListImagesRequest{Filter: &pb.ImageFilter{Image: &pb.ImageSpec{Image: nameFilter}}}
693705
logrus.Debugf("ListImagesRequest: %v", request)
694706
res, err := InterruptableRPC(nil, func(ctx context.Context) ([]*pb.Image, error) {
695707
return client.ListImages(ctx, request.Filter)
@@ -699,6 +711,16 @@ func ListImages(client internalapi.ImageManagerService, image string) (*pb.ListI
699711
}
700712
resp := &pb.ListImagesResponse{Images: res}
701713
logrus.Debugf("ListImagesResponse: %v", resp)
714+
715+
sort.Sort(imageByRef(resp.Images))
716+
717+
if len(conditionFilters) > 0 && len(resp.Images) > 0 {
718+
resp.Images, err = filterImagesList(resp.Images, conditionFilters)
719+
if err != nil {
720+
return nil, fmt.Errorf("filter images: %w", err)
721+
}
722+
}
723+
702724
return resp, nil
703725
}
704726

Diff for: cmd/crictl/sandbox.go

+75-14
Original file line numberDiff line numberDiff line change
@@ -219,19 +219,71 @@ var podStatusCommand = &cli.Command{
219219
Name: "template",
220220
Usage: "The template string is only used when output is go-template; The Template format is golang template",
221221
},
222+
&cli.StringFlag{
223+
Name: "name",
224+
Usage: "Filter by pod name regular expression pattern",
225+
},
226+
&cli.StringFlag{
227+
Name: "namespace",
228+
Usage: "Filter by pod namespace regular expression pattern",
229+
},
230+
&cli.StringFlag{
231+
Name: "state",
232+
Aliases: []string{"s"},
233+
Usage: "Filter by pod state",
234+
},
235+
&cli.StringSliceFlag{
236+
Name: "label",
237+
Usage: "Filter by key=value label",
238+
},
239+
&cli.BoolFlag{
240+
Name: "latest",
241+
Aliases: []string{"l"},
242+
Usage: "Show the most recently created pod",
243+
},
244+
&cli.IntFlag{
245+
Name: "last",
246+
Aliases: []string{"n"},
247+
Usage: "Show last n recently created pods. Set 0 for unlimited",
248+
},
222249
},
223250
Action: func(c *cli.Context) error {
224-
if c.NArg() == 0 {
225-
return cli.ShowSubcommandHelp(c)
226-
}
227251
runtimeClient, err := getRuntimeService(c, 0)
228252
if err != nil {
229253
return err
230254
}
231255

256+
ids := c.Args().Slice()
257+
258+
if len(ids) == 0 {
259+
opts := &listOptions{
260+
nameRegexp: c.String("name"),
261+
podNamespaceRegexp: c.String("namespace"),
262+
state: c.String("state"),
263+
latest: c.Bool("latest"),
264+
last: c.Int("last"),
265+
}
266+
opts.labels, err = parseLabelStringSlice(c.StringSlice("label"))
267+
if err != nil {
268+
return fmt.Errorf("parse label string slice: %w", err)
269+
}
270+
sbs, err := ListPodSandboxes(runtimeClient, opts)
271+
if err != nil {
272+
return fmt.Errorf("listing pod sandboxes: %w", err)
273+
}
274+
for _, sb := range sbs {
275+
ids = append(ids, sb.GetId())
276+
}
277+
}
278+
279+
if len(ids) == 0 {
280+
logrus.Error("No IDs provided or nothing found per filter")
281+
return cli.ShowSubcommandHelp(c)
282+
}
283+
232284
if err := podSandboxStatus(
233285
runtimeClient,
234-
c.Args().Slice(),
286+
ids,
235287
c.String("output"),
236288
c.Bool("quiet"),
237289
c.String("template"),
@@ -256,32 +308,32 @@ var listPodCommand = &cli.Command{
256308
&cli.StringFlag{
257309
Name: "name",
258310
Value: "",
259-
Usage: "filter by pod name regular expression pattern",
311+
Usage: "Filter by pod name regular expression pattern",
260312
},
261313
&cli.StringFlag{
262314
Name: "namespace",
263315
Value: "",
264-
Usage: "filter by pod namespace regular expression pattern",
316+
Usage: "Filter by pod namespace regular expression pattern",
265317
},
266318
&cli.StringFlag{
267319
Name: "state",
268320
Aliases: []string{"s"},
269321
Value: "",
270-
Usage: "filter by pod state",
322+
Usage: "Filter by pod state",
271323
},
272324
&cli.StringSliceFlag{
273325
Name: "label",
274-
Usage: "filter by key=value label",
326+
Usage: "Filter by key=value label",
275327
},
276328
&cli.BoolFlag{
277329
Name: "verbose",
278330
Aliases: []string{"v"},
279-
Usage: "show verbose info for pods",
331+
Usage: "Show verbose info for pods",
280332
},
281333
&cli.BoolFlag{
282334
Name: "quiet",
283335
Aliases: []string{"q"},
284-
Usage: "list only pod IDs",
336+
Usage: "List only pod IDs",
285337
},
286338
&cli.StringFlag{
287339
Name: "output",
@@ -327,7 +379,7 @@ var listPodCommand = &cli.Command{
327379
if err != nil {
328380
return err
329381
}
330-
if err = ListPodSandboxes(runtimeClient, opts); err != nil {
382+
if err = OutputPodSandboxes(runtimeClient, opts); err != nil {
331383
return fmt.Errorf("listing pod sandboxes: %w", err)
332384
}
333385
return nil
@@ -487,7 +539,7 @@ func outputPodSandboxStatusTable(r *pb.PodSandboxStatusResponse, verbose bool) {
487539

488540
// ListPodSandboxes sends a ListPodSandboxRequest to the server, and parses
489541
// the returned ListPodSandboxResponse.
490-
func ListPodSandboxes(client internalapi.RuntimeService, opts *listOptions) error {
542+
func ListPodSandboxes(client internalapi.RuntimeService, opts *listOptions) ([]*pb.PodSandbox, error) {
491543
filter := &pb.PodSandboxFilter{}
492544
if opts.id != "" {
493545
filter.Id = opts.id
@@ -518,9 +570,18 @@ func ListPodSandboxes(client internalapi.RuntimeService, opts *listOptions) erro
518570
})
519571
logrus.Debugf("ListPodSandboxResponse: %v", r)
520572
if err != nil {
521-
return err
573+
return nil, fmt.Errorf("call list sandboxes RPC: %w", err)
574+
}
575+
return getSandboxesList(r, opts), nil
576+
}
577+
578+
// OutputPodSandboxes sends a ListPodSandboxRequest to the server, and parses
579+
// the returned ListPodSandboxResponse for output.
580+
func OutputPodSandboxes(client internalapi.RuntimeService, opts *listOptions) error {
581+
r, err := ListPodSandboxes(client, opts)
582+
if err != nil {
583+
return fmt.Errorf("list pod sandboxes: %w", err)
522584
}
523-
r = getSandboxesList(r, opts)
524585

525586
switch opts.output {
526587
case outputTypeJSON:

0 commit comments

Comments
 (0)