1 package integration
2
3 import (
4 "bytes"
5 "fmt"
6 "os"
7 "os/exec"
8 "path/filepath"
9 "regexp"
10 "runtime"
11 "strconv"
12 "strings"
13 "syscall"
14 "testing"
15 "time"
16
17 "github.com/opencontainers/runc/libcontainer"
18 "github.com/opencontainers/runc/libcontainer/configs"
19 )
20
21 var busyboxTar string
22
23
24
25
26 func init() {
27
28
29 _, ex, _, _ := runtime.Caller(0)
30 getImages, err := filepath.Abs(filepath.Join(filepath.Dir(ex), "..", "..", "tests", "integration", "get-images.sh"))
31 if err != nil {
32 panic(err)
33 }
34
35 out, err := exec.Command(getImages).CombinedOutput()
36 if err != nil {
37 panic(fmt.Errorf("getImages error %w (output: %s)", err, out))
38 }
39
40 found := regexp.MustCompile(`(?m)^BUSYBOX_IMAGE=(.*)$`).FindSubmatchIndex(out)
41 if len(found) < 4 {
42 panic(fmt.Errorf("unable to find BUSYBOX_IMAGE=<value> in %q", out))
43 }
44 busyboxTar = string(out[found[2]:found[3]])
45
46 if _, err := os.Stat(busyboxTar); err != nil {
47 panic(err)
48 }
49 }
50
51 func ptrInt(v int) *int {
52 return &v
53 }
54
55 func newStdBuffers() *stdBuffers {
56 return &stdBuffers{
57 Stdin: bytes.NewBuffer(nil),
58 Stdout: bytes.NewBuffer(nil),
59 Stderr: bytes.NewBuffer(nil),
60 }
61 }
62
63 type stdBuffers struct {
64 Stdin *bytes.Buffer
65 Stdout *bytes.Buffer
66 Stderr *bytes.Buffer
67 }
68
69 func (b *stdBuffers) String() string {
70 s := []string{}
71 if b.Stderr != nil {
72 s = append(s, b.Stderr.String())
73 }
74 if b.Stdout != nil {
75 s = append(s, b.Stdout.String())
76 }
77 return strings.Join(s, "|")
78 }
79
80
81 func ok(t testing.TB, err error) {
82 t.Helper()
83 if err != nil {
84 t.Fatalf("unexpected error: %v", err)
85 }
86 }
87
88 func waitProcess(p *libcontainer.Process, t *testing.T) {
89 t.Helper()
90 status, err := p.Wait()
91 if err != nil {
92 t.Fatalf("unexpected error: %v", err)
93 }
94
95 if !status.Success() {
96 t.Fatalf("unexpected status: %v", status)
97 }
98 }
99
100
101
102 func newRootfs(t *testing.T) string {
103 t.Helper()
104 dir := t.TempDir()
105 if err := copyBusybox(dir); err != nil {
106 t.Fatal(err)
107 }
108
109
110
111 if err := traversePath(dir); err != nil {
112 t.Fatalf("Error making newRootfs path traversable by others: %v", err)
113 }
114
115 return dir
116 }
117
118
119
120
121 func traversePath(tPath string) error {
122
123 tempBase := os.TempDir()
124 if !strings.HasPrefix(tPath, tempBase) {
125 return fmt.Errorf("traversePath: %q is not a descendant of %q", tPath, tempBase)
126 }
127
128 var path string
129 for _, p := range strings.SplitAfter(tPath, "/") {
130 path = path + p
131 stats, err := os.Stat(path)
132 if err != nil {
133 return err
134 }
135
136 perm := stats.Mode().Perm()
137
138 if perm&0o5 == 0o5 {
139 continue
140 }
141
142 if strings.HasPrefix(tempBase, path) {
143 return fmt.Errorf("traversePath: directory %q MUST have read+exec permissions for others", path)
144 }
145
146 if err := os.Chmod(path, perm|0o5); err != nil {
147 return err
148 }
149 }
150
151 return nil
152 }
153
154 func remove(dir string) {
155 _ = os.RemoveAll(dir)
156 }
157
158
159
160 func copyBusybox(dest string) error {
161 out, err := exec.Command("sh", "-c", fmt.Sprintf("tar --exclude './dev/*' -C %q -xf %q", dest, busyboxTar)).CombinedOutput()
162 if err != nil {
163 return fmt.Errorf("untar error %w: %q", err, out)
164 }
165 return nil
166 }
167
168 func newContainer(t *testing.T, config *configs.Config) (libcontainer.Container, error) {
169 name := strings.ReplaceAll(t.Name(), "/", "_") + strconv.FormatInt(-int64(time.Now().Nanosecond()), 35)
170 root := t.TempDir()
171
172 f, err := libcontainer.New(root)
173 if err != nil {
174 return nil, err
175 }
176 return f.Create(name, config)
177 }
178
179
180
181
182
183 func runContainer(t *testing.T, config *configs.Config, args ...string) (buffers *stdBuffers, exitCode int, err error) {
184 container, err := newContainer(t, config)
185 if err != nil {
186 return nil, -1, err
187 }
188 defer destroyContainer(container)
189 buffers = newStdBuffers()
190 process := &libcontainer.Process{
191 Cwd: "/",
192 Args: args,
193 Env: standardEnvironment,
194 Stdin: buffers.Stdin,
195 Stdout: buffers.Stdout,
196 Stderr: buffers.Stderr,
197 Init: true,
198 }
199
200 err = container.Run(process)
201 if err != nil {
202 return buffers, -1, err
203 }
204 ps, err := process.Wait()
205 if err != nil {
206 return buffers, -1, err
207 }
208 status := ps.Sys().(syscall.WaitStatus)
209 if status.Exited() {
210 exitCode = status.ExitStatus()
211 } else if status.Signaled() {
212 exitCode = -int(status.Signal())
213 } else {
214 return buffers, -1, err
215 }
216 return
217 }
218
219
220
221 func runContainerOk(t *testing.T, config *configs.Config, args ...string) *stdBuffers {
222 buffers, exitCode, err := runContainer(t, config, args...)
223
224 t.Helper()
225 if err != nil {
226 t.Fatalf("%s: %s", buffers, err)
227 }
228 if exitCode != 0 {
229 t.Fatalf("exit code not 0. code %d stderr %q", exitCode, buffers.Stderr)
230 }
231
232 return buffers
233 }
234
235 func destroyContainer(container libcontainer.Container) {
236 _ = container.Destroy()
237 }
238
View as plain text