feat(#343): route container restart through rebuild queue
This commit is contained in:
parent
817d94023d
commit
dce2bd0686
4 changed files with 35 additions and 21 deletions
|
|
@ -38,9 +38,9 @@ somewhere."
|
|||
| `MetaUpdate` | `nix flake update` on the meta flake. The worker runs the lock bump itself, then enqueues a cascade of `Rebuild` entries with `parent_id` set to the meta-update's id. |
|
||||
| `Spawn` | First-deploy of a new agent (approval-driven). Same serialisation as `Rebuild` from the operator's POV. |
|
||||
| `Destroy` | For future use (`destroy --purge` does real I/O). Variant exists so the wire shape doesn't change later; not currently routed through the queue. |
|
||||
| `Restart` | Stop + start a container without touching config (~5-10s). Routed through the queue so it serialises against in-flight rebuilds for the same agent — prevents a restart racing a rebuild mid-flight. Sources: dashboard ↺ button, manager `restart` MCP tool. |
|
||||
|
||||
**Intentionally not queued** (sub-second ops; adding them adds dashboard noise
|
||||
without serving the "one at a time" goal): `start`, `stop`, `restart`, `kill`.
|
||||
**Intentionally not queued** (sub-second ops): `start`, `stop`, `kill`.
|
||||
|
||||
### Dedup
|
||||
|
||||
|
|
|
|||
|
|
@ -2720,15 +2720,15 @@ async fn post_restart(State(state): State<AppState>, AxumPath(name): AxumPath<St
|
|||
if let Some(reject) = guard_agent_name(&state, &logical).await {
|
||||
return reject;
|
||||
}
|
||||
lifecycle_action(
|
||||
&state,
|
||||
&name,
|
||||
crate::coordinator::TransientKind::Restarting,
|
||||
"restart",
|
||||
|n| async move { lifecycle::restart(&n).await },
|
||||
|s, n| s.coord.kick_agent(n, "container restarted"),
|
||||
)
|
||||
.await
|
||||
state.coord.rebuild_queue.enqueue(
|
||||
crate::rebuild_queue::QueueKind::Restart,
|
||||
logical,
|
||||
crate::rebuild_queue::QueueSource::Manual,
|
||||
"manual via dashboard ↺ R3START button".to_owned(),
|
||||
None,
|
||||
);
|
||||
state.coord.emit_rebuild_queue_snapshot();
|
||||
(StatusCode::OK, "ok").into_response()
|
||||
}
|
||||
|
||||
async fn post_start(State(state): State<AppState>, AxumPath(name): AxumPath<String>) -> Response {
|
||||
|
|
|
|||
|
|
@ -159,21 +159,21 @@ async fn dispatch(req: &ManagerRequest, coord: &Arc<Coordinator>) -> ManagerResp
|
|||
}
|
||||
}
|
||||
ManagerRequest::Restart { name } => {
|
||||
tracing::info!(%name, "manager: restart");
|
||||
tracing::info!(%name, "manager: enqueue restart");
|
||||
if name == crate::lifecycle::MANAGER_NAME {
|
||||
return ManagerResponse::Err {
|
||||
message: "refusing to restart the manager from itself".into(),
|
||||
};
|
||||
}
|
||||
match lifecycle::restart(name).await {
|
||||
Ok(()) => {
|
||||
coord.kick_agent(name, "container restarted");
|
||||
ManagerResponse::Ok
|
||||
}
|
||||
Err(e) => ManagerResponse::Err {
|
||||
message: format!("{e:#}"),
|
||||
},
|
||||
}
|
||||
coord.rebuild_queue.enqueue(
|
||||
crate::rebuild_queue::QueueKind::Restart,
|
||||
name.to_owned(),
|
||||
crate::rebuild_queue::QueueSource::Manual,
|
||||
"manager `restart` tool".to_owned(),
|
||||
None,
|
||||
);
|
||||
coord.emit_rebuild_queue_snapshot();
|
||||
ManagerResponse::Ok
|
||||
}
|
||||
ManagerRequest::Update { name } => {
|
||||
tracing::info!(%name, "manager: enqueue update");
|
||||
|
|
|
|||
|
|
@ -32,6 +32,10 @@ pub enum QueueKind {
|
|||
/// after the lock bump; children run as independent queue entries
|
||||
/// grouped under this parent's `id`. `agent` = `"hyperhive"`.
|
||||
StartupSweep,
|
||||
/// Stop + start a container without touching config. Fast op (~5-10s).
|
||||
/// Queued so it serialises against in-flight rebuilds for the same
|
||||
/// agent — prevents a restart racing a rebuild mid-flight.
|
||||
Restart,
|
||||
}
|
||||
|
||||
impl QueueKind {
|
||||
|
|
@ -42,6 +46,7 @@ impl QueueKind {
|
|||
QueueKind::Spawn => "spawn",
|
||||
QueueKind::Destroy => "destroy",
|
||||
QueueKind::StartupSweep => "startup_sweep",
|
||||
QueueKind::Restart => "restart",
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -591,6 +596,15 @@ async fn dispatch(
|
|||
// `finish` clears the step label; no explicit clear needed here.
|
||||
Ok(())
|
||||
}
|
||||
(QueueKind::Restart, _) => {
|
||||
let name = &entry.agent;
|
||||
let _guard = coord.transient_guard(name, crate::coordinator::TransientKind::Restarting);
|
||||
coord.set_queue_step(Some(entry.id), "nixos-container restart");
|
||||
crate::lifecycle::restart(name).await?;
|
||||
coord.kick_agent(name, "container restarted");
|
||||
coord.rescan_containers_and_emit().await;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Reference in a new issue