diff --git a/cmd/cri/options/options.go b/cmd/cri/options/options.go index d0e7a5d45..5bac8cb40 100644 --- a/cmd/cri/options/options.go +++ b/cmd/cri/options/options.go @@ -120,6 +120,10 @@ func NewContainerRuntimeOptions() *config.ContainerRuntimeOptions { PodSandboxImage: defaultPodSandboxImage, ImagePullProgressDeadline: metav1.Duration{Duration: 1 * time.Minute}, NetworkPluginName: "cni", + RuntimeHandler: []string{ + "spin=io.containerd.spin.v1", + "wasmedge=io.containerd.wasmedge.v1", + }, CNIBinDir: cniBinDir, CNIConfDir: cniConfDir, diff --git a/cmd/server.go b/cmd/server.go index 3279331bd..abbbeb500 100644 --- a/cmd/server.go +++ b/cmd/server.go @@ -18,7 +18,9 @@ package cmd import ( "fmt" + "regexp" "runtime" + "strings" "github.com/Mirantis/cri-dockerd/backend" "github.com/Mirantis/cri-dockerd/cmd/cri/options" @@ -181,6 +183,18 @@ func RunCriDockerd(f *options.DockerCRIFlags, stopCh <-chan struct{}) error { } } + runtimeHandlerValidate := regexp.MustCompile(`^\w+=\w+\.[\w\.]+$`) + runtimeHandler := make(map[string]string) + for _, handler := range r.RuntimeHandler { + //TODO validate + valid := runtimeHandlerValidate.Match([]byte(handler)) + if !valid { + logrus.Fatalf("Could not parse runtime handler \"%s\" parameter. Format shoud be \"runtimeClassName=io.containerd.runtime.v1\"", handler) + } + kv := strings.Split(handler, "=") + runtimeHandler[kv[0]] = kv[1] + } + // Initialize streaming configuration. (Not using TLS now) streamingConfig := &streaming.Config{ // Use a relative redirect (no scheme or host). @@ -196,6 +210,7 @@ func RunCriDockerd(f *options.DockerCRIFlags, stopCh <-chan struct{}) error { ds, err := core.NewDockerService( dockerClientConfig, r.PodSandboxImage, + runtimeHandler, streamingConfig, &pluginSettings, f.RuntimeCgroups, diff --git a/config/options.go b/config/options.go index 33d2ac3b2..678c1b534 100644 --- a/config/options.go +++ b/config/options.go @@ -83,6 +83,8 @@ type ContainerRuntimeOptions struct { // HairpinMode is the mode used to allow endpoints of a Service to load // balance back to themselves if they should try to access their own Service HairpinMode HairpinMode + // RuntimeHandler is a slice of runtime handler mappings e.g. runc=iocontainerd.runc.v2 + RuntimeHandler []string } // AddFlags has the set of flags needed by cri-dockerd @@ -139,6 +141,13 @@ func (s *ContainerRuntimeOptions) AddFlags(fs *pflag.FlagSet) { "The address to bind the CRI streaming server to. If not specified, it will bind to all addresses.", ), ) + + fs.StringSliceVar( + &s.RuntimeHandler, + "runtime-handler", + s.RuntimeHandler, + "Additional runtimes e.g. runc=io.containerd.runc.v2", + ) // Network plugin settings for Docker. fs.StringVar( &s.PodCIDR, diff --git a/core/container_create.go b/core/container_create.go index 8d72fb6ea..4571e980d 100644 --- a/core/container_create.go +++ b/core/container_create.go @@ -65,6 +65,7 @@ func (ds *dockerService) CreateContainer( } containerName := makeContainerName(sandboxConfig, config) terminationMessagePath, _ := config.Annotations["io.kubernetes.container.terminationMessagePath"] + json, _, err := ds.getPodSandboxDetails(podSandboxID) createConfig := types.ContainerCreateConfig{ Name: containerName, Config: &container.Config{ @@ -89,6 +90,7 @@ func (ds *dockerService) CreateContainer( RestartPolicy: container.RestartPolicy{ Name: "no", }, + Runtime: json.Config.Labels[runtimeLabelName], }, } diff --git a/core/docker_service.go b/core/docker_service.go index 52cfdf8c1..d0aba79d5 100644 --- a/core/docker_service.go +++ b/core/docker_service.go @@ -123,6 +123,7 @@ var internalLabelKeys = []string{containerTypeLabelKey, containerLogPathLabelKey func NewDockerService( clientConfig *config.ClientConfig, podSandboxImage string, + runtimeHandler map[string]string, streamingConfig *streaming.Config, pluginSettings *config.NetworkPluginSettings, cgroupsName string, @@ -145,6 +146,7 @@ func NewDockerService( client: c, os: config.RealOS{}, podSandboxImage: podSandboxImage, + runtimeHandler: runtimeHandler, streamingRuntime: &streaming.StreamingRuntime{ Client: client, ExecHandler: &NativeExecHandler{}, @@ -256,6 +258,7 @@ type dockerService struct { client libdocker.DockerClientInterface os config.OSInterface podSandboxImage string + runtimeHandler map[string]string streamingRuntime *streaming.StreamingRuntime streamingServer streaming.Server diff --git a/core/sandbox_helpers.go b/core/sandbox_helpers.go index c9ef2bbf4..d6db9dea1 100644 --- a/core/sandbox_helpers.go +++ b/core/sandbox_helpers.go @@ -18,13 +18,14 @@ package core import ( "fmt" + "os" + "strings" + "time" + "github.com/Mirantis/cri-dockerd/libdocker" "github.com/Mirantis/cri-dockerd/utils" "github.com/Mirantis/cri-dockerd/utils/errors" "k8s.io/kubernetes/pkg/credentialprovider" - "os" - "strings" - "time" "github.com/Mirantis/cri-dockerd/config" dockertypes "github.com/docker/docker/api/types" @@ -47,7 +48,8 @@ const ( defaultSandboxOOMAdj int = -998 // Name of the underlying container runtime - runtimeName = "docker" + runtimeName = "docker" + runtimeLabelName = "pod.spec.runtimeClassName" ) var ( @@ -55,6 +57,18 @@ var ( defaultSandboxGracePeriod = time.Duration(10) * time.Second ) +func (ds *dockerService) getRuntimeFromRuntimeClassName(runtimeClassName string) (string, error) { + if runtimeClassName == "" || runtimeClassName == runtimeName { + return runtimeClassName, nil + } + runtimeHandler, ok := ds.runtimeHandler[runtimeClassName] + if ok { + return runtimeHandler, nil + } else { + return "", fmt.Errorf("RuntimeHandler %q not supported", runtimeClassName) + } +} + // Returns whether the sandbox network is ready, and whether the sandbox is known func (ds *dockerService) getNetworkReady(podSandboxID string) (bool, bool) { ds.networkReadyLock.Lock() diff --git a/core/sandbox_run.go b/core/sandbox_run.go index 996582716..dbcc744fb 100644 --- a/core/sandbox_run.go +++ b/core/sandbox_run.go @@ -20,6 +20,7 @@ import ( "context" "encoding/json" "fmt" + "github.com/Mirantis/cri-dockerd/config" "github.com/Mirantis/cri-dockerd/utils/errors" v1 "k8s.io/cri-api/pkg/apis/runtime/v1" @@ -51,9 +52,6 @@ func (ds *dockerService) RunPodSandbox( } // Step 2: Create the sandbox container. - if r.GetRuntimeHandler() != "" && r.GetRuntimeHandler() != runtimeName { - return nil, fmt.Errorf("RuntimeHandler %q not supported", r.GetRuntimeHandler()) - } createConfig, err := ds.makeSandboxDockerConfig(containerConfig, image) if err != nil { return nil, fmt.Errorf( @@ -62,6 +60,13 @@ func (ds *dockerService) RunPodSandbox( err, ) } + // Map Kubernetes runtimeClassName to Docker runtime. + runtimeHandler, err := ds.getRuntimeFromRuntimeClassName(r.GetRuntimeHandler()) + if err != nil { + return nil, err + } + // TODO: find a better way to pass runtime from K8s Pod to containers + createConfig.Config.Labels[runtimeLabelName] = runtimeHandler createResp, err := ds.client.CreateContainer(*createConfig) if err != nil { createResp, err = recoverFromCreationConflictIfNeeded(ds.client, *createConfig, err)