Skip to content

Commit e38a909

Browse files
committed
image: Add tests for build/list/tag/push/pull/remove
Signed-off-by: Qasim Sarfraz <[email protected]>
1 parent f6c2b57 commit e38a909

File tree

10 files changed

+286
-11
lines changed

10 files changed

+286
-11
lines changed

Makefile

+5
Original file line numberDiff line numberDiff line change
@@ -268,6 +268,11 @@ integration-tests: kubectl-gadget
268268
-gadget-tag $(GADGET_TAG) \
269269
$$INTEGRATION_TESTS_PARAMS
270270

271+
272+
.PHONY: component-tests
273+
component-tests:
274+
go test -exec sudo -v ./integration/components/... -integration -timeout 5m --builder-image $(EBPF_BUILDER)
275+
271276
.PHONY: generate-documentation
272277
generate-documentation:
273278
go run -tags docs cmd/gen-doc/gen-doc.go -repo $(shell pwd)

cmd/common/image/build.go

+3-3
Original file line numberDiff line numberDiff line change
@@ -88,7 +88,7 @@ func NewBuildCmd() *cobra.Command {
8888

8989
opts.path = args[0]
9090

91-
return runBuild(opts)
91+
return runBuild(cmd, opts)
9292
},
9393
}
9494

@@ -102,7 +102,7 @@ func NewBuildCmd() *cobra.Command {
102102
return utils.MarkExperimental(cmd)
103103
}
104104

105-
func runBuild(opts *cmdOpts) error {
105+
func runBuild(cmd *cobra.Command, opts *cmdOpts) error {
106106
conf := &buildFile{
107107
EBPFSource: DEFAULT_EBPF_SOURCE,
108108
Wasm: DEFAULT_WASM,
@@ -186,7 +186,7 @@ func runBuild(opts *cmdOpts) error {
186186
return err
187187
}
188188

189-
fmt.Printf("Successfully built %s\n", desc.String())
189+
cmd.Printf("Successfully built %s\n", desc.String())
190190

191191
return nil
192192
}

cmd/common/image/list.go

+1-2
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,6 @@ package image
1717
import (
1818
"context"
1919
"fmt"
20-
"os"
2120
"strings"
2221

2322
"github.com/spf13/cobra"
@@ -52,7 +51,7 @@ func NewListCmd() *cobra.Command {
5251
})
5352
}
5453
formatter := textcolumns.NewFormatter(cols.GetColumnMap())
55-
formatter.WriteTable(os.Stdout, images)
54+
formatter.WriteTable(cmd.OutOrStdout(), images)
5655
return nil
5756
},
5857
}

cmd/common/image/pull.go

+2-2
Original file line numberDiff line numberDiff line change
@@ -34,12 +34,12 @@ func NewPullCmd() *cobra.Command {
3434
RunE: func(cmd *cobra.Command, args []string) error {
3535
image := args[0]
3636

37-
fmt.Printf("Pulling %s...\n", image)
37+
cmd.Printf("Pulling %s...\n", image)
3838
desc, err := oci.PullGadgetImage(context.TODO(), image, &authOpts)
3939
if err != nil {
4040
return fmt.Errorf("pulling gadget: %w", err)
4141
}
42-
fmt.Printf("Successfully pulled %s\n", desc.String())
42+
cmd.Printf("Successfully pulled %s\n", desc.String())
4343
return nil
4444
},
4545
}

cmd/common/image/push.go

+2-2
Original file line numberDiff line numberDiff line change
@@ -34,12 +34,12 @@ func NewPushCmd() *cobra.Command {
3434
RunE: func(cmd *cobra.Command, args []string) error {
3535
image := args[0]
3636

37-
fmt.Printf("Pushing %s...\n", image)
37+
cmd.Printf("Pushing %s...\n", image)
3838
desc, err := oci.PushGadgetImage(context.TODO(), image, &authOpts)
3939
if err != nil {
4040
return fmt.Errorf("pushing gadget: %w", err)
4141
}
42-
fmt.Printf("Successfully pushed %s\n", desc.String())
42+
cmd.Printf("Successfully pushed %s\n", desc.String())
4343
return nil
4444
},
4545
}

cmd/common/image/remove.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ func NewRemoveCmd() *cobra.Command {
3838
return fmt.Errorf("removing gadget image: %w", err)
3939
}
4040

41-
fmt.Printf("Successfully removed %s\n", image)
41+
cmd.Printf("Successfully removed %s\n", image)
4242

4343
return nil
4444
},

cmd/common/image/tag.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ func NewTagCmd() *cobra.Command {
3838
return fmt.Errorf("tagging image: %w", err)
3939
}
4040

