Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion Examples/Benchmark/BenchmarkHandler.swift
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ struct BenchmarkHandler: EventLoopLambdaHandler {
typealias Event = String
typealias Output = String

static func makeHandler(context: Lambda.InitializationContext) -> EventLoopFuture<Self> {
static func makeHandler(context: LambdaInitializationContext) -> EventLoopFuture<Self> {
context.eventLoop.makeSucceededFuture(BenchmarkHandler())
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ struct BenchmarkHandler: EventLoopLambdaHandler {
typealias Event = String
typealias Output = String

static func makeHandler(context: Lambda.InitializationContext) -> EventLoopFuture<Self> {
static func makeHandler(context: LambdaInitializationContext) -> EventLoopFuture<Self> {
context.eventLoop.makeSucceededFuture(BenchmarkHandler())
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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.
}

Expand Down
2 changes: 1 addition & 1 deletion Examples/Echo/Lambda.swift
Original file line number Diff line number Diff line change
Expand Up @@ -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.
}

Expand Down
2 changes: 1 addition & 1 deletion Examples/ErrorHandling/Lambda.swift
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
2 changes: 1 addition & 1 deletion Examples/Foundation/Lambda.swift
Original file line number Diff line number Diff line change
Expand Up @@ -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()
}
Expand Down
2 changes: 1 addition & 1 deletion Examples/JSON/Lambda.swift
Original file line number Diff line number Diff line change
Expand Up @@ -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.
}

Expand Down
2 changes: 1 addition & 1 deletion Examples/LocalDebugging/MyLambda/Lambda.swift
Original file line number Diff line number Diff line change
Expand Up @@ -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.
}

Expand Down
2 changes: 1 addition & 1 deletion Examples/Testing/Sources/Lambda.swift
Original file line number Diff line number Diff line change
Expand Up @@ -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.
}

Expand Down
6 changes: 3 additions & 3 deletions Sources/AWSLambdaRuntimeCore/ControlPlaneRequest.swift
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
4 changes: 2 additions & 2 deletions Sources/AWSLambdaRuntimeCore/HTTPClient.swift
Original file line number Diff line number Diff line change
Expand Up @@ -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)"
Expand Down
5 changes: 2 additions & 3 deletions Sources/AWSLambdaRuntimeCore/Lambda+LocalServer.swift
Original file line number Diff line number Diff line change
Expand Up @@ -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<Value>(invocationEndpoint: String? = nil, _ body: @escaping () -> Value) throws -> Value {
let server = LocalLambda.Server(invocationEndpoint: invocationEndpoint)
try server.start().wait()
Expand All @@ -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
Expand Down Expand Up @@ -299,5 +299,4 @@ private enum LocalLambda {
case cantBind
}
}

#endif
24 changes: 14 additions & 10 deletions Sources/AWSLambdaRuntimeCore/Lambda.swift
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand All @@ -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<Handler: ByteBufferLambdaHandler>(
configuration: Configuration = .init(),
configuration: LambdaConfiguration = .init(),
handlerType: Handler.Type
) -> Result<Int, Error> {
let _run = { (configuration: Configuration) -> Result<Int, Error> in
let _run = { (configuration: LambdaConfiguration) -> Result<Int, Error> in
Backtrace.install()
var logger = Logger(label: "Lambda")
logger.logLevel = configuration.general.logLevel
Expand Down Expand Up @@ -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)
}
}
100 changes: 49 additions & 51 deletions Sources/AWSLambdaRuntimeCore/LambdaConfiguration.swift
Original file line number Diff line number Diff line change
Expand Up @@ -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)"
}
}
80 changes: 39 additions & 41 deletions Sources/AWSLambdaRuntimeCore/LambdaContext.swift
Original file line number Diff line number Diff line change
Expand Up @@ -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()
)
}
}

Expand Down
Loading