integration: use test helper for error check

- improves readability
- less typing

Signed-off-by: Jörg Thalheim <joerg@higgsboson.tk>
This commit is contained in:
Jörg Thalheim 2015-04-04 22:29:50 +02:00
parent 9a25d3000c
commit 7d49705871
3 changed files with 94 additions and 237 deletions

View File

@ -29,9 +29,7 @@ func testExecPS(t *testing.T, userns bool) {
return return
} }
rootfs, err := newRootfs() rootfs, err := newRootfs()
if err != nil { ok(t, err)
t.Fatal(err)
}
defer remove(rootfs) defer remove(rootfs)
config := newTemplateConfig(rootfs) config := newTemplateConfig(rootfs)
if userns { if userns {
@ -64,21 +62,15 @@ func TestIPCPrivate(t *testing.T) {
} }
rootfs, err := newRootfs() rootfs, err := newRootfs()
if err != nil { ok(t, err)
t.Fatal(err)
}
defer remove(rootfs) defer remove(rootfs)
l, err := os.Readlink("/proc/1/ns/ipc") l, err := os.Readlink("/proc/1/ns/ipc")
if err != nil { ok(t, err)
t.Fatal(err)
}
config := newTemplateConfig(rootfs) config := newTemplateConfig(rootfs)
buffers, exitCode, err := runContainer(config, "", "readlink", "/proc/self/ns/ipc") buffers, exitCode, err := runContainer(config, "", "readlink", "/proc/self/ns/ipc")
if err != nil { ok(t, err)
t.Fatal(err)
}
if exitCode != 0 { if exitCode != 0 {
t.Fatalf("exit code not 0. code %d stderr %q", exitCode, buffers.Stderr) t.Fatalf("exit code not 0. code %d stderr %q", exitCode, buffers.Stderr)
@ -95,22 +87,16 @@ func TestIPCHost(t *testing.T) {
} }
rootfs, err := newRootfs() rootfs, err := newRootfs()
if err != nil { ok(t, err)
t.Fatal(err)
}
defer remove(rootfs) defer remove(rootfs)
l, err := os.Readlink("/proc/1/ns/ipc") l, err := os.Readlink("/proc/1/ns/ipc")
if err != nil { ok(t, err)
t.Fatal(err)
}
config := newTemplateConfig(rootfs) config := newTemplateConfig(rootfs)
config.Namespaces.Remove(configs.NEWIPC) config.Namespaces.Remove(configs.NEWIPC)
buffers, exitCode, err := runContainer(config, "", "readlink", "/proc/self/ns/ipc") buffers, exitCode, err := runContainer(config, "", "readlink", "/proc/self/ns/ipc")
if err != nil { ok(t, err)
t.Fatal(err)
}
if exitCode != 0 { if exitCode != 0 {
t.Fatalf("exit code not 0. code %d stderr %q", exitCode, buffers.Stderr) t.Fatalf("exit code not 0. code %d stderr %q", exitCode, buffers.Stderr)
@ -127,23 +113,17 @@ func TestIPCJoinPath(t *testing.T) {
} }
rootfs, err := newRootfs() rootfs, err := newRootfs()
if err != nil { ok(t, err)
t.Fatal(err)
}
defer remove(rootfs) defer remove(rootfs)
l, err := os.Readlink("/proc/1/ns/ipc") l, err := os.Readlink("/proc/1/ns/ipc")
if err != nil { ok(t, err)
t.Fatal(err)
}
config := newTemplateConfig(rootfs) config := newTemplateConfig(rootfs)
config.Namespaces.Add(configs.NEWIPC, "/proc/1/ns/ipc") config.Namespaces.Add(configs.NEWIPC, "/proc/1/ns/ipc")
buffers, exitCode, err := runContainer(config, "", "readlink", "/proc/self/ns/ipc") buffers, exitCode, err := runContainer(config, "", "readlink", "/proc/self/ns/ipc")
if err != nil { ok(t, err)
t.Fatal(err)
}
if exitCode != 0 { if exitCode != 0 {
t.Fatalf("exit code not 0. code %d stderr %q", exitCode, buffers.Stderr) t.Fatalf("exit code not 0. code %d stderr %q", exitCode, buffers.Stderr)
@ -160,9 +140,7 @@ func TestIPCBadPath(t *testing.T) {
} }
rootfs, err := newRootfs() rootfs, err := newRootfs()
if err != nil { ok(t, err)
t.Fatal(err)
}
defer remove(rootfs) defer remove(rootfs)
config := newTemplateConfig(rootfs) config := newTemplateConfig(rootfs)
@ -180,16 +158,12 @@ func TestRlimit(t *testing.T) {
} }
rootfs, err := newRootfs() rootfs, err := newRootfs()
if err != nil { ok(t, err)
t.Fatal(err)
}
defer remove(rootfs) defer remove(rootfs)
config := newTemplateConfig(rootfs) config := newTemplateConfig(rootfs)
out, _, err := runContainer(config, "", "/bin/sh", "-c", "ulimit -n") out, _, err := runContainer(config, "", "/bin/sh", "-c", "ulimit -n")
if err != nil { ok(t, err)
t.Fatal(err)
}
if limit := strings.TrimSpace(out.Stdout.String()); limit != "1025" { if limit := strings.TrimSpace(out.Stdout.String()); limit != "1025" {
t.Fatalf("expected rlimit to be 1025, got %s", limit) t.Fatalf("expected rlimit to be 1025, got %s", limit)
} }
@ -208,9 +182,7 @@ func newTestRoot() (string, error) {
func waitProcess(p *libcontainer.Process, t *testing.T) { func waitProcess(p *libcontainer.Process, t *testing.T) {
status, err := p.Wait() status, err := p.Wait()
if err != nil { ok(t, err)
t.Fatal(err)
}
if !status.Success() { if !status.Success() {
t.Fatal(status) t.Fatal(status)
} }
@ -221,35 +193,25 @@ func TestEnter(t *testing.T) {
return return
} }
root, err := newTestRoot() root, err := newTestRoot()
if err != nil { ok(t, err)
t.Fatal(err)
}
defer os.RemoveAll(root) defer os.RemoveAll(root)
rootfs, err := newRootfs() rootfs, err := newRootfs()
if err != nil { ok(t, err)
t.Fatal(err)
}
defer remove(rootfs) defer remove(rootfs)
config := newTemplateConfig(rootfs) config := newTemplateConfig(rootfs)
factory, err := libcontainer.New(root, libcontainer.Cgroupfs) factory, err := libcontainer.New(root, libcontainer.Cgroupfs)
if err != nil { ok(t, err)
t.Fatal(err)
}
container, err := factory.Create("test", config) container, err := factory.Create("test", config)
if err != nil { ok(t, err)
t.Fatal(err)
}
defer container.Destroy() defer container.Destroy()
// Execute a first process in the container // Execute a first process in the container
stdinR, stdinW, err := os.Pipe() stdinR, stdinW, err := os.Pipe()
if err != nil { ok(t, err)
t.Fatal(err)
}
var stdout, stdout2 bytes.Buffer var stdout, stdout2 bytes.Buffer
@ -262,19 +224,13 @@ func TestEnter(t *testing.T) {
err = container.Start(&pconfig) err = container.Start(&pconfig)
stdinR.Close() stdinR.Close()
defer stdinW.Close() defer stdinW.Close()
if err != nil { ok(t, err)
t.Fatal(err)
}
pid, err := pconfig.Pid() pid, err := pconfig.Pid()
if err != nil { ok(t, err)
t.Fatal(err)
}
// Execute another process in the container // Execute another process in the container
stdinR2, stdinW2, err := os.Pipe() stdinR2, stdinW2, err := os.Pipe()
if err != nil { ok(t, err)
t.Fatal(err)
}
pconfig2 := libcontainer.Process{ pconfig2 := libcontainer.Process{
Env: standardEnvironment, Env: standardEnvironment,
} }
@ -285,19 +241,13 @@ func TestEnter(t *testing.T) {
err = container.Start(&pconfig2) err = container.Start(&pconfig2)
stdinR2.Close() stdinR2.Close()
defer stdinW2.Close() defer stdinW2.Close()
if err != nil { ok(t, err)
t.Fatal(err)
}
pid2, err := pconfig2.Pid() pid2, err := pconfig2.Pid()
if err != nil { ok(t, err)
t.Fatal(err)
}
processes, err := container.Processes() processes, err := container.Processes()
if err != nil { ok(t, err)
t.Fatal(err)
}
n := 0 n := 0
for i := range processes { for i := range processes {
@ -318,14 +268,10 @@ func TestEnter(t *testing.T) {
// Check that both processes live in the same pidns // Check that both processes live in the same pidns
pidns := string(stdout.Bytes()) pidns := string(stdout.Bytes())
if err != nil { ok(t, err)
t.Fatal(err)
}
pidns2 := string(stdout2.Bytes()) pidns2 := string(stdout2.Bytes())
if err != nil { ok(t, err)
t.Fatal(err)
}
if pidns != pidns2 { if pidns != pidns2 {
t.Fatal("The second process isn't in the required pid namespace", pidns, pidns2) t.Fatal("The second process isn't in the required pid namespace", pidns, pidns2)
@ -337,28 +283,20 @@ func TestProcessEnv(t *testing.T) {
return return
} }
root, err := newTestRoot() root, err := newTestRoot()
if err != nil { ok(t, err)
t.Fatal(err)
}
defer os.RemoveAll(root) defer os.RemoveAll(root)
rootfs, err := newRootfs() rootfs, err := newRootfs()
if err != nil { ok(t, err)
t.Fatal(err)
}
defer remove(rootfs) defer remove(rootfs)
config := newTemplateConfig(rootfs) config := newTemplateConfig(rootfs)
factory, err := libcontainer.New(root, libcontainer.Cgroupfs) factory, err := libcontainer.New(root, libcontainer.Cgroupfs)
if err != nil { ok(t, err)
t.Fatal(err)
}
container, err := factory.Create("test", config) container, err := factory.Create("test", config)
if err != nil { ok(t, err)
t.Fatal(err)
}
defer container.Destroy() defer container.Destroy()
var stdout bytes.Buffer var stdout bytes.Buffer
@ -374,17 +312,12 @@ func TestProcessEnv(t *testing.T) {
Stdout: &stdout, Stdout: &stdout,
} }
err = container.Start(&pconfig) err = container.Start(&pconfig)
if err != nil { ok(t, err)
t.Fatal(err)
}
// Wait for process // Wait for process
waitProcess(&pconfig, t) waitProcess(&pconfig, t)
outputEnv := string(stdout.Bytes()) outputEnv := string(stdout.Bytes())
if err != nil {
t.Fatal(err)
}
// Check that the environment has the key/value pair we added // Check that the environment has the key/value pair we added
if !strings.Contains(outputEnv, "FOO=BAR") { if !strings.Contains(outputEnv, "FOO=BAR") {
@ -402,28 +335,20 @@ func TestProcessCaps(t *testing.T) {
return return
} }
root, err := newTestRoot() root, err := newTestRoot()
if err != nil { ok(t, err)
t.Fatal(err)
}
defer os.RemoveAll(root) defer os.RemoveAll(root)
rootfs, err := newRootfs() rootfs, err := newRootfs()
if err != nil { ok(t, err)
t.Fatal(err)
}
defer remove(rootfs) defer remove(rootfs)
config := newTemplateConfig(rootfs) config := newTemplateConfig(rootfs)
factory, err := libcontainer.New(root, libcontainer.Cgroupfs) factory, err := libcontainer.New(root, libcontainer.Cgroupfs)
if err != nil { ok(t, err)
t.Fatal(err)
}
container, err := factory.Create("test", config) container, err := factory.Create("test", config)
if err != nil { ok(t, err)
t.Fatal(err)
}
defer container.Destroy() defer container.Destroy()
processCaps := append(config.Capabilities, "NET_ADMIN") processCaps := append(config.Capabilities, "NET_ADMIN")
@ -437,17 +362,12 @@ func TestProcessCaps(t *testing.T) {
Stdout: &stdout, Stdout: &stdout,
} }
err = container.Start(&pconfig) err = container.Start(&pconfig)
if err != nil { ok(t, err)
t.Fatal(err)
}
// Wait for process // Wait for process
waitProcess(&pconfig, t) waitProcess(&pconfig, t)
outputStatus := string(stdout.Bytes()) outputStatus := string(stdout.Bytes())
if err != nil {
t.Fatal(err)
}
lines := strings.Split(outputStatus, "\n") lines := strings.Split(outputStatus, "\n")
@ -497,15 +417,11 @@ func testFreeze(t *testing.T, systemd bool) {
return return
} }
root, err := newTestRoot() root, err := newTestRoot()
if err != nil { ok(t, err)
t.Fatal(err)
}
defer os.RemoveAll(root) defer os.RemoveAll(root)
rootfs, err := newRootfs() rootfs, err := newRootfs()
if err != nil { ok(t, err)
t.Fatal(err)
}
defer remove(rootfs) defer remove(rootfs)
config := newTemplateConfig(rootfs) config := newTemplateConfig(rootfs)
@ -514,20 +430,14 @@ func testFreeze(t *testing.T, systemd bool) {
} }
factory, err := libcontainer.New(root, libcontainer.Cgroupfs) factory, err := libcontainer.New(root, libcontainer.Cgroupfs)
if err != nil { ok(t, err)
t.Fatal(err)
}
container, err := factory.Create("test", config) container, err := factory.Create("test", config)
if err != nil { ok(t, err)
t.Fatal(err)
}
defer container.Destroy() defer container.Destroy()
stdinR, stdinW, err := os.Pipe() stdinR, stdinW, err := os.Pipe()
if err != nil { ok(t, err)
t.Fatal(err)
}
pconfig := libcontainer.Process{ pconfig := libcontainer.Process{
Args: []string{"cat"}, Args: []string{"cat"},
@ -537,39 +447,28 @@ func testFreeze(t *testing.T, systemd bool) {
err = container.Start(&pconfig) err = container.Start(&pconfig)
stdinR.Close() stdinR.Close()
defer stdinW.Close() defer stdinW.Close()
if err != nil { ok(t, err)
t.Fatal(err)
}
pid, err := pconfig.Pid() pid, err := pconfig.Pid()
if err != nil { ok(t, err)
t.Fatal(err)
}
process, err := os.FindProcess(pid) process, err := os.FindProcess(pid)
if err != nil { ok(t, err)
t.Fatal(err)
}
if err := container.Pause(); err != nil { err = container.Pause()
t.Fatal(err) ok(t, err)
}
state, err := container.Status() state, err := container.Status()
if err != nil { ok(t, err)
t.Fatal(err) err = container.Resume()
} ok(t, err)
if err := container.Resume(); err != nil {
t.Fatal(err)
}
if state != libcontainer.Paused { if state != libcontainer.Paused {
t.Fatal("Unexpected state: ", state) t.Fatal("Unexpected state: ", state)
} }
stdinW.Close() stdinW.Close()
s, err := process.Wait() s, err := process.Wait()
if err != nil { ok(t, err)
t.Fatal(err)
}
if !s.Success() { if !s.Success() {
t.Fatal(s.String()) t.Fatal(s.String())
} }

View File

@ -16,22 +16,16 @@ func TestExecIn(t *testing.T) {
return return
} }
rootfs, err := newRootfs() rootfs, err := newRootfs()
if err != nil { ok(t, err)
t.Fatal(err)
}
defer remove(rootfs) defer remove(rootfs)
config := newTemplateConfig(rootfs) config := newTemplateConfig(rootfs)
container, err := newContainer(config) container, err := newContainer(config)
if err != nil { ok(t, err)
t.Fatal(err)
}
defer container.Destroy() defer container.Destroy()
// Execute a first process in the container // Execute a first process in the container
stdinR, stdinW, err := os.Pipe() stdinR, stdinW, err := os.Pipe()
if err != nil { ok(t, err)
t.Fatal(err)
}
process := &libcontainer.Process{ process := &libcontainer.Process{
Args: []string{"cat"}, Args: []string{"cat"},
Env: standardEnvironment, Env: standardEnvironment,
@ -40,9 +34,7 @@ func TestExecIn(t *testing.T) {
err = container.Start(process) err = container.Start(process)
stdinR.Close() stdinR.Close()
defer stdinW.Close() defer stdinW.Close()
if err != nil { ok(t, err)
t.Fatal(err)
}
buffers := newStdBuffers() buffers := newStdBuffers()
ps := &libcontainer.Process{ ps := &libcontainer.Process{
@ -53,12 +45,9 @@ func TestExecIn(t *testing.T) {
Stderr: buffers.Stderr, Stderr: buffers.Stderr,
} }
err = container.Start(ps) err = container.Start(ps)
if err != nil { ok(t, err)
t.Fatal(err) _, err = ps.Wait()
} ok(t, err)
if _, err := ps.Wait(); err != nil {
t.Fatal(err)
}
stdinW.Close() stdinW.Close()
if _, err := process.Wait(); err != nil { if _, err := process.Wait(); err != nil {
t.Log(err) t.Log(err)
@ -74,21 +63,15 @@ func TestExecInRlimit(t *testing.T) {
return return
} }
rootfs, err := newRootfs() rootfs, err := newRootfs()
if err != nil { ok(t, err)
t.Fatal(err)
}
defer remove(rootfs) defer remove(rootfs)
config := newTemplateConfig(rootfs) config := newTemplateConfig(rootfs)
container, err := newContainer(config) container, err := newContainer(config)
if err != nil { ok(t, err)
t.Fatal(err)
}
defer container.Destroy() defer container.Destroy()
stdinR, stdinW, err := os.Pipe() stdinR, stdinW, err := os.Pipe()
if err != nil { ok(t, err)
t.Fatal(err)
}
process := &libcontainer.Process{ process := &libcontainer.Process{
Args: []string{"cat"}, Args: []string{"cat"},
Env: standardEnvironment, Env: standardEnvironment,
@ -97,9 +80,7 @@ func TestExecInRlimit(t *testing.T) {
err = container.Start(process) err = container.Start(process)
stdinR.Close() stdinR.Close()
defer stdinW.Close() defer stdinW.Close()
if err != nil { ok(t, err)
t.Fatal(err)
}
buffers := newStdBuffers() buffers := newStdBuffers()
ps := &libcontainer.Process{ ps := &libcontainer.Process{
@ -110,12 +91,9 @@ func TestExecInRlimit(t *testing.T) {
Stderr: buffers.Stderr, Stderr: buffers.Stderr,
} }
err = container.Start(ps) err = container.Start(ps)
if err != nil { ok(t, err)
t.Fatal(err) _, err = ps.Wait()
} ok(t, err)
if _, err := ps.Wait(); err != nil {
t.Fatal(err)
}
stdinW.Close() stdinW.Close()
if _, err := process.Wait(); err != nil { if _, err := process.Wait(); err != nil {
t.Log(err) t.Log(err)
@ -131,22 +109,16 @@ func TestExecInError(t *testing.T) {
return return
} }
rootfs, err := newRootfs() rootfs, err := newRootfs()
if err != nil { ok(t, err)
t.Fatal(err)
}
defer remove(rootfs) defer remove(rootfs)
config := newTemplateConfig(rootfs) config := newTemplateConfig(rootfs)
container, err := newContainer(config) container, err := newContainer(config)
if err != nil { ok(t, err)
t.Fatal(err)
}
defer container.Destroy() defer container.Destroy()
// Execute a first process in the container // Execute a first process in the container
stdinR, stdinW, err := os.Pipe() stdinR, stdinW, err := os.Pipe()
if err != nil { ok(t, err)
t.Fatal(err)
}
process := &libcontainer.Process{ process := &libcontainer.Process{
Args: []string{"cat"}, Args: []string{"cat"},
Env: standardEnvironment, Env: standardEnvironment,
@ -160,9 +132,7 @@ func TestExecInError(t *testing.T) {
t.Log(err) t.Log(err)
} }
}() }()
if err != nil { ok(t, err)
t.Fatal(err)
}
unexistent := &libcontainer.Process{ unexistent := &libcontainer.Process{
Args: []string{"unexistent"}, Args: []string{"unexistent"},
@ -182,22 +152,16 @@ func TestExecInTTY(t *testing.T) {
return return
} }
rootfs, err := newRootfs() rootfs, err := newRootfs()
if err != nil { ok(t, err)
t.Fatal(err)
}
defer remove(rootfs) defer remove(rootfs)
config := newTemplateConfig(rootfs) config := newTemplateConfig(rootfs)
container, err := newContainer(config) container, err := newContainer(config)
if err != nil { ok(t, err)
t.Fatal(err)
}
defer container.Destroy() defer container.Destroy()
// Execute a first process in the container // Execute a first process in the container
stdinR, stdinW, err := os.Pipe() stdinR, stdinW, err := os.Pipe()
if err != nil { ok(t, err)
t.Fatal(err)
}
process := &libcontainer.Process{ process := &libcontainer.Process{
Args: []string{"cat"}, Args: []string{"cat"},
Env: standardEnvironment, Env: standardEnvironment,
@ -206,9 +170,7 @@ func TestExecInTTY(t *testing.T) {
err = container.Start(process) err = container.Start(process)
stdinR.Close() stdinR.Close()
defer stdinW.Close() defer stdinW.Close()
if err != nil { ok(t, err)
t.Fatal(err)
}
var stdout bytes.Buffer var stdout bytes.Buffer
ps := &libcontainer.Process{ ps := &libcontainer.Process{
@ -221,21 +183,16 @@ func TestExecInTTY(t *testing.T) {
io.Copy(&stdout, console) io.Copy(&stdout, console)
close(copy) close(copy)
}() }()
if err != nil { ok(t, err)
t.Fatal(err)
}
err = container.Start(ps) err = container.Start(ps)
if err != nil { ok(t, err)
t.Fatal(err)
}
select { select {
case <-time.After(5 * time.Second): case <-time.After(5 * time.Second):
t.Fatal("Waiting for copy timed out") t.Fatal("Waiting for copy timed out")
case <-copy: case <-copy:
} }
if _, err := ps.Wait(); err != nil { _, err = ps.Wait()
t.Fatal(err) ok(t, err)
}
stdinW.Close() stdinW.Close()
if _, err := process.Wait(); err != nil { if _, err := process.Wait(); err != nil {
t.Log(err) t.Log(err)
@ -251,22 +208,16 @@ func TestExecInEnvironment(t *testing.T) {
return return
} }
rootfs, err := newRootfs() rootfs, err := newRootfs()
if err != nil { ok(t, err)
t.Fatal(err)
}
defer remove(rootfs) defer remove(rootfs)
config := newTemplateConfig(rootfs) config := newTemplateConfig(rootfs)
container, err := newContainer(config) container, err := newContainer(config)
if err != nil { ok(t, err)
t.Fatal(err)
}
defer container.Destroy() defer container.Destroy()
// Execute a first process in the container // Execute a first process in the container
stdinR, stdinW, err := os.Pipe() stdinR, stdinW, err := os.Pipe()
if err != nil { ok(t, err)
t.Fatal(err)
}
process := &libcontainer.Process{ process := &libcontainer.Process{
Args: []string{"cat"}, Args: []string{"cat"},
Env: standardEnvironment, Env: standardEnvironment,
@ -275,9 +226,7 @@ func TestExecInEnvironment(t *testing.T) {
err = container.Start(process) err = container.Start(process)
stdinR.Close() stdinR.Close()
defer stdinW.Close() defer stdinW.Close()
if err != nil { ok(t, err)
t.Fatal(err)
}
buffers := newStdBuffers() buffers := newStdBuffers()
process2 := &libcontainer.Process{ process2 := &libcontainer.Process{
@ -293,9 +242,7 @@ func TestExecInEnvironment(t *testing.T) {
Stderr: buffers.Stderr, Stderr: buffers.Stderr,
} }
err = container.Start(process2) err = container.Start(process2)
if err != nil { ok(t, err)
t.Fatal(err)
}
if _, err := process2.Wait(); err != nil { if _, err := process2.Wait(); err != nil {
out := buffers.Stdout.String() out := buffers.Stdout.String()
t.Fatal(err, out) t.Fatal(err, out)

View File

@ -6,8 +6,11 @@ import (
"io/ioutil" "io/ioutil"
"os" "os"
"os/exec" "os/exec"
"path/filepath"
"runtime"
"strings" "strings"
"syscall" "syscall"
"testing"
"github.com/docker/libcontainer" "github.com/docker/libcontainer"
"github.com/docker/libcontainer/configs" "github.com/docker/libcontainer/configs"
@ -38,6 +41,14 @@ func (b *stdBuffers) String() string {
return strings.Join(s, "|") return strings.Join(s, "|")
} }
// ok fails the test if an err is not nil.
func ok(t testing.TB, err error) {
if err != nil {
_, file, line, _ := runtime.Caller(1)
t.Fatalf("%s:%d: unexpected error: %s\n\n", filepath.Base(file), line, err.Error())
}
}
// newRootfs creates a new tmp directory and copies the busybox root filesystem // newRootfs creates a new tmp directory and copies the busybox root filesystem
func newRootfs() (string, error) { func newRootfs() (string, error) {
dir, err := ioutil.TempDir("", "") dir, err := ioutil.TempDir("", "")