huskies: merge 1138 story In-container huskies self-update — huskies upgrade pulls a fresh binary without docker rebuild
This commit is contained in:
@@ -104,6 +104,10 @@ pub fn build_routes(
|
||||
route = route.at("/api/events", get(events::events_handler).data(buf));
|
||||
}
|
||||
|
||||
route = route
|
||||
.at("/api/upgrade", post(upgrade_trigger_handler))
|
||||
.at("/api/huskies-binary", get(serve_binary_handler));
|
||||
|
||||
if let Some(wa_ctx) = whatsapp_ctx {
|
||||
route = route.at(
|
||||
"/webhook/whatsapp",
|
||||
@@ -209,6 +213,72 @@ pub fn debug_crdt_handler(req: &poem::Request) -> poem::Response {
|
||||
.body(serde_json::to_string_pretty(&body).unwrap_or_default())
|
||||
}
|
||||
|
||||
/// `POST /api/upgrade` — trigger a self-update on the running sled.
|
||||
///
|
||||
/// Accepts `{"source_url": "http://gateway:3000/api/huskies-binary"}` and
|
||||
/// spawns the upgrade task in the background, returning 202 immediately.
|
||||
/// The connection will be dropped when `exec()` replaces the process.
|
||||
#[poem::handler]
|
||||
pub async fn upgrade_trigger_handler(
|
||||
body: poem::web::Json<serde_json::Value>,
|
||||
ctx: poem::web::Data<&std::sync::Arc<AppContext>>,
|
||||
) -> poem::Response {
|
||||
let source_url = match body
|
||||
.0
|
||||
.get("source_url")
|
||||
.and_then(|v| v.as_str())
|
||||
.map(|s| s.to_string())
|
||||
{
|
||||
Some(u) => u,
|
||||
None => {
|
||||
return poem::Response::builder()
|
||||
.status(StatusCode::BAD_REQUEST)
|
||||
.body("Missing required field: source_url");
|
||||
}
|
||||
};
|
||||
|
||||
let project_root = ctx.state.get_project_root().unwrap_or_default();
|
||||
|
||||
// Spawn upgrade in background so we can return 202 before exec() fires.
|
||||
tokio::spawn(async move {
|
||||
if let Err(e) = crate::upgrade::upgrade_and_reexec(&source_url, &project_root).await {
|
||||
crate::slog!("[upgrade] Upgrade failed: {e}");
|
||||
}
|
||||
});
|
||||
|
||||
poem::Response::builder()
|
||||
.status(StatusCode::ACCEPTED)
|
||||
.body("Upgrade triggered. The sled will re-exec momentarily.")
|
||||
}
|
||||
|
||||
/// `GET /api/huskies-binary` — serve the running binary so peer sleds can download it.
|
||||
///
|
||||
/// Streams `current_exe()` (the binary that is currently running) as an
|
||||
/// `application/octet-stream` download. Returns 500 if the path cannot be
|
||||
/// resolved or read.
|
||||
#[poem::handler]
|
||||
pub async fn serve_binary_handler() -> poem::Response {
|
||||
let exe = match std::env::current_exe() {
|
||||
Ok(p) => p,
|
||||
Err(e) => {
|
||||
return poem::Response::builder()
|
||||
.status(StatusCode::INTERNAL_SERVER_ERROR)
|
||||
.body(format!("Cannot resolve current executable: {e}"));
|
||||
}
|
||||
};
|
||||
|
||||
match tokio::fs::read(&exe).await {
|
||||
Ok(bytes) => poem::Response::builder()
|
||||
.status(StatusCode::OK)
|
||||
.header("Content-Type", "application/octet-stream")
|
||||
.header("Content-Disposition", "attachment; filename=\"huskies\"")
|
||||
.body(bytes),
|
||||
Err(e) => poem::Response::builder()
|
||||
.status(StatusCode::INTERNAL_SERVER_ERROR)
|
||||
.body(format!("Cannot read binary at {}: {e}", exe.display())),
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
Reference in New Issue
Block a user