Skip to content
Open

asdf #14

Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
31 commits
Select commit Hold shift + click to select a range
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
44 changes: 44 additions & 0 deletions .github/workflows/LAMBDA_LAYERS_SOP.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
# Lambda Layers Standard Operating Procedures (SOP)

## Overview

This document defines the standard operating procedures for managing Strands Agents Lambda layers across all AWS regions, Python versions, and architectures.

**Total: 136 individual Lambda layers** (17 regions × 2 architectures × 4 Python versions). All variants must maintain the same layer version number for each PyPI package version, with only one row per PyPI version appearing in documentation.

## Deployment Process

### 1. Initial Deployment
1. Run workflow with ALL options selected (default)
2. Specify PyPI package version
3. Type "Create Lambda Layer {package_version}" to confirm
4. All 136 individual layers deploy in parallel (4 Python × 2 arch × 17 regions)
5. Each layer gets its own unique name: `strands-agents-py{PYTHON_VERSION}-{ARCH}`

### 2. Version Buffering for New Variants
When adding new variants (new Python version, architecture, or region):

1. **Determine target layer version**: Check existing variants to find the highest layer version
2. **Buffer deployment**: Deploy new variants multiple times until layer version matches existing variants
3. **Example**: If existing variants are at layer version 5, deploy new variant 5 times to reach version 5

### 3. Handling Transient Failures
When some regions fail during deployment:

1. **Identify failed regions**: Check which combinations didn't complete successfully
2. **Targeted redeployment**: Use specific region/arch/Python inputs to redeploy failed combinations
3. **Version alignment**: Continue deploying until all variants reach the same layer version
4. **Verification**: Confirm all combinations have identical layer versions before updating docs

## Yank Process

### Yank Procedure
1. Use the `yank_lambda_layer` GitHub action workflow
2. Specify the layer version to yank
3. Type "Yank Lambda Layer {layer_version}" to confirm
4. **Full yank**: Run with ALL options selected (default) to yank all 136 variants
5. **Partial yank**: Specify Python versions, architectures, and regions for targeted yanking
6. **Update documentation**: Remove or mark the row as yanked in lambda-layers.md
7. **Communication**: Notify users through appropriate channels

**Note**: Yanking deletes layer versions completely. Existing Lambda functions using the layer continue to work, but new functions cannot use the yanked version.
198 changes: 198 additions & 0 deletions .github/workflows/publish-lambda-layer.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,198 @@
name: Publish PyPI Package to Lambda Layer

on:
workflow_dispatch:
inputs:
package_version:
description: 'Package version to download'
required: true
type: string
python_version:
description: 'Python version'
required: true
default: 'ALL'
type: choice
options: ['ALL', '3.10', '3.11', '3.12', '3.13']
architecture:
description: 'Architecture'
required: true
default: 'ALL'
type: choice
options: ['ALL', 'x86_64', 'aarch64']
region:
description: 'AWS region'
required: true
default: 'ALL'
type: choice
# Only non opt-in regions included for now
options: ['ALL', 'us-east-1', 'us-east-2', 'us-west-1', 'us-west-2', 'ap-south-1', 'ap-northeast-1', 'ap-northeast-2', 'ap-northeast-3', 'ap-southeast-1', 'ap-southeast-2', 'ca-central-1', 'eu-central-1', 'eu-west-1', 'eu-west-2', 'eu-west-3', 'eu-north-1', 'sa-east-1']
confirm:
description: 'Type "Create Lambda Layer {PyPI version}" to confirm publishing the layer'
required: true
type: string

env:
BUCKET_NAME: strands-agents-lambda-layer

jobs:
validate:
runs-on: ubuntu-latest
steps:
- name: Validate confirmation
run: |
CONFIRM="${{ inputs.confirm }}"
EXPECTED="Create Lambda Layer ${{ inputs.package_version }}"
if [ "$CONFIRM" != "$EXPECTED" ]; then
echo "Confirmation failed. You must type exactly '$EXPECTED' to proceed."
exit 1
fi
echo "Confirmation validated"

create-buckets:
needs: validate
runs-on: ubuntu-latest
strategy:
matrix:
region: ${{ inputs.region == 'ALL' && fromJson('["us-east-1", "us-east-2", "us-west-1", "us-west-2", "ap-south-1", "ap-northeast-1", "ap-northeast-2", "ap-northeast-3", "ap-southeast-1", "ap-southeast-2", "ca-central-1", "eu-central-1", "eu-west-1", "eu-west-2", "eu-west-3", "eu-north-1", "sa-east-1"]') || fromJson(format('["{0}"]', inputs.region)) }}
permissions:
id-token: write
steps:
- name: Configure AWS credentials
uses: aws-actions/configure-aws-credentials@v4
with:
role-to-assume: ${{ secrets.STRANDS_LAMBDA_LAYER_PUBLISHER_ROLE }}
aws-region: ${{ matrix.region }}

- name: Create S3 bucket
run: |
REGION="${{ matrix.region }}"
ACCOUNT_ID=$(aws sts get-caller-identity --query Account --output text)
REGIONAL_BUCKET="${{ env.BUCKET_NAME }}-${ACCOUNT_ID}-${REGION}"

