Skip to content

Release public Lambda layer #223

Release public Lambda layer

Release public Lambda layer #223

Workflow file for this run

name: Release public Lambda layer
on:
workflow_dispatch:
inputs:
layer_arn_keyword:
description: 'Clone layer from keyword ARN by substituting architecture parameter (ie: arn:aws:lambda:us-east-1:012345678912:layer:aws-otel-nodejs-wrapper-<ARCHITECTURE>-d8b20954c3284abcd08f5fb8178986a89cff7160:1)'
required: true
layer_name_keyword:
description: 'Publish layer to keyword name by substituting architecture parameter (ie: aws-otel-python-<ARCHITECTURE>-ver-1-7-1)'
required: true
aws_region:
description: 'Deploy to aws region'
required: true
default: "[ \"us-east-1\", \"us-east-2\", \"us-west-1\", \"us-west-2\", \"ap-south-1\", \"ap-northeast-2\", \"ap-southeast-1\", \"ap-southeast-2\", \"ap-northeast-1\", \"ca-central-1\", \"eu-central-1\", \"eu-west-1\", \"eu-west-2\", \"eu-west-3\", \"eu-north-1\", \"sa-east-1\" ]"
architecture:
# NOTE: (NathanielRN) The operator should be extra cautious if they are
# trying to modify the release "architecture" input, because the only
# reason they would is if the Soak Test Layer ARN Keyword only worked
# for ONE architecture and FAILED for the other and at the point we
# should be really certain that we want to release such a layer.
description: 'Architecture-compatible distributions of the layer to release in each region'
required: true
default: "[ \"amd64\", \"arm64\" ]"
env:
AMP_REGIONS: us-west-2,us-east-1,us-east-2,eu-central-1,eu-west-1
permissions:
id-token: write
contents: read
jobs:
validate-inputs:
runs-on: ubuntu-latest
steps:
- name: Validate `layer_name_keyword` (${{ github.event.inputs.layer_name_keyword }})
run: |
grep -Eq "aws-otel-(collector|java-agent|java-wrapper|nodejs|python)-<ARCHITECTURE>-ver-[0-9]+-[0-9]+-[0-9]+" <<< "${{ github.event.inputs.layer_name_keyword }}"
publish-prod:
runs-on: ubuntu-latest
needs: validate-inputs
strategy:
matrix:
architecture: ${{ fromJson(github.event.inputs.architecture) }}
aws_region: ${{ fromJson(github.event.inputs.aws_region) }}
steps:
- uses: aws-actions/[email protected]
with:
role-to-assume: ${{ secrets.INTEG_TEST_LAMBDA_ROLE_ARN }}
role-duration-seconds: 1200
aws-region: us-east-1
- name: Get layer ARN by substituting `${{ matrix.architecture }}` into Soak Test ARN keyword
run: |
echo LAYER_ARN=$(echo "${{ github.event.inputs.layer_arn_keyword }}" | sed 's/<ARCHITECTURE>/${{ matrix.architecture }}/') | tee --append $GITHUB_ENV
- name: Get layer name by substituting `${{ matrix.architecture }}` into Workflow Input name keyword
run: |
echo LAYER_NAME=$(echo "${{ github.event.inputs.layer_name_keyword }}" | sed 's/<ARCHITECTURE>/${{ matrix.architecture }}/') | tee --append $GITHUB_ENV
- name: Get layer kind by parsing `${{ github.event.inputs.layer_name_keyword }}`
run: |
echo LAYER_KIND=$(echo "${{ github.event.inputs.layer_name_keyword }}" | cut -d - -f 3) | tee --append $GITHUB_ENV
- name: download layer from source
run: |
URL=$(aws lambda get-layer-version-by-arn --arn ${{ env.LAYER_ARN }} --query Content.Location --output text)
curl $URL -o layer.zip
# switch to prod
- uses: aws-actions/[email protected]
with:
role-to-assume: ${{ secrets.PROD_LAMBDA_ROLE_ARN }}
role-duration-seconds: 1200
aws-region: ${{ matrix.aws_region }}
mask-aws-account-id: false
- name: Get bucket name for release run
run: |
echo BUCKET_NAME=publish-${{ env.LAYER_KIND }}-${{ matrix.architecture }}-${{ github.run_id }}-${{ matrix.aws_region }} | tee --append $GITHUB_ENV
- name: publish
run: |
aws s3 mb s3://${{ env.BUCKET_NAME }}
aws s3 cp layer.zip s3://${{ env.BUCKET_NAME }}
layerARN=$(
aws lambda publish-layer-version \
--layer-name ${{ env.LAYER_NAME }} \
--content S3Bucket=${{ env.BUCKET_NAME }},S3Key=layer.zip \
--query 'LayerVersionArn' \
--output text
)
echo $layerARN
mkdir ${{ env.LAYER_NAME }}
echo $layerARN > ${{ env.LAYER_NAME }}/${{ matrix.aws_region }}
cat ${{ env.LAYER_NAME }}/${{ matrix.aws_region }}
- name: public layer
run: |
layerVersion=$(
aws lambda list-layer-versions \
--layer-name ${{ env.LAYER_NAME }} \
--query 'max_by(LayerVersions, &Version).Version'
)
aws lambda add-layer-version-permission \
--layer-name ${{ env.LAYER_NAME }} \
--version-number $layerVersion \
--principal "*" \
--statement-id publish \
--action lambda:GetLayerVersion
- name: upload layer arn artifact
if: ${{ success() }}
uses: actions/upload-artifact@v4
with:
name: ${{ env.LAYER_NAME }}
path: ${{ env.LAYER_NAME }}/${{ matrix.aws_region }}
- name: clean s3
if: always()
run: |
aws s3 rb --force s3://${{ env.BUCKET_NAME }}
generate-note:
runs-on: ubuntu-latest
needs: publish-prod
strategy:
matrix:
architecture: ${{ fromJson(github.event.inputs.architecture) }}
steps:
- uses: hashicorp/setup-terraform@v2
- name: Get layer name by substituting `${{ matrix.architecture }}` into Workflow Input name keyword
run: |
echo LAYER_NAME=$(echo "${{ github.event.inputs.layer_name_keyword }}" | sed 's/<ARCHITECTURE>/${{ matrix.architecture }}/') | tee --append $GITHUB_ENV
- name: Get layer kind by parsing `${{ github.event.inputs.layer_name_keyword }}`
run: |
echo LAYER_KIND=$(echo "${{ github.event.inputs.layer_name_keyword }}" | cut -d - -f 3) | tee --append $GITHUB_ENV
- name: download layerARNs
uses: actions/download-artifact@v4
with:
name: ${{ env.LAYER_NAME }}
path: ${{ env.LAYER_NAME }}
- name: show layerARNs
run: |
for file in ${{ env.LAYER_NAME }}/*
do
echo $file
cat $file
done
- name: generate layer-note
working-directory: ${{ env.LAYER_NAME }}
run: |
echo "| Region | Layer ARN |" >> ../layer-note
echo "| ---- | ---- |" >> ../layer-note
for file in *
do
read arn < $file
echo "| " $file " | " $arn " |" >> ../layer-note
done
cd ..
cat layer-note
- name: generate tf layer
working-directory: ${{ env.LAYER_NAME }}
run: |
echo "locals {" >> ../layer.tf
if [ "${{ env.LAYER_KIND }}" != 'collector' ]
then
echo " sdk_layer_arns_${{ matrix.architecture }} = {" >> ../layer.tf
else
echo " collector_layer_arns_${{ matrix.architecture }} = {" >> ../layer.tf
fi
for file in *
do
read arn < $file
echo " \""$file"\" = \""$arn"\"" >> ../layer.tf
done
cd ..
echo " }" >> layer.tf
echo "}" >> layer.tf
terraform fmt layer.tf
cat layer.tf
- name: upload layer tf file
uses: actions/upload-artifact@v4
with:
name: layer_${{ matrix.architecture }}.tf
path: layer.tf
smoke-test:
name: Smoke Test - (${{ matrix.aws_region }} - ${{ github.event.inputs.layer_name_keyword }} - ${{ matrix.architecture }})
needs: generate-note
runs-on: ubuntu-latest
strategy:
matrix:
architecture: ${{ fromJson(github.event.inputs.architecture) }}
aws_region: ${{ fromJson(github.event.inputs.aws_region) }}
steps:
- name: Parse smoke test values from layer name - ${{ github.event.inputs.layer_name_keyword }}
# FIXME: (enowell) You can only Smoke Test 1 Sample App with this
# design. However, there are multiple Sample Apps that test the same
# Java Wrapper Lambda Layer.
#
# i.e.
# - `aws-sdk` and `okhttp` both test the `java-wrapper` layer but we
# can only test one (`aws-sdk`).
# - `go` and `dotnet` both test the `collector` layer but we can only
# test one (`go`).
run: |
LAYER_KIND=$(echo "${{ github.event.inputs.layer_name_keyword }}" | cut -d - -f 3)
if [ "$LAYER_KIND" = "python" ]; then
TEST_LANGUAGE=python
elif [ "$LAYER_KIND" = "collector" ]; then
TEST_LANGUAGE=go
else
TEST_LANGUAGE=$LAYER_KIND
fi
echo TEST_LANGUAGE=$TEST_LANGUAGE | tee --append $GITHUB_ENV
LAYER_INSTR_TYPE=$(echo "${{ github.event.inputs.layer_name_keyword }}" | cut -d - -f 4)
if [ "$LAYER_INSTR_TYPE" = "agent" ] || [ "$LAYER_INSTR_TYPE" = "wrapper" ]; then
TEST_INSTR_TYPE=$LAYER_INSTR_TYPE
else
TEST_INSTR_TYPE="wrapper"
fi
echo TEST_INSTR_TYPE=$TEST_INSTR_TYPE | tee --append $GITHUB_ENV
- uses: actions/checkout@v4
with:
submodules: recursive
- uses: actions/setup-java@v4
if: ${{ env.TEST_LANGUAGE == 'java' }}
with:
distribution: corretto
java-version: '17'
- name: Cache (Java)
uses: actions/cache@v3
if: ${{ env.TEST_LANGUAGE == 'java' }}
with:
path: |
~/go/pkg/mod
~/.gradle/caches
~/.gradle/wrapper
key: ${{ runner.os }}-gradle-${{ hashFiles('**/*.gradle*', '**/gradle-wrapper.properties') }}-go-${{ hashFiles('**/go.sum') }}
restore-keys: |
${{ runner.os }}-gradle-
- uses: actions/setup-node@v4
if: ${{ env.TEST_LANGUAGE == 'nodejs' }}
with:
node-version: '16'
- name: Cache (NodeJS)
uses: actions/cache@v3
if: ${{ env.TEST_LANGUAGE == 'nodejs' }}
with:
path: |
~/go/pkg/mod
~/.npm
key: ${{ runner.os }}-node-${{ hashFiles('**/package.json') }}-go-${{ hashFiles('**/go.sum') }}
restore-keys: |
${{ runner.os }}-node-
- uses: actions/setup-python@v5
if: ${{ env.TEST_LANGUAGE == 'python' }}
with:
python-version: '3.x'
- name: Cache (Python)
uses: actions/cache@v3
if: ${{ env.TEST_LANGUAGE == 'python' }}
with:
path: |
~/go/pkg/mod
~/.cache/pip
key: ${{ runner.os }}-pip-${{ hashFiles('**/requirements.txt') }}-go-${{ hashFiles('**/go.sum') }}
restore-keys: |
${{ runner.os }}-pip-
# FIXME: (enowell) Same as above - .NET uses the collector layer, but we
# only test Go with the collector layer so comment out for now.
# - name: Use .NET Language
# uses: actions/setup-dotnet@v1
# if: ${{ env.TEST_LANGUAGE == 'dotnet' }}
# with:
# dotnet-version: '3.1.x'
- name: Use Go Language
uses: actions/setup-go@v5
# NOTE: (enowell) In case the languages below need to build the
# collector, and because building the collector requires go 1.18 and
# above, always setup go 1.18.
# if: ${{ env.TEST_LANGUAGE == 'go' }}
with:
go-version: '~1.21.10'
check-latest: true
- name: download layer tf file
uses: actions/download-artifact@v4
with:
name: layer_${{ matrix.architecture }}.tf
- name: Get terraform directory
run: |
echo TERRAFORM_DIRECTORY=${{ env.TEST_LANGUAGE }}/sample-apps/aws-sdk/deploy/${{ env.TEST_INSTR_TYPE }} |
tee --append $GITHUB_ENV
- name: overwrite layer.tf
run: |
OLD_LAYER_TF_FILE=layer_${{ matrix.architecture }}.tf
cat ${{ env.TERRAFORM_DIRECTORY }}/$OLD_LAYER_TF_FILE
mv -f layer.tf ${{ env.TERRAFORM_DIRECTORY }}/$OLD_LAYER_TF_FILE
cat ${{ env.TERRAFORM_DIRECTORY }}/$OLD_LAYER_TF_FILE
# NOTE: (enowell) This builds BOTH the sample app functions AND the
# layer. But we only use the sample app build in the release workflow.
# Also, we NEED the architecture value because some apps (like .NET and
# Go) depend on this architecture value.
- name: Build functions
run: GOARCH=${{ matrix.architecture }} ./build.sh ${{ matrix.architecture }}
working-directory: ${{ env.TEST_LANGUAGE }}
- uses: aws-actions/[email protected]
with:
role-to-assume: ${{ secrets.INTEG_TEST_LAMBDA_ROLE_ARN }}
role-duration-seconds: 1200
aws-region: ${{ matrix.aws_region }}
- uses: hashicorp/setup-terraform@v2
- name: Initialize terraform
run: terraform init
working-directory: ${{ env.TERRAFORM_DIRECTORY }}
- name: Get Lambda Layer `amd64` architecture value
if: ${{ matrix.architecture == 'amd64' }}
run: echo LAMBDA_FUNCTION_ARCH=x86_64 | tee --append $GITHUB_ENV
- name: Get Lambda Layer `arm64` architecture value
if: ${{ matrix.architecture == 'arm64' }}
run: echo LAMBDA_FUNCTION_ARCH=arm64 | tee --append $GITHUB_ENV
- name: Apply terraform
run: terraform apply -auto-approve
working-directory: ${{ env.TERRAFORM_DIRECTORY }}
env:
TF_VAR_function_name: lambda-${{ env.TEST_LANGUAGE }}-aws-sdk-${{ env.TEST_INSTR_TYPE }}-${{ matrix.architecture }}-${{ github.run_id }}-${{ matrix.aws_region }}
TF_VAR_architecture: ${{ env.LAMBDA_FUNCTION_ARCH }}
- name: Extract endpoint
id: extract-endpoint
run: terraform output -raw api-gateway-url
working-directory: ${{ env.TERRAFORM_DIRECTORY }}
- name: Extract AMP endpoint
if: ${{ env.TEST_LANGUAGE == 'java' && env.TEST_INSTR_TYPE == 'agent' && contains(env.AMP_REGIONS, matrix.aws_region) }}
id: extract-amp-endpoint
run: terraform output -raw amp_endpoint
working-directory: ${{ env.TERRAFORM_DIRECTORY }}
- name: Send request to endpoint
run: curl -sS ${{ steps.extract-endpoint.outputs.stdout }}
- name: Checkout test framework
uses: actions/checkout@v4
with:
repository: aws-observability/aws-otel-test-framework
path: test-framework
- name: validate trace sample
run: |
cp adot/utils/expected-templates/${{ env.TEST_LANGUAGE }}-aws-sdk-${{ env.TEST_INSTR_TYPE }}.json test-framework/validator/src/main/resources/expected-data-template/lambdaExpectedTrace.mustache
cd test-framework
./gradlew :validator:run --args="-c default-lambda-validation.yml --endpoint ${{ steps.extract-endpoint.outputs.stdout }} --region $AWS_REGION"
- name: validate java agent metric sample
if: ${{ env.TEST_LANGUAGE == 'java' && env.TEST_INSTR_TYPE == 'agent' && contains(env.AMP_REGIONS, matrix.aws_region) }}
run: |
cp adot/utils/expected-templates/${{ env.TEST_LANGUAGE }}-aws-sdk-${{ env.TEST_INSTR_TYPE }}-metric.json test-framework/validator/src/main/resources/expected-data-template/ampExpectedMetric.mustache
cd test-framework
./gradlew :validator:run --args="-c prometheus-static-metric-validation.yml --cortex-instance-endpoint ${{ steps.extract-amp-endpoint.outputs.stdout }} --region $AWS_REGION"
- name: Destroy terraform
if: always()
run: terraform destroy -auto-approve
working-directory: ${{ env.TERRAFORM_DIRECTORY }}
env:
TF_VAR_architecture: ${{ env.LAMBDA_FUNCTION_ARCH }}