objectstore_server/
cli.rs

1use std::path::PathBuf;
2
3use anyhow::Result;
4use argh::FromArgs;
5
6use crate::config::Config;
7use crate::{healthcheck, observability, web};
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    Version(VersionCommand),
26}
27
28/// run the objectstore web server
29#[derive(Debug, FromArgs)]
30#[argh(subcommand, name = "run")]
31struct RunCommand {}
32
33/// perform a healthcheck against the running objectstore web server
34///
35/// This command checks if the objectstore server is available on the configured host and port. This
36/// is used for Docker healthchecks.
37#[derive(Debug, FromArgs)]
38#[argh(subcommand, name = "healthcheck")]
39struct HealthcheckCommand {}
40
41/// print the objectstore server version
42#[derive(Default, Debug, FromArgs)]
43#[argh(subcommand, name = "version")]
44struct VersionCommand {}
45
46/// Bootstrap the runtime and execute the CLI command.
47pub fn execute() -> Result<()> {
48    let args: Args = argh::from_env();
49
50    // Special switch to just print the version and exit.
51    if let Command::Version(_) = args.command {
52        println!("{}", env!("OBJECTSTORE_RELEASE"));
53        return Ok(());
54    }
55
56    let config = Config::load(args.config.as_deref())?;
57
58    // Ensure a rustls crypto provider is installed, required on distroless.
59    rustls::crypto::ring::default_provider()
60        .install_default()
61        .map_err(|_| anyhow::anyhow!("Failed to install rustls crypto provider"))?;
62
63    // Sentry should be initialized before creating the async runtime.
64    let _sentry_guard = observability::init_sentry(&config);
65
66    let runtime = tokio::runtime::Builder::new_multi_thread()
67        .thread_name("main-rt")
68        .enable_all()
69        .worker_threads(config.runtime.worker_threads)
70        .build()?;
71    let _runtime_guard = runtime.enter();
72
73    observability::init_tracing(&config);
74    tracing::debug!(?config);
75
76    let metrics_guard = observability::init_metrics(&config)?;
77
78    let result = runtime.block_on(async move {
79        match args.command {
80            Command::Run(RunCommand {}) => web::server(config).await,
81            Command::Healthcheck(HealthcheckCommand {}) => healthcheck::healthcheck(config).await,
82            Command::Version(VersionCommand {}) => unreachable!(),
83        }
84    });
85
86    // Flush metrics unconditionally before shutdown, even on error.
87    runtime.block_on(async {
88        if let Some(metrics_guard) = metrics_guard {
89            metrics_guard.flush(None).await.ok();
90        }
91    });
92
93    result
94}