Skip to content

Commit b0d297a

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 <sgrunert@redhat.com>
1 parent 8fa1e83 commit b0d297a

File tree

3 files changed

+199
-40
lines changed

3 files changed

+199
-40
lines changed

cmd/crictl/container.go

+80-8
Original file line numberDiff line numberDiff line change
@@ -508,19 +508,82 @@ 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+
Value: "",
514+
Usage: "Filter by container name regular expression pattern",
515+
},
516+
&cli.StringFlag{
517+
Name: "pod",
518+
Aliases: []string{"p"},
519+
Value: "",
520+
Usage: "Filter by pod id",
521+
},
522+
&cli.StringFlag{
523+
Name: "image",
524+
Value: "",
525+
Usage: "Filter by container image",
526+
},
527+
&cli.StringFlag{
528+
Name: "state",
529+
Aliases: []string{"s"},
530+
Value: "",
531+
Usage: "Filter by container state",
532+
},
533+
&cli.StringSliceFlag{
534+
Name: "label",
535+
Usage: "Filter by key=value label",
536+
},
537+
&cli.BoolFlag{
538+
Name: "latest",
539+
Aliases: []string{"l"},
540+
Usage: "Show the most recently created container (includes all states)",
541+
},
542+
&cli.IntFlag{
543+
Name: "last",
544+
Aliases: []string{"n"},
545+
Usage: "Show last n recently created containers (includes all states). Set 0 for unlimited.",
546+
},
511547
},
512548
Action: func(c *cli.Context) error {
513-
if c.NArg() == 0 {
514-
return errors.New("ID cannot be empty")
515-
}
516549
runtimeClient, err := getRuntimeService(c, 0)
517550
if err != nil {
518551
return err
519552
}
520553

554+
ids := c.Args().Slice()
555+
556+
if len(ids) == 0 {
557+
opts := &listOptions{
558+
nameRegexp: c.String("name"),
559+
podID: c.String("pod"),
560+
image: c.String("image"),
561+
state: c.String("state"),
562+
latest: c.Bool("latest"),
563+
last: c.Int("last"),
564+
}
565+
opts.labels, err = parseLabelStringSlice(c.StringSlice("label"))
566+
if err != nil {
567+
return err
568+
}
569+
570+
ctrs, err := ListContainers(runtimeClient, opts)
571+
if err != nil {
572+
return fmt.Errorf("list containers: %w", err)
573+
}
574+
for _, ctr := range ctrs {
575+
ids = append(ids, ctr.GetId())
576+
}
577+
}
578+
579+
if len(ids) == 0 {
580+
logrus.Error("No IDs provided or nothing found per filter")
581+
return cli.ShowSubcommandHelp(c)
582+
}
583+
521584
if err := containerStatus(
522585
runtimeClient,
523-
c.Args().Slice(),
586+
ids,
524587
c.String("output"),
525588
c.String("template"),
526589
c.Bool("quiet"),
@@ -644,7 +707,7 @@ var listContainersCommand = &cli.Command{
644707
return err
645708
}
646709

647-
if err = ListContainers(runtimeClient, imageClient, opts); err != nil {
710+
if err = OutputContainers(runtimeClient, imageClient, opts); err != nil {
648711
return fmt.Errorf("listing containers: %w", err)
649712
}
650713
return nil
@@ -1091,7 +1154,7 @@ func outputContainerStatusTable(r *pb.ContainerStatusResponse, verbose bool) {
10911154

10921155
// ListContainers sends a ListContainerRequest to the server, and parses
10931156
// the returned ListContainerResponse.
1094-
func ListContainers(runtimeClient internalapi.RuntimeService, imageClient internalapi.ImageManagerService, opts *listOptions) error {
1157+
func ListContainers(runtimeClient internalapi.RuntimeService, opts *listOptions) ([]*pb.Container, error) {
10951158
filter := &pb.ContainerFilter{}
10961159
if opts.id != "" {
10971160
filter.Id = opts.id
@@ -1135,9 +1198,18 @@ func ListContainers(runtimeClient internalapi.RuntimeService, imageClient intern
11351198
})
11361199
logrus.Debugf("ListContainerResponse: %v", r)
11371200
if err != nil {
1138-
return err
1201+
return nil, fmt.Errorf("call list containers RPC: %w", err)
1202+
}
1203+
return getContainersList(r, opts), nil
1204+
}
1205+
1206+
// OutputContainers sends a ListContainerRequest to the server, and parses
1207+
// the returned ListContainerResponse for output.
1208+
func OutputContainers(runtimeClient internalapi.RuntimeService, imageClient internalapi.ImageManagerService, opts *listOptions) error {
1209+
r, err := ListContainers(runtimeClient, opts)
1210+
if err != nil {
1211+
return fmt.Errorf("list containers: %w", err)
11391212
}
1140-
r = getContainersList(r, opts)
11411213

11421214
switch opts.output {
11431215
case outputTypeJSON:

cmd/crictl/image.go

+41-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,18 @@ 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+
Value: "",
304+
Usage: "Filter by image name",
305+
},
306+
&cli.StringSliceFlag{
307+
Name: "filter",
308+
Aliases: []string{"f"},
309+
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.",
310+
},
310311
},
311312
Action: func(c *cli.Context) error {
312-
if c.NArg() == 0 {
313-
return cli.ShowSubcommandHelp(c)
314-
}
315313
imageClient, err := getImageService(c)
316314
if err != nil {
317315
return err
@@ -324,10 +322,25 @@ var imageStatusCommand = &cli.Command{
324322
}
325323
tmplStr := c.String("template")
326324

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

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

689702
// ListImages sends a ListImagesRequest to the server, and parses
690703
// 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}}}
704+
func ListImages(client internalapi.ImageManagerService, nameFilter string, conditionFilters []string) (*pb.ListImagesResponse, error) {
705+
request := &pb.ListImagesRequest{Filter: &pb.ImageFilter{Image: &pb.ImageSpec{Image: nameFilter}}}
693706
logrus.Debugf("ListImagesRequest: %v", request)
694707
res, err := InterruptableRPC(nil, func(ctx context.Context) ([]*pb.Image, error) {
695708
return client.ListImages(ctx, request.Filter)
@@ -699,6 +712,16 @@ func ListImages(client internalapi.ImageManagerService, image string) (*pb.ListI
699712
}
700713
resp := &pb.ListImagesResponse{Images: res}
701714
logrus.Debugf("ListImagesResponse: %v", resp)
715+
716+
sort.Sort(imageByRef(resp.Images))
717+
718+
if len(conditionFilters) > 0 && len(resp.Images) > 0 {
719+
resp.Images, err = filterImagesList(resp.Images, conditionFilters)
720+
if err != nil {
721+
return nil, fmt.Errorf("filter images: %w", err)
722+
}
723+
}
724+
702725
return resp, nil
703726
}
704727

cmd/crictl/sandbox.go

+78-14
Original file line numberDiff line numberDiff line change
@@ -219,19 +219,74 @@ 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+
Value: "",
225+
Usage: "Filter by pod name regular expression pattern",
226+
},
227+
&cli.StringFlag{
228+
Name: "namespace",
229+
Value: "",
230+
Usage: "Filter by pod namespace regular expression pattern",
231+
},
232+
&cli.StringFlag{
233+
Name: "state",
234+
Aliases: []string{"s"},
235+
Value: "",
236+
Usage: "Filter by pod state",
237+
},
238+
&cli.StringSliceFlag{
239+
Name: "label",
240+
Usage: "Filter by key=value label",
241+
},
242+
&cli.BoolFlag{
243+
Name: "latest",
244+
Aliases: []string{"l"},
245+
Usage: "Show the most recently created pod",
246+
},
247+
&cli.IntFlag{
248+
Name: "last",
249+
Aliases: []string{"n"},
250+
Usage: "Show last n recently created pods. Set 0 for unlimited",
251+
},
222252
},
223253
Action: func(c *cli.Context) error {
224-
if c.NArg() == 0 {
225-
return cli.ShowSubcommandHelp(c)
226-
}
227254
runtimeClient, err := getRuntimeService(c, 0)
228255
if err != nil {
229256
return err
230257
}
231258

259+
ids := c.Args().Slice()
260+
261+
if len(ids) == 0 {
262+
opts := &listOptions{
263+
nameRegexp: c.String("name"),
264+
podNamespaceRegexp: c.String("namespace"),
265+
state: c.String("state"),
266+
latest: c.Bool("latest"),
267+
last: c.Int("last"),
268+
}
269+
opts.labels, err = parseLabelStringSlice(c.StringSlice("label"))
270+
if err != nil {
271+
return fmt.Errorf("parse label string slice: %w", err)
272+
}
273+
sbs, err := ListPodSandboxes(runtimeClient, opts)
274+
if err != nil {
275+
return fmt.Errorf("listing pod sandboxes: %w", err)
276+
}
277+
for _, sb := range sbs {
278+
ids = append(ids, sb.GetId())
279+
}
280+
}
281+
282+
if len(ids) == 0 {
283+
logrus.Error("No IDs provided or nothing found per filter")
284+
return cli.ShowSubcommandHelp(c)
285+
}
286+
232287
if err := podSandboxStatus(
233288
runtimeClient,
234-
c.Args().Slice(),
289+
ids,
235290
c.String("output"),
236291
c.Bool("quiet"),
237292
c.String("template"),
@@ -256,32 +311,32 @@ var listPodCommand = &cli.Command{
256311
&cli.StringFlag{
257312
Name: "name",
258313
Value: "",
259-
Usage: "filter by pod name regular expression pattern",
314+
Usage: "Filter by pod name regular expression pattern",
260315
},
261316
&cli.StringFlag{
262317
Name: "namespace",
263318
Value: "",
264-
Usage: "filter by pod namespace regular expression pattern",
319+
Usage: "Filter by pod namespace regular expression pattern",
265320
},
266321
&cli.StringFlag{
267322
Name: "state",
268323
Aliases: []string{"s"},
269324
Value: "",
270-
Usage: "filter by pod state",
325+
Usage: "Filter by pod state",
271326
},
272327
&cli.StringSliceFlag{
273328
Name: "label",
274-
Usage: "filter by key=value label",
329+
Usage: "Filter by key=value label",
275330
},
276331
&cli.BoolFlag{
277332
Name: "verbose",
278333
Aliases: []string{"v"},
279-
Usage: "show verbose info for pods",
334+
Usage: "Show verbose info for pods",
280335
},
281336
&cli.BoolFlag{
282337
Name: "quiet",
283338
Aliases: []string{"q"},
284-
Usage: "list only pod IDs",
339+
Usage: "List only pod IDs",
285340
},
286341
&cli.StringFlag{
287342
Name: "output",
@@ -327,7 +382,7 @@ var listPodCommand = &cli.Command{
327382
if err != nil {
328383
return err
329384
}
330-
if err = ListPodSandboxes(runtimeClient, opts); err != nil {
385+
if err = OutputPodSandboxes(runtimeClient, opts); err != nil {
331386
return fmt.Errorf("listing pod sandboxes: %w", err)
332387
}
333388
return nil
@@ -487,7 +542,7 @@ func outputPodSandboxStatusTable(r *pb.PodSandboxStatusResponse, verbose bool) {
487542

488543
// ListPodSandboxes sends a ListPodSandboxRequest to the server, and parses
489544
// the returned ListPodSandboxResponse.
490-
func ListPodSandboxes(client internalapi.RuntimeService, opts *listOptions) error {
545+
func ListPodSandboxes(client internalapi.RuntimeService, opts *listOptions) ([]*pb.PodSandbox, error) {
491546
filter := &pb.PodSandboxFilter{}
492547
if opts.id != "" {
493548
filter.Id = opts.id
@@ -518,9 +573,18 @@ func ListPodSandboxes(client internalapi.RuntimeService, opts *listOptions) erro
518573
})
519574
logrus.Debugf("ListPodSandboxResponse: %v", r)
520575
if err != nil {
521-
return err
576+
return nil, fmt.Errorf("call list sandboxes RPC: %w", err)
577+
}
578+
return getSandboxesList(r, opts), nil
579+
}
580+
581+
// OutputPodSandboxes sends a ListPodSandboxRequest to the server, and parses
582+
// the returned ListPodSandboxResponse for output.
583+
func OutputPodSandboxes(client internalapi.RuntimeService, opts *listOptions) error {
584+
r, err := ListPodSandboxes(client, opts)
585+
if err != nil {
586+
return fmt.Errorf("list pod sandboxes: %w", err)
522587
}
523-
r = getSandboxesList(r, opts)
524588

525589
switch opts.output {
526590
case outputTypeJSON:

0 commit comments

Comments
 (0)