Skip to content

Commit a06d838

Browse files
authored
Merge pull request #686 from NVIDIA/get-config-from-cmdline
Fetch current container runtime config
2 parents 4604e3b + f477dc0 commit a06d838

File tree

21 files changed

+596
-48
lines changed

21 files changed

+596
-48
lines changed

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

Lines changed: 54 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,10 @@ const (
4444
defaultContainerdConfigFilePath = "/etc/containerd/config.toml"
4545
defaultCrioConfigFilePath = "/etc/crio/crio.conf"
4646
defaultDockerConfigFilePath = "/etc/docker/daemon.json"
47+
48+
defaultConfigSource = configSourceFile
49+
configSourceCommand = "command"
50+
configSourceFile = "file"
4751
)
4852

4953
type command struct {
@@ -64,6 +68,7 @@ type config struct {
6468
dryRun bool
6569
runtime string
6670
configFilePath string
71+
configSource string
6772
mode string
6873
hookFilePath string
6974

@@ -120,6 +125,12 @@ func (m command) build() *cli.Command {
120125
Usage: "the config mode for runtimes that support multiple configuration mechanisms",
121126
Destination: &config.mode,
122127
},
128+
&cli.StringFlag{
129+
Name: "config-source",
130+
Usage: "the source to retrieve the container runtime configuration; one of [command, file]\"",
131+
Destination: &config.configSource,
132+
Value: defaultConfigSource,
133+
},
123134
&cli.StringFlag{
124135
Name: "oci-hook-path",
125136
Usage: "the path to the OCI runtime hook to create if --config-mode=oci-hook is specified. If no path is specified, the generated hook is output to STDOUT.\n\tNote: The use of OCI hooks is deprecated.",
@@ -202,6 +213,18 @@ func (m command) validateFlags(c *cli.Context, config *config) error {
202213
config.runtimeConfigOverrideJSON = ""
203214
}
204215

216+
switch config.configSource {
217+
case configSourceCommand:
218+
if config.runtime == "docker" {
219+
m.logger.Warningf("A %v Config Source is not supported for %v; using %v", config.configSource, config.runtime, configSourceFile)
220+
config.configSource = configSourceFile
221+
}
222+
case configSourceFile:
223+
break
224+
default:
225+
return fmt.Errorf("unrecognized Config Source: %v", config.configSource)
226+
}
227+
205228
return nil
206229
}
207230

@@ -220,20 +243,25 @@ func (m command) configureWrapper(c *cli.Context, config *config) error {
220243
func (m command) configureConfigFile(c *cli.Context, config *config) error {
221244
configFilePath := config.resolveConfigFilePath()
222245

223-
var cfg engine.Interface
224246
var err error
247+
configSource, err := config.resolveConfigSource()
248+
if err != nil {
249+
return err
250+
}
251+
252+
var cfg engine.Interface
225253
switch config.runtime {
226254
case "containerd":
227255
cfg, err = containerd.New(
228256
containerd.WithLogger(m.logger),
229257
containerd.WithPath(configFilePath),
230-
containerd.WithConfigSource(toml.FromFile(configFilePath)),
258+
containerd.WithConfigSource(configSource),
231259
)
232260
case "crio":
233261
cfg, err = crio.New(
234262
crio.WithLogger(m.logger),
235263
crio.WithPath(configFilePath),
236-
crio.WithConfigSource(toml.FromFile(configFilePath)),
264+
crio.WithConfigSource(configSource),
237265
)
238266
case "docker":
239267
cfg, err = docker.New(
@@ -295,6 +323,29 @@ func (c *config) resolveConfigFilePath() string {
295323
return ""
296324
}
297325

326+
// resolveConfigSource returns the default config source or the user provided config source
327+
func (c *config) resolveConfigSource() (toml.Loader, error) {
328+
switch c.configSource {
329+
case configSourceCommand:
330+
return c.getCommandConfigSource(), nil
331+
case configSourceFile:
332+
return toml.FromFile(c.configFilePath), nil
333+
default:
334+
return nil, fmt.Errorf("unrecognized config source: %s", c.configSource)
335+
}
336+
}
337+
338+
// getConfigSourceCommand returns the default cli command to fetch the current runtime config
339+
func (c *config) getCommandConfigSource() toml.Loader {
340+
switch c.runtime {
341+
case "containerd":
342+
return containerd.CommandLineSource("")
343+
case "crio":
344+
return crio.CommandLineSource("")
345+
}
346+
return toml.Empty
347+
}
348+
298349
// getOuputConfigPath returns the configured config path or "" if dry-run is enabled
299350
func (c *config) getOuputConfigPath() string {
300351
if c.dryRun {

internal/config/toml.go

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -170,11 +170,22 @@ func (t *Toml) Get(key string) interface{} {
170170
return (*toml.Tree)(t).Get(key)
171171
}
172172

173+
// GetDefault returns the value for the specified key and falls back to the default value if the Get call fails
174+
func (t *Toml) GetDefault(key string, def interface{}) interface{} {
175+
return (*toml.Tree)(t).GetDefault(key, def)
176+
}
177+
173178
// Set sets the specified key to the specified value in the TOML config.
174179
func (t *Toml) Set(key string, value interface{}) {
175180
(*toml.Tree)(t).Set(key, value)
176181
}
177182

183+
// WriteTo encode the Tree as Toml and writes it to the writer w.
184+
// Returns the number of bytes written in case of success, or an error if anything happened.
185+
func (t *Toml) WriteTo(w io.Writer) (int64, error) {
186+
return (*toml.Tree)(t).WriteTo(w)
187+
}
188+
178189
// commentDefaults applies the required comments for default values to the Toml.
179190
func (t *Toml) commentDefaults() *Toml {
180191
asToml := (*toml.Tree)(t)

pkg/config/engine/api.go

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,4 +23,10 @@ type Interface interface {
2323
Set(string, interface{})
2424
RemoveRuntime(string) error
2525
Save(string) (int64, error)
26+
GetRuntimeConfig(string) (RuntimeConfig, error)
27+
}
28+
29+
// RuntimeConfig defines the interface to query container runtime handler configuration
30+
type RuntimeConfig interface {
31+
GetBinaryPath() string
2632
}

pkg/config/engine/containerd/config_v1.go

Lines changed: 13 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -19,9 +19,8 @@ package containerd
1919
import (
2020
"fmt"
2121

22-
"github.com/pelletier/go-toml"
23-
2422
"github.com/NVIDIA/nvidia-container-toolkit/pkg/config/engine"
23+
"github.com/NVIDIA/nvidia-container-toolkit/pkg/config/toml"
2524
)
2625

2726
// ConfigV1 represents a version 1 containerd config
@@ -39,11 +38,7 @@ func (c *ConfigV1) AddRuntime(name string, path string, setAsDefault bool) error
3938

4039
config.Set("version", int64(1))
4140

42-
// By default we extract the runtime options from the runc settings; if this does not exist we get the options from the default runtime specified in the config.
43-
runtimeNamesForConfig := []string{"runc"}
44-
if name, ok := config.GetPath([]string{"plugins", "cri", "containerd", "default_runtime_name"}).(string); ok && name != "" {
45-
runtimeNamesForConfig = append(runtimeNamesForConfig, name)
46-
}
41+
runtimeNamesForConfig := engine.GetLowLevelRuntimes(c)
4742
for _, r := range runtimeNamesForConfig {
4843
options := config.GetSubtreeByPath([]string{"plugins", "cri", "containerd", "runtimes", r})
4944
if options == nil {
@@ -157,3 +152,14 @@ func (c *ConfigV1) Set(key string, value interface{}) {
157152
func (c ConfigV1) Save(path string) (int64, error) {
158153
return (Config)(c).Save(path)
159154
}
155+
156+
func (c *ConfigV1) GetRuntimeConfig(name string) (engine.RuntimeConfig, error) {
157+
if c == nil || c.Tree == nil {
158+
return nil, fmt.Errorf("config is nil")
159+
}
160+
runtimeData := c.GetSubtreeByPath([]string{"plugins", "cri", "containerd", "runtimes", name})
161+
162+
return &containerdCfgRuntime{
163+
tree: runtimeData,
164+
}, nil
165+
}

pkg/config/engine/containerd/config_v1_test.go

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -138,7 +138,7 @@ func TestAddRuntimeV1(t *testing.T) {
138138
`,
139139
},
140140
{
141-
description: "options from runc take precedence over default runtime",
141+
description: "options from the default runtime take precedence over runc",
142142
config: `
143143
[plugins]
144144
[plugins.cri]
@@ -186,14 +186,14 @@ func TestAddRuntimeV1(t *testing.T) {
186186
BinaryName = "/usr/bin/default"
187187
SystemdCgroup = false
188188
[plugins.cri.containerd.runtimes.test]
189-
privileged_without_host_devices = true
190-
runtime_engine = "engine"
191-
runtime_root = "root"
192-
runtime_type = "type"
189+
privileged_without_host_devices = false
190+
runtime_engine = "defaultengine"
191+
runtime_root = "defaultroot"
192+
runtime_type = "defaulttype"
193193
[plugins.cri.containerd.runtimes.test.options]
194194
BinaryName = "/usr/bin/test"
195195
Runtime = "/usr/bin/test"
196-
SystemdCgroup = true
196+
SystemdCgroup = false
197197
`,
198198
},
199199
}

pkg/config/engine/containerd/config_v2.go

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ package containerd
1919
import (
2020
"fmt"
2121

22+
"github.com/NVIDIA/nvidia-container-toolkit/pkg/config/engine"
2223
"github.com/NVIDIA/nvidia-container-toolkit/pkg/config/toml"
2324
)
2425

@@ -31,11 +32,7 @@ func (c *Config) AddRuntime(name string, path string, setAsDefault bool) error {
3132

3233
config.Set("version", int64(2))
3334

34-
// By default we extract the runtime options from the runc settings; if this does not exist we get the options from the default runtime specified in the config.
35-
runtimeNamesForConfig := []string{"runc"}
36-
if name, ok := config.GetPath([]string{"plugins", "io.containerd.grpc.v1.cri", "containerd", "default_runtime_name"}).(string); ok && name != "" {
37-
runtimeNamesForConfig = append(runtimeNamesForConfig, name)
38-
}
35+
runtimeNamesForConfig := engine.GetLowLevelRuntimes(c)
3936
for _, r := range runtimeNamesForConfig {
4037
options := config.GetSubtreeByPath([]string{"plugins", "io.containerd.grpc.v1.cri", "containerd", "runtimes", r})
4138
if options == nil {

pkg/config/engine/containerd/config_v2_test.go

Lines changed: 102 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -137,7 +137,7 @@ func TestAddRuntime(t *testing.T) {
137137
`,
138138
},
139139
{
140-
description: "options from runc take precedence over default runtime",
140+
description: "options from the default runtime take precedence over runc",
141141
config: `
142142
version = 2
143143
[plugins]
@@ -186,13 +186,13 @@ func TestAddRuntime(t *testing.T) {
186186
BinaryName = "/usr/bin/default"
187187
SystemdCgroup = false
188188
[plugins."io.containerd.grpc.v1.cri".containerd.runtimes.test]
189-
privileged_without_host_devices = true
190-
runtime_engine = "engine"
191-
runtime_root = "root"
192-
runtime_type = "type"
189+
privileged_without_host_devices = false
190+
runtime_engine = "defaultengine"
191+
runtime_root = "defaultroot"
192+
runtime_type = "defaulttype"
193193
[plugins."io.containerd.grpc.v1.cri".containerd.runtimes.test.options]
194194
BinaryName = "/usr/bin/test"
195-
SystemdCgroup = true
195+
SystemdCgroup = false
196196
`,
197197
},
198198
}
@@ -216,3 +216,99 @@ func TestAddRuntime(t *testing.T) {
216216
})
217217
}
218218
}
219+
220+
func TestGetRuntimeConfig(t *testing.T) {
221+
logger, _ := testlog.NewNullLogger()
222+
config := `
223+
version = 2
224+
[plugins]
225+
[plugins."io.containerd.grpc.v1.cri"]
226+
[plugins."io.containerd.grpc.v1.cri".containerd]
227+
default_runtime_name = "nvidia"
228+
disable_snapshot_annotations = true
229+
discard_unpacked_layers = false
230+
ignore_blockio_not_enabled_errors = false
231+
ignore_rdt_not_enabled_errors = false
232+
no_pivot = false
233+
snapshotter = "overlayfs"
234+
235+
[plugins."io.containerd.grpc.v1.cri".containerd.default_runtime]
236+
base_runtime_spec = ""
237+
cni_conf_dir = ""
238+
cni_max_conf_num = 0
239+
container_annotations = []
240+
pod_annotations = []
241+
privileged_without_host_devices = false
242+
privileged_without_host_devices_all_devices_allowed = false
243+
runtime_engine = ""
244+
runtime_path = ""
245+
runtime_root = ""
246+
runtime_type = ""
247+
sandbox_mode = ""
248+
snapshotter = ""
249+
250+
[plugins."io.containerd.grpc.v1.cri".containerd.runtimes]
251+
252+
[plugins."io.containerd.grpc.v1.cri".containerd.runtimes.runc]
253+
base_runtime_spec = ""
254+
cni_conf_dir = ""
255+
cni_max_conf_num = 0
256+
container_annotations = []
257+
pod_annotations = []
258+
privileged_without_host_devices = false
259+
privileged_without_host_devices_all_devices_allowed = false
260+
runtime_engine = ""
261+
runtime_path = ""
262+
runtime_root = ""
263+
runtime_type = "io.containerd.runc.v2"
264+
sandbox_mode = "podsandbox"
265+
snapshotter = ""
266+
267+
[plugins."io.containerd.grpc.v1.cri".containerd.runtimes.runc.options]
268+
BinaryName = "/usr/bin/runc"
269+
CriuImagePath = ""
270+
CriuPath = ""
271+
CriuWorkPath = ""
272+
IoGid = 0
273+
IoUid = 0
274+
NoNewKeyring = false
275+
NoPivotRoot = false
276+
Root = ""
277+
ShimCgroup = ""
278+
SystemdCgroup = false
279+
`
280+
testCases := []struct {
281+
description string
282+
runtime string
283+
expected string
284+
expectedError error
285+
}{
286+
{
287+
description: "valid runtime config, existing runtime",
288+
runtime: "runc",
289+
expected: "/usr/bin/runc",
290+
expectedError: nil,
291+
},
292+
{
293+
description: "valid runtime config, non-existing runtime",
294+
runtime: "some-other-runtime",
295+
expected: "",
296+
expectedError: nil,
297+
},
298+
}
299+
300+
for _, tc := range testCases {
301+
t.Run(tc.description, func(t *testing.T) {
302+
cfg, err := toml.Load(config)
303+
require.NoError(t, err)
304+
305+
c := &Config{
306+
Logger: logger,
307+
Tree: cfg,
308+
}
309+
rc, err := c.GetRuntimeConfig(tc.runtime)
310+
require.Equal(t, tc.expectedError, err)
311+
require.Equal(t, tc.expected, rc.GetBinaryPath())
312+
})
313+
}
314+
}

0 commit comments

Comments
 (0)