Skip to content

Commit e2ac820

Browse files
authored
add an option to start the local debugging server based on an env variable (#87)
motivation: make using the local debugging server easier to turn off/on without the need to change code when oyu are preparing to deploy changes: * add code to lambda so that in debug mode only, if the LOCAL_LAMBDA_SERVER_ENABLED env variable is set the local debugging server is started * make withLocalServer internal * update example code
1 parent cc345ba commit e2ac820

File tree

4 files changed

+61
-34
lines changed

4 files changed

+61
-34
lines changed

Examples/LocalDebugging/MyLambda/Sources/MyLambda/main.swift

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -15,9 +15,9 @@
1515
import AWSLambdaRuntime
1616
import Shared
1717

18-
try Lambda.withLocalServer {
19-
Lambda.run { (_: Lambda.Context, request: Request, callback: @escaping (Result<Response, Error>) -> Void) in
20-
// TODO: something useful
21-
callback(.success(Response(message: "Hello, \(request.name)!")))
22-
}
18+
// set LOCAL_LAMBDA_SERVER_ENABLED env variable to "true" to start
19+
// a local server simulator which will allow local debugging
20+
Lambda.run { (_, request: Request, callback: @escaping (Result<Response, Error>) -> Void) in
21+
// TODO: something useful
22+
callback(.success(Response(message: "Hello, \(request.name)!")))
2323
}

Examples/LocalDebugging/README.md

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -11,18 +11,25 @@ The example includes three modules:
1111
3. [Shared](Shared) is a SwiftPM library package used for shared code between the iOS application and the Lambda function,
1212
such as the Request and Response model objects.
1313

14-
The local debugging experience is achieved by running the Lambda function in the context of the debug only `Lambda.withLocalServer`
15-
function which starts a local emulator enabling the communication
14+
The local debugging experience is achieved by running the Lambda function in the context of the
15+
debug-only local lambda engine simulator which starts a local HTTP server enabling the communication
1616
between the iOS application and the Lambda function over HTTP.
1717

1818
To try out this example, open the workspace in Xcode and "run" the two targets,
1919
using the relevant `MyLambda` and `MyApp` Xcode schemas.
2020

21-
Start with running the `MyLambda` target on the "My Mac" destination, once it is up you should see a log message in the Xcode console saying
21+
Start with running the `MyLambda` target.
22+
* Switch to the `MyApp` scheme and select the "My Mac" destination
23+
* Set the `LOCAL_LAMBDA_SERVER_ENABLED` environment variable to `true` by editing the `MyLambda` scheme under `Run`.
24+
* Hit `Run`
25+
* Once it is up you should see a log message in the Xcode console saying
2226
`LocalLambdaServer started and listening on 127.0.0.1:7000, receiving payloads on /invoke`
2327
which means the local emulator is up and receiving traffic on port `7000` and expecting payloads on the `/invoke` endpoint.
2428

25-
Continue to run the `MyApp` target in a simulator destination. Once up, the application's UI should appear in the simulator allowing you
29+
Continue to run the `MyApp` target
30+
* Switch to the `MyApp` scheme and select a simulator destination.
31+
* Hit `Run`
32+
* Once up, the application's UI should appear in the simulator allowing you
2633
to interact with it.
2734

2835
Once both targets are running, set up breakpoints in the iOS application or Lambda function to observe the system behavior.

Sources/AWSLambdaRuntimeCore/Lambda+LocalServer.swift

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -35,11 +35,12 @@ extension Lambda {
3535
/// - body: Code to run within the context of the mock server. Typically this would be a Lambda.run function call.
3636
///
3737
/// - note: This API is designed stricly for local testing and is behind a DEBUG flag
38-
public static func withLocalServer(invocationEndpoint: String? = nil, _ body: @escaping () -> Void) throws {
38+
@discardableResult
39+
static func withLocalServer<Value>(invocationEndpoint: String? = nil, _ body: @escaping () -> Value) throws -> Value {
3940
let server = LocalLambda.Server(invocationEndpoint: invocationEndpoint)
4041
try server.start().wait()
4142
defer { try! server.stop() } // FIXME:
42-
body()
43+
return body()
4344
}
4445
}
4546

Sources/AWSLambdaRuntimeCore/Lambda.swift

Lines changed: 42 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -97,36 +97,55 @@ public enum Lambda {
9797
// for testing and internal use
9898
@discardableResult
9999
internal static func run(configuration: Configuration = .init(), factory: @escaping HandlerFactory) -> Result<Int, Error> {
100-
Backtrace.install()
101-
var logger = Logger(label: "Lambda")
102-
logger.logLevel = configuration.general.logLevel
100+
let _run = { (configuration: Configuration, factory: @escaping HandlerFactory) -> Result<Int, Error> in
101+
Backtrace.install()
102+
var logger = Logger(label: "Lambda")
103+
logger.logLevel = configuration.general.logLevel
103104

104-
var result: Result<Int, Error>!
105-
MultiThreadedEventLoopGroup.withCurrentThreadAsEventLoop { eventLoop in
106-
let lifecycle = Lifecycle(eventLoop: eventLoop, logger: logger, configuration: configuration, factory: factory)
107-
#if DEBUG
108-
let signalSource = trap(signal: configuration.lifecycle.stopSignal) { signal in
109-
logger.info("intercepted signal: \(signal)")
110-
lifecycle.shutdown()
111-
}
112-
#endif
113-
114-
lifecycle.start().flatMap {
115-
lifecycle.shutdownFuture
116-
}.whenComplete { lifecycleResult in
105+
var result: Result<Int, Error>!
106+
MultiThreadedEventLoopGroup.withCurrentThreadAsEventLoop { eventLoop in
107+
let lifecycle = Lifecycle(eventLoop: eventLoop, logger: logger, configuration: configuration, factory: factory)
117108
#if DEBUG
118-
signalSource.cancel()
109+
let signalSource = trap(signal: configuration.lifecycle.stopSignal) { signal in
110+
logger.info("intercepted signal: \(signal)")
111+
lifecycle.shutdown()
112+
}
119113
#endif
120-
eventLoop.shutdownGracefully { error in
121-
if let error = error {
122-
preconditionFailure("Failed to shutdown eventloop: \(error)")
114+
115+
lifecycle.start().flatMap {
116+
lifecycle.shutdownFuture
117+
}.whenComplete { lifecycleResult in
118+
#if DEBUG
119+
signalSource.cancel()
120+
#endif
121+
eventLoop.shutdownGracefully { error in
122+
if let error = error {
123+
preconditionFailure("Failed to shutdown eventloop: \(error)")
124+
}
123125
}
126+
result = lifecycleResult
124127
}
125-
result = lifecycleResult
126128
}
129+
130+
logger.info("shutdown completed")
131+
return result
127132
}
128133

129-
logger.info("shutdown completed")
130-
return result
134+
// start local server for debugging in DEBUG mode only
135+
#if DEBUG
136+
if Lambda.env("LOCAL_LAMBDA_SERVER_ENABLED").flatMap(Bool.init) ?? false {
137+
do {
138+
return try Lambda.withLocalServer {
139+
_run(configuration, factory)
140+
}
141+
} catch {
142+
return .failure(error)
143+
}
144+
} else {
145+
return _run(configuration, factory)
146+
}
147+
#else
148+
return _run(configuration, factory)
149+
#endif
131150
}
132151
}

0 commit comments

Comments
 (0)