diff --git a/Dockerfile b/Dockerfile index ad40229..6afe3aa 100644 --- a/Dockerfile +++ b/Dockerfile @@ -47,3 +47,5 @@ ENV RUSTICAL_DATA_STORE__SQLITE__DB_URL=/var/lib/rustical/db.sqlite3 LABEL org.opencontainers.image.authors="Lennart K github.com/lennart-k" LABEL org.opencontainers.image.licenses="AGPL-3.0-or-later" EXPOSE 4000 + +HEALTHCHECK --interval=30s --timeout=30s --start-period=3s --retries=3 CMD ["/usr/local/bin/rustical", "health"] diff --git a/src/app.rs b/src/app.rs index ff523fa..7634d2c 100644 --- a/src/app.rs +++ b/src/app.rs @@ -50,6 +50,8 @@ pub fn make_app( Arc::new(CombinedCalendarStore::new(cal_store).with_store(birthday_store)); let mut router = Router::new() + // endpoint to be used by healthcheck to see if rustical is online + .route("/ping", axum::routing::get(async || "Pong!")) .merge(caldav_router( "/caldav", auth_provider.clone(), diff --git a/src/commands/health.rs b/src/commands/health.rs new file mode 100644 index 0000000..f5cfe58 --- /dev/null +++ b/src/commands/health.rs @@ -0,0 +1,25 @@ +use crate::config::HttpConfig; +use clap::Parser; +use http::Method; + +#[derive(Parser, Debug)] +pub struct HealthArgs {} + +/// Healthcheck for running rustical instance +/// Currently just pings to see if it's reachable via HTTP +pub async fn cmd_health(http_config: HttpConfig, _health_args: HealthArgs) -> anyhow::Result<()> { + let client = reqwest::ClientBuilder::new().build().unwrap(); + + let endpoint = format!( + "http://{host}:{port}/ping", + host = http_config.host, + port = http_config.port + ) + .parse() + .unwrap(); + let request = reqwest::Request::new(Method::GET, endpoint); + + assert!(client.execute(request).await.unwrap().status().is_success()); + + Ok(()) +} diff --git a/src/commands/mod.rs b/src/commands/mod.rs index 7dd5ecd..cd17ea8 100644 --- a/src/commands/mod.rs +++ b/src/commands/mod.rs @@ -5,7 +5,8 @@ use crate::config::{ use clap::Parser; use rustical_frontend::FrontendConfig; -mod membership; +pub mod health; +pub mod membership; pub mod principals; #[derive(Debug, Parser)] diff --git a/src/main.rs b/src/main.rs index ab4ff79..b4d88f3 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,4 +1,5 @@ #![warn(clippy::all, clippy::pedantic, clippy::nursery)] +use crate::commands::health::{HealthArgs, cmd_health}; use crate::config::Config; use anyhow::Result; use app::make_app; @@ -45,6 +46,10 @@ struct Args { enum Command { GenConfig(commands::GenConfigArgs), Principals(PrincipalsArgs), + #[command( + about = "Healthcheck for running instance (Used for HEALTHCHECK in Docker container)" + )] + Health(HealthArgs), } async fn get_data_stores( @@ -85,6 +90,13 @@ async fn main() -> Result<()> { match args.command { Some(Command::GenConfig(gen_config_args)) => cmd_gen_config(gen_config_args)?, Some(Command::Principals(principals_args)) => cmd_principals(principals_args).await?, + Some(Command::Health(health_args)) => { + let config: Config = Figment::new() + .merge(Toml::file(&args.config_file)) + .merge(Env::prefixed("RUSTICAL_").split("__")) + .extract()?; + cmd_health(config.http, health_args).await?; + } None => { let config: Config = Figment::new() .merge(Toml::file(&args.config_file))