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:
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user