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

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:
Ophestra 2025-08-29 01:37:13 +09:00
parent a462341a0a
commit f24dd4ab8c
Signed by: cat
SSH Key Fingerprint: SHA256:gQ67O0enBZ7UdZypgtspB2FDM1g3GVw8nX0XSdcFw8Q
3 changed files with 105 additions and 8 deletions

View File

@ -6,6 +6,49 @@ import (
"syscall" "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 { type MountError struct {
Source, Target, Fstype string Source, Target, Fstype string

View File

@ -8,6 +8,52 @@ import (
"testing" "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) { func TestMountError(t *testing.T) {
testCases := []struct { testCases := []struct {
name string name string

View File

@ -184,12 +184,16 @@ func initEntrypoint(k syscallDispatcher, prepareLogger func(prefix string), setV
} }
if err := op.early(state, k); err != nil { if err := op.early(state, k); err != nil {
if m, ok := messageFromError(err); ok {
k.fatal(m)
} else {
k.printBaseErr(err, k.printBaseErr(err,
fmt.Sprintf("cannot prepare op at index %d:", i)) fmt.Sprintf("cannot prepare op at index %d:", i))
k.beforeExit() k.beforeExit()
k.exit(1) k.exit(1)
} }
} }
}
if err := k.mount(SourceTmpfsRootfs, intermediateHostPath, FstypeTmpfs, MS_NODEV|MS_NOSUID, zeroString); err != nil { if err := k.mount(SourceTmpfsRootfs, intermediateHostPath, FstypeTmpfs, MS_NODEV|MS_NOSUID, zeroString); err != nil {
k.fatalf("cannot mount intermediate root: %v", err) k.fatalf("cannot mount intermediate root: %v", err)
@ -224,12 +228,16 @@ func initEntrypoint(k syscallDispatcher, prepareLogger func(prefix string), setV
// ops already checked during early setup // ops already checked during early setup
k.verbosef("%s %s", op.prefix(), op) k.verbosef("%s %s", op.prefix(), op)
if err := op.apply(state, k); err != nil { if err := op.apply(state, k); err != nil {
if m, ok := messageFromError(err); ok {
k.fatal(m)
} else {
k.printBaseErr(err, k.printBaseErr(err,
fmt.Sprintf("cannot apply op at index %d:", i)) fmt.Sprintf("cannot apply op at index %d:", i))
k.beforeExit() k.beforeExit()
k.exit(1) k.exit(1)
} }
} }
}
// setup requiring host root complete at this point // setup requiring host root complete at this point
if err := k.mount(hostDir, hostDir, zeroString, MS_SILENT|MS_REC|MS_PRIVATE, zeroString); err != nil { if err := k.mount(hostDir, hostDir, zeroString, MS_SILENT|MS_REC|MS_PRIVATE, zeroString); err != nil {