Use separate exception codes for better Sentry grouping (#363)

This commit is contained in:
Nikolay Edigaryev 2022-12-19 21:40:55 +04:00 committed by GitHub
parent 9cdb7087f2
commit 1380ece108
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
10 changed files with 74 additions and 25 deletions

View File

@ -19,7 +19,7 @@ struct IP: AsyncParsableCommand {
let vmMACAddress = MACAddress(fromString: vmConfig.macAddress.string)!
guard let ipViaDHCP = try await IP.resolveIP(vmMACAddress, secondsToWait: wait) else {
throw RuntimeError("no IP address found, is your VM running?")
throw RuntimeError.NoIPAddressFound("no IP address found, is your VM running?")
}
let arpCache = try ARPCache()

View File

@ -47,7 +47,7 @@ struct Login: AsyncParsableCommand {
credentialsProviders: [credentialsProvider])
try await registry.ping()
} catch {
throw RuntimeError("invalid credentials: \(error)")
throw RuntimeError.InvalidCredentials("invalid credentials: \(error)")
}
try KeychainCredentialsProvider().store(host: host, user: user, password: password)

View File

@ -117,7 +117,7 @@ struct Run: AsyncParsableCommand {
}
if try !FileLock(lockURL: additionalDiskAttachment.url).trylock() {
throw RuntimeError("disk \(additionalDiskAttachment.url.path) seems to be already in use, "
throw RuntimeError.DiskAlreadyInUse("disk \(additionalDiskAttachment.url.path) seems to be already in use, "
+ "unmount it first in Finder")
}
}
@ -154,7 +154,7 @@ struct Run: AsyncParsableCommand {
// [1]: https://man.openbsd.org/fcntl
let lock = try PIDLock(lockURL: vmDir.configURL)
if try !lock.trylock() {
throw RuntimeError("Virtual machine \"\(name)\" is already running!", exitCode: 2)
throw RuntimeError.VMAlreadyRunning("VM \"\(name)\" is already running!")
}
let task = Task {

View File

@ -19,7 +19,7 @@ struct Stop: AsyncParsableCommand {
// Find the VM's PID
var pid = try lock.pid()
if pid == 0 {
throw RuntimeError("VM \(name) is not running", exitCode: 2)
throw RuntimeError.VMNotRunning("VM \"\(name)\" is not running")
}
// Try to gracefully terminate the VM
@ -54,7 +54,7 @@ struct Stop: AsyncParsableCommand {
if ret != 0 {
let details = Errno(rawValue: CInt(errno))
throw RuntimeError("failed to forcefully terminate the VM \(name): \(details)")
throw RuntimeError.VMTerminationFailed("failed to forcefully terminate the VM \"\(name)\": \(details)")
}
}
}

View File

@ -106,7 +106,7 @@ struct RemoteName: Comparable, Hashable, CustomStringConvertible {
try ParseTreeWalker().walk(referenceCollector, try parser.root())
if let error = errorCollector.error {
throw RuntimeError("failed to parse remote name: \(error)")
throw RuntimeError.FailedToParseRemoteName("\(error)")
}
host = referenceCollector.host!
@ -120,7 +120,7 @@ struct RemoteName: Comparable, Hashable, CustomStringConvertible {
} else if reference.starts(with: ":") {
self.reference = Reference(tag: String(reference.dropFirst(1)))
} else {
throw RuntimeError("failed to parse remote name: unknown reference format")
throw RuntimeError.FailedToParseRemoteName("unknown reference format")
}
} else {
self.reference = Reference(tag: "latest")

View File

@ -44,7 +44,7 @@ class PIDLock {
let details = Errno(rawValue: CInt(errno))
throw RuntimeError("\(message): \(details)")
throw RuntimeError.PIDLockFailed("\(message): \(details)")
}
return (true, result)

View File

@ -106,8 +106,8 @@ struct Root: AsyncParsableCommand {
print(error)
if let runtimeError = error as? RuntimeError {
Foundation.exit(runtimeError.exitCode)
if let errorWithExitCode = error as? HasExitCode {
Foundation.exit(errorWithExitCode.exitCode)
}
Foundation.exit(1)

View File

@ -13,7 +13,7 @@ extension URL {
let times = [accessDate.asTimeval(), modificationDate.asTimeval()]
let ret = utimes(path, times)
if ret != 0 {
throw RuntimeError("utimes(2) failed: \(ret.explanation())")
throw RuntimeError.FailedToUpdateAccessDate("utimes(2) failed: \(ret.explanation())")
}
}
}

View File

@ -41,7 +41,7 @@ struct VMDirectory: Prunable {
func initialize(overwrite: Bool = false) throws {
if !overwrite && initialized {
throw RuntimeError("VM directory is already initialized, preventing overwrite")
throw RuntimeError.VMDirectoryAlreadyInitialized("VM directory is already initialized, preventing overwrite")
}
try FileManager.default.createDirectory(at: baseURL, withIntermediateDirectories: true, attributes: nil)
@ -53,11 +53,11 @@ struct VMDirectory: Prunable {
func validate() throws {
if !FileManager.default.fileExists(atPath: baseURL.path) {
throw RuntimeError("the specified VM does not exist")
throw RuntimeError.VMDoesNotExist(name: baseURL.lastPathComponent)
}
if !initialized {
throw RuntimeError("VM is missing some of its files (\(configURL.lastPathComponent),"
throw RuntimeError.VMMissingFiles("VM is missing some of its files (\(configURL.lastPathComponent),"
+ " \(diskURL.lastPathComponent) or \(nvramURL.lastPathComponent))")
}
}

View File

@ -26,7 +26,7 @@ class VMStorageHelper {
return try closure()
} catch {
if error.isFileNotFound() {
throw RuntimeError("source VM \"\(name)\" not found, is it listed in \"tart list\"?")
throw RuntimeError.VMDoesNotExist(name: name)
}
throw error
@ -40,17 +40,66 @@ extension Error {
}
}
class RuntimeError: Error, CustomStringConvertible {
let message: String
let exitCode: Int32
enum RuntimeError : Error {
case VMDoesNotExist(name: String)
case VMMissingFiles(_ message: String)
case VMNotRunning(_ message: String)
case VMAlreadyRunning(_ message: String)
case NoIPAddressFound(_ message: String)
case DiskAlreadyInUse(_ message: String)
case FailedToUpdateAccessDate(_ message: String)
case PIDLockFailed(_ message: String)
case FailedToParseRemoteName(_ message: String)
case VMTerminationFailed(_ message: String)
case InvalidCredentials(_ message: String)
case VMDirectoryAlreadyInitialized(_ message: String)
}
init(_ message: String, exitCode: Int32 = 1) {
self.message = message
self.exitCode = exitCode
protocol HasExitCode {
var exitCode: Int32 { get }
}
extension RuntimeError : CustomStringConvertible {
public var description: String {
switch self {
case .VMDoesNotExist(let name):
return "the specified VM \"\(name)\" does not exist"
case .VMMissingFiles(let message):
return message
case .VMNotRunning(let message):
return message
case .VMAlreadyRunning(let message):
return message
case .NoIPAddressFound(let message):
return message
case .DiskAlreadyInUse(let message):
return message
case .FailedToUpdateAccessDate(let message):
return message
case .PIDLockFailed(let message):
return message
case .FailedToParseRemoteName(let cause):
return "failed to parse remote name: \(cause)"
case .VMTerminationFailed(let message):
return message
case .InvalidCredentials(let message):
return message
case .VMDirectoryAlreadyInitialized(let message):
return message
}
}
}
var description: String {
message
extension RuntimeError : HasExitCode {
var exitCode: Int32 {
switch self {
case .VMNotRunning:
return 2
case .VMAlreadyRunning:
return 2
default:
return 1
}
}
}
@ -60,7 +109,7 @@ class RuntimeError: Error, CustomStringConvertible {
extension RuntimeError : CustomNSError {
var errorUserInfo: [String : Any] {
[
NSDebugDescriptionErrorKey: message,
NSDebugDescriptionErrorKey: description,
]
}
}