Skip to content

prefix data structures with Lambda instead of namespacing them #256

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
Apr 15, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
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