Skip to content

Commit 0f573b6

Browse files
committed
Merge #648: E2E tests: Improve E2E tests runner
f18e68c fix: tracker checker return error code when it fails (Jose Celano) e5cd81b refactor: [#647] E2E tests. Extract function (Jose Celano) 68f71be refactor: [#647] E2E tests. Extract strcut TrackerContainer (Jose Celano) ddad4a4 ci: [#647] E2E tests. Make sure there are not panics in logs (Jose Celano) 670927c ci: [#647] E2E tests. Make sure we run at least one service per type (Jose Celano) 0afab09 refactor: [#647] extract strcut RunOptions (Jose Celano) Pull request description: - [x] Check that we run all services (UDP, HTTP, HealCheck API). - [x] Check that the logs don't contain any panics. ACKs for top commit: josecelano: ACK f18e68c Tree-SHA512: 02b8561a08c92220a6a558a1cc0007da31be394236f7ec6b4cc05d2404c3755e45bff0f7f04aed686d98950d45ce0577e5ff915a874ac3ce7239d7b81cf477e0
2 parents 14f88e9 + f18e68c commit 0f573b6

File tree

7 files changed

+312
-82
lines changed

7 files changed

+312
-82
lines changed

src/bin/tracker_checker.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -7,5 +7,5 @@ use torrust_tracker::checker::app;
77

88
#[tokio::main]
99
async fn main() {
10-
app::run().await;
10+
app::run().await.expect("Some checks fail");
1111
}

src/checker/app.rs

+7-3
Original file line numberDiff line numberDiff line change
@@ -2,18 +2,22 @@ use std::sync::Arc;
22

33
use super::config::Configuration;
44
use super::console::Console;
5+
use super::service::{CheckError, Service};
56
use crate::checker::config::parse_from_json;
6-
use crate::checker::service::Service;
77

88
pub const NUMBER_OF_ARGUMENTS: usize = 2;
99

10+
/// # Errors
11+
///
12+
/// If some checks fails it will return a vector with all failing checks.
13+
///
1014
/// # Panics
1115
///
1216
/// Will panic if:
1317
///
1418
/// - It can't read the json configuration file.
1519
/// - The configuration file is invalid.
16-
pub async fn run() {
20+
pub async fn run() -> Result<(), Vec<CheckError>> {
1721
let args = parse_arguments();
1822
let config = setup_config(&args);
1923
let console_printer = Console {};
@@ -22,7 +26,7 @@ pub async fn run() {
2226
console: console_printer,
2327
};
2428

25-
service.run_checks().await;
29+
service.run_checks().await
2630
}
2731

2832
pub struct Arguments {

src/checker/service.rs

+31-5
Original file line numberDiff line numberDiff line change
@@ -14,12 +14,24 @@ pub struct Service {
1414
pub(crate) console: Console,
1515
}
1616

17+
#[derive(Debug)]
18+
pub enum CheckError {
19+
UdpError,
20+
HttpError,
21+
HealthCheckError { url: Url },
22+
}
23+
1724
impl Service {
18-
pub async fn run_checks(&self) {
25+
/// # Errors
26+
///
27+
/// Will return OK is all checks pass or an array with the check errors.
28+
pub async fn run_checks(&self) -> Result<(), Vec<CheckError>> {
1929
self.console.println("Running checks for trackers ...");
30+
2031
self.check_udp_trackers();
2132
self.check_http_trackers();
22-
self.run_health_checks().await;
33+
34+
self.run_health_checks().await
2335
}
2436

2537
fn check_udp_trackers(&self) {
@@ -38,11 +50,22 @@ impl Service {
3850
}
3951
}
4052

41-
async fn run_health_checks(&self) {
53+
async fn run_health_checks(&self) -> Result<(), Vec<CheckError>> {
4254
self.console.println("Health checks ...");
4355

56+
let mut check_errors = vec![];
57+
4458
for health_check_url in &self.config.health_checks {
45-
self.run_health_check(health_check_url.clone()).await;
59+
match self.run_health_check(health_check_url.clone()).await {
60+
Ok(()) => {}
61+
Err(err) => check_errors.push(err),
62+
}
63+
}
64+
65+
if check_errors.is_empty() {
66+
Ok(())
67+
} else {
68+
Err(check_errors)
4669
}
4770
}
4871

@@ -62,22 +85,25 @@ impl Service {
6285
.println(&format!("{} - HTTP tracker at {} is OK (TODO)", "✓".green(), url));
6386
}
6487

65-
async fn run_health_check(&self, url: Url) {
88+
async fn run_health_check(&self, url: Url) -> Result<(), CheckError> {
6689
let client = Client::builder().timeout(Duration::from_secs(5)).build().unwrap();
6790

6891
match client.get(url.clone()).send().await {
6992
Ok(response) => {
7093
if response.status().is_success() {
7194
self.console
7295
.println(&format!("{} - Health API at {} is OK", "✓".green(), url));
96+
Ok(())
7397
} else {
7498
self.console
7599
.eprintln(&format!("{} - Health API at {} failing: {:?}", "✗".red(), url, response));
100+
Err(CheckError::HealthCheckError { url })
76101
}
77102
}
78103
Err(err) => {
79104
self.console
80105
.eprintln(&format!("{} - Health API at {} failing: {:?}", "✗".red(), url, err));
106+
Err(CheckError::HealthCheckError { url })
81107
}
82108
}
83109
}

src/e2e/docker.rs

+65-8
Original file line numberDiff line numberDiff line change
@@ -4,25 +4,35 @@ use std::process::{Command, Output};
44
use std::thread::sleep;
55
use std::time::{Duration, Instant};
66

7-
use log::debug;
7+
use log::{debug, info};
88

99
/// Docker command wrapper.
1010
pub struct Docker {}
1111

12+
#[derive(Clone, Debug)]
1213
pub struct RunningContainer {
14+
pub image: String,
1315
pub name: String,
1416
pub output: Output,
1517
}
1618

1719
impl Drop for RunningContainer {
18-
/// Ensures that the temporary container is stopped and removed when the
19-
/// struct goes out of scope.
20+
/// Ensures that the temporary container is stopped when the struct goes out
21+
/// of scope.
2022
fn drop(&mut self) {
21-
let _unused = Docker::stop(self);
22-
let _unused = Docker::remove(&self.name);
23+
info!("Dropping running container: {}", self.name);
24+
if Docker::is_container_running(&self.name) {
25+
let _unused = Docker::stop(self);
26+
}
2327
}
2428
}
2529

30+
/// `docker run` command options.
31+
pub struct RunOptions {
32+
pub env_vars: Vec<(String, String)>,
33+
pub ports: Vec<String>,
34+
}
35+
2636
impl Docker {
2737
/// Builds a Docker image from a given Dockerfile.
2838
///
@@ -55,7 +65,7 @@ impl Docker {
5565
/// # Errors
5666
///
5767
/// Will fail if the docker run command fails.
58-
pub fn run(image: &str, container: &str, env_vars: &[(String, String)], ports: &[String]) -> io::Result<RunningContainer> {
68+
pub fn run(image: &str, container: &str, options: &RunOptions) -> io::Result<RunningContainer> {
5969
let initial_args = vec![
6070
"run".to_string(),
6171
"--detach".to_string(),
@@ -65,14 +75,14 @@ impl Docker {
6575

6676
// Add environment variables
6777
let mut env_var_args: Vec<String> = vec![];
68-
for (key, value) in env_vars {
78+
for (key, value) in &options.env_vars {
6979
env_var_args.push("--env".to_string());
7080
env_var_args.push(format!("{key}={value}"));
7181
}
7282

7383
// Add port mappings
7484
let mut port_args: Vec<String> = vec![];
75-
for port in ports {
85+
for port in &options.ports {
7686
port_args.push("--publish".to_string());
7787
port_args.push(port.to_string());
7888
}
@@ -85,6 +95,7 @@ impl Docker {
8595

8696
if output.status.success() {
8797
Ok(RunningContainer {
98+
image: image.to_owned(),
8899
name: container.to_owned(),
89100
output,
90101
})
@@ -174,4 +185,50 @@ impl Docker {
174185

175186
false
176187
}
188+
189+
/// Checks if a Docker container is running.
190+
///
191+
/// # Arguments
192+
///
193+
/// * `container` - The name of the Docker container.
194+
///
195+
/// # Returns
196+
///
197+
/// `true` if the container is running, `false` otherwise.
198+
#[must_use]
199+
pub fn is_container_running(container: &str) -> bool {
200+
match Command::new("docker")
201+
.args(["ps", "-f", &format!("name={container}"), "--format", "{{.Names}}"])
202+
.output()
203+
{
204+
Ok(output) => {
205+
let output_str = String::from_utf8_lossy(&output.stdout);
206+
output_str.contains(container)
207+
}
208+
Err(_) => false,
209+
}
210+
}
211+
212+
/// Checks if a Docker container exists.
213+
///
214+
/// # Arguments
215+
///
216+
/// * `container` - The name of the Docker container.
217+
///
218+
/// # Returns
219+
///
220+
/// `true` if the container exists, `false` otherwise.
221+
#[must_use]
222+
pub fn container_exist(container: &str) -> bool {
223+
match Command::new("docker")
224+
.args(["ps", "-a", "-f", &format!("name={container}"), "--format", "{{.Names}}"])
225+
.output()
226+
{
227+
Ok(output) => {
228+
let output_str = String::from_utf8_lossy(&output.stdout);
229+
output_str.contains(container)
230+
}
231+
Err(_) => false,
232+
}
233+
}
177234
}

src/e2e/mod.rs

+1
Original file line numberDiff line numberDiff line change
@@ -2,3 +2,4 @@ pub mod docker;
22
pub mod logs_parser;
33
pub mod runner;
44
pub mod temp_dir;
5+
pub mod tracker_container;

0 commit comments

Comments
 (0)