Skip to content

Commit 101a1d4

Browse files
committed
USE Matchers
Signed-off-by: Evan Lezar <[email protected]>
1 parent 0348394 commit 101a1d4

File tree

2 files changed

+119
-87
lines changed

2 files changed

+119
-87
lines changed

tests/e2e/nvidia-ctk_containerd_test.go

Lines changed: 115 additions & 84 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ package e2e
1919
import (
2020
"context"
2121
"fmt"
22+
"path/filepath"
2223

2324
. "github.com/onsi/ginkgo/v2"
2425
. "github.com/onsi/gomega"
@@ -50,41 +51,38 @@ exit 1
5051

5152
// containerdTestEnv defines the test environment for different containerd versions
5253
type containerdTestEnv struct {
53-
name string
54-
image string
55-
configVersion int
56-
pluginPath string
57-
hasDefaultImports bool
54+
name string
55+
image string
56+
configVersion int64
57+
pluginPath string
5858
}
5959

6060
// Define both containerd versions to test
6161
var containerdEnvs = []containerdTestEnv{
6262
{
63-
name: "containerd-1.7",
64-
image: "kindest/node:v1.30.0@sha256:047357ac0cfea04663786a612ba1eaba9702bef25227a794b52890dd8bcd692e",
65-
configVersion: 2,
66-
pluginPath: "io.containerd.grpc.v1.cri",
67-
hasDefaultImports: false,
63+
name: "containerd-1.7",
64+
image: "kindest/node:v1.30.0@sha256:047357ac0cfea04663786a612ba1eaba9702bef25227a794b52890dd8bcd692e",
65+
configVersion: 2,
66+
pluginPath: "io.containerd.grpc.v1.cri",
6867
},
6968
{
70-
name: "containerd-2.1",
71-
image: "docker.io/kindest/base:v20250521-31a79fd4",
72-
configVersion: 3,
73-
pluginPath: "io.containerd.cri.v1.runtime",
74-
hasDefaultImports: true,
69+
name: "containerd-2.1",
70+
image: "docker.io/kindest/base:v20250521-31a79fd4",
71+
configVersion: 3,
72+
pluginPath: "io.containerd.cri.v1.runtime",
7573
},
7674
}
7775

7876
// Integration tests for containerd drop-in config functionality
7977
var _ = Describe("containerd", Ordered, ContinueOnFailure, Label("container-runtime"), func() {
8078
// Run all tests for each containerd version
8179
for _, env := range containerdEnvs {
82-
env := env // capture loop variable
83-
84-
Context(fmt.Sprintf("with %s", env.name), Ordered, func() {
80+
Context(env.name, Ordered, func() {
8581
var (
86-
nestedContainerRunner Runner
87-
containerName = fmt.Sprintf("nvctk-e2e-containerd-%s-tests", env.name)
82+
nestedContainerRunner Runner
83+
containerName = "nvctk-e2e-containerd-tests-" + env.name
84+
originalTopLevelConfigContents string
85+
// originalTopLevelConfigToml *toml.Tree
8886
)
8987

9088
// ensureContainerdRunning starts containerd if not running and waits for it to be ready
@@ -124,7 +122,15 @@ var _ = Describe("containerd", Ordered, ContinueOnFailure, Label("container-runt
124122
var err error
125123

126124
// Create the nested container with the global cache mounted
127-
nestedContainerRunner, err = NewNestedContainerRunner(runner, env.image, installCTK, containerName, localCacheDir)
125+
// TODO: This runner doesn't actually NEED GPU access.
126+
nestedContainerRunner, err = NewNestedContainerRunner(runner, env.image, false, containerName, localCacheDir)
127+
Expect(err).ToNot(HaveOccurred())
128+
129+
// Store the contents of the original config.
130+
originalTopLevelConfigContents, _, err = nestedContainerRunner.Run("cat /etc/containerd/config.toml")
131+
Expect(err).ToNot(HaveOccurred())
132+
133+
_, err = toml.Load(originalTopLevelConfigContents)
128134
Expect(err).ToNot(HaveOccurred())
129135

130136
// Backup original containerd configuration
@@ -133,7 +139,7 @@ var _ = Describe("containerd", Ordered, ContinueOnFailure, Label("container-runt
133139
if [ -d /etc/containerd/conf.d ]; then
134140
cp -r /etc/containerd/conf.d /tmp/containerd-conf.d.backup
135141
fi
136-
142+
137143
# Backup the original config.toml
138144
if [ -f /etc/containerd/config.toml ]; then
139145
cp /etc/containerd/config.toml /tmp/containerd-config.toml.backup
@@ -174,7 +180,7 @@ var _ = Describe("containerd", Ordered, ContinueOnFailure, Label("container-runt
174180
rm -rf /etc/containerd/conf.d
175181
mkdir -p /etc/containerd/conf.d
176182
fi
177-
183+
178184
# Restore the original config.toml
179185
if [ -f /tmp/containerd-config.toml.backup ]; then
180186
cp /tmp/containerd-config.toml.backup /etc/containerd/config.toml
@@ -196,12 +202,16 @@ var _ = Describe("containerd", Ordered, ContinueOnFailure, Label("container-runt
196202
_, _, err := nestedContainerRunner.Run(`nvidia-ctk runtime configure --runtime=containerd --config=/etc/containerd/config.toml --drop-in-config=/etc/containerd/conf.d/99-nvidia.toml --set-as-default --cdi.enabled`)
197203
Expect(err).ToNot(HaveOccurred(), "Failed to configure containerd")
198204

199-
// For containerd 1.7, verify nvidia-ctk added imports directive to main config
200-
if !env.hasDefaultImports {
201-
output, _, err := nestedContainerRunner.Run(`grep "^imports" /etc/containerd/config.toml`)
202-
Expect(err).ToNot(HaveOccurred(), "nvidia-ctk should have added imports directive to main config")
203-
Expect(output).To(ContainSubstring(`imports = ["/etc/containerd/conf.d/*.toml"]`))
204-
}
205+
topLevelConfigContents, _, err := nestedContainerRunner.Run("cat /etc/containerd/config.toml")
206+
Expect(err).ToNot(HaveOccurred())
207+
208+
_, err = toml.Load(topLevelConfigContents)
209+
Expect(err).ToNot(HaveOccurred())
210+
211+
dropInConfigContents, _, err := nestedContainerRunner.Run("cat /etc/containerd/conf.d/99-nvidia.toml")
212+
Expect(err).ToNot(HaveOccurred())
213+
214+
Expect(dropInConfigContents).ToNot(BeEmpty())
205215

206216
// restart containerd
207217
err = restartContainerdAndWait(nestedContainerRunner)
@@ -212,53 +222,64 @@ var _ = Describe("containerd", Ordered, ContinueOnFailure, Label("container-runt
212222
Expect(err).ToNot(HaveOccurred())
213223

214224
// Parse the TOML output
215-
config, err := parseContainerdConfig(output)
225+
config, err := toml.Load(output)
216226
Expect(err).ToNot(HaveOccurred(), "Failed to parse containerd config")
217227

218-
// Verify config version
219-
version := config.Get("version")
220-
Expect(version).To(BeNumerically("==", env.configVersion))
221-
222-
// Verify imports
223-
// Note: containerd config dump behavior differs between versions:
224-
// - containerd 1.7: Shows resolved file paths from glob patterns
225-
// - containerd 2.x: May show the glob pattern or omit imports entirely
226-
if env.configVersion == 2 {
227-
// containerd 1.7 shows actual resolved imports
228-
err = validateImports(config, []string{"/etc/containerd/conf.d/99-nvidia.toml"}, true)
229-
Expect(err).ToNot(HaveOccurred(), "Import validation failed")
230-
}
231-
// For containerd 2.x, imports validation is skipped as the behavior is inconsistent
232-
233-
// Get plugin configuration
234-
pluginConfig, err := getPluginConfig(config, env.configVersion)
235-
Expect(err).ToNot(HaveOccurred(), "Failed to get plugin config")
236-
237-
// Verify CDI is enabled
238-
cdiEnabled, err := getCDIEnabled(pluginConfig)
239-
Expect(err).ToNot(HaveOccurred(), "Failed to get CDI config")
240-
Expect(cdiEnabled).To(BeTrue(), "CDI should be enabled")
241-
242-
// Verify default runtime
243-
defaultRuntime, err := getDefaultRuntime(pluginConfig)
244-
Expect(err).ToNot(HaveOccurred(), "Failed to get default runtime")
245-
Expect(defaultRuntime).To(Equal("nvidia"), "Default runtime should be nvidia")
246-
247-
// Get runtimes configuration
248-
runtimes, err := getRuntimesConfig(pluginConfig)
249-
Expect(err).ToNot(HaveOccurred(), "Failed to get runtimes config")
250-
251-
// Verify NVIDIA runtime exists and is properly configured
252-
nvidiaRuntime, exists := runtimes["nvidia"]
253-
Expect(exists).To(BeTrue(), "nvidia runtime should exist")
254-
255-
// Validate nvidia runtime configuration
256-
expectedOptions := map[string]interface{}{
257-
"BinaryName": "/usr/bin/nvidia-container-runtime",
258-
"SystemdCgroup": true,
228+
BeAValidConfig := func(env *containerdTestEnv) types.GomegaMatcher {
229+
return And(
230+
WithTransform(
231+
func(c *toml.Tree) map[string]any {
232+
return c.ToMap()
233+
},
234+
And(
235+
HaveKeyWithValue("version", env.configVersion),
236+
HaveKeyWithValue("imports", WithTransform(func(is []any) []string {
237+
var basePaths []string
238+
for _, i := range is {
239+
basePaths = append(basePaths, filepath.Dir(i.(string)))
240+
}
241+
return basePaths
242+
},
243+
// TODO: We could do better at matching the following:
244+
// /etc/containerd/conf.d/99-nvidia.toml
245+
// /etc/containerd/conf.d/*.toml
246+
ContainElements("/etc/containerd/conf.d"),
247+
)),
248+
),
249+
),
250+
WithTransform(
251+
// Get the plugins config.
252+
func(c *toml.Tree) map[string]any {
253+
pt := c.GetPath([]string{"plugins", env.pluginPath})
254+
if pt != nil {
255+
return pt.(*toml.Tree).ToMap()
256+
}
257+
return nil
258+
},
259+
And(
260+
HaveKeyWithValue("enable_cdi", true),
261+
HaveKeyWithValue("containerd",
262+
And(
263+
// TODO: This should depend on whether we set the default.
264+
HaveKeyWithValue("default_runtime_name", "nvidia"),
265+
HaveKeyWithValue("runtimes", HaveKeyWithValue("nvidia",
266+
And(
267+
HaveKeyWithValue("runtime_type", "io.containerd.runc.v2"),
268+
HaveKeyWithValue("options",
269+
And(
270+
HaveKeyWithValue("BinaryName", "/usr/bin/nvidia-container-runtime"),
271+
HaveKeyWithValue("SystemdCgroup", true),
272+
),
273+
),
274+
),
275+
)),
276+
),
277+
),
278+
),
279+
),
280+
)
259281
}
260-
err = validateRuntimeConfig(nvidiaRuntime, "", expectedOptions)
261-
Expect(err).ToNot(HaveOccurred(), "NVIDIA runtime validation failed")
282+
Expect(config).To(BeAValidConfig(&env))
262283
})
263284
})
264285

@@ -275,14 +296,14 @@ var _ = Describe("containerd", Ordered, ContinueOnFailure, Label("container-runt
275296
[plugins."io.containerd.grpc.v1.cri"]
276297
[plugins."io.containerd.grpc.v1.cri".containerd]
277298
default_runtime_name = "kata"
278-
299+
279300
[plugins."io.containerd.grpc.v1.cri".containerd.runtimes]
280301
[plugins."io.containerd.grpc.v1.cri".containerd.runtimes.runc]
281302
runtime_type = "io.containerd.runc.v2"
282-
303+
283304
[plugins."io.containerd.grpc.v1.cri".containerd.runtimes.kata]
284305
runtime_type = "io.containerd.kata.v2"
285-
306+
286307
[plugins."io.containerd.grpc.v1.cri".containerd.runtimes.kata.options]
287308
ConfigPath = "/etc/kata-containers/configuration.toml"`
288309
} else {
@@ -293,14 +314,14 @@ var _ = Describe("containerd", Ordered, ContinueOnFailure, Label("container-runt
293314
[plugins."io.containerd.cri.v1.runtime"]
294315
[plugins."io.containerd.cri.v1.runtime".containerd]
295316
default_runtime_name = "kata"
296-
317+
297318
[plugins."io.containerd.cri.v1.runtime".containerd.runtimes]
298319
[plugins."io.containerd.cri.v1.runtime".containerd.runtimes.runc]
299320
runtime_type = "io.containerd.runc.v2"
300-
321+
301322
[plugins."io.containerd.cri.v1.runtime".containerd.runtimes.kata]
302323
runtime_type = "io.containerd.kata.v2"
303-
324+
304325
[plugins."io.containerd.cri.v1.runtime".containerd.runtimes.kata.options]
305326
ConfigPath = "/etc/kata-containers/configuration.toml"`
306327
}
@@ -382,11 +403,11 @@ version = 3
382403
[plugins."io.containerd.cri.v1.runtime"]
383404
[plugins."io.containerd.cri.v1.runtime".containerd]
384405
default_runtime_name = "runc"
385-
406+
386407
[plugins."io.containerd.cri.v1.runtime".containerd.runtimes]
387408
[plugins."io.containerd.cri.v1.runtime".containerd.runtimes.runc]
388409
runtime_type = "io.containerd.runc.v2"
389-
410+
390411
[plugins."io.containerd.cri.v1.runtime".containerd.runtimes.runc.options]
391412
BinaryName = "/usr/bin/runc"
392413
SystemdCgroup = true
@@ -577,7 +598,7 @@ func tomlTreeToMap(tree *toml.Tree) map[string]interface{} {
577598
}
578599

579600
// getPluginConfig navigates to the appropriate plugin configuration based on containerd version
580-
func getPluginConfig(tree *toml.Tree, version int) (*toml.Tree, error) {
601+
func getPluginConfig(tree *toml.Tree, version int64) (*toml.Tree, error) {
581602
var pluginPath []string
582603
if version == 2 {
583604
pluginPath = []string{"plugins", "io.containerd.grpc.v1.cri"}
@@ -602,8 +623,18 @@ func getPluginConfig(tree *toml.Tree, version int) (*toml.Tree, error) {
602623
}
603624

604625
// getRuntimesConfig gets the runtimes configuration from the plugin config
605-
func getRuntimesConfig(pluginConfig *toml.Tree) (map[string]interface{}, error) {
606-
runtimes := pluginConfig.GetPath([]string{"containerd", "runtimes"})
626+
func getRuntimesConfig(pluginConfig *toml.Tree, _ int64) (map[string]interface{}, error) {
627+
containerdSection := pluginConfig.Get("containerd")
628+
if containerdSection == nil {
629+
return nil, fmt.Errorf("containerd section not found")
630+
}
631+
632+
containerdTree, ok := containerdSection.(*toml.Tree)
633+
if !ok {
634+
return nil, fmt.Errorf("containerd section is not a TOML tree")
635+
}
636+
637+
runtimes := containerdTree.Get("runtimes")
607638
if runtimes == nil {
608639
return nil, fmt.Errorf("runtimes section not found")
609640
}

tests/e2e/runner.go

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -100,7 +100,7 @@ func NewRunner(opts ...runnerOption) Runner {
100100
// NewNestedContainerRunner creates a new nested container runner.
101101
// A nested container runs a container inside another container based on a
102102
// given runner (remote or local).
103-
func NewNestedContainerRunner(runner Runner, baseImage string, installCTK bool, containerName string, cacheDir string) (Runner, error) {
103+
func NewNestedContainerRunner(runner Runner, baseImage string, mountToolkitFromHost bool, containerName string, cacheDir string) (Runner, error) {
104104
// If a container with the same name exists from a previous test run, remove it first.
105105
// Ignore errors as container might not exist
106106
_, _, err := runner.Run(fmt.Sprintf("docker rm -f %s 2>/dev/null || true", containerName))
@@ -116,7 +116,8 @@ func NewNestedContainerRunner(runner Runner, baseImage string, installCTK bool,
116116
)
117117
}
118118

119-
if !installCTK {
119+
if mountToolkitFromHost {
120+
// TODO: This is actually ONLY needed for the CLI tests.
120121
// If installCTK is false, we use the preinstalled toolkit.
121122
// This means we need to add toolkit libraries and binaries from the "host"
122123

@@ -312,7 +313,7 @@ type outerContainer struct {
312313
}
313314

314315
func (o *outerContainer) Render() (string, error) {
315-
tmpl, err := template.New("startContainer").Parse(`docker run -d --name {{.Name}} --privileged --runtime=nvidia \
316+
tmpl, err := template.New("startContainer").Parse(`docker run -d --name {{.Name}} --privileged \
316317
-e NVIDIA_VISIBLE_DEVICES=runtime.nvidia.com/gpu=all \
317318
-e NVIDIA_DRIVER_CAPABILITIES=all \
318319
{{ range $i, $a := .AdditionalArguments -}}

0 commit comments

Comments
 (0)