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. |
|
| `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. |
|
| `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. |
|
| `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
|
**Intentionally not queued** (sub-second ops): `start`, `stop`, `kill`.
|
||||||
without serving the "one at a time" goal): `start`, `stop`, `restart`, `kill`.
|
|
||||||
|
|
||||||
### Dedup
|
### 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 {
|
if let Some(reject) = guard_agent_name(&state, &logical).await {
|
||||||
return reject;
|
return reject;
|
||||||
}
|
}
|
||||||
lifecycle_action(
|
state.coord.rebuild_queue.enqueue(
|
||||||
&state,
|
crate::rebuild_queue::QueueKind::Restart,
|
||||||
&name,
|
logical,
|
||||||
crate::coordinator::TransientKind::Restarting,
|
crate::rebuild_queue::QueueSource::Manual,
|
||||||
"restart",
|
"manual via dashboard ↺ R3START button".to_owned(),
|
||||||
|n| async move { lifecycle::restart(&n).await },
|
None,
|
||||||
|s, n| s.coord.kick_agent(n, "container restarted"),
|
);
|
||||||
)
|
state.coord.emit_rebuild_queue_snapshot();
|
||||||
.await
|
(StatusCode::OK, "ok").into_response()
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn post_start(State(state): State<AppState>, AxumPath(name): AxumPath<String>) -> 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 } => {
|
ManagerRequest::Restart { name } => {
|
||||||
tracing::info!(%name, "manager: restart");
|
tracing::info!(%name, "manager: enqueue restart");
|
||||||
if name == crate::lifecycle::MANAGER_NAME {
|
if name == crate::lifecycle::MANAGER_NAME {
|
||||||
return ManagerResponse::Err {
|
return ManagerResponse::Err {
|
||||||
message: "refusing to restart the manager from itself".into(),
|
message: "refusing to restart the manager from itself".into(),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
match lifecycle::restart(name).await {
|
coord.rebuild_queue.enqueue(
|
||||||
Ok(()) => {
|
crate::rebuild_queue::QueueKind::Restart,
|
||||||
coord.kick_agent(name, "container restarted");
|
name.to_owned(),
|
||||||
ManagerResponse::Ok
|
crate::rebuild_queue::QueueSource::Manual,
|
||||||
}
|
"manager `restart` tool".to_owned(),
|
||||||
Err(e) => ManagerResponse::Err {
|
None,
|
||||||
message: format!("{e:#}"),
|
);
|
||||||
},
|
coord.emit_rebuild_queue_snapshot();
|
||||||
}
|
ManagerResponse::Ok
|
||||||
}
|
}
|
||||||
ManagerRequest::Update { name } => {
|
ManagerRequest::Update { name } => {
|
||||||
tracing::info!(%name, "manager: enqueue update");
|
tracing::info!(%name, "manager: enqueue update");
|
||||||
|
|
|
||||||
|
|
@ -32,6 +32,10 @@ pub enum QueueKind {
|
||||||
/// after the lock bump; children run as independent queue entries
|
/// after the lock bump; children run as independent queue entries
|
||||||
/// grouped under this parent's `id`. `agent` = `"hyperhive"`.
|
/// grouped under this parent's `id`. `agent` = `"hyperhive"`.
|
||||||
StartupSweep,
|
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 {
|
impl QueueKind {
|
||||||
|
|
@ -42,6 +46,7 @@ impl QueueKind {
|
||||||
QueueKind::Spawn => "spawn",
|
QueueKind::Spawn => "spawn",
|
||||||
QueueKind::Destroy => "destroy",
|
QueueKind::Destroy => "destroy",
|
||||||
QueueKind::StartupSweep => "startup_sweep",
|
QueueKind::StartupSweep => "startup_sweep",
|
||||||
|
QueueKind::Restart => "restart",
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -591,6 +596,15 @@ async fn dispatch(
|
||||||
// `finish` clears the step label; no explicit clear needed here.
|
// `finish` clears the step label; no explicit clear needed here.
|
||||||
Ok(())
|
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