|
| 1 | +name: Deploy V3 SAR |
| 2 | + |
| 3 | +# PROCESS |
| 4 | +# |
| 5 | +# 1. This workflow starts after the layer artifact is produced on `publish_v3_layer` |
| 6 | +# 2. We use the same layer artifact to ensure the SAR app is consistent with the published Lambda Layer |
| 7 | +# 3. We publish the SAR for both x86_64 and arm64 (see `matrix` section) |
| 8 | +# 4. We use `sam package` and `sam publish` to publish the SAR app |
| 9 | +# 5. We remove the previous Canary stack (if present) and deploy a new one to test the SAR App. We retain the Canary in the account for debugging purposes |
| 10 | +# 6. Finally the published SAR app is made public on the PROD environment |
| 11 | + |
| 12 | +# USAGE |
| 13 | +# |
| 14 | +# NOTE: meant to be used with ./.github/workflows/publish_v2_layer.yml |
| 15 | +# |
| 16 | +# sar-beta: |
| 17 | +# needs: build-layer |
| 18 | +# permissions: |
| 19 | +# # lower privilege propagated from parent workflow (release.yml) |
| 20 | +# id-token: write |
| 21 | +# contents: read |
| 22 | +# pull-requests: none |
| 23 | +# pages: none |
| 24 | +# uses: ./.github/workflows/reusable_deploy_v3_sar.yml |
| 25 | +# secrets: inherit |
| 26 | +# with: |
| 27 | +# stage: "BETA" |
| 28 | +# artefact-name: "cdk-layer-artefact" |
| 29 | +# environment: "layer-beta" |
| 30 | +# package-version: ${{ inputs.latest_published_version }} |
| 31 | +# source_code_artifact_name: ${{ inputs.source_code_artifact_name }} |
| 32 | +# source_code_integrity_hash: ${{ inputs.source_code_integrity_hash }} |
| 33 | + |
| 34 | +permissions: |
| 35 | + id-token: write |
| 36 | + contents: read |
| 37 | + |
| 38 | +env: |
| 39 | + NODE_VERSION: 16.12 |
| 40 | + AWS_REGION: eu-west-1 |
| 41 | + SAR_NAME: aws-lambda-powertools-python-layer |
| 42 | + TEST_STACK_NAME: serverlessrepo-v2-powertools-layer-test-stack |
| 43 | + RELEASE_COMMIT: ${{ github.sha }} # it gets propagated from the caller for security reasons |
| 44 | + |
| 45 | +on: |
| 46 | + workflow_call: |
| 47 | + inputs: |
| 48 | + stage: |
| 49 | + description: "Deployment stage (BETA, PROD)" |
| 50 | + required: true |
| 51 | + type: string |
| 52 | + artefact-name: |
| 53 | + description: "CDK Layer Artefact name to download" |
| 54 | + required: true |
| 55 | + type: string |
| 56 | + package-version: |
| 57 | + description: "The version of the package to deploy" |
| 58 | + required: true |
| 59 | + type: string |
| 60 | + environment: |
| 61 | + description: "GitHub Environment to use for encrypted secrets" |
| 62 | + required: true |
| 63 | + type: string |
| 64 | + source_code_artifact_name: |
| 65 | + description: "Artifact name to restore sealed source code" |
| 66 | + type: string |
| 67 | + required: true |
| 68 | + source_code_integrity_hash: |
| 69 | + description: "Sealed source code integrity hash" |
| 70 | + type: string |
| 71 | + required: true |
| 72 | + |
| 73 | +jobs: |
| 74 | + deploy-sar-app: |
| 75 | + runs-on: ubuntu-latest |
| 76 | + environment: ${{ inputs.environment }} |
| 77 | + strategy: |
| 78 | + matrix: |
| 79 | + architecture: ["x86_64", "arm64"] |
| 80 | + steps: |
| 81 | + - name: checkout |
| 82 | + uses: actions/checkout@a5ac7e51b41094c92402da3b24376905380afc29 # v4.1.6 |
| 83 | + with: |
| 84 | + ref: ${{ env.RELEASE_COMMIT }} |
| 85 | + |
| 86 | + - name: Restore sealed source code |
| 87 | + uses: ./.github/actions/seal-restore |
| 88 | + with: |
| 89 | + integrity_hash: ${{ inputs.source_code_integrity_hash }} |
| 90 | + artifact_name: ${{ inputs.source_code_artifact_name }} |
| 91 | + |
| 92 | + |
| 93 | + - name: AWS credentials |
| 94 | + uses: aws-actions/configure-aws-credentials@5fd3084fc36e372ff1fff382a39b10d03659f355 # v2.2.0 |
| 95 | + with: |
| 96 | + aws-region: ${{ env.AWS_REGION }} |
| 97 | + role-to-assume: ${{ secrets.AWS_LAYERS_ROLE_ARN }} |
| 98 | + |
| 99 | + # NOTE |
| 100 | + # We connect to Layers account to log our intent to publish a SAR Layer |
| 101 | + # we then jump to our specific SAR Account with the correctly scoped IAM Role |
| 102 | + # this allows us to have a single trail when a release occurs for a given layer (beta+prod+SAR beta+SAR prod) |
| 103 | + - name: AWS credentials SAR role |
| 104 | + uses: aws-actions/configure-aws-credentials@5fd3084fc36e372ff1fff382a39b10d03659f355 # v2.2.0 |
| 105 | + id: aws-credentials-sar-role |
| 106 | + with: |
| 107 | + aws-access-key-id: ${{ env.AWS_ACCESS_KEY_ID }} |
| 108 | + aws-secret-access-key: ${{ env.AWS_SECRET_ACCESS_KEY }} |
| 109 | + aws-session-token: ${{ env.AWS_SESSION_TOKEN }} |
| 110 | + role-duration-seconds: 1200 |
| 111 | + aws-region: ${{ env.AWS_REGION }} |
| 112 | + role-to-assume: ${{ secrets.AWS_SAR_V2_ROLE_ARN }} |
| 113 | + - name: Setup Node.js |
| 114 | + uses: actions/setup-node@60edb5dd545a775178f52524783378180af0d1f8 # v4.0.2 |
| 115 | + with: |
| 116 | + node-version: ${{ env.NODE_VERSION }} |
| 117 | + - name: Download artifact |
| 118 | + uses: actions/download-artifact@65a9edc5881444af0b9093a5e628f2fe47ea3b2e # v4.1.7 |
| 119 | + with: |
| 120 | + name: ${{ inputs.artefact-name }} |
| 121 | + - name: Unzip artefact |
| 122 | + run: unzip cdk.out.zip |
| 123 | + - name: Configure SAR name |
| 124 | + run: | |
| 125 | + if [[ "${{ inputs.stage }}" == "BETA" ]]; then |
| 126 | + SAR_NAME="test-${SAR_NAME}" |
| 127 | + fi |
| 128 | + echo SAR_NAME="${SAR_NAME}" >> "$GITHUB_ENV" |
| 129 | + - name: Adds arm64 suffix to SAR name |
| 130 | + if: ${{ matrix.architecture == 'arm64' }} |
| 131 | + run: echo SAR_NAME="${SAR_NAME}-arm64" >> "$GITHUB_ENV" |
| 132 | + - name: Normalize semantic version |
| 133 | + id: semantic-version # v2.0.0a0 -> v2.0.0-a0 |
| 134 | + env: |
| 135 | + VERSION: ${{ inputs.package-version }} |
| 136 | + run: | |
| 137 | + VERSION="${VERSION/a/-a}" |
| 138 | + echo "VERSION=${VERSION}" >> "$GITHUB_OUTPUT" |
| 139 | + - name: Prepare SAR App |
| 140 | + env: |
| 141 | + VERSION: ${{ steps.semantic-version.outputs.VERSION }} |
| 142 | + run: | |
| 143 | + # From the generated LayerStack cdk.out artifact, find the layer asset path for the correct architecture. |
| 144 | + # We'll use this as the source directory of our SAR. This way we are re-using the same layer asset for our SAR. |
| 145 | + asset=$(jq -jc '.Resources[] | select(.Properties.CompatibleArchitectures == ["${{ matrix.architecture }}"]) | .Metadata."aws:asset:path"' cdk.out/LayerV2Stack.template.json) |
| 146 | +
|
| 147 | + # fill in the SAR SAM template |
| 148 | + sed \ |
| 149 | + -e "s|<VERSION>|${VERSION}|g" \ |
| 150 | + -e "s/<SAR_APP_NAME>/${{ env.SAR_NAME }}/g" \ |
| 151 | + -e "s|<LAYER_CONTENT_PATH>|./cdk.out/$asset|g" \ |
| 152 | + layer/sar/template.txt > template.yml |
| 153 | +
|
| 154 | + # SAR needs a README and a LICENSE, so just copy the ones from the repo |
| 155 | + cp README.md LICENSE "./cdk.out/$asset/" |
| 156 | +
|
| 157 | + # Debug purposes |
| 158 | + cat template.yml |
| 159 | + - name: Deploy SAR |
| 160 | + run: | |
| 161 | + # Package the SAR to our SAR S3 bucket, and publish it |
| 162 | + sam package --template-file template.yml --output-template-file packaged.yml --s3-bucket ${{ secrets.AWS_SAR_S3_BUCKET }} |
| 163 | + sam publish --template packaged.yml --region "$AWS_REGION" |
| 164 | + - name: Deploy BETA canary |
| 165 | + if: ${{ inputs.stage == 'BETA' }} |
| 166 | + run: | |
| 167 | + if [[ "${{ matrix.architecture }}" == "arm64" ]]; then |
| 168 | + TEST_STACK_NAME="${TEST_STACK_NAME}-arm64" |
| 169 | + fi |
| 170 | +
|
| 171 | + echo "Check if stack does not exist" |
| 172 | + stack_exists=$(aws cloudformation list-stacks --query "StackSummaries[?(StackName == '$TEST_STACK_NAME' && StackStatus == 'CREATE_COMPLETE')].{StackId:StackId, StackName:StackName, CreationTime:CreationTime, StackStatus:StackStatus}" --output text) |
| 173 | +
|
| 174 | + if [[ -n "$stack_exists" ]] ; then |
| 175 | + echo "Found test deployment stack, removing..." |
| 176 | + aws cloudformation delete-stack --stack-name "$TEST_STACK_NAME" |
| 177 | + aws cloudformation wait stack-delete-complete --stack-name "$TEST_STACK_NAME" |
| 178 | + fi |
| 179 | +
|
| 180 | + echo "Creating canary stack" |
| 181 | + echo "Stack name: $TEST_STACK_NAME" |
| 182 | + aws serverlessrepo create-cloud-formation-change-set \ |
| 183 | + --application-id arn:aws:serverlessrepo:${{ env.AWS_REGION }}:${{ steps.aws-credentials-sar-role.outputs.aws-account-id }}:applications/${{ env.SAR_NAME }} \ |
| 184 | + --stack-name "${TEST_STACK_NAME/serverlessrepo-/}" \ |
| 185 | + --capabilities CAPABILITY_NAMED_IAM |
| 186 | +
|
| 187 | + CHANGE_SET_ID=$(aws cloudformation list-change-sets --stack-name "$TEST_STACK_NAME" --query 'Summaries[*].ChangeSetId' --output text) |
| 188 | + aws cloudformation wait change-set-create-complete --change-set-name "$CHANGE_SET_ID" |
| 189 | + aws cloudformation execute-change-set --change-set-name "$CHANGE_SET_ID" |
| 190 | + aws cloudformation wait stack-create-complete --stack-name "$TEST_STACK_NAME" |
| 191 | +
|
| 192 | + echo "Waiting until stack deployment completes..." |
| 193 | +
|
| 194 | + echo "Exit with error if stack is not in CREATE_COMPLETE" |
| 195 | + stack_exists=$(aws cloudformation list-stacks --query "StackSummaries[?(StackName == '$TEST_STACK_NAME' && StackStatus == 'CREATE_COMPLETE')].{StackId:StackId, StackName:StackName, CreationTime:CreationTime, StackStatus:StackStatus}") |
| 196 | + if [[ -z "$stack_exists" ]] ; then |
| 197 | + echo "Could find successful deployment, exit error..." |
| 198 | + exit 1 |
| 199 | + fi |
| 200 | + echo "Deployment successful" |
| 201 | + - name: Publish SAR |
| 202 | + if: ${{ inputs.stage == 'PROD' }} |
| 203 | + run: | |
| 204 | + # wait until SAR registers the app, otherwise it fails to make it public |
| 205 | + sleep 15 |
| 206 | + echo "Make SAR app public" |
| 207 | + aws serverlessrepo put-application-policy \ |
| 208 | + --application-id arn:aws:serverlessrepo:${{ env.AWS_REGION }}:${{ steps.aws-credentials-sar-role.outputs.aws-account-id }}:applications/${{ env.SAR_NAME }} \ |
| 209 | + --statements Principals='*',Actions=Deploy |
0 commit comments