Skip to content
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
5 changes: 4 additions & 1 deletion .env
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
# Customize these values as needed for testing both local and on github

# Handlers to build
HANDLERS_TO_BUILD=basic-lambda
HANDLERS_TO_BUILD="basic-lambda basic-sqs http-basic-lambda basic-lambda-concurrent"

HANDLER=basic-lambda

Expand All @@ -11,3 +11,6 @@ OUTPUT_DIR=test/dockerized/tasks

# Max concurrent Lambda invocations for LMI mode
RIE_MAX_CONCURRENCY=4

# Branch of containerized-test-runner-for-aws-lambda to clone
TEST_RUNNER_BRANCH=main
35 changes: 34 additions & 1 deletion .github/workflows/dockerized-test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -44,4 +44,37 @@ jobs:
with:
suiteFileArray: '["./test/dockerized/suites/*.json"]'
dockerImageName: 'local/test-base'
taskFolder: './test/dockerized/tasks'
taskFolder: './test/dockerized/tasks'

dockerized-test-concurrent:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6

- uses: dtolnay/rust-toolchain@stable
- uses: Swatinem/rust-cache@v2

- name: Build Lambda artifacts for testing
run: |
mkdir -p test/dockerized/tasks
HANDLERS_TO_BUILD="basic-lambda-concurrent" OUTPUT_DIR="$(pwd)/test/dockerized/tasks" make build-examples
ls -la test/dockerized/tasks/
- name: Build base test image with RIE and custom entrypoint
run: docker build -t local/test-base -f Dockerfile.test .

- name: Create Docker network for concurrent tests
run: docker network create concurrent-test-net

- name: Run concurrent scenarios
uses: aws/containerized-test-runner-for-aws-lambda@main
with:
suiteFileArray: '[]'
dockerImageName: 'local/test-base'
taskFolder: './test/dockerized/tasks'
scenarioDir: './test/dockerized/scenarios'
dockerSharedNetwork: 'concurrent-test-net'

