feat(#2290): converge unification cleanup — pull preamble into lifecycle
Collapse the scattered ensure_agent_runtime_dir calls into the lifecycle functions themselves so callers have a single responsibility: - lifecycle::spawn: calls ensure_agent_runtime_dir before write_dropins. Callers (handle_spawn, ensure_root_agent) no longer need a separate preamble step. - lifecycle::rebuild_no_meta spawn path: calls ensure_agent_runtime_dir before write_dropins. apply_commit / merge_config_pr flows no longer need a manual ensure_agent_runtime_dir. - run_create (job-queue): drops ensure_agent_runtime_dir + register_agent. The tail Reconcile's converge_start_preamble handles the runtime dir and mcp_sockets::spawn_poll handles the listener. Create stays purely 'provision + create', not 'create + start'. - handle_spawn (server.rs): drops manual preamble; lifecycle::spawn owns it. Drops unneeded unregister_agent on failure (supervisor handles listener). - ensure_root_agent (auto_update.rs): drops manual ensure_agent_runtime_dir. - actions.rs apply_commit / merge_config_pr: drop manual ensure_agent_runtime_dir; rebuild_no_meta's spawn path handles it. Result: ensure_agent_runtime_dir lives in exactly two places — lifecycle::spawn (direct spawn) and converge_start_preamble (start/reconcile path). All other callers are clean call sites.
This commit is contained in:
parent
a45f65bd73
commit
afdd8c6c9f
5 changed files with 15 additions and 19 deletions
|
|
@ -159,10 +159,8 @@ pub async fn run_approval_apply_commit(
|
||||||
approval_id: i64,
|
approval_id: i64,
|
||||||
) -> Result<()> {
|
) -> Result<()> {
|
||||||
let approval = fetch_approval_for_worker(coord, approval_id, ApprovalKind::ApplyCommit)?;
|
let approval = fetch_approval_for_worker(coord, approval_id, ApprovalKind::ApplyCommit)?;
|
||||||
// Create the bind-mount source dir (first-spawn may not have it yet).
|
// Runtime dir creation is handled inside lifecycle::rebuild_no_meta's
|
||||||
// MCP listener registration is deferred to mcp_sockets::spawn_poll
|
// spawn path (first-spawn) or is already present for rebuilds.
|
||||||
// which fires within 10 s of the container coming up.
|
|
||||||
lifecycle::ensure_agent_runtime_dir(&approval.agent)?;
|
|
||||||
let agent_dir = Coordinator::agent_dir(&approval.agent);
|
let agent_dir = Coordinator::agent_dir(&approval.agent);
|
||||||
let applied_dir = Coordinator::agent_applied_dir(&approval.agent);
|
let applied_dir = Coordinator::agent_applied_dir(&approval.agent);
|
||||||
coord.set_queue_step(queue_entry_id, "apply commit");
|
coord.set_queue_step(queue_entry_id, "apply commit");
|
||||||
|
|
@ -196,7 +194,6 @@ pub async fn run_approval_merge_config_pr(
|
||||||
approval_id: i64,
|
approval_id: i64,
|
||||||
) -> Result<()> {
|
) -> Result<()> {
|
||||||
let approval = fetch_approval_for_worker(coord, approval_id, ApprovalKind::MergeConfigPr)?;
|
let approval = fetch_approval_for_worker(coord, approval_id, ApprovalKind::MergeConfigPr)?;
|
||||||
lifecycle::ensure_agent_runtime_dir(&approval.agent)?;
|
|
||||||
let agent_dir = Coordinator::agent_dir(&approval.agent);
|
let agent_dir = Coordinator::agent_dir(&approval.agent);
|
||||||
let applied_dir = Coordinator::agent_applied_dir(&approval.agent);
|
let applied_dir = Coordinator::agent_applied_dir(&approval.agent);
|
||||||
coord.set_queue_step(queue_entry_id, "merge config pr");
|
coord.set_queue_step(queue_entry_id, "merge config pr");
|
||||||
|
|
|
||||||
|
|
@ -181,11 +181,6 @@ async fn run_swap(coord: &Arc<Coordinator>, claim: &Claim, ctx: &Ctx<'_>) -> Res
|
||||||
/// build+create — no prebuild needed).
|
/// build+create — no prebuild needed).
|
||||||
async fn run_create(coord: &Arc<Coordinator>, claim: &Claim, ctx: &Ctx<'_>) -> Result<NodeOutput> {
|
async fn run_create(coord: &Arc<Coordinator>, claim: &Claim, ctx: &Ctx<'_>) -> Result<NodeOutput> {
|
||||||
let name = &claim.agent;
|
let name = &claim.agent;
|
||||||
// First-spawn: create the bind-mount source dir (tmpfs — empty after
|
|
||||||
// reboot). Register the MCP listener eagerly so it's ready when the
|
|
||||||
// tail Reconcile starts the container and the harness connects.
|
|
||||||
crate::lifecycle::ensure_agent_runtime_dir(name)?;
|
|
||||||
coord.register_agent(name)?;
|
|
||||||
let agent_dir = Coordinator::agent_dir(name);
|
let agent_dir = Coordinator::agent_dir(name);
|
||||||
let hive = coord.hive_env();
|
let hive = coord.hive_env();
|
||||||
let paths = Coordinator::agent_paths(name, agent_dir);
|
let paths = Coordinator::agent_paths(name, agent_dir);
|
||||||
|
|
@ -194,6 +189,9 @@ async fn run_create(coord: &Arc<Coordinator>, claim: &Claim, ctx: &Ctx<'_>) -> R
|
||||||
// (sync_agents commit) before `nixos-container create` — hold the
|
// (sync_agents commit) before `nixos-container create` — hold the
|
||||||
// deploy-window gate so that commit can't land inside another
|
// deploy-window gate so that commit can't land inside another
|
||||||
// node's staged deploy window.
|
// node's staged deploy window.
|
||||||
|
// Runtime dir creation and MCP listener registration are deferred to
|
||||||
|
// the tail Reconcile's converge_start_preamble / mcp_sockets supervisor
|
||||||
|
// so this node stays purely "provision + create", not "create + start".
|
||||||
let _window = crate::meta::exclusive().await;
|
let _window = crate::meta::exclusive().await;
|
||||||
crate::lifecycle::create_container(name, &hive, &paths).await?;
|
crate::lifecycle::create_container(name, &hive, &paths).await?;
|
||||||
Ok(NodeOutput::default())
|
Ok(NodeOutput::default())
|
||||||
|
|
|
||||||
|
|
@ -275,6 +275,9 @@ async fn port_collision(self_name: &str) -> Option<String> {
|
||||||
|
|
||||||
pub async fn spawn(name: &str, hive: &HiveEnv, paths: &AgentPaths) -> Result<()> {
|
pub async fn spawn(name: &str, hive: &HiveEnv, paths: &AgentPaths) -> Result<()> {
|
||||||
create_container(name, hive, paths).await?;
|
create_container(name, hive, paths).await?;
|
||||||
|
// Runtime dir must exist before nixos-container start (nspawn bind-mount
|
||||||
|
// source). Create it here so callers don't need a separate preamble step.
|
||||||
|
ensure_agent_runtime_dir(name)?;
|
||||||
write_dropins(name, hive, paths).await?;
|
write_dropins(name, hive, paths).await?;
|
||||||
priv_run("start", name).await
|
priv_run("start", name).await
|
||||||
}
|
}
|
||||||
|
|
@ -629,6 +632,8 @@ pub async fn rebuild_no_meta(
|
||||||
// See `docs/coordinator.md::Spawn path`.
|
// See `docs/coordinator.md::Spawn path`.
|
||||||
on_step("nixos-container create");
|
on_step("nixos-container create");
|
||||||
priv_run("create", name).await?;
|
priv_run("create", name).await?;
|
||||||
|
// Runtime dir must exist before nixos-container start.
|
||||||
|
ensure_agent_runtime_dir(name)?;
|
||||||
write_dropins(name, hive, paths).await?;
|
write_dropins(name, hive, paths).await?;
|
||||||
on_step("nixos-container start");
|
on_step("nixos-container start");
|
||||||
priv_run("start", name).await?;
|
priv_run("start", name).await?;
|
||||||
|
|
|
||||||
|
|
@ -202,14 +202,12 @@ async fn dispatch(req: &HostRequest, coord: Arc<Coordinator>) -> HostResponse {
|
||||||
/// registration and notifying the manager on failure.
|
/// registration and notifying the manager on failure.
|
||||||
async fn handle_spawn(coord: &Arc<Coordinator>, name: &str) -> Result<HostResponse> {
|
async fn handle_spawn(coord: &Arc<Coordinator>, name: &str) -> Result<HostResponse> {
|
||||||
tracing::info!(%name, "spawn");
|
tracing::info!(%name, "spawn");
|
||||||
// Create the bind-mount source dir (pure filesystem, no Coordinator dep).
|
|
||||||
// MCP listener registration happens eagerly here (not deferred to the
|
|
||||||
// supervisor) so the socket is ready before the harness's first turn.
|
|
||||||
lifecycle::ensure_agent_runtime_dir(name)?;
|
|
||||||
coord.register_agent(name)?;
|
|
||||||
let agent_dir = Coordinator::agent_dir(name);
|
let agent_dir = Coordinator::agent_dir(name);
|
||||||
let hive = coord.hive_env();
|
let hive = coord.hive_env();
|
||||||
let paths = Coordinator::agent_paths(name, agent_dir);
|
let paths = Coordinator::agent_paths(name, agent_dir);
|
||||||
|
// lifecycle::spawn creates the runtime dir internally before start, so
|
||||||
|
// no manual ensure_agent_runtime_dir here. MCP listener registration is
|
||||||
|
// handled by mcp_sockets::spawn_poll on its first post-start tick.
|
||||||
match lifecycle::spawn(name, &hive, &paths).await {
|
match lifecycle::spawn(name, &hive, &paths).await {
|
||||||
Ok(()) => {
|
Ok(()) => {
|
||||||
if let Err(e) = coord.power.set(name, crate::power::Wanted::Up) {
|
if let Err(e) = coord.power.set(name, crate::power::Wanted::Up) {
|
||||||
|
|
@ -225,7 +223,6 @@ async fn handle_spawn(coord: &Arc<Coordinator>, name: &str) -> Result<HostRespon
|
||||||
tokio::spawn(lifecycle::sync_tmpfiles());
|
tokio::spawn(lifecycle::sync_tmpfiles());
|
||||||
}
|
}
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
// Roll back socket registration if container creation failed.
|
|
||||||
coord.unregister_agent(name);
|
coord.unregister_agent(name);
|
||||||
coord.notify_manager(&hive_sh4re::HelperEvent::Spawned {
|
coord.notify_manager(&hive_sh4re::HelperEvent::Spawned {
|
||||||
agent: name.to_owned(),
|
agent: name.to_owned(),
|
||||||
|
|
|
||||||
|
|
@ -153,9 +153,8 @@ pub async fn ensure_root_agent(coord: &Arc<Coordinator>) -> Result<()> {
|
||||||
return Ok(());
|
return Ok(());
|
||||||
}
|
}
|
||||||
tracing::info!("manager container missing — spawning");
|
tracing::info!("manager container missing — spawning");
|
||||||
lifecycle::ensure_agent_runtime_dir(MANAGER_NAME)?;
|
// lifecycle::spawn creates the runtime dir internally; no manual
|
||||||
// Manager has no MCP listener (socket_server::start_manager owns its
|
// ensure_agent_runtime_dir needed here.
|
||||||
// socket); just need the dir + path value.
|
|
||||||
let runtime = Coordinator::agent_dir(MANAGER_NAME);
|
let runtime = Coordinator::agent_dir(MANAGER_NAME);
|
||||||
let hive = coord.hive_env();
|
let hive = coord.hive_env();
|
||||||
let paths = Coordinator::agent_paths(MANAGER_NAME, runtime);
|
let paths = Coordinator::agent_paths(MANAGER_NAME, runtime);
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue