Skip to content

Commit a6bea79

Browse files
committed
Fix --image filter for crictl inspect and exec
Signed-off-by: Sascha Grunert <sgrunert@redhat.com>
1 parent 0a460af commit a6bea79

File tree

7 files changed

+126
-15
lines changed

7 files changed

+126
-15
lines changed

cmd/crictl/container.go

Lines changed: 23 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -532,13 +532,23 @@ var containerStatusCommand = &cli.Command{
532532
Aliases: []string{"n"},
533533
Usage: "Show last n recently created containers (includes all states). Set 0 for unlimited.",
534534
},
535+
&cli.BoolFlag{
536+
Name: "all",
537+
Aliases: []string{"a"},
538+
Usage: "Show all containers",
539+
},
535540
},
536541
Action: func(c *cli.Context) error {
537542
runtimeClient, err := getRuntimeService(c, 0)
538543
if err != nil {
539544
return err
540545
}
541546

547+
imageClient, err := getImageService(c)
548+
if err != nil {
549+
return err
550+
}
551+
542552
ids := c.Args().Slice()
543553

544554
if len(ids) == 0 {
@@ -550,13 +560,14 @@ var containerStatusCommand = &cli.Command{
550560
state: c.String("state"),
551561
latest: c.Bool("latest"),
552562
last: c.Int("last"),
563+
all: c.Bool("all"),
553564
}
554565
opts.labels, err = parseLabelStringSlice(c.StringSlice("label"))
555566
if err != nil {
556567
return err
557568
}
558569

559-
ctrs, err := ListContainers(runtimeClient, opts)
570+
ctrs, err := ListContainers(runtimeClient, imageClient, opts)
560571
if err != nil {
561572
return fmt.Errorf("listing containers: %w", err)
562573
}
@@ -1145,7 +1156,7 @@ func outputContainerStatusTable(r *pb.ContainerStatusResponse, verbose bool) {
11451156

11461157
// ListContainers sends a ListContainerRequest to the server, and parses
11471158
// the returned ListContainerResponse.
1148-
func ListContainers(runtimeClient internalapi.RuntimeService, opts *listOptions) ([]*pb.Container, error) {
1159+
func ListContainers(runtimeClient internalapi.RuntimeService, imageClient internalapi.ImageManagerService, opts *listOptions) ([]*pb.Container, error) {
11491160
filter := &pb.ContainerFilter{}
11501161
if opts.id != "" {
11511162
filter.Id = opts.id
@@ -1191,13 +1202,13 @@ func ListContainers(runtimeClient internalapi.RuntimeService, opts *listOptions)
11911202
if err != nil {
11921203
return nil, fmt.Errorf("call list containers RPC: %w", err)
11931204
}
1194-
return getContainersList(r, opts), nil
1205+
return getContainersList(imageClient, r, opts)
11951206
}
11961207

11971208
// OutputContainers sends a ListContainerRequest to the server, and parses
11981209
// the returned ListContainerResponse for output.
11991210
func OutputContainers(runtimeClient internalapi.RuntimeService, imageClient internalapi.ImageManagerService, opts *listOptions) error {
1200-
r, err := ListContainers(runtimeClient, opts)
1211+
r, err := ListContainers(runtimeClient, imageClient, opts)
12011212
if err != nil {
12021213
return fmt.Errorf("list containers: %w", err)
12031214
}
@@ -1218,11 +1229,6 @@ func OutputContainers(runtimeClient internalapi.RuntimeService, imageClient inte
12181229
display.AddRow([]string{columnContainer, columnImage, columnCreated, columnState, columnName, columnAttempt, columnPodID, columnPodName, columnNamespace})
12191230
}
12201231
for _, c := range r {
1221-
if match, err := matchesImage(imageClient, opts.image, c.GetImage().GetImage()); err != nil {
1222-
return fmt.Errorf("check image match: %w", err)
1223-
} else if !match {
1224-
continue
1225-
}
12261232
if opts.quiet {
12271233
fmt.Printf("%s\n", c.Id)
12281234
continue
@@ -1326,9 +1332,15 @@ func getFromLabels(labels map[string]string, label string) string {
13261332
return "unknown"
13271333
}
13281334

1329-
func getContainersList(containersList []*pb.Container, opts *listOptions) []*pb.Container {
1335+
func getContainersList(imageClient internalapi.ImageManagerService, containersList []*pb.Container, opts *listOptions) ([]*pb.Container, error) {
13301336
filtered := []*pb.Container{}
13311337
for _, c := range containersList {
1338+
if match, err := matchesImage(imageClient, opts.image, c.GetImage().GetImage()); err != nil {
1339+
return nil, fmt.Errorf("check image match: %w", err)
1340+
} else if !match {
1341+
continue
1342+
}
1343+
13321344
podNamespace := getPodNamespaceFromLabels(c.Labels)
13331345
// Filter by pod name/namespace regular expressions.
13341346
if c.Metadata != nil &&
@@ -1353,5 +1365,5 @@ func getContainersList(containersList []*pb.Container, opts *listOptions) []*pb.
13531365
return b
13541366
}(n, len(filtered))
13551367

1356-
return filtered[:n]
1368+
return filtered[:n], nil
13571369
}

cmd/crictl/container_test.go

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,8 @@ func fakeContainer(name string, createdAt int64) *pb.Container {
4747

4848
var _ = DescribeTable("getContainersList",
4949
func(input []*pb.Container, options *listOptions, indexes []int) {
50-
actual := getContainersList(input, options)
50+
actual, err := getContainersList(nil, input, options)
51+
Expect(err).NotTo(HaveOccurred())
5152
var expected []*pb.Container
5253
for _, i := range indexes {
5354
expected = append(expected, input[i])

cmd/crictl/exec.go

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -154,6 +154,11 @@ var runtimeExecCommand = &cli.Command{
154154
return err
155155
}
156156

157+
imageClient, err := getImageService(c)
158+
if err != nil {
159+
return err
160+
}
161+
157162
// Assume a regular exec where the first arg is the container ID.
158163
ids := []string{c.Args().First()}
159164
cmd := c.Args().Slice()[1:]
@@ -193,7 +198,7 @@ var runtimeExecCommand = &cli.Command{
193198
return err
194199
}
195200

196-
ctrs, err := ListContainers(runtimeClient, opts)
201+
ctrs, err := ListContainers(runtimeClient, imageClient, opts)
197202
if err != nil {
198203
return fmt.Errorf("listing containers: %w", err)
199204
}

cmd/crictl/util.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -492,7 +492,7 @@ func matchesRegex(pattern, target string) bool {
492492
}
493493

494494
func matchesImage(imageClient internalapi.ImageManagerService, image, containerImage string) (bool, error) {
495-
if image == "" {
495+
if image == "" || imageClient == nil {
496496
return true, nil
497497
}
498498
r1, err := ImageStatus(imageClient, image, false)

test/e2e/inspect_test.go

Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,92 @@
1+
/*
2+
Copyright 2025 The Kubernetes 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 e2e
18+
19+
import (
20+
"bytes"
21+
"encoding/json"
22+
"fmt"
23+
"os"
24+
25+
. "github.com/onsi/ginkgo/v2"
26+
. "github.com/onsi/gomega"
27+
. "github.com/onsi/gomega/gexec"
28+
)
29+
30+
// The actual test suite.
31+
var _ = t.Describe("inspect", func() {
32+
const imageLatest = registry + "test-image-latest"
33+
sandbox := ""
34+
35+
BeforeEach(func() {
36+
sb, err := os.CreateTemp("", "sandbox-")
37+
Expect(err).NotTo(HaveOccurred())
38+
_, err = fmt.Fprintf(sb, `{ "metadata": { "name": "sb", "uid": "uid", "namespace": "ns" }}`)
39+
Expect(err).NotTo(HaveOccurred())
40+
41+
ctr, err := os.CreateTemp("", "container-")
42+
Expect(err).NotTo(HaveOccurred())
43+
_, err = fmt.Fprintf(ctr, `{ "metadata": { "name": "ctr" }, "image": { "image": "`+imageLatest+`" }, "args": [] }`)
44+
Expect(err).NotTo(HaveOccurred())
45+
46+
res := t.Crictl(fmt.Sprintf("run %s %s", ctr.Name(), sb.Name()))
47+
Expect(res).To(Exit(0))
48+
Expect(os.RemoveAll(sb.Name())).NotTo(HaveOccurred())
49+
Expect(os.RemoveAll(ctr.Name())).NotTo(HaveOccurred())
50+
51+
res = t.Crictl("pods --name sb -q")
52+
Expect(res).To(Exit(0))
53+
sandbox = string(bytes.TrimSpace(res.Out.Contents()))
54+
})
55+
56+
AfterEach(func() {
57+
res := t.Crictl("rmp -f " + sandbox)
58+
Expect(res).To(Exit(0))
59+
})
60+
61+
validateSingleResponse := func(contents []byte) {
62+
singleResponse := map[string]any{}
63+
Expect(json.Unmarshal(contents, &singleResponse)).NotTo(HaveOccurred())
64+
Expect(singleResponse).To(HaveKey("info"))
65+
Expect(singleResponse).To(HaveKey("status"))
66+
}
67+
68+
expectNothingFound := func(contents []byte) {
69+
Expect(string(contents)).To(ContainSubstring("nothing found per filter"))
70+
}
71+
72+
It("should succeed", func() {
73+
res := t.Crictl("inspect -a")
74+
Expect(res).To(Exit(0))
75+
validateSingleResponse(res.Out.Contents())
76+
77+
// Not output without `--all` since container is exited
78+
res = t.Crictl("inspect")
79+
Expect(res).To(Exit(0))
80+
expectNothingFound(res.Err.Contents())
81+
82+
// Should allow filter per image name
83+
res = t.Crictl("inspect -a --image " + imageLatest)
84+
Expect(res).To(Exit(0))
85+
validateSingleResponse(res.Out.Contents())
86+
87+
// Should filter nothing if image does not match
88+
res = t.Crictl("inspect -a --image wrong")
89+
Expect(res).To(Exit(0))
90+
expectNothingFound(res.Err.Contents())
91+
})
92+
})

test/e2e/inspecti_test.go

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,6 @@ import (
2929
var _ = t.Describe("inspecti", func() {
3030
const (
3131
imageSuccessText = "Image is up to date"
32-
registry = "gcr.io/k8s-staging-cri-tools/"
3332
image1 = registry + "test-image-2"
3433
image2 = registry + "test-image-3"
3534
)

test/e2e/suite_test.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,8 @@ import (
2525
. "sigs.k8s.io/cri-tools/test/framework"
2626
)
2727

28+
const registry = "gcr.io/k8s-staging-cri-tools/"
29+
2830
// TestE2E runs the created specs.
2931
func TestE2E(t *testing.T) {
3032
t.Parallel()

0 commit comments

Comments
 (0)