From 0266e4e6cf90bea681100e799a06419c32e3fa26 Mon Sep 17 00:00:00 2001 From: Frank Chen Date: Fri, 26 Jun 2026 14:14:36 -0700 Subject: [PATCH 1/5] ci: publish durable execution lambda layer --- .github/scripts/build_lambda_layer.py | 172 ++++++++++++++++++ .../scripts/tests/test_build_lambda_layer.py | 77 ++++++++ .github/workflows/lambda-layer-publish.yml | 145 +++++++++++++++ .github/workflows/test-parser.yml | 12 +- README.md | 1 + RELEASING.md | 2 + .../README.md | 51 ++++++ 7 files changed, 458 insertions(+), 2 deletions(-) create mode 100644 .github/scripts/build_lambda_layer.py create mode 100644 .github/scripts/tests/test_build_lambda_layer.py create mode 100644 .github/workflows/lambda-layer-publish.yml diff --git a/.github/scripts/build_lambda_layer.py b/.github/scripts/build_lambda_layer.py new file mode 100644 index 00000000..bbfdf1bc --- /dev/null +++ b/.github/scripts/build_lambda_layer.py @@ -0,0 +1,172 @@ +#!/usr/bin/env python3 +# SPDX-FileCopyrightText: 2025-present Amazon.com, Inc. or its affiliates. +# +# SPDX-License-Identifier: Apache-2.0 +from __future__ import annotations + +import argparse +import shutil +import subprocess +import sys +import tempfile +import zipfile +from dataclasses import dataclass +from pathlib import Path + + +ARCHITECTURE_PLATFORMS = { + "x86_64": "manylinux2014_x86_64", + "arm64": "manylinux2014_aarch64", +} + + +@dataclass(frozen=True) +class BuildConfig: + output: Path + target_python: str + architecture: str + sdk_distribution: Path | None = None + otel_distribution: Path | None = None + sdk_requirement: str = "aws-durable-execution-sdk-python" + otel_requirement: str = "aws-durable-execution-sdk-python-otel" + build_dir: Path | None = None + + +def build_layer(config: BuildConfig) -> Path: + """Build a Lambda layer zip containing the SDK and OTel plugin.""" + + _validate_config(config) + + with tempfile.TemporaryDirectory() as temp_dir: + work_dir = config.build_dir or Path(temp_dir) / "layer" + if work_dir.exists(): + shutil.rmtree(work_dir) + layer_python_dir = work_dir / "python" + layer_python_dir.mkdir(parents=True) + + _install_layer_dependencies(config, layer_python_dir) + _write_zip(config.output, work_dir) + + return config.output + + +def _validate_config(config: BuildConfig) -> None: + if config.architecture not in ARCHITECTURE_PLATFORMS: + supported = ", ".join(sorted(ARCHITECTURE_PLATFORMS)) + raise ValueError( + f"Unsupported architecture: {config.architecture}. " + f"Supported architectures: {supported}" + ) + + if not config.target_python.startswith("3."): + raise ValueError("target_python must be a Python 3 minor version, such as 3.12") + + for distribution in (config.sdk_distribution, config.otel_distribution): + if distribution is not None and not distribution.is_file(): + raise FileNotFoundError(distribution) + + +def _install_layer_dependencies(config: BuildConfig, target_dir: Path) -> None: + python_version = config.target_python + abi = f"cp{python_version.replace('.', '')}" + platform = ARCHITECTURE_PLATFORMS[config.architecture] + requirements = [ + str(config.sdk_distribution or config.sdk_requirement), + str(config.otel_distribution or config.otel_requirement), + ] + + command = [ + sys.executable, + "-m", + "pip", + "install", + "--upgrade", + "--target", + str(target_dir), + "--platform", + platform, + "--implementation", + "cp", + "--python-version", + python_version, + "--abi", + abi, + "--only-binary", + ":all:", + "--no-compile", + *requirements, + ] + subprocess.run(command, check=True) + + +def _write_zip(output: Path, layer_root: Path) -> None: + output.parent.mkdir(parents=True, exist_ok=True) + if output.exists(): + output.unlink() + + with zipfile.ZipFile(output, "w", compression=zipfile.ZIP_DEFLATED) as archive: + for path in sorted(layer_root.rglob("*")): + if path.is_file(): + archive.write(path, path.relative_to(layer_root)) + + +def main(argv: list[str] | None = None) -> int: + parser = argparse.ArgumentParser( + prog="build_lambda_layer.py", + description="Build the AWS Durable Execution SDK Python Lambda layer.", + ) + subparsers = parser.add_subparsers(dest="command", required=True) + + build_parser = subparsers.add_parser("build", help="build a Lambda layer zip") + build_parser.add_argument("--output", type=Path, required=True) + build_parser.add_argument( + "--target-python", + required=True, + help="Lambda Python minor version, for example 3.12", + ) + build_parser.add_argument( + "--architecture", + choices=["x86_64", "arm64"], + required=True, + help="Lambda instruction set architecture.", + ) + build_parser.add_argument("--sdk-distribution", type=Path) + build_parser.add_argument("--otel-distribution", type=Path) + build_parser.add_argument( + "--sdk-requirement", + default="aws-durable-execution-sdk-python", + help="Package specifier used when --sdk-distribution is not provided.", + ) + build_parser.add_argument( + "--otel-requirement", + default="aws-durable-execution-sdk-python-otel", + help="Package specifier used when --otel-distribution is not provided.", + ) + build_parser.add_argument( + "--build-dir", + type=Path, + help="Optional scratch directory. Existing contents are replaced.", + ) + + args = parser.parse_args(argv) + if args.command == "build": + output = build_layer( + BuildConfig( + output=args.output, + target_python=args.target_python, + architecture=args.architecture, + sdk_distribution=args.sdk_distribution, + otel_distribution=args.otel_distribution, + sdk_requirement=args.sdk_requirement, + otel_requirement=args.otel_requirement, + build_dir=args.build_dir, + ) + ) + print(output) + return 0 + + return 1 + + +if __name__ == "__main__": + raise SystemExit(main()) diff --git a/.github/scripts/tests/test_build_lambda_layer.py b/.github/scripts/tests/test_build_lambda_layer.py new file mode 100644 index 00000000..67cd8263 --- /dev/null +++ b/.github/scripts/tests/test_build_lambda_layer.py @@ -0,0 +1,77 @@ +from __future__ import annotations + +import os +import subprocess +import sys +import zipfile +from pathlib import Path + +import pytest + + +sys.path.insert(0, os.path.dirname(os.path.dirname(__file__))) + +from build_lambda_layer import BuildConfig, build_layer + + +def test_build_layer_installs_dependencies_and_zips_lambda_layout( + monkeypatch: pytest.MonkeyPatch, + tmp_path: Path, +) -> None: + sdk_wheel = tmp_path / "aws_durable_execution_sdk_python-1.0.0-py3-none-any.whl" + otel_wheel = ( + tmp_path / "aws_durable_execution_sdk_python_otel-1.0.0-py3-none-any.whl" + ) + sdk_wheel.write_text("sdk") + otel_wheel.write_text("otel") + commands: list[list[str]] = [] + + def fake_run(command: list[str], check: bool) -> subprocess.CompletedProcess[str]: + commands.append(command) + target = Path(command[command.index("--target") + 1]) + (target / "aws_durable_execution_sdk_python").mkdir() + (target / "aws_durable_execution_sdk_python" / "__init__.py").write_text("") + (target / "aws_durable_execution_sdk_python_otel").mkdir() + (target / "aws_durable_execution_sdk_python_otel" / "__init__.py").write_text( + "" + ) + return subprocess.CompletedProcess(command, 0) + + monkeypatch.setattr(subprocess, "run", fake_run) + + output = build_layer( + BuildConfig( + output=tmp_path / "layer.zip", + target_python="3.12", + architecture="arm64", + sdk_distribution=sdk_wheel, + otel_distribution=otel_wheel, + ) + ) + + assert output == tmp_path / "layer.zip" + assert commands[0][commands[0].index("--platform") + 1] == "manylinux2014_aarch64" + assert commands[0][commands[0].index("--abi") + 1] == "cp312" + assert "--no-compile" in commands[0] + assert str(sdk_wheel) in commands[0] + assert str(otel_wheel) in commands[0] + + with zipfile.ZipFile(output) as archive: + assert ( + "python/aws_durable_execution_sdk_python/__init__.py" in archive.namelist() + ) + assert ( + "python/aws_durable_execution_sdk_python_otel/__init__.py" + in archive.namelist() + ) + + +def test_build_layer_rejects_unsupported_architecture(tmp_path: Path) -> None: + with pytest.raises(ValueError, match="Unsupported architecture"): + build_layer( + BuildConfig( + output=tmp_path / "layer.zip", + target_python="3.12", + architecture="sparc", + ) + ) diff --git a/.github/workflows/lambda-layer-publish.yml b/.github/workflows/lambda-layer-publish.yml new file mode 100644 index 00000000..c5e3f52a --- /dev/null +++ b/.github/workflows/lambda-layer-publish.yml @@ -0,0 +1,145 @@ +name: Publish Lambda Layer + +on: + release: + types: [published] + workflow_dispatch: + inputs: + regions: + description: "Comma-separated AWS regions to publish to" + required: false + default: "us-east-1" + +permissions: + contents: read + id-token: write + +env: + AWS_REGION: us-east-1 + LAYER_NAME_PREFIX: aws-durable-execution-sdk-python + LAYER_REGIONS: ${{ github.event.inputs.regions || vars.LAMBDA_LAYER_REGIONS || 'us-east-1' }} + +jobs: + publish-layer: + runs-on: ubuntu-latest + strategy: + fail-fast: false + matrix: + include: + - target_python: "3.11" + runtime: python3.11 + runtime_slug: python311 + architecture: x86_64 + - target_python: "3.11" + runtime: python3.11 + runtime_slug: python311 + architecture: arm64 + - target_python: "3.12" + runtime: python3.12 + runtime_slug: python312 + architecture: x86_64 + - target_python: "3.12" + runtime: python3.12 + runtime_slug: python312 + architecture: arm64 + - target_python: "3.13" + runtime: python3.13 + runtime_slug: python313 + architecture: x86_64 + - target_python: "3.13" + runtime: python3.13 + runtime_slug: python313 + architecture: arm64 + - target_python: "3.14" + runtime: python3.14 + runtime_slug: python314 + architecture: x86_64 + - target_python: "3.14" + runtime: python3.14 + runtime_slug: python314 + architecture: arm64 + + steps: + - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 + with: + ref: ${{ github.event.release.tag_name || github.ref }} + + - name: Set up Python + uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0 + with: + python-version: "3.11" + + - name: Install build tools + run: python -m pip install --upgrade hatch==1.16.5 + + - name: Build SDK and OTel distributions + run: | + cd packages/aws-durable-execution-sdk-python + hatch build + cd "$GITHUB_WORKSPACE/packages/aws-durable-execution-sdk-python-otel" + hatch build + + - name: Read package versions + id: versions + run: | + SDK_VERSION=$(grep "^__version__" packages/aws-durable-execution-sdk-python/src/aws_durable_execution_sdk_python/__about__.py | cut -d'"' -f2) + OTEL_VERSION=$(grep "^__version__" packages/aws-durable-execution-sdk-python-otel/src/aws_durable_execution_sdk_python_otel/__about__.py | cut -d'"' -f2) + echo "sdk_version=${SDK_VERSION}" >> "$GITHUB_OUTPUT" + echo "otel_version=${OTEL_VERSION}" >> "$GITHUB_OUTPUT" + + - name: Build layer zip + id: build-layer + run: | + SDK_WHEEL=$(ls packages/aws-durable-execution-sdk-python/dist/aws_durable_execution_sdk_python-*.whl | head -n 1) + OTEL_WHEEL=$(ls packages/aws-durable-execution-sdk-python-otel/dist/aws_durable_execution_sdk_python_otel-*.whl | head -n 1) + LAYER_ZIP="dist/${LAYER_NAME_PREFIX}-${{ matrix.runtime_slug }}-${{ matrix.architecture }}.zip" + python .github/scripts/build_lambda_layer.py build \ + --sdk-distribution "$SDK_WHEEL" \ + --otel-distribution "$OTEL_WHEEL" \ + --target-python "${{ matrix.target_python }}" \ + --architecture "${{ matrix.architecture }}" \ + --output "$LAYER_ZIP" + echo "layer_zip=${LAYER_ZIP}" >> "$GITHUB_OUTPUT" + + - name: Configure AWS Credentials + uses: aws-actions/configure-aws-credentials@v6 + with: + role-to-assume: ${{ secrets.LAMBDA_LAYER_PUBLISH_IAM_ROLE_ARN }} + role-session-name: durableExecutionPythonLayerPublish + aws-region: ${{ env.AWS_REGION }} + + - name: Publish layer + run: | + IFS=',' read -ra REGIONS <<< "${LAYER_REGIONS}" + for REGION in "${REGIONS[@]}"; do + REGION=$(echo "$REGION" | xargs) + if [ -z "$REGION" ]; then + continue + fi + + LAYER_NAME="${LAYER_NAME_PREFIX}-${{ matrix.runtime_slug }}-${{ matrix.architecture }}" + VERSION_NUMBER=$(aws lambda publish-layer-version \ + --layer-name "$LAYER_NAME" \ + --description "AWS Durable Execution SDK for Python ${{ steps.versions.outputs.sdk_version }} with OTel plugin ${{ steps.versions.outputs.otel_version }} (${{ matrix.runtime }}/${{ matrix.architecture }})" \ + --zip-file "fileb://${{ steps.build-layer.outputs.layer_zip }}" \ + --compatible-runtimes "${{ matrix.runtime }}" \ + --compatible-architectures "${{ matrix.architecture }}" \ + --license-info "Apache-2.0" \ + --region "$REGION" \ + --query Version \ + --output text) + + aws lambda add-layer-version-permission \ + --layer-name "$LAYER_NAME" \ + --version-number "$VERSION_NUMBER" \ + --statement-id public-layer-access \ + --action lambda:GetLayerVersion \ + --principal "*" \ + --region "$REGION" + done + + - name: Upload layer artifact + uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 + with: + name: lambda-layer-${{ matrix.runtime_slug }}-${{ matrix.architecture }} + path: ${{ steps.build-layer.outputs.layer_zip }} diff --git a/.github/workflows/test-parser.yml b/.github/workflows/test-parser.yml index fbea1810..2c853a64 100644 --- a/.github/workflows/test-parser.yml +++ b/.github/workflows/test-parser.yml @@ -3,11 +3,13 @@ name: Test Parser on: pull_request: paths: + - '.github/scripts/build_lambda_layer.py' - '.github/scripts/parse_sdk_branch.py' - '.github/scripts/tests/**' push: branches: [ main ] paths: + - '.github/scripts/build_lambda_layer.py' - '.github/scripts/parse_sdk_branch.py' - '.github/scripts/tests/**' @@ -20,5 +22,11 @@ jobs: steps: - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 - - name: Run parser tests - run: python .github/scripts/tests/test_parse_sdk_branch.py + - name: Install test dependencies + run: python -m pip install pytest + + - name: Run script tests + run: | + python -m pytest \ + .github/scripts/tests/test_build_lambda_layer.py \ + .github/scripts/tests/test_parse_sdk_branch.py diff --git a/README.md b/README.md index 3a772717..63910eca 100644 --- a/README.md +++ b/README.md @@ -25,6 +25,7 @@ Build reliable, long-running AWS Lambda workflows with checkpointed steps, waits | Package | Description | Version | | --- | --- | --- | | `aws-durable-execution-sdk-python` | Execution SDK for Lambda durable functions | [![PyPI - Version](https://img.shields.io/pypi/v/aws-durable-execution-sdk-python.svg)](https://pypi.org/project/aws-durable-execution-sdk-python) | +| `aws-durable-execution-sdk-python-otel` | OpenTelemetry plugin for durable execution traces | [![PyPI - Version](https://img.shields.io/pypi/v/aws-durable-execution-sdk-python-otel.svg)](https://pypi.org/project/aws-durable-execution-sdk-python-otel) | | `aws-durable-execution-sdk-python-testing` | Local/cloud test runner and pytest helpers | [![PyPI - Version](https://img.shields.io/pypi/v/aws-durable-execution-sdk-python-testing.svg)](https://pypi.org/project/aws-durable-execution-sdk-python-testing) | ## 🚀 Quick Start diff --git a/RELEASING.md b/RELEASING.md index d148b7c5..5fabdd15 100644 --- a/RELEASING.md +++ b/RELEASING.md @@ -65,6 +65,8 @@ The workflow runs on the `release: [published]` event, so it fires whenever a re > **Note:** The workflow builds and publishes all packages in the matrix. Ensure the version in each package's `__about__.py` is correct before publishing. If only one package has a version bump, PyPI will reject the re-upload of the unchanged package (which is expected and harmless since `fail-fast: false` is set). +Creating a GitHub Release also triggers the [`lambda-layer-publish.yml`](.github/workflows/lambda-layer-publish.yml) workflow. The workflow builds the SDK and OTel wheels from the release tag, packages them into Lambda layer zips for each supported Python runtime and architecture, and publishes public layer versions to the regions configured by the `LAMBDA_LAYER_REGIONS` repository variable. If no regions are configured, it publishes to `us-east-1`. + ## Release Notes Format Release notes should maintain separate timelines for each package. Use the following structure: diff --git a/packages/aws-durable-execution-sdk-python-otel/README.md b/packages/aws-durable-execution-sdk-python-otel/README.md index e8941721..43a2738c 100644 --- a/packages/aws-durable-execution-sdk-python-otel/README.md +++ b/packages/aws-durable-execution-sdk-python-otel/README.md @@ -17,6 +17,57 @@ OpenTelemetry instrumentation plugin for the [AWS Durable Execution SDK for Pyth pip install aws-durable-execution-sdk-python-otel ``` +## SDK and OTel Lambda Layer + +This package also includes build tooling for a Lambda layer that installs both +the AWS Durable Execution SDK and this OTel plugin into a Python Lambda function. +The generated zip uses the standard Lambda layer layout: + +```text +python/ + aws_durable_execution_sdk_python/ + aws_durable_execution_sdk_python_otel/ + ... +``` + +Build SDK and OTel wheels from this repository, then build the layer zip: + +```bash +(cd packages/aws-durable-execution-sdk-python && hatch build) +(cd packages/aws-durable-execution-sdk-python-otel && hatch build) + +python .github/scripts/build_lambda_layer.py build \ + --sdk-distribution packages/aws-durable-execution-sdk-python/dist/aws_durable_execution_sdk_python-*.whl \ + --otel-distribution packages/aws-durable-execution-sdk-python-otel/dist/aws_durable_execution_sdk_python_otel-*.whl \ + --target-python 3.12 \ + --architecture x86_64 \ + --output dist/aws-durable-execution-sdk-python-layer-python312-x86_64.zip +``` + +Use `--architecture arm64` for Lambda functions running on Graviton. + +Publish the generated zip as a Lambda layer: + +```bash +VERSION_NUMBER=$(aws lambda publish-layer-version \ + --layer-name aws-durable-execution-sdk-python-python312-x86_64 \ + --zip-file fileb://dist/aws-durable-execution-sdk-python-layer-python312-x86_64.zip \ + --compatible-runtimes python3.12 \ + --compatible-architectures x86_64 \ + --license-info Apache-2.0 \ + --query Version \ + --output text) + +aws lambda add-layer-version-permission \ + --layer-name aws-durable-execution-sdk-python-python312-x86_64 \ + --version-number "$VERSION_NUMBER" \ + --statement-id public-layer-access \ + --action lambda:GetLayerVersion \ + --principal "*" +``` + +The release workflow publishes public layer versions automatically. + ## Quick Start using X-Ray/CloudWatch Tracing 1. Add the [ADOT Lambda Layer](#1-adot-lambda-layer) to your function and set `AWS_LAMBDA_EXEC_WRAPPER=/opt/otel-instrument` From 55ff0a19ff4620c25b2ba944121d51d518f2269a Mon Sep 17 00:00:00 2001 From: Frank Chen Date: Fri, 26 Jun 2026 14:20:10 -0700 Subject: [PATCH 2/5] ci: verify layer workflow on pull requests --- .github/workflows/lambda-layer-publish.yml | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/.github/workflows/lambda-layer-publish.yml b/.github/workflows/lambda-layer-publish.yml index c5e3f52a..42432c2c 100644 --- a/.github/workflows/lambda-layer-publish.yml +++ b/.github/workflows/lambda-layer-publish.yml @@ -1,6 +1,13 @@ name: Publish Lambda Layer on: + pull_request: + branches: [main] + paths: + - ".github/scripts/build_lambda_layer.py" + - ".github/workflows/lambda-layer-publish.yml" + - "packages/aws-durable-execution-sdk-python/**" + - "packages/aws-durable-execution-sdk-python-otel/**" release: types: [published] workflow_dispatch: @@ -102,6 +109,7 @@ jobs: echo "layer_zip=${LAYER_ZIP}" >> "$GITHUB_OUTPUT" - name: Configure AWS Credentials + if: github.event_name != 'pull_request' uses: aws-actions/configure-aws-credentials@v6 with: role-to-assume: ${{ secrets.LAMBDA_LAYER_PUBLISH_IAM_ROLE_ARN }} @@ -109,6 +117,7 @@ jobs: aws-region: ${{ env.AWS_REGION }} - name: Publish layer + if: github.event_name != 'pull_request' run: | IFS=',' read -ra REGIONS <<< "${LAYER_REGIONS}" for REGION in "${REGIONS[@]}"; do From 1f71799f8b4dba74e7219960d2ae730f54bd2b56 Mon Sep 17 00:00:00 2001 From: Frank Chen Date: Fri, 26 Jun 2026 14:21:29 -0700 Subject: [PATCH 3/5] ci: publish layer during pull request verification --- .github/workflows/lambda-layer-publish.yml | 2 -- 1 file changed, 2 deletions(-) diff --git a/.github/workflows/lambda-layer-publish.yml b/.github/workflows/lambda-layer-publish.yml index 42432c2c..b42fdfd8 100644 --- a/.github/workflows/lambda-layer-publish.yml +++ b/.github/workflows/lambda-layer-publish.yml @@ -109,7 +109,6 @@ jobs: echo "layer_zip=${LAYER_ZIP}" >> "$GITHUB_OUTPUT" - name: Configure AWS Credentials - if: github.event_name != 'pull_request' uses: aws-actions/configure-aws-credentials@v6 with: role-to-assume: ${{ secrets.LAMBDA_LAYER_PUBLISH_IAM_ROLE_ARN }} @@ -117,7 +116,6 @@ jobs: aws-region: ${{ env.AWS_REGION }} - name: Publish layer - if: github.event_name != 'pull_request' run: | IFS=',' read -ra REGIONS <<< "${LAYER_REGIONS}" for REGION in "${REGIONS[@]}"; do From 127fa28bef341253bb6b19760daec3e28d94a83e Mon Sep 17 00:00:00 2001 From: Frank Chen Date: Fri, 26 Jun 2026 14:29:06 -0700 Subject: [PATCH 4/5] ci: fall back to integration role for layer publish test --- .github/workflows/lambda-layer-publish.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/lambda-layer-publish.yml b/.github/workflows/lambda-layer-publish.yml index b42fdfd8..e9d21db6 100644 --- a/.github/workflows/lambda-layer-publish.yml +++ b/.github/workflows/lambda-layer-publish.yml @@ -111,7 +111,7 @@ jobs: - name: Configure AWS Credentials uses: aws-actions/configure-aws-credentials@v6 with: - role-to-assume: ${{ secrets.LAMBDA_LAYER_PUBLISH_IAM_ROLE_ARN }} + role-to-assume: ${{ secrets.LAMBDA_LAYER_PUBLISH_IAM_ROLE_ARN || secrets.ACTIONS_INTEGRATION_ROLE_NAME }} role-session-name: durableExecutionPythonLayerPublish aws-region: ${{ env.AWS_REGION }} From 4d837900303768dcfe8592f69d5deadf96070b25 Mon Sep 17 00:00:00 2001 From: Frank Chen Date: Fri, 26 Jun 2026 14:46:40 -0700 Subject: [PATCH 5/5] docs: clarify adot wrapper layer requirement --- .../README.md | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/packages/aws-durable-execution-sdk-python-otel/README.md b/packages/aws-durable-execution-sdk-python-otel/README.md index 43a2738c..38949f29 100644 --- a/packages/aws-durable-execution-sdk-python-otel/README.md +++ b/packages/aws-durable-execution-sdk-python-otel/README.md @@ -68,12 +68,18 @@ aws lambda add-layer-version-permission \ The release workflow publishes public layer versions automatically. +This SDK layer does not include the ADOT Lambda wrapper executable. Only set +`AWS_LAMBDA_EXEC_WRAPPER=/opt/otel-instrument` when your function also includes +the ADOT Lambda layer described below; otherwise Lambda fails during init because +`/opt/otel-instrument` does not exist. + ## Quick Start using X-Ray/CloudWatch Tracing -1. Add the [ADOT Lambda Layer](#1-adot-lambda-layer) to your function and set `AWS_LAMBDA_EXEC_WRAPPER=/opt/otel-instrument` -2. Enable [X-Ray Active Tracing](#2-aws-x-ray-active-tracing) on the function -3. Pass `OtelPlugin` to your handler's `plugins` list -4. Add X-Ray write permissions +1. Add the SDK+OTel layer for this package +2. Add the [ADOT Lambda Layer](#1-adot-lambda-layer) to your function and set `AWS_LAMBDA_EXEC_WRAPPER=/opt/otel-instrument` +3. Enable [X-Ray Active Tracing](#2-aws-x-ray-active-tracing) on the function +4. Pass `OtelPlugin` to your handler's `plugins` list +5. Add X-Ray write permissions ### 1. ADOT Lambda Layer