ctr: add container create, config flag for spec
Signed-off-by: Jess Valarezo <valarezo.jessica@gmail.com>
This commit is contained in:
parent
b268261446
commit
2c9ce2e693
@ -10,6 +10,7 @@ import (
|
||||
|
||||
"github.com/containerd/containerd"
|
||||
"github.com/containerd/containerd/cmd/ctr/commands"
|
||||
"github.com/containerd/containerd/cmd/ctr/commands/run"
|
||||
"github.com/containerd/containerd/log"
|
||||
"github.com/urfave/cli"
|
||||
)
|
||||
@ -20,6 +21,7 @@ var Command = cli.Command{
|
||||
Usage: "manage containers",
|
||||
Aliases: []string{"c", "container"},
|
||||
Subcommands: []cli.Command{
|
||||
createCommand,
|
||||
deleteCommand,
|
||||
infoCommand,
|
||||
listCommand,
|
||||
@ -27,6 +29,35 @@ var Command = cli.Command{
|
||||
},
|
||||
}
|
||||
|
||||
var createCommand = cli.Command{
|
||||
Name: "create",
|
||||
Usage: "create container",
|
||||
ArgsUsage: "[flags] Image|RootFS CONTAINER",
|
||||
Flags: append(commands.SnapshotterFlags, run.ContainerFlags...),
|
||||
Action: func(context *cli.Context) error {
|
||||
var (
|
||||
id = context.Args().Get(1)
|
||||
ref = context.Args().First()
|
||||
)
|
||||
if ref == "" {
|
||||
return errors.New("image ref must be provided")
|
||||
}
|
||||
if id == "" {
|
||||
return errors.New("container id must be provided")
|
||||
}
|
||||
client, ctx, cancel, err := commands.NewClient(context)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer cancel()
|
||||
_, err = run.NewContainer(ctx, client, context)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
},
|
||||
}
|
||||
|
||||
var listCommand = cli.Command{
|
||||
Name: "list",
|
||||
Aliases: []string{"ls"},
|
||||
@ -146,13 +177,13 @@ func deleteContainer(ctx context.Context, client *containerd.Client, id string,
|
||||
var setLabelsCommand = cli.Command{
|
||||
Name: "label",
|
||||
Usage: "set and clear labels for a container",
|
||||
ArgsUsage: "[flags] <name> [<key>=<value>, ...]",
|
||||
ArgsUsage: "[flags] CONTAINER [<key>=<value>, ...]",
|
||||
Description: "set and clear labels for a container",
|
||||
Flags: []cli.Flag{},
|
||||
Action: func(context *cli.Context) error {
|
||||
containerID, labels := commands.ObjectWithLabelArgs(context)
|
||||
if containerID == "" {
|
||||
return errors.New("please specify a container")
|
||||
return errors.New("container id must be provided")
|
||||
}
|
||||
client, ctx, cancel, err := commands.NewClient(context)
|
||||
if err != nil {
|
||||
|
@ -3,7 +3,9 @@ package run
|
||||
import (
|
||||
gocontext "context"
|
||||
"encoding/csv"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"runtime"
|
||||
"strings"
|
||||
|
||||
@ -20,6 +22,62 @@ import (
|
||||
"github.com/urfave/cli"
|
||||
)
|
||||
|
||||
// ContainerFlags are cli flags specifying container options
|
||||
var ContainerFlags = []cli.Flag{
|
||||
cli.StringFlag{
|
||||
Name: "config,c",
|
||||
Usage: "path to the runtime-specific spec config file",
|
||||
},
|
||||
cli.StringFlag{
|
||||
Name: "checkpoint",
|
||||
Usage: "provide the checkpoint digest to restore the container",
|
||||
},
|
||||
cli.StringFlag{
|
||||
Name: "cwd",
|
||||
Usage: "specify the working directory of the process",
|
||||
},
|
||||
cli.StringSliceFlag{
|
||||
Name: "env",
|
||||
Usage: "specify additional container environment variables (i.e. FOO=bar)",
|
||||
},
|
||||
cli.StringSliceFlag{
|
||||
Name: "label",
|
||||
Usage: "specify additional labels (i.e. foo=bar)",
|
||||
},
|
||||
cli.StringSliceFlag{
|
||||
Name: "mount",
|
||||
Usage: "specify additional container mount (ex: type=bind,src=/tmp,dest=/host,options=rbind:ro)",
|
||||
},
|
||||
cli.BoolFlag{
|
||||
Name: "net-host",
|
||||
Usage: "enable host networking for the container",
|
||||
},
|
||||
cli.BoolFlag{
|
||||
Name: "read-only",
|
||||
Usage: "set the containers filesystem as readonly",
|
||||
},
|
||||
cli.StringFlag{
|
||||
Name: "runtime",
|
||||
Usage: "runtime name (io.containerd.runtime.v1.linux, io.containerd.runtime.v1.windows, io.containerd.runtime.v1.com.vmware.linux)",
|
||||
Value: fmt.Sprintf("io.containerd.runtime.v1.%s", runtime.GOOS),
|
||||
},
|
||||
cli.BoolFlag{
|
||||
Name: "tty,t",
|
||||
Usage: "allocate a TTY for the container",
|
||||
},
|
||||
}
|
||||
|
||||
func loadSpec(path string, s *specs.Spec) error {
|
||||
raw, err := ioutil.ReadFile(path)
|
||||
if err != nil {
|
||||
return errors.New("cannot load spec config file")
|
||||
}
|
||||
if err := json.Unmarshal(raw, s); err != nil {
|
||||
return errors.New("decoding spec config file failed")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func withMounts(context *cli.Context) oci.SpecOpts {
|
||||
return func(ctx gocontext.Context, client oci.Client, container *containers.Container, s *specs.Spec) error {
|
||||
mounts := make([]specs.Mount, 0)
|
||||
@ -75,47 +133,10 @@ var Command = cli.Command{
|
||||
Usage: "run a container",
|
||||
ArgsUsage: "[flags] Image|RootFS ID [COMMAND] [ARG...]",
|
||||
Flags: append([]cli.Flag{
|
||||
cli.BoolFlag{
|
||||
Name: "tty,t",
|
||||
Usage: "allocate a TTY for the container",
|
||||
},
|
||||
cli.StringFlag{
|
||||
Name: "runtime",
|
||||
Usage: "runtime name (io.containerd.runtime.v1.linux, io.containerd.runtime.v1.windows, io.containerd.runtime.v1.com.vmware.linux)",
|
||||
Value: fmt.Sprintf("io.containerd.runtime.v1.%s", runtime.GOOS),
|
||||
},
|
||||
cli.BoolFlag{
|
||||
Name: "readonly",
|
||||
Usage: "set the containers filesystem as readonly",
|
||||
},
|
||||
cli.BoolFlag{
|
||||
Name: "net-host",
|
||||
Usage: "enable host networking for the container",
|
||||
},
|
||||
cli.StringSliceFlag{
|
||||
Name: "mount",
|
||||
Usage: "specify additional container mount (ex: type=bind,src=/tmp,dst=/host,options=rbind:ro)",
|
||||
},
|
||||
cli.StringSliceFlag{
|
||||
Name: "env",
|
||||
Usage: "specify additional container environment variables (i.e. FOO=bar)",
|
||||
},
|
||||
cli.StringSliceFlag{
|
||||
Name: "label",
|
||||
Usage: "specify additional labels (foo=bar)",
|
||||
},
|
||||
cli.BoolFlag{
|
||||
Name: "rm",
|
||||
Usage: "remove the container after running",
|
||||
},
|
||||
cli.StringFlag{
|
||||
Name: "checkpoint",
|
||||
Usage: "provide the checkpoint digest to restore the container",
|
||||
},
|
||||
cli.StringFlag{
|
||||
Name: "cwd",
|
||||
Usage: "specify the working directory of the process",
|
||||
},
|
||||
cli.BoolFlag{
|
||||
Name: "null-io",
|
||||
Usage: "send all IO to /dev/null",
|
||||
@ -128,18 +149,18 @@ var Command = cli.Command{
|
||||
Name: "fifo-dir",
|
||||
Usage: "directory used for storing IO FIFOs",
|
||||
},
|
||||
}, commands.SnapshotterFlags...),
|
||||
}, append(commands.SnapshotterFlags, ContainerFlags...)...),
|
||||
Action: func(context *cli.Context) error {
|
||||
var (
|
||||
err error
|
||||
|
||||
id = context.Args().Get(1)
|
||||
imageRef = context.Args().First()
|
||||
tty = context.Bool("tty")
|
||||
detach = context.Bool("detach")
|
||||
id = context.Args().Get(1)
|
||||
ref = context.Args().First()
|
||||
tty = context.Bool("tty")
|
||||
detach = context.Bool("detach")
|
||||
)
|
||||
|
||||
if imageRef == "" {
|
||||
if ref == "" {
|
||||
return errors.New("image ref must be provided")
|
||||
}
|
||||
if id == "" {
|
||||
@ -150,7 +171,7 @@ var Command = cli.Command{
|
||||
return err
|
||||
}
|
||||
defer cancel()
|
||||
container, err := newContainer(ctx, client, context)
|
||||
container, err := NewContainer(ctx, client, context)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@ -197,7 +218,6 @@ var Command = cli.Command{
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if _, err := task.Delete(ctx); err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -22,7 +22,8 @@ func init() {
|
||||
})
|
||||
}
|
||||
|
||||
func newContainer(ctx gocontext.Context, client *containerd.Client, context *cli.Context) (containerd.Container, error) {
|
||||
// NewContainer creates a new container
|
||||
func NewContainer(ctx gocontext.Context, client *containerd.Client, context *cli.Context) (containerd.Container, error) {
|
||||
var (
|
||||
ref = context.Args().First()
|
||||
id = context.Args().Get(1)
|
||||
@ -40,8 +41,12 @@ func newContainer(ctx gocontext.Context, client *containerd.Client, context *cli
|
||||
var (
|
||||
opts []oci.SpecOpts
|
||||
cOpts []containerd.NewContainerOpts
|
||||
spec containerd.NewContainerOpts
|
||||
)
|
||||
opts = append(opts, oci.WithEnv(context.StringSlice("env")))
|
||||
opts = append(opts, withMounts(context))
|
||||
cOpts = append(cOpts, containerd.WithContainerLabels(commands.LabelArgs(context.StringSlice("label"))))
|
||||
cOpts = append(cOpts, containerd.WithRuntime(context.String("runtime"), nil))
|
||||
if context.Bool("rootfs") {
|
||||
opts = append(opts, oci.WithRootFSPath(ref))
|
||||
} else {
|
||||
@ -50,19 +55,17 @@ func newContainer(ctx gocontext.Context, client *containerd.Client, context *cli
|
||||
return nil, err
|
||||
}
|
||||
opts = append(opts, oci.WithImageConfig(image))
|
||||
cOpts = append(cOpts, containerd.WithImage(image))
|
||||
cOpts = append(cOpts, containerd.WithSnapshotter(context.String("snapshotter")))
|
||||
// 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))
|
||||
cOpts = append(cOpts,
|
||||
containerd.WithImage(image),
|
||||
containerd.WithSnapshotter(context.String("snapshotter")),
|
||||
// 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.
|
||||
containerd.WithNewSnapshot(id, image))
|
||||
}
|
||||
if context.Bool("readonly") {
|
||||
opts = append(opts, oci.WithRootFSReadonly())
|
||||
}
|
||||
cOpts = append(cOpts, containerd.WithRuntime(context.String("runtime"), nil))
|
||||
opts = append(opts, oci.WithEnv(context.StringSlice("env")))
|
||||
opts = append(opts, withMounts(context))
|
||||
if len(args) > 0 {
|
||||
opts = append(opts, oci.WithProcessArgs(args...))
|
||||
}
|
||||
@ -75,10 +78,20 @@ func newContainer(ctx gocontext.Context, client *containerd.Client, context *cli
|
||||
if context.Bool("net-host") {
|
||||
opts = append(opts, oci.WithHostNamespace(specs.NetworkNamespace), oci.WithHostHostsFile, oci.WithHostResolvconf)
|
||||
}
|
||||
if context.IsSet("config") {
|
||||
var s specs.Spec
|
||||
if err := loadSpec(context.String("config"), &s); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
spec = containerd.WithSpec(&s, opts...)
|
||||
} else {
|
||||
spec = containerd.WithNewSpec(opts...)
|
||||
}
|
||||
cOpts = append(cOpts, spec)
|
||||
|
||||
// oci.WithImageConfig (WithUsername, WithUserID) depends on rootfs snapshot for resolving /etc/passwd.
|
||||
// So cOpts needs to have precedence over opts.
|
||||
// TODO: WithUsername, WithUserID should additionally support non-snapshot rootfs
|
||||
cOpts = append(cOpts, []containerd.NewContainerOpts{containerd.WithNewSpec(opts...)}...)
|
||||
return client.NewContainer(ctx, id, cOpts...)
|
||||
}
|
||||
|
||||
|
@ -29,7 +29,8 @@ func withTTY(terminal bool) oci.SpecOpts {
|
||||
return oci.WithTTY(int(size.Width), int(size.Height))
|
||||
}
|
||||
|
||||
func newContainer(ctx gocontext.Context, client *containerd.Client, context *cli.Context) (containerd.Container, error) {
|
||||
// NewContainer creates a new container
|
||||
func NewContainer(ctx gocontext.Context, client *containerd.Client, context *cli.Context) (containerd.Container, error) {
|
||||
var (
|
||||
ref = context.Args().First()
|
||||
id = context.Args().Get(1)
|
||||
@ -44,25 +45,36 @@ func newContainer(ctx gocontext.Context, client *containerd.Client, context *cli
|
||||
var (
|
||||
opts []oci.SpecOpts
|
||||
cOpts []containerd.NewContainerOpts
|
||||
spec containerd.NewContainerOpts
|
||||
)
|
||||
opts = append(opts, oci.WithImageConfig(image))
|
||||
opts = append(opts, oci.WithEnv(context.StringSlice("env")))
|
||||
opts = append(opts, withMounts(context))
|
||||
opts = append(opts, withTTY(context.Bool("tty")))
|
||||
if len(args) > 0 {
|
||||
opts = append(opts, oci.WithProcessArgs(args...))
|
||||
}
|
||||
if cwd := context.String("cwd"); cwd != "" {
|
||||
opts = append(opts, oci.WithProcessCwd(cwd))
|
||||
}
|
||||
opts = append(opts, withTTY(context.Bool("tty")))
|
||||
|
||||
if context.IsSet("config") {
|
||||
var s specs.Spec
|
||||
if err := loadSpec(context.String("config"), &s); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
spec = containerd.WithSpec(&s, opts...)
|
||||
} else {
|
||||
spec = containerd.WithNewSpec(opts...)
|
||||
}
|
||||
|
||||
cOpts = append(cOpts, containerd.WithContainerLabels(commands.LabelArgs(context.StringSlice("label"))))
|
||||
cOpts = append(cOpts, containerd.WithImage(image))
|
||||
cOpts = append(cOpts, containerd.WithSnapshotter(context.String("snapshotter")))
|
||||
cOpts = append(cOpts, containerd.WithNewSnapshot(id, image))
|
||||
cOpts = append(cOpts, containerd.WithRuntime(context.String("runtime"), nil))
|
||||
cOpts = append(cOpts, spec)
|
||||
|
||||
cOpts = append([]containerd.NewContainerOpts{containerd.WithNewSpec(opts...)}, cOpts...)
|
||||
return client.NewContainer(ctx, id, cOpts...)
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user