update tests
Signed-off-by: Evan Hazlett <ejhazlett@gmail.com>
This commit is contained in:
parent
4fdf720b84
commit
40caece8dc
@ -71,10 +71,6 @@ var (
|
|||||||
Name: "config,c",
|
Name: "config,c",
|
||||||
Usage: "path to the runtime-specific spec config file",
|
Usage: "path to the runtime-specific spec config file",
|
||||||
},
|
},
|
||||||
cli.StringFlag{
|
|
||||||
Name: "checkpoint",
|
|
||||||
Usage: "provide the checkpoint digest to restore the container",
|
|
||||||
},
|
|
||||||
cli.StringFlag{
|
cli.StringFlag{
|
||||||
Name: "cwd",
|
Name: "cwd",
|
||||||
Usage: "specify the working directory of the process",
|
Usage: "specify the working directory of the process",
|
||||||
|
@ -44,14 +44,6 @@ func NewContainer(ctx gocontext.Context, client *containerd.Client, context *cli
|
|||||||
id = context.Args().Get(1)
|
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 (
|
var (
|
||||||
opts []oci.SpecOpts
|
opts []oci.SpecOpts
|
||||||
cOpts []containerd.NewContainerOpts
|
cOpts []containerd.NewContainerOpts
|
||||||
|
@ -124,6 +124,12 @@ func WithCheckpointRW(ctx context.Context, client *Client, c *containers.Contain
|
|||||||
return nil
|
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
|
// GetIndexByMediaType returns the index in a manifest for the specified media type
|
||||||
func GetIndexByMediaType(index *imagespec.Index, mt string) (*imagespec.Descriptor, error) {
|
func GetIndexByMediaType(index *imagespec.Index, mt string) (*imagespec.Descriptor, error) {
|
||||||
for _, d := range index.Manifests {
|
for _, d := range index.Manifests {
|
||||||
|
@ -28,8 +28,11 @@ import (
|
|||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/containerd/containerd/oci"
|
"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) {
|
func TestCheckpointRestorePTY(t *testing.T) {
|
||||||
@ -41,6 +44,9 @@ func TestCheckpointRestorePTY(t *testing.T) {
|
|||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
defer client.Close()
|
defer client.Close()
|
||||||
|
if client.runtime == v1runtime {
|
||||||
|
t.Skip()
|
||||||
|
}
|
||||||
|
|
||||||
var (
|
var (
|
||||||
ctx, cancel = testContext()
|
ctx, cancel = testContext()
|
||||||
@ -56,7 +62,8 @@ func TestCheckpointRestorePTY(t *testing.T) {
|
|||||||
WithNewSnapshot(id, image),
|
WithNewSnapshot(id, image),
|
||||||
WithNewSpec(oci.WithImageConfig(image),
|
WithNewSpec(oci.WithImageConfig(image),
|
||||||
oci.WithProcessArgs("sh", "-c", "read A; echo z${A}z"),
|
oci.WithProcessArgs("sh", "-c", "read A; echo z${A}z"),
|
||||||
oci.WithTTY))
|
oci.WithTTY),
|
||||||
|
)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
@ -83,7 +90,12 @@ func TestCheckpointRestorePTY(t *testing.T) {
|
|||||||
t.Fatal(err)
|
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 {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
@ -94,6 +106,10 @@ func TestCheckpointRestorePTY(t *testing.T) {
|
|||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
direct.Delete()
|
direct.Delete()
|
||||||
|
if err := container.Delete(ctx, WithSnapshotCleanup); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
direct, err = newDirectIO(ctx, true)
|
direct, err = newDirectIO(ctx, true)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
@ -109,6 +125,15 @@ func TestCheckpointRestorePTY(t *testing.T) {
|
|||||||
io.Copy(buf, direct.Stdout)
|
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,
|
if task, err = container.NewTask(ctx, direct.IOCreate,
|
||||||
WithTaskCheckpoint(checkpoint)); err != nil {
|
WithTaskCheckpoint(checkpoint)); err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
@ -146,6 +171,9 @@ func TestCheckpointRestore(t *testing.T) {
|
|||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
defer client.Close()
|
defer client.Close()
|
||||||
|
if client.runtime == v1runtime {
|
||||||
|
t.Skip()
|
||||||
|
}
|
||||||
|
|
||||||
var (
|
var (
|
||||||
ctx, cancel = testContext()
|
ctx, cancel = testContext()
|
||||||
@ -157,7 +185,7 @@ func TestCheckpointRestore(t *testing.T) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
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 {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
@ -178,7 +206,11 @@ func TestCheckpointRestore(t *testing.T) {
|
|||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
checkpoint, err := task.Checkpoint(ctx, withExit(client))
|
checkpoint, err := container.Checkpoint(ctx, testCheckpointName+"restore", []CheckpointOpts{
|
||||||
|
WithCheckpointRuntime,
|
||||||
|
WithCheckpointRW,
|
||||||
|
WithCheckpointTask,
|
||||||
|
}...)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
@ -188,6 +220,19 @@ func TestCheckpointRestore(t *testing.T) {
|
|||||||
if _, err := task.Delete(ctx); err != nil {
|
if _, err := task.Delete(ctx); err != nil {
|
||||||
t.Fatal(err)
|
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 {
|
if task, err = container.NewTask(ctx, empty(), WithTaskCheckpoint(checkpoint)); err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
@ -217,6 +262,9 @@ func TestCheckpointRestoreNewContainer(t *testing.T) {
|
|||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
defer client.Close()
|
defer client.Close()
|
||||||
|
if client.runtime == v1runtime {
|
||||||
|
t.Skip()
|
||||||
|
}
|
||||||
|
|
||||||
id := t.Name()
|
id := t.Name()
|
||||||
ctx, cancel := testContext()
|
ctx, cancel := testContext()
|
||||||
@ -226,7 +274,7 @@ func TestCheckpointRestoreNewContainer(t *testing.T) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
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 {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
@ -247,7 +295,11 @@ func TestCheckpointRestoreNewContainer(t *testing.T) {
|
|||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
checkpoint, err := task.Checkpoint(ctx, withExit(client))
|
checkpoint, err := container.Checkpoint(ctx, testCheckpointName+"newcontainer", []CheckpointOpts{
|
||||||
|
WithCheckpointRuntime,
|
||||||
|
WithCheckpointRW,
|
||||||
|
WithCheckpointTask,
|
||||||
|
}...)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
@ -260,7 +312,13 @@ func TestCheckpointRestoreNewContainer(t *testing.T) {
|
|||||||
if err := container.Delete(ctx, WithSnapshotCleanup); err != nil {
|
if err := container.Delete(ctx, WithSnapshotCleanup); err != nil {
|
||||||
t.Fatal(err)
|
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)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
if task, err = container.NewTask(ctx, empty(), WithTaskCheckpoint(checkpoint)); err != nil {
|
if task, err = container.NewTask(ctx, empty(), WithTaskCheckpoint(checkpoint)); err != nil {
|
||||||
@ -290,11 +348,14 @@ func TestCheckpointLeaveRunning(t *testing.T) {
|
|||||||
if !supportsCriu {
|
if !supportsCriu {
|
||||||
t.Skip("system does not have criu installed")
|
t.Skip("system does not have criu installed")
|
||||||
}
|
}
|
||||||
client, err := New(address)
|
client, err := newClient(t, address)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
defer client.Close()
|
defer client.Close()
|
||||||
|
if client.runtime == v1runtime {
|
||||||
|
t.Skip()
|
||||||
|
}
|
||||||
|
|
||||||
var (
|
var (
|
||||||
ctx, cancel = testContext()
|
ctx, cancel = testContext()
|
||||||
@ -327,7 +388,12 @@ func TestCheckpointLeaveRunning(t *testing.T) {
|
|||||||
t.Fatal(err)
|
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)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -345,19 +411,3 @@ func TestCheckpointLeaveRunning(t *testing.T) {
|
|||||||
|
|
||||||
<-statusC
|
<-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
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
@ -26,81 +26,12 @@ import (
|
|||||||
"syscall"
|
"syscall"
|
||||||
|
|
||||||
"github.com/containerd/containerd/containers"
|
"github.com/containerd/containerd/containers"
|
||||||
"github.com/containerd/containerd/content"
|
|
||||||
"github.com/containerd/containerd/errdefs"
|
"github.com/containerd/containerd/errdefs"
|
||||||
"github.com/containerd/containerd/images"
|
|
||||||
"github.com/containerd/containerd/mount"
|
"github.com/containerd/containerd/mount"
|
||||||
"github.com/containerd/containerd/platforms"
|
"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/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
|
// WithRemappedSnapshot creates a new snapshot and remaps the uid/gid for the
|
||||||
// filesystem to be used by a container with user namespaces
|
// filesystem to be used by a container with user namespaces
|
||||||
func WithRemappedSnapshot(id string, i Image, uid, gid uint32) NewContainerOpts {
|
func WithRemappedSnapshot(id string, i Image, uid, gid uint32) NewContainerOpts {
|
||||||
|
@ -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 {
|
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 {
|
return func(ctx context.Context, client *Client, c *containers.Container) error {
|
||||||
// apply rw layer
|
// apply rw layer
|
||||||
|
Loading…
Reference in New Issue
Block a user