container/init: handle unwrapped errors
All checks were successful
Test / Create distribution (push) Successful in 34s
Test / Sandbox (push) Successful in 1m59s
Test / Hpkg (push) Successful in 3m32s
Test / Sandbox (race detector) (push) Successful in 3m54s
Test / Hakurei (race detector) (push) Successful in 5m16s
Test / Hakurei (push) Successful in 2m12s
Test / Flake checks (push) Successful in 1m29s
All checks were successful
Test / Create distribution (push) Successful in 34s
Test / Sandbox (push) Successful in 1m59s
Test / Hpkg (push) Successful in 3m32s
Test / Sandbox (race detector) (push) Successful in 3m54s
Test / Hakurei (race detector) (push) Successful in 5m16s
Test / Hakurei (push) Successful in 2m12s
Test / Flake checks (push) Successful in 1m29s
This is much cleaner from both the return statement and the error handling. Signed-off-by: Ophestra <cat@gensokyo.uk>
This commit is contained in:
parent
a462341a0a
commit
f24dd4ab8c
@ -6,6 +6,49 @@ import (
|
||||
"syscall"
|
||||
)
|
||||
|
||||
// messageFromError returns a printable error message for a supported concrete type.
|
||||
func messageFromError(err error) (string, bool) {
|
||||
if m, ok := messagePrefixP[MountError, *MountError]("cannot ", err); ok {
|
||||
return m, ok
|
||||
}
|
||||
if m, ok := messagePrefixP[os.PathError, *os.PathError]("cannot ", err); ok {
|
||||
return m, ok
|
||||
}
|
||||
if m, ok := messagePrefixP[AbsoluteError, *AbsoluteError]("", err); ok {
|
||||
return m, ok
|
||||
}
|
||||
if m, ok := messagePrefix[OpRepeatError]("", err); ok {
|
||||
return m, ok
|
||||
}
|
||||
if m, ok := messagePrefix[OpStateError]("", err); ok {
|
||||
return m, ok
|
||||
}
|
||||
|
||||
return zeroString, false
|
||||
}
|
||||
|
||||
// messagePrefix checks and prefixes the error message of a non-pointer error.
|
||||
// While this is usable for pointer errors, such use should be avoided as nil check is omitted.
|
||||
func messagePrefix[T error](prefix string, err error) (string, bool) {
|
||||
var targetError T
|
||||
if errors.As(err, &targetError) {
|
||||
return prefix + targetError.Error(), true
|
||||
}
|
||||
return zeroString, false
|
||||
}
|
||||
|
||||
// messagePrefixP checks and prefixes the error message of a pointer error.
|
||||
func messagePrefixP[V any, T interface {
|
||||
*V
|
||||
error
|
||||
}](prefix string, err error) (string, bool) {
|
||||
var targetError T
|
||||
if errors.As(err, &targetError) && targetError != nil {
|
||||
return prefix + targetError.Error(), true
|
||||
}
|
||||
return zeroString, false
|
||||
}
|
||||
|
||||
type MountError struct {
|
||||
Source, Target, Fstype string
|
||||
|
||||
|
@ -8,6 +8,52 @@ import (
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestMessageFromError(t *testing.T) {
|
||||
testCases := []struct {
|
||||
name string
|
||||
err error
|
||||
want string
|
||||
wantOk bool
|
||||
}{
|
||||
{"mount", &MountError{
|
||||
Source: SourceTmpfsEphemeral,
|
||||
Target: "/sysroot/tmp",
|
||||
Fstype: FstypeTmpfs,
|
||||
Flags: syscall.MS_NOSUID | syscall.MS_NODEV,
|
||||
Data: zeroString,
|
||||
Errno: syscall.EINVAL,
|
||||
}, "cannot mount tmpfs on /sysroot/tmp: invalid argument", true},
|
||||
|
||||
{"path", &os.PathError{
|
||||
Op: "mount",
|
||||
Path: "/sysroot",
|
||||
Err: errUnique,
|
||||
}, "cannot mount /sysroot: unique error injected by the test suite", true},
|
||||
|
||||
{"absolute", &AbsoluteError{"etc/mtab"},
|
||||
`path "etc/mtab" is not absolute`, true},
|
||||
|
||||
{"repeat", OpRepeatError("autoetc"),
|
||||
"autoetc is not repeatable", true},
|
||||
|
||||
{"state", OpStateError("overlay"),
|
||||
"impossible overlay state reached", true},
|
||||
|
||||
{"unsupported", errUnique, zeroString, false},
|
||||
}
|
||||
for _, tc := range testCases {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
got, ok := messageFromError(tc.err)
|
||||
if got != tc.want {
|
||||
t.Errorf("messageFromError: %q, want %q", got, tc.want)
|
||||
}
|
||||
if ok != tc.wantOk {
|
||||
t.Errorf("messageFromError: ok = %v, want %v", ok, tc.wantOk)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestMountError(t *testing.T) {
|
||||
testCases := []struct {
|
||||
name string
|
||||
|
@ -184,10 +184,14 @@ func initEntrypoint(k syscallDispatcher, prepareLogger func(prefix string), setV
|
||||
}
|
||||
|
||||
if err := op.early(state, k); err != nil {
|
||||
k.printBaseErr(err,
|
||||
fmt.Sprintf("cannot prepare op at index %d:", i))
|
||||
k.beforeExit()
|
||||
k.exit(1)
|
||||
if m, ok := messageFromError(err); ok {
|
||||
k.fatal(m)
|
||||
} else {
|
||||
k.printBaseErr(err,
|
||||
fmt.Sprintf("cannot prepare op at index %d:", i))
|
||||
k.beforeExit()
|
||||
k.exit(1)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -224,10 +228,14 @@ func initEntrypoint(k syscallDispatcher, prepareLogger func(prefix string), setV
|
||||
// ops already checked during early setup
|
||||
k.verbosef("%s %s", op.prefix(), op)
|
||||
if err := op.apply(state, k); err != nil {
|
||||
k.printBaseErr(err,
|
||||
fmt.Sprintf("cannot apply op at index %d:", i))
|
||||
k.beforeExit()
|
||||
k.exit(1)
|
||||
if m, ok := messageFromError(err); ok {
|
||||
k.fatal(m)
|
||||
} else {
|
||||
k.printBaseErr(err,
|
||||
fmt.Sprintf("cannot apply op at index %d:", i))
|
||||
k.beforeExit()
|
||||
k.exit(1)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user