Pre-check binary writability before accepting an upgrade request

/api/upgrade now verifies the target can be replaced (create + remove
the swap's tmp file) before returning 202. A sled that cannot write
its own binary — e.g. a container predating the /opt/huskies/bin
layout — fails phase 1 of `upgrade all` loudly instead of returning
202, staying healthy, and silently remaining on the old version, which
is exactly what happened on the first fleet deploy.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019fHdm92yjvguPi2LiXfLB9
This commit is contained in:
Timmy
2026-07-15 17:53:56 +01:00
co-authored by Claude Fable 5
parent ba0a38d403
commit be0c88c801
2 changed files with 36 additions and 7 deletions
+11 -2
View File
@@ -255,9 +255,18 @@ pub async fn upgrade_trigger_handler(
}
};
// Fail fast if the binary cannot be replaced — a background failure after
// a 202 looks like a healthy sled that silently stayed on the old version.
if let Err(e) = crate::upgrade::preflight_target_writable() {
return poem::Response::builder()
.status(StatusCode::INTERNAL_SERVER_ERROR)
.body(e);
}
let project_root = ctx.state.get_project_root().unwrap_or_default();
// Spawn upgrade in background so we can return 202 before exec() fires.
// Spawn the download + swap in the background so we can return 202 before
// the process exits for its Docker restart.
tokio::spawn(async move {
if let Err(e) = crate::upgrade::upgrade_and_reexec(&source_url, &project_root).await {
crate::slog!("[upgrade] Upgrade failed: {e}");
@@ -266,7 +275,7 @@ pub async fn upgrade_trigger_handler(
poem::Response::builder()
.status(StatusCode::ACCEPTED)
.body("Upgrade triggered. The sled will re-exec momentarily.")
.body("Upgrade triggered. The sled will restart momentarily.")
}
/// Canonical artifact filename for sled binaries on this deployment's platform.