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 5, 2024
1 parent 4604e3b commit d81c5ce
Show file tree
Hide file tree
Showing 21 changed files with 673 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
}
28 changes: 28 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,29 @@ 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 c.tree != nil {
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 @@ -157,3 +173,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
}
16 changes: 16 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,25 @@ 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 c.tree != nil {
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
96 changes: 96 additions & 0 deletions pkg/config/engine/containerd/config_v2_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -216,3 +216,99 @@ func TestAddRuntime(t *testing.T) {
})
}
}

func TestGetRuntimeConfig(t *testing.T) {
logger, _ := testlog.NewNullLogger()
config := `
version = 2
[plugins]
[plugins."io.containerd.grpc.v1.cri"]
[plugins."io.containerd.grpc.v1.cri".containerd]
default_runtime_name = "nvidia"
disable_snapshot_annotations = true
discard_unpacked_layers = false
ignore_blockio_not_enabled_errors = false
ignore_rdt_not_enabled_errors = false
no_pivot = false
snapshotter = "overlayfs"
[plugins."io.containerd.grpc.v1.cri".containerd.default_runtime]
base_runtime_spec = ""
cni_conf_dir = ""
cni_max_conf_num = 0
container_annotations = []
pod_annotations = []
privileged_without_host_devices = false
privileged_without_host_devices_all_devices_allowed = false
runtime_engine = ""
runtime_path = ""
runtime_root = ""
runtime_type = ""
sandbox_mode = ""
snapshotter = ""
[plugins."io.containerd.grpc.v1.cri".containerd.runtimes]
[plugins."io.containerd.grpc.v1.cri".containerd.runtimes.runc]
base_runtime_spec = ""
cni_conf_dir = ""
cni_max_conf_num = 0
container_annotations = []
pod_annotations = []
privileged_without_host_devices = false
privileged_without_host_devices_all_devices_allowed = false
runtime_engine = ""
runtime_path = ""
runtime_root = ""
runtime_type = "io.containerd.runc.v2"
sandbox_mode = "podsandbox"
snapshotter = ""
[plugins."io.containerd.grpc.v1.cri".containerd.runtimes.runc.options]
BinaryName = "/usr/bin/runc"
CriuImagePath = ""
CriuPath = ""
CriuWorkPath = ""
IoGid = 0
IoUid = 0
NoNewKeyring = false
NoPivotRoot = false
Root = ""
ShimCgroup = ""
SystemdCgroup = false
`
testCases := []struct {
description string
runtime string
expected string
expectedError error
}{
{
description: "valid runtime config, existing runtime",
runtime: "runc",
expected: "/usr/bin/runc",
expectedError: nil,
},
{
description: "valid runtime config, non-existing runtime",
runtime: "some-other-runtime",
expected: "",
expectedError: nil,
},
}

for _, tc := range testCases {
t.Run(tc.description, func(t *testing.T) {
cfg, err := toml.Load(config)
require.NoError(t, err)

c := &Config{
Logger: logger,
Tree: cfg,
}
rc, err := c.GetRuntimeConfig(tc.runtime)
require.Equal(t, tc.expectedError, err)
require.Equal(t, tc.expected, rc.GetBinPath())
})
}
}
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
}
Loading

0 comments on commit d81c5ce

Please sign in to comment.