Self-upgrade fixes — PLATFORM_ACR_NAME env and failed_pre restart¶
Motivation¶
Starting a self-upgrade from the dashboard failed immediately with:
The upgrade row went to failed_pre and could not be restarted from the UI:
the Start button was enabled but every retry returned HTTP 409
upgrade already in progress (state=failed_pre).
Root causes (two distinct bugs)¶
-
Missing env on the worker. The self-upgrade pipeline runs as a Celery task on the worker sidecar. Its image builder (
api.services.upgrade.image_builder._acr_name()) readsPLATFORM_ACR_NAMEfrom the environment, but that variable was only defined on the terminal sidecar ininfra/modules/containerAppControl.bicep. The api / worker / beat sidecars never received it, soaz acr buildaborted in pre-flight. -
failed_prewas a stuck terminal state.start_upgrade_inlineonly accepted the CAS transitionidle → queued. Once the row was infailed_pre(or any terminal failure / success state) the start was refused with 409, and there was no reset path anywhere in the app. The SPA's Start button is enabled for thefailed/rolled_backphases, so the user could click it but always got 409 — the upgrade flow was permanently wedged after a single pre-build failure.
User-facing change¶
- The self-upgrade
az acr buildstep now finds the platform ACR and proceeds. - A failed or completed upgrade can be restarted from the dashboard.
startnow accepts a fresh upgrade from any non-active state (idle,failed_pre,failed_rollout,rolled_back,rollback_failed,succeeded). Genuinely in-flight states (queued,fetching,building,patching,rolling_out,validating,confirming,rolling_back) still block a concurrent start with 409.
API / IaC diff summary¶
infra/modules/containerAppControl.bicep(+ compiled.json): addPLATFORM_ACR_NAME(valueplatformAcrName) to the api, worker, and beat sidecarenvarrays. The terminal sidecar already had it.api/tasks/upgrade/pipeline.py: add_RESTARTABLE_START_STATESand_cas_start_from_restartable();start_upgrade_inlinenow tries the→ queuedCAS from each restartable state (idle first) instead of onlyidle → queued. The 409-on-active-state contract is unchanged.- No response-shape change;
UpgradeStartRefused/ 409 semantics preserved for in-flight states.
Validation evidence¶
- Live root-cause confirmation:
GET /api/upgrade/status→state=failed_pre, phase_detail="… PLATFORM_ACR_NAME is not set …";POST /api/upgrade/start→409 "upgrade already in progress (state=failed_pre)". - Live env remediation applied to the running revision (api/worker/beat now
carry
PLATFORM_ACR_NAME=acrelbdashboard3abp67bppe), verified viaaz containerapp revision show … env[?name=='PLATFORM_ACR_NAME']. - New regression tests in
api/tests/test_upgrade_task.py:test_start_recovers_from_failed_pre,test_start_recovers_from_any_terminal_state(parametrised over all five terminal states), andtest_start_refused_while_active(parametrised over all eight active states). uv run pytest -q api/tests→ 3036 passed, 3 skipped.uv run ruff checkon touched files → clean.