Skip to content

Commit e0e054d

Browse files
[patch] Gate cloud smoke cleanup through trusted workflow
Move destructive cleanup into a trusted workflow and make fallback cleanup bounded, dependency-aware, and verifiable across GCP, DigitalOcean, and Linode.
1 parent f33117c commit e0e054d

5 files changed

Lines changed: 769 additions & 153 deletions

File tree

.github/workflows/cloud-smoke-cleanup.yml

Lines changed: 60 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -9,57 +9,100 @@ on:
99

1010
permissions:
1111
contents: read
12-
id-token: write
1312

1413
jobs:
1514
cleanup:
1615
name: Destroy ${{ matrix.name }}
17-
if: github.event.workflow_run.head_repository.full_name == github.repository && contains(fromJSON('["failure","cancelled","timed_out"]'), github.event.workflow_run.conclusion)
16+
if: github.event.workflow_run.event == 'pull_request' && github.event.workflow_run.head_repository.full_name == github.repository && contains(fromJSON('["failure","cancelled","timed_out"]'), github.event.workflow_run.conclusion)
1817
runs-on: ubuntu-24.04
1918
strategy:
2019
fail-fast: false
2120
matrix:
2221
include:
2322
- name: DigitalOcean ISLE
23+
kind: app
2424
provider: digitalocean
2525
template: isle
26+
environment: cloud-smoke-cleanup-digitalocean
27+
concurrency_group: cloud-compose-smoke-digitalocean-isle
2628
- name: Linode WordPress
29+
kind: app
2730
provider: linode
2831
template: wp
29-
- name: GCP WordPress
30-
provider: gcp
31-
template: wp
32+
environment: cloud-smoke-cleanup-linode
33+
concurrency_group: cloud-compose-smoke-linode-wp
34+
- name: Linode Ansible Drupal config-management
35+
kind: config-management
36+
provider: linode
37+
method: ansible
38+
environment: cloud-smoke-cleanup-linode
39+
concurrency_group: cloud-compose-config-management-ansible-drupal
40+
- name: Linode Salt Drupal config-management
41+
kind: config-management
42+
provider: linode
43+
method: salt
44+
environment: cloud-smoke-cleanup-linode
45+
concurrency_group: cloud-compose-config-management-salt-drupal
46+
environment: ${{ matrix.environment }}
47+
concurrency:
48+
group: ${{ matrix.concurrency_group }}
49+
cancel-in-progress: false
50+
env:
51+
CLOUD_COMPOSE_SMOKE_RUN_ID: ${{ github.event.workflow_run.id }}
52+
DIGITALOCEAN_TOKEN: ${{ matrix.provider == 'digitalocean' && secrets.DIGITALOCEAN_TOKEN || '' }}
53+
LINODE_TOKEN: ${{ matrix.provider == 'linode' && secrets.LINODE_TOKEN || '' }}
54+
steps:
55+
- name: Checkout repository
56+
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7
57+
with:
58+
ref: ${{ github.sha }}
59+
60+
- name: Sweep provider smoke resources
61+
if: matrix.kind == 'app'
62+
run: ci/cloud-smoke.sh sweep-${{ matrix.provider }}-${{ matrix.template }}
63+
64+
- name: Sweep config-management smoke resources
65+
if: matrix.kind == 'config-management'
66+
run: ci/config-management-cloud-smoke.sh sweep-${{ matrix.method }}-drupal
67+
68+
gcp-cleanup:
69+
name: Destroy GCP WordPress
70+
if: github.event.workflow_run.event == 'pull_request' && github.event.workflow_run.head_repository.full_name == github.repository && contains(fromJSON('["failure","cancelled","timed_out"]'), github.event.workflow_run.conclusion)
71+
runs-on: ubuntu-24.04
72+
environment: cloud-smoke-cleanup-gcp
73+
permissions:
74+
contents: read
75+
id-token: write
3276
concurrency:
33-
group: cloud-compose-smoke-${{ matrix.provider }}-${{ matrix.template }}
77+
group: cloud-compose-smoke-gcp-wp
3478
cancel-in-progress: false
3579
env:
3680
CLOUD_COMPOSE_SMOKE_RUN_ID: ${{ github.event.workflow_run.id }}
37-
DIGITALOCEAN_TOKEN: ${{ secrets.DIGITALOCEAN_TOKEN }}
3881
GCLOUD_OIDC_POOL: ${{ vars.GCLOUD_OIDC_POOL || secrets.GCLOUD_OIDC_POOL }}
3982
GCLOUD_PROJECT: ${{ vars.GCLOUD_PROJECT || secrets.GCLOUD_PROJECT }}
4083
GCLOUD_REGION: ${{ vars.GCLOUD_REGION || secrets.GCLOUD_REGION }}
4184
GSA: ${{ vars.GSA || secrets.GSA }}
42-
LINODE_TOKEN: ${{ secrets.LINODE_TOKEN }}
4385
steps:
4486
- name: Checkout repository
4587
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7
88+
with:
89+
ref: ${{ github.sha }}
90+
91+
- name: Check GCP cleanup configuration
92+
run: |
93+
test -n "$GCLOUD_OIDC_POOL" || { echo "GCLOUD_OIDC_POOL environment variable or secret is required for GCP cleanup"; exit 1; }
94+
test -n "$GSA" || { echo "GSA environment variable or secret is required for GCP cleanup"; exit 1; }
95+
test -n "$GCLOUD_PROJECT" || { echo "GCLOUD_PROJECT environment variable or secret is required for GCP cleanup"; exit 1; }
4696
4797
- name: Authenticate to Google Cloud
48-
if: matrix.provider == 'gcp' && env.GCLOUD_OIDC_POOL != '' && env.GSA != '' && env.GCLOUD_PROJECT != ''
4998
uses: google-github-actions/auth@7c6bc770dae815cd3e89ee6cdf493a5fab2cc093 # v3
5099
with:
51100
workload_identity_provider: ${{ env.GCLOUD_OIDC_POOL }}
52101
service_account: ${{ env.GSA }}
53102
project_id: ${{ env.GCLOUD_PROJECT }}
54103

