mirror of
https://github.com/NVIDIA/nvidia-container-toolkit
synced 2025-04-02 12:00:18 +00:00
Merge pull request #805 from alam0rt/add-v3-containerd-config
Add support for containerd v3 configs
This commit is contained in:
commit
11acbe7ca2
@ -18,12 +18,13 @@ package engine
|
||||
|
||||
// Interface defines the API for a runtime config updater.
|
||||
type Interface interface {
|
||||
DefaultRuntime() string
|
||||
AddRuntime(string, string, bool) error
|
||||
Set(string, interface{})
|
||||
DefaultRuntime() string
|
||||
GetRuntimeConfig(string) (RuntimeConfig, error)
|
||||
RemoveRuntime(string) error
|
||||
Save(string) (int64, error)
|
||||
GetRuntimeConfig(string) (RuntimeConfig, error)
|
||||
Set(string, interface{})
|
||||
String() string
|
||||
}
|
||||
|
||||
// RuntimeConfig defines the interface to query container runtime handler configuration
|
||||
|
@ -30,40 +30,40 @@ func (c *Config) AddRuntime(name string, path string, setAsDefault bool) error {
|
||||
}
|
||||
config := *c.Tree
|
||||
|
||||
config.Set("version", int64(2))
|
||||
config.Set("version", c.Version)
|
||||
|
||||
runtimeNamesForConfig := engine.GetLowLevelRuntimes(c)
|
||||
for _, r := range runtimeNamesForConfig {
|
||||
options := config.GetSubtreeByPath([]string{"plugins", "io.containerd.grpc.v1.cri", "containerd", "runtimes", r})
|
||||
options := config.GetSubtreeByPath([]string{"plugins", c.CRIRuntimePluginName, "containerd", "runtimes", r})
|
||||
if options == nil {
|
||||
continue
|
||||
}
|
||||
c.Logger.Debugf("using options from runtime %v: %v", r, options)
|
||||
config.SetPath([]string{"plugins", "io.containerd.grpc.v1.cri", "containerd", "runtimes", name}, options.Copy())
|
||||
config.SetPath([]string{"plugins", c.CRIRuntimePluginName, "containerd", "runtimes", name}, options.Copy())
|
||||
break
|
||||
}
|
||||
|
||||
if config.GetPath([]string{"plugins", "io.containerd.grpc.v1.cri", "containerd", "runtimes", name}) == nil {
|
||||
if config.GetPath([]string{"plugins", c.CRIRuntimePluginName, "containerd", "runtimes", name}) == nil {
|
||||
c.Logger.Warningf("could not infer options from runtimes %v; using defaults", runtimeNamesForConfig)
|
||||
config.SetPath([]string{"plugins", "io.containerd.grpc.v1.cri", "containerd", "runtimes", name, "runtime_type"}, c.RuntimeType)
|
||||
config.SetPath([]string{"plugins", "io.containerd.grpc.v1.cri", "containerd", "runtimes", name, "runtime_root"}, "")
|
||||
config.SetPath([]string{"plugins", "io.containerd.grpc.v1.cri", "containerd", "runtimes", name, "runtime_engine"}, "")
|
||||
config.SetPath([]string{"plugins", "io.containerd.grpc.v1.cri", "containerd", "runtimes", name, "privileged_without_host_devices"}, false)
|
||||
config.SetPath([]string{"plugins", c.CRIRuntimePluginName, "containerd", "runtimes", name, "runtime_type"}, c.RuntimeType)
|
||||
config.SetPath([]string{"plugins", c.CRIRuntimePluginName, "containerd", "runtimes", name, "runtime_root"}, "")
|
||||
config.SetPath([]string{"plugins", c.CRIRuntimePluginName, "containerd", "runtimes", name, "runtime_engine"}, "")
|
||||
config.SetPath([]string{"plugins", c.CRIRuntimePluginName, "containerd", "runtimes", name, "privileged_without_host_devices"}, false)
|
||||
}
|
||||
|
||||
if len(c.ContainerAnnotations) > 0 {
|
||||
annotations, err := c.getRuntimeAnnotations([]string{"plugins", "io.containerd.grpc.v1.cri", "containerd", "runtimes", name, "container_annotations"})
|
||||
annotations, err := c.getRuntimeAnnotations([]string{"plugins", c.CRIRuntimePluginName, "containerd", "runtimes", name, "container_annotations"})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
annotations = append(c.ContainerAnnotations, annotations...)
|
||||
config.SetPath([]string{"plugins", "io.containerd.grpc.v1.cri", "containerd", "runtimes", name, "container_annotations"}, annotations)
|
||||
config.SetPath([]string{"plugins", c.CRIRuntimePluginName, "containerd", "runtimes", name, "container_annotations"}, annotations)
|
||||
}
|
||||
|
||||
config.SetPath([]string{"plugins", "io.containerd.grpc.v1.cri", "containerd", "runtimes", name, "options", "BinaryName"}, path)
|
||||
config.SetPath([]string{"plugins", c.CRIRuntimePluginName, "containerd", "runtimes", name, "options", "BinaryName"}, path)
|
||||
|
||||
if setAsDefault {
|
||||
config.SetPath([]string{"plugins", "io.containerd.grpc.v1.cri", "containerd", "default_runtime_name"}, name)
|
||||
config.SetPath([]string{"plugins", c.CRIRuntimePluginName, "containerd", "default_runtime_name"}, name)
|
||||
}
|
||||
|
||||
*c.Tree = config
|
||||
@ -99,13 +99,13 @@ func (c *Config) getRuntimeAnnotations(path []string) ([]string, error) {
|
||||
// Set sets the specified containerd option.
|
||||
func (c *Config) Set(key string, value interface{}) {
|
||||
config := *c.Tree
|
||||
config.SetPath([]string{"plugins", "io.containerd.grpc.v1.cri", key}, value)
|
||||
config.SetPath([]string{"plugins", c.CRIRuntimePluginName, key}, value)
|
||||
*c.Tree = config
|
||||
}
|
||||
|
||||
// DefaultRuntime returns the default runtime for the cri-o config
|
||||
func (c Config) DefaultRuntime() string {
|
||||
if runtime, ok := c.GetPath([]string{"plugins", "io.containerd.grpc.v1.cri", "containerd", "default_runtime_name"}).(string); ok {
|
||||
if runtime, ok := c.GetPath([]string{"plugins", c.CRIRuntimePluginName, "containerd", "default_runtime_name"}).(string); ok {
|
||||
return runtime
|
||||
}
|
||||
return ""
|
||||
@ -119,14 +119,14 @@ func (c *Config) RemoveRuntime(name string) error {
|
||||
|
||||
config := *c.Tree
|
||||
|
||||
config.DeletePath([]string{"plugins", "io.containerd.grpc.v1.cri", "containerd", "runtimes", name})
|
||||
if runtime, ok := config.GetPath([]string{"plugins", "io.containerd.grpc.v1.cri", "containerd", "default_runtime_name"}).(string); ok {
|
||||
config.DeletePath([]string{"plugins", c.CRIRuntimePluginName, "containerd", "runtimes", name})
|
||||
if runtime, ok := config.GetPath([]string{"plugins", c.CRIRuntimePluginName, "containerd", "default_runtime_name"}).(string); ok {
|
||||
if runtime == name {
|
||||
config.DeletePath([]string{"plugins", "io.containerd.grpc.v1.cri", "containerd", "default_runtime_name"})
|
||||
config.DeletePath([]string{"plugins", c.CRIRuntimePluginName, "containerd", "default_runtime_name"})
|
||||
}
|
||||
}
|
||||
|
||||
runtimePath := []string{"plugins", "io.containerd.grpc.v1.cri", "containerd", "runtimes", name}
|
||||
runtimePath := []string{"plugins", c.CRIRuntimePluginName, "containerd", "runtimes", name}
|
||||
for i := 0; i < len(runtimePath); i++ {
|
||||
if runtimes, ok := config.GetPath(runtimePath[:len(runtimePath)-i]).(*toml.Tree); ok {
|
||||
if len(runtimes.Keys()) == 0 {
|
@ -46,7 +46,7 @@ func TestAddRuntime(t *testing.T) {
|
||||
privileged_without_host_devices = false
|
||||
runtime_engine = ""
|
||||
runtime_root = ""
|
||||
runtime_type = ""
|
||||
runtime_type = "io.containerd.runc.v2"
|
||||
[plugins."io.containerd.grpc.v1.cri".containerd.runtimes.test.options]
|
||||
BinaryName = "/usr/bin/test"
|
||||
`,
|
||||
@ -195,24 +195,85 @@ func TestAddRuntime(t *testing.T) {
|
||||
SystemdCgroup = false
|
||||
`,
|
||||
},
|
||||
{
|
||||
description: "empty v3 spec is supported",
|
||||
config: `
|
||||
version = 3
|
||||
`,
|
||||
expectedConfig: `
|
||||
version = 3
|
||||
[plugins]
|
||||
[plugins."io.containerd.cri.v1.runtime"]
|
||||
[plugins."io.containerd.cri.v1.runtime".containerd]
|
||||
[plugins."io.containerd.cri.v1.runtime".containerd.runtimes]
|
||||
[plugins."io.containerd.cri.v1.runtime".containerd.runtimes.test]
|
||||
privileged_without_host_devices = false
|
||||
runtime_engine = ""
|
||||
runtime_root = ""
|
||||
runtime_type = "io.containerd.runc.v2"
|
||||
[plugins."io.containerd.cri.v1.runtime".containerd.runtimes.test.options]
|
||||
BinaryName = "/usr/bin/test"
|
||||
`,
|
||||
expectedError: nil,
|
||||
},
|
||||
{
|
||||
description: "v3 spec is supported",
|
||||
config: `
|
||||
version = 3
|
||||
[plugins]
|
||||
[plugins."io.containerd.cri.v1.runtime"]
|
||||
[plugins."io.containerd.cri.v1.runtime".containerd]
|
||||
[plugins."io.containerd.cri.v1.runtime".containerd.runtimes]
|
||||
[plugins."io.containerd.cri.v1.runtime".containerd.runtimes.runc]
|
||||
privileged_without_host_devices = true
|
||||
runtime_engine = "engine"
|
||||
runtime_root = "root"
|
||||
runtime_type = "type"
|
||||
[plugins."io.containerd.cri.v1.runtime".containerd.runtimes.runc.options]
|
||||
BinaryName = "/usr/bin/runc"
|
||||
SystemdCgroup = true
|
||||
`,
|
||||
expectedConfig: `
|
||||
version = 3
|
||||
[plugins]
|
||||
[plugins."io.containerd.cri.v1.runtime"]
|
||||
[plugins."io.containerd.cri.v1.runtime".containerd]
|
||||
[plugins."io.containerd.cri.v1.runtime".containerd.runtimes]
|
||||
[plugins."io.containerd.cri.v1.runtime".containerd.runtimes.runc]
|
||||
privileged_without_host_devices = true
|
||||
runtime_engine = "engine"
|
||||
runtime_root = "root"
|
||||
runtime_type = "type"
|
||||
[plugins."io.containerd.cri.v1.runtime".containerd.runtimes.runc.options]
|
||||
BinaryName = "/usr/bin/runc"
|
||||
SystemdCgroup = true
|
||||
[plugins."io.containerd.cri.v1.runtime".containerd.runtimes.test]
|
||||
privileged_without_host_devices = true
|
||||
runtime_engine = "engine"
|
||||
runtime_root = "root"
|
||||
runtime_type = "type"
|
||||
[plugins."io.containerd.cri.v1.runtime".containerd.runtimes.test.options]
|
||||
BinaryName = "/usr/bin/test"
|
||||
SystemdCgroup = true
|
||||
`,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range testCases {
|
||||
t.Run(tc.description, func(t *testing.T) {
|
||||
cfg, err := toml.Load(tc.config)
|
||||
require.NoError(t, err)
|
||||
expectedConfig, err := toml.Load(tc.expectedConfig)
|
||||
require.NoError(t, err)
|
||||
|
||||
c := &Config{
|
||||
Logger: logger,
|
||||
Tree: cfg,
|
||||
}
|
||||
c, err := New(
|
||||
WithLogger(logger),
|
||||
WithConfigSource(toml.FromString(tc.config)),
|
||||
)
|
||||
require.NoError(t, err)
|
||||
|
||||
err = c.AddRuntime("test", "/usr/bin/test", tc.setAsDefault)
|
||||
require.NoError(t, err)
|
||||
|
||||
require.EqualValues(t, expectedConfig.String(), cfg.String())
|
||||
require.EqualValues(t, expectedConfig.String(), c.String())
|
||||
})
|
||||
}
|
||||
}
|
||||
@ -299,13 +360,13 @@ func TestGetRuntimeConfig(t *testing.T) {
|
||||
|
||||
for _, tc := range testCases {
|
||||
t.Run(tc.description, func(t *testing.T) {
|
||||
cfg, err := toml.Load(config)
|
||||
|
||||
c, err := New(
|
||||
WithLogger(logger),
|
||||
WithConfigSource(toml.FromString(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.GetBinaryPath())
|
@ -70,18 +70,20 @@ func (c *ConfigV1) AddRuntime(name string, path string, setAsDefault bool) error
|
||||
config.SetPath([]string{"plugins", "cri", "containerd", "runtimes", name, "options", "BinaryName"}, path)
|
||||
config.SetPath([]string{"plugins", "cri", "containerd", "runtimes", name, "options", "Runtime"}, path)
|
||||
|
||||
if setAsDefault && c.UseDefaultRuntimeName {
|
||||
config.SetPath([]string{"plugins", "cri", "containerd", "default_runtime_name"}, name)
|
||||
} else if setAsDefault {
|
||||
// Note: This is deprecated in containerd 1.4.0 and will be removed in 1.5.0
|
||||
if config.GetPath([]string{"plugins", "cri", "containerd", "default_runtime"}) == nil {
|
||||
config.SetPath([]string{"plugins", "cri", "containerd", "default_runtime", "runtime_type"}, c.RuntimeType)
|
||||
config.SetPath([]string{"plugins", "cri", "containerd", "default_runtime", "runtime_root"}, "")
|
||||
config.SetPath([]string{"plugins", "cri", "containerd", "default_runtime", "runtime_engine"}, "")
|
||||
config.SetPath([]string{"plugins", "cri", "containerd", "default_runtime", "privileged_without_host_devices"}, false)
|
||||
if setAsDefault {
|
||||
if !c.UseLegacyConfig {
|
||||
config.SetPath([]string{"plugins", "cri", "containerd", "default_runtime_name"}, name)
|
||||
} else {
|
||||
// Note: This is deprecated in containerd 1.4.0 and will be removed in 1.5.0
|
||||
if config.GetPath([]string{"plugins", "cri", "containerd", "default_runtime"}) == nil {
|
||||
config.SetPath([]string{"plugins", "cri", "containerd", "default_runtime", "runtime_type"}, c.RuntimeType)
|
||||
config.SetPath([]string{"plugins", "cri", "containerd", "default_runtime", "runtime_root"}, "")
|
||||
config.SetPath([]string{"plugins", "cri", "containerd", "default_runtime", "runtime_engine"}, "")
|
||||
config.SetPath([]string{"plugins", "cri", "containerd", "default_runtime", "privileged_without_host_devices"}, false)
|
||||
}
|
||||
config.SetPath([]string{"plugins", "cri", "containerd", "default_runtime", "options", "BinaryName"}, path)
|
||||
config.SetPath([]string{"plugins", "cri", "containerd", "default_runtime", "options", "Runtime"}, path)
|
||||
}
|
||||
config.SetPath([]string{"plugins", "cri", "containerd", "default_runtime", "options", "BinaryName"}, path)
|
||||
config.SetPath([]string{"plugins", "cri", "containerd", "default_runtime", "options", "Runtime"}, path)
|
||||
}
|
||||
|
||||
*c.Tree = config
|
||||
|
@ -200,20 +200,21 @@ func TestAddRuntimeV1(t *testing.T) {
|
||||
|
||||
for _, tc := range testCases {
|
||||
t.Run(tc.description, func(t *testing.T) {
|
||||
cfg, err := toml.Load(tc.config)
|
||||
require.NoError(t, err)
|
||||
expectedConfig, err := toml.Load(tc.expectedConfig)
|
||||
require.NoError(t, err)
|
||||
|
||||
c := &ConfigV1{
|
||||
Logger: logger,
|
||||
Tree: cfg,
|
||||
}
|
||||
c, err := New(
|
||||
WithLogger(logger),
|
||||
WithConfigSource(toml.FromString(tc.config)),
|
||||
WithUseLegacyConfig(true),
|
||||
WithRuntimeType(""),
|
||||
)
|
||||
require.NoError(t, err)
|
||||
|
||||
err = c.AddRuntime("test", "/usr/bin/test", tc.setAsDefault)
|
||||
require.NoError(t, err)
|
||||
|
||||
require.EqualValues(t, expectedConfig.String(), cfg.String())
|
||||
require.EqualValues(t, expectedConfig.String(), c.String())
|
||||
})
|
||||
}
|
||||
}
|
||||
|
@ -24,13 +24,28 @@ import (
|
||||
"github.com/NVIDIA/nvidia-container-toolkit/pkg/config/toml"
|
||||
)
|
||||
|
||||
const (
|
||||
defaultConfigVersion = 2
|
||||
defaultRuntimeType = "io.containerd.runc.v2"
|
||||
)
|
||||
|
||||
// Config represents the containerd config
|
||||
type Config struct {
|
||||
*toml.Tree
|
||||
Logger logger.Interface
|
||||
RuntimeType string
|
||||
UseDefaultRuntimeName bool
|
||||
ContainerAnnotations []string
|
||||
Version int64
|
||||
Logger logger.Interface
|
||||
RuntimeType string
|
||||
ContainerAnnotations []string
|
||||
// UseLegacyConfig indicates whether a config file pre v1.3 should be generated.
|
||||
// For version 1 config prior to containerd v1.4 the default runtime was
|
||||
// specified in a containerd.runtimes.default_runtime section.
|
||||
// This was deprecated in v1.4 in favour of containerd.default_runtime_name.
|
||||
// Support for this section has been removed in v2.0.
|
||||
UseLegacyConfig bool
|
||||
// CRIRuntimePluginName represents the fully qualified name of the containerd plugin
|
||||
// for the CRI runtime service. The name of this plugin was changed in v3 of the
|
||||
// containerd configuration file.
|
||||
CRIRuntimePluginName string
|
||||
}
|
||||
|
||||
var _ engine.Interface = (*Config)(nil)
|
||||
@ -55,7 +70,8 @@ func (c *containerdCfgRuntime) GetBinaryPath() string {
|
||||
// New creates a containerd config with the specified options
|
||||
func New(opts ...Option) (engine.Interface, error) {
|
||||
b := &builder{
|
||||
runtimeType: defaultRuntimeType,
|
||||
configVersion: defaultConfigVersion,
|
||||
runtimeType: defaultRuntimeType,
|
||||
}
|
||||
for _, opt := range opts {
|
||||
opt(b)
|
||||
@ -72,55 +88,74 @@ func New(opts ...Option) (engine.Interface, error) {
|
||||
return nil, fmt.Errorf("failed to load config: %v", err)
|
||||
}
|
||||
|
||||
configVersion, err := b.parseVersion(tomlConfig)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to parse config version: %w", err)
|
||||
}
|
||||
b.logger.Infof("Using config version %v", configVersion)
|
||||
|
||||
criRuntimePluginName, err := b.criRuntimePluginName(configVersion)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to get CRI runtime plugin name: %w", err)
|
||||
}
|
||||
b.logger.Infof("Using CRI runtime plugin name %q", criRuntimePluginName)
|
||||
|
||||
cfg := &Config{
|
||||
Tree: tomlConfig,
|
||||
Logger: b.logger,
|
||||
RuntimeType: b.runtimeType,
|
||||
UseDefaultRuntimeName: b.useLegacyConfig,
|
||||
ContainerAnnotations: b.containerAnnotations,
|
||||
Tree: tomlConfig,
|
||||
Version: configVersion,
|
||||
CRIRuntimePluginName: criRuntimePluginName,
|
||||
Logger: b.logger,
|
||||
RuntimeType: b.runtimeType,
|
||||
UseLegacyConfig: b.useLegacyConfig,
|
||||
ContainerAnnotations: b.containerAnnotations,
|
||||
}
|
||||
|
||||
version, err := cfg.parseVersion(b.useLegacyConfig)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to parse config version: %v", err)
|
||||
}
|
||||
switch version {
|
||||
switch configVersion {
|
||||
case 1:
|
||||
return (*ConfigV1)(cfg), nil
|
||||
case 2:
|
||||
default:
|
||||
return cfg, nil
|
||||
}
|
||||
|
||||
return nil, fmt.Errorf("unsupported config version: %v", version)
|
||||
}
|
||||
|
||||
// parseVersion returns the version of the config
|
||||
func (c *Config) parseVersion(useLegacyConfig bool) (int, error) {
|
||||
defaultVersion := 2
|
||||
if useLegacyConfig {
|
||||
defaultVersion = 1
|
||||
func (b *builder) parseVersion(c *toml.Tree) (int64, error) {
|
||||
if c == nil || len(c.Keys()) == 0 {
|
||||
// No config exists, or the config file is empty.
|
||||
if b.useLegacyConfig {
|
||||
// If a legacy config is explicitly requested, we default to a v1 config.
|
||||
return 1, nil
|
||||
}
|
||||
// Use the requested version.
|
||||
return int64(b.configVersion), nil
|
||||
}
|
||||
|
||||
switch v := c.Get("version").(type) {
|
||||
case nil:
|
||||
switch len(c.Keys()) {
|
||||
case 0: // No config exists, or the config file is empty, use version inferred from containerd
|
||||
return defaultVersion, nil
|
||||
default: // A config file exists, has content, and no version is set
|
||||
return 1, nil
|
||||
}
|
||||
return 1, nil
|
||||
case int64:
|
||||
return int(v), nil
|
||||
return v, nil
|
||||
default:
|
||||
return -1, fmt.Errorf("unsupported type for version field: %v", v)
|
||||
}
|
||||
}
|
||||
|
||||
func (b *builder) criRuntimePluginName(configVersion int64) (string, error) {
|
||||
switch configVersion {
|
||||
case 1:
|
||||
return "cri", nil
|
||||
case 2:
|
||||
return "io.containerd.grpc.v1.cri", nil
|
||||
default:
|
||||
return "io.containerd.cri.v1.runtime", nil
|
||||
}
|
||||
}
|
||||
|
||||
func (c *Config) GetRuntimeConfig(name string) (engine.RuntimeConfig, error) {
|
||||
if c == nil || c.Tree == nil {
|
||||
return nil, fmt.Errorf("config is nil")
|
||||
}
|
||||
runtimeData := c.GetSubtreeByPath([]string{"plugins", "io.containerd.grpc.v1.cri", "containerd", "runtimes", name})
|
||||
runtimeData := c.GetSubtreeByPath([]string{"plugins", c.CRIRuntimePluginName, "containerd", "runtimes", name})
|
||||
return &containerdCfgRuntime{
|
||||
tree: runtimeData,
|
||||
}, nil
|
||||
|
@ -21,16 +21,13 @@ import (
|
||||
"github.com/NVIDIA/nvidia-container-toolkit/pkg/config/toml"
|
||||
)
|
||||
|
||||
const (
|
||||
defaultRuntimeType = "io.containerd.runc.v2"
|
||||
)
|
||||
|
||||
type builder struct {
|
||||
logger logger.Interface
|
||||
configSource toml.Loader
|
||||
configVersion int
|
||||
useLegacyConfig bool
|
||||
path string
|
||||
runtimeType string
|
||||
useLegacyConfig bool
|
||||
containerAnnotations []string
|
||||
}
|
||||
|
||||
@ -65,13 +62,20 @@ func WithRuntimeType(runtimeType string) Option {
|
||||
}
|
||||
}
|
||||
|
||||
// WithUseLegacyConfig sets the useLegacyConfig flag for the config builder
|
||||
// WithUseLegacyConfig sets the useLegacyConfig flag for the config builder.
|
||||
func WithUseLegacyConfig(useLegacyConfig bool) Option {
|
||||
return func(b *builder) {
|
||||
b.useLegacyConfig = useLegacyConfig
|
||||
}
|
||||
}
|
||||
|
||||
// WithConfigVersion sets the config version for the config builder
|
||||
func WithConfigVersion(configVersion int) Option {
|
||||
return func(b *builder) {
|
||||
b.configVersion = configVersion
|
||||
}
|
||||
}
|
||||
|
||||
// WithContainerAnnotations sets the container annotations for the config builder
|
||||
func WithContainerAnnotations(containerAnnotations ...string) Option {
|
||||
return func(b *builder) {
|
||||
|
@ -166,3 +166,13 @@ func (c *Config) GetRuntimeConfig(name string) (engine.RuntimeConfig, error) {
|
||||
}
|
||||
return &dockerRuntime{}, nil
|
||||
}
|
||||
|
||||
// String returns the string representation of the JSON config.
|
||||
func (c Config) String() string {
|
||||
output, err := json.MarshalIndent(c, "", " ")
|
||||
if err != nil {
|
||||
return fmt.Sprintf("invalid JSON: %v", err)
|
||||
}
|
||||
|
||||
return string(output)
|
||||
}
|
||||
|
26
pkg/config/toml/source-map.go
Normal file
26
pkg/config/toml/source-map.go
Normal file
@ -0,0 +1,26 @@
|
||||
/**
|
||||
# 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
|
||||
|
||||
type tomlMap map[string]interface{}
|
||||
|
||||
var _ Loader = (*tomlFile)(nil)
|
||||
|
||||
// Load loads the contents of the specified TOML file as a map.
|
||||
func (l tomlMap) Load() (*Tree, error) {
|
||||
return LoadMap(l)
|
||||
}
|
26
pkg/config/toml/source-string.go
Normal file
26
pkg/config/toml/source-string.go
Normal file
@ -0,0 +1,26 @@
|
||||
/**
|
||||
# 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
|
||||
|
||||
type tomlString string
|
||||
|
||||
var _ Loader = (*tomlFile)(nil)
|
||||
|
||||
// Load loads the contents of the specified TOML file as a map.
|
||||
func (l tomlString) Load() (*Tree, error) {
|
||||
return Load(string(l))
|
||||
}
|
@ -25,15 +25,6 @@ type Loader interface {
|
||||
Load() (*Tree, error)
|
||||
}
|
||||
|
||||
// FromFile creates a TOML source from the specified file.
|
||||
// If an empty string is passed an empty toml config is used.
|
||||
func FromFile(path string) Loader {
|
||||
if path == "" {
|
||||
return Empty
|
||||
}
|
||||
return tomlFile(path)
|
||||
}
|
||||
|
||||
// FromCommandLine creates a TOML source from the output of a shell command and its corresponding args.
|
||||
// If the command is empty, an empty config is returned.
|
||||
func FromCommandLine(cmds ...string) Loader {
|
||||
@ -45,3 +36,30 @@ func FromCommandLine(cmds ...string) Loader {
|
||||
args: cmds[1:],
|
||||
}
|
||||
}
|
||||
|
||||
// FromFile creates a TOML source from the specified file.
|
||||
// If an empty string is passed an empty toml config is used.
|
||||
func FromFile(path string) Loader {
|
||||
if path == "" {
|
||||
return Empty
|
||||
}
|
||||
return tomlFile(path)
|
||||
}
|
||||
|
||||
// FromMap creates a TOML source for the specified map.
|
||||
// If an empty map is passed and empty tomly config is used.
|
||||
func FromMap(m map[string]interface{}) Loader {
|
||||
if m == nil {
|
||||
return Empty
|
||||
}
|
||||
return tomlMap(m)
|
||||
}
|
||||
|
||||
// FromString creates a TOML source for the specified contents.
|
||||
// If an empty string is passed an empty toml config is used.
|
||||
func FromString(contents string) Loader {
|
||||
if contents == "" {
|
||||
return Empty
|
||||
}
|
||||
return tomlString(contents)
|
||||
}
|
||||
|
@ -88,37 +88,36 @@ func TestUpdateV1ConfigDefaultRuntime(t *testing.T) {
|
||||
SetAsDefault: tc.setAsDefault,
|
||||
}
|
||||
|
||||
cfg, err := toml.Empty.Load()
|
||||
require.NoError(t, err, "%d: %v", i, tc)
|
||||
|
||||
v1 := &containerd.ConfigV1{
|
||||
Logger: logger,
|
||||
Tree: cfg,
|
||||
UseDefaultRuntimeName: !tc.legacyConfig,
|
||||
RuntimeType: runtimeType,
|
||||
}
|
||||
v1, err := containerd.New(
|
||||
containerd.WithLogger(logger),
|
||||
containerd.WithConfigSource(toml.Empty),
|
||||
containerd.WithRuntimeType(runtimeType),
|
||||
containerd.WithUseLegacyConfig(tc.legacyConfig),
|
||||
containerd.WithConfigVersion(1),
|
||||
)
|
||||
require.NoError(t, err)
|
||||
|
||||
err = o.UpdateConfig(v1)
|
||||
require.NoError(t, err, "%d: %v", i, tc)
|
||||
require.NoError(t, err)
|
||||
|
||||
defaultRuntimeName := v1.GetPath([]string{"plugins", "cri", "containerd", "default_runtime_name"})
|
||||
require.EqualValues(t, tc.expectedDefaultRuntimeName, defaultRuntimeName, "%d: %v", i, tc)
|
||||
cfg := v1.(*containerd.ConfigV1)
|
||||
defaultRuntimeName := cfg.GetPath([]string{"plugins", "cri", "containerd", "default_runtime_name"})
|
||||
require.EqualValues(t, tc.expectedDefaultRuntimeName, defaultRuntimeName)
|
||||
|
||||
defaultRuntime := v1.GetPath([]string{"plugins", "cri", "containerd", "default_runtime"})
|
||||
defaultRuntime := cfg.GetPath([]string{"plugins", "cri", "containerd", "default_runtime"})
|
||||
if tc.expectedDefaultRuntimeBinary == nil {
|
||||
require.Nil(t, defaultRuntime, "%d: %v", i, tc)
|
||||
require.Nil(t, defaultRuntime)
|
||||
} else {
|
||||
require.NotNil(t, defaultRuntime)
|
||||
|
||||
expected, err := defaultRuntimeTomlConfigV1(tc.expectedDefaultRuntimeBinary.(string))
|
||||
require.NoError(t, err, "%d: %v", i, tc)
|
||||
require.NoError(t, err)
|
||||
|
||||
configContents, _ := toml.Marshal(defaultRuntime.(*toml.Tree))
|
||||
expectedContents, _ := toml.Marshal(expected)
|
||||
|
||||
require.Equal(t, string(expectedContents), string(configContents), "%d: %v: %v", i, tc)
|
||||
}
|
||||
|
||||
})
|
||||
}
|
||||
}
|
||||
@ -234,24 +233,22 @@ func TestUpdateV1Config(t *testing.T) {
|
||||
RuntimeDir: runtimeDir,
|
||||
}
|
||||
|
||||
cfg, err := toml.Empty.Load()
|
||||
v1, err := containerd.New(
|
||||
containerd.WithLogger(logger),
|
||||
containerd.WithConfigSource(toml.Empty),
|
||||
containerd.WithRuntimeType(runtimeType),
|
||||
containerd.WithConfigVersion(1),
|
||||
containerd.WithContainerAnnotations("cdi.k8s.io/*"),
|
||||
)
|
||||
require.NoError(t, err)
|
||||
|
||||
v1 := &containerd.ConfigV1{
|
||||
Logger: logger,
|
||||
Tree: cfg,
|
||||
UseDefaultRuntimeName: true,
|
||||
RuntimeType: runtimeType,
|
||||
ContainerAnnotations: []string{"cdi.k8s.io/*"},
|
||||
}
|
||||
|
||||
err = o.UpdateConfig(v1)
|
||||
require.NoError(t, err)
|
||||
|
||||
expected, err := toml.TreeFromMap(tc.expectedConfig)
|
||||
require.NoError(t, err)
|
||||
|
||||
require.Equal(t, expected.String(), cfg.String())
|
||||
require.Equal(t, expected.String(), v1.String())
|
||||
})
|
||||
}
|
||||
}
|
||||
@ -393,29 +390,28 @@ func TestUpdateV1ConfigWithRuncPresent(t *testing.T) {
|
||||
RuntimeDir: runtimeDir,
|
||||
}
|
||||
|
||||
cfg, err := toml.TreeFromMap(runcConfigMapV1("/runc-binary"))
|
||||
v1, err := containerd.New(
|
||||
containerd.WithLogger(logger),
|
||||
containerd.WithConfigSource(toml.FromMap(runcConfigMapV1("/runc-binary"))),
|
||||
containerd.WithRuntimeType(runtimeType),
|
||||
containerd.WithConfigVersion(1),
|
||||
containerd.WithContainerAnnotations("cdi.k8s.io/*"),
|
||||
)
|
||||
require.NoError(t, err)
|
||||
|
||||
v1 := &containerd.ConfigV1{
|
||||
Logger: logger,
|
||||
Tree: cfg,
|
||||
UseDefaultRuntimeName: true,
|
||||
RuntimeType: runtimeType,
|
||||
ContainerAnnotations: []string{"cdi.k8s.io/*"},
|
||||
}
|
||||
|
||||
err = o.UpdateConfig(v1)
|
||||
require.NoError(t, err)
|
||||
|
||||
expected, err := toml.TreeFromMap(tc.expectedConfig)
|
||||
require.NoError(t, err)
|
||||
|
||||
require.Equal(t, expected.String(), cfg.String())
|
||||
require.Equal(t, expected.String(), v1.String())
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestRevertV1Config(t *testing.T) {
|
||||
logger, _ := testlog.NewNullLogger()
|
||||
testCases := []struct {
|
||||
config map[string]interface {
|
||||
}
|
||||
@ -469,25 +465,22 @@ func TestRevertV1Config(t *testing.T) {
|
||||
RuntimeName: "nvidia",
|
||||
}
|
||||
|
||||
cfg, err := toml.LoadMap(tc.config)
|
||||
require.NoError(t, err, "%d: %v", i, tc)
|
||||
|
||||
expected, err := toml.TreeFromMap(tc.expected)
|
||||
require.NoError(t, err, "%d: %v", i, tc)
|
||||
require.NoError(t, err)
|
||||
|
||||
v1 := &containerd.ConfigV1{
|
||||
Tree: cfg,
|
||||
UseDefaultRuntimeName: true,
|
||||
RuntimeType: runtimeType,
|
||||
}
|
||||
v1, err := containerd.New(
|
||||
containerd.WithLogger(logger),
|
||||
containerd.WithConfigSource(toml.FromMap(tc.config)),
|
||||
containerd.WithRuntimeType(runtimeType),
|
||||
|
||||
containerd.WithContainerAnnotations("cdi.k8s.io/*"),
|
||||
)
|
||||
require.NoError(t, err)
|
||||
|
||||
err = o.RevertConfig(v1)
|
||||
require.NoError(t, err, "%d: %v", i, tc)
|
||||
require.NoError(t, err)
|
||||
|
||||
configContents, _ := toml.Marshal(cfg)
|
||||
expectedContents, _ := toml.Marshal(expected)
|
||||
|
||||
require.Equal(t, string(expectedContents), string(configContents), "%d: %v", i, tc)
|
||||
require.Equal(t, expected.String(), v1.String())
|
||||
})
|
||||
}
|
||||
}
|
||||
|
@ -72,18 +72,19 @@ func TestUpdateV2ConfigDefaultRuntime(t *testing.T) {
|
||||
SetAsDefault: tc.setAsDefault,
|
||||
}
|
||||
|
||||
cfg, err := toml.LoadMap(map[string]interface{}{})
|
||||
v2, err := containerd.New(
|
||||
containerd.WithLogger(logger),
|
||||
containerd.WithConfigSource(toml.Empty),
|
||||
containerd.WithRuntimeType(runtimeType),
|
||||
containerd.WithContainerAnnotations("cdi.k8s.io/*"),
|
||||
)
|
||||
require.NoError(t, err)
|
||||
|
||||
v2 := &containerd.Config{
|
||||
Logger: logger,
|
||||
Tree: cfg,
|
||||
RuntimeType: runtimeType,
|
||||
}
|
||||
|
||||
err = o.UpdateConfig(v2)
|
||||
require.NoError(t, err)
|
||||
|
||||
cfg := v2.(*containerd.Config)
|
||||
|
||||
defaultRuntimeName := cfg.GetPath([]string{"plugins", "io.containerd.grpc.v1.cri", "containerd", "default_runtime_name"})
|
||||
require.EqualValues(t, tc.expectedDefaultRuntimeName, defaultRuntimeName)
|
||||
})
|
||||
@ -195,23 +196,21 @@ func TestUpdateV2Config(t *testing.T) {
|
||||
RuntimeDir: runtimeDir,
|
||||
}
|
||||
|
||||
cfg, err := toml.LoadMap(map[string]interface{}{})
|
||||
v2, err := containerd.New(
|
||||
containerd.WithLogger(logger),
|
||||
containerd.WithConfigSource(toml.Empty),
|
||||
containerd.WithRuntimeType(runtimeType),
|
||||
containerd.WithContainerAnnotations("cdi.k8s.io/*"),
|
||||
)
|
||||
require.NoError(t, err)
|
||||
|
||||
v2 := &containerd.Config{
|
||||
Logger: logger,
|
||||
Tree: cfg,
|
||||
RuntimeType: runtimeType,
|
||||
ContainerAnnotations: []string{"cdi.k8s.io/*"},
|
||||
}
|
||||
|
||||
err = o.UpdateConfig(v2)
|
||||
require.NoError(t, err)
|
||||
|
||||
expected, err := toml.TreeFromMap(tc.expectedConfig)
|
||||
require.NoError(t, err)
|
||||
|
||||
require.Equal(t, expected.String(), cfg.String())
|
||||
require.Equal(t, expected.String(), v2.String())
|
||||
})
|
||||
}
|
||||
|
||||
@ -348,28 +347,28 @@ func TestUpdateV2ConfigWithRuncPresent(t *testing.T) {
|
||||
RuntimeDir: runtimeDir,
|
||||
}
|
||||
|
||||
cfg, err := toml.LoadMap(runcConfigMapV2("/runc-binary"))
|
||||
v2, err := containerd.New(
|
||||
containerd.WithLogger(logger),
|
||||
containerd.WithConfigSource(toml.FromMap(runcConfigMapV2("/runc-binary"))),
|
||||
containerd.WithRuntimeType(runtimeType),
|
||||
containerd.WithContainerAnnotations("cdi.k8s.io/*"),
|
||||
)
|
||||
require.NoError(t, err)
|
||||
|
||||
v2 := &containerd.Config{
|
||||
Logger: logger,
|
||||
Tree: cfg,
|
||||
RuntimeType: runtimeType,
|
||||
ContainerAnnotations: []string{"cdi.k8s.io/*"},
|
||||
}
|
||||
|
||||
err = o.UpdateConfig(v2)
|
||||
require.NoError(t, err)
|
||||
|
||||
expected, err := toml.TreeFromMap(tc.expectedConfig)
|
||||
require.NoError(t, err)
|
||||
|
||||
require.Equal(t, expected.String(), cfg.String())
|
||||
require.Equal(t, expected.String(), v2.String())
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestRevertV2Config(t *testing.T) {
|
||||
logger, _ := testlog.NewNullLogger()
|
||||
|
||||
testCases := []struct {
|
||||
config map[string]interface {
|
||||
}
|
||||
@ -418,24 +417,21 @@ func TestRevertV2Config(t *testing.T) {
|
||||
RuntimeName: "nvidia",
|
||||
}
|
||||
|
||||
cfg, err := toml.LoadMap(tc.config)
|
||||
require.NoError(t, err)
|
||||
|
||||
expected, err := toml.TreeFromMap(tc.expected)
|
||||
require.NoError(t, err)
|
||||
|
||||
v2 := &containerd.Config{
|
||||
Tree: cfg,
|
||||
RuntimeType: runtimeType,
|
||||
}
|
||||
v2, err := containerd.New(
|
||||
containerd.WithLogger(logger),
|
||||
containerd.WithConfigSource(toml.FromMap(tc.config)),
|
||||
containerd.WithRuntimeType(runtimeType),
|
||||
containerd.WithContainerAnnotations("cdi.k8s.io/*"),
|
||||
)
|
||||
require.NoError(t, err)
|
||||
|
||||
err = o.RevertConfig(v2)
|
||||
require.NoError(t, err)
|
||||
|
||||
configContents, _ := toml.Marshal(cfg)
|
||||
expectedContents, _ := toml.Marshal(expected)
|
||||
|
||||
require.Equal(t, string(expectedContents), string(configContents))
|
||||
require.Equal(t, expected.String(), v2.String())
|
||||
})
|
||||
}
|
||||
}
|
||||
@ -454,6 +450,7 @@ func runtimeMapV2(binary string) map[string]interface{} {
|
||||
|
||||
func runcConfigMapV2(binary string) map[string]interface{} {
|
||||
return map[string]interface{}{
|
||||
"version": 2,
|
||||
"plugins": map[string]interface{}{
|
||||
"io.containerd.grpc.v1.cri": map[string]interface{}{
|
||||
"containerd": map[string]interface{}{
|
||||
|
@ -52,8 +52,11 @@ type Options struct {
|
||||
func Flags(opts *Options) []cli.Flag {
|
||||
flags := []cli.Flag{
|
||||
&cli.BoolFlag{
|
||||
Name: "use-legacy-config",
|
||||
Usage: "Specify whether a legacy (pre v1.3) config should be used",
|
||||
Name: "use-legacy-config",
|
||||
Usage: "Specify whether a legacy (pre v1.3) config should be used. " +
|
||||
"This ensures that a version 1 container config is created by default and that the " +
|
||||
"containerd.runtimes.default_runtime config section is used to define the default " +
|
||||
"runtime instead of container.default_runtime_name.",
|
||||
Destination: &opts.useLegacyConfig,
|
||||
EnvVars: []string{"CONTAINERD_USE_LEGACY_CONFIG"},
|
||||
},
|
||||
|
Loading…
Reference in New Issue
Block a user