Skip to content

Commit

Permalink
fetch current container runtime config through the command line
Browse files Browse the repository at this point in the history
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]>
  • Loading branch information
tariq1890 committed Oct 2, 2024
1 parent 19482da commit 51272d0
Show file tree
Hide file tree
Showing 19 changed files with 487 additions and 89 deletions.
4 changes: 4 additions & 0 deletions .golangci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,10 @@ linters:
linters-settings:
goimports:
local-prefixes: github.com/NVIDIA/nvidia-container-toolkit
gosec:
excludes:
# TODO: Consider hardening security of command line invocations
- G204

issues:
exclude:
Expand Down
72 changes: 58 additions & 14 deletions cmd/nvidia-ctk/runtime/configure/configure.go
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,14 @@ const (
defaultContainerdConfigFilePath = "/etc/containerd/config.toml"
defaultCrioConfigFilePath = "/etc/crio/crio.conf"
defaultDockerConfigFilePath = "/etc/docker/daemon.json"

defaultConfigSource = configSourceCommand
configSourceCommand = "command"
configSourceFile = "file"

runtimeContainerd = "containerd"
runtimeCrio = "crio"
runtimeDocker = "docker"
)

type command struct {
Expand All @@ -64,6 +72,7 @@ type config struct {
dryRun bool
runtime string
configFilePath string
configSource string
mode string
hookFilePath string

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

switch config.runtime {
case "containerd", "crio", "docker":
case runtimeContainerd, runtimeCrio, runtimeDocker:
break
default:
return fmt.Errorf("unrecognized runtime '%v'", config.runtime)
}

switch config.runtime {
case "containerd", "crio":
case runtimeContainerd, runtimeCrio:
if config.nvidiaRuntime.path == defaultNVIDIARuntimeExecutable {
config.nvidiaRuntime.path = defaultNVIDIARuntimeExpecutablePath
}
Expand All @@ -190,7 +205,7 @@ func (m command) validateFlags(c *cli.Context, config *config) error {
}
}

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

var cfg engine.Interface
var err error
configSource, err := config.resolveConfigSource()
if err != nil {
return err
}

var cfg engine.Interface
switch config.runtime {
case "containerd":
case runtimeContainerd:
cfg, err = containerd.New(
containerd.WithLogger(m.logger),
containerd.WithPath(configFilePath),
containerd.WithConfigSource(toml.FromFile(configFilePath)),
containerd.WithConfigSource(configSource),
)
case "crio":
case runtimeCrio:
cfg, err = crio.New(
crio.WithLogger(m.logger),
crio.WithPath(configFilePath),
crio.WithConfigSource(toml.FromFile(configFilePath)),
crio.WithConfigSource(configSource),
)
case "docker":
case runtimeDocker:
cfg, err = docker.New(
docker.WithLogger(m.logger),
docker.WithPath(configFilePath),
Expand Down Expand Up @@ -285,16 +305,40 @@ func (c *config) resolveConfigFilePath() string {
return c.configFilePath
}
switch c.runtime {
case "containerd":
case runtimeContainerd:
return defaultContainerdConfigFilePath
case "crio":
case runtimeCrio:
return defaultCrioConfigFilePath
case "docker":
case runtimeDocker:
return defaultDockerConfigFilePath
}
return ""
}

// resolveConfigSource returns the default config source or the user provided config source
func (c *config) resolveConfigSource() (toml.Loader, error) {
switch c.configSource {
case configSourceCommand:
cmd := c.getConfigSourceCommand()
return toml.FromCommandLine(cmd), nil
case configSourceFile:
return toml.FromFile(c.configFilePath), nil
default:
return nil, fmt.Errorf("unrecognized config source: %s", c.configSource)
}
}

// getConfigSourceCommand returns the default cli command to fetch the current runtime config
func (c *config) getConfigSourceCommand() []string {
switch c.runtime {
case runtimeContainerd:
return []string{"containerd", "config", "dump"}
case runtimeCrio:
return []string{"crio", "status", "config"}
}
return []string{}
}

// getOuputConfigPath returns the configured config path or "" if dry-run is enabled
func (c *config) getOuputConfigPath() string {
if c.dryRun {
Expand All @@ -318,9 +362,9 @@ func enableCDI(config *config, cfg engine.Interface) error {
return nil
}
switch config.runtime {
case "containerd":
case runtimeContainerd:
cfg.Set("enable_cdi", true)
case "docker":
case runtimeDocker:
cfg.Set("features", map[string]bool{"cdi": true})
default:
return fmt.Errorf("enabling CDI in %s is not supported", config.runtime)
Expand Down
15 changes: 15 additions & 0 deletions internal/config/toml.go
Original file line number Diff line number Diff line change
Expand Up @@ -170,11 +170,26 @@ func (t *Toml) Get(key string) interface{} {
return (*toml.Tree)(t).Get(key)
}

// GetDefault returns the value for the specified key and falls back to the default value if the Get call fails
func (t *Toml) GetDefault(key string, def interface{}) interface{} {
val := t.Get(key)
if val == nil {
return def
}
return val
}

// Set sets the specified key to the specified value in the TOML config.
func (t *Toml) Set(key string, value interface{}) {
(*toml.Tree)(t).Set(key, value)
}

// WriteTo encode the Tree as Toml and writes it to the writer w.
// Returns the number of bytes written in case of success, or an error if anything happened.
func (t *Toml) WriteTo(w io.Writer) (int64, error) {
return (*toml.Tree)(t).WriteTo(w)
}

// commentDefaults applies the required comments for default values to the Toml.
func (t *Toml) commentDefaults() *Toml {
asToml := (*toml.Tree)(t)
Expand Down
6 changes: 6 additions & 0 deletions pkg/config/engine/api.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,4 +23,10 @@ type Interface interface {
Set(string, interface{})
RemoveRuntime(string) error
Save(string) (int64, error)
GetRuntimeConfig(string) (RuntimeConfig, error)
}

// RuntimeConfig defines the interface to query container runtime handler configuration
type RuntimeConfig interface {
GetBinPath() string
}
38 changes: 38 additions & 0 deletions pkg/config/engine/containerd/config_v1.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,13 +22,27 @@ import (
"github.com/pelletier/go-toml"

"github.com/NVIDIA/nvidia-container-toolkit/pkg/config/engine"
cfgtoml "github.com/NVIDIA/nvidia-container-toolkit/pkg/config/toml"
)

// ConfigV1 represents a version 1 containerd config
type ConfigV1 Config

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

type ctrdCfgV1Runtime struct {
tree *cfgtoml.Tree
}

var _ engine.RuntimeConfig = (*ctrdCfgV1Runtime)(nil)

func (c *ctrdCfgV1Runtime) GetBinPath() string {
if binPath, ok := c.tree.GetPath([]string{"options", "BinaryName"}).(string); ok {
return binPath
}
return ""
}

// AddRuntime adds a runtime to the containerd config
func (c *ConfigV1) AddRuntime(name string, path string, setAsDefault bool) error {
if c == nil || c.Tree == nil {
Expand Down Expand Up @@ -146,6 +160,18 @@ func (c *ConfigV1) RemoveRuntime(name string) error {
return nil
}

func (c *ConfigV1) GetRuntime(name string) (interface{}, error) {
if c == nil || c.Tree == nil {
return nil, fmt.Errorf("config is nil")
}
config := *c.Tree
runtimeData, ok := config.GetPath([]string{"plugins", "cri", "containerd", "runtimes", name}).(*toml.Tree)
if !ok {
return nil, fmt.Errorf("invalid toml object")
}
return runtimeData, nil
}

// SetOption sets the specified containerd option.
func (c *ConfigV1) Set(key string, value interface{}) {
config := *c.Tree
Expand All @@ -157,3 +183,15 @@ func (c *ConfigV1) Set(key string, value interface{}) {
func (c ConfigV1) Save(path string) (int64, error) {
return (Config)(c).Save(path)
}

func (c *ConfigV1) GetRuntimeConfig(name string) (engine.RuntimeConfig, error) {
if c == nil || c.Tree == nil {
return nil, fmt.Errorf("config is nil")
}
config := *c.Tree
runtimeData := config.GetSubtreeByPath([]string{"plugins", "cri", "containerd", "runtimes", name})

return &ctrdCfgV1Runtime{
tree: runtimeData,
}, nil
}
14 changes: 14 additions & 0 deletions pkg/config/engine/containerd/config_v2.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,9 +19,23 @@ package containerd
import (
"fmt"

"github.com/NVIDIA/nvidia-container-toolkit/pkg/config/engine"
"github.com/NVIDIA/nvidia-container-toolkit/pkg/config/toml"
)

type ctrdCfgV2Runtime struct {
tree *toml.Tree
}

var _ engine.RuntimeConfig = (*ctrdCfgV2Runtime)(nil)

func (c *ctrdCfgV2Runtime) GetBinPath() string {
if binPath, ok := c.tree.GetPath([]string{"options", "BinaryName"}).(string); ok {
return binPath
}
return ""
}

// AddRuntime adds a runtime to the containerd config
func (c *Config) AddRuntime(name string, path string, setAsDefault bool) error {
if c == nil || c.Tree == nil {
Expand Down
11 changes: 11 additions & 0 deletions pkg/config/engine/containerd/containerd.go
Original file line number Diff line number Diff line change
Expand Up @@ -98,3 +98,14 @@ func (c *Config) parseVersion(useLegacyConfig bool) (int, error) {
return -1, fmt.Errorf("unsupported type for version field: %v", v)
}
}

func (c *Config) GetRuntimeConfig(name string) (engine.RuntimeConfig, error) {
if c == nil || c.Tree == nil {
return nil, fmt.Errorf("config is nil")
}
config := *c.Tree
runtimeData := config.GetSubtreeByPath([]string{"plugins", "io.containerd.grpc.v1.cri", "containerd", "runtimes", name})
return &ctrdCfgV2Runtime{
tree: runtimeData,
}, nil
}
29 changes: 27 additions & 2 deletions pkg/config/engine/crio/crio.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,19 @@ type Config struct {
Logger logger.Interface
}

type crioRuntime struct {
tree *toml.Tree
}

var _ engine.RuntimeConfig = (*crioRuntime)(nil)

func (c *crioRuntime) GetBinPath() string {
if binaryPath, ok := c.tree.GetPath([]string{"runtime_path"}).(string); ok {
return binaryPath
}
return ""
}

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

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

config := *c.Tree

// 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.
runtimeNamesForConfig := []string{"runc"}
// 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.
var runtimeNamesForConfig []string
if name, ok := config.GetPath([]string{"crio", "runtime", "default_runtime"}).(string); ok && name != "" {
runtimeNamesForConfig = append(runtimeNamesForConfig, name)
}
runtimeNamesForConfig = append(runtimeNamesForConfig, "runc")
for _, r := range runtimeNamesForConfig {
if options, ok := config.GetPath([]string{"crio", "runtime", "runtimes", r}).(*toml.Tree); ok {
c.Logger.Debugf("using options from runtime %v: %v", r, options.String())
Expand Down Expand Up @@ -129,3 +143,14 @@ func (c *Config) RemoveRuntime(name string) error {
*c.Tree = config
return nil
}

func (c *Config) GetRuntimeConfig(name string) (engine.RuntimeConfig, error) {
if c == nil || c.Tree == nil {
return nil, fmt.Errorf("config is nil")
}
config := *c.Tree
runtimeData := config.GetSubtreeByPath([]string{"crio", "runtime", "runtimes", name})
return &crioRuntime{
tree: runtimeData,
}, nil
}
4 changes: 2 additions & 2 deletions pkg/config/engine/crio/crio_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -91,7 +91,7 @@ func TestAddRuntime(t *testing.T) {
`,
},
{
description: "options from runc take precedence over default runtime",
description: "options from runc DO NOT take precedence over default runtime",
config: `
[crio]
[crio.runtime]
Expand Down Expand Up @@ -120,7 +120,7 @@ func TestAddRuntime(t *testing.T) {
[crio.runtime.runtimes.test]
runtime_path = "/usr/bin/test"
runtime_type = "oci"
runc_option = "option"
default_option = "option"
`,
},
}
Expand Down
Loading

0 comments on commit 51272d0

Please sign in to comment.