Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
2db6f9a
remove unused script
sebsto Jan 4, 2026
e01ef93
refcator files on Lambda runtime
sebsto Jan 4, 2026
2766f3f
add support for LLambda Managed Instances
sebsto Jan 4, 2026
a9bc954
add examples
sebsto Jan 4, 2026
4610626
Merge branch 'main' into sebsto/lambda-managed-instances-v2
sebsto Jan 4, 2026
79408e7
swift format
sebsto Jan 4, 2026
0f93212
cleanup
sebsto Jan 4, 2026
ec514de
fix yaml lint
sebsto Jan 4, 2026
e6ceca8
fix missing license header
sebsto Jan 4, 2026
e3d37e8
add Managed Instances to the integration tests
sebsto Jan 4, 2026
c435933
add some tests for LambdaManagedRuntime
sebsto Jan 4, 2026
8ea2da5
Update Examples/ManagedInstances/Sources/Streaming/main.swift
sebsto Jan 4, 2026
32d62a1
fix typos
sebsto Jan 4, 2026
08032de
add lambda managed instances section in the readme
sebsto Jan 4, 2026
80fe7c7
Resolve merge conflicts
Jan 13, 2026
d13cac0
rever github action
Jan 13, 2026
22fd03c
Merge branch 'main' into sebsto/lambda-managed-instances-v2
Jan 13, 2026
4091d2b
Merge branch 'main' into sebsto/lambda-managed-instances-v2
Jan 13, 2026
a6c217e
revert line breaks on LambdaRuntime+JSON
Jan 13, 2026
2459313
remove convenience code
Jan 13, 2026
cf76f83
Remove Sendable on VoidEncoder
Jan 13, 2026
c69c103
make static lambda context for testing, public
Jan 13, 2026
51caf80
add convenience methods
Jan 13, 2026
d6404fc
add example
Jan 13, 2026
e2e1c49
re-add unit tests and integration test
Jan 13, 2026
5ecb0d6
Merge branch 'main' into sebsto/lambda-managed-instances-v2
sebsto Jan 27, 2026
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 .github/workflows/pull_request.yml
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ jobs:
# We pass the list of examples here, but we can't pass an array as argument
# Instead, we pass a String with a valid JSON array.
# The workaround is mentioned here https://github.com/orgs/community/discussions/11692
examples: "[ 'APIGatewayV1', 'APIGatewayV2', 'APIGatewayV2+LambdaAuthorizer', 'BackgroundTasks', 'HelloJSON', 'HelloWorld', 'HelloWorldNoTraits', 'HummingbirdLambda', 'MultiSourceAPI', 'MultiTenant', 'ResourcesPackaging', 'S3EventNotifier', 'S3_AWSSDK', 'S3_Soto', 'Streaming+APIGateway', 'Streaming+FunctionUrl', 'Streaming+Codable', 'ServiceLifecycle+Postgres', 'Testing', 'Tutorial' ]"
examples: "[ 'APIGatewayV1', 'APIGatewayV2', 'APIGatewayV2+LambdaAuthorizer', 'BackgroundTasks', 'HelloJSON', 'HelloWorld', 'HelloWorldNoTraits', 'HummingbirdLambda', 'ManagedInstances', 'MultiSourceAPI', 'MultiTenant', 'ResourcesPackaging', 'S3EventNotifier', 'S3_AWSSDK', 'S3_Soto', 'Streaming+APIGateway', 'Streaming+FunctionUrl', 'Streaming+Codable', 'ServiceLifecycle+Postgres', 'Testing', 'Tutorial' ]"
archive_plugin_examples: "[ 'HelloWorld', 'ResourcesPackaging' ]"
archive_plugin_enabled: true

Expand Down
3 changes: 3 additions & 0 deletions Examples/ManagedInstances/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
response.json
samconfig.toml
Makefile
60 changes: 60 additions & 0 deletions Examples/ManagedInstances/BackgroundTasks/main.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
//===----------------------------------------------------------------------===//
//
// This source file is part of the SwiftAWSLambdaRuntime open source project
//
// Copyright SwiftAWSLambdaRuntime project authors
// Copyright (c) Amazon.com, Inc. or its affiliates.
// Licensed under Apache License v2.0
//
// See LICENSE.txt for license information
// See CONTRIBUTORS.txt for the list of SwiftAWSLambdaRuntime project authors
//
// SPDX-License-Identifier: Apache-2.0
//
//===----------------------------------------------------------------------===//

