Repository navigation
fix(gcp): GKE rebuilds that can repeat - #2125
Merged
Merged
Conversation
…ewall rules a GKE cluster leaves
…ames the burned-ID case
…retries only while the network stands
…e its stubs, and a retry's failure is explained
A relative TMPDIR made the stub PATH entry relative. The guard passed from the test's cwd, then teardown.sh cd'd into opentofu/ where the entry no longer resolved, and the real terramate ran with the destroy confirmed. The stub directory is now resolved to an absolute path, and the guard refuses a relative one.
Smana
force-pushed
the
fix/gke-rebuild-hygiene
branch
from
September 29, 2026 10:01
1a08124 to
2b0ce63
Compare
Contributor
🔍 Rendered manifest diff — this PR vs
|
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Platform fix — merged when green and reviewed, after G-1 (#2123) (owner, 2026-09-29; GP-21). Stacked on
fix/openbao-stage2-gcp; GitHub retargets it tomainonce #2123 merges.Every change below is a bug a live gcp-0 rebuild hit. Together they make a GKE teardown-and-rebuild repeatable.
What changes
xplane_*roles_v3); the destroystate rms the roles, the deploy adopts them (scripts/ops/gcp/adopt-custom-roles.sh). external-dns reads the role from the newgcp_dns_editor_roleConfigMap keygke/configureapply bypasses the configure script: no.tls/ca.pem, and a restoredjwt/gcp-0mount keeps the destroyed cluster's issuerjwt/gcp-0before its applydeploy_identity_provideridentity_provider_urluntil the standalone configure runaws-load-balancer-controller, which gcp-0 never runs: no private record was ever writtendependsOnobjectCreator, so the OpenBao snapshot identity's bucket grant was denied: no snapshotsk8s-*firewall rules in no tofu state; the firewall rules block the VPC deleteteardown.shreports them; on a confirmed teardown it runs the new guardedsweep-lb-orphans.sh --apply, and retries the destroy once only if the network still stands81524a7 restores the dated incident (2026-09-16, #2044) behind stage 2's flux-operator
state rmcomment.Custom-role hardening (601bd3a), from review of 414075c:
state rmruns only on the roles actually in state, with-lock-timeout=5mand no|| true. Astate rmlost to a lock, backend or auth error now stops the destroy, instead of letting the cluster destroy delete the roles and burn their IDs.adopt-custom-roles.shtreats onlyNOT_FOUNDas absent. Any other describe error (expired auth,invalid_rapt) exits 1 rather than letting the apply create a role that exists.[absent ]line names the burned-ID case: a role deleted more than 7 days ago isNOT_FOUNDbut its ID stays reserved, so if the create fails, bumpcustom_role_suffix.custom_role_suffixhas no default: a run withoutvariables.tfvarsfails instead of planning the live roles' replacement.scripts/ops/tasks.yamlnow indexes both new operator scripts:ops:gcp:sweep-lb-orphansandops:gcp:adopt-custom-roles.Teardown and sweep flow
flowchart TD A[teardown.sh] --> B[terramate --reverse --continue-on-error destroy] B --> C{TM_CLOUD wants gcp<br/>and TM_DESTROY_CONFIRMED=true?} C -- no --> V C -- yes --> D[sweep-lb-orphans.sh --apply] D --> E{any GKE cluster in the project,<br/>in any state, or list failed?} E -- yes --> R[exit 3: nothing swept,<br/>clusters named] --> N E -- no --> F["delete project-wide forwarding rules<br/>whose description carries kubernetes.io/service-name"] F --> G[delete target pools<br/>same description marker] G --> H["delete k8s-* firewall rules<br/>on vpc-europe-west4-dev only"] H --> N{network vpc-europe-west4-dev<br/>still describes?} N -- yes --> J[retry destroy once<br/>shared with the AWS lane] N -- no --> V J --> V[verify against GCP:<br/>clusters, instances, forwarding rules,<br/>target pools, k8s-* firewall rules, disks] V --> K{anything left<br/>or unverifiable?} K -- yes --> X[exit non-zero] K -- no --> Y[exit 0]--apply.--clusteronly names the teardown in the output.TM_CLOUD=alldestroys at most twice.gcp/openbao/cluster's pre-destroy snapshot of a gone node). The snapshot is deliberately not auto-skipped, since the first pass may never have reached that stack. When the exit is non-zero but the cloud verifies clean, the summary prints a note: re-run withTM_OPENBAO_SKIP_SNAPSHOT=trueonly if the first pass's snapshot succeeded. The same note is inwebsite/content/docs/reference/commands.md.What a live apply does
google_project_iam_custom_role.crossplane_{dns,storage,role_reader}keep their addresses; only the ForceNewrole_idchanges.adopt-custom-roles.shtofu apply[absent ]_v3roles_v3roles live, out of state (after a teardown by the new destroy)_v3role soft-deleted ≤ 7 days[deleted]: the apply's create undeletes it_v3role deleted > 7 days ago (describe readsNOT_FOUND, ID still reserved up to 37 days)[absent ], naming the bumpcustom_role_suffixto_v4_v2roles[ok](checks the address, not the ID)NOT_FOUND(expired auth,invalid_rapt) stops the deploy at the adopt step, before any apply.ProjectIAMMemberforns/kube-system/sa/external-dnsoutlives the cluster, so that grant stays live until gcp-0 returns.previewdoes not adopt (it must not mutate state), so after a teardown it shows the three roles as creates thatdeploywill import.gke/configurenow readsdns_editor_rolefrom init's state; a configure plan against an init state from older code fails until init is re-applied (deploy order already applies init first).Gates
task check(go-task 3.53.1)validate-manifests.shRENDER: overlays=95 charts=70 standalone=108 failed=0;Valid: 2093, Invalid: 0, Skipped: 0scripts/ci/tests/run.sh36 passed, 1 skipped, 0 failed(skip:test-vector-vrl, novectorbinary)validate-links.sh/validate-doc-claims.sh/verify-doc-paths.shbash scripts/ci/tests/test-teardown-gcp-sweep.shPASS. Against cac5a07's scripts it fails on "another cluster alive" (exit 0, LBs deleted) and "a clean teardown with the network gone" (2 destroy runs, not 1). The failed-list and filter-scoping cases already held there; each fails on a one-line mutant that removes the guard or a filterbash scripts/ci/tests/test-adopt-custom-roles.shall checks passedpython3 scripts/ci/tests/test-gcp-gke-init-workflow.pyPASSbash scripts/ci/tests/test-terramate-script-refs.shshellcheck -x -S warningonsweep-lb-orphans.sh,teardown.sh,adopt-custom-roles.shand the sweep testtofu fmt -check opentofu/gcp/gke/inittrivy config --exit-code=1onopentofu/gcp/gke/inittofu -chdir=opentofu/gcp/gke/configure validateSuccess! The configuration is valid.Tests stub
gcloud,terramateandaws; nothing here ran against GCP. The teardown test runs the realteardown.shwithTM_DESTROY_CONFIRMED=true, so its setup fails closed and it aborts before every invocation unless the stub directory is absolute and each CLI resolves to, and executes as, its stub. Proved with logging decoys on PATH: an unguarded version reached a decoyterramatedestroy 4 times; with a relativeTMPDIR, the version before1a081247still leaked 3 (teardown.shcds, so a relative stub entry stops resolving); the current test: 0.ADR
None: these are fixes, not a technology choice.
Live evidence
Phase 8, after the 2026-09-29 21:00 reset: a gcp-0 deploy → teardown → rebuild from
integration/agent-factory.opentofu/gcp/gke/configure/.tls/ca.pemand adoptsjwt/gcp-0_v3roles created, then adopted on the rebuildteardown.shreportsTarget pools/k8s-* firewall rules, the sweep clears them, and the VPC deletes