2021-08-23 13:14:53 +00:00
|
|
|
//go:build !windows
|
2020-07-07 20:33:46 +00:00
|
|
|
// +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"
|
2021-01-04 19:43:19 +00:00
|
|
|
"github.com/docker/docker/errdefs"
|
2023-02-17 19:12:06 +00:00
|
|
|
"github.com/docker/docker/libcontainerd/shimopts"
|
2020-07-07 20:33:46 +00:00
|
|
|
"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)
|
|
|
|
}
|
2023-02-17 19:12:06 +00:00
|
|
|
conf.Runtimes[config.LinuxV2RuntimeName] = types.Runtime{Path: defaultRuntimeName, ShimConfig: defaultV2ShimConfig(conf, defaultRuntimeName)}
|
2020-07-15 04:58:06 +00:00
|
|
|
conf.Runtimes[config.StockRuntimeName] = conf.Runtimes[config.LinuxV2RuntimeName]
|
2020-07-07 20:33:46 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
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")
|
2022-11-18 08:05:42 +00:00
|
|
|
runtimeOldDir := runtimeDir + "-old"
|
2020-07-07 20:33:46 +00:00
|
|
|
// Remove old temp directory if any
|
2022-11-18 08:05:42 +00:00
|
|
|
os.RemoveAll(runtimeOldDir)
|
2022-10-05 14:28:35 +00:00
|
|
|
tmpDir, err := os.MkdirTemp(daemon.configStore.Root, "gen-runtimes")
|
2020-07-07 20:33:46 +00:00
|
|
|
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
|
|
|
|
}
|
|
|
|
|
2022-11-18 08:05:42 +00:00
|
|
|
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")
|
2022-02-08 22:14:23 +00:00
|
|
|
if err = os.RemoveAll(runtimeDir); err != nil {
|
2022-11-18 08:05:42 +00:00
|
|
|
logrus.WithError(err).WithField("dir", runtimeDir).
|
|
|
|
Warn("failed to remove old runtimes dir")
|
2022-02-08 22:14:23 +00:00
|
|
|
return
|
|
|
|
}
|
2020-07-07 20:33:46 +00:00
|
|
|
}
|
|
|
|
if err = os.Rename(tmpDir, runtimeDir); err != nil {
|
|
|
|
err = errors.Wrap(err, "failed to setup runtimes dir, new containers may not start")
|
|
|
|
return
|
|
|
|
}
|
2022-11-18 08:05:42 +00:00
|
|
|
if err = os.RemoveAll(runtimeOldDir); err != nil {
|
|
|
|
logrus.WithError(err).WithField("dir", runtimeOldDir).
|
2020-07-07 20:33:46 +00:00
|
|
|
Warn("failed to remove old runtimes dir")
|
|
|
|
}
|
|
|
|
}()
|
|
|
|
|
2023-02-17 19:12:06 +00:00
|
|
|
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)
|
2020-07-07 20:33:46 +00:00
|
|
|
}
|
2023-02-17 19:12:06 +00:00
|
|
|
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)
|
|
|
|
}
|
|
|
|
}
|
2020-07-07 20:33:46 +00:00
|
|
|
}
|
2023-02-17 19:12:06 +00:00
|
|
|
runtimes[name] = rt
|
2020-07-07 20:33:46 +00:00
|
|
|
}
|
|
|
|
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.
|
2023-02-17 19:12:06 +00:00
|
|
|
func (daemon *Daemon) rewriteRuntimePath(name, p string, args []string) string {
|
2020-07-07 20:33:46 +00:00
|
|
|
if len(args) == 0 {
|
2023-02-17 19:12:06 +00:00
|
|
|
return p
|
2020-07-07 20:33:46 +00:00
|
|
|
}
|
|
|
|
|
2023-02-17 19:12:06 +00:00
|
|
|
return filepath.Join(daemon.configStore.Root, "runtimes", name)
|
2020-07-07 20:33:46 +00:00
|
|
|
}
|
2021-01-04 19:43:19 +00:00
|
|
|
|
2023-02-17 19:12:06 +00:00
|
|
|
func (daemon *Daemon) getRuntime(name string) (shim string, opts interface{}, err error) {
|
2021-01-04 19:43:19 +00:00
|
|
|
rt := daemon.configStore.GetRuntime(name)
|
|
|
|
if rt == nil {
|
2022-08-17 18:50:19 +00:00
|
|
|
if !config.IsPermissibleC8dRuntimeName(name) {
|
2023-02-17 19:12:06 +00:00
|
|
|
return "", nil, errdefs.InvalidParameter(errors.Errorf("unknown or invalid runtime name: %s", name))
|
daemon: support other containerd runtimes (MVP)
Contrary to popular belief, the OCI Runtime specification does not
specify the command-line API for runtimes. Looking at containerd's
architecture from the lens of the OCI Runtime spec, the _shim_ is the
OCI Runtime and runC is "just" an implementation detail of the
io.containerd.runc.v2 runtime. When one configures a non-default runtime
in Docker, what they're really doing is instructing Docker to create
containers using the io.containerd.runc.v2 runtime with a configuration
option telling the runtime that the runC binary is at some non-default
path. Consequently, only OCI runtimes which are compatible with the
io.containerd.runc.v2 shim, such as crun, can be used in this manner.
Other OCI runtimes, including kata-containers v2, come with their own
containerd shim and are not compatible with io.containerd.runc.v2.
As Docker has not historically provided a way to select a non-default
runtime which requires its own shim, runtimes such as kata-containers v2
could not be used with Docker.
Allow other containerd shims to be used with Docker; no daemon
configuration required. If the daemon is instructed to create a
container with a runtime name which does not match any of the configured
or stock runtimes, it passes the name along to containerd verbatim. A
user can start a container with the kata-containers runtime, for
example, simply by calling
docker run --runtime io.containerd.kata.v2
Runtime names which containerd would interpret as a path to an arbitrary
binary are disallowed. While handy for development and testing it is not
strictly necessary and would allow anyone with Engine API access to
trivially execute any binary on the host as root, so we have decided it
would be safest for our users if it was not allowed.
It is not yet possible to set an alternative containerd shim as the
default runtime; it can only be configured per-container.
Signed-off-by: Cory Snider <csnider@mirantis.com>
2022-07-20 20:12:01 +00:00
|
|
|
}
|
2023-02-17 19:12:06 +00:00
|
|
|
return name, nil, nil
|
2021-01-04 19:43:19 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if len(rt.Args) > 0 {
|
2023-02-17 19:12:06 +00:00
|
|
|
// 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")
|
2021-01-04 19:43:19 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-02-17 19:12:06 +00:00
|
|
|
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
|
2021-01-04 19:43:19 +00:00
|
|
|
}
|
|
|
|
|
2023-02-17 19:12:06 +00:00
|
|
|
return rt.ShimConfig.Binary, rt.ShimConfig.Opts, nil
|
2021-01-04 19:43:19 +00:00
|
|
|
}
|