objectstore_server/
cli.rs1use std::fs::File;
2use std::path::PathBuf;
3use std::thread;
4use std::time::Duration;
5
6use anyhow::Result;
7use argh::FromArgs;
8
9use crate::config::Config;
10use crate::endpoints::health::SHUTDOWN_MARKER_PATH;
11use crate::{healthcheck, observability, web};
12
13#[derive(Debug, FromArgs)]
15struct Args {
16 #[argh(option, short = 'c')]
18 pub config: Option<PathBuf>,
19
20 #[argh(subcommand)]
21 pub command: Command,
22}
23
24#[derive(Debug, FromArgs)]
25#[argh(subcommand)]
26enum Command {
27 Run(RunCommand),
28 Healthcheck(HealthcheckCommand),
29 Version(VersionCommand),
30 Sleep(SleepCommand),
31 Down(DownCommand),
32}
33
34#[derive(Debug, FromArgs)]
36#[argh(subcommand, name = "run")]
37struct RunCommand {}
38
39#[derive(Debug, FromArgs)]
44#[argh(subcommand, name = "healthcheck")]
45struct HealthcheckCommand {}
46
47#[derive(Default, Debug, FromArgs)]
49#[argh(subcommand, name = "version")]
50struct VersionCommand {}
51
52#[derive(Debug, FromArgs)]
54#[argh(subcommand, name = "sleep")]
55struct SleepCommand {
56 #[argh(positional)]
57 seconds: u64,
58}
59
60#[derive(Debug, FromArgs)]
62#[argh(subcommand, name = "down")]
63struct DownCommand {
64 #[argh(positional)]
66 sleep_seconds: Option<u64>,
67}
68
69pub fn execute() -> Result<()> {
71 let args: Args = argh::from_env();
72
73 if let Command::Version(_) = args.command {
75 println!("{}", env!("OBJECTSTORE_RELEASE"));
76 return Ok(());
77 }
78
79 if let Command::Sleep(SleepCommand { seconds }) = args.command {
80 thread::sleep(Duration::from_secs(seconds));
81 return Ok(());
82 }
83
84 if let Command::Down(DownCommand { sleep_seconds }) = args.command {
85 File::create(SHUTDOWN_MARKER_PATH)?;
86 if let Some(seconds) = sleep_seconds {
87 thread::sleep(Duration::from_secs(seconds));
88 }
89 return Ok(());
90 }
91
92 let config = Config::load(args.config.as_deref())?;
93
94 rustls::crypto::ring::default_provider()
96 .install_default()
97 .map_err(|_| anyhow::anyhow!("Failed to install rustls crypto provider"))?;
98
99 let _sentry_guard = observability::init_sentry(&config);
101
102 let runtime = tokio::runtime::Builder::new_multi_thread()
103 .thread_name("main-rt")
104 .enable_all()
105 .worker_threads(config.runtime.worker_threads)
106 .build()?;
107 let _runtime_guard = runtime.enter();
108
109 observability::init_tracing(&config);
110 tracing::debug!(?config);
111
112 let metrics_guard = observability::init_metrics(&config)?;
113
114 let result = runtime.block_on(async move {
115 match args.command {
116 Command::Run(RunCommand {}) => web::server(config).await,
117 Command::Healthcheck(HealthcheckCommand {}) => healthcheck::healthcheck(config).await,
118 Command::Version(VersionCommand {})
119 | Command::Sleep(SleepCommand { .. })
120 | Command::Down(DownCommand { .. }) => {
121 unreachable!()
122 }
123 }
124 });
125
126 runtime.block_on(async {
128 if let Some(metrics_guard) = metrics_guard {
129 metrics_guard.flush(None).await.ok();
130 }
131 });
132
133 result
134}