diff --git a/.github/workflows/e2e.yml b/.github/workflows/e2e.yml new file mode 100644 index 000000000000..2d44d8c2434d --- /dev/null +++ b/.github/workflows/e2e.yml @@ -0,0 +1,92 @@ +name: E2E Test +run-name: 'e2e: test-${{ github.run_id }}' + +on: + workflow_dispatch: + +permissions: + id-token: write + contents: read + pull-requests: write + actions: read + +jobs: + init: + uses: ./.github/workflows/pulumi.yml + with: + cmd: init + stack: test-${{ github.run_id }} + project: e2e + working-directory: test + comment-on-pr: false + secrets-provider: ${{ vars.GCP_KMS_KEY }} + secrets: inherit + + preview-create: + needs: init + uses: ./.github/workflows/pulumi.yml + with: + cmd: preview + stack: test-${{ github.run_id }} + project: e2e + working-directory: test + comment-on-pr: false + secrets: inherit + + up: + needs: preview-create + uses: ./.github/workflows/pulumi.yml + with: + cmd: up + stack: test-${{ github.run_id }} + project: e2e + working-directory: test + comment-on-pr: false + secrets: inherit + + preview-nop: + needs: up + uses: ./.github/workflows/pulumi.yml + with: + cmd: preview + stack: test-${{ github.run_id }} + project: e2e + working-directory: test + comment-on-pr: false + secrets: inherit + + destroy: + needs: [up, preview-nop] + if: always() && needs.up.result == 'success' + uses: ./.github/workflows/pulumi.yml + with: + cmd: destroy + stack: test-${{ github.run_id }} + project: e2e + working-directory: test + comment-on-pr: false + secrets: inherit + + preview-recreate: + needs: destroy + if: always() && needs.destroy.result == 'success' + uses: ./.github/workflows/pulumi.yml + with: + cmd: preview + stack: test-${{ github.run_id }} + project: e2e + working-directory: test + comment-on-pr: false + secrets: inherit + + stack-rm: + needs: [destroy, preview-recreate] + if: always() && needs.destroy.result == 'success' + uses: ./.github/workflows/pulumi.yml + with: + cmd: stack-rm + stack: test-${{ github.run_id }} + project: e2e + working-directory: test + comment-on-pr: false + secrets: inherit diff --git a/.github/workflows/pulumi.yml b/.github/workflows/pulumi.yml index 694f71fac1d4..e4af365f295e 100644 --- a/.github/workflows/pulumi.yml +++ b/.github/workflows/pulumi.yml @@ -5,7 +5,7 @@ on: workflow_call: inputs: cmd: - description: 'Pulumi command (preview, up, refresh)' + description: 'Pulumi command (preview, up, refresh, destroy, init, stack-rm)' required: true type: string stack: @@ -53,10 +53,17 @@ on: required: false default: 'a2b415984ed69668323572cc4df274992932c304' type: string + secrets-provider: + description: 'Secrets provider for stack init (e.g. "gcpkms://projects/.../cryptoKeys/...")' + required: false + type: string secrets: PULUMI_ACCESS_TOKEN: description: 'Pulumi Cloud access token (if using Pulumi Cloud backend)' required: false + PULUMI_CONFIG_PASSPHRASE: + description: 'Passphrase for stack encryption (leave unset for empty passphrase on self-managed backends)' + required: false # Pulumi fork with --patch flag for GitHub-compatible diff output # https://github.com/Open-Athena/pulumi/commits/patch @@ -162,9 +169,23 @@ jobs: - name: Run Pulumi ${{ inputs.cmd }} id: pulumi working-directory: ${{ inputs.working-directory }} + env: + PULUMI_CONFIG_PASSPHRASE: ${{ secrets.PULUMI_CONFIG_PASSPHRASE }} run: | set -o pipefail + case "${{ inputs.cmd }}" in + init) + pulumi stack init ${{ inputs.stack }} \ + ${{ inputs.secrets-provider && format('--secrets-provider="{0}"', inputs.secrets-provider) || '' }} + exit 0 + ;; + stack-rm) + pulumi stack rm ${{ inputs.stack }} --yes --force + exit 0 + ;; + esac + pulumi stack select ${{ inputs.stack }} # Remove profile if set (use env vars instead) @@ -180,6 +201,9 @@ jobs: refresh) pulumi refresh --yes --non-interactive 2>&1 | tee pulumi-output.txt ;; + destroy) + pulumi destroy --yes --non-interactive 2>&1 | tee pulumi-output.txt + ;; *) echo "::error::Unknown command: ${{ inputs.cmd }}" exit 1 @@ -204,7 +228,7 @@ jobs: PREFIX="${{ inputs.project && format('{0}: ', inputs.project) || '' }}" # Dynamic fence length: one longer than any backtick run in content (min 3) - MAX=$(grep -oE '\`+' pulumi-output.txt | awk '{print length}' | sort -rn | head -1) + MAX=$(grep -oE '[`]+' pulumi-output.txt | awk '{print length}' | sort -rn | head -1) FENCE=$(printf '`%.0s' $(seq 1 $(( ${MAX:-0} > 2 ? MAX + 1 : 3 )))) # Build shared output body @@ -259,6 +283,8 @@ jobs: - name: Export outputs if: inputs.cmd == 'up' working-directory: ${{ inputs.working-directory }} + env: + PULUMI_CONFIG_PASSPHRASE: ${{ secrets.PULUMI_CONFIG_PASSPHRASE }} run: | echo "### Pulumi Stack Outputs" >> $GITHUB_STEP_SUMMARY echo '```' >> $GITHUB_STEP_SUMMARY diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml new file mode 100644 index 000000000000..75f96d4e1bea --- /dev/null +++ b/.github/workflows/test.yml @@ -0,0 +1,33 @@ +name: Test +run-name: 'test: pulumi ${{ inputs.cmd }}' + +on: + workflow_dispatch: + inputs: + cmd: + description: 'Pulumi command' + required: true + default: 'preview' + type: choice + options: + - preview + - up + - refresh + - destroy + - init + +permissions: + id-token: write + contents: read + pull-requests: write + actions: read + +jobs: + pulumi: + uses: ./.github/workflows/pulumi.yml + with: + cmd: ${{ inputs.cmd }} + stack: test + project: pulumi-v1-test + working-directory: test + secrets: inherit diff --git a/test/Pulumi.test.yaml b/test/Pulumi.test.yaml new file mode 100644 index 000000000000..1a38cefb176a --- /dev/null +++ b/test/Pulumi.test.yaml @@ -0,0 +1,2 @@ +config: + aws:region: us-east-1 diff --git a/test/Pulumi.yaml b/test/Pulumi.yaml new file mode 100644 index 000000000000..7c31d7def837 --- /dev/null +++ b/test/Pulumi.yaml @@ -0,0 +1,5 @@ +name: pulumi-v1-test +description: Test stack for the pulumi-v1 reusable workflow +runtime: python +backend: + url: gs://oa-pulumi diff --git a/test/__main__.py b/test/__main__.py new file mode 100644 index 000000000000..51f2f05bd3d9 --- /dev/null +++ b/test/__main__.py @@ -0,0 +1,41 @@ +"""Test stack for the pulumi-v1 reusable workflow. + +Creates lightweight, free AWS resources to exercise `pulumi preview --patch` +diff output, job summaries, and PR comments. +""" +import pulumi +import pulumi_aws as aws +import pulumi_tls as tls + +config = pulumi.Config() +env = pulumi.get_stack() + +# EC2 Key Pair (via TLS-generated key) +key = tls.PrivateKey("test-key", algorithm="ED25519") +key_pair = aws.ec2.KeyPair("test-key-pair", + key_name=f"pulumi-v1-test-{env}", + public_key=key.public_key_openssh, +) + +# IAM Role (no trust policy; just exists for diff testing) +role = aws.iam.Role("test-role", + name=f"pulumi-v1-test-{env}", + assume_role_policy="""{ + "Version": "2012-10-17", + "Statement": [{ + "Effect": "Deny", + "Principal": {"Service": "ec2.amazonaws.com"}, + "Action": "sts:AssumeRole" + }] + }""", +) + +# CloudWatch Log Group +log_group = aws.cloudwatch.LogGroup("test-log-group", + name=f"/pulumi-v1/test/{env}", + retention_in_days=1, +) + +pulumi.export("key_pair_name", key_pair.key_name) +pulumi.export("role_arn", role.arn) +pulumi.export("log_group_name", log_group.name) diff --git a/test/requirements.txt b/test/requirements.txt new file mode 100644 index 000000000000..3629c82bf7b9 --- /dev/null +++ b/test/requirements.txt @@ -0,0 +1,3 @@ +pulumi>=3.0.0 +pulumi-aws>=6.0.0 +pulumi-tls>=5.0.0