diff --git a/cmd/ctr/run_unix.go b/cmd/ctr/run_unix.go index f6037fc31..e29f3f157 100644 --- a/cmd/ctr/run_unix.go +++ b/cmd/ctr/run_unix.go @@ -84,7 +84,7 @@ func newContainer(ctx gocontext.Context, client *containerd.Client, context *cli ) cOpts = append(cOpts, containerd.WithContainerLabels(labelArgs(context.StringSlice("label")))) if context.Bool("rootfs") { - opts = append(opts, containerd.WithRootFSPath(ref, context.Bool("readonly"))) + opts = append(opts, containerd.WithRootFSPath(ref)) } else { image, err := client.GetImage(ctx, ref) if err != nil { @@ -93,11 +93,13 @@ func newContainer(ctx gocontext.Context, client *containerd.Client, context *cli opts = append(opts, containerd.WithImageConfig(image)) cOpts = append(cOpts, containerd.WithImage(image)) cOpts = append(cOpts, containerd.WithSnapshotter(context.String("snapshotter"))) - if context.Bool("readonly") { - cOpts = append(cOpts, containerd.WithNewSnapshotView(id, image)) - } else { - cOpts = append(cOpts, containerd.WithNewSnapshot(id, image)) - } + // Even when "readonly" is set, we don't use KindView snapshot here. (#1495) + // We pass writable snapshot to the OCI runtime, and the runtime remounts it as read-only, + // after creating some mount points on demand. + cOpts = append(cOpts, containerd.WithNewSnapshot(id, image)) + } + if context.Bool("readonly") { + opts = append(opts, containerd.WithRootFSReadonly()) } cOpts = append(cOpts, containerd.WithRuntime(context.String("runtime"), nil)) diff --git a/spec_opts_unix.go b/spec_opts_unix.go index 0f32a0d7b..337095cfa 100644 --- a/spec_opts_unix.go +++ b/spec_opts_unix.go @@ -138,17 +138,28 @@ func WithImageConfig(i Image) SpecOpts { } // WithRootFSPath specifies unmanaged rootfs path. -func WithRootFSPath(path string, readonly bool) SpecOpts { +func WithRootFSPath(path string) SpecOpts { return func(_ context.Context, _ *Client, _ *containers.Container, s *specs.Spec) error { - s.Root = &specs.Root{ - Path: path, - Readonly: readonly, + if s.Root == nil { + s.Root = &specs.Root{} } + s.Root.Path = path // Entrypoint is not set here (it's up to caller) return nil } } +// WithRootFSReadonly sets specs.Root.Readonly to true +func WithRootFSReadonly() SpecOpts { + return func(_ context.Context, _ *Client, _ *containers.Container, s *specs.Spec) error { + if s.Root == nil { + s.Root = &specs.Root{} + } + s.Root.Readonly = true + 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 {