Fix corrupt state archive: self-heal the default group and guard rad shutdown - #12840
Conversation
Dependency Review✅ No vulnerabilities or license issues or OpenSSF Scorecard issues found.Scanned FilesNone |
There was a problem hiding this comment.
Pull request overview
Fixes a deterministic failure in the repo-based deploy pipeline where the default resource group was being created before rad startup and then wiped when rad startup restored the control-plane database snapshot.
Changes:
- Stop creating/switching the
defaultresource group in the “Configure Radius workspace” step (leave workspace creation only). - Create and switch to the
defaultresource group afterrad startupcompletes, with expanded inline documentation explaining why.
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## main #12840 +/- ##
=======================================
Coverage 59.97% 59.98%
=======================================
Files 776 776
Lines 46410 46410
=======================================
+ Hits 27836 27837 +1
+ Misses 18574 18573 -1 ☔ View full report in Codecov by Harness. 🚀 New features to boost your workflow:
|
The repo-based deploy pipeline persists control-plane state at teardown via rad shutdown, which runs under if: always() so a partially-applied deploy is not lost. But it ran unconditionally: a workflow that failed before the restore-state action's rad startup completed would still back up the uninitialized control-plane databases and push them over the durable state archive. Because rad startup restores that archive wholesale (pg_dump --clean --if-exists), one such run corrupts state for every future run -- the archive is replaced with blank state, restored on the next run, re-persisted, and so on. This is what surfaced as a deterministic 'resource group "default" not found' after #12829 stopped rad install kubernetes from re-seeding the group server-side. Gate persistence on a successful restore: restore-state writes a marker after rad startup succeeds, and teardown skips rad shutdown when the marker is absent. rad startup is still a legitimate no-op on the first run (empty archive) but succeeds and writes the marker, so first-run seeding and persistence after a later deploy failure both still happen; only runs that never reached a successful startup are prevented from overwriting the archive. Add a wiring test asserting the marker is written after rad startup and that teardown gates rad shutdown on it. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Signed-off-by: sk593 <shruthikumar@microsoft.com>
d53bef2 to
ff82857
Compare
The repo-based pipeline deploys Radius.Core applications, but the teardown 'Show application status' step ran the legacy 'rad app list', which queries the Applications.Core plane and lists none of the deployed apps. Use 'rad app list --preview' to match the Radius.Core surface the rest of the pipeline uses. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Signed-off-by: sk593 <shruthikumar@microsoft.com>
Replace the grep-based wiring test with one that extracts the actual run blocks from the restore-state and teardown composite actions and executes them with stubbed rad/git, asserting real behavior: - restore-state writes the state-restored marker after rad startup succeeds - teardown skips rad shutdown when the marker is absent (a run that failed before startup must not overwrite the durable state archive) - teardown runs rad shutdown when the marker is present - teardown lists applications with rad app list --preview, not the legacy Applications.Core plane Renamed the target to test-teardown and verified via mutation that each assertion fails when its invariant is broken. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Signed-off-by: sk593 <shruthikumar@microsoft.com>
Replace the RUNNER_TEMP file marker with an explicit composite-action output (restore-state's state-restored) piped to the teardown action's state-restored input, so the startup->shutdown dependency is visible in the workflow YAML and fails safe if the steps are split across jobs. Move rad group create/switch after rad startup so an already-corrupt, group-less state archive self-heals on the next run, and emit a ::warning:: when persistence is skipped. Surface (not swallow) a failed rad app list --preview at teardown. Rework teardown_test.sh for the output/input mechanism: assert the group is created after startup, add a negative path where rad startup fails and the output is never set, assert the first-run no-op still sets it, assert every workflow wires restore-state->teardown in the same job, and clean up a single scratch dir on exit. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Signed-off-by: sk593 <shruthikumar@microsoft.com>
Discover teardown-using workflows dynamically instead of hardcoding, so a new caller is covered automatically, and assert teardown and restore-state are used by the same set of workflows. Extract the wiring check into a reusable helper and, for every discovered workflow, assert both the real (positive) wiring and that the check rejects each broken variant: missing `id: restore-state`, missing state-restored pass-through, and restore-state/teardown split across jobs. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Signed-off-by: sk593 <shruthikumar@microsoft.com>
Radius functional test overviewClick here to see the test run details
Test Status⌛ Building Radius and pushing container images for functional tests... |
Description
The repo-based deploy pipeline (
run-rad-commands-azure.yml/run-rad-commands-aws.yml, and thedelete-*workflows) restores control-plane state at setup withrad startupand persists it at teardown withrad shutdown.rad startuprestores the previous run's snapshot withpg_dump --clean --if-exists— a wholesale drop-and-replace of the control-plane PostgreSQL databases, not a merge.Two independent problems corrupted the shared state archive and made deploys fail deterministically with
resource group "default" not found:A corrupt (group-less) archive never repaired itself. After Revert default resource group and environment creation from rad install kubernetes #12829 stopped
rad install kubernetesfrom re-seeding thedefaultgroup server-side, the pipeline created it beforerad startup. Since startup restores the databases wholesale, a group created before it is dropped whenever the restored snapshot lacks the group — so once the archive was group-less it stayed that way, and every deploy failed:A pre-startup failure overwrote the good archive with blank state.
rad shutdownran unconditionally underif: always(). A workflow that failed beforerad startupcompleted (setup, cluster connect, OIDC, credential registration, or startup itself) still hit teardown, backed up the uninitialized databases, and overwrote the archive. The next run restored that blank state wholesale and re-persisted it — self-perpetuating corruption.Fix
A two-part fix; neither half is sufficient alone.
1. Create the
defaultgroup afterrad startup(self-heal). Movingrad group create/switchbelow the restore means the group is (re)created on top of whatever the restore produced, so it is present for the deploy and persisted by the nextrad shutdown. A group-less archive now heals itself on the next successful run.rad startuphas no resource-group dependency, so nothing needs the group earlier.2. Guard
rad shutdownon a successful restore.restore-stateexposes astate-restoredoutput, set totrueonly afterrad startupsucceeds. The workflow passes it intoteardown'sstate-restoredinput; when it is nottrue, teardown skipsrad shutdown(emitting a::warning::) so a run that never reached startup cannot overwrite the archive. This replaces the earlier${RUNNER_TEMP}file marker with an explicit action output→input, so the dependency is visible in the workflow YAML and fails safe if the steps are ever split across jobs.This preserves the behaviors we do want:
rad startupis a no-op on the first run (empty archive) but still succeeds and sets the output, sorad shutdownruns and seeds the archive.true, sorad shutdownstill runs and that work is not lost.The guard is pure workflow orchestration — it does not couple
rad startupandrad shutdownas commands.Also in this PR
teardown:rad app list→rad app list --preview. The pipeline deploysRadius.Coreapplications, but the teardown status step queried the legacyApplications.Coreplane and listed nothing.--previewmatches the surface the rest of the pipeline uses, and a failed listing now surfaces a::warning::instead of being swallowed by|| true.Type of change
Fixes
Fixes #12838
Testing
.github/extension/actions/teardown/teardown_test.sh(wired intobuild/test.mkastest-teardown) extracts the actualrun:blocks from therestore-stateandteardowncomposite actions and executes them with stubbedrad/git(no cluster/CLI), asserting real behavior:restore-statesetsstate-restored=trueon$GITHUB_OUTPUTafterrad startup, and creates thedefaultgroup after startup (order asserted from the invocation log).rad startupfails, the block exits non-zero and never sets the output.rad startupstill sets the output.teardownrunsrad shutdownonly whenstate-restored == "true", and otherwise skips with a::warning::.teardownlists apps viarad app list --previewand warns (does not swallow) on failure.restore-state→teardownwithin the same job.Verified by mutation that each invariant, when broken, fails the test.
make test-teardownandmake test-extension-action-shell-syntaxpass; the test isshellcheck-clean.Follow-ups (out of scope)
Raised in review and intentionally deferred — neither is required to fix #12838:
rad shutdownshould refuse to persist a degenerate/empty snapshot (defense-in-depth for control-plane degradation after a successful startup).concurrency:group to serialize workflows that share the state archive.Notes
Example failing runs (before this fix): https://github.com/sk593/aks-store-demo/actions/runs/33015265288, https://github.com/sk593/aks-store-demo/actions/runs/33016468544