refactor(#2916): destroy submits a DAG instead of an imperative teardown
Destroy was a straight-line async fn with no queue node behind it, so
nothing in the graph could answer "is this container going down on
purpose?". That gap is why an imperative crash-watch suppression guard
existed: an RAII handle held for the operation's duration, a second way
to say what every other lifecycle op already says through its node.
Reuse the existing Stop node rather than teaching a new node to stop
things:
Stop -> DestroyContainer -> (PurgeState) -> DestroyBookkeeping
Stop already declares takes_container_down honestly, so the suppression
is now derived from the graph like every other op's. It also turns the
precondition into an edge: DestroyContainer runs only under a completed
Stop, so it operates on an already-stopped container and carries
takes_container_down = false permanently. A container still alive at
that point is a real bug and stays loud instead of being absorbed by a
flag -- which matters because a wrong true silently swallows a crash
while a wrong false only costs a spurious event.
Removes suppress_crash_watch, CrashWatchSuppression, crash_suppressed,
crash_watch_suppressed and NO_NODE_LABEL. The migration call sites went
with the obsolete startup migrations, so destroy was the last caller and
intent now has exactly one home.
destroy() becomes a submit-and-return, matching every sibling endpoint
(rebuild, kill, restart, start, pause, resume) -- it was the only
lifecycle op that awaited its work. The container rescan moves into the
bookkeeping tail, so ContainerRemoved now arrives after the 200 rather
than before it.
Also drops an orphaned doc-comment in coordinator.rs: two stacked blocks
where only the second described crash_suppressed, the first documenting
a field that no longer exists. Removing the field would have re-pointed
it at recent_transient.
This commit is contained in:
parent
2c7872841a
commit
6338939657
9 changed files with 326 additions and 194 deletions
|
|
@ -78,6 +78,36 @@ pub enum NodeKind {
|
|||
/// First-spawn `nixos-container create` proper. Assumes the
|
||||
/// upstream `Provision` node already registered the agent in meta.
|
||||
Create { agent: String },
|
||||
/// `nixos-container destroy` plus the un-registration that follows it:
|
||||
/// drop the agent from the coordinator's roster and clear its ephemeral
|
||||
/// runtime dir.
|
||||
///
|
||||
/// **Deliberately not in [`NodeKind::takes_container_down`]**, and that
|
||||
/// is the design rather than an oversight. This node runs *downstream of
|
||||
/// a `Stop`*, which already carries the flag honestly, so by the time it
|
||||
/// claims there is nothing left to take down. A container still live here
|
||||
/// is a real bug and must page someone — a `true` would absorb exactly
|
||||
/// that signal, and the flag's whole asymmetry (see that method) is that
|
||||
/// a wrong `true` silently swallows a crash.
|
||||
DestroyContainer { agent: String },
|
||||
/// The `purge = true` half of a destroy: delete the agent's state
|
||||
/// subvolume (via hive-priv, since a subvolume root defeats
|
||||
/// `remove_dir_all`) plus its state and applied dirs. Its own node
|
||||
/// because it is conditional — a plain destroy never inserts it — and
|
||||
/// because it is the irreversible step, so it earns a distinct row in
|
||||
/// the graph rather than hiding inside a bookkeeping tail.
|
||||
PurgeState { agent: String },
|
||||
/// The post-destroy bookkeeping tail: meta sync, fail the agent's pending
|
||||
/// approvals, drop the durable power intent, notify the manager, rescan
|
||||
/// containers, re-emit the tombstone + schedule snapshots, resync
|
||||
/// tmpfiles. Split from [`NodeKind::DestroyContainer`] for the same
|
||||
/// reason [`NodeKind::RebuildBookkeeping`] is split from `Swap`:
|
||||
/// dashboard visibility and retry granularity for work that is pure
|
||||
/// store/meta bookkeeping and touches no container.
|
||||
///
|
||||
/// `purge` only selects the wording of the approval-failure reason and
|
||||
/// the manager notification; the destructive work is `PurgeState`'s.
|
||||
DestroyBookkeeping { agent: String, purge: bool },
|
||||
/// Meta flake lock bump. `sweep = false`: `meta::lock_update`
|
||||
/// (commit fused, under `META_LOCK`) with this node's own `inputs`;
|
||||
/// `sweep = true`: `meta::lock_update_hyperhive`, *non-fatal* (a
|
||||
|
|
@ -341,6 +371,9 @@ impl NodeKind {
|
|||
NodeKind::RebuildBookkeeping { .. } => "rebuild_bookkeeping",
|
||||
NodeKind::Provision { .. } => "provision",
|
||||
NodeKind::Create { .. } => "create",
|
||||
NodeKind::DestroyContainer { .. } => "destroy_container",
|
||||
NodeKind::PurgeState { .. } => "purge_state",
|
||||
NodeKind::DestroyBookkeeping { .. } => "destroy_bookkeeping",
|
||||
NodeKind::MetaLock { .. } => "meta_lock",
|
||||
NodeKind::Reconcile { .. } => "reconcile",
|
||||
NodeKind::Start { .. } => "start",
|
||||
|
|
@ -378,6 +411,9 @@ impl NodeKind {
|
|||
| NodeKind::RebuildBookkeeping { agent }
|
||||
| NodeKind::Provision { agent }
|
||||
| NodeKind::Create { agent }
|
||||
| NodeKind::DestroyContainer { agent }
|
||||
| NodeKind::PurgeState { agent }
|
||||
| NodeKind::DestroyBookkeeping { agent, .. }
|
||||
| NodeKind::Reconcile { agent }
|
||||
| NodeKind::Start { agent }
|
||||
| NodeKind::Stop { agent }
|
||||
|
|
@ -435,6 +471,12 @@ impl NodeKind {
|
|||
// - `Create` / `Start` / `SetWanted{up}` bring a container UP. A
|
||||
// container disappearing *while starting* is a genuine crash and has
|
||||
// to keep reporting as one.
|
||||
// - `DestroyContainer` looks like the most obvious `true` on this list
|
||||
// and is the one that must stay `false`. It is edged downstream of a
|
||||
// `Stop`, so the container is already down when it claims; the stop
|
||||
// that the operator asked for is accounted for by the node that
|
||||
// performs it. A container found alive at destroy time is a genuine
|
||||
// bug, and a `true` here would suppress the alert that says so.
|
||||
// - `Reconcile` is a planner; it fans out `Start` / `Stop`, which carry
|
||||
// their own answer.
|
||||
// - `DeployWindow` brackets a deploy without itself stopping anything.
|
||||
|
|
|
|||
Loading…
Reference in a new issue