import AWSLambdaRuntime

#if canImport(FoundationEssentials)
import FoundationEssentials
#else
import Foundation
#endif

// for a simple struct as this one, the compiler automatically infers Sendable
// With Lambda Managed Instances, your handler struct MUST be Sendable
struct BackgroundProcessingHandler: LambdaWithBackgroundProcessingHandler, Sendable {
struct Input: Decodable {
let message: String
}

struct Greeting: Encodable {
let echoedMessage: String
}

typealias Event = Input
typealias Output = Greeting

func handle(
_ event: Event,
outputWriter: some LambdaResponseWriter<Output>,
context: LambdaContext
) async throws {
// Return result to the Lambda control plane
context.logger.debug("BackgroundProcessingHandler - message received")
try await outputWriter.write(Greeting(echoedMessage: event.message))

// Perform some background work, e.g:
context.logger.debug("BackgroundProcessingHandler - response sent. Performing background tasks.")
try await Task.sleep(for: .seconds(10))

// Exit the function. All asynchronous work has been executed before exiting the scope of this function.
// Follows structured concurrency principles.
context.logger.debug("BackgroundProcessingHandler - Background tasks completed. Returning")
return
}
}

let adapter = LambdaCodableAdapterSendable(handler: BackgroundProcessingHandler())
let runtime = LambdaManagedRuntime(handler: adapter)
try await runtime.run()
41 changes: 41 additions & 0 deletions Examples/ManagedInstances/HelloJSON/main.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
//===----------------------------------------------------------------------===//
//
// This source file is part of the SwiftAWSLambdaRuntime open source project
//
// Copyright SwiftAWSLambdaRuntime project authors
// Copyright (c) Amazon.com, Inc. or its affiliates.
// Licensed under Apache License v2.0
//
// See LICENSE.txt for license information
// See CONTRIBUTORS.txt for the list of SwiftAWSLambdaRuntime project authors
//
// SPDX-License-Identifier: Apache-2.0
//
//===----------------------------------------------------------------------===//

import AWSLambdaRuntime

// in this example we are receiving and responding with JSON structures

// the data structure to represent the input parameter
struct HelloRequest: Decodable {
let name: String
let age: Int
}

// the data structure to represent the output response
struct HelloResponse: Encodable {
let greetings: String
}

// the Lambda runtime
let runtime = LambdaManagedRuntime {
(event: HelloRequest, context: LambdaContext) in

HelloResponse(
greetings: "Hello \(event.name). You look \(event.age > 30 ? "younger" : "older") than your age."
)
}

// start the loop
try await runtime.run()
46 changes: 46 additions & 0 deletions Examples/ManagedInstances/Package.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
// swift-tools-version:6.2

import PackageDescription

let package = Package(
name: "swift-aws-lambda-runtime-example",
platforms: [.macOS(.v15)],
products: [
.executable(name: "HelloJSON", targets: ["HelloJSON"]),
.executable(name: "Streaming", targets: ["Streaming"]),
.executable(name: "BackgroundTasks", targets: ["BackgroundTasks"]),
],
dependencies: [
// For local development (default)
.package(name: "swift-aws-lambda-runtime", path: "../.."),

// For standalone usage, comment the line above and uncomment below:
// .package(url: "https://github.com/awslabs/swift-aws-lambda-runtime.git", from: "2.0.0"),

.package(url: "https://github.com/awslabs/swift-aws-lambda-events.git", from: "1.0.0"),
],
targets: [
.executableTarget(
name: "HelloJSON",
dependencies: [
.product(name: "AWSLambdaRuntime", package: "swift-aws-lambda-runtime")
],
path: "Sources/HelloJSON"
),
.executableTarget(
name: "Streaming",
dependencies: [
.product(name: "AWSLambdaRuntime", package: "swift-aws-lambda-runtime"),
.product(name: "AWSLambdaEvents", package: "swift-aws-lambda-events"),
],
path: "Sources/Streaming"
),
.executableTarget(
name: "BackgroundTasks",
dependencies: [
.product(name: "AWSLambdaRuntime", package: "swift-aws-lambda-runtime")
],
path: "Sources/BackgroundTasks"
),
]
)
126 changes: 126 additions & 0 deletions Examples/ManagedInstances/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,126 @@
# Lambda Managed Instances Example

