Skip to content

Commit 752d253

Browse files
committed
client, ctr: allow specifying unmanaged rootfs dir
e.g. ctr run -t --rm --rootfs /tmp/busybox-rootfs foo /bin/sh (--rm removes the container but does not remove rootfs dir, of course) Signed-off-by: Akihiro Suda <[email protected]>
1 parent 1a054c6 commit 752d253

3 files changed

Lines changed: 68 additions & 50 deletions

File tree

cmd/ctr/run.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,7 @@ func withMounts(context *cli.Context) containerd.SpecOpts {
4747
var runCommand = cli.Command{
4848
Name: "run",
4949
Usage: "run a container",
50-
ArgsUsage: "IMAGE CONTAINER [COMMAND] [ARG...]",
50+
ArgsUsage: "Image|RootFS ID [COMMAND] [ARG...]",
5151
Flags: append([]cli.Flag{
5252
cli.BoolFlag{
5353
Name: "tty,t",

cmd/ctr/run_unix.go

Lines changed: 47 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,13 @@ import (
1818
"github.com/urfave/cli"
1919
)
2020

21+
func init() {
22+
runCommand.Flags = append(runCommand.Flags, cli.BoolFlag{
23+
Name: "rootfs",
24+
Usage: "Use custom rootfs that is not managed by containerd snapshotter.",
25+
})
26+
}
27+
2128
func handleConsoleResize(ctx gocontext.Context, task resizer, con console.Console) error {
2229
// do an initial resize of the console
2330
size, err := con.Size()
@@ -54,66 +61,61 @@ func setHostNetworking() containerd.SpecOpts {
5461

5562
func newContainer(ctx gocontext.Context, client *containerd.Client, context *cli.Context) (containerd.Container, error) {
5663
var (
57-
err error
58-
checkpointIndex digest.Digest
59-
60-
ref = context.Args().First()
61-
id = context.Args().Get(1)
62-
args = context.Args()[2:]
63-
tty = context.Bool("tty")
64-
labelStrings = context.StringSlice("label")
64+
ref = context.Args().First()
65+
id = context.Args().Get(1)
66+
args = context.Args()[2:]
6567
)
6668

67-
labels := labelArgs(labelStrings)
68-
6969
if raw := context.String("checkpoint"); raw != "" {
70-
if checkpointIndex, err = digest.Parse(raw); err != nil {
70+
checkpointIndex, err := digest.Parse(raw)
71+
if err != nil {
7172
return nil, err
7273
}
73-
}
74-
image, err := client.GetImage(ctx, ref)
75-
if err != nil {
76-
return nil, err
74+
if checkpointIndex != "" {
75+
return client.NewContainer(ctx, id, containerd.WithCheckpoint(v1.Descriptor{
76+
Digest: checkpointIndex,
77+
}, id))
78+
}
7779
}
7880

79-
if checkpointIndex == "" {
80-
opts := []containerd.SpecOpts{
81-
containerd.WithImageConfig(ctx, image),
82-
withEnv(context),
83-
withMounts(context),
84-
}
85-
if len(args) > 0 {
86-
opts = append(opts, containerd.WithProcessArgs(args...))
87-
}
88-
if tty {
89-
opts = append(opts, withTTY())
90-
}
91-
if context.Bool("net-host") {
92-
opts = append(opts, setHostNetworking())
93-
}
94-
spec, err := containerd.GenerateSpec(opts...)
81+
var (
82+
opts []containerd.SpecOpts
83+
cOpts []containerd.NewContainerOpts
84+
)
85+
cOpts = append(cOpts, containerd.WithContainerLabels(labelArgs(context.StringSlice("label"))))
86+
if context.Bool("rootfs") {
87+
opts = append(opts, containerd.WithRootFSPath(ref, context.Bool("readonly")))
88+
} else {
89+
image, err := client.GetImage(ctx, ref)
9590
if err != nil {
9691
return nil, err
9792
}
98-
var rootfs containerd.NewContainerOpts
93+
opts = append(opts, containerd.WithImageConfig(ctx, image))
94+
cOpts = append(cOpts, containerd.WithImage(image))
9995
if context.Bool("readonly") {
100-
rootfs = containerd.WithNewSnapshotView(id, image)
96+
cOpts = append(cOpts, containerd.WithNewSnapshotView(id, image))
10197
} else {
102-
rootfs = containerd.WithNewSnapshot(id, image)
98+
cOpts = append(cOpts, containerd.WithNewSnapshot(id, image))
10399
}
104-
105-
return client.NewContainer(ctx, id,
106-
containerd.WithSpec(spec),
107-
containerd.WithImage(image),
108-
containerd.WithContainerLabels(labels),
109-
containerd.WithSnapshotter(context.String("snapshotter")),
110-
rootfs,
111-
)
100+
cOpts = append(cOpts, containerd.WithSnapshotter(context.String("snapshotter")))
112101
}
113102

114-
return client.NewContainer(ctx, id, containerd.WithCheckpoint(v1.Descriptor{
115-
Digest: checkpointIndex,
116-
}, id))
103+
opts = append(opts, withEnv(context), withMounts(context))
104+
if len(args) > 0 {
105+
opts = append(opts, containerd.WithProcessArgs(args...))
106+
}
107+
if context.Bool("tty") {
108+
opts = append(opts, withTTY())
109+
}
110+
if context.Bool("net-host") {
111+
opts = append(opts, setHostNetworking())
112+
}
113+
spec, err := containerd.GenerateSpec(opts...)
114+
if err != nil {
115+
return nil, err
116+
}
117+
cOpts = append([]containerd.NewContainerOpts{containerd.WithSpec(spec)}, cOpts...)
118+
return client.NewContainer(ctx, id, cOpts...)
117119
}
118120

119121
func newTask(ctx gocontext.Context, container containerd.Container, checkpoint digest.Digest, tty bool) (containerd.Task, error) {

spec_unix.go

Lines changed: 20 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,12 @@ const (
3030
defaultRootfsPath = "rootfs"
3131
)
3232

33+
var (
34+
defaultEnv = []string{
35+
"PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin",
36+
}
37+
)
38+
3339
func defaltCaps() []string {
3440
return []string{
3541
"CAP_CHOWN",
@@ -76,6 +82,7 @@ func createDefaultSpec() (*specs.Spec, error) {
7682
Path: defaultRootfsPath,
7783
},
7884
Process: &specs.Process{
85+
Env: defaultEnv,
7986
Cwd: "/",
8087
NoNewPrivileges: true,
8188
User: specs.User{
@@ -220,10 +227,7 @@ func WithImageConfig(ctx context.Context, i Image) SpecOpts {
220227
default:
221228
return fmt.Errorf("unknown image config media type %s", ic.MediaType)
222229
}
223-
env := []string{
224-
"PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin",
225-
}
226-
s.Process.Env = append(env, config.Env...)
230+
s.Process.Env = append(s.Process.Env, config.Env...)
227231
var (
228232
uid, gid uint32
229233
)
@@ -262,6 +266,18 @@ func WithImageConfig(ctx context.Context, i Image) SpecOpts {
262266
}
263267
}
264268

269+
// WithRootFSPath specifies unmanaged rootfs path.
270+
func WithRootFSPath(path string, readonly bool) SpecOpts {
271+
return func(s *specs.Spec) error {
272+
s.Root = &specs.Root{
273+
Path: path,
274+
Readonly: readonly,
275+
}
276+
// Entrypoint is not set here (it's up to caller)
277+
return nil
278+
}
279+
}
280+
265281
// WithSpec sets the provided spec for a new container
266282
func WithSpec(spec *specs.Spec) NewContainerOpts {
267283
return func(ctx context.Context, client *Client, c *containers.Container) error {

0 commit comments

Comments
 (0)