diff --git a/vminitd/Sources/vminitd/Server+GRPC.swift b/vminitd/Sources/vminitd/Server+GRPC.swift index 868a96b2..7c80ad14 100644 --- a/vminitd/Sources/vminitd/Server+GRPC.swift +++ b/vminitd/Sources/vminitd/Server+GRPC.swift @@ -44,6 +44,39 @@ private let _kill = Glibc.kill private let _sync = Glibc.sync #endif +extension ContainerizationError { + func toGRPCStatus(operation: String) -> GRPCStatus { + let message = "\(operation): \(self)" + let code: GRPCStatus.Code = { + switch self.code { + case .invalidArgument: + return .invalidArgument + case .notFound: + return .notFound + case .exists: + return .alreadyExists + case .cancelled: + return .cancelled + case .unsupported: + return .unimplemented + case .unknown: + return .unknown + case .internalError: + return .internalError + case .interrupted: + return .unavailable + case .invalidState: + return .failedPrecondition + case .timeout: + return .deadlineExceeded + default: + return .internalError + } + }() + return GRPCStatus(code: code, message: message, cause: self) + } +} + extension Initd: Com_Apple_Containerization_Sandbox_V3_SandboxContextAsyncProvider { func setTime( request: Com_Apple_Containerization_Sandbox_V3_SetTimeRequest, @@ -432,14 +465,14 @@ extension Initd: Com_Apple_Containerization_Sandbox_V3_SandboxContextAsyncProvid "configuration": "\(request.configuration.count)", ]) - if !request.hasContainerID { - throw ContainerizationError( - .invalidArgument, - message: "processes in the root of the vm not implemented" - ) - } - do { + if !request.hasContainerID { + throw ContainerizationError( + .invalidArgument, + message: "processes in the root of the vm not implemented" + ) + } + var ociSpec = try JSONDecoder().decode( ContainerizationOCI.Spec.self, from: request.configuration @@ -498,6 +531,15 @@ extension Initd: Com_Apple_Containerization_Sandbox_V3_SandboxContextAsyncProvid } return .init() + } catch let err as ContainerizationError { + log.error( + "createProcess", + metadata: [ + "id": "\(request.id)", + "containerID": "\(request.containerID)", + "error": "\(err)", + ]) + throw err.toGRPCStatus(operation: "createProcess: failed to create process") } catch { log.error( "createProcess", @@ -506,10 +548,7 @@ extension Initd: Com_Apple_Containerization_Sandbox_V3_SandboxContextAsyncProvid "containerID": "\(request.containerID)", "error": "\(error)", ]) - if error is GRPCStatus { - throw error - } - throw GRPCStatus(code: .internalError, message: "createProcess: \(error)") + throw GRPCStatus(code: .internalError, message: "createProcess: failed to create process: \(error)") } } @@ -525,17 +564,37 @@ extension Initd: Com_Apple_Containerization_Sandbox_V3_SandboxContextAsyncProvid "signal": "\(request.signal)", ]) - if !request.hasContainerID { - throw ContainerizationError( - .invalidArgument, - message: "processes in the root of the vm not implemented" - ) - } + do { + if !request.hasContainerID { + throw ContainerizationError( + .invalidArgument, + message: "processes in the root of the vm not implemented" + ) + } - let ctr = try await self.state.get(container: request.containerID) - try await ctr.kill(execID: request.id, request.signal) + let ctr = try await self.state.get(container: request.containerID) + try await ctr.kill(execID: request.id, request.signal) - return .init() + return .init() + } catch let err as ContainerizationError { + log.error( + "killProcess", + metadata: [ + "id": "\(request.id)", + "containerID": "\(request.containerID)", + "error": "\(err)", + ]) + throw err.toGRPCStatus(operation: "killProcess: failed to kill process") + } catch { + log.error( + "killProcess", + metadata: [ + "id": "\(request.id)", + "containerID": "\(request.containerID)", + "error": "\(error)", + ]) + throw GRPCStatus(code: .internalError, message: "killProcess: failed to kill process: \(error)") + } } func deleteProcess( @@ -548,14 +607,14 @@ extension Initd: Com_Apple_Containerization_Sandbox_V3_SandboxContextAsyncProvid "containerID": "\(request.containerID)", ]) - if !request.hasContainerID { - throw ContainerizationError( - .invalidArgument, - message: "processes in the root of the vm not implemented" - ) - } - do { + if !request.hasContainerID { + throw ContainerizationError( + .invalidArgument, + message: "processes in the root of the vm not implemented" + ) + } + let ctr = try await self.state.get(container: request.containerID) // Are we trying to delete the container itself? @@ -568,6 +627,15 @@ extension Initd: Com_Apple_Containerization_Sandbox_V3_SandboxContextAsyncProvid } return .init() + } catch let err as ContainerizationError { + log.error( + "deleteProcess", + metadata: [ + "id": "\(request.id)", + "containerID": "\(request.containerID)", + "error": "\(err)", + ]) + throw err.toGRPCStatus(operation: "deleteProcess: failed to delete process") } catch { log.error( "deleteProcess", @@ -576,10 +644,7 @@ extension Initd: Com_Apple_Containerization_Sandbox_V3_SandboxContextAsyncProvid "containerID": "\(request.containerID)", "error": "\(error)", ]) - throw GRPCStatus( - code: .internalError, - message: "deleteProcess: \(error)" - ) + throw GRPCStatus(code: .internalError, message: "deleteProcess: failed to delete process: \(error)") } } @@ -593,20 +658,29 @@ extension Initd: Com_Apple_Containerization_Sandbox_V3_SandboxContextAsyncProvid "containerID": "\(request.containerID)", ]) - if !request.hasContainerID { - throw ContainerizationError( - .invalidArgument, - message: "processes in the root of the vm not implemented" - ) - } - do { + if !request.hasContainerID { + throw ContainerizationError( + .invalidArgument, + message: "processes in the root of the vm not implemented" + ) + } + let ctr = try await self.state.get(container: request.containerID) let pid = try await ctr.start(execID: request.id) return .with { $0.pid = pid } + } catch let err as ContainerizationError { + log.error( + "startProcess", + metadata: [ + "id": "\(request.id)", + "containerID": "\(request.containerID)", + "error": "\(err)", + ]) + throw err.toGRPCStatus(operation: "startProcess: failed to start process") } catch { log.error( "startProcess", @@ -632,20 +706,29 @@ extension Initd: Com_Apple_Containerization_Sandbox_V3_SandboxContextAsyncProvid "containerID": "\(request.containerID)", ]) - if !request.hasContainerID { - throw ContainerizationError( - .invalidArgument, - message: "processes in the root of the vm not implemented" - ) - } - do { + if !request.hasContainerID { + throw ContainerizationError( + .invalidArgument, + message: "processes in the root of the vm not implemented" + ) + } + let ctr = try await self.state.get(container: request.containerID) let size = Terminal.Size( width: UInt16(request.columns), height: UInt16(request.rows) ) try await ctr.resize(execID: request.id, size: size) + } catch let err as ContainerizationError { + log.error( + "resizeProcess", + metadata: [ + "id": "\(request.id)", + "containerID": "\(request.containerID)", + "error": "\(err)", + ]) + throw err.toGRPCStatus(operation: "resizeProcess: failed to resize process") } catch { log.error( "resizeProcess", @@ -673,14 +756,14 @@ extension Initd: Com_Apple_Containerization_Sandbox_V3_SandboxContextAsyncProvid "containerID": "\(request.containerID)", ]) - if !request.hasContainerID { - throw ContainerizationError( - .invalidArgument, - message: "processes in the root of the vm not implemented" - ) - } - do { + if !request.hasContainerID { + throw ContainerizationError( + .invalidArgument, + message: "processes in the root of the vm not implemented" + ) + } + let ctr = try await self.state.get(container: request.containerID) let exitStatus = try await ctr.wait(execID: request.id) @@ -688,6 +771,15 @@ extension Initd: Com_Apple_Containerization_Sandbox_V3_SandboxContextAsyncProvid $0.exitCode = exitStatus.exitStatus $0.exitedAt = Google_Protobuf_Timestamp(date: exitStatus.exitedAt) } + } catch let err as ContainerizationError { + log.error( + "waitProcess", + metadata: [ + "id": "\(request.id)", + "containerID": "\(request.containerID)", + "error": "\(err)", + ]) + throw err.toGRPCStatus(operation: "waitProcess: failed to wait on process") } catch { log.error( "waitProcess", @@ -713,19 +805,28 @@ extension Initd: Com_Apple_Containerization_Sandbox_V3_SandboxContextAsyncProvid "containerID": "\(request.containerID)", ]) - if !request.hasContainerID { - throw ContainerizationError( - .invalidArgument, - message: "processes in the root of the vm not implemented" - ) - } - do { + if !request.hasContainerID { + throw ContainerizationError( + .invalidArgument, + message: "processes in the root of the vm not implemented" + ) + } + let ctr = try await self.state.get(container: request.containerID) try await ctr.closeStdin(execID: request.id) return .init() + } catch let err as ContainerizationError { + log.error( + "closeProcessStdin", + metadata: [ + "id": "\(request.id)", + "containerID": "\(request.containerID)", + "error": "\(err)", + ]) + throw err.toGRPCStatus(operation: "closeProcessStdin: failed to close process stdin") } catch { log.error( "closeProcessStdin", @@ -788,7 +889,7 @@ extension Initd: Com_Apple_Containerization_Sandbox_V3_SandboxContextAsyncProvid metadata: [ "error": "\(error)" ]) - throw GRPCStatus(code: .internalError, message: "ip-addr-add: \(error)") + throw GRPCStatus(code: .internalError, message: "failed to set IP address on interface \(request.interface): \(error)") } return .init() @@ -846,7 +947,7 @@ extension Initd: Com_Apple_Containerization_Sandbox_V3_SandboxContextAsyncProvid metadata: [ "error": "\(error)" ]) - throw GRPCStatus(code: .internalError, message: "ip-route-add-default: \(error)") + throw GRPCStatus(code: .internalError, message: "failed to set default gateway on interface \(request.interface): \(error)") } return .init() @@ -887,7 +988,7 @@ extension Initd: Com_Apple_Containerization_Sandbox_V3_SandboxContextAsyncProvid metadata: [ "error": "\(error)" ]) - throw GRPCStatus(code: .internalError, message: "configure-dns: \(error)") + throw GRPCStatus(code: .internalError, message: "failed to configure DNS at location \(request.location): \(error)") } return .init()