Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
42 changes: 29 additions & 13 deletions cmd/nvidia-ctk/runtime/configure/configure.go
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,10 @@ const (
defaultContainerdConfigFilePath = "/etc/containerd/config.toml"
defaultCrioConfigFilePath = "/etc/crio/crio.conf"
defaultDockerConfigFilePath = "/etc/docker/daemon.json"

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

type command struct {
Expand Down Expand Up @@ -174,14 +178,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 +194,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 @@ -219,23 +223,24 @@ func (m command) configureWrapper(c *cli.Context, config *config) error {
// configureConfigFile updates the specified container engine config file to enable the NVIDIA runtime.
func (m command) configureConfigFile(c *cli.Context, config *config) error {
configFilePath := config.resolveConfigFilePath()
configCommand := config.resolveConfigCommand()

var cfg engine.Interface
var err error
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(toml.FromCommandLine(configCommand)),
)
case "crio":
case runtimeCrio:
cfg, err = crio.New(
crio.WithLogger(m.logger),
crio.WithPath(configFilePath),
crio.WithConfigSource(toml.FromFile(configFilePath)),
crio.WithConfigSource(toml.FromCommandLine(configCommand)),
)
case "docker":
case runtimeDocker:
cfg, err = docker.New(
docker.WithLogger(m.logger),
docker.WithPath(configFilePath),
Expand Down Expand Up @@ -285,16 +290,27 @@ 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 ""
}

// resolveConfigCommand returns the default cli command to fetch the current runtime config
func (c *config) resolveConfigCommand() []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 +334,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) (Runtime, error)
}

// Runtime defines the interface to query container runtime handler configuration
type Runtime interface {
GetBinPath() string
}
24 changes: 24 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,25 @@ 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
}

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 @@ -157,3 +169,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.Runtime, 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
}
11 changes: 11 additions & 0 deletions pkg/config/engine/containerd/config_v2.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,17 @@ import (
"github.com/NVIDIA/nvidia-container-toolkit/pkg/config/toml"
)

type ctrdCfgV2Runtime struct {
tree *toml.Tree
}

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.Runtime, 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
}
27 changes: 25 additions & 2 deletions pkg/config/engine/crio/crio.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,17 @@ type Config struct {
Logger logger.Interface
}

type crioRuntime struct {
tree *toml.Tree
}

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

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

// New creates a cri-o config with the specified options
Expand Down Expand Up @@ -65,11 +76,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 +141,14 @@ func (c *Config) RemoveRuntime(name string) error {
*c.Tree = config
return nil
}

func (c *Config) GetRuntimeConfig(name string) (engine.Runtime, 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
5 changes: 5 additions & 0 deletions pkg/config/engine/docker/docker.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ package docker

import (
"encoding/json"
"errors"
"fmt"

"github.com/NVIDIA/nvidia-container-toolkit/internal/logger"
Expand Down Expand Up @@ -132,3 +133,7 @@ func (c Config) Save(path string) (int64, error) {
n, err := config.Raw(path).Write(output)
return int64(n), err
}

func (c *Config) GetRuntimeConfig(name string) (engine.Runtime, error) {
return nil, errors.New("Not Implemented")
}
44 changes: 44 additions & 0 deletions pkg/config/toml/source-cli.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
/**
# Copyright 2024 NVIDIA CORPORATION
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
**/

package toml

import (
"bytes"
"fmt"
"os/exec"
)

type tomlCliSource struct {
command string
args []string
}

func (c tomlCliSource) Load() (*Tree, error) {
//nolint:gosec // Subprocess launched with a potential tainted input or cmd arguments
cmd := exec.Command(c.command, c.args...)

var outb bytes.Buffer
var errb bytes.Buffer

cmd.Stdout = &outb
cmd.Stderr = &errb
if err := cmd.Run(); err != nil {
return nil, fmt.Errorf("failed to run command %v %v: %w", c.command, c.args, err)
}

return LoadBytes(outb.Bytes())
}
13 changes: 13 additions & 0 deletions pkg/config/toml/source.go
Original file line number Diff line number Diff line change
Expand Up @@ -33,3 +33,16 @@ func FromFile(path string) Loader {
}
return tomlFile(path)
}

// FromCommandLine creates a TOML source from the output
// of a shell command specified via string slice.
// If an empty slice is passed an empty toml config is used.
func FromCommandLine(params []string) Loader {
if len(params) == 0 {
return Empty
}
return &tomlCliSource{
command: params[0],
args: params[1:],
}
}
Loading