isb_core/stack/controller/
dns.rs1use std::collections::BTreeSet;
6use std::net::IpAddr;
7use std::sync::Arc;
8
9use super::{Controller, StackDef, Worker};
10use crate::org::OrgId;
11
12#[derive(Debug, Clone, Default, PartialEq, Eq)]
15pub struct DnsScope {
16 pub name: String,
18 pub alias: BTreeSet<String>,
21}
22
23pub type DnsScopeFn = Arc<dyn Fn(&OrgId, &str) -> Option<DnsScope> + Send + Sync>;
27
28pub(super) fn prune(defs: &[StackDef]) {
31 for def in defs {
32 let dir = crate::discovery::org_dir(&def.org);
33 let keep: Vec<(String, String)> = defs
34 .iter()
35 .filter(|d| d.org == def.org)
36 .flat_map(|d| d.file.services.keys().map(|s| (d.name.clone(), s.clone())))
37 .collect();
38 crate::discovery::prune(&dir, &keep);
39 }
40}
41
42impl Controller {
43 pub fn set_dns_scope(&self, f: DnsScopeFn) {
47 *self.inner.dns_scope.lock().unwrap() = Some(f);
48 }
49
50 pub fn republish_dns(&self, org: &OrgId) {
55 let stacks = self.inner.stacks.lock().unwrap();
56 let ours: BTreeSet<String> = stacks
57 .iter()
58 .filter(|(_, d)| d.org == *org)
59 .map(|(q, _)| q.clone())
60 .collect();
61 drop(stacks);
62 let workers = self.inner.workers.lock().unwrap();
63 for ((q, _), w) in workers.iter() {
64 if ours.contains(q) {
65 let _slot = w.slot.lock().unwrap();
66 w.wake.notify_all();
67 }
68 }
69 }
70}
71
72impl Worker {
73 pub(super) fn sync_dns(&mut self) {
77 let dir = crate::discovery::org_dir(&self.org);
78 self.sync_dns_in(&dir);
79 }
80
81 fn dns_alias(&self) -> Option<String> {
83 let f = self.inner.dns_scope.lock().unwrap().clone()?;
84 let s = f(&self.org, &self.stack)?;
85 (s.name != self.stack && s.alias.contains(&self.service)).then_some(s.name)
86 }
87
88 pub(super) fn sync_dns_in(&mut self, dir: &std::path::Path) {
89 let mut ips: Vec<IpAddr> = self
90 .rt
91 .values()
92 .filter(|r| r.in_rotation)
93 .filter_map(|r| r.ip)
94 .collect();
95 ips.sort();
96 let fresh = self.dns_last.is_none() && ips.is_empty();
100 if fresh || !dir.is_dir() {
101 return;
102 }
103 let want = (ips, self.dns_alias());
104 if self.dns_last.as_ref() == Some(&want) {
105 return;
106 }
107 let (ips, alias) = &want;
108 match crate::discovery::publish(
109 dir,
110 &self.org,
111 &self.stack,
112 &self.service,
113 ips,
114 alias.as_deref(),
115 ) {
116 Ok(()) => {
117 self.dns_last = Some(want);
118 self.dns_error = None;
119 }
120 Err(e) => {
121 let e = e.to_string();
122 if self.dns_error.as_deref() != Some(&e) {
123 self.event(
124 "warn",
125 None,
126 &format!("cannot publish the service name: {e}"),
127 );
128 self.dns_error = Some(e);
129 }
130 }
131 }
132 }
133
134 pub(super) fn unpublish_dns(&mut self) {
136 if let Some(dir) = Some(crate::discovery::org_dir(&self.org)).filter(|d| d.is_dir()) {
137 if let Err(e) =
138 crate::discovery::publish(&dir, &self.org, &self.stack, &self.service, &[], None)
139 {
140 self.log(&format!("cannot remove the service name: {e}"));
141 }
142 }
143 self.dns_last = Some((Vec::new(), None));
144 }
145}