Create checkpointed image in client
Allow a user provided name for the checkpoint as well as a default generated name for the checkpoint image. Signed-off-by: Michael Crosby <crosbymichael@gmail.com>
This commit is contained in:
parent
a19fd6ed6e
commit
e201be5196
@ -4,7 +4,6 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
|
|
||||||
"github.com/containerd/containerd"
|
"github.com/containerd/containerd"
|
||||||
"github.com/containerd/containerd/images"
|
|
||||||
"github.com/pkg/errors"
|
"github.com/pkg/errors"
|
||||||
"github.com/urfave/cli"
|
"github.com/urfave/cli"
|
||||||
)
|
)
|
||||||
@ -49,20 +48,7 @@ var taskCheckpointCommand = cli.Command{
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
fmt.Println(checkpoint.Name())
|
||||||
labels := map[string]string{
|
|
||||||
"containerd.io/checkpoint": "true",
|
|
||||||
}
|
|
||||||
img := images.Image{
|
|
||||||
Name: checkpoint.Digest.String(),
|
|
||||||
Target: checkpoint,
|
|
||||||
Labels: labels,
|
|
||||||
}
|
|
||||||
_, err = client.ImageService().Create(ctx, img)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
fmt.Println(checkpoint.Digest.String())
|
|
||||||
return nil
|
return nil
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
@ -9,7 +9,6 @@ import (
|
|||||||
"github.com/containerd/console"
|
"github.com/containerd/console"
|
||||||
"github.com/containerd/containerd"
|
"github.com/containerd/containerd"
|
||||||
"github.com/containerd/containerd/containers"
|
"github.com/containerd/containerd/containers"
|
||||||
digest "github.com/opencontainers/go-digest"
|
|
||||||
specs "github.com/opencontainers/runtime-spec/specs-go"
|
specs "github.com/opencontainers/runtime-spec/specs-go"
|
||||||
"github.com/pkg/errors"
|
"github.com/pkg/errors"
|
||||||
"github.com/sirupsen/logrus"
|
"github.com/sirupsen/logrus"
|
||||||
@ -96,8 +95,7 @@ var runCommand = cli.Command{
|
|||||||
}, snapshotterFlags...),
|
}, snapshotterFlags...),
|
||||||
Action: func(context *cli.Context) error {
|
Action: func(context *cli.Context) error {
|
||||||
var (
|
var (
|
||||||
err error
|
err error
|
||||||
checkpointIndex digest.Digest
|
|
||||||
|
|
||||||
ctx, cancel = appContext(context)
|
ctx, cancel = appContext(context)
|
||||||
id = context.Args().Get(1)
|
id = context.Args().Get(1)
|
||||||
@ -112,11 +110,6 @@ var runCommand = cli.Command{
|
|||||||
if id == "" {
|
if id == "" {
|
||||||
return errors.New("container id must be provided")
|
return errors.New("container id must be provided")
|
||||||
}
|
}
|
||||||
if raw := context.String("checkpoint"); raw != "" {
|
|
||||||
if checkpointIndex, err = digest.Parse(raw); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
client, err := newClient(context)
|
client, err := newClient(context)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
@ -128,7 +121,7 @@ var runCommand = cli.Command{
|
|||||||
if context.Bool("rm") {
|
if context.Bool("rm") {
|
||||||
defer container.Delete(ctx, containerd.WithSnapshotCleanup)
|
defer container.Delete(ctx, containerd.WithSnapshotCleanup)
|
||||||
}
|
}
|
||||||
task, err := newTask(ctx, container, checkpointIndex, tty)
|
task, err := newTask(ctx, client, container, context.String("checkpoint"), tty)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -11,8 +11,6 @@ import (
|
|||||||
|
|
||||||
"github.com/containerd/console"
|
"github.com/containerd/console"
|
||||||
"github.com/containerd/containerd"
|
"github.com/containerd/containerd"
|
||||||
digest "github.com/opencontainers/go-digest"
|
|
||||||
"github.com/opencontainers/image-spec/specs-go/v1"
|
|
||||||
specs "github.com/opencontainers/runtime-spec/specs-go"
|
specs "github.com/opencontainers/runtime-spec/specs-go"
|
||||||
"github.com/sirupsen/logrus"
|
"github.com/sirupsen/logrus"
|
||||||
"github.com/urfave/cli"
|
"github.com/urfave/cli"
|
||||||
@ -67,15 +65,11 @@ func newContainer(ctx gocontext.Context, client *containerd.Client, context *cli
|
|||||||
)
|
)
|
||||||
|
|
||||||
if raw := context.String("checkpoint"); raw != "" {
|
if raw := context.String("checkpoint"); raw != "" {
|
||||||
checkpointIndex, err := digest.Parse(raw)
|
im, err := client.GetImage(ctx, raw)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
if checkpointIndex != "" {
|
return client.NewContainer(ctx, id, containerd.WithCheckpoint(im, id))
|
||||||
return client.NewContainer(ctx, id, containerd.WithCheckpoint(v1.Descriptor{
|
|
||||||
Digest: checkpointIndex,
|
|
||||||
}, id))
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
var (
|
var (
|
||||||
@ -120,7 +114,7 @@ func newContainer(ctx gocontext.Context, client *containerd.Client, context *cli
|
|||||||
return client.NewContainer(ctx, id, cOpts...)
|
return client.NewContainer(ctx, id, cOpts...)
|
||||||
}
|
}
|
||||||
|
|
||||||
func newTask(ctx gocontext.Context, container containerd.Container, checkpoint digest.Digest, tty bool) (containerd.Task, error) {
|
func newTask(ctx gocontext.Context, client *containerd.Client, container containerd.Container, checkpoint string, tty bool) (containerd.Task, error) {
|
||||||
if checkpoint == "" {
|
if checkpoint == "" {
|
||||||
io := containerd.Stdio
|
io := containerd.Stdio
|
||||||
if tty {
|
if tty {
|
||||||
@ -128,7 +122,9 @@ func newTask(ctx gocontext.Context, container containerd.Container, checkpoint d
|
|||||||
}
|
}
|
||||||
return container.NewTask(ctx, io)
|
return container.NewTask(ctx, io)
|
||||||
}
|
}
|
||||||
return container.NewTask(ctx, containerd.Stdio, containerd.WithTaskCheckpoint(v1.Descriptor{
|
im, err := client.GetImage(ctx, checkpoint)
|
||||||
Digest: checkpoint,
|
if err != nil {
|
||||||
}))
|
return nil, err
|
||||||
|
}
|
||||||
|
return container.NewTask(ctx, containerd.Stdio, containerd.WithTaskCheckpoint(im))
|
||||||
}
|
}
|
||||||
|
@ -9,7 +9,6 @@ import (
|
|||||||
"github.com/containerd/containerd/containers"
|
"github.com/containerd/containerd/containers"
|
||||||
"github.com/containerd/containerd/errdefs"
|
"github.com/containerd/containerd/errdefs"
|
||||||
"github.com/containerd/containerd/log"
|
"github.com/containerd/containerd/log"
|
||||||
digest "github.com/opencontainers/go-digest"
|
|
||||||
specs "github.com/opencontainers/runtime-spec/specs-go"
|
specs "github.com/opencontainers/runtime-spec/specs-go"
|
||||||
"github.com/pkg/errors"
|
"github.com/pkg/errors"
|
||||||
"github.com/sirupsen/logrus"
|
"github.com/sirupsen/logrus"
|
||||||
@ -118,7 +117,7 @@ func newContainer(ctx gocontext.Context, client *containerd.Client, context *cli
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
func newTask(ctx gocontext.Context, container containerd.Container, _ digest.Digest, tty bool) (containerd.Task, error) {
|
func newTask(ctx gocontext.Context, client *containerd.Client, container containerd.Container, _ string, tty bool) (containerd.Task, error) {
|
||||||
io := containerd.Stdio
|
io := containerd.Stdio
|
||||||
if tty {
|
if tty {
|
||||||
io = containerd.StdioTerminal
|
io = containerd.StdioTerminal
|
||||||
|
@ -2,7 +2,6 @@ package main
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/containerd/console"
|
"github.com/containerd/console"
|
||||||
"github.com/opencontainers/go-digest"
|
|
||||||
"github.com/pkg/errors"
|
"github.com/pkg/errors"
|
||||||
"github.com/sirupsen/logrus"
|
"github.com/sirupsen/logrus"
|
||||||
"github.com/urfave/cli"
|
"github.com/urfave/cli"
|
||||||
@ -41,7 +40,7 @@ var taskStartCommand = cli.Command{
|
|||||||
|
|
||||||
tty := spec.Process.Terminal
|
tty := spec.Process.Terminal
|
||||||
|
|
||||||
task, err := newTask(ctx, container, digest.Digest(""), tty)
|
task, err := newTask(ctx, client, container, "", tty)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -24,11 +24,14 @@ import (
|
|||||||
// WithCheckpoint allows a container to be created from the checkpointed information
|
// WithCheckpoint allows a container to be created from the checkpointed information
|
||||||
// provided by the descriptor. The image, snapshot, and runtime specifications are
|
// provided by the descriptor. The image, snapshot, and runtime specifications are
|
||||||
// restored on the container
|
// restored on the container
|
||||||
func WithCheckpoint(desc v1.Descriptor, snapshotKey string) NewContainerOpts {
|
func WithCheckpoint(im Image, snapshotKey string) NewContainerOpts {
|
||||||
// set image and rw, and spec
|
// set image and rw, and spec
|
||||||
return func(ctx context.Context, client *Client, c *containers.Container) error {
|
return func(ctx context.Context, client *Client, c *containers.Container) error {
|
||||||
id := desc.Digest
|
var (
|
||||||
store := client.ContentStore()
|
desc = im.Target()
|
||||||
|
id = desc.Digest
|
||||||
|
store = client.ContentStore()
|
||||||
|
)
|
||||||
index, err := decodeIndex(ctx, store, id)
|
index, err := decodeIndex(ctx, store, id)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
@ -85,8 +88,9 @@ func WithCheckpoint(desc v1.Descriptor, snapshotKey string) NewContainerOpts {
|
|||||||
// WithTaskCheckpoint allows a task to be created with live runtime and memory data from a
|
// WithTaskCheckpoint allows a task to be created with live runtime and memory data from a
|
||||||
// previous checkpoint. Additional software such as CRIU may be required to
|
// previous checkpoint. Additional software such as CRIU may be required to
|
||||||
// restore a task from a checkpoint
|
// restore a task from a checkpoint
|
||||||
func WithTaskCheckpoint(desc v1.Descriptor) NewTaskOpts {
|
func WithTaskCheckpoint(im Image) NewTaskOpts {
|
||||||
return func(ctx context.Context, c *Client, info *TaskInfo) error {
|
return func(ctx context.Context, c *Client, info *TaskInfo) error {
|
||||||
|
desc := im.Target()
|
||||||
id := desc.Digest
|
id := desc.Digest
|
||||||
index, err := decodeIndex(ctx, c.ContentStore(), id)
|
index, err := decodeIndex(ctx, c.ContentStore(), id)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
59
task.go
59
task.go
@ -17,6 +17,7 @@ import (
|
|||||||
"github.com/containerd/containerd/content"
|
"github.com/containerd/containerd/content"
|
||||||
"github.com/containerd/containerd/diff"
|
"github.com/containerd/containerd/diff"
|
||||||
"github.com/containerd/containerd/errdefs"
|
"github.com/containerd/containerd/errdefs"
|
||||||
|
"github.com/containerd/containerd/images"
|
||||||
"github.com/containerd/containerd/log"
|
"github.com/containerd/containerd/log"
|
||||||
"github.com/containerd/containerd/mount"
|
"github.com/containerd/containerd/mount"
|
||||||
"github.com/containerd/containerd/plugin"
|
"github.com/containerd/containerd/plugin"
|
||||||
@ -88,6 +89,7 @@ func WithStdinCloser(r *IOCloseInfo) {
|
|||||||
|
|
||||||
// CheckpointTaskInfo allows specific checkpoint information to be set for the task
|
// CheckpointTaskInfo allows specific checkpoint information to be set for the task
|
||||||
type CheckpointTaskInfo struct {
|
type CheckpointTaskInfo struct {
|
||||||
|
Name string
|
||||||
// ParentCheckpoint is the digest of a parent checkpoint
|
// ParentCheckpoint is the digest of a parent checkpoint
|
||||||
ParentCheckpoint digest.Digest
|
ParentCheckpoint digest.Digest
|
||||||
// Options hold runtime specific settings for checkpointing a task
|
// Options hold runtime specific settings for checkpointing a task
|
||||||
@ -124,7 +126,7 @@ 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
|
||||||
Checkpoint(context.Context, ...CheckpointTaskOpts) (v1.Descriptor, 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
|
||||||
// LoadProcess loads a previously created exec'd process
|
// LoadProcess loads a previously created exec'd process
|
||||||
@ -350,46 +352,73 @@ func (t *task) Resize(ctx context.Context, w, h uint32) error {
|
|||||||
return errdefs.FromGRPC(err)
|
return errdefs.FromGRPC(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t *task) Checkpoint(ctx context.Context, opts ...CheckpointTaskOpts) (d v1.Descriptor, err error) {
|
func (t *task) Checkpoint(ctx context.Context, opts ...CheckpointTaskOpts) (Image, error) {
|
||||||
request := &tasks.CheckpointTaskRequest{
|
request := &tasks.CheckpointTaskRequest{
|
||||||
ContainerID: t.id,
|
ContainerID: t.id,
|
||||||
}
|
}
|
||||||
var i CheckpointTaskInfo
|
var i CheckpointTaskInfo
|
||||||
for _, o := range opts {
|
for _, o := range opts {
|
||||||
if err := o(&i); err != nil {
|
if err := o(&i); err != nil {
|
||||||
return d, err
|
return nil, err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
const checkpointDateFormat = "01-02-2006-15:04:05"
|
||||||
|
// set a default name
|
||||||
|
if i.Name == "" {
|
||||||
|
i.Name = fmt.Sprintf("io.containerd/checkpoint/%s:%s", t.id, time.Now().Format(checkpointDateFormat))
|
||||||
|
}
|
||||||
request.ParentCheckpoint = i.ParentCheckpoint
|
request.ParentCheckpoint = i.ParentCheckpoint
|
||||||
if i.Options != nil {
|
if i.Options != nil {
|
||||||
any, err := typeurl.MarshalAny(i.Options)
|
any, err := typeurl.MarshalAny(i.Options)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return d, err
|
return nil, err
|
||||||
}
|
}
|
||||||
request.Options = any
|
request.Options = any
|
||||||
}
|
}
|
||||||
// make sure we pause it and resume after all other filesystem operations are completed
|
// make sure we pause it and resume after all other filesystem operations are completed
|
||||||
if err := t.Pause(ctx); err != nil {
|
if err := t.Pause(ctx); err != nil {
|
||||||
return d, err
|
return nil, err
|
||||||
}
|
}
|
||||||
defer t.Resume(ctx)
|
defer t.Resume(ctx)
|
||||||
cr, err := t.client.ContainerService().Get(ctx, t.id)
|
cr, err := t.client.ContainerService().Get(ctx, t.id)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return d, err
|
return nil, err
|
||||||
|
}
|
||||||
|
index := v1.Index{
|
||||||
|
Annotations: make(map[string]string),
|
||||||
}
|
}
|
||||||
var index v1.Index
|
|
||||||
if err := t.checkpointTask(ctx, &index, request); err != nil {
|
if err := t.checkpointTask(ctx, &index, request); err != nil {
|
||||||
return d, err
|
return nil, err
|
||||||
}
|
}
|
||||||
if err := t.checkpointImage(ctx, &index, cr.Image); err != nil {
|
if cr.Image != "" {
|
||||||
return d, err
|
if err := t.checkpointImage(ctx, &index, cr.Image); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
index.Annotations["image.name"] = cr.Image
|
||||||
}
|
}
|
||||||
if err := t.checkpointRWSnapshot(ctx, &index, cr.Snapshotter, cr.SnapshotKey); err != nil {
|
if cr.SnapshotKey != "" {
|
||||||
return d, err
|
if err := t.checkpointRWSnapshot(ctx, &index, cr.Snapshotter, cr.SnapshotKey); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
}
|
}
|
||||||
index.Annotations = make(map[string]string)
|
desc, err := t.writeIndex(ctx, &index)
|
||||||
index.Annotations["image.name"] = cr.Image
|
if err != nil {
|
||||||
return t.writeIndex(ctx, &index)
|
return nil, err
|
||||||
|
}
|
||||||
|
im := images.Image{
|
||||||
|
Name: i.Name,
|
||||||
|
Target: desc,
|
||||||
|
Labels: map[string]string{
|
||||||
|
"containerd.io/checkpoint": "true",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
if im, err = t.client.ImageService().Create(ctx, im); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return &image{
|
||||||
|
client: t.client,
|
||||||
|
i: im,
|
||||||
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// UpdateTaskInfo allows updated specific settings to be changed on a task
|
// UpdateTaskInfo allows updated specific settings to be changed on a task
|
||||||
|
@ -28,6 +28,14 @@ func WithExit(r *CheckpointTaskInfo) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// WithCheckpointName sets the image name for the checkpoint
|
||||||
|
func WithCheckpointName(name string) CheckpointTaskOpts {
|
||||||
|
return func(r *CheckpointTaskInfo) error {
|
||||||
|
r.Name = name
|
||||||
|
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