hst: replace internal/app error
All checks were successful
Test / Create distribution (push) Successful in 43s
Test / Hpkg (push) Successful in 4m3s
Test / Sandbox (race detector) (push) Successful in 4m36s
Test / Hakurei (race detector) (push) Successful in 5m17s
Test / Sandbox (push) Successful in 1m27s
Test / Hakurei (push) Successful in 2m15s
Test / Flake checks (push) Successful in 1m28s
All checks were successful
Test / Create distribution (push) Successful in 43s
Test / Hpkg (push) Successful in 4m3s
Test / Sandbox (race detector) (push) Successful in 4m36s
Test / Hakurei (race detector) (push) Successful in 5m17s
Test / Sandbox (push) Successful in 1m27s
Test / Hakurei (push) Successful in 2m15s
Test / Flake checks (push) Successful in 1m28s
This turns out to still be quite useful across internal/app and its relatives. Perhaps a cleaner replacement for baseError. Signed-off-by: Ophestra <cat@gensokyo.uk>
This commit is contained in:
30
hst/hst.go
30
hst/hst.go
@@ -2,12 +2,42 @@
|
||||
package hst
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"net"
|
||||
"os"
|
||||
|
||||
"hakurei.app/container"
|
||||
"hakurei.app/container/seccomp"
|
||||
"hakurei.app/system"
|
||||
"hakurei.app/system/dbus"
|
||||
)
|
||||
|
||||
// An AppError is returned while starting an app according to [hst.Config].
|
||||
type AppError struct {
|
||||
Step string
|
||||
Err error
|
||||
Msg string
|
||||
}
|
||||
|
||||
func (e *AppError) Error() string { return e.Err.Error() }
|
||||
func (e *AppError) Unwrap() error { return e.Err }
|
||||
func (e *AppError) Message() string {
|
||||
if e.Msg != "" {
|
||||
return e.Msg
|
||||
}
|
||||
|
||||
switch {
|
||||
case errors.As(e.Err, new(*os.PathError)),
|
||||
errors.As(e.Err, new(*os.LinkError)),
|
||||
errors.As(e.Err, new(*os.SyscallError)),
|
||||
errors.As(e.Err, new(*net.OpError)):
|
||||
return "cannot " + e.Error()
|
||||
|
||||
default:
|
||||
return "cannot " + e.Step + ": " + e.Error()
|
||||
}
|
||||
}
|
||||
|
||||
// Paths contains environment-dependent paths used by hakurei.
|
||||
type Paths struct {
|
||||
// temporary directory returned by [os.TempDir] (usually `/tmp`)
|
||||
|
||||
@@ -2,11 +2,93 @@ package hst_test
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"net"
|
||||
"os"
|
||||
"syscall"
|
||||
"testing"
|
||||
|
||||
"hakurei.app/container"
|
||||
"hakurei.app/container/stub"
|
||||
"hakurei.app/hst"
|
||||
)
|
||||
|
||||
func TestAppError(t *testing.T) {
|
||||
testCases := []struct {
|
||||
name string
|
||||
err error
|
||||
s string
|
||||
message string
|
||||
is, isF error
|
||||
}{
|
||||
{"message", &hst.AppError{Step: "obtain uid from hsu", Err: stub.UniqueError(0),
|
||||
Msg: "the setuid helper is missing: /run/wrappers/bin/hsu"},
|
||||
"unique error 0 injected by the test suite",
|
||||
"the setuid helper is missing: /run/wrappers/bin/hsu",
|
||||
stub.UniqueError(0), os.ErrNotExist},
|
||||
|
||||
{"os.PathError", &hst.AppError{Step: "passthrough os.PathError",
|
||||
Err: &os.PathError{Op: "stat", Path: "/proc/nonexistent", Err: os.ErrNotExist}},
|
||||
"stat /proc/nonexistent: file does not exist",
|
||||
"cannot stat /proc/nonexistent: file does not exist",
|
||||
os.ErrNotExist, stub.UniqueError(0xdeadbeef)},
|
||||
|
||||
{"os.LinkError", &hst.AppError{Step: "passthrough os.LinkError",
|
||||
Err: &os.LinkError{Op: "link", Old: "/proc/self", New: "/proc/nonexistent", Err: os.ErrNotExist}},
|
||||
"link /proc/self /proc/nonexistent: file does not exist",
|
||||
"cannot link /proc/self /proc/nonexistent: file does not exist",
|
||||
os.ErrNotExist, stub.UniqueError(0xdeadbeef)},
|
||||
|
||||
{"os.SyscallError", &hst.AppError{Step: "passthrough os.SyscallError",
|
||||
Err: &os.SyscallError{Syscall: "meow", Err: syscall.ENOSYS}},
|
||||
"meow: function not implemented",
|
||||
"cannot meow: function not implemented",
|
||||
syscall.ENOSYS, syscall.ENOTRECOVERABLE},
|
||||
|
||||
{"net.OpError", &hst.AppError{Step: "passthrough net.OpError",
|
||||
Err: &net.OpError{Op: "dial", Net: "cat", Err: net.UnknownNetworkError("cat")}},
|
||||
"dial cat: unknown network cat",
|
||||
"cannot dial cat: unknown network cat",
|
||||
net.UnknownNetworkError("cat"), syscall.ENOTRECOVERABLE},
|
||||
|
||||
{"default", &hst.AppError{Step: "initialise container configuration", Err: stub.UniqueError(1)},
|
||||
"unique error 1 injected by the test suite",
|
||||
"cannot initialise container configuration: unique error 1 injected by the test suite",
|
||||
stub.UniqueError(1), os.ErrInvalid},
|
||||
}
|
||||
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: %s, want %s", got, tc.s)
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("message", func(t *testing.T) {
|
||||
gotMessage, gotMessageOk := container.GetErrorMessage(tc.err)
|
||||
if want := tc.message != "\x00"; gotMessageOk != want {
|
||||
t.Errorf("GetErrorMessage: ok = %v, want %v", gotMessage, want)
|
||||
}
|
||||
|
||||
if gotMessageOk {
|
||||
if gotMessage != tc.message {
|
||||
t.Errorf("GetErrorMessage: %s, want %s", gotMessage, tc.message)
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("is", func(t *testing.T) {
|
||||
if !errors.Is(tc.err, tc.is) {
|
||||
t.Errorf("Is: unexpected false for %v", tc.is)
|
||||
}
|
||||
if errors.Is(tc.err, tc.isF) {
|
||||
t.Errorf("Is: unexpected true for %v", tc.isF)
|
||||
}
|
||||
})
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestTemplate(t *testing.T) {
|
||||
const want = `{
|
||||
"id": "org.chromium.Chromium",
|
||||
|
||||
Reference in New Issue
Block a user