checkpoint: add copts to checkpoint; save snapshotter to annotation
Signed-off-by: Evan Hazlett <ejhazlett@gmail.com>
This commit is contained in:
parent
40caece8dc
commit
6f2f4e4343
@ -18,7 +18,6 @@ package containers
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"errors"
|
|
||||||
"fmt"
|
"fmt"
|
||||||
"os"
|
"os"
|
||||||
"strings"
|
"strings"
|
||||||
@ -32,6 +31,7 @@ import (
|
|||||||
"github.com/containerd/containerd/errdefs"
|
"github.com/containerd/containerd/errdefs"
|
||||||
"github.com/containerd/containerd/log"
|
"github.com/containerd/containerd/log"
|
||||||
"github.com/containerd/typeurl"
|
"github.com/containerd/typeurl"
|
||||||
|
"github.com/pkg/errors"
|
||||||
"github.com/urfave/cli"
|
"github.com/urfave/cli"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -309,7 +309,7 @@ var checkpointCommand = cli.Command{
|
|||||||
if id == "" {
|
if id == "" {
|
||||||
return errors.New("container id must be provided")
|
return errors.New("container id must be provided")
|
||||||
}
|
}
|
||||||
ref := context.Args()[1]
|
ref := context.Args().Get(1)
|
||||||
if ref == "" {
|
if ref == "" {
|
||||||
return errors.New("ref must be provided")
|
return errors.New("ref must be provided")
|
||||||
}
|
}
|
||||||
@ -346,7 +346,11 @@ var checkpointCommand = cli.Command{
|
|||||||
if err := task.Pause(ctx); err != nil {
|
if err := task.Pause(ctx); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
defer task.Resume(ctx)
|
defer func() {
|
||||||
|
if err := task.Resume(ctx); err != nil {
|
||||||
|
fmt.Println(errors.Wrap(err, "error resuming task"))
|
||||||
|
}
|
||||||
|
}()
|
||||||
}
|
}
|
||||||
|
|
||||||
if _, err := container.Checkpoint(ctx, ref, opts...); err != nil {
|
if _, err := container.Checkpoint(ctx, ref, opts...); err != nil {
|
||||||
@ -362,21 +366,21 @@ var restoreCommand = cli.Command{
|
|||||||
Usage: "restore a container from checkpoint",
|
Usage: "restore a container from checkpoint",
|
||||||
ArgsUsage: "CONTAINER REF",
|
ArgsUsage: "CONTAINER REF",
|
||||||
Flags: []cli.Flag{
|
Flags: []cli.Flag{
|
||||||
cli.BoolFlag{
|
|
||||||
Name: "live",
|
|
||||||
Usage: "restore the runtime and memory data from the checkpoint",
|
|
||||||
},
|
|
||||||
cli.BoolFlag{
|
cli.BoolFlag{
|
||||||
Name: "rw",
|
Name: "rw",
|
||||||
Usage: "restore the rw layer from the checkpoint",
|
Usage: "restore the rw layer from the checkpoint",
|
||||||
},
|
},
|
||||||
|
cli.BoolFlag{
|
||||||
|
Name: "live",
|
||||||
|
Usage: "restore the runtime and memory data from the checkpoint",
|
||||||
|
},
|
||||||
},
|
},
|
||||||
Action: func(context *cli.Context) error {
|
Action: func(context *cli.Context) error {
|
||||||
id := context.Args().First()
|
id := context.Args().First()
|
||||||
if id == "" {
|
if id == "" {
|
||||||
return errors.New("container id must be provided")
|
return errors.New("container id must be provided")
|
||||||
}
|
}
|
||||||
ref := context.Args()[1]
|
ref := context.Args().Get(1)
|
||||||
if ref == "" {
|
if ref == "" {
|
||||||
return errors.New("ref must be provided")
|
return errors.New("ref must be provided")
|
||||||
}
|
}
|
||||||
@ -391,6 +395,7 @@ var restoreCommand = cli.Command{
|
|||||||
if !errdefs.IsNotFound(err) {
|
if !errdefs.IsNotFound(err) {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
// TODO (ehazlett): consider other options (always/never fetch)
|
||||||
ck, err := client.Fetch(ctx, ref)
|
ck, err := client.Fetch(ctx, ref)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
@ -401,7 +406,6 @@ var restoreCommand = cli.Command{
|
|||||||
opts := []containerd.RestoreOpts{
|
opts := []containerd.RestoreOpts{
|
||||||
containerd.WithRestoreImage,
|
containerd.WithRestoreImage,
|
||||||
containerd.WithRestoreSpec,
|
containerd.WithRestoreSpec,
|
||||||
containerd.WithRestoreSnapshot,
|
|
||||||
containerd.WithRestoreRuntime,
|
containerd.WithRestoreRuntime,
|
||||||
}
|
}
|
||||||
if context.Bool("rw") {
|
if context.Bool("rw") {
|
||||||
|
@ -39,8 +39,9 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
checkpointImageNameLabel = "image.name"
|
checkpointImageNameLabel = "org.opencontainers.image.ref.name"
|
||||||
checkpointRuntimeNameLabel = "runtime.name"
|
checkpointRuntimeNameLabel = "io.containerd.checkpoint.runtime"
|
||||||
|
checkpointSnapshotterNameLabel = "io.containerd.checkpoint.snapshotter"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Container is a metadata object for container resources and task creation
|
// Container is a metadata object for container resources and task creation
|
||||||
@ -318,6 +319,8 @@ func (c *container) Checkpoint(ctx context.Context, ref string, opts ...Checkpoi
|
|||||||
index.Annotations[checkpointImageNameLabel] = img.Name()
|
index.Annotations[checkpointImageNameLabel] = img.Name()
|
||||||
// add runtime info to index
|
// add runtime info to index
|
||||||
index.Annotations[checkpointRuntimeNameLabel] = info.Runtime.Name
|
index.Annotations[checkpointRuntimeNameLabel] = info.Runtime.Name
|
||||||
|
// add snapshotter info to index
|
||||||
|
index.Annotations[checkpointSnapshotterNameLabel] = info.Snapshotter
|
||||||
|
|
||||||
// process remaining opts
|
// process remaining opts
|
||||||
for _, o := range opts {
|
for _, o := range opts {
|
||||||
|
@ -26,6 +26,7 @@ import (
|
|||||||
"github.com/containerd/containerd/containers"
|
"github.com/containerd/containerd/containers"
|
||||||
"github.com/containerd/containerd/diff"
|
"github.com/containerd/containerd/diff"
|
||||||
"github.com/containerd/containerd/images"
|
"github.com/containerd/containerd/images"
|
||||||
|
"github.com/containerd/containerd/platforms"
|
||||||
"github.com/containerd/containerd/rootfs"
|
"github.com/containerd/containerd/rootfs"
|
||||||
"github.com/containerd/containerd/runtime/v2/runc/options"
|
"github.com/containerd/containerd/runtime/v2/runc/options"
|
||||||
"github.com/containerd/typeurl"
|
"github.com/containerd/typeurl"
|
||||||
@ -67,16 +68,29 @@ func WithCheckpointTask(ctx context.Context, client *Client, c *containers.Conta
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
for _, d := range task.Descriptors {
|
for _, d := range task.Descriptors {
|
||||||
|
platformSpec := platforms.DefaultSpec()
|
||||||
index.Manifests = append(index.Manifests, imagespec.Descriptor{
|
index.Manifests = append(index.Manifests, imagespec.Descriptor{
|
||||||
MediaType: d.MediaType,
|
MediaType: d.MediaType,
|
||||||
Size: d.Size_,
|
Size: d.Size_,
|
||||||
Digest: d.Digest,
|
Digest: d.Digest,
|
||||||
Platform: &imagespec.Platform{
|
Platform: &platformSpec,
|
||||||
OS: runtime.GOOS,
|
|
||||||
Architecture: runtime.GOARCH,
|
|
||||||
},
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
// save copts
|
||||||
|
data, err := any.Marshal()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
r := bytes.NewReader(data)
|
||||||
|
desc, err := writeContent(ctx, client.ContentStore(), images.MediaTypeContainerd1CheckpointOptions, c.ID+"-checkpoint-options", r)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
desc.Platform = &imagespec.Platform{
|
||||||
|
OS: runtime.GOOS,
|
||||||
|
Architecture: runtime.GOARCH,
|
||||||
|
}
|
||||||
|
index.Manifests = append(index.Manifests, desc)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -128,7 +128,6 @@ func TestCheckpointRestorePTY(t *testing.T) {
|
|||||||
if container, err = client.Restore(ctx, id, checkpoint, []RestoreOpts{
|
if container, err = client.Restore(ctx, id, checkpoint, []RestoreOpts{
|
||||||
WithRestoreImage,
|
WithRestoreImage,
|
||||||
WithRestoreSpec,
|
WithRestoreSpec,
|
||||||
WithRestoreSnapshot,
|
|
||||||
WithRestoreRuntime,
|
WithRestoreRuntime,
|
||||||
WithRestoreRW,
|
WithRestoreRW,
|
||||||
}...); err != nil {
|
}...); err != nil {
|
||||||
@ -227,7 +226,6 @@ func TestCheckpointRestore(t *testing.T) {
|
|||||||
if container, err = client.Restore(ctx, id, checkpoint, []RestoreOpts{
|
if container, err = client.Restore(ctx, id, checkpoint, []RestoreOpts{
|
||||||
WithRestoreImage,
|
WithRestoreImage,
|
||||||
WithRestoreSpec,
|
WithRestoreSpec,
|
||||||
WithRestoreSnapshot,
|
|
||||||
WithRestoreRuntime,
|
WithRestoreRuntime,
|
||||||
WithRestoreRW,
|
WithRestoreRW,
|
||||||
}...); err != nil {
|
}...); err != nil {
|
||||||
@ -315,7 +313,6 @@ func TestCheckpointRestoreNewContainer(t *testing.T) {
|
|||||||
if container, err = client.Restore(ctx, id, checkpoint, []RestoreOpts{
|
if container, err = client.Restore(ctx, id, checkpoint, []RestoreOpts{
|
||||||
WithRestoreImage,
|
WithRestoreImage,
|
||||||
WithRestoreSpec,
|
WithRestoreSpec,
|
||||||
WithRestoreSnapshot,
|
|
||||||
WithRestoreRuntime,
|
WithRestoreRuntime,
|
||||||
WithRestoreRW,
|
WithRestoreRW,
|
||||||
}...); err != nil {
|
}...); err != nil {
|
||||||
|
@ -33,8 +33,10 @@ import (
|
|||||||
var (
|
var (
|
||||||
// ErrImageNameNotFoundInIndex is returned when the image name is not found in the index
|
// ErrImageNameNotFoundInIndex is returned when the image name is not found in the index
|
||||||
ErrImageNameNotFoundInIndex = errors.New("image name not found in index")
|
ErrImageNameNotFoundInIndex = errors.New("image name not found in index")
|
||||||
// ErrRuntimeNameNotFoundInIndex is returned when the runtime name is not found in the index
|
// ErrRuntimeNameNotFoundInIndex is returned when the runtime is not found in the index
|
||||||
ErrRuntimeNameNotFoundInIndex = errors.New("runtime name not found in index")
|
ErrRuntimeNameNotFoundInIndex = errors.New("runtime not found in index")
|
||||||
|
// ErrSnapshotterNameNotFoundInIndex is returned when the snapshotter is not found in the index
|
||||||
|
ErrSnapshotterNameNotFoundInIndex = errors.New("snapshotter not found in index")
|
||||||
)
|
)
|
||||||
|
|
||||||
// RestoreOpts are options to manage the restore operation
|
// RestoreOpts are options to manage the restore operation
|
||||||
@ -47,12 +49,26 @@ func WithRestoreImage(ctx context.Context, id string, client *Client, checkpoint
|
|||||||
if !ok || name == "" {
|
if !ok || name == "" {
|
||||||
return ErrRuntimeNameNotFoundInIndex
|
return ErrRuntimeNameNotFoundInIndex
|
||||||
}
|
}
|
||||||
|
snapshotter, ok := index.Annotations[checkpointSnapshotterNameLabel]
|
||||||
|
if !ok || name == "" {
|
||||||
|
return ErrSnapshotterNameNotFoundInIndex
|
||||||
|
}
|
||||||
i, err := client.GetImage(ctx, name)
|
i, err := client.GetImage(ctx, name)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
diffIDs, err := i.(*image).i.RootFS(ctx, client.ContentStore(), platforms.Default())
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
parent := identity.ChainID(diffIDs).String()
|
||||||
|
if _, err := client.SnapshotService(snapshotter).Prepare(ctx, id, parent); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
c.Image = i.Name()
|
c.Image = i.Name()
|
||||||
|
c.SnapshotKey = id
|
||||||
|
c.Snapshotter = snapshotter
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -113,31 +129,6 @@ func WithRestoreSpec(ctx context.Context, id string, client *Client, checkpoint
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// WithRestoreSnapshot restores the snapshot from the checkpoint for the container
|
|
||||||
func WithRestoreSnapshot(ctx context.Context, id string, client *Client, checkpoint Image, index *imagespec.Index) NewContainerOpts {
|
|
||||||
return func(ctx context.Context, client *Client, c *containers.Container) error {
|
|
||||||
imageName, ok := index.Annotations[checkpointImageNameLabel]
|
|
||||||
if !ok || imageName == "" {
|
|
||||||
return ErrRuntimeNameNotFoundInIndex
|
|
||||||
}
|
|
||||||
i, err := client.GetImage(ctx, imageName)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
diffIDs, err := i.(*image).i.RootFS(ctx, client.ContentStore(), platforms.Default())
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
parent := identity.ChainID(diffIDs).String()
|
|
||||||
if _, err := client.SnapshotService(DefaultSnapshotter).Prepare(ctx, id, parent); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
c.SnapshotKey = id
|
|
||||||
c.Snapshotter = DefaultSnapshotter
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// WithRestoreRW restores the rw layer 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 {
|
||||||
|
@ -34,7 +34,7 @@ const (
|
|||||||
MediaTypeContainerd1Resource = "application/vnd.containerd.container.resource.tar"
|
MediaTypeContainerd1Resource = "application/vnd.containerd.container.resource.tar"
|
||||||
MediaTypeContainerd1RW = "application/vnd.containerd.container.rw.tar"
|
MediaTypeContainerd1RW = "application/vnd.containerd.container.rw.tar"
|
||||||
MediaTypeContainerd1CheckpointConfig = "application/vnd.containerd.container.checkpoint.config.v1+proto"
|
MediaTypeContainerd1CheckpointConfig = "application/vnd.containerd.container.checkpoint.config.v1+proto"
|
||||||
MediaTypeContainerd1CheckpointImageName = "application/vnd.containerd.container.checkpoint.image.name"
|
MediaTypeContainerd1CheckpointOptions = "application/vnd.containerd.container.checkpoint.options.v1+proto"
|
||||||
MediaTypeContainerd1CheckpointRuntimeName = "application/vnd.containerd.container.checkpoint.runtime.name"
|
MediaTypeContainerd1CheckpointRuntimeName = "application/vnd.containerd.container.checkpoint.runtime.name"
|
||||||
MediaTypeContainerd1CheckpointRuntimeOptions = "application/vnd.containerd.container.checkpoint.runtime.options+proto"
|
MediaTypeContainerd1CheckpointRuntimeOptions = "application/vnd.containerd.container.checkpoint.runtime.options+proto"
|
||||||
// Legacy Docker schema1 manifest
|
// Legacy Docker schema1 manifest
|
||||||
|
Loading…
Reference in New Issue
Block a user