From bbc67338af39172e10524d7c5b5162709e0ccb2e Mon Sep 17 00:00:00 2001 From: Jose Celano Date: Tue, 23 Jan 2024 17:34:26 +0000 Subject: [PATCH] ci: [#634] E2E test runner: open services port in docker run and run tracker checker --- .github/workflows/testing.yaml | 2 +- src/e2e/docker.rs | 12 ++++- src/e2e/logs.rs | 3 +- src/e2e/runner.rs | 85 +++++++++++++++++++++++++--------- 4 files changed, 76 insertions(+), 26 deletions(-) diff --git a/.github/workflows/testing.yaml b/.github/workflows/testing.yaml index e6624263..f2df9689 100644 --- a/.github/workflows/testing.yaml +++ b/.github/workflows/testing.yaml @@ -158,4 +158,4 @@ jobs: - id: test name: Run E2E Tests - run: cargo run --bin e2e_tests_runner + run: cargo run --bin e2e_tests_runner ./share/default/config/tracker.e2e.container.sqlite3.toml diff --git a/src/e2e/docker.rs b/src/e2e/docker.rs index 6806d789..1c158626 100644 --- a/src/e2e/docker.rs +++ b/src/e2e/docker.rs @@ -39,7 +39,7 @@ impl Docker { /// # Errors /// /// Will fail if the docker run command fails. - pub fn run(image: &str, container: &str, env_vars: &[(String, String)]) -> io::Result { + pub fn run(image: &str, container: &str, env_vars: &[(String, String)], ports: &[String]) -> io::Result { let initial_args = vec![ "run".to_string(), "--detach".to_string(), @@ -47,13 +47,21 @@ impl Docker { container.to_string(), ]; + // Add environment variables let mut env_var_args: Vec = vec![]; for (key, value) in env_vars { env_var_args.push("--env".to_string()); env_var_args.push(format!("{key}={value}")); } - let args = [initial_args, env_var_args, [image.to_string()].to_vec()].concat(); + // Add port mappings + let mut port_args: Vec = vec![]; + for port in ports { + port_args.push("--publish".to_string()); + port_args.push(port.to_string()); + } + + let args = [initial_args, env_var_args, port_args, [image.to_string()].to_vec()].concat(); debug!("Docker run args: {:?}", args); diff --git a/src/e2e/logs.rs b/src/e2e/logs.rs index 98219a9c..7cb2ceaa 100644 --- a/src/e2e/logs.rs +++ b/src/e2e/logs.rs @@ -17,7 +17,8 @@ impl RunningServices { if let Some(start) = line.find(heal_check_pattern) { let address = &line[start + heal_check_pattern.len()..].trim(); - self.health_checks.push(Self::replace_wildcard_ip_with_localhost(address)); + self.health_checks + .push(format!("{}/health_check", Self::replace_wildcard_ip_with_localhost(address))); } else if let Some(start) = line.find(udp_tracker_pattern) { let address = &line[start + udp_tracker_pattern.len()..].trim(); self.udp_trackers.push(Self::replace_wildcard_ip_with_localhost(address)); diff --git a/src/e2e/runner.rs b/src/e2e/runner.rs index f5cacaa8..364a447d 100644 --- a/src/e2e/runner.rs +++ b/src/e2e/runner.rs @@ -1,7 +1,9 @@ -use std::env; use std::fs::File; use std::io::Write; +use std::path::{Path, PathBuf}; +use std::process::Command; use std::time::Duration; +use std::{env, io}; use log::{debug, LevelFilter}; use rand::distributions::Alphanumeric; @@ -22,17 +24,7 @@ pub const NUMBER_OF_ARGUMENTS: usize = 2; /// - It can't change to the new temp dir. /// - It can't revert the dit to the previous one. pub fn run() { - /* todo: - - - [x] Build the docker image. - - [x] Run the docker image. - - [x] Wait until the container is healthy. - - [x] Parse logs to get running services. - - [x] Build config file for the tracker_checker. - - [ ] Run the tracker_checker. - - [x] Stop the container. - - */ + // todo: stop container or revert current dir on panics. setup_logging(LevelFilter::Debug); @@ -42,9 +34,10 @@ pub fn run() { let tracker_config = read_tracker_config(&args.tracker_config_path); - let container_tag = "torrust-tracker:local"; + let container_tag: &str = "torrust-tracker:local"; + let tracker_checker_config_file = "tracker_checker.json"; - //Docker::build("./Containerfile", container_tag).expect("A tracker local docker image should be built"); + Docker::build("./Containerfile", container_tag).expect("A tracker local docker image should be built"); println!( "Current dir: {:?}", @@ -62,9 +55,18 @@ pub fn run() { let container_name = generate_random_container_name("tracker_"); + // code-review: if we want to use port 0 we don't know which ports we have to open. + // Besides, if we don't use port 0 we should get the port numbers from the tracker configuration. + // We could not use docker, but the intention was to create E2E tests including containerization. println!("Running docker tracker image: {container_name} ..."); let env_vars = [("TORRUST_TRACKER_CONFIG".to_string(), tracker_config.to_string())]; - Docker::run(container_tag, &container_name, &env_vars).expect("A tracker local docker image should be running"); + let ports = [ + "6969:6969/udp".to_string(), + "7070:7070/tcp".to_string(), + "1212:1212/tcp".to_string(), + "1313:1313/tcp".to_string(), + ]; + Docker::run(container_tag, &container_name, &env_vars, &ports).expect("A tracker local docker image should be running"); println!("Waiting for the container {container_name} to be healthy ..."); let is_healthy = Docker::wait_until_is_healthy(&container_name, Duration::from_secs(10)); @@ -80,28 +82,37 @@ pub fn run() { let logs = Docker::logs(&container_name).expect("Logs should be captured from running container"); - debug!("Logs after starting the container: {logs}"); + debug!("Logs after starting the container:\n{logs}"); let mut config = RunningServices::default(); config.extract_from_logs(&logs); let json = serde_json::to_string_pretty(&config).expect("Running services should be serialized into JSON"); - println!("Tracker checker configuration: {json}"); - let tracker_checker_config_path = "./tracker_checker.json"; + let tracker_checker_config_path = format!("./{tracker_checker_config_file}"); - let mut file = File::create(tracker_checker_config_path).expect("Tracker checker config file to be created"); + let mut file = File::create(tracker_checker_config_path.clone()).expect("Tracker checker config file to be created"); file.write_all(json.as_bytes()) .expect("Tracker checker config file to be written"); println!("Tracker checker configuration file: {tracker_checker_config_path} \n{json}"); - println!("Stopping docker tracker image: {container_name} ..."); - Docker::stop(&container_name).expect("A tracker local docker image should be stopped"); - println!("Revert current dir to: {:?}", temp_dir_handler.original_dir); temp_dir_handler .revert_to_original_dir() .expect("The app should revert dir from temp dir to the original one"); + + let mut absolute_tracker_checker_config_path = PathBuf::from(&temp_dir_handler.temp_dir.path()); + absolute_tracker_checker_config_path.push(tracker_checker_config_file); + + println!( + "Running tacker checker: cargo --bin tracker_checker {}", + absolute_tracker_checker_config_path.display() + ); + + run_tracker_checker(&absolute_tracker_checker_config_path).expect("Tracker checker should check running services"); + + println!("Stopping docker tracker image: {container_name} ..."); + Docker::stop(&container_name).expect("A tracker local docker image should be stopped"); } fn setup_logging(level: LevelFilter) { @@ -159,3 +170,33 @@ fn generate_random_container_name(prefix: &str) -> String { format!("{prefix}{rand_string}") } + +/// Runs the tracker checker +/// +/// ```text +/// cargo run --bin tracker_checker "./share/default/config/tracker_checker.json" +/// ``` +/// +/// # Errors +/// +/// Will return an error if the tracker checker fails. +/// +/// # Panics +/// +/// Will panic if the config path is not a valid string. +pub fn run_tracker_checker(config_path: &Path) -> io::Result<()> { + let path = config_path.to_str().expect("The path should be a valid string"); + + let status = Command::new("cargo") + .args(["run", "--bin", "tracker_checker", path]) + .status()?; + + if status.success() { + Ok(()) + } else { + Err(io::Error::new( + io::ErrorKind::Other, + format!("Failed to run tracker checker with config file {path}"), + )) + } +}