From 206b5e6f1d4a85e8b74da44b8059f80e6b9aec80 Mon Sep 17 00:00:00 2001 From: boucher Date: Mon, 18 May 2015 14:52:26 -0700 Subject: [PATCH] First attempt at an integration test for checkpoint/restore. Adds iptables as a requirement of criu in the Dockerfile. Docker-DCO-1.1-Signed-off-by: Ross Boucher (github: boucher) --- Dockerfile | 2 +- factory_linux.go | 1 + integration/checkpoint_test.go | 163 +++++++++++++++++++++++++++++++++ 3 files changed, 165 insertions(+), 1 deletion(-) create mode 100644 integration/checkpoint_test.go diff --git a/Dockerfile b/Dockerfile index e6c05660d..a88549889 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,7 +1,7 @@ FROM golang:1.4 RUN echo "deb http://ftp.us.debian.org/debian testing main contrib" >> /etc/apt/sources.list -RUN apt-get update && apt-get install -y criu=1.5.2-1 && rm -rf /var/lib/apt/lists/* +RUN apt-get update && apt-get install -y iptables criu=1.5.2-1 && rm -rf /var/lib/apt/lists/* RUN go get golang.org/x/tools/cmd/cover diff --git a/factory_linux.go b/factory_linux.go index a1c7289f9..26b351ace 100644 --- a/factory_linux.go +++ b/factory_linux.go @@ -106,6 +106,7 @@ func New(root string, options ...func(*LinuxFactory) error) (Factory, error) { l := &LinuxFactory{ Root: root, Validator: validate.New(), + CriuPath: "criu", } InitArgs(os.Args[0], "init")(l) Cgroupfs(l) diff --git a/integration/checkpoint_test.go b/integration/checkpoint_test.go new file mode 100644 index 000000000..45400aee5 --- /dev/null +++ b/integration/checkpoint_test.go @@ -0,0 +1,163 @@ +package integration + +import ( + "bytes" + "io/ioutil" + "os" + "strings" + "testing" + + "github.com/docker/libcontainer" +) + +func TestCheckpoint(t *testing.T) { + if testing.Short() { + return + } + root, err := newTestRoot() + if err != nil { + t.Fatal(err) + } + defer os.RemoveAll(root) + + rootfs, err := newRootfs() + if err != nil { + t.Fatal(err) + } + defer remove(rootfs) + + config := newTemplateConfig(rootfs) + + factory, err := libcontainer.New(root, libcontainer.Cgroupfs) + + if err != nil { + t.Fatal(err) + } + + container, err := factory.Create("test", config) + if err != nil { + t.Fatal(err) + } + defer container.Destroy() + + stdinR, stdinW, err := os.Pipe() + if err != nil { + t.Fatal(err) + } + + var stdout bytes.Buffer + + pconfig := libcontainer.Process{ + Args: []string{"cat"}, + Env: standardEnvironment, + Stdin: stdinR, + Stdout: &stdout, + } + + err = container.Start(&pconfig) + stdinR.Close() + defer stdinW.Close() + if err != nil { + t.Fatal(err) + } + + pid, err := pconfig.Pid() + if err != nil { + t.Fatal(err) + } + + process, err := os.FindProcess(pid) + if err != nil { + t.Fatal(err) + } + + imagesDir, err := ioutil.TempDir("", "criu") + if err != nil { + t.Fatal(err) + } + defer os.RemoveAll(imagesDir) + + checkpointOpts := &libcontainer.CriuOpts{ + ImagesDirectory: imagesDir, + WorkDirectory: imagesDir, + } + + if err := container.Checkpoint(checkpointOpts); err != nil { + t.Fatal(err) + } + + state, err := container.Status() + if err != nil { + t.Fatal(err) + } + + if state != libcontainer.Checkpointed { + t.Fatal("Unexpected state: ", state) + } + + stdinW.Close() + _, err = process.Wait() + if err != nil { + t.Fatal(err) + } + + // reload the container + container, err = factory.Load("test") + if err != nil { + t.Fatal(err) + } + + restoreStdinR, restoreStdinW, err := os.Pipe() + if err != nil { + t.Fatal(err) + } + + restoreProcessConfig := &libcontainer.Process{ + Stdin: restoreStdinR, + Stdout: &stdout, + } + + err = container.Restore(restoreProcessConfig, &libcontainer.CriuOpts{ + ImagesDirectory: imagesDir, + }) + restoreStdinR.Close() + defer restoreStdinW.Close() + + state, err = container.Status() + if err != nil { + t.Fatal(err) + } + if state != libcontainer.Running { + t.Fatal("Unexpected state: ", state) + } + + pid, err = restoreProcessConfig.Pid() + if err != nil { + t.Fatal(err) + } + + process, err = os.FindProcess(pid) + if err != nil { + t.Fatal(err) + } + + _, err = restoreStdinW.WriteString("Hello!") + if err != nil { + t.Fatal(err) + } + + restoreStdinW.Close() + s, err := process.Wait() + if err != nil { + t.Fatal(err) + } + + if !s.Success() { + t.Fatal(s.String(), pid) + } + + output := string(stdout.Bytes()) + if !strings.Contains(output, "Hello!") { + t.Fatal("Did not restore the pipe correctly:", output) + } +}