Skip to content

Commit 40c3a75

Browse files
committed
Fix handling of existing imports in containerd
This change fixes the handling of existing imports in containerd config files. Without this fix the existing imports were dropped and replaced with the import directive for the generated drop-in file. Signed-off-by: Evan Lezar <[email protected]>
1 parent b715dc8 commit 40c3a75

File tree

3 files changed

+131
-4
lines changed

3 files changed

+131
-4
lines changed

cmd/nvidia-ctk/runtime/configure/configure_test.go

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,36 @@ func TestConfigureLifecycle(t *testing.T) {
4343
assertConditions func(*testing.T, string) error
4444
}{
4545
// Containerd v2 test cases
46+
{
47+
description: "containerd: config exists with imports",
48+
args: []string{
49+
"--runtime", "containerd",
50+
"--config", "{{ .testRoot }}/etc/containerd/config.toml",
51+
"--drop-in-config", "{{ .testRoot }}/etc/containerd/conf.d/99-nvidia.toml",
52+
},
53+
prepareEnvironment: func(t *testing.T, testRoot string) error {
54+
configPath := filepath.Join(testRoot, "etc/containerd/config.toml")
55+
require.NoError(t, os.MkdirAll(filepath.Dir(configPath), 0755))
56+
57+
initialConfig := `version = 2
58+
imports = ["/foo/bar/*.toml"]
59+
`
60+
return os.WriteFile(configPath, []byte(initialConfig), 0600)
61+
},
62+
assertConditions: func(t *testing.T, testRoot string) error {
63+
mainConfig := filepath.Join(testRoot, "etc/containerd/config.toml")
64+
content, err := os.ReadFile(mainConfig)
65+
require.NoError(t, err)
66+
67+
expectedTemplate := `imports = ["/foo/bar/*.toml", "{{ .testRoot }}/etc/containerd/conf.d/*.toml"]
68+
version = 2
69+
`
70+
expected := strings.ReplaceAll(expectedTemplate, "{{ .testRoot }}", testRoot)
71+
72+
require.Equal(t, expected, string(content))
73+
return nil
74+
},
75+
},
4676
{
4777
description: "containerd: v2 config does not exist with drop-in",
4878
args: []string{

pkg/config/engine/containerd/config_drop_in.go

Lines changed: 27 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -173,12 +173,35 @@ func (c *topLevelConfig) removeVersion() {
173173
c.config.Delete("version")
174174
}
175175

176+
func (c *topLevelConfig) getCurrentImports() []string {
177+
rawImports := c.config.Get("imports")
178+
if rawImports == nil {
179+
return nil
180+
}
181+
182+
if importsStringSlice, ok := rawImports.([]string); ok {
183+
return importsStringSlice
184+
}
185+
186+
importsAnySlice, ok := rawImports.([]any)
187+
if !ok {
188+
return nil
189+
}
190+
var importsStringSlice []string
191+
for _, importAny := range importsAnySlice {
192+
importString, ok := importAny.(string)
193+
if !ok {
194+
continue
195+
}
196+
importsStringSlice = append(importsStringSlice, importString)
197+
}
198+
199+
return importsStringSlice
200+
}
201+
176202
func (c *topLevelConfig) ensureImports(dropInFilename string) {
177203
config := c.config.Tree
178-
var currentImports []string
179-
if ci, ok := c.config.Get("imports").([]string); ok {
180-
currentImports = ci
181-
}
204+
currentImports := c.getCurrentImports()
182205

183206
requiredImport := c.importPattern(dropInFilename)
184207
for _, currentImport := range currentImports {
Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
/**
2+
# SPDX-FileCopyrightText: Copyright (c) 2025 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
3+
# SPDX-License-Identifier: Apache-2.0
4+
#
5+
# Licensed under the Apache License, Version 2.0 (the "License");
6+
# you may not use this file except in compliance with the License.
7+
# You may obtain a copy of the License at
8+
#
9+
# http://www.apache.org/licenses/LICENSE-2.0
10+
#
11+
# Unless required by applicable law or agreed to in writing, software
12+
# distributed under the License is distributed on an "AS IS" BASIS,
13+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
# See the License for the specific language governing permissions and
15+
# limitations under the License.
16+
**/
17+
18+
package containerd
19+
20+
import (
21+
"testing"
22+
23+
"github.com/stretchr/testify/require"
24+
25+
"github.com/NVIDIA/nvidia-container-toolkit/pkg/config/toml"
26+
)
27+
28+
func TestEnsureImports(t *testing.T) {
29+
testCases := []struct {
30+
description string
31+
configMap map[string]any
32+
path string
33+
expectedImports []string
34+
}{
35+
{
36+
description: "empty",
37+
path: "/another/path/file.toml",
38+
expectedImports: []string{"/another/path/*.toml"},
39+
},
40+
{
41+
description: "existing imports as string slice",
42+
configMap: map[string]any{
43+
"imports": []string{"/foo/bar/*.toml"},
44+
},
45+
path: "/another/path/file.toml",
46+
expectedImports: []string{"/foo/bar/*.toml", "/another/path/*.toml"},
47+
},
48+
{
49+
description: "existing imports as any slice",
50+
configMap: map[string]any{
51+
"imports": []any{"/foo/bar/*.toml"},
52+
},
53+
path: "/another/path/file.toml",
54+
expectedImports: []string{"/foo/bar/*.toml", "/another/path/*.toml"},
55+
},
56+
}
57+
58+
for _, tc := range testCases {
59+
t.Run(tc.description, func(t *testing.T) {
60+
cut := topLevelConfig{
61+
config: &Config{
62+
Tree: func() *toml.Tree {
63+
t, _ := toml.FromMap(tc.configMap).Load()
64+
return t
65+
}(),
66+
},
67+
}
68+
69+
cut.ensureImports("/another/path/file.toml")
70+
require.EqualValues(t, tc.expectedImports, cut.config.Get("imports"))
71+
})
72+
}
73+
74+
}

0 commit comments

Comments
 (0)