diff --git a/cmd/ctr/commands/containers/containers.go b/cmd/ctr/commands/containers/containers.go index 9cf5df465..5bd9fb456 100644 --- a/cmd/ctr/commands/containers/containers.go +++ b/cmd/ctr/commands/containers/containers.go @@ -20,10 +20,10 @@ var Command = cli.Command{ Usage: "manage containers", Aliases: []string{"c"}, Subcommands: []cli.Command{ - listCommand, deleteCommand, - setLabelsCommand, infoCommand, + listCommand, + setLabelsCommand, }, } diff --git a/cmd/ctr/commands/content/content.go b/cmd/ctr/commands/content/content.go index 2cfbeaa06..d3cd8da4e 100644 --- a/cmd/ctr/commands/content/content.go +++ b/cmd/ctr/commands/content/content.go @@ -27,16 +27,16 @@ var ( Name: "content", Usage: "manage content", Subcommands: cli.Commands{ - listCommand, - ingestCommand, activeIngestCommand, - getCommand, - editCommand, deleteCommand, - setLabelsCommand, + editCommand, fetchCommand, fetchObjectCommand, + getCommand, + ingestCommand, + listCommand, pushObjectCommand, + setLabelsCommand, }, } diff --git a/cmd/ctr/commands/namespaces/namespaces.go b/cmd/ctr/commands/namespaces/namespaces.go index ec0ba3739..bedaf8522 100644 --- a/cmd/ctr/commands/namespaces/namespaces.go +++ b/cmd/ctr/commands/namespaces/namespaces.go @@ -20,9 +20,9 @@ var Command = cli.Command{ Usage: "manage namespaces", Subcommands: cli.Commands{ createCommand, - setLabelsCommand, listCommand, removeCommand, + setLabelsCommand, }, } diff --git a/cmd/ctr/pprof.go b/cmd/ctr/commands/pprof/pprof.go similarity index 95% rename from cmd/ctr/pprof.go rename to cmd/ctr/commands/pprof/pprof.go index 68e232480..8192d1135 100644 --- a/cmd/ctr/pprof.go +++ b/cmd/ctr/commands/pprof/pprof.go @@ -1,4 +1,4 @@ -package main +package pprof import ( "fmt" @@ -17,7 +17,8 @@ type pprofDialer struct { addr string } -var pprofCommand = cli.Command{ +// Command is the cli command for providing golang pprof outputs for containerd +var Command = cli.Command{ Name: "pprof", Usage: "provide golang pprof outputs for containerd", Flags: []cli.Flag{ @@ -28,12 +29,12 @@ var pprofCommand = cli.Command{ }, }, Subcommands: []cli.Command{ + pprofBlockCommand, pprofGoroutinesCommand, pprofHeapCommand, pprofProfileCommand, - pprofTraceCommand, - pprofBlockCommand, pprofThreadcreateCommand, + pprofTraceCommand, }, } @@ -91,7 +92,7 @@ var pprofTraceCommand = cli.Command{ Flags: []cli.Flag{ cli.DurationFlag{ Name: "seconds,s", - Usage: "Trace time (seconds)", + Usage: "trace time (seconds)", Value: time.Duration(5 * time.Second), }, }, diff --git a/cmd/ctr/pprof_unix.go b/cmd/ctr/commands/pprof/pprof_unix.go similarity index 94% rename from cmd/ctr/pprof_unix.go rename to cmd/ctr/commands/pprof/pprof_unix.go index 009b5da5e..5d22e3edd 100644 --- a/cmd/ctr/pprof_unix.go +++ b/cmd/ctr/commands/pprof/pprof_unix.go @@ -1,6 +1,6 @@ // +build !windows -package main +package pprof import "net" diff --git a/cmd/ctr/pprof_windows.go b/cmd/ctr/commands/pprof/pprof_windows.go similarity index 95% rename from cmd/ctr/pprof_windows.go rename to cmd/ctr/commands/pprof/pprof_windows.go index c7bacebee..c59c175fe 100644 --- a/cmd/ctr/pprof_windows.go +++ b/cmd/ctr/commands/pprof/pprof_windows.go @@ -1,4 +1,4 @@ -package main +package pprof import ( "net" diff --git a/cmd/ctr/run.go b/cmd/ctr/commands/run/run.go similarity index 64% rename from cmd/ctr/run.go rename to cmd/ctr/commands/run/run.go index 88bacf2d2..184f6540a 100644 --- a/cmd/ctr/run.go +++ b/cmd/ctr/commands/run/run.go @@ -1,13 +1,16 @@ -package main +package run import ( gocontext "context" + "encoding/csv" "fmt" "runtime" + "strings" "github.com/containerd/console" "github.com/containerd/containerd" "github.com/containerd/containerd/cmd/ctr/commands" + "github.com/containerd/containerd/cmd/ctr/commands/tasks" "github.com/containerd/containerd/containers" specs "github.com/opencontainers/runtime-spec/specs-go" "github.com/pkg/errors" @@ -15,10 +18,6 @@ import ( "github.com/urfave/cli" ) -type resizer interface { - Resize(ctx gocontext.Context, w, h uint32) error -} - func withEnv(context *cli.Context) containerd.SpecOpts { return func(_ gocontext.Context, _ *containerd.Client, _ *containers.Container, s *specs.Spec) error { env := context.StringSlice("env") @@ -42,10 +41,84 @@ func withMounts(context *cli.Context) containerd.SpecOpts { } } -var runCommand = cli.Command{ +// parseMountFlag parses a mount string in the form "type=foo,source=/path,destination=/target,options=rbind:rw" +func parseMountFlag(m string) (specs.Mount, error) { + mount := specs.Mount{} + r := csv.NewReader(strings.NewReader(m)) + + fields, err := r.Read() + if err != nil { + return mount, err + } + + for _, field := range fields { + v := strings.Split(field, "=") + if len(v) != 2 { + return mount, fmt.Errorf("invalid mount specification: expected key=val") + } + + key := v[0] + val := v[1] + switch key { + case "type": + mount.Type = val + case "source", "src": + mount.Source = val + case "destination", "dst": + mount.Destination = val + case "options": + mount.Options = strings.Split(val, ":") + default: + return mount, fmt.Errorf("mount option %q not supported", key) + } + } + + return mount, nil +} + +// replaceOrAppendEnvValues returns the defaults with the overrides either +// replaced by env key or appended to the list +func replaceOrAppendEnvValues(defaults, overrides []string) []string { + cache := make(map[string]int, len(defaults)) + for i, e := range defaults { + parts := strings.SplitN(e, "=", 2) + cache[parts[0]] = i + } + + for _, value := range overrides { + // Values w/o = means they want this env to be removed/unset. + if !strings.Contains(value, "=") { + if i, exists := cache[value]; exists { + defaults[i] = "" // Used to indicate it should be removed + } + continue + } + + // Just do a normal set/update + parts := strings.SplitN(value, "=", 2) + if i, exists := cache[parts[0]]; exists { + defaults[i] = value + } else { + defaults = append(defaults, value) + } + } + + // Now remove all entries that we want to "unset" + for i := 0; i < len(defaults); i++ { + if defaults[i] == "" { + defaults = append(defaults[:i], defaults[i+1:]...) + i-- + } + } + + return defaults +} + +// Command runs a container +var Command = cli.Command{ Name: "run", Usage: "run a container", - ArgsUsage: "Image|RootFS ID [COMMAND] [ARG...]", + ArgsUsage: "[flags] Image|RootFS ID [COMMAND] [ARG...]", Flags: append([]cli.Flag{ cli.BoolFlag{ Name: "tty,t", @@ -125,7 +198,7 @@ var runCommand = cli.Command{ if context.Bool("rm") && !detach { defer container.Delete(ctx, containerd.WithSnapshotCleanup) } - task, err := newTask(ctx, client, container, context.String("checkpoint"), tty, context.Bool("null-io")) + task, err := tasks.NewTask(ctx, client, container, context.String("checkpoint"), tty, context.Bool("null-io")) if err != nil { return err } @@ -151,7 +224,7 @@ var runCommand = cli.Command{ return nil } if tty { - if err := handleConsoleResize(ctx, task, con); err != nil { + if err := tasks.HandleConsoleResize(ctx, task, con); err != nil { logrus.WithError(err).Error("console resize") } } else { diff --git a/cmd/ctr/run_unix.go b/cmd/ctr/commands/run/run_unix.go similarity index 62% rename from cmd/ctr/run_unix.go rename to cmd/ctr/commands/run/run_unix.go index a2a8574ae..ee518abd5 100644 --- a/cmd/ctr/run_unix.go +++ b/cmd/ctr/commands/run/run_unix.go @@ -1,56 +1,23 @@ // +build !windows -package main +package run import ( gocontext "context" - "os" - "os/signal" - "golang.org/x/sys/unix" - - "github.com/containerd/console" "github.com/containerd/containerd" "github.com/containerd/containerd/cmd/ctr/commands" specs "github.com/opencontainers/runtime-spec/specs-go" - "github.com/pkg/errors" - "github.com/sirupsen/logrus" "github.com/urfave/cli" ) func init() { - runCommand.Flags = append(runCommand.Flags, cli.BoolFlag{ + Command.Flags = append(Command.Flags, cli.BoolFlag{ Name: "rootfs", - Usage: "use custom rootfs that is not managed by containerd snapshotter.", + Usage: "use custom rootfs that is not managed by containerd snapshotter", }) } -func handleConsoleResize(ctx gocontext.Context, task resizer, con console.Console) error { - // do an initial resize of the console - size, err := con.Size() - if err != nil { - return err - } - if err := task.Resize(ctx, uint32(size.Width), uint32(size.Height)); err != nil { - logrus.WithError(err).Error("resize pty") - } - s := make(chan os.Signal, 16) - signal.Notify(s, unix.SIGWINCH) - go func() { - for range s { - size, err := con.Size() - if err != nil { - logrus.WithError(err).Error("get pty size") - continue - } - if err := task.Resize(ctx, uint32(size.Width), uint32(size.Height)); err != nil { - logrus.WithError(err).Error("resize pty") - } - } - }() - return nil -} - func withTTY() containerd.SpecOpts { return containerd.WithTTY } @@ -115,24 +82,3 @@ func newContainer(ctx gocontext.Context, client *containerd.Client, context *cli cOpts = append([]containerd.NewContainerOpts{containerd.WithNewSpec(opts...)}, cOpts...) return client.NewContainer(ctx, id, cOpts...) } - -func newTask(ctx gocontext.Context, client *containerd.Client, container containerd.Container, checkpoint string, tty, nullIO bool) (containerd.Task, error) { - if checkpoint == "" { - io := containerd.Stdio - if tty { - io = containerd.StdioTerminal - } - if nullIO { - if tty { - return nil, errors.New("tty and null-io cannot be used together") - } - io = containerd.NullIO - } - return container.NewTask(ctx, io) - } - im, err := client.GetImage(ctx, checkpoint) - if err != nil { - return nil, err - } - return container.NewTask(ctx, containerd.Stdio, containerd.WithTaskCheckpoint(im)) -} diff --git a/cmd/ctr/run_windows.go b/cmd/ctr/commands/run/run_windows.go similarity index 67% rename from cmd/ctr/run_windows.go rename to cmd/ctr/commands/run/run_windows.go index b50c8dbf4..5a004b65f 100644 --- a/cmd/ctr/run_windows.go +++ b/cmd/ctr/commands/run/run_windows.go @@ -1,15 +1,13 @@ -package main +package run import ( gocontext "context" - "time" "github.com/containerd/console" "github.com/containerd/containerd" "github.com/containerd/containerd/cmd/ctr/commands" "github.com/containerd/containerd/containers" "github.com/containerd/containerd/errdefs" - "github.com/containerd/containerd/log" specs "github.com/opencontainers/runtime-spec/specs-go" "github.com/pkg/errors" "github.com/sirupsen/logrus" @@ -19,7 +17,7 @@ import ( const pipeRoot = `\\.\pipe` func init() { - runCommand.Flags = append(runCommand.Flags, cli.StringSliceFlag{ + Command.Flags = append(Command.Flags, cli.StringSliceFlag{ Name: "layer", Usage: "HCSSHIM Layers to be used", }) @@ -36,34 +34,6 @@ func withLayers(context *cli.Context) containerd.SpecOpts { } } -func handleConsoleResize(ctx gocontext.Context, task resizer, con console.Console) error { - // do an initial resize of the console - size, err := con.Size() - if err != nil { - return err - } - go func() { - prevSize := size - for { - time.Sleep(time.Millisecond * 250) - - size, err := con.Size() - if err != nil { - log.G(ctx).WithError(err).Error("get pty size") - continue - } - - if size.Width != prevSize.Width || size.Height != prevSize.Height { - if err := task.Resize(ctx, uint32(size.Width), uint32(size.Height)); err != nil { - logrus.WithError(err).Error("resize pty") - } - prevSize = size - } - } - }() - return nil -} - func withTTY(terminal bool) containerd.SpecOpts { if !terminal { return func(ctx gocontext.Context, client *containerd.Client, c *containers.Container, s *specs.Spec) error { @@ -117,17 +87,3 @@ func newContainer(ctx gocontext.Context, client *containerd.Client, context *cli // TODO(mlaventure): containerd.WithImage(image), ) } - -func newTask(ctx gocontext.Context, client *containerd.Client, container containerd.Container, _ string, tty, nullIO bool) (containerd.Task, error) { - io := containerd.Stdio - if tty { - io = containerd.StdioTerminal - } - if nullIO { - if tty { - return nil, errors.New("tty and null-io cannot be used together") - } - io = containerd.NullIO - } - return container.NewTask(ctx, io) -} diff --git a/cmd/ctr/utils_unix.go b/cmd/ctr/commands/shim/io_unix.go similarity index 98% rename from cmd/ctr/utils_unix.go rename to cmd/ctr/commands/shim/io_unix.go index 69967f6f3..f328f006b 100644 --- a/cmd/ctr/utils_unix.go +++ b/cmd/ctr/commands/shim/io_unix.go @@ -1,6 +1,6 @@ // +build !windows -package main +package shim import ( gocontext "context" diff --git a/cmd/ctr/utils_windows.go b/cmd/ctr/commands/shim/io_windows.go similarity index 99% rename from cmd/ctr/utils_windows.go rename to cmd/ctr/commands/shim/io_windows.go index ac364f792..4125b3fdd 100644 --- a/cmd/ctr/utils_windows.go +++ b/cmd/ctr/commands/shim/io_windows.go @@ -1,4 +1,4 @@ -package main +package shim import ( "io" diff --git a/cmd/ctr/shim.go b/cmd/ctr/commands/shim/shim.go similarity index 70% rename from cmd/ctr/shim.go rename to cmd/ctr/commands/shim/shim.go index c3145cbb4..63428c99c 100644 --- a/cmd/ctr/shim.go +++ b/cmd/ctr/commands/shim/shim.go @@ -1,6 +1,6 @@ // +build !windows -package main +package shim import ( "fmt" @@ -45,7 +45,8 @@ var fifoFlags = []cli.Flag{ }, } -var shimCommand = cli.Command{ +// Command is the cli command for interacting with a shim +var Command = cli.Command{ Name: "shim", Usage: "interact with a shim directly", Flags: []cli.Flag{ @@ -55,89 +56,14 @@ var shimCommand = cli.Command{ }, }, Subcommands: []cli.Command{ - shimCreateCommand, - shimStartCommand, - shimDeleteCommand, - shimStateCommand, - shimExecCommand, + deleteCommand, + execCommand, + startCommand, + stateCommand, }, } -var shimCreateCommand = cli.Command{ - Name: "create", - Usage: "create a container with a shim", - Flags: append(fifoFlags, - cli.StringFlag{ - Name: "bundle", - Usage: "bundle path for the container", - }, - cli.StringFlag{ - Name: "runtime", - Value: "runc", - Usage: "runtime to use for the container", - }, - cli.BoolFlag{ - Name: "attach,a", - Usage: "stay attached to the container and open the fifos", - }, - ), - Action: func(context *cli.Context) error { - var ( - id = context.Args().First() - ctx = gocontext.Background() - ) - - if id == "" { - return errors.New("container id must be provided") - } - service, err := getShimService(context) - if err != nil { - return err - } - tty := context.Bool("tty") - wg, err := prepareStdio(context.String("stdin"), context.String("stdout"), context.String("stderr"), tty) - if err != nil { - return err - } - r, err := service.Create(ctx, &shim.CreateTaskRequest{ - ID: id, - Bundle: context.String("bundle"), - Runtime: context.String("runtime"), - Stdin: context.String("stdin"), - Stdout: context.String("stdout"), - Stderr: context.String("stderr"), - Terminal: tty, - }) - if err != nil { - return err - } - fmt.Printf("container created with id %s and pid %d\n", id, r.Pid) - if context.Bool("attach") { - if tty { - current := console.Current() - defer current.Reset() - if err := current.SetRaw(); err != nil { - return err - } - size, err := current.Size() - if err != nil { - return err - } - if _, err := service.ResizePty(ctx, &shim.ResizePtyRequest{ - ID: id, - Width: uint32(size.Width), - Height: uint32(size.Height), - }); err != nil { - return err - } - } - wg.Wait() - } - return nil - }, -} - -var shimStartCommand = cli.Command{ +var startCommand = cli.Command{ Name: "start", Usage: "start a container with a shim", Action: func(context *cli.Context) error { @@ -152,7 +78,7 @@ var shimStartCommand = cli.Command{ }, } -var shimDeleteCommand = cli.Command{ +var deleteCommand = cli.Command{ Name: "delete", Usage: "delete a container with a shim", Action: func(context *cli.Context) error { @@ -169,7 +95,7 @@ var shimDeleteCommand = cli.Command{ }, } -var shimStateCommand = cli.Command{ +var stateCommand = cli.Command{ Name: "state", Usage: "get the state of all the processes of the shim", Action: func(context *cli.Context) error { @@ -188,7 +114,7 @@ var shimStateCommand = cli.Command{ }, } -var shimExecCommand = cli.Command{ +var execCommand = cli.Command{ Name: "exec", Usage: "exec a new process in the shim's container", Flags: append(fifoFlags, diff --git a/cmd/ctr/commands/signals_linux.go b/cmd/ctr/commands/signal_map_linux.go similarity index 100% rename from cmd/ctr/commands/signals_linux.go rename to cmd/ctr/commands/signal_map_linux.go diff --git a/cmd/ctr/commands/signals_unix.go b/cmd/ctr/commands/signal_map_unix.go similarity index 100% rename from cmd/ctr/commands/signals_unix.go rename to cmd/ctr/commands/signal_map_unix.go diff --git a/cmd/ctr/commands/signals_windows.go b/cmd/ctr/commands/signal_map_windows.go similarity index 100% rename from cmd/ctr/commands/signals_windows.go rename to cmd/ctr/commands/signal_map_windows.go diff --git a/cmd/ctr/commands/snapshot/snapshot.go b/cmd/ctr/commands/snapshot/snapshot.go index 9def7a819..734871823 100644 --- a/cmd/ctr/commands/snapshot/snapshot.go +++ b/cmd/ctr/commands/snapshot/snapshot.go @@ -23,17 +23,17 @@ var Command = cli.Command{ Usage: "manage snapshots", Flags: commands.SnapshotterFlags, Subcommands: cli.Commands{ - listCommand, - usageCommand, - removeCommand, - prepareCommand, - viewCommand, - treeCommand, - mountCommand, commitCommand, infoCommand, + listCommand, + mountCommand, + prepareCommand, + removeCommand, setLabelCommand, + treeCommand, unpackCommand, + usageCommand, + viewCommand, }, } diff --git a/cmd/ctr/attach.go b/cmd/ctr/commands/tasks/attach.go similarity index 92% rename from cmd/ctr/attach.go rename to cmd/ctr/commands/tasks/attach.go index f1af6ab30..22e6fb90e 100644 --- a/cmd/ctr/attach.go +++ b/cmd/ctr/commands/tasks/attach.go @@ -1,4 +1,4 @@ -package main +package tasks import ( "os" @@ -10,7 +10,7 @@ import ( "github.com/urfave/cli" ) -var taskAttachCommand = cli.Command{ +var attachCommand = cli.Command{ Name: "attach", Usage: "attach to the IO of a running container", ArgsUsage: "CONTAINER", @@ -51,7 +51,7 @@ var taskAttachCommand = cli.Command{ } if tty { - if err := handleConsoleResize(ctx, task, con); err != nil { + if err := HandleConsoleResize(ctx, task, con); err != nil { logrus.WithError(err).Error("console resize") } } else { diff --git a/cmd/ctr/checkpoint.go b/cmd/ctr/commands/tasks/checkpoint.go similarity index 92% rename from cmd/ctr/checkpoint.go rename to cmd/ctr/commands/tasks/checkpoint.go index 6f4c2fdd9..6915caf84 100644 --- a/cmd/ctr/checkpoint.go +++ b/cmd/ctr/commands/tasks/checkpoint.go @@ -1,4 +1,4 @@ -package main +package tasks import ( "fmt" @@ -9,10 +9,10 @@ import ( "github.com/urfave/cli" ) -var taskCheckpointCommand = cli.Command{ +var checkpointCommand = cli.Command{ Name: "checkpoint", Usage: "checkpoint a container", - ArgsUsage: "CONTAINER", + ArgsUsage: "[flags] CONTAINER", Flags: []cli.Flag{ cli.BoolFlag{ Name: "exit", diff --git a/cmd/ctr/task_delete.go b/cmd/ctr/commands/tasks/delete.go similarity index 93% rename from cmd/ctr/task_delete.go rename to cmd/ctr/commands/tasks/delete.go index dbae378c1..ba60fe625 100644 --- a/cmd/ctr/task_delete.go +++ b/cmd/ctr/commands/tasks/delete.go @@ -1,11 +1,11 @@ -package main +package tasks import ( "github.com/containerd/containerd/cmd/ctr/commands" "github.com/urfave/cli" ) -var taskDeleteCommand = cli.Command{ +var deleteCommand = cli.Command{ Name: "delete", Usage: "delete a task", ArgsUsage: "CONTAINER", diff --git a/cmd/ctr/exec.go b/cmd/ctr/commands/tasks/exec.go similarity index 93% rename from cmd/ctr/exec.go rename to cmd/ctr/commands/tasks/exec.go index 65659827d..f3303ade2 100644 --- a/cmd/ctr/exec.go +++ b/cmd/ctr/commands/tasks/exec.go @@ -1,4 +1,4 @@ -package main +package tasks import ( "errors" @@ -11,10 +11,10 @@ import ( ) //TODO:(jessvalarezo) exec-id is optional here, update to required arg -var taskExecCommand = cli.Command{ +var execCommand = cli.Command{ Name: "exec", Usage: "execute additional processes in an existing container", - ArgsUsage: "CONTAINER CMD [ARG...]", + ArgsUsage: "[flags] CONTAINER CMD [ARG...]", Flags: []cli.Flag{ cli.StringFlag{ Name: "cwd", @@ -84,7 +84,7 @@ var taskExecCommand = cli.Command{ } } if tty { - if err := handleConsoleResize(ctx, process, con); err != nil { + if err := HandleConsoleResize(ctx, process, con); err != nil { logrus.WithError(err).Error("console resize") } } else { diff --git a/cmd/ctr/kill.go b/cmd/ctr/commands/tasks/kill.go similarity index 94% rename from cmd/ctr/kill.go rename to cmd/ctr/commands/tasks/kill.go index 159f409ee..5b1a5f427 100644 --- a/cmd/ctr/kill.go +++ b/cmd/ctr/commands/tasks/kill.go @@ -1,4 +1,4 @@ -package main +package tasks import ( "github.com/containerd/containerd/cmd/ctr/commands" @@ -8,10 +8,10 @@ import ( // TODO:(jessvalarezo) the pid flag is not used here // update to be able to signal given pid -var taskKillCommand = cli.Command{ +var killCommand = cli.Command{ Name: "kill", Usage: "signal a container (default: SIGTERM)", - ArgsUsage: "CONTAINER", + ArgsUsage: "[flags] CONTAINER", Flags: []cli.Flag{ cli.StringFlag{ Name: "signal, s", diff --git a/cmd/ctr/task.go b/cmd/ctr/commands/tasks/list.go similarity index 68% rename from cmd/ctr/task.go rename to cmd/ctr/commands/tasks/list.go index 243394380..0ae3def51 100644 --- a/cmd/ctr/task.go +++ b/cmd/ctr/commands/tasks/list.go @@ -1,4 +1,4 @@ -package main +package tasks import ( "fmt" @@ -10,28 +10,11 @@ import ( "github.com/urfave/cli" ) -var tasksCommand = cli.Command{ - Name: "tasks", - Usage: "manage tasks", - Aliases: []string{"t"}, - Subcommands: []cli.Command{ - taskAttachCommand, - taskCheckpointCommand, - taskExecCommand, - taskKillCommand, - taskPauseCommand, - taskPsCommand, - taskResumeCommand, - taskStartCommand, - taskDeleteCommand, - taskListCommand, - }, -} - -var taskListCommand = cli.Command{ - Name: "list", - Usage: "list tasks", - Aliases: []string{"ls"}, +var listCommand = cli.Command{ + Name: "list", + Usage: "list tasks", + Aliases: []string{"ls"}, + ArgsUsage: "[flags]", Flags: []cli.Flag{ cli.BoolFlag{ Name: "quiet, q", diff --git a/cmd/ctr/pause.go b/cmd/ctr/commands/tasks/pause.go similarity index 91% rename from cmd/ctr/pause.go rename to cmd/ctr/commands/tasks/pause.go index 92944f67e..3f46456a1 100644 --- a/cmd/ctr/pause.go +++ b/cmd/ctr/commands/tasks/pause.go @@ -1,11 +1,11 @@ -package main +package tasks import ( "github.com/containerd/containerd/cmd/ctr/commands" "github.com/urfave/cli" ) -var taskPauseCommand = cli.Command{ +var pauseCommand = cli.Command{ Name: "pause", Usage: "pause an existing container", ArgsUsage: "CONTAINER", diff --git a/cmd/ctr/ps.go b/cmd/ctr/commands/tasks/ps.go similarity index 96% rename from cmd/ctr/ps.go rename to cmd/ctr/commands/tasks/ps.go index 058dec473..f371e4458 100644 --- a/cmd/ctr/ps.go +++ b/cmd/ctr/commands/tasks/ps.go @@ -1,4 +1,4 @@ -package main +package tasks import ( "fmt" @@ -11,7 +11,7 @@ import ( "github.com/urfave/cli" ) -var taskPsCommand = cli.Command{ +var psCommand = cli.Command{ Name: "ps", Usage: "list processes for container", ArgsUsage: "CONTAINER", diff --git a/cmd/ctr/resume.go b/cmd/ctr/commands/tasks/resume.go similarity index 91% rename from cmd/ctr/resume.go rename to cmd/ctr/commands/tasks/resume.go index 92d860711..d1680edd3 100644 --- a/cmd/ctr/resume.go +++ b/cmd/ctr/commands/tasks/resume.go @@ -1,11 +1,11 @@ -package main +package tasks import ( "github.com/containerd/containerd/cmd/ctr/commands" "github.com/urfave/cli" ) -var taskResumeCommand = cli.Command{ +var resumeCommand = cli.Command{ Name: "resume", Usage: "resume a paused container", ArgsUsage: "CONTAINER", diff --git a/cmd/ctr/start.go b/cmd/ctr/commands/tasks/start.go similarity index 90% rename from cmd/ctr/start.go rename to cmd/ctr/commands/tasks/start.go index 9f37af9ee..df09a9b9a 100644 --- a/cmd/ctr/start.go +++ b/cmd/ctr/commands/tasks/start.go @@ -1,4 +1,4 @@ -package main +package tasks import ( "github.com/containerd/console" @@ -8,7 +8,7 @@ import ( "github.com/urfave/cli" ) -var taskStartCommand = cli.Command{ +var startCommand = cli.Command{ Name: "start", Usage: "start a container that have been created", ArgsUsage: "CONTAINER", @@ -43,7 +43,7 @@ var taskStartCommand = cli.Command{ tty := spec.Process.Terminal - task, err := newTask(ctx, client, container, "", tty, context.Bool("null-io")) + task, err := NewTask(ctx, client, container, "", tty, context.Bool("null-io")) if err != nil { return err } @@ -66,7 +66,7 @@ var taskStartCommand = cli.Command{ return err } if tty { - if err := handleConsoleResize(ctx, task, con); err != nil { + if err := HandleConsoleResize(ctx, task, con); err != nil { logrus.WithError(err).Error("console resize") } } else { diff --git a/cmd/ctr/commands/tasks/tasks.go b/cmd/ctr/commands/tasks/tasks.go new file mode 100644 index 000000000..356e3e4fe --- /dev/null +++ b/cmd/ctr/commands/tasks/tasks.go @@ -0,0 +1,30 @@ +package tasks + +import ( + gocontext "context" + + "github.com/urfave/cli" +) + +type resizer interface { + Resize(ctx gocontext.Context, w, h uint32) error +} + +// Command is the cli command for managing tasks +var Command = cli.Command{ + Name: "tasks", + Usage: "manage tasks", + Aliases: []string{"t"}, + Subcommands: []cli.Command{ + attachCommand, + checkpointCommand, + deleteCommand, + execCommand, + listCommand, + killCommand, + pauseCommand, + psCommand, + resumeCommand, + startCommand, + }, +} diff --git a/cmd/ctr/commands/tasks/tasks_unix.go b/cmd/ctr/commands/tasks/tasks_unix.go new file mode 100644 index 000000000..edda13b31 --- /dev/null +++ b/cmd/ctr/commands/tasks/tasks_unix.go @@ -0,0 +1,64 @@ +// +build !windows + +package tasks + +import ( + gocontext "context" + "os" + "os/signal" + + "github.com/containerd/console" + "github.com/containerd/containerd" + "github.com/containerd/containerd/log" + "github.com/pkg/errors" + "golang.org/x/sys/unix" +) + +// HandleConsoleResize resizes the console +func HandleConsoleResize(ctx gocontext.Context, task resizer, con console.Console) error { + // do an initial resize of the console + size, err := con.Size() + if err != nil { + return err + } + if err := task.Resize(ctx, uint32(size.Width), uint32(size.Height)); err != nil { + log.G(ctx).WithError(err).Error("resize pty") + } + s := make(chan os.Signal, 16) + signal.Notify(s, unix.SIGWINCH) + go func() { + for range s { + size, err := con.Size() + if err != nil { + log.G(ctx).WithError(err).Error("get pty size") + continue + } + if err := task.Resize(ctx, uint32(size.Width), uint32(size.Height)); err != nil { + log.G(ctx).WithError(err).Error("resize pty") + } + } + }() + return nil +} + +// NewTask creates a new task +func NewTask(ctx gocontext.Context, client *containerd.Client, container containerd.Container, checkpoint string, tty, nullIO bool) (containerd.Task, error) { + if checkpoint == "" { + io := containerd.Stdio + if tty { + io = containerd.StdioTerminal + } + if nullIO { + if tty { + return nil, errors.New("tty and null-io cannot be used together") + } + io = containerd.NullIO + } + return container.NewTask(ctx, io) + } + im, err := client.GetImage(ctx, checkpoint) + if err != nil { + return nil, err + } + return container.NewTask(ctx, containerd.Stdio, containerd.WithTaskCheckpoint(im)) +} diff --git a/cmd/ctr/commands/tasks/tasks_windows.go b/cmd/ctr/commands/tasks/tasks_windows.go new file mode 100644 index 000000000..dda2fb1d6 --- /dev/null +++ b/cmd/ctr/commands/tasks/tasks_windows.go @@ -0,0 +1,55 @@ +package tasks + +import ( + gocontext "context" + "time" + + "github.com/containerd/console" + "github.com/containerd/containerd" + "github.com/containerd/containerd/log" + "github.com/pkg/errors" +) + +// HandleConsoleResize resizes the console +func HandleConsoleResize(ctx gocontext.Context, task resizer, con console.Console) error { + // do an initial resize of the console + size, err := con.Size() + if err != nil { + return err + } + go func() { + prevSize := size + for { + time.Sleep(time.Millisecond * 250) + + size, err := con.Size() + if err != nil { + log.G(ctx).WithError(err).Error("get pty size") + continue + } + + if size.Width != prevSize.Width || size.Height != prevSize.Height { + if err := task.Resize(ctx, uint32(size.Width), uint32(size.Height)); err != nil { + log.G(ctx).WithError(err).Error("resize pty") + } + prevSize = size + } + } + }() + return nil +} + +// NewTask creates a new task +func NewTask(ctx gocontext.Context, client *containerd.Client, container containerd.Container, _ string, tty, nullIO bool) (containerd.Task, error) { + io := containerd.Stdio + if tty { + io = containerd.StdioTerminal + } + if nullIO { + if tty { + return nil, errors.New("tty and null-io cannot be used together") + } + io = containerd.NullIO + } + return container.NewTask(ctx, io) +} diff --git a/cmd/ctr/main.go b/cmd/ctr/main.go index f3e547324..3b96a0413 100644 --- a/cmd/ctr/main.go +++ b/cmd/ctr/main.go @@ -12,7 +12,10 @@ import ( "github.com/containerd/containerd/cmd/ctr/commands/images" namespacesCmd "github.com/containerd/containerd/cmd/ctr/commands/namespaces" "github.com/containerd/containerd/cmd/ctr/commands/plugins" + "github.com/containerd/containerd/cmd/ctr/commands/pprof" + "github.com/containerd/containerd/cmd/ctr/commands/run" "github.com/containerd/containerd/cmd/ctr/commands/snapshot" + "github.com/containerd/containerd/cmd/ctr/commands/tasks" versionCmd "github.com/containerd/containerd/cmd/ctr/commands/version" "github.com/containerd/containerd/namespaces" "github.com/containerd/containerd/server" @@ -79,10 +82,10 @@ containerd CLI events.Command, images.Command, namespacesCmd.Command, - pprofCommand, - runCommand, + pprof.Command, + run.Command, snapshot.Command, - tasksCommand, + tasks.Command, }, extraCmds...) app.Before = func(context *cli.Context) error { if context.GlobalBool("debug") { diff --git a/cmd/ctr/main_unix.go b/cmd/ctr/main_unix.go index a8c51956c..cd25dc213 100644 --- a/cmd/ctr/main_unix.go +++ b/cmd/ctr/main_unix.go @@ -2,6 +2,8 @@ package main +import "github.com/containerd/containerd/cmd/ctr/commands/shim" + func init() { - extraCmds = append(extraCmds, shimCommand) + extraCmds = append(extraCmds, shim.Command) } diff --git a/cmd/ctr/utils.go b/cmd/ctr/utils.go deleted file mode 100644 index ad00b40ef..000000000 --- a/cmd/ctr/utils.go +++ /dev/null @@ -1,82 +0,0 @@ -package main - -import ( - "encoding/csv" - "fmt" - "strings" - - specs "github.com/opencontainers/runtime-spec/specs-go" -) - -// parseMountFlag parses a mount string in the form "type=foo,source=/path,destination=/target,options=rbind:rw" -func parseMountFlag(m string) (specs.Mount, error) { - mount := specs.Mount{} - r := csv.NewReader(strings.NewReader(m)) - - fields, err := r.Read() - if err != nil { - return mount, err - } - - for _, field := range fields { - v := strings.Split(field, "=") - if len(v) != 2 { - return mount, fmt.Errorf("invalid mount specification: expected key=val") - } - - key := v[0] - val := v[1] - switch key { - case "type": - mount.Type = val - case "source", "src": - mount.Source = val - case "destination", "dst": - mount.Destination = val - case "options": - mount.Options = strings.Split(val, ":") - default: - return mount, fmt.Errorf("mount option %q not supported", key) - } - } - - return mount, nil -} - -// replaceOrAppendEnvValues returns the defaults with the overrides either -// replaced by env key or appended to the list -func replaceOrAppendEnvValues(defaults, overrides []string) []string { - cache := make(map[string]int, len(defaults)) - for i, e := range defaults { - parts := strings.SplitN(e, "=", 2) - cache[parts[0]] = i - } - - for _, value := range overrides { - // Values w/o = means they want this env to be removed/unset. - if !strings.Contains(value, "=") { - if i, exists := cache[value]; exists { - defaults[i] = "" // Used to indicate it should be removed - } - continue - } - - // Just do a normal set/update - parts := strings.SplitN(value, "=", 2) - if i, exists := cache[parts[0]]; exists { - defaults[i] = value - } else { - defaults = append(defaults, value) - } - } - - // Now remove all entries that we want to "unset" - for i := 0; i < len(defaults); i++ { - if defaults[i] == "" { - defaults = append(defaults[:i], defaults[i+1:]...) - i-- - } - } - - return defaults -}