55104
- name: Install gcloud
56-
if: matrix.provider == 'gcp' && env.GCLOUD_OIDC_POOL != '' && env.GSA != '' && env.GCLOUD_PROJECT != ''
57105
uses: google-github-actions/setup-gcloud@aa5489c8933f4cc7a4f7d45035b3b1440c9c10db # v3
58106

59-
- name: Sweep smoke resources
60-
if: matrix.provider != 'gcp' || (env.GCLOUD_OIDC_POOL != '' && env.GSA != '' && env.GCLOUD_PROJECT != '')
61-
run: ci/cloud-smoke.sh sweep-${{ matrix.provider }}-${{ matrix.template }}
62-
63-
- name: Skip GCP cleanup
64-
if: matrix.provider == 'gcp' && (env.GCLOUD_OIDC_POOL == '' || env.GSA == '' || env.GCLOUD_PROJECT == '')
65-
run: echo "Skipping GCP cleanup because GCP smoke secrets are not configured."
107+
- name: Sweep GCP smoke resources
108+
run: ci/cloud-smoke.sh sweep-gcp-wp

.github/workflows/cloud-smoke.yml

Lines changed: 3 additions & 77 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,9 @@ jobs:
3030
- name: Run Terraform lint
3131
run: make terraform-lint-check
3232

