diff --git a/pkg/config/engine/containerd/config_v3_test.go b/pkg/config/engine/containerd/config_v3_test.go new file mode 100644 index 00000000..a1adbece --- /dev/null +++ b/pkg/config/engine/containerd/config_v3_test.go @@ -0,0 +1,313 @@ +/** +# 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 containerd + +import ( + "testing" + + testlog "github.com/sirupsen/logrus/hooks/test" + "github.com/stretchr/testify/require" + + "github.com/NVIDIA/nvidia-container-toolkit/pkg/config/toml" +) + +func TestAddRuntimeV3(t *testing.T) { + logger, _ := testlog.NewNullLogger() + testCases := []struct { + description string + config string + setAsDefault bool + expectedConfig string + expectedError error + }{ + { + description: "empty config not default runtime", + 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 = "" + [plugins."io.containerd.grpc.v1.cri".containerd.runtimes.test.options] + BinaryName = "/usr/bin/test" + `, + expectedError: nil, + }, + { + description: "options from runc are imported", + 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 + `, + }, + { + description: "options from default runtime are imported", + config: ` + version = 3 + [plugins] + [plugins."io.containerd.grpc.v1.cri"] + [plugins."io.containerd.grpc.v1.cri".containerd] + default_runtime_name = "default" + [plugins."io.containerd.grpc.v1.cri".containerd.runtimes] + [plugins."io.containerd.grpc.v1.cri".containerd.runtimes.default] + privileged_without_host_devices = true + runtime_engine = "engine" + runtime_root = "root" + runtime_type = "type" + [plugins."io.containerd.grpc.v1.cri".containerd.runtimes.default.options] + BinaryName = "/usr/bin/default" + SystemdCgroup = true + `, + expectedConfig: ` + version = 3 + [plugins] + [plugins."io.containerd.grpc.v1.cri"] + [plugins."io.containerd.grpc.v1.cri".containerd] + default_runtime_name = "default" + [plugins."io.containerd.grpc.v1.cri".containerd.runtimes] + [plugins."io.containerd.grpc.v1.cri".containerd.runtimes.default] + privileged_without_host_devices = true + runtime_engine = "engine" + runtime_root = "root" + runtime_type = "type" + [plugins."io.containerd.grpc.v1.cri".containerd.runtimes.default.options] + BinaryName = "/usr/bin/default" + 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 + `, + }, + { + description: "options from the default runtime take precedence over runc", + config: ` + version = 3 + [plugins] + [plugins."io.containerd.grpc.v1.cri"] + [plugins."io.containerd.grpc.v1.cri".containerd] + default_runtime_name = "default" + [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.default] + privileged_without_host_devices = false + runtime_engine = "defaultengine" + runtime_root = "defaultroot" + runtime_type = "defaulttype" + [plugins."io.containerd.grpc.v1.cri".containerd.runtimes.default.options] + BinaryName = "/usr/bin/default" + SystemdCgroup = false + `, + expectedConfig: ` + version = 3 + [plugins] + [plugins."io.containerd.grpc.v1.cri"] + [plugins."io.containerd.grpc.v1.cri".containerd] + default_runtime_name = "default" + [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.default] + privileged_without_host_devices = false + runtime_engine = "defaultengine" + runtime_root = "defaultroot" + runtime_type = "defaulttype" + [plugins."io.containerd.grpc.v1.cri".containerd.runtimes.default.options] + BinaryName = "/usr/bin/default" + SystemdCgroup = false + [plugins."io.containerd.grpc.v1.cri".containerd.runtimes.test] + privileged_without_host_devices = false + runtime_engine = "defaultengine" + runtime_root = "defaultroot" + runtime_type = "defaulttype" + [plugins."io.containerd.grpc.v1.cri".containerd.runtimes.test.options] + BinaryName = "/usr/bin/test" + SystemdCgroup = false + `, + }, + } + + 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 := &ConfigV3{ + Logger: logger, + Tree: cfg, + } + + err = c.AddRuntime("test", "/usr/bin/test", tc.setAsDefault) + require.NoError(t, err) + + require.EqualValues(t, expectedConfig.String(), cfg.String()) + }) + } +} + +func TestGetRuntimeConfigV3(t *testing.T) { + logger, _ := testlog.NewNullLogger() + config := ` + version = 3 + [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 = "" + 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 := &ConfigV3{ + Logger: logger, + Tree: cfg, + } + rc, err := c.GetRuntimeConfig(tc.runtime) + require.Equal(t, tc.expectedError, err) + require.Equal(t, tc.expected, rc.GetBinaryPath()) + }) + } +} diff --git a/pkg/config/engine/containerd/containerd.go b/pkg/config/engine/containerd/containerd.go index a5b08810..cec4f3e2 100644 --- a/pkg/config/engine/containerd/containerd.go +++ b/pkg/config/engine/containerd/containerd.go @@ -89,6 +89,8 @@ func New(opts ...Option) (engine.Interface, error) { return (*ConfigV1)(cfg), nil case 2: return cfg, nil + case 3: + return (*ConfigV3)(cfg), nil } return nil, fmt.Errorf("unsupported config version: %v", version) @@ -96,7 +98,7 @@ func New(opts ...Option) (engine.Interface, error) { // parseVersion returns the version of the config func (c *Config) parseVersion(useLegacyConfig bool) (int, error) { - defaultVersion := 2 + defaultVersion := 3 if useLegacyConfig { defaultVersion = 1 } diff --git a/pkg/config/engine/containerd/option.go b/pkg/config/engine/containerd/option.go index 6174a9ca..3dd579c5 100644 --- a/pkg/config/engine/containerd/option.go +++ b/pkg/config/engine/containerd/option.go @@ -30,7 +30,7 @@ type builder struct { configSource toml.Loader path string runtimeType string - useLegacyConfig bool + configVersion int64 containerAnnotations []string } @@ -68,7 +68,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 int64) Option { + return func(b *builder) { + b.configVersion = configVersion } }