refactor(#1474): extract dispatch arm logic into helpers (server + agent_server)
This commit is contained in:
parent
59054b1f57
commit
c6d9f59c4d
2 changed files with 476 additions and 396 deletions
|
|
@ -73,42 +73,10 @@ async fn handle(stream: UnixStream, coord: Arc<Coordinator>) -> Result<()> {
|
|||
}
|
||||
}
|
||||
|
||||
#[allow(
|
||||
clippy::too_many_lines,
|
||||
reason = "flat dispatch table: one match arm per host-socket request \
|
||||
variant; splitting it would scatter the routing logic"
|
||||
)]
|
||||
async fn dispatch(req: &HostRequest, coord: Arc<Coordinator>) -> HostResponse {
|
||||
let result: anyhow::Result<HostResponse> = async {
|
||||
Ok(match req {
|
||||
HostRequest::Spawn { name } => {
|
||||
tracing::info!(%name, "spawn");
|
||||
let agent_dir = coord.ensure_runtime(name)?;
|
||||
let hive = coord.hive_env();
|
||||
let paths = Coordinator::agent_paths(name, agent_dir);
|
||||
match lifecycle::spawn(name, &hive, &paths).await {
|
||||
Ok(()) => {
|
||||
coord.notify_manager(&hive_sh4re::HelperEvent::Spawned {
|
||||
agent: name.clone(),
|
||||
ok: true,
|
||||
note: None,
|
||||
sha: None,
|
||||
});
|
||||
}
|
||||
Err(e) => {
|
||||
// Roll back socket registration if container creation failed.
|
||||
coord.unregister_agent(name);
|
||||
coord.notify_manager(&hive_sh4re::HelperEvent::Spawned {
|
||||
agent: name.clone(),
|
||||
ok: false,
|
||||
note: Some(format!("{e:#}")),
|
||||
sha: None,
|
||||
});
|
||||
return Err(e);
|
||||
}
|
||||
}
|
||||
HostResponse::success()
|
||||
}
|
||||
HostRequest::Spawn { name } => handle_spawn(&coord, name).await?,
|
||||
HostRequest::RequestSpawn { name } => {
|
||||
tracing::info!(%name, "request_spawn");
|
||||
let id =
|
||||
|
|
@ -118,86 +86,18 @@ async fn dispatch(req: &HostRequest, coord: Arc<Coordinator>) -> HostResponse {
|
|||
tracing::info!(%id, %name, "spawn approval queued");
|
||||
HostResponse::success()
|
||||
}
|
||||
HostRequest::Kill { name } => {
|
||||
tracing::info!(%name, "kill");
|
||||
lifecycle::kill(name).await?;
|
||||
coord.unregister_agent(name);
|
||||
coord.notify_manager(&hive_sh4re::HelperEvent::Killed {
|
||||
agent: name.clone(),
|
||||
});
|
||||
HostResponse::success()
|
||||
}
|
||||
HostRequest::Kill { name } => handle_kill(&coord, name).await?,
|
||||
HostRequest::Restart { name } => {
|
||||
tracing::info!(%name, "restart");
|
||||
lifecycle::restart(name).await?;
|
||||
HostResponse::success()
|
||||
}
|
||||
HostRequest::RestartAll => {
|
||||
tracing::info!("restart-all");
|
||||
let agents = lifecycle::list().await?;
|
||||
let mut ok_agents: Vec<String> = Vec::new();
|
||||
let mut errors: Vec<String> = Vec::new();
|
||||
for agent in &agents {
|
||||
if let Err(e) = lifecycle::restart(agent).await {
|
||||
tracing::warn!(%agent, error = ?e, "restart-all: failed to restart agent");
|
||||
errors.push(format!("{agent}: {e:#}"));
|
||||
} else {
|
||||
ok_agents.push(agent.clone());
|
||||
}
|
||||
}
|
||||
if errors.is_empty() {
|
||||
HostResponse::list(ok_agents)
|
||||
} else {
|
||||
HostResponse {
|
||||
ok: false,
|
||||
error: Some(errors.join("; ")),
|
||||
agents: Some(ok_agents),
|
||||
approvals: None,
|
||||
}
|
||||
}
|
||||
}
|
||||
HostRequest::RestartAll => handle_restart_all().await?,
|
||||
HostRequest::Destroy { name, purge } => {
|
||||
actions::destroy(&coord, name, *purge).await?;
|
||||
HostResponse::success()
|
||||
}
|
||||
HostRequest::Rebuild { name } => {
|
||||
tracing::info!(%name, "rebuild");
|
||||
let agent_dir = coord.ensure_runtime(name)?;
|
||||
let hive = coord.hive_env();
|
||||
let paths = Coordinator::agent_paths(name, agent_dir);
|
||||
let result = lifecycle::rebuild(name, &hive, &paths, &|_| (), &|_| ()).await;
|
||||
// Mirror auto_update::rebuild_agent — the manager wants
|
||||
// to know about every rebuild attempt regardless of
|
||||
// which surface triggered it, especially failures
|
||||
// (build error → manager can adjust the agent's
|
||||
// agent.nix). Without this the admin-socket CLI was
|
||||
// a notify-gap.
|
||||
match &result {
|
||||
Ok(()) => {
|
||||
coord.notify_manager(&hive_sh4re::HelperEvent::Rebuilt {
|
||||
agent: name.clone(),
|
||||
ok: true,
|
||||
note: None,
|
||||
sha: None,
|
||||
tag: None,
|
||||
});
|
||||
// Wake the agent's next turn with the
|
||||
// "you were rebuilt" hint. Same pattern as
|
||||
// auto_update::rebuild_agent and the dashboard
|
||||
// rebuild path — this is the CLI's equivalent.
|
||||
coord.kick_agent(name, "container rebuilt");
|
||||
}
|
||||
Err(e) => coord.notify_manager(&hive_sh4re::HelperEvent::Rebuilt {
|
||||
agent: name.clone(),
|
||||
ok: false,
|
||||
note: Some(format!("{e:#}")),
|
||||
sha: None,
|
||||
tag: None,
|
||||
}),
|
||||
}
|
||||
result?;
|
||||
HostResponse::success()
|
||||
}
|
||||
HostRequest::Rebuild { name } => handle_rebuild(&coord, name).await?,
|
||||
HostRequest::List => HostResponse::list(lifecycle::list().await?),
|
||||
HostRequest::Pending => HostResponse::pending(coord.approvals.pending()?),
|
||||
HostRequest::Approve { id } => {
|
||||
|
|
@ -229,3 +129,111 @@ async fn dispatch(req: &HostRequest, coord: Arc<Coordinator>) -> HostResponse {
|
|||
Err(e) => HostResponse::error(format!("{e:#}")),
|
||||
}
|
||||
}
|
||||
|
||||
/// Create + start the container for `name`, rolling back socket
|
||||
/// registration and notifying the manager on failure.
|
||||
async fn handle_spawn(coord: &Arc<Coordinator>, name: &str) -> Result<HostResponse> {
|
||||
tracing::info!(%name, "spawn");
|
||||
let agent_dir = coord.ensure_runtime(name)?;
|
||||
let hive = coord.hive_env();
|
||||
let paths = Coordinator::agent_paths(name, agent_dir);
|
||||
match lifecycle::spawn(name, &hive, &paths).await {
|
||||
Ok(()) => {
|
||||
coord.notify_manager(&hive_sh4re::HelperEvent::Spawned {
|
||||
agent: name.to_owned(),
|
||||
ok: true,
|
||||
note: None,
|
||||
sha: None,
|
||||
});
|
||||
}
|
||||
Err(e) => {
|
||||
// Roll back socket registration if container creation failed.
|
||||
coord.unregister_agent(name);
|
||||
coord.notify_manager(&hive_sh4re::HelperEvent::Spawned {
|
||||
agent: name.to_owned(),
|
||||
ok: false,
|
||||
note: Some(format!("{e:#}")),
|
||||
sha: None,
|
||||
});
|
||||
return Err(e);
|
||||
}
|
||||
}
|
||||
Ok(HostResponse::success())
|
||||
}
|
||||
|
||||
/// Kill `name`'s container, unregister its socket, notify the manager.
|
||||
async fn handle_kill(coord: &Arc<Coordinator>, name: &str) -> Result<HostResponse> {
|
||||
tracing::info!(%name, "kill");
|
||||
lifecycle::kill(name).await?;
|
||||
coord.unregister_agent(name);
|
||||
coord.notify_manager(&hive_sh4re::HelperEvent::Killed {
|
||||
agent: name.to_owned(),
|
||||
});
|
||||
Ok(HostResponse::success())
|
||||
}
|
||||
|
||||
/// Restart every container, aggregating per-agent failures into one
|
||||
/// response rather than aborting on the first error.
|
||||
async fn handle_restart_all() -> Result<HostResponse> {
|
||||
tracing::info!("restart-all");
|
||||
let agents = lifecycle::list().await?;
|
||||
let mut ok_agents: Vec<String> = Vec::new();
|
||||
let mut errors: Vec<String> = Vec::new();
|
||||
for agent in &agents {
|
||||
if let Err(e) = lifecycle::restart(agent).await {
|
||||
tracing::warn!(%agent, error = ?e, "restart-all: failed to restart agent");
|
||||
errors.push(format!("{agent}: {e:#}"));
|
||||
} else {
|
||||
ok_agents.push(agent.clone());
|
||||
}
|
||||
}
|
||||
if errors.is_empty() {
|
||||
Ok(HostResponse::list(ok_agents))
|
||||
} else {
|
||||
Ok(HostResponse {
|
||||
ok: false,
|
||||
error: Some(errors.join("; ")),
|
||||
agents: Some(ok_agents),
|
||||
approvals: None,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
/// Rebuild `name`'s container, notifying the manager of the outcome
|
||||
/// (success or failure) and kicking the agent's next turn on success.
|
||||
async fn handle_rebuild(coord: &Arc<Coordinator>, name: &str) -> Result<HostResponse> {
|
||||
tracing::info!(%name, "rebuild");
|
||||
let agent_dir = coord.ensure_runtime(name)?;
|
||||
let hive = coord.hive_env();
|
||||
let paths = Coordinator::agent_paths(name, agent_dir);
|
||||
let result = lifecycle::rebuild(name, &hive, &paths, &|_| (), &|_| ()).await;
|
||||
// Mirror auto_update::rebuild_agent — the manager wants to know
|
||||
// about every rebuild attempt regardless of which surface triggered
|
||||
// it, especially failures (build error → manager can adjust the
|
||||
// agent's agent.nix). Without this the admin-socket CLI was a
|
||||
// notify-gap.
|
||||
match &result {
|
||||
Ok(()) => {
|
||||
coord.notify_manager(&hive_sh4re::HelperEvent::Rebuilt {
|
||||
agent: name.to_owned(),
|
||||
ok: true,
|
||||
note: None,
|
||||
sha: None,
|
||||
tag: None,
|
||||
});
|
||||
// Wake the agent's next turn with the "you were rebuilt"
|
||||
// hint. Same pattern as auto_update::rebuild_agent and the
|
||||
// dashboard rebuild path — this is the CLI's equivalent.
|
||||
coord.kick_agent(name, "container rebuilt");
|
||||
}
|
||||
Err(e) => coord.notify_manager(&hive_sh4re::HelperEvent::Rebuilt {
|
||||
agent: name.to_owned(),
|
||||
ok: false,
|
||||
note: Some(format!("{e:#}")),
|
||||
sha: None,
|
||||
tag: None,
|
||||
}),
|
||||
}
|
||||
result?;
|
||||
Ok(HostResponse::success())
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue