use clap::Parser;
use miette::Result;
use bestool_tamanu::services::{self, ExpectedState, Expectation, Supervisor};
use crate::actions::{
Context,
tamanu::{
TamanuArgs,
lifecycle::{self, Instance, WaitForDb},
},
};
#[derive(Debug, Clone, Parser)]
#[clap(verbatim_doc_comment)]
pub struct StopArgs {
pub names: Vec<String>,
}
pub async fn run(args: StopArgs, ctx: Context) -> Result<()> {
let tamanu = ctx.require::<TamanuArgs>();
let (supervisor, expectations) =
lifecycle::config_and_expectations(tamanu, WaitForDb::No).await?;
let names: Vec<&str> = args.names.iter().map(String::as_str).collect();
let matched = services::match_names(&expectations, &names)?;
lifecycle::warn_unknown_expectations(&matched);
let discovered = lifecycle::discover(supervisor).await?;
let groups = lifecycle::group_by_expectation(&matched, &discovered);
let targets = plan_stop(supervisor, &groups);
if targets.is_empty() {
tracing::info!("nothing to stop; everything matched is already down");
return Ok(());
}
lifecycle::ensure_root_or_reexec(supervisor)?;
tracing::info!(?targets, "stopping");
lifecycle::stop_targets(supervisor, &targets).await?;
lifecycle::wait_stopped(supervisor, &targets).await?;
Ok(())
}
fn plan_stop(
supervisor: Supervisor,
groups: &[(&Expectation, Vec<Instance>)],
) -> Vec<String> {
let mut targets = Vec::new();
for (exp, instances) in groups {
if exp.state != ExpectedState::Up {
continue;
}
for inst in instances {
if !inst.running {
continue;
}
let id = match supervisor {
Supervisor::Systemd => inst.unit(),
Supervisor::Pm2 => inst.name.clone(),
};
targets.push(id);
}
}
targets
}