33+
- name: Validate fallback cleanup behavior
34+
run: bash ci/cloud-smoke-cleanup-contract.sh
35+
3336
config-management-lint:
3437
name: Ansible and Salt lint
3538
runs-on: ubuntu-24.04
@@ -159,80 +162,3 @@ jobs:
159162
- name: Skip GCP destroy
160163
if: always() && matrix.provider == 'gcp' && (env.GCLOUD_OIDC_POOL == '' || env.GSA == '' || env.GCLOUD_PROJECT == '')
161164
run: echo "Skipping GCP destroy because GCP smoke secrets are not configured and no GCP resources were applied."
162-
163-
config-management-cleanup:
164-
name: Destroy Linode ${{ matrix.method }} Drupal config-management
165-
needs: config-management-cloud-smoke
166-
if: always() && (failure() || cancelled()) && github.event.pull_request.head.repo.full_name == github.repository
167-
runs-on: ubuntu-24.04
168-
strategy:
169-
fail-fast: false
170-
matrix:
171-
method:
172-
- ansible
173-
- salt
174-
concurrency:
175-
group: cloud-compose-config-management-${{ matrix.method }}-drupal
176-
cancel-in-progress: false
177-
env:
178-
CLOUD_COMPOSE_SMOKE_RUN_ID: ${{ github.run_id }}
179-
LINODE_TOKEN: ${{ secrets.LINODE_TOKEN }}
180-
steps:
181-
- name: Checkout repository
182-
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7
183-
184-
- name: Sweep Linode config-management smoke resources
185-
run: ci/config-management-cloud-smoke.sh sweep-${{ matrix.method }}-drupal
186-
187-
cleanup:
188-
name: Destroy ${{ matrix.name }}
189-
needs: smoke
190-
if: always() && (failure() || cancelled())
191-
runs-on: ubuntu-24.04
192-
strategy:
193-
fail-fast: false
194-
matrix:
195-
include:
196-
- name: DigitalOcean ISLE
197-
provider: digitalocean
198-
template: isle
199-
- name: Linode WordPress
200-
provider: linode
201-
template: wp
202-
- name: GCP WordPress
203-
provider: gcp
204-
template: wp
205-
concurrency:
206-
group: cloud-compose-smoke-${{ matrix.provider }}-${{ matrix.template }}
207-
cancel-in-progress: false
208-
env:
209-
CLOUD_COMPOSE_SMOKE_RUN_ID: ${{ github.run_id }}
210-
DIGITALOCEAN_TOKEN: ${{ secrets.DIGITALOCEAN_TOKEN }}
211-
GCLOUD_OIDC_POOL: ${{ vars.GCLOUD_OIDC_POOL || secrets.GCLOUD_OIDC_POOL }}
212-
GCLOUD_PROJECT: ${{ vars.GCLOUD_PROJECT || secrets.GCLOUD_PROJECT }}
213-
GCLOUD_REGION: ${{ vars.GCLOUD_REGION || secrets.GCLOUD_REGION }}
214-
GSA: ${{ vars.GSA || secrets.GSA }}
215-
LINODE_TOKEN: ${{ secrets.LINODE_TOKEN }}
216-
steps:
217-
- name: Checkout repository
218-
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7
219-
220-
- name: Authenticate to Google Cloud
221-
if: matrix.provider == 'gcp' && env.GCLOUD_OIDC_POOL != '' && env.GSA != '' && env.GCLOUD_PROJECT != ''
222-
uses: google-github-actions/auth@7c6bc770dae815cd3e89ee6cdf493a5fab2cc093 # v3
223-
with:
224-
workload_identity_provider: ${{ env.GCLOUD_OIDC_POOL }}
225-
service_account: ${{ env.GSA }}
226-
project_id: ${{ env.GCLOUD_PROJECT }}
227-
228-
- name: Install gcloud
229-
if: matrix.provider == 'gcp' && env.GCLOUD_OIDC_POOL != '' && env.GSA != '' && env.GCLOUD_PROJECT != ''
230-
uses: google-github-actions/setup-gcloud@aa5489c8933f4cc7a4f7d45035b3b1440c9c10db # v3
231-
232-
- name: Sweep smoke resources
233-
if: matrix.provider != 'gcp' || (env.GCLOUD_OIDC_POOL != '' && env.GSA != '' && env.GCLOUD_PROJECT != '')
234-
run: ci/cloud-smoke.sh sweep-${{ matrix.provider }}-${{ matrix.template }}
235-
236-
- name: Skip GCP cleanup
237-
if: matrix.provider == 'gcp' && (env.GCLOUD_OIDC_POOL == '' || env.GSA == '' || env.GCLOUD_PROJECT == '')
238-
run: echo "Skipping GCP cleanup because GCP smoke secrets are not configured."

0 commit comments

Comments
 (0)