objectstore_server/
cli.rs

1use std::path::PathBuf;
2
3use anyhow::Result;
4use argh::FromArgs;
5
6use crate::config::Config;
7use crate::{healthcheck, http, observability};
8
9/// Objectstore API webserver.
10#[derive(Debug, FromArgs)]
11struct Args {
12    /// path to the YAML configuration file
13    #[argh(option, short = 'c')]
14    pub config: Option<PathBuf>,
15
16    #[argh(subcommand)]
17    pub command: Command,
18}
19
20#[derive(Debug, FromArgs)]
21#[argh(subcommand)]
22enum Command {
23    Run(RunCommand),
24    Healthcheck(HealthcheckCommand),
25}
26
27/// run the objectstore web server
28#[derive(Debug, FromArgs)]
29#[argh(subcommand, name = "run")]
30struct RunCommand {}
31
32/// perform a healthcheck against the running objectstore web server
33///
34/// This command checks if the objectstore server is available on the configured host and port. This
35/// is used for Docker healthchecks.
36#[derive(Debug, FromArgs)]
37#[argh(subcommand, name = "healthcheck")]
38struct HealthcheckCommand {}
39
40/// Bootstrap the runtime and execute the CLI command.
41pub fn execute() -> Result<()> {
42    let args: Args = argh::from_env();
43    let config = Config::load(args.config.as_deref())?;
44
45    // Ensure a rustls crypto provider is installed, required on distroless.
46    rustls::crypto::ring::default_provider()
47        .install_default()
48        .map_err(|_| anyhow::anyhow!("Failed to install rustls crypto provider"))?;
49
50    // Sentry should be initialized before creating the async runtime.
51    let _sentry_guard = observability::init_sentry(&config);
52
53    let runtime = tokio::runtime::Builder::new_multi_thread()
54        .thread_name("main-rt")
55        .enable_all()
56        .worker_threads(config.runtime.worker_threads)
57        .build()?;
58    let _runtime_guard = runtime.enter();
59
60    observability::init_tracing(&config);
61    tracing::debug!(?config);
62
63    let metrics_guard = observability::init_metrics(&config)?;
64
65    let result = runtime.block_on(async move {
66        match args.command {
67            Command::Run(RunCommand {}) => http::server(config).await,
68            Command::Healthcheck(HealthcheckCommand {}) => healthcheck::healthcheck(config).await,
69        }
70    });
71
72    // Flush metrics unconditionally before shutdown, even on error.
73    runtime.block_on(async {
74        if let Some(metrics_guard) = metrics_guard {
75            metrics_guard.flush(None).await.ok();
76        }
77    });
78
79    result
80}