91 lines
2.8 KiB
Go
91 lines
2.8 KiB
Go
|
// +build linux
|
||
|
|
||
|
package main
|
||
|
|
||
|
import (
|
||
|
"fmt"
|
||
|
"strconv"
|
||
|
"strings"
|
||
|
|
||
|
"github.com/codegangsta/cli"
|
||
|
"github.com/opencontainers/runc/libcontainer"
|
||
|
)
|
||
|
|
||
|
var checkpointCommand = cli.Command{
|
||
|
Name: "checkpoint",
|
||
|
Usage: "checkpoint a running container",
|
||
|
Flags: []cli.Flag{
|
||
|
cli.StringFlag{Name: "image-path", Value: "", Usage: "path for saving criu image files"},
|
||
|
cli.StringFlag{Name: "work-path", Value: "", Usage: "path for saving work files and logs"},
|
||
|
cli.BoolFlag{Name: "leave-running", Usage: "leave the process running after checkpointing"},
|
||
|
cli.BoolFlag{Name: "tcp-established", Usage: "allow open tcp connections"},
|
||
|
cli.BoolFlag{Name: "ext-unix-sk", Usage: "allow external unix sockets"},
|
||
|
cli.BoolFlag{Name: "shell-job", Usage: "allow shell jobs"},
|
||
|
cli.StringFlag{Name: "page-server", Value: "", Usage: "ADDRESS:PORT of the page server"},
|
||
|
cli.BoolFlag{Name: "file-locks", Usage: "handle file locks, for safety"},
|
||
|
cli.StringFlag{Name: "manage-cgroups-mode", Value: "", Usage: "cgroups mode: 'soft' (default), 'full' and 'strict'."},
|
||
|
},
|
||
|
Action: func(context *cli.Context) {
|
||
|
container, err := getContainer(context)
|
||
|
if err != nil {
|
||
|
fatal(err)
|
||
|
}
|
||
|
options := criuOptions(context)
|
||
|
status, err := container.Status()
|
||
|
if err != nil {
|
||
|
fatal(err)
|
||
|
}
|
||
|
if status == libcontainer.Checkpointed {
|
||
|
fatal(fmt.Errorf("Container with id %s already checkpointed", context.GlobalString("id")))
|
||
|
}
|
||
|
// these are the mandatory criu options for a container
|
||
|
setPageServer(context, options)
|
||
|
setManageCgroupsMode(context, options)
|
||
|
if err := container.Checkpoint(options); err != nil {
|
||
|
fatal(err)
|
||
|
}
|
||
|
},
|
||
|
}
|
||
|
|
||
|
func getCheckpointImagePath(context *cli.Context) string {
|
||
|
imagePath := context.String("image-path")
|
||
|
if imagePath == "" {
|
||
|
imagePath = getDefaultImagePath(context)
|
||
|
}
|
||
|
return imagePath
|
||
|
}
|
||
|
|
||
|
func setPageServer(context *cli.Context, options *libcontainer.CriuOpts) {
|
||
|
// xxx following criu opts are optional
|
||
|
// The dump image can be sent to a criu page server
|
||
|
if psOpt := context.String("page-server"); psOpt != "" {
|
||
|
addressPort := strings.Split(psOpt, ":")
|
||
|
if len(addressPort) != 2 {
|
||
|
fatal(fmt.Errorf("Use --page-server ADDRESS:PORT to specify page server"))
|
||
|
}
|
||
|
portInt, err := strconv.Atoi(addressPort[1])
|
||
|
if err != nil {
|
||
|
fatal(fmt.Errorf("Invalid port number"))
|
||
|
}
|
||
|
options.PageServer = libcontainer.CriuPageServerInfo{
|
||
|
Address: addressPort[0],
|
||
|
Port: int32(portInt),
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
func setManageCgroupsMode(context *cli.Context, options *libcontainer.CriuOpts) {
|
||
|
if cgOpt := context.String("manage-cgroups-mode"); cgOpt != "" {
|
||
|
switch cgOpt {
|
||
|
case "soft":
|
||
|
options.ManageCgroupsMode = libcontainer.CRIU_CG_MODE_SOFT
|
||
|
case "full":
|
||
|
options.ManageCgroupsMode = libcontainer.CRIU_CG_MODE_FULL
|
||
|
case "strict":
|
||
|
options.ManageCgroupsMode = libcontainer.CRIU_CG_MODE_STRICT
|
||
|
default:
|
||
|
fatal(fmt.Errorf("Invalid manage cgroups mode"))
|
||
|
}
|
||
|
}
|
||
|
}
|