Skip to content

Commit 9a43829

Browse files
Implement Drop-In Lifecycle Support for Containerd Configuration
Signed-off-by: Carlos Eduardo Arango Gutierrez <[email protected]>
1 parent 470b841 commit 9a43829

File tree

11 files changed

+302
-5
lines changed

11 files changed

+302
-5
lines changed

cmd/nvidia-ctk-installer/container/container.go

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,9 @@ type Options struct {
4949
SetAsDefault bool
5050
RestartMode string
5151
HostRootMount string
52+
// NvidiaConfig specifies the path to the NVIDIA-specific config file to use instead of
53+
// modifying the main configuration file.
54+
NvidiaConfig string
5255
}
5356

5457
// Configure applies the options to the specified config

cmd/nvidia-ctk-installer/container/runtime/containerd/containerd.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -180,5 +180,6 @@ func getRuntimeConfig(o *container.Options, co *Options) (engine.Interface, erro
180180
containerd.WithRuntimeType(co.runtimeType),
181181
containerd.WithUseLegacyConfig(co.useLegacyConfig),
182182
containerd.WithContainerAnnotations(co.containerAnnotationsFromCDIPrefixes()...),
183+
containerd.WithNvidiaConfig(o.NvidiaConfig),
183184
)
184185
}

cmd/nvidia-ctk-installer/container/runtime/crio/crio.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -206,5 +206,6 @@ func getRuntimeConfig(o *container.Options) (engine.Interface, error) {
206206
toml.FromFile(o.Config),
207207
),
208208
),
209+
crio.WithNvidiaConfig(o.NvidiaConfig),
209210
)
210211
}

