From 40caece8dc9426eef146c24b8c03d77c78af832c Mon Sep 17 00:00:00 2001 From: Evan Hazlett Date: Thu, 20 Sep 2018 21:52:47 -0400 Subject: [PATCH] update tests Signed-off-by: Evan Hazlett --- cmd/ctr/commands/commands.go | 4 -- cmd/ctr/commands/run/run_unix.go | 8 --- container_checkpoint_opts.go | 6 ++ container_checkpoint_test.go | 104 +++++++++++++++++++++++-------- container_opts_unix.go | 69 -------------------- container_restore_opts.go | 2 +- 6 files changed, 84 insertions(+), 109 deletions(-) diff --git a/cmd/ctr/commands/commands.go b/cmd/ctr/commands/commands.go index cba982454..7c3d95998 100644 --- a/cmd/ctr/commands/commands.go +++ b/cmd/ctr/commands/commands.go @@ -71,10 +71,6 @@ var ( Name: "config,c", Usage: "path to the runtime-specific spec config file", }, - cli.StringFlag{ - Name: "checkpoint", - Usage: "provide the checkpoint digest to restore the container", - }, cli.StringFlag{ Name: "cwd", Usage: "specify the working directory of the process", diff --git a/cmd/ctr/commands/run/run_unix.go b/cmd/ctr/commands/run/run_unix.go index 11c5f2f50..3a0f813c5 100644 --- a/cmd/ctr/commands/run/run_unix.go +++ b/cmd/ctr/commands/run/run_unix.go @@ -44,14 +44,6 @@ func NewContainer(ctx gocontext.Context, client *containerd.Client, context *cli id = context.Args().Get(1) } - if raw := context.String("checkpoint"); raw != "" { - im, err := client.GetImage(ctx, raw) - if err != nil { - return nil, err - } - return client.NewContainer(ctx, id, containerd.WithCheckpoint(im, id), containerd.WithRuntime(context.String("runtime"), nil)) - } - var ( opts []oci.SpecOpts cOpts []containerd.NewContainerOpts diff --git a/container_checkpoint_opts.go b/container_checkpoint_opts.go index 3f78a4f12..f336fb219 100644 --- a/container_checkpoint_opts.go +++ b/container_checkpoint_opts.go @@ -124,6 +124,12 @@ func WithCheckpointRW(ctx context.Context, client *Client, c *containers.Contain return nil } +// WithCheckpointTaskExit causes the task to exit after checkpoint +func WithCheckpointTaskExit(ctx context.Context, client *Client, c *containers.Container, index *imagespec.Index, copts *options.CheckpointOptions) error { + copts.Exit = true + return nil +} + // GetIndexByMediaType returns the index in a manifest for the specified media type func GetIndexByMediaType(index *imagespec.Index, mt string) (*imagespec.Descriptor, error) { for _, d := range index.Manifests { diff --git a/container_checkpoint_test.go b/container_checkpoint_test.go index 695cfdac8..c4f0d7f37 100644 --- a/container_checkpoint_test.go +++ b/container_checkpoint_test.go @@ -28,8 +28,11 @@ import ( "testing" "github.com/containerd/containerd/oci" - "github.com/containerd/containerd/runtime/linux/runctypes" - "github.com/containerd/containerd/runtime/v2/runc/options" +) + +const ( + v1runtime = "io.containerd.runtime.v1.linux" + testCheckpointName = "checkpoint-test:latest" ) func TestCheckpointRestorePTY(t *testing.T) { @@ -41,6 +44,9 @@ func TestCheckpointRestorePTY(t *testing.T) { t.Fatal(err) } defer client.Close() + if client.runtime == v1runtime { + t.Skip() + } var ( ctx, cancel = testContext() @@ -56,7 +62,8 @@ func TestCheckpointRestorePTY(t *testing.T) { WithNewSnapshot(id, image), WithNewSpec(oci.WithImageConfig(image), oci.WithProcessArgs("sh", "-c", "read A; echo z${A}z"), - oci.WithTTY)) + oci.WithTTY), + ) if err != nil { t.Fatal(err) } @@ -83,7 +90,12 @@ func TestCheckpointRestorePTY(t *testing.T) { t.Fatal(err) } - checkpoint, err := task.Checkpoint(ctx, withExit(client)) + checkpoint, err := container.Checkpoint(ctx, testCheckpointName+"withpty", []CheckpointOpts{ + WithCheckpointRuntime, + WithCheckpointRW, + WithCheckpointTaskExit, + WithCheckpointTask, + }...) if err != nil { t.Fatal(err) } @@ -94,6 +106,10 @@ func TestCheckpointRestorePTY(t *testing.T) { t.Fatal(err) } direct.Delete() + if err := container.Delete(ctx, WithSnapshotCleanup); err != nil { + t.Fatal(err) + } + direct, err = newDirectIO(ctx, true) if err != nil { t.Fatal(err) @@ -109,6 +125,15 @@ func TestCheckpointRestorePTY(t *testing.T) { io.Copy(buf, direct.Stdout) }() + if container, err = client.Restore(ctx, id, checkpoint, []RestoreOpts{ + WithRestoreImage, + WithRestoreSpec, + WithRestoreSnapshot, + WithRestoreRuntime, + WithRestoreRW, + }...); err != nil { + t.Fatal(err) + } if task, err = container.NewTask(ctx, direct.IOCreate, WithTaskCheckpoint(checkpoint)); err != nil { t.Fatal(err) @@ -146,6 +171,9 @@ func TestCheckpointRestore(t *testing.T) { t.Fatal(err) } defer client.Close() + if client.runtime == v1runtime { + t.Skip() + } var ( ctx, cancel = testContext() @@ -157,7 +185,7 @@ func TestCheckpointRestore(t *testing.T) { if err != nil { t.Fatal(err) } - container, err := client.NewContainer(ctx, id, WithNewSnapshot(id, image), WithNewSpec(oci.WithImageConfig(image), oci.WithProcessArgs("sleep", "100"))) + container, err := client.NewContainer(ctx, id, WithNewSnapshot(id, image), WithNewSpec(oci.WithImageConfig(image), oci.WithProcessArgs("sleep", "10"))) if err != nil { t.Fatal(err) } @@ -178,7 +206,11 @@ func TestCheckpointRestore(t *testing.T) { t.Fatal(err) } - checkpoint, err := task.Checkpoint(ctx, withExit(client)) + checkpoint, err := container.Checkpoint(ctx, testCheckpointName+"restore", []CheckpointOpts{ + WithCheckpointRuntime, + WithCheckpointRW, + WithCheckpointTask, + }...) if err != nil { t.Fatal(err) } @@ -188,6 +220,19 @@ func TestCheckpointRestore(t *testing.T) { if _, err := task.Delete(ctx); err != nil { t.Fatal(err) } + if err := container.Delete(ctx, WithSnapshotCleanup); err != nil { + t.Fatal(err) + } + + if container, err = client.Restore(ctx, id, checkpoint, []RestoreOpts{ + WithRestoreImage, + WithRestoreSpec, + WithRestoreSnapshot, + WithRestoreRuntime, + WithRestoreRW, + }...); err != nil { + t.Fatal(err) + } if task, err = container.NewTask(ctx, empty(), WithTaskCheckpoint(checkpoint)); err != nil { t.Fatal(err) } @@ -217,6 +262,9 @@ func TestCheckpointRestoreNewContainer(t *testing.T) { t.Fatal(err) } defer client.Close() + if client.runtime == v1runtime { + t.Skip() + } id := t.Name() ctx, cancel := testContext() @@ -226,7 +274,7 @@ func TestCheckpointRestoreNewContainer(t *testing.T) { if err != nil { t.Fatal(err) } - container, err := client.NewContainer(ctx, id, WithNewSnapshot(id, image), WithNewSpec(oci.WithImageConfig(image), oci.WithProcessArgs("sleep", "100"))) + container, err := client.NewContainer(ctx, id, WithNewSnapshot(id, image), WithNewSpec(oci.WithImageConfig(image), oci.WithProcessArgs("sleep", "5"))) if err != nil { t.Fatal(err) } @@ -247,7 +295,11 @@ func TestCheckpointRestoreNewContainer(t *testing.T) { t.Fatal(err) } - checkpoint, err := task.Checkpoint(ctx, withExit(client)) + checkpoint, err := container.Checkpoint(ctx, testCheckpointName+"newcontainer", []CheckpointOpts{ + WithCheckpointRuntime, + WithCheckpointRW, + WithCheckpointTask, + }...) if err != nil { t.Fatal(err) } @@ -260,7 +312,13 @@ func TestCheckpointRestoreNewContainer(t *testing.T) { if err := container.Delete(ctx, WithSnapshotCleanup); err != nil { t.Fatal(err) } - if container, err = client.NewContainer(ctx, id, WithCheckpoint(checkpoint, id)); err != nil { + if container, err = client.Restore(ctx, id, checkpoint, []RestoreOpts{ + WithRestoreImage, + WithRestoreSpec, + WithRestoreSnapshot, + WithRestoreRuntime, + WithRestoreRW, + }...); err != nil { t.Fatal(err) } if task, err = container.NewTask(ctx, empty(), WithTaskCheckpoint(checkpoint)); err != nil { @@ -290,11 +348,14 @@ func TestCheckpointLeaveRunning(t *testing.T) { if !supportsCriu { t.Skip("system does not have criu installed") } - client, err := New(address) + client, err := newClient(t, address) if err != nil { t.Fatal(err) } defer client.Close() + if client.runtime == v1runtime { + t.Skip() + } var ( ctx, cancel = testContext() @@ -327,7 +388,12 @@ func TestCheckpointLeaveRunning(t *testing.T) { t.Fatal(err) } - if _, err := task.Checkpoint(ctx); err != nil { + // checkpoint + if _, err := container.Checkpoint(ctx, testCheckpointName+"leaverunning", []CheckpointOpts{ + WithCheckpointRuntime, + WithCheckpointRW, + WithCheckpointTask, + }...); err != nil { t.Fatal(err) } @@ -345,19 +411,3 @@ func TestCheckpointLeaveRunning(t *testing.T) { <-statusC } - -func withExit(client *Client) CheckpointTaskOpts { - return func(r *CheckpointTaskInfo) error { - switch client.runtime { - case "io.containerd.runc.v1": - r.Options = &options.CheckpointOptions{ - Exit: true, - } - default: - r.Options = &runctypes.CheckpointOptions{ - Exit: true, - } - } - return nil - } -} diff --git a/container_opts_unix.go b/container_opts_unix.go index c0622f67f..9e013f1a4 100644 --- a/container_opts_unix.go +++ b/container_opts_unix.go @@ -26,81 +26,12 @@ import ( "syscall" "github.com/containerd/containerd/containers" - "github.com/containerd/containerd/content" "github.com/containerd/containerd/errdefs" - "github.com/containerd/containerd/images" "github.com/containerd/containerd/mount" "github.com/containerd/containerd/platforms" - "github.com/gogo/protobuf/proto" - protobuf "github.com/gogo/protobuf/types" "github.com/opencontainers/image-spec/identity" - "github.com/opencontainers/image-spec/specs-go/v1" - "github.com/pkg/errors" ) -// WithCheckpoint allows a container to be created from the checkpointed information -// provided by the descriptor. The image, snapshot, and runtime specifications are -// restored on the container -func WithCheckpoint(im Image, snapshotKey string) NewContainerOpts { - // set image and rw, and spec - return func(ctx context.Context, client *Client, c *containers.Container) error { - var ( - desc = im.Target() - store = client.ContentStore() - ) - index, err := decodeIndex(ctx, store, desc) - if err != nil { - return err - } - var rw *v1.Descriptor - for _, m := range index.Manifests { - switch m.MediaType { - case v1.MediaTypeImageLayer: - fk := m - rw = &fk - case images.MediaTypeDockerSchema2Manifest, images.MediaTypeDockerSchema2ManifestList: - config, err := images.Config(ctx, store, m, platforms.Default()) - if err != nil { - return errors.Wrap(err, "unable to resolve image config") - } - diffIDs, err := images.RootFS(ctx, store, config) - if err != nil { - return errors.Wrap(err, "unable to get rootfs") - } - setSnapshotterIfEmpty(c) - if _, err := client.SnapshotService(c.Snapshotter).Prepare(ctx, snapshotKey, identity.ChainID(diffIDs).String()); err != nil { - if !errdefs.IsAlreadyExists(err) { - return err - } - } - c.Image = index.Annotations["image.name"] - case images.MediaTypeContainerd1CheckpointConfig: - data, err := content.ReadBlob(ctx, store, m) - if err != nil { - return errors.Wrap(err, "unable to read checkpoint config") - } - var any protobuf.Any - if err := proto.Unmarshal(data, &any); err != nil { - return err - } - c.Spec = &any - } - } - if rw != nil { - // apply the rw snapshot to the new rw layer - mounts, err := client.SnapshotService(c.Snapshotter).Mounts(ctx, snapshotKey) - if err != nil { - return errors.Wrapf(err, "unable to get mounts for %s", snapshotKey) - } - if _, err := client.DiffService().Apply(ctx, *rw, mounts); err != nil { - return errors.Wrap(err, "unable to apply rw diff") - } - } - c.SnapshotKey = snapshotKey - return nil - } -} - // WithRemappedSnapshot creates a new snapshot and remaps the uid/gid for the // filesystem to be used by a container with user namespaces func WithRemappedSnapshot(id string, i Image, uid, gid uint32) NewContainerOpts { diff --git a/container_restore_opts.go b/container_restore_opts.go index a6b41ff9d..bfb6effb0 100644 --- a/container_restore_opts.go +++ b/container_restore_opts.go @@ -138,7 +138,7 @@ func WithRestoreSnapshot(ctx context.Context, id string, client *Client, checkpo } } -// WithRestoreSnapshot restores the snapshot from the checkpoint for the container +// WithRestoreRW restores the rw layer from the checkpoint for the container func WithRestoreRW(ctx context.Context, id string, client *Client, checkpoint Image, index *imagespec.Index) NewContainerOpts { return func(ctx context.Context, client *Client, c *containers.Container) error { // apply rw layer