ctr: move tasks, run to commands package

Signed-off-by: Jess Valarezo <valarezo.jessica@gmail.com>
This commit is contained in:
Jess Valarezo
2017-10-31 11:57:41 -07:00
parent fe3d9a70fa
commit c3b70f1d0b
18 changed files with 271 additions and 244 deletions

View File

@@ -0,0 +1,72 @@
package tasks
import (
"os"
"github.com/containerd/console"
"github.com/containerd/containerd"
"github.com/containerd/containerd/cmd/ctr/commands"
"github.com/sirupsen/logrus"
"github.com/urfave/cli"
)
var attachCommand = cli.Command{
Name: "attach",
Usage: "attach to the IO of a running container",
ArgsUsage: "CONTAINER",
Action: func(context *cli.Context) error {
client, ctx, cancel, err := commands.NewClient(context)
if err != nil {
return err
}
defer cancel()
container, err := client.LoadContainer(ctx, context.Args().First())
if err != nil {
return err
}
spec, err := container.Spec(ctx)
if err != nil {
return err
}
var (
con console.Console
tty = spec.Process.Terminal
)
if tty {
con = console.Current()
defer con.Reset()
if err := con.SetRaw(); err != nil {
return err
}
}
task, err := container.Task(ctx, containerd.WithAttach(os.Stdin, os.Stdout, os.Stderr))
if err != nil {
return err
}
defer task.Delete(ctx)
statusC, err := task.Wait(ctx)
if err != nil {
return err
}
if tty {
if err := HandleConsoleResize(ctx, task, con); err != nil {
logrus.WithError(err).Error("console resize")
}
} else {
sigc := commands.ForwardAllSignals(ctx, task)
defer commands.StopCatch(sigc)
}
ec := <-statusC
code, _, err := ec.Result()
if err != nil {
return err
}
if code != 0 {
return cli.NewExitError("", int(code))
}
return nil
},
}

View File

@@ -0,0 +1,51 @@
package tasks
import (
"fmt"
"github.com/containerd/containerd"
"github.com/containerd/containerd/cmd/ctr/commands"
"github.com/pkg/errors"
"github.com/urfave/cli"
)
var checkpointCommand = cli.Command{
Name: "checkpoint",
Usage: "checkpoint a container",
ArgsUsage: "[flags] CONTAINER",
Flags: []cli.Flag{
cli.BoolFlag{
Name: "exit",
Usage: "stop the container after the checkpoint",
},
},
Action: func(context *cli.Context) error {
id := context.Args().First()
if id == "" {
return errors.New("container id must be provided")
}
client, ctx, cancel, err := commands.NewClient(context)
if err != nil {
return err
}
defer cancel()
container, err := client.LoadContainer(ctx, id)
if err != nil {
return err
}
task, err := container.Task(ctx, nil)
if err != nil {
return err
}
var opts []containerd.CheckpointTaskOpts
if context.Bool("exit") {
opts = append(opts, containerd.WithExit)
}
checkpoint, err := task.Checkpoint(ctx, opts...)
if err != nil {
return err
}
fmt.Println(checkpoint.Name())
return nil
},
}

View File

@@ -0,0 +1,35 @@
package tasks
import (
"github.com/containerd/containerd/cmd/ctr/commands"
"github.com/urfave/cli"
)
var deleteCommand = cli.Command{
Name: "delete",
Usage: "delete a task",
ArgsUsage: "CONTAINER",
Action: func(context *cli.Context) error {
client, ctx, cancel, err := commands.NewClient(context)
if err != nil {
return err
}
defer cancel()
container, err := client.LoadContainer(ctx, context.Args().First())
if err != nil {
return err
}
task, err := container.Task(ctx, nil)
if err != nil {
return err
}
status, err := task.Delete(ctx)
if err != nil {
return err
}
if ec := status.ExitCode(); ec != 0 {
return cli.NewExitError("", int(ec))
}
return nil
},
}

View File

