use std::net::IpAddr;
use arcbox_engine::machine::machine_hostname;
use super::Runtime;
pub(super) const MACHINE_DNS_OWNER_PREFIX: &str = "machine:";
impl Runtime {
pub async fn register_machine_dns(&self, machine: &str, ip: IpAddr) {
let hostname = match machine_hostname(machine) {
Ok(hostname) => hostname,
Err(e) => {
tracing::warn!(machine, error = %e, "machine has no publishable hostname");
return;
}
};
if let Some(other) = self.machine_holding_hostname(machine, &hostname).await {
tracing::warn!(
machine,
hostname,
other_machine = other,
"two machines share one hostname; the name now answers for the one that \
started last — rename one of them"
);
}
self.register_dns(&Self::machine_dns_owner(machine), &[hostname], ip)
.await;
}
pub(super) async fn machine_holding_hostname(
&self,
machine: &str,
hostname: &str,
) -> Option<String> {
let own = Self::machine_dns_owner(machine);
self.dns_entries
.read()
.await
.iter()
.filter(|(owner, _)| **owner != own)
.find(|(owner, entry)| {
owner.starts_with(MACHINE_DNS_OWNER_PREFIX)
&& entry.hostnames.iter().any(|name| name == hostname)
})
.and_then(|(owner, _)| owner.strip_prefix(MACHINE_DNS_OWNER_PREFIX))
.map(str::to_owned)
}
pub async fn deregister_machine_dns(&self, machine: &str) {
self.deregister_dns_by_id(&Self::machine_dns_owner(machine))
.await;
}
pub async fn registered_machine_dns_names(&self) -> Vec<String> {
self.dns_entries
.read()
.await
.keys()
.filter_map(|owner| owner.strip_prefix(MACHINE_DNS_OWNER_PREFIX))
.map(str::to_owned)
.collect()
}
#[must_use]
pub fn machine_dns_name(&self, machine: &str) -> Option<String> {
let hostname = machine_hostname(machine).ok()?;
self.network_manager
.dns_domain()
.map(|domain| format!("{hostname}.{domain}"))
}
fn machine_dns_owner(machine: &str) -> String {
format!("{MACHINE_DNS_OWNER_PREFIX}{machine}")
}
}