refactor(#2591): make NodeKind the queue payload — drop JobPayload, agent into variants
This commit is contained in:
parent
2294cd4516
commit
be2dfa8cd3
8 changed files with 233 additions and 177 deletions
|
|
@ -56,12 +56,12 @@ pub enum NodeKind {
|
|||
/// skipped when the container is already down — it only exists to
|
||||
/// shrink the swap's downtime, which a stopped agent doesn't need
|
||||
/// (the sync + dir prep still run; `Swap` builds inline).
|
||||
Prebuild { relock: bool },
|
||||
Prebuild { agent: String, relock: bool },
|
||||
/// `nixos-container update` profile-swap (requires the container
|
||||
/// stopped). Re-applies nspawn flags + resource limits first —
|
||||
/// rebuild is the reconcile verb. The post-rebuild bookkeeping tail
|
||||
/// lives in the sibling `PostSwap` node.
|
||||
Swap,
|
||||
Swap { agent: String },
|
||||
/// The post-`Swap` bookkeeping tail as a first-class node: rev marker,
|
||||
/// forge + matrix sync, manager kick, container rescan, meta-inputs
|
||||
/// snapshot. Split out of `Swap` for dashboard visibility + retry
|
||||
|
|
@ -71,16 +71,16 @@ pub enum NodeKind {
|
|||
/// recovery still runs. Store/forge/matrix work only — no nix build, so
|
||||
/// build-slot-exempt; the agent lease taken at `Swap` is held across the
|
||||
/// whole chain until `Reconcile` settles, so it's not re-declared here.
|
||||
PostSwap,
|
||||
PostSwap { agent: String },
|
||||
/// First-spawn pre-create provisioning: proposed/applied repos,
|
||||
/// state subvolume, and meta registration (`sync_agents`). Runs
|
||||
/// ahead of `Create` so the `nixos-container create --flake
|
||||
/// meta#<name>` ref resolves. Store/meta-only — no container yet —
|
||||
/// so it's lease- and build-slot-exempt like `Prebuild`.
|
||||
Provision,
|
||||
Provision { agent: String },
|
||||
/// First-spawn `nixos-container create` proper. Assumes the
|
||||
/// upstream `Provision` node already registered the agent in meta.
|
||||
Create,
|
||||
Create { agent: String },
|
||||
/// Meta flake lock bump. `sweep = false`: `meta::lock_update`
|
||||
/// (commit fused, under `META_LOCK`) with the DAG's `inputs`;
|
||||
/// `sweep = true`: `meta::lock_update_hyperhive`, *non-fatal* (a
|
||||
|
|
@ -97,36 +97,36 @@ pub enum NodeKind {
|
|||
/// `Offline` & up, else noop). The mechanical work is not done in
|
||||
/// this node — it fans a child [`NodeKind::Start`] / [`NodeKind::Stop`]
|
||||
/// DAG out at runtime so the sub-step is a first-class DAG node.
|
||||
Reconcile,
|
||||
Reconcile { agent: String },
|
||||
/// Mechanical container start: the start preamble (runtime dir +
|
||||
/// drop-ins), `start_with_fallback`, MCP listener registration, and
|
||||
/// the manager kick. Fanned out by a [`NodeKind::Reconcile`] that
|
||||
/// observed `wanted = Up` and the container down.
|
||||
Start,
|
||||
Start { agent: String },
|
||||
/// Mechanical container stop: `nixos-container` kill, MCP listener
|
||||
/// unregister, and the `Killed` manager notify. Fanned out by a
|
||||
/// [`NodeKind::Reconcile`] that observed `wanted = Offline` and up.
|
||||
Stop,
|
||||
Stop { agent: String },
|
||||
/// Mechanical `nixos-container stop` for the profile swap. Never
|
||||
/// touches `wanted`. Noop if already stopped.
|
||||
StopForUpdate,
|
||||
StopForUpdate { agent: String },
|
||||
/// Set the graceful-stop fence + kick the harness so it runs one
|
||||
/// stop-checkpoint turn.
|
||||
Signal,
|
||||
Signal { agent: String },
|
||||
/// Await the harness clearing the fence, bounded by
|
||||
/// `GRACEFUL_STOP_TIMEOUT`. Resolves ok either way — the
|
||||
/// downstream `Reconcile` performs the actual stop.
|
||||
Drain,
|
||||
Drain { agent: String },
|
||||
/// `set_nspawn_flags` + `set_resource_limits` + daemon-reload.
|
||||
WriteDropin,
|
||||
WriteDropin { agent: String },
|
||||
/// Commit `tool-groups.json` / `capabilities.json` per the DAG's
|
||||
/// `perm_payload` (commit fused under `META_LOCK`).
|
||||
WritePermFile,
|
||||
WritePermFile { agent: String },
|
||||
/// Opaque approval deploy pipeline (`MergeConfigPr`): the two-phase
|
||||
/// prepare/finalize/abort meta deploy stays inside `actions.rs` in v1 —
|
||||
/// deliberately not
|
||||
/// modeled as scheduler nodes (see the design doc §9).
|
||||
ApprovalDeploy,
|
||||
ApprovalDeploy { agent: String },
|
||||
/// Write the agent's durable power intent (`wanted = Up` when `up`, else
|
||||
/// `Offline`) as a first-class DAG node, at the head of a power-op
|
||||
/// template so the downstream `Reconcile` reads it. Replaces the old
|
||||
|
|
@ -140,7 +140,7 @@ pub enum NodeKind {
|
|||
/// the DAG. (In `stale_start` the lease is thus held across the head
|
||||
/// `Prebuild`, but that's a no-op there — the agent is down, so prebuild
|
||||
/// is skipped.)
|
||||
SetWanted { up: bool },
|
||||
SetWanted { agent: String, up: bool },
|
||||
/// The **DAG container** node: one per submitted DAG, carrying the group's
|
||||
/// domain metadata. Every template node hangs *under* it (its subtree), so
|
||||
/// the container's `NodeId` **is** the DAG id, its rolled-up state **is** the
|
||||
|
|
@ -166,35 +166,60 @@ impl NodeKind {
|
|||
pub fn as_str(&self) -> &'static str {
|
||||
match self {
|
||||
NodeKind::Prebuild { .. } => "prebuild",
|
||||
NodeKind::Swap => "swap",
|
||||
NodeKind::PostSwap => "post_swap",
|
||||
NodeKind::Provision => "provision",
|
||||
NodeKind::Create => "create",
|
||||
NodeKind::Swap { .. } => "swap",
|
||||
NodeKind::PostSwap { .. } => "post_swap",
|
||||
NodeKind::Provision { .. } => "provision",
|
||||
NodeKind::Create { .. } => "create",
|
||||
NodeKind::MetaLock { .. } => "meta_lock",
|
||||
NodeKind::Reconcile => "reconcile",
|
||||
NodeKind::Start => "start",
|
||||
NodeKind::Stop => "stop",
|
||||
NodeKind::StopForUpdate => "stop_for_update",
|
||||
NodeKind::Signal => "signal",
|
||||
NodeKind::Drain => "drain",
|
||||
NodeKind::WriteDropin => "write_dropin",
|
||||
NodeKind::WritePermFile => "write_perm_file",
|
||||
NodeKind::ApprovalDeploy => "approval_deploy",
|
||||
NodeKind::Reconcile { .. } => "reconcile",
|
||||
NodeKind::Start { .. } => "start",
|
||||
NodeKind::Stop { .. } => "stop",
|
||||
NodeKind::StopForUpdate { .. } => "stop_for_update",
|
||||
NodeKind::Signal { .. } => "signal",
|
||||
NodeKind::Drain { .. } => "drain",
|
||||
NodeKind::WriteDropin { .. } => "write_dropin",
|
||||
NodeKind::WritePermFile { .. } => "write_perm_file",
|
||||
NodeKind::ApprovalDeploy { .. } => "approval_deploy",
|
||||
NodeKind::SetWanted { .. } => "set_wanted",
|
||||
NodeKind::Dag { .. } => "dag",
|
||||
}
|
||||
}
|
||||
|
||||
/// The agent this node targets, or `""` for agentless kinds
|
||||
/// ([`NodeKind::MetaLock`] on the `hyperhive` pseudo-agent, and the
|
||||
/// [`NodeKind::Dag`] container).
|
||||
#[must_use]
|
||||
pub fn agent(&self) -> &str {
|
||||
match self {
|
||||
NodeKind::Prebuild { agent, .. }
|
||||
| NodeKind::Swap { agent }
|
||||
| NodeKind::PostSwap { agent }
|
||||
| NodeKind::Provision { agent }
|
||||
| NodeKind::Create { agent }
|
||||
| NodeKind::Reconcile { agent }
|
||||
| NodeKind::Start { agent }
|
||||
| NodeKind::Stop { agent }
|
||||
| NodeKind::StopForUpdate { agent }
|
||||
| NodeKind::Signal { agent }
|
||||
| NodeKind::Drain { agent }
|
||||
| NodeKind::WriteDropin { agent }
|
||||
| NodeKind::WritePermFile { agent }
|
||||
| NodeKind::ApprovalDeploy { agent }
|
||||
| NodeKind::SetWanted { agent, .. } => agent,
|
||||
NodeKind::MetaLock { .. } | NodeKind::Dag { .. } => "",
|
||||
}
|
||||
}
|
||||
|
||||
/// Nix-heavy kinds hold one of the `buildSlots` semaphore permits
|
||||
/// for the node's duration.
|
||||
pub fn needs_build_slot(&self) -> bool {
|
||||
matches!(
|
||||
self,
|
||||
NodeKind::Prebuild { .. }
|
||||
| NodeKind::Swap
|
||||
| NodeKind::Create
|
||||
| NodeKind::Swap { .. }
|
||||
| NodeKind::Create { .. }
|
||||
| NodeKind::MetaLock { .. }
|
||||
| NodeKind::ApprovalDeploy
|
||||
| NodeKind::ApprovalDeploy { .. }
|
||||
)
|
||||
}
|
||||
|
||||
|
|
@ -209,14 +234,14 @@ impl NodeKind {
|
|||
pub fn needs_lease(&self) -> bool {
|
||||
matches!(
|
||||
self,
|
||||
NodeKind::Swap
|
||||
| NodeKind::Create
|
||||
| NodeKind::Reconcile
|
||||
| NodeKind::StopForUpdate
|
||||
| NodeKind::Signal
|
||||
| NodeKind::Drain
|
||||
| NodeKind::WriteDropin
|
||||
| NodeKind::ApprovalDeploy
|
||||
NodeKind::Swap { .. }
|
||||
| NodeKind::Create { .. }
|
||||
| NodeKind::Reconcile { .. }
|
||||
| NodeKind::StopForUpdate { .. }
|
||||
| NodeKind::Signal { .. }
|
||||
| NodeKind::Drain { .. }
|
||||
| NodeKind::WriteDropin { .. }
|
||||
| NodeKind::ApprovalDeploy { .. }
|
||||
| NodeKind::SetWanted { .. }
|
||||
)
|
||||
}
|
||||
|
|
@ -225,9 +250,9 @@ impl NodeKind {
|
|||
/// Submit-time spec for one node.
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct NodeSpec {
|
||||
/// The agent this node's work targets. Built by the `templates.rs` `node`
|
||||
/// helper, which stamps the template's agent onto every node.
|
||||
pub agent: String,
|
||||
/// The node's payload — [`NodeKind`] is the queue's payload type directly,
|
||||
/// and each variant carries the agent it targets (a DAG can span agents;
|
||||
/// the queue derives per-agent leasing from [`NodeKind::agent`]).
|
||||
pub kind: NodeKind,
|
||||
pub deps: Vec<Dep>,
|
||||
/// The **structural parent** axis — the spec-local index of this node's
|
||||
|
|
|
|||
Loading…
Reference in a new issue