This example demonstrates deploying Swift Lambda functions to Lambda Managed Instances using AWS SAM. Lambda Managed Instances provide serverless simplicity with EC2 flexibility and cost optimization by running your functions on customer-owned EC2 instances.

## Functions Included

1. **HelloJSON** - JSON input/output with structured data types
2. **Streaming** - Demonstrates response streaming capabilities
3. **BackgroundTasks** - Handles long-running background processing

## Prerequisites

- AWS CLI configured with appropriate permissions
- SAM CLI installed
- Swift 6.0+ installed
- An existing [Lambda Managed Instances capacity provider](https://docs.aws.amazon.com/lambda/latest/dg/lambda-managed-instances-capacity-providers.html)

## Capacity Provider Configuration

[Create your own capacity provider](https://docs.aws.amazon.com/lambda/latest/dg/lambda-managed-instances-capacity-providers.html#lambda-managed-instances-creating-capacity-provider) before deploying this example.

This example uses a pre-configured capacity provider with the ARN:
```
arn:aws:lambda:us-west-2:486652066693:capacity-provider:TestEC2
```

## Deployment

```bash
# Build the Swift packages
swift package archive --allow-network-access docker

# Change the values below to match your setup
REGION=us-west-2
CAPACITY_PROVIDER=arn:aws:lambda:us-west-2:<YOUR ACCOUNT ID>:capacity-provider:<YOUR CAPACITY PROVIDER NAME>

# Deploy using SAM
sam deploy \
--resolve-s3 \
--template-file template.yaml \
--stack-name swift-lambda-managed-instances \
--capabilities CAPABILITY_IAM \
--region ${REGION} \
--parameter-overrides \
CapacityProviderArn=${CAPACITY_PROVIDER}
```

## Function Details

### HelloJSON Function
- **Timeout**: 15 seconds (default)
- **Concurrency**: 8 per execution environment (default)
- **Input**: JSON `{"name": "string", "age": number}`
- **Output**: JSON `{"greetings": "string"}`

### Streaming Function
- **Timeout**: 60 seconds
- **Concurrency**: 8 per execution environment (default)
- **Features**: Response streaming enabled
- **Output**: Streams numbers with pauses

### BackgroundTasks Function
- **Timeout**: 300 seconds (5 minutes)
- **Concurrency**: 8 per execution environment (default)
- **Input**: JSON `{"message": "string"}`
- **Features**: Long-running background processing after response

## Testing with AWS CLI

After deployment, invoke each function with the AWS CLI:

### Test HelloJSON Function
```bash
REGION=us-west-2
aws lambda invoke \
--region ${REGION} \
--function-name swift-lambda-managed-instances-HelloJSON \
--payload $(echo '{ "name" : "Swift Developer", "age" : 50 }' | base64) \
out.txt && cat out.txt && rm out.txt

# Expected output: {"greetings": "Hello Swift Developer. You look older than your age."}
```

### Test Streaming Function
```bash
# Get the Streaming URL
REGION=us-west-2
STREAMING_URL=$(aws cloudformation describe-stacks \
--stack-name swift-lambda-managed-instances \
--region ${REGION} \
--query 'Stacks[0].Outputs[?OutputKey==`StreamingFunctionUrl`].OutputValue' \
--output text)

# Set the AWS_ACCESS_KEY_ID, AWS_SECRET_ACCESS_KEY, and AWS_SESSION_TOKEN environment variables
eval $(aws configure export-credentials --format env)

# Test with curl (streaming response)
curl "$STREAMING_URL" \
--user "${AWS_ACCESS_KEY_ID}":"${AWS_SECRET_ACCESS_KEY}" \
--aws-sigv4 "aws:amz:${REGION}:lambda" \
-H "x-amz-security-token: ${AWS_SESSION_TOKEN}" \
--no-buffer

# Expected output: Numbers streaming with pauses
```

### Test BackgroundTasks Function
```bash
# Test with AWS CLI
REGION=us-west-2
aws lambda invoke \
--region ${REGION} \
--function-name swift-lambda-managed-instances-BackgroundTasks \
--payload $(echo '{ "message" : "Additional processing in the background" }' | base64) \
out.txt && cat out.txt && rm out.txt

# Expected output: {"echoedMessage": "Additional processing in the background"}
# Note: Background processing continues after response is sent
```

## Cleanup

To remove all resources:
```bash
sam delete --stack-name swift-lambda-managed-instances --region ${REGION}
```
60 changes: 60 additions & 0 deletions Examples/ManagedInstances/Sources/BackgroundTasks/main.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
//===----------------------------------------------------------------------===//
//
// This source file is part of the SwiftAWSLambdaRuntime open source project
//
// Copyright SwiftAWSLambdaRuntime project authors
// Copyright (c) Amazon.com, Inc. or its affiliates.
// Licensed under Apache License v2.0
//
// See LICENSE.txt for license information
// See CONTRIBUTORS.txt for the list of SwiftAWSLambdaRuntime project authors
//
// SPDX-License-Identifier: Apache-2.0
//
//===----------------------------------------------------------------------===//

import AWSLambdaRuntime

#if canImport(FoundationEssentials)
import FoundationEssentials
#else
import Foundation
#endif

// for a simple struct as this one, the compiler automatically infers Sendable
// With Lambda Managed Instances, your handler struct MUST be Sendable
struct BackgroundProcessingHandler: LambdaWithBackgroundProcessingHandler, Sendable {
struct Input: Decodable {
let message: String
}

struct Greeting: Encodable {
let echoedMessage: String
}

typealias Event = Input
typealias Output = Greeting

func handle(
_ event: Event,
outputWriter: some LambdaResponseWriter<Output>,
context: LambdaContext
) async throws {
// Return result to the Lambda control plane
context.logger.debug("BackgroundProcessingHandler - message received")
try await outputWriter.write(Greeting(echoedMessage: event.message))

// Perform some background work, e.g:
context.logger.debug("BackgroundProcessingHandler - response sent. Performing background tasks.")
try await Task.sleep(for: .seconds(10))

// Exit the function. All asynchronous work has been executed before exiting the scope of this function.
// Follows structured concurrency principles.
context.logger.debug("BackgroundProcessingHandler - Background tasks completed. Returning")
return
}
}

let adapter = LambdaCodableAdapterSendable(handler: BackgroundProcessingHandler())
let runtime = LambdaManagedRuntime(handler: adapter)
try await runtime.run()
41 changes: 41 additions & 0 deletions Examples/ManagedInstances/Sources/HelloJSON/main.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
//===----------------------------------------------------------------------===//
//
// This source file is part of the SwiftAWSLambdaRuntime open source project
//
// Copyright SwiftAWSLambdaRuntime project authors
// Copyright (c) Amazon.com, Inc. or its affiliates.
// Licensed under Apache License v2.0
//
// See LICENSE.txt for license information
// See CONTRIBUTORS.txt for the list of SwiftAWSLambdaRuntime project authors
//
// SPDX-License-Identifier: Apache-2.0
//
//===----------------------------------------------------------------------===//

import AWSLambdaRuntime

// in this example we are receiving and responding with JSON structures

// the data structure to represent the input parameter
struct HelloRequest: Decodable {
let name: String
let age: Int
}

// the data structure to represent the output response
struct HelloResponse: Encodable {
let greetings: String
}

// the Lambda runtime
let runtime = LambdaManagedRuntime {
(event: HelloRequest, context: LambdaContext) in

HelloResponse(
greetings: "Hello \(event.name). You look \(event.age > 30 ? "younger" : "older") than your age."
)
}

// start the loop
try await runtime.run()
Loading