Skip to main content

max / makenotwork

3.4 KB · 96 lines History Blame Raw
1 //! SyncKit field-version task. Polls MNW's internal client-versions endpoint on a
2 //! per-target interval and records the distribution so `target_status` can answer
3 //! which SDK is actually in the field.
4 //!
5 //! Unlike every other task here, this one fires no alerts. There is no bad
6 //! distribution to alert on (see `pom::checks::synckit_fleet`), and paging on the
7 //! remaining case, a readout PoM cannot take, would mean an email every hour for
8 //! as long as a stale token went unnoticed. It surfaces as a degraded condition
9 //! on the target instead, which is where an operator already looks.
10
11 use tokio::task::JoinHandle;
12 use tracing::{info, warn};
13
14 use pom::checks::synckit_fleet;
15 use pom::config::Config;
16 use pom::db;
17
18 use super::{CheckInterval, configured_targets};
19
20 pub(crate) fn spawn_synckit_fleet_tasks(
21 config: &Config,
22 pool: &sqlx::SqlitePool,
23 cancel: &tokio_util::sync::CancellationToken,
24 ) -> Vec<JoinHandle<()>> {
25 let mut handles = Vec::new();
26
27 // The endpoint is authed with the alerts ingest token, so an alert config that
28 // omits it leaves this check with no credential. Read it once here rather than
29 // per tick: a token that appears later arrives by config reload, which restarts
30 // these tasks anyway.
31 let token = config
32 .alerts
33 .as_ref()
34 .and_then(|a| a.alerts_ingest_token.clone());
35
36 for (name, target_config) in configured_targets(config) {
37 let Some(fleet_config) = target_config.synckit_fleet else {
38 continue;
39 };
40
41 let Some(token) = token.clone() else {
42 // Loud, because the alternative is a configured check that silently
43 // never runs and a fleet column that stays blank for weeks.
44 warn!(
45 "{name}: synckit fleet check configured but alerts.alerts_ingest_token is unset, \
46 not spawning"
47 );
48 continue;
49 };
50
51 let name = name.clone();
52 let pool = pool.clone();
53 let cancel = cancel.clone();
54
55 info!(
56 "{name}: synckit fleet check every {}s against {} ({}d window)",
57 fleet_config.interval_secs, fleet_config.base_url, fleet_config.window_days,
58 );
59
60 handles.push(tokio::spawn(async move {
61 let mut ticks = CheckInterval::new(fleet_config.interval_secs, cancel);
62
63 while ticks.next().await {
64 let result = synckit_fleet::check_synckit_fleet(
65 &name,
66 &fleet_config.base_url,
67 &token,
68 fleet_config.window_days,
69 fleet_config.timeout_secs,
70 )
71 .await;
72
73 match &result.error {
74 Some(error) => warn!(
75 target = %name,
76 %error,
77 "synckit fleet readout unavailable"
78 ),
79 None => info!(
80 "{name}: synckit fleet {} device(s) over {}d across {} version(s)",
81 result.devices,
82 result.window_days,
83 result.versions.len()
84 ),
85 }
86
87 if let Err(e) = db::insert_synckit_fleet_check(&pool, &result).await {
88 tracing::error!("{name}: failed to store synckit fleet check: {e}");
89 }
90 }
91 }));
92 }
93
94 handles
95 }
96