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 }