jobq: templates swallow the DagSpec layer
DagSpec described the graph the templates were about to build, one layer below the templates themselves. Per #2972 the templates should be that unit, so the spec type is gone and every declarer writes onto the job builder directly. - delete DagSpec<F> and its hand-written Debug impl - submit(source, reason, declare: impl FnOnce(&Job)) replaces the pre-built-spec signature; submit_and_emit follows - all six templates take &Job; the Source is now the caller's to pass, which spawn and approval_deploy previously hardcoded while the other four did not - power_dag dissolves into stop_nodes/start_nodes/restart_nodes, which borrow their targets instead of owning them 315 tests pass unchanged.
This commit is contained in:
parent
96ef592fee
commit
6899f574f6
7 changed files with 347 additions and 550 deletions
|
|
@ -26,7 +26,7 @@
|
|||
|
||||
use hive_jobq::TerminalState;
|
||||
|
||||
use super::model::{DagSpec, NodeKind, PermPayload, Source};
|
||||
use super::model::{NodeKind, PermPayload};
|
||||
use super::resource::Resource;
|
||||
use super::{Handle, Job};
|
||||
|
||||
|
|
@ -304,29 +304,17 @@ pub(crate) fn deploy_rebuild_nodes(b: &Job, agent: &str, approval_id: i64) {
|
|||
/// whole `StopForUpdate`→`Swap`→`PostSwap` subtree, so those three cover every
|
||||
/// node. Edging `Reconcile` alone would not do: it is `AfterAny` `Prebuild`, so
|
||||
/// it reaches `Done` even after a failed swap and the tail would report success.
|
||||
pub fn rebuild(
|
||||
agent: &str,
|
||||
source: Source,
|
||||
reason: String,
|
||||
relock: bool,
|
||||
) -> DagSpec<impl FnOnce(&Job) + use<>> {
|
||||
let agent = agent.to_owned();
|
||||
DagSpec {
|
||||
source,
|
||||
reason,
|
||||
declare: Box::new(move |b: &Job| {
|
||||
let roots = rebuild_nodes(
|
||||
b,
|
||||
&agent,
|
||||
RebuildOpts {
|
||||
relock,
|
||||
graceful: false,
|
||||
},
|
||||
None,
|
||||
);
|
||||
emit_rebuilt_tails(b, &agent, &roots.all());
|
||||
}),
|
||||
}
|
||||
pub fn rebuild(b: &Job, agent: &str, relock: bool) {
|
||||
let roots = rebuild_nodes(
|
||||
b,
|
||||
agent,
|
||||
RebuildOpts {
|
||||
relock,
|
||||
graceful: false,
|
||||
},
|
||||
None,
|
||||
);
|
||||
emit_rebuilt_tails(b, agent, &roots.all());
|
||||
}
|
||||
|
||||
/// Approval-driven deploy (`MergeConfigPr`) as a phase subtree rather than the
|
||||
|
|
@ -354,55 +342,44 @@ pub fn rebuild(
|
|||
///
|
||||
/// The window still spans the container build, as it must: `prepare_deploy`
|
||||
/// leaves `flake.lock` staged-uncommitted for the build's whole duration.
|
||||
pub fn approval_deploy(
|
||||
agent: &str,
|
||||
approval_id: i64,
|
||||
reason: String,
|
||||
) -> DagSpec<impl FnOnce(&Job) + use<>> {
|
||||
let agent = agent.to_owned();
|
||||
DagSpec {
|
||||
source: Source::Approval,
|
||||
reason,
|
||||
declare: Box::new(move |b: &Job| {
|
||||
let a = || agent.clone();
|
||||
// The window is the widest holder in the tree: it brackets a nix
|
||||
// build (`BuildSlot`), takes the container down across the swap
|
||||
// (`Agent`), and serialises the meta mutation its subtree performs
|
||||
// (`MetaWindow`). All three are held for its whole subtree, which
|
||||
// is what lets the appended rebuild's `MetaSync` and the
|
||||
// `FinalizeDeploy` re-enter rather than contend.
|
||||
let window = b
|
||||
.node(NodeKind::DeployWindow {
|
||||
agent: a(),
|
||||
approval_id,
|
||||
})
|
||||
.needs(Resource::BuildSlot)
|
||||
.needs(Resource::Agent(a()))
|
||||
.needs(Resource::MetaWindow);
|
||||
let verify = b
|
||||
.node(NodeKind::MergeVerify {
|
||||
agent: a(),
|
||||
approval_id,
|
||||
})
|
||||
.part_of(window);
|
||||
let apply = b
|
||||
.node(NodeKind::DeployApply {
|
||||
agent: a(),
|
||||
approval_id,
|
||||
})
|
||||
.part_of(window)
|
||||
.after_ok(verify);
|
||||
let _tail = b
|
||||
.node(NodeKind::DeployTail {
|
||||
agent: a(),
|
||||
approval_id,
|
||||
})
|
||||
.part_of(window)
|
||||
.after_any(apply);
|
||||
pub fn approval_deploy(b: &Job, agent: &str, approval_id: i64) {
|
||||
let a = || agent.to_owned();
|
||||
// The window is the widest holder in the tree: it brackets a nix
|
||||
// build (`BuildSlot`), takes the container down across the swap
|
||||
// (`Agent`), and serialises the meta mutation its subtree performs
|
||||
// (`MetaWindow`). All three are held for its whole subtree, which
|
||||
// is what lets the appended rebuild's `MetaSync` and the
|
||||
// `FinalizeDeploy` re-enter rather than contend.
|
||||
let window = b
|
||||
.node(NodeKind::DeployWindow {
|
||||
agent: a(),
|
||||
approval_id,
|
||||
})
|
||||
.needs(Resource::BuildSlot)
|
||||
.needs(Resource::Agent(a()))
|
||||
.needs(Resource::MetaWindow);
|
||||
let verify = b
|
||||
.node(NodeKind::MergeVerify {
|
||||
agent: a(),
|
||||
approval_id,
|
||||
})
|
||||
.part_of(window);
|
||||
let apply = b
|
||||
.node(NodeKind::DeployApply {
|
||||
agent: a(),
|
||||
approval_id,
|
||||
})
|
||||
.part_of(window)
|
||||
.after_ok(verify);
|
||||
let _tail = b
|
||||
.node(NodeKind::DeployTail {
|
||||
agent: a(),
|
||||
approval_id,
|
||||
})
|
||||
.part_of(window)
|
||||
.after_any(apply);
|
||||
|
||||
resolve_approval_tails(b, approval_id, window);
|
||||
}),
|
||||
}
|
||||
resolve_approval_tails(b, approval_id, window);
|
||||
}
|
||||
|
||||
/// First-deploy spawn (approval-driven): `Provision` (proposed/applied
|
||||
|
|
@ -416,34 +393,27 @@ pub fn approval_deploy(
|
|||
/// container was never created). Closed by a `ResolveApproval` tail root edged
|
||||
/// `AfterAny` onto `Provision` — the DAG's only other group-root, so its roll-up
|
||||
/// already carries the whole cascade.
|
||||
pub fn spawn(agent: &str, approval_id: i64, reason: String) -> DagSpec<impl FnOnce(&Job) + use<>> {
|
||||
let agent = agent.to_owned();
|
||||
DagSpec {
|
||||
source: Source::Approval,
|
||||
reason,
|
||||
declare: Box::new(move |b: &Job| {
|
||||
let a = || agent.clone();
|
||||
let provision = b
|
||||
.node(NodeKind::Provision { agent: a() })
|
||||
.needs(Resource::MetaWindow);
|
||||
let create = b
|
||||
.node(NodeKind::Create { agent: a() })
|
||||
.needs(Resource::BuildSlot)
|
||||
.needs(Resource::Agent(a()))
|
||||
.part_of(provision);
|
||||
let dropin = b
|
||||
.node(NodeKind::WriteDropin { agent: a() })
|
||||
.needs(Resource::Agent(a()))
|
||||
.part_of(create);
|
||||
let _reconcile = b
|
||||
.node(NodeKind::Reconcile { agent: a() })
|
||||
.needs(Resource::Agent(a()))
|
||||
.part_of(create)
|
||||
.after_ok(dropin);
|
||||
pub fn spawn(b: &Job, agent: &str, approval_id: i64) {
|
||||
let a = || agent.to_owned();
|
||||
let provision = b
|
||||
.node(NodeKind::Provision { agent: a() })
|
||||
.needs(Resource::MetaWindow);
|
||||
let create = b
|
||||
.node(NodeKind::Create { agent: a() })
|
||||
.needs(Resource::BuildSlot)
|
||||
.needs(Resource::Agent(a()))
|
||||
.part_of(provision);
|
||||
let dropin = b
|
||||
.node(NodeKind::WriteDropin { agent: a() })
|
||||
.needs(Resource::Agent(a()))
|
||||
.part_of(create);
|
||||
let _reconcile = b
|
||||
.node(NodeKind::Reconcile { agent: a() })
|
||||
.needs(Resource::Agent(a()))
|
||||
.part_of(create)
|
||||
.after_ok(dropin);
|
||||
|
||||
resolve_approval_tails(b, approval_id, provision);
|
||||
}),
|
||||
}
|
||||
resolve_approval_tails(b, approval_id, provision);
|
||||
}
|
||||
|
||||
/// Perm change: commit the JSON file(s), then the rebuild subgraph so
|
||||
|
|
@ -451,39 +421,27 @@ pub fn spawn(agent: &str, approval_id: i64, reason: String) -> DagSpec<impl FnOn
|
|||
/// effect in the container. Group-roots are `WritePermFile` plus the rebuild
|
||||
/// subgraph's `MetaSync` / `Prebuild` / `Reconcile`, so the `EmitRebuilt` tail
|
||||
/// edges all four.
|
||||
pub fn perm_change(
|
||||
agent: &str,
|
||||
source: Source,
|
||||
reason: String,
|
||||
payload: PermPayload,
|
||||
) -> DagSpec<impl FnOnce(&Job) + use<>> {
|
||||
let agent = agent.to_owned();
|
||||
DagSpec {
|
||||
source,
|
||||
reason,
|
||||
declare: Box::new(move |b: &Job| {
|
||||
let write = b
|
||||
.node(NodeKind::WritePermFile {
|
||||
agent: agent.clone(),
|
||||
payload,
|
||||
})
|
||||
.needs(Resource::MetaWindow);
|
||||
let roots = rebuild_nodes(
|
||||
b,
|
||||
&agent,
|
||||
RebuildOpts {
|
||||
relock: true,
|
||||
graceful: false,
|
||||
},
|
||||
Some(write),
|
||||
);
|
||||
emit_rebuilt_tails(
|
||||
b,
|
||||
&agent,
|
||||
&[write, roots.meta_sync, roots.prebuild, roots.reconcile],
|
||||
);
|
||||
}),
|
||||
}
|
||||
pub fn perm_change(b: &Job, agent: &str, payload: PermPayload) {
|
||||
let write = b
|
||||
.node(NodeKind::WritePermFile {
|
||||
agent: agent.to_owned(),
|
||||
payload,
|
||||
})
|
||||
.needs(Resource::MetaWindow);
|
||||
let roots = rebuild_nodes(
|
||||
b,
|
||||
agent,
|
||||
RebuildOpts {
|
||||
relock: true,
|
||||
graceful: false,
|
||||
},
|
||||
Some(write),
|
||||
);
|
||||
emit_rebuilt_tails(
|
||||
b,
|
||||
agent,
|
||||
&[write, roots.meta_sync, roots.prebuild, roots.reconcile],
|
||||
);
|
||||
}
|
||||
|
||||
/// Meta-input lock bump. The `MetaLock` executor grows one rebuild subgraph
|
||||
|
|
@ -496,33 +454,22 @@ pub fn perm_change(
|
|||
/// so the "hyperhive" pseudo-agent gets no pill), giving each cascade agent
|
||||
/// crash-watch suppression during its `Swap` — the property the old child
|
||||
/// `Rebuild` DAGs carried via their own transient.
|
||||
pub fn meta_update(
|
||||
inputs: Vec<String>,
|
||||
source: Source,
|
||||
reason: String,
|
||||
approval_id: Option<i64>,
|
||||
) -> DagSpec<impl FnOnce(&Job) + use<>> {
|
||||
DagSpec {
|
||||
source,
|
||||
reason,
|
||||
declare: Box::new(move |b: &Job| {
|
||||
let lock = b
|
||||
.node(NodeKind::MetaLock {
|
||||
sweep: false,
|
||||
fanout: None,
|
||||
inputs,
|
||||
})
|
||||
.needs(Resource::BuildSlot)
|
||||
.needs(Resource::MetaWindow);
|
||||
// The bump itself has no side effect, so an operator-driven one ends
|
||||
// at the `MetaLock`; an approval-driven one still has its row to
|
||||
// resolve and gets the per-outcome tails edged onto that single
|
||||
// group-root — whose roll-up covers the rebuild subgraphs `MetaLock`
|
||||
// grows into itself.
|
||||
if let Some(approval_id) = approval_id {
|
||||
resolve_approval_tails(b, approval_id, lock);
|
||||
}
|
||||
}),
|
||||
pub fn meta_update(b: &Job, inputs: Vec<String>, approval_id: Option<i64>) {
|
||||
let lock = b
|
||||
.node(NodeKind::MetaLock {
|
||||
sweep: false,
|
||||
fanout: None,
|
||||
inputs,
|
||||
})
|
||||
.needs(Resource::BuildSlot)
|
||||
.needs(Resource::MetaWindow);
|
||||
// The bump itself has no side effect, so an operator-driven one ends
|
||||
// at the `MetaLock`; an approval-driven one still has its row to
|
||||
// resolve and gets the per-outcome tails edged onto that single
|
||||
// group-root — whose roll-up covers the rebuild subgraphs `MetaLock`
|
||||
// grows into itself.
|
||||
if let Some(approval_id) = approval_id {
|
||||
resolve_approval_tails(b, approval_id, lock);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -535,20 +482,10 @@ pub fn meta_update(
|
|||
/// checks), so a parent move needs no container rebuild to take effect.
|
||||
/// No transient pill either — the node is agentless (no lease to hang one
|
||||
/// off of) and near-instant. No tail node: the write is the whole effect.
|
||||
pub fn reparent(
|
||||
moves: Vec<(hive_types::Ident, Option<hive_types::Ident>)>,
|
||||
source: Source,
|
||||
reason: String,
|
||||
) -> DagSpec<impl FnOnce(&Job) + use<>> {
|
||||
DagSpec {
|
||||
source,
|
||||
reason,
|
||||
declare: Box::new(move |b: &Job| {
|
||||
let _reparent = b
|
||||
.node(NodeKind::Reparent { moves })
|
||||
.needs(Resource::MetaWindow);
|
||||
}),
|
||||
}
|
||||
pub fn reparent(b: &Job, moves: Vec<(hive_types::Ident, Option<hive_types::Ident>)>) {
|
||||
let _reparent = b
|
||||
.node(NodeKind::Reparent { moves })
|
||||
.needs(Resource::MetaWindow);
|
||||
}
|
||||
|
||||
// The boot is assembled inline in `workers/auto_update.rs::submit_boot_tree`
|
||||
|
|
|
|||
Loading…
Reference in a new issue