Skip to content

Commit

Permalink
feat: [#508] Health Check API but no checks yet
Browse files Browse the repository at this point in the history
New Health CHeck API, but it is not checking anything yet.

You can call it at:

http://localhost:1313/health_check
  • Loading branch information
josecelano committed Nov 24, 2023
1 parent 48ac64f commit e1a45a2
Show file tree
Hide file tree
Showing 19 changed files with 275 additions and 33 deletions.
5 changes: 4 additions & 1 deletion Containerfile
Original file line number Diff line number Diff line change
Expand Up @@ -101,18 +101,21 @@ ARG USER_ID=1000
ARG UDP_PORT=6969
ARG HTTP_PORT=7070
ARG API_PORT=1212
ARG HEALTH_CHECK_API_PORT=1313

ENV TORRUST_TRACKER_PATH_CONFIG=${TORRUST_TRACKER_PATH_CONFIG}
ENV TORRUST_TRACKER_DATABASE_DRIVER=${TORRUST_TRACKER_DATABASE_DRIVER}
ENV USER_ID=${USER_ID}
ENV UDP_PORT=${UDP_PORT}
ENV HTTP_PORT=${HTTP_PORT}
ENV API_PORT=${API_PORT}
ENV HEALTH_CHECK_API_PORT=${HEALTH_CHECK_API_PORT}
ENV TZ=Etc/UTC

EXPOSE ${UDP_PORT}/udp
EXPOSE ${HTTP_PORT}/tcp
EXPOSE ${API_PORT}/tcp
EXPOSE ${HEALTH_CHECK_API_PORT}/tcp

RUN mkdir -p /var/lib/torrust/tracker /var/log/torrust/tracker /etc/torrust/tracker

Expand All @@ -137,6 +140,6 @@ FROM runtime as release
ENV RUNTIME="release"
COPY --from=test /app/ /usr/
HEALTHCHECK --interval=5s --timeout=5s --start-period=3s --retries=3 \
CMD /usr/bin/http_health_check http://localhost:${API_PORT}/health_check \
CMD /usr/bin/http_health_check http://localhost:${HEALTH_CHECK_API_PORT}/health_check \
|| exit 1
CMD ["/usr/bin/torrust-tracker"]
1 change: 1 addition & 0 deletions docs/containers.md
Original file line number Diff line number Diff line change
Expand Up @@ -146,6 +146,7 @@ The following environmental variables can be set:
- `UDP_PORT` - The port for the UDP tracker. This should match the port used in the configuration, (default `6969`).
- `HTTP_PORT` - The port for the HTTP tracker. This should match the port used in the configuration, (default `7070`).
- `API_PORT` - The port for the tracker API. This should match the port used in the configuration, (default `1212`).
- `HEALTH_CHECK_API_PORT` - The port for the Health Check API. This should match the port used in the configuration, (default `1313`).


### Sockets
Expand Down
50 changes: 35 additions & 15 deletions packages/configuration/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -191,40 +191,43 @@
//! The default configuration is:
//!
//! ```toml
//! log_level = "info"
//! mode = "public"
//! announce_interval = 120
//! db_driver = "Sqlite3"
//! db_path = "./storage/tracker/lib/database/sqlite3.db"
//! announce_interval = 120
//! min_announce_interval = 120
//! external_ip = "0.0.0.0"
//! inactive_peer_cleanup_interval = 600
//! log_level = "info"
//! max_peer_timeout = 900
//! min_announce_interval = 120
//! mode = "public"
//! on_reverse_proxy = false
//! external_ip = "0.0.0.0"
//! tracker_usage_statistics = true
//! persistent_torrent_completed_stat = false
//! inactive_peer_cleanup_interval = 600
//! remove_peerless_torrents = true
//! tracker_usage_statistics = true
//!
//! [[udp_trackers]]
//! enabled = false
//! bind_address = "0.0.0.0:6969"
//! enabled = false
//!
//! [[http_trackers]]
//! enabled = false
//! bind_address = "0.0.0.0:7070"
//! ssl_enabled = false
//! enabled = false
//! ssl_cert_path = ""
//! ssl_enabled = false
//! ssl_key_path = ""
//!
//! [http_api]
//! enabled = true
//! bind_address = "127.0.0.1:1212"
//! ssl_enabled = false
//! enabled = true
//! ssl_cert_path = ""
//! ssl_enabled = false
//! ssl_key_path = ""
//!
//! [http_api.access_tokens]
//! admin = "MyAccessToken"
//!
//! [health_check_api]
//! bind_address = "127.0.0.1:1313"
//!```
use std::collections::{HashMap, HashSet};
use std::net::IpAddr;
Expand Down Expand Up @@ -342,7 +345,7 @@ pub struct HttpApi {
/// The address the tracker will bind to.
/// The format is `ip:port`, for example `0.0.0.0:6969`. If you want to
/// listen to all interfaces, use `0.0.0.0`. If you want the operating
/// system to choose a random port, use port `0`.
/// system to choose a random port, use port `0`.
pub bind_address: String,
/// Weather the HTTP API will use SSL or not.
pub ssl_enabled: bool,
Expand All @@ -363,9 +366,7 @@ impl HttpApi {
fn override_admin_token(&mut self, api_admin_token: &str) {
self.access_tokens.insert("admin".to_string(), api_admin_token.to_string());
}
}

impl HttpApi {
/// Checks if the given token is one of the token in the configuration.
#[must_use]
pub fn contains_token(&self, token: &str) -> bool {
Expand All @@ -375,6 +376,17 @@ impl HttpApi {
}
}

/// Configuration for the Health Check API.
#[serde_as]
#[derive(Serialize, Deserialize, PartialEq, Eq, Debug, Clone)]
pub struct HealthCheckApi {
/// The address the API will bind to.
/// The format is `ip:port`, for example `127.0.0.1:1313`. If you want to
/// listen to all interfaces, use `0.0.0.0`. If you want the operating
/// system to choose a random port, use port `0`.
pub bind_address: String,
}

/// Core configuration for the tracker.
#[allow(clippy::struct_excessive_bools)]
#[derive(Serialize, Deserialize, PartialEq, Eq, Debug)]
Expand Down Expand Up @@ -465,6 +477,8 @@ pub struct Configuration {
pub http_trackers: Vec<HttpTracker>,
/// The HTTP API configuration.
pub http_api: HttpApi,
/// The Health Check API configuration.
pub health_check_api: HealthCheckApi,
}

/// Errors that can occur when loading the configuration.
Expand Down Expand Up @@ -529,6 +543,9 @@ impl Default for Configuration {
.cloned()
.collect(),
},
health_check_api: HealthCheckApi {
bind_address: String::from("127.0.0.1:1313"),
},
};
configuration.udp_trackers.push(UdpTracker {
enabled: false,
Expand Down Expand Up @@ -676,6 +693,9 @@ mod tests {
[http_api.access_tokens]
admin = "MyAccessToken"
[health_check_api]
bind_address = "127.0.0.1:1313"
"#
.lines()
.map(str::trim_start)
Expand Down
4 changes: 4 additions & 0 deletions packages/test-helpers/src/configuration.rs
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,10 @@ pub fn ephemeral() -> Configuration {
config.http_api.enabled = true;
config.http_api.bind_address = format!("127.0.0.1:{}", &api_port);

// Ephemeral socket address for Health Check API
let health_check_api_port = 0u16;
config.health_check_api.bind_address = format!("127.0.0.1:{}", &health_check_api_port);

// Ephemeral socket address for UDP tracker
let udp_port = 0u16;
config.udp_trackers[0].enabled = true;
Expand Down
3 changes: 3 additions & 0 deletions share/default/config/tracker.container.mysql.toml
Original file line number Diff line number Diff line change
Expand Up @@ -36,3 +36,6 @@ ssl_key_path = "/var/lib/torrust/tracker/tls/localhost.key"

[http_api.access_tokens]
admin = "MyAccessToken"

[health_check_api]
bind_address = "127.0.0.1:1313"
3 changes: 3 additions & 0 deletions share/default/config/tracker.container.sqlite3.toml
Original file line number Diff line number Diff line change
Expand Up @@ -36,3 +36,6 @@ ssl_key_path = "/var/lib/torrust/tracker/tls/localhost.key"

[http_api.access_tokens]
admin = "MyAccessToken"

[health_check_api]
bind_address = "127.0.0.1:1313"
3 changes: 3 additions & 0 deletions share/default/config/tracker.development.sqlite3.toml
Original file line number Diff line number Diff line change
Expand Up @@ -32,3 +32,6 @@ ssl_key_path = ""

[http_api.access_tokens]
admin = "MyAccessToken"

[health_check_api]
bind_address = "127.0.0.1:1313"
18 changes: 14 additions & 4 deletions src/app.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,11 @@
//! - Loading data from the database when it's needed.
//! - Starting some jobs depending on the configuration.
//!
//! The started jobs may be:
//! Jobs executed always:
//!
//! - Health Check API
//!
//! Optional jobs:
//!
//! - Torrent cleaner: it removes inactive peers and (optionally) peerless torrents.
//! - UDP trackers: the user can enable multiple UDP tracker on several ports.
Expand All @@ -23,13 +27,16 @@ use log::warn;
use tokio::task::JoinHandle;
use torrust_tracker_configuration::Configuration;

use crate::bootstrap::jobs::{http_tracker, torrent_cleanup, tracker_apis, udp_tracker};
use crate::bootstrap::jobs::{health_check_api, http_tracker, torrent_cleanup, tracker_apis, udp_tracker};
use crate::servers::http::Version;
use crate::tracker;

/// # Panics
///
/// Will panic if the socket address for API can't be parsed.
/// Will panic if:
///
/// - Can't retrieve tracker keys from database.
/// - Can't load whitelist from database.
pub async fn start(config: Arc<Configuration>, tracker: Arc<tracker::Tracker>) -> Vec<JoinHandle<()>> {
let mut jobs: Vec<JoinHandle<()>> = Vec::new();

Expand Down Expand Up @@ -78,10 +85,13 @@ pub async fn start(config: Arc<Configuration>, tracker: Arc<tracker::Tracker>) -
jobs.push(tracker_apis::start_job(&config.http_api, tracker.clone()).await);
}

// Remove torrents without peers, every interval
// Start runners to remove torrents without peers, every interval
if config.inactive_peer_cleanup_interval > 0 {
jobs.push(torrent_cleanup::start_job(&config, &tracker));
}

// Start Health Check API
jobs.push(health_check_api::start_job(&config.health_check_api).await);

Check warning on line 94 in src/app.rs

View check run for this annotation

Codecov / codecov/patch

src/app.rs#L94

Added line #L94 was not covered by tests

jobs
}
74 changes: 74 additions & 0 deletions src/bootstrap/jobs/health_check_api.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
//! Health Check API job starter.
//!
//! The [`health_check_api::start_job`](crate::bootstrap::jobs::health_check_api::start_job)
//! function starts the Health Check REST API.
//!
//! The [`health_check_api::start_job`](crate::bootstrap::jobs::health_check_api::start_job)
//! function spawns a new asynchronous task, that tasks is the "**launcher**".
//! The "**launcher**" starts the actual server and sends a message back
//! to the main application. The main application waits until receives
//! the message [`ApiServerJobStarted`]
//! from the "**launcher**".
//!
//! The "**launcher**" is an intermediary thread that decouples the Health Check
//! API server from the process that handles it.
//!
//! Refer to the [configuration documentation](https://docs.rs/torrust-tracker-configuration)
//! for the API configuration options.
use std::net::SocketAddr;

use log::info;
use tokio::sync::oneshot;
use tokio::task::JoinHandle;
use torrust_tracker_configuration::HealthCheckApi;

use crate::servers::health_check_api::server;

/// This is the message that the "launcher" spawned task sends to the main
/// application process to notify the API server was successfully started.
///
/// > **NOTICE**: it does not mean the API server is ready to receive requests.
/// It only means the new server started. It might take some time to the server
/// to be ready to accept request.
#[derive(Debug)]

Check warning on line 33 in src/bootstrap/jobs/health_check_api.rs

View check run for this annotation

Codecov / codecov/patch

src/bootstrap/jobs/health_check_api.rs#L33

Added line #L33 was not covered by tests
pub struct ApiServerJobStarted {
pub bound_addr: SocketAddr,

Check warning on line 35 in src/bootstrap/jobs/health_check_api.rs

View check run for this annotation

Codecov / codecov/patch

src/bootstrap/jobs/health_check_api.rs#L35

Added line #L35 was not covered by tests
}

/// This function starts a new Health Check API server with the provided
/// configuration.
///
/// The functions starts a new concurrent task that will run the API server.
/// This task will send a message to the main application process to notify
/// that the API server was successfully started.
///
/// # Panics
///
/// It would panic if unable to send the `ApiServerJobStarted` notice.
pub async fn start_job(config: &HealthCheckApi) -> JoinHandle<()> {
let bind_addr = config

Check warning on line 49 in src/bootstrap/jobs/health_check_api.rs

View check run for this annotation

Codecov / codecov/patch

src/bootstrap/jobs/health_check_api.rs#L48-L49

Added lines #L48 - L49 were not covered by tests
.bind_address
.parse::<std::net::SocketAddr>()
.expect("Health Check API bind_address invalid.");

Check warning on line 52 in src/bootstrap/jobs/health_check_api.rs

View check run for this annotation

Codecov / codecov/patch

src/bootstrap/jobs/health_check_api.rs#L52

Added line #L52 was not covered by tests

let (tx, rx) = oneshot::channel::<ApiServerJobStarted>();

Check warning on line 54 in src/bootstrap/jobs/health_check_api.rs

View check run for this annotation

Codecov / codecov/patch

src/bootstrap/jobs/health_check_api.rs#L54

Added line #L54 was not covered by tests

// Run the API server
let join_handle = tokio::spawn(async move {
info!("Starting Health Check API server: http://{}", bind_addr);

Check warning on line 58 in src/bootstrap/jobs/health_check_api.rs

View check run for this annotation

Codecov / codecov/patch

src/bootstrap/jobs/health_check_api.rs#L57-L58

Added lines #L57 - L58 were not covered by tests

let handle = server::start(bind_addr, tx);

Check warning on line 60 in src/bootstrap/jobs/health_check_api.rs

View check run for this annotation

Codecov / codecov/patch

src/bootstrap/jobs/health_check_api.rs#L60

Added line #L60 was not covered by tests

if let Ok(()) = handle.await {
info!("Health Check API server on http://{} stopped", bind_addr);

Check warning on line 63 in src/bootstrap/jobs/health_check_api.rs

View check run for this annotation

Codecov / codecov/patch

src/bootstrap/jobs/health_check_api.rs#L62-L63

Added lines #L62 - L63 were not covered by tests
}
});

Check warning on line 65 in src/bootstrap/jobs/health_check_api.rs

View check run for this annotation

Codecov / codecov/patch

src/bootstrap/jobs/health_check_api.rs#L65

Added line #L65 was not covered by tests

// Wait until the API server job is running
match rx.await {
Ok(_msg) => info!("Torrust Health Check API server started"),
Err(e) => panic!("the Health Check API server was dropped: {e}"),

Check warning on line 70 in src/bootstrap/jobs/health_check_api.rs

View check run for this annotation

Codecov / codecov/patch

src/bootstrap/jobs/health_check_api.rs#L68-L70

Added lines #L68 - L70 were not covered by tests
}

join_handle
}

Check warning on line 74 in src/bootstrap/jobs/health_check_api.rs

View check run for this annotation

Codecov / codecov/patch

src/bootstrap/jobs/health_check_api.rs#L73-L74

Added lines #L73 - L74 were not covered by tests
1 change: 1 addition & 0 deletions src/bootstrap/jobs/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
//! 2. Launch all the application services as concurrent jobs.
//!
//! This modules contains all the functions needed to start those jobs.
pub mod health_check_api;
pub mod http_tracker;
pub mod torrent_cleanup;
pub mod tracker_apis;
Expand Down
29 changes: 16 additions & 13 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -148,41 +148,44 @@
//! The default configuration is:
//!
//! ```toml
//! log_level = "info"
//! mode = "public"
//! announce_interval = 120
//! db_driver = "Sqlite3"
//! db_path = "./storage/tracker/lib/database/sqlite3.db"
//! announce_interval = 120
//! min_announce_interval = 120
//! external_ip = "0.0.0.0"
//! inactive_peer_cleanup_interval = 600
//! log_level = "info"
//! max_peer_timeout = 900
//! min_announce_interval = 120
//! mode = "public"
//! on_reverse_proxy = false
//! external_ip = "0.0.0.0"
//! tracker_usage_statistics = true
//! persistent_torrent_completed_stat = false
//! inactive_peer_cleanup_interval = 600
//! remove_peerless_torrents = true
//! tracker_usage_statistics = true
//!
//! [[udp_trackers]]
//! enabled = false
//! bind_address = "0.0.0.0:6969"
//! enabled = false
//!
//! [[http_trackers]]
//! enabled = false
//! bind_address = "0.0.0.0:7070"
//! ssl_enabled = false
//! enabled = false
//! ssl_cert_path = ""
//! ssl_enabled = false
//! ssl_key_path = ""
//!
//! [http_api]
//! enabled = true
//! bind_address = "127.0.0.1:1212"
//! ssl_enabled = false
//! enabled = true
//! ssl_cert_path = ""
//! ssl_enabled = false
//! ssl_key_path = ""
//!
//! [http_api.access_tokens]
//! admin = "MyAccessToken"
//! ```
//!
//! [health_check_api]

Check warning on line 186 in src/lib.rs

View check run for this annotation

Codecov / codecov/patch

src/lib.rs#L185-L186

Added lines #L185 - L186 were not covered by tests
//! bind_address = "127.0.0.1:1313"
//!```
//!
//! The default configuration includes one disabled UDP server, one disabled HTTP server and the enabled API.
//!
Expand Down
1 change: 1 addition & 0 deletions src/servers/health_check_api/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
pub mod server;
Loading

0 comments on commit e1a45a2

Please sign in to comment.