From a3f06d2cca77d02fd9109eaaa8f143b174f44c40 Mon Sep 17 00:00:00 2001 From: Russell Cohen Date: Mon, 30 Jan 2023 14:25:43 -0500 Subject: [PATCH 01/24] Use docker login (wip) --- .github/workflows/ci-pr.yml | 40 +++++++++++++++++++++++++++++++++++-- 1 file changed, 38 insertions(+), 2 deletions(-) diff --git a/.github/workflows/ci-pr.yml b/.github/workflows/ci-pr.yml index c0713225e0..3302f5c7ec 100644 --- a/.github/workflows/ci-pr.yml +++ b/.github/workflows/ci-pr.yml @@ -16,7 +16,43 @@ jobs: # This job detects if the PR made changes to build tools. If it did, then it builds a new # build Docker image. Otherwise, it downloads a build image from Public ECR. In both cases, # it uploads the image as a build artifact for other jobs to download and use. + acquire-base-image-with-login: + permissions: + id-token: write + contents: read + continue-on-error: true + name: Acquire Base Image + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + with: + path: smithy-rs + fetch-depth: 0 + - name: Acquire credentials (optional) + continue-on-error: true + uses: aws-actions/configure-aws-credentials@v1-node16 + with: + role-to-assume: ${{ secrets.SMITHY_RS_PUBLIC_ECR_PUSH_ROLE_ARN }} + role-session-name: GitHubActions + aws-region: us-west-2 + - name: Login to ECR + continue-on-error: true + run: | + aws ecr-public get-login-password --region us-east-1 | docker login --username AWS --password-stdin public.ecr.aws + - name: Acquire base image + id: acquire + env: + DOCKER_BUILDKIT: 1 + run: ./smithy-rs/tools/ci-build/acquire-build-image + - name: Upload base image + uses: actions/upload-artifact@v3 + with: + name: smithy-rs-base-image + path: smithy-rs-base-image + retention-days: 1 + acquire-base-image: + continue-on-error: true name: Acquire Base Image runs-on: ubuntu-latest steps: @@ -38,7 +74,7 @@ jobs: # Run shared CI after the Docker build image has either been rebuilt or found in ECR ci: - needs: acquire-base-image + needs: acquire-base-image-with-login uses: ./.github/workflows/ci.yml with: run_sdk_examples: true @@ -46,7 +82,7 @@ jobs: # The PR bot requires a Docker build image, so make it depend on the `acquire-base-image` job. pr_bot: name: PR Bot - needs: acquire-base-image + needs: acquire-base-image-with-login # Only run this job on pull requests (not directly on main) if: ${{ github.head_ref }} uses: ./.github/workflows/pull-request-bot.yml From e6a9a018a840d121321c1f7f0e252fcd5ab109d0 Mon Sep 17 00:00:00 2001 From: Russell Cohen Date: Mon, 30 Jan 2023 14:41:31 -0500 Subject: [PATCH 02/24] setup job hierarchy --- .github/workflows/ci-pr.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/ci-pr.yml b/.github/workflows/ci-pr.yml index 3302f5c7ec..56c16def2f 100644 --- a/.github/workflows/ci-pr.yml +++ b/.github/workflows/ci-pr.yml @@ -52,8 +52,8 @@ jobs: retention-days: 1 acquire-base-image: - continue-on-error: true name: Acquire Base Image + needs: acquire-base-image runs-on: ubuntu-latest steps: - uses: actions/checkout@v3 @@ -74,7 +74,7 @@ jobs: # Run shared CI after the Docker build image has either been rebuilt or found in ECR ci: - needs: acquire-base-image-with-login + needs: acquire-base-image uses: ./.github/workflows/ci.yml with: run_sdk_examples: true @@ -82,7 +82,7 @@ jobs: # The PR bot requires a Docker build image, so make it depend on the `acquire-base-image` job. pr_bot: name: PR Bot - needs: acquire-base-image-with-login + needs: acquire-base-image # Only run this job on pull requests (not directly on main) if: ${{ github.head_ref }} uses: ./.github/workflows/pull-request-bot.yml From 01cab224780863567edac6d1905f04b4b05d144d Mon Sep 17 00:00:00 2001 From: Russell Cohen Date: Mon, 30 Jan 2023 15:05:13 -0500 Subject: [PATCH 03/24] Move save code to always run, fix throttling backoff --- .github/workflows/ci-pr.yml | 36 ------------------------------ tools/ci-build/acquire-build-image | 17 +++++++------- 2 files changed, 9 insertions(+), 44 deletions(-) diff --git a/.github/workflows/ci-pr.yml b/.github/workflows/ci-pr.yml index 56c16def2f..c0713225e0 100644 --- a/.github/workflows/ci-pr.yml +++ b/.github/workflows/ci-pr.yml @@ -16,44 +16,8 @@ jobs: # This job detects if the PR made changes to build tools. If it did, then it builds a new # build Docker image. Otherwise, it downloads a build image from Public ECR. In both cases, # it uploads the image as a build artifact for other jobs to download and use. - acquire-base-image-with-login: - permissions: - id-token: write - contents: read - continue-on-error: true - name: Acquire Base Image - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v3 - with: - path: smithy-rs - fetch-depth: 0 - - name: Acquire credentials (optional) - continue-on-error: true - uses: aws-actions/configure-aws-credentials@v1-node16 - with: - role-to-assume: ${{ secrets.SMITHY_RS_PUBLIC_ECR_PUSH_ROLE_ARN }} - role-session-name: GitHubActions - aws-region: us-west-2 - - name: Login to ECR - continue-on-error: true - run: | - aws ecr-public get-login-password --region us-east-1 | docker login --username AWS --password-stdin public.ecr.aws - - name: Acquire base image - id: acquire - env: - DOCKER_BUILDKIT: 1 - run: ./smithy-rs/tools/ci-build/acquire-build-image - - name: Upload base image - uses: actions/upload-artifact@v3 - with: - name: smithy-rs-base-image - path: smithy-rs-base-image - retention-days: 1 - acquire-base-image: name: Acquire Base Image - needs: acquire-base-image runs-on: ubuntu-latest steps: - uses: actions/checkout@v3 diff --git a/tools/ci-build/acquire-build-image b/tools/ci-build/acquire-build-image index 7c4bbd7a62..be1a1cc822 100755 --- a/tools/ci-build/acquire-build-image +++ b/tools/ci-build/acquire-build-image @@ -94,7 +94,7 @@ class Shell: print("-------------------") not_found_message = "not found: manifest unknown" - throttle_message = "toomanyrequests: Rate exceeded" + throttle_message = "toomanyrequests:" retryable_messages = ["net/http: TLS handshake timeout"] if status == 0: return DockerPullResult.SUCCESS @@ -185,16 +185,17 @@ def acquire_build_image(context=Context.default(), shell=Shell()): announce("Building a new image locally.") shell.docker_build_base_image(context.image_tag, context.tools_path) - if context.github_actions: - announce("Saving base image for use in later jobs...") - shell.docker_save( - LOCAL_BASE_IMAGE_NAME, - context.image_tag, - context.start_path + "/smithy-rs-base-image" - ) else: announce("Successfully pulled remote image!") shell.docker_tag(REMOTE_BASE_IMAGE_NAME, context.image_tag, LOCAL_BASE_IMAGE_NAME, context.image_tag) + + if context.github_actions: + announce("Saving base image for use in later jobs...") + shell.docker_save( + LOCAL_BASE_IMAGE_NAME, + context.image_tag, + context.start_path + "/smithy-rs-base-image" + ) else: announce("Base image found locally! No retrieval or rebuild necessary.") From b232210bd7bbe0214fc3f9ef2c099b4ead643733 Mon Sep 17 00:00:00 2001 From: Russell Cohen Date: Mon, 30 Jan 2023 15:07:43 -0500 Subject: [PATCH 04/24] use ECR login --- .github/workflows/ci-pr.yml | 36 ++++++++++++++++++++++++++++++++++++ 1 file changed, 36 insertions(+) diff --git a/.github/workflows/ci-pr.yml b/.github/workflows/ci-pr.yml index c0713225e0..4b9ee25745 100644 --- a/.github/workflows/ci-pr.yml +++ b/.github/workflows/ci-pr.yml @@ -16,8 +16,44 @@ jobs: # This job detects if the PR made changes to build tools. If it did, then it builds a new # build Docker image. Otherwise, it downloads a build image from Public ECR. In both cases, # it uploads the image as a build artifact for other jobs to download and use. + acquire-base-image-with-login: + permissions: + id-token: write + contents: read + continue-on-error: true + name: Acquire Base Image (with ECR login) + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + with: + path: smithy-rs + fetch-depth: 0 + - name: Acquire credentials (optional) + continue-on-error: true + uses: aws-actions/configure-aws-credentials@v1-node16 + with: + role-to-assume: ${{ secrets.SMITHY_RS_PUBLIC_ECR_PUSH_ROLE_ARN }} + role-session-name: GitHubActions + aws-region: us-west-2 + - name: Login to ECR + continue-on-error: true + run: | + aws ecr-public get-login-password --region us-east-1 | docker login --username AWS --password-stdin public.ecr.aws + - name: Acquire base image + id: acquire + env: + DOCKER_BUILDKIT: 1 + run: ./smithy-rs/tools/ci-build/acquire-build-image + - name: Upload base image + uses: actions/upload-artifact@v3 + with: + name: smithy-rs-base-image + path: smithy-rs-base-image + retention-days: 1 + acquire-base-image: name: Acquire Base Image + needs: acquire-base-image runs-on: ubuntu-latest steps: - uses: actions/checkout@v3 From 2d34997056f5cfe14e35ad1e836ac0d9b9861a7f Mon Sep 17 00:00:00 2001 From: Russell Cohen Date: Mon, 30 Jan 2023 15:25:10 -0500 Subject: [PATCH 05/24] hax --- tools/ci-build/tools-hash | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tools/ci-build/tools-hash b/tools/ci-build/tools-hash index 55b532332d..a78249dc84 100755 --- a/tools/ci-build/tools-hash +++ b/tools/ci-build/tools-hash @@ -11,4 +11,5 @@ set -eo pipefail cd "$(dirname "$0")" cd "$(git rev-parse --show-toplevel)" -git ls-files -s --full-name "tools" | git hash-object --stdin +# git ls-files -s --full-name "tools" | grep -v acquire-build-image | git hash-object --stdin +echo "ef34a35a310a993d1fbc39c0a6e869dda7e80af1" From f40cfa5291f79c007b30670cdc560c299b908f53 Mon Sep 17 00:00:00 2001 From: Russell Cohen Date: Mon, 30 Jan 2023 15:32:06 -0500 Subject: [PATCH 06/24] fix loop --- .github/workflows/ci-pr.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci-pr.yml b/.github/workflows/ci-pr.yml index 4b9ee25745..fb6a3fbb22 100644 --- a/.github/workflows/ci-pr.yml +++ b/.github/workflows/ci-pr.yml @@ -53,7 +53,7 @@ jobs: acquire-base-image: name: Acquire Base Image - needs: acquire-base-image + needs: acquire-base-image-with-login runs-on: ubuntu-latest steps: - uses: actions/checkout@v3 From 0d643148bfe780ec2ad70d470b78a3823a85402a Mon Sep 17 00:00:00 2001 From: Russell Cohen Date: Tue, 31 Jan 2023 11:01:30 -0500 Subject: [PATCH 07/24] fix chmod on script --- tools/ci-build/acquire-build-image | 0 1 file changed, 0 insertions(+), 0 deletions(-) mode change 100644 => 100755 tools/ci-build/acquire-build-image diff --git a/tools/ci-build/acquire-build-image b/tools/ci-build/acquire-build-image old mode 100644 new mode 100755 From badeb7c45ff9843961256878985feff455bf18c7 Mon Sep 17 00:00:00 2001 From: Russell Cohen Date: Tue, 31 Jan 2023 11:27:43 -0500 Subject: [PATCH 08/24] backout changes to acquire-build-image --- tools/ci-build/acquire-build-image | 37 ++++++++++++------------------ 1 file changed, 15 insertions(+), 22 deletions(-) diff --git a/tools/ci-build/acquire-build-image b/tools/ci-build/acquire-build-image index 6aaa854671..7c4bbd7a62 100755 --- a/tools/ci-build/acquire-build-image +++ b/tools/ci-build/acquire-build-image @@ -69,13 +69,6 @@ class Context: return Context(start_path, script_path, tools_path, user_id, image_tag, allow_local_build, github_actions) -def output_contains_any(stdout, stderr, messages): - for message in messages: - if message in stdout or message in stderr: - return True - return False - - # Mockable shell commands class Shell: # Returns the platform that this script is running on @@ -100,18 +93,19 @@ class Shell: print(stderr) print("-------------------") - - not_found_messages = ["not found: manifest unknown"] - throttle_messages = ["toomanyrequests: Rate exceeded", "toomanyrequests: Data limit exceeded"] + not_found_message = "not found: manifest unknown" + throttle_message = "toomanyrequests: Rate exceeded" retryable_messages = ["net/http: TLS handshake timeout"] if status == 0: return DockerPullResult.SUCCESS - elif output_contains_any(stdout, stderr, throttle_messages): + elif throttle_message in stdout or throttle_message in stderr: return DockerPullResult.ERROR_THROTTLED - elif output_contains_any(stdout, stderr, not_found_messages): + elif not_found_message in stdout or not_found_message in stderr: return DockerPullResult.NOT_FOUND - elif output_contains_any(stdout, stderr, retryable_messages): - return DockerPullResult.RETRYABLE_ERROR + else: + for message in retryable_messages: + if message in stdout or message in stderr: + return DockerPullResult.RETRYABLE_ERROR return DockerPullResult.UNKNOWN_ERROR # Builds the base image with the Dockerfile in `path` and tags with with `image_tag` @@ -191,17 +185,16 @@ def acquire_build_image(context=Context.default(), shell=Shell()): announce("Building a new image locally.") shell.docker_build_base_image(context.image_tag, context.tools_path) + if context.github_actions: + announce("Saving base image for use in later jobs...") + shell.docker_save( + LOCAL_BASE_IMAGE_NAME, + context.image_tag, + context.start_path + "/smithy-rs-base-image" + ) else: announce("Successfully pulled remote image!") shell.docker_tag(REMOTE_BASE_IMAGE_NAME, context.image_tag, LOCAL_BASE_IMAGE_NAME, context.image_tag) - - if context.github_actions: - announce("Saving base image for use in later jobs...") - shell.docker_save( - LOCAL_BASE_IMAGE_NAME, - context.image_tag, - context.start_path + "/smithy-rs-base-image" - ) else: announce("Base image found locally! No retrieval or rebuild necessary.") From 2124e9845adf05512f3d8ffb7ef143e9587c5ea0 Mon Sep 17 00:00:00 2001 From: Russell Cohen Date: Tue, 31 Jan 2023 14:17:47 -0500 Subject: [PATCH 09/24] wip save a token --- .github/workflows/ci-pr.yml | 41 ++++++++++++++++--------------------- 1 file changed, 18 insertions(+), 23 deletions(-) diff --git a/.github/workflows/ci-pr.yml b/.github/workflows/ci-pr.yml index fb6a3fbb22..77e846e006 100644 --- a/.github/workflows/ci-pr.yml +++ b/.github/workflows/ci-pr.yml @@ -16,46 +16,41 @@ jobs: # This job detects if the PR made changes to build tools. If it did, then it builds a new # build Docker image. Otherwise, it downloads a build image from Public ECR. In both cases, # it uploads the image as a build artifact for other jobs to download and use. - acquire-base-image-with-login: + save-docker-login-token: + outputs: + docker-login-password: ${{ steps.set-token.outputs.docker-login-password }} permissions: id-token: write contents: read continue-on-error: true - name: Acquire Base Image (with ECR login) + name: Save a docker login token runs-on: ubuntu-latest steps: - - uses: actions/checkout@v3 - with: - path: smithy-rs - fetch-depth: 0 - - name: Acquire credentials (optional) - continue-on-error: true + - name: Attempt to load a docker login password uses: aws-actions/configure-aws-credentials@v1-node16 with: role-to-assume: ${{ secrets.SMITHY_RS_PUBLIC_ECR_PUSH_ROLE_ARN }} role-session-name: GitHubActions aws-region: us-west-2 - - name: Login to ECR - continue-on-error: true + - name: Save the docker login password to the output + id: set-token run: | - aws ecr-public get-login-password --region us-east-1 | docker login --username AWS --password-stdin public.ecr.aws - - name: Acquire base image - id: acquire - env: - DOCKER_BUILDKIT: 1 - run: ./smithy-rs/tools/ci-build/acquire-build-image - - name: Upload base image - uses: actions/upload-artifact@v3 - with: - name: smithy-rs-base-image - path: smithy-rs-base-image - retention-days: 1 + ENCRYPTED_PAYLOAD=$( + gpg --symmetric --batch --passphrase "(AWS_REGION=us-east-1 aws secretsmanager get-secret-value --secret-id github-pgp-key --query SecretString --output text)" --output - <(echo "not-a-real-token") | base64 -w0 + ) + echo "payload: $ENCRYPTED_PAYLOAD" + echo "docker-login-password=$ENCRYPTED_PAYLOAD" >> $GITHUB_OUTPUT + acquire-base-image: name: Acquire Base Image - needs: acquire-base-image-with-login + needs: save-docker-login-token runs-on: ubuntu-latest + env: + ENCRYPTED_DOCKER_PASSWORD: ${{ needs.save-docker-login-token.outputs.docker-login-password }} steps: + - name: debug token + run: echo $ENCRYPTED_DOCKER_PASSWORD - uses: actions/checkout@v3 with: path: smithy-rs From 288f4772d930038a7c8d81f8d8ea1966106ed304 Mon Sep 17 00:00:00 2001 From: Russell Cohen Date: Tue, 31 Jan 2023 15:48:33 -0500 Subject: [PATCH 10/24] Try actually logging in... --- .github/workflows/ci-pr.yml | 3 ++- tools/ci-build/acquire-build-image | 25 +++++++++++++++++++++++-- 2 files changed, 25 insertions(+), 3 deletions(-) diff --git a/.github/workflows/ci-pr.yml b/.github/workflows/ci-pr.yml index 77e846e006..e07674d68d 100644 --- a/.github/workflows/ci-pr.yml +++ b/.github/workflows/ci-pr.yml @@ -36,7 +36,7 @@ jobs: id: set-token run: | ENCRYPTED_PAYLOAD=$( - gpg --symmetric --batch --passphrase "(AWS_REGION=us-east-1 aws secretsmanager get-secret-value --secret-id github-pgp-key --query SecretString --output text)" --output - <(echo "not-a-real-token") | base64 -w0 + gpg --symmetric --batch --passphrase "{{ secrets.DOCKER_LOGIN_TOKEN_PASSPHRASE }}" --output - <(echo "not-a-real-token") | base64 -w0 ) echo "payload: $ENCRYPTED_PAYLOAD" echo "docker-login-password=$ENCRYPTED_PAYLOAD" >> $GITHUB_OUTPUT @@ -48,6 +48,7 @@ jobs: runs-on: ubuntu-latest env: ENCRYPTED_DOCKER_PASSWORD: ${{ needs.save-docker-login-token.outputs.docker-login-password }} + DOCKER_PASSPHRASE: ${{ secrets.DOCKER_LOGIN_TOKEN_PASSPHRASE }} steps: - name: debug token run: echo $ENCRYPTED_DOCKER_PASSWORD diff --git a/tools/ci-build/acquire-build-image b/tools/ci-build/acquire-build-image index 7c4bbd7a62..a24bf1c7b3 100755 --- a/tools/ci-build/acquire-build-image +++ b/tools/ci-build/acquire-build-image @@ -154,17 +154,38 @@ def run(command, cwd=None): # Returns (status, output) from a shell command -def get_cmd_output(command, cwd=None, check=True): +def get_cmd_output(command, cwd=None, check=True, **kwargs): result = subprocess.run( shlex.split(command), capture_output=True, check=check, - cwd=cwd + cwd=cwd, + **kwargs ) return (result.returncode, result.stdout.decode("utf-8").strip(), result.stderr.decode("utf-8").strip()) +def decrypt_and_login(secret): + import base64 + decoded = base64.b64decode(secret, validate=True) + token = os.getenv("DOCKER_PASSPHRASE") + if token is None: + print('no passphrase') + return + (code, password, err) = (get_cmd_output(f"gpg --decrypt --batch --quiet --passphrase {token} --output -", check=False, + input=decoded)) + if code != 0: + print(err) + return + (_, output, _) = get_cmd_output("docker login --username AWS --password-stdin public.ecr.aws", input=password.encode('utf-8')) + print(output) + + def acquire_build_image(context=Context.default(), shell=Shell()): + import os + docker_password = os.getenv("ENCRYPTED_DOCKER_PASSWORD") + if docker_password is not None: + decrypt_and_login(docker_password) # If the image doesn't already exist locally, then look remotely if not shell.docker_image_exists_locally(LOCAL_BASE_IMAGE_NAME, context.image_tag): announce("Base image not found locally.") From fedb329ee661d9b50a883313670648049b0db69f Mon Sep 17 00:00:00 2001 From: Russell Cohen Date: Tue, 31 Jan 2023 16:16:48 -0500 Subject: [PATCH 11/24] bail --- .github/workflows/ci-pr.yml | 2 +- tools/ci-build/acquire-build-image | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/ci-pr.yml b/.github/workflows/ci-pr.yml index e07674d68d..779554d3bd 100644 --- a/.github/workflows/ci-pr.yml +++ b/.github/workflows/ci-pr.yml @@ -36,7 +36,7 @@ jobs: id: set-token run: | ENCRYPTED_PAYLOAD=$( - gpg --symmetric --batch --passphrase "{{ secrets.DOCKER_LOGIN_TOKEN_PASSPHRASE }}" --output - <(echo "not-a-real-token") | base64 -w0 + gpg --symmetric --batch --passphrase "${{ secrets.DOCKER_LOGIN_TOKEN_PASSPHRASE }}" --output - <(echo "not-a-real-token") | base64 -w0 ) echo "payload: $ENCRYPTED_PAYLOAD" echo "docker-login-password=$ENCRYPTED_PAYLOAD" >> $GITHUB_OUTPUT diff --git a/tools/ci-build/acquire-build-image b/tools/ci-build/acquire-build-image index a24bf1c7b3..d1209d9adb 100755 --- a/tools/ci-build/acquire-build-image +++ b/tools/ci-build/acquire-build-image @@ -172,11 +172,11 @@ def decrypt_and_login(secret): if token is None: print('no passphrase') return - (code, password, err) = (get_cmd_output(f"gpg --decrypt --batch --quiet --passphrase {token} --output -", check=False, + (code, password, err) = (get_cmd_output(f"gpg --decrypt --batch --quiet --passphrase '{token}' --output -", check=False, input=decoded)) if code != 0: print(err) - return + raise (_, output, _) = get_cmd_output("docker login --username AWS --password-stdin public.ecr.aws", input=password.encode('utf-8')) print(output) From 85f77532eb706ccd77af2d4043f8729874f23022 Mon Sep 17 00:00:00 2001 From: Russell Cohen Date: Tue, 31 Jan 2023 16:21:07 -0500 Subject: [PATCH 12/24] use a real token --- .github/workflows/ci-pr.yml | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/.github/workflows/ci-pr.yml b/.github/workflows/ci-pr.yml index 779554d3bd..432d667c36 100644 --- a/.github/workflows/ci-pr.yml +++ b/.github/workflows/ci-pr.yml @@ -36,9 +36,8 @@ jobs: id: set-token run: | ENCRYPTED_PAYLOAD=$( - gpg --symmetric --batch --passphrase "${{ secrets.DOCKER_LOGIN_TOKEN_PASSPHRASE }}" --output - <(echo "not-a-real-token") | base64 -w0 + gpg --symmetric --batch --passphrase "${{ secrets.DOCKER_LOGIN_TOKEN_PASSPHRASE }}" --output - <(aws ecr-public get-login-password --region us-east-1) | base64 -w0 ) - echo "payload: $ENCRYPTED_PAYLOAD" echo "docker-login-password=$ENCRYPTED_PAYLOAD" >> $GITHUB_OUTPUT @@ -50,8 +49,6 @@ jobs: ENCRYPTED_DOCKER_PASSWORD: ${{ needs.save-docker-login-token.outputs.docker-login-password }} DOCKER_PASSPHRASE: ${{ secrets.DOCKER_LOGIN_TOKEN_PASSPHRASE }} steps: - - name: debug token - run: echo $ENCRYPTED_DOCKER_PASSWORD - uses: actions/checkout@v3 with: path: smithy-rs From 0a130cda3b00d22400bda4b312b29892747f98a2 Mon Sep 17 00:00:00 2001 From: Russell Cohen Date: Tue, 31 Jan 2023 16:26:55 -0500 Subject: [PATCH 13/24] use a real token for all jobs --- .github/workflows/ci-pr.yml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.github/workflows/ci-pr.yml b/.github/workflows/ci-pr.yml index 432d667c36..c1a284c3cc 100644 --- a/.github/workflows/ci-pr.yml +++ b/.github/workflows/ci-pr.yml @@ -68,6 +68,9 @@ jobs: # Run shared CI after the Docker build image has either been rebuilt or found in ECR ci: needs: acquire-base-image + env: + ENCRYPTED_DOCKER_PASSWORD: ${{ needs.save-docker-login-token.outputs.docker-login-password }} + DOCKER_PASSPHRASE: ${{ secrets.DOCKER_LOGIN_TOKEN_PASSPHRASE }} uses: ./.github/workflows/ci.yml with: run_sdk_examples: true From ed66404ce0fccbc7a82baadafde8b1cd5e24db00 Mon Sep 17 00:00:00 2001 From: Russell Cohen Date: Tue, 31 Jan 2023 16:28:44 -0500 Subject: [PATCH 14/24] use a real token for all jobs --- .github/workflows/ci-pr.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/ci-pr.yml b/.github/workflows/ci-pr.yml index c1a284c3cc..a9c97582d8 100644 --- a/.github/workflows/ci-pr.yml +++ b/.github/workflows/ci-pr.yml @@ -68,12 +68,12 @@ jobs: # Run shared CI after the Docker build image has either been rebuilt or found in ECR ci: needs: acquire-base-image - env: - ENCRYPTED_DOCKER_PASSWORD: ${{ needs.save-docker-login-token.outputs.docker-login-password }} - DOCKER_PASSPHRASE: ${{ secrets.DOCKER_LOGIN_TOKEN_PASSPHRASE }} uses: ./.github/workflows/ci.yml with: run_sdk_examples: true + env: + ENCRYPTED_DOCKER_PASSWORD: ${{ needs.save-docker-login-token.outputs.docker-login-password }} + DOCKER_PASSPHRASE: ${{ secrets.DOCKER_LOGIN_TOKEN_PASSPHRASE }} # The PR bot requires a Docker build image, so make it depend on the `acquire-base-image` job. pr_bot: From 29063944d222dec73c1e9fe327bd13c795bdc1e0 Mon Sep 17 00:00:00 2001 From: Russell Cohen Date: Tue, 31 Jan 2023 16:32:41 -0500 Subject: [PATCH 15/24] use a real token for all jobs --- .github/workflows/ci-pr.yml | 10 ++++++---- .github/workflows/ci.yml | 7 +++++++ 2 files changed, 13 insertions(+), 4 deletions(-) diff --git a/.github/workflows/ci-pr.yml b/.github/workflows/ci-pr.yml index a9c97582d8..10dca8fd70 100644 --- a/.github/workflows/ci-pr.yml +++ b/.github/workflows/ci-pr.yml @@ -67,13 +67,15 @@ jobs: # Run shared CI after the Docker build image has either been rebuilt or found in ECR ci: - needs: acquire-base-image + needs: + - save-docker-login-token + - acquire-base-image uses: ./.github/workflows/ci.yml with: run_sdk_examples: true - env: - ENCRYPTED_DOCKER_PASSWORD: ${{ needs.save-docker-login-token.outputs.docker-login-password }} - DOCKER_PASSPHRASE: ${{ secrets.DOCKER_LOGIN_TOKEN_PASSPHRASE }} + secrets: + ENCRYPTED_DOCKER_PASSWORD: ${{ needs.save-docker-login-token.outputs.docker-login-password }} + DOCKER_PASSPHRASE: ${{ secrets.DOCKER_LOGIN_TOKEN_PASSPHRASE }} # The PR bot requires a Docker build image, so make it depend on the `acquire-base-image` job. pr_bot: diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 94c0ed4681..59b76680a1 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -14,10 +14,17 @@ on: required: false default: false type: boolean + secrets: + ENCRYPTED_DOCKER_PASSWORD: + required: false + DOCKER_PASSPHRASE: + required: false env: rust_version: 1.62.1 rust_toolchain_components: clippy,rustfmt + ENCRYPTED_DOCKER_PASSWORD: ${{ secrets.ENCRYPTED_DOCKER_PASSWORD }} + DOCKER_PASSPHRASE: ${{ secrets.DOCKER_PASSPHRASE }} jobs: # The `generate` job runs scripts that produce artifacts that are required by the `test` job, From 0ff39972a65ebcd0004ff3ada19f2c92be89789b Mon Sep 17 00:00:00 2001 From: Russell Cohen Date: Wed, 1 Feb 2023 11:37:32 -0500 Subject: [PATCH 16/24] fix docs, add more logs --- .github/workflows/ci-pr.yml | 8 +++++--- tools/ci-build/acquire-build-image | 1 + 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/.github/workflows/ci-pr.yml b/.github/workflows/ci-pr.yml index 10dca8fd70..3672e67e3b 100644 --- a/.github/workflows/ci-pr.yml +++ b/.github/workflows/ci-pr.yml @@ -13,9 +13,8 @@ concurrency: cancel-in-progress: true jobs: - # This job detects if the PR made changes to build tools. If it did, then it builds a new - # build Docker image. Otherwise, it downloads a build image from Public ECR. In both cases, - # it uploads the image as a build artifact for other jobs to download and use. + # This job will, if possible, save a docker login password to the job outputs. The token will + # be encrypted with the passphrase stored as a GitHub secret. The login password expires after 12h. save-docker-login-token: outputs: docker-login-password: ${{ steps.set-token.outputs.docker-login-password }} @@ -41,6 +40,9 @@ jobs: echo "docker-login-password=$ENCRYPTED_PAYLOAD" >> $GITHUB_OUTPUT + # This job detects if the PR made changes to build tools. If it did, then it builds a new + # build Docker image. Otherwise, it downloads a build image from Public ECR. In both cases, + # it uploads the image as a build artifact for other jobs to download and use. acquire-base-image: name: Acquire Base Image needs: save-docker-login-token diff --git a/tools/ci-build/acquire-build-image b/tools/ci-build/acquire-build-image index d1209d9adb..5ebaa6fbfc 100755 --- a/tools/ci-build/acquire-build-image +++ b/tools/ci-build/acquire-build-image @@ -172,6 +172,7 @@ def decrypt_and_login(secret): if token is None: print('no passphrase') return + print(f'decrypting. input len: {len(decoded)}, token len: {len(token)}') (code, password, err) = (get_cmd_output(f"gpg --decrypt --batch --quiet --passphrase '{token}' --output -", check=False, input=decoded)) if code != 0: From 62c82ee6097ca5e3e078508a7f3b9606212290cf Mon Sep 17 00:00:00 2001 From: Russell Cohen Date: Wed, 1 Feb 2023 11:52:57 -0500 Subject: [PATCH 17/24] rename secret name --- .github/workflows/ci-pr.yml | 5 +++-- .github/workflows/ci.yml | 5 +++-- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/.github/workflows/ci-pr.yml b/.github/workflows/ci-pr.yml index 3672e67e3b..d1023f99be 100644 --- a/.github/workflows/ci-pr.yml +++ b/.github/workflows/ci-pr.yml @@ -15,6 +15,7 @@ concurrency: jobs: # This job will, if possible, save a docker login password to the job outputs. The token will # be encrypted with the passphrase stored as a GitHub secret. The login password expires after 12h. + # The login password is encrypted with the repo secret DOCKER_LOGIN_TOKEN_PASSPHRASE save-docker-login-token: outputs: docker-login-password: ${{ steps.set-token.outputs.docker-login-password }} @@ -49,7 +50,7 @@ jobs: runs-on: ubuntu-latest env: ENCRYPTED_DOCKER_PASSWORD: ${{ needs.save-docker-login-token.outputs.docker-login-password }} - DOCKER_PASSPHRASE: ${{ secrets.DOCKER_LOGIN_TOKEN_PASSPHRASE }} + DOCKER_LOGIN_TOKEN_PASSPHRASE: ${{ secrets.DOCKER_LOGIN_TOKEN_PASSPHRASE }} steps: - uses: actions/checkout@v3 with: @@ -77,7 +78,7 @@ jobs: run_sdk_examples: true secrets: ENCRYPTED_DOCKER_PASSWORD: ${{ needs.save-docker-login-token.outputs.docker-login-password }} - DOCKER_PASSPHRASE: ${{ secrets.DOCKER_LOGIN_TOKEN_PASSPHRASE }} + DOCKER_LOGIN_TOKEN_PASSPHRASE: ${{ secrets.DOCKER_LOGIN_TOKEN_PASSPHRASE }} # The PR bot requires a Docker build image, so make it depend on the `acquire-base-image` job. pr_bot: diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 59b76680a1..a761ec6bbb 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -15,16 +15,17 @@ on: default: false type: boolean secrets: + # the docker login password for ECR. This is ENCRYPTED_DOCKER_PASSWORD: required: false - DOCKER_PASSPHRASE: + DOCKER_LOGIN_TOKEN_PASSPHRASE: required: false env: rust_version: 1.62.1 rust_toolchain_components: clippy,rustfmt ENCRYPTED_DOCKER_PASSWORD: ${{ secrets.ENCRYPTED_DOCKER_PASSWORD }} - DOCKER_PASSPHRASE: ${{ secrets.DOCKER_PASSPHRASE }} + DOCKER_LOGIN_TOKEN_PASSPHRASE: ${{ secrets.DOCKER_LOGIN_TOKEN_PASSPHRASE }} jobs: # The `generate` job runs scripts that produce artifacts that are required by the `test` job, From d9d710bccc74e349eee409017f5eea7c025eeb56 Mon Sep 17 00:00:00 2001 From: Russell Cohen Date: Wed, 1 Feb 2023 13:19:42 -0500 Subject: [PATCH 18/24] docs, naming, nicer errors --- .github/workflows/ci-pr.yml | 1 - tools/ci-build/acquire-build-image | 20 +++++++++++--------- 2 files changed, 11 insertions(+), 10 deletions(-) diff --git a/.github/workflows/ci-pr.yml b/.github/workflows/ci-pr.yml index d1023f99be..d1ddc4d07e 100644 --- a/.github/workflows/ci-pr.yml +++ b/.github/workflows/ci-pr.yml @@ -55,7 +55,6 @@ jobs: - uses: actions/checkout@v3 with: path: smithy-rs - fetch-depth: 0 - name: Acquire base image id: acquire env: diff --git a/tools/ci-build/acquire-build-image b/tools/ci-build/acquire-build-image index 5ebaa6fbfc..3f31db55d9 100755 --- a/tools/ci-build/acquire-build-image +++ b/tools/ci-build/acquire-build-image @@ -158,11 +158,16 @@ def get_cmd_output(command, cwd=None, check=True, **kwargs): result = subprocess.run( shlex.split(command), capture_output=True, - check=check, + check=False, cwd=cwd, **kwargs ) - return (result.returncode, result.stdout.decode("utf-8").strip(), result.stderr.decode("utf-8").strip()) + stdout = result.stdout.decode("utf-8").strip() + stderr = result.stderr.decode("utf-8").strip() + if check and result.returncode != 0: + raise Exception(f"failed to run '{command}.\n{stdout}\n{stderr}") + + return result.returncode, stdout, stderr def decrypt_and_login(secret): @@ -172,13 +177,10 @@ def decrypt_and_login(secret): if token is None: print('no passphrase') return - print(f'decrypting. input len: {len(decoded)}, token len: {len(token)}') - (code, password, err) = (get_cmd_output(f"gpg --decrypt --batch --quiet --passphrase '{token}' --output -", check=False, - input=decoded)) - if code != 0: - print(err) - raise - (_, output, _) = get_cmd_output("docker login --username AWS --password-stdin public.ecr.aws", input=password.encode('utf-8')) + (code, password, err) = get_cmd_output(f"gpg --decrypt --batch --quiet --passphrase '{token}' --output -", + input=decoded) + (_, output, _) = get_cmd_output("docker login --username AWS --password-stdin public.ecr.aws", + input=password.encode('utf-8')) print(output) From 33d7cb12851e1a3663bb110216608a5bec076767 Mon Sep 17 00:00:00 2001 From: Russell Cohen Date: Thu, 2 Feb 2023 14:37:44 -0500 Subject: [PATCH 19/24] wip --- .github/scripts/acquire-build-image | 53 ++++++++++++++++++----------- 1 file changed, 34 insertions(+), 19 deletions(-) diff --git a/.github/scripts/acquire-build-image b/.github/scripts/acquire-build-image index 9d49b3d51f..2c6d58e6db 100755 --- a/.github/scripts/acquire-build-image +++ b/.github/scripts/acquire-build-image @@ -11,6 +11,7 @@ import subprocess import sys import time import unittest +import base64 REMOTE_BASE_IMAGE_NAME = "public.ecr.aws/w0m4q9l7/github-awslabs-smithy-rs-ci" LOCAL_BASE_IMAGE_NAME = "smithy-rs-base-image" @@ -41,7 +42,8 @@ class Platform(Enum): # Script context class Context: - def __init__(self, start_path, script_path, tools_path, user_id, image_tag, allow_local_build, github_actions): + def __init__(self, start_path, script_path, tools_path, user_id, image_tag, allow_local_build, github_actions, + encrpyted_docker_password, docker_passphrase): self.start_path = start_path self.script_path = script_path self.tools_path = tools_path @@ -50,6 +52,8 @@ class Context: self.image_tag = image_tag self.allow_local_build = allow_local_build self.github_actions = github_actions + self.encrypted_docker_password = encrpyted_docker_password + self.docker_passphrase = docker_passphrase @staticmethod def default(): @@ -60,6 +64,9 @@ class Context: image_tag = get_cmd_output("./docker-image-hash", cwd=script_path)[1] allow_local_build = os.getenv("ALLOW_LOCAL_BUILD") != "false" github_actions = os.getenv("GITHUB_ACTIONS") == "true" + encrypted_docker_password = os.getenv("ENCRYPTED_DOCKER_PASSWORD") + docker_passphrase = os.getenv("DOCKER_LOGIN_TOKEN_PASSPHRASE") + print(f"Start path: {start_path}") print(f"Script path: {script_path}") print(f"Tools path: {tools_path}") @@ -67,7 +74,9 @@ class Context: print(f"Required base image tag: {image_tag}") print(f"Allow local build: {allow_local_build}") print(f"Running in GitHub Actions: {github_actions}") - return Context(start_path, script_path, tools_path, user_id, image_tag, allow_local_build, github_actions) + return Context(start_path=start_path, script_path=script_path, tools_path=tools_path, user_id=user_id, + image_tag=image_tag, allow_local_build=allow_local_build, github_actions=github_actions, + encrpyted_docker_password=encrypted_docker_password, docker_passphrase=docker_passphrase) # Mockable shell commands @@ -156,8 +165,11 @@ def run(command, cwd=None): # Returns (status, output) from a shell command def get_cmd_output(command, cwd=None, check=True, **kwargs): + if isinstance(command, str): + command = shlex.split(command) + result = subprocess.run( - shlex.split(command), + command, capture_output=True, check=False, cwd=cwd, @@ -171,25 +183,22 @@ def get_cmd_output(command, cwd=None, check=True, **kwargs): return result.returncode, stdout, stderr -def decrypt_and_login(secret): - import base64 +def decrypt_and_login(secret, passphrase): decoded = base64.b64decode(secret, validate=True) - token = os.getenv("DOCKER_PASSPHRASE") - if token is None: - print('no passphrase') - return - (code, password, err) = get_cmd_output(f"gpg --decrypt --batch --quiet --passphrase '{token}' --output -", - input=decoded) + if not token: + raise Exception("a secret was set but no passphrase was set (or it was empty)") + assert not "'" in token # prevent shell escape funny business + (code, password, err) = get_cmd_output( + ["gpg", "--decrypt", "--batch", "--quiet", "--passphrase", "{token}", "--output", "-"], + input=decoded) (_, output, _) = get_cmd_output("docker login --username AWS --password-stdin public.ecr.aws", input=password.encode('utf-8')) - print(output) + print("Docker login success!") def acquire_build_image(context=Context.default(), shell=Shell()): - import os - docker_password = os.getenv("ENCRYPTED_DOCKER_PASSWORD") - if docker_password is not None: - decrypt_and_login(docker_password) + if context.encrypted_docker_password is not None: + decrypt_and_login(context.encrypted_docker_password, context.docker_passphrase) # If the image doesn't already exist locally, then look remotely if not shell.docker_image_exists_locally(LOCAL_BASE_IMAGE_NAME, context.image_tag): announce("Base image not found locally.") @@ -230,15 +239,17 @@ def acquire_build_image(context=Context.default(), shell=Shell()): class SelfTest(unittest.TestCase): - def test_context(self, allow_local_build=False, github_actions=False): + def test_context(self, github_actions=False, **kwargs): return Context( start_path="/tmp/test/start-path", script_path="/tmp/test/script-path", tools_path="/tmp/test/tools-path", user_id="123", image_tag="someimagetag", - allow_local_build=allow_local_build, - github_actions=github_actions + encrpyted_docker_password=None, + docker_passphrase=None, + github_actions=github_actions, + **kwargs ) def mock_shell(self): @@ -266,6 +277,10 @@ class SelfTest(unittest.TestCase): ) ) + def test_docker_login(self): + shell = self.mock_shell() + acquire_build_image(self.test_context(encrpyted_docker_password="asdf", docker_passphrase="1234")) + def test_retry_immediate_success(self): shell = self.mock_shell() shell.docker_pull.side_effect = [DockerPullResult.SUCCESS] From 75148857bf21c19f7a7f9fc52c76e2646fa680c1 Mon Sep 17 00:00:00 2001 From: Russell Cohen Date: Thu, 2 Feb 2023 14:45:55 -0500 Subject: [PATCH 20/24] fix CI --- .github/scripts/acquire-build-image | 30 ++++++++++++++--------------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/.github/scripts/acquire-build-image b/.github/scripts/acquire-build-image index 2c6d58e6db..bc3245a861 100755 --- a/.github/scripts/acquire-build-image +++ b/.github/scripts/acquire-build-image @@ -185,9 +185,8 @@ def get_cmd_output(command, cwd=None, check=True, **kwargs): def decrypt_and_login(secret, passphrase): decoded = base64.b64decode(secret, validate=True) - if not token: + if not passphrase: raise Exception("a secret was set but no passphrase was set (or it was empty)") - assert not "'" in token # prevent shell escape funny business (code, password, err) = get_cmd_output( ["gpg", "--decrypt", "--batch", "--quiet", "--passphrase", "{token}", "--output", "-"], input=decoded) @@ -239,17 +238,18 @@ def acquire_build_image(context=Context.default(), shell=Shell()): class SelfTest(unittest.TestCase): - def test_context(self, github_actions=False, **kwargs): + def test_context(self, github_actions=False, allow_local_build=False, encrypted_docker_password=None, + docker_passphrase=None): return Context( start_path="/tmp/test/start-path", script_path="/tmp/test/script-path", tools_path="/tmp/test/tools-path", user_id="123", image_tag="someimagetag", - encrpyted_docker_password=None, - docker_passphrase=None, + encrpyted_docker_password=encrypted_docker_password, + docker_passphrase=docker_passphrase, github_actions=github_actions, - **kwargs + allow_local_build=allow_local_build, ) def mock_shell(self): @@ -279,7 +279,7 @@ class SelfTest(unittest.TestCase): def test_docker_login(self): shell = self.mock_shell() - acquire_build_image(self.test_context(encrpyted_docker_password="asdf", docker_passphrase="1234")) + acquire_build_image(self.test_context(encrypted_docker_password="asdf", docker_passphrase="1234")) def test_retry_immediate_success(self): shell = self.mock_shell() @@ -408,7 +408,7 @@ class SelfTest(unittest.TestCase): shell.docker_image_exists_locally.assert_called_once() shell.docker_tag.assert_called_with(LOCAL_BASE_IMAGE_NAME, "someimagetag", LOCAL_BASE_IMAGE_NAME, LOCAL_TAG) - shell.docker_build_build_image.assert_called_with("123", "/tmp/test/tools-path") + shell.docker_build_build_image.assert_called_with("123", "/tmp/test/tools-path/ci-build") # When: # - the base image doesn't exist locally @@ -425,10 +425,10 @@ class SelfTest(unittest.TestCase): self.assertEqual(0, acquire_build_image(context, shell)) shell.docker_image_exists_locally.assert_called_once() - shell.docker_build_base_image.assert_called_with("someimagetag", "/tmp/test/tools-path") + shell.docker_build_base_image.assert_called_with("someimagetag", "/tmp/test/tools-path/ci-build") shell.docker_save.assert_not_called() shell.docker_tag.assert_called_with(LOCAL_BASE_IMAGE_NAME, "someimagetag", LOCAL_BASE_IMAGE_NAME, LOCAL_TAG) - shell.docker_build_build_image.assert_called_with("123", "/tmp/test/tools-path") + shell.docker_build_build_image.assert_called_with("123", "/tmp/test/tools-path/ci-build") # When: # - the base image doesn't exist locally @@ -445,10 +445,10 @@ class SelfTest(unittest.TestCase): self.assertEqual(0, acquire_build_image(context, shell)) shell.docker_image_exists_locally.assert_called_once() - shell.docker_build_base_image.assert_called_with("someimagetag", "/tmp/test/tools-path") + shell.docker_build_base_image.assert_called_with("someimagetag", "/tmp/test/tools-path/ci-build") shell.docker_save.assert_not_called() shell.docker_tag.assert_called_with(LOCAL_BASE_IMAGE_NAME, "someimagetag", LOCAL_BASE_IMAGE_NAME, LOCAL_TAG) - shell.docker_build_build_image.assert_called_with("123", "/tmp/test/tools-path") + shell.docker_build_build_image.assert_called_with("123", "/tmp/test/tools-path/ci-build") # When: # - the base image doesn't exist locally @@ -465,14 +465,14 @@ class SelfTest(unittest.TestCase): self.assertEqual(0, acquire_build_image(context, shell)) shell.docker_image_exists_locally.assert_called_once() - shell.docker_build_base_image.assert_called_with("someimagetag", "/tmp/test/tools-path") + shell.docker_build_base_image.assert_called_with("someimagetag", "/tmp/test/tools-path/ci-build") shell.docker_save.assert_called_with( LOCAL_BASE_IMAGE_NAME, "someimagetag", "/tmp/test/start-path/smithy-rs-base-image" ) shell.docker_tag.assert_called_with(LOCAL_BASE_IMAGE_NAME, "someimagetag", LOCAL_BASE_IMAGE_NAME, LOCAL_TAG) - shell.docker_build_build_image.assert_called_with("123", "/tmp/test/tools-path") + shell.docker_build_build_image.assert_called_with("123", "/tmp/test/tools-path/ci-build") # When: # - the base image doesn't exist locally @@ -512,7 +512,7 @@ class SelfTest(unittest.TestCase): call(REMOTE_BASE_IMAGE_NAME, "someimagetag", LOCAL_BASE_IMAGE_NAME, "someimagetag"), call(LOCAL_BASE_IMAGE_NAME, "someimagetag", LOCAL_BASE_IMAGE_NAME, LOCAL_TAG) ]) - shell.docker_build_build_image.assert_called_with("123", "/tmp/test/tools-path") + shell.docker_build_build_image.assert_called_with("123", "/tmp/test/tools-path/ci-build") def main(): From ffa2d71d059eced2c528d701277cf0b0ebf221e9 Mon Sep 17 00:00:00 2001 From: Russell Cohen Date: Thu, 2 Feb 2023 14:56:14 -0500 Subject: [PATCH 21/24] refactor and add tests --- .github/scripts/acquire-build-image | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/.github/scripts/acquire-build-image b/.github/scripts/acquire-build-image index bc3245a861..dedc302202 100755 --- a/.github/scripts/acquire-build-image +++ b/.github/scripts/acquire-build-image @@ -93,6 +93,9 @@ class Shell: (status, _, _) = get_cmd_output(f"docker inspect \"{image_name}:{image_tag}\"", check=False) return status == 0 + def docker_login(self, password): + get_cmd_output("docker login --username AWS --password-stdin public.ecr.aws", input=password.encode('utf-8')) + # Pulls the requested `image_name` with `image_tag`. Returns `DockerPullResult`. def docker_pull(self, image_name, image_tag): (status, stdout, stderr) = get_cmd_output(f"docker pull \"{image_name}:{image_tag}\"", check=False) @@ -183,21 +186,20 @@ def get_cmd_output(command, cwd=None, check=True, **kwargs): return result.returncode, stdout, stderr -def decrypt_and_login(secret, passphrase): +def decrypt_and_login(shell, secret, passphrase): decoded = base64.b64decode(secret, validate=True) if not passphrase: raise Exception("a secret was set but no passphrase was set (or it was empty)") (code, password, err) = get_cmd_output( - ["gpg", "--decrypt", "--batch", "--quiet", "--passphrase", "{token}", "--output", "-"], + ["gpg", "--decrypt", "--batch", "--quiet", "--passphrase", passphrase, "--output", "-"], input=decoded) - (_, output, _) = get_cmd_output("docker login --username AWS --password-stdin public.ecr.aws", - input=password.encode('utf-8')) + shell.docker_login(password) print("Docker login success!") def acquire_build_image(context=Context.default(), shell=Shell()): if context.encrypted_docker_password is not None: - decrypt_and_login(context.encrypted_docker_password, context.docker_passphrase) + decrypt_and_login(shell, context.encrypted_docker_password, context.docker_passphrase) # If the image doesn't already exist locally, then look remotely if not shell.docker_image_exists_locally(LOCAL_BASE_IMAGE_NAME, context.image_tag): announce("Base image not found locally.") @@ -261,6 +263,7 @@ class SelfTest(unittest.TestCase): shell.docker_pull = MagicMock() shell.docker_save = MagicMock() shell.docker_tag = MagicMock() + shell.docker_login = MagicMock() return shell def test_retry_architecture_mismatch(self): @@ -279,7 +282,10 @@ class SelfTest(unittest.TestCase): def test_docker_login(self): shell = self.mock_shell() - acquire_build_image(self.test_context(encrypted_docker_password="asdf", docker_passphrase="1234")) + acquire_build_image(self.test_context( + encrypted_docker_password="jA0ECQMCvYU/JxsX3g/70j0BxbLLW8QaFWWb/DqY9gPhTuEN/xdYVxaoDnV6Fha+lAWdT7xN0qZr5DHPBalLfVvvM1SEXRBI8qnfXyGI", + docker_passphrase="secret"), shell) + shell.docker_login.assert_called_with("payload") def test_retry_immediate_success(self): shell = self.mock_shell() From a1607cf5984566196c04a9c3eaaae962cca723ad Mon Sep 17 00:00:00 2001 From: Russell Cohen Date: Thu, 2 Feb 2023 15:30:46 -0500 Subject: [PATCH 22/24] remove hack --- .github/scripts/docker-image-hash | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/.github/scripts/docker-image-hash b/.github/scripts/docker-image-hash index a78249dc84..6acc955126 100755 --- a/.github/scripts/docker-image-hash +++ b/.github/scripts/docker-image-hash @@ -11,5 +11,4 @@ set -eo pipefail cd "$(dirname "$0")" cd "$(git rev-parse --show-toplevel)" -# git ls-files -s --full-name "tools" | grep -v acquire-build-image | git hash-object --stdin -echo "ef34a35a310a993d1fbc39c0a6e869dda7e80af1" +git ls-files -s --full-name "tools" | grep -v acquire-build-image | git hash-object --stdin From dbc002537589cd5859b0e5d867344e38a1b88985 Mon Sep 17 00:00:00 2001 From: Russell Cohen Date: Thu, 2 Feb 2023 16:29:46 -0500 Subject: [PATCH 23/24] fix bad merge --- .github/scripts/acquire-build-image | 28 ++++++++++++++++------------ 1 file changed, 16 insertions(+), 12 deletions(-) diff --git a/.github/scripts/acquire-build-image b/.github/scripts/acquire-build-image index f624531887..ec2ed027b1 100755 --- a/.github/scripts/acquire-build-image +++ b/.github/scripts/acquire-build-image @@ -43,7 +43,7 @@ class Platform(Enum): # Script context class Context: def __init__(self, start_path, script_path, tools_path, user_id, image_tag, allow_local_build, github_actions, - encrpyted_docker_password, docker_passphrase): + encrypted_docker_password, docker_passphrase): self.start_path = start_path self.script_path = script_path self.tools_path = tools_path @@ -52,7 +52,7 @@ class Context: self.image_tag = image_tag self.allow_local_build = allow_local_build self.github_actions = github_actions - self.encrypted_docker_password = encrpyted_docker_password + self.encrypted_docker_password = encrypted_docker_password self.docker_passphrase = docker_passphrase @staticmethod @@ -76,9 +76,15 @@ class Context: print(f"Running in GitHub Actions: {github_actions}") return Context(start_path=start_path, script_path=script_path, tools_path=tools_path, user_id=user_id, image_tag=image_tag, allow_local_build=allow_local_build, github_actions=github_actions, - encrpyted_docker_password=encrypted_docker_password, docker_passphrase=docker_passphrase) + encrypted_docker_password=encrypted_docker_password, docker_passphrase=docker_passphrase) +def output_contains_any(stdout, stderr, messages): + for message in messages: + if message in stdout or message in stderr: + return True + return False + # Mockable shell commands class Shell: # Returns the platform that this script is running on @@ -106,19 +112,17 @@ class Shell: print(stderr) print("-------------------") - not_found_message = "not found: manifest unknown" - throttle_message = "toomanyrequests: Rate exceeded" + not_found_messages = ["not found: manifest unknown"] + throttle_messages = ["toomanyrequests:"] retryable_messages = ["net/http: TLS handshake timeout"] if status == 0: return DockerPullResult.SUCCESS - elif throttle_message in stdout or throttle_message in stderr: + elif output_contains_any(stdout, stderr, throttle_messages): return DockerPullResult.ERROR_THROTTLED - elif not_found_message in stdout or not_found_message in stderr: + elif output_contains_any(stdout, stderr, not_found_messages): return DockerPullResult.NOT_FOUND - else: - for message in retryable_messages: - if message in stdout or message in stderr: - return DockerPullResult.RETRYABLE_ERROR + elif output_contains_any(stdout, stderr, retryable_messages): + return DockerPullResult.RETRYABLE_ERROR return DockerPullResult.UNKNOWN_ERROR # Builds the base image with the Dockerfile in `path` and tags with with `image_tag` @@ -248,7 +252,7 @@ class SelfTest(unittest.TestCase): tools_path="/tmp/test/tools-path", user_id="123", image_tag="someimagetag", - encrpyted_docker_password=encrypted_docker_password, + encrypted_docker_password=encrypted_docker_password, docker_passphrase=docker_passphrase, github_actions=github_actions, allow_local_build=allow_local_build, From 1ee44b4825f55a36f968625afc86ddfafccd5abd Mon Sep 17 00:00:00 2001 From: Russell Cohen Date: Thu, 2 Feb 2023 16:30:48 -0500 Subject: [PATCH 24/24] revert another bad commit --- .github/scripts/docker-image-hash | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/scripts/docker-image-hash b/.github/scripts/docker-image-hash index 6acc955126..55b532332d 100755 --- a/.github/scripts/docker-image-hash +++ b/.github/scripts/docker-image-hash @@ -11,4 +11,4 @@ set -eo pipefail cd "$(dirname "$0")" cd "$(git rev-parse --show-toplevel)" -git ls-files -s --full-name "tools" | grep -v acquire-build-image | git hash-object --stdin +git ls-files -s --full-name "tools" | git hash-object --stdin