diff options
| author | Ophestra <cat@gensokyo.uk> | 2025-08-30 23:44:48 +0900 |
|---|---|---|
| committer | Ophestra <cat@gensokyo.uk> | 2025-08-30 23:44:48 +0900 |
| commit | 712cfc06d7e541c6e28b4b127a4760b4cfccffa8 (patch) | |
| tree | 3dbd2bc0fc03ba5d108646b342392f5fdf841c90 /container/container_test.go | |
| parent | f5abce9df5727904a1daa246025a87c4bfe62553 (diff) | |
container: wrap container init start errors
This helps indicate the exact origin and nature of the error. This eliminates generic WrapErr from container.
Signed-off-by: Ophestra <cat@gensokyo.uk>
Diffstat (limited to 'container/container_test.go')
| -rw-r--r-- | container/container_test.go | 142 |
1 files changed, 128 insertions, 14 deletions
diff --git a/container/container_test.go b/container/container_test.go index 90fba871..0d408bf1 100644 --- a/container/container_test.go +++ b/container/container_test.go @@ -10,6 +10,7 @@ import ( "os" "os/exec" "os/signal" + "reflect" "strconv" "strings" "syscall" @@ -26,6 +27,100 @@ import ( "hakurei.app/ldd" ) +func TestStartError(t *testing.T) { + testCases := []struct { + name string + err error + s string + is error + isF error + }{ + {"params env", &container.StartError{ + Fatal: true, + Step: "set up params stream", + Err: container.ErrReceiveEnv, + }, + "set up params stream: environment variable not set", + container.ErrReceiveEnv, syscall.EBADF}, + + {"params", &container.StartError{ + Fatal: true, + Step: "set up params stream", + Err: &os.SyscallError{Syscall: "pipe2", Err: syscall.EBADF}, + }, + "set up params stream pipe2: bad file descriptor", + syscall.EBADF, os.ErrInvalid}, + + {"PR_SET_NO_NEW_PRIVS", &container.StartError{ + Fatal: true, + Step: "prctl(PR_SET_NO_NEW_PRIVS)", + Err: syscall.EPERM, + }, + "prctl(PR_SET_NO_NEW_PRIVS): operation not permitted", + syscall.EPERM, syscall.EACCES}, + + {"landlock abi", &container.StartError{ + Step: "get landlock ABI", + Err: syscall.ENOSYS, + }, + "get landlock ABI: function not implemented", + syscall.ENOSYS, syscall.ENOEXEC}, + + {"landlock old", &container.StartError{ + Step: "kernel version too old for LANDLOCK_SCOPE_ABSTRACT_UNIX_SOCKET", + Err: syscall.ENOSYS, + Origin: true, + }, + "kernel version too old for LANDLOCK_SCOPE_ABSTRACT_UNIX_SOCKET", + syscall.ENOSYS, syscall.ENOSPC}, + + {"landlock create", &container.StartError{ + Fatal: true, + Step: "create landlock ruleset", + Err: syscall.EBADFD, + }, + "create landlock ruleset: file descriptor in bad state", + syscall.EBADFD, syscall.EBADF}, + + {"landlock enforce", &container.StartError{ + Fatal: true, + Step: "enforce landlock ruleset", + Err: syscall.ENOTRECOVERABLE, + }, + "enforce landlock ruleset: state not recoverable", + syscall.ENOTRECOVERABLE, syscall.ETIMEDOUT}, + + {"start", &container.StartError{ + Step: "start container init", + Err: &os.PathError{ + Op: "fork/exec", + Path: "/proc/nonexistent", + Err: syscall.ENOENT, + }, Passthrough: true, + }, + "fork/exec /proc/nonexistent: no such file or directory", + syscall.ENOENT, syscall.ENOSYS}, + } + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + t.Run("error", func(t *testing.T) { + if got := tc.err.Error(); got != tc.s { + t.Errorf("Error: %q, want %q", got, tc.s) + } + }) + + t.Run("is", func(t *testing.T) { + if !errors.Is(tc.err, tc.is) { + t.Error("Is: unexpected false") + } + if errors.Is(tc.err, tc.isF) { + t.Errorf("Is: unexpected true") + } + }) + }) + } +} + const ( ignore = "\x00" ignoreV = -1 @@ -217,9 +312,11 @@ func TestContainer(t *testing.T) { t.Run("cancel", testContainerCancel(nil, func(t *testing.T, c *container.Container) { wantErr := context.Canceled wantExitCode := 0 - if err := c.Wait(); !errors.Is(err, wantErr) { - container.GetOutput().PrintBaseErr(err, "wait:") - t.Errorf("Wait: error = %v, want %v", err, wantErr) + if err := c.Wait(); !reflect.DeepEqual(err, wantErr) { + if m, ok := container.InternalMessageFromError(err); ok { + t.Error(m) + } + t.Errorf("Wait: error = %#v, want %#v", err, wantErr) } if ps := c.ProcessState(); ps == nil { t.Errorf("ProcessState unexpectedly returned nil") @@ -233,7 +330,9 @@ func TestContainer(t *testing.T) { }, func(t *testing.T, c *container.Container) { var exitError *exec.ExitError if err := c.Wait(); !errors.As(err, &exitError) { - container.GetOutput().PrintBaseErr(err, "wait:") + if m, ok := container.InternalMessageFromError(err); ok { + t.Error(m) + } t.Errorf("Wait: error = %v", err) } if code := exitError.ExitCode(); code != blockExitCodeInterrupt { @@ -313,17 +412,26 @@ func TestContainer(t *testing.T) { if err := c.Start(); err != nil { _, _ = output.WriteTo(os.Stdout) - container.GetOutput().PrintBaseErr(err, "start:") - t.Fatalf("cannot start container: %v", err) + if m, ok := container.InternalMessageFromError(err); ok { + t.Fatal(m) + } else { + t.Fatalf("cannot start container: %v", err) + } } else if err = c.Serve(); err != nil { _, _ = output.WriteTo(os.Stdout) - container.GetOutput().PrintBaseErr(err, "serve:") - t.Errorf("cannot serve setup params: %v", err) + if m, ok := container.InternalMessageFromError(err); ok { + t.Error(m) + } else { + t.Errorf("cannot serve setup params: %v", err) + } } if err := c.Wait(); err != nil { _, _ = output.WriteTo(os.Stdout) - container.GetOutput().PrintBaseErr(err, "wait:") - t.Fatalf("wait: %v", err) + if m, ok := container.InternalMessageFromError(err); ok { + t.Fatal(m) + } else { + t.Fatalf("wait: %v", err) + } } }) } @@ -376,11 +484,17 @@ func testContainerCancel( } if err := c.Start(); err != nil { - container.GetOutput().PrintBaseErr(err, "start:") - t.Fatalf("cannot start container: %v", err) + if m, ok := container.InternalMessageFromError(err); ok { + t.Fatal(m) + } else { + t.Fatalf("cannot start container: %v", err) + } } else if err = c.Serve(); err != nil { - container.GetOutput().PrintBaseErr(err, "serve:") - t.Errorf("cannot serve setup params: %v", err) + if m, ok := container.InternalMessageFromError(err); ok { + t.Error(m) + } else { + t.Errorf("cannot serve setup params: %v", err) + } } <-ready cancel() |
