container: access test case by index in helper
Some checks failed
Test / Create distribution (push) Successful in 33s
Test / Sandbox (push) Successful in 1m59s
Test / Hakurei (push) Successful in 3m27s
Test / Sandbox (race detector) (push) Successful in 3m55s
Test / Planterette (push) Successful in 4m15s
Test / Hakurei (race detector) (push) Successful in 4m55s
Test / Flake checks (push) Failing after 36s
Some checks failed
Test / Create distribution (push) Successful in 33s
Test / Sandbox (push) Successful in 1m59s
Test / Hakurei (push) Successful in 3m27s
Test / Sandbox (race detector) (push) Successful in 3m55s
Test / Planterette (push) Successful in 4m15s
Test / Hakurei (race detector) (push) Successful in 4m55s
Test / Flake checks (push) Failing after 36s
This is more elegant and allows for much easier extension of the tests. Mountinfo is still serialised however due to libPaths nondeterminism. Signed-off-by: Ophestra <cat@gensokyo.uk>
This commit is contained in:
parent
3633d24ebc
commit
c959381ed2
@ -4,13 +4,17 @@ import (
|
|||||||
"bytes"
|
"bytes"
|
||||||
"context"
|
"context"
|
||||||
"encoding/gob"
|
"encoding/gob"
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
"log"
|
"log"
|
||||||
"os"
|
"os"
|
||||||
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
"syscall"
|
"syscall"
|
||||||
"testing"
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"hakurei.app/command"
|
||||||
"hakurei.app/container"
|
"hakurei.app/container"
|
||||||
"hakurei.app/container/seccomp"
|
"hakurei.app/container/seccomp"
|
||||||
"hakurei.app/container/vfs"
|
"hakurei.app/container/vfs"
|
||||||
@ -23,11 +27,60 @@ import (
|
|||||||
const (
|
const (
|
||||||
ignore = "\x00"
|
ignore = "\x00"
|
||||||
ignoreV = -1
|
ignoreV = -1
|
||||||
|
|
||||||
|
pathWantMnt = "/etc/hakurei/want-mnt"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestMain(m *testing.M) {
|
var containerTestCases = []struct {
|
||||||
container.TryArgv0(hlog.Output{}, hlog.Prepare, internal.InstallOutput)
|
name string
|
||||||
os.Exit(m.Run())
|
filter bool
|
||||||
|
session bool
|
||||||
|
net bool
|
||||||
|
ops *container.Ops
|
||||||
|
|
||||||
|
mnt []*vfs.MountInfoEntry
|
||||||
|
uid int
|
||||||
|
gid int
|
||||||
|
|
||||||
|
rules []seccomp.NativeRule
|
||||||
|
flags seccomp.ExportFlag
|
||||||
|
presets seccomp.FilterPreset
|
||||||
|
}{
|
||||||
|
{"minimal", true, false, false,
|
||||||
|
new(container.Ops), nil,
|
||||||
|
1000, 100, nil, 0, seccomp.PresetStrict},
|
||||||
|
{"allow", true, true, true,
|
||||||
|
new(container.Ops), nil,
|
||||||
|
1000, 100, nil, 0, seccomp.PresetExt | seccomp.PresetDenyDevel},
|
||||||
|
{"no filter", false, true, true,
|
||||||
|
new(container.Ops), nil,
|
||||||
|
1000, 100, nil, 0, seccomp.PresetExt},
|
||||||
|
{"custom rules", true, true, true,
|
||||||
|
new(container.Ops), nil,
|
||||||
|
1, 31, []seccomp.NativeRule{{seccomp.ScmpSyscall(syscall.SYS_SETUID), seccomp.ScmpErrno(syscall.EPERM), nil}}, 0, seccomp.PresetExt},
|
||||||
|
{"tmpfs", true, false, false,
|
||||||
|
new(container.Ops).
|
||||||
|
Tmpfs(hst.Tmp, 0, 0755),
|
||||||
|
[]*vfs.MountInfoEntry{
|
||||||
|
ent("/", hst.Tmp, "rw,nosuid,nodev,relatime", "tmpfs", "tmpfs", ignore),
|
||||||
|
},
|
||||||
|
9, 9, nil, 0, seccomp.PresetStrict},
|
||||||
|
{"dev", true, true /* go test output is not a tty */, false,
|
||||||
|
new(container.Ops).
|
||||||
|
Dev("/dev").
|
||||||
|
Mqueue("/dev/mqueue"),
|
||||||
|
[]*vfs.MountInfoEntry{
|
||||||
|
ent("/", "/dev", "rw,nosuid,nodev,relatime", "tmpfs", "devtmpfs", ignore),
|
||||||
|
ent("/null", "/dev/null", "rw,nosuid", "devtmpfs", "devtmpfs", ignore),
|
||||||
|
ent("/zero", "/dev/zero", "rw,nosuid", "devtmpfs", "devtmpfs", ignore),
|
||||||
|
ent("/full", "/dev/full", "rw,nosuid", "devtmpfs", "devtmpfs", ignore),
|
||||||
|
ent("/random", "/dev/random", "rw,nosuid", "devtmpfs", "devtmpfs", ignore),
|
||||||
|
ent("/urandom", "/dev/urandom", "rw,nosuid", "devtmpfs", "devtmpfs", ignore),
|
||||||
|
ent("/tty", "/dev/tty", "rw,nosuid", "devtmpfs", "devtmpfs", ignore),
|
||||||
|
ent("/", "/dev/pts", "rw,nosuid,noexec,relatime", "devpts", "devpts", "rw,mode=620,ptmxmode=666"),
|
||||||
|
ent("/", "/dev/mqueue", "rw,nosuid,nodev,noexec,relatime", "mqueue", "mqueue", "rw"),
|
||||||
|
},
|
||||||
|
1971, 100, nil, 0, seccomp.PresetStrict},
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestContainer(t *testing.T) {
|
func TestContainer(t *testing.T) {
|
||||||
@ -39,89 +92,30 @@ func TestContainer(t *testing.T) {
|
|||||||
t.Cleanup(func() { container.SetOutput(oldOutput) })
|
t.Cleanup(func() { container.SetOutput(oldOutput) })
|
||||||
}
|
}
|
||||||
|
|
||||||
testCases := []struct {
|
for i, tc := range containerTestCases {
|
||||||
name string
|
|
||||||
filter bool
|
|
||||||
session bool
|
|
||||||
net bool
|
|
||||||
ops *container.Ops
|
|
||||||
mnt []*vfs.MountInfoEntry
|
|
||||||
host string
|
|
||||||
rules []seccomp.NativeRule
|
|
||||||
flags seccomp.ExportFlag
|
|
||||||
presets seccomp.FilterPreset
|
|
||||||
}{
|
|
||||||
{"minimal", true, false, false,
|
|
||||||
new(container.Ops), nil, "test-minimal",
|
|
||||||
nil, 0, seccomp.PresetStrict},
|
|
||||||
{"allow", true, true, true,
|
|
||||||
new(container.Ops), nil, "test-minimal",
|
|
||||||
nil, 0, seccomp.PresetExt | seccomp.PresetDenyDevel},
|
|
||||||
{"no filter", false, true, true,
|
|
||||||
new(container.Ops), nil, "test-no-filter",
|
|
||||||
nil, 0, seccomp.PresetExt},
|
|
||||||
{"custom rules", true, true, true,
|
|
||||||
new(container.Ops), nil, "test-no-filter",
|
|
||||||
[]seccomp.NativeRule{
|
|
||||||
{seccomp.ScmpSyscall(syscall.SYS_SETUID), seccomp.ScmpErrno(syscall.EPERM), nil},
|
|
||||||
}, 0, seccomp.PresetExt},
|
|
||||||
{"tmpfs", true, false, false,
|
|
||||||
new(container.Ops).
|
|
||||||
Tmpfs(hst.Tmp, 0, 0755),
|
|
||||||
[]*vfs.MountInfoEntry{
|
|
||||||
e("/", hst.Tmp, "rw,nosuid,nodev,relatime", "tmpfs", "tmpfs", ignore),
|
|
||||||
}, "test-tmpfs",
|
|
||||||
nil, 0, seccomp.PresetStrict},
|
|
||||||
{"dev", true, true /* go test output is not a tty */, false,
|
|
||||||
new(container.Ops).
|
|
||||||
Dev("/dev").
|
|
||||||
Mqueue("/dev/mqueue"),
|
|
||||||
[]*vfs.MountInfoEntry{
|
|
||||||
e("/", "/dev", "rw,nosuid,nodev,relatime", "tmpfs", "devtmpfs", ignore),
|
|
||||||
e("/null", "/dev/null", "rw,nosuid", "devtmpfs", "devtmpfs", ignore),
|
|
||||||
e("/zero", "/dev/zero", "rw,nosuid", "devtmpfs", "devtmpfs", ignore),
|
|
||||||
e("/full", "/dev/full", "rw,nosuid", "devtmpfs", "devtmpfs", ignore),
|
|
||||||
e("/random", "/dev/random", "rw,nosuid", "devtmpfs", "devtmpfs", ignore),
|
|
||||||
e("/urandom", "/dev/urandom", "rw,nosuid", "devtmpfs", "devtmpfs", ignore),
|
|
||||||
e("/tty", "/dev/tty", "rw,nosuid", "devtmpfs", "devtmpfs", ignore),
|
|
||||||
e("/", "/dev/pts", "rw,nosuid,noexec,relatime", "devpts", "devpts", "rw,mode=620,ptmxmode=666"),
|
|
||||||
e("/", "/dev/mqueue", "rw,nosuid,nodev,noexec,relatime", "mqueue", "mqueue", "rw"),
|
|
||||||
}, "",
|
|
||||||
nil, 0, seccomp.PresetStrict},
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, tc := range testCases {
|
|
||||||
t.Run(tc.name, func(t *testing.T) {
|
t.Run(tc.name, func(t *testing.T) {
|
||||||
ctx, cancel := context.WithTimeout(t.Context(), 5*time.Second)
|
ctx, cancel := context.WithTimeout(t.Context(), 5*time.Second)
|
||||||
defer cancel()
|
defer cancel()
|
||||||
|
|
||||||
c := container.New(ctx, "/usr/bin/sandbox.test", "-test.v",
|
hostname := hostnameFromTestCase(tc.name)
|
||||||
"-test.run=TestHelperCheckContainer", "--", "check", tc.host)
|
c := container.New(ctx, os.Args[0], "container", strconv.Itoa(i))
|
||||||
c.Uid = 1000
|
prepareHelper(c)
|
||||||
c.Gid = 100
|
c.Uid = tc.uid
|
||||||
c.Hostname = tc.host
|
c.Gid = tc.gid
|
||||||
|
c.Hostname = hostname
|
||||||
c.Stdout, c.Stderr = os.Stdout, os.Stderr
|
c.Stdout, c.Stderr = os.Stdout, os.Stderr
|
||||||
c.Ops = tc.ops
|
*c.Ops = append(*c.Ops, *tc.ops...)
|
||||||
c.SeccompRules = tc.rules
|
c.SeccompRules = tc.rules
|
||||||
c.SeccompFlags = tc.flags | seccomp.AllowMultiarch
|
c.SeccompFlags = tc.flags | seccomp.AllowMultiarch
|
||||||
c.SeccompPresets = tc.presets
|
c.SeccompPresets = tc.presets
|
||||||
c.SeccompDisable = !tc.filter
|
c.SeccompDisable = !tc.filter
|
||||||
c.RetainSession = tc.session
|
c.RetainSession = tc.session
|
||||||
c.HostNet = tc.net
|
c.HostNet = tc.net
|
||||||
if c.Args[5] == "" {
|
|
||||||
if name, err := os.Hostname(); err != nil {
|
|
||||||
t.Fatalf("cannot get hostname: %v", err)
|
|
||||||
} else {
|
|
||||||
c.Args[5] = name
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
c.
|
c.
|
||||||
Tmpfs("/tmp", 0, 0755).
|
Tmpfs("/tmp", 0, 0755).
|
||||||
Bind(os.Args[0], os.Args[0], 0).
|
Bind(os.Args[0], os.Args[0], 0).
|
||||||
Mkdir("/usr/bin", 0755).
|
Place("/etc/hostname", []byte(hostname))
|
||||||
Link(os.Args[0], "/usr/bin/sandbox.test").
|
|
||||||
Place("/etc/hostname", []byte(c.Args[5]))
|
|
||||||
// in case test has cgo enabled
|
// in case test has cgo enabled
|
||||||
var libPaths []string
|
var libPaths []string
|
||||||
if entries, err := ldd.Exec(ctx, os.Args[0]); err != nil {
|
if entries, err := ldd.Exec(ctx, os.Args[0]); err != nil {
|
||||||
@ -135,23 +129,33 @@ func TestContainer(t *testing.T) {
|
|||||||
// needs /proc to check mountinfo
|
// needs /proc to check mountinfo
|
||||||
c.Proc("/proc")
|
c.Proc("/proc")
|
||||||
|
|
||||||
|
// mountinfo cannot be resolved directly by helper due to libPaths nondeterminism
|
||||||
mnt := make([]*vfs.MountInfoEntry, 0, 3+len(libPaths))
|
mnt := make([]*vfs.MountInfoEntry, 0, 3+len(libPaths))
|
||||||
mnt = append(mnt, e("/sysroot", "/", "rw,nosuid,nodev,relatime", "tmpfs", "rootfs", ignore))
|
mnt = append(mnt, ent("/sysroot", "/", "rw,nosuid,nodev,relatime", "tmpfs", "rootfs", ignore))
|
||||||
mnt = append(mnt, tc.mnt...)
|
mnt = append(mnt, tc.mnt...)
|
||||||
mnt = append(mnt,
|
mnt = append(mnt,
|
||||||
e("/", "/tmp", "rw,nosuid,nodev,relatime", "tmpfs", "tmpfs", ignore),
|
// Tmpfs("/tmp", 0, 0755)
|
||||||
e(ignore, os.Args[0], "ro,nosuid,nodev,relatime", ignore, ignore, ignore),
|
ent("/", "/tmp", "rw,nosuid,nodev,relatime", "tmpfs", "tmpfs", ignore),
|
||||||
e(ignore, "/etc/hostname", "ro,nosuid,nodev,relatime", "tmpfs", "rootfs", ignore),
|
// Bind(os.Args[0], os.Args[0], 0)
|
||||||
|
ent(ignore, os.Args[0], "ro,nosuid,nodev,relatime", ignore, ignore, ignore),
|
||||||
|
// Place("/etc/hostname", []byte(hostname))
|
||||||
|
ent(ignore, "/etc/hostname", "ro,nosuid,nodev,relatime", "tmpfs", "rootfs", ignore),
|
||||||
)
|
)
|
||||||
for _, name := range libPaths {
|
for _, name := range libPaths {
|
||||||
mnt = append(mnt, e(ignore, name, "ro,nosuid,nodev,relatime", ignore, ignore, ignore))
|
// Bind(name, name, 0)
|
||||||
|
mnt = append(mnt, ent(ignore, name, "ro,nosuid,nodev,relatime", ignore, ignore, ignore))
|
||||||
}
|
}
|
||||||
mnt = append(mnt, e("/", "/proc", "rw,nosuid,nodev,noexec,relatime", "proc", "proc", "rw"))
|
mnt = append(mnt,
|
||||||
|
// Proc("/proc")
|
||||||
|
ent("/", "/proc", "rw,nosuid,nodev,noexec,relatime", "proc", "proc", "rw"),
|
||||||
|
// Place(pathWantMnt, want.Bytes())
|
||||||
|
ent(ignore, pathWantMnt, "ro,nosuid,nodev,relatime", "tmpfs", "rootfs", ignore),
|
||||||
|
)
|
||||||
want := new(bytes.Buffer)
|
want := new(bytes.Buffer)
|
||||||
if err := gob.NewEncoder(want).Encode(mnt); err != nil {
|
if err := gob.NewEncoder(want).Encode(mnt); err != nil {
|
||||||
t.Fatalf("cannot serialise expected mount points: %v", err)
|
t.Fatalf("cannot serialise expected mount points: %v", err)
|
||||||
}
|
}
|
||||||
c.Stdin = want
|
c.Place(pathWantMnt, want.Bytes())
|
||||||
|
|
||||||
if err := c.Start(); err != nil {
|
if err := c.Start(); err != nil {
|
||||||
hlog.PrintBaseError(err, "start:")
|
hlog.PrintBaseError(err, "start:")
|
||||||
@ -168,7 +172,7 @@ func TestContainer(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func e(root, target, vfsOptstr, fsType, source, fsOptstr string) *vfs.MountInfoEntry {
|
func ent(root, target, vfsOptstr, fsType, source, fsOptstr string) *vfs.MountInfoEntry {
|
||||||
return &vfs.MountInfoEntry{
|
return &vfs.MountInfoEntry{
|
||||||
ID: ignoreV,
|
ID: ignoreV,
|
||||||
Parent: ignoreV,
|
Parent: ignoreV,
|
||||||
@ -183,6 +187,10 @@ func e(root, target, vfsOptstr, fsType, source, fsOptstr string) *vfs.MountInfoE
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func hostnameFromTestCase(name string) string {
|
||||||
|
return "test-" + strings.Join(strings.Fields(name), "-")
|
||||||
|
}
|
||||||
|
|
||||||
func TestContainerString(t *testing.T) {
|
func TestContainerString(t *testing.T) {
|
||||||
c := container.New(t.Context(), "ldd", "/usr/bin/env")
|
c := container.New(t.Context(), "ldd", "/usr/bin/env")
|
||||||
c.SeccompFlags |= seccomp.AllowMultiarch
|
c.SeccompFlags |= seccomp.AllowMultiarch
|
||||||
@ -196,72 +204,93 @@ func TestContainerString(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestHelperCheckContainer(t *testing.T) {
|
func init() {
|
||||||
if len(os.Args) != 6 || os.Args[4] != "check" {
|
helperCommands = append(helperCommands, func(c command.Command) {
|
||||||
return
|
c.Command("container", command.UsageInternal, func(args []string) error {
|
||||||
}
|
if len(args) != 1 {
|
||||||
|
return syscall.EINVAL
|
||||||
t.Run("user", func(t *testing.T) {
|
|
||||||
if uid := syscall.Getuid(); uid != 1000 {
|
|
||||||
t.Errorf("Getuid: %d, want 1000", uid)
|
|
||||||
}
|
|
||||||
if gid := syscall.Getgid(); gid != 100 {
|
|
||||||
t.Errorf("Getgid: %d, want 100", gid)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
t.Run("hostname", func(t *testing.T) {
|
|
||||||
if name, err := os.Hostname(); err != nil {
|
|
||||||
t.Fatalf("cannot get hostname: %v", err)
|
|
||||||
} else if name != os.Args[5] {
|
|
||||||
t.Errorf("Hostname: %q, want %q", name, os.Args[5])
|
|
||||||
}
|
|
||||||
|
|
||||||
if p, err := os.ReadFile("/etc/hostname"); err != nil {
|
|
||||||
t.Fatalf("%v", err)
|
|
||||||
} else if string(p) != os.Args[5] {
|
|
||||||
t.Errorf("/etc/hostname: %q, want %q", string(p), os.Args[5])
|
|
||||||
}
|
|
||||||
})
|
|
||||||
t.Run("mount", func(t *testing.T) {
|
|
||||||
var mnt []*vfs.MountInfoEntry
|
|
||||||
if err := gob.NewDecoder(os.Stdin).Decode(&mnt); err != nil {
|
|
||||||
t.Fatalf("cannot receive expected mount points: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
var d *vfs.MountInfoDecoder
|
|
||||||
if f, err := os.Open("/proc/self/mountinfo"); err != nil {
|
|
||||||
t.Fatalf("cannot open mountinfo: %v", err)
|
|
||||||
} else {
|
|
||||||
d = vfs.NewMountInfoDecoder(f)
|
|
||||||
}
|
|
||||||
|
|
||||||
i := 0
|
|
||||||
for cur := range d.Entries() {
|
|
||||||
if i == len(mnt) {
|
|
||||||
t.Errorf("got more than %d entries", len(mnt))
|
|
||||||
break
|
|
||||||
}
|
}
|
||||||
|
tc := containerTestCases[0]
|
||||||
// ugly hack but should be reliable and is less likely to false negative than comparing by parsed flags
|
if i, err := strconv.Atoi(args[0]); err != nil {
|
||||||
cur.VfsOptstr = strings.TrimSuffix(cur.VfsOptstr, ",relatime")
|
return fmt.Errorf("cannot parse test case index: %v", err)
|
||||||
cur.VfsOptstr = strings.TrimSuffix(cur.VfsOptstr, ",noatime")
|
|
||||||
mnt[i].VfsOptstr = strings.TrimSuffix(mnt[i].VfsOptstr, ",relatime")
|
|
||||||
mnt[i].VfsOptstr = strings.TrimSuffix(mnt[i].VfsOptstr, ",noatime")
|
|
||||||
|
|
||||||
if !cur.EqualWithIgnore(mnt[i], "\x00") {
|
|
||||||
t.Errorf("[FAIL] %s", cur)
|
|
||||||
} else {
|
} else {
|
||||||
t.Logf("[ OK ] %s", cur)
|
tc = containerTestCases[i]
|
||||||
}
|
}
|
||||||
|
|
||||||
i++
|
if uid := syscall.Getuid(); uid != tc.uid {
|
||||||
}
|
return fmt.Errorf("uid: %d, want %d", uid, tc.uid)
|
||||||
if err := d.Err(); err != nil {
|
}
|
||||||
t.Errorf("cannot parse mountinfo: %v", err)
|
if gid := syscall.Getgid(); gid != tc.gid {
|
||||||
}
|
return fmt.Errorf("gid: %d, want %d", gid, tc.gid)
|
||||||
|
}
|
||||||
|
|
||||||
if i != len(mnt) {
|
wantHost := hostnameFromTestCase(tc.name)
|
||||||
t.Errorf("got %d entries, want %d", i, len(mnt))
|
if host, err := os.Hostname(); err != nil {
|
||||||
}
|
return fmt.Errorf("cannot get hostname: %v", err)
|
||||||
|
} else if host != wantHost {
|
||||||
|
return fmt.Errorf("hostname: %q, want %q", host, wantHost)
|
||||||
|
}
|
||||||
|
|
||||||
|
if p, err := os.ReadFile("/etc/hostname"); err != nil {
|
||||||
|
return fmt.Errorf("cannot read /etc/hostname: %v", err)
|
||||||
|
} else if string(p) != wantHost {
|
||||||
|
return fmt.Errorf("/etc/hostname: %q, want %q", string(p), wantHost)
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
var fail bool
|
||||||
|
|
||||||
|
var mnt []*vfs.MountInfoEntry
|
||||||
|
if f, err := os.Open(pathWantMnt); err != nil {
|
||||||
|
return fmt.Errorf("cannot open expected mount points: %v", err)
|
||||||
|
} else if err = gob.NewDecoder(f).Decode(&mnt); err != nil {
|
||||||
|
return fmt.Errorf("cannot parse expected mount points: %v", err)
|
||||||
|
} else if err = f.Close(); err != nil {
|
||||||
|
return fmt.Errorf("cannot close expected mount points: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
var d *vfs.MountInfoDecoder
|
||||||
|
if f, err := os.Open("/proc/self/mountinfo"); err != nil {
|
||||||
|
return fmt.Errorf("cannot open mountinfo: %v", err)
|
||||||
|
} else {
|
||||||
|
d = vfs.NewMountInfoDecoder(f)
|
||||||
|
}
|
||||||
|
|
||||||
|
i := 0
|
||||||
|
for cur := range d.Entries() {
|
||||||
|
if i == len(mnt) {
|
||||||
|
return fmt.Errorf("got more than %d entries", len(mnt))
|
||||||
|
}
|
||||||
|
|
||||||
|
// ugly hack but should be reliable and is less likely to false negative than comparing by parsed flags
|
||||||
|
cur.VfsOptstr = strings.TrimSuffix(cur.VfsOptstr, ",relatime")
|
||||||
|
cur.VfsOptstr = strings.TrimSuffix(cur.VfsOptstr, ",noatime")
|
||||||
|
mnt[i].VfsOptstr = strings.TrimSuffix(mnt[i].VfsOptstr, ",relatime")
|
||||||
|
mnt[i].VfsOptstr = strings.TrimSuffix(mnt[i].VfsOptstr, ",noatime")
|
||||||
|
|
||||||
|
if !cur.EqualWithIgnore(mnt[i], "\x00") {
|
||||||
|
fail = true
|
||||||
|
log.Printf("[FAIL] %s", cur)
|
||||||
|
} else {
|
||||||
|
log.Printf("[ OK ] %s", cur)
|
||||||
|
}
|
||||||
|
|
||||||
|
i++
|
||||||
|
}
|
||||||
|
if err := d.Err(); err != nil {
|
||||||
|
return fmt.Errorf("cannot parse mountinfo: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if i != len(mnt) {
|
||||||
|
return fmt.Errorf("got %d entries, want %d", i, len(mnt))
|
||||||
|
}
|
||||||
|
|
||||||
|
if fail {
|
||||||
|
return errors.New("one or more mountinfo entries do not match")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
})
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@ -270,6 +270,7 @@ func Init(prepare func(prefix string), setVerbose func(verbose bool)) {
|
|||||||
cmd.ExtraFiles = extraFiles
|
cmd.ExtraFiles = extraFiles
|
||||||
cmd.Dir = params.Dir
|
cmd.Dir = params.Dir
|
||||||
|
|
||||||
|
msg.Verbosef("starting initial program %s", params.Path)
|
||||||
if err := cmd.Start(); err != nil {
|
if err := cmd.Start(); err != nil {
|
||||||
log.Fatalf("%v", err)
|
log.Fatalf("%v", err)
|
||||||
}
|
}
|
||||||
|
43
container/init_test.go
Normal file
43
container/init_test.go
Normal file
@ -0,0 +1,43 @@
|
|||||||
|
package container_test
|
||||||
|
|
||||||
|
import (
|
||||||
|
"log"
|
||||||
|
"os"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"hakurei.app/command"
|
||||||
|
"hakurei.app/container"
|
||||||
|
"hakurei.app/internal"
|
||||||
|
"hakurei.app/internal/hlog"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
envDoCheck = "HAKUREI_TEST_DO_CHECK"
|
||||||
|
)
|
||||||
|
|
||||||
|
var helperCommands []func(c command.Command)
|
||||||
|
|
||||||
|
func TestMain(m *testing.M) {
|
||||||
|
container.TryArgv0(hlog.Output{}, hlog.Prepare, internal.InstallOutput)
|
||||||
|
|
||||||
|
if os.Getenv(envDoCheck) == "1" {
|
||||||
|
c := command.New(os.Stderr, log.Printf, "helper", func(args []string) error {
|
||||||
|
log.SetFlags(0)
|
||||||
|
log.SetPrefix("helper: ")
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
for _, f := range helperCommands {
|
||||||
|
f(c)
|
||||||
|
}
|
||||||
|
c.MustParse(os.Args[1:], func(err error) {
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal(err.Error())
|
||||||
|
}
|
||||||
|
})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
os.Exit(m.Run())
|
||||||
|
}
|
||||||
|
|
||||||
|
func prepareHelper(c *container.Container) { c.Env = append(c.Env, envDoCheck+"=1") }
|
Loading…
x
Reference in New Issue
Block a user