Skip to main content

max / makenotwork

3.2 KB · 92 lines History Blame Raw
1 //! Background check tasks spawned by `pom serve`, one spawner per check kind.
2
3 mod backup;
4 mod cors;
5 mod dns;
6 mod health;
7 mod meta_alert;
8 mod prune;
9 mod routes;
10 mod scan_pipeline;
11 mod synckit_fleet;
12 mod systemd;
13 mod tls;
14 mod whois;
15
16 use tokio_util::sync::CancellationToken;
17
18 use pom::config::{Config, TargetConfig};
19
20 /// The shared cadence of every background check: a fixed interval that skips its
21 /// immediate first tick, delays rather than bursts after a missed one, and yields
22 /// to cancellation without waiting out the remaining interval.
23 ///
24 /// This hands the caller a ticker instead of taking the check body as a callback.
25 /// A callback would have to borrow `pool`/`name`/the target config out of its own
26 /// captures across an await, which needs `AsyncFnMut::CallRefFuture` to name and
27 /// bound as `Send` (unstable). Driving the loop from the caller sidesteps that
28 /// entirely: every borrow stays local to the caller's own async block.
29 pub(crate) struct CheckInterval {
30 interval: tokio::time::Interval,
31 cancel: CancellationToken,
32 startup_tick_pending: bool,
33 }
34
35 impl CheckInterval {
36 pub(crate) fn new(interval_secs: u64, cancel: CancellationToken) -> Self {
37 let mut interval = tokio::time::interval(std::time::Duration::from_secs(interval_secs));
38 interval.set_missed_tick_behavior(tokio::time::MissedTickBehavior::Delay);
39 Self {
40 interval,
41 cancel,
42 startup_tick_pending: true,
43 }
44 }
45
46 /// Wait for the next tick. Returns `false` once cancelled, so the call site
47 /// reads `while ticks.next().await { ... }`.
48 ///
49 /// The first call consumes the immediate startup tick, so a check fires one
50 /// full interval after boot rather than the instant the process starts.
51 pub(crate) async fn next(&mut self) -> bool {
52 if self.startup_tick_pending {
53 self.startup_tick_pending = false;
54 self.interval.tick().await;
55 }
56 tokio::select! {
57 () = self.cancel.cancelled() => false,
58 _ = self.interval.tick() => true,
59 }
60 }
61 }
62
63 /// Every configured target paired with its config, in `target_names` order.
64 ///
65 /// The spawners all opened with the same `for name in config.target_names()` /
66 /// `get_target(&name).unwrap().clone()` pair; the `unwrap` is only sound because
67 /// the name came from the map itself, which this keeps in one place.
68 pub(crate) fn configured_targets(
69 config: &Config,
70 ) -> impl Iterator<Item = (String, TargetConfig)> + use<'_> {
71 config.target_names().into_iter().map(|name| {
72 let target = config
73 .get_target(&name)
74 .expect("name came from target_names")
75 .clone();
76 (name, target)
77 })
78 }
79
80 pub(crate) use backup::spawn_backup_tasks;
81 pub(crate) use cors::spawn_cors_tasks;
82 pub(crate) use dns::spawn_dns_tasks;
83 pub(crate) use health::spawn_health_tasks;
84 pub(crate) use meta_alert::spawn_meta_alert_task;
85 pub(crate) use prune::spawn_prune_task;
86 pub(crate) use routes::spawn_route_tasks;
87 pub(crate) use scan_pipeline::spawn_scan_pipeline_tasks;
88 pub(crate) use synckit_fleet::spawn_synckit_fleet_tasks;
89 pub(crate) use systemd::spawn_systemd_tasks;
90 pub(crate) use tls::spawn_tls_tasks;
91 pub(crate) use whois::spawn_whois_tasks;
92