objectstore_server/
cli.rs

1//! Command-line interface for the objectstore server.
2//!
3//! This module defines the CLI argument structure and dispatches to the
4//! appropriate command implementation. The main entry point is [`execute`].
5
6use std::fs::File;
7use std::path::PathBuf;
8use std::thread;
9use std::time::Duration;
10
11use anyhow::Result;
12use argh::FromArgs;
13
14use crate::config::Config;
15use crate::endpoints::health::SHUTDOWN_MARKER_PATH;
16use crate::{healthcheck, observability, web};
17
18/// Objectstore API webserver.
19#[derive(Debug, FromArgs)]
20struct Args {
21    /// path to the YAML configuration file
22    #[argh(option, short = 'c')]
23    pub config: Option<PathBuf>,
24
25    #[argh(subcommand)]
26    pub command: Command,
27}
28
29#[derive(Debug, FromArgs)]
30#[argh(subcommand)]
31enum Command {
32    Run(RunCommand),
33    Healthcheck(HealthcheckCommand),
34    Version(VersionCommand),
35    Sleep(SleepCommand),
36    Down(DownCommand),
37}
38
39/// run the objectstore web server
40#[derive(Debug, FromArgs)]
41#[argh(subcommand, name = "run")]
42struct RunCommand {}
43
44/// perform a healthcheck against the running objectstore web server
45///
46/// This command checks if the objectstore server is available on the configured host and port. This
47/// is used for Docker healthchecks.
48#[derive(Debug, FromArgs)]
49#[argh(subcommand, name = "healthcheck")]
50struct HealthcheckCommand {}
51
52/// print the objectstore server version
53#[derive(Default, Debug, FromArgs)]
54#[argh(subcommand, name = "version")]
55struct VersionCommand {}
56
57/// sleep for the specified number of seconds
58#[derive(Debug, FromArgs)]
59#[argh(subcommand, name = "sleep")]
60struct SleepCommand {
61    #[argh(positional)]
62    seconds: u64,
63}
64
65/// mark the server as not ready
66#[derive(Debug, FromArgs)]
67#[argh(subcommand, name = "down")]
68struct DownCommand {
69    /// seconds to sleep after creating the marker file
70    #[argh(positional)]
71    sleep_seconds: Option<u64>,
72}
73
74/// Bootstrap the runtime and execute the CLI command.
75pub fn execute() -> Result<()> {
76    let args: Args = argh::from_env();
77
78    // Special switch to just print the version and exit.
79    if let Command::Version(_) = args.command {
80        println!("{}", env!("OBJECTSTORE_RELEASE"));
81        return Ok(());
82    }
83
84    if let Command::Sleep(SleepCommand { seconds }) = args.command {
85        thread::sleep(Duration::from_secs(seconds));
86        return Ok(());
87    }
88
89    if let Command::Down(DownCommand { sleep_seconds }) = args.command {
90        File::create(SHUTDOWN_MARKER_PATH)?;
91        if let Some(seconds) = sleep_seconds {
92            thread::sleep(Duration::from_secs(seconds));
93        }
94        return Ok(());
95    }
96
97    let config = Config::load(args.config.as_deref())?;
98
99    // Ensure a rustls crypto provider is installed, required on distroless.
100    rustls::crypto::ring::default_provider()
101        .install_default()
102        .map_err(|_| anyhow::anyhow!("Failed to install rustls crypto provider"))?;
103
104    // Sentry should be initialized before creating the async runtime.
105    let _sentry_guard = observability::init_sentry(&config);
106
107    let runtime = tokio::runtime::Builder::new_multi_thread()
108        .thread_name("main-rt")
109        .enable_all()
110        .worker_threads(config.runtime.worker_threads)
111        .build()?;
112    let _runtime_guard = runtime.enter();
113
114    observability::init_tracing(&config);
115    tracing::debug!(?config);
116
117    objectstore_metrics::init(&config.metrics)?;
118
119    runtime.block_on(async move {
120        match args.command {
121            Command::Run(RunCommand {}) => web::server(config).await,
122            Command::Healthcheck(HealthcheckCommand {}) => healthcheck::healthcheck(config).await,
123            Command::Version(VersionCommand {})
124            | Command::Sleep(SleepCommand { .. })
125            | Command::Down(DownCommand { .. }) => {
126                unreachable!()
127            }
128        }
129    })
130}