From 2446c1d6a01f648f0af1e33164da84b13307a489 Mon Sep 17 00:00:00 2001 From: Timothy Miller Date: Thu, 19 Mar 2026 23:22:20 -0400 Subject: [PATCH] Bump crate to 2.0.8 and refine updater behavior Deduplicate up-to-date messages by tracking noop keys and move logging to the updater so callers only log the first noop. Reuse a single reqwest Client for IP detection instead of rebuilding it for each call. Always ping heartbeat even when there are no meaningful changes. Fix Pushover shoutrrr parsing (token@user order) and update tests --- Cargo.lock | 2 +- Cargo.toml | 2 +- src/cloudflare.rs | 7 +- src/main.rs | 78 ++++++++++------ src/notifier.rs | 7 +- src/updater.rs | 220 +++++++++++++++++++++++++++++++++------------- 6 files changed, 219 insertions(+), 97 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index f43c49a..1a3aff4 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -109,7 +109,7 @@ dependencies = [ [[package]] name = "cloudflare-ddns" -version = "2.0.6" +version = "2.0.8" dependencies = [ "chrono", "idna", diff --git a/Cargo.toml b/Cargo.toml index b2ff7b0..90f490c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "cloudflare-ddns" -version = "2.0.7" +version = "2.0.8" edition = "2021" description = "Access your home network remotely via a custom domain name without a static IP" license = "GPL-3.0" diff --git a/src/cloudflare.rs b/src/cloudflare.rs index 1501c1e..ba1008f 100644 --- a/src/cloudflare.rs +++ b/src/cloudflare.rs @@ -467,7 +467,7 @@ impl CloudflareHandle { self.update_record(zone_id, &record.id, &payload, ppfmt).await; } } else { - ppfmt.infof(pp::EMOJI_SKIP, &format!("Record {fqdn} is up to date ({ip_str})")); + // Caller handles "up to date" logging based on SetResult::Noop } } else { // Find an existing managed record to update, or create new @@ -668,10 +668,7 @@ impl CloudflareHandle { .collect(); if to_add.is_empty() && ids_to_delete.is_empty() { - ppfmt.infof( - pp::EMOJI_SKIP, - &format!("WAF list {} is up to date", waf_list.describe()), - ); + // Caller handles "up to date" logging based on SetResult::Noop return SetResult::Noop; } diff --git a/src/main.rs b/src/main.rs index bb6df3e..4778d0f 100644 --- a/src/main.rs +++ b/src/main.rs @@ -11,8 +11,10 @@ use crate::cloudflare::{Auth, CloudflareHandle}; use crate::config::{AppConfig, CronSchedule}; use crate::notifier::{CompositeNotifier, Heartbeat, Message}; use crate::pp::PP; +use std::collections::HashSet; use std::sync::atomic::{AtomicBool, Ordering}; use std::sync::Arc; +use reqwest::Client; use tokio::signal; use tokio::time::{sleep, Duration}; @@ -117,13 +119,17 @@ async fn main() { heartbeat.start().await; let mut cf_cache = cf_ip_filter::CachedCloudflareFilter::new(); + let detection_client = Client::builder() + .timeout(app_config.detection_timeout) + .build() + .unwrap_or_default(); if app_config.legacy_mode { // --- Legacy mode (original cloudflare-ddns behavior) --- - run_legacy_mode(&app_config, &handle, ¬ifier, &heartbeat, &ppfmt, running, &mut cf_cache).await; + run_legacy_mode(&app_config, &handle, ¬ifier, &heartbeat, &ppfmt, running, &mut cf_cache, &detection_client).await; } else { // --- Env var mode (cf-ddns behavior) --- - run_env_mode(&app_config, &handle, ¬ifier, &heartbeat, &ppfmt, running, &mut cf_cache).await; + run_env_mode(&app_config, &handle, ¬ifier, &heartbeat, &ppfmt, running, &mut cf_cache, &detection_client).await; } // On shutdown: delete records if configured @@ -146,12 +152,15 @@ async fn run_legacy_mode( ppfmt: &PP, running: Arc, cf_cache: &mut cf_ip_filter::CachedCloudflareFilter, + detection_client: &Client, ) { let legacy = match &config.legacy_config { Some(l) => l, None => return, }; + let mut noop_reported = HashSet::new(); + if config.repeat { match (legacy.a, legacy.aaaa) { (true, true) => println!( @@ -168,7 +177,7 @@ async fn run_legacy_mode( } while running.load(Ordering::SeqCst) { - updater::update_once(config, handle, notifier, heartbeat, cf_cache, ppfmt).await; + updater::update_once(config, handle, notifier, heartbeat, cf_cache, ppfmt, &mut noop_reported, detection_client).await; for _ in 0..legacy.ttl { if !running.load(Ordering::SeqCst) { @@ -178,7 +187,7 @@ async fn run_legacy_mode( } } } else { - updater::update_once(config, handle, notifier, heartbeat, cf_cache, ppfmt).await; + updater::update_once(config, handle, notifier, heartbeat, cf_cache, ppfmt, &mut noop_reported, detection_client).await; } } @@ -190,11 +199,14 @@ async fn run_env_mode( ppfmt: &PP, running: Arc, cf_cache: &mut cf_ip_filter::CachedCloudflareFilter, + detection_client: &Client, ) { + let mut noop_reported = HashSet::new(); + match &config.update_cron { CronSchedule::Once => { if config.update_on_start { - updater::update_once(config, handle, notifier, heartbeat, cf_cache, ppfmt).await; + updater::update_once(config, handle, notifier, heartbeat, cf_cache, ppfmt, &mut noop_reported, detection_client).await; } } schedule => { @@ -210,7 +222,7 @@ async fn run_env_mode( // Update on start if configured if config.update_on_start { - updater::update_once(config, handle, notifier, heartbeat, cf_cache, ppfmt).await; + updater::update_once(config, handle, notifier, heartbeat, cf_cache, ppfmt, &mut noop_reported, detection_client).await; } // Main loop @@ -237,7 +249,7 @@ async fn run_env_mode( return; } - updater::update_once(config, handle, notifier, heartbeat, cf_cache, ppfmt).await; + updater::update_once(config, handle, notifier, heartbeat, cf_cache, ppfmt, &mut noop_reported, detection_client).await; } } } @@ -386,6 +398,7 @@ mod tests { config: &[LegacyCloudflareEntry], ttl: i64, purge_unknown_records: bool, + noop_reported: &mut std::collections::HashSet, ) { for entry in config { #[derive(serde::Deserialize)] @@ -487,8 +500,10 @@ mod tests { } } + let noop_key = format!("{fqdn}:{record_type}"); if let Some(ref id) = identifier { if modified { + noop_reported.remove(&noop_key); if self.dry_run { println!("[DRY RUN] Would update record {fqdn} -> {ip}"); } else { @@ -504,23 +519,30 @@ mod tests { ) .await; } - } else if self.dry_run { - println!("[DRY RUN] Record {fqdn} is up to date ({ip})"); + } else if noop_reported.insert(noop_key) { + if self.dry_run { + println!("[DRY RUN] Record {fqdn} is up to date"); + } else { + println!("Record {fqdn} is up to date"); + } } - } else if self.dry_run { - println!("[DRY RUN] Would add new record {fqdn} -> {ip}"); } else { - println!("Adding new record {fqdn} -> {ip}"); - let create_endpoint = - format!("zones/{}/dns_records", entry.zone_id); - let _: Option = self - .cf_api( - &create_endpoint, - "POST", - &entry.authentication.api_token, - Some(&record), - ) - .await; + noop_reported.remove(&noop_key); + if self.dry_run { + println!("[DRY RUN] Would add new record {fqdn} -> {ip}"); + } else { + println!("Adding new record {fqdn} -> {ip}"); + let create_endpoint = + format!("zones/{}/dns_records", entry.zone_id); + let _: Option = self + .cf_api( + &create_endpoint, + "POST", + &entry.authentication.api_token, + Some(&record), + ) + .await; + } } if purge_unknown_records { @@ -640,7 +662,7 @@ mod tests { let ddns = TestDdnsClient::new(&mock_server.uri()); let config = test_config(zone_id); - ddns.commit_record("198.51.100.7", "A", &config.cloudflare, 300, false) + ddns.commit_record("198.51.100.7", "A", &config.cloudflare, 300, false, &mut std::collections::HashSet::new()) .await; } @@ -689,7 +711,7 @@ mod tests { let ddns = TestDdnsClient::new(&mock_server.uri()); let config = test_config(zone_id); - ddns.commit_record("198.51.100.7", "A", &config.cloudflare, 300, false) + ddns.commit_record("198.51.100.7", "A", &config.cloudflare, 300, false, &mut std::collections::HashSet::new()) .await; } @@ -732,7 +754,7 @@ mod tests { let ddns = TestDdnsClient::new(&mock_server.uri()); let config = test_config(zone_id); - ddns.commit_record("198.51.100.7", "A", &config.cloudflare, 300, false) + ddns.commit_record("198.51.100.7", "A", &config.cloudflare, 300, false, &mut std::collections::HashSet::new()) .await; } @@ -766,7 +788,7 @@ mod tests { let ddns = TestDdnsClient::new(&mock_server.uri()).dry_run(); let config = test_config(zone_id); - ddns.commit_record("198.51.100.7", "A", &config.cloudflare, 300, false) + ddns.commit_record("198.51.100.7", "A", &config.cloudflare, 300, false, &mut std::collections::HashSet::new()) .await; } @@ -823,7 +845,7 @@ mod tests { ip4_provider: None, ip6_provider: None, }; - ddns.commit_record("198.51.100.7", "A", &config.cloudflare, 300, true) + ddns.commit_record("198.51.100.7", "A", &config.cloudflare, 300, true, &mut std::collections::HashSet::new()) .await; } @@ -925,7 +947,7 @@ mod tests { ip6_provider: None, }; - ddns.commit_record("203.0.113.99", "A", &config.cloudflare, 300, false) + ddns.commit_record("203.0.113.99", "A", &config.cloudflare, 300, false, &mut std::collections::HashSet::new()) .await; } } diff --git a/src/notifier.rs b/src/notifier.rs index f342d26..55e26e7 100644 --- a/src/notifier.rs +++ b/src/notifier.rs @@ -406,7 +406,7 @@ fn parse_shoutrrr_url(url_str: &str) -> Result { service_type: ShoutrrrServiceType::Pushover, webhook_url: format!( "https://api.pushover.net/1/messages.json?token={}&user={}", - parts[1], parts[0] + parts[0], parts[1] ), }); } @@ -868,7 +868,7 @@ mod tests { #[test] fn test_parse_pushover() { - let result = parse_shoutrrr_url("pushover://userkey@apitoken").unwrap(); + let result = parse_shoutrrr_url("pushover://apitoken@userkey").unwrap(); assert_eq!( result.webhook_url, "https://api.pushover.net/1/messages.json?token=apitoken&user=userkey" @@ -1307,7 +1307,8 @@ mod tests { #[test] fn test_pushover_url_query_parsing() { // Verify that the pushover webhook URL format contains the right params - let service = parse_shoutrrr_url("pushover://myuser@mytoken").unwrap(); + // shoutrrr format: pushover://token@user + let service = parse_shoutrrr_url("pushover://mytoken@myuser").unwrap(); let parsed = url::Url::parse(&service.webhook_url).unwrap(); let params: std::collections::HashMap<_, _> = parsed.query_pairs().collect(); assert_eq!(params.get("token").unwrap().as_ref(), "mytoken"); diff --git a/src/updater.rs b/src/updater.rs index 960fbc5..059b8ef 100644 --- a/src/updater.rs +++ b/src/updater.rs @@ -6,7 +6,7 @@ use crate::notifier::{CompositeNotifier, Heartbeat, Message}; use crate::pp::{self, PP}; use crate::provider::IpType; use reqwest::Client; -use std::collections::HashMap; +use std::collections::{HashMap, HashSet}; use std::net::IpAddr; use std::time::Duration; @@ -18,18 +18,15 @@ pub async fn update_once( heartbeat: &Heartbeat, cf_cache: &mut CachedCloudflareFilter, ppfmt: &PP, + noop_reported: &mut HashSet, + detection_client: &Client, ) -> bool { - let detection_client = Client::builder() - .timeout(config.detection_timeout) - .build() - .unwrap_or_default(); - let mut all_ok = true; let mut messages = Vec::new(); let mut notify = false; // NEW: track meaningful events if config.legacy_mode { - all_ok = update_legacy(config, cf_cache, ppfmt).await; + all_ok = update_legacy(config, cf_cache, ppfmt, noop_reported, detection_client).await; } else { // Detect IPs for each provider let mut detected_ips: HashMap> = HashMap::new(); @@ -153,9 +150,11 @@ pub async fn update_once( ) .await; + let noop_key = format!("{domain_str}:{record_type}"); match result { SetResult::Updated => { - notify = true; // NEW + noop_reported.remove(&noop_key); + notify = true; let ip_strs: Vec = ips.iter().map(|ip| ip.to_string()).collect(); messages.push(Message::new_ok(&format!( "Updated {domain_str} -> {}", @@ -163,13 +162,18 @@ pub async fn update_once( ))); } SetResult::Failed => { - notify = true; // NEW + noop_reported.remove(&noop_key); + notify = true; all_ok = false; messages.push(Message::new_fail(&format!( "Failed to update {domain_str}" ))); } - SetResult::Noop => {} + SetResult::Noop => { + if noop_reported.insert(noop_key) { + ppfmt.infof(pp::EMOJI_SKIP, &format!("Record {domain_str} is up to date")); + } + } } } } @@ -194,32 +198,37 @@ pub async fn update_once( ) .await; + let noop_key = format!("waf:{}", waf_list.describe()); match result { SetResult::Updated => { - notify = true; // NEW + noop_reported.remove(&noop_key); + notify = true; messages.push(Message::new_ok(&format!( "Updated WAF list {}", waf_list.describe() ))); } SetResult::Failed => { - notify = true; // NEW + noop_reported.remove(&noop_key); + notify = true; all_ok = false; messages.push(Message::new_fail(&format!( "Failed to update WAF list {}", waf_list.describe() ))); } - SetResult::Noop => {} + SetResult::Noop => { + if noop_reported.insert(noop_key) { + ppfmt.infof(pp::EMOJI_SKIP, &format!("WAF list {} is up to date", waf_list.describe())); + } + } } } } - // Send heartbeat ONLY if something meaningful happened - if notify { - let heartbeat_msg = Message::merge(messages.clone()); - heartbeat.ping(&heartbeat_msg).await; - } + // Always ping heartbeat so monitors know the updater is alive + let heartbeat_msg = Message::merge(messages.clone()); + heartbeat.ping(&heartbeat_msg).await; // Send notifications ONLY when IP changed or failed if notify { @@ -236,29 +245,27 @@ pub async fn update_once( /// IP-family-bound clients (0.0.0.0 for IPv4, [::] for IPv6). This prevents the old /// wrong-family warning on dual-stack hosts and honours `ip4_provider`/`ip6_provider` /// overrides from config.json. -async fn update_legacy(config: &AppConfig, cf_cache: &mut CachedCloudflareFilter, ppfmt: &PP) -> bool { +async fn update_legacy( + config: &AppConfig, + cf_cache: &mut CachedCloudflareFilter, + ppfmt: &PP, + noop_reported: &mut HashSet, + detection_client: &Client, +) -> bool { let legacy = match &config.legacy_config { Some(l) => l, None => return false, }; - let client = Client::builder() - .timeout(config.update_timeout) - .build() - .unwrap_or_default(); - let ddns = LegacyDdnsClient { - client, + client: Client::builder() + .timeout(config.update_timeout) + .build() + .unwrap_or_default(), cf_api_base: "https://api.cloudflare.com/client/v4".to_string(), dry_run: config.dry_run, }; - // Detect IPs using the shared provider abstraction - let detection_client = Client::builder() - .timeout(config.detection_timeout) - .build() - .unwrap_or_default(); - let mut ips = HashMap::new(); for (ip_type, provider) in &config.providers { @@ -339,6 +346,7 @@ async fn update_legacy(config: &AppConfig, cf_cache: &mut CachedCloudflareFilter &legacy.cloudflare, legacy.ttl, legacy.purge_unknown_records, + noop_reported, ) .await; @@ -490,9 +498,10 @@ impl LegacyDdnsClient { config: &[LegacyCloudflareEntry], ttl: i64, purge_unknown_records: bool, + noop_reported: &mut HashSet, ) { for ip in ips.values() { - self.commit_record(ip, config, ttl, purge_unknown_records) + self.commit_record(ip, config, ttl, purge_unknown_records, noop_reported) .await; } } @@ -503,6 +512,7 @@ impl LegacyDdnsClient { config: &[LegacyCloudflareEntry], ttl: i64, purge_unknown_records: bool, + noop_reported: &mut HashSet, ) { for entry in config { let zone_resp: Option> = self @@ -578,8 +588,10 @@ impl LegacyDdnsClient { } } + let noop_key = format!("{fqdn}:{}", ip.record_type); if let Some(ref id) = identifier { if modified { + noop_reported.remove(&noop_key); if self.dry_run { println!("[DRY RUN] Would update record {fqdn} -> {}", ip.ip); } else { @@ -590,17 +602,24 @@ impl LegacyDdnsClient { .cf_api(&update_endpoint, "PUT", entry, Some(&record)) .await; } - } else if self.dry_run { - println!("[DRY RUN] Record {fqdn} is up to date ({})", ip.ip); + } else if noop_reported.insert(noop_key) { + if self.dry_run { + println!("[DRY RUN] Record {fqdn} is up to date"); + } else { + println!("Record {fqdn} is up to date"); + } } - } else if self.dry_run { - println!("[DRY RUN] Would add new record {fqdn} -> {}", ip.ip); } else { - println!("Adding new record {fqdn} -> {}", ip.ip); - let create_endpoint = format!("zones/{}/dns_records", entry.zone_id); - let _: Option = self - .cf_api(&create_endpoint, "POST", entry, Some(&record)) - .await; + noop_reported.remove(&noop_key); + if self.dry_run { + println!("[DRY RUN] Would add new record {fqdn} -> {}", ip.ip); + } else { + println!("Adding new record {fqdn} -> {}", ip.ip); + let create_endpoint = format!("zones/{}/dns_records", entry.zone_id); + let _: Option = self + .cf_api(&create_endpoint, "POST", entry, Some(&record)) + .await; + } } if purge_unknown_records { @@ -804,11 +823,12 @@ mod tests { let ppfmt = pp(); let mut cf_cache = CachedCloudflareFilter::new(); - let ok = update_once(&config, &cf, ¬ifier, &heartbeat, &mut cf_cache, &ppfmt).await; + let ok = update_once(&config, &cf, ¬ifier, &heartbeat, &mut cf_cache, &ppfmt, &mut HashSet::new(), &Client::new()).await; assert!(ok); } - /// update_once returns true (all_ok) when IP is already correct (Noop). + /// update_once returns true (all_ok) when IP is already correct (Noop), + /// and populates noop_reported so subsequent calls suppress the message. #[tokio::test] async fn test_update_once_noop_when_record_up_to_date() { let server = MockServer::start().await; @@ -853,8 +873,90 @@ mod tests { let ppfmt = pp(); let mut cf_cache = CachedCloudflareFilter::new(); - let ok = update_once(&config, &cf, ¬ifier, &heartbeat, &mut cf_cache, &ppfmt).await; + let mut noop_reported = HashSet::new(); + + // First call: noop_reported is empty, so "up to date" is reported and key is inserted + let ok = update_once(&config, &cf, ¬ifier, &heartbeat, &mut cf_cache, &ppfmt, &mut noop_reported, &Client::new()).await; assert!(ok); + assert!(noop_reported.contains("home.example.com:A"), "noop_reported should contain the domain key after first noop"); + + // Second call: noop_reported already has the key, so the message is suppressed + let ok = update_once(&config, &cf, ¬ifier, &heartbeat, &mut cf_cache, &ppfmt, &mut noop_reported, &Client::new()).await; + assert!(ok); + assert_eq!(noop_reported.len(), 1, "noop_reported should still have exactly one entry"); + } + + /// noop_reported is cleared when a record is updated, so "up to date" prints again + /// on the next noop cycle. + #[tokio::test] + async fn test_update_once_noop_reported_cleared_on_change() { + let server = MockServer::start().await; + let zone_id = "zone-abc"; + let domain = "home.example.com"; + let old_ip = "198.51.100.42"; + let new_ip = "198.51.100.99"; + + // Zone lookup + Mock::given(method("GET")) + .and(path("/zones")) + .and(query_param("name", domain)) + .respond_with( + ResponseTemplate::new(200).set_body_json(zones_response(zone_id, "example.com")), + ) + .mount(&server) + .await; + + // List existing records - record has old IP, will be updated + Mock::given(method("GET")) + .and(path_regex(format!("/zones/{zone_id}/dns_records"))) + .respond_with( + ResponseTemplate::new(200) + .set_body_json(dns_records_one("rec-1", domain, old_ip)), + ) + .mount(&server) + .await; + + // Create record (new IP doesn't match existing, so it creates + deletes stale) + Mock::given(method("POST")) + .and(path(format!("/zones/{zone_id}/dns_records"))) + .respond_with( + ResponseTemplate::new(200) + .set_body_json(dns_record_created("rec-2", domain, new_ip)), + ) + .mount(&server) + .await; + + // Delete stale record + Mock::given(method("DELETE")) + .and(path(format!("/zones/{zone_id}/dns_records/rec-1"))) + .respond_with(ResponseTemplate::new(200).set_body_json(serde_json::json!({"result": {}}))) + .mount(&server) + .await; + + let mut providers = HashMap::new(); + providers.insert( + IpType::V4, + ProviderType::Literal { + ips: vec![new_ip.parse::().unwrap()], + }, + ); + let mut domains = HashMap::new(); + domains.insert(IpType::V4, vec![domain.to_string()]); + + let config = make_config(providers, domains, vec![], false); + let cf = handle(&server.uri()); + let notifier = empty_notifier(); + let heartbeat = empty_heartbeat(); + let ppfmt = pp(); + + // Pre-populate noop_reported as if a previous cycle reported it + let mut noop_reported = HashSet::new(); + noop_reported.insert("home.example.com:A".to_string()); + + let mut cf_cache = CachedCloudflareFilter::new(); + let ok = update_once(&config, &cf, ¬ifier, &heartbeat, &mut cf_cache, &ppfmt, &mut noop_reported, &Client::new()).await; + assert!(ok); + assert!(!noop_reported.contains("home.example.com:A"), "noop_reported should be cleared after an update"); } /// update_once returns true even when IP detection yields empty (no providers configured), @@ -898,7 +1000,7 @@ mod tests { // all_ok = true because no zone-level errors occurred (empty ips just noop or warn) let mut cf_cache = CachedCloudflareFilter::new(); - let ok = update_once(&config, &cf, ¬ifier, &heartbeat, &mut cf_cache, &ppfmt).await; + let ok = update_once(&config, &cf, ¬ifier, &heartbeat, &mut cf_cache, &ppfmt, &mut HashSet::new(), &Client::new()).await; // Providers with None are not inserted in loop, so no IP detection warning is emitted, // no detected_ips entry is created, and set_ips is called with empty slice -> Noop. assert!(ok); @@ -948,7 +1050,7 @@ mod tests { let ppfmt = pp(); let mut cf_cache = CachedCloudflareFilter::new(); - let ok = update_once(&config, &cf, ¬ifier, &heartbeat, &mut cf_cache, &ppfmt).await; + let ok = update_once(&config, &cf, ¬ifier, &heartbeat, &mut cf_cache, &ppfmt, &mut HashSet::new(), &Client::new()).await; assert!(!ok, "Expected false when zone is not found"); } @@ -998,7 +1100,7 @@ mod tests { // dry_run returns Updated from set_ips (it signals intent), all_ok should be true let mut cf_cache = CachedCloudflareFilter::new(); - let ok = update_once(&config, &cf, ¬ifier, &heartbeat, &mut cf_cache, &ppfmt).await; + let ok = update_once(&config, &cf, ¬ifier, &heartbeat, &mut cf_cache, &ppfmt, &mut HashSet::new(), &Client::new()).await; assert!(ok); } @@ -1064,7 +1166,7 @@ mod tests { let ppfmt = pp(); let mut cf_cache = CachedCloudflareFilter::new(); - let ok = update_once(&config, &cf, ¬ifier, &heartbeat, &mut cf_cache, &ppfmt).await; + let ok = update_once(&config, &cf, ¬ifier, &heartbeat, &mut cf_cache, &ppfmt, &mut HashSet::new(), &Client::new()).await; assert!(ok); } @@ -1118,7 +1220,7 @@ mod tests { let ppfmt = pp(); let mut cf_cache = CachedCloudflareFilter::new(); - let ok = update_once(&config, &cf, ¬ifier, &heartbeat, &mut cf_cache, &ppfmt).await; + let ok = update_once(&config, &cf, ¬ifier, &heartbeat, &mut cf_cache, &ppfmt, &mut HashSet::new(), &Client::new()).await; assert!(ok); } @@ -1158,7 +1260,7 @@ mod tests { let ppfmt = pp(); let mut cf_cache = CachedCloudflareFilter::new(); - let ok = update_once(&config, &cf, ¬ifier, &heartbeat, &mut cf_cache, &ppfmt).await; + let ok = update_once(&config, &cf, ¬ifier, &heartbeat, &mut cf_cache, &ppfmt, &mut HashSet::new(), &Client::new()).await; assert!(!ok, "Expected false when WAF list is not found"); } @@ -1243,7 +1345,7 @@ mod tests { let ppfmt = pp(); let mut cf_cache = CachedCloudflareFilter::new(); - let ok = update_once(&config, &cf, ¬ifier, &heartbeat, &mut cf_cache, &ppfmt).await; + let ok = update_once(&config, &cf, ¬ifier, &heartbeat, &mut cf_cache, &ppfmt, &mut HashSet::new(), &Client::new()).await; assert!(ok); } @@ -1260,7 +1362,7 @@ mod tests { let ppfmt = pp(); let mut cf_cache = CachedCloudflareFilter::new(); - let ok = update_once(&config, &cf, ¬ifier, &heartbeat, &mut cf_cache, &ppfmt).await; + let ok = update_once(&config, &cf, ¬ifier, &heartbeat, &mut cf_cache, &ppfmt, &mut HashSet::new(), &Client::new()).await; assert!(ok); } @@ -1645,7 +1747,7 @@ mod tests { // set_ips with empty ips and no existing records = Noop; all_ok = true let mut cf_cache = CachedCloudflareFilter::new(); - let ok = update_once(&config, &cf, ¬ifier, &heartbeat, &mut cf_cache, &ppfmt).await; + let ok = update_once(&config, &cf, ¬ifier, &heartbeat, &mut cf_cache, &ppfmt, &mut HashSet::new(), &Client::new()).await; assert!(ok); } // ------------------------------------------------------- @@ -1850,7 +1952,7 @@ mod tests { subdomains: vec![LegacySubdomainEntry::Simple("@".to_string())], proxied: false, }]; - ddns.commit_record(&ip, &config, 300, false).await; + ddns.commit_record(&ip, &config, 300, false, &mut HashSet::new()).await; } #[tokio::test] @@ -1906,7 +2008,7 @@ mod tests { subdomains: vec![LegacySubdomainEntry::Simple("@".to_string())], proxied: false, }]; - ddns.commit_record(&ip, &config, 300, false).await; + ddns.commit_record(&ip, &config, 300, false, &mut HashSet::new()).await; } #[tokio::test] @@ -1949,7 +2051,7 @@ mod tests { proxied: false, }]; // Should not POST - ddns.commit_record(&ip, &config, 300, false).await; + ddns.commit_record(&ip, &config, 300, false, &mut HashSet::new()).await; } #[tokio::test] @@ -2002,7 +2104,7 @@ mod tests { }], proxied: false, }]; - ddns.commit_record(&ip, &config, 300, false).await; + ddns.commit_record(&ip, &config, 300, false, &mut HashSet::new()).await; } #[tokio::test] @@ -2054,7 +2156,7 @@ mod tests { subdomains: vec![LegacySubdomainEntry::Simple("@".to_string())], proxied: false, }]; - ddns.commit_record(&ip, &config, 300, true).await; + ddns.commit_record(&ip, &config, 300, true, &mut HashSet::new()).await; } #[tokio::test] @@ -2104,7 +2206,7 @@ mod tests { subdomains: vec![LegacySubdomainEntry::Simple("@".to_string())], proxied: false, }]; - ddns.update_ips(&ips, &config, 300, false).await; + ddns.update_ips(&ips, &config, 300, false, &mut HashSet::new()).await; } #[tokio::test]