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
32 changes: 17 additions & 15 deletions .github/workflows/integration-test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -35,15 +35,10 @@ jobs:
ruby-version: '3.3'
working-directory: demo/lambdas/ruby

- name: Setup LocalStack
uses: localstack/setup-localstack@main
with:
image-tag: 'latest'
use-pro: 'true'
configuration: LS_LOG=trace
install-awslocal: 'true'
env:
LOCALSTACK_AUTH_TOKEN: ${{ secrets.LOCALSTACK_AUTH_TOKEN }}
- name: Install lstk
run: |
npm install -g @localstack/lstk aws-cdk
lstk setup aws

- name: Install CDK dependencies
run: cd cdk && npm ci
Expand All @@ -57,29 +52,36 @@ jobs:
- name: Install Node.js Lambda dependencies
run: cd demo/lambdas/nodejs && npm install --omit=dev

- name: Start LocalStack
env:
LOCALSTACK_AUTH_TOKEN: ${{ secrets.LOCALSTACK_AUTH_TOKEN }}
run: |
docker pull localstack/localstack-pro:latest
LOCALSTACK_LS_LOG=trace lstk start

- name: Bootstrap CDK
working-directory: cdk
env:
AWS_ACCESS_KEY_ID: test
AWS_SECRET_ACCESS_KEY: test
AWS_DEFAULT_REGION: us-east-1
run: npx cdklocal bootstrap
run: lstk cdk bootstrap

- name: Deploy CDK stack
working-directory: cdk
env:
AWS_ACCESS_KEY_ID: test
AWS_SECRET_ACCESS_KEY: test
AWS_DEFAULT_REGION: us-east-1
run: npx cdklocal deploy LocalstackDemoStack --require-approval never
run: lstk cdk deploy LocalstackDemoStack --require-approval never

- name: Upload frontend to S3
env:
AWS_ACCESS_KEY_ID: test
AWS_SECRET_ACCESS_KEY: test
AWS_DEFAULT_REGION: us-east-1
AWS_ENDPOINT_URL: http://localhost.localstack.cloud:4566
run: awslocal s3 sync demo/web/ s3://archive-bucket/
run: lstk aws s3 sync demo/web/ s3://archive-bucket/

