Skip to content

Commit

Permalink
First implementation of migrate
Browse files Browse the repository at this point in the history
  • Loading branch information
xetorthio committed Sep 19, 2015
1 parent f703a35 commit 9e7b156
Show file tree
Hide file tree
Showing 7 changed files with 131 additions and 46 deletions.
7 changes: 7 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -22,3 +22,10 @@ _testmain.go
*.exe
*.test
*.prof

cmt
runtime.json
config.json
rootfs
cpuinfo.img
descriptors.json
12 changes: 12 additions & 0 deletions clean.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
#!/bin/bash

sudo rm dump.tar.gz
ssh 172.31.60.136 <<EOF
cd lala
sudo runc kill
sudo rm -fr /var/run/opencontainer/
sudo rm -fr images
sudo rm dump.tar.gz
sudo sh -c 'runc start &'
echo "DONE"
EOF
4 changes: 3 additions & 1 deletion cmd/cmd.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,15 @@ import (
)

type Cmd interface {
Run(c string, args ...string) (string, string, error)
Run(name string, args ...string) (string, string, error)
Output(name string, args ...string) (string, string, error)
URL(path string) *url.URL
}

func Scp(src, dest *url.URL) error {
scpCmd := NewLocal()
_, _, err := scpCmd.Run("scp", formatCopyURL(src), formatCopyURL(dest))

return err
}

Expand Down
8 changes: 8 additions & 0 deletions cmd/local.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package cmd

import (
"bytes"
"log"
"net/url"
"os/exec"
)
Expand All @@ -27,6 +28,13 @@ func (c *LocalCmd) Run(name string, args ...string) (string, string, error) {
return stdout.String(), stderr.String(), err
}

func (c *LocalCmd) Output(name string, args ...string) (string, string, error) {
log.Println(name, args)
stdout, stderr, err := c.Run(name, args...)
log.Println(stdout, stderr)
return stdout, stderr, err
}

