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}