daemon: allow shimv2 runtimes to be configured

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>
This commit is contained in:
Cory Snider 2023-02-17 14:12:06 -05:00
parent c030f7f40d
commit b0eed5ade6
14 changed files with 713 additions and 76 deletions

View file

@ -653,12 +653,18 @@ type Checkpoint struct {
// Runtime describes an OCI runtime // Runtime describes an OCI runtime
type Runtime struct { type Runtime struct {
Path string `json:"path"` // "Legacy" runtime configuration for runc-compatible runtimes.
Path string `json:"path,omitempty"`
Args []string `json:"runtimeArgs,omitempty"` Args []string `json:"runtimeArgs,omitempty"`
// Shimv2 runtime configuration. Mutually exclusive with the legacy config above.
Type string `json:"runtimeType,omitempty"`
Options map[string]interface{} `json:"options,omitempty"`
// This is exposed here only for internal use // This is exposed here only for internal use
// It is not currently supported to specify custom shim configs ShimConfig *ShimConfig `json:"-"`
Shim *ShimConfig `json:"-"`
} }
// ShimConfig is used by runtime to configure containerd shims // ShimConfig is used by runtime to configure containerd shims

View file

@ -6,7 +6,6 @@ package daemon
import ( import (
"testing" "testing"
"github.com/docker/docker/api/types"
containertypes "github.com/docker/docker/api/types/container" containertypes "github.com/docker/docker/api/types/container"
"github.com/docker/docker/daemon/config" "github.com/docker/docker/daemon/config"
"github.com/docker/go-connections/nat" "github.com/docker/go-connections/nat"
@ -33,9 +32,8 @@ func TestContainerWarningHostAndPublishPorts(t *testing.T) {
NetworkMode: "host", NetworkMode: "host",
PortBindings: tc.ports, PortBindings: tc.ports,
} }
cs := &config.Config{ cs := &config.Config{}
Runtimes: map[string]types.Runtime{"runc": {}}, configureRuntimes(cs)
}
d := &Daemon{configStore: cs} d := &Daemon{configStore: cs}
wrns, err := d.verifyContainerSettings(hostConfig, &containertypes.Config{}, false) wrns, err := d.verifyContainerSettings(hostConfig, &containertypes.Config{}, false)
assert.NilError(t, err) assert.NilError(t, err)

View file

@ -908,15 +908,17 @@ func NewDaemon(ctx context.Context, config *config.Config, pluginStore *plugin.S
} }
} }
var rt types.Runtime var (
shim string
shimOpts interface{}
)
if runtime.GOOS != "windows" { if runtime.GOOS != "windows" {
rtPtr, err := d.getRuntime(config.GetDefaultRuntimeName()) shim, shimOpts, err = d.getRuntime(config.GetDefaultRuntimeName())
if err != nil { if err != nil {
return nil, err return nil, err
} }
rt = *rtPtr
} }
return pluginexec.New(ctx, getPluginExecRoot(config), pluginCli, config.ContainerdPluginNamespace, m, rt) return pluginexec.New(ctx, getPluginExecRoot(config), pluginCli, config.ContainerdPluginNamespace, m, shim, shimOpts)
} }
// Plugin system initialization should happen before restore. Do not change order. // Plugin system initialization should happen before restore. Do not change order.

View file

@ -705,7 +705,7 @@ func verifyPlatformContainerSettings(daemon *Daemon, hostConfig *containertypes.
hostConfig.Runtime = daemon.configStore.GetDefaultRuntimeName() hostConfig.Runtime = daemon.configStore.GetDefaultRuntimeName()
} }
if _, err := daemon.getRuntime(hostConfig.Runtime); err != nil { if _, _, err := daemon.getRuntime(hostConfig.Runtime); err != nil {
return warnings, err return warnings, err
} }

View file