func (c *LocalCmd) URL(path string) *url.URL {
return &url.URL{
Path: path,
Expand Down
10 changes: 9 additions & 1 deletion cmd/ssh.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import (
"bytes"
"fmt"
"io/ioutil"
"log"
"net"
"net/url"
"os"
Expand All @@ -23,7 +24,8 @@ type SSHCmd struct {
func NewSSH(user, host string) *SSHCmd {
c := SSHCmd{}
c.config.User = user
c.host = host
chunks := strings.Split(host, ":")
c.host = chunks[0]
return &c
}

Expand Down Expand Up @@ -103,6 +105,12 @@ func (r *SSHCmd) Run(name string, args ...string) (string, string, error) {

return stdout.String(), stderr.String(), err
}
func (r *SSHCmd) Output(name string, args ...string) (string, string, error) {
log.Println(name, args)
stdout, stderr, err := r.Run(name, args...)
log.Println(stdout, stderr)
return stdout, stderr, err
}

func (c *SSHCmd) URL(path string) *url.URL {
return &url.URL{
Expand Down
89 changes: 70 additions & 19 deletions migrate/migrate.go
Original file line number Diff line number Diff line change
@@ -1,36 +1,87 @@
package migrate

import "github.com/codegangsta/cli"
import (
"fmt"
"log"
"path/filepath"

"github.com/codegangsta/cli"
"github.com/marcosnils/cmt/cmd"
"github.com/marcosnils/cmt/validate"
)

var Command = cli.Command{
Name: "migrate",
Usage: "Migrate running container",
Flags: []cli.Flag{
cli.StringFlag{
Name: "source-host",
Name: "src",
Usage: "Source host where the container is running",
},
cli.StringFlag{
Name: "container-dir",
Usage: "Directory where the container is running in the source host",
},
cli.StringFlag{
Name: "target-host",
Name: "dst",
Usage: "Target host to migrate the container",
},
cli.StringFlag{
Name: "target-container-dir",
Usage: "Directory to copy container data in target host",
},
cli.BoolFlag{
Name: "pre-dump",
Usage: "Perform pre-dumps when migrating",
},
cli.DurationFlag{
Name: "max-downtime",
Usage: "Max downtime allowed when performing pre-dumps",
},
},
Action: func(c *cli.Context) {
srcUrl := validate.ParseURL(c.String("src"))
dstUrl := validate.ParseURL(c.String("dst"))

log.Println("Performing validations")
src, dst := validate.Validate(srcUrl, dstUrl)

log.Println("Preparing everything to do a checkpoint")
imagesPath := fmt.Sprintf("%s/images", srcUrl.Path)
containerId := getContainerId(srcUrl.Path)

_, _, err := src.Run("mkdir", "-p", imagesPath)
if err != nil {
log.Fatal(err)
}

log.Println("Performing the checkpoint")
_, _, err = src.Run("sudo", "runc", "--id", containerId, "checkpoint", "--image-path", imagesPath)
if err != nil {
log.Fatal(err)
}

srcTarFile := fmt.Sprintf("%s/dump.tar.gz", srcUrl.Path)
dstTarFile := fmt.Sprintf("%s/images/dump.tar.gz", dstUrl.Path)
_, _, err = src.Run("sudo", "tar", "-czf", srcTarFile, "-C", fmt.Sprintf("%s/", imagesPath), ".")
if err != nil {
log.Fatal(err)
}

log.Println("Copying checkpoint image to dst")
_, _, err = dst.Run("mkdir", "-p", fmt.Sprintf("%s/images", dstUrl.Path))
if err != nil {
log.Fatal(err)
}

err = cmd.Scp(src.URL(srcTarFile), dst.URL(fmt.Sprintf("%s/images", dstUrl.Path)))
if err != nil {
log.Fatal(err)
}

log.Println("Preparing image at destination host")
_, _, err = dst.Run("sudo", "tar", "-C", fmt.Sprintf("%s/images", dstUrl.Path), "-xvzf", dstTarFile)
if err != nil {
log.Fatal(err)
}

log.Println("Performing the restore")
configFilePath := fmt.Sprintf("%s/config.json", dstUrl.Path)
runtimeFilePath := fmt.Sprintf("%s/runtime.json", dstUrl.Path)
dstImagesPath := fmt.Sprintf("%s/images", dstUrl.Path)
_, _, err = dst.Output("sudo", "runc", "--id", containerId, "restore", "--image-path", dstImagesPath, "--config-file", configFilePath, "--runtime-file", runtimeFilePath)
if err != nil {
log.Fatal(err)
}

},
}

func getContainerId(path string) string {
_, id := filepath.Split(path)
return id
}
47 changes: 22 additions & 25 deletions validate/validate.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,46 +28,41 @@ var Command = cli.Command{
},
},
Action: func(c *cli.Context) {
src := c.String("src")
if src != "" && !strings.HasPrefix(src, "ssh://") {
src = fmt.Sprintf("ssh://%s", src)
}

dst := c.String("dst")
if dst != "" && !strings.HasPrefix(dst, "ssh://") {
dst = fmt.Sprintf("ssh://%s", dst)
}

srcURL := parseURL(src)
dstURL := parseURL(dst)
srcURL := ParseURL(c.String("src"))
dstURL := ParseURL(c.String("dst"))

Validate(srcURL, dstURL)
println("Validation succeded")

},
}

func parseURL(stringURL string) *url.URL {
if stringURL == "" {
func ParseURL(rawurl string) *url.URL {
if rawurl == "" {
return nil
}
// We do this hack beacuse url.Parse require a schema to do the right thing
schemaUrl := rawurl
if !strings.HasPrefix(rawurl, "ssh://") {
schemaUrl = fmt.Sprintf("ssh://%s", rawurl)
}

parsedURL, err := url.Parse(stringURL)
if err != nil || parsedURL.Host == "" {
log.Fatal("Error parsing host: ", stringURL)
u, err := url.Parse(schemaUrl)
if err != nil {
log.Fatal("Error parsing host: ", rawurl)
}

return parsedURL
return u

}

func Validate(src, dst *url.URL) {
if src == nil && dst == nil {
log.Fatal("Either one of dst or src must be specified")
func Validate(src, dst *url.URL) (srcCmd, dstCmd cmd.Cmd) {
if src == nil || dst == nil {
log.Fatal("Both src and dst must be specified")
}

srcCmd := getCommand(src)
dstCmd := getCommand(dst)
srcCmd = GetCommand(src)
dstCmd = GetCommand(dst)

if e := checkVersion(srcCmd, dstCmd, "criu"); e != nil {
log.Fatal(e)
Expand All @@ -87,6 +82,8 @@ func Validate(src, dst *url.URL) {
if e := checkCPUCompat(srcCmd, dstCmd); e != nil {
log.Fatal(e)
}

return
}

func checkCPUCompat(srcCmd, dstCmd cmd.Cmd) error {
Expand Down Expand Up @@ -135,8 +132,8 @@ func checkKernelCap(c cmd.Cmd) error {
return err
}

func getCommand(hostURL *url.URL) cmd.Cmd {
if hostURL != nil {
func GetCommand(hostURL *url.URL) cmd.Cmd {
if hostURL.Host != "" {
rc := cmd.NewSSH(hostURL.User.Username(), hostURL.Host)
if err := rc.UseAgent(); err != nil {
log.Fatal("Unable to use SSH agent for host: ", hostURL.String())
Expand Down

0 comments on commit 9e7b156

Please sign in to comment.