diff --git a/ctr/checkpoint.go b/ctr/checkpoint.go new file mode 100644 index 0000000..7118ae4 --- /dev/null +++ b/ctr/checkpoint.go @@ -0,0 +1,119 @@ +package main + +import ( + "fmt" + "os" + "text/tabwriter" + + "github.com/codegangsta/cli" + "github.com/docker/containerd/api/grpc/types" + netcontext "golang.org/x/net/context" +) + +var CheckpointCommand = cli.Command{ + Name: "checkpoints", + Usage: "list all checkpoints", + Subcommands: []cli.Command{ + ListCheckpointCommand, + CreateCheckpointCommand, + }, + Action: listCheckpoints, +} + +var ListCheckpointCommand = cli.Command{ + Name: "list", + Usage: "list all checkpoints for a container", + Action: listCheckpoints, +} + +func listCheckpoints(context *cli.Context) { + var ( + cli = getClient() + id = context.Args().First() + ) + if id == "" { + fatal("container id cannot be empty", 1) + } + resp, err := cli.ListCheckpoint(netcontext.Background(), &types.ListCheckpointRequest{ + Id: id, + }) + if err != nil { + fatal(err.Error(), 1) + } + w := tabwriter.NewWriter(os.Stdout, 20, 1, 3, ' ', 0) + fmt.Fprint(w, "NAME\tTCP\tUNIX SOCKETS\tSHELL\n") + for _, c := range resp.Checkpoints { + fmt.Fprintf(w, "%s\t%v\t%v\t%v\n", c.Name, c.Tcp, c.UnixSockets, c.Shell) + } + if err := w.Flush(); err != nil { + fatal(err.Error(), 1) + } +} + +var CreateCheckpointCommand = cli.Command{ + Name: "create", + Usage: "create a new checkpoint for the container", + Flags: []cli.Flag{ + cli.BoolFlag{ + Name: "tcp", + Usage: "persist open tcp connections", + }, + cli.BoolFlag{ + Name: "unix-sockets", + Usage: "perist unix sockets", + }, + cli.BoolFlag{ + Name: "exit", + Usage: "exit the container after the checkpoint completes successfully", + }, + cli.BoolFlag{ + Name: "shell", + Usage: "checkpoint shell jobs", + }, + }, + Action: func(context *cli.Context) { + var ( + containerID = context.Args().Get(0) + name = context.Args().Get(1) + ) + if containerID == "" { + fatal("container id at cannot be empty", 1) + } + if name == "" { + fatal("checkpoint name cannot be empty", 1) + } + cli := getClient() + if _, err := cli.CreateCheckpoint(netcontext.Background(), &types.CreateCheckpointRequest{ + Id: containerID, + Checkpoint: &types.Checkpoint{ + Name: name, + }, + }); err != nil { + fatal(err.Error(), 1) + } + }, +} + +var DeleteCheckpointCommand = cli.Command{ + Name: "delete", + Usage: "delete a container's checkpoint", + Action: func(context *cli.Context) { + var ( + containerID = context.Args().Get(0) + name = context.Args().Get(1) + ) + if containerID == "" { + fatal("container id at cannot be empty", 1) + } + if name == "" { + fatal("checkpoint name cannot be empty", 1) + } + cli := getClient() + if _, err := cli.DeleteCheckpoint(netcontext.Background(), &types.DeleteCheckpointRequest{ + Id: containerID, + Name: name, + }); err != nil { + fatal(err.Error(), 1) + } + }, +} diff --git a/ctr/container.go b/ctr/container.go index 70f05f3..574402e 100644 --- a/ctr/container.go +++ b/ctr/container.go @@ -5,7 +5,6 @@ import ( "os" "text/tabwriter" - "github.com/Sirupsen/logrus" "github.com/codegangsta/cli" "github.com/docker/containerd/api/grpc/types" netcontext "golang.org/x/net/context" @@ -47,10 +46,10 @@ func listContainers(context *cli.Context) { w := tabwriter.NewWriter(os.Stdout, 20, 1, 3, ' ', 0) fmt.Fprint(w, "ID\tPATH\tSTATUS\tPID1\n") for _, c := range resp.Containers { - fmt.Fprintf(w, "%s\t%s\t%s\n", c.Id, c.BundlePath, c.Status, c.Processes[0].Pid) + fmt.Fprintf(w, "%s\t%s\t%s\t%d\n", c.Id, c.BundlePath, c.Status, c.Processes[0].Pid) } if err := w.Flush(); err != nil { - logrus.Fatal(err) + fatal(err.Error(), 1) } } @@ -58,11 +57,6 @@ var StartCommand = cli.Command{ Name: "start", Usage: "start a container", Flags: []cli.Flag{ - cli.StringFlag{ - Name: "id", - Value: "", - Usage: "id of the container", - }, cli.StringFlag{ Name: "checkpoint,c", Value: "", @@ -70,11 +64,13 @@ var StartCommand = cli.Command{ }, }, Action: func(context *cli.Context) { - path := context.Args().First() + var ( + id = context.Args().Get(0) + path = context.Args().Get(1) + ) if path == "" { fatal("bundle path cannot be empty", 1) } - id := context.String("id") if id == "" { fatal("container id cannot be empty", 1) } diff --git a/ctr/main.go b/ctr/main.go index a189d2c..4a2d8f1 100644 --- a/ctr/main.go +++ b/ctr/main.go @@ -35,6 +35,7 @@ func main() { } app.Commands = []cli.Command{ ContainersCommand, + CheckpointCommand, } app.Before = func(context *cli.Context) error { if context.GlobalBool("debug") { @@ -48,6 +49,6 @@ func main() { } func fatal(err string, code int) { - fmt.Fprintf(os.Stderr, "[ctr] %s", err) + fmt.Fprintf(os.Stderr, "[ctr] %s\n", err) os.Exit(code) }