Skip to content

Commit 09f2eb4

Browse files
authored
wip: Deploy Examples to Lambda (#13)
1 parent b8eb7ba commit 09f2eb4

11 files changed

Lines changed: 7058 additions & 0 deletions

File tree

.github/model/lambda.json

Lines changed: 6566 additions & 0 deletions
Large diffs are not rendered by default.
Lines changed: 172 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,172 @@
1+
name: Deploy Python Examples
2+
3+
on:
4+
pull_request:
5+
branches: [ "main", "development"]
6+
paths:
7+
- 'src/aws_durable_execution_sdk_python_testing/**'
8+
- 'examples/**'
9+
- '.github/workflows/deploy-examples.yml'
10+
workflow_dispatch:
11+
12+
env:
13+
AWS_REGION: us-west-2
14+
15+
permissions:
16+
id-token: write
17+
contents: read
18+
19+
jobs:
20+
setup:
21+
runs-on: ubuntu-latest
22+
outputs:
23+
examples: ${{ steps.get-examples.outputs.examples }}
24+
steps:
25+
- uses: actions/checkout@v4
26+
27+
- name: Get examples from catalog
28+
id: get-examples
29+
working-directory: ./examples
30+
run: |
31+
echo "examples=$(jq -c '.examples | map(select(.integration == true))' examples-catalog.json)" >> $GITHUB_OUTPUT
32+
33+
integration-test:
34+
needs: setup
35+
runs-on: ubuntu-latest
36+
name: ${{ matrix.example.name }}
37+
strategy:
38+
matrix:
39+
example: ${{ fromJson(needs.setup.outputs.examples) }}
40+
fail-fast: false
41+
42+
steps:
43+
- uses: actions/checkout@v4
44+
45+
- name: Setup SSH Agent
46+
uses: webfactory/ssh-agent@v0.9.0
47+
with:
48+
ssh-private-key: ${{ secrets.SDK_KEY }}
49+
50+
- name: Setup Python
51+
uses: actions/setup-python@v4
52+
with:
53+
python-version: '3.13'
54+
55+
- name: Configure AWS credentials
56+
if: github.event_name != 'workflow_dispatch' || github.actor != 'nektos/act'
57+
uses: aws-actions/configure-aws-credentials@v4
58+
with:
59+
role-to-assume: "${{ secrets.ACTIONS_INTEGRATION_ROLE_NAME }}"
60+
role-session-name: pythonTestingLibraryGitHubIntegrationTest
61+
aws-region: ${{ env.AWS_REGION }}
62+
63+
- name: Install custom Lambda model
64+
run: |
65+
aws configure add-model --service-model file://.github/model/lambda.json --service-name lambda
66+
67+
- name: Install Hatch
68+
run: pip install hatch
69+
70+
- name: Build examples
71+
run: hatch run examples:build
72+
73+
- name: Deploy Lambda function - ${{ matrix.example.name }}
74+
env:
75+
AWS_ACCOUNT_ID: ${{ secrets.AWS_ACCOUNT_ID }}
76+
LAMBDA_ENDPOINT: ${{ secrets.LAMBDA_ENDPOINT }}
77+
INVOKE_ACCOUNT_ID: ${{ secrets.INVOKE_ACCOUNT_ID }}
78+
KMS_KEY_ARN: ${{ secrets.KMS_KEY_ARN }}
79+
run: |
80+
# Build function name
81+
EXAMPLE_NAME_CLEAN=$(echo "${{ matrix.example.name }}" | sed 's/ //g')
82+
if [ "${{ github.event_name }}" = "pull_request" ]; then
83+
FUNCTION_NAME="${EXAMPLE_NAME_CLEAN}-Python-PR-${{ github.event.number }}"
84+
else
85+
FUNCTION_NAME="${EXAMPLE_NAME_CLEAN}-Python"
86+
fi
87+
88+
# Extract handler file name
89+
HANDLER_FILE=$(echo "${{ matrix.example.handler }}" | sed 's/\.handler$//')
90+
91+
echo "Deploying $HANDLER_FILE as $FUNCTION_NAME"
92+
hatch run examples:deploy "$HANDLER_FILE" "$FUNCTION_NAME"
93+
94+
# Store function name for later steps
95+
echo "FUNCTION_NAME=$FUNCTION_NAME" >> $GITHUB_ENV
96+
97+
- name: Invoke Lambda function - ${{ matrix.example.name }}
98+
env:
99+
LAMBDA_ENDPOINT: ${{ secrets.LAMBDA_ENDPOINT }}
100+
run: |
101+
echo "Testing function: $FUNCTION_NAME"
102+
aws lambda invoke \
103+
--function-name "$FUNCTION_NAME" \
104+
--cli-binary-format raw-in-base64-out \
105+
--payload '{"name": "World"}' \
106+
--region "${{ env.AWS_REGION }}" \
107+
--endpoint-url "$LAMBDA_ENDPOINT" \
108+
/tmp/response.json \
109+
> /tmp/invoke_response.json
110+
111+
echo "Response:"
112+
cat /tmp/response.json
113+
114+
echo "Full Invoke Response:"
115+
cat /tmp/invoke_response.json
116+
117+
echo "All Response Headers:"
118+
jq -r '.ResponseMetadata.HTTPHeaders' /tmp/invoke_response.json || echo "No HTTPHeaders found"
119+
120+
# Extract invocation ID from response headers
121+
INVOCATION_ID=$(jq -r '.ResponseMetadata.HTTPHeaders["x-amzn-invocation-id"] // empty' /tmp/invoke_response.json)
122+
if [ -n "$INVOCATION_ID" ]; then
123+
echo "INVOCATION_ID=$INVOCATION_ID" >> $GITHUB_ENV
124+
echo "Captured Invocation ID: $INVOCATION_ID"
125+
else
126+
echo "Warning: Could not capture invocation ID from response"
127+
fi
128+
129+
- name: Find Durable Execution - ${{ matrix.example.name }}
130+
env:
131+
LAMBDA_ENDPOINT: ${{ secrets.LAMBDA_ENDPOINT }}
132+
run: |
133+
echo "Listing durable executions for function: $FUNCTION_NAME"
134+
aws lambda list-durable-executions \
135+
--function-name "$FUNCTION_NAME" \
136+
--region "${{ env.AWS_REGION }}" \
137+
--endpoint-url "$LAMBDA_ENDPOINT" \
138+
--cli-binary-format raw-in-base64-out \
139+
--status-filter SUCCEEDED \
140+
> /tmp/executions.json
141+
echo "Durable Executions:"
142+
cat /tmp/executions.json
143+
144+
# Extract the first execution ARN for history retrieval
145+
EXECUTION_ARN=$(jq -r '.DurableExecutions[0].DurableExecutionArn // empty' /tmp/executions.json)
146+
echo "EXECUTION_ARN=$EXECUTION_ARN" >> $GITHUB_ENV
147+
148+
- name: Get Durable Execution History - ${{ matrix.example.name }}
149+
if: env.EXECUTION_ARN != ''
150+
env:
151+
LAMBDA_ENDPOINT: ${{ secrets.LAMBDA_ENDPOINT }}
152+
run: |
153+
echo "Getting execution history for: $EXECUTION_ARN"
154+
aws lambda get-durable-execution-history \
155+
--durable-execution-arn "$EXECUTION_ARN" \
156+
--region "${{ env.AWS_REGION }}" \
157+
--endpoint-url "$LAMBDA_ENDPOINT" \
158+
--cli-binary-format raw-in-base64-out \
159+
> /tmp/history.json
160+
echo "Execution History:"
161+
cat /tmp/history.json
162+
163+
# - name: Cleanup Lambda function
164+
# if: always()
165+
# env:
166+
# LAMBDA_ENDPOINT: ${{ secrets.LAMBDA_ENDPOINT }}
167+
# run: |
168+
# echo "Deleting function: $FUNCTION_NAME"
169+
# aws lambda delete-function \
170+
# --function-name "$FUNCTION_NAME" \
171+
# --endpoint-url "$LAMBDA_ENDPOINT" \
172+
# --region "${{ env.AWS_REGION }}" || echo "Function already deleted or doesn't exist"

examples/.env.template

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
# AWS Configuration for Lambda Deployment
2+
AWS_REGION=us-west-2
3+
AWS_ACCOUNT_ID=123456789012
4+
LAMBDA_ENDPOINT=https://lambda.us-west-2.amazonaws.com
5+
INVOKE_ACCOUNT_ID=123456789012
6+
KMS_KEY_ARN=arn:aws:kms:us-west-2:123456789012:key/12345678-1234-1234-1234-123456789012

examples/.gitignore

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
build/
2+
*.zip
3+
.env
4+
.aws-sam/

examples/README.md

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
# Python Durable Functions Examples
2+
3+
## Local Testing with SAM
4+
5+
Test functions locally:
6+
```bash
7+
sam local invoke HelloWorldFunction
8+
```
9+
10+
Test with custom event:
11+
```bash
12+
sam local invoke HelloWorldFunction -e event.json
13+
```
14+
15+
## Deploy Functions
16+
17+
Deploy with Python script:
18+
```bash
19+
python3 deploy.py hello_world
20+
```
21+
22+
Deploy with SAM:
23+
```bash
24+
sam build
25+
sam deploy --guided
26+
```
27+
28+
## Environment Variables
29+
30+
- `AWS_ACCOUNT_ID`: Your AWS account ID
31+
- `LAMBDA_ENDPOINT`: Your Lambda service endpoint
32+
- `INVOKE_ACCOUNT_ID`: Account ID allowed to invoke functions
33+
- `AWS_REGION`: AWS region (default: us-west-2)
34+
- `KMS_KEY_ARN`: KMS key for encryption (optional)
35+
36+
## Available Examples
37+
38+
- **hello_world**: Simple hello world function
39+
40+
## Adding New Examples
41+
42+
1. Add your Python function to `src/`
43+
2. Update `examples-catalog.json` and `template.yaml`
44+
3. Deploy using either script above

examples/build.py

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
#!/usr/bin/env python3
2+
3+
import shutil
4+
import site
5+
from pathlib import Path
6+
7+
8+
def build():
9+
"""Build examples with SDK dependencies from current environment."""
10+
examples_dir = Path(__file__).parent
11+
build_dir = examples_dir / "build"
12+
13+
# Clean build directory
14+
if build_dir.exists():
15+
shutil.rmtree(build_dir)
16+
build_dir.mkdir()
17+
18+
print("Copying SDK from current environment...")
19+
20+
# Copy the SDK from current environment (hatch installs it)
21+
for site_dir in site.getsitepackages():
22+
sdk_path = Path(site_dir) / "aws_durable_execution_sdk_python"
23+
if sdk_path.exists():
24+
shutil.copytree(sdk_path, build_dir / "aws_durable_execution_sdk_python")
25+
print(f"Copied SDK from {sdk_path}")
26+
break
27+
else:
28+
print("SDK not found in site-packages")
29+
30+
print("Copying testing SDK source...")
31+
32+
# Copy testing SDK source
33+
sdk_src = examples_dir.parent / "src" / "aws_durable_execution_sdk_python_testing"
34+
if sdk_src.exists():
35+
shutil.copytree(sdk_src, build_dir / "aws_durable_execution_sdk_python_testing")
36+
37+
print("Copying example functions...")
38+
39+
# Copy example source files
40+
src_dir = examples_dir / "src"
41+
for py_file in src_dir.glob("*.py"):
42+
if py_file.name != "__init__.py":
43+
shutil.copy2(py_file, build_dir)
44+
45+
print(f"Build complete: {build_dir}")
46+
47+
48+
if __name__ == "__main__":
49+
build()

0 commit comments

Comments
 (0)