containerd/container_opts_unix.go
Stephen J Day 677257f032
service/snapshotter: move default to client
In order to enforce strict handling of snapshotter values on the
container object, the defaults have been moved to the client side. This
ensures that we correctly qualify the snapshotter under use when from
the container at the time it was created, rather than possibly losing
the metadata on a change of default.

Signed-off-by: Stephen J Day <stephen.day@docker.com>
2017-08-18 14:43:12 -07:00

119 lines
3.3 KiB
Go

// +build !windows
package containerd
import (
"context"
"encoding/json"
"fmt"
"github.com/containerd/containerd/api/types"
"github.com/containerd/containerd/containers"
"github.com/containerd/containerd/content"
"github.com/containerd/containerd/errdefs"
"github.com/containerd/containerd/images"
"github.com/gogo/protobuf/proto"
protobuf "github.com/gogo/protobuf/types"
digest "github.com/opencontainers/go-digest"
"github.com/opencontainers/image-spec/identity"
"github.com/opencontainers/image-spec/specs-go/v1"
)
// 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(desc v1.Descriptor, rootfsID string) NewContainerOpts {
// set image and rw, and spec
return func(ctx context.Context, client *Client, c *containers.Container) error {
id := desc.Digest
store := client.ContentStore()
index, err := decodeIndex(ctx, store, id)
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:
config, err := images.Config(ctx, store, m)
if err != nil {
return err
}
diffIDs, err := images.RootFS(ctx, store, config)
if err != nil {
return err
}
setSnapshotterIfEmpty(c)
if _, err := client.SnapshotService(c.Snapshotter).Prepare(ctx, rootfsID, 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.Digest)
if err != nil {
return err
}
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, rootfsID)
if err != nil {
return err
}
if _, err := client.DiffService().Apply(ctx, *rw, mounts); err != nil {
return err
}
}
c.RootFS = rootfsID
return nil
}
}
// 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
// restore a task from a checkpoint
func WithTaskCheckpoint(desc v1.Descriptor) NewTaskOpts {
return func(ctx context.Context, c *Client, info *TaskInfo) error {
id := desc.Digest
index, err := decodeIndex(ctx, c.ContentStore(), id)
if err != nil {
return err
}
for _, m := range index.Manifests {
if m.MediaType == images.MediaTypeContainerd1Checkpoint {
info.Checkpoint = &types.Descriptor{
MediaType: m.MediaType,
Size_: m.Size,
Digest: m.Digest,
}
return nil
}
}
return fmt.Errorf("checkpoint not found in index %s", id)
}
}
func decodeIndex(ctx context.Context, store content.Store, id digest.Digest) (*v1.Index, error) {
var index v1.Index
p, err := content.ReadBlob(ctx, store, id)
if err != nil {
return nil, err
}
if err := json.Unmarshal(p, &index); err != nil {
return nil, err
}
return &index, nil
}