add API to gracefully terminate name server & resolver
This commit is contained in:
parent
3e78cfa30e
commit
095b68b887
|
@ -137,11 +137,12 @@ impl Container {
|
|||
|
||||
pub fn spawn(&self, cmd: &[&str]) -> Result<Child> {
|
||||
let mut command = Command::new("docker");
|
||||
command.stdout(Stdio::piped()).stderr(Stdio::piped());
|
||||
command.args(["exec", "-t", &self.inner.id]).args(cmd);
|
||||
|
||||
let inner = command.spawn()?;
|
||||
Ok(Child {
|
||||
inner,
|
||||
inner: Some(inner),
|
||||
_container: self.inner.clone(),
|
||||
})
|
||||
}
|
||||
|
@ -164,13 +165,22 @@ struct Inner {
|
|||
// runs inside of, to prevent the scenario of the container being destroyed _before_
|
||||
// the child is killed
|
||||
pub struct Child {
|
||||
inner: process::Child,
|
||||
inner: Option<process::Child>,
|
||||
_container: Arc<Inner>,
|
||||
}
|
||||
|
||||
impl Child {
|
||||
pub fn wait(mut self) -> Result<Output> {
|
||||
let output = self.inner.take().expect("unreachable").wait_with_output()?;
|
||||
output.try_into()
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for Child {
|
||||
fn drop(&mut self) {
|
||||
let _ = self.inner.kill();
|
||||
if let Some(mut inner) = self.inner.take() {
|
||||
let _ = inner.kill();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -152,7 +152,7 @@ impl<'a> NameServer<'a, Stopped> {
|
|||
Ok(NameServer {
|
||||
container,
|
||||
zone_file,
|
||||
state: Running { _child: child },
|
||||
state: Running { child },
|
||||
})
|
||||
}
|
||||
}
|
||||
|
@ -188,7 +188,7 @@ impl<'a> NameServer<'a, Signed> {
|
|||
Ok(NameServer {
|
||||
container,
|
||||
zone_file,
|
||||
state: Running { _child: child },
|
||||
state: Running { child },
|
||||
})
|
||||
}
|
||||
|
||||
|
@ -209,6 +209,31 @@ impl<'a> NameServer<'a, Signed> {
|
|||
}
|
||||
}
|
||||
|
||||
impl<'a> NameServer<'a, Running> {
|
||||
/// gracefully terminates the name server collecting all logs
|
||||
pub fn terminate(self) -> Result<String> {
|
||||
let pidfile = "/run/nsd/nsd.pid";
|
||||
// if `terminate` is called right after `start` NSD may not have had the chance to create
|
||||
// the PID file so if it doesn't exist wait for a bit before invoking `kill`
|
||||
let kill = format!(
|
||||
"test -f {pidfile} || sleep 1
|
||||
kill -TERM $(cat {pidfile})"
|
||||
);
|
||||
self.container.status_ok(&["sh", "-c", &kill])?;
|
||||
let output = self.state.child.wait()?;
|
||||
|
||||
if !output.status.success() {
|
||||
return Err("could not terminate the `unbound` process".into());
|
||||
}
|
||||
|
||||
assert!(
|
||||
output.stderr.is_empty(),
|
||||
"stderr should be returned if not empty"
|
||||
);
|
||||
Ok(output.stdout)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, S> NameServer<'a, S> {
|
||||
pub fn ipv4_addr(&self) -> Ipv4Addr {
|
||||
self.container.ipv4_addr()
|
||||
|
@ -238,7 +263,7 @@ pub struct Signed {
|
|||
}
|
||||
|
||||
pub struct Running {
|
||||
_child: Child,
|
||||
child: Child,
|
||||
}
|
||||
|
||||
fn primary_ns(ns_count: usize) -> FQDN<'static> {
|
||||
|
@ -345,4 +370,14 @@ mod tests {
|
|||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn terminate_works() -> Result<()> {
|
||||
let ns = NameServer::new(FQDN::ROOT)?.start()?;
|
||||
let logs = ns.terminate()?;
|
||||
|
||||
assert!(logs.contains("nsd starting"));
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
|
|
@ -7,7 +7,7 @@ use crate::Result;
|
|||
|
||||
pub struct RecursiveResolver {
|
||||
container: Container,
|
||||
_child: Child,
|
||||
child: Child,
|
||||
}
|
||||
|
||||
impl RecursiveResolver {
|
||||
|
@ -37,15 +37,33 @@ impl RecursiveResolver {
|
|||
|
||||
let child = container.spawn(&["unbound", "-d"])?;
|
||||
|
||||
Ok(Self {
|
||||
_child: child,
|
||||
container,
|
||||
})
|
||||
Ok(Self { child, container })
|
||||
}
|
||||
|
||||
pub fn ipv4_addr(&self) -> Ipv4Addr {
|
||||
self.container.ipv4_addr()
|
||||
}
|
||||
|
||||
/// gracefully terminates the name server collecting all logs
|
||||
pub fn terminate(self) -> Result<String> {
|
||||
let pidfile = "/run/unbound.pid";
|
||||
let kill = format!(
|
||||
"test -f {pidfile} || sleep 1
|
||||
kill -TERM $(cat {pidfile})"
|
||||
);
|
||||
self.container.status_ok(&["sh", "-c", &kill])?;
|
||||
let output = self.child.wait()?;
|
||||
|
||||
if !output.status.success() {
|
||||
return Err("could not terminate the `unbound` process".into());
|
||||
}
|
||||
|
||||
assert!(
|
||||
output.stderr.is_empty(),
|
||||
"stderr should be returned if not empty"
|
||||
);
|
||||
Ok(output.stdout)
|
||||
}
|
||||
}
|
||||
|
||||
fn unbound_conf(use_dnssec: bool) -> String {
|
||||
|
@ -229,4 +247,15 @@ mod tests {
|
|||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn terminate_works() -> Result<()> {
|
||||
let resolver = RecursiveResolver::start(&[], &[])?;
|
||||
let logs = resolver.terminate()?;
|
||||
|
||||
eprintln!("{logs}");
|
||||
assert!(logs.contains("start of service"));
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue
Block a user