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> {
|
pub fn spawn(&self, cmd: &[&str]) -> Result<Child> {
|
||||||
let mut command = Command::new("docker");
|
let mut command = Command::new("docker");
|
||||||
|
command.stdout(Stdio::piped()).stderr(Stdio::piped());
|
||||||
command.args(["exec", "-t", &self.inner.id]).args(cmd);
|
command.args(["exec", "-t", &self.inner.id]).args(cmd);
|
||||||
|
|
||||||
let inner = command.spawn()?;
|
let inner = command.spawn()?;
|
||||||
Ok(Child {
|
Ok(Child {
|
||||||
inner,
|
inner: Some(inner),
|
||||||
_container: self.inner.clone(),
|
_container: self.inner.clone(),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@ -164,13 +165,22 @@ struct Inner {
|
|||||||
// runs inside of, to prevent the scenario of the container being destroyed _before_
|
// runs inside of, to prevent the scenario of the container being destroyed _before_
|
||||||
// the child is killed
|
// the child is killed
|
||||||
pub struct Child {
|
pub struct Child {
|
||||||
inner: process::Child,
|
inner: Option<process::Child>,
|
||||||
_container: Arc<Inner>,
|
_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 {
|
impl Drop for Child {
|
||||||
fn drop(&mut self) {
|
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 {
|
Ok(NameServer {
|
||||||
container,
|
container,
|
||||||
zone_file,
|
zone_file,
|
||||||
state: Running { _child: child },
|
state: Running { child },
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -188,7 +188,7 @@ impl<'a> NameServer<'a, Signed> {
|
|||||||
Ok(NameServer {
|
Ok(NameServer {
|
||||||
container,
|
container,
|
||||||
zone_file,
|
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> {
|
impl<'a, S> NameServer<'a, S> {
|
||||||
pub fn ipv4_addr(&self) -> Ipv4Addr {
|
pub fn ipv4_addr(&self) -> Ipv4Addr {
|
||||||
self.container.ipv4_addr()
|
self.container.ipv4_addr()
|
||||||
@ -238,7 +263,7 @@ pub struct Signed {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub struct Running {
|
pub struct Running {
|
||||||
_child: Child,
|
child: Child,
|
||||||
}
|
}
|
||||||
|
|
||||||
fn primary_ns(ns_count: usize) -> FQDN<'static> {
|
fn primary_ns(ns_count: usize) -> FQDN<'static> {
|
||||||
@ -345,4 +370,14 @@ mod tests {
|
|||||||
|
|
||||||
Ok(())
|
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 {
|
pub struct RecursiveResolver {
|
||||||
container: Container,
|
container: Container,
|
||||||
_child: Child,
|
child: Child,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl RecursiveResolver {
|
impl RecursiveResolver {
|
||||||
@ -37,15 +37,33 @@ impl RecursiveResolver {
|
|||||||
|
|
||||||
let child = container.spawn(&["unbound", "-d"])?;
|
let child = container.spawn(&["unbound", "-d"])?;
|
||||||
|
|
||||||
Ok(Self {
|
Ok(Self { child, container })
|
||||||
_child: child,
|
|
||||||
container,
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn ipv4_addr(&self) -> Ipv4Addr {
|
pub fn ipv4_addr(&self) -> Ipv4Addr {
|
||||||
self.container.ipv4_addr()
|
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 {
|
fn unbound_conf(use_dnssec: bool) -> String {
|
||||||
@ -229,4 +247,15 @@ mod tests {
|
|||||||
|
|
||||||
Ok(())
|
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