diff --git a/README.md b/README.md index dab91d222..a57b25d44 100644 --- a/README.md +++ b/README.md @@ -86,7 +86,7 @@ image, err := client.Pull(context, "docker.io/library/redis:latest", containerd. // allocate a new RW root filesystem for a container based on the image redis, err := client.NewContainer(context, "redis-master", containerd.WithSpec(spec), - containerd.WithNewRootFS("redis-rootfs", image), + containerd.WithNewSnapshot("redis-rootfs", image), ) // use a readonly filesystem with multiple containers @@ -94,7 +94,7 @@ for i := 0; i < 10; i++ { id := fmt.Sprintf("id-%s", i) container, err := client.NewContainer(ctx, id, containerd.WithSpec(spec), - containerd.WithNewReadonlyRootFS(id, image), + containerd.WithNewSnapshotView(id, image), ) } ``` diff --git a/benchmark_test.go b/benchmark_test.go index 3920f497a..1b154de87 100644 --- a/benchmark_test.go +++ b/benchmark_test.go @@ -28,7 +28,7 @@ func BenchmarkContainerCreate(b *testing.B) { var containers []Container defer func() { for _, c := range containers { - if err := c.Delete(ctx, WithRootFSDeletion); err != nil { + if err := c.Delete(ctx, WithSnapshotCleanup); err != nil { b.Error(err) } } @@ -38,7 +38,7 @@ func BenchmarkContainerCreate(b *testing.B) { b.ResetTimer() for i := 0; i < b.N; i++ { id := fmt.Sprintf("%s-%d", b.Name(), i) - container, err := client.NewContainer(ctx, id, WithSpec(spec), WithNewRootFS(id, image)) + container, err := client.NewContainer(ctx, id, WithSpec(spec), WithNewSnapshot(id, image)) if err != nil { b.Error(err) return @@ -71,7 +71,7 @@ func BenchmarkContainerStart(b *testing.B) { var containers []Container defer func() { for _, c := range containers { - if err := c.Delete(ctx, WithRootFSDeletion); err != nil { + if err := c.Delete(ctx, WithSnapshotCleanup); err != nil { b.Error(err) } } @@ -79,7 +79,7 @@ func BenchmarkContainerStart(b *testing.B) { for i := 0; i < b.N; i++ { id := fmt.Sprintf("%s-%d", b.Name(), i) - container, err := client.NewContainer(ctx, id, WithSpec(spec), WithNewRootFS(id, image)) + container, err := client.NewContainer(ctx, id, WithSpec(spec), WithNewSnapshot(id, image)) if err != nil { b.Error(err) return diff --git a/checkpoint_test.go b/checkpoint_test.go index 599ac7f4b..8fd1fba16 100644 --- a/checkpoint_test.go +++ b/checkpoint_test.go @@ -33,12 +33,12 @@ func TestCheckpointRestore(t *testing.T) { t.Error(err) return } - container, err := client.NewContainer(ctx, id, WithSpec(spec), WithNewRootFS(id, image)) + container, err := client.NewContainer(ctx, id, WithSpec(spec), WithNewSnapshot(id, image)) if err != nil { t.Error(err) return } - defer container.Delete(ctx, WithRootFSDeletion) + defer container.Delete(ctx, WithSnapshotCleanup) task, err := container.NewTask(ctx, empty()) if err != nil { @@ -123,12 +123,12 @@ func TestCheckpointRestoreNewContainer(t *testing.T) { t.Error(err) return } - container, err := client.NewContainer(ctx, id, WithSpec(spec), WithNewRootFS(id, image)) + container, err := client.NewContainer(ctx, id, WithSpec(spec), WithNewSnapshot(id, image)) if err != nil { t.Error(err) return } - defer container.Delete(ctx, WithRootFSDeletion) + defer container.Delete(ctx, WithSnapshotCleanup) task, err := container.NewTask(ctx, empty()) if err != nil { @@ -163,7 +163,7 @@ func TestCheckpointRestoreNewContainer(t *testing.T) { t.Error(err) return } - if err := container.Delete(ctx, WithRootFSDeletion); err != nil { + if err := container.Delete(ctx, WithSnapshotCleanup); err != nil { t.Error(err) return } @@ -226,12 +226,12 @@ func TestCheckpointLeaveRunning(t *testing.T) { t.Error(err) return } - container, err := client.NewContainer(ctx, id, WithSpec(spec), WithNewRootFS(id, image)) + container, err := client.NewContainer(ctx, id, WithSpec(spec), WithNewSnapshot(id, image)) if err != nil { t.Error(err) return } - defer container.Delete(ctx, WithRootFSDeletion) + defer container.Delete(ctx, WithSnapshotCleanup) task, err := container.NewTask(ctx, empty()) if err != nil { diff --git a/client.go b/client.go index f8b4b904d..92ff67092 100644 --- a/client.go +++ b/client.go @@ -161,8 +161,8 @@ func WithContainerLabels(labels map[string]string) NewContainerOpts { } } -// WithExistingRootFS uses an existing root filesystem for the container -func WithExistingRootFS(id string) NewContainerOpts { +// 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 { @@ -173,9 +173,9 @@ func WithExistingRootFS(id string) NewContainerOpts { } } -// WithNewRootFS allocates a new snapshot to be used by the container as the +// WithNewSnapshot allocates a new snapshot to be used by the container as the // root filesystem in read-write mode -func WithNewRootFS(id string, i Image) NewContainerOpts { +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 { @@ -190,9 +190,9 @@ func WithNewRootFS(id string, i Image) NewContainerOpts { } } -// WithNewReadonlyRootFS allocates a new snapshot to be used by the container as the +// WithNewSnapshotView allocates a new snapshot to be used by the container as the // root filesystem in read-only mode -func WithNewReadonlyRootFS(id string, i Image) NewContainerOpts { +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 { diff --git a/cmd/ctr/delete.go b/cmd/ctr/delete.go index ba579b9f3..290513db9 100644 --- a/cmd/ctr/delete.go +++ b/cmd/ctr/delete.go @@ -26,7 +26,7 @@ var deleteCommand = cli.Command{ } deleteOpts := []containerd.DeleteOpts{} if !context.Bool("keep-snapshot") { - deleteOpts = append(deleteOpts, containerd.WithRootFSDeletion) + deleteOpts = append(deleteOpts, containerd.WithSnapshotCleanup) } container, err := client.LoadContainer(ctx, context.Args().First()) if err != nil { diff --git a/cmd/ctr/run.go b/cmd/ctr/run.go index 4a892bc58..2ebb65b17 100644 --- a/cmd/ctr/run.go +++ b/cmd/ctr/run.go @@ -119,7 +119,7 @@ var runCommand = cli.Command{ return err } if context.Bool("rm") { - defer container.Delete(ctx, containerd.WithRootFSDeletion) + defer container.Delete(ctx, containerd.WithSnapshotCleanup) } task, err := newTask(ctx, container, checkpointIndex, tty) if err != nil { diff --git a/cmd/ctr/run_unix.go b/cmd/ctr/run_unix.go index 51a477382..79259a21e 100644 --- a/cmd/ctr/run_unix.go +++ b/cmd/ctr/run_unix.go @@ -97,9 +97,9 @@ func newContainer(ctx gocontext.Context, client *containerd.Client, context *cli } var rootfs containerd.NewContainerOpts if context.Bool("readonly") { - rootfs = containerd.WithNewReadonlyRootFS(id, image) + rootfs = containerd.WithNewSnapshotView(id, image) } else { - rootfs = containerd.WithNewRootFS(id, image) + rootfs = containerd.WithNewSnapshot(id, image) } return client.NewContainer(ctx, id, diff --git a/container.go b/container.go index 04be3ad29..3da1eefac 100644 --- a/container.go +++ b/container.go @@ -11,6 +11,7 @@ import ( "github.com/containerd/containerd/api/types" "github.com/containerd/containerd/containers" "github.com/containerd/containerd/errdefs" + "github.com/containerd/containerd/mount" "github.com/containerd/containerd/typeurl" specs "github.com/opencontainers/runtime-spec/specs-go" "github.com/pkg/errors" @@ -107,8 +108,8 @@ func (c *container) Spec() (*specs.Spec, error) { return &s, nil } -// WithRootFSDeletion deletes the rootfs allocated for the container -func WithRootFSDeletion(ctx context.Context, client *Client, c containers.Container) error { +// 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) } @@ -154,6 +155,13 @@ func (c *container) Image(ctx context.Context) (Image, error) { type NewTaskOpts func(context.Context, *Client, *TaskInfo) error +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) { c.mu.Lock() defer c.mu.Unlock() @@ -188,6 +196,15 @@ func (c *container) NewTask(ctx context.Context, ioCreate IOCreation, opts ...Ne return nil, err } } + if info.RootFS != nil { + for _, m := range info.RootFS { + request.Rootfs = append(request.Rootfs, &types.Mount{ + Type: m.Type, + Source: m.Source, + Options: m.Options, + }) + } + } if info.Options != nil { any, err := typeurl.MarshalAny(info.Options) if err != nil { diff --git a/container_linux_test.go b/container_linux_test.go index a31c4e063..cd676614b 100644 --- a/container_linux_test.go +++ b/container_linux_test.go @@ -37,12 +37,12 @@ func TestContainerUpdate(t *testing.T) { spec.Linux.Resources.Memory = &specs.LinuxMemory{ Limit: &limit, } - container, err := client.NewContainer(ctx, id, WithSpec(spec), WithNewRootFS(id, image)) + container, err := client.NewContainer(ctx, id, WithSpec(spec), WithNewSnapshot(id, image)) if err != nil { t.Error(err) return } - defer container.Delete(ctx, WithRootFSDeletion) + defer container.Delete(ctx, WithSnapshotCleanup) task, err := container.NewTask(ctx, empty()) if err != nil { diff --git a/container_test.go b/container_test.go index a285ef4f5..38e8b699f 100644 --- a/container_test.go +++ b/container_test.go @@ -106,12 +106,12 @@ func TestContainerStart(t *testing.T) { t.Error(err) return } - container, err := client.NewContainer(ctx, id, WithSpec(spec), withNewRootFS(id, image)) + container, err := client.NewContainer(ctx, id, WithSpec(spec), withNewSnapshot(id, image)) if err != nil { t.Error(err) return } - defer container.Delete(ctx, WithRootFSDeletion) + defer container.Delete(ctx, WithSnapshotCleanup) task, err := container.NewTask(ctx, Stdio) if err != nil { @@ -178,12 +178,12 @@ func TestContainerOutput(t *testing.T) { t.Error(err) return } - container, err := client.NewContainer(ctx, id, WithSpec(spec), withNewRootFS(id, image)) + container, err := client.NewContainer(ctx, id, WithSpec(spec), withNewSnapshot(id, image)) if err != nil { t.Error(err) return } - defer container.Delete(ctx, WithRootFSDeletion) + defer container.Delete(ctx, WithSnapshotCleanup) stdout := bytes.NewBuffer(nil) task, err := container.NewTask(ctx, NewIO(bytes.NewBuffer(nil), stdout, bytes.NewBuffer(nil))) @@ -251,12 +251,12 @@ func TestContainerExec(t *testing.T) { t.Error(err) return } - container, err := client.NewContainer(ctx, id, WithSpec(spec), withNewRootFS(id, image)) + container, err := client.NewContainer(ctx, id, WithSpec(spec), withNewSnapshot(id, image)) if err != nil { t.Error(err) return } - defer container.Delete(ctx, WithRootFSDeletion) + defer container.Delete(ctx, WithSnapshotCleanup) task, err := container.NewTask(ctx, empty()) if err != nil { @@ -348,12 +348,12 @@ func TestContainerPids(t *testing.T) { t.Error(err) return } - container, err := client.NewContainer(ctx, id, WithSpec(spec), withNewRootFS(id, image)) + container, err := client.NewContainer(ctx, id, WithSpec(spec), withNewSnapshot(id, image)) if err != nil { t.Error(err) return } - defer container.Delete(ctx, WithRootFSDeletion) + defer container.Delete(ctx, WithSnapshotCleanup) task, err := container.NewTask(ctx, empty()) if err != nil { @@ -427,12 +427,12 @@ func TestContainerCloseIO(t *testing.T) { t.Error(err) return } - container, err := client.NewContainer(ctx, id, WithSpec(spec), withNewRootFS(id, image)) + container, err := client.NewContainer(ctx, id, WithSpec(spec), withNewSnapshot(id, image)) if err != nil { t.Error(err) return } - defer container.Delete(ctx, WithRootFSDeletion) + defer container.Delete(ctx, WithSnapshotCleanup) const expected = "hello" + newLine stdout := bytes.NewBuffer(nil) @@ -525,12 +525,12 @@ func TestContainerAttach(t *testing.T) { t.Error(err) return } - container, err := client.NewContainer(ctx, id, WithSpec(spec), withNewRootFS(id, image)) + container, err := client.NewContainer(ctx, id, WithSpec(spec), withNewSnapshot(id, image)) if err != nil { t.Error(err) return } - defer container.Delete(ctx, WithRootFSDeletion) + defer container.Delete(ctx, WithSnapshotCleanup) expected := "hello" + newLine stdout := bytes.NewBuffer(nil) @@ -651,12 +651,12 @@ func TestDeleteRunningContainer(t *testing.T) { t.Error(err) return } - container, err := client.NewContainer(ctx, id, WithSpec(spec), withNewRootFS(id, image)) + container, err := client.NewContainer(ctx, id, WithSpec(spec), withNewSnapshot(id, image)) if err != nil { t.Error(err) return } - defer container.Delete(ctx, WithRootFSDeletion) + defer container.Delete(ctx, WithSnapshotCleanup) task, err := container.NewTask(ctx, empty()) if err != nil { @@ -679,7 +679,7 @@ func TestDeleteRunningContainer(t *testing.T) { return } - err = container.Delete(ctx, WithRootFSDeletion) + err = container.Delete(ctx, WithSnapshotCleanup) if err == nil { t.Error("delete did not error with running task") } @@ -720,7 +720,7 @@ func TestContainerKill(t *testing.T) { t.Error(err) return } - container, err := client.NewContainer(ctx, id, WithSpec(spec), withNewRootFS(id, image)) + container, err := client.NewContainer(ctx, id, WithSpec(spec), withNewSnapshot(id, image)) if err != nil { t.Error(err) return @@ -790,12 +790,12 @@ func TestContainerNoBinaryExists(t *testing.T) { t.Error(err) return } - container, err := client.NewContainer(ctx, id, WithSpec(spec), withNewRootFS(id, image)) + container, err := client.NewContainer(ctx, id, WithSpec(spec), withNewSnapshot(id, image)) if err != nil { t.Error(err) return } - defer container.Delete(ctx, WithRootFSDeletion) + defer container.Delete(ctx, WithSnapshotCleanup) task, err := container.NewTask(ctx, Stdio) switch runtime.GOOS { @@ -842,12 +842,12 @@ func TestContainerExecNoBinaryExists(t *testing.T) { t.Error(err) return } - container, err := client.NewContainer(ctx, id, WithSpec(spec), withNewRootFS(id, image)) + container, err := client.NewContainer(ctx, id, WithSpec(spec), withNewSnapshot(id, image)) if err != nil { t.Error(err) return } - defer container.Delete(ctx, WithRootFSDeletion) + defer container.Delete(ctx, WithSnapshotCleanup) task, err := container.NewTask(ctx, empty()) if err != nil { diff --git a/helpers_unix_test.go b/helpers_unix_test.go index 604caced7..7186d70fe 100644 --- a/helpers_unix_test.go +++ b/helpers_unix_test.go @@ -46,6 +46,6 @@ func withImageConfig(ctx context.Context, i Image) SpecOpts { return WithImageConfig(ctx, i) } -func withNewRootFS(id string, i Image) NewContainerOpts { - return WithNewRootFS(id, i) +func withNewSnapshot(id string, i Image) NewContainerOpts { + return WithNewSnapshot(id, i) } diff --git a/helpers_windows_test.go b/helpers_windows_test.go index d5c2bf5d6..9a6e632a2 100644 --- a/helpers_windows_test.go +++ b/helpers_windows_test.go @@ -57,8 +57,8 @@ func withImageConfig(ctx context.Context, i Image) SpecOpts { } } -func withNewRootFS(id string, i Image) NewContainerOpts { - // TODO: when windows has a snapshotter remove the withNewRootFS helper +func withNewSnapshot(id string, i Image) NewContainerOpts { + // TODO: when windows has a snapshotter remove the withNewSnapshot helper return func(ctx context.Context, client *Client, c *containers.Container) error { return nil } diff --git a/task.go b/task.go index 61191d312..015c8cc7c 100644 --- a/task.go +++ b/task.go @@ -16,6 +16,7 @@ import ( "github.com/containerd/containerd/content" "github.com/containerd/containerd/errdefs" "github.com/containerd/containerd/linux/runcopts" + "github.com/containerd/containerd/mount" "github.com/containerd/containerd/rootfs" "github.com/containerd/containerd/typeurl" digest "github.com/opencontainers/go-digest" @@ -55,6 +56,7 @@ type CheckpointTaskOpts func(*CheckpointTaskInfo) error type TaskInfo struct { Checkpoint *types.Descriptor + RootFS []mount.Mount Options interface{} }