@@ -0,0 +1,108 @@
package tasks
import (
"errors"
"github.com/containerd/console"
"github.com/containerd/containerd"
"github.com/containerd/containerd/cmd/ctr/commands"
"github.com/sirupsen/logrus"
"github.com/urfave/cli"
)
//TODO:(jessvalarezo) exec-id is optional here, update to required arg
var execCommand = cli.Command{
Name: "exec",
Usage: "execute additional processes in an existing container",
ArgsUsage: "[flags] CONTAINER CMD [ARG...]",
Flags: []cli.Flag{
cli.StringFlag{
Name: "cwd",
Usage: "working directory of the new process",
},
cli.BoolFlag{
Name: "tty,t",
Usage: "allocate a TTY for the container",
},
cli.StringFlag{
Name: "exec-id",
Usage: "exec specific id for the process",
},
},
Action: func(context *cli.Context) error {
var (
id = context.Args().First()
args = context.Args().Tail()
tty = context.Bool("tty")
)
if id == "" {
return errors.New("container id must be provided")
}
client, ctx, cancel, err := commands.NewClient(context)
if err != nil {
return err
}
defer cancel()
container, err := client.LoadContainer(ctx, id)
if err != nil {
return err
}
spec, err := container.Spec(ctx)
if err != nil {
return err
}
task, err := container.Task(ctx, nil)
if err != nil {
return err
}
pspec := spec.Process
pspec.Terminal = tty
pspec.Args = args
io := containerd.Stdio
if tty {
io = containerd.StdioTerminal
}
process, err := task.Exec(ctx, context.String("exec-id"), pspec, io)
if err != nil {
return err
}
defer process.Delete(ctx)
statusC, err := process.Wait(ctx)
if err != nil {
return err
}
var con console.Console
if tty {
con = console.Current()
defer con.Reset()
if err := con.SetRaw(); err != nil {
return err
}
}
if tty {
if err := HandleConsoleResize(ctx, process, con); err != nil {
logrus.WithError(err).Error("console resize")
}
} else {
sigc := commands.ForwardAllSignals(ctx, process)
defer commands.StopCatch(sigc)
}
if err := process.Start(ctx); err != nil {
return err
}
status := <-statusC
code, _, err := status.Result()
if err != nil {
return err
}
if code != 0 {
return cli.NewExitError("", int(code))
}
return nil
},
}

View File

@@ -0,0 +1,62 @@
package tasks
import (
"github.com/containerd/containerd/cmd/ctr/commands"
"github.com/pkg/errors"
"github.com/urfave/cli"
)
// TODO:(jessvalarezo) the pid flag is not used here
// update to be able to signal given pid
var killCommand = cli.Command{
Name: "kill",
Usage: "signal a container (default: SIGTERM)",
ArgsUsage: "[flags] CONTAINER",
Flags: []cli.Flag{
cli.StringFlag{
Name: "signal, s",
Value: "SIGTERM",
Usage: "signal to send to the container",
},
cli.IntFlag{
Name: "pid",
Usage: "pid to kill",
Value: 0,
},
cli.BoolFlag{
Name: "all, a",
Usage: "send signal to all processes inside the container",
},
},
Action: func(context *cli.Context) error {
id := context.Args().First()
if id == "" {
return errors.New("container id must be provided")
}
signal, err := commands.ParseSignal(context.String("signal"))
if err != nil {
return err
}
var (
pid = context.Int("pid")
all = context.Bool("all")
)
if pid > 0 && all {
return errors.New("enter a pid or all; not both")
}
client, ctx, cancel, err := commands.NewClient(context)
if err != nil {
return err
}
defer cancel()
container, err := client.LoadContainer(ctx, id)
if err != nil {
return err
}
task, err := container.Task(ctx, nil)
if err != nil {
return err
}
return task.Kill(ctx, signal)
},
}

View File

@@ -0,0 +1,55 @@
package tasks
import (
"fmt"
"os"
"text/tabwriter"
tasks "github.com/containerd/containerd/api/services/tasks/v1"
"github.com/containerd/containerd/cmd/ctr/commands"
"github.com/urfave/cli"
)
var listCommand = cli.Command{
Name: "list",
Usage: "list tasks",
Aliases: []string{"ls"},
ArgsUsage: "[flags]",
Flags: []cli.Flag{
cli.BoolFlag{
Name: "quiet, q",
Usage: "print only the task id & pid",
},
},
Action: func(context *cli.Context) error {
quiet := context.Bool("quiet")
client, ctx, cancel, err := commands.NewClient(context)
if err != nil {
return err
}
defer cancel()
s := client.TaskService()
response, err := s.List(ctx, &tasks.ListTasksRequest{})
if err != nil {
return err
}
if quiet {
for _, task := range response.Tasks {
fmt.Println(task.ID)
}
return nil
}
w := tabwriter.NewWriter(os.Stdout, 4, 8, 4, ' ', 0)
fmt.Fprintln(w, "TASK\tPID\tSTATUS\t")
for _, task := range response.Tasks {
if _, err := fmt.Fprintf(w, "%s\t%d\t%s\n",
task.ID,
task.Pid,
task.Status.String(),
); err != nil {
return err
}
}
return w.Flush()
},
}

View File

