torrust_tracker/console/clients/checker/
app.rs

1//! Program to run checks against running trackers.
2//!
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//!
12//! ```text
13//! TORRUST_CHECKER_CONFIG=$(cat "./share/default/config/tracker_checker.json") cargo run --bin tracker_checker
14//! ```
15//!
16//! Another real example to test the Torrust demo tracker:
17//!
18//! ```text
19//! TORRUST_CHECKER_CONFIG='{
20//!     "udp_trackers": ["144.126.245.19:6969"],
21//!     "http_trackers": ["https://tracker.torrust-demo.com"],
22//!     "health_checks": ["https://tracker.torrust-demo.com/api/health_check"]
23//! }' cargo run --bin tracker_checker
24//! ```
25//!
26//! The output should be something like the following:
27//!
28//! ```json
29//! {
30//!   "udp_trackers": [
31//!     {
32//!       "url": "144.126.245.19:6969",
33//!       "status": {
34//!         "code": "ok",
35//!         "message": ""
36//!       }
37//!     }
38//!   ],
39//!   "http_trackers": [
40//!     {
41//!       "url": "https://tracker.torrust-demo.com/",
42//!       "status": {
43//!         "code": "ok",
44//!         "message": ""
45//!       }
46//!     }
47//!   ],
48//!   "health_checks": [
49//!     {
50//!       "url": "https://tracker.torrust-demo.com/api/health_check",
51//!       "status": {
52//!         "code": "ok",
53//!         "message": ""
54//!       }
55//!     }
56//!   ]
57//! }
58//! ```
59use std::path::PathBuf;
60use std::sync::Arc;
61
62use anyhow::{Context, Result};
63use clap::Parser;
64use tracing::level_filters::LevelFilter;
65
66use super::config::Configuration;
67use super::console::Console;
68use super::service::{CheckResult, Service};
69use crate::console::clients::checker::config::parse_from_json;
70
71#[derive(Parser, Debug)]
72#[clap(author, version, about, long_about = None)]
73struct Args {
74    /// Path to the JSON configuration file.
75    #[clap(short, long, env = "TORRUST_CHECKER_CONFIG_PATH")]
76    config_path: Option<PathBuf>,
77
78    /// Direct configuration content in JSON.
79    #[clap(env = "TORRUST_CHECKER_CONFIG", hide_env_values = true)]
80    config_content: Option<String>,
81}
82
83/// # Errors
84///
85/// Will return an error if the configuration was not provided.
86pub async fn run() -> Result<Vec<CheckResult>> {
87    tracing_stdout_init(LevelFilter::INFO);
88
89    let args = Args::parse();
90
91    let config = setup_config(args)?;
92
93    let console_printer = Console {};
94
95    let service = Service {
96        config: Arc::new(config),
97        console: console_printer,
98    };
99
100    service.run_checks().await.context("it should run the check tasks")
101}
102
103fn tracing_stdout_init(filter: LevelFilter) {
104    tracing_subscriber::fmt().with_max_level(filter).init();
105    tracing::debug!("Logging initialized");
106}
107
108fn setup_config(args: Args) -> Result<Configuration> {
109    match (args.config_path, args.config_content) {
110        (Some(config_path), _) => load_config_from_file(&config_path),
111        (_, Some(config_content)) => parse_from_json(&config_content).context("invalid config format"),
112        _ => Err(anyhow::anyhow!("no configuration provided")),
113    }
114}
115
116fn load_config_from_file(path: &PathBuf) -> Result<Configuration> {
117    let file_content = std::fs::read_to_string(path).with_context(|| format!("can't read config file {path:?}"))?;
118
119    parse_from_json(&file_content).context("invalid config format")
120}