refactor: extract maybe_update_primary for testable upstream re-detection
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -85,6 +85,20 @@ impl UpstreamPool {
|
|||||||
self.primary = primary;
|
self.primary = primary;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Update the primary upstream if `new_addr` (parsed with `port`) differs
|
||||||
|
/// from the current preferred upstream. Returns `true` if the pool changed.
|
||||||
|
pub fn maybe_update_primary(&mut self, new_addr: &str, port: u16) -> bool {
|
||||||
|
let Ok(new_sock) = format!("{}:{}", new_addr, port).parse::<SocketAddr>() else {
|
||||||
|
return false;
|
||||||
|
};
|
||||||
|
let new_upstream = Upstream::Udp(new_sock);
|
||||||
|
if self.preferred() == Some(&new_upstream) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
self.primary = vec![new_upstream];
|
||||||
|
true
|
||||||
|
}
|
||||||
|
|
||||||
pub fn label(&self) -> String {
|
pub fn label(&self) -> String {
|
||||||
match self.preferred() {
|
match self.preferred() {
|
||||||
Some(u) => {
|
Some(u) => {
|
||||||
@@ -469,4 +483,33 @@ mod tests {
|
|||||||
assert_eq!(result.header.id, 0xABCD);
|
assert_eq!(result.header.id, 0xABCD);
|
||||||
assert_eq!(result.answers.len(), 1);
|
assert_eq!(result.answers.len(), 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn maybe_update_primary_swaps_when_different() {
|
||||||
|
let mut pool = UpstreamPool::new(
|
||||||
|
vec![Upstream::Udp("1.2.3.4:53".parse().unwrap())],
|
||||||
|
vec![Upstream::Udp("8.8.8.8:53".parse().unwrap())],
|
||||||
|
);
|
||||||
|
assert!(pool.maybe_update_primary("5.6.7.8", 53));
|
||||||
|
assert_eq!(pool.preferred().unwrap().to_string(), "5.6.7.8:53");
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn maybe_update_primary_noop_when_same() {
|
||||||
|
let mut pool = UpstreamPool::new(
|
||||||
|
vec![Upstream::Udp("1.2.3.4:53".parse().unwrap())],
|
||||||
|
vec![],
|
||||||
|
);
|
||||||
|
assert!(!pool.maybe_update_primary("1.2.3.4", 53));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn maybe_update_primary_rejects_invalid_addr() {
|
||||||
|
let mut pool = UpstreamPool::new(
|
||||||
|
vec![Upstream::Udp("1.2.3.4:53".parse().unwrap())],
|
||||||
|
vec![],
|
||||||
|
);
|
||||||
|
assert!(!pool.maybe_update_primary("not-an-ip", 53));
|
||||||
|
assert_eq!(pool.preferred().unwrap().to_string(), "1.2.3.4:53");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
15
src/main.rs
15
src/main.rs
@@ -610,17 +610,10 @@ async fn network_watch_loop(ctx: Arc<numa::ctx::ServerCtx>) {
|
|||||||
.default_upstream
|
.default_upstream
|
||||||
.or_else(numa::system_dns::detect_dhcp_dns)
|
.or_else(numa::system_dns::detect_dhcp_dns)
|
||||||
.unwrap_or_else(|| QUAD9_IP.to_string());
|
.unwrap_or_else(|| QUAD9_IP.to_string());
|
||||||
if let Ok(new_sock) =
|
let mut pool = ctx.upstream_pool.lock().unwrap();
|
||||||
format!("{}:{}", new_addr, ctx.upstream_port).parse::<SocketAddr>()
|
if pool.maybe_update_primary(&new_addr, ctx.upstream_port) {
|
||||||
{
|
info!("upstream changed → {}", pool.label());
|
||||||
let new_upstream = Upstream::Udp(new_sock);
|
changed = true;
|
||||||
let mut pool = ctx.upstream_pool.lock().unwrap();
|
|
||||||
let current = pool.preferred().cloned();
|
|
||||||
if current.as_ref() != Some(&new_upstream) {
|
|
||||||
info!("upstream changed: {} → {}", pool.label(), new_upstream);
|
|
||||||
pool.set_primary(vec![new_upstream]);
|
|
||||||
changed = true;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user