cmd/nvidia-ctk-installer/container/runtime/runtime.go

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,12 @@ func Flags(opts *Options) []cli.Flag {
5454
Destination: &opts.Config,
5555
Sources: cli.EnvVars("RUNTIME_CONFIG", "CONTAINERD_CONFIG", "DOCKER_CONFIG"),
5656
},
57+
&cli.StringFlag{
58+
Name: "nvidia-config",
59+
Usage: "Path to the NVIDIA-specific config file to create. When specified, runtime configurations are saved to this file instead of modifying the main config file",
60+
Destination: &opts.NvidiaConfig,
61+
Sources: cli.EnvVars("RUNTIME_NVIDIA_CONFIG"),
62+
},
5763
&cli.StringFlag{
5864
Name: "executable-path",
5965
Usage: "The path to the runtime executable. This is used to extract the current config",

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

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,8 @@ const (
4949
defaultConfigSource = configSourceFile
5050
configSourceCommand = "command"
5151
configSourceFile = "file"
52+
53+
defaultNVIDIARuntimeConfigFilePath = "/etc/nvidia-container-runtime/config.d/99-nvidia.conf"
5254
)
5355

5456
type command struct {
@@ -73,6 +75,7 @@ type config struct {
7375
configSource string
7476
mode string
7577
hookFilePath string
78+
nvidiaConfig string
7679

7780
nvidiaRuntime struct {
7881
name string
@@ -118,6 +121,12 @@ func (m command) build() *cli.Command {
118121
Usage: "path to the config file for the target runtime",
119122
Destination: &config.configFilePath,
120123
},
124+
&cli.StringFlag{
125+
Name: "drop-in-config",
126+
Usage: "path to the NVIDIA-specific config file to create. When specified, runtime configurations are saved to this file instead of modifying the main config file",
127+
Destination: &config.nvidiaConfig,
128+
Value: defaultNVIDIARuntimeConfigFilePath,
129+
},
121130
&cli.StringFlag{
122131
Name: "executable-path",
123132
Usage: "The path to the runtime executable. This is used to extract the current config",
@@ -268,12 +277,14 @@ func (m command) configureConfigFile(config *config) error {
268277
containerd.WithLogger(m.logger),
269278
containerd.WithPath(config.configFilePath),
270279
containerd.WithConfigSource(configSource),
280+
containerd.WithNvidiaConfig(config.nvidiaConfig),
271281
)
272282
case "crio":
273283
cfg, err = crio.New(
274284
crio.WithLogger(m.logger),
275285
crio.WithPath(config.configFilePath),
276286
crio.WithConfigSource(configSource),
287+
crio.WithNvidiaConfig(config.nvidiaConfig),
277288
)
278289
case "docker":
279290
cfg, err = docker.New(

pkg/config/engine/containerd/config.go

Lines changed: 160 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,8 @@ package containerd
1818

1919
import (
2020
"fmt"
21+
"os"
22+
"path/filepath"
2123

2224
"github.com/NVIDIA/nvidia-container-toolkit/pkg/config/engine"
2325
"github.com/NVIDIA/nvidia-container-toolkit/pkg/config/toml"
@@ -123,12 +125,38 @@ func (c *Config) EnableCDI() {
123125
*c.Tree = config
124126
}
125127

126-
// RemoveRuntime removes a runtime from the docker config
128+
// RemoveRuntime removes a runtime from the containerd config
127129
func (c *Config) RemoveRuntime(name string) error {
128130
if c == nil || c.Tree == nil {
129131
return nil
130132
}
131133

134+
// If using NVIDIA-specific configuration, handle file cleanup
135+
if c.nvidiaConfig != "" {
136+
// Check if all NVIDIA runtimes are being removed
137+
remainingNvidiaRuntimes := 0
138+
if runtimes := c.GetPath([]string{"plugins", c.CRIRuntimePluginName, "containerd", "runtimes"}); runtimes != nil {
139+
if runtimesTree, ok := runtimes.(*toml.Tree); ok {
140+
for _, runtimeName := range runtimesTree.Keys() {
141+
if c.isNvidiaRuntime(runtimeName) && runtimeName != name {
142+
remainingNvidiaRuntimes++
143+
}
144+
}
145+
}
146+
}
147+
148+
// If this is the last NVIDIA runtime, remove the NVIDIA config file
149+
if remainingNvidiaRuntimes == 0 {
150+
if err := os.Remove(c.nvidiaConfig); err != nil && !os.IsNotExist(err) {
151+
c.Logger.Warningf("Failed to remove NVIDIA config file %s: %v", c.nvidiaConfig, err)
152+
} else {
153+
c.Logger.Infof("Removed NVIDIA config file: %s", c.nvidiaConfig)
154+
}
155+
// Don't modify the in-memory tree when using NVIDIA-specific configuration
156+
return nil
157+
}
158+
}
159+
132160
config := *c.Tree
133161

134162
config.DeletePath([]string{"plugins", c.CRIRuntimePluginName, "containerd", "runtimes", name})
@@ -154,3 +182,134 @@ func (c *Config) RemoveRuntime(name string) error {
154182
*c.Tree = config
155183
return nil
156184
}
185+
186+
// Save writes the config to the specified path or NVIDIA-specific config file
187+
func (c *Config) Save(path string) (int64, error) {
188+
if c.nvidiaConfig == "" {
189+
// Backward compatibility: save to main config
190+
return c.Tree.Save(path)
191+
}
192+
193+
// Ensure directory for NVIDIA config file exists
194+
dir := filepath.Dir(c.nvidiaConfig)
195+
if err := os.MkdirAll(dir, 0755); err != nil {
196+
return 0, fmt.Errorf("failed to create directory for NVIDIA config: %w", err)
197+
}
198+
199+
// Save runtime configs to NVIDIA config file
200+
nvidiaConfig := c.extractRuntimeConfig()
201+
n, err := nvidiaConfig.Save(c.nvidiaConfig)
202+
if err != nil {
203+
return n, fmt.Errorf("failed to save NVIDIA config: %w", err)
204+
}
205+
206+
// Update main config with imports directive
207+
if err := c.updateMainConfigImports(path); err != nil {
208+
// Try to clean up the NVIDIA config file on error
209+
os.Remove(c.nvidiaConfig)
210+
return n, fmt.Errorf("failed to update main config imports: %w", err)
211+
}
212+
213+
c.Logger.Infof("Wrote NVIDIA runtime configuration to: %s", c.nvidiaConfig)
214+
return n, nil
215+
}
216+
217+
// extractRuntimeConfig creates a new config tree with only runtime configurations
218+
func (c *Config) extractRuntimeConfig() *toml.Tree {
219+
config, _ := toml.TreeFromMap(map[string]interface{}{
220+
"version": c.Version,
221+
})
222+
223+
// Extract runtime configurations for NVIDIA runtimes
224+
if runtimes := c.GetPath([]string{"plugins", c.CRIRuntimePluginName, "containerd", "runtimes"}); runtimes != nil {
225+
if runtimesTree, ok := runtimes.(*toml.Tree); ok {
226+
nvidiaRuntimes, _ := toml.TreeFromMap(map[string]interface{}{})
227+
for _, name := range runtimesTree.Keys() {
228+
if c.isNvidiaRuntime(name) {
229+
if runtime := runtimesTree.Get(name); runtime != nil {
230+
nvidiaRuntimes.Set(name, runtime)
231+
}
232+
}
233+
}
234+
if len(nvidiaRuntimes.Keys()) > 0 {
235+
config.SetPath([]string{"plugins", c.CRIRuntimePluginName, "containerd", "runtimes"}, nvidiaRuntimes)
236+
}
237+
}
238+
}
239+
240+
// Extract default runtime name if it's one of ours
241+
if defaultRuntime, ok := c.GetPath([]string{"plugins", c.CRIRuntimePluginName, "containerd", "default_runtime_name"}).(string); ok {
242+
if c.isNvidiaRuntime(defaultRuntime) {
243+
config.SetPath([]string{"plugins", c.CRIRuntimePluginName, "containerd", "default_runtime_name"}, defaultRuntime)
244+
}
245+
}
246+
247+
// Extract CDI enablement
248+
if cdiEnabled, ok := c.GetPath([]string{"plugins", c.CRIRuntimePluginName, "enable_cdi"}).(bool); ok && cdiEnabled {
249+
config.SetPath([]string{"plugins", c.CRIRuntimePluginName, "enable_cdi"}, true)
250+
}
251+
252+
return config
253+
}
254+
255+
// updateMainConfigImports ensures the main config includes an imports directive
256+
func (c *Config) updateMainConfigImports(path string) error {
257+
// Load the main config file
258+
mainConfig, err := toml.FromFile(path).Load()
259+
if err != nil {
260+
// If the file doesn't exist, create a minimal config with imports
261+
if os.IsNotExist(err) {
262+
mainConfig, _ = toml.TreeFromMap(map[string]interface{}{
263+
"version": c.Version,
264+
})
265+
} else {
266+
return fmt.Errorf("failed to load main config: %w", err)
267+
}
268+
}
269+
270+
// Add imports directive if not present
271+
importPattern := c.nvidiaConfig
272+
imports := mainConfig.Get("imports")
273+
if imports == nil {
274+
mainConfig.Set("imports", []string{importPattern})
275+
} else if importsList, ok := imports.([]interface{}); ok {
276+
// Check if the import pattern already exists
277+
found := false
278+
for _, imp := range importsList {
279+
if impStr, ok := imp.(string); ok && impStr == importPattern {
280+
found = true
281+
break
282+
}
283+
}
284+
if !found {
285+
// Add our import pattern
286+
importsList = append(importsList, importPattern)
287+
mainConfig.Set("imports", importsList)
288+
}
289+
} else if importsStrList, ok := imports.([]string); ok {
290+
// Check if the import pattern already exists
291+
found := false
292+
for _, imp := range importsStrList {
293+
if imp == importPattern {
294+
found = true
295+
break
296+
}
297+
}
298+
if !found {
299+
// Add our import pattern
300+
importsStrList = append(importsStrList, importPattern)
301+
mainConfig.Set("imports", importsStrList)
302+
}
303+
} else {
304+
return fmt.Errorf("unexpected imports type: %T", imports)
305+
}
306+
307+
// Save the updated main config
308+
_, err = mainConfig.Save(path)
309+
return err
310+
}
311+
312+
// isNvidiaRuntime checks if the runtime name is an NVIDIA runtime
313+
func (c *Config) isNvidiaRuntime(name string) bool {
314+
return name == "nvidia" || name == "nvidia-cdi" || name == "nvidia-legacy"
315+
}

pkg/config/engine/containerd/config_v1.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -122,7 +122,7 @@ func (c *ConfigV1) RemoveRuntime(name string) error {
122122

123123
// Save writes the config to a file
124124
func (c ConfigV1) Save(path string) (int64, error) {
125-
return (Config)(c).Save(path)
125+
return (*Config)(&c).Save(path)
126126
}
127127

128128
func (c *ConfigV1) GetRuntimeConfig(name string) (engine.RuntimeConfig, error) {

pkg/config/engine/containerd/containerd.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,9 @@ type Config struct {
4646
// for the CRI runtime service. The name of this plugin was changed in v3 of the
4747
// containerd configuration file.
4848
CRIRuntimePluginName string
49+
// nvidiaConfig specifies the path to the NVIDIA-specific configuration file.
50+
// If set, runtime configurations will be saved to this file instead of the main config.
51+
nvidiaConfig string
4952
}
5053

5154
var _ engine.Interface = (*Config)(nil)
@@ -108,6 +111,7 @@ func New(opts ...Option) (engine.Interface, error) {
108111
RuntimeType: b.runtimeType,
109112
UseLegacyConfig: b.useLegacyConfig,
110113
ContainerAnnotations: b.containerAnnotations,
114+
nvidiaConfig: b.nvidiaConfig,
111115
}
112116

113117
switch configVersion {

pkg/config/engine/containerd/option.go

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ type builder struct {
2929
path string
3030
runtimeType string
3131
containerAnnotations []string
32+
nvidiaConfig string
3233
}
3334

3435
// Option defines a function that can be used to configure the config builder
@@ -82,3 +83,12 @@ func WithContainerAnnotations(containerAnnotations ...string) Option {
8283
b.containerAnnotations = containerAnnotations
8384
}
8485
}
86+
87+
// WithNvidiaConfig sets the NVIDIA-specific config file path for the config builder.
88+
// When set, configurations will be saved to this file instead of modifying
89+
// the main config file directly.
90+
func WithNvidiaConfig(path string) Option {
91+
return func(b *builder) {
92+
b.nvidiaConfig = path
93+
}
94+
}

0 commit comments

Comments
 (0)