torrust_tracker/bootstrap/jobs/
tracker_apis.rs

1//! Tracker API job starter.
2//!
3//! The [`tracker_apis::start_job`](crate::bootstrap::jobs::tracker_apis::start_job)
4//! function starts a the HTTP tracker REST API.
5//!
6//! > **NOTICE**: that even thought there is only one job the API has different
7//! > versions. API consumers can choose which version to use. The API version is
8//! > part of the URL, for example: `http://localhost:1212/api/v1/stats`.
9//!
10//! The [`tracker_apis::start_job`](crate::bootstrap::jobs::tracker_apis::start_job)  
11//! function spawns a new asynchronous task, that tasks is the "**launcher**".
12//! The "**launcher**" starts the actual server and sends a message back
13//! to the main application. The main application waits until receives
14//! the message [`ApiServerJobStarted`]
15//! from the "**launcher**".
16//!
17//! The "**launcher**" is an intermediary thread that decouples the API server
18//! from the process that handles it. The API could be used independently
19//! in the future. In that case it would not need to notify a parent process.
20//!
21//! Refer to the [configuration documentation](https://docs.rs/torrust-tracker-configuration)
22//! for the API configuration options.
23use std::net::SocketAddr;
24use std::sync::Arc;
25
26use axum_server::tls_rustls::RustlsConfig;
27use tokio::task::JoinHandle;
28use torrust_tracker_configuration::{AccessTokens, HttpApi};
29use tracing::instrument;
30
31use super::make_rust_tls;
32use crate::core;
33use crate::servers::apis::server::{ApiServer, Launcher};
34use crate::servers::apis::Version;
35use crate::servers::registar::ServiceRegistrationForm;
36
37/// This is the message that the "launcher" spawned task sends to the main
38/// application process to notify the API server was successfully started.
39///
40/// > **NOTICE**: it does not mean the API server is ready to receive requests.
41/// > It only means the new server started. It might take some time to the server
42/// > to be ready to accept request.
43#[derive(Debug)]
44pub struct ApiServerJobStarted();
45
46/// This function starts a new API server with the provided configuration.
47///
48/// The functions starts a new concurrent task that will run the API server.
49/// This task will send a message to the main application process to notify
50/// that the API server was successfully started.
51///
52/// # Panics
53///
54/// It would panic if unable to send the  `ApiServerJobStarted` notice.
55///
56///
57#[instrument(skip(config, tracker, form))]
58pub async fn start_job(
59    config: &HttpApi,
60    tracker: Arc<core::Tracker>,
61    form: ServiceRegistrationForm,
62    version: Version,
63) -> Option<JoinHandle<()>> {
64    let bind_to = config.bind_address;
65
66    let tls = make_rust_tls(&config.tsl_config)
67        .await
68        .map(|tls| tls.expect("it should have a valid tracker api tls configuration"));
69
70    let access_tokens = Arc::new(config.access_tokens.clone());
71
72    match version {
73        Version::V1 => Some(start_v1(bind_to, tls, tracker.clone(), form, access_tokens).await),
74    }
75}
76
77#[allow(clippy::async_yields_async)]
78#[instrument(skip(socket, tls, tracker, form, access_tokens))]
79async fn start_v1(
80    socket: SocketAddr,
81    tls: Option<RustlsConfig>,
82    tracker: Arc<core::Tracker>,
83    form: ServiceRegistrationForm,
84    access_tokens: Arc<AccessTokens>,
85) -> JoinHandle<()> {
86    let server = ApiServer::new(Launcher::new(socket, tls))
87        .start(tracker, form, access_tokens)
88        .await
89        .expect("it should be able to start to the tracker api");
90
91    tokio::spawn(async move {
92        assert!(!server.state.halt_task.is_closed(), "Halt channel should be open");
93        server.state.task.await.expect("failed to close service");
94    })
95}
96
97#[cfg(test)]
98mod tests {
99    use std::sync::Arc;
100
101    use torrust_tracker_test_helpers::configuration::ephemeral_public;
102
103    use crate::bootstrap::app::initialize_with_configuration;
104    use crate::bootstrap::jobs::tracker_apis::start_job;
105    use crate::servers::apis::Version;
106    use crate::servers::registar::Registar;
107
108    #[tokio::test]
109    async fn it_should_start_http_tracker() {
110        let cfg = Arc::new(ephemeral_public());
111        let config = &cfg.http_api.clone().unwrap();
112        let tracker = initialize_with_configuration(&cfg);
113        let version = Version::V1;
114
115        start_job(config, tracker, Registar::default().give_form(), version)
116            .await
117            .expect("it should be able to join to the tracker api start-job");
118    }
119}