Skip to content

Commit e838b70

Browse files
whummerclaude
andauthored
further workshop enhancements; clean old workshop folders; update README (#7)
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
1 parent 103ec17 commit e838b70

108 files changed

Lines changed: 483 additions & 21446 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.github/workflows/build-test.yml

Lines changed: 27 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -13,32 +13,44 @@ jobs:
1313
- name: Checkout
1414
uses: actions/checkout@v2
1515

16-
- name: Start up LocalStack
16+
- name: Install tools
17+
run: |
18+
pip install --pre localstack awscli-local[ver1] terraform-local pytest requests
19+
20+
# Terraform
21+
curl -fsSL https://apt.releases.hashicorp.com/gpg | sudo gpg --dearmor -o /usr/share/keyrings/hashicorp-archive-keyring.gpg
22+
echo "deb [signed-by=/usr/share/keyrings/hashicorp-archive-keyring.gpg] https://apt.releases.hashicorp.com $(lsb_release -cs) main" \
23+
| sudo tee /etc/apt/sources.list.d/hashicorp.list
24+
sudo apt-get update -qq && sudo apt-get install -y terraform
25+
26+
- name: Start LocalStack
1727
env:
1828
LOCALSTACK_AUTH_TOKEN: ${{ secrets.LOCALSTACK_AUTH_TOKEN }}
1929
run: |
2030
docker pull localstack/localstack-pro &
21-
22-
# install CLI tools
23-
pip install --pre localstack localstack-ext awscli-local[ver1]
24-
25-
# start LocalStack
26-
DEBUG=1 localstack start -d
31+
DEBUG=1 LOCALSTACK_APPINSPECTOR_ENABLE=1 localstack start -d
2732
localstack wait
2833
29-
- name: Run simple test
30-
env:
31-
LOCALSTACK_AUTH_TOKEN: ${{ secrets.LOCALSTACK_AUTH_TOKEN }}
34+
- name: Deploy infrastructure via Terraform
3235
run: |
33-
set -e
36+
cd 01-serverless-app/terraform
37+
tflocal init
38+
tflocal apply -auto-approve
3439
35-
# deploy test resources
36-
(cd 00-hello-world; ./deploy.sh)
40+
- name: Build and push fulfillment image
41+
run: |
42+
ECR_REGISTRY="000000000000.dkr.ecr.us-east-1.localhost.localstack.cloud:4566"
43+
awslocal ecr get-login-password | \
44+
docker login --username AWS --password-stdin "$ECR_REGISTRY"
45+
docker build -t "$ECR_REGISTRY/fulfillment:latest" 01-serverless-app/services/fulfillment
46+
docker push "$ECR_REGISTRY/fulfillment:latest"
3747
38-
# run assertion
39-
curl http://testwebsite.s3-website.localhost.localstack.cloud:4566 | grep 'You are running LocalStack'
48+
- name: Run E2E tests
49+
run: |
50+
cd 02-e2e-testing && pytest tests/ -v
4051
4152
- name: Print LocalStack logs
53+
if: always()
4254
run: |
4355
localstack logs
4456
localstack stop

00-hello-world/README.md

Lines changed: 0 additions & 15 deletions
This file was deleted.

00-hello-world/bucket-policy.json

Lines changed: 0 additions & 12 deletions
This file was deleted.

00-hello-world/deploy.sh

Lines changed: 0 additions & 13 deletions
This file was deleted.

00-hello-world/index.html

Lines changed: 0 additions & 27 deletions
This file was deleted.

00-hello-world/list_s3_objects.py

Lines changed: 0 additions & 29 deletions
This file was deleted.

01-serverless-app/lambdas/order_processor/handler.py

Lines changed: 1 addition & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -6,15 +6,11 @@
66
import boto3
77

88
dynamodb = boto3.resource("dynamodb")
9-
s3 = boto3.client("s3")
109
sfn = boto3.client("stepfunctions")
1110

12-
TABLE_NAME = os.environ["ORDERS_TABLE"]
13-
RECEIPTS_BUCKET = os.environ["RECEIPTS_BUCKET"]
11+
TABLE_NAME = os.environ["ORDERS_TABLE"]
1412
STATE_MACHINE_ARN = os.environ["STATE_MACHINE_ARN"]
1513

16-
TERMINAL_STATUSES = {"fulfilled", "failed"}
17-
1814

1915
def now():
2016
return datetime.now(timezone.utc).strftime("%Y-%m-%dT%H:%M:%SZ")
@@ -64,7 +60,6 @@ def handler(event, context):
6460

6561
if step == "validate": return validate(order)
6662
if step == "process_payment": return process_payment(order)
67-
if step == "fulfill": return fulfill(order)
6863
if step == "handle_failure": return handle_failure(order)
6964

7065
raise ValueError(f"Unknown step: {step}")
@@ -82,20 +77,6 @@ def process_payment(order):
8277
return order
8378

8479

85-
def fulfill(order):
86-
time.sleep(2)
87-
set_status(order["order_id"], "fulfilled")
88-
receipt = {k: order[k] for k in ("order_id", "item", "quantity")}
89-
receipt["status"] = "fulfilled"
90-
s3.put_object(
91-
Bucket=RECEIPTS_BUCKET,
92-
Key=f"receipts/{order['order_id']}.json",
93-
Body=json.dumps(receipt),
94-
ContentType="application/json",
95-
)
96-
return order
97-
98-
9980
def handle_failure(order):
10081
set_status(order["order_id"], "failed")
10182
return order
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
FROM python:3.12-slim
2+
WORKDIR /app
3+
RUN pip install --no-cache-dir boto3
4+
COPY fulfill.py .
5+
CMD ["python", "fulfill.py"]
Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
"""ECS task: fulfill an order — update DynamoDB status and write S3 receipt."""
2+
import json
3+
import os
4+
from datetime import datetime, timezone
5+
6+
import boto3
7+
8+
ORDER_ID = os.environ["ORDER_ID"]
9+
TABLE_NAME = os.environ["ORDERS_TABLE"]
10+
RECEIPTS_BUCKET = os.environ["RECEIPTS_BUCKET"]
11+
12+
endpoint = os.environ.get("AWS_ENDPOINT_URL", "")
13+
print(f"Connecting to endpoint: {endpoint or '(default AWS)'}", flush=True)
14+
15+
dynamodb = boto3.resource("dynamodb")
16+
s3 = boto3.client("s3")
17+
18+
19+
def now():
20+
return datetime.now(timezone.utc).strftime("%Y-%m-%dT%H:%M:%SZ")
21+
22+
23+
table = dynamodb.Table(TABLE_NAME)
24+
25+
resp = table.get_item(Key={"order_id": ORDER_ID})
26+
order = resp["Item"]
27+
28+
fulfilled_at = now()
29+
30+
# Write receipt to S3 first — DynamoDB status is updated last so the test
31+
# (and any consumer) only sees "fulfilled" once the receipt already exists.
32+
receipt = {
33+
"order_id": ORDER_ID,
34+
"item": order["item"],
35+
"quantity": int(order["quantity"]),
36+
"status": "fulfilled",
37+
"fulfilled_at": fulfilled_at,
38+
}
39+
s3.put_object(
40+
Bucket=RECEIPTS_BUCKET,
41+
Key=f"receipts/{ORDER_ID}.json",
42+
Body=json.dumps(receipt),
43+
ContentType="application/json",
44+
)
45+
46+
table.update_item(
47+
Key={"order_id": ORDER_ID},
48+
UpdateExpression="SET #s = :s, fulfilled_at = :ts",
49+
ExpressionAttributeNames={"#s": "status"},
50+
ExpressionAttributeValues={":s": "fulfilled", ":ts": fulfilled_at},
51+
)
52+
53+
print(f"Order {ORDER_ID} fulfilled: {order['item']} x{order['quantity']}")

0 commit comments

Comments
 (0)