Skip to content

Commit 5b6cf7b

Browse files
committed
Merge #657: Tracker Checker: pass configuration with env var
392ffab refactor: [#656] E2E runner. Pass config as env var to Tracker Checker (Jose Celano) 1bab582 feat: [#656] Tracker Checker sopports env var for config (Jose Celano) 7f43fbd chore: [656] add cargo dep feature (Jose Celano) 8543190 refactor: Tracker Checker: use clap and anyhow (Jose Celano) Pull request description: Allows passing the configuration with an env var: Run providing a config file path (option or env var): ```console cargo run --bin tracker_checker -- --config-path "./share/default/config/tracker_checker.json" TORRUST_CHECKER_CONFIG_PATH="./share/default/config/tracker_checker.json" cargo run --bin tracker_checker ``` Run providing the configuration: ```console TORRUST_CHECKER_CONFIG=$(cat "./share/default/config/tracker_checker.json") cargo run --bin tracker_checker ``` This way we don't need to create a temp file in CI. See https://github.com/torrust/torrust-tracker/blob/develop/src/e2e/runner.rs#L71-L73 ### Subtasks - [x] Use `clap` and `anyhow`. - [x] Add env var `TORRUST_TRACKER_CHECKER_CONFIG`. - [x] Use the new env var in the E2E runner binary. ACKs for top commit: josecelano: ACK 392ffab Tree-SHA512: abf25491bc04b1f07a0d60389a72ee5dca439591a82531f4847f47ee1a203613e05d17db78c8e71b0125e83db38c7fa2d3bb368210a0554459a4b570ab012dd4
2 parents 75a502f + 392ffab commit 5b6cf7b

9 files changed

+85
-191
lines changed

Cargo.toml

+1-1
Original file line numberDiff line numberDiff line change
@@ -72,7 +72,7 @@ uuid = { version = "1", features = ["v4"] }
7272
colored = "2.1.0"
7373
url = "2.5.0"
7474
tempfile = "3.9.0"
75-
clap = { version = "4.4.18", features = ["derive"]}
75+
clap = { version = "4.4.18", features = ["derive", "env"]}
7676
anyhow = "1.0.79"
7777

7878
[dev-dependencies]

src/bin/tracker_checker.rs

+10-1
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,16 @@
11
//! Program to run checks against running trackers.
22
//!
3+
//! Run providing a config file path:
4+
//!
5+
//! ```text
6+
//! cargo run --bin tracker_checker -- --config-path "./share/default/config/tracker_checker.json"
7+
//! TORRUST_CHECKER_CONFIG_PATH="./share/default/config/tracker_checker.json" cargo run --bin tracker_checker
8+
//! ```
9+
//!
10+
//! Run providing the configuration:
11+
//!
312
//! ```text
4-
//! cargo run --bin tracker_checker "./share/default/config/tracker_checker.json"
13+
//! TORRUST_CHECKER_CONFIG=$(cat "./share/default/config/tracker_checker.json") cargo run --bin tracker_checker
514
//! ```
615
use torrust_tracker::checker::app;
716

src/checker/app.rs

+32-35
Original file line numberDiff line numberDiff line change
@@ -1,57 +1,54 @@
1+
use std::path::PathBuf;
12
use std::sync::Arc;
23

4+
use anyhow::{Context, Result};
5+
use clap::Parser;
6+
37
use super::config::Configuration;
48
use super::console::Console;
5-
use super::service::{CheckError, Service};
9+
use super::service::{CheckResult, Service};
610
use crate::checker::config::parse_from_json;
711

8-
pub const NUMBER_OF_ARGUMENTS: usize = 2;
12+
#[derive(Parser, Debug)]
13+
#[clap(author, version, about, long_about = None)]
14+
struct Args {
15+
/// Path to the JSON configuration file.
16+
#[clap(short, long, env = "TORRUST_CHECKER_CONFIG_PATH")]
17+
config_path: Option<PathBuf>,
18+
19+
/// Direct configuration content in JSON.
20+
#[clap(env = "TORRUST_CHECKER_CONFIG", hide_env_values = true)]
21+
config_content: Option<String>,
22+
}
923

1024
/// # Errors
1125
///
12-
/// If some checks fails it will return a vector with all failing checks.
13-
///
14-
/// # Panics
15-
///
16-
/// Will panic if:
17-
///
18-
/// - It can't read the json configuration file.
19-
/// - The configuration file is invalid.
20-
pub async fn run() -> Result<(), Vec<CheckError>> {
21-
let args = parse_arguments();
22-
let config = setup_config(&args);
26+
/// Will return an error if the configuration was not provided.
27+
pub async fn run() -> Result<Vec<CheckResult>> {
28+
let args = Args::parse();
29+
30+
let config = setup_config(args)?;
31+
2332
let console_printer = Console {};
33+
2434
let service = Service {
2535
config: Arc::new(config),
2636
console: console_printer,
2737
};
2838

29-
service.run_checks().await
30-
}
31-
32-
pub struct Arguments {
33-
pub config_path: String,
39+
Ok(service.run_checks().await)
3440
}
3541

36-
fn parse_arguments() -> Arguments {
37-
let args: Vec<String> = std::env::args().collect();
38-
39-
if args.len() < NUMBER_OF_ARGUMENTS {
40-
eprintln!("Usage: cargo run --bin tracker_checker <PATH_TO_CONFIG_FILE>");
41-
eprintln!("For example: cargo run --bin tracker_checker ./share/default/config/tracker_checker.json");
42-
std::process::exit(1);
43-
}
44-
45-
let config_path = &args[1];
46-
47-
Arguments {
48-
config_path: config_path.to_string(),
42+
fn setup_config(args: Args) -> Result<Configuration> {
43+
match (args.config_path, args.config_content) {
44+
(Some(config_path), _) => load_config_from_file(&config_path),
45+
(_, Some(config_content)) => parse_from_json(&config_content).context("invalid config format"),
46+
_ => Err(anyhow::anyhow!("no configuration provided")),
4947
}
5048
}
5149

52-
fn setup_config(args: &Arguments) -> Configuration {
53-
let file_content = std::fs::read_to_string(args.config_path.clone())
54-
.unwrap_or_else(|_| panic!("Can't read config file {}", args.config_path));
50+
fn load_config_from_file(path: &PathBuf) -> Result<Configuration> {
51+
let file_content = std::fs::read_to_string(path).with_context(|| format!("can't read config file {path:?}"))?;
5552

56-
parse_from_json(&file_content).expect("Invalid config format")
53+
parse_from_json(&file_content).context("invalid config format")
5754
}

src/checker/config.rs

+3
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
use std::error::Error;
12
use std::fmt;
23
use std::net::SocketAddr;
34

@@ -43,6 +44,8 @@ pub enum ConfigurationError {
4344
InvalidUrl(url::ParseError),
4445
}
4546

47+
impl Error for ConfigurationError {}
48+
4649
impl fmt::Display for ConfigurationError {
4750
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
4851
match self {

src/checker/service.rs

+8-10
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,8 @@ pub struct Service {
1414
pub(crate) console: Console,
1515
}
1616

17+
pub type CheckResult = Result<(), CheckError>;
18+
1719
#[derive(Debug)]
1820
pub enum CheckError {
1921
UdpError,
@@ -25,7 +27,7 @@ impl Service {
2527
/// # Errors
2628
///
2729
/// Will return OK is all checks pass or an array with the check errors.
28-
pub async fn run_checks(&self) -> Result<(), Vec<CheckError>> {
30+
pub async fn run_checks(&self) -> Vec<CheckResult> {
2931
self.console.println("Running checks for trackers ...");
3032

3133
self.check_udp_trackers();
@@ -50,23 +52,19 @@ impl Service {
5052
}
5153
}
5254

53-
async fn run_health_checks(&self) -> Result<(), Vec<CheckError>> {
55+
async fn run_health_checks(&self) -> Vec<CheckResult> {
5456
self.console.println("Health checks ...");
5557

56-
let mut check_errors = vec![];
58+
let mut check_results = vec![];
5759

5860
for health_check_url in &self.config.health_checks {
5961
match self.run_health_check(health_check_url.clone()).await {
60-
Ok(()) => {}
61-
Err(err) => check_errors.push(err),
62+
Ok(()) => check_results.push(Ok(())),
63+
Err(err) => check_results.push(Err(err)),
6264
}
6365
}
6466

65-
if check_errors.is_empty() {
66-
Ok(())
67-
} else {
68-
Err(check_errors)
69-
}
67+
check_results
7068
}
7169

7270
fn check_udp_tracker(&self, address: &SocketAddr) {

src/e2e/mod.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
pub mod docker;
22
pub mod logs_parser;
33
pub mod runner;
4-
pub mod temp_dir;
4+
pub mod tracker_checker;
55
pub mod tracker_container;

src/e2e/runner.rs

+5-90
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,9 @@
1-
use std::fs::File;
2-
use std::io::Write;
3-
use std::path::{Path, PathBuf};
4-
use std::process::Command;
5-
use std::{env, io};
6-
71
use log::{debug, info, LevelFilter};
82

93
use super::tracker_container::TrackerContainer;
104
use crate::e2e::docker::RunOptions;
115
use crate::e2e::logs_parser::RunningServices;
12-
use crate::e2e::temp_dir::Handler;
6+
use crate::e2e::tracker_checker::{self};
137

148
/* code-review:
159
- We use always the same docker image name. Should we use a random image name (tag)?
@@ -19,10 +13,9 @@ use crate::e2e::temp_dir::Handler;
1913
Should we remove the image too?
2014
*/
2115

22-
pub const NUMBER_OF_ARGUMENTS: usize = 2;
16+
const NUMBER_OF_ARGUMENTS: usize = 2;
2317
const CONTAINER_IMAGE: &str = "torrust-tracker:local";
2418
const CONTAINER_NAME_PREFIX: &str = "tracker_";
25-
const TRACKER_CHECKER_CONFIG_FILE: &str = "tracker_checker.json";
2619

2720
pub struct Arguments {
2821
pub tracker_config_path: String,
@@ -63,14 +56,10 @@ pub fn run() {
6356

6457
assert_there_is_at_least_one_service_per_type(&running_services);
6558

66-
let temp_dir = create_temp_dir();
67-
68-
let tracker_checker_config_path =
69-
create_tracker_checker_config_file(&running_services, temp_dir.temp_dir.path(), TRACKER_CHECKER_CONFIG_FILE);
59+
let tracker_checker_config =
60+
serde_json::to_string_pretty(&running_services).expect("Running services should be serialized into JSON");
7061

71-
// todo: inject the configuration with an env variable so that we don't have
72-
// to create the temporary directory/file.
73-
run_tracker_checker(&tracker_checker_config_path).expect("All tracker services should be running correctly");
62+
tracker_checker::run(&tracker_checker_config).expect("All tracker services should be running correctly");
7463

7564
// More E2E tests could be added here in the future.
7665
// For example: `cargo test ...` for only E2E tests, using this shared test env.
@@ -128,19 +117,6 @@ fn read_file(path: &str) -> String {
128117
std::fs::read_to_string(path).unwrap_or_else(|_| panic!("Can't read file {path}"))
129118
}
130119

131-
fn create_temp_dir() -> Handler {
132-
debug!(
133-
"Current dir: {:?}",
134-
env::current_dir().expect("It should return the current dir")
135-
);
136-
137-
let temp_dir_handler = Handler::new().expect("A temp dir should be created");
138-
139-
info!("Temp dir created: {:?}", temp_dir_handler.temp_dir);
140-
141-
temp_dir_handler
142-
}
143-
144120
fn assert_there_is_at_least_one_service_per_type(running_services: &RunningServices) {
145121
assert!(
146122
!running_services.udp_trackers.is_empty(),
@@ -155,64 +131,3 @@ fn assert_there_is_at_least_one_service_per_type(running_services: &RunningServi
155131
"At least one Health Check should be enabled in E2E tests configuration"
156132
);
157133
}
158-
159-
fn create_tracker_checker_config_file(running_services: &RunningServices, config_path: &Path, config_name: &str) -> PathBuf {
160-
let tracker_checker_config =
161-
serde_json::to_string_pretty(&running_services).expect("Running services should be serialized into JSON");
162-
163-
let mut tracker_checker_config_path = PathBuf::from(&config_path);
164-
tracker_checker_config_path.push(config_name);
165-
166-
write_tracker_checker_config_file(&tracker_checker_config_path, &tracker_checker_config);
167-
168-
tracker_checker_config_path
169-
}
170-
171-
fn write_tracker_checker_config_file(config_file_path: &Path, config: &str) {
172-
info!(
173-
"Writing Tracker Checker configuration file: {:?} \n{config}",
174-
config_file_path
175-
);
176-
177-
let mut file = File::create(config_file_path).expect("Tracker checker config file to be created");
178-
179-
file.write_all(config.as_bytes())
180-
.expect("Tracker checker config file to be written");
181-
}
182-
183-
/// Runs the Tracker Checker.
184-
///
185-
/// For example:
186-
///
187-
/// ```text
188-
/// cargo run --bin tracker_checker "./share/default/config/tracker_checker.json"
189-
/// ```
190-
///
191-
/// # Errors
192-
///
193-
/// Will return an error if the tracker checker fails.
194-
///
195-
/// # Panics
196-
///
197-
/// Will panic if the config path is not a valid string.
198-
pub fn run_tracker_checker(config_path: &Path) -> io::Result<()> {
199-
info!(
200-
"Running Tracker Checker: cargo --bin tracker_checker {}",
201-
config_path.display()
202-
);
203-
204-
let path = config_path.to_str().expect("The path should be a valid string");
205-
206-
let status = Command::new("cargo")
207-
.args(["run", "--bin", "tracker_checker", path])
208-
.status()?;
209-
210-
if status.success() {
211-
Ok(())
212-
} else {
213-
Err(io::Error::new(
214-
io::ErrorKind::Other,
215-
format!("Failed to run Tracker Checker with config file {path}"),
216-
))
217-
}
218-
}

src/e2e/temp_dir.rs

-53
This file was deleted.

src/e2e/tracker_checker.rs

+25
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
use std::io;
2+
use std::process::Command;
3+
4+
use log::info;
5+
6+
/// Runs the Tracker Checker.
7+
///
8+
/// # Errors
9+
///
10+
/// Will return an error if the Tracker Checker fails.
11+
pub fn run(config_content: &str) -> io::Result<()> {
12+
info!("Running Tracker Checker: TORRUST_CHECKER_CONFIG=[config] cargo run --bin tracker_checker");
13+
info!("Tracker Checker config:\n{config_content}");
14+
15+
let status = Command::new("cargo")
16+
.env("TORRUST_CHECKER_CONFIG", config_content)
17+
.args(["run", "--bin", "tracker_checker"])
18+
.status()?;
19+
20+
if status.success() {
21+
Ok(())
22+
} else {
23+
Err(io::Error::new(io::ErrorKind::Other, "Failed to run Tracker Checker"))
24+
}
25+
}

0 commit comments

Comments
 (0)