@ -14,6 +14,7 @@ import (
"github.com/docker/docker/api/types" "github.com/docker/docker/api/types"
"github.com/docker/docker/daemon/config" "github.com/docker/docker/daemon/config"
"github.com/docker/docker/errdefs" "github.com/docker/docker/errdefs"
"github.com/docker/docker/libcontainerd/shimopts"
"github.com/pkg/errors" "github.com/pkg/errors"
"github.com/sirupsen/logrus" "github.com/sirupsen/logrus"
) )
@ -31,7 +32,7 @@ func configureRuntimes(conf *config.Config) {
if conf.Runtimes == nil { if conf.Runtimes == nil {
conf.Runtimes = make(map[string]types.Runtime) conf.Runtimes = make(map[string]types.Runtime)
} }
conf.Runtimes[config.LinuxV2RuntimeName] = types.Runtime{Path: defaultRuntimeName, Shim: defaultV2ShimConfig(conf, defaultRuntimeName)} conf.Runtimes[config.LinuxV2RuntimeName] = types.Runtime{Path: defaultRuntimeName, ShimConfig: defaultV2ShimConfig(conf, defaultRuntimeName)}
conf.Runtimes[config.StockRuntimeName] = conf.Runtimes[config.LinuxV2RuntimeName] conf.Runtimes[config.StockRuntimeName] = conf.Runtimes[config.LinuxV2RuntimeName]
} }
@ -88,17 +89,42 @@ func (daemon *Daemon) initRuntimes(runtimes map[string]types.Runtime) (err error
} }
}() }()
for name, rt := range runtimes { for name := range runtimes {
if len(rt.Args) > 0 { rt := runtimes[name]
script := filepath.Join(tmpDir, name) if rt.Path == "" && rt.Type == "" {
content := fmt.Sprintf("#!/bin/sh\n%s %s $@\n", rt.Path, strings.Join(rt.Args, " ")) return errors.Errorf("runtime %s: either a runtimeType or a path must be configured", name)
if err := os.WriteFile(script, []byte(content), 0700); err != nil { }
return err 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)
}
} }
} }
if rt.Shim == nil { runtimes[name] = rt
rt.Shim = defaultV2ShimConfig(daemon.configStore, rt.Path)
}
} }
return nil return nil
} }
@ -106,40 +132,39 @@ func (daemon *Daemon) initRuntimes(runtimes map[string]types.Runtime) (err error
// rewriteRuntimePath is used for runtimes which have custom arguments supplied. // 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. // 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. // 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, error) { func (daemon *Daemon) rewriteRuntimePath(name, p string, args []string) string {
if len(args) == 0 { if len(args) == 0 {
return p, nil return p
} }
// Check that the runtime path actually exists here so that we can return a well known error. return filepath.Join(daemon.configStore.Root, "runtimes", name)
if _, err := exec.LookPath(p); err != nil {
return "", errors.Wrap(err, "error while looking up the specified runtime path")
}
return filepath.Join(daemon.configStore.Root, "runtimes", name), nil
} }
func (daemon *Daemon) getRuntime(name string) (*types.Runtime, error) { func (daemon *Daemon) getRuntime(name string) (shim string, opts interface{}, err error) {
rt := daemon.configStore.GetRuntime(name) rt := daemon.configStore.GetRuntime(name)
if rt == nil { if rt == nil {
if !config.IsPermissibleC8dRuntimeName(name) { if !config.IsPermissibleC8dRuntimeName(name) {
return nil, errdefs.InvalidParameter(errors.Errorf("unknown or invalid runtime name: %s", name)) return "", nil, errdefs.InvalidParameter(errors.Errorf("unknown or invalid runtime name: %s", name))
} }
return &types.Runtime{Shim: &types.ShimConfig{Binary: name}}, nil return name, nil, nil
} }
if len(rt.Args) > 0 { if len(rt.Args) > 0 {
p, err := daemon.rewriteRuntimePath(name, rt.Path, rt.Args) // Check that the path of the runtime which the script wraps actually exists so
if err != nil { // that we can return a well known error which references the configured path
return nil, err // 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")
} }
rt.Path = p
rt.Args = nil
} }
if rt.Shim == nil { if rt.ShimConfig == nil {
rt.Shim = defaultV2ShimConfig(daemon.configStore, rt.Path) // 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, nil return rt.ShimConfig.Binary, rt.ShimConfig.Opts, nil
} }

View file

@ -8,6 +8,7 @@ import (
"path/filepath" "path/filepath"
"testing" "testing"
"github.com/containerd/containerd/plugin"
v2runcoptions "github.com/containerd/containerd/runtime/v2/runc/options" v2runcoptions "github.com/containerd/containerd/runtime/v2/runc/options"
"gotest.tools/v3/assert" "gotest.tools/v3/assert"
is "gotest.tools/v3/assert/cmp" is "gotest.tools/v3/assert/cmp"
@ -17,13 +18,110 @@ import (
"github.com/docker/docker/errdefs" "github.com/docker/docker/errdefs"
) )
func TestInitRuntimes_InvalidConfigs(t *testing.T) {
cases := []struct {
name string
runtime types.Runtime
expectErr string
}{
{
name: "Empty",
expectErr: "either a runtimeType or a path must be configured",
},
{
name: "ArgsOnly",
runtime: types.Runtime{Args: []string{"foo", "bar"}},
expectErr: "either a runtimeType or a path must be configured",
},
{
name: "OptionsOnly",
runtime: types.Runtime{Options: map[string]interface{}{"hello": "world"}},
expectErr: "either a runtimeType or a path must be configured",
},
{
name: "PathAndType",
runtime: types.Runtime{Path: "/bin/true", Type: "io.containerd.runsc.v1"},
expectErr: "cannot configure both",
},
{
name: "PathAndOptions",
runtime: types.Runtime{Path: "/bin/true", Options: map[string]interface{}{"a": "b"}},
expectErr: "options cannot be used with a path runtime",
},
{
name: "TypeAndArgs",
runtime: types.Runtime{Type: "io.containerd.runsc.v1", Args: []string{"--version"}},
expectErr: "args cannot be used with a runtimeType runtime",
},
{
name: "PathArgsOptions",
runtime: types.Runtime{
Path: "/bin/true",
Args: []string{"--version"},
Options: map[string]interface{}{"hmm": 3},
},
expectErr: "options cannot be used with a path runtime",
},
{
name: "TypeOptionsArgs",
runtime: types.Runtime{
Type: "io.containerd.kata.v2",
Options: map[string]interface{}{"a": "b"},
Args: []string{"--help"},
},
expectErr: "args cannot be used with a runtimeType runtime",
},
{
name: "PathArgsTypeOptions",
runtime: types.Runtime{
Path: "/bin/true",
Args: []string{"foo"},
Type: "io.containerd.runsc.v1",
Options: map[string]interface{}{"a": "b"},
},
expectErr: "cannot configure both",
},
}
for _, tt := range cases {
t.Run(tt.name, func(t *testing.T) {
cfg, err := config.New()
assert.NilError(t, err)
d := &Daemon{configStore: cfg}
d.configStore.Root = t.TempDir()
assert.Assert(t, os.Mkdir(filepath.Join(d.configStore.Root, "runtimes"), 0700))
err = d.initRuntimes(map[string]types.Runtime{"myruntime": tt.runtime})
assert.Check(t, is.ErrorContains(err, tt.expectErr))
})
}
}
func TestGetRuntime(t *testing.T) { func TestGetRuntime(t *testing.T) {
// Configured runtimes can have any arbitrary name, including names // Configured runtimes can have any arbitrary name, including names
// which would not be allowed as implicit runtime names. Explicit takes // which would not be allowed as implicit runtime names. Explicit takes
// precedence over implicit. // precedence over implicit.
const configuredRtName = "my/custom.shim.v1" const configuredRtName = "my/custom.runtime.v1"
configuredRuntime := types.Runtime{Path: "/bin/true"} configuredRuntime := types.Runtime{Path: "/bin/true"}
const rtWithArgsName = "withargs"
rtWithArgs := types.Runtime{
Path: "/bin/false",
Args: []string{"--version"},
}
const shimWithOptsName = "shimwithopts"
shimWithOpts := types.Runtime{
Type: plugin.RuntimeRuncV2,
Options: map[string]interface{}{"IoUid": 42},
}
const shimAliasName = "wasmedge"
shimAlias := types.Runtime{Type: "io.containerd.wasmedge.v1"}
const configuredShimByPathName = "shimwithpath"
configuredShimByPath := types.Runtime{Type: "/path/to/my/shim"}
cfg, err := config.New() cfg, err := config.New()
assert.NilError(t, err) assert.NilError(t, err)
@ -31,7 +129,11 @@ func TestGetRuntime(t *testing.T) {
d.configStore.Root = t.TempDir() d.configStore.Root = t.TempDir()
assert.Assert(t, os.Mkdir(filepath.Join(d.configStore.Root, "runtimes"), 0700)) assert.Assert(t, os.Mkdir(filepath.Join(d.configStore.Root, "runtimes"), 0700))
d.configStore.Runtimes = map[string]types.Runtime{ d.configStore.Runtimes = map[string]types.Runtime{
configuredRtName: configuredRuntime, configuredRtName: configuredRuntime,
rtWithArgsName: rtWithArgs,
shimWithOptsName: shimWithOpts,
shimAliasName: shimAlias,
configuredShimByPathName: configuredShimByPath,
} }
configureRuntimes(d.configStore) configureRuntimes(d.configStore)
assert.Assert(t, d.loadRuntimes()) assert.Assert(t, d.loadRuntimes())
@ -39,36 +141,33 @@ func TestGetRuntime(t *testing.T) {
stockRuntime, ok := d.configStore.Runtimes[config.StockRuntimeName] stockRuntime, ok := d.configStore.Runtimes[config.StockRuntimeName]
assert.Assert(t, ok, "stock runtime could not be found (test needs to be updated)") assert.Assert(t, ok, "stock runtime could not be found (test needs to be updated)")
configdOpts := *stockRuntime.Shim.Opts.(*v2runcoptions.Options) configdOpts := *stockRuntime.ShimConfig.Opts.(*v2runcoptions.Options)
configdOpts.BinaryName = configuredRuntime.Path configdOpts.BinaryName = configuredRuntime.Path
wantConfigdRuntime := configuredRuntime
wantConfigdRuntime.Shim = &types.ShimConfig{
Binary: stockRuntime.Shim.Binary,
Opts: &configdOpts,
}
for _, tt := range []struct { for _, tt := range []struct {
name, runtime string name, runtime string
want *types.Runtime wantShim string
wantOpts interface{}
}{ }{
{ {
name: "StockRuntime", name: "StockRuntime",
runtime: config.StockRuntimeName, runtime: config.StockRuntimeName,
want: &stockRuntime, wantShim: stockRuntime.ShimConfig.Binary,
wantOpts: stockRuntime.ShimConfig.Opts,
}, },
{ {
name: "ShimName", name: "ShimName",
runtime: "io.containerd.my-shim.v42", runtime: "io.containerd.my-shim.v42",
want: &types.Runtime{Shim: &types.ShimConfig{Binary: "io.containerd.my-shim.v42"}}, wantShim: "io.containerd.my-shim.v42",
}, },
{ {
// containerd is pretty loose about the format of runtime names. Perhaps too // containerd is pretty loose about the format of runtime names. Perhaps too
// loose. The only requirements are that the name contain a dot and (depending // loose. The only requirements are that the name contain a dot and (depending
// on the containerd version) not start with a dot. It does not enforce any // on the containerd version) not start with a dot. It does not enforce any
// particular format of the dot-delimited components of the name. // particular format of the dot-delimited components of the name.
name: "VersionlessShimName", name: "VersionlessShimName",
runtime: "io.containerd.my-shim", runtime: "io.containerd.my-shim",
want: &types.Runtime{Shim: &types.ShimConfig{Binary: "io.containerd.my-shim"}}, wantShim: "io.containerd.my-shim",
}, },
{ {
name: "IllformedShimName", name: "IllformedShimName",
@ -91,16 +190,45 @@ func TestGetRuntime(t *testing.T) {
runtime: "my/io.containerd.runc.v2", runtime: "my/io.containerd.runc.v2",
}, },
{ {
name: "ConfiguredRuntime", name: "ConfiguredRuntime",
runtime: configuredRtName, runtime: configuredRtName,
want: &wantConfigdRuntime, wantShim: stockRuntime.ShimConfig.Binary,
wantOpts: &configdOpts,
},
{
name: "RuntimeWithArgs",
runtime: rtWithArgsName,
wantShim: stockRuntime.ShimConfig.Binary,
wantOpts: defaultV2ShimConfig(
d.configStore,
d.rewriteRuntimePath(
rtWithArgsName,
rtWithArgs.Path,
rtWithArgs.Args)).Opts,
},
{
name: "ShimWithOpts",
runtime: shimWithOptsName,
wantShim: shimWithOpts.Type,
wantOpts: &v2runcoptions.Options{IoUid: 42},
},
{
name: "ShimAlias",
runtime: shimAliasName,
wantShim: shimAlias.Type,
},
{
name: "ConfiguredShimByPath",
runtime: configuredShimByPathName,
wantShim: configuredShimByPath.Type,
}, },
} { } {
tt := tt tt := tt
t.Run(tt.name, func(t *testing.T) { t.Run(tt.name, func(t *testing.T) {
got, err := d.getRuntime(tt.runtime) gotShim, gotOpts, err := d.getRuntime(tt.runtime)
assert.Check(t, is.DeepEqual(got, tt.want)) assert.Check(t, is.Equal(gotShim, tt.wantShim))
if tt.want != nil { assert.Check(t, is.DeepEqual(gotOpts, tt.wantOpts))
if tt.wantShim != "" {
assert.Check(t, err) assert.Check(t, err)
} else { } else {
assert.Check(t, errdefs.IsInvalidParameter(err)) assert.Check(t, errdefs.IsInvalidParameter(err))

View file

@ -1,10 +1,9 @@
package daemon package daemon
import ( import (
"github.com/docker/docker/api/types" "errors"
"github.com/pkg/errors"
) )
func (daemon *Daemon) getRuntime(name string) (*types.Runtime, error) { func (daemon *Daemon) getRuntime(name string) (shim string, opts interface{}, err error) {
return nil, errors.New("not implemented") return "", nil, errors.New("not implemented")
} }

View file

@ -15,10 +15,10 @@ func (daemon *Daemon) getLibcontainerdCreateOptions(container *container.Contain
container.CheckpointTo(daemon.containersReplica) container.CheckpointTo(daemon.containersReplica)
} }
rt, err := daemon.getRuntime(container.HostConfig.Runtime) binary, opts, err := daemon.getRuntime(container.HostConfig.Runtime)
if err != nil { if err != nil {
return "", nil, setExitCodeFromError(container.SetExitCode, err) return "", nil, setExitCodeFromError(container.SetExitCode, err)
} }
return rt.Shim.Binary, rt.Shim.Opts, nil return binary, opts, nil
} }

View file

@ -0,0 +1,38 @@
package shimopts
import (
runhcsoptions "github.com/Microsoft/hcsshim/cmd/containerd-shim-runhcs-v1/options"
runtimeoptions "github.com/containerd/containerd/pkg/runtimeoptions/v1"
"github.com/containerd/containerd/plugin"
runcoptions "github.com/containerd/containerd/runtime/v2/runc/options"
"github.com/pelletier/go-toml"
)
// Generate converts opts into a runtime options value for the runtimeType which
// can be passed into containerd.
func Generate(runtimeType string, opts map[string]interface{}) (interface{}, error) {
// This is horrible, but we have no other choice. The containerd client
// can only handle options values which can be marshaled into a
// typeurl.Any. And we're in good company: cri-containerd handles shim
// options in the same way.
var out interface{}
switch runtimeType {
case plugin.RuntimeRuncV1, plugin.RuntimeRuncV2:
out = &runcoptions.Options{}
case "io.containerd.runhcs.v1":
out = &runhcsoptions.Options{}
default:
out = &runtimeoptions.Options{}
}
// We can't use mergo.Map as it is too strict about type-assignability
// with numeric types.
tree, err := toml.TreeFromMap(opts)
if err != nil {
return nil, err
}
if err := tree.Unmarshal(out); err != nil {
return nil, err
}
return out, nil
}

View file

@ -9,7 +9,6 @@ import (
"github.com/containerd/containerd" "github.com/containerd/containerd"
"github.com/containerd/containerd/cio" "github.com/containerd/containerd/cio"
"github.com/docker/docker/api/types"
"github.com/docker/docker/errdefs" "github.com/docker/docker/errdefs"
"github.com/docker/docker/libcontainerd" "github.com/docker/docker/libcontainerd"
libcontainerdtypes "github.com/docker/docker/libcontainerd/types" libcontainerdtypes "github.com/docker/docker/libcontainerd/types"
@ -24,11 +23,12 @@ type ExitHandler interface {
} }
// New creates a new containerd plugin executor // New creates a new containerd plugin executor
func New(ctx context.Context, rootDir string, cli *containerd.Client, ns string, exitHandler ExitHandler, runtime types.Runtime) (*Executor, error) { func New(ctx context.Context, rootDir string, cli *containerd.Client, ns string, exitHandler ExitHandler, shim string, shimOpts interface{}) (*Executor, error) {
e := &Executor{ e := &Executor{
rootDir: rootDir, rootDir: rootDir,
exitHandler: exitHandler, exitHandler: exitHandler,
runtime: runtime, shim: shim,
shimOpts: shimOpts,
plugins: make(map[string]*c8dPlugin), plugins: make(map[string]*c8dPlugin),
} }
@ -45,7 +45,8 @@ type Executor struct {
rootDir string rootDir string
client libcontainerdtypes.Client client libcontainerdtypes.Client
exitHandler ExitHandler exitHandler ExitHandler
runtime types.Runtime shim string
shimOpts interface{}
mu sync.Mutex // Guards plugins map mu sync.Mutex // Guards plugins map
plugins map[string]*c8dPlugin plugins map[string]*c8dPlugin
@ -75,7 +76,7 @@ func (p c8dPlugin) deleteTaskAndContainer(ctx context.Context) {
func (e *Executor) Create(id string, spec specs.Spec, stdout, stderr io.WriteCloser) error { func (e *Executor) Create(id string, spec specs.Spec, stdout, stderr io.WriteCloser) error {
ctx := context.Background() ctx := context.Background()
log := logrus.WithField("plugin", id) log := logrus.WithField("plugin", id)
ctr, err := libcontainerd.ReplaceContainer(ctx, e.client, id, &spec, e.runtime.Shim.Binary, e.runtime.Shim.Opts) ctr, err := libcontainerd.ReplaceContainer(ctx, e.client, id, &spec, e.shim, e.shimOpts)
if err != nil { if err != nil {
return errors.Wrap(err, "error creating containerd container for plugin") return errors.Wrap(err, "error creating containerd container for plugin")
} }

View file

@ -0,0 +1,397 @@
// Code generated by protoc-gen-gogo. DO NOT EDIT.
// source: github.com/containerd/containerd/pkg/runtimeoptions/v1/api.proto
package runtimeoptions_v1
import (
fmt "fmt"
_ "github.com/gogo/protobuf/gogoproto"
proto "github.com/gogo/protobuf/proto"
io "io"
math "math"
math_bits "math/bits"
reflect "reflect"
strings "strings"
)
// Reference imports to suppress errors if they are not otherwise used.
var _ = proto.Marshal
var _ = fmt.Errorf
var _ = math.Inf
// This is a compile-time assertion to ensure that this generated file
// is compatible with the proto package it is being compiled against.
// A compilation error at this line likely means your copy of the
// proto package needs to be updated.
const _ = proto.GoGoProtoPackageIsVersion3 // please upgrade the proto package
type Options struct {
// TypeUrl specifies the type of the content inside the config file.
TypeUrl string `protobuf:"bytes,1,opt,name=type_url,json=typeUrl,proto3" json:"type_url,omitempty"`
// ConfigPath specifies the filesystem location of the config file
// used by the runtime.
ConfigPath string `protobuf:"bytes,2,opt,name=config_path,json=configPath,proto3" json:"config_path,omitempty"`
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_sizecache int32 `json:"-"`
}
func (m *Options) Reset() { *m = Options{} }
func (*Options) ProtoMessage() {}
func (*Options) Descriptor() ([]byte, []int) {
return fileDescriptor_7700dd27e3487aa6, []int{0}
}
func (m *Options) XXX_Unmarshal(b []byte) error {
return m.Unmarshal(b)
}
func (m *Options) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
if deterministic {
return xxx_messageInfo_Options.Marshal(b, m, deterministic)
} else {
b = b[:cap(b)]
n, err := m.MarshalToSizedBuffer(b)
if err != nil {
return nil, err
}
return b[:n], nil
}
}
func (m *Options) XXX_Merge(src proto.Message) {
xxx_messageInfo_Options.Merge(m, src)
}
func (m *Options) XXX_Size() int {
return m.Size()
}
func (m *Options) XXX_DiscardUnknown() {
xxx_messageInfo_Options.DiscardUnknown(m)
}
var xxx_messageInfo_Options proto.InternalMessageInfo
func (m *Options) GetTypeUrl() string {
if m != nil {
return m.TypeUrl
}
return ""
}
func (m *Options) GetConfigPath() string {
if m != nil {
return m.ConfigPath
}
return ""
}
func init() {
proto.RegisterType((*Options)(nil), "runtimeoptions.v1.Options")
}
func init() {
proto.RegisterFile("github.com/containerd/containerd/pkg/runtimeoptions/v1/api.proto", fileDescriptor_7700dd27e3487aa6)
}
var fileDescriptor_7700dd27e3487aa6 = []byte{
// 214 bytes of a gzipped FileDescriptorProto
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xe2, 0x72, 0x48, 0xcf, 0x2c, 0xc9,
0x28, 0x4d, 0xd2, 0x4b, 0xce, 0xcf, 0xd5, 0x4f, 0xce, 0xcf, 0x2b, 0x49, 0xcc, 0xcc, 0x4b, 0x2d,
0x4a, 0x41, 0x66, 0x16, 0x64, 0xa7, 0xeb, 0x17, 0x95, 0xe6, 0x95, 0x64, 0xe6, 0xa6, 0xe6, 0x17,
0x94, 0x64, 0xe6, 0xe7, 0x15, 0xeb, 0x97, 0x19, 0xea, 0x27, 0x16, 0x64, 0xea, 0x15, 0x14, 0xe5,
0x97, 0xe4, 0x0b, 0x09, 0xa2, 0x4a, 0xea, 0x95, 0x19, 0x4a, 0xe9, 0x22, 0x19, 0x9a, 0x9e, 0x9f,
0x9e, 0xaf, 0x0f, 0x56, 0x99, 0x54, 0x9a, 0x06, 0xe6, 0x81, 0x39, 0x60, 0x16, 0xc4, 0x04, 0x25,
0x57, 0x2e, 0x76, 0x7f, 0x88, 0x66, 0x21, 0x49, 0x2e, 0x8e, 0x92, 0xca, 0x82, 0xd4, 0xf8, 0xd2,
0xa2, 0x1c, 0x09, 0x46, 0x05, 0x46, 0x0d, 0xce, 0x20, 0x76, 0x10, 0x3f, 0xb4, 0x28, 0x47, 0x48,
0x9e, 0x8b, 0x3b, 0x39, 0x3f, 0x2f, 0x2d, 0x33, 0x3d, 0xbe, 0x20, 0xb1, 0x24, 0x43, 0x82, 0x09,
0x2c, 0xcb, 0x05, 0x11, 0x0a, 0x48, 0x2c, 0xc9, 0x70, 0x4a, 0x3b, 0xf1, 0x50, 0x8e, 0xf1, 0xc6,
0x43, 0x39, 0x86, 0x86, 0x47, 0x72, 0x8c, 0x27, 0x1e, 0xc9, 0x31, 0x5e, 0x78, 0x24, 0xc7, 0xf8,
0xe0, 0x91, 0x1c, 0xe3, 0x84, 0xc7, 0x72, 0x0c, 0x51, 0x1e, 0xe4, 0x79, 0xd4, 0x1a, 0x55, 0x24,
0xbe, 0xcc, 0x30, 0x89, 0x0d, 0xec, 0x6a, 0x63, 0x40, 0x00, 0x00, 0x00, 0xff, 0xff, 0x91, 0x3c,
0x3e, 0x79, 0x3b, 0x01, 0x00, 0x00,
}
func (m *Options) Marshal() (dAtA []byte, err error) {
size := m.Size()
dAtA = make([]byte, size)
n, err := m.MarshalToSizedBuffer(dAtA[:size])
if err != nil {
return nil, err
}
return dAtA[:n], nil
}
func (m *Options) MarshalTo(dAtA []byte) (int, error) {
size := m.Size()
return m.MarshalToSizedBuffer(dAtA[:size])
}
func (m *Options) MarshalToSizedBuffer(dAtA []byte) (int, error) {
i := len(dAtA)
_ = i
var l int
_ = l
if len(m.ConfigPath) > 0 {
i -= len(m.ConfigPath)
copy(dAtA[i:], m.ConfigPath)
i = encodeVarintApi(dAtA, i, uint64(len(m.ConfigPath)))
i--
dAtA[i] = 0x12
}
if len(m.TypeUrl) > 0 {
i -= len(m.TypeUrl)
copy(dAtA[i:], m.TypeUrl)
i = encodeVarintApi(dAtA, i, uint64(len(m.TypeUrl)))
i--
dAtA[i] = 0xa
}
return len(dAtA) - i, nil
}
func encodeVarintApi(dAtA []byte, offset int, v uint64) int {
offset -= sovApi(v)
base := offset
for v >= 1<<7 {
dAtA[offset] = uint8(v&0x7f | 0x80)
v >>= 7
offset++
}
dAtA[offset] = uint8(v)
return base
}
func (m *Options) Size() (n int) {
if m == nil {
return 0
}
var l int
_ = l
l = len(m.TypeUrl)
if l > 0 {
n += 1 + l + sovApi(uint64(l))
}
l = len(m.ConfigPath)
if l > 0 {
n += 1 + l + sovApi(uint64(l))
}
return n
}
func sovApi(x uint64) (n int) {
return (math_bits.Len64(x|1) + 6) / 7
}
func sozApi(x uint64) (n int) {
return sovApi(uint64((x << 1) ^ uint64((int64(x) >> 63))))
}
func (this *Options) String() string {
if this == nil {
return "nil"
}
s := strings.Join([]string{`&Options{`,
`TypeUrl:` + fmt.Sprintf("%v", this.TypeUrl) + `,`,
`ConfigPath:` + fmt.Sprintf("%v", this.ConfigPath) + `,`,
`}`,
}, "")
return s
}
func valueToStringApi(v interface{}) string {
rv := reflect.ValueOf(v)
if rv.IsNil() {
return "nil"
}
pv := reflect.Indirect(rv).Interface()
return fmt.Sprintf("*%v", pv)
}
func (m *Options) Unmarshal(dAtA []byte) error {
l := len(dAtA)
iNdEx := 0
for iNdEx < l {
preIndex := iNdEx
var wire uint64
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowApi
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
wire |= uint64(b&0x7F) << shift
if b < 0x80 {
break
}
}
fieldNum := int32(wire >> 3)
wireType := int(wire & 0x7)
if wireType == 4 {
return fmt.Errorf("proto: Options: wiretype end group for non-group")
}
if fieldNum <= 0 {
return fmt.Errorf("proto: Options: illegal tag %d (wire type %d)", fieldNum, wire)
}
switch fieldNum {
case 1:
if wireType != 2 {
return fmt.Errorf("proto: wrong wireType = %d for field TypeUrl", wireType)
}
var stringLen uint64
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowApi
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
stringLen |= uint64(b&0x7F) << shift
if b < 0x80 {
break
}
}
intStringLen := int(stringLen)
if intStringLen < 0 {
return ErrInvalidLengthApi
}
postIndex := iNdEx + intStringLen
if postIndex < 0 {
return ErrInvalidLengthApi
}
if postIndex > l {
return io.ErrUnexpectedEOF
}
m.TypeUrl = string(dAtA[iNdEx:postIndex])
iNdEx = postIndex
case 2:
if wireType != 2 {
return fmt.Errorf("proto: wrong wireType = %d for field ConfigPath", wireType)
}
var stringLen uint64
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowApi
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
stringLen |= uint64(b&0x7F) << shift
if b < 0x80 {
break
}
}
intStringLen := int(stringLen)
if intStringLen < 0 {
return ErrInvalidLengthApi
}
postIndex := iNdEx + intStringLen
if postIndex < 0 {
return ErrInvalidLengthApi
}
if postIndex > l {
return io.ErrUnexpectedEOF
}
m.ConfigPath = string(dAtA[iNdEx:postIndex])
iNdEx = postIndex
default:
iNdEx = preIndex
skippy, err := skipApi(dAtA[iNdEx:])
if err != nil {
return err
}
if (skippy < 0) || (iNdEx+skippy) < 0 {
return ErrInvalidLengthApi
}
if (iNdEx + skippy) > l {
return io.ErrUnexpectedEOF
}
iNdEx += skippy
}
}
if iNdEx > l {
return io.ErrUnexpectedEOF
}
return nil
}
func skipApi(dAtA []byte) (n int, err error) {
l := len(dAtA)
iNdEx := 0
depth := 0
for iNdEx < l {
var wire uint64
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return 0, ErrIntOverflowApi
}
if iNdEx >= l {
return 0, io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
wire |= (uint64(b) & 0x7F) << shift
if b < 0x80 {
break
}
}
wireType := int(wire & 0x7)
switch wireType {
case 0:
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return 0, ErrIntOverflowApi
}
if iNdEx >= l {
return 0, io.ErrUnexpectedEOF
}
iNdEx++
if dAtA[iNdEx-1] < 0x80 {
break
}
}
case 1:
iNdEx += 8
case 2:
var length int
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return 0, ErrIntOverflowApi
}
if iNdEx >= l {
return 0, io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
length |= (int(b) & 0x7F) << shift
if b < 0x80 {
break
}
}
if length < 0 {
return 0, ErrInvalidLengthApi
}
iNdEx += length
case 3:
depth++
case 4:
if depth == 0 {
return 0, ErrUnexpectedEndOfGroupApi
}
depth--
case 5:
iNdEx += 4
default:
return 0, fmt.Errorf("proto: illegal wireType %d", wireType)
}
if iNdEx < 0 {
return 0, ErrInvalidLengthApi
}
if depth == 0 {
return iNdEx, nil
}
}
return 0, io.ErrUnexpectedEOF
}
var (
ErrInvalidLengthApi = fmt.Errorf("proto: negative length found during unmarshaling")
ErrIntOverflowApi = fmt.Errorf("proto: integer overflow")
ErrUnexpectedEndOfGroupApi = fmt.Errorf("proto: unexpected end of group")
)

View file

@ -0,0 +1,25 @@
// To regenerate api.pb.go run `make protos`
syntax = "proto3";
package runtimeoptions.v1;
import "github.com/gogo/protobuf/gogoproto/gogo.proto";
option (gogoproto.goproto_stringer_all) = false;
option (gogoproto.stringer_all) = true;
option (gogoproto.goproto_getters_all) = true;
option (gogoproto.marshaler_all) = true;
option (gogoproto.sizer_all) = true;
option (gogoproto.unmarshaler_all) = true;
option (gogoproto.goproto_unrecognized_all) = false;
option go_package = "github.com/containerd/containerd/pkg/runtimeoptions/v1;runtimeoptions_v1";
message Options {
// TypeUrl specifies the type of the content inside the config file.
string type_url = 1;
// ConfigPath specifies the filesystem location of the config file
// used by the runtime.
string config_path = 2;
}

View file

@ -0,0 +1,17 @@
/*
Copyright The containerd Authors.
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 runtimeoptions_v1 //nolint

1
vendor/modules.txt vendored
View file

@ -259,6 +259,7 @@ github.com/containerd/containerd/pkg/apparmor
github.com/containerd/containerd/pkg/cap github.com/containerd/containerd/pkg/cap
github.com/containerd/containerd/pkg/dialer github.com/containerd/containerd/pkg/dialer
github.com/containerd/containerd/pkg/kmutex github.com/containerd/containerd/pkg/kmutex
github.com/containerd/containerd/pkg/runtimeoptions/v1
github.com/containerd/containerd/pkg/seccomp github.com/containerd/containerd/pkg/seccomp
github.com/containerd/containerd/pkg/shutdown github.com/containerd/containerd/pkg/shutdown
github.com/containerd/containerd/pkg/ttrpcutil github.com/containerd/containerd/pkg/ttrpcutil