use Ipv4Addr type for IP addresses

This commit is contained in:
Jorge Aparicio 2024-02-05 14:42:20 +01:00
parent d79581bdcc
commit c7e0580c7a
3 changed files with 56 additions and 46 deletions

View File

@ -1,4 +1,4 @@
use std::process::Child; use std::{net::Ipv4Addr, process::Child};
use minijinja::{context, Environment}; use minijinja::{context, Environment};
@ -34,8 +34,8 @@ impl AuthoritativeNameServer {
Ok(Self { child, container }) Ok(Self { child, container })
} }
pub fn ip_addr(&self) -> Result<String> { pub fn ipv4_addr(&self) -> Ipv4Addr {
self.container.ip_addr() self.container.ipv4_addr()
} }
} }
@ -84,7 +84,7 @@ mod tests {
#[test] #[test]
fn tld_setup() -> Result<()> { fn tld_setup() -> Result<()> {
let tld_ns = AuthoritativeNameServer::start(Domain::Tld { domain: "com." })?; let tld_ns = AuthoritativeNameServer::start(Domain::Tld { domain: "com." })?;
let ip_addr = tld_ns.ip_addr()?; let ip_addr = tld_ns.ipv4_addr();
let client = Container::run()?; let client = Container::run()?;
let output = client.exec(&["dig", &format!("@{ip_addr}"), "SOA", "com."])?; let output = client.exec(&["dig", &format!("@{ip_addr}"), "SOA", "com."])?;
@ -100,7 +100,7 @@ mod tests {
#[test] #[test]
fn root_setup() -> Result<()> { fn root_setup() -> Result<()> {
let root_ns = AuthoritativeNameServer::start(Domain::Root)?; let root_ns = AuthoritativeNameServer::start(Domain::Root)?;
let ip_addr = root_ns.ip_addr()?; let ip_addr = root_ns.ipv4_addr();
let client = Container::run()?; let client = Container::run()?;
let output = client.exec(&["dig", &format!("@{ip_addr}"), "SOA", "."])?; let output = client.exec(&["dig", &format!("@{ip_addr}"), "SOA", "."])?;

View File

@ -1,4 +1,6 @@
use core::str;
use std::fs; use std::fs;
use std::net::Ipv4Addr;
use std::path::Path; use std::path::Path;
use std::process::{self, Child, Output}; use std::process::{self, Child, Output};
use std::process::{Command, Stdio}; use std::process::{Command, Stdio};
@ -10,8 +12,10 @@ use tempfile::NamedTempFile;
use crate::Result; use crate::Result;
pub struct Container { pub struct Container {
id: String,
_name: String, _name: String,
id: String,
// TODO probably also want the IPv6 address
ipv4_addr: Ipv4Addr,
} }
impl Container { impl Container {
@ -61,15 +65,16 @@ impl Container {
return Err(format!("`{command:?}` failed").into()); return Err(format!("`{command:?}` failed").into());
} }
let id = core::str::from_utf8(&output.stdout)?.trim().to_string(); let id = str::from_utf8(&output.stdout)?.trim().to_string();
dbg!(&id); dbg!(&id);
let container = Self {
let ipv4_addr = get_ipv4_addr(&id)?;
Ok(Self {
id, id,
_name: container_name, _name: container_name,
}; ipv4_addr,
dbg!(container.ip_addr()?); })
Ok(container)
} }
pub fn cp(&self, path_in_container: &str, file_contents: &str, chmod: &str) -> Result<()> { pub fn cp(&self, path_in_container: &str, file_contents: &str, chmod: &str) -> Result<()> {
@ -114,29 +119,33 @@ impl Container {
Ok(child) Ok(child)
} }
// TODO cache this to avoid calling `docker inspect` every time pub fn ipv4_addr(&self) -> Ipv4Addr {
pub fn ip_addr(&self) -> Result<String> { self.ipv4_addr
let mut command = Command::new("docker");
command
.args([
"inspect",
"-f",
"{{range.NetworkSettings.Networks}}{{.IPAddress}}{{end}}",
])
.arg(&self.id);
let output = command.output()?;
if !output.status.success() {
return Err(format!("`{command:?}` failed").into());
}
let ip_addr = core::str::from_utf8(&output.stdout)?.trim().to_string();
dbg!(&ip_addr);
Ok(ip_addr)
} }
} }
// TODO cache this to avoid calling `docker inspect` every time
fn get_ipv4_addr(container_id: &str) -> Result<Ipv4Addr> {
let mut command = Command::new("docker");
command
.args([
"inspect",
"-f",
"{{range.NetworkSettings.Networks}}{{.IPAddress}}{{end}}",
])
.arg(container_id);
let output = command.output()?;
if !output.status.success() {
return Err(format!("`{command:?}` failed").into());
}
let ipv4_addr = str::from_utf8(&output.stdout)?.trim().to_string();
dbg!(&ipv4_addr);
Ok(ipv4_addr.parse()?)
}
// ensure the container gets deleted // ensure the container gets deleted
impl Drop for Container { impl Drop for Container {
fn drop(&mut self) { fn drop(&mut self) {
@ -152,8 +161,6 @@ impl Drop for Container {
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use std::net::Ipv4Addr;
use crate::CHMOD_RW_EVERYONE; use crate::CHMOD_RW_EVERYONE;
use super::*; use super::*;
@ -169,11 +176,12 @@ mod tests {
} }
#[test] #[test]
fn ip_addr_works() -> Result<()> { fn ipv4_addr_works() -> Result<()> {
let container = Container::run()?; let container = Container::run()?;
let ipv4_addr = container.ipv4_addr();
let ip_addr = container.ip_addr()?; let output = container.exec(&["ping", "-c1", &format!("{ipv4_addr}")])?;
assert!(ip_addr.parse::<Ipv4Addr>().is_ok()); assert!(output.status.success());
Ok(()) Ok(())
} }

View File

@ -1,3 +1,4 @@
use std::net::Ipv4Addr;
use std::process::Child; use std::process::Child;
use serde::Serialize; use serde::Serialize;
@ -13,7 +14,7 @@ pub struct RecursiveResolver {
#[derive(Serialize)] #[derive(Serialize)]
pub struct RootServer { pub struct RootServer {
name: String, name: String,
ip_addr: String, ip_addr: Ipv4Addr,
} }
fn root_hints(roots: &[RootServer]) -> String { fn root_hints(roots: &[RootServer]) -> String {
@ -38,8 +39,8 @@ impl RecursiveResolver {
Ok(Self { child, container }) Ok(Self { child, container })
} }
pub fn ip_addr(&self) -> Result<String> { pub fn ipv4_addr(&self) -> Ipv4Addr {
self.container.ip_addr() self.container.ipv4_addr()
} }
} }
@ -56,14 +57,15 @@ mod tests {
use super::*; use super::*;
#[test] #[test]
#[ignore = "FIXME"]
fn can_resolve() -> Result<()> { fn can_resolve() -> Result<()> {
let root_ns = AuthoritativeNameServer::start(crate::Domain::Root)?; let root_ns = AuthoritativeNameServer::start(crate::Domain::Root)?;
let roots = &[RootServer { let roots = &[RootServer {
name: "my.root-server.com".to_string(), name: "my.root-server.com".to_string(),
ip_addr: root_ns.ip_addr()?, ip_addr: root_ns.ipv4_addr(),
}]; }];
let resolver = RecursiveResolver::start(roots)?; let resolver = RecursiveResolver::start(roots)?;
let resolver_ip_addr = resolver.ip_addr()?; let resolver_ip_addr = resolver.ipv4_addr();
let container = Container::run()?; let container = Container::run()?;
let output = container.exec(&["dig", &format!("@{}", resolver_ip_addr), "example.com"])?; let output = container.exec(&["dig", &format!("@{}", resolver_ip_addr), "example.com"])?;
@ -77,15 +79,15 @@ mod tests {
#[test] #[test]
fn root_hints_template_works() { fn root_hints_template_works() {
let expected = [ let expected = [
("a.root-server.com", "172.17.0.1"), ("a.root-server.com", Ipv4Addr::new(172, 17, 0, 1)),
("b.root-server.com", "172.17.0.2"), ("b.root-server.com", Ipv4Addr::new(172, 17, 0, 2)),
]; ];
let roots = expected let roots = expected
.iter() .iter()
.map(|(ns_name, ip_addr)| RootServer { .map(|(ns_name, ip_addr)| RootServer {
name: ns_name.to_string(), name: ns_name.to_string(),
ip_addr: ip_addr.to_string(), ip_addr: *ip_addr,
}) })
.collect::<Vec<_>>(); .collect::<Vec<_>>();
@ -117,7 +119,7 @@ mod tests {
.unwrap(); .unwrap();
assert_eq!(expected_ns_name, ns_name); assert_eq!(expected_ns_name, ns_name);
assert_eq!("A", record_type); assert_eq!("A", record_type);
assert_eq!(expected_ip_addr, ip_addr); assert_eq!(expected_ip_addr.to_string(), ip_addr);
} }
} }
} }