- name: Run end-to-end test
env:
Expand All @@ -89,7 +91,7 @@ jobs:
AWS_ENDPOINT_URL: http://localhost.localstack.cloud:4566
run: |
echo "Discovering REST API ID..."
apiId=$(awslocal apigateway get-rest-apis --output json \
apiId=$(lstk aws apigateway get-rest-apis --output json \
| jq -r '.items[] | select(.name=="localstack-demo") | .id')
echo "API ID: $apiId"

Expand All @@ -110,10 +112,10 @@ jobs:
done

echo "Verifying S3 result file..."
awslocal s3 cp "s3://archive-bucket/$reqID/result.txt" - | grep -q "Archive result" \
lstk aws s3 cp "s3://archive-bucket/$reqID/result.txt" - | grep -q "Archive result" \
&& echo "✅ End-to-end test passed!" \
|| (echo "❌ S3 result not found" && exit 1)

- name: LocalStack logs (on failure)
if: failure()
run: localstack logs
run: lstk logs
30 changes: 13 additions & 17 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,8 @@ check: ## Check if all required prerequisites are installed
@command -v docker > /dev/null 2>&1 || { echo "Docker is not installed. Please install Docker and try again."; exit 1; }
@command -v node > /dev/null 2>&1 || { echo "Node.js is not installed. Please install Node.js and try again."; exit 1; }
@command -v aws > /dev/null 2>&1 || { echo "AWS CLI is not installed. Please install AWS CLI and try again."; exit 1; }
@command -v awslocal > /dev/null 2>&1 || { echo "awslocal is not installed. Run: pip install awscli-local"; exit 1; }
@command -v localstack > /dev/null 2>&1 || { echo "LocalStack CLI is not installed. Run: pip install localstack"; exit 1; }
@command -v lstk > /dev/null 2>&1 || { echo "lstk is not installed. Run: npm install -g @localstack/lstk"; exit 1; }
@command -v cdk > /dev/null 2>&1 || { echo "AWS CDK CLI is not installed. Run: npm install -g aws-cdk"; exit 1; }
@command -v jq > /dev/null 2>&1 || { echo "jq is not installed. See https://jqlang.github.io/jq/download/"; exit 1; }
@test -n "$(LOCALSTACK_AUTH_TOKEN)" || { echo "LOCALSTACK_AUTH_TOKEN is not set. Find your token at https://app.localstack.cloud/workspace/auth-token"; exit 1; }
@echo "All required prerequisites are available."
Expand All @@ -46,51 +46,47 @@ build: ## Build the CDK TypeScript app
$(CDIR) && npm run build

bootstrap: ## Bootstrap CDK for LocalStack
$(CDIR) && npm run build && npx cdklocal bootstrap
$(CDIR) && npm run build && lstk cdk bootstrap

deploy: ## Build, deploy CDK stack to LocalStack, and upload frontend
$(MAKE) bundle-ruby bundle-node
$(MAKE) bootstrap
$(CDIR) && npx cdklocal deploy LocalstackDemoStack --require-approval never
$(CDIR) && lstk cdk deploy LocalstackDemoStack --require-approval never
@echo "Uploading frontend to s3://archive-bucket/ ..."
awslocal s3 sync demo/web/ s3://archive-bucket/
lstk aws s3 sync demo/web/ s3://archive-bucket/
@echo ""
@echo "Done! Open http://localhost:4566/archive-bucket/index.html in your browser."

destroy: ## Destroy the deployed CDK stack on LocalStack
$(CDIR) && npx cdklocal destroy LocalstackDemoStack
$(CDIR) && lstk cdk destroy LocalstackDemoStack

send-request: ## Send a test request and poll S3 for the result
@command -v jq > /dev/null 2>&1 || { echo "jq is not installed. See https://jqlang.github.io/jq/download/"; exit 1; }
@echo "Looking up REST API ID..."
@apiId=$$(awslocal apigateway get-rest-apis --output json | jq -r '.items[] | select(.name=="localstack-demo") | .id'); \
@apiId=$$(lstk aws apigateway get-rest-apis --output json | jq -r '.items[] | select(.name=="localstack-demo") | .id'); \
echo "Sending request to API ID '$$apiId' ..."; \
reqID=$$(curl -s -d '{}' "http://localhost:4566/_aws/execute-api/$$apiId/local/requests" | jq -r .requestID); \
echo "Received request ID '$$reqID'"; \
for i in 1 2 3 4 5 6 7 8 9 10; do \
echo "Polling s3://archive-bucket/ for result ..."; \
awslocal s3 ls s3://archive-bucket/ | grep "$$reqID" && exit 0; \
lstk aws s3 ls s3://archive-bucket/ | grep "$$reqID" && exit 0; \
sleep 3; \
done; \
echo "Timed out waiting for result."

start: ## Start LocalStack in detached mode
start: ## Start LocalStack
@echo "Starting LocalStack..."
@test -n "$(LOCALSTACK_AUTH_TOKEN)" || { echo "LOCALSTACK_AUTH_TOKEN is not set. Find your token at https://app.localstack.cloud/workspace/auth-token"; exit 1; }
@LOCALSTACK_AUTH_TOKEN=$(LOCALSTACK_AUTH_TOKEN) localstack start -d
@unset AWS_ENDPOINT_URL; LOCALSTACK_AUTH_TOKEN=$(LOCALSTACK_AUTH_TOKEN) lstk start
@echo "LocalStack started successfully."

stop: ## Stop LocalStack
@echo "Stopping LocalStack..."
@localstack stop
@unset AWS_ENDPOINT_URL; lstk stop
@echo "LocalStack stopped successfully."

ready: ## Wait until the LocalStack container is ready
@echo "Waiting on the LocalStack container..."
@localstack wait -t 30 && echo "LocalStack is ready to use!" || { echo "Gave up waiting on LocalStack, exiting."; exit 1; }

logs: ## Save LocalStack logs to logs.txt
@localstack logs > logs.txt
@unset AWS_ENDPOINT_URL; lstk logs > logs.txt

PKG_SUB_DIRS := $(dir $(shell find . -type d -name node_modules -prune -o -type d -name "vendor" -prune -o -type f -name package.json -print))

Expand All @@ -100,4 +96,4 @@ update-deps: ## Update npm dependencies in all sub-projects
done

.PHONY: usage check install bundle-ruby bundle-node build bootstrap deploy deploy-aws \
destroy destroy-aws send-request start stop ready logs update-deps
destroy destroy-aws send-request start stop logs update-deps
13 changes: 6 additions & 7 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -34,10 +34,10 @@ The following diagram shows the architecture that this sample application builds

## Prerequisites

- A valid [LocalStack for AWS license](https://localstack.cloud/pricing). Your license provides a [`LOCALSTACK_AUTH_TOKEN`](https://docs.localstack.cloud/getting-started/auth-token/) to activate LocalStack.
- [`localstack` CLI](https://docs.localstack.cloud/getting-started/installation/#localstack-cli).
- [AWS CLI](https://docs.localstack.cloud/user-guide/integrations/aws-cli/) with the [`awslocal` wrapper](https://docs.localstack.cloud/user-guide/integrations/aws-cli/#localstack-aws-cli-awslocal).
- [CDK](https://docs.localstack.cloud/user-guide/integrations/aws-cdk/) with the [`cdklocal`](https://www.npmjs.com/package/aws-cdk-local) wrapper (installed automatically via `cdk/package.json`).
- A valid [LocalStack for AWS license](https://localstack.cloud/pricing). Your license provides a [`LOCALSTACK_AUTH_TOKEN`](https://docs.localstack.cloud/aws/getting-started/auth-token/) to activate LocalStack.
- [`lstk` CLI](https://docs.localstack.cloud/aws/developer-tools/running-localstack/lstk/), installed via `npm install -g @localstack/lstk` or `brew install localstack/tap/lstk`.
- [AWS CLI](https://docs.localstack.cloud/user-guide/integrations/aws-cli/), required by `lstk aws`.
- [CDK](https://docs.localstack.cloud/user-guide/integrations/aws-cdk/) installed globally (`npm install -g aws-cdk`), used via the `lstk cdk` proxy.
- [Node.js 22+](https://nodejs.org/en/download/)
- [Docker](https://docs.docker.com/get-docker/) — required to bundle Ruby Lambda gems.
- [`jq`](https://jqlang.github.io/jq/download/)
Expand Down Expand Up @@ -65,7 +65,6 @@ Set your LocalStack auth token and start LocalStack:
```bash
export LOCALSTACK_AUTH_TOKEN=<your-auth-token>
make start
make ready
```

Deploy the full stack (bundles Lambda dependencies, bootstraps CDK, deploys, uploads frontend):
Expand Down Expand Up @@ -126,7 +125,7 @@ Polling s3://archive-bucket/ for result ...
You can also browse the contents of the archive bucket directly:

```bash
awslocal s3 ls s3://archive-bucket/
lstk aws s3 ls s3://archive-bucket/
```

## Summary
Expand All @@ -136,7 +135,7 @@ This sample application demonstrates how to build and test a polyglot serverless
- Defining AWS infrastructure (API Gateway, Lambda, SQS, Step Functions, DynamoDB, S3) entirely with **AWS CDK in TypeScript**.
- Running **three Lambda runtimes** (Node.js, Python, Ruby) side-by-side in the same CDK stack.
- Serving a **React frontend from S3** that auto-discovers the API Gateway endpoint and polls for status updates.
- Using `cdklocal` and `awslocal` to streamline **local deployment and testing** without touching real AWS.
- Using the `lstk cdk` and `lstk aws` proxies to streamline **local deployment and testing** without touching real AWS.
- Providing a **GitHub Actions workflow** that runs the full integration test suite on every push.

## Learn More
Expand Down
22 changes: 0 additions & 22 deletions cdk/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 0 additions & 1 deletion cdk/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,6 @@
"devDependencies": {
"@types/node": "^22.13.0",
"aws-cdk": "^2.1111.0",
"aws-cdk-local": "^2.0.0",
"ts-node": "^10.9.2",
"typescript": "^5.7.0"
}
Expand Down
Loading