41-
fmt.Printf("Successfully tagged with %s\n", desc.String())
41+
cmd.Printf("Successfully tagged with %s\n", desc.String())
4242
return nil
4343
},
4444
}
+216
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,216 @@
1+
// Copyright 2024 The Inspektor Gadget authors
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// http://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
package image
16+
17+
import (
18+
"bytes"
19+
"fmt"
20+
"os"
21+
"testing"
22+
23+
"github.com/spf13/cobra"
24+
"github.com/stretchr/testify/require"
25+
26+
commonImage "github.com/inspektor-gadget/inspektor-gadget/cmd/common/image"
27+
. "github.com/inspektor-gadget/inspektor-gadget/integration"
28+
)
29+
30+
func TestImage(t *testing.T) {
31+
// ensure that experimental is enabled
32+
os.Setenv("IG_EXPERIMENTAL", "true")
33+
34+
// start registry
35+
r := StartRegistry(t, "test-image-registry")
36+
t.Cleanup(func() {
37+
r.Stop(t)
38+
})
39+
if len(r.PortBindings()) == 0 || r.PortBindings()["5000/tcp"] == nil {
40+
t.Fatal("registry port not exposed")
41+
}
42+
pb := r.PortBindings()["5000/tcp"][0]
43+
registryAddr := fmt.Sprintf("%s:%s", pb.HostIP, pb.HostPort)
44+
45+
testReg := "reg.com"
46+
testRepo := "repo1"
47+
testTag := "tag1"
48+
testLocalImage := fmt.Sprintf("%s/%s:%s", testReg, testRepo, testTag)
49+
testRegistryImage := fmt.Sprintf("%s/%s:%s", registryAddr, testRepo, testTag)
50+
51+
// ensure all images are removed
52+
t.Cleanup(func() {
53+
// remove local image
54+
cmd := commonImage.NewRemoveCmd()
55+
cmd.SetArgs([]string{testLocalImage})
56+
cmd.Execute()
57+
58+
// remove registry image
59+
cmd = commonImage.NewRemoveCmd()
60+
cmd.SetArgs([]string{testRegistryImage})
61+
cmd.Execute()
62+
})
63+
64+
type testCase struct {
65+
name string
66+
cmd *cobra.Command
67+
args []string
68+
expectedStdout []string
69+
expectedStderr []string
70+
negateExpected bool
71+
}
72+
73+
testCases := []testCase{
74+
{
75+
name: "build",
76+
cmd: commonImage.NewBuildCmd(),
77+
args: []string{
78+
"--builder-image", *testBuilderImage, "--tag", testLocalImage, "../../../gadgets/trace_open",
79+
},
80+
expectedStdout: []string{
81+
fmt.Sprintf("Successfully built %s", testLocalImage),
82+
},
83+
},
84+
{
85+
name: "list",
86+
cmd: commonImage.NewListCmd(),
87+
args: []string{},
88+
expectedStdout: []string{
89+
testRepo,
90+
testTag,
91+
},
92+
},
93+
{
94+
name: "tag",
95+
cmd: commonImage.NewTagCmd(),
96+
args: []string{testLocalImage, testRegistryImage},
97+
expectedStdout: []string{
98+
fmt.Sprintf("Successfully tagged with %s", testRegistryImage),
99+
},
100+
},
101+
{
102+
name: "push",
103+
cmd: commonImage.NewPushCmd(),
104+
args: []string{testRegistryImage, "--insecure"},
105+
expectedStdout: []string{
106+
fmt.Sprintf("Successfully pushed %s", testRegistryImage),
107+
},
108+
},
109+
{
110+
name: "push-invalid-image",
111+
cmd: commonImage.NewPushCmd(),
112+
args: []string{"unknown", "--insecure"},
113+
expectedStderr: []string{
114+
"failed to resolve ghcr.io/inspektor-gadget/gadget/unknown:latest: not found",
115+
},
116+
},
117+
{
118+
name: "push-unknown-tag",
119+
cmd: commonImage.NewPushCmd(),
120+
args: []string{fmt.Sprintf("%s/%s:%s", registryAddr, testRepo, "unknown"), "--insecure"},
121+
expectedStderr: []string{
122+
fmt.Sprintf("%s/%s:%s: not found", registryAddr, testRepo, "unknown"),
123+
},
124+
},
125+
{
126+
name: "remove-local-image",
127+
cmd: commonImage.NewRemoveCmd(),
128+
args: []string{testLocalImage},
129+
expectedStdout: []string{
130+
fmt.Sprintf("Successfully removed %s", testLocalImage),
131+
},
132+
},
133+
{
134+
name: "remove-registry-image",
135+
cmd: commonImage.NewRemoveCmd(),
136+
args: []string{testRegistryImage},
137+
expectedStdout: []string{
138+
fmt.Sprintf("Successfully removed %s", testRegistryImage),
139+
},
140+
},
141+
{
142+
name: "validate-remove",
143+
cmd: commonImage.NewListCmd(),
144+
args: []string{},
145+
negateExpected: true,
146+
// can't use combined repository here as REPOSITORY column can be truncated
147+
expectedStdout: []string{
148+
registryAddr,
149+
testRepo,
150+
},
151+
},
152+
{
153+
name: "pull",
154+
cmd: commonImage.NewPullCmd(),
155+
args: []string{testRegistryImage, "--insecure"},
156+
expectedStdout: []string{
157+
fmt.Sprintf("Successfully pulled %s", testRegistryImage),
158+
},
159+
},
160+
{
161+
name: "validate-pull",
162+
cmd: commonImage.NewListCmd(),
163+
args: []string{},
164+
expectedStdout: []string{
165+
registryAddr,
166+
testTag,
167+
},
168+
},
169+
{
170+
name: "pull-invalid-image",
171+
cmd: commonImage.NewPullCmd(),
172+
args: []string{"unknown", "--insecure"},
173+
expectedStderr: []string{
174+
"failed to resolve ghcr.io/inspektor-gadget/gadget/unknown:latest",
175+
},
176+
},
177+
{
178+
name: "pull-unknown-tag",
179+
cmd: commonImage.NewPullCmd(),
180+
args: []string{fmt.Sprintf("%s/%s:%s", registryAddr, testRepo, "unknown"), "--insecure"},
181+
expectedStderr: []string{
182+
fmt.Sprintf("%s/%s:%s: not found", registryAddr, testRepo, "unknown"),
183+
},
184+
},
185+
}
186+
187+
for _, tc := range testCases {
188+
t.Run(tc.name, func(t *testing.T) {
189+
var stdout, stderr bytes.Buffer
190+
tc.cmd.SetArgs(tc.args)
191+
tc.cmd.SetOut(&stdout)
192+
tc.cmd.SetErr(&stderr)
193+
err := tc.cmd.Execute()
194+
if len(tc.expectedStderr) > 0 {
195+
require.NotNil(t, err)
196+
for _, expected := range tc.expectedStderr {
197+
if tc.negateExpected {
198+
require.NotContains(t, stderr.String(), expected)
199+
continue
200+
}
201+
require.Contains(t, stderr.String(), expected)
202+
}
203+
return
204+
}
205+
require.Nil(t, err)
206+
require.Empty(t, stderr.String())
207+
for _, expected := range tc.expectedStdout {
208+
if tc.negateExpected {
209+
require.NotContains(t, stdout.String(), expected)
210+
continue
211+
}
212+
require.Contains(t, stdout.String(), expected)
213+
}
214+
})
215+
}
216+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
// Copyright 2024 The Inspektor Gadget authors
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// http://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
package image
16+
17+
import (
18+
"flag"
19+
"fmt"
20+
"os"
21+
"testing"
22+
)
23+
24+
var (
25+
integration = flag.Bool("integration", false, "run integration tests")
26+
testBuilderImage = flag.String("builder-image", "ghcr.io/inspektor-gadget/ebpf-builder:latest", "ebpf builder image")
27+
)
28+
29+
func TestMain(m *testing.M) {
30+
flag.Parse()
31+
32+
if !*integration {
33+
fmt.Println("Skipping image tests")
34+
os.Exit(0)
35+
}
36+
37+
fmt.Println("Running image tests")
38+
os.Exit(m.Run())
39+
}

integration/helpers.go

+16
Original file line numberDiff line numberDiff line change
@@ -23,10 +23,12 @@ import (
2323
"strings"
2424
"testing"
2525

26+
"github.com/docker/go-connections/nat"
2627
"github.com/google/go-cmp/cmp/cmpopts"
2728
"github.com/stretchr/testify/require"
2829

2930
containercollection "github.com/inspektor-gadget/inspektor-gadget/pkg/container-collection"
31+
"github.com/inspektor-gadget/inspektor-gadget/pkg/container-utils/testutils"
3032
eventtypes "github.com/inspektor-gadget/inspektor-gadget/pkg/types"
3133
)
3234

@@ -276,3 +278,17 @@ func GetIPVersion(t *testing.T, address string) uint8 {
276278
t.Fatalf("Failed to determine IP version for address %s", address)
277279
return 0
278280
}
281+
282+
func StartRegistry(t *testing.T, name string) testutils.Container {
283+
t.Helper()
284+
285+
c := testutils.NewDockerContainer(name, "registry serve /etc/docker/registry/config.yml",
286+
testutils.WithImage("registry:2"),
287+
testutils.WithoutWait(),
288+
testutils.WithPortBindings(nat.PortMap{
289+
"5000/tcp": []nat.PortBinding{{HostIP: "127.0.0.1"}},
290+
}),
291+
)
292+
c.Start(t)
293+
return c
294+
}

0 commit comments

Comments
 (0)