@@ -0,0 +1,28 @@
package tasks
import (
"github.com/containerd/containerd/cmd/ctr/commands"
"github.com/urfave/cli"
)
var pauseCommand = cli.Command{
Name: "pause",
Usage: "pause an existing container",
ArgsUsage: "CONTAINER",
Action: func(context *cli.Context) error {
client, ctx, cancel, err := commands.NewClient(context)
if err != nil {
return err
}
defer cancel()
container, err := client.LoadContainer(ctx, context.Args().First())
if err != nil {
return err
}
task, err := container.Task(ctx, nil)
if err != nil {
return err
}
return task.Pause(ctx)
},
}

View File

@@ -0,0 +1,59 @@
package tasks
import (
"fmt"
"os"
"text/tabwriter"
"github.com/containerd/containerd/cmd/ctr/commands"
"github.com/containerd/containerd/windows/hcsshimtypes"
"github.com/pkg/errors"
"github.com/urfave/cli"
)
var psCommand = cli.Command{
Name: "ps",
Usage: "list processes for container",
ArgsUsage: "CONTAINER",
Action: func(context *cli.Context) error {
id := context.Args().First()
if id == "" {
return errors.New("container id must be provided")
}
client, ctx, cancel, err := commands.NewClient(context)
if err != nil {
return err
}
defer cancel()
container, err := client.LoadContainer(ctx, id)
if err != nil {
return err
}
task, err := container.Task(ctx, nil)
if err != nil {
return err
}
processes, err := task.Pids(ctx)
if err != nil {
return err
}
w := tabwriter.NewWriter(os.Stdout, 10, 1, 3, ' ', 0)
fmt.Fprintln(w, "PID\tINFO")
for _, ps := range processes {
if ps.Info != nil {
var details hcsshimtypes.ProcessDetails
if err := details.Unmarshal(ps.Info.Value); err == nil {
if _, err := fmt.Fprintf(w, "%d\t%+v\n", ps.Pid, details); err != nil {
return err
}
}
} else {
if _, err := fmt.Fprintf(w, "%d\t-\n", ps.Pid); err != nil {
return err
}
}
}
return w.Flush()
},
}

View File

@@ -0,0 +1,28 @@
package tasks
import (
"github.com/containerd/containerd/cmd/ctr/commands"
"github.com/urfave/cli"
)
var resumeCommand = cli.Command{
Name: "resume",
Usage: "resume a paused container",
ArgsUsage: "CONTAINER",
Action: func(context *cli.Context) error {
client, ctx, cancel, err := commands.NewClient(context)
if err != nil {
return err
}
defer cancel()
container, err := client.LoadContainer(ctx, context.Args().First())
if err != nil {
return err
}
task, err := container.Task(ctx, nil)
if err != nil {
return err
}
return task.Resume(ctx)
},
}

View File

@@ -0,0 +1,90 @@
package tasks
import (
"github.com/containerd/console"
"github.com/containerd/containerd/cmd/ctr/commands"
"github.com/pkg/errors"
"github.com/sirupsen/logrus"
"github.com/urfave/cli"
)
var startCommand = cli.Command{
Name: "start",
Usage: "start a container that have been created",
ArgsUsage: "CONTAINER",
Flags: []cli.Flag{
cli.BoolFlag{
Name: "null-io",
Usage: "send all IO to /dev/null",
},
},
Action: func(context *cli.Context) error {
var (
err error
id = context.Args().Get(0)
)
if id == "" {
return errors.New("container id must be provided")
}
client, ctx, cancel, err := commands.NewClient(context)
if err != nil {
return err
}
defer cancel()
container, err := client.LoadContainer(ctx, id)
if err != nil {
return err
}
spec, err := container.Spec(ctx)
if err != nil {
return err
}
tty := spec.Process.Terminal
task, err := NewTask(ctx, client, container, "", tty, context.Bool("null-io"))
if err != nil {
return err
}
defer task.Delete(ctx)
statusC, err := task.Wait(ctx)
if err != nil {
return err
}
var con console.Console
if tty {
con = console.Current()
defer con.Reset()
if err := con.SetRaw(); err != nil {
return err
}
}
if err := task.Start(ctx); err != nil {
return err
}
if tty {
if err := HandleConsoleResize(ctx, task, con); err != nil {
logrus.WithError(err).Error("console resize")
}
} else {
sigc := commands.ForwardAllSignals(ctx, task)
defer commands.StopCatch(sigc)
}
status := <-statusC
code, _, err := status.Result()
if err != nil {
return err
}
if _, err := task.Delete(ctx); err != nil {
return err
}
if code != 0 {
return cli.NewExitError("", int(code))
}
return nil
},
}

View File

@@ -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,
},
}

View File

@@ -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))
}

View File

@@ -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)
}