- name: Remove Docker network
if: always()
run: docker network rm concurrent-test-net || true
57 changes: 43 additions & 14 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,23 +1,52 @@
target
# Rust
target/
**/*.rs.bk
*.pdb

# Cargo
/.cargo
lambda-runtime/libtest.rmeta
lambda-integration-tests/target
Cargo.lock
.test-runner

# Built AWS Lambda zipfile
lambda.zip
# IDE
.vscode/
.idea/
*.iml
*.ipr
*.iws
.kiro/

# output.json from example docs
output.json
# macOS
.DS_Store
.AppleDouble
.LSOverride

# Environment
.env
*.env.local

.aws-sam
build
.vscode
# AWS / SAM
.aws-sam/
build/
output.json
lambda.zip

node_modules
cdk.out
# CDK
node_modules/
cdk.out/

# Test artifacts
Dockerfile.test-with-tasks
.test-runner/
test/dockerized/tasks/
Dockerfile.test-with-tasks
.test/

# Python
__pycache__/
*.pyc
*.pyo
*.pyd
.pytest_cache/

# Misc
lambda-runtime/libtest.rmeta
lambda-integration-tests/target/
69 changes: 43 additions & 26 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ INTEG_EXTENSIONS := extension-fn extension-trait logs-trait
# Using musl to run extensions on both AL1 and AL2
INTEG_ARCH := x86_64-unknown-linux-musl
RIE_MAX_CONCURRENCY ?= 4
TEST_RUNNER_BRANCH ?= main
OUTPUT_DIR ?= test/dockerized/tasks
HANDLERS_TO_BUILD ?=
HANDLER ?=
Expand All @@ -14,12 +15,12 @@ HANDLER ?=
-include .env
export

.PHONY: help pr-check integration-tests check-event-features fmt build-examples test-rie test-rie-lmi nuke test-dockerized
.PHONY: help pr-check integration-tests check-event-features fmt build-examples build-test-runner test-rie test-rie-lmi nuke test-dockerized test-dockerized-concurrent

.DEFAULT_GOAL := help

define uppercase
$(shell sed -r 's/(^|-)(\w)/\U\2/g' <<< $(1))
$(shell echo '$(1)' | sed -r 's/(^|-)(\w)/\U\2/g')
endef

pr-check:
Expand Down Expand Up @@ -129,23 +130,39 @@ build-examples:
nuke:
docker kill $$(docker ps -q)

test-dockerized: build-examples
@echo "Running dockerized tests locally..."

build-test-runner: build-examples
@echo "Building base Docker image with RIE and custom entrypoint..."
docker build \
-t local/test-base \
-f Dockerfile.test \
.

@echo "Setting up containerized test runner..."
@if [ ! -d ".test-runner" ]; then \
echo "Cloning containerized-test-runner-for-aws-lambda..."; \
git clone --quiet https://github.com/aws/containerized-test-runner-for-aws-lambda.git .test-runner; \
git clone --quiet --branch $(TEST_RUNNER_BRANCH) https://github.com/aws/containerized-test-runner-for-aws-lambda.git .test-runner; \
fi
@echo "Building test runner Docker image..."
@docker build -t test-runner:local -f .test-runner/Dockerfile .test-runner


test-dockerized-concurrent: build-test-runner
@echo "Running concurrent scenarios in Docker..."
@docker network rm concurrent-test-net 2>/dev/null || true
@docker network create concurrent-test-net
@docker run --rm \
-e INPUT_SUITE_FILE_ARRAY='[]' \
-e INPUT_SCENARIO_DIR=/workspace/test/dockerized/scenarios \
-e DOCKER_IMAGE_NAME=local/test-base \
-e TASK_FOLDER=./test/dockerized/tasks \
-e GITHUB_WORKSPACE=/workspace \
-e DOCKER_SHARED_NETWORK=concurrent-test-net \
-v /var/run/docker.sock:/var/run/docker.sock \
-v "$(CURDIR):/workspace" \
-w /workspace \
test-runner:local
@docker network rm --force concurrent-test-net 2>/dev/null || true

test-dockerized: build-test-runner
@echo "Running tests in Docker..."
@docker run --rm \
-e INPUT_SUITE_FILE_ARRAY='["./test/dockerized/suites/*.json"]' \
Expand All @@ -168,23 +185,23 @@ help: ## Show this help message
@echo 'Usage: make [target]'
@echo ''
@echo 'Available targets:'
@echo ' pr-check Run pre-commit checks (fmt, clippy, tests)'
@echo ' integration-tests Build and run AWS integration tests'
@echo ' check-event-features Test individual event features'
@echo ' fmt Format code with cargo fmt'
@echo ' build-examples Build example Lambda functions'
@echo ' Usage: EXAMPLES="basic-lambda" OUTPUT_DIR=/make build-examples'
@echo ' test-rie Test Lambda with Runtime Interface Emulator'
@echo ' Usage: HANDLERS_TO_BUILD="basic-lambda basic-sqs" make test-rie'
@echo ' Usage: HANDLERS_TO_BUILD="basic-lambda" HANDLER="basic-lambda" make test-rie'
@echo ' test-rie-lmi Test RIE in Lambda Managed Instance mode'
@echo ' Usage: RIE_MAX_CONCURRENCY=4 HANDLERS_TO_BUILD="basic-lambda-concurrent" make test-rie-lmi'
@echo ' test-dockerized Run dockerized test harness'
@echo ' nuke Kill all running Docker containers'
@echo ' pr-check Run pre-commit checks (fmt, clippy, tests)'
@echo ' integration-tests Build and run AWS integration tests'
@echo ' check-event-features Test individual event features'
@echo ' fmt Format code with cargo fmt'
@echo ' build-examples Build example Lambda functions'
@echo ' Usage: EXAMPLES="basic-lambda" OUTPUT_DIR=/ make build-examples'
@echo ' test-rie Test Lambda with Runtime Interface Emulator'
@echo ' Usage: HANDLERS_TO_BUILD="basic-lambda basic-sqs" make test-rie'
@echo ' Usage: HANDLERS_TO_BUILD="basic-lambda" HANDLER="basic-lambda" make test-rie'
@echo ' test-rie-lmi Test RIE in Lambda Managed Instance mode'
@echo ' Usage: RIE_MAX_CONCURRENCY=4 HANDLERS_TO_BUILD="basic-lambda-concurrent" make test-rie-lmi'
@echo ' test-dockerized Run dockerized test harness'
@echo ' test-dockerized-concurrent Run concurrent LMI test scenarios'
@echo ' nuke Kill all running Docker containers'
@echo ''
@echo 'Environment variables:'
@echo ' EXAMPLES Space-separated list of examples to build (for build-examples)'
@echo ' HANDLERS_TO_BUILD Space-separated list of handlers to build for RIE (for test-rie)'
@echo ' HANDLER Specific handler to run (defaults to first in HANDLERS_TO_BUILD)'
@echo ' OUTPUT_DIR Directory for built binaries (default: /tmp/var-task for build-examples, /var/task for Docker)'
@echo ' RIE_MAX_CONCURRENCY Max concurrent Lambda invocations for LMI mode (for test-rie-lmi)'
@echo ' HANDLERS_TO_BUILD Space-separated list of handlers to build for RIE (for test-rie)'
@echo ' HANDLER Specific handler to run (defaults to first in HANDLERS_TO_BUILD)'
@echo ' OUTPUT_DIR Directory for built binaries (default: test/dockerized/tasks)'
@echo ' RIE_MAX_CONCURRENCY Max concurrent Lambda invocations for LMI mode (for test-rie-lmi)'
39 changes: 27 additions & 12 deletions examples/basic-lambda-concurrent/src/main.rs
Original file line number Diff line number Diff line change
@@ -1,32 +1,46 @@
// This example requires the following input to succeed:
// { "command": "do something" }

use lambda_runtime::{service_fn, tracing, Error, LambdaEvent};
use lambda_runtime::{service_fn, tracing, Diagnostic, Error, LambdaEvent};
use serde::{Deserialize, Serialize};

/// This is also a made-up example. Requests come into the runtime as unicode
/// strings in json format, which can map to any structure that implements `serde::Deserialize`
/// The runtime pays no attention to the contents of the request payload.
#[derive(Deserialize)]
struct Request {
command: String,
}

/// This is a made-up example of what a response structure may look like.
/// There is no restriction on what it can be. The runtime requires responses
/// to be serialized into json. The runtime pays no attention
/// to the contents of the response payload.
#[derive(Serialize)]
struct Response {
req_id: String,
msg: String,
}

#[derive(Debug)]
struct HandlerError(String);

impl std::fmt::Display for HandlerError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{}", self.0)
}
}

impl From<HandlerError> for Diagnostic {
fn from(e: HandlerError) -> Diagnostic {
Diagnostic {
error_type: "HandlerError".into(),
error_message: e.0,
}
}
}

#[tokio::main]
async fn main() -> Result<(), Error> {
// required to enable CloudWatch error logging by the runtime
tracing::init_default_subscriber();

let max_concurrency = std::env::var("AWS_LAMBDA_MAX_CONCURRENCY").unwrap_or_else(|_| "not set".to_string());
tracing::info!(AWS_LAMBDA_MAX_CONCURRENCY = %max_concurrency, "starting concurrent handler");

let func = service_fn(my_handler);
if let Err(err) = lambda_runtime::run_concurrent(func).await {
tracing::error!(error = %err, "run error");
Expand All @@ -35,17 +49,18 @@ async fn main() -> Result<(), Error> {
Ok(())
}

pub(crate) async fn my_handler(event: LambdaEvent<Request>) -> Result<Response, Error> {
// extract some useful info from the request
pub(crate) async fn my_handler(event: LambdaEvent<Request>) -> Result<Response, HandlerError> {
let command = event.payload.command;

// prepare the response
if command == "fail" {
return Err(HandlerError("simulated handler error".into()));
}

let resp = Response {
req_id: event.context.request_id,
msg: format!("Command {command} executed."),
};

// return `Response` (it will be serialized to JSON automatically by the runtime)
Ok(resp)
}

Expand Down
25 changes: 25 additions & 0 deletions scripts/pre-commit
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
#!/bin/sh
# Pre-commit hook to run cargo fmt
#
# To install this hook, run:
# cp scripts/pre-commit .git/hooks/pre-commit
# chmod +x .git/hooks/pre-commit

echo "Running cargo fmt..."

# Run cargo fmt on the entire workspace
cargo fmt --all -- --check

# Check if cargo fmt found any formatting issues
if [ $? -ne 0 ]; then
echo ""
echo "❌ Code formatting issues detected!"
echo "Running 'cargo fmt --all' to fix formatting..."
cargo fmt --all
echo ""
echo "✅ Formatting applied. Please review the changes and commit again."
exit 1
fi

echo "✅ Code formatting is correct!"
exit 0
Loading
Loading