Move client's options to separate files
This should help in discovering the available options for a given object. Signed-off-by: Kenfe-Mickael Laventure <mickael.laventure@gmail.com>
This commit is contained in:
parent
6c1a04f3a4
commit
56b18c1d1f
155
client.go
155
client.go
@ -36,7 +36,6 @@ import (
|
|||||||
"github.com/containerd/containerd/snapshot"
|
"github.com/containerd/containerd/snapshot"
|
||||||
"github.com/containerd/containerd/typeurl"
|
"github.com/containerd/containerd/typeurl"
|
||||||
pempty "github.com/golang/protobuf/ptypes/empty"
|
pempty "github.com/golang/protobuf/ptypes/empty"
|
||||||
"github.com/opencontainers/image-spec/identity"
|
|
||||||
ocispec "github.com/opencontainers/image-spec/specs-go/v1"
|
ocispec "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/pkg/errors"
|
"github.com/pkg/errors"
|
||||||
@ -53,33 +52,6 @@ func init() {
|
|||||||
typeurl.Register(&specs.WindowsResources{}, "opencontainers/runtime-spec", major, "WindowsResources")
|
typeurl.Register(&specs.WindowsResources{}, "opencontainers/runtime-spec", major, "WindowsResources")
|
||||||
}
|
}
|
||||||
|
|
||||||
type clientOpts struct {
|
|
||||||
defaultns string
|
|
||||||
dialOptions []grpc.DialOption
|
|
||||||
}
|
|
||||||
|
|
||||||
// ClientOpt allows callers to set options on the containerd client
|
|
||||||
type ClientOpt func(c *clientOpts) error
|
|
||||||
|
|
||||||
// WithDefaultNamespace sets the default namespace on the client
|
|
||||||
//
|
|
||||||
// Any operation that does not have a namespace set on the context will
|
|
||||||
// be provided the default namespace
|
|
||||||
func WithDefaultNamespace(ns string) ClientOpt {
|
|
||||||
return func(c *clientOpts) error {
|
|
||||||
c.defaultns = ns
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// WithDialOpts allows grpc.DialOptions to be set on the connection
|
|
||||||
func WithDialOpts(opts []grpc.DialOption) ClientOpt {
|
|
||||||
return func(c *clientOpts) error {
|
|
||||||
c.dialOptions = opts
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// New returns a new containerd client that is connected to the containerd
|
// New returns a new containerd client that is connected to the containerd
|
||||||
// instance provided by address
|
// instance provided by address
|
||||||
func New(address string, opts ...ClientOpt) (*Client, error) {
|
func New(address string, opts ...ClientOpt) (*Client, error) {
|
||||||
@ -154,90 +126,6 @@ func (c *Client) Containers(ctx context.Context, filters ...string) ([]Container
|
|||||||
return out, nil
|
return out, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewContainerOpts allows the caller to set additional options when creating a container
|
|
||||||
type NewContainerOpts func(ctx context.Context, client *Client, c *containers.Container) error
|
|
||||||
|
|
||||||
// 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
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 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 {
|
|
||||||
// check that the snapshot exists, if not, fail on creation
|
|
||||||
if _, err := client.SnapshotService(c.Snapshotter).Mounts(ctx, id); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
c.RootFS = 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
|
|
||||||
}
|
|
||||||
if _, err := client.SnapshotService(c.Snapshotter).Prepare(ctx, id, identity.ChainID(diffIDs).String()); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
c.RootFS = id
|
|
||||||
c.Image = i.Name()
|
|
||||||
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
|
|
||||||
}
|
|
||||||
if _, err := client.SnapshotService(c.Snapshotter).View(ctx, id, identity.ChainID(diffIDs).String()); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
c.RootFS = id
|
|
||||||
c.Image = i.Name()
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 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) NewContainerOpts {
|
|
||||||
return func(ctx context.Context, client *Client, c *containers.Container) error {
|
|
||||||
c.Runtime = containers.RuntimeInfo{
|
|
||||||
Name: name,
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// WithSnapshotter sets the provided snapshotter for use by the container
|
|
||||||
func WithSnapshotter(name string) NewContainerOpts {
|
|
||||||
return func(ctx context.Context, client *Client, c *containers.Container) error {
|
|
||||||
c.Snapshotter = name
|
|
||||||
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
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewContainer will create a new container in container with the provided id
|
// NewContainer will create a new container in container with the provided id
|
||||||
// the id must be unique within the namespace
|
// the id must be unique within the namespace
|
||||||
func (c *Client) NewContainer(ctx context.Context, id string, opts ...NewContainerOpts) (Container, error) {
|
func (c *Client) NewContainer(ctx context.Context, id string, opts ...NewContainerOpts) (Container, error) {
|
||||||
@ -268,9 +156,6 @@ func (c *Client) LoadContainer(ctx context.Context, id string) (Container, error
|
|||||||
return containerFromRecord(c, r), nil
|
return containerFromRecord(c, r), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// RemoteOpts allows the caller to set distribution options for a remote
|
|
||||||
type RemoteOpts func(*Client, *RemoteContext) error
|
|
||||||
|
|
||||||
// RemoteContext is used to configure object resolutions and transfers with
|
// RemoteContext is used to configure object resolutions and transfers with
|
||||||
// remote content stores and image providers.
|
// remote content stores and image providers.
|
||||||
type RemoteContext struct {
|
type RemoteContext struct {
|
||||||
@ -305,46 +190,6 @@ func defaultRemoteContext() *RemoteContext {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// WithPullUnpack is used to unpack an image after pull. This
|
|
||||||
// uses the snapshotter, content store, and diff service
|
|
||||||
// configured for the client.
|
|
||||||
func WithPullUnpack(client *Client, c *RemoteContext) error {
|
|
||||||
c.Unpack = true
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// WithPullSnapshotter specifies snapshotter name used for unpacking
|
|
||||||
func WithPullSnapshotter(snapshotterName string) RemoteOpts {
|
|
||||||
return func(client *Client, c *RemoteContext) error {
|
|
||||||
c.Snapshotter = snapshotterName
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// WithSchema1Conversion is used to convert Docker registry schema 1
|
|
||||||
// manifests to oci manifests on pull. Without this option schema 1
|
|
||||||
// manifests will return a not supported error.
|
|
||||||
func WithSchema1Conversion(client *Client, c *RemoteContext) error {
|
|
||||||
c.ConvertSchema1 = true
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// WithResolver specifies the resolver to use.
|
|
||||||
func WithResolver(resolver remotes.Resolver) RemoteOpts {
|
|
||||||
return func(client *Client, c *RemoteContext) error {
|
|
||||||
c.Resolver = resolver
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// WithImageHandler adds a base handler to be called on dispatch.
|
|
||||||
func WithImageHandler(h images.Handler) RemoteOpts {
|
|
||||||
return func(client *Client, c *RemoteContext) error {
|
|
||||||
c.BaseHandlers = append(c.BaseHandlers, h)
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Pull downloads the provided content into containerd's content store
|
// Pull downloads the provided content into containerd's content store
|
||||||
func (c *Client) Pull(ctx context.Context, ref string, opts ...RemoteOpts) (Image, error) {
|
func (c *Client) Pull(ctx context.Context, ref string, opts ...RemoteOpts) (Image, error) {
|
||||||
pullCtx := defaultRemoteContext()
|
pullCtx := defaultRemoteContext()
|
||||||
|
77
client_opts.go
Normal file
77
client_opts.go
Normal file
@ -0,0 +1,77 @@
|
|||||||
|
package containerd
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/containerd/containerd/images"
|
||||||
|
"github.com/containerd/containerd/remotes"
|
||||||
|
"google.golang.org/grpc"
|
||||||
|
)
|
||||||
|
|
||||||
|
type clientOpts struct {
|
||||||
|
defaultns string
|
||||||
|
dialOptions []grpc.DialOption
|
||||||
|
}
|
||||||
|
|
||||||
|
// ClientOpt allows callers to set options on the containerd client
|
||||||
|
type ClientOpt func(c *clientOpts) error
|
||||||
|
|
||||||
|
// WithDefaultNamespace sets the default namespace on the client
|
||||||
|
//
|
||||||
|
// Any operation that does not have a namespace set on the context will
|
||||||
|
// be provided the default namespace
|
||||||
|
func WithDefaultNamespace(ns string) ClientOpt {
|
||||||
|
return func(c *clientOpts) error {
|
||||||
|
c.defaultns = ns
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// WithDialOpts allows grpc.DialOptions to be set on the connection
|
||||||
|
func WithDialOpts(opts []grpc.DialOption) ClientOpt {
|
||||||
|
return func(c *clientOpts) error {
|
||||||
|
c.dialOptions = opts
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// RemoteOpts allows the caller to set distribution options for a remote
|
||||||
|
type RemoteOpts func(*Client, *RemoteContext) error
|
||||||
|
|
||||||
|
// WithPullUnpack is used to unpack an image after pull. This
|
||||||
|
// uses the snapshotter, content store, and diff service
|
||||||
|
// configured for the client.
|
||||||
|
func WithPullUnpack(client *Client, c *RemoteContext) error {
|
||||||
|
c.Unpack = true
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// WithPullSnapshotter specifies snapshotter name used for unpacking
|
||||||
|
func WithPullSnapshotter(snapshotterName string) RemoteOpts {
|
||||||
|
return func(client *Client, c *RemoteContext) error {
|
||||||
|
c.Snapshotter = snapshotterName
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// WithSchema1Conversion is used to convert Docker registry schema 1
|
||||||
|
// manifests to oci manifests on pull. Without this option schema 1
|
||||||
|
// manifests will return a not supported error.
|
||||||
|
func WithSchema1Conversion(client *Client, c *RemoteContext) error {
|
||||||
|
c.ConvertSchema1 = true
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// WithResolver specifies the resolver to use.
|
||||||
|
func WithResolver(resolver remotes.Resolver) RemoteOpts {
|
||||||
|
return func(client *Client, c *RemoteContext) error {
|
||||||
|
c.Resolver = resolver
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// WithImageHandler adds a base handler to be called on dispatch.
|
||||||
|
func WithImageHandler(h images.Handler) RemoteOpts {
|
||||||
|
return func(client *Client, c *RemoteContext) error {
|
||||||
|
c.BaseHandlers = append(c.BaseHandlers, h)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
20
container.go
20
container.go
@ -11,7 +11,6 @@ import (
|
|||||||
"github.com/containerd/containerd/api/types"
|
"github.com/containerd/containerd/api/types"
|
||||||
"github.com/containerd/containerd/containers"
|
"github.com/containerd/containerd/containers"
|
||||||
"github.com/containerd/containerd/errdefs"
|
"github.com/containerd/containerd/errdefs"
|
||||||
"github.com/containerd/containerd/mount"
|
|
||||||
"github.com/containerd/containerd/typeurl"
|
"github.com/containerd/containerd/typeurl"
|
||||||
specs "github.com/opencontainers/runtime-spec/specs-go"
|
specs "github.com/opencontainers/runtime-spec/specs-go"
|
||||||
"github.com/pkg/errors"
|
"github.com/pkg/errors"
|
||||||
@ -122,14 +121,6 @@ func (c *container) Spec() (*specs.Spec, error) {
|
|||||||
return &s, nil
|
return &s, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// WithSnapshotCleanup deletes the rootfs allocated for the container
|
|
||||||
func WithSnapshotCleanup(ctx context.Context, client *Client, c containers.Container) error {
|
|
||||||
if c.RootFS != "" {
|
|
||||||
return client.SnapshotService(c.Snapshotter).Remove(ctx, c.RootFS)
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Delete deletes an existing container
|
// Delete deletes an existing container
|
||||||
// an error is returned if the container has running tasks
|
// an error is returned if the container has running tasks
|
||||||
func (c *container) Delete(ctx context.Context, opts ...DeleteOpts) (err error) {
|
func (c *container) Delete(ctx context.Context, opts ...DeleteOpts) (err error) {
|
||||||
@ -167,17 +158,6 @@ func (c *container) Image(ctx context.Context) (Image, error) {
|
|||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewTaskOpts allows the caller to set options on a new task
|
|
||||||
type NewTaskOpts func(context.Context, *Client, *TaskInfo) error
|
|
||||||
|
|
||||||
// WithRootFS allows a task to be created without a snapshot being allocated to its container
|
|
||||||
func WithRootFS(mounts []mount.Mount) NewTaskOpts {
|
|
||||||
return func(ctx context.Context, c *Client, ti *TaskInfo) error {
|
|
||||||
ti.RootFS = mounts
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *container) NewTask(ctx context.Context, ioCreate IOCreation, opts ...NewTaskOpts) (Task, error) {
|
func (c *container) NewTask(ctx context.Context, ioCreate IOCreation, opts ...NewTaskOpts) (Task, error) {
|
||||||
c.mu.Lock()
|
c.mu.Lock()
|
||||||
defer c.mu.Unlock()
|
defer c.mu.Unlock()
|
||||||
|
100
container_opts.go
Normal file
100
container_opts.go
Normal file
@ -0,0 +1,100 @@
|
|||||||
|
package containerd
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
|
||||||
|
"github.com/containerd/containerd/containers"
|
||||||
|
"github.com/opencontainers/image-spec/identity"
|
||||||
|
)
|
||||||
|
|
||||||
|
// 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) NewContainerOpts {
|
||||||
|
return func(ctx context.Context, client *Client, c *containers.Container) error {
|
||||||
|
c.Runtime = containers.RuntimeInfo{
|
||||||
|
Name: name,
|
||||||
|
}
|
||||||
|
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
|
||||||
|
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 {
|
||||||
|
// check that the snapshot exists, if not, fail on creation
|
||||||
|
if _, err := client.SnapshotService(c.Snapshotter).Mounts(ctx, id); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
c.RootFS = 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
|
||||||
|
}
|
||||||
|
if _, err := client.SnapshotService(c.Snapshotter).Prepare(ctx, id, identity.ChainID(diffIDs).String()); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
c.RootFS = id
|
||||||
|
c.Image = i.Name()
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// WithSnapshotCleanup deletes the rootfs allocated for the container
|
||||||
|
func WithSnapshotCleanup(ctx context.Context, client *Client, c containers.Container) error {
|
||||||
|
if c.RootFS != "" {
|
||||||
|
return client.SnapshotService(c.Snapshotter).Remove(ctx, c.RootFS)
|
||||||
|
}
|
||||||
|
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
|
||||||
|
}
|
||||||
|
if _, err := client.SnapshotService(c.Snapshotter).View(ctx, id, identity.ChainID(diffIDs).String()); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
c.RootFS = id
|
||||||
|
c.Image = i.Name()
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
11
spec.go
11
spec.go
@ -2,17 +2,6 @@ package containerd
|
|||||||
|
|
||||||
import specs "github.com/opencontainers/runtime-spec/specs-go"
|
import specs "github.com/opencontainers/runtime-spec/specs-go"
|
||||||
|
|
||||||
// SpecOpts sets spec specific information to a newly generated OCI spec
|
|
||||||
type SpecOpts func(s *specs.Spec) error
|
|
||||||
|
|
||||||
// WithProcessArgs replaces the args on the generated spec
|
|
||||||
func WithProcessArgs(args ...string) SpecOpts {
|
|
||||||
return func(s *specs.Spec) error {
|
|
||||||
s.Process.Args = args
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// GenerateSpec will generate a default spec from the provided image
|
// GenerateSpec will generate a default spec from the provided image
|
||||||
// for use as a containerd container
|
// for use as a containerd container
|
||||||
func GenerateSpec(opts ...SpecOpts) (*specs.Spec, error) {
|
func GenerateSpec(opts ...SpecOpts) (*specs.Spec, error) {
|
||||||
|
14
spec_opts.go
Normal file
14
spec_opts.go
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
package containerd
|
||||||
|
|
||||||
|
import specs "github.com/opencontainers/runtime-spec/specs-go"
|
||||||
|
|
||||||
|
// SpecOpts sets spec specific information to a newly generated OCI spec
|
||||||
|
type SpecOpts func(s *specs.Spec) error
|
||||||
|
|
||||||
|
// WithProcessArgs replaces the args on the generated spec
|
||||||
|
func WithProcessArgs(args ...string) SpecOpts {
|
||||||
|
return func(s *specs.Spec) error {
|
||||||
|
s.Process.Args = args
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
265
spec_opts_unix.go
Normal file
265
spec_opts_unix.go
Normal file
@ -0,0 +1,265 @@
|
|||||||
|
// +build !windows
|
||||||
|
|
||||||
|
package containerd
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/containerd/containerd/containers"
|
||||||
|
"github.com/containerd/containerd/images"
|
||||||
|
"github.com/containerd/containerd/typeurl"
|
||||||
|
"github.com/opencontainers/image-spec/identity"
|
||||||
|
"github.com/opencontainers/image-spec/specs-go/v1"
|
||||||
|
specs "github.com/opencontainers/runtime-spec/specs-go"
|
||||||
|
)
|
||||||
|
|
||||||
|
// WithTTY sets the information on the spec as well as the environment variables for
|
||||||
|
// using a TTY
|
||||||
|
func WithTTY(s *specs.Spec) error {
|
||||||
|
s.Process.Terminal = true
|
||||||
|
s.Process.Env = append(s.Process.Env, "TERM=xterm")
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// WithHostNamespace allows a task to run inside the host's linux namespace
|
||||||
|
func WithHostNamespace(ns specs.LinuxNamespaceType) SpecOpts {
|
||||||
|
return func(s *specs.Spec) error {
|
||||||
|
for i, n := range s.Linux.Namespaces {
|
||||||
|
if n.Type == ns {
|
||||||
|
s.Linux.Namespaces = append(s.Linux.Namespaces[:i], s.Linux.Namespaces[i+1:]...)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// WithLinuxNamespace uses the passed in namespace for the spec. If a namespace of the same type already exists in the
|
||||||
|
// spec, the existing namespace is replaced by the one provided.
|
||||||
|
func WithLinuxNamespace(ns specs.LinuxNamespace) SpecOpts {
|
||||||
|
return func(s *specs.Spec) error {
|
||||||
|
for i, n := range s.Linux.Namespaces {
|
||||||
|
if n.Type == ns.Type {
|
||||||
|
before := s.Linux.Namespaces[:i]
|
||||||
|
after := s.Linux.Namespaces[i+1:]
|
||||||
|
s.Linux.Namespaces = append(before, ns)
|
||||||
|
s.Linux.Namespaces = append(s.Linux.Namespaces, after...)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
s.Linux.Namespaces = append(s.Linux.Namespaces, ns)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// WithImageConfig configures the spec to from the configuration of an Image
|
||||||
|
func WithImageConfig(ctx context.Context, i Image) SpecOpts {
|
||||||
|
return func(s *specs.Spec) error {
|
||||||
|
var (
|
||||||
|
image = i.(*image)
|
||||||
|
store = image.client.ContentStore()
|
||||||
|
)
|
||||||
|
ic, err := image.i.Config(ctx, store)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
var (
|
||||||
|
ociimage v1.Image
|
||||||
|
config v1.ImageConfig
|
||||||
|
)
|
||||||
|
switch ic.MediaType {
|
||||||
|
case v1.MediaTypeImageConfig, images.MediaTypeDockerSchema2Config:
|
||||||
|
r, err := store.Reader(ctx, ic.Digest)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if err := json.NewDecoder(r).Decode(&ociimage); err != nil {
|
||||||
|
r.Close()
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
r.Close()
|
||||||
|
config = ociimage.Config
|
||||||
|
default:
|
||||||
|
return fmt.Errorf("unknown image config media type %s", ic.MediaType)
|
||||||
|
}
|
||||||
|
s.Process.Env = append(s.Process.Env, config.Env...)
|
||||||
|
var (
|
||||||
|
uid, gid uint32
|
||||||
|
)
|
||||||
|
cmd := config.Cmd
|
||||||
|
s.Process.Args = append(config.Entrypoint, cmd...)
|
||||||
|
if config.User != "" {
|
||||||
|
parts := strings.Split(config.User, ":")
|
||||||
|
switch len(parts) {
|
||||||
|
case 1:
|
||||||
|
v, err := strconv.ParseUint(parts[0], 0, 10)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
uid, gid = uint32(v), uint32(v)
|
||||||
|
case 2:
|
||||||
|
v, err := strconv.ParseUint(parts[0], 0, 10)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
uid = uint32(v)
|
||||||
|
if v, err = strconv.ParseUint(parts[1], 0, 10); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
gid = uint32(v)
|
||||||
|
default:
|
||||||
|
return fmt.Errorf("invalid USER value %s", config.User)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
s.Process.User.UID, s.Process.User.GID = uid, gid
|
||||||
|
cwd := config.WorkingDir
|
||||||
|
if cwd == "" {
|
||||||
|
cwd = "/"
|
||||||
|
}
|
||||||
|
s.Process.Cwd = cwd
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// WithRootFSPath specifies unmanaged rootfs path.
|
||||||
|
func WithRootFSPath(path string, readonly bool) SpecOpts {
|
||||||
|
return func(s *specs.Spec) error {
|
||||||
|
s.Root = &specs.Root{
|
||||||
|
Path: path,
|
||||||
|
Readonly: readonly,
|
||||||
|
}
|
||||||
|
// Entrypoint is not set here (it's up to caller)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// WithSpec sets the provided spec for a new container
|
||||||
|
func WithSpec(spec *specs.Spec) NewContainerOpts {
|
||||||
|
return func(ctx context.Context, client *Client, c *containers.Container) error {
|
||||||
|
any, err := typeurl.MarshalAny(spec)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
c.Spec = any
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// WithResources sets the provided resources on the spec for task updates
|
||||||
|
func WithResources(resources *specs.LinuxResources) UpdateTaskOpts {
|
||||||
|
return func(ctx context.Context, client *Client, r *UpdateTaskInfo) error {
|
||||||
|
r.Resources = resources
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// WithNoNewPrivileges sets no_new_privileges on the process for the container
|
||||||
|
func WithNoNewPrivileges(s *specs.Spec) error {
|
||||||
|
s.Process.NoNewPrivileges = true
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// WithHostHostsFile bind-mounts the host's /etc/hosts into the container as readonly
|
||||||
|
func WithHostHostsFile(s *specs.Spec) error {
|
||||||
|
s.Mounts = append(s.Mounts, specs.Mount{
|
||||||
|
Destination: "/etc/hosts",
|
||||||
|
Type: "bind",
|
||||||
|
Source: "/etc/hosts",
|
||||||
|
Options: []string{"rbind", "ro"},
|
||||||
|
})
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// WithHostResoveconf bind-mounts the host's /etc/resolv.conf into the container as readonly
|
||||||
|
func WithHostResoveconf(s *specs.Spec) error {
|
||||||
|
s.Mounts = append(s.Mounts, specs.Mount{
|
||||||
|
Destination: "/etc/resolv.conf",
|
||||||
|
Type: "bind",
|
||||||
|
Source: "/etc/resolv.conf",
|
||||||
|
Options: []string{"rbind", "ro"},
|
||||||
|
})
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// WithHostLocaltime bind-mounts the host's /etc/localtime into the container as readonly
|
||||||
|
func WithHostLocaltime(s *specs.Spec) error {
|
||||||
|
s.Mounts = append(s.Mounts, specs.Mount{
|
||||||
|
Destination: "/etc/localtime",
|
||||||
|
Type: "bind",
|
||||||
|
Source: "/etc/localtime",
|
||||||
|
Options: []string{"rbind", "ro"},
|
||||||
|
})
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// WithUserNamespace sets the uid and gid mappings for the task
|
||||||
|
// this can be called multiple times to add more mappings to the generated spec
|
||||||
|
func WithUserNamespace(container, host, size uint32) SpecOpts {
|
||||||
|
return func(s *specs.Spec) error {
|
||||||
|
var hasUserns bool
|
||||||
|
for _, ns := range s.Linux.Namespaces {
|
||||||
|
if ns.Type == specs.UserNamespace {
|
||||||
|
hasUserns = true
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if !hasUserns {
|
||||||
|
s.Linux.Namespaces = append(s.Linux.Namespaces, specs.LinuxNamespace{
|
||||||
|
Type: specs.UserNamespace,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
mapping := specs.LinuxIDMapping{
|
||||||
|
ContainerID: container,
|
||||||
|
HostID: host,
|
||||||
|
Size: size,
|
||||||
|
}
|
||||||
|
s.Linux.UIDMappings = append(s.Linux.UIDMappings, mapping)
|
||||||
|
s.Linux.GIDMappings = append(s.Linux.GIDMappings, mapping)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// WithRemappedSnapshot creates a new snapshot and remaps the uid/gid for the
|
||||||
|
// filesystem to be used by a container with user namespaces
|
||||||
|
func WithRemappedSnapshot(id string, i Image, uid, gid uint32) 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
|
||||||
|
}
|
||||||
|
var (
|
||||||
|
snapshotter = client.SnapshotService(c.Snapshotter)
|
||||||
|
parent = identity.ChainID(diffIDs).String()
|
||||||
|
usernsID = fmt.Sprintf("%s-%d-%d", parent, uid, gid)
|
||||||
|
)
|
||||||
|
if _, err := snapshotter.Stat(ctx, usernsID); err == nil {
|
||||||
|
if _, err := snapshotter.Prepare(ctx, id, usernsID); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
c.RootFS = id
|
||||||
|
c.Image = i.Name()
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
mounts, err := snapshotter.Prepare(ctx, usernsID+"-remap", parent)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if err := remapRootFS(mounts, uid, gid); err != nil {
|
||||||
|
snapshotter.Remove(ctx, usernsID)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if err := snapshotter.Commit(ctx, usernsID, usernsID+"-remap"); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if _, err := snapshotter.Prepare(ctx, id, usernsID); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
c.RootFS = id
|
||||||
|
c.Image = i.Name()
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
80
spec_opts_windows.go
Normal file
80
spec_opts_windows.go
Normal file
@ -0,0 +1,80 @@
|
|||||||
|
// +build windows
|
||||||
|
|
||||||
|
package containerd
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"github.com/containerd/containerd/containers"
|
||||||
|
"github.com/containerd/containerd/images"
|
||||||
|
"github.com/containerd/containerd/typeurl"
|
||||||
|
"github.com/opencontainers/image-spec/specs-go/v1"
|
||||||
|
specs "github.com/opencontainers/runtime-spec/specs-go"
|
||||||
|
)
|
||||||
|
|
||||||
|
func WithImageConfig(ctx context.Context, i Image) SpecOpts {
|
||||||
|
return func(s *specs.Spec) error {
|
||||||
|
var (
|
||||||
|
image = i.(*image)
|
||||||
|
store = image.client.ContentStore()
|
||||||
|
)
|
||||||
|
ic, err := image.i.Config(ctx, store)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
var (
|
||||||
|
ociimage v1.Image
|
||||||
|
config v1.ImageConfig
|
||||||
|
)
|
||||||
|
switch ic.MediaType {
|
||||||
|
case v1.MediaTypeImageConfig, images.MediaTypeDockerSchema2Config:
|
||||||
|
r, err := store.Reader(ctx, ic.Digest)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if err := json.NewDecoder(r).Decode(&ociimage); err != nil {
|
||||||
|
r.Close()
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
r.Close()
|
||||||
|
config = ociimage.Config
|
||||||
|
default:
|
||||||
|
return fmt.Errorf("unknown image config media type %s", ic.MediaType)
|
||||||
|
}
|
||||||
|
s.Process.Env = config.Env
|
||||||
|
s.Process.Args = append(config.Entrypoint, config.Cmd...)
|
||||||
|
s.Process.User = specs.User{
|
||||||
|
Username: config.User,
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func WithTTY(width, height int) SpecOpts {
|
||||||
|
return func(s *specs.Spec) error {
|
||||||
|
s.Process.Terminal = true
|
||||||
|
s.Process.ConsoleSize.Width = uint(width)
|
||||||
|
s.Process.ConsoleSize.Height = uint(height)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func WithSpec(spec *specs.Spec) NewContainerOpts {
|
||||||
|
return func(ctx context.Context, client *Client, c *containers.Container) error {
|
||||||
|
any, err := typeurl.MarshalAny(spec)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
c.Spec = any
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func WithResources(resources *specs.WindowsResources) UpdateTaskOpts {
|
||||||
|
return func(ctx context.Context, client *Client, r *UpdateTaskInfo) error {
|
||||||
|
r.Resources = resources
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
256
spec_unix.go
256
spec_unix.go
@ -3,25 +3,16 @@
|
|||||||
package containerd
|
package containerd
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
|
||||||
"encoding/json"
|
|
||||||
"fmt"
|
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"strconv"
|
|
||||||
"strings"
|
"strings"
|
||||||
"syscall"
|
"syscall"
|
||||||
|
|
||||||
"golang.org/x/sys/unix"
|
"golang.org/x/sys/unix"
|
||||||
|
|
||||||
"github.com/containerd/containerd/containers"
|
|
||||||
"github.com/containerd/containerd/fs"
|
"github.com/containerd/containerd/fs"
|
||||||
"github.com/containerd/containerd/images"
|
|
||||||
"github.com/containerd/containerd/mount"
|
"github.com/containerd/containerd/mount"
|
||||||
"github.com/containerd/containerd/typeurl"
|
|
||||||
"github.com/opencontainers/image-spec/identity"
|
|
||||||
"github.com/opencontainers/image-spec/specs-go/v1"
|
|
||||||
specs "github.com/opencontainers/runtime-spec/specs-go"
|
specs "github.com/opencontainers/runtime-spec/specs-go"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -162,253 +153,6 @@ func createDefaultSpec() (*specs.Spec, error) {
|
|||||||
return s, nil
|
return s, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// WithTTY sets the information on the spec as well as the environment variables for
|
|
||||||
// using a TTY
|
|
||||||
func WithTTY(s *specs.Spec) error {
|
|
||||||
s.Process.Terminal = true
|
|
||||||
s.Process.Env = append(s.Process.Env, "TERM=xterm")
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// WithHostNamespace allows a task to run inside the host's linux namespace
|
|
||||||
func WithHostNamespace(ns specs.LinuxNamespaceType) SpecOpts {
|
|
||||||
return func(s *specs.Spec) error {
|
|
||||||
for i, n := range s.Linux.Namespaces {
|
|
||||||
if n.Type == ns {
|
|
||||||
s.Linux.Namespaces = append(s.Linux.Namespaces[:i], s.Linux.Namespaces[i+1:]...)
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// WithLinuxNamespace uses the passed in namespace for the spec. If a namespace of the same type already exists in the
|
|
||||||
// spec, the existing namespace is replaced by the one provided.
|
|
||||||
func WithLinuxNamespace(ns specs.LinuxNamespace) SpecOpts {
|
|
||||||
return func(s *specs.Spec) error {
|
|
||||||
for i, n := range s.Linux.Namespaces {
|
|
||||||
if n.Type == ns.Type {
|
|
||||||
before := s.Linux.Namespaces[:i]
|
|
||||||
after := s.Linux.Namespaces[i+1:]
|
|
||||||
s.Linux.Namespaces = append(before, ns)
|
|
||||||
s.Linux.Namespaces = append(s.Linux.Namespaces, after...)
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
s.Linux.Namespaces = append(s.Linux.Namespaces, ns)
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// WithImageConfig configures the spec to from the configuration of an Image
|
|
||||||
func WithImageConfig(ctx context.Context, i Image) SpecOpts {
|
|
||||||
return func(s *specs.Spec) error {
|
|
||||||
var (
|
|
||||||
image = i.(*image)
|
|
||||||
store = image.client.ContentStore()
|
|
||||||
)
|
|
||||||
ic, err := image.i.Config(ctx, store)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
var (
|
|
||||||
ociimage v1.Image
|
|
||||||
config v1.ImageConfig
|
|
||||||
)
|
|
||||||
switch ic.MediaType {
|
|
||||||
case v1.MediaTypeImageConfig, images.MediaTypeDockerSchema2Config:
|
|
||||||
r, err := store.Reader(ctx, ic.Digest)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
if err := json.NewDecoder(r).Decode(&ociimage); err != nil {
|
|
||||||
r.Close()
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
r.Close()
|
|
||||||
config = ociimage.Config
|
|
||||||
default:
|
|
||||||
return fmt.Errorf("unknown image config media type %s", ic.MediaType)
|
|
||||||
}
|
|
||||||
s.Process.Env = append(s.Process.Env, config.Env...)
|
|
||||||
var (
|
|
||||||
uid, gid uint32
|
|
||||||
)
|
|
||||||
cmd := config.Cmd
|
|
||||||
s.Process.Args = append(config.Entrypoint, cmd...)
|
|
||||||
if config.User != "" {
|
|
||||||
parts := strings.Split(config.User, ":")
|
|
||||||
switch len(parts) {
|
|
||||||
case 1:
|
|
||||||
v, err := strconv.ParseUint(parts[0], 0, 10)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
uid, gid = uint32(v), uint32(v)
|
|
||||||
case 2:
|
|
||||||
v, err := strconv.ParseUint(parts[0], 0, 10)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
uid = uint32(v)
|
|
||||||
if v, err = strconv.ParseUint(parts[1], 0, 10); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
gid = uint32(v)
|
|
||||||
default:
|
|
||||||
return fmt.Errorf("invalid USER value %s", config.User)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
s.Process.User.UID, s.Process.User.GID = uid, gid
|
|
||||||
cwd := config.WorkingDir
|
|
||||||
if cwd == "" {
|
|
||||||
cwd = "/"
|
|
||||||
}
|
|
||||||
s.Process.Cwd = cwd
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// WithRootFSPath specifies unmanaged rootfs path.
|
|
||||||
func WithRootFSPath(path string, readonly bool) SpecOpts {
|
|
||||||
return func(s *specs.Spec) error {
|
|
||||||
s.Root = &specs.Root{
|
|
||||||
Path: path,
|
|
||||||
Readonly: readonly,
|
|
||||||
}
|
|
||||||
// Entrypoint is not set here (it's up to caller)
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// WithSpec sets the provided spec for a new container
|
|
||||||
func WithSpec(spec *specs.Spec) NewContainerOpts {
|
|
||||||
return func(ctx context.Context, client *Client, c *containers.Container) error {
|
|
||||||
any, err := typeurl.MarshalAny(spec)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
c.Spec = any
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// WithResources sets the provided resources on the spec for task updates
|
|
||||||
func WithResources(resources *specs.LinuxResources) UpdateTaskOpts {
|
|
||||||
return func(ctx context.Context, client *Client, r *UpdateTaskInfo) error {
|
|
||||||
r.Resources = resources
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// WithNoNewPrivileges sets no_new_privileges on the process for the container
|
|
||||||
func WithNoNewPrivileges(s *specs.Spec) error {
|
|
||||||
s.Process.NoNewPrivileges = true
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// WithHostHostsFile bind-mounts the host's /etc/hosts into the container as readonly
|
|
||||||
func WithHostHostsFile(s *specs.Spec) error {
|
|
||||||
s.Mounts = append(s.Mounts, specs.Mount{
|
|
||||||
Destination: "/etc/hosts",
|
|
||||||
Type: "bind",
|
|
||||||
Source: "/etc/hosts",
|
|
||||||
Options: []string{"rbind", "ro"},
|
|
||||||
})
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// WithHostResoveconf bind-mounts the host's /etc/resolv.conf into the container as readonly
|
|
||||||
func WithHostResoveconf(s *specs.Spec) error {
|
|
||||||
s.Mounts = append(s.Mounts, specs.Mount{
|
|
||||||
Destination: "/etc/resolv.conf",
|
|
||||||
Type: "bind",
|
|
||||||
Source: "/etc/resolv.conf",
|
|
||||||
Options: []string{"rbind", "ro"},
|
|
||||||
})
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// WithHostLocaltime bind-mounts the host's /etc/localtime into the container as readonly
|
|
||||||
func WithHostLocaltime(s *specs.Spec) error {
|
|
||||||
s.Mounts = append(s.Mounts, specs.Mount{
|
|
||||||
Destination: "/etc/localtime",
|
|
||||||
Type: "bind",
|
|
||||||
Source: "/etc/localtime",
|
|
||||||
Options: []string{"rbind", "ro"},
|
|
||||||
})
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// WithUserNamespace sets the uid and gid mappings for the task
|
|
||||||
// this can be called multiple times to add more mappings to the generated spec
|
|
||||||
func WithUserNamespace(container, host, size uint32) SpecOpts {
|
|
||||||
return func(s *specs.Spec) error {
|
|
||||||
var hasUserns bool
|
|
||||||
for _, ns := range s.Linux.Namespaces {
|
|
||||||
if ns.Type == specs.UserNamespace {
|
|
||||||
hasUserns = true
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if !hasUserns {
|
|
||||||
s.Linux.Namespaces = append(s.Linux.Namespaces, specs.LinuxNamespace{
|
|
||||||
Type: specs.UserNamespace,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
mapping := specs.LinuxIDMapping{
|
|
||||||
ContainerID: container,
|
|
||||||
HostID: host,
|
|
||||||
Size: size,
|
|
||||||
}
|
|
||||||
s.Linux.UIDMappings = append(s.Linux.UIDMappings, mapping)
|
|
||||||
s.Linux.GIDMappings = append(s.Linux.GIDMappings, mapping)
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// WithRemappedSnapshot creates a new snapshot and remaps the uid/gid for the
|
|
||||||
// filesystem to be used by a container with user namespaces
|
|
||||||
func WithRemappedSnapshot(id string, i Image, uid, gid uint32) 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
|
|
||||||
}
|
|
||||||
var (
|
|
||||||
snapshotter = client.SnapshotService(c.Snapshotter)
|
|
||||||
parent = identity.ChainID(diffIDs).String()
|
|
||||||
usernsID = fmt.Sprintf("%s-%d-%d", parent, uid, gid)
|
|
||||||
)
|
|
||||||
if _, err := snapshotter.Stat(ctx, usernsID); err == nil {
|
|
||||||
if _, err := snapshotter.Prepare(ctx, id, usernsID); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
c.RootFS = id
|
|
||||||
c.Image = i.Name()
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
mounts, err := snapshotter.Prepare(ctx, usernsID+"-remap", parent)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
if err := remapRootFS(mounts, uid, gid); err != nil {
|
|
||||||
snapshotter.Remove(ctx, usernsID)
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
if err := snapshotter.Commit(ctx, usernsID, usernsID+"-remap"); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
if _, err := snapshotter.Prepare(ctx, id, usernsID); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
c.RootFS = id
|
|
||||||
c.Image = i.Name()
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func remapRootFS(mounts []mount.Mount, uid, gid uint32) error {
|
func remapRootFS(mounts []mount.Mount, uid, gid uint32) error {
|
||||||
root, err := ioutil.TempDir("", "ctd-remap")
|
root, err := ioutil.TempDir("", "ctd-remap")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -1,16 +1,6 @@
|
|||||||
package containerd
|
package containerd
|
||||||
|
|
||||||
import (
|
import specs "github.com/opencontainers/runtime-spec/specs-go"
|
||||||
"context"
|
|
||||||
"encoding/json"
|
|
||||||
"fmt"
|
|
||||||
|
|
||||||
"github.com/containerd/containerd/containers"
|
|
||||||
"github.com/containerd/containerd/images"
|
|
||||||
"github.com/containerd/containerd/typeurl"
|
|
||||||
"github.com/opencontainers/image-spec/specs-go/v1"
|
|
||||||
specs "github.com/opencontainers/runtime-spec/specs-go"
|
|
||||||
)
|
|
||||||
|
|
||||||
func createDefaultSpec() (*specs.Spec, error) {
|
func createDefaultSpec() (*specs.Spec, error) {
|
||||||
return &specs.Spec{
|
return &specs.Spec{
|
||||||
@ -31,68 +21,3 @@ func createDefaultSpec() (*specs.Spec, error) {
|
|||||||
},
|
},
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func WithImageConfig(ctx context.Context, i Image) SpecOpts {
|
|
||||||
return func(s *specs.Spec) error {
|
|
||||||
var (
|
|
||||||
image = i.(*image)
|
|
||||||
store = image.client.ContentStore()
|
|
||||||
)
|
|
||||||
ic, err := image.i.Config(ctx, store)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
var (
|
|
||||||
ociimage v1.Image
|
|
||||||
config v1.ImageConfig
|
|
||||||
)
|
|
||||||
switch ic.MediaType {
|
|
||||||
case v1.MediaTypeImageConfig, images.MediaTypeDockerSchema2Config:
|
|
||||||
r, err := store.Reader(ctx, ic.Digest)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
if err := json.NewDecoder(r).Decode(&ociimage); err != nil {
|
|
||||||
r.Close()
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
r.Close()
|
|
||||||
config = ociimage.Config
|
|
||||||
default:
|
|
||||||
return fmt.Errorf("unknown image config media type %s", ic.MediaType)
|
|
||||||
}
|
|
||||||
s.Process.Env = config.Env
|
|
||||||
s.Process.Args = append(config.Entrypoint, config.Cmd...)
|
|
||||||
s.Process.User = specs.User{
|
|
||||||
Username: config.User,
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func WithTTY(width, height int) SpecOpts {
|
|
||||||
return func(s *specs.Spec) error {
|
|
||||||
s.Process.Terminal = true
|
|
||||||
s.Process.ConsoleSize.Width = uint(width)
|
|
||||||
s.Process.ConsoleSize.Height = uint(height)
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func WithSpec(spec *specs.Spec) NewContainerOpts {
|
|
||||||
return func(ctx context.Context, client *Client, c *containers.Container) error {
|
|
||||||
any, err := typeurl.MarshalAny(spec)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
c.Spec = any
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func WithResources(resources *specs.WindowsResources) UpdateTaskOpts {
|
|
||||||
return func(ctx context.Context, client *Client, r *UpdateTaskInfo) error {
|
|
||||||
r.Resources = resources
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
9
task.go
9
task.go
@ -16,7 +16,6 @@ import (
|
|||||||
"github.com/containerd/containerd/api/types"
|
"github.com/containerd/containerd/api/types"
|
||||||
"github.com/containerd/containerd/content"
|
"github.com/containerd/containerd/content"
|
||||||
"github.com/containerd/containerd/errdefs"
|
"github.com/containerd/containerd/errdefs"
|
||||||
"github.com/containerd/containerd/linux/runcopts"
|
|
||||||
"github.com/containerd/containerd/mount"
|
"github.com/containerd/containerd/mount"
|
||||||
"github.com/containerd/containerd/rootfs"
|
"github.com/containerd/containerd/rootfs"
|
||||||
"github.com/containerd/containerd/runtime"
|
"github.com/containerd/containerd/runtime"
|
||||||
@ -467,11 +466,3 @@ func writeContent(ctx context.Context, store content.Store, mediaType, ref strin
|
|||||||
Size: size,
|
Size: size,
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// WithExit causes the task to exit after a successful checkpoint
|
|
||||||
func WithExit(r *CheckpointTaskInfo) error {
|
|
||||||
r.Options = &runcopts.CheckpointOptions{
|
|
||||||
Exit: true,
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
27
task_opts.go
Normal file
27
task_opts.go
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
package containerd
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
|
||||||
|
"github.com/containerd/containerd/linux/runcopts"
|
||||||
|
"github.com/containerd/containerd/mount"
|
||||||
|
)
|
||||||
|
|
||||||
|
// NewTaskOpts allows the caller to set options on a new task
|
||||||
|
type NewTaskOpts func(context.Context, *Client, *TaskInfo) error
|
||||||
|
|
||||||
|
// WithRootFS allows a task to be created without a snapshot being allocated to its container
|
||||||
|
func WithRootFS(mounts []mount.Mount) NewTaskOpts {
|
||||||
|
return func(ctx context.Context, c *Client, ti *TaskInfo) error {
|
||||||
|
ti.RootFS = mounts
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// WithExit causes the task to exit after a successful checkpoint
|
||||||
|
func WithExit(r *CheckpointTaskInfo) error {
|
||||||
|
r.Options = &runcopts.CheckpointOptions{
|
||||||
|
Exit: true,
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user