diff --git a/Examples/Benchmark/BenchmarkHandler.swift b/Examples/Benchmark/BenchmarkHandler.swift index 5ce09a42..ed89629d 100644 --- a/Examples/Benchmark/BenchmarkHandler.swift +++ b/Examples/Benchmark/BenchmarkHandler.swift @@ -25,7 +25,7 @@ struct BenchmarkHandler: EventLoopLambdaHandler { typealias Event = String typealias Output = String - static func makeHandler(context: Lambda.InitializationContext) -> EventLoopFuture { + static func makeHandler(context: LambdaInitializationContext) -> EventLoopFuture { context.eventLoop.makeSucceededFuture(BenchmarkHandler()) } diff --git a/Examples/Deployment/Sources/Benchmark/BenchmarkHandler.swift b/Examples/Deployment/Sources/Benchmark/BenchmarkHandler.swift index 1338fa7e..87754176 100644 --- a/Examples/Deployment/Sources/Benchmark/BenchmarkHandler.swift +++ b/Examples/Deployment/Sources/Benchmark/BenchmarkHandler.swift @@ -25,7 +25,7 @@ struct BenchmarkHandler: EventLoopLambdaHandler { typealias Event = String typealias Output = String - static func makeHandler(context: Lambda.InitializationContext) -> EventLoopFuture { + static func makeHandler(context: LambdaInitializationContext) -> EventLoopFuture { context.eventLoop.makeSucceededFuture(BenchmarkHandler()) } diff --git a/Examples/Deployment/Sources/HelloWorld/HelloWorldHandler.swift b/Examples/Deployment/Sources/HelloWorld/HelloWorldHandler.swift index 1e3d189a..cddae31b 100644 --- a/Examples/Deployment/Sources/HelloWorld/HelloWorldHandler.swift +++ b/Examples/Deployment/Sources/HelloWorld/HelloWorldHandler.swift @@ -20,7 +20,7 @@ struct HelloWorldHandler: LambdaHandler { typealias Event = String typealias Output = String - init(context: Lambda.InitializationContext) async throws { + init(context: LambdaInitializationContext) async throws { // setup your resources that you want to reuse here. } diff --git a/Examples/Echo/Lambda.swift b/Examples/Echo/Lambda.swift index 2b2b5763..58b6a32e 100644 --- a/Examples/Echo/Lambda.swift +++ b/Examples/Echo/Lambda.swift @@ -21,7 +21,7 @@ struct MyLambda: LambdaHandler { typealias Event = String typealias Output = String - init(context: Lambda.InitializationContext) async throws { + init(context: LambdaInitializationContext) async throws { // setup your resources that you want to reuse for every invocation here. } diff --git a/Examples/ErrorHandling/Lambda.swift b/Examples/ErrorHandling/Lambda.swift index 8bfe2c6e..d11d21c1 100644 --- a/Examples/ErrorHandling/Lambda.swift +++ b/Examples/ErrorHandling/Lambda.swift @@ -21,7 +21,7 @@ struct MyLambda: LambdaHandler { typealias Event = Request typealias Output = Response - init(context: Lambda.InitializationContext) async throws {} + init(context: LambdaInitializationContext) async throws {} func handle(_ request: Request, context: LambdaContext) async throws -> Response { // switch over the error type "requested" by the request, and trigger such error accordingly diff --git a/Examples/Foundation/Lambda.swift b/Examples/Foundation/Lambda.swift index 6454db5d..dda564f0 100644 --- a/Examples/Foundation/Lambda.swift +++ b/Examples/Foundation/Lambda.swift @@ -30,7 +30,7 @@ struct MyLambda: LambdaHandler { let calculator: ExchangeRatesCalculator - init(context: Lambda.InitializationContext) async throws { + init(context: LambdaInitializationContext) async throws { // the ExchangeRatesCalculator() can be reused over and over self.calculator = ExchangeRatesCalculator() } diff --git a/Examples/JSON/Lambda.swift b/Examples/JSON/Lambda.swift index 91b8af7b..0c2664d8 100644 --- a/Examples/JSON/Lambda.swift +++ b/Examples/JSON/Lambda.swift @@ -30,7 +30,7 @@ struct MyLambda: LambdaHandler { typealias Event = Request typealias Output = Response - init(context: Lambda.InitializationContext) async throws { + init(context: LambdaInitializationContext) async throws { // setup your resources that you want to reuse for every invocation here. } diff --git a/Examples/LocalDebugging/MyLambda/Lambda.swift b/Examples/LocalDebugging/MyLambda/Lambda.swift index 7d74c60a..582d20ae 100644 --- a/Examples/LocalDebugging/MyLambda/Lambda.swift +++ b/Examples/LocalDebugging/MyLambda/Lambda.swift @@ -23,7 +23,7 @@ struct MyLambda: LambdaHandler { typealias Event = Request typealias Output = Response - init(context: Lambda.InitializationContext) async throws { + init(context: LambdaInitializationContext) async throws { // setup your resources that you want to reuse for every invocation here. } diff --git a/Examples/Testing/Sources/Lambda.swift b/Examples/Testing/Sources/Lambda.swift index e0531bb1..51714798 100644 --- a/Examples/Testing/Sources/Lambda.swift +++ b/Examples/Testing/Sources/Lambda.swift @@ -21,7 +21,7 @@ struct MyLambda: LambdaHandler { typealias Event = String typealias Output = String - init(context: Lambda.InitializationContext) async throws { + init(context: LambdaInitializationContext) async throws { // setup your resources that you want to reuse for every invocation here. } diff --git a/Sources/AWSLambdaRuntimeCore/ControlPlaneRequest.swift b/Sources/AWSLambdaRuntimeCore/ControlPlaneRequest.swift index 14c5f2a7..411dc5ad 100644 --- a/Sources/AWSLambdaRuntimeCore/ControlPlaneRequest.swift +++ b/Sources/AWSLambdaRuntimeCore/ControlPlaneRequest.swift @@ -38,17 +38,17 @@ struct Invocation: Hashable { init(headers: HTTPHeaders) throws { guard let requestID = headers.first(name: AmazonHeaders.requestID), !requestID.isEmpty else { - throw Lambda.RuntimeError.invocationMissingHeader(AmazonHeaders.requestID) + throw LambdaRuntimeError.invocationMissingHeader(AmazonHeaders.requestID) } guard let deadline = headers.first(name: AmazonHeaders.deadline), let unixTimeInMilliseconds = Int64(deadline) else { - throw Lambda.RuntimeError.invocationMissingHeader(AmazonHeaders.deadline) + throw LambdaRuntimeError.invocationMissingHeader(AmazonHeaders.deadline) } guard let invokedFunctionARN = headers.first(name: AmazonHeaders.invokedFunctionARN) else { - throw Lambda.RuntimeError.invocationMissingHeader(AmazonHeaders.invokedFunctionARN) + throw LambdaRuntimeError.invocationMissingHeader(AmazonHeaders.invokedFunctionARN) } self.requestID = requestID diff --git a/Sources/AWSLambdaRuntimeCore/HTTPClient.swift b/Sources/AWSLambdaRuntimeCore/HTTPClient.swift index 045cd968..7e724485 100644 --- a/Sources/AWSLambdaRuntimeCore/HTTPClient.swift +++ b/Sources/AWSLambdaRuntimeCore/HTTPClient.swift @@ -22,13 +22,13 @@ import NIOPosix /// This means we can avoid locks and other concurrency concern we would otherwise need to build into the client internal final class HTTPClient { private let eventLoop: EventLoop - private let configuration: Lambda.Configuration.RuntimeEngine + private let configuration: LambdaConfiguration.RuntimeEngine private let targetHost: String private var state = State.disconnected private var executing = false - init(eventLoop: EventLoop, configuration: Lambda.Configuration.RuntimeEngine) { + init(eventLoop: EventLoop, configuration: LambdaConfiguration.RuntimeEngine) { self.eventLoop = eventLoop self.configuration = configuration self.targetHost = "\(self.configuration.ip):\(self.configuration.port)" diff --git a/Sources/AWSLambdaRuntimeCore/Lambda+LocalServer.swift b/Sources/AWSLambdaRuntimeCore/Lambda+LocalServer.swift index 1e09d867..1f8c9e0f 100644 --- a/Sources/AWSLambdaRuntimeCore/Lambda+LocalServer.swift +++ b/Sources/AWSLambdaRuntimeCore/Lambda+LocalServer.swift @@ -35,7 +35,7 @@ extension Lambda { /// - invocationEndpoint: The endpoint to post events to. /// - body: Code to run within the context of the mock server. Typically this would be a Lambda.run function call. /// - /// - note: This API is designed stricly for local testing and is behind a DEBUG flag + /// - note: This API is designed strictly for local testing and is behind a DEBUG flag internal static func withLocalServer(invocationEndpoint: String? = nil, _ body: @escaping () -> Value) throws -> Value { let server = LocalLambda.Server(invocationEndpoint: invocationEndpoint) try server.start().wait() @@ -55,7 +55,7 @@ private enum LocalLambda { private let invocationEndpoint: String public init(invocationEndpoint: String?) { - let configuration = Lambda.Configuration() + let configuration = LambdaConfiguration() var logger = Logger(label: "LocalLambdaServer") logger.logLevel = configuration.general.logLevel self.logger = logger @@ -299,5 +299,4 @@ private enum LocalLambda { case cantBind } } - #endif diff --git a/Sources/AWSLambdaRuntimeCore/Lambda.swift b/Sources/AWSLambdaRuntimeCore/Lambda.swift index 1bf4f0dc..0f976c76 100644 --- a/Sources/AWSLambdaRuntimeCore/Lambda.swift +++ b/Sources/AWSLambdaRuntimeCore/Lambda.swift @@ -24,14 +24,6 @@ import NIOCore import NIOPosix public enum Lambda { - /// Utility to access/read environment variables - public static func env(_ name: String) -> String? { - guard let value = getenv(name) else { - return nil - } - return String(cString: value) - } - /// Run a Lambda defined by implementing the ``ByteBufferLambdaHandler`` protocol. /// The Runtime will manage the Lambdas application lifecycle automatically. It will invoke the /// ``ByteBufferLambdaHandler/makeHandler(context:)`` to create a new Handler. @@ -42,10 +34,10 @@ public enum Lambda { /// /// - note: This is a blocking operation that will run forever, as its lifecycle is managed by the AWS Lambda Runtime Engine. internal static func run( - configuration: Configuration = .init(), + configuration: LambdaConfiguration = .init(), handlerType: Handler.Type ) -> Result { - let _run = { (configuration: Configuration) -> Result in + let _run = { (configuration: LambdaConfiguration) -> Result in Backtrace.install() var logger = Logger(label: "Lambda") logger.logLevel = configuration.general.logLevel @@ -97,3 +89,15 @@ public enum Lambda { #endif } } + +// MARK: - Public API + +extension Lambda { + /// Utility to access/read environment variables + public static func env(_ name: String) -> String? { + guard let value = getenv(name) else { + return nil + } + return String(cString: value) + } +} diff --git a/Sources/AWSLambdaRuntimeCore/LambdaConfiguration.swift b/Sources/AWSLambdaRuntimeCore/LambdaConfiguration.swift index c2615a9a..33d056f8 100644 --- a/Sources/AWSLambdaRuntimeCore/LambdaConfiguration.swift +++ b/Sources/AWSLambdaRuntimeCore/LambdaConfiguration.swift @@ -16,73 +16,71 @@ import Dispatch import Logging import NIOCore -extension Lambda { - internal struct Configuration: CustomStringConvertible { - let general: General - let lifecycle: Lifecycle - let runtimeEngine: RuntimeEngine +internal struct LambdaConfiguration: CustomStringConvertible { + let general: General + let lifecycle: Lifecycle + let runtimeEngine: RuntimeEngine - init() { - self.init(general: .init(), lifecycle: .init(), runtimeEngine: .init()) - } + init() { + self.init(general: .init(), lifecycle: .init(), runtimeEngine: .init()) + } - init(general: General? = nil, lifecycle: Lifecycle? = nil, runtimeEngine: RuntimeEngine? = nil) { - self.general = general ?? General() - self.lifecycle = lifecycle ?? Lifecycle() - self.runtimeEngine = runtimeEngine ?? RuntimeEngine() - } + init(general: General? = nil, lifecycle: Lifecycle? = nil, runtimeEngine: RuntimeEngine? = nil) { + self.general = general ?? General() + self.lifecycle = lifecycle ?? Lifecycle() + self.runtimeEngine = runtimeEngine ?? RuntimeEngine() + } - struct General: CustomStringConvertible { - let logLevel: Logger.Level + struct General: CustomStringConvertible { + let logLevel: Logger.Level - init(logLevel: Logger.Level? = nil) { - self.logLevel = logLevel ?? env("LOG_LEVEL").flatMap(Logger.Level.init) ?? .info - } - - var description: String { - "\(General.self)(logLevel: \(self.logLevel))" - } + init(logLevel: Logger.Level? = nil) { + self.logLevel = logLevel ?? Lambda.env("LOG_LEVEL").flatMap(Logger.Level.init) ?? .info } - struct Lifecycle: CustomStringConvertible { - let id: String - let maxTimes: Int - let stopSignal: Signal + var description: String { + "\(General.self)(logLevel: \(self.logLevel))" + } + } - init(id: String? = nil, maxTimes: Int? = nil, stopSignal: Signal? = nil) { - self.id = id ?? "\(DispatchTime.now().uptimeNanoseconds)" - self.maxTimes = maxTimes ?? env("MAX_REQUESTS").flatMap(Int.init) ?? 0 - self.stopSignal = stopSignal ?? env("STOP_SIGNAL").flatMap(Int32.init).flatMap(Signal.init) ?? Signal.TERM - precondition(self.maxTimes >= 0, "maxTimes must be equal or larger than 0") - } + struct Lifecycle: CustomStringConvertible { + let id: String + let maxTimes: Int + let stopSignal: Signal - var description: String { - "\(Lifecycle.self)(id: \(self.id), maxTimes: \(self.maxTimes), stopSignal: \(self.stopSignal))" - } + init(id: String? = nil, maxTimes: Int? = nil, stopSignal: Signal? = nil) { + self.id = id ?? "\(DispatchTime.now().uptimeNanoseconds)" + self.maxTimes = maxTimes ?? Lambda.env("MAX_REQUESTS").flatMap(Int.init) ?? 0 + self.stopSignal = stopSignal ?? Lambda.env("STOP_SIGNAL").flatMap(Int32.init).flatMap(Signal.init) ?? Signal.TERM + precondition(self.maxTimes >= 0, "maxTimes must be equal or larger than 0") } - struct RuntimeEngine: CustomStringConvertible { - let ip: String - let port: Int - let requestTimeout: TimeAmount? + var description: String { + "\(Lifecycle.self)(id: \(self.id), maxTimes: \(self.maxTimes), stopSignal: \(self.stopSignal))" + } + } - init(address: String? = nil, keepAlive: Bool? = nil, requestTimeout: TimeAmount? = nil) { - let ipPort = (address ?? env("AWS_LAMBDA_RUNTIME_API"))?.split(separator: ":") ?? ["127.0.0.1", "7000"] - guard ipPort.count == 2, let port = Int(ipPort[1]) else { - preconditionFailure("invalid ip+port configuration \(ipPort)") - } - self.ip = String(ipPort[0]) - self.port = port - self.requestTimeout = requestTimeout ?? env("REQUEST_TIMEOUT").flatMap(Int64.init).flatMap { .milliseconds($0) } - } + struct RuntimeEngine: CustomStringConvertible { + let ip: String + let port: Int + let requestTimeout: TimeAmount? - var description: String { - "\(RuntimeEngine.self)(ip: \(self.ip), port: \(self.port), requestTimeout: \(String(describing: self.requestTimeout))" + init(address: String? = nil, keepAlive: Bool? = nil, requestTimeout: TimeAmount? = nil) { + let ipPort = (address ?? Lambda.env("AWS_LAMBDA_RUNTIME_API"))?.split(separator: ":") ?? ["127.0.0.1", "7000"] + guard ipPort.count == 2, let port = Int(ipPort[1]) else { + preconditionFailure("invalid ip+port configuration \(ipPort)") } + self.ip = String(ipPort[0]) + self.port = port + self.requestTimeout = requestTimeout ?? Lambda.env("REQUEST_TIMEOUT").flatMap(Int64.init).flatMap { .milliseconds($0) } } var description: String { - "\(Configuration.self)\n \(self.general))\n \(self.lifecycle)\n \(self.runtimeEngine)" + "\(RuntimeEngine.self)(ip: \(self.ip), port: \(self.port), requestTimeout: \(String(describing: self.requestTimeout))" } } + + var description: String { + "\(Self.self)\n \(self.general))\n \(self.lifecycle)\n \(self.runtimeEngine)" + } } diff --git a/Sources/AWSLambdaRuntimeCore/LambdaContext.swift b/Sources/AWSLambdaRuntimeCore/LambdaContext.swift index 6a0596f0..60727f65 100644 --- a/Sources/AWSLambdaRuntimeCore/LambdaContext.swift +++ b/Sources/AWSLambdaRuntimeCore/LambdaContext.swift @@ -24,48 +24,46 @@ import NIOCore // MARK: - InitializationContext -extension Lambda { - /// Lambda runtime initialization context. - /// The Lambda runtime generates and passes the `InitializationContext` to the Handlers - /// ``ByteBufferLambdaHandler/makeHandler(context:)`` or ``LambdaHandler/init(context:)`` - /// as an argument. - public struct InitializationContext: _AWSLambdaSendable { - /// `Logger` to log with - /// - /// - note: The `LogLevel` can be configured using the `LOG_LEVEL` environment variable. - public let logger: Logger - - /// The `EventLoop` the Lambda is executed on. Use this to schedule work with. - /// - /// - note: The `EventLoop` is shared with the Lambda runtime engine and should be handled with extra care. - /// Most importantly the `EventLoop` must never be blocked. - public let eventLoop: EventLoop - - /// `ByteBufferAllocator` to allocate `ByteBuffer` - public let allocator: ByteBufferAllocator - - /// `Terminator` to register shutdown operations - public let terminator: LambdaTerminator - - init(logger: Logger, eventLoop: EventLoop, allocator: ByteBufferAllocator, terminator: LambdaTerminator) { - self.eventLoop = eventLoop - self.logger = logger - self.allocator = allocator - self.terminator = terminator - } +/// Lambda runtime initialization context. +/// The Lambda runtime generates and passes the `LambdaInitializationContext` to the Handlers +/// ``ByteBufferLambdaHandler/makeHandler(context:)`` or ``LambdaHandler/init(context:)`` +/// as an argument. +public struct LambdaInitializationContext: _AWSLambdaSendable { + /// `Logger` to log with + /// + /// - note: The `LogLevel` can be configured using the `LOG_LEVEL` environment variable. + public let logger: Logger - /// This interface is not part of the public API and must not be used by adopters. This API is not part of semver versioning. - public static func __forTestsOnly( - logger: Logger, - eventLoop: EventLoop - ) -> InitializationContext { - InitializationContext( - logger: logger, - eventLoop: eventLoop, - allocator: ByteBufferAllocator(), - terminator: LambdaTerminator() - ) - } + /// The `EventLoop` the Lambda is executed on. Use this to schedule work with. + /// + /// - note: The `EventLoop` is shared with the Lambda runtime engine and should be handled with extra care. + /// Most importantly the `EventLoop` must never be blocked. + public let eventLoop: EventLoop + + /// `ByteBufferAllocator` to allocate `ByteBuffer` + public let allocator: ByteBufferAllocator + + /// `Terminator` to register shutdown operations + public let terminator: LambdaTerminator + + init(logger: Logger, eventLoop: EventLoop, allocator: ByteBufferAllocator, terminator: LambdaTerminator) { + self.eventLoop = eventLoop + self.logger = logger + self.allocator = allocator + self.terminator = terminator + } + + /// This interface is not part of the public API and must not be used by adopters. This API is not part of semver versioning. + public static func __forTestsOnly( + logger: Logger, + eventLoop: EventLoop + ) -> LambdaInitializationContext { + LambdaInitializationContext( + logger: logger, + eventLoop: eventLoop, + allocator: ByteBufferAllocator(), + terminator: LambdaTerminator() + ) } } diff --git a/Sources/AWSLambdaRuntimeCore/LambdaHandler.swift b/Sources/AWSLambdaRuntimeCore/LambdaHandler.swift index 8bb61179..98a8a496 100644 --- a/Sources/AWSLambdaRuntimeCore/LambdaHandler.swift +++ b/Sources/AWSLambdaRuntimeCore/LambdaHandler.swift @@ -33,7 +33,7 @@ public protocol LambdaHandler: EventLoopLambdaHandler { /// Examples for this can be HTTP or database clients. /// - parameters: /// - context: Runtime `InitializationContext`. - init(context: Lambda.InitializationContext) async throws + init(context: LambdaInitializationContext) async throws /// The Lambda handling method /// Concrete Lambda handlers implement this method to provide the Lambda functionality. @@ -48,7 +48,7 @@ public protocol LambdaHandler: EventLoopLambdaHandler { @available(macOS 12, iOS 15, tvOS 15, watchOS 8, *) extension LambdaHandler { - public static func makeHandler(context: Lambda.InitializationContext) -> EventLoopFuture { + public static func makeHandler(context: LambdaInitializationContext) -> EventLoopFuture { let promise = context.eventLoop.makePromise(of: Self.self) promise.completeWithTask { try await Self(context: context) @@ -181,7 +181,7 @@ public protocol ByteBufferLambdaHandler { /// connections and HTTP clients for example. It is encouraged to use the given `EventLoop`'s conformance /// to `EventLoopGroup` when initializing NIO dependencies. This will improve overall performance, as it /// minimizes thread hopping. - static func makeHandler(context: Lambda.InitializationContext) -> EventLoopFuture + static func makeHandler(context: LambdaInitializationContext) -> EventLoopFuture /// The Lambda handling method /// Concrete Lambda handlers implement this method to provide the Lambda functionality. diff --git a/Sources/AWSLambdaRuntimeCore/LambdaRunner.swift b/Sources/AWSLambdaRuntimeCore/LambdaRunner.swift index 8cb7fbe9..54e90f95 100644 --- a/Sources/AWSLambdaRuntimeCore/LambdaRunner.swift +++ b/Sources/AWSLambdaRuntimeCore/LambdaRunner.swift @@ -16,91 +16,89 @@ import Dispatch import Logging import NIOCore -extension Lambda { - /// LambdaRunner manages the Lambda runtime workflow, or business logic. - internal final class Runner { - private let runtimeClient: RuntimeClient - private let eventLoop: EventLoop - private let allocator: ByteBufferAllocator +/// LambdaRunner manages the Lambda runtime workflow, or business logic. +internal final class LambdaRunner { + private let runtimeClient: LambdaRuntimeClient + private let eventLoop: EventLoop + private let allocator: ByteBufferAllocator - private var isGettingNextInvocation = false + private var isGettingNextInvocation = false - init(eventLoop: EventLoop, configuration: Configuration) { - self.eventLoop = eventLoop - self.runtimeClient = RuntimeClient(eventLoop: self.eventLoop, configuration: configuration.runtimeEngine) - self.allocator = ByteBufferAllocator() - } + init(eventLoop: EventLoop, configuration: LambdaConfiguration) { + self.eventLoop = eventLoop + self.runtimeClient = LambdaRuntimeClient(eventLoop: self.eventLoop, configuration: configuration.runtimeEngine) + self.allocator = ByteBufferAllocator() + } + + /// Run the user provided initializer. This *must* only be called once. + /// + /// - Returns: An `EventLoopFuture` fulfilled with the outcome of the initialization. + func initialize(logger: Logger, terminator: LambdaTerminator, handlerType: Handler.Type) -> EventLoopFuture { + logger.debug("initializing lambda") + // 1. create the handler from the factory + // 2. report initialization error if one occurred + let context = LambdaInitializationContext( + logger: logger, + eventLoop: self.eventLoop, + allocator: self.allocator, + terminator: terminator + ) + return Handler.makeHandler(context: context) + // Hopping back to "our" EventLoop is important in case the factory returns a future + // that originated from a foreign EventLoop/EventLoopGroup. + // This can happen if the factory uses a library (let's say a database client) that manages its own threads/loops + // for whatever reason and returns a future that originated from that foreign EventLoop. + .hop(to: self.eventLoop) + .peekError { error in + self.runtimeClient.reportInitializationError(logger: logger, error: error).peekError { reportingError in + // We're going to bail out because the init failed, so there's not a lot we can do other than log + // that we couldn't report this error back to the runtime. + logger.error("failed reporting initialization error to lambda runtime engine: \(reportingError)") + } + } + } - /// Run the user provided initializer. This *must* only be called once. - /// - /// - Returns: An `EventLoopFuture` fulfilled with the outcome of the initialization. - func initialize(logger: Logger, terminator: LambdaTerminator, handlerType: Handler.Type) -> EventLoopFuture { - logger.debug("initializing lambda") - // 1. create the handler from the factory - // 2. report initialization error if one occurred - let context = InitializationContext( + func run(logger: Logger, handler: Handler) -> EventLoopFuture { + logger.debug("lambda invocation sequence starting") + // 1. request invocation from lambda runtime engine + self.isGettingNextInvocation = true + return self.runtimeClient.getNextInvocation(logger: logger).peekError { error in + logger.error("could not fetch work from lambda runtime engine: \(error)") + }.flatMap { invocation, bytes in + // 2. send invocation to handler + self.isGettingNextInvocation = false + let context = LambdaContext( logger: logger, eventLoop: self.eventLoop, allocator: self.allocator, - terminator: terminator + invocation: invocation ) - return Handler.makeHandler(context: context) - // Hopping back to "our" EventLoop is important in case the factory returns a future - // that originated from a foreign EventLoop/EventLoopGroup. - // This can happen if the factory uses a library (let's say a database client) that manages its own threads/loops + logger.debug("sending invocation to lambda handler \(handler)") + return handler.handle(bytes, context: context) + // Hopping back to "our" EventLoop is important in case the handler returns a future that + // originiated from a foreign EventLoop/EventLoopGroup. + // This can happen if the handler uses a library (lets say a DB client) that manages its own threads/loops // for whatever reason and returns a future that originated from that foreign EventLoop. .hop(to: self.eventLoop) - .peekError { error in - self.runtimeClient.reportInitializationError(logger: logger, error: error).peekError { reportingError in - // We're going to bail out because the init failed, so there's not a lot we can do other than log - // that we couldn't report this error back to the runtime. - logger.error("failed reporting initialization error to lambda runtime engine: \(reportingError)") + .mapResult { result in + if case .failure(let error) = result { + logger.warning("lambda handler returned an error: \(error)") } + return (invocation, result) } - } - - func run(logger: Logger, handler: Handler) -> EventLoopFuture { - logger.debug("lambda invocation sequence starting") - // 1. request invocation from lambda runtime engine - self.isGettingNextInvocation = true - return self.runtimeClient.getNextInvocation(logger: logger).peekError { error in - logger.error("could not fetch work from lambda runtime engine: \(error)") - }.flatMap { invocation, bytes in - // 2. send invocation to handler - self.isGettingNextInvocation = false - let context = LambdaContext( - logger: logger, - eventLoop: self.eventLoop, - allocator: self.allocator, - invocation: invocation - ) - logger.debug("sending invocation to lambda handler \(handler)") - return handler.handle(bytes, context: context) - // Hopping back to "our" EventLoop is important in case the handler returns a future that - // originiated from a foreign EventLoop/EventLoopGroup. - // This can happen if the handler uses a library (lets say a DB client) that manages its own threads/loops - // for whatever reason and returns a future that originated from that foreign EventLoop. - .hop(to: self.eventLoop) - .mapResult { result in - if case .failure(let error) = result { - logger.warning("lambda handler returned an error: \(error)") - } - return (invocation, result) - } - }.flatMap { invocation, result in - // 3. report results to runtime engine - self.runtimeClient.reportResults(logger: logger, invocation: invocation, result: result).peekError { error in - logger.error("could not report results to lambda runtime engine: \(error)") - } + }.flatMap { invocation, result in + // 3. report results to runtime engine + self.runtimeClient.reportResults(logger: logger, invocation: invocation, result: result).peekError { error in + logger.error("could not report results to lambda runtime engine: \(error)") } } + } - /// cancels the current run, if we are waiting for next invocation (long poll from Lambda control plane) - /// only needed for debugging purposes. - func cancelWaitingForNextInvocation() { - if self.isGettingNextInvocation { - self.runtimeClient.cancel() - } + /// cancels the current run, if we are waiting for next invocation (long poll from Lambda control plane) + /// only needed for debugging purposes. + func cancelWaitingForNextInvocation() { + if self.isGettingNextInvocation { + self.runtimeClient.cancel() } } } diff --git a/Sources/AWSLambdaRuntimeCore/LambdaRuntime.swift b/Sources/AWSLambdaRuntimeCore/LambdaRuntime.swift index 19057e14..3827039f 100644 --- a/Sources/AWSLambdaRuntimeCore/LambdaRuntime.swift +++ b/Sources/AWSLambdaRuntimeCore/LambdaRuntime.swift @@ -23,7 +23,7 @@ public final class LambdaRuntime { private let eventLoop: EventLoop private let shutdownPromise: EventLoopPromise private let logger: Logger - private let configuration: Lambda.Configuration + private let configuration: LambdaConfiguration private var state = State.idle { willSet { @@ -41,7 +41,7 @@ public final class LambdaRuntime { self.init(eventLoop: eventLoop, logger: logger, configuration: .init()) } - init(eventLoop: EventLoop, logger: Logger, configuration: Lambda.Configuration) { + init(eventLoop: EventLoop, logger: Logger, configuration: LambdaConfiguration) { self.eventLoop = eventLoop self.shutdownPromise = eventLoop.makePromise(of: Int.self) self.logger = logger @@ -82,7 +82,7 @@ public final class LambdaRuntime { var logger = self.logger logger[metadataKey: "lifecycleId"] = .string(self.configuration.lifecycle.id) let terminator = LambdaTerminator() - let runner = Lambda.Runner(eventLoop: self.eventLoop, configuration: self.configuration) + let runner = LambdaRunner(eventLoop: self.eventLoop, configuration: self.configuration) let startupFuture = runner.initialize(logger: logger, terminator: terminator, handlerType: Handler.self) startupFuture.flatMap { handler -> EventLoopFuture> in @@ -99,7 +99,7 @@ public final class LambdaRuntime { // if, we had an error shutting down the handler, we want to concatenate it with // the runner result logger.error("Error shutting down handler: \(error)") - throw Lambda.RuntimeError.shutdownError(shutdownError: error, runnerResult: runnerResult) + throw LambdaRuntimeError.shutdownError(shutdownError: error, runnerResult: runnerResult) }.flatMapResult { _ -> Result in // we had no error shutting down the lambda. let's return the runner's result runnerResult @@ -176,7 +176,7 @@ public final class LambdaRuntime { private enum State { case idle case initializing - case active(Lambda.Runner, Handler) + case active(LambdaRunner, Handler) case shuttingdown case shutdown diff --git a/Sources/AWSLambdaRuntimeCore/LambdaRuntimeClient.swift b/Sources/AWSLambdaRuntimeCore/LambdaRuntimeClient.swift index ddb946a5..29d04b9d 100644 --- a/Sources/AWSLambdaRuntimeCore/LambdaRuntimeClient.swift +++ b/Sources/AWSLambdaRuntimeCore/LambdaRuntimeClient.swift @@ -26,123 +26,119 @@ import NIOHTTP1 /// * /runtime/invocation/response /// * /runtime/invocation/error /// * /runtime/init/error -extension Lambda { - internal struct RuntimeClient { - private let eventLoop: EventLoop - private let allocator = ByteBufferAllocator() - private let httpClient: HTTPClient +internal struct LambdaRuntimeClient { + private let eventLoop: EventLoop + private let allocator = ByteBufferAllocator() + private let httpClient: HTTPClient - init(eventLoop: EventLoop, configuration: Configuration.RuntimeEngine) { - self.eventLoop = eventLoop - self.httpClient = HTTPClient(eventLoop: eventLoop, configuration: configuration) - } + init(eventLoop: EventLoop, configuration: LambdaConfiguration.RuntimeEngine) { + self.eventLoop = eventLoop + self.httpClient = HTTPClient(eventLoop: eventLoop, configuration: configuration) + } - /// Requests invocation from the control plane. - func getNextInvocation(logger: Logger) -> EventLoopFuture<(Invocation, ByteBuffer)> { - let url = Consts.invocationURLPrefix + Consts.getNextInvocationURLSuffix - logger.debug("requesting work from lambda runtime engine using \(url)") - return self.httpClient.get(url: url, headers: RuntimeClient.defaultHeaders).flatMapThrowing { response in - guard response.status == .ok else { - throw RuntimeError.badStatusCode(response.status) - } - let invocation = try Invocation(headers: response.headers) - guard let event = response.body else { - throw RuntimeError.noBody - } - return (invocation, event) - }.flatMapErrorThrowing { error in - switch error { - case HTTPClient.Errors.timeout: - throw RuntimeError.upstreamError("timeout") - case HTTPClient.Errors.connectionResetByPeer: - throw RuntimeError.upstreamError("connectionResetByPeer") - default: - throw error - } + /// Requests invocation from the control plane. + func getNextInvocation(logger: Logger) -> EventLoopFuture<(Invocation, ByteBuffer)> { + let url = Consts.invocationURLPrefix + Consts.getNextInvocationURLSuffix + logger.debug("requesting work from lambda runtime engine using \(url)") + return self.httpClient.get(url: url, headers: LambdaRuntimeClient.defaultHeaders).flatMapThrowing { response in + guard response.status == .ok else { + throw LambdaRuntimeError.badStatusCode(response.status) + } + let invocation = try Invocation(headers: response.headers) + guard let event = response.body else { + throw LambdaRuntimeError.noBody + } + return (invocation, event) + }.flatMapErrorThrowing { error in + switch error { + case HTTPClient.Errors.timeout: + throw LambdaRuntimeError.upstreamError("timeout") + case HTTPClient.Errors.connectionResetByPeer: + throw LambdaRuntimeError.upstreamError("connectionResetByPeer") + default: + throw error } } + } - /// Reports a result to the Runtime Engine. - func reportResults(logger: Logger, invocation: Invocation, result: Result) -> EventLoopFuture { - var url = Consts.invocationURLPrefix + "/" + invocation.requestID - var body: ByteBuffer? - let headers: HTTPHeaders + /// Reports a result to the Runtime Engine. + func reportResults(logger: Logger, invocation: Invocation, result: Result) -> EventLoopFuture { + var url = Consts.invocationURLPrefix + "/" + invocation.requestID + var body: ByteBuffer? + let headers: HTTPHeaders - switch result { - case .success(let buffer): - url += Consts.postResponseURLSuffix - body = buffer - headers = RuntimeClient.defaultHeaders - case .failure(let error): - url += Consts.postErrorURLSuffix - let errorResponse = ErrorResponse(errorType: Consts.functionError, errorMessage: "\(error)") - let bytes = errorResponse.toJSONBytes() - body = self.allocator.buffer(capacity: bytes.count) - body!.writeBytes(bytes) - headers = RuntimeClient.errorHeaders + switch result { + case .success(let buffer): + url += Consts.postResponseURLSuffix + body = buffer + headers = LambdaRuntimeClient.defaultHeaders + case .failure(let error): + url += Consts.postErrorURLSuffix + let errorResponse = ErrorResponse(errorType: Consts.functionError, errorMessage: "\(error)") + let bytes = errorResponse.toJSONBytes() + body = self.allocator.buffer(capacity: bytes.count) + body!.writeBytes(bytes) + headers = LambdaRuntimeClient.errorHeaders + } + logger.debug("reporting results to lambda runtime engine using \(url)") + return self.httpClient.post(url: url, headers: headers, body: body).flatMapThrowing { response in + guard response.status == .accepted else { + throw LambdaRuntimeError.badStatusCode(response.status) } - logger.debug("reporting results to lambda runtime engine using \(url)") - return self.httpClient.post(url: url, headers: headers, body: body).flatMapThrowing { response in - guard response.status == .accepted else { - throw RuntimeError.badStatusCode(response.status) - } - return () - }.flatMapErrorThrowing { error in - switch error { - case HTTPClient.Errors.timeout: - throw RuntimeError.upstreamError("timeout") - case HTTPClient.Errors.connectionResetByPeer: - throw RuntimeError.upstreamError("connectionResetByPeer") - default: - throw error - } + return () + }.flatMapErrorThrowing { error in + switch error { + case HTTPClient.Errors.timeout: + throw LambdaRuntimeError.upstreamError("timeout") + case HTTPClient.Errors.connectionResetByPeer: + throw LambdaRuntimeError.upstreamError("connectionResetByPeer") + default: + throw error } } + } - /// Reports an initialization error to the Runtime Engine. - func reportInitializationError(logger: Logger, error: Error) -> EventLoopFuture { - let url = Consts.postInitErrorURL - let errorResponse = ErrorResponse(errorType: Consts.initializationError, errorMessage: "\(error)") - let bytes = errorResponse.toJSONBytes() - var body = self.allocator.buffer(capacity: bytes.count) - body.writeBytes(bytes) - logger.warning("reporting initialization error to lambda runtime engine using \(url)") - return self.httpClient.post(url: url, headers: RuntimeClient.errorHeaders, body: body).flatMapThrowing { response in - guard response.status == .accepted else { - throw RuntimeError.badStatusCode(response.status) - } - return () - }.flatMapErrorThrowing { error in - switch error { - case HTTPClient.Errors.timeout: - throw RuntimeError.upstreamError("timeout") - case HTTPClient.Errors.connectionResetByPeer: - throw RuntimeError.upstreamError("connectionResetByPeer") - default: - throw error - } + /// Reports an initialization error to the Runtime Engine. + func reportInitializationError(logger: Logger, error: Error) -> EventLoopFuture { + let url = Consts.postInitErrorURL + let errorResponse = ErrorResponse(errorType: Consts.initializationError, errorMessage: "\(error)") + let bytes = errorResponse.toJSONBytes() + var body = self.allocator.buffer(capacity: bytes.count) + body.writeBytes(bytes) + logger.warning("reporting initialization error to lambda runtime engine using \(url)") + return self.httpClient.post(url: url, headers: LambdaRuntimeClient.errorHeaders, body: body).flatMapThrowing { response in + guard response.status == .accepted else { + throw LambdaRuntimeError.badStatusCode(response.status) + } + return () + }.flatMapErrorThrowing { error in + switch error { + case HTTPClient.Errors.timeout: + throw LambdaRuntimeError.upstreamError("timeout") + case HTTPClient.Errors.connectionResetByPeer: + throw LambdaRuntimeError.upstreamError("connectionResetByPeer") + default: + throw error } } + } - /// Cancels the current request, if one is running. Only needed for debugging purposes - func cancel() { - self.httpClient.cancel() - } + /// Cancels the current request, if one is running. Only needed for debugging purposes + func cancel() { + self.httpClient.cancel() } } -extension Lambda { - internal enum RuntimeError: Error { - case badStatusCode(HTTPResponseStatus) - case upstreamError(String) - case invocationMissingHeader(String) - case noBody - case json(Error) - case shutdownError(shutdownError: Error, runnerResult: Result) - } +internal enum LambdaRuntimeError: Error { + case badStatusCode(HTTPResponseStatus) + case upstreamError(String) + case invocationMissingHeader(String) + case noBody + case json(Error) + case shutdownError(shutdownError: Error, runnerResult: Result) } -extension Lambda.RuntimeClient { +extension LambdaRuntimeClient { internal static let defaultHeaders = HTTPHeaders([("user-agent", "Swift-Lambda/Unknown")]) /// These headers must be sent along an invocation or initialization error report diff --git a/Sources/AWSLambdaTesting/Lambda+Testing.swift b/Sources/AWSLambdaTesting/Lambda+Testing.swift index 827bf3b9..11e2bf89 100644 --- a/Sources/AWSLambdaTesting/Lambda+Testing.swift +++ b/Sources/AWSLambdaTesting/Lambda+Testing.swift @@ -72,7 +72,7 @@ extension Lambda { let eventLoop = eventLoopGroup.next() let promise = eventLoop.makePromise(of: Handler.self) - let initContext = Lambda.InitializationContext.__forTestsOnly( + let initContext = LambdaInitializationContext.__forTestsOnly( logger: logger, eventLoop: eventLoop ) diff --git a/Tests/AWSLambdaRuntimeCoreTests/LambdaHandlerTest.swift b/Tests/AWSLambdaRuntimeCoreTests/LambdaHandlerTest.swift index a41f6a57..98b49ca7 100644 --- a/Tests/AWSLambdaRuntimeCoreTests/LambdaHandlerTest.swift +++ b/Tests/AWSLambdaRuntimeCoreTests/LambdaHandlerTest.swift @@ -33,7 +33,7 @@ class LambdaHandlerTest: XCTestCase { var initialized = false - init(context: Lambda.InitializationContext) async throws { + init(context: LambdaInitializationContext) async throws { XCTAssertFalse(self.initialized) try await Task.sleep(nanoseconds: 100 * 1000 * 1000) // 0.1 seconds self.initialized = true @@ -45,7 +45,7 @@ class LambdaHandlerTest: XCTestCase { } let maxTimes = Int.random(in: 10 ... 20) - let configuration = Lambda.Configuration(lifecycle: .init(maxTimes: maxTimes)) + let configuration = LambdaConfiguration(lifecycle: .init(maxTimes: maxTimes)) let result = Lambda.run(configuration: configuration, handlerType: TestBootstrapHandler.self) assertLambdaRuntimeResult(result, shoudHaveRun: maxTimes) } @@ -62,7 +62,7 @@ class LambdaHandlerTest: XCTestCase { var initialized = false - init(context: Lambda.InitializationContext) async throws { + init(context: LambdaInitializationContext) async throws { XCTAssertFalse(self.initialized) try await Task.sleep(nanoseconds: 100 * 1000 * 1000) // 0.1 seconds throw TestError("kaboom") @@ -74,7 +74,7 @@ class LambdaHandlerTest: XCTestCase { } let maxTimes = Int.random(in: 10 ... 20) - let configuration = Lambda.Configuration(lifecycle: .init(maxTimes: maxTimes)) + let configuration = LambdaConfiguration(lifecycle: .init(maxTimes: maxTimes)) let result = Lambda.run(configuration: configuration, handlerType: TestBootstrapHandler.self) assertLambdaRuntimeResult(result, shouldFailWithError: TestError("kaboom")) } @@ -89,7 +89,7 @@ class LambdaHandlerTest: XCTestCase { typealias Event = String typealias Output = String - init(context: Lambda.InitializationContext) {} + init(context: LambdaInitializationContext) {} func handle(_ event: String, context: LambdaContext) async throws -> String { event @@ -97,7 +97,7 @@ class LambdaHandlerTest: XCTestCase { } let maxTimes = Int.random(in: 1 ... 10) - let configuration = Lambda.Configuration(lifecycle: .init(maxTimes: maxTimes)) + let configuration = LambdaConfiguration(lifecycle: .init(maxTimes: maxTimes)) let result = Lambda.run(configuration: configuration, handlerType: Handler.self) assertLambdaRuntimeResult(result, shoudHaveRun: maxTimes) } @@ -112,13 +112,13 @@ class LambdaHandlerTest: XCTestCase { typealias Event = String typealias Output = Void - init(context: Lambda.InitializationContext) {} + init(context: LambdaInitializationContext) {} func handle(_ event: String, context: LambdaContext) async throws {} } let maxTimes = Int.random(in: 1 ... 10) - let configuration = Lambda.Configuration(lifecycle: .init(maxTimes: maxTimes)) + let configuration = LambdaConfiguration(lifecycle: .init(maxTimes: maxTimes)) let result = Lambda.run(configuration: configuration, handlerType: Handler.self) assertLambdaRuntimeResult(result, shoudHaveRun: maxTimes) @@ -134,7 +134,7 @@ class LambdaHandlerTest: XCTestCase { typealias Event = String typealias Output = String - init(context: Lambda.InitializationContext) {} + init(context: LambdaInitializationContext) {} func handle(_ event: String, context: LambdaContext) async throws -> String { throw TestError("boom") @@ -142,7 +142,7 @@ class LambdaHandlerTest: XCTestCase { } let maxTimes = Int.random(in: 1 ... 10) - let configuration = Lambda.Configuration(lifecycle: .init(maxTimes: maxTimes)) + let configuration = LambdaConfiguration(lifecycle: .init(maxTimes: maxTimes)) let result = Lambda.run(configuration: configuration, handlerType: Handler.self) assertLambdaRuntimeResult(result, shoudHaveRun: maxTimes) } @@ -159,7 +159,7 @@ class LambdaHandlerTest: XCTestCase { typealias Event = String typealias Output = String - static func makeHandler(context: Lambda.InitializationContext) -> EventLoopFuture { + static func makeHandler(context: LambdaInitializationContext) -> EventLoopFuture { context.eventLoop.makeSucceededFuture(Handler()) } @@ -169,7 +169,7 @@ class LambdaHandlerTest: XCTestCase { } let maxTimes = Int.random(in: 1 ... 10) - let configuration = Lambda.Configuration(lifecycle: .init(maxTimes: maxTimes)) + let configuration = LambdaConfiguration(lifecycle: .init(maxTimes: maxTimes)) let result = Lambda.run(configuration: configuration, handlerType: Handler.self) assertLambdaRuntimeResult(result, shoudHaveRun: maxTimes) } @@ -183,7 +183,7 @@ class LambdaHandlerTest: XCTestCase { typealias Event = String typealias Output = Void - static func makeHandler(context: Lambda.InitializationContext) -> EventLoopFuture { + static func makeHandler(context: LambdaInitializationContext) -> EventLoopFuture { context.eventLoop.makeSucceededFuture(Handler()) } @@ -193,7 +193,7 @@ class LambdaHandlerTest: XCTestCase { } let maxTimes = Int.random(in: 1 ... 10) - let configuration = Lambda.Configuration(lifecycle: .init(maxTimes: maxTimes)) + let configuration = LambdaConfiguration(lifecycle: .init(maxTimes: maxTimes)) let result = Lambda.run(configuration: configuration, handlerType: Handler.self) assertLambdaRuntimeResult(result, shoudHaveRun: maxTimes) } @@ -207,7 +207,7 @@ class LambdaHandlerTest: XCTestCase { typealias Event = String typealias Output = String - static func makeHandler(context: Lambda.InitializationContext) -> EventLoopFuture { + static func makeHandler(context: LambdaInitializationContext) -> EventLoopFuture { context.eventLoop.makeSucceededFuture(Handler()) } @@ -217,7 +217,7 @@ class LambdaHandlerTest: XCTestCase { } let maxTimes = Int.random(in: 1 ... 10) - let configuration = Lambda.Configuration(lifecycle: .init(maxTimes: maxTimes)) + let configuration = LambdaConfiguration(lifecycle: .init(maxTimes: maxTimes)) let result = Lambda.run(configuration: configuration, handlerType: Handler.self) assertLambdaRuntimeResult(result, shoudHaveRun: maxTimes) } @@ -231,7 +231,7 @@ class LambdaHandlerTest: XCTestCase { typealias Event = String typealias Output = String - static func makeHandler(context: Lambda.InitializationContext) -> EventLoopFuture { + static func makeHandler(context: LambdaInitializationContext) -> EventLoopFuture { context.eventLoop.makeFailedFuture(TestError("kaboom")) } diff --git a/Tests/AWSLambdaRuntimeCoreTests/LambdaHandlers.swift b/Tests/AWSLambdaRuntimeCoreTests/LambdaHandlers.swift index 41cdcf8c..c2f3fc9e 100644 --- a/Tests/AWSLambdaRuntimeCoreTests/LambdaHandlers.swift +++ b/Tests/AWSLambdaRuntimeCoreTests/LambdaHandlers.swift @@ -20,7 +20,7 @@ struct EchoHandler: EventLoopLambdaHandler { typealias Event = String typealias Output = String - static func makeHandler(context: Lambda.InitializationContext) -> EventLoopFuture { + static func makeHandler(context: LambdaInitializationContext) -> EventLoopFuture { context.eventLoop.makeSucceededFuture(EchoHandler()) } @@ -35,7 +35,7 @@ struct StartupErrorHandler: EventLoopLambdaHandler { typealias Event = String typealias Output = String - static func makeHandler(context: Lambda.InitializationContext) -> EventLoopFuture { + static func makeHandler(context: LambdaInitializationContext) -> EventLoopFuture { context.eventLoop.makeFailedFuture(StartupError()) } @@ -51,7 +51,7 @@ struct RuntimeErrorHandler: EventLoopLambdaHandler { typealias Event = String typealias Output = Void - static func makeHandler(context: Lambda.InitializationContext) -> EventLoopFuture { + static func makeHandler(context: LambdaInitializationContext) -> EventLoopFuture { context.eventLoop.makeSucceededFuture(RuntimeErrorHandler()) } diff --git a/Tests/AWSLambdaRuntimeCoreTests/LambdaRuntimeClientTest.swift b/Tests/AWSLambdaRuntimeCoreTests/LambdaRuntimeClientTest.swift index 2130d0e2..83e18c2e 100644 --- a/Tests/AWSLambdaRuntimeCoreTests/LambdaRuntimeClientTest.swift +++ b/Tests/AWSLambdaRuntimeCoreTests/LambdaRuntimeClientTest.swift @@ -64,7 +64,7 @@ class LambdaRuntimeClientTest: XCTestCase { } } XCTAssertThrowsError(try runLambda(behavior: Behavior(), handlerType: EchoHandler.self)) { - XCTAssertEqual($0 as? Lambda.RuntimeError, .badStatusCode(.internalServerError)) + XCTAssertEqual($0 as? LambdaRuntimeError, .badStatusCode(.internalServerError)) } } @@ -90,7 +90,7 @@ class LambdaRuntimeClientTest: XCTestCase { } } XCTAssertThrowsError(try runLambda(behavior: Behavior(), handlerType: EchoHandler.self)) { - XCTAssertEqual($0 as? Lambda.RuntimeError, .noBody) + XCTAssertEqual($0 as? LambdaRuntimeError, .noBody) } } @@ -117,7 +117,7 @@ class LambdaRuntimeClientTest: XCTestCase { } } XCTAssertThrowsError(try runLambda(behavior: Behavior(), handlerType: EchoHandler.self)) { - XCTAssertEqual($0 as? Lambda.RuntimeError, .invocationMissingHeader(AmazonHeaders.requestID)) + XCTAssertEqual($0 as? LambdaRuntimeError, .invocationMissingHeader(AmazonHeaders.requestID)) } } @@ -142,7 +142,7 @@ class LambdaRuntimeClientTest: XCTestCase { } } XCTAssertThrowsError(try runLambda(behavior: Behavior(), handlerType: EchoHandler.self)) { - XCTAssertEqual($0 as? Lambda.RuntimeError, .badStatusCode(.internalServerError)) + XCTAssertEqual($0 as? LambdaRuntimeError, .badStatusCode(.internalServerError)) } } @@ -167,7 +167,7 @@ class LambdaRuntimeClientTest: XCTestCase { } } XCTAssertThrowsError(try runLambda(behavior: Behavior(), handlerType: RuntimeErrorHandler.self)) { - XCTAssertEqual($0 as? Lambda.RuntimeError, .badStatusCode(.internalServerError)) + XCTAssertEqual($0 as? LambdaRuntimeError, .badStatusCode(.internalServerError)) } } @@ -223,7 +223,7 @@ class LambdaRuntimeClientTest: XCTestCase { defer { XCTAssertNoThrow(try server.stop()) } let logger = Logger(label: "TestLogger") - let client = Lambda.RuntimeClient(eventLoop: eventLoopGroup.next(), configuration: .init(address: "127.0.0.1:\(server.serverPort)")) + let client = LambdaRuntimeClient(eventLoop: eventLoopGroup.next(), configuration: .init(address: "127.0.0.1:\(server.serverPort)")) let result = client.reportInitializationError(logger: logger, error: TestError("boom")) var inboundHeader: HTTPServerRequestPart? @@ -252,7 +252,7 @@ class LambdaRuntimeClientTest: XCTestCase { defer { XCTAssertNoThrow(try server.stop()) } let logger = Logger(label: "TestLogger") - let client = Lambda.RuntimeClient(eventLoop: eventLoopGroup.next(), configuration: .init(address: "127.0.0.1:\(server.serverPort)")) + let client = LambdaRuntimeClient(eventLoop: eventLoopGroup.next(), configuration: .init(address: "127.0.0.1:\(server.serverPort)")) let header = HTTPHeaders([ (AmazonHeaders.requestID, "test"), @@ -292,7 +292,7 @@ class LambdaRuntimeClientTest: XCTestCase { defer { XCTAssertNoThrow(try server.stop()) } let logger = Logger(label: "TestLogger") - let client = Lambda.RuntimeClient(eventLoop: eventLoopGroup.next(), configuration: .init(address: "127.0.0.1:\(server.serverPort)")) + let client = LambdaRuntimeClient(eventLoop: eventLoopGroup.next(), configuration: .init(address: "127.0.0.1:\(server.serverPort)")) let header = HTTPHeaders([ (AmazonHeaders.requestID, "test"), diff --git a/Tests/AWSLambdaRuntimeCoreTests/LambdaRuntimeTest.swift b/Tests/AWSLambdaRuntimeCoreTests/LambdaRuntimeTest.swift index fba1dde7..11aac73e 100644 --- a/Tests/AWSLambdaRuntimeCoreTests/LambdaRuntimeTest.swift +++ b/Tests/AWSLambdaRuntimeCoreTests/LambdaRuntimeTest.swift @@ -55,7 +55,7 @@ class LambdaRuntimeTest: XCTestCase { XCTAssertNoThrow(_ = try eventLoop.flatSubmit { runtime.start() }.wait()) XCTAssertThrowsError(_ = try runtime.shutdownFuture.wait()) { - XCTAssertEqual(.badStatusCode(HTTPResponseStatus.internalServerError), $0 as? Lambda.RuntimeError) + XCTAssertEqual(.badStatusCode(HTTPResponseStatus.internalServerError), $0 as? LambdaRuntimeError) } } @@ -74,7 +74,7 @@ class LambdaRuntimeTest: XCTestCase { typealias Event = String typealias Output = Void - static func makeHandler(context: Lambda.InitializationContext) -> EventLoopFuture { + static func makeHandler(context: LambdaInitializationContext) -> EventLoopFuture { // register shutdown operation context.terminator.register(name: "test 1", handler: { eventLoop in eventLoop.makeFailedFuture(ShutdownError(description: "error 1")) @@ -105,7 +105,7 @@ class LambdaRuntimeTest: XCTestCase { XCTAssertNoThrow(try eventLoop.flatSubmit { runtime.start() }.wait()) XCTAssertThrowsError(try runtime.shutdownFuture.wait()) { error in - guard case Lambda.RuntimeError.shutdownError(let shutdownError, .failure(let runtimeError)) = error else { + guard case LambdaRuntimeError.shutdownError(let shutdownError, .failure(let runtimeError)) = error else { XCTFail("Unexpected error: \(error)"); return } @@ -114,7 +114,7 @@ class LambdaRuntimeTest: XCTestCase { ShutdownError(description: "error 2"), ShutdownError(description: "error 1"), ])) - XCTAssertEqual(runtimeError as? Lambda.RuntimeError, .badStatusCode(.internalServerError)) + XCTAssertEqual(runtimeError as? LambdaRuntimeError, .badStatusCode(.internalServerError)) } } } diff --git a/Tests/AWSLambdaRuntimeCoreTests/LambdaTest.swift b/Tests/AWSLambdaRuntimeCoreTests/LambdaTest.swift index a5fd7daf..1cf6aa1a 100644 --- a/Tests/AWSLambdaRuntimeCoreTests/LambdaTest.swift +++ b/Tests/AWSLambdaRuntimeCoreTests/LambdaTest.swift @@ -30,7 +30,7 @@ class LambdaTest: XCTestCase { defer { XCTAssertNoThrow(try server.stop().wait()) } let maxTimes = Int.random(in: 10 ... 20) - let configuration = Lambda.Configuration(lifecycle: .init(maxTimes: maxTimes)) + let configuration = LambdaConfiguration(lifecycle: .init(maxTimes: maxTimes)) let result = Lambda.run(configuration: configuration, handlerType: EchoHandler.self) assertLambdaRuntimeResult(result, shoudHaveRun: maxTimes) } @@ -41,7 +41,7 @@ class LambdaTest: XCTestCase { defer { XCTAssertNoThrow(try server.stop().wait()) } let maxTimes = Int.random(in: 10 ... 20) - let configuration = Lambda.Configuration(lifecycle: .init(maxTimes: maxTimes)) + let configuration = LambdaConfiguration(lifecycle: .init(maxTimes: maxTimes)) let result = Lambda.run(configuration: configuration, handlerType: RuntimeErrorHandler.self) assertLambdaRuntimeResult(result, shoudHaveRun: maxTimes) } @@ -92,7 +92,7 @@ class LambdaTest: XCTestCase { let signal = Signal.ALRM let maxTimes = 1000 - let configuration = Lambda.Configuration(lifecycle: .init(maxTimes: maxTimes, stopSignal: signal)) + let configuration = LambdaConfiguration(lifecycle: .init(maxTimes: maxTimes, stopSignal: signal)) DispatchQueue(label: "test").async { // we need to schedule the signal before we start the long running `Lambda.run`, since @@ -117,10 +117,10 @@ class LambdaTest: XCTestCase { XCTAssertNoThrow(try server.start().wait()) defer { XCTAssertNoThrow(try server.stop().wait()) } - let configuration = Lambda.Configuration(lifecycle: .init(maxTimes: 1), - runtimeEngine: .init(requestTimeout: .milliseconds(timeout))) + let configuration = LambdaConfiguration(lifecycle: .init(maxTimes: 1), + runtimeEngine: .init(requestTimeout: .milliseconds(timeout))) let result = Lambda.run(configuration: configuration, handlerType: EchoHandler.self) - assertLambdaRuntimeResult(result, shouldFailWithError: Lambda.RuntimeError.upstreamError("timeout")) + assertLambdaRuntimeResult(result, shouldFailWithError: LambdaRuntimeError.upstreamError("timeout")) } func testDisconnect() { @@ -128,9 +128,9 @@ class LambdaTest: XCTestCase { XCTAssertNoThrow(try server.start().wait()) defer { XCTAssertNoThrow(try server.stop().wait()) } - let configuration = Lambda.Configuration(lifecycle: .init(maxTimes: 1)) + let configuration = LambdaConfiguration(lifecycle: .init(maxTimes: 1)) let result = Lambda.run(configuration: configuration, handlerType: EchoHandler.self) - assertLambdaRuntimeResult(result, shouldFailWithError: Lambda.RuntimeError.upstreamError("connectionResetByPeer")) + assertLambdaRuntimeResult(result, shouldFailWithError: LambdaRuntimeError.upstreamError("connectionResetByPeer")) } func testBigEvent() { @@ -139,7 +139,7 @@ class LambdaTest: XCTestCase { XCTAssertNoThrow(try server.start().wait()) defer { XCTAssertNoThrow(try server.stop().wait()) } - let configuration = Lambda.Configuration(lifecycle: .init(maxTimes: 1)) + let configuration = LambdaConfiguration(lifecycle: .init(maxTimes: 1)) let result = Lambda.run(configuration: configuration, handlerType: EchoHandler.self) assertLambdaRuntimeResult(result, shoudHaveRun: 1) } @@ -150,7 +150,7 @@ class LambdaTest: XCTestCase { defer { XCTAssertNoThrow(try server.stop().wait()) } let maxTimes = 10 - let configuration = Lambda.Configuration(lifecycle: .init(maxTimes: maxTimes)) + let configuration = LambdaConfiguration(lifecycle: .init(maxTimes: maxTimes)) let result = Lambda.run(configuration: configuration, handlerType: EchoHandler.self) assertLambdaRuntimeResult(result, shoudHaveRun: maxTimes) } @@ -161,7 +161,7 @@ class LambdaTest: XCTestCase { defer { XCTAssertNoThrow(try server.stop().wait()) } let maxTimes = 10 - let configuration = Lambda.Configuration(lifecycle: .init(maxTimes: maxTimes)) + let configuration = LambdaConfiguration(lifecycle: .init(maxTimes: maxTimes)) let result = Lambda.run(configuration: configuration, handlerType: EchoHandler.self) assertLambdaRuntimeResult(result, shoudHaveRun: maxTimes) } @@ -191,7 +191,7 @@ class LambdaTest: XCTestCase { } let result = Lambda.run(configuration: .init(), handlerType: EchoHandler.self) - assertLambdaRuntimeResult(result, shouldFailWithError: Lambda.RuntimeError.badStatusCode(.internalServerError)) + assertLambdaRuntimeResult(result, shouldFailWithError: LambdaRuntimeError.badStatusCode(.internalServerError)) } func testDeadline() { @@ -262,7 +262,7 @@ class LambdaTest: XCTestCase { typealias Event = String typealias Output = String - static func makeHandler(context: Lambda.InitializationContext) -> EventLoopFuture { + static func makeHandler(context: LambdaInitializationContext) -> EventLoopFuture { context.eventLoop.makeSucceededFuture(Handler()) } @@ -278,13 +278,13 @@ class LambdaTest: XCTestCase { defer { XCTAssertNoThrow(try server.stop().wait()) } let logger = Logger(label: "TestLogger") - let configuration = Lambda.Configuration(runtimeEngine: .init(requestTimeout: .milliseconds(100))) + let configuration = LambdaConfiguration(runtimeEngine: .init(requestTimeout: .milliseconds(100))) let handler1 = Handler() let task = Task.detached { print(configuration.description) logger.info("hello") - let runner = Lambda.Runner(eventLoop: eventLoopGroup.next(), configuration: configuration) + let runner = LambdaRunner(eventLoop: eventLoopGroup.next(), configuration: configuration) try runner.run(logger: logger, handler: handler1).wait() diff --git a/Tests/AWSLambdaRuntimeCoreTests/Utils.swift b/Tests/AWSLambdaRuntimeCoreTests/Utils.swift index 5d57a555..49cd7708 100644 --- a/Tests/AWSLambdaRuntimeCoreTests/Utils.swift +++ b/Tests/AWSLambdaRuntimeCoreTests/Utils.swift @@ -22,9 +22,9 @@ func runLambda(behavior: LambdaServerBehavior, let eventLoopGroup = MultiThreadedEventLoopGroup(numberOfThreads: 1) defer { XCTAssertNoThrow(try eventLoopGroup.syncShutdownGracefully()) } let logger = Logger(label: "TestLogger") - let configuration = Lambda.Configuration(runtimeEngine: .init(requestTimeout: .milliseconds(100))) + let configuration = LambdaConfiguration(runtimeEngine: .init(requestTimeout: .milliseconds(100))) let terminator = LambdaTerminator() - let runner = Lambda.Runner(eventLoop: eventLoopGroup.next(), configuration: configuration) + let runner = LambdaRunner(eventLoop: eventLoopGroup.next(), configuration: configuration) let server = try MockLambdaServer(behavior: behavior).start().wait() defer { XCTAssertNoThrow(try server.stop().wait()) } try runner.initialize(logger: logger, terminator: terminator, handlerType: handlerType).flatMap { handler in @@ -61,8 +61,8 @@ extension Date { } } -extension Lambda.RuntimeError: Equatable { - public static func == (lhs: Lambda.RuntimeError, rhs: Lambda.RuntimeError) -> Bool { +extension LambdaRuntimeError: Equatable { + public static func == (lhs: Self, rhs: Self) -> Bool { // technically incorrect, but good enough for our tests String(describing: lhs) == String(describing: rhs) } diff --git a/Tests/AWSLambdaRuntimeTests/Lambda+CodableTest.swift b/Tests/AWSLambdaRuntimeTests/Lambda+CodableTest.swift index b0f2152d..eceaa2d8 100644 --- a/Tests/AWSLambdaRuntimeTests/Lambda+CodableTest.swift +++ b/Tests/AWSLambdaRuntimeTests/Lambda+CodableTest.swift @@ -43,7 +43,7 @@ class CodableLambdaTest: XCTestCase { var expected: Request? - static func makeHandler(context: Lambda.InitializationContext) -> EventLoopFuture { + static func makeHandler(context: LambdaInitializationContext) -> EventLoopFuture { context.eventLoop.makeSucceededFuture(Handler()) } @@ -72,7 +72,7 @@ class CodableLambdaTest: XCTestCase { var expected: Request? - static func makeHandler(context: Lambda.InitializationContext) -> EventLoopFuture { + static func makeHandler(context: LambdaInitializationContext) -> EventLoopFuture { context.eventLoop.makeSucceededFuture(Handler()) } @@ -99,7 +99,7 @@ class CodableLambdaTest: XCTestCase { var expected: Request? - init(context: Lambda.InitializationContext) async throws {} + init(context: LambdaInitializationContext) async throws {} func handle(_ event: Request, context: LambdaContext) async throws { XCTAssertEqual(event, self.expected) @@ -128,7 +128,7 @@ class CodableLambdaTest: XCTestCase { var expected: Request? - init(context: Lambda.InitializationContext) async throws {} + init(context: LambdaInitializationContext) async throws {} func handle(_ event: Request, context: LambdaContext) async throws -> Response { XCTAssertEqual(event, self.expected) @@ -168,8 +168,8 @@ class CodableLambdaTest: XCTestCase { ) } - func newInitContext() -> Lambda.InitializationContext { - Lambda.InitializationContext( + func newInitContext() -> LambdaInitializationContext { + LambdaInitializationContext( logger: Logger(label: "test"), eventLoop: self.eventLoopGroup.next(), allocator: ByteBufferAllocator(), diff --git a/Tests/AWSLambdaTestingTests/Tests.swift b/Tests/AWSLambdaTestingTests/Tests.swift index 5801f605..f4520aa2 100644 --- a/Tests/AWSLambdaTestingTests/Tests.swift +++ b/Tests/AWSLambdaTestingTests/Tests.swift @@ -33,7 +33,7 @@ class LambdaTestingTests: XCTestCase { typealias Event = Request typealias Output = Response - init(context: Lambda.InitializationContext) {} + init(context: LambdaInitializationContext) {} func handle(_ event: Request, context: LambdaContext) async throws -> Response { Response(message: "echo" + event.name) @@ -57,7 +57,7 @@ class LambdaTestingTests: XCTestCase { typealias Event = Request typealias Output = Void - init(context: Lambda.InitializationContext) {} + init(context: LambdaInitializationContext) {} func handle(_ event: Request, context: LambdaContext) async throws { LambdaTestingTests.VoidLambdaHandlerInvokeCount += 1 @@ -77,7 +77,7 @@ class LambdaTestingTests: XCTestCase { typealias Event = String typealias Output = Void - init(context: Lambda.InitializationContext) {} + init(context: LambdaInitializationContext) {} func handle(_ event: String, context: LambdaContext) async throws { throw MyError() @@ -94,7 +94,7 @@ class LambdaTestingTests: XCTestCase { typealias Event = String typealias Output = String - init(context: Lambda.InitializationContext) {} + init(context: LambdaInitializationContext) {} func handle(_ event: String, context: LambdaContext) async throws -> String { try await Task.sleep(nanoseconds: 500 * 1000 * 1000)