Merge pull request #2830 from Ace-Tang/support_cr_without_image

cr: support checkpoint/restore without image
This commit is contained in:
Phil Estes 2018-12-20 13:24:37 -05:00 committed by GitHub
commit 06e04bc5a9
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
15 changed files with 855 additions and 103 deletions

View File

@ -36,6 +36,14 @@ var checkpointCommand = cli.Command{
Name: "exit", Name: "exit",
Usage: "stop the container after the checkpoint", Usage: "stop the container after the checkpoint",
}, },
cli.StringFlag{
Name: "image-path",
Usage: "path to criu image files",
},
cli.StringFlag{
Name: "work-path",
Usage: "path to criu work files and logs",
},
}, },
Action: func(context *cli.Context) error { Action: func(context *cli.Context) error {
id := context.Args().First() id := context.Args().First()
@ -59,40 +67,55 @@ var checkpointCommand = cli.Command{
if err != nil { if err != nil {
return err return err
} }
var opts []containerd.CheckpointTaskOpts opts := []containerd.CheckpointTaskOpts{withCheckpointOpts(info.Runtime.Name, context)}
if context.Bool("exit") {
opts = append(opts, withExit(info.Runtime.Name))
}
checkpoint, err := task.Checkpoint(ctx, opts...) checkpoint, err := task.Checkpoint(ctx, opts...)
if err != nil { if err != nil {
return err return err
} }
fmt.Println(checkpoint.Name()) if context.String("image-path") == "" {
fmt.Println(checkpoint.Name())
}
return nil return nil
}, },
} }
func withExit(rt string) containerd.CheckpointTaskOpts { // withCheckpointOpts only suitable for runc runtime now
func withCheckpointOpts(rt string, context *cli.Context) containerd.CheckpointTaskOpts {
return func(r *containerd.CheckpointTaskInfo) error { return func(r *containerd.CheckpointTaskInfo) error {
imagePath := context.String("image-path")
workPath := context.String("work-path")
switch rt { switch rt {
case "io.containerd.runc.v1": case "io.containerd.runc.v1":
if r.Options == nil { if r.Options == nil {
r.Options = &options.CheckpointOptions{ r.Options = &options.CheckpointOptions{}
Exit: true, }
} opts, _ := r.Options.(*options.CheckpointOptions)
} else {
opts, _ := r.Options.(*options.CheckpointOptions) if context.Bool("exit") {
opts.Exit = true opts.Exit = true
} }
default: if imagePath != "" {
opts.ImagePath = imagePath
}
if workPath != "" {
opts.WorkPath = workPath
}
case "io.containerd.runtime.v1.linux":
if r.Options == nil { if r.Options == nil {
r.Options = &runctypes.CheckpointOptions{ r.Options = &runctypes.CheckpointOptions{}
Exit: true, }
} opts, _ := r.Options.(*runctypes.CheckpointOptions)
} else {
opts, _ := r.Options.(*runctypes.CheckpointOptions) if context.Bool("exit") {
opts.Exit = true opts.Exit = true
} }
if imagePath != "" {
opts.ImagePath = imagePath
}
if workPath != "" {
opts.WorkPath = workPath
}
} }
return nil return nil
} }

View File

@ -22,16 +22,19 @@ import (
"bytes" "bytes"
"fmt" "fmt"
"io" "io"
"io/ioutil"
"os"
"path/filepath"
"strings" "strings"
"sync" "sync"
"syscall" "syscall"
"testing" "testing"
"github.com/containerd/containerd/cio"
"github.com/containerd/containerd/oci" "github.com/containerd/containerd/oci"
) )
const ( const (
v1runtime = "io.containerd.runtime.v1.linux"
testCheckpointName = "checkpoint-test:latest" testCheckpointName = "checkpoint-test:latest"
) )
@ -408,3 +411,123 @@ func TestCheckpointLeaveRunning(t *testing.T) {
<-statusC <-statusC
} }
func TestCRWithImagePath(t *testing.T) {
if !supportsCriu {
t.Skip("system does not have criu installed")
}
client, err := newClient(t, address)
if err != nil {
t.Fatal(err)
}
defer client.Close()
var (
ctx, cancel = testContext()
id = t.Name() + "-checkpoint"
)
defer cancel()
image, err := client.GetImage(ctx, testImage)
if err != nil {
t.Fatal(err)
}
container, err := client.NewContainer(ctx, id, WithNewSnapshot(id, image), WithNewSpec(oci.WithImageConfig(image), oci.WithProcessArgs("top")))
if err != nil {
t.Fatal(err)
}
defer container.Delete(ctx, WithSnapshotCleanup)
task, err := container.NewTask(ctx, empty())
if err != nil {
t.Fatal(err)
}
statusC, err := task.Wait(ctx)
if err != nil {
t.Fatal(err)
}
if err := task.Start(ctx); err != nil {
t.Fatal(err)
}
// create image path store criu image files
crDir, err := ioutil.TempDir("", "test-cr")
if err != nil {
t.Fatal(err)
}
defer os.RemoveAll(crDir)
imagePath := filepath.Join(crDir, "cr")
// checkpoint task
if _, err := task.Checkpoint(ctx, WithCheckpointImagePath(client.runtime, imagePath)); err != nil {
t.Fatal(err)
}
if err := task.Kill(ctx, syscall.SIGKILL); err != nil {
t.Fatal(err)
}
<-statusC
task.Delete(ctx)
// check image files have been dumped into image path
if files, err := ioutil.ReadDir(imagePath); err != nil || len(files) == 0 {
t.Fatal("failed to checkpoint with image path set")
}
// restore task with same container image and checkpoint directory,
// the restore process should finish in millisecond level
id = t.Name() + "-restore"
ncontainer, err := client.NewContainer(ctx, id, WithNewSnapshot(id, image), WithNewSpec(oci.WithImageConfig(image)))
if err != nil {
t.Fatal(err)
}
defer ncontainer.Delete(ctx, WithSnapshotCleanup)
ntask, err := ncontainer.NewTask(ctx, empty(), WithRestoreImagePath(client.runtime, imagePath))
if err != nil {
t.Fatal(err)
}
statusC, err = ntask.Wait(ctx)
if err != nil {
t.Fatal(err)
}
if err := ntask.Start(ctx); err != nil {
t.Fatal(err)
}
// check top process is existed in restored container
spec, err := container.Spec(ctx)
if err != nil {
t.Fatal(err)
}
stdout := bytes.NewBuffer(nil)
spec.Process.Args = []string{"ps", "-ef"}
process, err := ntask.Exec(ctx, t.Name()+"_exec", spec.Process, cio.NewCreator(withByteBuffers(stdout)))
if err != nil {
t.Fatal(err)
}
processStatusC, err := process.Wait(ctx)
if err != nil {
t.Fatal(err)
}
if err := process.Start(ctx); err != nil {
t.Fatal(err)
}
<-processStatusC
if _, err := process.Delete(ctx); err != nil {
t.Fatal(err)
}
if !strings.Contains(stdout.String(), "top") {
t.Errorf("except top process exists in restored container but not, got output %s", stdout.String())
}
// we wrote the same thing after attach
if err := ntask.Kill(ctx, syscall.SIGKILL); err != nil {
t.Fatal(err)
}
<-statusC
ntask.Delete(ctx)
}

View File

