Kill running acceptance test nodes on error (#1770)
And show the output from those nodes. These changes help us diagnose errors that happen while one or more acceptance test nodes are running.
This commit is contained in:
parent
e61b5e50a2
commit
bcb9cead88
|
|
@ -320,6 +320,34 @@ impl<T> TestChild<T> {
|
||||||
Err(report)
|
Err(report)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Kill `child`, wait for its output, and use that output as the context for
|
||||||
|
/// an error report based on `error`.
|
||||||
|
#[instrument(skip(self, result))]
|
||||||
|
pub fn kill_on_error<V, E>(mut self, result: Result<V, E>) -> Result<(V, Self), Report>
|
||||||
|
where
|
||||||
|
E: Into<Report> + Send + Sync + 'static,
|
||||||
|
{
|
||||||
|
let mut error: Report = match result {
|
||||||
|
Ok(success) => return Ok((success, self)),
|
||||||
|
Err(error) => error.into(),
|
||||||
|
};
|
||||||
|
|
||||||
|
if self.is_running() {
|
||||||
|
let kill_res = self.kill();
|
||||||
|
if let Err(kill_err) = kill_res {
|
||||||
|
error = error.wrap_err(kill_err);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let output_res = self.wait_with_output();
|
||||||
|
let error = match output_res {
|
||||||
|
Err(output_err) => error.wrap_err(output_err),
|
||||||
|
Ok(output) => error.context_from(&output),
|
||||||
|
};
|
||||||
|
|
||||||
|
Err(error)
|
||||||
|
}
|
||||||
|
|
||||||
fn past_deadline(&self) -> bool {
|
fn past_deadline(&self) -> bool {
|
||||||
self.deadline
|
self.deadline
|
||||||
.map(|deadline| Instant::now() > deadline)
|
.map(|deadline| Instant::now() > deadline)
|
||||||
|
|
|
||||||
|
|
@ -951,7 +951,7 @@ async fn metrics_endpoint() -> Result<()> {
|
||||||
config.metrics.endpoint_addr = Some(endpoint.parse().unwrap());
|
config.metrics.endpoint_addr = Some(endpoint.parse().unwrap());
|
||||||
|
|
||||||
let dir = TempDir::new("zebrad_tests")?.with_config(&mut config)?;
|
let dir = TempDir::new("zebrad_tests")?.with_config(&mut config)?;
|
||||||
let mut child = dir.spawn_child(&["start"])?;
|
let child = dir.spawn_child(&["start"])?;
|
||||||
|
|
||||||
// Run `zebrad` for a few seconds before testing the endpoint
|
// Run `zebrad` for a few seconds before testing the endpoint
|
||||||
// Since we're an async function, we have to use a sleep future, not thread sleep.
|
// Since we're an async function, we have to use a sleep future, not thread sleep.
|
||||||
|
|
@ -961,9 +961,11 @@ async fn metrics_endpoint() -> Result<()> {
|
||||||
let client = Client::new();
|
let client = Client::new();
|
||||||
|
|
||||||
// Test metrics endpoint
|
// Test metrics endpoint
|
||||||
let res = client.get(url.try_into().expect("url is valid")).await?;
|
let res = client.get(url.try_into().expect("url is valid")).await;
|
||||||
|
let (res, child) = child.kill_on_error(res)?;
|
||||||
assert!(res.status().is_success());
|
assert!(res.status().is_success());
|
||||||
let body = hyper::body::to_bytes(res).await?;
|
let body = hyper::body::to_bytes(res).await;
|
||||||
|
let (body, mut child) = child.kill_on_error(body)?;
|
||||||
assert!(
|
assert!(
|
||||||
std::str::from_utf8(&body)
|
std::str::from_utf8(&body)
|
||||||
.expect("metrics response is valid UTF-8")
|
.expect("metrics response is valid UTF-8")
|
||||||
|
|
@ -1004,7 +1006,7 @@ async fn tracing_endpoint() -> Result<()> {
|
||||||
config.tracing.endpoint_addr = Some(endpoint.parse().unwrap());
|
config.tracing.endpoint_addr = Some(endpoint.parse().unwrap());
|
||||||
|
|
||||||
let dir = TempDir::new("zebrad_tests")?.with_config(&mut config)?;
|
let dir = TempDir::new("zebrad_tests")?.with_config(&mut config)?;
|
||||||
let mut child = dir.spawn_child(&["start"])?;
|
let child = dir.spawn_child(&["start"])?;
|
||||||
|
|
||||||
// Run `zebrad` for a few seconds before testing the endpoint
|
// Run `zebrad` for a few seconds before testing the endpoint
|
||||||
// Since we're an async function, we have to use a sleep future, not thread sleep.
|
// Since we're an async function, we have to use a sleep future, not thread sleep.
|
||||||
|
|
@ -1016,9 +1018,11 @@ async fn tracing_endpoint() -> Result<()> {
|
||||||
// Test tracing endpoint
|
// Test tracing endpoint
|
||||||
let res = client
|
let res = client
|
||||||
.get(url_default.try_into().expect("url_default is valid"))
|
.get(url_default.try_into().expect("url_default is valid"))
|
||||||
.await?;
|
.await;
|
||||||
|
let (res, child) = child.kill_on_error(res)?;
|
||||||
assert!(res.status().is_success());
|
assert!(res.status().is_success());
|
||||||
let body = hyper::body::to_bytes(res).await?;
|
let body = hyper::body::to_bytes(res).await;
|
||||||
|
let (body, child) = child.kill_on_error(body)?;
|
||||||
assert!(std::str::from_utf8(&body).unwrap().contains(
|
assert!(std::str::from_utf8(&body).unwrap().contains(
|
||||||
"This HTTP endpoint allows dynamic control of the filter applied to\ntracing events."
|
"This HTTP endpoint allows dynamic control of the filter applied to\ntracing events."
|
||||||
));
|
));
|
||||||
|
|
@ -1027,13 +1031,16 @@ async fn tracing_endpoint() -> Result<()> {
|
||||||
let request = Request::post(url_filter.clone())
|
let request = Request::post(url_filter.clone())
|
||||||
.body(Body::from("zebrad=debug"))
|
.body(Body::from("zebrad=debug"))
|
||||||
.unwrap();
|
.unwrap();
|
||||||
let _post = client.request(request).await?;
|
let post = client.request(request).await;
|
||||||
|
let (_post, child) = child.kill_on_error(post)?;
|
||||||
|
|
||||||
let tracing_res = client
|
let tracing_res = client
|
||||||
.get(url_filter.try_into().expect("url_filter is valid"))
|
.get(url_filter.try_into().expect("url_filter is valid"))
|
||||||
.await?;
|
.await;
|
||||||
|
let (tracing_res, child) = child.kill_on_error(tracing_res)?;
|
||||||
assert!(tracing_res.status().is_success());
|
assert!(tracing_res.status().is_success());
|
||||||
let tracing_body = hyper::body::to_bytes(tracing_res).await?;
|
let tracing_body = hyper::body::to_bytes(tracing_res).await;
|
||||||
|
let (tracing_body, mut child) = child.kill_on_error(tracing_body)?;
|
||||||
assert!(std::str::from_utf8(&tracing_body)
|
assert!(std::str::from_utf8(&tracing_body)
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.contains("zebrad=debug"));
|
.contains("zebrad=debug"));
|
||||||
|
|
@ -1201,25 +1208,35 @@ where
|
||||||
}
|
}
|
||||||
|
|
||||||
// Start the first node
|
// Start the first node
|
||||||
let mut node1 = first_dir.spawn_child(&["start"])?;
|
let node1 = first_dir.spawn_child(&["start"])?;
|
||||||
|
|
||||||
// Wait a bit to spawn the second node, we want the first fully started.
|
// Wait a bit to spawn the second node, we want the first fully started.
|
||||||
std::thread::sleep(LAUNCH_DELAY);
|
std::thread::sleep(LAUNCH_DELAY);
|
||||||
|
|
||||||
// Spawn the second node
|
// Spawn the second node
|
||||||
let node2 = second_dir.spawn_child(&["start"])?;
|
let node2 = second_dir.spawn_child(&["start"]);
|
||||||
|
let (node2, mut node1) = node1.kill_on_error(node2)?;
|
||||||
|
|
||||||
// Wait a few seconds and kill first node.
|
// Wait a few seconds and kill first node.
|
||||||
// Second node is terminated by panic, no need to kill.
|
// Second node is terminated by panic, no need to kill.
|
||||||
std::thread::sleep(LAUNCH_DELAY);
|
std::thread::sleep(LAUNCH_DELAY);
|
||||||
node1.kill()?;
|
let node1_kill_res = node1.kill();
|
||||||
|
let (_, node2) = node2.kill_on_error(node1_kill_res)?;
|
||||||
|
|
||||||
// In node1 we want to check for the success regex
|
// In node1 we want to check for the success regex
|
||||||
let output1 = node1.wait_with_output()?;
|
// If there are any errors, we also want to print the node2 output.
|
||||||
output1.stdout_contains(first_stdout_regex)?;
|
let output1 = node1.wait_with_output();
|
||||||
output1
|
let (output1, node2) = node2.kill_on_error(output1)?;
|
||||||
|
let res1 = output1.stdout_contains(first_stdout_regex);
|
||||||
|
let (_, node2) = node2.kill_on_error(res1)?;
|
||||||
|
let res1 = output1
|
||||||
.assert_was_killed()
|
.assert_was_killed()
|
||||||
.wrap_err("Possible port conflict. Are there other acceptance tests running?")?;
|
.wrap_err("Possible port conflict. Are there other acceptance tests running?");
|
||||||
|
let (_, mut node2) = node2.kill_on_error(res1)?;
|
||||||
|
|
||||||
|
// node2 should have panicked due to a conflict. Kill it here
|
||||||
|
// anyway, so it doesn't outlive the test on error.
|
||||||
|
let _ = node2.kill();
|
||||||
|
|
||||||
// In the second node we look for the conflict regex
|
// In the second node we look for the conflict regex
|
||||||
let output2 = node2.wait_with_output()?;
|
let output2 = node2.wait_with_output()?;
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue