test: add test for c/r without image
add test for both v1, v2 runtime Signed-off-by: Ace-Tang <aceapril@126.com>
This commit is contained in:
parent
6593399e9f
commit
e20ba5fa51
@ -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)
|
||||||
|
}
|
||||||
|
61
task_opts.go
61
task_opts.go
@ -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
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user