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()
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
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()
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
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()
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