Skip to content

Commit e1a45a2

Browse files
committed
feat: [torrust#508] Health Check API but no checks yet
New Health CHeck API, but it is not checking anything yet. You can call it at: http://localhost:1313/health_check
1 parent 48ac64f commit e1a45a2

File tree

19 files changed

+275
-33
lines changed

19 files changed

+275
-33
lines changed

Containerfile

+4-1
Original file line numberDiff line numberDiff line change
@@ -101,18 +101,21 @@ ARG USER_ID=1000
101101
ARG UDP_PORT=6969
102102
ARG HTTP_PORT=7070
103103
ARG API_PORT=1212
104+
ARG HEALTH_CHECK_API_PORT=1313
104105

105106
ENV TORRUST_TRACKER_PATH_CONFIG=${TORRUST_TRACKER_PATH_CONFIG}
106107
ENV TORRUST_TRACKER_DATABASE_DRIVER=${TORRUST_TRACKER_DATABASE_DRIVER}
107108
ENV USER_ID=${USER_ID}
108109
ENV UDP_PORT=${UDP_PORT}
109110
ENV HTTP_PORT=${HTTP_PORT}
110111
ENV API_PORT=${API_PORT}
112+
ENV HEALTH_CHECK_API_PORT=${HEALTH_CHECK_API_PORT}
111113
ENV TZ=Etc/UTC
112114

113115
EXPOSE ${UDP_PORT}/udp
114116
EXPOSE ${HTTP_PORT}/tcp
115117
EXPOSE ${API_PORT}/tcp
118+
EXPOSE ${HEALTH_CHECK_API_PORT}/tcp
116119

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

@@ -137,6 +140,6 @@ FROM runtime as release
137140
ENV RUNTIME="release"
138141
COPY --from=test /app/ /usr/
139142
HEALTHCHECK --interval=5s --timeout=5s --start-period=3s --retries=3 \
140-
CMD /usr/bin/http_health_check http://localhost:${API_PORT}/health_check \
143+
CMD /usr/bin/http_health_check http://localhost:${HEALTH_CHECK_API_PORT}/health_check \
141144
|| exit 1
142145
CMD ["/usr/bin/torrust-tracker"]

docs/containers.md

+1
Original file line numberDiff line numberDiff line change
@@ -146,6 +146,7 @@ The following environmental variables can be set:
146146
- `UDP_PORT` - The port for the UDP tracker. This should match the port used in the configuration, (default `6969`).
147147
- `HTTP_PORT` - The port for the HTTP tracker. This should match the port used in the configuration, (default `7070`).
148148
- `API_PORT` - The port for the tracker API. This should match the port used in the configuration, (default `1212`).
149+
- `HEALTH_CHECK_API_PORT` - The port for the Health Check API. This should match the port used in the configuration, (default `1313`).
149150

150151

151152
### Sockets

packages/configuration/src/lib.rs

+35-15
Original file line numberDiff line numberDiff line change
@@ -191,40 +191,43 @@
191191
//! The default configuration is:
192192
//!
193193
//! ```toml
194-
//! log_level = "info"
195-
//! mode = "public"
194+
//! announce_interval = 120
196195
//! db_driver = "Sqlite3"
197196
//! db_path = "./storage/tracker/lib/database/sqlite3.db"
198-
//! announce_interval = 120
199-
//! min_announce_interval = 120
197+
//! external_ip = "0.0.0.0"
198+
//! inactive_peer_cleanup_interval = 600
199+
//! log_level = "info"
200200
//! max_peer_timeout = 900
201+
//! min_announce_interval = 120
202+
//! mode = "public"
201203
//! on_reverse_proxy = false
202-
//! external_ip = "0.0.0.0"
203-
//! tracker_usage_statistics = true
204204
//! persistent_torrent_completed_stat = false
205-
//! inactive_peer_cleanup_interval = 600
206205
//! remove_peerless_torrents = true
206+
//! tracker_usage_statistics = true
207207
//!
208208
//! [[udp_trackers]]
209-
//! enabled = false
210209
//! bind_address = "0.0.0.0:6969"
210+
//! enabled = false
211211
//!
212212
//! [[http_trackers]]
213-
//! enabled = false
214213
//! bind_address = "0.0.0.0:7070"
215-
//! ssl_enabled = false
214+
//! enabled = false
216215
//! ssl_cert_path = ""
216+
//! ssl_enabled = false
217217
//! ssl_key_path = ""
218218
//!
219219
//! [http_api]
220-
//! enabled = true
221220
//! bind_address = "127.0.0.1:1212"
222-
//! ssl_enabled = false
221+
//! enabled = true
223222
//! ssl_cert_path = ""
223+
//! ssl_enabled = false
224224
//! ssl_key_path = ""
225225
//!
226226
//! [http_api.access_tokens]
227227
//! admin = "MyAccessToken"
228+
//!
229+
//! [health_check_api]
230+
//! bind_address = "127.0.0.1:1313"
228231
//!```
229232
use std::collections::{HashMap, HashSet};
230233
use std::net::IpAddr;
@@ -342,7 +345,7 @@ pub struct HttpApi {
342345
/// The address the tracker will bind to.
343346
/// The format is `ip:port`, for example `0.0.0.0:6969`. If you want to
344347
/// listen to all interfaces, use `0.0.0.0`. If you want the operating
345-
/// system to choose a random port, use port `0`.
348+
/// system to choose a random port, use port `0`.
346349
pub bind_address: String,
347350
/// Weather the HTTP API will use SSL or not.
348351
pub ssl_enabled: bool,
@@ -363,9 +366,7 @@ impl HttpApi {
363366
fn override_admin_token(&mut self, api_admin_token: &str) {
364367
self.access_tokens.insert("admin".to_string(), api_admin_token.to_string());
365368
}
366-
}
367369

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

379+
/// Configuration for the Health Check API.
380+
#[serde_as]
381+
#[derive(Serialize, Deserialize, PartialEq, Eq, Debug, Clone)]
382+
pub struct HealthCheckApi {
383+
/// The address the API will bind to.
384+
/// The format is `ip:port`, for example `127.0.0.1:1313`. If you want to
385+
/// listen to all interfaces, use `0.0.0.0`. If you want the operating
386+
/// system to choose a random port, use port `0`.
387+
pub bind_address: String,
388+
}
389+
378390
/// Core configuration for the tracker.
379391
#[allow(clippy::struct_excessive_bools)]
380392
#[derive(Serialize, Deserialize, PartialEq, Eq, Debug)]
@@ -465,6 +477,8 @@ pub struct Configuration {
465477
pub http_trackers: Vec<HttpTracker>,
466478
/// The HTTP API configuration.
467479
pub http_api: HttpApi,
480+
/// The Health Check API configuration.
481+
pub health_check_api: HealthCheckApi,
468482
}
469483

470484
/// Errors that can occur when loading the configuration.
@@ -529,6 +543,9 @@ impl Default for Configuration {
529543
.cloned()
530544
.collect(),
531545
},
546+
health_check_api: HealthCheckApi {
547+
bind_address: String::from("127.0.0.1:1313"),
548+
},
532549
};
533550
configuration.udp_trackers.push(UdpTracker {
534551
enabled: false,
@@ -676,6 +693,9 @@ mod tests {
676693
677694
[http_api.access_tokens]
678695
admin = "MyAccessToken"
696+
697+
[health_check_api]
698+
bind_address = "127.0.0.1:1313"
679699
"#
680700
.lines()
681701
.map(str::trim_start)

packages/test-helpers/src/configuration.rs

+4
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,10 @@ pub fn ephemeral() -> Configuration {
3737
config.http_api.enabled = true;
3838
config.http_api.bind_address = format!("127.0.0.1:{}", &api_port);
3939

40+
// Ephemeral socket address for Health Check API
41+
let health_check_api_port = 0u16;
42+
config.health_check_api.bind_address = format!("127.0.0.1:{}", &health_check_api_port);
43+
4044
// Ephemeral socket address for UDP tracker
4145
let udp_port = 0u16;
4246
config.udp_trackers[0].enabled = true;

share/default/config/tracker.container.mysql.toml

+3
Original file line numberDiff line numberDiff line change
@@ -36,3 +36,6 @@ ssl_key_path = "/var/lib/torrust/tracker/tls/localhost.key"
3636

3737
[http_api.access_tokens]
3838
admin = "MyAccessToken"
39+
40+
[health_check_api]
41+
bind_address = "127.0.0.1:1313"

share/default/config/tracker.container.sqlite3.toml

+3
Original file line numberDiff line numberDiff line change
@@ -36,3 +36,6 @@ ssl_key_path = "/var/lib/torrust/tracker/tls/localhost.key"
3636

3737
[http_api.access_tokens]
3838
admin = "MyAccessToken"
39+
40+
[health_check_api]
41+
bind_address = "127.0.0.1:1313"

share/default/config/tracker.development.sqlite3.toml

+3
Original file line numberDiff line numberDiff line change
@@ -32,3 +32,6 @@ ssl_key_path = ""
3232

3333
[http_api.access_tokens]
3434
admin = "MyAccessToken"
35+
36+
[health_check_api]
37+
bind_address = "127.0.0.1:1313"

src/app.rs

+14-4
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,11 @@
1111
//! - Loading data from the database when it's needed.
1212
//! - Starting some jobs depending on the configuration.
1313
//!
14-
//! The started jobs may be:
14+
//! Jobs executed always:
15+
//!
16+
//! - Health Check API
17+
//!
18+
//! Optional jobs:
1519
//!
1620
//! - Torrent cleaner: it removes inactive peers and (optionally) peerless torrents.
1721
//! - UDP trackers: the user can enable multiple UDP tracker on several ports.
@@ -23,13 +27,16 @@ use log::warn;
2327
use tokio::task::JoinHandle;
2428
use torrust_tracker_configuration::Configuration;
2529

26-
use crate::bootstrap::jobs::{http_tracker, torrent_cleanup, tracker_apis, udp_tracker};
30+
use crate::bootstrap::jobs::{health_check_api, http_tracker, torrent_cleanup, tracker_apis, udp_tracker};
2731
use crate::servers::http::Version;
2832
use crate::tracker;
2933

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

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

81-
// Remove torrents without peers, every interval
88+
// Start runners to remove torrents without peers, every interval
8289
if config.inactive_peer_cleanup_interval > 0 {
8390
jobs.push(torrent_cleanup::start_job(&config, &tracker));
8491
}
8592

93+
// Start Health Check API
94+
jobs.push(health_check_api::start_job(&config.health_check_api).await);
95+
8696
jobs
8797
}
+74
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
//! Health Check API job starter.
2+
//!
3+
//! The [`health_check_api::start_job`](crate::bootstrap::jobs::health_check_api::start_job)
4+
//! function starts the Health Check REST API.
5+
//!
6+
//! The [`health_check_api::start_job`](crate::bootstrap::jobs::health_check_api::start_job)
7+
//! function spawns a new asynchronous task, that tasks is the "**launcher**".
8+
//! The "**launcher**" starts the actual server and sends a message back
9+
//! to the main application. The main application waits until receives
10+
//! the message [`ApiServerJobStarted`]
11+
//! from the "**launcher**".
12+
//!
13+
//! The "**launcher**" is an intermediary thread that decouples the Health Check
14+
//! API server from the process that handles it.
15+
//!
16+
//! Refer to the [configuration documentation](https://docs.rs/torrust-tracker-configuration)
17+
//! for the API configuration options.
18+
use std::net::SocketAddr;
19+
20+
use log::info;
21+
use tokio::sync::oneshot;
22+
use tokio::task::JoinHandle;
23+
use torrust_tracker_configuration::HealthCheckApi;
24+
25+
use crate::servers::health_check_api::server;
26+
27+
/// This is the message that the "launcher" spawned task sends to the main
28+
/// application process to notify the API server was successfully started.
29+
///
30+
/// > **NOTICE**: it does not mean the API server is ready to receive requests.
31+
/// It only means the new server started. It might take some time to the server
32+
/// to be ready to accept request.
33+
#[derive(Debug)]
34+
pub struct ApiServerJobStarted {
35+
pub bound_addr: SocketAddr,
36+
}
37+
38+
/// This function starts a new Health Check API server with the provided
39+
/// configuration.
40+
///
41+
/// The functions starts a new concurrent task that will run the API server.
42+
/// This task will send a message to the main application process to notify
43+
/// that the API server was successfully started.
44+
///
45+
/// # Panics
46+
///
47+
/// It would panic if unable to send the `ApiServerJobStarted` notice.
48+
pub async fn start_job(config: &HealthCheckApi) -> JoinHandle<()> {
49+
let bind_addr = config
50+
.bind_address
51+
.parse::<std::net::SocketAddr>()
52+
.expect("Health Check API bind_address invalid.");
53+
54+
let (tx, rx) = oneshot::channel::<ApiServerJobStarted>();
55+
56+
// Run the API server
57+
let join_handle = tokio::spawn(async move {
58+
info!("Starting Health Check API server: http://{}", bind_addr);
59+
60+
let handle = server::start(bind_addr, tx);
61+
62+
if let Ok(()) = handle.await {
63+
info!("Health Check API server on http://{} stopped", bind_addr);
64+
}
65+
});
66+
67+
// Wait until the API server job is running
68+
match rx.await {
69+
Ok(_msg) => info!("Torrust Health Check API server started"),
70+
Err(e) => panic!("the Health Check API server was dropped: {e}"),
71+
}
72+
73+
join_handle
74+
}

src/bootstrap/jobs/mod.rs

+1
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
//! 2. Launch all the application services as concurrent jobs.
77
//!
88
//! This modules contains all the functions needed to start those jobs.
9+
pub mod health_check_api;
910
pub mod http_tracker;
1011
pub mod torrent_cleanup;
1112
pub mod tracker_apis;

src/lib.rs

+16-13
Original file line numberDiff line numberDiff line change
@@ -148,41 +148,44 @@
148148
//! The default configuration is:
149149
//!
150150
//! ```toml
151-
//! log_level = "info"
152-
//! mode = "public"
151+
//! announce_interval = 120
153152
//! db_driver = "Sqlite3"
154153
//! db_path = "./storage/tracker/lib/database/sqlite3.db"
155-
//! announce_interval = 120
156-
//! min_announce_interval = 120
154+
//! external_ip = "0.0.0.0"
155+
//! inactive_peer_cleanup_interval = 600
156+
//! log_level = "info"
157157
//! max_peer_timeout = 900
158+
//! min_announce_interval = 120
159+
//! mode = "public"
158160
//! on_reverse_proxy = false
159-
//! external_ip = "0.0.0.0"
160-
//! tracker_usage_statistics = true
161161
//! persistent_torrent_completed_stat = false
162-
//! inactive_peer_cleanup_interval = 600
163162
//! remove_peerless_torrents = true
163+
//! tracker_usage_statistics = true
164164
//!
165165
//! [[udp_trackers]]
166-
//! enabled = false
167166
//! bind_address = "0.0.0.0:6969"
167+
//! enabled = false
168168
//!
169169
//! [[http_trackers]]
170-
//! enabled = false
171170
//! bind_address = "0.0.0.0:7070"
172-
//! ssl_enabled = false
171+
//! enabled = false
173172
//! ssl_cert_path = ""
173+
//! ssl_enabled = false
174174
//! ssl_key_path = ""
175175
//!
176176
//! [http_api]
177-
//! enabled = true
178177
//! bind_address = "127.0.0.1:1212"
179-
//! ssl_enabled = false
178+
//! enabled = true
180179
//! ssl_cert_path = ""
180+
//! ssl_enabled = false
181181
//! ssl_key_path = ""
182182
//!
183183
//! [http_api.access_tokens]
184184
//! admin = "MyAccessToken"
185-
//! ```
185+
//!
186+
//! [health_check_api]
187+
//! bind_address = "127.0.0.1:1313"
188+
//!```
186189
//!
187190
//! The default configuration includes one disabled UDP server, one disabled HTTP server and the enabled API.
188191
//!

src/servers/health_check_api/mod.rs

+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
pub mod server;

0 commit comments

Comments
 (0)