b0eed5ade6
Kubernetes only permits RuntimeClass values which are valid lowercase RFC 1123 labels, which disallows the period character. This prevents cri-dockerd from being able to support configuring alternative shimv2 runtimes for a pod as shimv2 runtime names must contain at least one period character. Add support for configuring named shimv2 runtimes in daemon.json so that runtime names can be aliased to Kubernetes-compatible names. Allow options to be set on shimv2 runtimes in daemon.json. The names of the new daemon runtime config fields have been selected to correspond with the equivalent field names in cri-containerd's configuration so that users can more easily follow documentation from the runtime vendor written for cri-containerd and apply it to daemon.json. Signed-off-by: Cory Snider <csnider@mirantis.com>
170 lines
5.6 KiB
Go
170 lines
5.6 KiB
Go
//go:build !windows
|
|
// +build !windows
|
|
|
|
package daemon
|
|
|
|
import (
|
|
"fmt"
|
|
"os"
|
|
"os/exec"
|
|
"path/filepath"
|
|
"strings"
|
|
|
|
v2runcoptions "github.com/containerd/containerd/runtime/v2/runc/options"
|
|
"github.com/docker/docker/api/types"
|
|
"github.com/docker/docker/daemon/config"
|
|
"github.com/docker/docker/errdefs"
|
|
"github.com/docker/docker/libcontainerd/shimopts"
|
|
"github.com/pkg/errors"
|
|
"github.com/sirupsen/logrus"
|
|
)
|
|
|
|
const (
|
|
defaultRuntimeName = "runc"
|
|
|
|
linuxShimV2 = "io.containerd.runc.v2"
|
|
)
|
|
|
|
func configureRuntimes(conf *config.Config) {
|
|
if conf.DefaultRuntime == "" {
|
|
conf.DefaultRuntime = config.StockRuntimeName
|
|
}
|
|
if conf.Runtimes == nil {
|
|
conf.Runtimes = make(map[string]types.Runtime)
|
|
}
|
|
conf.Runtimes[config.LinuxV2RuntimeName] = types.Runtime{Path: defaultRuntimeName, ShimConfig: defaultV2ShimConfig(conf, defaultRuntimeName)}
|
|
conf.Runtimes[config.StockRuntimeName] = conf.Runtimes[config.LinuxV2RuntimeName]
|
|
}
|
|
|
|
func defaultV2ShimConfig(conf *config.Config, runtimePath string) *types.ShimConfig {
|
|
return &types.ShimConfig{
|
|
Binary: linuxShimV2,
|
|
Opts: &v2runcoptions.Options{
|
|
BinaryName: runtimePath,
|
|
Root: filepath.Join(conf.ExecRoot, "runtime-"+defaultRuntimeName),
|
|
SystemdCgroup: UsingSystemd(conf),
|
|
NoPivotRoot: os.Getenv("DOCKER_RAMDISK") != "",
|
|
},
|
|
}
|
|
}
|
|
|
|
func (daemon *Daemon) loadRuntimes() error {
|
|
return daemon.initRuntimes(daemon.configStore.Runtimes)
|
|
}
|
|
|
|
func (daemon *Daemon) initRuntimes(runtimes map[string]types.Runtime) (err error) {
|
|
runtimeDir := filepath.Join(daemon.configStore.Root, "runtimes")
|
|
runtimeOldDir := runtimeDir + "-old"
|
|
// Remove old temp directory if any
|
|
os.RemoveAll(runtimeOldDir)
|
|
tmpDir, err := os.MkdirTemp(daemon.configStore.Root, "gen-runtimes")
|
|
if err != nil {
|
|
return errors.Wrap(err, "failed to get temp dir to generate runtime scripts")
|
|
}
|
|
defer func() {
|
|
if err != nil {
|
|
if err1 := os.RemoveAll(tmpDir); err1 != nil {
|
|
logrus.WithError(err1).WithField("dir", tmpDir).
|
|
Warn("failed to remove tmp dir")
|
|
}
|
|
return
|
|
}
|
|
|
|
if err = os.Rename(runtimeDir, runtimeOldDir); err != nil {
|
|
logrus.WithError(err).WithField("dir", runtimeDir).
|
|
Warn("failed to rename runtimes dir to old. Will try to removing it")
|
|
if err = os.RemoveAll(runtimeDir); err != nil {
|
|
logrus.WithError(err).WithField("dir", runtimeDir).
|
|
Warn("failed to remove old runtimes dir")
|
|
return
|
|
}
|
|
}
|
|
if err = os.Rename(tmpDir, runtimeDir); err != nil {
|
|
err = errors.Wrap(err, "failed to setup runtimes dir, new containers may not start")
|
|
return
|
|
}
|
|
if err = os.RemoveAll(runtimeOldDir); err != nil {
|
|
logrus.WithError(err).WithField("dir", runtimeOldDir).
|
|
Warn("failed to remove old runtimes dir")
|
|
}
|
|
}()
|
|
|
|
for name := range runtimes {
|
|
rt := runtimes[name]
|
|
if rt.Path == "" && rt.Type == "" {
|
|
return errors.Errorf("runtime %s: either a runtimeType or a path must be configured", name)
|
|
}
|
|
if rt.Path != "" {
|
|
if rt.Type != "" {
|
|
return errors.Errorf("runtime %s: cannot configure both path and runtimeType for the same runtime", name)
|
|
}
|
|
if len(rt.Options) > 0 {
|
|
return errors.Errorf("runtime %s: options cannot be used with a path runtime", name)
|
|
}
|
|
|
|
if len(rt.Args) > 0 {
|
|
script := filepath.Join(tmpDir, name)
|
|
content := fmt.Sprintf("#!/bin/sh\n%s %s $@\n", rt.Path, strings.Join(rt.Args, " "))
|
|
if err := os.WriteFile(script, []byte(content), 0700); err != nil {
|
|
return err
|
|
}
|
|
}
|
|
rt.ShimConfig = defaultV2ShimConfig(daemon.configStore, daemon.rewriteRuntimePath(name, rt.Path, rt.Args))
|
|
} else {
|
|
if len(rt.Args) > 0 {
|
|
return errors.Errorf("runtime %s: args cannot be used with a runtimeType runtime", name)
|
|
}
|
|
// Unlike implicit runtimes, there is no restriction on configuring a shim by path.
|
|
rt.ShimConfig = &types.ShimConfig{Binary: rt.Type}
|
|
if len(rt.Options) > 0 {
|
|
// It has to be a pointer type or there'll be a panic in containerd/typeurl when we try to start the container.
|
|
rt.ShimConfig.Opts, err = shimopts.Generate(rt.Type, rt.Options)
|
|
if err != nil {
|
|
return errors.Wrapf(err, "runtime %v", name)
|
|
}
|
|
}
|
|
}
|
|
runtimes[name] = rt
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// rewriteRuntimePath is used for runtimes which have custom arguments supplied.
|
|
// This is needed because the containerd API only calls the OCI runtime binary, there is no options for extra arguments.
|
|
// To support this case, the daemon wraps the specified runtime in a script that passes through those arguments.
|
|
func (daemon *Daemon) rewriteRuntimePath(name, p string, args []string) string {
|
|
if len(args) == 0 {
|
|
return p
|
|
}
|
|
|
|
return filepath.Join(daemon.configStore.Root, "runtimes", name)
|
|
}
|
|
|
|
func (daemon *Daemon) getRuntime(name string) (shim string, opts interface{}, err error) {
|
|
rt := daemon.configStore.GetRuntime(name)
|
|
if rt == nil {
|
|
if !config.IsPermissibleC8dRuntimeName(name) {
|
|
return "", nil, errdefs.InvalidParameter(errors.Errorf("unknown or invalid runtime name: %s", name))
|
|
}
|
|
return name, nil, nil
|
|
}
|
|
|
|
if len(rt.Args) > 0 {
|
|
// Check that the path of the runtime which the script wraps actually exists so
|
|
// that we can return a well known error which references the configured path
|
|
// instead of the wrapper script's.
|
|
if _, err := exec.LookPath(rt.Path); err != nil {
|
|
return "", nil, errors.Wrap(err, "error while looking up the specified runtime path")
|
|
}
|
|
}
|
|
|
|
if rt.ShimConfig == nil {
|
|
// Should never happen as daemon.initRuntimes always sets
|
|
// ShimConfig and config reloading is synchronized.
|
|
err := errdefs.System(errors.Errorf("BUG: runtime %s: rt.ShimConfig == nil", name))
|
|
logrus.Error(err)
|
|
return "", nil, err
|
|
}
|
|
|
|
return rt.ShimConfig.Binary, rt.ShimConfig.Opts, nil
|
|
}
|