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 <suda.akihiro@lab.ntt.co.jp>
This commit is contained in:
Akihiro Suda 2017-07-13 09:53:32 +00:00
parent 1a054c67b1
commit 752d253f40
3 changed files with 74 additions and 56 deletions

View File

@ -47,7 +47,7 @@ func withMounts(context *cli.Context) containerd.SpecOpts {
var runCommand = cli.Command{ var runCommand = cli.Command{
Name: "run", Name: "run",
Usage: "run a container", Usage: "run a container",
ArgsUsage: "IMAGE CONTAINER [COMMAND] [ARG...]", ArgsUsage: "Image|RootFS ID [COMMAND] [ARG...]",
Flags: append([]cli.Flag{ Flags: append([]cli.Flag{
cli.BoolFlag{ cli.BoolFlag{
Name: "tty,t", Name: "tty,t",

View File

@ -18,6 +18,13 @@ import (
"github.com/urfave/cli" "github.com/urfave/cli"
) )
func init() {
runCommand.Flags = append(runCommand.Flags, cli.BoolFlag{
Name: "rootfs",
Usage: "Use custom rootfs that is not managed by containerd snapshotter.",
})
}
func handleConsoleResize(ctx gocontext.Context, task resizer, con console.Console) error { func handleConsoleResize(ctx gocontext.Context, task resizer, con console.Console) error {
// do an initial resize of the console // do an initial resize of the console
size, err := con.Size() size, err := con.Size()
@ -54,66 +61,61 @@ func setHostNetworking() containerd.SpecOpts {
func newContainer(ctx gocontext.Context, client *containerd.Client, context *cli.Context) (containerd.Container, error) { func newContainer(ctx gocontext.Context, client *containerd.Client, context *cli.Context) (containerd.Container, error) {
var ( var (
err error ref = context.Args().First()
checkpointIndex digest.Digest id = context.Args().Get(1)
args = context.Args()[2:]
ref = context.Args().First()
id = context.Args().Get(1)
args = context.Args()[2:]
tty = context.Bool("tty")
labelStrings = context.StringSlice("label")
) )
labels := labelArgs(labelStrings)
if raw := context.String("checkpoint"); raw != "" { if raw := context.String("checkpoint"); raw != "" {
if checkpointIndex, err = digest.Parse(raw); err != nil { checkpointIndex, err := digest.Parse(raw)
return nil, err
}
}
image, err := client.GetImage(ctx, ref)
if err != nil {
return nil, err
}
if checkpointIndex == "" {
opts := []containerd.SpecOpts{
containerd.WithImageConfig(ctx, image),
withEnv(context),
withMounts(context),
}
if len(args) > 0 {
opts = append(opts, containerd.WithProcessArgs(args...))
}
if tty {
opts = append(opts, withTTY())
}
if context.Bool("net-host") {
opts = append(opts, setHostNetworking())
}
spec, err := containerd.GenerateSpec(opts...)
if err != nil { if err != nil {
return nil, err return nil, err
} }
var rootfs containerd.NewContainerOpts if checkpointIndex != "" {
if context.Bool("readonly") { return client.NewContainer(ctx, id, containerd.WithCheckpoint(v1.Descriptor{
rootfs = containerd.WithNewSnapshotView(id, image) Digest: checkpointIndex,
} else { }, id))
rootfs = containerd.WithNewSnapshot(id, image)
} }
return client.NewContainer(ctx, id,
containerd.WithSpec(spec),
containerd.WithImage(image),
containerd.WithContainerLabels(labels),
containerd.WithSnapshotter(context.String("snapshotter")),
rootfs,
)
} }
return client.NewContainer(ctx, id, containerd.WithCheckpoint(v1.Descriptor{ var (
Digest: checkpointIndex, opts []containerd.SpecOpts
}, id)) cOpts []containerd.NewContainerOpts
)
cOpts = append(cOpts, containerd.WithContainerLabels(labelArgs(context.StringSlice("label"))))
if context.Bool("rootfs") {
opts = append(opts, containerd.WithRootFSPath(ref, context.Bool("readonly")))
} else {
image, err := client.GetImage(ctx, ref)
if err != nil {
return nil, err
}
opts = append(opts, containerd.WithImageConfig(ctx, image))
cOpts = append(cOpts, containerd.WithImage(image))
if context.Bool("readonly") {
cOpts = append(cOpts, containerd.WithNewSnapshotView(id, image))
} else {
cOpts = append(cOpts, containerd.WithNewSnapshot(id, image))
}
cOpts = append(cOpts, containerd.WithSnapshotter(context.String("snapshotter")))
}
opts = append(opts, withEnv(context), withMounts(context))
if len(args) > 0 {
opts = append(opts, containerd.WithProcessArgs(args...))
}
if context.Bool("tty") {
opts = append(opts, withTTY())
}
if context.Bool("net-host") {
opts = append(opts, setHostNetworking())
}
spec, err := containerd.GenerateSpec(opts...)
if err != nil {
return nil, err
}
cOpts = append([]containerd.NewContainerOpts{containerd.WithSpec(spec)}, cOpts...)
return client.NewContainer(ctx, id, cOpts...)
} }
func newTask(ctx gocontext.Context, container containerd.Container, checkpoint digest.Digest, tty bool) (containerd.Task, error) { func newTask(ctx gocontext.Context, container containerd.Container, checkpoint digest.Digest, tty bool) (containerd.Task, error) {

View File

@ -30,6 +30,12 @@ const (
defaultRootfsPath = "rootfs" defaultRootfsPath = "rootfs"
) )
var (
defaultEnv = []string{
"PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin",
}
)
func defaltCaps() []string { func defaltCaps() []string {
return []string{ return []string{
"CAP_CHOWN", "CAP_CHOWN",
@ -76,6 +82,7 @@ func createDefaultSpec() (*specs.Spec, error) {
Path: defaultRootfsPath, Path: defaultRootfsPath,
}, },
Process: &specs.Process{ Process: &specs.Process{
Env: defaultEnv,
Cwd: "/", Cwd: "/",
NoNewPrivileges: true, NoNewPrivileges: true,
User: specs.User{ User: specs.User{
@ -220,10 +227,7 @@ func WithImageConfig(ctx context.Context, i Image) SpecOpts {
default: default:
return fmt.Errorf("unknown image config media type %s", ic.MediaType) return fmt.Errorf("unknown image config media type %s", ic.MediaType)
} }
env := []string{ s.Process.Env = append(s.Process.Env, config.Env...)
"PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin",
}
s.Process.Env = append(env, config.Env...)
var ( var (
uid, gid uint32 uid, gid uint32
) )
@ -262,6 +266,18 @@ func WithImageConfig(ctx context.Context, i Image) SpecOpts {
} }
} }
// 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 // WithSpec sets the provided spec for a new container
func WithSpec(spec *specs.Spec) NewContainerOpts { func WithSpec(spec *specs.Spec) NewContainerOpts {
return func(ctx context.Context, client *Client, c *containers.Container) error { return func(ctx context.Context, client *Client, c *containers.Container) error {