fix(missions): place a microvm mission before returning from on_launch

Self-inflicted, one commit old, and found by running a real mission: the early
return I added for "a microvm mission materialises no team" sat ABOVE the microVM
placement block in the same function, so on_launch returned before ever choosing
a node. The mission then failed with the executor's own guard — "mission has no
target_node_id ... a microvm mission cannot run on the gateway, which has no
/dev/kvm" — which is the guard working exactly as designed, on a cause one layer
further up.

Placement now runs first. Worth noting the shape: adding an early return to a
long function silently skipped everything below it that the same runtime_kind
depends on.

461 tests pass, clippy clean.
This commit is contained in:
Omar Sobh
2026-08-05 16:55:58 -07:00
parent 521b8dea10
commit 1cd81a8b2a
+37 -33
View File
@@ -120,6 +120,43 @@ pub async fn on_launch(
);
}
// microVM PLACEMENT MUST COME BEFORE the early return below. It did not, and
// the first real microvm mission failed with "mission has no target_node_id" —
// the executor's own guard firing correctly on a mission this function had
// returned from before ever choosing a node for it.
// microVM placement. KVM is a hard predicate, not a preference: gw-04 —
// where every mission runs today — is itself a VM without nested
// virtualisation and has no /dev/kvm, so a microvm mission landing there
// cannot start. Resolve a capable node now and fail the launch if there is
// none, because the alternative is a mission that sits in 'running' having
// never had anywhere to run.
if mission.runtime_kind == "microvm" {
let capable =
cm_db::repo::nodes::online_with_capabilities(pool, mission.workspace_id, &["microvm"])
.await
.map_err(|e| format!("looking up microvm-capable nodes: {e}"))?;
const HOW_TO_FIX: &str = "needs /dev/kvm and firecracker installed — \
see scripts/fc-node-setup.sh";
let chosen = match mission.target_node_id {
// An explicit target is a request, not a guarantee. Honour it only
// if the node actually reports the capability.
Some(want) => *capable
.iter()
.find(|n| n.as_uuid() == want)
.ok_or_else(|| format!("mission targets node {want}, which is not an online node reporting microvm capability ({HOW_TO_FIX})"))?,
None => *capable
.first()
.ok_or_else(|| format!("no online node reports microvm capability ({HOW_TO_FIX})"))?,
};
sqlx::query("UPDATE missions SET target_node_id = $1, updated_at = now() WHERE id = $2")
.bind(chosen.as_uuid())
.bind(mission_id)
.execute(pool)
.await
.map_err(|e| format!("pin mission {mission_id} to node {chosen:?}: {e}"))?;
eprintln!("mission_orchestrator: mission {mission_id} placed on microvm node {chosen:?}");
}
// A microVM mission materialises no team. Its phases run as one `claude -p`
// inside a VM (`microvm_executor`), so there is no claw graph to provision —
// and demanding one rejected the launch of a well-formed mission with "pick
@@ -289,39 +326,6 @@ pub async fn on_launch(
}
}
// microVM placement. KVM is a hard predicate, not a preference: gw-04 —
// where every mission runs today — is itself a VM without nested
// virtualisation and has no /dev/kvm, so a microvm mission landing there
// cannot start. Resolve a capable node now and fail the launch if there is
// none, because the alternative is a mission that sits in 'running' having
// never had anywhere to run.
if mission.runtime_kind == "microvm" {
let capable =
cm_db::repo::nodes::online_with_capabilities(pool, mission.workspace_id, &["microvm"])
.await
.map_err(|e| format!("looking up microvm-capable nodes: {e}"))?;
const HOW_TO_FIX: &str = "needs /dev/kvm and firecracker installed — \
see scripts/fc-node-setup.sh";
let chosen = match mission.target_node_id {
// An explicit target is a request, not a guarantee. Honour it only
// if the node actually reports the capability.
Some(want) => *capable
.iter()
.find(|n| n.as_uuid() == want)
.ok_or_else(|| format!("mission targets node {want}, which is not an online node reporting microvm capability ({HOW_TO_FIX})"))?,
None => *capable
.first()
.ok_or_else(|| format!("no online node reports microvm capability ({HOW_TO_FIX})"))?,
};
sqlx::query("UPDATE missions SET target_node_id = $1, updated_at = now() WHERE id = $2")
.bind(chosen.as_uuid())
.bind(mission_id)
.execute(pool)
.await
.map_err(|e| format!("pin mission {mission_id} to node {chosen:?}: {e}"))?;
eprintln!("mission_orchestrator: mission {mission_id} placed on microvm node {chosen:?}");
}
// Herdr second-runtime: if runtime_kind='local_herdr', spawn a
// pane on target_node running the first available local CLI.
// Non-fatal on failure — the operator sees the error in server