if ! aws s3api head-bucket --bucket "$REGIONAL_BUCKET" 2>/dev/null; then
if [ "$REGION" = "us-east-1" ]; then
aws s3api create-bucket --bucket "$REGIONAL_BUCKET" --region "$REGION" 2>/dev/null || echo "Bucket $REGIONAL_BUCKET already exists"
else
aws s3api create-bucket --bucket "$REGIONAL_BUCKET" --region "$REGION" --create-bucket-configuration LocationConstraint="$REGION" 2>/dev/null || echo "Bucket $REGIONAL_BUCKET already exists"
fi
echo "S3 bucket ready: $REGIONAL_BUCKET"
else
echo "S3 bucket already exists: $REGIONAL_BUCKET"
fi

package-and-upload:
needs: create-buckets
runs-on: ubuntu-latest
strategy:
matrix:
python-version: ${{ inputs.python_version == 'ALL' && fromJson('["3.10", "3.11", "3.12", "3.13"]') || fromJson(format('["{0}"]', inputs.python_version)) }}
architecture: ${{ inputs.architecture == 'ALL' && fromJson('["x86_64", "aarch64"]') || fromJson(format('["{0}"]', inputs.architecture)) }}
region: ${{ inputs.region == 'ALL' && fromJson('["us-east-1", "us-east-2", "us-west-1", "us-west-2", "ap-south-1", "ap-northeast-1", "ap-northeast-2", "ap-northeast-3", "ap-southeast-1", "ap-southeast-2", "ca-central-1", "eu-central-1", "eu-west-1", "eu-west-2", "eu-west-3", "eu-north-1", "sa-east-1"]') || fromJson(format('["{0}"]', inputs.region)) }}

permissions:
id-token: write

steps:
- name: Set up Python
uses: actions/setup-python@v4
with:
python-version: ${{ matrix.python-version }}

- name: Configure AWS credentials
uses: aws-actions/configure-aws-credentials@v4
with:
role-to-assume: ${{ secrets.STRANDS_LAMBDA_LAYER_PUBLISHER_ROLE }}
aws-region: ${{ matrix.region }}

- name: Create layer directory structure
run: |
mkdir -p layer/python

- name: Download and install package
run: |
pip install strands-agents==${{ inputs.package_version }} \
--python-version ${{ matrix.python-version }} \
--platform manylinux2014_${{ matrix.architecture }} \
-t layer/python/ \
--only-binary=:all:

- name: Create layer zip
run: |
cd layer
zip -r ../lambda-layer.zip .

- name: Upload to S3
run: |
PYTHON_VERSION="${{ matrix.python-version }}"
ARCH="${{ matrix.architecture }}"
REGION="${{ matrix.region }}"
LAYER_NAME="strands-agents-py${PYTHON_VERSION//./_}-${ARCH}"
ACCOUNT_ID=$(aws sts get-caller-identity --query Account --output text)
BUCKET_NAME="${{ env.BUCKET_NAME }}-${ACCOUNT_ID}-${REGION}"
LAYER_KEY="$LAYER_NAME/v${{ inputs.package_version }}/lambda-layer.zip"

aws s3 cp lambda-layer.zip "s3://$BUCKET_NAME/$LAYER_KEY" --region "$REGION"
echo "Uploaded layer to s3://$BUCKET_NAME/$LAYER_KEY"

publish-layer:
needs: package-and-upload
runs-on: ubuntu-latest
strategy:
matrix:
python-version: ${{ inputs.python_version == 'ALL' && fromJson('["3.10", "3.11", "3.12", "3.13"]') || fromJson(format('["{0}"]', inputs.python_version)) }}
architecture: ${{ inputs.architecture == 'ALL' && fromJson('["x86_64", "aarch64"]') || fromJson(format('["{0}"]', inputs.architecture)) }}
region: ${{ inputs.region == 'ALL' && fromJson('["us-east-1", "us-east-2", "us-west-1", "us-west-2", "ap-south-1", "ap-northeast-1", "ap-northeast-2", "ap-northeast-3", "ap-southeast-1", "ap-southeast-2", "ca-central-1", "eu-central-1", "eu-west-1", "eu-west-2", "eu-west-3", "eu-north-1", "sa-east-1"]') || fromJson(format('["{0}"]', inputs.region)) }}

permissions:
id-token: write

steps:
- name: Configure AWS credentials
uses: aws-actions/configure-aws-credentials@v4
with:
role-to-assume: ${{ secrets.STRANDS_LAMBDA_LAYER_PUBLISHER_ROLE }}
aws-region: ${{ matrix.region }}

- name: Publish layer
run: |
PYTHON_VERSION="${{ matrix.python-version }}"
ARCH="${{ matrix.architecture }}"
REGION="${{ matrix.region }}"
LAYER_NAME="strands-agents-py${PYTHON_VERSION//./_}-${ARCH}"
ACCOUNT_ID=$(aws sts get-caller-identity --query Account --output text)
REGION_BUCKET="${{ env.BUCKET_NAME }}-${ACCOUNT_ID}-${REGION}"
LAYER_KEY="$LAYER_NAME/v${{ inputs.package_version }}/lambda-layer.zip"

DESCRIPTION="PyPI package: strands-agents v${{ inputs.package_version }} (Python $PYTHON_VERSION, $ARCH)"

# Set compatible architecture based on matrix architecture
if [ "$ARCH" = "x86_64" ]; then
COMPATIBLE_ARCH="x86_64"
else
COMPATIBLE_ARCH="arm64"
fi

LAYER_OUTPUT=$(aws lambda publish-layer-version \
--layer-name $LAYER_NAME \
--description "$DESCRIPTION" \
--content S3Bucket=$REGION_BUCKET,S3Key=$LAYER_KEY \
--compatible-runtimes python${{ matrix.python-version }} \
--compatible-architectures $COMPATIBLE_ARCH \
--region "$REGION" \
--license-info Apache-2.0 \
--output json)

LAYER_ARN=$(echo "$LAYER_OUTPUT" | jq -r '.LayerArn')
LAYER_VERSION=$(echo "$LAYER_OUTPUT" | jq -r '.Version')

echo "Published layer version $LAYER_VERSION with ARN: $LAYER_ARN in region $REGION"

aws lambda add-layer-version-permission \
--layer-name $LAYER_NAME \
--version-number $LAYER_VERSION \
--statement-id public \
--action lambda:GetLayerVersion \
--principal '*' \
--region "$REGION"

echo "Successfully published layer version $LAYER_VERSION in region $REGION"
81 changes: 81 additions & 0 deletions .github/workflows/yank-lambda-layer.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
name: Yank Lambda Layer

on:
workflow_dispatch:
inputs:
layer_version:
description: 'Layer version to yank'
required: true
type: string
python_version:
description: 'Python version'
required: true
default: 'ALL'
type: choice
options: ['ALL', '3.10', '3.11', '3.12', '3.13']
architecture:
description: 'Architecture'
required: true
default: 'ALL'
type: choice
options: ['ALL', 'x86_64', 'aarch64']
region:
description: 'AWS region'
required: true
default: 'ALL'
type: choice
# Only non opt-in regions included for now
options: ['ALL', 'us-east-1', 'us-east-2', 'us-west-1', 'us-west-2', 'ap-south-1', 'ap-northeast-1', 'ap-northeast-2', 'ap-northeast-3', 'ap-southeast-1', 'ap-southeast-2', 'ca-central-1', 'eu-central-1', 'eu-west-1', 'eu-west-2', 'eu-west-3', 'eu-north-1', 'sa-east-1']
confirm:
description: 'Type "Yank Lambda Layer {layer version}" to confirm yanking the layer'
required: true
type: string

jobs:
yank-layer:
runs-on: ubuntu-latest
continue-on-error: true
strategy:
fail-fast: false
matrix:
python-version: ${{ inputs.python_version == 'ALL' && fromJson('["3.10", "3.11", "3.12", "3.13"]') || fromJson(format('["{0}"]', inputs.python_version)) }}
architecture: ${{ inputs.architecture == 'ALL' && fromJson('["x86_64", "aarch64"]') || fromJson(format('["{0}"]', inputs.architecture)) }}
region: ${{ inputs.region == 'ALL' && fromJson('["us-east-1", "us-east-2", "us-west-1", "us-west-2", "ap-south-1", "ap-northeast-1", "ap-northeast-2", "ap-northeast-3", "ap-southeast-1", "ap-southeast-2", "ca-central-1", "eu-central-1", "eu-west-1", "eu-west-2", "eu-west-3", "eu-north-1", "sa-east-1"]') || fromJson(format('["{0}"]', inputs.region)) }}

permissions:
id-token: write

steps:
- name: Validate confirmation
run: |
CONFIRM="${{ inputs.confirm }}"
EXPECTED="Yank Lambda Layer ${{ inputs.layer_version }}"
if [ "$CONFIRM" != "$EXPECTED" ]; then
echo "Confirmation failed. You must type exactly '$EXPECTED' to proceed."
exit 1
fi
echo "Confirmation validated"

- name: Configure AWS credentials
uses: aws-actions/configure-aws-credentials@v4
with:
role-to-assume: ${{ secrets.STRANDS_LAMBDA_LAYER_PUBLISHER_ROLE }}
aws-region: ${{ matrix.region }}

- name: Yank layer
run: |
PYTHON_VERSION="${{ matrix.python-version }}"
ARCH="${{ matrix.architecture }}"
REGION="${{ matrix.region }}"
LAYER_NAME="strands-agents-py${PYTHON_VERSION//./_}-${ARCH}"
LAYER_VERSION="${{ inputs.layer_version }}"

echo "Attempting to yank layer $LAYER_NAME version $LAYER_VERSION in region $REGION"

# Delete the layer version completely
aws lambda delete-layer-version \
--layer-name $LAYER_NAME \
--version-number $LAYER_VERSION \
--region "$REGION"

echo "Completed yank attempt for layer $LAYER_NAME version $LAYER_VERSION in region $REGION"
Loading