...

Source file src/github.com/opencontainers/runc/libcontainer/integration/checkpoint_test.go

Documentation: github.com/opencontainers/runc/libcontainer/integration

     1  package integration
     2  
     3  import (
     4  	"bufio"
     5  	"bytes"
     6  	"os"
     7  	"os/exec"
     8  	"path/filepath"
     9  	"regexp"
    10  	"strings"
    11  	"testing"
    12  
    13  	"github.com/opencontainers/runc/libcontainer"
    14  	"golang.org/x/sys/unix"
    15  )
    16  
    17  func showFile(t *testing.T, fname string) {
    18  	t.Helper()
    19  	t.Logf("=== %s ===\n", fname)
    20  
    21  	f, err := os.Open(fname)
    22  	if err != nil {
    23  		t.Log(err)
    24  		return
    25  	}
    26  	defer f.Close() //nolint: errcheck
    27  
    28  	scanner := bufio.NewScanner(f)
    29  	for scanner.Scan() {
    30  		t.Log(scanner.Text())
    31  	}
    32  
    33  	if err := scanner.Err(); err != nil {
    34  		t.Log(err)
    35  		return
    36  	}
    37  
    38  	t.Logf("=== END ===\n")
    39  }
    40  
    41  func TestUsernsCheckpoint(t *testing.T) {
    42  	if _, err := os.Stat("/proc/self/ns/user"); os.IsNotExist(err) {
    43  		t.Skip("Test requires userns.")
    44  	}
    45  	cmd := exec.Command("criu", "check", "--feature", "userns")
    46  	if err := cmd.Run(); err != nil {
    47  		t.Skip("Unable to c/r a container with userns")
    48  	}
    49  	testCheckpoint(t, true)
    50  }
    51  
    52  func TestCheckpoint(t *testing.T) {
    53  	testCheckpoint(t, false)
    54  }
    55  
    56  func testCheckpoint(t *testing.T, userns bool) {
    57  	if testing.Short() {
    58  		return
    59  	}
    60  
    61  	if _, err := exec.LookPath("criu"); err != nil {
    62  		t.Skipf("criu binary not found: %v", err)
    63  	}
    64  
    65  	// Workaround for https://github.com/opencontainers/runc/issues/3532.
    66  	out, err := exec.Command("rpm", "-q", "criu").CombinedOutput()
    67  	if err == nil && regexp.MustCompile(`^criu-3\.17-[123]\.el9`).Match(out) {
    68  		t.Skip("Test requires criu >= 3.17-4 on CentOS Stream 9.")
    69  	}
    70  
    71  	config := newTemplateConfig(t, &tParam{userns: userns})
    72  	factory, err := libcontainer.New(t.TempDir())
    73  	ok(t, err)
    74  
    75  	container, err := factory.Create("test", config)
    76  	ok(t, err)
    77  	defer destroyContainer(container)
    78  
    79  	stdinR, stdinW, err := os.Pipe()
    80  	ok(t, err)
    81  
    82  	var stdout bytes.Buffer
    83  
    84  	pconfig := libcontainer.Process{
    85  		Cwd:    "/",
    86  		Args:   []string{"cat"},
    87  		Env:    standardEnvironment,
    88  		Stdin:  stdinR,
    89  		Stdout: &stdout,
    90  		Init:   true,
    91  	}
    92  
    93  	err = container.Run(&pconfig)
    94  	_ = stdinR.Close()
    95  	defer stdinW.Close() //nolint: errcheck
    96  	ok(t, err)
    97  
    98  	pid, err := pconfig.Pid()
    99  	ok(t, err)
   100  
   101  	process, err := os.FindProcess(pid)
   102  	ok(t, err)
   103  
   104  	tmp := t.TempDir()
   105  
   106  	parentDir := filepath.Join(tmp, "criu-parent")
   107  	preDumpOpts := &libcontainer.CriuOpts{
   108  		ImagesDirectory: parentDir,
   109  		WorkDirectory:   parentDir,
   110  		PreDump:         true,
   111  	}
   112  	preDumpLog := filepath.Join(preDumpOpts.WorkDirectory, "dump.log")
   113  
   114  	if err := container.Checkpoint(preDumpOpts); err != nil {
   115  		showFile(t, preDumpLog)
   116  		t.Fatal(err)
   117  	}
   118  
   119  	state, err := container.Status()
   120  	ok(t, err)
   121  
   122  	if state != libcontainer.Running {
   123  		t.Fatal("Unexpected preDump state: ", state)
   124  	}
   125  
   126  	imagesDir := filepath.Join(tmp, "criu")
   127  
   128  	checkpointOpts := &libcontainer.CriuOpts{
   129  		ImagesDirectory: imagesDir,
   130  		WorkDirectory:   imagesDir,
   131  		ParentImage:     "../criu-parent",
   132  	}
   133  	dumpLog := filepath.Join(checkpointOpts.WorkDirectory, "dump.log")
   134  	restoreLog := filepath.Join(checkpointOpts.WorkDirectory, "restore.log")
   135  
   136  	if err := container.Checkpoint(checkpointOpts); err != nil {
   137  		showFile(t, dumpLog)
   138  		t.Fatal(err)
   139  	}
   140  
   141  	state, err = container.Status()
   142  	ok(t, err)
   143  
   144  	if state != libcontainer.Stopped {
   145  		t.Fatal("Unexpected state checkpoint: ", state)
   146  	}
   147  
   148  	_ = stdinW.Close()
   149  	_, err = process.Wait()
   150  	ok(t, err)
   151  
   152  	// reload the container
   153  	container, err = factory.Load("test")
   154  	ok(t, err)
   155  
   156  	restoreStdinR, restoreStdinW, err := os.Pipe()
   157  	ok(t, err)
   158  
   159  	var restoreStdout bytes.Buffer
   160  	restoreProcessConfig := &libcontainer.Process{
   161  		Cwd:    "/",
   162  		Stdin:  restoreStdinR,
   163  		Stdout: &restoreStdout,
   164  		Init:   true,
   165  	}
   166  
   167  	err = container.Restore(restoreProcessConfig, checkpointOpts)
   168  	_ = restoreStdinR.Close()
   169  	defer restoreStdinW.Close() //nolint: errcheck
   170  	if err != nil {
   171  		showFile(t, restoreLog)
   172  		t.Fatal(err)
   173  	}
   174  
   175  	state, err = container.Status()
   176  	ok(t, err)
   177  	if state != libcontainer.Running {
   178  		t.Fatal("Unexpected restore state: ", state)
   179  	}
   180  
   181  	pid, err = restoreProcessConfig.Pid()
   182  	ok(t, err)
   183  
   184  	err = unix.Kill(pid, 0)
   185  	ok(t, err)
   186  
   187  	_, err = restoreStdinW.WriteString("Hello!")
   188  	ok(t, err)
   189  
   190  	_ = restoreStdinW.Close()
   191  	waitProcess(restoreProcessConfig, t)
   192  
   193  	output := restoreStdout.String()
   194  	if !strings.Contains(output, "Hello!") {
   195  		t.Fatal("Did not restore the pipe correctly:", output)
   196  	}
   197  }
   198  

View as plain text