Watch
0
0
Fork
You've already forked hyperhive
0
hyperhive/swarm-controller/src/agent_renewal.rs
atlas 9e06e191a3 swarm-controller: first credential-renewal pass waits for the queue connection
The renewal pass ran the moment swarm-controller started, before its
queue client had connected, so `WantedWriter::view` refused with
"client state Pending" and the pass logged
`agent credential renewal: pass failed; retrying next tick`. That fired
twice in 24h on muede-lpt2, both under a second after start, and would
trip a Grafana rule on that WARN on ordinary restarts.

The first pass now waits until the queue client is connected, polling
`swarm_queue_client::ensure_connected` every 5s the way
`AgentIconReader::create_when_connected` does. The wait is bounded by
one RECONCILE_INTERVAL (5 min): the old code's retry after a false start
also came one interval later, so a queue that never connects gets its
first pass no later than before. Hitting the bound logs one WARN and runs
the pass anyway. Only startup waits; a later disconnect still fails a
pass and logs the WARN.

Refs #4717
2026-09-30 15:46:56 +02:00

662 lines
24 KiB
Rust

//! Renewing each live agent's two store credentials by age: its mTLS
//! certificate (`swarm/agents/<agent>/bao-mtls`) once it is past half its
//! validity, and its queue secret (`swarm/agents/<agent>/queue`) once it is
//! [`SECRET_RENEW_AFTER`] old.
//!
//! The certificate's age is its own `notBefore`/`notAfter`. The queue secret
//! has no expiry and `swarm-nats-auth` checks nothing about time, so its age is
//! the controller's own record, [`queue::AgentCredential::minted_at`]; a secret
//! without one is stamped, value unchanged ([`SecretStep::Backfill`]).
//!
//! Either replacement reaches the agent at its next start, when the container
//! is handed the certificate and fetches the secret; nothing pulls either into
//! a running container. The old certificate stays valid until it expires. The
//! old secret stops working at once: the queue checks it only at `CONNECT`, so
//! an open connection is unaffected, but a reconnect before that restart
//! presents the old secret and is denied.
//!
//! [`spawn`]'s pass, once the queue is up and every [`RECONCILE_INTERVAL`],
//! inserts a job per agent with anything due: `MintAgentIdentity` for the
//! certificate ([`crate::agent_identity::mint_and_verify`], which keeps the
//! queue secret as it is) and `RenewAgentQueueCredential` ([`renew`]) for the
//! secret, the second after the first when both are due. The decisions are
//! pure ([`live_agents`], [`cert_is_due`], [`secret_step`], [`plan`]) so the
//! 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.
use std::collections::BTreeSet;
use std::sync::Arc;
use anyhow::{Context, Result, bail};
use swarm_queue_client::wanted::{AgentState, HiveWanted};
use swarm_secret_client::{SecretStore, mtls, queue};
use x509_cert::der::DecodePem as _;
use crate::wanted::WantedWriter;
/// Age at which a queue secret is re-minted: half the agent certificate's
/// 90-day life (`agentPkiLeafTtl` in `swarm-bao.nix`), so the two renew on one
/// cadence.
pub const SECRET_RENEW_AFTER: std::time::Duration = std::time::Duration::from_hours(45 * 24);
/// How often [`spawn`] re-checks every agent. Also the longest it waits for
/// the queue connection before its first pass.
const RECONCILE_INTERVAL: std::time::Duration = std::time::Duration::from_mins(5);
/// How often [`spawn`] checks the queue connection before its first pass.
const CONNECT_POLL: std::time::Duration = std::time::Duration::from_secs(5);
/// Now, in the unix seconds [`queue::AgentCredential::minted_at`] holds.
pub fn unix_now() -> i64 {
std::time::SystemTime::now()
.duration_since(std::time::UNIX_EPOCH)
.map_or(0, |d| i64::try_from(d.as_secs()).unwrap_or(i64::MAX))
}
/// What a stored queue secret needs at `now`.
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum SecretStep {
/// Younger than [`SECRET_RENEW_AFTER`].
Keep,
/// No mint time recorded: stamp `now` beside the unchanged value, so its
/// clock starts here and no holder of it is cut off.
Backfill,
/// At least [`SECRET_RENEW_AFTER`] old: replace the value.
Remint,
}
/// Decide [`SecretStep`] for `stored` at `now`.
pub fn secret_step(stored: &queue::AgentCredential, now: i64) -> SecretStep {
let renew_after = i64::try_from(SECRET_RENEW_AFTER.as_secs()).unwrap_or(i64::MAX);
match stored.minted_at {
None => SecretStep::Backfill,
Some(at) if now.saturating_sub(at) >= renew_after => SecretStep::Remint,
Some(_) => SecretStep::Keep,
}
}
/// The object `step` writes for `agent` at `now`, or `None` for
/// [`SecretStep::Keep`].
///
/// # Errors
/// When a re-mint cannot draw randomness from the kernel.
pub fn apply_secret_step(
step: SecretStep,
stored: &queue::AgentCredential,
agent: &str,
now: i64,
) -> Result<Option<queue::AgentCredential>> {
Ok(match step {
SecretStep::Keep => None,
SecretStep::Backfill => Some(queue::AgentCredential {
minted_at: Some(now),
..stored.clone()
}),
SecretStep::Remint => Some(fresh(agent, now)?),
})
}
/// A certificate's validity window, `(notBefore, notAfter)` in unix seconds.
///
/// # Errors
/// When `pem` is not one PEM-encoded X.509 certificate.
pub fn cert_validity(pem: &str) -> Result<(i64, i64)> {
let cert = x509_cert::Certificate::from_pem(pem.as_bytes())
.context("decoding the stored certificate")?;
let validity = &cert.tbs_certificate.validity;
let secs = |t: x509_cert::time::Time| {
i64::try_from(t.to_unix_duration().as_secs()).unwrap_or(i64::MAX)
};
Ok((secs(validity.not_before), secs(validity.not_after)))
}
/// Whether a certificate valid from `not_before` to `not_after` is past half
/// its life at `now`.
pub fn cert_is_due(not_before: i64, not_after: i64, now: i64) -> bool {
let half = not_after.saturating_sub(not_before) / 2;
now >= not_before.saturating_add(half)
}
/// A new queue credential for `agent`, minted at `now`. Shared with agent
/// creation, so every secret this daemon writes carries a mint time.
///
/// # Errors
/// When the kernel will not supply randomness.
pub fn fresh(agent: &str, now: i64) -> Result<queue::AgentCredential> {
Ok(queue::AgentCredential {
value: crate::agent_identity::generate_queue_secret()?,
agent: agent.to_owned(),
minted_at: Some(now),
})
}
/// Every agent declared on at least one hive in a state other than
/// `Destroyed`.
pub fn live_agents(declarations: &[HiveWanted]) -> BTreeSet<String> {
declarations
.iter()
.flat_map(|d| &d.agents)
.filter(|(_, wanted)| wanted.state != AgentState::Destroyed)
.map(|(agent, _)| agent.clone())
.collect()
}
/// What one pass found for one credential of one live agent.
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum Observed {
/// Stored and due for replacement, with its age in seconds.
Due(i64),
/// A queue secret stored with no mint time: due for
/// [`SecretStep::Backfill`], never for a new value.
Unstamped,
/// Stored and not yet due.
Current,
/// Nothing stored. Never renewed: see the module doc.
Absent,
/// The read or the decode failed. Nothing is known, so nothing is done.
Unknown,
}
/// What one pass found for one live agent.
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct AgentObserved {
pub cert: Observed,
pub secret: Observed,
}
/// One agent's share of a pass: which of its credentials to renew.
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct Renewal {
pub agent: String,
pub cert: bool,
pub secret: bool,
}
/// 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`].
pub fn plan(observed: &[(String, AgentObserved)]) -> Vec<Renewal> {
observed
.iter()
.map(|(agent, o)| Renewal {
agent: agent.clone(),
cert: matches!(o.cert, Observed::Due(_)),
secret: matches!(o.secret, Observed::Due(_) | Observed::Unstamped),
})
.filter(|r| r.cert || r.secret)
.collect()
}
/// An age in seconds, for a log line: whole days.
struct Age(i64);
impl std::fmt::Display for Age {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{}d", self.0 / 86_400)
}
}
/// Backfill or re-mint `agent`'s queue secret if it is still stored and still
/// needs it ([`secret_step`]). The whole job of the `RenewAgentQueueCredential`
/// node.
///
/// Re-decides rather than trusting the pass that queued it, so a node queued
/// twice acts once. Reads the write back before reporting success.
///
/// # Errors
/// When the store refuses a step, or returns a different object than the one
/// just written.
pub async fn renew(agent: &str) -> Result<()> {
let path = queue::agent_queue_path(agent)?;
let store = crate::store::connect()
.await
.context("logging in to the swarm secret store")?;
let Some(stored) = store
.read_optional::<queue::AgentCredential>(&path)
.await
.with_context(|| format!("reading {path}"))?
else {
tracing::info!(agent, %path, "agent queue credential: nothing stored, nothing to renew");
return Ok(());
};
let now = unix_now();
let step = secret_step(&stored, now);
let Some(written) = apply_secret_step(step, &stored, agent, now)? else {
tracing::debug!(agent, "agent queue credential is current; left as it is");
return Ok(());
};
store
.write(&path, &written)
.await
.with_context(|| format!("writing the agent queue credential at {path}"))?;
let read_back: queue::AgentCredential = store
.read(&path)
.await
.with_context(|| format!("reading {path} back"))?;
if read_back != written {
bail!("the store returned a different object at {path} than the one just written");
}
if let Some(at) = stored.minted_at {
tracing::info!(
agent,
%path,
old_age = %Age(now.saturating_sub(at)),
"agent queue credential re-minted; the agent presents it from its next restart"
);
} else {
tracing::info!(
agent,
%path,
"agent queue credential had no mint time; stamped now, value unchanged"
);
}
Ok(())
}
/// What the store says about `agent`'s certificate.
async fn observe_cert(store: &SecretStore, agent: &str, now: i64) -> Observed {
let stored = match mtls::identity_path(agent) {
Ok(path) => store.read_optional::<mtls::Credential>(&path).await,
Err(e) => Err(e),
};
let credential = match stored {
Ok(Some(credential)) => credential,
Ok(None) => return Observed::Absent,
Err(e) => {
tracing::warn!(agent, error = %e, "agent certificate: store read failed");
return Observed::Unknown;
}
};
match cert_validity(&credential.cert) {
Ok((not_before, not_after)) if cert_is_due(not_before, not_after, now) => {
Observed::Due(now.saturating_sub(not_before))
}
Ok(_) => Observed::Current,
Err(e) => {
tracing::warn!(agent, error = %format!("{e:#}"), "agent certificate: unreadable");
Observed::Unknown
}
}
}
/// What the store says about `agent`'s queue secret.
async fn observe_secret(store: &SecretStore, agent: &str, now: i64) -> Observed {
let stored = match queue::agent_queue_path(agent) {
Ok(path) => store.read_optional::<queue::AgentCredential>(&path).await,
Err(e) => Err(e),
};
match stored {
Ok(Some(stored)) => match (secret_step(&stored, now), stored.minted_at) {
(SecretStep::Remint, Some(at)) => Observed::Due(now.saturating_sub(at)),
(SecretStep::Backfill, _) => Observed::Unstamped,
_ => Observed::Current,
},
Ok(None) => Observed::Absent,
Err(e) => {
tracing::warn!(agent, error = %e, "agent queue credential: store read failed");
Observed::Unknown
}
}
}
/// One pass: every live agent, observed. Fails as a whole when any hive's
/// declaration cannot be read, since that hive may be the one declaring an
/// agent destroyed.
async fn observe_all(
wanted: &WantedWriter,
hives: &[String],
) -> Result<Vec<(String, AgentObserved)>> {
let mut declarations = Vec::with_capacity(hives.len());
for hive in hives {
if let Some(d) = wanted
.view(hive)
.await
.with_context(|| format!("reading {hive}'s wanted-state declaration"))?
{
declarations.push(d);
}
}
let store = crate::store::connect()
.await
.context("logging in to the swarm secret store")?;
let now = unix_now();
let mut observed = Vec::new();
for agent in live_agents(&declarations) {
let o = AgentObserved {
cert: observe_cert(&store, &agent, now).await,
secret: observe_secret(&store, &agent, now).await,
};
if let Observed::Due(age) = o.cert {
tracing::info!(agent, age = %Age(age), "agent certificate past half its life; re-issuing");
}
if let Observed::Due(age) = o.secret {
tracing::info!(agent, age = %Age(age), "agent queue credential due; re-minting");
}
if o.secret == Observed::Unstamped {
tracing::info!(
agent,
"agent queue credential has no mint time; stamping it"
);
}
observed.push((agent, o));
}
Ok(observed)
}
/// Wait until `connected` holds, checking every [`CONNECT_POLL`] for at most
/// [`RECONCILE_INTERVAL`]. Returns whether it held.
async fn wait_for_queue(connected: impl Fn() -> bool) -> bool {
let deadline = tokio::time::Instant::now() + RECONCILE_INTERVAL;
loop {
if connected() {
return true;
}
if tokio::time::Instant::now() >= deadline {
return false;
}
tokio::time::sleep(CONNECT_POLL).await;
}
}
/// [`spawn`]'s loop, with the connection check and the pass handed in.
async fn run<P, F>(connected: impl Fn() -> bool, mut pass: P, enqueue: impl Fn(Vec<Renewal>))
where
P: FnMut() -> F,
F: std::future::Future<Output = Result<Vec<(String, AgentObserved)>>>,
{
if !wait_for_queue(connected).await {
tracing::warn!(
waited_s = RECONCILE_INTERVAL.as_secs(),
"agent credential renewal: queue still not connected; running the first pass anyway"
);
}
let mut ticker = tokio::time::interval(RECONCILE_INTERVAL);
loop {
ticker.tick().await;
match pass().await {
Ok(observed) => {
let renewals = plan(&observed);
if renewals.is_empty() {
tracing::debug!(
checked = observed.len(),
"agent credential renewal: none due"
);
} else {
tracing::info!(
checked = observed.len(),
renewing = renewals.len(),
"agent credential renewal: queueing"
);
enqueue(renewals);
}
}
Err(e) => tracing::warn!(
error = %format!("{e:#}"),
retry_in_s = RECONCILE_INTERVAL.as_secs(),
"agent credential renewal: pass failed; retrying next tick"
),
}
}
}
/// Check every live agent once the queue is connected and every
/// [`RECONCILE_INTERVAL`] after, and hand the renewals due to `enqueue`, which
/// inserts a job for each.
///
/// Only the first pass waits for the connection, and for at most one
/// [`RECONCILE_INTERVAL`]. A pass that fails is logged and retried on the next
/// tick; it never stops the daemon.
pub fn spawn(
wanted: Arc<WantedWriter>,
hives: Vec<String>,
enqueue: impl Fn(Vec<Renewal>) + Send + 'static,
) {
tokio::spawn(async move {
run(
|| wanted.is_connected(),
|| observe_all(&wanted, &hives),
enqueue,
)
.await;
});
}
#[cfg(test)]
mod tests {
use super::*;
use swarm_queue_client::wanted::AgentWanted;
const NOW: i64 = 1_790_000_000;
const DAY: i64 = 86_400;
/// Self-signed, CN `hive-agent-atlas`, valid 2026-01-01T00:00:00Z to
/// 2026-04-01T00:00:00Z: 90 days, the agent role's `ttl`. Its key was
/// discarded.
const CERT_PEM: &str = "-----BEGIN CERTIFICATE-----
MIIBizCCATGgAwIBAgIUSFpuYmy1UoHvq/MYiyrAudRaWXcwCgYIKoZIzj0EAwIw
GzEZMBcGA1UEAwwQaGl2ZS1hZ2VudC1hdGxhczAeFw0yNjAxMDEwMDAwMDBaFw0y
NjA0MDEwMDAwMDBaMBsxGTAXBgNVBAMMEGhpdmUtYWdlbnQtYXRsYXMwWTATBgcq
hkjOPQIBBggqhkjOPQMBBwNCAAR7p2eZAxjiE1RdsuSHE3CcTjbnd4swzfnmqUPW
9NJN7mvOtqEzxCKfOSFjaIhhyShIQ41/V6vGi80EPDB+uR56o1MwUTAdBgNVHQ4E
FgQUxgZFsN1VY3ODrPS1NUfY/x3EnmswHwYDVR0jBBgwFoAUxgZFsN1VY3ODrPS1
NUfY/x3EnmswDwYDVR0TAQH/BAUwAwEB/zAKBggqhkjOPQQDAgNIADBFAiEAnU9e
WOioNTUNK9b6FignejpK5FpyrODUuH/iL4TqCh0CIAz6GzJeLebhRPvYlfVUofdV
hWx3sOwmUgjkRQXoxY+p
-----END CERTIFICATE-----
";
const CERT_NOT_BEFORE: i64 = 1_767_225_600;
const CERT_NOT_AFTER: i64 = 1_775_001_600;
fn minted(at: Option<i64>) -> queue::AgentCredential {
queue::AgentCredential {
value: "Ab9_-zSECRET".to_owned(),
agent: "atlas".to_owned(),
minted_at: at,
}
}
#[test]
fn a_secret_without_a_mint_time_is_backfilled_with_its_value_unchanged() {
let stored = minted(None);
assert_eq!(secret_step(&stored, NOW), SecretStep::Backfill);
let written = apply_secret_step(SecretStep::Backfill, &stored, "atlas", NOW)
.expect("no randomness needed")
.expect("a backfill writes");
assert_eq!(written.value, stored.value, "no holder of it is cut off");
assert_eq!(written.agent, stored.agent);
assert_eq!(written.minted_at, Some(NOW), "its clock starts now");
assert_eq!(secret_step(&written, NOW), SecretStep::Keep);
}
#[test]
fn a_secret_is_re_minted_from_forty_five_days_with_a_new_value_and_mint_time() {
for age in [45 * DAY, 90 * DAY] {
let stored = minted(Some(NOW - age));
assert_eq!(secret_step(&stored, NOW), SecretStep::Remint, "{age}");
let written = apply_secret_step(SecretStep::Remint, &stored, "atlas", NOW)
.expect("the kernel supplies randomness")
.expect("a re-mint writes");
assert_ne!(written.value, stored.value);
assert_eq!(written.minted_at, Some(NOW));
assert_eq!(written.agent, "atlas");
assert_eq!(secret_step(&written, NOW), SecretStep::Keep);
}
let a = fresh("atlas", NOW).expect("randomness");
let b = fresh("atlas", NOW).expect("randomness");
assert_ne!(a.value, b.value, "two re-mints must not agree");
}
/// Includes a mint time ahead of the clock, which must not overflow into a
/// large age.
#[test]
fn a_secret_younger_than_forty_five_days_is_left_alone() {
for at in [NOW, NOW - 45 * DAY + 1, NOW + DAY] {
let stored = minted(Some(at));
assert_eq!(secret_step(&stored, NOW), SecretStep::Keep, "{at}");
assert_eq!(
apply_secret_step(SecretStep::Keep, &stored, "atlas", NOW).expect("no IO"),
None
);
}
}
#[test]
fn a_certificates_validity_is_read_from_the_certificate() {
assert_eq!(
cert_validity(CERT_PEM).expect("a well-formed certificate"),
(CERT_NOT_BEFORE, CERT_NOT_AFTER)
);
}
#[test]
fn something_that_is_not_a_certificate_is_an_error() {
assert!(cert_validity("not a certificate").is_err());
assert!(cert_validity("").is_err());
}
#[test]
fn a_ninety_day_certificate_is_due_from_day_forty_five() {
let (nb, na) = (CERT_NOT_BEFORE, CERT_NOT_AFTER);
assert!(!cert_is_due(nb, na, nb));
assert!(!cert_is_due(nb, na, nb + 45 * DAY - 1));
assert!(cert_is_due(nb, na, nb + 45 * DAY));
assert!(cert_is_due(nb, na, na + DAY), "an expired one too");
}
fn declared(agents: &[(&str, AgentState)]) -> HiveWanted {
let mut d = HiveWanted::default();
for (agent, state) in agents {
d.agents
.insert((*agent).to_owned(), AgentWanted { state: *state });
}
d
}
#[test]
fn a_destroyed_agent_is_not_live_and_every_other_state_is() {
let live = live_agents(&[declared(&[
("up", AgentState::Up),
("offline", AgentState::Offline),
("paused", AgentState::Paused),
("gone", AgentState::Destroyed),
])]);
assert_eq!(
live.into_iter().collect::<Vec<_>>(),
["offline", "paused", "up"]
);
}
/// An agent destroyed on one hive and declared on another is live: it
/// still runs somewhere.
#[test]
fn an_agent_live_on_any_hive_is_live() {
let live = live_agents(&[
declared(&[("atlas", AgentState::Destroyed)]),
declared(&[("atlas", AgentState::Up)]),
]);
assert!(live.contains("atlas"), "{live:?}");
}
#[test]
fn no_declaration_is_no_live_agent() {
assert!(live_agents(&[]).is_empty());
}
#[test]
fn only_due_credentials_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))),
("unstamped".to_owned(), o(Current, Unstamped)),
("neither".to_owned(), o(Current, Absent)),
("unknown".to_owned(), o(Unknown, Unknown)),
];
let r = |agent: &str, cert, secret| Renewal {
agent: agent.to_owned(),
cert,
secret,
};
assert_eq!(
plan(&observed),
[
r("both", true, true),
r("cert", true, false),
r("secret", false, true),
r("unstamped", false, true),
]
);
}
#[test]
fn the_age_logged_is_whole_days() {
assert_eq!(Age(46 * DAY + 5).to_string(), "46d");
}
/// [`run`] over a connection the test flips and a pass that only counts
/// itself.
fn counting_run() -> (
Arc<std::sync::atomic::AtomicBool>,
Arc<std::sync::atomic::AtomicUsize>,
tokio::task::JoinHandle<()>,
) {
use std::sync::atomic::{AtomicBool, AtomicUsize, Ordering};
let connected = Arc::new(AtomicBool::new(false));
let passes = Arc::new(AtomicUsize::new(0));
let (c, p) = (Arc::clone(&connected), Arc::clone(&passes));
let task = tokio::spawn(run(
move || c.load(Ordering::SeqCst),
move || {
p.fetch_add(1, Ordering::SeqCst);
std::future::ready(Ok(Vec::new()))
},
|_| {},
));
(connected, passes, task)
}
/// The sleeps end between [`CONNECT_POLL`] boundaries, so a check and an
/// assertion never fall on the same instant.
#[tokio::test(start_paused = true)]
async fn the_first_pass_waits_for_the_queue_and_later_passes_do_not() {
use std::sync::atomic::Ordering;
let (connected, passes, task) = counting_run();
tokio::time::sleep(std::time::Duration::from_secs(62)).await;
assert_eq!(passes.load(Ordering::SeqCst), 0, "no pass while Pending");
connected.store(true, Ordering::SeqCst);
tokio::time::sleep(CONNECT_POLL).await;
assert_eq!(passes.load(Ordering::SeqCst), 1, "a pass once connected");
connected.store(false, Ordering::SeqCst);
tokio::time::sleep(RECONCILE_INTERVAL).await;
assert_eq!(
passes.load(Ordering::SeqCst),
2,
"a later disconnect does not hold back the next pass"
);
task.abort();
}
#[tokio::test(start_paused = true)]
async fn a_queue_that_never_connects_gets_a_pass_after_one_interval() {
use std::sync::atomic::Ordering;
let (_connected, passes, task) = counting_run();
tokio::time::sleep(RECONCILE_INTERVAL.saturating_sub(std::time::Duration::from_secs(2)))
.await;
assert_eq!(passes.load(Ordering::SeqCst), 0);
tokio::time::sleep(std::time::Duration::from_secs(4)).await;
assert_eq!(passes.load(Ordering::SeqCst), 1);
task.abort();
}
}