From c11d433b59b8e0ca6a218d744874a96a83cb3327 Mon Sep 17 00:00:00 2001 From: Sam Lockart Date: Mon, 18 Nov 2024 16:20:55 +1100 Subject: [PATCH] Add support for containerd version 3 config This change adds support for containerd configs with version=3. From the perspective of the runtime configuration the contents of the config are the same. This means that we just have to load the new version and ensure that this is propagated to the generated config. Note that we still use a default config of version=2 since we need to ensure compatibility with older containerd versions (1.6.x and 1.7.x). Signed-off-by: Sam Lockart Signed-off-by: Evan Lezar --- .../containerd/{config_v2.go => config.go} | 2 +- .../engine/containerd/config_v2_test.go | 62 +++++++++++++++++++ pkg/config/engine/containerd/containerd.go | 39 ++++++------ pkg/config/engine/containerd/option.go | 15 +++-- 4 files changed, 93 insertions(+), 25 deletions(-) rename pkg/config/engine/containerd/{config_v2.go => config.go} (99%) diff --git a/pkg/config/engine/containerd/config_v2.go b/pkg/config/engine/containerd/config.go similarity index 99% rename from pkg/config/engine/containerd/config_v2.go rename to pkg/config/engine/containerd/config.go index 562ccdf8..8b76edbb 100644 --- a/pkg/config/engine/containerd/config_v2.go +++ b/pkg/config/engine/containerd/config.go @@ -30,7 +30,7 @@ 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 { diff --git a/pkg/config/engine/containerd/config_v2_test.go b/pkg/config/engine/containerd/config_v2_test.go index 6304e210..80c2675f 100644 --- a/pkg/config/engine/containerd/config_v2_test.go +++ b/pkg/config/engine/containerd/config_v2_test.go @@ -195,6 +195,68 @@ func TestAddRuntime(t *testing.T) { SystemdCgroup = false `, }, + { + description: "empty v3 spec is supported", + config: ` + version = 3 + `, + expectedConfig: ` + version = 3 + [plugins] + [plugins."io.containerd.grpc.v1.cri"] + [plugins."io.containerd.grpc.v1.cri".containerd] + [plugins."io.containerd.grpc.v1.cri".containerd.runtimes] + [plugins."io.containerd.grpc.v1.cri".containerd.runtimes.test] + privileged_without_host_devices = false + runtime_engine = "" + runtime_root = "" + runtime_type = "io.containerd.runc.v2" + [plugins."io.containerd.grpc.v1.cri".containerd.runtimes.test.options] + BinaryName = "/usr/bin/test" + `, + expectedError: nil, + }, + { + description: "v3 spec is supported", + config: ` + version = 3 + [plugins] + [plugins."io.containerd.grpc.v1.cri"] + [plugins."io.containerd.grpc.v1.cri".containerd] + [plugins."io.containerd.grpc.v1.cri".containerd.runtimes] + [plugins."io.containerd.grpc.v1.cri".containerd.runtimes.runc] + privileged_without_host_devices = true + runtime_engine = "engine" + runtime_root = "root" + runtime_type = "type" + [plugins."io.containerd.grpc.v1.cri".containerd.runtimes.runc.options] + BinaryName = "/usr/bin/runc" + SystemdCgroup = true + `, + expectedConfig: ` + version = 3 + [plugins] + [plugins."io.containerd.grpc.v1.cri"] + [plugins."io.containerd.grpc.v1.cri".containerd] + [plugins."io.containerd.grpc.v1.cri".containerd.runtimes] + [plugins."io.containerd.grpc.v1.cri".containerd.runtimes.runc] + privileged_without_host_devices = true + runtime_engine = "engine" + runtime_root = "root" + runtime_type = "type" + [plugins."io.containerd.grpc.v1.cri".containerd.runtimes.runc.options] + BinaryName = "/usr/bin/runc" + SystemdCgroup = true + [plugins."io.containerd.grpc.v1.cri".containerd.runtimes.test] + privileged_without_host_devices = true + runtime_engine = "engine" + runtime_root = "root" + runtime_type = "type" + [plugins."io.containerd.grpc.v1.cri".containerd.runtimes.test.options] + BinaryName = "/usr/bin/test" + SystemdCgroup = true + `, + }, } for _, tc := range testCases { diff --git a/pkg/config/engine/containerd/containerd.go b/pkg/config/engine/containerd/containerd.go index a5b08810..b8f993a9 100644 --- a/pkg/config/engine/containerd/containerd.go +++ b/pkg/config/engine/containerd/containerd.go @@ -24,9 +24,15 @@ 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 + Version int64 Logger logger.Interface RuntimeType string UseDefaultRuntimeName bool @@ -55,7 +61,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,45 +79,41 @@ 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) + } + cfg := &Config{ Tree: tomlConfig, + Version: configVersion, Logger: b.logger, RuntimeType: b.runtimeType, - UseDefaultRuntimeName: b.useLegacyConfig, + UseDefaultRuntimeName: configVersion == 1, 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: + case 2, 3: return cfg, nil } - - return nil, fmt.Errorf("unsupported config version: %v", version) + return nil, fmt.Errorf("unsupported config version: %v", configVersion) } // 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) { 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 + return int64(b.configVersion), nil default: // A config file exists, has content, and no version is set return 1, nil } case int64: - return int(v), nil + return v, nil default: return -1, fmt.Errorf("unsupported type for version field: %v", v) } diff --git a/pkg/config/engine/containerd/option.go b/pkg/config/engine/containerd/option.go index 6174a9ca..d6412fec 100644 --- a/pkg/config/engine/containerd/option.go +++ b/pkg/config/engine/containerd/option.go @@ -21,16 +21,12 @@ 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 path string runtimeType string - useLegacyConfig bool containerAnnotations []string } @@ -68,7 +64,14 @@ func WithRuntimeType(runtimeType string) Option { // WithUseLegacyConfig sets the useLegacyConfig flag for the config builder func WithUseLegacyConfig(useLegacyConfig bool) Option { return func(b *builder) { - b.useLegacyConfig = useLegacyConfig + b.configVersion = 1 + } +} + +// WithConfigVersion sets the config version for the config builder +func WithConfigVersion(configVersion int) Option { + return func(b *builder) { + b.configVersion = configVersion } }