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" | 	"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 | ||||||
| 
 | 
 | ||||||
|  | |||||||
| @ -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 | ||||||
|  | |||||||
| @ -184,10 +184,14 @@ 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 { | ||||||
| 			k.printBaseErr(err, | 			if m, ok := messageFromError(err); ok { | ||||||
| 				fmt.Sprintf("cannot prepare op at index %d:", i)) | 				k.fatal(m) | ||||||
| 			k.beforeExit() | 			} else { | ||||||
| 			k.exit(1) | 				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 | 		// 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 { | ||||||
| 			k.printBaseErr(err, | 			if m, ok := messageFromError(err); ok { | ||||||
| 				fmt.Sprintf("cannot apply op at index %d:", i)) | 				k.fatal(m) | ||||||
| 			k.beforeExit() | 			} else { | ||||||
| 			k.exit(1) | 				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