Watch
0
0
Fork
You've already forked hyperhive
0

mint a live agent's missing store identity from the renewal pass, and log the step a mint stopped at

This commit is contained in:
atlas 2026-10-29 03:17:16 +01:00 • committed by mara
commit 8126e0fffa

View file

@ -24,10 +24,10 @@
//! tests pin them; the IO on either side only reads or acts.
//!
//! **Only agents some hive is declared to run are renewed** ([`live_agents`]):
//! the wanted-state declarations are where a destroy is recorded, and an agent
//! declared nowhere, or only as `Destroyed`, is skipped. A credential that is
//! not stored is skipped too — creating one is agent creation's job, and a
//! destroy deletes it.
//! a destroy is recorded in the wanted-state declarations, and an agent declared
//! nowhere, or only as `Destroyed`, is skipped. A live agent with no stored
//! certificate gets one minted, as a failed mint at creation is retried nowhere
//! else, unless its mint is in flight. A missing queue secret stays missing.
use std::collections::BTreeSet;
use std::sync::Arc;
@ -141,7 +141,8 @@ pub enum Observed {
Unstamped,
/// Stored and not yet due.
Current,
/// Nothing stored. Never renewed: see the module doc.
/// Nothing stored. A certificate is minted for a live agent; a queue secret
/// is left alone (see the module doc).
Absent,
/// The read or the decode failed. Nothing is known, so nothing is done.
Unknown,
@ -163,14 +164,14 @@ pub struct Renewal {
}
/// The renewals a pass queues: one per agent with a certificate that is
/// [`Observed::Due`], or a queue secret that is [`Observed::Due`] or
/// [`Observed::Unstamped`].
/// [`Observed::Due`] or [`Observed::Absent`], or a queue secret that is
/// [`Observed::Due`] or [`Observed::Unstamped`].
pub fn plan(observed: &[(String, AgentObserved)]) -> Vec<Renewal> {
observed
.iter()
.map(|(agent, o)| Renewal {
agent: agent.clone(),
cert: matches!(o.cert, Observed::Due(_)),
cert: matches!(o.cert, Observed::Due(_) | Observed::Absent),
secret: matches!(o.secret, Observed::Due(_) | Observed::Unstamped),
})
.filter(|r| r.cert || r.secret)
@ -319,6 +320,12 @@ async fn observe_all(
if let Observed::Due(age) = o.cert {
tracing::info!(agent, age = %Age(age), "agent certificate past half its life; re-issuing");
}
if o.cert == Observed::Absent {
tracing::info!(
agent,
"agent has no stored certificate; minting its store identity"
);
}
if let Observed::Due(age) = o.secret {
tracing::info!(agent, age = %Age(age), "agent queue credential due; re-minting");
}
@ -525,16 +532,19 @@ mod tests {
}
#[test]
fn only_due_credentials_are_planned() {
fn due_and_missing_certificates_and_due_secrets_are_planned() {
use Observed::{Absent, Current, Due, Unknown, Unstamped};
let o = |cert, secret| AgentObserved { cert, secret };
let observed = [
("both".to_owned(), o(Due(46 * DAY), Due(45 * DAY))),
("cert".to_owned(), o(Due(46 * DAY), Current)),
("secret".to_owned(), o(Absent, Due(45 * DAY))),
("secret".to_owned(), o(Current, Due(45 * DAY))),
("unstamped".to_owned(), o(Current, Unstamped)),
("neither".to_owned(), o(Current, Absent)),
("unknown".to_owned(), o(Unknown, Unknown)),
("no identity".to_owned(), o(Absent, Absent)),
("no cert, old secret".to_owned(), o(Absent, Due(45 * DAY))),
("unreadable cert".to_owned(), o(Unknown, Absent)),
];
let r = |agent: &str, cert, secret| Renewal {
agent: agent.to_owned(),
@ -548,6 +558,40 @@ mod tests {
r("cert", true, false),
r("secret", false, true),
r("unstamped", false, true),
r("no identity", true, false),
r("no cert, old secret", true, true),
]
);
}
/// A pass over declarations: the agent with no stored identity is minted
/// for when it is live and not when it is destroyed. The destroyed agent is
/// never observed, which is what [`live_agents`] does before the store is
/// read.
#[test]
fn a_missing_identity_is_minted_for_a_live_agent_and_not_a_destroyed_one() {
let declarations = [declared(&[
("sock", AgentState::Up),
("paused", AgentState::Paused),
("gone", AgentState::Destroyed),
])];
let nothing_stored = AgentObserved {
cert: Observed::Absent,
secret: Observed::Absent,
};
let observed: Vec<_> = live_agents(&declarations)
.into_iter()
.map(|agent| (agent, nothing_stored))
.collect();
let planned: Vec<_> = plan(&observed)
.into_iter()
.map(|r| (r.agent, r.cert, r.secret))
.collect();
assert_eq!(
planned,
[
("paused".to_owned(), true, false),
("sock".to_owned(), true, false)
]
);
}