Merge pull request #1454 from mlaventure/per-container-runtime-binary

Per container runtime binary
This commit is contained in:
Phil Estes 2017-09-01 13:25:17 -04:00 committed by GitHub
commit 4291fb4803
13 changed files with 461 additions and 249 deletions

View File

@ -66,6 +66,19 @@ func main() {
Name: "workdir,w",
Usage: "path used to store large temporary data",
},
cli.StringFlag{
Name: "runtime-root",
Usage: "root directory for the runtime",
Value: shim.RuncRoot,
},
cli.StringFlag{
Name: "criu,c",
Usage: "path to criu",
},
cli.BoolFlag{
Name: "systemd-cgroup",
Usage: "set runtime to use systemd-cgroup",
},
}
app.Before = func(context *cli.Context) error {
if context.GlobalBool("debug") {
@ -90,9 +103,14 @@ func main() {
return err
}
sv, err := shim.NewService(
path,
context.GlobalString("namespace"),
context.GlobalString("workdir"),
shim.Config{
Path: path,
Namespace: context.GlobalString("namespace"),
WorkDir: context.GlobalString("workdir"),
Criu: context.GlobalString("criu"),
SystemdCgroup: context.GlobalBool("systemd-cgroup"),
RuntimeRoot: context.GlobalString("runtime-root"),
},
&remoteEventsPublisher{client: e},
)
if err != nil {

View File

@ -851,3 +851,48 @@ func TestDaemonRestartWithRunningShim(t *testing.T) {
t.Errorf("pid %d still exists", pid)
}
}
func TestContainerRuntimeOptions(t *testing.T) {
t.Parallel()
client, err := newClient(t, address)
if err != nil {
t.Fatal(err)
}
defer client.Close()
var (
image Image
ctx, cancel = testContext()
id = t.Name()
)
defer cancel()
image, err = client.GetImage(ctx, testImage)
if err != nil {
t.Error(err)
return
}
container, err := client.NewContainer(
ctx, id,
WithNewSpec(withImageConfig(image), withExitStatus(7)),
withNewSnapshot(id, image),
WithRuntime("io.containerd.runtime.v1.linux", &runcopts.RuncOptions{Runtime: "no-runc"}),
)
if err != nil {
t.Error(err)
return
}
defer container.Delete(ctx, WithSnapshotCleanup)
task, err := container.NewTask(ctx, empty())
if err == nil {
t.Errorf("task creation should have failed")
task.Delete(ctx)
return
}
if !strings.Contains(err.Error(), `"no-runc"`) {
t.Errorf("task creation should have failed because of lack of executable. Instead failed with: %v", err.Error())
}
}

View File

@ -12,23 +12,19 @@ import (
"github.com/containerd/containerd/events"
"github.com/containerd/containerd/linux/runcopts"
client "github.com/containerd/containerd/linux/shim"
"github.com/containerd/containerd/runtime"
"github.com/containerd/containerd/typeurl"
"github.com/pkg/errors"
)
func loadBundle(path, workdir, namespace, id string, events *events.Exchange) *bundle {
func loadBundle(id, path, workdir string) *bundle {
return &bundle{
path: path,
namespace: namespace,
id: id,
events: events,
workDir: workdir,
id: id,
path: path,
workDir: workdir,
}
}
// newBundle creates a new bundle on disk at the provided path for the given id
func newBundle(namespace, id, path, workDir string, spec []byte, events *events.Exchange) (b *bundle, err error) {
func newBundle(id, path, workDir string, spec []byte) (b *bundle, err error) {
if err := os.MkdirAll(path, 0711); err != nil {
return nil, err
}
@ -61,56 +57,43 @@ func newBundle(namespace, id, path, workDir string, spec []byte, events *events.
defer f.Close()
_, err = io.Copy(f, bytes.NewReader(spec))
return &bundle{
id: id,
path: path,
workDir: workDir,
namespace: namespace,
events: events,
id: id,
path: path,
workDir: workDir,
}, err
}
type bundle struct {
id string
path string
workDir string
namespace string
events *events.Exchange
id string
path string
workDir string
}
// NewShim connects to the shim managing the bundle and tasks
func (b *bundle) NewShim(ctx context.Context, binary, grpcAddress string, remote, debug bool, createOpts runtime.CreateOpts, exitHandler func()) (*client.Client, error) {
opt := client.WithStart(binary, grpcAddress, debug, exitHandler)
if !remote {
opt = client.WithLocal(b.events)
type shimOpt func(*bundle, string, *runcopts.RuncOptions) (client.Config, client.ClientOpt)
func ShimRemote(shim, daemonAddress, cgroup string, debug bool, exitHandler func()) shimOpt {
return func(b *bundle, ns string, ropts *runcopts.RuncOptions) (client.Config, client.ClientOpt) {
return b.shimConfig(ns, ropts),
client.WithStart(shim, b.shimAddress(ns), daemonAddress, cgroup, debug, exitHandler)
}
var options runcopts.CreateOptions
if createOpts.Options != nil {
v, err := typeurl.UnmarshalAny(createOpts.Options)
if err != nil {
return nil, err
}
options = *v.(*runcopts.CreateOptions)
}
return client.New(ctx, client.Config{
Address: b.shimAddress(),
Path: b.path,
Namespace: b.namespace,
CgroupPath: options.ShimCgroup,
WorkDir: b.workDir,
}, opt)
}
// Connect reconnects to an existing shim
func (b *bundle) Connect(ctx context.Context, remote bool) (*client.Client, error) {
opt := client.WithConnect
if !remote {
opt = client.WithLocal(b.events)
func ShimLocal(exchange *events.Exchange) shimOpt {
return func(b *bundle, ns string, ropts *runcopts.RuncOptions) (client.Config, client.ClientOpt) {
return b.shimConfig(ns, ropts), client.WithLocal(exchange)
}
return client.New(ctx, client.Config{
Address: b.shimAddress(),
Path: b.path,
Namespace: b.namespace,
}, opt)
}
func ShimConnect() shimOpt {
return func(b *bundle, ns string, ropts *runcopts.RuncOptions) (client.Config, client.ClientOpt) {
return b.shimConfig(ns, ropts), client.WithConnect(b.shimAddress(ns))
}
}
// NewShimClient connects to the shim managing the bundle and tasks creating it if needed
func (b *bundle) NewShimClient(ctx context.Context, namespace string, getClientOpts shimOpt, runcOpts *runcopts.RuncOptions) (*client.Client, error) {
cfg, opt := getClientOpts(b, namespace, runcOpts)
return client.New(ctx, cfg, opt)
}
// Delete deletes the bundle from disk
@ -127,7 +110,28 @@ func (b *bundle) Delete() error {
return errors.Wrapf(err, "Failed to remove both bundle and workdir locations: %v", err2)
}
func (b *bundle) shimAddress() string {
return filepath.Join(string(filepath.Separator), "containerd-shim", b.namespace, b.id, "shim.sock")
func (b *bundle) shimAddress(namespace string) string {
return filepath.Join(string(filepath.Separator), "containerd-shim", namespace, b.id, "shim.sock")
}
func (b *bundle) shimConfig(namespace string, runcOptions *runcopts.RuncOptions) client.Config {
var (
criuPath string
runtimeRoot string
systemdCgroup bool
)
if runcOptions != nil {
criuPath = runcOptions.CriuPath
systemdCgroup = runcOptions.SystemdCgroup
runtimeRoot = runcOptions.RuntimeRoot
}
return client.Config{
Path: b.path,
WorkDir: b.workDir,
Namespace: namespace,
Criu: criuPath,
RuntimeRoot: runtimeRoot,
SystemdCgroup: systemdCgroup,
}
}

View File

@ -37,8 +37,10 @@ var _ = math.Inf
const _ = proto.GoGoProtoPackageIsVersion2 // please upgrade the proto package
type RuncOptions struct {
CriuPath string `protobuf:"bytes,1,opt,name=criu_path,json=criuPath,proto3" json:"criu_path,omitempty"`
SystemdCgroup string `protobuf:"bytes,2,opt,name=systemd_cgroup,json=systemdCgroup,proto3" json:"systemd_cgroup,omitempty"`
Runtime string `protobuf:"bytes,1,opt,name=runtime,proto3" json:"runtime,omitempty"`
RuntimeRoot string `protobuf:"bytes,2,opt,name=runtime_root,json=runtimeRoot,proto3" json:"runtime_root,omitempty"`
CriuPath string `protobuf:"bytes,3,opt,name=criu_path,json=criuPath,proto3" json:"criu_path,omitempty"`
SystemdCgroup bool `protobuf:"varint,4,opt,name=systemd_cgroup,json=systemdCgroup,proto3" json:"systemd_cgroup,omitempty"`
}
func (m *RuncOptions) Reset() { *m = RuncOptions{} }
@ -95,17 +97,33 @@ func (m *RuncOptions) MarshalTo(dAtA []byte) (int, error) {
_ = i
var l int
_ = l
if len(m.CriuPath) > 0 {
if len(m.Runtime) > 0 {
dAtA[i] = 0xa
i++
i = encodeVarintRunc(dAtA, i, uint64(len(m.Runtime)))
i += copy(dAtA[i:], m.Runtime)
}
if len(m.RuntimeRoot) > 0 {
dAtA[i] = 0x12
i++
i = encodeVarintRunc(dAtA, i, uint64(len(m.RuntimeRoot)))
i += copy(dAtA[i:], m.RuntimeRoot)
}
if len(m.CriuPath) > 0 {
dAtA[i] = 0x1a
i++
i = encodeVarintRunc(dAtA, i, uint64(len(m.CriuPath)))
i += copy(dAtA[i:], m.CriuPath)
}
if len(m.SystemdCgroup) > 0 {
dAtA[i] = 0x12
if m.SystemdCgroup {
dAtA[i] = 0x20
i++
if m.SystemdCgroup {
dAtA[i] = 1
} else {
dAtA[i] = 0
}
i++
i = encodeVarintRunc(dAtA, i, uint64(len(m.SystemdCgroup)))
i += copy(dAtA[i:], m.SystemdCgroup)
}
return i, nil
}
@ -334,13 +352,20 @@ func encodeVarintRunc(dAtA []byte, offset int, v uint64) int {
func (m *RuncOptions) Size() (n int) {
var l int
_ = l
l = len(m.Runtime)
if l > 0 {
n += 1 + l + sovRunc(uint64(l))
}
l = len(m.RuntimeRoot)
if l > 0 {
n += 1 + l + sovRunc(uint64(l))
}
l = len(m.CriuPath)
if l > 0 {
n += 1 + l + sovRunc(uint64(l))
}
l = len(m.SystemdCgroup)
if l > 0 {
n += 1 + l + sovRunc(uint64(l))
if m.SystemdCgroup {
n += 2
}
return n
}
@ -432,6 +457,8 @@ func (this *RuncOptions) String() string {
return "nil"
}
s := strings.Join([]string{`&RuncOptions{`,
`Runtime:` + fmt.Sprintf("%v", this.Runtime) + `,`,
`RuntimeRoot:` + fmt.Sprintf("%v", this.RuntimeRoot) + `,`,
`CriuPath:` + fmt.Sprintf("%v", this.CriuPath) + `,`,
`SystemdCgroup:` + fmt.Sprintf("%v", this.SystemdCgroup) + `,`,
`}`,
@ -510,6 +537,64 @@ func (m *RuncOptions) Unmarshal(dAtA []byte) error {
}
switch fieldNum {
case 1:
if wireType != 2 {
return fmt.Errorf("proto: wrong wireType = %d for field Runtime", wireType)
}
var stringLen uint64
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowRunc
}
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 ErrInvalidLengthRunc
}
postIndex := iNdEx + intStringLen
if postIndex > l {
return io.ErrUnexpectedEOF
}
m.Runtime = string(dAtA[iNdEx:postIndex])
iNdEx = postIndex
case 2:
if wireType != 2 {
return fmt.Errorf("proto: wrong wireType = %d for field RuntimeRoot", wireType)
}
var stringLen uint64
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowRunc
}
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 ErrInvalidLengthRunc
}
postIndex := iNdEx + intStringLen
if postIndex > l {
return io.ErrUnexpectedEOF
}
m.RuntimeRoot = string(dAtA[iNdEx:postIndex])
iNdEx = postIndex
case 3:
if wireType != 2 {
return fmt.Errorf("proto: wrong wireType = %d for field CriuPath", wireType)
}
@ -538,11 +623,11 @@ func (m *RuncOptions) Unmarshal(dAtA []byte) error {
}
m.CriuPath = string(dAtA[iNdEx:postIndex])
iNdEx = postIndex
case 2:
if wireType != 2 {
case 4:
if wireType != 0 {
return fmt.Errorf("proto: wrong wireType = %d for field SystemdCgroup", wireType)
}
var stringLen uint64
var v int
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowRunc
@ -552,21 +637,12 @@ func (m *RuncOptions) Unmarshal(dAtA []byte) error {
}
b := dAtA[iNdEx]
iNdEx++
stringLen |= (uint64(b) & 0x7F) << shift
v |= (int(b) & 0x7F) << shift
if b < 0x80 {
break
}
}
intStringLen := int(stringLen)
if intStringLen < 0 {
return ErrInvalidLengthRunc
}
postIndex := iNdEx + intStringLen
if postIndex > l {
return io.ErrUnexpectedEOF
}
m.SystemdCgroup = string(dAtA[iNdEx:postIndex])
iNdEx = postIndex
m.SystemdCgroup = bool(v != 0)
default:
iNdEx = preIndex
skippy, err := skipRunc(dAtA[iNdEx:])
@ -1163,33 +1239,35 @@ func init() {
}
var fileDescriptorRunc = []byte{
// 438 bytes of a gzipped FileDescriptorProto
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xdc, 0x93, 0xb1, 0x6f, 0xd4, 0x30,
0x18, 0xc5, 0x9b, 0xb6, 0xb4, 0x89, 0xaf, 0x57, 0xc0, 0x50, 0x29, 0x14, 0x11, 0xca, 0x09, 0xa4,
0xb2, 0xdc, 0x49, 0xb0, 0x20, 0xd8, 0xb8, 0x11, 0x28, 0x25, 0xc0, 0xc2, 0x62, 0xa5, 0xbe, 0x8f,
0xc4, 0xba, 0xe4, 0xfb, 0x2c, 0xdb, 0xa1, 0xb9, 0x8d, 0x3f, 0xaf, 0x23, 0x62, 0x62, 0xa4, 0xf9,
0x47, 0x40, 0x71, 0x2e, 0x85, 0x95, 0x95, 0xed, 0xf9, 0xf7, 0x9e, 0x9e, 0xa5, 0x27, 0x7d, 0xec,
0x79, 0xae, 0x5c, 0x51, 0x9f, 0x4d, 0x25, 0x55, 0x33, 0x49, 0xe8, 0x32, 0x85, 0x60, 0x16, 0x7f,
0xcb, 0x52, 0x61, 0xdd, 0xcc, 0x4c, 0x8d, 0x92, 0xb4, 0xb3, 0x5e, 0x4c, 0xb5, 0x21, 0x47, 0xfc,
0xe0, 0x4f, 0x6a, 0xea, 0x53, 0xd3, 0xce, 0x3c, 0xbc, 0x9d, 0x53, 0x4e, 0x3e, 0x31, 0xeb, 0x54,
0x1f, 0x9e, 0xbc, 0x63, 0xa3, 0xb4, 0x46, 0xf9, 0x56, 0x3b, 0x45, 0x68, 0xf9, 0x5d, 0x16, 0x49,
0xa3, 0x6a, 0xa1, 0x33, 0x57, 0xc4, 0xc1, 0x51, 0x70, 0x1c, 0xa5, 0x61, 0x07, 0x4e, 0x33, 0x57,
0xf0, 0x47, 0x6c, 0xdf, 0xae, 0xac, 0x83, 0x6a, 0x21, 0x64, 0x6e, 0xa8, 0xd6, 0xf1, 0xa6, 0x4f,
0x8c, 0xd7, 0x74, 0xee, 0xe1, 0xe4, 0xfb, 0x26, 0x1b, 0xcf, 0x0d, 0x64, 0x0e, 0x86, 0xd6, 0x09,
0x1b, 0x23, 0x09, 0xad, 0xbe, 0x90, 0x13, 0x86, 0xc8, 0xf9, 0xe6, 0x30, 0x1d, 0x21, 0x9d, 0x76,
0x2c, 0x25, 0x72, 0xfc, 0x0e, 0x0b, 0x49, 0x03, 0x0a, 0x27, 0xfb, 0xda, 0x30, 0xdd, 0xed, 0xde,
0x1f, 0xa4, 0xe6, 0x4f, 0xd8, 0x01, 0x34, 0x0e, 0x0c, 0x66, 0xa5, 0xa8, 0x51, 0x35, 0xc2, 0x92,
0x5c, 0x82, 0xb3, 0xf1, 0x96, 0xcf, 0xdd, 0x1a, 0xcc, 0x8f, 0xa8, 0x9a, 0xf7, 0xbd, 0xc5, 0x0f,
0x59, 0xe8, 0xc0, 0x54, 0x0a, 0xb3, 0x32, 0xde, 0xf6, 0xb1, 0xab, 0x37, 0xbf, 0xc7, 0xd8, 0x67,
0x55, 0x82, 0x28, 0x49, 0x2e, 0x6d, 0x7c, 0xcd, 0xbb, 0x51, 0x47, 0x5e, 0x77, 0x80, 0x3f, 0x66,
0x37, 0xa0, 0xd2, 0x6e, 0x25, 0x30, 0xab, 0xc0, 0xea, 0x4c, 0x82, 0x8d, 0x77, 0x8e, 0xb6, 0x8e,
0xa3, 0xf4, 0xba, 0xe7, 0x27, 0x57, 0x98, 0x3f, 0x60, 0x7b, 0xfd, 0x12, 0x56, 0x54, 0xb4, 0x80,
0x78, 0xd7, 0xef, 0x31, 0x5a, 0xb3, 0x37, 0xb4, 0x00, 0xfe, 0x90, 0xed, 0x23, 0x09, 0x84, 0x73,
0xb1, 0x84, 0x95, 0x51, 0x98, 0xc7, 0xa1, 0xff, 0x70, 0x0f, 0xe9, 0x04, 0xce, 0x5f, 0xf5, 0x8c,
0xdf, 0x67, 0x23, 0x5b, 0xa8, 0x6a, 0xd8, 0x35, 0xf2, 0x3d, 0xac, 0x43, 0xeb, 0x51, 0x7f, 0x05,
0xec, 0xe6, 0xbc, 0x00, 0xb9, 0xd4, 0xa4, 0xd0, 0x0d, 0xc3, 0x72, 0xb6, 0x0d, 0x8d, 0x1a, 0xf6,
0xf4, 0xfa, 0x7f, 0x1d, 0xf2, 0x65, 0x7a, 0x71, 0x99, 0x6c, 0xfc, 0xb8, 0x4c, 0x36, 0xbe, 0xb6,
0x49, 0x70, 0xd1, 0x26, 0xc1, 0xb7, 0x36, 0x09, 0x7e, 0xb6, 0x49, 0xf0, 0xe9, 0xd9, 0x3f, 0x1e,
0xcb, 0x8b, 0x41, 0x9c, 0xed, 0xf8, 0x23, 0x78, 0xfa, 0x3b, 0x00, 0x00, 0xff, 0xff, 0x00, 0x19,
0xba, 0x8f, 0x6f, 0x03, 0x00, 0x00,
// 467 bytes of a gzipped FileDescriptorProto
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xdc, 0x93, 0x41, 0x6f, 0xd3, 0x40,
0x10, 0x85, 0xbb, 0x6d, 0x69, 0x9d, 0x4d, 0x53, 0x60, 0xa1, 0x92, 0x29, 0xc2, 0x84, 0x08, 0xa4,
0x70, 0x49, 0x24, 0xb8, 0x20, 0xb8, 0x91, 0x23, 0x50, 0x2a, 0x03, 0x17, 0x2e, 0x2b, 0x77, 0x33,
0x24, 0xab, 0xd8, 0x33, 0xab, 0xdd, 0x35, 0x75, 0x6e, 0xfc, 0x02, 0x7e, 0x57, 0x8f, 0x88, 0x13,
0x47, 0x9a, 0x3f, 0x02, 0xf2, 0xda, 0x2e, 0x5c, 0xb9, 0x72, 0x7b, 0xf3, 0xbd, 0xb1, 0xe7, 0xe9,
0x49, 0xcb, 0x9f, 0x2f, 0xb4, 0x5f, 0x96, 0x67, 0x13, 0x45, 0xc5, 0x54, 0x11, 0xfa, 0x4c, 0x23,
0xd8, 0xf9, 0xdf, 0x32, 0xd7, 0x58, 0x56, 0x53, 0x5b, 0xa2, 0x22, 0xe3, 0x5d, 0x10, 0x13, 0x63,
0xc9, 0x93, 0x38, 0xfa, 0xb3, 0x35, 0x09, 0x5b, 0x93, 0xda, 0x3c, 0xbe, 0xbd, 0xa0, 0x05, 0x85,
0x8d, 0x69, 0xad, 0x9a, 0xe5, 0xd1, 0x57, 0xc6, 0xfb, 0x69, 0x89, 0xea, 0xad, 0xf1, 0x9a, 0xd0,
0x89, 0x98, 0xef, 0xdb, 0x12, 0xbd, 0x2e, 0x20, 0x66, 0x43, 0x36, 0xee, 0xa5, 0xdd, 0x28, 0x1e,
0xf0, 0x83, 0x56, 0x4a, 0x4b, 0xe4, 0xe3, 0xed, 0x60, 0xf7, 0x5b, 0x96, 0x12, 0x79, 0x71, 0x97,
0xf7, 0x94, 0xd5, 0xa5, 0x34, 0x99, 0x5f, 0xc6, 0x3b, 0xc1, 0x8f, 0x6a, 0x70, 0x9a, 0xf9, 0xa5,
0x78, 0xc4, 0x0f, 0xdd, 0xda, 0x79, 0x28, 0xe6, 0x52, 0x2d, 0x2c, 0x95, 0x26, 0xde, 0x1d, 0xb2,
0x71, 0x94, 0x0e, 0x5a, 0x3a, 0x0b, 0x70, 0xf4, 0x7d, 0x9b, 0x0f, 0x66, 0x16, 0x32, 0x0f, 0x5d,
0xa4, 0x11, 0x1f, 0x20, 0x49, 0xa3, 0x3f, 0x93, 0x6f, 0x2e, 0xb3, 0xf0, 0x5d, 0x1f, 0xe9, 0xb4,
0x66, 0xe1, 0xf2, 0x1d, 0x1e, 0x91, 0x01, 0x94, 0x5e, 0x99, 0x10, 0x2c, 0x4a, 0xf7, 0xeb, 0xf9,
0xbd, 0x32, 0xe2, 0x09, 0x3f, 0x82, 0xca, 0x83, 0xc5, 0x2c, 0x97, 0x25, 0xea, 0x4a, 0x3a, 0x52,
0x2b, 0xf0, 0x2e, 0x04, 0x8c, 0xd2, 0x5b, 0x9d, 0xf9, 0x01, 0x75, 0xf5, 0xae, 0xb1, 0xc4, 0x31,
0x8f, 0x3c, 0xd8, 0x42, 0x63, 0x96, 0xb7, 0x29, 0xaf, 0x66, 0x71, 0x8f, 0xf3, 0x4f, 0x3a, 0x07,
0x99, 0x93, 0x5a, 0xb9, 0xf8, 0x5a, 0x70, 0x7b, 0x35, 0x79, 0x5d, 0x03, 0xf1, 0x98, 0xdf, 0x80,
0xc2, 0xf8, 0xb5, 0xc4, 0xac, 0x00, 0x67, 0x32, 0x05, 0x2e, 0xde, 0x1b, 0xee, 0x8c, 0x7b, 0xe9,
0xf5, 0xc0, 0x4f, 0xae, 0x70, 0xdd, 0x68, 0xd3, 0x84, 0x93, 0x05, 0xcd, 0x21, 0xde, 0x6f, 0x1a,
0x6d, 0xd9, 0x1b, 0x9a, 0x83, 0x78, 0xc8, 0x0f, 0x91, 0x24, 0xc2, 0xb9, 0x5c, 0xc1, 0xda, 0x6a,
0x5c, 0xc4, 0x51, 0x38, 0x78, 0x80, 0x74, 0x02, 0xe7, 0xaf, 0x1a, 0x26, 0xee, 0xf3, 0xbe, 0x5b,
0xea, 0xa2, 0xeb, 0xb5, 0x17, 0xfe, 0xc3, 0x6b, 0xd4, 0x96, 0xfa, 0x8b, 0xf1, 0x9b, 0xb3, 0x25,
0xa8, 0x95, 0x21, 0x8d, 0xbe, 0x2b, 0x56, 0xf0, 0x5d, 0xa8, 0x74, 0xd7, 0x67, 0xd0, 0xff, 0x6b,
0x91, 0x2f, 0xd3, 0x8b, 0xcb, 0x64, 0xeb, 0xc7, 0x65, 0xb2, 0xf5, 0x65, 0x93, 0xb0, 0x8b, 0x4d,
0xc2, 0xbe, 0x6d, 0x12, 0xf6, 0x73, 0x93, 0xb0, 0x8f, 0xcf, 0xfe, 0xf1, 0xa9, 0xbd, 0xe8, 0xc4,
0xd9, 0x5e, 0x78, 0x42, 0x4f, 0x7f, 0x07, 0x00, 0x00, 0xff, 0xff, 0xe6, 0x26, 0x29, 0x60, 0xad,
0x03, 0x00, 0x00,
}

View File

@ -7,8 +7,10 @@ import "gogoproto/gogo.proto";
option go_package = "github.com/containerd/containerd/linux/runcopts;runcopts";
message RuncOptions {
string criu_path = 1;
string systemd_cgroup = 2;
string runtime = 1;
string runtime_root = 2;
string criu_path = 3;
bool systemd_cgroup = 4;
}
message CreateOptions {

View File

@ -13,9 +13,11 @@ import (
"github.com/boltdb/bolt"
eventsapi "github.com/containerd/containerd/api/services/events/v1"
"github.com/containerd/containerd/api/types"
"github.com/containerd/containerd/containers"
"github.com/containerd/containerd/errdefs"
"github.com/containerd/containerd/events"
"github.com/containerd/containerd/identifiers"
"github.com/containerd/containerd/linux/runcopts"
client "github.com/containerd/containerd/linux/shim"
shim "github.com/containerd/containerd/linux/shim/v1"
"github.com/containerd/containerd/log"
@ -25,6 +27,7 @@ import (
"github.com/containerd/containerd/reaper"
"github.com/containerd/containerd/runtime"
"github.com/containerd/containerd/sys"
"github.com/containerd/containerd/typeurl"
runc "github.com/containerd/go-runc"
google_protobuf "github.com/golang/protobuf/ptypes/empty"
"github.com/pkg/errors"
@ -67,6 +70,8 @@ type Config struct {
Shim string `toml:"shim,omitempty"`
// Runtime is a path or name of an OCI runtime used by the shim
Runtime string `toml:"runtime,omitempty"`
// RuntimeRoot is the path that shall be used by the OCI runtime for its data
RuntimeRoot string `toml:"runtime_root,omitempty"`
// NoShim calls runc directly from within the pkg
NoShim bool `toml:"no_shim,omitempty"`
// Debug enable debug on the shim
@ -90,17 +95,14 @@ func New(ic *plugin.InitContext) (interface{}, error) {
}
cfg := ic.Config.(*Config)
r := &Runtime{
root: ic.Root,
state: ic.State,
remote: !cfg.NoShim,
shim: cfg.Shim,
shimDebug: cfg.ShimDebug,
runtime: cfg.Runtime,
monitor: monitor.(runtime.TaskMonitor),
tasks: runtime.NewTaskList(),
db: m.(*bolt.DB),
address: ic.Address,
events: ic.Events,
root: ic.Root,
state: ic.State,
monitor: monitor.(runtime.TaskMonitor),
tasks: runtime.NewTaskList(),
db: m.(*bolt.DB),
address: ic.Address,
events: ic.Events,
config: cfg,
}
tasks, err := r.restoreTasks(ic.Context)
if err != nil {
@ -116,18 +118,16 @@ func New(ic *plugin.InitContext) (interface{}, error) {
}
type Runtime struct {
root string
state string
shim string
shimDebug bool
runtime string
remote bool
address string
root string
state string
address string
monitor runtime.TaskMonitor
tasks *runtime.TaskList
db *bolt.DB
events *events.Exchange
config *Config
}
func (r *Runtime) ID() string {
@ -144,14 +144,18 @@ func (r *Runtime) Create(ctx context.Context, id string, opts runtime.CreateOpts
return nil, errors.Wrapf(err, "invalid task id")
}
ropts, err := r.getRuncOptions(ctx, id)
if err != nil {
return nil, err
}
ec := reaper.Default.Subscribe()
defer reaper.Default.Unsubscribe(ec)
bundle, err := newBundle(
namespace, id,
bundle, err := newBundle(id,
filepath.Join(r.state, namespace),
filepath.Join(r.root, namespace),
opts.Spec.Value, r.events)
opts.Spec.Value)
if err != nil {
return nil, err
}
@ -160,36 +164,50 @@ func (r *Runtime) Create(ctx context.Context, id string, opts runtime.CreateOpts
bundle.Delete()
}
}()
s, err := bundle.NewShim(ctx, r.shim, r.address, r.remote, r.shimDebug, opts, func() {
t, err := r.tasks.Get(ctx, id)
if err != nil {
// Task was never started or was already sucessfully deleted
return
}
lc := t.(*Task)
// Stop the monitor
if err := r.monitor.Stop(lc); err != nil {
log.G(ctx).WithError(err).WithFields(logrus.Fields{
shimopt := ShimLocal(r.events)
if !r.config.NoShim {
var cgroup string
if opts.Options != nil {
v, err := typeurl.UnmarshalAny(opts.Options)
if err != nil {
return nil, err
}
cgroup = v.(*runcopts.CreateOptions).ShimCgroup
}
shimopt = ShimRemote(r.config.Shim, r.address, cgroup, r.config.ShimDebug, func() {
t, err := r.tasks.Get(ctx, id)
if err != nil {
// Task was never started or was already sucessfully deleted
return
}
lc := t.(*Task)
// Stop the monitor
if err := r.monitor.Stop(lc); err != nil {
log.G(ctx).WithError(err).WithFields(logrus.Fields{
"id": id,
"namespace": namespace,
}).Warn("failed to stop monitor")
}
log.G(ctx).WithFields(logrus.Fields{
"id": id,
"namespace": namespace,
}).Warn("failed to stop monitor")
}
}).Warn("cleaning up after killed shim")
err = r.cleanupAfterDeadShim(context.Background(), bundle, namespace, id, lc.pid, ec)
if err == nil {
r.tasks.Delete(ctx, lc)
} else {
log.G(ctx).WithError(err).WithFields(logrus.Fields{
"id": id,
"namespace": namespace,
}).Warn("failed to clen up after killed shim")
}
})
}
log.G(ctx).WithFields(logrus.Fields{
"id": id,
"namespace": namespace,
}).Warn("cleaning up after killed shim")
err = r.cleanupAfterDeadShim(context.Background(), bundle, namespace, id, lc.pid, ec)
if err == nil {
r.tasks.Delete(ctx, lc)
} else {
log.G(ctx).WithError(err).WithFields(logrus.Fields{
"id": id,
"namespace": namespace,
}).Warn("failed to clen up after killed shim")
}
})
s, err := bundle.NewShimClient(ctx, namespace, shimopt, ropts)
if err != nil {
return nil, err
}
@ -200,10 +218,15 @@ func (r *Runtime) Create(ctx context.Context, id string, opts runtime.CreateOpts
}
}
}()
runtime := r.config.Runtime
if ropts != nil && ropts.Runtime != "" {
runtime = ropts.Runtime
}
sopts := &shim.CreateTaskRequest{
ID: id,
Bundle: bundle.path,
Runtime: r.runtime,
Runtime: runtime,
Stdin: opts.IO.Stdin,
Stdout: opts.IO.Stdout,
Stderr: opts.IO.Stderr,
@ -256,11 +279,9 @@ func (r *Runtime) Delete(ctx context.Context, c runtime.Task) (*runtime.Exit, er
}
bundle := loadBundle(
lc.id,
filepath.Join(r.state, namespace, lc.id),
filepath.Join(r.root, namespace, lc.id),
namespace,
lc.id,
r.events,
)
if err := bundle.Delete(); err != nil {
log.G(ctx).WithError(err).Error("failed to delete bundle")
@ -313,10 +334,11 @@ func (r *Runtime) loadTasks(ctx context.Context, ns string) ([]*Task, error) {
}
id := path.Name()
bundle := loadBundle(
id,
filepath.Join(r.state, ns, id),
filepath.Join(r.root, ns, id),
ns, id, r.events)
s, err := bundle.Connect(ctx, r.remote)
)
s, err := bundle.NewShimClient(ctx, ns, ShimConnect(), nil)
if err != nil {
log.G(ctx).WithError(err).WithFields(logrus.Fields{
"id": id,
@ -386,6 +408,7 @@ func (r *Runtime) terminate(ctx context.Context, bundle *bundle, ns, id string)
if err != nil {
return err
}
if err := rt.Delete(ctx, id, &runc.DeleteOpts{
Force: true,
}); err != nil {
@ -401,18 +424,56 @@ func (r *Runtime) terminate(ctx context.Context, bundle *bundle, ns, id string)
}
func (r *Runtime) getRuntime(ctx context.Context, ns, id string) (*runc.Runc, error) {
ropts, err := r.getRuncOptions(ctx, id)
if err != nil {
return nil, err
}
var (
cmd = r.config.Runtime
root = client.RuncRoot
)
if ropts != nil {
if ropts.Runtime != "" {
cmd = ropts.Runtime
}
if ropts.RuntimeRoot != "" {
root = ropts.RuntimeRoot
}
}
return &runc.Runc{
Command: cmd,
LogFormat: runc.JSON,
PdeathSignal: unix.SIGKILL,
Root: filepath.Join(root, ns),
}, nil
}
func (r *Runtime) getRuncOptions(ctx context.Context, id string) (*runcopts.RuncOptions, error) {
var container containers.Container
if err := r.db.View(func(tx *bolt.Tx) error {
store := metadata.NewContainerStore(tx)
var err error
_, err = store.Get(ctx, id)
container, err = store.Get(ctx, id)
return err
}); err != nil {
return nil, err
}
return &runc.Runc{
Command: r.runtime,
LogFormat: runc.JSON,
PdeathSignal: unix.SIGKILL,
Root: filepath.Join(client.RuncRoot, ns),
}, nil
if container.Runtime.Options != nil {
v, err := typeurl.UnmarshalAny(container.Runtime.Options)
if err != nil {
return nil, err
}
ropts, ok := v.(*runcopts.RuncOptions)
if !ok {
return nil, errors.New("invalid runtime options format")
}
return ropts, nil
}
return nil, nil
}

View File

@ -30,20 +30,20 @@ import (
type ClientOpt func(context.Context, Config) (shim.ShimClient, io.Closer, error)
// WithStart executes a new shim process
func WithStart(binary, address string, debug bool, exitHandler func()) ClientOpt {
func WithStart(binary, address, daemonAddress, cgroup string, debug bool, exitHandler func()) ClientOpt {
return func(ctx context.Context, config Config) (_ shim.ShimClient, _ io.Closer, err error) {
socket, err := newSocket(config)
socket, err := newSocket(address)
if err != nil {
return nil, nil, err
}
defer socket.Close()
f, err := socket.File()
if err != nil {
return nil, nil, errors.Wrapf(err, "failed to get fd for socket %s", config.Address)
return nil, nil, errors.Wrapf(err, "failed to get fd for socket %s", address)
}
defer f.Close()
cmd := newCommand(binary, address, debug, config, f)
cmd := newCommand(binary, daemonAddress, debug, config, f)
ec, err := reaper.Default.Start(cmd)
if err != nil {
return nil, nil, errors.Wrapf(err, "failed to start shim")
@ -59,19 +59,23 @@ func WithStart(binary, address string, debug bool, exitHandler func()) ClientOpt
}()
log.G(ctx).WithFields(logrus.Fields{
"pid": cmd.Process.Pid,
"address": config.Address,
"address": address,
"debug": debug,
}).Infof("shim %s started", binary)
// set shim in cgroup if it is provided
if config.CgroupPath != "" {
if err := setCgroup(ctx, config, cmd); err != nil {
if cgroup != "" {
if err := setCgroup(cgroup, cmd); err != nil {
return nil, nil, err
}
log.G(ctx).WithFields(logrus.Fields{
"pid": cmd.Process.Pid,
"address": address,
}).Infof("shim placed in cgroup %s", cgroup)
}
if err = sys.SetOOMScore(cmd.Process.Pid, sys.OOMScoreMaxKillable); err != nil {
return nil, nil, errors.Wrap(err, "failed to set OOM Score on shim")
}
c, clo, err := WithConnect(ctx, config)
c, clo, err := WithConnect(address)(ctx, config)
if err != nil {
return nil, nil, errors.Wrap(err, "failed to connect")
}
@ -79,15 +83,26 @@ func WithStart(binary, address string, debug bool, exitHandler func()) ClientOpt
}
}
func newCommand(binary, address string, debug bool, config Config, socket *os.File) *exec.Cmd {
func newCommand(binary, daemonAddress string, debug bool, config Config, socket *os.File) *exec.Cmd {
args := []string{
"--namespace", config.Namespace,
"--address", address,
"--workdir", config.WorkDir,
"--address", daemonAddress,
}
if config.Criu != "" {
args = append(args, "--criu-path", config.Criu)
}
if config.RuntimeRoot != "" {
args = append(args, "--runtime-root", config.RuntimeRoot)
}
if config.SystemdCgroup {
args = append(args, "--systemd-cgroup")
}
if debug {
args = append(args, "--debug")
}
cmd := exec.Command(binary, args...)
cmd.Dir = config.Path
// make sure the shim can be re-parented to system init
@ -102,13 +117,13 @@ func newCommand(binary, address string, debug bool, config Config, socket *os.Fi
return cmd
}
func newSocket(config Config) (*net.UnixListener, error) {
if len(config.Address) > 106 {
return nil, errors.Errorf("%q: unix socket path too long (limit 106)", config.Address)
func newSocket(address string) (*net.UnixListener, error) {
if len(address) > 106 {
return nil, errors.Errorf("%q: unix socket path too long (limit 106)", address)
}
l, err := net.Listen("unix", "\x00"+config.Address)
l, err := net.Listen("unix", "\x00"+address)
if err != nil {
return nil, errors.Wrapf(err, "failed to listen to abstract unix socket %q", config.Address)
return nil, errors.Wrapf(err, "failed to listen to abstract unix socket %q", address)
}
return l.(*net.UnixListener), nil
@ -144,18 +159,20 @@ func dialAddress(address string) string {
}
// WithConnect connects to an existing shim
func WithConnect(ctx context.Context, config Config) (shim.ShimClient, io.Closer, error) {
conn, err := connect(config.Address, annonDialer)
if err != nil {
return nil, nil, err
func WithConnect(address string) ClientOpt {
return func(ctx context.Context, config Config) (shim.ShimClient, io.Closer, error) {
conn, err := connect(address, annonDialer)
if err != nil {
return nil, nil, err
}
return shim.NewShimClient(conn), conn, nil
}
return shim.NewShimClient(conn), conn, nil
}
// WithLocal uses an in process shim
func WithLocal(publisher events.Publisher) func(context.Context, Config) (shim.ShimClient, io.Closer, error) {
return func(ctx context.Context, config Config) (shim.ShimClient, io.Closer, error) {
service, err := NewService(config.Path, config.Namespace, config.WorkDir, publisher)
service, err := NewService(config, publisher)
if err != nil {
return nil, nil, err
}
@ -164,11 +181,12 @@ func WithLocal(publisher events.Publisher) func(context.Context, Config) (shim.S
}
type Config struct {
Address string
Path string
Namespace string
CgroupPath string
WorkDir string
Path string
Namespace string
WorkDir string
Criu string
RuntimeRoot string
SystemdCgroup bool
}
// New returns a new shim client

View File

@ -3,14 +3,11 @@
package shim
import (
"context"
"os/exec"
"syscall"
"github.com/containerd/cgroups"
"github.com/containerd/containerd/log"
"github.com/pkg/errors"
"github.com/sirupsen/logrus"
)
var atter = syscall.SysProcAttr{
@ -18,19 +15,15 @@ var atter = syscall.SysProcAttr{
Setpgid: true,
}
func setCgroup(ctx context.Context, config Config, cmd *exec.Cmd) error {
cg, err := cgroups.Load(cgroups.V1, cgroups.StaticPath(config.CgroupPath))
func setCgroup(cgroupPath string, cmd *exec.Cmd) error {
cg, err := cgroups.Load(cgroups.V1, cgroups.StaticPath(cgroupPath))
if err != nil {
return errors.Wrapf(err, "failed to load cgroup %s", config.CgroupPath)
return errors.Wrapf(err, "failed to load cgroup %s", cgroupPath)
}
if err := cg.Add(cgroups.Process{
Pid: cmd.Process.Pid,
}); err != nil {
return errors.Wrapf(err, "failed to join cgroup %s", config.CgroupPath)
return errors.Wrapf(err, "failed to join cgroup %s", cgroupPath)
}
log.G(ctx).WithFields(logrus.Fields{
"pid": cmd.Process.Pid,
"address": config.Address,
}).Infof("shim placed in cgroup %s", config.CgroupPath)
return nil
}

View File

@ -3,7 +3,6 @@
package shim
import (
"context"
"os/exec"
"syscall"
)
@ -12,6 +11,6 @@ var atter = syscall.SysProcAttr{
Setpgid: true,
}
func setCgroup(ctx context.Context, config Config, cmd *exec.Cmd) error {
func setCgroup(cgroupPath string, cmd *exec.Cmd) error {
return nil
}

View File

@ -56,7 +56,7 @@ type initProcess struct {
rootfs string
}
func newInitProcess(context context.Context, plat platform, path, namespace, workDir string, r *shimapi.CreateTaskRequest) (*initProcess, error) {
func (s *Service) newInitProcess(context context.Context, r *shimapi.CreateTaskRequest) (*initProcess, error) {
var success bool
if err := identifiers.Validate(r.ID); err != nil {
@ -71,7 +71,7 @@ func newInitProcess(context context.Context, plat platform, path, namespace, wor
options = *v.(*runcopts.CreateOptions)
}
rootfs := filepath.Join(path, "rootfs")
rootfs := filepath.Join(s.config.Path, "rootfs")
// count the number of successful mounts so we can undo
// what was actually done rather than what should have been
// done.
@ -94,17 +94,19 @@ func newInitProcess(context context.Context, plat platform, path, namespace, wor
}
}
runtime := &runc.Runc{
Command: r.Runtime,
Log: filepath.Join(path, "log.json"),
LogFormat: runc.JSON,
PdeathSignal: syscall.SIGKILL,
Root: filepath.Join(RuncRoot, namespace),
Command: r.Runtime,
Log: filepath.Join(s.config.Path, "log.json"),
LogFormat: runc.JSON,
PdeathSignal: syscall.SIGKILL,
Root: filepath.Join(s.config.RuntimeRoot, s.config.Namespace),
Criu: s.config.Criu,
SystemdCgroup: s.config.SystemdCgroup,
}
p := &initProcess{
id: r.ID,
bundle: r.Bundle,
runtime: runtime,
platform: plat,
platform: s.platform,
stdio: stdio{
stdin: r.Stdin,
stdout: r.Stdout,
@ -112,7 +114,7 @@ func newInitProcess(context context.Context, plat platform, path, namespace, wor
terminal: r.Terminal,
},
rootfs: rootfs,
workDir: workDir,
workDir: s.config.WorkDir,
}
p.initState = &createdState{p: p}
var (
@ -133,7 +135,7 @@ func newInitProcess(context context.Context, plat platform, path, namespace, wor
return nil, errors.Wrap(err, "failed to create OCI runtime io pipes")
}
}
pidFile := filepath.Join(path, InitPidFile)
pidFile := filepath.Join(s.config.Path, InitPidFile)
if r.Checkpoint != "" {
opts := &runc.RestoreOpts{
CheckpointOpts: runc.CheckpointOpts{
@ -178,7 +180,7 @@ func newInitProcess(context context.Context, plat platform, path, namespace, wor
if err != nil {
return nil, errors.Wrap(err, "failed to retrieve console master")
}
console, err = plat.copyConsole(context, console, r.Stdin, r.Stdout, r.Stderr, &p.WaitGroup, &copyWaitGroup)
console, err = s.platform.copyConsole(context, console, r.Stdin, r.Stdout, r.Stderr, &p.WaitGroup, &copyWaitGroup)
if err != nil {
return nil, errors.Wrap(err, "failed to start console copy")
}

View File

@ -33,7 +33,7 @@ func (c *local) Start(ctx context.Context, in *shimapi.StartRequest, opts ...grp
func (c *local) Delete(ctx context.Context, in *google_protobuf.Empty, opts ...grpc.CallOption) (*shimapi.DeleteResponse, error) {
// make sure we unmount the containers rootfs for this local
if err := unix.Unmount(filepath.Join(c.s.path, "rootfs"), 0); err != nil {
if err := unix.Unmount(filepath.Join(c.s.config.Path, "rootfs"), 0); err != nil {
return nil, err
}
return c.s.Delete(ctx, in)

View File

@ -32,23 +32,21 @@ var empty = &google_protobuf.Empty{}
const RuncRoot = "/run/containerd/runc"
// NewService returns a new shim service that can be used via GRPC
func NewService(path, namespace, workDir string, publisher events.Publisher) (*Service, error) {
if namespace == "" {
func NewService(config Config, publisher events.Publisher) (*Service, error) {
if config.Namespace == "" {
return nil, fmt.Errorf("shim namespace cannot be empty")
}
context := namespaces.WithNamespace(context.Background(), namespace)
context := namespaces.WithNamespace(context.Background(), config.Namespace)
context = log.WithLogger(context, logrus.WithFields(logrus.Fields{
"namespace": namespace,
"namespace": config.Namespace,
"path": config.Path,
"pid": os.Getpid(),
"path": path,
}))
s := &Service{
path: path,
config: config,
context: context,
processes: make(map[string]process),
events: make(chan interface{}, 4096),
namespace: namespace,
context: context,
workDir: workDir,
ec: reaper.Default.Subscribe(),
}
go s.processExits()
@ -67,25 +65,24 @@ type platform interface {
}
type Service struct {
path string
id string
bundle string
mu sync.Mutex
processes map[string]process
events chan interface{}
deferredEvent interface{}
namespace string
context context.Context
ec chan runc.Exit
mu sync.Mutex
workDir string
platform platform
config Config
context context.Context
processes map[string]process
events chan interface{}
platform platform
ec chan runc.Exit
// Filled by Create()
id string
bundle string
}
func (s *Service) Create(ctx context.Context, r *shimapi.CreateTaskRequest) (*shimapi.CreateTaskResponse, error) {
s.mu.Lock()
defer s.mu.Unlock()
process, err := newInitProcess(ctx, s.platform, s.path, s.namespace, s.workDir, r)
process, err := s.newInitProcess(ctx, r)
if err != nil {
return nil, errdefs.ToGRPC(err)
}
@ -194,7 +191,7 @@ func (s *Service) Exec(ctx context.Context, r *shimapi.ExecProcessRequest) (*goo
return nil, errdefs.ToGRPCf(errdefs.ErrFailedPrecondition, "container must be created")
}
process, err := newExecProcess(ctx, s.path, r, p.(*initProcess), r.ID)
process, err := newExecProcess(ctx, s.config.Path, r, p.(*initProcess), r.ID)
if err != nil {
return nil, errdefs.ToGRPC(err)
}

View File

@ -335,18 +335,13 @@ func writeContainer(bkt *bolt.Bucket, container *containers.Container) error {
return err
}
obkt, err := rbkt.CreateBucket(bucketKeyOptions)
if err != nil {
return err
}
if container.Runtime.Options != nil {
data, err := proto.Marshal(container.Runtime.Options)
if err != nil {
return err
}
if err := obkt.Put(bucketKeyOptions, data); err != nil {
if err := rbkt.Put(bucketKeyOptions, data); err != nil {
return err
}
}