container: check cancel signal delivery
All checks were successful
Test / Create distribution (push) Successful in 32s
Test / Sandbox (push) Successful in 1m55s
Test / Hakurei (push) Successful in 2m50s
Test / Sandbox (race detector) (push) Successful in 3m46s
Test / Planterette (push) Successful in 3m52s
Test / Hakurei (race detector) (push) Successful in 4m28s
Test / Flake checks (push) Successful in 1m18s
All checks were successful
Test / Create distribution (push) Successful in 32s
Test / Sandbox (push) Successful in 1m55s
Test / Hakurei (push) Successful in 2m50s
Test / Sandbox (race detector) (push) Successful in 3m46s
Test / Planterette (push) Successful in 3m52s
Test / Hakurei (race detector) (push) Successful in 4m28s
Test / Flake checks (push) Successful in 1m18s
This change also makes some parts of the test more robust. Signed-off-by: Ophestra <cat@gensokyo.uk>
This commit is contained in:
parent
a1e5f020f4
commit
65fe09caf9
@ -71,6 +71,7 @@ type (
|
|||||||
Hostname string
|
Hostname string
|
||||||
// Sequential container setup ops.
|
// Sequential container setup ops.
|
||||||
*Ops
|
*Ops
|
||||||
|
|
||||||
// Seccomp system call filter rules.
|
// Seccomp system call filter rules.
|
||||||
SeccompRules []seccomp.NativeRule
|
SeccompRules []seccomp.NativeRule
|
||||||
// Extra seccomp flags.
|
// Extra seccomp flags.
|
||||||
@ -79,6 +80,7 @@ type (
|
|||||||
SeccompPresets seccomp.FilterPreset
|
SeccompPresets seccomp.FilterPreset
|
||||||
// Do not load seccomp program.
|
// Do not load seccomp program.
|
||||||
SeccompDisable bool
|
SeccompDisable bool
|
||||||
|
|
||||||
// Permission bits of newly created parent directories.
|
// Permission bits of newly created parent directories.
|
||||||
// The zero value is interpreted as 0755.
|
// The zero value is interpreted as 0755.
|
||||||
ParentPerm os.FileMode
|
ParentPerm os.FileMode
|
||||||
@ -121,7 +123,7 @@ func (p *Container) Start() error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
p.cmd = exec.CommandContext(ctx, MustExecutable())
|
p.cmd = exec.CommandContext(ctx, MustExecutable())
|
||||||
p.cmd.Args = []string{"init"}
|
p.cmd.Args = []string{initName}
|
||||||
p.cmd.Stdin, p.cmd.Stdout, p.cmd.Stderr = p.Stdin, p.Stdout, p.Stderr
|
p.cmd.Stdin, p.cmd.Stdout, p.cmd.Stderr = p.Stdin, p.Stdout, p.Stderr
|
||||||
p.cmd.WaitDelay = p.WaitDelay
|
p.cmd.WaitDelay = p.WaitDelay
|
||||||
if p.Cancel != nil {
|
if p.Cancel != nil {
|
||||||
|
@ -12,7 +12,6 @@ import (
|
|||||||
"strings"
|
"strings"
|
||||||
"syscall"
|
"syscall"
|
||||||
"testing"
|
"testing"
|
||||||
"time"
|
|
||||||
|
|
||||||
"hakurei.app/command"
|
"hakurei.app/command"
|
||||||
"hakurei.app/container"
|
"hakurei.app/container"
|
||||||
@ -21,7 +20,6 @@ import (
|
|||||||
"hakurei.app/hst"
|
"hakurei.app/hst"
|
||||||
"hakurei.app/internal"
|
"hakurei.app/internal"
|
||||||
"hakurei.app/internal/hlog"
|
"hakurei.app/internal/hlog"
|
||||||
"hakurei.app/ldd"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
@ -92,18 +90,54 @@ func TestContainer(t *testing.T) {
|
|||||||
t.Cleanup(func() { container.SetOutput(oldOutput) })
|
t.Cleanup(func() { container.SetOutput(oldOutput) })
|
||||||
}
|
}
|
||||||
|
|
||||||
|
t.Run("cancel", func(t *testing.T) {
|
||||||
|
ctx, cancel := context.WithTimeout(t.Context(), helperDefaultTimeout)
|
||||||
|
|
||||||
|
c := helperNewContainer(ctx, "block")
|
||||||
|
c.Stdout, c.Stderr = os.Stdout, os.Stderr
|
||||||
|
c.WaitDelay = helperDefaultTimeout
|
||||||
|
|
||||||
|
ready := make(chan struct{})
|
||||||
|
if r, w, err := os.Pipe(); err != nil {
|
||||||
|
t.Fatalf("cannot pipe: %v", err)
|
||||||
|
} else {
|
||||||
|
c.ExtraFiles = append(c.ExtraFiles, w)
|
||||||
|
go func() {
|
||||||
|
defer close(ready)
|
||||||
|
if _, err = r.Read(make([]byte, 1)); err != nil {
|
||||||
|
panic(err.Error())
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := c.Start(); err != nil {
|
||||||
|
hlog.PrintBaseError(err, "start:")
|
||||||
|
t.Fatalf("cannot start container: %v", err)
|
||||||
|
} else if err = c.Serve(); err != nil {
|
||||||
|
hlog.PrintBaseError(err, "serve:")
|
||||||
|
t.Errorf("cannot serve setup params: %v", err)
|
||||||
|
}
|
||||||
|
<-ready
|
||||||
|
cancel()
|
||||||
|
wantErr := context.Canceled
|
||||||
|
if err := c.Wait(); !errors.Is(err, wantErr) {
|
||||||
|
hlog.PrintBaseError(err, "wait:")
|
||||||
|
t.Fatalf("Wait: error = %v, want %v", err, wantErr)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
for i, tc := range containerTestCases {
|
for i, tc := range containerTestCases {
|
||||||
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(), helperDefaultTimeout)
|
||||||
defer cancel()
|
defer cancel()
|
||||||
|
|
||||||
hostname := hostnameFromTestCase(tc.name)
|
var libPaths []string
|
||||||
c := container.New(ctx, os.Args[0], "container", strconv.Itoa(i))
|
c := helperNewContainerLibPaths(ctx, &libPaths, "container", strconv.Itoa(i))
|
||||||
prepareHelper(c)
|
|
||||||
c.Uid = tc.uid
|
c.Uid = tc.uid
|
||||||
c.Gid = tc.gid
|
c.Gid = tc.gid
|
||||||
c.Hostname = hostname
|
c.Hostname = hostnameFromTestCase(tc.name)
|
||||||
c.Stdout, c.Stderr = os.Stdout, os.Stderr
|
c.Stdout, c.Stderr = os.Stdout, os.Stderr
|
||||||
|
c.WaitDelay = helperDefaultTimeout
|
||||||
*c.Ops = append(*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
|
||||||
@ -114,38 +148,27 @@ func TestContainer(t *testing.T) {
|
|||||||
|
|
||||||
c.
|
c.
|
||||||
Tmpfs("/tmp", 0, 0755).
|
Tmpfs("/tmp", 0, 0755).
|
||||||
Bind(os.Args[0], os.Args[0], 0).
|
Place("/etc/hostname", []byte(c.Hostname))
|
||||||
Place("/etc/hostname", []byte(hostname))
|
|
||||||
// in case test has cgo enabled
|
|
||||||
var libPaths []string
|
|
||||||
if entries, err := ldd.Exec(ctx, os.Args[0]); err != nil {
|
|
||||||
log.Fatalf("ldd: %v", err)
|
|
||||||
} else {
|
|
||||||
libPaths = ldd.Path(entries)
|
|
||||||
}
|
|
||||||
for _, name := range libPaths {
|
|
||||||
c.Bind(name, name, 0)
|
|
||||||
}
|
|
||||||
// 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
|
// 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, ent("/sysroot", "/", "rw,nosuid,nodev,relatime", "tmpfs", "rootfs", ignore))
|
|
||||||
mnt = append(mnt, tc.mnt...)
|
|
||||||
mnt = append(mnt,
|
mnt = append(mnt,
|
||||||
// Tmpfs("/tmp", 0, 0755)
|
ent("/sysroot", "/", "rw,nosuid,nodev,relatime", "tmpfs", "rootfs", ignore),
|
||||||
ent("/", "/tmp", "rw,nosuid,nodev,relatime", "tmpfs", "tmpfs", ignore),
|
// Bind(os.Args[0], helperInnerPath, 0)
|
||||||
// Bind(os.Args[0], os.Args[0], 0)
|
ent(ignore, helperInnerPath, "ro,nosuid,nodev,relatime", ignore, ignore, ignore),
|
||||||
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 {
|
||||||
// Bind(name, name, 0)
|
// Bind(name, name, 0)
|
||||||
mnt = append(mnt, ent(ignore, name, "ro,nosuid,nodev,relatime", ignore, ignore, ignore))
|
mnt = append(mnt, ent(ignore, name, "ro,nosuid,nodev,relatime", ignore, ignore, ignore))
|
||||||
}
|
}
|
||||||
|
mnt = append(mnt, tc.mnt...)
|
||||||
mnt = append(mnt,
|
mnt = append(mnt,
|
||||||
|
// Tmpfs("/tmp", 0, 0755)
|
||||||
|
ent("/", "/tmp", "rw,nosuid,nodev,relatime", "tmpfs", "tmpfs", ignore),
|
||||||
|
// Place("/etc/hostname", []byte(hostname))
|
||||||
|
ent(ignore, "/etc/hostname", "ro,nosuid,nodev,relatime", "tmpfs", "rootfs", ignore),
|
||||||
// Proc("/proc")
|
// Proc("/proc")
|
||||||
ent("/", "/proc", "rw,nosuid,nodev,noexec,relatime", "proc", "proc", "rw"),
|
ent("/", "/proc", "rw,nosuid,nodev,noexec,relatime", "proc", "proc", "rw"),
|
||||||
// Place(pathWantMnt, want.Bytes())
|
// Place(pathWantMnt, want.Bytes())
|
||||||
@ -206,6 +229,13 @@ func TestContainerString(t *testing.T) {
|
|||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
helperCommands = append(helperCommands, func(c command.Command) {
|
helperCommands = append(helperCommands, func(c command.Command) {
|
||||||
|
c.Command("block", command.UsageInternal, func(args []string) error {
|
||||||
|
if _, err := os.NewFile(3, "sync").Write([]byte{0}); err != nil {
|
||||||
|
return fmt.Errorf("write to sync pipe: %v", err)
|
||||||
|
}
|
||||||
|
select {}
|
||||||
|
})
|
||||||
|
|
||||||
c.Command("container", command.UsageInternal, func(args []string) error {
|
c.Command("container", command.UsageInternal, func(args []string) error {
|
||||||
if len(args) != 1 {
|
if len(args) != 1 {
|
||||||
return syscall.EINVAL
|
return syscall.EINVAL
|
||||||
|
@ -367,9 +367,11 @@ func Init(prepare func(prefix string), setVerbose func(verbose bool)) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const initName = "init"
|
||||||
|
|
||||||
// TryArgv0 calls [Init] if the last element of argv0 is "init".
|
// TryArgv0 calls [Init] if the last element of argv0 is "init".
|
||||||
func TryArgv0(v Msg, prepare func(prefix string), setVerbose func(verbose bool)) {
|
func TryArgv0(v Msg, prepare func(prefix string), setVerbose func(verbose bool)) {
|
||||||
if len(os.Args) > 0 && path.Base(os.Args[0]) == "init" {
|
if len(os.Args) > 0 && path.Base(os.Args[0]) == initName {
|
||||||
msg = v
|
msg = v
|
||||||
Init(prepare, setVerbose)
|
Init(prepare, setVerbose)
|
||||||
msg.BeforeExit()
|
msg.BeforeExit()
|
||||||
|
@ -1,18 +1,24 @@
|
|||||||
package container_test
|
package container_test
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"context"
|
||||||
"log"
|
"log"
|
||||||
"os"
|
"os"
|
||||||
"testing"
|
"testing"
|
||||||
|
"time"
|
||||||
|
|
||||||
"hakurei.app/command"
|
"hakurei.app/command"
|
||||||
"hakurei.app/container"
|
"hakurei.app/container"
|
||||||
"hakurei.app/internal"
|
"hakurei.app/internal"
|
||||||
"hakurei.app/internal/hlog"
|
"hakurei.app/internal/hlog"
|
||||||
|
"hakurei.app/ldd"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
envDoCheck = "HAKUREI_TEST_DO_CHECK"
|
envDoCheck = "HAKUREI_TEST_DO_CHECK"
|
||||||
|
|
||||||
|
helperDefaultTimeout = 5 * time.Second
|
||||||
|
helperInnerPath = "/usr/bin/helper"
|
||||||
)
|
)
|
||||||
|
|
||||||
var helperCommands []func(c command.Command)
|
var helperCommands []func(c command.Command)
|
||||||
@ -40,4 +46,24 @@ func TestMain(m *testing.M) {
|
|||||||
os.Exit(m.Run())
|
os.Exit(m.Run())
|
||||||
}
|
}
|
||||||
|
|
||||||
func prepareHelper(c *container.Container) { c.Env = append(c.Env, envDoCheck+"=1") }
|
func helperNewContainerLibPaths(ctx context.Context, libPaths *[]string, args ...string) (c *container.Container) {
|
||||||
|
c = container.New(ctx, helperInnerPath, args...)
|
||||||
|
c.Env = append(c.Env, envDoCheck+"=1")
|
||||||
|
c.Bind(os.Args[0], helperInnerPath, 0)
|
||||||
|
|
||||||
|
// in case test has cgo enabled
|
||||||
|
if entries, err := ldd.Exec(ctx, os.Args[0]); err != nil {
|
||||||
|
log.Fatalf("ldd: %v", err)
|
||||||
|
} else {
|
||||||
|
*libPaths = ldd.Path(entries)
|
||||||
|
}
|
||||||
|
for _, name := range *libPaths {
|
||||||
|
c.Bind(name, name, 0)
|
||||||
|
}
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func helperNewContainer(ctx context.Context, args ...string) (c *container.Container) {
|
||||||
|
return helperNewContainerLibPaths(ctx, new([]string), args...)
|
||||||
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user