@ -112,6 +112,20 @@ file {
type: TYPE_UINT32 type: TYPE_UINT32
json_name: "ioGid" json_name: "ioGid"
} }
field {
name: "criu_work_path"
number: 12
label: LABEL_OPTIONAL
type: TYPE_STRING
json_name: "criuWorkPath"
}
field {
name: "criu_image_path"
number: 13
label: LABEL_OPTIONAL
type: TYPE_STRING
json_name: "criuImagePath"
}
} }
message_type { message_type {
name: "CheckpointOptions" name: "CheckpointOptions"
@ -164,6 +178,20 @@ file {
type: TYPE_STRING type: TYPE_STRING
json_name: "cgroupsMode" json_name: "cgroupsMode"
} }
field {
name: "work_path"
number: 8
label: LABEL_OPTIONAL
type: TYPE_STRING
json_name: "workPath"
}
field {
name: "image_path"
number: 9
label: LABEL_OPTIONAL
type: TYPE_STRING
json_name: "imagePath"
}
} }
message_type { message_type {
name: "ProcessDetails" name: "ProcessDetails"

View File

@ -60,6 +60,8 @@ type CreateOptions struct {
ShimCgroup string `protobuf:"bytes,9,opt,name=shim_cgroup,json=shimCgroup,proto3" json:"shim_cgroup,omitempty"` ShimCgroup string `protobuf:"bytes,9,opt,name=shim_cgroup,json=shimCgroup,proto3" json:"shim_cgroup,omitempty"`
IoUid uint32 `protobuf:"varint,10,opt,name=io_uid,json=ioUid,proto3" json:"io_uid,omitempty"` IoUid uint32 `protobuf:"varint,10,opt,name=io_uid,json=ioUid,proto3" json:"io_uid,omitempty"`
IoGid uint32 `protobuf:"varint,11,opt,name=io_gid,json=ioGid,proto3" json:"io_gid,omitempty"` IoGid uint32 `protobuf:"varint,11,opt,name=io_gid,json=ioGid,proto3" json:"io_gid,omitempty"`
CriuWorkPath string `protobuf:"bytes,12,opt,name=criu_work_path,json=criuWorkPath,proto3" json:"criu_work_path,omitempty"`
CriuImagePath string `protobuf:"bytes,13,opt,name=criu_image_path,json=criuImagePath,proto3" json:"criu_image_path,omitempty"`
} }
func (m *CreateOptions) Reset() { *m = CreateOptions{} } func (m *CreateOptions) Reset() { *m = CreateOptions{} }
@ -74,6 +76,8 @@ type CheckpointOptions struct {
FileLocks bool `protobuf:"varint,5,opt,name=file_locks,json=fileLocks,proto3" json:"file_locks,omitempty"` FileLocks bool `protobuf:"varint,5,opt,name=file_locks,json=fileLocks,proto3" json:"file_locks,omitempty"`
EmptyNamespaces []string `protobuf:"bytes,6,rep,name=empty_namespaces,json=emptyNamespaces" json:"empty_namespaces,omitempty"` EmptyNamespaces []string `protobuf:"bytes,6,rep,name=empty_namespaces,json=emptyNamespaces" json:"empty_namespaces,omitempty"`
CgroupsMode string `protobuf:"bytes,7,opt,name=cgroups_mode,json=cgroupsMode,proto3" json:"cgroups_mode,omitempty"` CgroupsMode string `protobuf:"bytes,7,opt,name=cgroups_mode,json=cgroupsMode,proto3" json:"cgroups_mode,omitempty"`
WorkPath string `protobuf:"bytes,8,opt,name=work_path,json=workPath,proto3" json:"work_path,omitempty"`
ImagePath string `protobuf:"bytes,9,opt,name=image_path,json=imagePath,proto3" json:"image_path,omitempty"`
} }
func (m *CheckpointOptions) Reset() { *m = CheckpointOptions{} } func (m *CheckpointOptions) Reset() { *m = CheckpointOptions{} }
@ -252,6 +256,18 @@ func (m *CreateOptions) MarshalTo(dAtA []byte) (int, error) {
i++ i++
i = encodeVarintRunc(dAtA, i, uint64(m.IoGid)) i = encodeVarintRunc(dAtA, i, uint64(m.IoGid))
} }
if len(m.CriuWorkPath) > 0 {
dAtA[i] = 0x62
i++
i = encodeVarintRunc(dAtA, i, uint64(len(m.CriuWorkPath)))
i += copy(dAtA[i:], m.CriuWorkPath)
}
if len(m.CriuImagePath) > 0 {
dAtA[i] = 0x6a
i++
i = encodeVarintRunc(dAtA, i, uint64(len(m.CriuImagePath)))
i += copy(dAtA[i:], m.CriuImagePath)
}
return i, nil return i, nil
} }
@ -341,6 +357,18 @@ func (m *CheckpointOptions) MarshalTo(dAtA []byte) (int, error) {
i = encodeVarintRunc(dAtA, i, uint64(len(m.CgroupsMode))) i = encodeVarintRunc(dAtA, i, uint64(len(m.CgroupsMode)))
i += copy(dAtA[i:], m.CgroupsMode) i += copy(dAtA[i:], m.CgroupsMode)
} }
if len(m.WorkPath) > 0 {
dAtA[i] = 0x42
i++
i = encodeVarintRunc(dAtA, i, uint64(len(m.WorkPath)))
i += copy(dAtA[i:], m.WorkPath)
}
if len(m.ImagePath) > 0 {
dAtA[i] = 0x4a
i++
i = encodeVarintRunc(dAtA, i, uint64(len(m.ImagePath)))
i += copy(dAtA[i:], m.ImagePath)
}
return i, nil return i, nil
} }
@ -439,6 +467,14 @@ func (m *CreateOptions) Size() (n int) {
if m.IoGid != 0 { if m.IoGid != 0 {
n += 1 + sovRunc(uint64(m.IoGid)) n += 1 + sovRunc(uint64(m.IoGid))
} }
l = len(m.CriuWorkPath)
if l > 0 {
n += 1 + l + sovRunc(uint64(l))
}
l = len(m.CriuImagePath)
if l > 0 {
n += 1 + l + sovRunc(uint64(l))
}
return n return n
} }
@ -470,6 +506,14 @@ func (m *CheckpointOptions) Size() (n int) {
if l > 0 { if l > 0 {
n += 1 + l + sovRunc(uint64(l)) n += 1 + l + sovRunc(uint64(l))
} }
l = len(m.WorkPath)
if l > 0 {
n += 1 + l + sovRunc(uint64(l))
}
l = len(m.ImagePath)
if l > 0 {
n += 1 + l + sovRunc(uint64(l))
}
return n return n
} }
@ -525,6 +569,8 @@ func (this *CreateOptions) String() string {
`ShimCgroup:` + fmt.Sprintf("%v", this.ShimCgroup) + `,`, `ShimCgroup:` + fmt.Sprintf("%v", this.ShimCgroup) + `,`,
`IoUid:` + fmt.Sprintf("%v", this.IoUid) + `,`, `IoUid:` + fmt.Sprintf("%v", this.IoUid) + `,`,
`IoGid:` + fmt.Sprintf("%v", this.IoGid) + `,`, `IoGid:` + fmt.Sprintf("%v", this.IoGid) + `,`,
`CriuWorkPath:` + fmt.Sprintf("%v", this.CriuWorkPath) + `,`,
`CriuImagePath:` + fmt.Sprintf("%v", this.CriuImagePath) + `,`,
`}`, `}`,
}, "") }, "")
return s return s
@ -541,6 +587,8 @@ func (this *CheckpointOptions) String() string {
`FileLocks:` + fmt.Sprintf("%v", this.FileLocks) + `,`, `FileLocks:` + fmt.Sprintf("%v", this.FileLocks) + `,`,
`EmptyNamespaces:` + fmt.Sprintf("%v", this.EmptyNamespaces) + `,`, `EmptyNamespaces:` + fmt.Sprintf("%v", this.EmptyNamespaces) + `,`,
`CgroupsMode:` + fmt.Sprintf("%v", this.CgroupsMode) + `,`, `CgroupsMode:` + fmt.Sprintf("%v", this.CgroupsMode) + `,`,
`WorkPath:` + fmt.Sprintf("%v", this.WorkPath) + `,`,
`ImagePath:` + fmt.Sprintf("%v", this.ImagePath) + `,`,
`}`, `}`,
}, "") }, "")
return s return s
@ -994,6 +1042,64 @@ func (m *CreateOptions) Unmarshal(dAtA []byte) error {
break break
} }
} }
case 12:
if wireType != 2 {
return fmt.Errorf("proto: wrong wireType = %d for field CriuWorkPath", 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.CriuWorkPath = string(dAtA[iNdEx:postIndex])
iNdEx = postIndex
case 13:
if wireType != 2 {
return fmt.Errorf("proto: wrong wireType = %d for field CriuImagePath", 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.CriuImagePath = string(dAtA[iNdEx:postIndex])
iNdEx = postIndex
default: default:
iNdEx = preIndex iNdEx = preIndex
skippy, err := skipRunc(dAtA[iNdEx:]) skippy, err := skipRunc(dAtA[iNdEx:])
@ -1202,6 +1308,64 @@ func (m *CheckpointOptions) Unmarshal(dAtA []byte) error {
} }
m.CgroupsMode = string(dAtA[iNdEx:postIndex]) m.CgroupsMode = string(dAtA[iNdEx:postIndex])
iNdEx = postIndex iNdEx = postIndex
case 8:
if wireType != 2 {
return fmt.Errorf("proto: wrong wireType = %d for field WorkPath", 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.WorkPath = string(dAtA[iNdEx:postIndex])
iNdEx = postIndex
case 9:
if wireType != 2 {
return fmt.Errorf("proto: wrong wireType = %d for field ImagePath", 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.ImagePath = string(dAtA[iNdEx:postIndex])
iNdEx = postIndex
default: default:
iNdEx = preIndex iNdEx = preIndex
skippy, err := skipRunc(dAtA[iNdEx:]) skippy, err := skipRunc(dAtA[iNdEx:])
@ -1412,39 +1576,43 @@ func init() {
} }
var fileDescriptorRunc = []byte{ var fileDescriptorRunc = []byte{
// 541 bytes of a gzipped FileDescriptorProto // 604 bytes of a gzipped FileDescriptorProto
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xdc, 0x93, 0xc1, 0x6e, 0xd3, 0x40, 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xdc, 0x94, 0xcf, 0x6e, 0xd3, 0x40,
0x10, 0x86, 0x6b, 0xda, 0x26, 0xce, 0xa4, 0x29, 0xb0, 0x50, 0xc9, 0x14, 0x91, 0x86, 0x00, 0x52, 0x10, 0xc6, 0xeb, 0xfe, 0x49, 0x9c, 0x49, 0xd2, 0xc2, 0x42, 0x25, 0xd3, 0xaa, 0x69, 0x08, 0x7f,
0xb8, 0xa4, 0x12, 0x88, 0x13, 0xb7, 0xa6, 0x08, 0x55, 0x40, 0xa9, 0x0c, 0x95, 0x10, 0x42, 0x5a, 0x14, 0x2e, 0xa9, 0x04, 0xe2, 0xc4, 0xad, 0x29, 0x42, 0x15, 0x50, 0x2a, 0x43, 0x05, 0x42, 0x48,
0xb9, 0xeb, 0x21, 0x59, 0xc5, 0xde, 0x59, 0x79, 0xd7, 0xd4, 0xb9, 0xf5, 0x09, 0x78, 0xae, 0x1e, 0x2b, 0x77, 0x3d, 0x24, 0xab, 0xc4, 0x3b, 0x96, 0x77, 0x4d, 0x92, 0x1b, 0x4f, 0xc0, 0x0b, 0xf1,
0x39, 0x72, 0x42, 0x34, 0x2f, 0x02, 0xf2, 0xda, 0x0e, 0x9c, 0x39, 0x72, 0xfb, 0xe7, 0xfb, 0xc7, 0x02, 0x3d, 0x21, 0x8e, 0x9c, 0x10, 0xcd, 0x93, 0xa0, 0x5d, 0xc7, 0x69, 0xcf, 0x1c, 0xb9, 0xcd,
0x9e, 0xd1, 0xbf, 0x1a, 0x98, 0x4c, 0xa5, 0x9d, 0xe5, 0x67, 0x63, 0x41, 0xe9, 0xbe, 0x20, 0x65, 0xfc, 0xe6, 0xb3, 0x67, 0xf4, 0x7d, 0xb2, 0xa1, 0x3f, 0x90, 0x66, 0x98, 0x9f, 0xf7, 0x04, 0x25,
0x23, 0xa9, 0x30, 0x8b, 0xff, 0x96, 0x59, 0xae, 0xac, 0x4c, 0x71, 0x3f, 0x91, 0x2a, 0x2f, 0xca, 0x07, 0x82, 0x94, 0x89, 0xa4, 0xc2, 0x2c, 0xbe, 0x5e, 0x66, 0xb9, 0x32, 0x32, 0xc1, 0x83, 0xb1,
0x4a, 0xd8, 0x85, 0x46, 0xe3, 0xd4, 0x58, 0x67, 0x64, 0x89, 0xed, 0xfc, 0x69, 0x1f, 0xbb, 0xb6, 0x54, 0xf9, 0xd4, 0x76, 0xc2, 0xcc, 0x52, 0xd4, 0xae, 0xea, 0xa5, 0x19, 0x19, 0x62, 0xdb, 0x57,
0x71, 0x69, 0xee, 0xde, 0x9e, 0xd2, 0x94, 0x5c, 0xc7, 0x7e, 0xa9, 0xaa, 0xe6, 0xe1, 0x57, 0x0f, 0xf2, 0x9e, 0x93, 0xf5, 0xec, 0x70, 0xe7, 0xf6, 0x80, 0x06, 0xe4, 0x14, 0x07, 0xb6, 0x2a, 0xc4,
0xba, 0x61, 0xae, 0xc4, 0x5b, 0x6d, 0x25, 0x29, 0xc3, 0x02, 0x68, 0xd7, 0x23, 0x02, 0x6f, 0xe0, 0x9d, 0x6f, 0x1e, 0xd4, 0xc3, 0x5c, 0x89, 0x37, 0xa9, 0x91, 0xa4, 0x34, 0x0b, 0xa0, 0xba, 0x58,
0x8d, 0x3a, 0x61, 0x53, 0xb2, 0xfb, 0xb0, 0x55, 0x4b, 0x9e, 0x11, 0xd9, 0xe0, 0x9a, 0xb3, 0xbb, 0x11, 0x78, 0x6d, 0xaf, 0x5b, 0x0b, 0xcb, 0x96, 0xdd, 0x85, 0xc6, 0xa2, 0xe4, 0x19, 0x91, 0x09,
0x35, 0x0b, 0x89, 0x2c, 0xbb, 0x0b, 0x1d, 0x91, 0xc9, 0x9c, 0xeb, 0xc8, 0xce, 0x82, 0x75, 0xe7, 0x56, 0xdd, 0xb8, 0xbe, 0x60, 0x21, 0x91, 0x61, 0xbb, 0x50, 0x13, 0x99, 0xcc, 0x79, 0x1a, 0x99,
0xfb, 0x25, 0x38, 0x89, 0xec, 0x8c, 0x3d, 0x82, 0x6d, 0xb3, 0x30, 0x16, 0xd3, 0x98, 0x8b, 0x69, 0x61, 0xb0, 0xe6, 0xe6, 0xbe, 0x05, 0xa7, 0x91, 0x19, 0xb2, 0x07, 0xb0, 0xa9, 0x67, 0xda, 0x60,
0x46, 0xb9, 0x0e, 0x36, 0x06, 0xde, 0xc8, 0x0f, 0x7b, 0x35, 0x9d, 0x38, 0x38, 0xbc, 0x58, 0x87, 0x12, 0x73, 0x31, 0xc8, 0x28, 0x4f, 0x83, 0xf5, 0xb6, 0xd7, 0xf5, 0xc3, 0xe6, 0x82, 0xf6, 0x1d,
0xde, 0x24, 0xc3, 0xc8, 0x62, 0xb3, 0xd2, 0x10, 0x7a, 0x8a, 0xb8, 0x96, 0x5f, 0xc8, 0x56, 0x93, 0xec, 0xfc, 0x58, 0x83, 0x66, 0x3f, 0xc3, 0xc8, 0x60, 0x79, 0x52, 0x07, 0x9a, 0x8a, 0x78, 0x2a,
0x3d, 0xf7, 0x5d, 0x57, 0xd1, 0x49, 0xc9, 0xdc, 0xe4, 0x3b, 0xe0, 0x93, 0x46, 0xc5, 0xad, 0xd0, 0xbf, 0x90, 0x29, 0x36, 0x7b, 0xee, 0xb9, 0xba, 0xa2, 0x53, 0xcb, 0xdc, 0xe6, 0x3b, 0xe0, 0x53,
0x6e, 0x31, 0x3f, 0x6c, 0x97, 0xf5, 0x7b, 0xa1, 0xd9, 0x13, 0xd8, 0xc1, 0xc2, 0x62, 0xa6, 0xa2, 0x8a, 0x8a, 0x1b, 0x91, 0xba, 0xc3, 0xfc, 0xb0, 0x6a, 0xfb, 0x77, 0x22, 0x65, 0x8f, 0x61, 0x1b,
0x84, 0xe7, 0x4a, 0x16, 0xdc, 0x90, 0x98, 0xa3, 0x35, 0x6e, 0x41, 0x3f, 0xbc, 0xd5, 0x98, 0xa7, 0xa7, 0x06, 0x33, 0x15, 0x8d, 0x79, 0xae, 0xe4, 0x94, 0x6b, 0x12, 0x23, 0x34, 0xda, 0x1d, 0xe8,
0x4a, 0x16, 0xef, 0x2a, 0x8b, 0xed, 0x82, 0x6f, 0x31, 0x4b, 0xa5, 0x8a, 0x92, 0x7a, 0xcb, 0x55, 0x87, 0xb7, 0xca, 0xe1, 0x99, 0x92, 0xd3, 0xb7, 0xc5, 0x88, 0xed, 0x80, 0x6f, 0x30, 0x4b, 0xa4,
0xcd, 0xee, 0x01, 0x7c, 0x96, 0x09, 0xf2, 0x84, 0xc4, 0xdc, 0x04, 0x9b, 0xce, 0xed, 0x94, 0xe4, 0x8a, 0xc6, 0x8b, 0x2b, 0x97, 0x3d, 0xdb, 0x03, 0xf8, 0x2c, 0xc7, 0xc8, 0xc7, 0x24, 0x46, 0x3a,
0x75, 0x09, 0xd8, 0x63, 0xb8, 0x81, 0xa9, 0xb6, 0x0b, 0xae, 0xa2, 0x14, 0x8d, 0x8e, 0x04, 0x9a, 0xd8, 0x70, 0xd3, 0x9a, 0x25, 0xaf, 0x2c, 0x60, 0x8f, 0xe0, 0x06, 0x26, 0xa9, 0x99, 0x71, 0x15,
0xa0, 0x35, 0x58, 0x1f, 0x75, 0xc2, 0xeb, 0x8e, 0x1f, 0xaf, 0x70, 0x99, 0x68, 0x95, 0x84, 0xe1, 0x25, 0xa8, 0xd3, 0x48, 0xa0, 0x0e, 0x2a, 0xed, 0xb5, 0x6e, 0x2d, 0xdc, 0x72, 0xfc, 0x64, 0x89,
0x29, 0xc5, 0x18, 0xb4, 0xab, 0x44, 0x6b, 0xf6, 0x86, 0x62, 0x64, 0x0f, 0x61, 0x5b, 0x11, 0x57, 0xad, 0xa3, 0x85, 0x13, 0x9a, 0x27, 0x14, 0x63, 0x50, 0x2d, 0x1c, 0x5d, 0xb0, 0xd7, 0x14, 0x23,
0x78, 0xce, 0xe7, 0xb8, 0xc8, 0xa4, 0x9a, 0x06, 0xbe, 0x1b, 0xb8, 0xa5, 0xe8, 0x18, 0xcf, 0x5f, 0xbb, 0x0f, 0x9b, 0x8a, 0xb8, 0xc2, 0x09, 0x1f, 0xe1, 0x2c, 0x93, 0x6a, 0x10, 0xf8, 0x6e, 0x61,
0x55, 0x8c, 0xed, 0x41, 0xd7, 0xcc, 0x64, 0xda, 0xe4, 0xda, 0x71, 0xff, 0x81, 0x12, 0x55, 0xa1, 0x43, 0xd1, 0x09, 0x4e, 0x5e, 0x16, 0x8c, 0xed, 0x43, 0x5d, 0x0f, 0x65, 0x52, 0xfa, 0x5a, 0x73,
0xb2, 0x1d, 0x68, 0x49, 0xe2, 0xb9, 0x8c, 0x03, 0x18, 0x78, 0xa3, 0x5e, 0xb8, 0x29, 0xe9, 0x54, 0xef, 0x01, 0x8b, 0x0a, 0x53, 0xd9, 0x36, 0x54, 0x24, 0xf1, 0x5c, 0xc6, 0x01, 0xb4, 0xbd, 0x6e,
0xc6, 0x35, 0x9e, 0xca, 0x38, 0xe8, 0x36, 0xf8, 0xa5, 0x8c, 0x87, 0xbf, 0x3c, 0xb8, 0x39, 0x99, 0x33, 0xdc, 0x90, 0x74, 0x26, 0xe3, 0x05, 0x1e, 0xc8, 0x38, 0xa8, 0x97, 0xf8, 0x85, 0x8c, 0xed,
0xa1, 0x98, 0x6b, 0x92, 0xca, 0x36, 0xcf, 0xc0, 0x60, 0x03, 0x0b, 0xd9, 0xa4, 0xef, 0xf4, 0xff, 0x52, 0x17, 0xe3, 0x84, 0xb2, 0x51, 0x91, 0x65, 0xc3, 0xbd, 0xb1, 0x61, 0xe9, 0x7b, 0xca, 0x46,
0x1a, 0xfb, 0xf0, 0x19, 0x6c, 0x9f, 0x64, 0x24, 0xd0, 0x98, 0x43, 0xb4, 0x91, 0x4c, 0x0c, 0x7b, 0x2e, 0xcf, 0x87, 0xb0, 0xe5, 0x54, 0x32, 0x89, 0x06, 0x58, 0xc8, 0x9a, 0x4e, 0xd6, 0xb4, 0xf8,
0x00, 0x6d, 0x2c, 0x50, 0x70, 0x19, 0x57, 0x77, 0x71, 0x00, 0xcb, 0x1f, 0x7b, 0xad, 0x17, 0x05, 0xd8, 0x52, 0xab, 0xeb, 0x7c, 0x5f, 0x85, 0x9b, 0xfd, 0x21, 0x8a, 0x51, 0x4a, 0x52, 0x99, 0x32,
0x8a, 0xa3, 0xc3, 0xb0, 0x55, 0x5a, 0x47, 0xf1, 0xc1, 0xa7, 0xcb, 0xab, 0xfe, 0xda, 0xf7, 0xab, 0x54, 0x06, 0xeb, 0x38, 0x95, 0x65, 0x96, 0xae, 0xfe, 0x6f, 0x43, 0xdc, 0x85, 0xda, 0x95, 0x95,
0xfe, 0xda, 0xc5, 0xb2, 0xef, 0x5d, 0x2e, 0xfb, 0xde, 0xb7, 0x65, 0xdf, 0xfb, 0xb9, 0xec, 0x7b, 0x7e, 0xf1, 0x59, 0x4c, 0x4a, 0x1b, 0xf7, 0x00, 0xae, 0x39, 0x58, 0x44, 0x57, 0x93, 0x4b, 0xf7,
0x1f, 0x0f, 0xfe, 0xf5, 0xb0, 0x9f, 0xaf, 0xd4, 0x87, 0xb5, 0xb3, 0x96, 0xbb, 0xd9, 0xa7, 0xbf, 0x9e, 0xc2, 0xe6, 0x69, 0x46, 0x02, 0xb5, 0x3e, 0x42, 0x13, 0xc9, 0xb1, 0x66, 0xf7, 0xa0, 0x8a,
0x03, 0x00, 0x00, 0xff, 0xff, 0x18, 0xa1, 0x4b, 0x5b, 0x27, 0x04, 0x00, 0x00, 0x53, 0x14, 0x5c, 0xc6, 0xc5, 0x17, 0x7a, 0x08, 0xf3, 0xdf, 0xfb, 0x95, 0xe7, 0x53, 0x14, 0xc7,
0x47, 0x61, 0xc5, 0x8e, 0x8e, 0xe3, 0xc3, 0x4f, 0x17, 0x97, 0xad, 0x95, 0x5f, 0x97, 0xad, 0x95,
0xaf, 0xf3, 0x96, 0x77, 0x31, 0x6f, 0x79, 0x3f, 0xe7, 0x2d, 0xef, 0xcf, 0xbc, 0xe5, 0x7d, 0x3c,
0xfc, 0xd7, 0x5f, 0xcc, 0xb3, 0x65, 0xf5, 0x61, 0xe5, 0xbc, 0xe2, 0xfe, 0x1e, 0x4f, 0xfe, 0x06,
0x00, 0x00, 0xff, 0xff, 0x7f, 0x24, 0x6f, 0x2e, 0xb1, 0x04, 0x00, 0x00,
} }

View File

@ -25,6 +25,8 @@ message CreateOptions {
string shim_cgroup = 9; string shim_cgroup = 9;
uint32 io_uid = 10; uint32 io_uid = 10;
uint32 io_gid = 11; uint32 io_gid = 11;
string criu_work_path = 12;
string criu_image_path = 13;
} }
message CheckpointOptions { message CheckpointOptions {
@ -35,6 +37,8 @@ message CheckpointOptions {
bool file_locks = 5; bool file_locks = 5;
repeated string empty_namespaces = 6; repeated string empty_namespaces = 6;
string cgroups_mode = 7; string cgroups_mode = 7;
string work_path = 8;
string image_path = 9;
} }
message ProcessDetails { message ProcessDetails {

View File

@ -76,6 +76,7 @@ type Init struct {
IoGID int IoGID int
NoPivotRoot bool NoPivotRoot bool
NoNewKeyring bool NoNewKeyring bool
CriuWorkPath string
} }
// NewRunc returns a new runc instance for a process // NewRunc returns a new runc instance for a process
@ -132,7 +133,7 @@ func (p *Init) Create(ctx context.Context, r *CreateConfig) error {
opts := &runc.RestoreOpts{ opts := &runc.RestoreOpts{
CheckpointOpts: runc.CheckpointOpts{ CheckpointOpts: runc.CheckpointOpts{
ImagePath: r.Checkpoint, ImagePath: r.Checkpoint,
WorkDir: p.WorkDir, WorkDir: p.CriuWorkPath,
ParentPath: r.ParentCheckpoint, ParentPath: r.ParentCheckpoint,
}, },
PidFile: pidFile, PidFile: pidFile,
@ -427,8 +428,12 @@ func (p *Init) checkpoint(ctx context.Context, r *CheckpointConfig) error {
if !r.Exit { if !r.Exit {
actions = append(actions, runc.LeaveRunning) actions = append(actions, runc.LeaveRunning)
} }
work := filepath.Join(p.WorkDir, "criu-work") // keep criu work directory if criu work dir is set
defer os.RemoveAll(work) work := r.WorkDir
if work == "" {
work = filepath.Join(p.WorkDir, "criu-work")
defer os.RemoveAll(work)
}
if err := p.runtime.Checkpoint(ctx, p.id, &runc.CheckpointOpts{ if err := p.runtime.Checkpoint(ctx, p.id, &runc.CheckpointOpts{
WorkDir: work, WorkDir: work,
ImagePath: r.Path, ImagePath: r.Path,

View File

@ -55,6 +55,7 @@ type ExecConfig struct {
// CheckpointConfig holds task checkpoint configuration // CheckpointConfig holds task checkpoint configuration
type CheckpointConfig struct { type CheckpointConfig struct {
WorkDir string
Path string Path string
Exit bool Exit bool
AllowOpenTCP bool AllowOpenTCP bool

View File

@ -448,6 +448,7 @@ func (s *Service) Checkpoint(ctx context.Context, r *shimapi.CheckpointTaskReque
AllowTerminal: options.Terminal, AllowTerminal: options.Terminal,
FileLocks: options.FileLocks, FileLocks: options.FileLocks,
EmptyNamespaces: options.EmptyNamespaces, EmptyNamespaces: options.EmptyNamespaces,
WorkDir: options.WorkPath,
}); err != nil { }); err != nil {
return nil, errdefs.ToGRPC(err) return nil, errdefs.ToGRPC(err)
} }
@ -657,5 +658,11 @@ func newInit(ctx context.Context, path, workDir, runtimeRoot, namespace, criu st
p.IoGID = int(options.IoGid) p.IoGID = int(options.IoGid)
p.NoPivotRoot = options.NoPivotRoot p.NoPivotRoot = options.NoPivotRoot
p.NoNewKeyring = options.NoNewKeyring p.NoNewKeyring = options.NoNewKeyring
p.CriuWorkPath = options.CriuWorkPath
if p.CriuWorkPath == "" {
// if criu work path not set, use container WorkDir
p.CriuWorkPath = p.WorkDir
}
return p, nil return p, nil
} }

View File

@ -67,6 +67,20 @@ file {
type: TYPE_BOOL type: TYPE_BOOL
json_name: "systemdCgroup" json_name: "systemdCgroup"
} }
field {
name: "criu_image_path"
number: 10
label: LABEL_OPTIONAL
type: TYPE_STRING
json_name: "criuImagePath"
}
field {
name: "criu_work_path"
number: 11
label: LABEL_OPTIONAL
type: TYPE_STRING
json_name: "criuWorkPath"
}
} }
message_type { message_type {
name: "CheckpointOptions" name: "CheckpointOptions"
@ -119,6 +133,20 @@ file {
type: TYPE_STRING type: TYPE_STRING
json_name: "cgroupsMode" json_name: "cgroupsMode"
} }
field {
name: "image_path"
number: 8
label: LABEL_OPTIONAL
type: TYPE_STRING
json_name: "imagePath"
}
field {
name: "work_path"
number: 9
label: LABEL_OPTIONAL
type: TYPE_STRING
json_name: "workPath"
}
} }
message_type { message_type {
name: "ProcessDetails" name: "ProcessDetails"

View File

@ -55,6 +55,10 @@ type Options struct {
CriuPath string `protobuf:"bytes,8,opt,name=criu_path,json=criuPath,proto3" json:"criu_path,omitempty"` CriuPath string `protobuf:"bytes,8,opt,name=criu_path,json=criuPath,proto3" json:"criu_path,omitempty"`
// enable systemd cgroups // enable systemd cgroups
SystemdCgroup bool `protobuf:"varint,9,opt,name=systemd_cgroup,json=systemdCgroup,proto3" json:"systemd_cgroup,omitempty"` SystemdCgroup bool `protobuf:"varint,9,opt,name=systemd_cgroup,json=systemdCgroup,proto3" json:"systemd_cgroup,omitempty"`
// criu image path
CriuImagePath string `protobuf:"bytes,10,opt,name=criu_image_path,json=criuImagePath,proto3" json:"criu_image_path,omitempty"`
// criu work path
CriuWorkPath string `protobuf:"bytes,11,opt,name=criu_work_path,json=criuWorkPath,proto3" json:"criu_work_path,omitempty"`
} }
func (m *Options) Reset() { *m = Options{} } func (m *Options) Reset() { *m = Options{} }
@ -76,6 +80,10 @@ type CheckpointOptions struct {
EmptyNamespaces []string `protobuf:"bytes,6,rep,name=empty_namespaces,json=emptyNamespaces" json:"empty_namespaces,omitempty"` EmptyNamespaces []string `protobuf:"bytes,6,rep,name=empty_namespaces,json=emptyNamespaces" json:"empty_namespaces,omitempty"`
// set the cgroups mode, soft, full, strict // set the cgroups mode, soft, full, strict
CgroupsMode string `protobuf:"bytes,7,opt,name=cgroups_mode,json=cgroupsMode,proto3" json:"cgroups_mode,omitempty"` CgroupsMode string `protobuf:"bytes,7,opt,name=cgroups_mode,json=cgroupsMode,proto3" json:"cgroups_mode,omitempty"`
// checkpoint image path
ImagePath string `protobuf:"bytes,8,opt,name=image_path,json=imagePath,proto3" json:"image_path,omitempty"`
// checkpoint work path
WorkPath string `protobuf:"bytes,9,opt,name=work_path,json=workPath,proto3" json:"work_path,omitempty"`
} }
func (m *CheckpointOptions) Reset() { *m = CheckpointOptions{} } func (m *CheckpointOptions) Reset() { *m = CheckpointOptions{} }
@ -175,6 +183,18 @@ func (m *Options) MarshalTo(dAtA []byte) (int, error) {
} }
i++ i++
} }
if len(m.CriuImagePath) > 0 {
dAtA[i] = 0x52
i++
i = encodeVarintOci(dAtA, i, uint64(len(m.CriuImagePath)))
i += copy(dAtA[i:], m.CriuImagePath)
}
if len(m.CriuWorkPath) > 0 {
dAtA[i] = 0x5a
i++
i = encodeVarintOci(dAtA, i, uint64(len(m.CriuWorkPath)))
i += copy(dAtA[i:], m.CriuWorkPath)
}
return i, nil return i, nil
} }
@ -264,6 +284,18 @@ func (m *CheckpointOptions) MarshalTo(dAtA []byte) (int, error) {
i = encodeVarintOci(dAtA, i, uint64(len(m.CgroupsMode))) i = encodeVarintOci(dAtA, i, uint64(len(m.CgroupsMode)))
i += copy(dAtA[i:], m.CgroupsMode) i += copy(dAtA[i:], m.CgroupsMode)
} }
if len(m.ImagePath) > 0 {
dAtA[i] = 0x42
i++
i = encodeVarintOci(dAtA, i, uint64(len(m.ImagePath)))
i += copy(dAtA[i:], m.ImagePath)
}
if len(m.WorkPath) > 0 {
dAtA[i] = 0x4a
i++
i = encodeVarintOci(dAtA, i, uint64(len(m.WorkPath)))
i += copy(dAtA[i:], m.WorkPath)
}
return i, nil return i, nil
} }
@ -334,6 +366,14 @@ func (m *Options) Size() (n int) {
if m.SystemdCgroup { if m.SystemdCgroup {
n += 2 n += 2
} }
l = len(m.CriuImagePath)
if l > 0 {
n += 1 + l + sovOci(uint64(l))
}
l = len(m.CriuWorkPath)
if l > 0 {
n += 1 + l + sovOci(uint64(l))
}
return n return n
} }
@ -365,6 +405,14 @@ func (m *CheckpointOptions) Size() (n int) {
if l > 0 { if l > 0 {
n += 1 + l + sovOci(uint64(l)) n += 1 + l + sovOci(uint64(l))
} }
l = len(m.ImagePath)
if l > 0 {
n += 1 + l + sovOci(uint64(l))
}
l = len(m.WorkPath)
if l > 0 {
n += 1 + l + sovOci(uint64(l))
}
return n return n
} }
@ -405,6 +453,8 @@ func (this *Options) String() string {
`Root:` + fmt.Sprintf("%v", this.Root) + `,`, `Root:` + fmt.Sprintf("%v", this.Root) + `,`,
`CriuPath:` + fmt.Sprintf("%v", this.CriuPath) + `,`, `CriuPath:` + fmt.Sprintf("%v", this.CriuPath) + `,`,
`SystemdCgroup:` + fmt.Sprintf("%v", this.SystemdCgroup) + `,`, `SystemdCgroup:` + fmt.Sprintf("%v", this.SystemdCgroup) + `,`,
`CriuImagePath:` + fmt.Sprintf("%v", this.CriuImagePath) + `,`,
`CriuWorkPath:` + fmt.Sprintf("%v", this.CriuWorkPath) + `,`,
`}`, `}`,
}, "") }, "")
return s return s
@ -421,6 +471,8 @@ func (this *CheckpointOptions) String() string {
`FileLocks:` + fmt.Sprintf("%v", this.FileLocks) + `,`, `FileLocks:` + fmt.Sprintf("%v", this.FileLocks) + `,`,
`EmptyNamespaces:` + fmt.Sprintf("%v", this.EmptyNamespaces) + `,`, `EmptyNamespaces:` + fmt.Sprintf("%v", this.EmptyNamespaces) + `,`,
`CgroupsMode:` + fmt.Sprintf("%v", this.CgroupsMode) + `,`, `CgroupsMode:` + fmt.Sprintf("%v", this.CgroupsMode) + `,`,
`ImagePath:` + fmt.Sprintf("%v", this.ImagePath) + `,`,
`WorkPath:` + fmt.Sprintf("%v", this.WorkPath) + `,`,
`}`, `}`,
}, "") }, "")
return s return s
@ -686,6 +738,64 @@ func (m *Options) Unmarshal(dAtA []byte) error {
} }
} }
m.SystemdCgroup = bool(v != 0) m.SystemdCgroup = bool(v != 0)
case 10:
if wireType != 2 {
return fmt.Errorf("proto: wrong wireType = %d for field CriuImagePath", wireType)
}
var stringLen uint64
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowOci
}
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 ErrInvalidLengthOci
}
postIndex := iNdEx + intStringLen
if postIndex > l {
return io.ErrUnexpectedEOF
}
m.CriuImagePath = string(dAtA[iNdEx:postIndex])
iNdEx = postIndex
case 11:
if wireType != 2 {
return fmt.Errorf("proto: wrong wireType = %d for field CriuWorkPath", wireType)
}
var stringLen uint64
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowOci
}
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 ErrInvalidLengthOci
}
postIndex := iNdEx + intStringLen
if postIndex > l {
return io.ErrUnexpectedEOF
}
m.CriuWorkPath = string(dAtA[iNdEx:postIndex])
iNdEx = postIndex
default: default:
iNdEx = preIndex iNdEx = preIndex
skippy, err := skipOci(dAtA[iNdEx:]) skippy, err := skipOci(dAtA[iNdEx:])
@ -894,6 +1004,64 @@ func (m *CheckpointOptions) Unmarshal(dAtA []byte) error {
} }
m.CgroupsMode = string(dAtA[iNdEx:postIndex]) m.CgroupsMode = string(dAtA[iNdEx:postIndex])
iNdEx = postIndex iNdEx = postIndex
case 8:
if wireType != 2 {
return fmt.Errorf("proto: wrong wireType = %d for field ImagePath", wireType)
}
var stringLen uint64
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowOci
}
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 ErrInvalidLengthOci
}
postIndex := iNdEx + intStringLen
if postIndex > l {
return io.ErrUnexpectedEOF
}
m.ImagePath = string(dAtA[iNdEx:postIndex])
iNdEx = postIndex
case 9:
if wireType != 2 {
return fmt.Errorf("proto: wrong wireType = %d for field WorkPath", wireType)
}
var stringLen uint64
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowOci
}
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 ErrInvalidLengthOci
}
postIndex := iNdEx + intStringLen
if postIndex > l {
return io.ErrUnexpectedEOF
}
m.WorkPath = string(dAtA[iNdEx:postIndex])
iNdEx = postIndex
default: default:
iNdEx = preIndex iNdEx = preIndex
skippy, err := skipOci(dAtA[iNdEx:]) skippy, err := skipOci(dAtA[iNdEx:])
@ -1104,39 +1272,42 @@ func init() {
} }
var fileDescriptorOci = []byte{ var fileDescriptorOci = []byte{
// 529 bytes of a gzipped FileDescriptorProto // 587 bytes of a gzipped FileDescriptorProto
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x9c, 0x92, 0xcd, 0x6e, 0xd3, 0x4c, 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x9c, 0x93, 0xcf, 0x6e, 0xd3, 0x40,
0x14, 0x86, 0xeb, 0xfe, 0x38, 0xf6, 0xf4, 0xe7, 0xfb, 0x18, 0xa8, 0x64, 0x8a, 0x70, 0x43, 0x00, 0x10, 0x87, 0xeb, 0xfe, 0x49, 0xec, 0x4d, 0x93, 0xc2, 0x42, 0x25, 0xd3, 0x8a, 0x34, 0x94, 0x82,
0x29, 0x6c, 0x12, 0x51, 0xc4, 0x8a, 0x0d, 0x6a, 0x8b, 0x50, 0x05, 0x94, 0xca, 0x50, 0x09, 0x75, 0xc2, 0x25, 0x11, 0x45, 0x9c, 0xb8, 0xa0, 0xb6, 0x08, 0x55, 0x40, 0xa9, 0x0c, 0x15, 0xa8, 0x97,
0x33, 0x72, 0xc6, 0x07, 0xe7, 0x28, 0xf1, 0x1c, 0xcb, 0x33, 0x4e, 0x9d, 0x1d, 0xf7, 0xc1, 0x0d, 0x95, 0xbb, 0x1e, 0x9c, 0x51, 0xe2, 0x1d, 0xcb, 0xbb, 0x69, 0xd2, 0x1b, 0xef, 0xc5, 0x0b, 0xf4,
0x75, 0xc9, 0x92, 0x15, 0xa2, 0xb9, 0x11, 0x90, 0xc7, 0x4e, 0x61, 0xcd, 0xca, 0xef, 0x3c, 0xef, 0xc8, 0x91, 0x13, 0xa2, 0xb9, 0xf1, 0x16, 0x68, 0xd7, 0x4e, 0xdb, 0x33, 0x27, 0xcf, 0x7e, 0xf3,
0xf1, 0x68, 0xe6, 0xd1, 0xb0, 0xc3, 0x14, 0xcd, 0xb8, 0x1c, 0x0d, 0x24, 0x65, 0x43, 0x49, 0xca, 0xf3, 0x78, 0xfd, 0xad, 0x96, 0xed, 0xa5, 0x68, 0x06, 0xe3, 0xb3, 0x9e, 0xa4, 0xac, 0x2f, 0x49,
0xc4, 0xa8, 0xa0, 0x48, 0xfe, 0x8e, 0x45, 0xa9, 0x0c, 0x66, 0x30, 0x9c, 0x1d, 0xd4, 0x51, 0x0e, 0x99, 0x18, 0x15, 0x14, 0xc9, 0xed, 0xb2, 0x18, 0x2b, 0x83, 0x19, 0xf4, 0xcf, 0x77, 0x6d, 0x29,
0x29, 0x37, 0x48, 0x4a, 0x0f, 0x49, 0xe2, 0x20, 0x2f, 0xc8, 0x10, 0xe7, 0x7f, 0xa6, 0x07, 0xf5, 0xfb, 0x94, 0x1b, 0x24, 0xa5, 0xfb, 0x24, 0xb1, 0x97, 0x17, 0x64, 0x88, 0xf3, 0x9b, 0x74, 0xcf,
0xc8, 0x60, 0xf6, 0x74, 0xef, 0x4e, 0x4a, 0x29, 0xd9, 0x7a, 0x58, 0xa7, 0x66, 0xb2, 0xf7, 0x75, 0x46, 0x7a, 0xe7, 0xcf, 0x37, 0xee, 0xa7, 0x94, 0x92, 0x6b, 0xf7, 0x6d, 0x55, 0x26, 0xb7, 0xff,
0x95, 0x75, 0xde, 0x37, 0xff, 0xf3, 0x1e, 0xdb, 0x56, 0x24, 0x72, 0x9c, 0x91, 0x11, 0x05, 0x91, 0x2e, 0xb2, 0xfa, 0xc7, 0xf2, 0x7d, 0xbe, 0xcd, 0x9a, 0x8a, 0x44, 0x8e, 0xe7, 0x64, 0x44, 0x41,
0x09, 0x9c, 0xae, 0xd3, 0xf7, 0xa2, 0x4d, 0x45, 0x67, 0x35, 0x8b, 0x88, 0x0c, 0x7f, 0xc4, 0x76, 0x64, 0x42, 0xaf, 0xe3, 0x75, 0xfd, 0xa8, 0xa1, 0xe8, 0xd8, 0xb2, 0x88, 0xc8, 0xf0, 0x1d, 0xd6,
0x14, 0x09, 0x05, 0x97, 0x62, 0x02, 0xf3, 0x02, 0x55, 0x1a, 0xac, 0xda, 0xa1, 0x2d, 0x45, 0xa7, 0x52, 0x24, 0x14, 0x4c, 0xc4, 0x10, 0x2e, 0x0a, 0x54, 0x69, 0xb8, 0xe8, 0x42, 0xab, 0x8a, 0x8e,
0x70, 0xf9, 0xa6, 0x61, 0x7c, 0x9f, 0x6d, 0xea, 0x31, 0x66, 0x42, 0xa6, 0x05, 0x95, 0x79, 0xb0, 0x60, 0xf2, 0xae, 0x64, 0x7c, 0x8b, 0x35, 0xf4, 0x00, 0x33, 0x21, 0xd3, 0x82, 0xc6, 0x79, 0xb8,
0xd6, 0x75, 0xfa, 0x7e, 0xc4, 0x6a, 0x74, 0x64, 0x09, 0xdf, 0x65, 0x2e, 0x92, 0x28, 0x31, 0x09, 0xd4, 0xf1, 0xba, 0x41, 0xc4, 0x2c, 0xda, 0x77, 0x84, 0xaf, 0xb3, 0x1a, 0x92, 0x18, 0x63, 0x12,
0xd6, 0xbb, 0x4e, 0x7f, 0x3b, 0xda, 0x40, 0x3a, 0xc7, 0xa4, 0xc5, 0x29, 0x26, 0xc1, 0xc6, 0x12, 0x2e, 0x77, 0xbc, 0x6e, 0x33, 0x5a, 0x41, 0x3a, 0xc1, 0xa4, 0xc2, 0x29, 0x26, 0xe1, 0xca, 0x1c,
0xbf, 0xc6, 0xa4, 0xde, 0x6e, 0x84, 0x2a, 0x2e, 0xe6, 0x42, 0xc5, 0x19, 0x04, 0x6e, 0xb3, 0x5d, 0xbf, 0xc5, 0xc4, 0x8e, 0x3b, 0x43, 0x15, 0x17, 0x17, 0x42, 0xc5, 0x19, 0x84, 0xb5, 0x72, 0x5c,
0x83, 0x4e, 0xe3, 0x0c, 0x38, 0x67, 0xeb, 0xf6, 0xc0, 0x1d, 0xdb, 0xd8, 0xcc, 0xef, 0x31, 0x5f, 0x89, 0x8e, 0xe2, 0x0c, 0x38, 0x67, 0xcb, 0x6e, 0xc3, 0x75, 0xd7, 0x71, 0x35, 0xdf, 0x64, 0x81,
0x16, 0x58, 0x8a, 0x3c, 0x36, 0xe3, 0xc0, 0xb3, 0x85, 0x57, 0x83, 0xb3, 0xd8, 0x8c, 0xf9, 0x63, 0x2c, 0x70, 0x2c, 0xf2, 0xd8, 0x0c, 0x42, 0xdf, 0x35, 0x7c, 0x0b, 0x8e, 0x63, 0x33, 0xe0, 0x4f,
0xb6, 0xa3, 0xe7, 0xda, 0x40, 0x96, 0x2c, 0xcf, 0xe8, 0xdb, 0x6b, 0x6c, 0xb7, 0xb4, 0x39, 0x66, 0x58, 0x4b, 0x5f, 0x68, 0x03, 0x59, 0x32, 0xdf, 0x63, 0xe0, 0x7e, 0xa3, 0x59, 0xd1, 0x6a, 0x9b,
0xef, 0x97, 0xc3, 0x6e, 0x1d, 0x8d, 0x41, 0x4e, 0x72, 0x42, 0x65, 0x96, 0x9e, 0x38, 0x5b, 0x87, 0x4f, 0xd9, 0x9a, 0x9b, 0x81, 0x59, 0x9c, 0x42, 0x39, 0x89, 0xb9, 0x49, 0x4d, 0x8b, 0x0f, 0x2d,
0x0a, 0x97, 0x7a, 0x6c, 0xe6, 0x77, 0x99, 0x47, 0x39, 0x28, 0x61, 0x64, 0xde, 0x1a, 0xe9, 0xd4, 0x75, 0xe3, 0x76, 0x58, 0xcb, 0xe5, 0x26, 0x54, 0x0c, 0xcb, 0x58, 0xc3, 0xc5, 0x56, 0x2d, 0xfd,
0xeb, 0x8f, 0x32, 0xe7, 0x07, 0x6c, 0x17, 0x2a, 0x03, 0x85, 0x8a, 0xa7, 0xa2, 0x54, 0x58, 0x09, 0x42, 0xc5, 0xd0, 0xa6, 0xb6, 0x7f, 0x2c, 0xb2, 0xbb, 0xfb, 0x03, 0x90, 0xc3, 0x9c, 0x50, 0x99,
0x4d, 0x72, 0x02, 0x46, 0x5b, 0x2d, 0x5e, 0x74, 0x7b, 0x59, 0x9e, 0x2b, 0xac, 0x3e, 0x34, 0x15, 0xb9, 0x75, 0xce, 0x96, 0x61, 0x8a, 0x73, 0xd9, 0xae, 0xe6, 0x0f, 0x98, 0x4f, 0x39, 0x28, 0x61,
0xdf, 0x63, 0x9e, 0x81, 0x22, 0x43, 0x15, 0x4f, 0xad, 0x21, 0x2f, 0xba, 0x59, 0xf3, 0xfb, 0x8c, 0x64, 0x5e, 0xf9, 0xad, 0xdb, 0xf5, 0x67, 0x99, 0xf3, 0x5d, 0xb6, 0x0e, 0x53, 0x03, 0x85, 0x8a,
0x7d, 0xc6, 0x29, 0x88, 0x29, 0xc9, 0x89, 0xb6, 0xa2, 0xbc, 0xc8, 0xaf, 0xc9, 0xdb, 0x1a, 0xf0, 0x47, 0x62, 0xac, 0x70, 0x2a, 0x34, 0xc9, 0x21, 0x18, 0xed, 0x24, 0xfb, 0xd1, 0xbd, 0x79, 0xf3,
0x27, 0xec, 0x7f, 0xc8, 0x72, 0xd3, 0xb8, 0xd2, 0x79, 0x2c, 0x41, 0x07, 0x6e, 0x77, 0xad, 0xef, 0x44, 0xe1, 0xf4, 0x53, 0xd9, 0xe2, 0x1b, 0xcc, 0x37, 0x50, 0x64, 0xa8, 0xe2, 0x91, 0xf3, 0xed,
0x47, 0xff, 0x59, 0x7e, 0x7a, 0x83, 0xf9, 0x03, 0xb6, 0xd5, 0xdc, 0x5e, 0x8b, 0x8c, 0x12, 0x68, 0x47, 0xd7, 0x6b, 0xfe, 0x90, 0xb1, 0x6f, 0x38, 0x02, 0x31, 0x22, 0x39, 0xd4, 0x4e, 0xbb, 0x1f,
0xf5, 0x6d, 0xb6, 0xec, 0x1d, 0x25, 0xd0, 0x7b, 0xce, 0x76, 0xce, 0x0a, 0x92, 0xa0, 0xf5, 0x31, 0x05, 0x96, 0xbc, 0xb7, 0x80, 0x3f, 0x63, 0x77, 0x20, 0xcb, 0x4d, 0x69, 0x5e, 0xe7, 0xb1, 0x04,
0x98, 0x18, 0xa7, 0x9a, 0x3f, 0x64, 0x1d, 0xa8, 0x40, 0x0a, 0x4c, 0xac, 0x00, 0xff, 0x90, 0x2d, 0x1d, 0xd6, 0x3a, 0x4b, 0xdd, 0x20, 0x5a, 0x73, 0xfc, 0xe8, 0x1a, 0xf3, 0x47, 0x6c, 0xb5, 0x74,
0x7e, 0xec, 0xbb, 0xaf, 0x2a, 0x90, 0x27, 0xc7, 0x91, 0x5b, 0x57, 0x27, 0xc9, 0xe1, 0xc5, 0xd5, 0xa9, 0x45, 0x46, 0x09, 0x54, 0x87, 0xd1, 0xa8, 0xd8, 0x07, 0x4a, 0xc0, 0x7e, 0xec, 0x96, 0xca,
0x75, 0xb8, 0xf2, 0xfd, 0x3a, 0x5c, 0xf9, 0xb2, 0x08, 0x9d, 0xab, 0x45, 0xe8, 0x7c, 0x5b, 0x84, 0xf2, 0x50, 0x02, 0xbc, 0xd6, 0xb8, 0xc9, 0x82, 0x1b, 0x83, 0x41, 0x79, 0x64, 0x93, 0xb9, 0xbd,
0xce, 0xcf, 0x45, 0xe8, 0x5c, 0xbc, 0xfc, 0xd7, 0xe7, 0xfd, 0xa2, 0xfd, 0x7e, 0x5a, 0x19, 0xb9, 0x97, 0xac, 0x75, 0x5c, 0x90, 0x04, 0xad, 0x0f, 0xc0, 0xc4, 0x38, 0xd2, 0xfc, 0x31, 0xab, 0xc3,
0xf6, 0xed, 0x3e, 0xfb, 0x1d, 0x00, 0x00, 0xff, 0xff, 0x78, 0xf4, 0x16, 0x4e, 0x2b, 0x03, 0x00, 0x14, 0xa4, 0xc0, 0xc4, 0xc9, 0x0b, 0xf6, 0xd8, 0xec, 0xf7, 0x56, 0xed, 0xcd, 0x14, 0xe4, 0xe1,
0x00, 0x41, 0x54, 0xb3, 0xad, 0xc3, 0x64, 0xef, 0xf4, 0xf2, 0xaa, 0xbd, 0xf0, 0xeb, 0xaa, 0xbd, 0xf0,
0x7d, 0xd6, 0xf6, 0x2e, 0x67, 0x6d, 0xef, 0xe7, 0xac, 0xed, 0xfd, 0x99, 0xb5, 0xbd, 0xd3, 0xd7,
0xff, 0x7b, 0xd1, 0x5e, 0x55, 0xcf, 0xaf, 0x0b, 0x67, 0x35, 0x77, 0x8b, 0x5e, 0xfc, 0x0b, 0x00,
0x00, 0xff, 0xff, 0x90, 0x50, 0x79, 0xf2, 0xb5, 0x03, 0x00, 0x00,
} }

