Skip to main content

max / makenotwork

3.8 KB · 107 lines History Blame Raw
1 //! Background DNS record check task.
2
3 use tokio::task::JoinHandle;
4 use tracing::info;
5
6 use pom::alerts::Alerter;
7 use pom::checks::dns;
8 use pom::config::Config;
9 use pom::db;
10
11 use super::{CheckInterval, configured_targets};
12
13 pub(crate) fn spawn_dns_tasks(
14 config: &Config,
15 pool: &sqlx::SqlitePool,
16 cancel: &tokio_util::sync::CancellationToken,
17 alerter: Option<&Alerter>,
18 ) -> Vec<JoinHandle<()>> {
19 let dns_interval_secs = config.serve.dns_check_interval_secs;
20 let mut handles = Vec::new();
21
22 for (name, target_config) in configured_targets(config) {
23 if target_config.dns.is_empty() {
24 continue;
25 }
26 let dns_records = target_config.dns.clone();
27 let label = target_config.label.clone();
28 let pool = pool.clone();
29 let alerter = alerter.cloned();
30 let cancel = cancel.clone();
31 let n = dns_records.len();
32
33 info!("{name}: DNS check every {dns_interval_secs}s ({n} records)");
34
35 handles.push(tokio::spawn(async move {
36 // Prune stale DNS data from DB (records removed from config)
37 let expected_dns_keys: Vec<(String, String)> = dns_records
38 .iter()
39 .map(|d| (d.name.clone(), d.record_type.to_string()))
40 .collect();
41 match db::prune_stale_dns(&pool, &name, &expected_dns_keys).await {
42 Ok(0) => {}
43 Ok(n) => info!("{name}: pruned {n} stale DNS check rows"),
44 Err(e) => tracing::error!("{name}: failed to prune stale DNS: {e}"),
45 }
46
47 let mut ticks = CheckInterval::new(dns_interval_secs, cancel);
48 let mut prev_mismatched: std::collections::HashSet<(
49 String,
50 pom::types::DnsRecordType,
51 )> = std::collections::HashSet::new();
52
53 while ticks.next().await {
54 let results = dns::check_dns(&name, &dns_records).await;
55
56 for result in &results {
57 if let Err(e) = db::insert_dns_check(&pool, result).await {
58 tracing::error!(
59 "{}: failed to store DNS check for {} {}: {e}",
60 name,
61 result.name,
62 result.record_type
63 );
64 }
65 }
66
67 let current_mismatched: std::collections::HashSet<(
68 String,
69 pom::types::DnsRecordType,
70 )> = results
71 .iter()
72 .filter(|r| !r.matches)
73 .map(|r| (r.name.clone(), r.record_type))
74 .collect();
75
76 let ok_count = results.iter().filter(|r| r.matches).count();
77 info!("{name}: DNS {ok_count}/{n} match");
78
79 if let Some(ref alerter) = alerter {
80 // New mismatches
81 let new_mismatches: Vec<&pom::types::DnsCheckResult> = results
82 .iter()
83 .filter(|r| {
84 !r.matches
85 && !prev_mismatched.contains(&(r.name.clone(), r.record_type))
86 })
87 .collect();
88 if !new_mismatches.is_empty() {
89 let owned: Vec<pom::types::DnsCheckResult> =
90 new_mismatches.into_iter().cloned().collect();
91 alerter.send_dns_mismatch_alert(&name, &label, &owned).await;
92 }
93
94 // All recovered
95 if !prev_mismatched.is_empty() && current_mismatched.is_empty() {
96 alerter.send_dns_recovery_alert(&name, &label).await;
97 }
98 }
99
100 prev_mismatched = current_mismatched;
101 }
102 }));
103 }
104
105 handles
106 }
107