wanted: converge a destroyed declaration by tearing the container down
This commit is contained in:
parent
e82a735745
commit
6fd5bc1c4f
2 changed files with 61 additions and 8 deletions
|
|
@ -212,6 +212,7 @@ async fn converge(coord: &Arc<Coordinator>, declared: &HiveWanted) -> Result<()>
|
|||
deploy = plan.deploy.len(),
|
||||
start = plan.start.len(),
|
||||
stop = plan.stop.len(),
|
||||
destroy = plan.destroy.len(),
|
||||
"wanted state: converging"
|
||||
);
|
||||
|
||||
|
|
@ -234,8 +235,16 @@ async fn converge(coord: &Arc<Coordinator>, declared: &HiveWanted) -> Result<()>
|
|||
.await
|
||||
.context("queueing the declared stops")?;
|
||||
}
|
||||
// No purge: this issue's own scope is tearing the container down, not
|
||||
// wiping its persistent state. `actions::destroy` submits its own DAG
|
||||
// node and emits its own queue snapshot, so there is nothing to await
|
||||
// or fold into `queued` here — same fire-and-forget contract every other
|
||||
// lifecycle op in that module has.
|
||||
for agent in &plan.destroy {
|
||||
crate::actions::destroy(coord, agent, false);
|
||||
}
|
||||
if queued {
|
||||
// The power ops emit their own; the first deploys do not.
|
||||
// The power ops and destroy emit their own; the first deploys do not.
|
||||
coord.emit_rebuild_queue_snapshot();
|
||||
}
|
||||
Ok(())
|
||||
|
|
@ -247,6 +256,7 @@ struct Plan {
|
|||
deploy: Vec<String>,
|
||||
start: Vec<String>,
|
||||
stop: Vec<String>,
|
||||
destroy: Vec<String>,
|
||||
}
|
||||
|
||||
/// Turn one declaration into that plan — pure, so what the loop does with a
|
||||
|
|
@ -274,6 +284,7 @@ fn plan(
|
|||
Converge::Deploy => plan.deploy.push(agent.clone()),
|
||||
Converge::Start => plan.start.push(agent.clone()),
|
||||
Converge::Stop => plan.stop.push(agent.clone()),
|
||||
Converge::Destroy => plan.destroy.push(agent.clone()),
|
||||
Converge::Nothing => {}
|
||||
}
|
||||
}
|
||||
|
|
@ -288,6 +299,10 @@ enum Converge {
|
|||
Deploy,
|
||||
Start,
|
||||
Stop,
|
||||
/// Declared destroyed, and this hive still has a container for it — run
|
||||
/// the teardown once. Irreversible, so unlike `Start`/`Stop` there is no
|
||||
/// intent to compare against: presence alone decides it.
|
||||
Destroy,
|
||||
/// The hive already agrees with the declaration.
|
||||
Nothing,
|
||||
}
|
||||
|
|
@ -305,9 +320,11 @@ fn decide(state: AgentState, present: bool, intent: Option<Wanted>) -> Converge
|
|||
Converge::Start
|
||||
}
|
||||
}
|
||||
// Declared offline and not here: creating a container in order to
|
||||
// leave it stopped is not what the declaration asks for.
|
||||
AgentState::Offline if !present => Converge::Nothing,
|
||||
// Declared offline/destroyed and not here: creating a container in
|
||||
// order to leave it stopped, or to immediately re-destroy it, is not
|
||||
// what either declaration asks for — absence plus a stopped-or-gone
|
||||
// declaration is agreement, not a repair to queue.
|
||||
AgentState::Offline | AgentState::Destroyed if !present => Converge::Nothing,
|
||||
AgentState::Offline => {
|
||||
if intent == Some(Wanted::Offline) {
|
||||
Converge::Nothing
|
||||
|
|
@ -315,6 +332,10 @@ fn decide(state: AgentState, present: bool, intent: Option<Wanted>) -> Converge
|
|||
Converge::Stop
|
||||
}
|
||||
}
|
||||
// Power intent plays no part here — a destroyed agent has no
|
||||
// running/stopped distinction left to converge to, only present or
|
||||
// not.
|
||||
AgentState::Destroyed => Converge::Destroy,
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -364,6 +385,7 @@ mod tests {
|
|||
deploy: vec![],
|
||||
start: vec!["adopted".to_owned()],
|
||||
stop: vec![],
|
||||
destroy: vec![],
|
||||
}
|
||||
);
|
||||
}
|
||||
|
|
@ -432,6 +454,29 @@ mod tests {
|
|||
assert_eq!(decide(AgentState::Offline, false, None), Converge::Nothing);
|
||||
}
|
||||
|
||||
/// A present agent declared destroyed gets torn down — the whole point of
|
||||
/// this state. Intent is irrelevant, unlike `Up`/`Offline`: even an
|
||||
/// agent whose power intent is `Up` still gets destroyed, since a
|
||||
/// terminal declaration outranks a steady-state one.
|
||||
#[test]
|
||||
fn a_present_agent_declared_destroyed_is_destroyed() {
|
||||
assert_eq!(decide(AgentState::Destroyed, true, None), Converge::Destroy);
|
||||
assert_eq!(
|
||||
decide(AgentState::Destroyed, true, Some(Wanted::Up)),
|
||||
Converge::Destroy
|
||||
);
|
||||
}
|
||||
|
||||
/// Already gone: destroying an absent agent would be acting on nothing.
|
||||
/// Its control is the present case above, which must destroy.
|
||||
#[test]
|
||||
fn an_absent_agent_declared_destroyed_is_left_absent() {
|
||||
assert_eq!(
|
||||
decide(AgentState::Destroyed, false, None),
|
||||
Converge::Nothing
|
||||
);
|
||||
}
|
||||
|
||||
/// The watch's half of "absence is not a deletion order". `Put` is the
|
||||
/// control: without it this would pass on a function that refused
|
||||
/// everything, which would silently stop the fast path converging at all.
|
||||
|
|
@ -456,7 +501,7 @@ mod tests {
|
|||
/// pass forever without measuring anything.
|
||||
#[test]
|
||||
fn every_state_this_build_knows_is_covered_above() {
|
||||
for state in [AgentState::Up, AgentState::Offline] {
|
||||
for state in [AgentState::Up, AgentState::Offline, AgentState::Destroyed] {
|
||||
let seen = [true, false].iter().any(|present| {
|
||||
decide(state, *present, None) != Converge::Nothing
|
||||
|| decide(state, *present, Some(Wanted::Up)) != Converge::Nothing
|
||||
|
|
|
|||
Loading…
Reference in a new issue