Skip to content

Commit b3ce3a4

Browse files
authored
Merge pull request #1303 from elezar/drop-in-in-container
Add support for drop-in config files in a container
2 parents 6363f07 + 0f11fb2 commit b3ce3a4

File tree

7 files changed

+136
-11
lines changed

7 files changed

+136
-11
lines changed

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

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,8 @@ const (
3535

3636
// Options defines the shared options for the CLIs to configure containers runtimes.
3737
type Options struct {
38-
DropInConfig string
38+
DropInConfig string
39+
DropInConfigHostPath string
3940
// TopLevelConfigPath stores the path to the top-level config for the runtime.
4041
TopLevelConfigPath string
4142
Socket string

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

Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -590,6 +590,84 @@ version = 2
590590

591591
expectedDropIn := `version = 2
592592
593+
[plugins]
594+
595+
[plugins."io.containerd.grpc.v1.cri"]
596+
597+
[plugins."io.containerd.grpc.v1.cri".containerd]
598+
599+
[plugins."io.containerd.grpc.v1.cri".containerd.runtimes]
600+
601+
[plugins."io.containerd.grpc.v1.cri".containerd.runtimes.nvidia]
602+
privileged_without_host_devices = false
603+
runtime_engine = ""
604+
runtime_root = ""
605+
runtime_type = "io.containerd.runc.v2"
606+
607+
[plugins."io.containerd.grpc.v1.cri".containerd.runtimes.nvidia.options]
608+
BinaryName = "/usr/bin/nvidia-container-runtime"
609+
610+
[plugins."io.containerd.grpc.v1.cri".containerd.runtimes.nvidia-cdi]
611+
privileged_without_host_devices = false
612+
runtime_engine = ""
613+
runtime_root = ""
614+
runtime_type = "io.containerd.runc.v2"
615+
616+
[plugins."io.containerd.grpc.v1.cri".containerd.runtimes.nvidia-cdi.options]
617+
BinaryName = "/usr/bin/nvidia-container-runtime.cdi"
618+
619+
[plugins."io.containerd.grpc.v1.cri".containerd.runtimes.nvidia-legacy]
620+
privileged_without_host_devices = false
621+
runtime_engine = ""
622+
runtime_root = ""
623+
runtime_type = "io.containerd.runc.v2"
624+
625+
[plugins."io.containerd.grpc.v1.cri".containerd.runtimes.nvidia-legacy.options]
626+
BinaryName = "/usr/bin/nvidia-container-runtime.legacy"
627+
`
628+
require.Equal(t, expectedDropIn, string(actualDropIn))
629+
return nil
630+
},
631+
assertCleanupPostConditions: func(t *testing.T, co *container.Options, o *Options) error {
632+
require.NoFileExists(t, co.TopLevelConfigPath)
633+
require.NoFileExists(t, co.DropInConfig)
634+
return nil
635+
},
636+
},
637+
{
638+
description: "v2: top-level config does not exist with drop-in-config-host-path",
639+
containerOptions: container.Options{
640+
TopLevelConfigPath: "{{ .testRoot }}/etc/containerd/config.toml",
641+
DropInConfig: "{{ .testRoot }}/conf.d/99-nvidia.toml",
642+
DropInConfigHostPath: "/some/host/path/conf.d/99-nvidia.toml",
643+
RuntimeName: "nvidia",
644+
RuntimeDir: "/usr/bin",
645+
SetAsDefault: false,
646+
RestartMode: "none",
647+
ExecutablePath: "not-containerd",
648+
},
649+
options: Options{
650+
runtimeType: "io.containerd.runc.v2",
651+
},
652+
assertSetupPostConditions: func(t *testing.T, co *container.Options, o *Options) error {
653+
require.FileExists(t, co.TopLevelConfigPath)
654+
655+
actual, err := os.ReadFile(co.TopLevelConfigPath)
656+
require.NoError(t, err)
657+
658+
expected := `imports = ["/some/host/path/conf.d/*.toml"]
659+
version = 2
660+
`
661+
require.Equal(t, expected, string(actual))
662+
663+
require.NoFileExists(t, co.DropInConfigHostPath)
664+
require.FileExists(t, co.DropInConfig)
665+
666+
actualDropIn, err := os.ReadFile(co.DropInConfig)
667+
require.NoError(t, err)
668+
669+
expectedDropIn := `version = 2
670+
593671
[plugins]
594672
595673
[plugins."io.containerd.grpc.v1.cri"]

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

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ package containerd
1919
import (
2020
"encoding/json"
2121
"fmt"
22+
"path/filepath"
2223

2324
log "github.com/sirupsen/logrus"
2425
cli "github.com/urfave/cli/v3"
@@ -171,7 +172,7 @@ func GetLowlevelRuntimePaths(o *container.Options, co *Options) ([]string, error
171172
}
172173

173174
func getRuntimeConfig(o *container.Options, co *Options) (engine.Interface, error) {
174-
return containerd.New(
175+
options := []containerd.Option{
175176
containerd.WithTopLevelConfigPath(o.TopLevelConfigPath),
176177
containerd.WithConfigSource(
177178
toml.LoadFirst(
@@ -182,5 +183,12 @@ func getRuntimeConfig(o *container.Options, co *Options) (engine.Interface, erro
182183
containerd.WithRuntimeType(co.runtimeType),
183184
containerd.WithUseLegacyConfig(co.useLegacyConfig),
184185
containerd.WithContainerAnnotations(co.containerAnnotationsFromCDIPrefixes()...),
185-
)
186+
}
187+
if o.DropInConfigHostPath != "" && o.DropInConfig != "" {
188+
options = append(options,
189+
containerd.WithContainerPathAsHostPath(filepath.Dir(o.DropInConfig), filepath.Dir(o.DropInConfigHostPath)),
190+
)
191+
}
192+
193+
return containerd.New(options...)
186194
}

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

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,13 @@ func Flags(opts *Options) []cli.Flag {
6161
Destination: &opts.DropInConfig,
6262
Sources: cli.EnvVars("RUNTIME_DROP_IN_CONFIG"),
6363
},
64+
&cli.StringFlag{
65+
Name: "drop-in-config-host-path",
66+
Usage: "When running in a container, this is the path to the drop-in config (--drop-in-config) on the host. " +
67+
"This is required to correctly update the top-level config if required since paths there must be host-relative.",
68+
Destination: &opts.DropInConfigHostPath,
69+
Sources: cli.EnvVars("RUNTIME_DROP_IN_CONFIG_HOST_PATH"),
70+
},
6471
&cli.StringFlag{
6572
Name: "executable-path",
6673
Usage: "The path to the runtime executable. This is used to extract the current config",

pkg/config/engine/containerd/config_drop_in.go

Lines changed: 23 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -37,15 +37,17 @@ var _ engine.Interface = (*ConfigWithDropIn)(nil)
3737
// A topLevelConfig stores the original on-disk top-level config.
3838
// The path to the config is also stored to allow it to be modified if required.
3939
type topLevelConfig struct {
40-
path string
41-
config *Config
40+
path string
41+
containerToHostPathMap map[string]string
42+
config *Config
4243
}
4344

44-
func NewConfigWithDropIn(topLevelConfigPath string, tlConfig *Config, dropInConfig engine.Interface) *ConfigWithDropIn {
45+
func NewConfigWithDropIn(topLevelConfigPath string, containerToHostPathMap map[string]string, tlConfig *Config, dropInConfig engine.Interface) *ConfigWithDropIn {
4546
return &ConfigWithDropIn{
4647
topLevelConfig: &topLevelConfig{
47-
path: topLevelConfigPath,
48-
config: tlConfig,
48+
path: topLevelConfigPath,
49+
containerToHostPathMap: containerToHostPathMap,
50+
config: tlConfig,
4951
},
5052
Interface: dropInConfig,
5153
}
@@ -117,13 +119,27 @@ func (c *topLevelConfig) removeImports(dropInFilename string) {
117119
return
118120
}
119121

120-
requiredImport := filepath.Dir(dropInFilename) + "/*.toml"
122+
requiredImport := c.importPattern(dropInFilename)
121123
if currentImports[0] != requiredImport {
122124
return
123125
}
124126
c.config.Delete("imports")
125127
}
126128

129+
func (c *topLevelConfig) importPattern(dropInFilename string) string {
130+
return c.asHostPath(filepath.Dir(dropInFilename)) + "/*.toml"
131+
}
132+
133+
func (c *topLevelConfig) asHostPath(path string) string {
134+
if c.containerToHostPathMap == nil {
135+
return path
136+
}
137+
if hostPath, ok := c.containerToHostPathMap[path]; ok {
138+
return hostPath
139+
}
140+
return path
141+
}
142+
127143
// removeVersion removes the version if it is the ONLY field in the file.
128144
func (c *topLevelConfig) removeVersion() {
129145
if len(c.config.Keys()) > 1 {
@@ -142,7 +158,7 @@ func (c *topLevelConfig) ensureImports(dropInFilename string) {
142158
currentImports = ci
143159
}
144160

145-
requiredImport := filepath.Dir(dropInFilename) + "/*.toml"
161+
requiredImport := c.importPattern(dropInFilename)
146162
for _, currentImport := range currentImports {
147163
// If the requiredImport is already present, then we need not update the config.
148164
if currentImport == requiredImport {

pkg/config/engine/containerd/containerd.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -141,7 +141,7 @@ func New(opts ...Option) (engine.Interface, error) {
141141
},
142142
}
143143

144-
cfg := NewConfigWithDropIn(b.topLevelConfigPath, topLevelConfig, dropInConfig)
144+
cfg := NewConfigWithDropIn(b.topLevelConfigPath, b.containerToHostPathMap, topLevelConfig, dropInConfig)
145145
return cfg, nil
146146
}
147147
}

pkg/config/engine/containerd/option.go

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,11 +29,26 @@ type builder struct {
2929
topLevelConfigPath string
3030
runtimeType string
3131
containerAnnotations []string
32+
33+
containerToHostPathMap map[string]string
3234
}
3335

3436
// Option defines a function that can be used to configure the config builder
3537
type Option func(*builder)
3638

39+
// WithContainerPathAsHostPath maps a given container path to a host path.
40+
func WithContainerPathAsHostPath(containerPath string, hostPath string) Option {
41+
return func(b *builder) {
42+
if containerPath == "" || hostPath == "" || containerPath == hostPath {
43+
return
44+
}
45+
if b.containerToHostPathMap == nil {
46+
b.containerToHostPathMap = make(map[string]string)
47+
}
48+
b.containerToHostPathMap[containerPath] = hostPath
49+
}
50+
}
51+
3752
// WithLogger sets the logger for the config builder
3853
func WithLogger(logger logger.Interface) Option {
3954
return func(b *builder) {

0 commit comments

Comments
 (0)