
To allow for updating extensions without collisions, we have moved to using a map type that can be explicitly selected via the field path for updates. This ensures that multiple parties can operate on their extensions without stepping on each other's toes or incurring an inordinate number of round trips. Signed-off-by: Stephen J Day <stephen.day@docker.com>
155 lines
4.7 KiB
Go
155 lines
4.7 KiB
Go
package containerd
|
|
|
|
import (
|
|
"context"
|
|
|
|
"github.com/containerd/containerd/containers"
|
|
"github.com/containerd/containerd/errdefs"
|
|
"github.com/containerd/containerd/typeurl"
|
|
"github.com/gogo/protobuf/types"
|
|
"github.com/opencontainers/image-spec/identity"
|
|
"github.com/pkg/errors"
|
|
)
|
|
|
|
// NewContainerOpts allows the caller to set additional options when creating a container
|
|
type NewContainerOpts func(ctx context.Context, client *Client, c *containers.Container) error
|
|
|
|
// WithRuntime allows a user to specify the runtime name and additional options that should
|
|
// be used to create tasks for the container
|
|
func WithRuntime(name string, options interface{}) NewContainerOpts {
|
|
return func(ctx context.Context, client *Client, c *containers.Container) error {
|
|
var (
|
|
any *types.Any
|
|
err error
|
|
)
|
|
if options != nil {
|
|
any, err = typeurl.MarshalAny(options)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
}
|
|
c.Runtime = containers.RuntimeInfo{
|
|
Name: name,
|
|
Options: any,
|
|
}
|
|
return nil
|
|
}
|
|
}
|
|
|
|
// WithImage sets the provided image as the base for the container
|
|
func WithImage(i Image) NewContainerOpts {
|
|
return func(ctx context.Context, client *Client, c *containers.Container) error {
|
|
c.Image = i.Name()
|
|
return nil
|
|
}
|
|
}
|
|
|
|
// WithContainerLabels adds the provided labels to the container
|
|
func WithContainerLabels(labels map[string]string) NewContainerOpts {
|
|
return func(_ context.Context, _ *Client, c *containers.Container) error {
|
|
c.Labels = labels
|
|
return nil
|
|
}
|
|
}
|
|
|
|
// WithSnapshotter sets the provided snapshotter for use by the container
|
|
//
|
|
// This option must appear before other snapshotter options to have an effect.
|
|
func WithSnapshotter(name string) NewContainerOpts {
|
|
return func(ctx context.Context, client *Client, c *containers.Container) error {
|
|
c.Snapshotter = name
|
|
return nil
|
|
}
|
|
}
|
|
|
|
// WithSnapshot uses an existing root filesystem for the container
|
|
func WithSnapshot(id string) NewContainerOpts {
|
|
return func(ctx context.Context, client *Client, c *containers.Container) error {
|
|
setSnapshotterIfEmpty(c)
|
|
// check that the snapshot exists, if not, fail on creation
|
|
if _, err := client.SnapshotService(c.Snapshotter).Mounts(ctx, id); err != nil {
|
|
return err
|
|
}
|
|
c.SnapshotKey = id
|
|
return nil
|
|
}
|
|
}
|
|
|
|
// WithNewSnapshot allocates a new snapshot to be used by the container as the
|
|
// root filesystem in read-write mode
|
|
func WithNewSnapshot(id string, i Image) NewContainerOpts {
|
|
return func(ctx context.Context, client *Client, c *containers.Container) error {
|
|
diffIDs, err := i.(*image).i.RootFS(ctx, client.ContentStore())
|
|
if err != nil {
|
|
return err
|
|
}
|
|
setSnapshotterIfEmpty(c)
|
|
if _, err := client.SnapshotService(c.Snapshotter).Prepare(ctx, id, identity.ChainID(diffIDs).String()); err != nil {
|
|
return err
|
|
}
|
|
c.SnapshotKey = id
|
|
c.Image = i.Name()
|
|
return nil
|
|
}
|
|
}
|
|
|
|
// WithSnapshotCleanup deletes the rootfs snapshot allocated for the container
|
|
func WithSnapshotCleanup(ctx context.Context, client *Client, c containers.Container) error {
|
|
if c.SnapshotKey != "" {
|
|
if c.Snapshotter == "" {
|
|
return errors.Wrapf(errdefs.ErrInvalidArgument, "container.Snapshotter must be set to cleanup rootfs snapshot")
|
|
}
|
|
return client.SnapshotService(c.Snapshotter).Remove(ctx, c.SnapshotKey)
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// WithNewSnapshotView allocates a new snapshot to be used by the container as the
|
|
// root filesystem in read-only mode
|
|
func WithNewSnapshotView(id string, i Image) NewContainerOpts {
|
|
return func(ctx context.Context, client *Client, c *containers.Container) error {
|
|
diffIDs, err := i.(*image).i.RootFS(ctx, client.ContentStore())
|
|
if err != nil {
|
|
return err
|
|
}
|
|
setSnapshotterIfEmpty(c)
|
|
if _, err := client.SnapshotService(c.Snapshotter).View(ctx, id, identity.ChainID(diffIDs).String()); err != nil {
|
|
return err
|
|
}
|
|
c.SnapshotKey = id
|
|
c.Image = i.Name()
|
|
return nil
|
|
}
|
|
}
|
|
|
|
func setSnapshotterIfEmpty(c *containers.Container) {
|
|
if c.Snapshotter == "" {
|
|
c.Snapshotter = DefaultSnapshotter
|
|
}
|
|
}
|
|
|
|
// WithContainerExtension appends extension data to the container object.
|
|
// Use this to decorate the container object with additional data for the client
|
|
// integration.
|
|
//
|
|
// Make sure to register the type of `extension` in the typeurl package via
|
|
// `typeurl.Register` otherwise the type data will be inferred, including how
|
|
// to encode and decode the object.
|
|
func WithContainerExtension(name string, extension interface{}) NewContainerOpts {
|
|
return func(ctx context.Context, client *Client, c *containers.Container) error {
|
|
any, err := typeurl.MarshalAny(extension)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
if name == "" {
|
|
return errors.Wrapf(errdefs.ErrInvalidArgument, "extension key must not be zero-length")
|
|
}
|
|
if c.Extensions == nil {
|
|
c.Extensions = make(map[string]types.Any)
|
|
}
|
|
c.Extensions[name] = *any
|
|
return nil
|
|
}
|
|
}
|