github.com/opencontainers/runc@v1.2.0-rc.1.0.20240520010911-492dc558cdd6/libcontainer/integration/checkpoint_test.go (about)

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