Skip to content

Commit a2d56b8

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 image. Signed-off-by: Sascha Grunert <sgrunert@redhat.com>
1 parent 64a491b commit a2d56b8

File tree

3 files changed

+191
-40
lines changed

3 files changed

+191
-40
lines changed

cmd/crictl/container.go

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

544+
ids := c.Args().Slice()
545+
546+
if len(ids) == 0 {
547+
opts := &listOptions{
548+
nameRegexp: c.String("name"),
549+
podID: c.String("pod"),
550+
image: c.String("image"),
551+
state: c.String("state"),
552+
latest: c.Bool("latest"),
553+
last: c.Int("last"),
554+
}
555+
opts.labels, err = parseLabelStringSlice(c.StringSlice("label"))
556+
if err != nil {
557+
return err
558+
}
559+
560+
ctrs, err := ListContainers(runtimeClient, opts)
561+
if err != nil {
562+
return fmt.Errorf("listing containers: %w", err)
563+
}
564+
for _, ctr := range ctrs {
565+
ids = append(ids, ctr.GetId())
566+
}
567+
}
568+
569+
if len(ids) == 0 {
570+
logrus.Error("No IDs provided or nothing found per filter")
571+
return cli.ShowSubcommandHelp(c)
572+
}
573+
515574
if err := containerStatus(
516575
runtimeClient,
517-
c.Args().Slice(),
576+
ids,
518577
c.String("output"),
519578
c.String("template"),
520579
c.Bool("quiet"),
@@ -633,7 +692,7 @@ var listContainersCommand = &cli.Command{
633692
return err
634693
}
635694

636-
if err = ListContainers(runtimeClient, imageClient, opts); err != nil {
695+
if err = OutputContainers(runtimeClient, imageClient, opts); err != nil {
637696
return fmt.Errorf("listing containers: %w", err)
638697
}
639698
return nil
@@ -1080,7 +1139,7 @@ func outputContainerStatusTable(r *pb.ContainerStatusResponse, verbose bool) {
10801139

10811140
// ListContainers sends a ListContainerRequest to the server, and parses
10821141
// the returned ListContainerResponse.
1083-
func ListContainers(runtimeClient internalapi.RuntimeService, imageClient internalapi.ImageManagerService, opts *listOptions) error {
1142+
func ListContainers(runtimeClient internalapi.RuntimeService, opts *listOptions) ([]*pb.Container, error) {
10841143
filter := &pb.ContainerFilter{}
10851144
if opts.id != "" {
10861145
filter.Id = opts.id
@@ -1124,9 +1183,18 @@ func ListContainers(runtimeClient internalapi.RuntimeService, imageClient intern
11241183
})
11251184
logrus.Debugf("ListContainerResponse: %v", r)
11261185
if err != nil {
1127-
return err
1186+
return nil, fmt.Errorf("call list containers RPC: %w", err)
1187+
}
1188+
return getContainersList(r, opts), nil
1189+
}
1190+
1191+
// OutputContainers sends a ListContainerRequest to the server, and parses
1192+
// the returned ListContainerResponse for output.
1193+
func OutputContainers(runtimeClient internalapi.RuntimeService, imageClient internalapi.ImageManagerService, opts *listOptions) error {
1194+
r, err := ListContainers(runtimeClient, opts)
1195+
if err != nil {
1196+
return fmt.Errorf("list containers: %w", err)
11281197
}
1129-
r = getContainersList(r, opts)
11301198

11311199
switch opts.output {
11321200
case outputTypeJSON:

cmd/crictl/image.go

+40-18
Original file line numberDiff line numberDiff line change
@@ -191,20 +191,11 @@ var listImageCommand = &cli.Command{
191191
return err
192192
}
193193

194-
r, err := ListImages(imageClient, c.Args().First())
194+
r, err := ListImages(imageClient, c.Args().First(), c.StringSlice("filter"))
195195
if err != nil {
196196
return fmt.Errorf("listing images: %w", err)
197197
}
198198

199-
sort.Sort(imageByRef(r.Images))
200-
201-
if len(c.StringSlice("filter")) > 0 && len(r.Images) > 0 {
202-
r.Images, err = filterImagesList(r.Images, c.StringSlice("filter"))
203-
if err != nil {
204-
return fmt.Errorf("listing images: %w", err)
205-
}
206-
}
207-
208199
switch c.String("output") {
209200
case outputTypeJSON:
210201
return outputProtobufObjAsJSON(r)
@@ -303,11 +294,17 @@ var imageStatusCommand = &cli.Command{
303294
Name: "template",
304295
Usage: "The template string is only used when output is go-template; The Template format is golang template",
305296
},
297+
&cli.StringFlag{
298+
Name: "name",
299+
Usage: "Filter by image name",
300+
},
301+
&cli.StringSliceFlag{
302+
Name: "filter",
303+
Aliases: []string{"f"},
304+
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.",
305+
},
306306
},
307307
Action: func(c *cli.Context) error {
308-
if c.NArg() == 0 {
309-
return cli.ShowSubcommandHelp(c)
310-
}
311308
imageClient, err := getImageService(c)
312309
if err != nil {
313310
return err
@@ -320,10 +317,25 @@ var imageStatusCommand = &cli.Command{
320317
}
321318
tmplStr := c.String("template")
322319

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

332+
if len(ids) == 0 {
333+
logrus.Error("No IDs provided or nothing found per filter")
334+
return cli.ShowSubcommandHelp(c)
335+
}
336+
337+
statuses := []statusData{}
338+
for _, id := range ids {
327339
r, err := ImageStatus(imageClient, id, verbose)
328340
if err != nil {
329341
return fmt.Errorf("image status for %q request: %w", id, err)
@@ -684,8 +696,8 @@ func PullImageWithSandbox(client internalapi.ImageManagerService, image string,
684696

685697
// ListImages sends a ListImagesRequest to the server, and parses
686698
// the returned ListImagesResponse.
687-
func ListImages(client internalapi.ImageManagerService, image string) (*pb.ListImagesResponse, error) {
688-
request := &pb.ListImagesRequest{Filter: &pb.ImageFilter{Image: &pb.ImageSpec{Image: image}}}
699+
func ListImages(client internalapi.ImageManagerService, nameFilter string, conditionFilters []string) (*pb.ListImagesResponse, error) {
700+
request := &pb.ListImagesRequest{Filter: &pb.ImageFilter{Image: &pb.ImageSpec{Image: nameFilter}}}
689701
logrus.Debugf("ListImagesRequest: %v", request)
690702
res, err := InterruptableRPC(nil, func(ctx context.Context) ([]*pb.Image, error) {
691703
return client.ListImages(ctx, request.Filter)
@@ -695,6 +707,16 @@ func ListImages(client internalapi.ImageManagerService, image string) (*pb.ListI
695707
}
696708
resp := &pb.ListImagesResponse{Images: res}
697709
logrus.Debugf("ListImagesResponse: %v", resp)
710+
711+
sort.Sort(imageByRef(resp.Images))
712+
713+
if len(conditionFilters) > 0 && len(resp.Images) > 0 {
714+
resp.Images, err = filterImagesList(resp.Images, conditionFilters)
715+
if err != nil {
716+
return nil, fmt.Errorf("filter images: %w", err)
717+
}
718+
}
719+
698720
return resp, nil
699721
}
700722

cmd/crictl/sandbox.go

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

255+
ids := c.Args().Slice()
256+
257+
if len(ids) == 0 {
258+
opts := &listOptions{
259+
nameRegexp: c.String("name"),
260+
podNamespaceRegexp: c.String("namespace"),
261+
state: c.String("state"),
262+
latest: c.Bool("latest"),
263+
last: c.Int("last"),
264+
}
265+
opts.labels, err = parseLabelStringSlice(c.StringSlice("label"))
266+
if err != nil {
267+
return fmt.Errorf("parse label string slice: %w", err)
268+
}
269+
sbs, err := ListPodSandboxes(runtimeClient, opts)
270+
if err != nil {
271+
return fmt.Errorf("listing pod sandboxes: %w", err)
272+
}
273+
for _, sb := range sbs {
274+
ids = append(ids, sb.GetId())
275+
}
276+
}
277+
278+
if len(ids) == 0 {
279+
logrus.Error("No IDs provided or nothing found per filter")
280+
return cli.ShowSubcommandHelp(c)
281+
}
282+
231283
if err := podSandboxStatus(
232284
runtimeClient,
233-
c.Args().Slice(),
285+
ids,
234286
c.String("output"),
235287
c.Bool("quiet"),
236288
c.String("template"),
@@ -253,30 +305,30 @@ var listPodCommand = &cli.Command{
253305
},
254306
&cli.StringFlag{
255307
Name: "name",
256-
Usage: "filter by pod name regular expression pattern",
308+
Usage: "Filter by pod name regular expression pattern",
257309
},
258310
&cli.StringFlag{
259311
Name: "namespace",
260-
Usage: "filter by pod namespace regular expression pattern",
312+
Usage: "Filter by pod namespace regular expression pattern",
261313
},
262314
&cli.StringFlag{
263315
Name: "state",
264316
Aliases: []string{"s"},
265-
Usage: "filter by pod state",
317+
Usage: "Filter by pod state",
266318
},
267319
&cli.StringSliceFlag{
268320
Name: "label",
269-
Usage: "filter by key=value label",
321+
Usage: "Filter by key=value label",
270322
},
271323
&cli.BoolFlag{
272324
Name: "verbose",
273325
Aliases: []string{"v"},
274-
Usage: "show verbose info for pods",
326+
Usage: "Show verbose info for pods",
275327
},
276328
&cli.BoolFlag{
277329
Name: "quiet",
278330
Aliases: []string{"q"},
279-
Usage: "list only pod IDs",
331+
Usage: "List only pod IDs",
280332
},
281333
&cli.StringFlag{
282334
Name: "output",
@@ -322,7 +374,7 @@ var listPodCommand = &cli.Command{
322374
if err != nil {
323375
return err
324376
}
325-
if err = ListPodSandboxes(runtimeClient, opts); err != nil {
377+
if err = OutputPodSandboxes(runtimeClient, opts); err != nil {
326378
return fmt.Errorf("listing pod sandboxes: %w", err)
327379
}
328380
return nil
@@ -482,7 +534,7 @@ func outputPodSandboxStatusTable(r *pb.PodSandboxStatusResponse, verbose bool) {
482534

483535
// ListPodSandboxes sends a ListPodSandboxRequest to the server, and parses
484536
// the returned ListPodSandboxResponse.
485-
func ListPodSandboxes(client internalapi.RuntimeService, opts *listOptions) error {
537+
func ListPodSandboxes(client internalapi.RuntimeService, opts *listOptions) ([]*pb.PodSandbox, error) {
486538
filter := &pb.PodSandboxFilter{}
487539
if opts.id != "" {
488540
filter.Id = opts.id
@@ -513,9 +565,18 @@ func ListPodSandboxes(client internalapi.RuntimeService, opts *listOptions) erro
513565
})
514566
logrus.Debugf("ListPodSandboxResponse: %v", r)
515567
if err != nil {
516-
return err
568+
return nil, fmt.Errorf("call list sandboxes RPC: %w", err)
569+
}
570+
return getSandboxesList(r, opts), nil
571+
}
572+
573+
// OutputPodSandboxes sends a ListPodSandboxRequest to the server, and parses
574+
// the returned ListPodSandboxResponse for output.
575+
func OutputPodSandboxes(client internalapi.RuntimeService, opts *listOptions) error {
576+
r, err := ListPodSandboxes(client, opts)
577+
if err != nil {
578+
return fmt.Errorf("list pod sandboxes: %w", err)
517579
}
518-
r = getSandboxesList(r, opts)
519580

520581
switch opts.output {
521582
case outputTypeJSON:

0 commit comments

Comments
 (0)