Skip to content

Commit c7f19f1

Browse files
committed
fetch current container runtime config through the command line
Signed-off-by: Tariq Ibrahim <[email protected]>
1 parent 4f440de commit c7f19f1

File tree

14 files changed

+333
-31
lines changed

14 files changed

+333
-31
lines changed

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

Lines changed: 29 additions & 13 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+
runtimeContainerd = "containerd"
49+
runtimeCrio = "crio"
50+
runtimeDocker = "docker"
4751
)
4852

4953
type command struct {
@@ -174,14 +178,14 @@ func (m command) validateFlags(c *cli.Context, config *config) error {
174178
config.mode = "config-file"
175179

176180
switch config.runtime {
177-
case "containerd", "crio", "docker":
181+
case runtimeContainerd, runtimeCrio, runtimeDocker:
178182
break
179183
default:
180184
return fmt.Errorf("unrecognized runtime '%v'", config.runtime)
181185
}
182186

183187
switch config.runtime {
184-
case "containerd", "crio":
188+
case runtimeContainerd, runtimeCrio:
185189
if config.nvidiaRuntime.path == defaultNVIDIARuntimeExecutable {
186190
config.nvidiaRuntime.path = defaultNVIDIARuntimeExpecutablePath
187191
}
@@ -190,7 +194,7 @@ func (m command) validateFlags(c *cli.Context, config *config) error {
190194
}
191195
}
192196

193-
if config.runtime != "containerd" && config.runtime != "docker" {
197+
if config.runtime != runtimeContainerd && config.runtime != runtimeDocker {
194198
if config.cdi.enabled {
195199
m.logger.Warningf("Ignoring cdi.enabled flag for %v", config.runtime)
196200
}
@@ -219,23 +223,24 @@ func (m command) configureWrapper(c *cli.Context, config *config) error {
219223
// configureConfigFile updates the specified container engine config file to enable the NVIDIA runtime.
220224
func (m command) configureConfigFile(c *cli.Context, config *config) error {
221225
configFilePath := config.resolveConfigFilePath()
226+
configCommand := config.resolveConfigCommand()
222227

223228
var cfg engine.Interface
224229
var err error
225230
switch config.runtime {
226-
case "containerd":
231+
case runtimeContainerd:
227232
cfg, err = containerd.New(
228233
containerd.WithLogger(m.logger),
229234
containerd.WithPath(configFilePath),
230-
containerd.WithConfigSource(toml.FromFile(configFilePath)),
235+
containerd.WithConfigSource(toml.FromCommandLine(configCommand)),
231236
)
232-
case "crio":
237+
case runtimeCrio:
233238
cfg, err = crio.New(
234239
crio.WithLogger(m.logger),
235240
crio.WithPath(configFilePath),
236-
crio.WithConfigSource(toml.FromFile(configFilePath)),
241+
crio.WithConfigSource(toml.FromCommandLine(configCommand)),
237242
)
238-
case "docker":
243+
case runtimeDocker:
239244
cfg, err = docker.New(
240245
docker.WithLogger(m.logger),
241246
docker.WithPath(configFilePath),
@@ -285,16 +290,27 @@ func (c *config) resolveConfigFilePath() string {
285290
return c.configFilePath
286291
}
287292
switch c.runtime {
288-
case "containerd":
293+
case runtimeContainerd:
289294
return defaultContainerdConfigFilePath
290-
case "crio":
295+
case runtimeCrio:
291296
return defaultCrioConfigFilePath
292-
case "docker":
297+
case runtimeDocker:
293298
return defaultDockerConfigFilePath
294299
}
295300
return ""
296301
}
297302

303+
// resolveConfigCommand returns the default cli command to fetch the current runtime config
304+
func (c *config) resolveConfigCommand() []string {
305+
switch c.runtime {
306+
case runtimeContainerd:
307+
return []string{"containerd", "config", "dump"}
308+
case runtimeCrio:
309+
return []string{"crio", "status", "config"}
310+
}
311+
return []string{}
312+
}
313+
298314
// getOuputConfigPath returns the configured config path or "" if dry-run is enabled
299315
func (c *config) getOuputConfigPath() string {
300316
if c.dryRun {
@@ -318,9 +334,9 @@ func enableCDI(config *config, cfg engine.Interface) error {
318334
return nil
319335
}
320336
switch config.runtime {
321-
case "containerd":
337+
case runtimeContainerd:
322338
cfg.Set("enable_cdi", true)
323-
case "docker":
339+
case runtimeDocker:
324340
cfg.Set("features", map[string]bool{"cdi": true})
325341
default:
326342
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) (Runtime, error)
27+
}
28+
29+
// Runtime defines the interface to query container runtime handler configuration
30+
type Runtime interface {
31+
GetBinPath() string
2632
}

pkg/config/engine/containerd/config_v1.go

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,13 +22,25 @@ import (
2222
"github.com/pelletier/go-toml"
2323

2424
"github.com/NVIDIA/nvidia-container-toolkit/pkg/config/engine"
25+
cfgtoml "github.com/NVIDIA/nvidia-container-toolkit/pkg/config/toml"
2526
)
2627

2728
// ConfigV1 represents a version 1 containerd config
2829
type ConfigV1 Config
2930

3031
var _ engine.Interface = (*ConfigV1)(nil)
3132

33+
type ctrdCfgV1Runtime struct {
34+
tree *cfgtoml.Tree
35+
}
36+
37+
func (c *ctrdCfgV1Runtime) GetBinPath() string {
38+
if binPath, ok := c.tree.GetPath([]string{"options", "BinaryName"}).(string); ok {
39+
return binPath
40+
}
41+
return ""
42+
}
43+
3244
// AddRuntime adds a runtime to the containerd config
3345
func (c *ConfigV1) AddRuntime(name string, path string, setAsDefault bool) error {
3446
if c == nil || c.Tree == nil {
@@ -157,3 +169,15 @@ func (c *ConfigV1) Set(key string, value interface{}) {
157169
func (c ConfigV1) Save(path string) (int64, error) {
158170
return (Config)(c).Save(path)
159171
}
172+
173+
func (c *ConfigV1) GetRuntimeConfig(name string) (engine.Runtime, error) {
174+
if c == nil || c.Tree == nil {
175+
return nil, fmt.Errorf("config is nil")
176+
}
177+
config := *c.Tree
178+
runtimeData := config.GetSubtreeByPath([]string{"plugins", "cri", "containerd", "runtimes", name})
179+
180+
return &ctrdCfgV1Runtime{
181+
tree: runtimeData,
182+
}, nil
183+
}

pkg/config/engine/containerd/config_v2.go

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,17 @@ import (
2222
"github.com/NVIDIA/nvidia-container-toolkit/pkg/config/toml"
2323
)
2424

25+
type ctrdCfgV2Runtime struct {
26+
tree *toml.Tree
27+
}
28+
29+
func (c *ctrdCfgV2Runtime) GetBinPath() string {
30+
if binPath, ok := c.tree.GetPath([]string{"options", "BinaryName"}).(string); ok {
31+
return binPath
32+
}
33+
return ""
34+
}
35+
2536
// AddRuntime adds a runtime to the containerd config
2637
func (c *Config) AddRuntime(name string, path string, setAsDefault bool) error {
2738
if c == nil || c.Tree == nil {

pkg/config/engine/containerd/containerd.go

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -98,3 +98,14 @@ func (c *Config) parseVersion(useLegacyConfig bool) (int, error) {
9898
return -1, fmt.Errorf("unsupported type for version field: %v", v)
9999
}
100100
}
101+
102+
func (c *Config) GetRuntimeConfig(name string) (engine.Runtime, error) {
103+
if c == nil || c.Tree == nil {
104+
return nil, fmt.Errorf("config is nil")
105+
}
106+
config := *c.Tree
107+
runtimeData := config.GetSubtreeByPath([]string{"plugins", "io.containerd.grpc.v1.cri", "containerd", "runtimes", name})
108+
return &ctrdCfgV2Runtime{
109+
tree: runtimeData,
110+
}, nil
111+
}

pkg/config/engine/crio/crio.go

Lines changed: 25 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,17 @@ type Config struct {
3030
Logger logger.Interface
3131
}
3232

33+
type crioRuntime struct {
34+
tree *toml.Tree
35+
}
36+
37+
func (c *crioRuntime) GetBinPath() string {
38+
if binaryPath, ok := c.tree.GetPath([]string{"runtime_path"}).(string); ok && binaryPath != "" {
39+
return binaryPath
40+
}
41+
return ""
42+
}
43+
3344
var _ engine.Interface = (*Config)(nil)
3445

3546
// New creates a cri-o config with the specified options
@@ -65,11 +76,12 @@ func (c *Config) AddRuntime(name string, path string, setAsDefault bool) error {
6576

6677
config := *c.Tree
6778

68-
// 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.
69-
runtimeNamesForConfig := []string{"runc"}
79+
// 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.
80+
var runtimeNamesForConfig []string
7081
if name, ok := config.GetPath([]string{"crio", "runtime", "default_runtime"}).(string); ok && name != "" {
7182
runtimeNamesForConfig = append(runtimeNamesForConfig, name)
7283
}
84+
runtimeNamesForConfig = append(runtimeNamesForConfig, "runc")
7385
for _, r := range runtimeNamesForConfig {
7486
if options, ok := config.GetPath([]string{"crio", "runtime", "runtimes", r}).(*toml.Tree); ok {
7587
c.Logger.Debugf("using options from runtime %v: %v", r, options.String())
@@ -129,3 +141,14 @@ func (c *Config) RemoveRuntime(name string) error {
129141
*c.Tree = config
130142
return nil
131143
}
144+
145+
func (c *Config) GetRuntimeConfig(name string) (engine.Runtime, error) {
146+
if c == nil || c.Tree == nil {
147+
return nil, fmt.Errorf("config is nil")
148+
}
149+
config := *c.Tree
150+
runtimeData := config.GetSubtreeByPath([]string{"crio", "runtime", "runtimes", name})
151+
return &crioRuntime{
152+
tree: runtimeData,
153+
}, nil
154+
}

pkg/config/engine/crio/crio_test.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -91,7 +91,7 @@ func TestAddRuntime(t *testing.T) {
9191
`,
9292
},
9393
{
94-
description: "options from runc take precedence over default runtime",
94+
description: "options from runc do NOT take precedence over default runtime",
9595
config: `
9696
[crio]
9797
[crio.runtime]
@@ -120,7 +120,7 @@ func TestAddRuntime(t *testing.T) {
120120
[crio.runtime.runtimes.test]
121121
runtime_path = "/usr/bin/test"
122122
runtime_type = "oci"
123-
runc_option = "option"
123+
default_option = "option"
124124
`,
125125
},
126126
}

pkg/config/engine/docker/docker.go

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ package docker
1818

1919
import (
2020
"encoding/json"
21+
"errors"
2122
"fmt"
2223

2324
"github.com/NVIDIA/nvidia-container-toolkit/internal/logger"
@@ -132,3 +133,7 @@ func (c Config) Save(path string) (int64, error) {
132133
n, err := config.Raw(path).Write(output)
133134
return int64(n), err
134135
}
136+
137+
func (c *Config) GetRuntimeConfig(name string) (engine.Runtime, error) {
138+
return nil, errors.New("Not Implemented")
139+
}

pkg/config/toml/source-cli.go

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
/**
2+
# Copyright 2024 NVIDIA CORPORATION
3+
#
4+
# Licensed under the Apache License, Version 2.0 (the "License");
5+
# you may not use this file except in compliance with the License.
6+
# You may obtain a copy of the License at
7+
#
8+
# http://www.apache.org/licenses/LICENSE-2.0
9+
#
10+
# Unless required by applicable law or agreed to in writing, software
11+
# distributed under the License is distributed on an "AS IS" BASIS,
12+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
# See the License for the specific language governing permissions and
14+
# limitations under the License.
15+
**/
16+
17+
package toml
18+
19+
import (
20+
"bytes"
21+
"fmt"
22+
"os/exec"
23+
)
24+
25+
type tomlCliSource struct {
26+
command string
27+
args []string
28+
}
29+
30+
func (c tomlCliSource) Load() (*Tree, error) {
31+
//nolint:gosec // Subprocess launched with a potential tainted input or cmd arguments
32+
cmd := exec.Command(c.command, c.args...)
33+
34+
var outb bytes.Buffer
35+
var errb bytes.Buffer
36+
37+
cmd.Stdout = &outb
38+
cmd.Stderr = &errb
39+
if err := cmd.Run(); err != nil {
40+
return nil, fmt.Errorf("failed to run command %v %v: %w", c.command, c.args, err)
41+
}
42+
43+
return LoadBytes(outb.Bytes())
44+
}

0 commit comments

Comments
 (0)