Skip to content

Commit a3693c8

Browse files
committed
fetch current container runtime config through the command line
Signed-off-by: Tariq Ibrahim <[email protected]> add default runtime binary path to runtimes field of toolkit config toml Signed-off-by: Tariq Ibrahim <[email protected]>
1 parent 4604e3b commit a3693c8

File tree

19 files changed

+581
-39
lines changed

19 files changed

+581
-39
lines changed

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

Lines changed: 58 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,14 @@ 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"
51+
52+
runtimeContainerd = "containerd"
53+
runtimeCrio = "crio"
54+
runtimeDocker = "docker"
4755
)
4856

4957
type command struct {
@@ -64,6 +72,7 @@ type config struct {
6472
dryRun bool
6573
runtime string
6674
configFilePath string
75+
configSource string
6776
mode string
6877
hookFilePath string
6978

@@ -120,6 +129,12 @@ func (m command) build() *cli.Command {
120129
Usage: "the config mode for runtimes that support multiple configuration mechanisms",
121130
Destination: &config.mode,
122131
},
132+
&cli.StringFlag{
133+
Name: "config-source",
134+
Usage: "the source to retrieve the container runtime configuration; one of [command, file]\"",
135+
Destination: &config.configSource,
136+
Value: defaultConfigSource,
137+
},
123138
&cli.StringFlag{
124139
Name: "oci-hook-path",
125140
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.",
@@ -174,14 +189,14 @@ func (m command) validateFlags(c *cli.Context, config *config) error {
174189
config.mode = "config-file"
175190

176191
switch config.runtime {
177-
case "containerd", "crio", "docker":
192+
case runtimeContainerd, runtimeCrio, runtimeDocker:
178193
break
179194
default:
180195
return fmt.Errorf("unrecognized runtime '%v'", config.runtime)
181196
}
182197

183198
switch config.runtime {
184-
case "containerd", "crio":
199+
case runtimeContainerd, runtimeCrio:
185200
if config.nvidiaRuntime.path == defaultNVIDIARuntimeExecutable {
186201
config.nvidiaRuntime.path = defaultNVIDIARuntimeExpecutablePath
187202
}
@@ -190,7 +205,7 @@ func (m command) validateFlags(c *cli.Context, config *config) error {
190205
}
191206
}
192207

193-
if config.runtime != "containerd" && config.runtime != "docker" {
208+
if config.runtime != runtimeContainerd && config.runtime != runtimeDocker {
194209
if config.cdi.enabled {
195210
m.logger.Warningf("Ignoring cdi.enabled flag for %v", config.runtime)
196211
}
@@ -220,22 +235,27 @@ func (m command) configureWrapper(c *cli.Context, config *config) error {
220235
func (m command) configureConfigFile(c *cli.Context, config *config) error {
221236
configFilePath := config.resolveConfigFilePath()
222237

223-
var cfg engine.Interface
224238
var err error
239+
configSource, err := config.resolveConfigSource()
240+
if err != nil {
241+
return err
242+
}
243+
244+
var cfg engine.Interface
225245
switch config.runtime {
226-
case "containerd":
246+
case runtimeContainerd:
227247
cfg, err = containerd.New(
228248
containerd.WithLogger(m.logger),
229249
containerd.WithPath(configFilePath),
230-
containerd.WithConfigSource(toml.FromFile(configFilePath)),
250+
containerd.WithConfigSource(configSource),
231251
)
232-
case "crio":
252+
case runtimeCrio:
233253
cfg, err = crio.New(
234254
crio.WithLogger(m.logger),
235255
crio.WithPath(configFilePath),
236-
crio.WithConfigSource(toml.FromFile(configFilePath)),
256+
crio.WithConfigSource(configSource),
237257
)
238-
case "docker":
258+
case runtimeDocker:
239259
cfg, err = docker.New(
240260
docker.WithLogger(m.logger),
241261
docker.WithPath(configFilePath),
@@ -285,16 +305,40 @@ func (c *config) resolveConfigFilePath() string {
285305
return c.configFilePath
286306
}
287307
switch c.runtime {
288-
case "containerd":
308+
case runtimeContainerd:
289309
return defaultContainerdConfigFilePath
290-
case "crio":
310+
case runtimeCrio:
291311
return defaultCrioConfigFilePath
292-
case "docker":
312+
case runtimeDocker:
293313
return defaultDockerConfigFilePath
294314
}
295315
return ""
296316
}
297317

318+
// resolveConfigSource returns the default config source or the user provided config source
319+
func (c *config) resolveConfigSource() (toml.Loader, error) {
320+
switch c.configSource {
321+
case configSourceCommand:
322+
cmd, args := c.getConfigSourceCommand()
323+
return toml.FromCommandLine(cmd, args...), nil
324+
case configSourceFile:
325+
return toml.FromFile(c.configFilePath), nil
326+
default:
327+
return nil, fmt.Errorf("unrecognized config source: %s", c.configSource)
328+
}
329+
}
330+
331+
// getConfigSourceCommand returns the default cli command to fetch the current runtime config
332+
func (c *config) getConfigSourceCommand() (string, []string) {
333+
switch c.runtime {
334+
case runtimeContainerd:
335+
return "containerd", []string{"config", "dump"}
336+
case runtimeCrio:
337+
return "crio", []string{"status", "config"}
338+
}
339+
return "", []string{}
340+
}
341+
298342
// getOuputConfigPath returns the configured config path or "" if dry-run is enabled
299343
func (c *config) getOuputConfigPath() string {
300344
if c.dryRun {
@@ -318,9 +362,9 @@ func enableCDI(config *config, cfg engine.Interface) error {
318362
return nil
319363
}
320364
switch config.runtime {
321-
case "containerd":
365+
case runtimeContainerd:
322366
cfg.Set("enable_cdi", true)
323-
case "docker":
367+
case runtimeDocker:
324368
cfg.Set("features", map[string]bool{"cdi": true})
325369
default:
326370
return fmt.Errorf("enabling CDI in %s is not supported", config.runtime)

internal/config/toml.go

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -170,11 +170,26 @@ 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+
val := t.Get(key)
176+
if val == nil {
177+
return def
178+
}
179+
return val
180+
}
181+
173182
// Set sets the specified key to the specified value in the TOML config.
174183
func (t *Toml) Set(key string, value interface{}) {
175184
(*toml.Tree)(t).Set(key, value)
176185
}
177186

187+
// WriteTo encode the Tree as Toml and writes it to the writer w.
188+
// Returns the number of bytes written in case of success, or an error if anything happened.
189+
func (t *Toml) WriteTo(w io.Writer) (int64, error) {
190+
return (*toml.Tree)(t).WriteTo(w)
191+
}
192+
178193
// commentDefaults applies the required comments for default values to the Toml.
179194
func (t *Toml) commentDefaults() *Toml {
180195
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+
GetBinPath() string
2632
}

pkg/config/engine/containerd/config_v1.go

Lines changed: 12 additions & 2 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
@@ -157,3 +156,14 @@ func (c *ConfigV1) Set(key string, value interface{}) {
157156
func (c ConfigV1) Save(path string) (int64, error) {
158157
return (Config)(c).Save(path)
159158
}
159+
160+
func (c *ConfigV1) GetRuntimeConfig(name string) (engine.RuntimeConfig, error) {
161+
if c == nil || c.Tree == nil {
162+
return nil, fmt.Errorf("config is nil")
163+
}
164+
runtimeData := c.GetSubtreeByPath([]string{"plugins", "cri", "containerd", "runtimes", name})
165+
166+
return &containerdCfgRuntime{
167+
tree: runtimeData,
168+
}, nil
169+
}

pkg/config/engine/containerd/config_v2_test.go

Lines changed: 96 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -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.GetBinPath())
312+
})
313+
}
314+
}

pkg/config/engine/containerd/containerd.go

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,21 @@ type Config struct {
3535

3636
var _ engine.Interface = (*Config)(nil)
3737

38+
type containerdCfgRuntime struct {
39+
tree *toml.Tree
40+
}
41+
42+
var _ engine.RuntimeConfig = (*containerdCfgRuntime)(nil)
43+
44+
func (c *containerdCfgRuntime) GetBinPath() string {
45+
if c != nil && c.tree != nil {
46+
if binPath, ok := c.tree.GetPath([]string{"options", "BinaryName"}).(string); ok {
47+
return binPath
48+
}
49+
}
50+
return ""
51+
}
52+
3853
// New creates a containerd config with the specified options
3954
func New(opts ...Option) (engine.Interface, error) {
4055
b := &builder{
@@ -98,3 +113,13 @@ func (c *Config) parseVersion(useLegacyConfig bool) (int, error) {
98113
return -1, fmt.Errorf("unsupported type for version field: %v", v)
99114
}
100115
}
116+
117+
func (c *Config) GetRuntimeConfig(name string) (engine.RuntimeConfig, error) {
118+
if c == nil || c.Tree == nil {
119+
return nil, fmt.Errorf("config is nil")
120+
}
121+
runtimeData := c.GetSubtreeByPath([]string{"plugins", "io.containerd.grpc.v1.cri", "containerd", "runtimes", name})
122+
return &containerdCfgRuntime{
123+
tree: runtimeData,
124+
}, nil
125+
}

pkg/config/engine/containerd/option.go

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -78,3 +78,14 @@ func WithContainerAnnotations(containerAnnotations ...string) Option {
7878
b.containerAnnotations = containerAnnotations
7979
}
8080
}
81+
82+
// CommandLineSource returns the CLI-based containerd config loader
83+
func CommandLineSource(hostRoot string) toml.Loader {
84+
var cliArgs []string
85+
if hostRoot != "" {
86+
cliArgs = append(cliArgs, "chroot", hostRoot)
87+
}
88+
cliArgs = append(cliArgs, "containerd", "config", "dump")
89+
90+
return toml.FromCommandLine(cliArgs[0], cliArgs[1:]...)
91+
}

0 commit comments

Comments
 (0)