View File

@ -25,6 +25,10 @@ message Options {
string criu_path = 8; string criu_path = 8;
// enable systemd cgroups // enable systemd cgroups
bool systemd_cgroup = 9; bool systemd_cgroup = 9;
// criu image path
string criu_image_path = 10;
// criu work path
string criu_work_path = 11;
} }
message CheckpointOptions { message CheckpointOptions {
@ -42,6 +46,10 @@ message CheckpointOptions {
repeated string empty_namespaces = 6; repeated string empty_namespaces = 6;
// set the cgroups mode, soft, full, strict // set the cgroups mode, soft, full, strict
string cgroups_mode = 7; string cgroups_mode = 7;
// checkpoint image path
string image_path = 8;
// checkpoint work path
string work_path = 9;
} }
message ProcessDetails { message ProcessDetails {

View File

@ -562,6 +562,7 @@ func (s *service) Checkpoint(ctx context.Context, r *taskAPI.CheckpointTaskReque
AllowTerminal: opts.Terminal, AllowTerminal: opts.Terminal,
FileLocks: opts.FileLocks, FileLocks: opts.FileLocks,
EmptyNamespaces: opts.EmptyNamespaces, EmptyNamespaces: opts.EmptyNamespaces,
WorkDir: opts.WorkPath,
}); err != nil { }); err != nil {
return nil, errdefs.ToGRPC(err) return nil, errdefs.ToGRPC(err)
} }
@ -806,5 +807,11 @@ func newInit(ctx context.Context, path, workDir, namespace string, platform rpro
p.IoGID = int(options.IoGid) p.IoGID = int(options.IoGid)
p.NoPivotRoot = options.NoPivotRoot p.NoPivotRoot = options.NoPivotRoot
p.NoNewKeyring = options.NoNewKeyring p.NoNewKeyring = options.NoNewKeyring
p.CriuWorkPath = options.CriuWorkPath
if p.CriuWorkPath == "" {
// if criu work path not set, use container WorkDir
p.CriuWorkPath = p.WorkDir
}
return p, nil return p, nil
} }

View File

@ -41,7 +41,9 @@ import (
"github.com/containerd/containerd/mount" "github.com/containerd/containerd/mount"
"github.com/containerd/containerd/plugin" "github.com/containerd/containerd/plugin"
"github.com/containerd/containerd/runtime" "github.com/containerd/containerd/runtime"
"github.com/containerd/containerd/runtime/linux/runctypes"
"github.com/containerd/containerd/runtime/v2" "github.com/containerd/containerd/runtime/v2"
"github.com/containerd/containerd/runtime/v2/runc/options"
"github.com/containerd/containerd/services" "github.com/containerd/containerd/services"
"github.com/containerd/typeurl" "github.com/containerd/typeurl"
ptypes "github.com/gogo/protobuf/types" ptypes "github.com/gogo/protobuf/types"
@ -123,11 +125,16 @@ type local struct {
} }
func (l *local) Create(ctx context.Context, r *api.CreateTaskRequest, _ ...grpc.CallOption) (*api.CreateTaskResponse, error) { func (l *local) Create(ctx context.Context, r *api.CreateTaskRequest, _ ...grpc.CallOption) (*api.CreateTaskResponse, error) {
var ( container, err := l.getContainer(ctx, r.ContainerID)
checkpointPath string if err != nil {
err error return nil, errdefs.ToGRPC(err)
) }
if r.Checkpoint != nil { checkpointPath, err := getRestorePath(container.Runtime.Name, r.Options)
if err != nil {
return nil, err
}
// jump get checkpointPath from checkpoint image
if checkpointPath != "" && r.Checkpoint != nil {
checkpointPath, err = ioutil.TempDir(os.Getenv("XDG_RUNTIME_DIR"), "ctrd-checkpoint") checkpointPath, err = ioutil.TempDir(os.Getenv("XDG_RUNTIME_DIR"), "ctrd-checkpoint")
if err != nil { if err != nil {
return nil, err return nil, err
@ -149,10 +156,6 @@ func (l *local) Create(ctx context.Context, r *api.CreateTaskRequest, _ ...grpc.
return nil, err return nil, err
} }
} }
container, err := l.getContainer(ctx, r.ContainerID)
if err != nil {
return nil, errdefs.ToGRPC(err)
}
opts := runtime.CreateOpts{ opts := runtime.CreateOpts{
Spec: container.Spec, Spec: container.Spec,
IO: runtime.IO{ IO: runtime.IO{
@ -478,14 +481,27 @@ func (l *local) Checkpoint(ctx context.Context, r *api.CheckpointTaskRequest, _
if err != nil { if err != nil {
return nil, err return nil, err
} }
image, err := ioutil.TempDir(os.Getenv("XDG_RUNTIME_DIR"), "ctd-checkpoint") image, err := getCheckpointPath(container.Runtime.Name, r.Options)
if err != nil { if err != nil {
return nil, errdefs.ToGRPC(err) return nil, err
}
checkpointImageExists := false
if image == "" {
checkpointImageExists = true
image, err = ioutil.TempDir(os.Getenv("XDG_RUNTIME_DIR"), "ctd-checkpoint")
if err != nil {
return nil, errdefs.ToGRPC(err)
}
defer os.RemoveAll(image)
} }
defer os.RemoveAll(image)
if err := t.Checkpoint(ctx, image, r.Options); err != nil { if err := t.Checkpoint(ctx, image, r.Options); err != nil {
return nil, errdefs.ToGRPC(err) return nil, errdefs.ToGRPC(err)
} }
// do not commit checkpoint image if checkpoint ImagePath is passed,
// return if checkpointImageExists is false
if !checkpointImageExists {
return &api.CheckpointTaskResponse{}, nil
}
// write checkpoint to the content store // write checkpoint to the content store
tar := archive.Diff(ctx, "", image) tar := archive.Diff(ctx, "", image)
cp, err := l.writeContent(ctx, images.MediaTypeContainerd1Checkpoint, image, tar) cp, err := l.writeContent(ctx, images.MediaTypeContainerd1Checkpoint, image, tar)
@ -663,3 +679,71 @@ func (l *local) allRuntimes() (o []runtime.PlatformRuntime) {
o = append(o, l.v2Runtime) o = append(o, l.v2Runtime)
return o return o
} }
// getCheckpointPath only suitable for runc runtime now
func getCheckpointPath(runtime string, option *ptypes.Any) (string, error) {
if option == nil {
return "", nil
}
var checkpointPath string
switch runtime {
case "io.containerd.runc.v1":
v, err := typeurl.UnmarshalAny(option)
if err != nil {
return "", err
}
opts, ok := v.(*options.CheckpointOptions)
if !ok {
return "", fmt.Errorf("invalid task checkpoint option for %s", runtime)
}
checkpointPath = opts.ImagePath
case "io.containerd.runtime.v1.linux":
v, err := typeurl.UnmarshalAny(option)
if err != nil {
return "", err
}
opts, ok := v.(*runctypes.CheckpointOptions)
if !ok {
return "", fmt.Errorf("invalid task checkpoint option for %s", runtime)
}
checkpointPath = opts.ImagePath
}
return checkpointPath, nil
}
// getRestorePath only suitable for runc runtime now
func getRestorePath(runtime string, option *ptypes.Any) (string, error) {
if option == nil {
return "", nil
}
var restorePath string
switch runtime {
case "io.containerd.runc.v1":
v, err := typeurl.UnmarshalAny(option)
if err != nil {
return "", err
}
opts, ok := v.(*options.Options)
if !ok {
return "", fmt.Errorf("invalid task create option for %s", runtime)
}
restorePath = opts.CriuImagePath
case "io.containerd.runtime.v1.linux":
v, err := typeurl.UnmarshalAny(option)
if err != nil {
return "", err
}
opts, ok := v.(*runctypes.CreateOptions)
if !ok {
return "", fmt.Errorf("invalid task create option for %s", runtime)
}
restorePath = opts.CriuImagePath
}
return restorePath, nil
}

34
task.go
View File

@ -37,6 +37,8 @@ import (
"github.com/containerd/containerd/mount" "github.com/containerd/containerd/mount"
"github.com/containerd/containerd/plugin" "github.com/containerd/containerd/plugin"
"github.com/containerd/containerd/rootfs" "github.com/containerd/containerd/rootfs"
"github.com/containerd/containerd/runtime/linux/runctypes"
"github.com/containerd/containerd/runtime/v2/runc/options"
"github.com/containerd/typeurl" "github.com/containerd/typeurl"
google_protobuf "github.com/gogo/protobuf/types" google_protobuf "github.com/gogo/protobuf/types"
digest "github.com/opencontainers/go-digest" digest "github.com/opencontainers/go-digest"
@ -147,6 +149,8 @@ type Task interface {
// OCI Index that can be push and pulled from a remote resource. // OCI Index that can be push and pulled from a remote resource.
// //
// Additional software like CRIU maybe required to checkpoint and restore tasks // Additional software like CRIU maybe required to checkpoint and restore tasks
// NOTE: Checkpoint supports to dump task information to a directory, in this way,
// an empty OCI Index will be returned.
Checkpoint(context.Context, ...CheckpointTaskOpts) (Image, error) Checkpoint(context.Context, ...CheckpointTaskOpts) (Image, error)
// Update modifies executing tasks with updated settings // Update modifies executing tasks with updated settings
Update(context.Context, ...UpdateTaskOpts) error Update(context.Context, ...UpdateTaskOpts) error
@ -387,6 +391,8 @@ func (t *task) Resize(ctx context.Context, w, h uint32) error {
return errdefs.FromGRPC(err) return errdefs.FromGRPC(err)
} }
// NOTE: Checkpoint supports to dump task information to a directory, in this way, an empty
// OCI Index will be returned.
func (t *task) Checkpoint(ctx context.Context, opts ...CheckpointTaskOpts) (Image, error) { func (t *task) Checkpoint(ctx context.Context, opts ...CheckpointTaskOpts) (Image, error) {
ctx, done, err := t.client.WithLease(ctx) ctx, done, err := t.client.WithLease(ctx)
if err != nil { if err != nil {
@ -433,6 +439,12 @@ func (t *task) Checkpoint(ctx context.Context, opts ...CheckpointTaskOpts) (Imag
if err := t.checkpointTask(ctx, &index, request); err != nil { if err := t.checkpointTask(ctx, &index, request); err != nil {
return nil, err return nil, err
} }
// if checkpoint image path passed, jump checkpoint image,
// return an empty image
if isCheckpointPathExist(cr.Runtime.Name, i.Options) {
return NewImage(t.client, images.Image{}), nil
}
if cr.Image != "" { if cr.Image != "" {
if err := t.checkpointImage(ctx, &index, cr.Image); err != nil { if err := t.checkpointImage(ctx, &index, cr.Image); err != nil {
return nil, err return nil, err
@ -542,6 +554,7 @@ func (t *task) checkpointTask(ctx context.Context, index *v1.Index, request *tas
if err != nil { if err != nil {
return errdefs.FromGRPC(err) return errdefs.FromGRPC(err)
} }
// NOTE: response.Descriptors can be an empty slice if checkpoint image is jumped
// add the checkpoint descriptors to the index // add the checkpoint descriptors to the index
for _, d := range response.Descriptors { for _, d := range response.Descriptors {
index.Manifests = append(index.Manifests, v1.Descriptor{ index.Manifests = append(index.Manifests, v1.Descriptor{
@ -619,3 +632,24 @@ func writeContent(ctx context.Context, store content.Ingester, mediaType, ref st
Size: size, Size: size,
}, nil }, nil
} }
// isCheckpointPathExist only suitable for runc runtime now
func isCheckpointPathExist(runtime string, v interface{}) bool {
if v == nil {
return false
}
switch runtime {
case "io.containerd.runc.v1":
if opts, ok := v.(*options.CheckpointOptions); ok && opts.ImagePath != "" {
return true
}
case "io.containerd.runtime.v1.linux":
if opts, ok := v.(*runctypes.CheckpointOptions); ok && opts.ImagePath != "" {
return true
}
}
return false
}

View File

@ -27,11 +27,18 @@ import (
"github.com/containerd/containerd/errdefs" "github.com/containerd/containerd/errdefs"
"github.com/containerd/containerd/images" "github.com/containerd/containerd/images"
"github.com/containerd/containerd/mount" "github.com/containerd/containerd/mount"
"github.com/containerd/containerd/runtime/linux/runctypes"
"github.com/containerd/containerd/runtime/v2/runc/options"
imagespec "github.com/opencontainers/image-spec/specs-go/v1" imagespec "github.com/opencontainers/image-spec/specs-go/v1"
"github.com/opencontainers/runtime-spec/specs-go" "github.com/opencontainers/runtime-spec/specs-go"
"github.com/pkg/errors" "github.com/pkg/errors"
) )
const (
v1runtime = "io.containerd.runtime.v1.linux"
v2runtime = "io.containerd.runc.v1"
)
// NewTaskOpts allows the caller to set options on a new task // NewTaskOpts allows the caller to set options on a new task
type NewTaskOpts func(context.Context, *Client, *TaskInfo) error type NewTaskOpts func(context.Context, *Client, *TaskInfo) error
@ -89,6 +96,60 @@ func WithCheckpointName(name string) CheckpointTaskOpts {
} }
} }
// WithCheckpointImagePath sets image path for checkpoint option
func WithCheckpointImagePath(rt, path string) CheckpointTaskOpts {
return func(r *CheckpointTaskInfo) error {
switch rt {
case v1runtime:
if r.Options == nil {
r.Options = &runctypes.CheckpointOptions{}
}
opts, ok := r.Options.(*runctypes.CheckpointOptions)
if !ok {
return errors.New("invalid v1 checkpoint options format")
}
opts.ImagePath = path
case v2runtime:
if r.Options == nil {
r.Options = &options.CheckpointOptions{}
}
opts, ok := r.Options.(*options.CheckpointOptions)
if !ok {
return errors.New("invalid v2 checkpoint options format")
}
opts.ImagePath = path
}
return nil
}
}
// WithRestoreImagePath sets image path for create option
func WithRestoreImagePath(rt, path string) NewTaskOpts {
return func(ctx context.Context, c *Client, ti *TaskInfo) error {
switch rt {
case v1runtime:
if ti.Options == nil {
ti.Options = &runctypes.CreateOptions{}
}
opts, ok := ti.Options.(*runctypes.CreateOptions)
if !ok {
return errors.New("invalid v1 create options format")
}
opts.CriuImagePath = path
case v2runtime:
if ti.Options == nil {
ti.Options = &options.Options{}
}
opts, ok := ti.Options.(*options.Options)
if !ok {
return errors.New("invalid v2 create options format")
}
opts.CriuImagePath = path
}
return nil
}
}
// ProcessDeleteOpts allows the caller to set options for the deletion of a task // ProcessDeleteOpts allows the caller to set options for the deletion of a task
type ProcessDeleteOpts func(context.Context, Process) error type ProcessDeleteOpts func(context.Context, Process) error