port_plumber/
cmd_resource.rs1use std::time::Duration;
2
3use crate::config::ResourceConfig;
4use crate::healthcheck::HealthcheckCommand;
5use crate::runner::CmdRunner;
6
7pub enum CmdResource {
8 Empty,
9 Command {
10 runner: CmdRunner,
11 warmup: Duration,
12 healthcheck: Option<HealthcheckCommand>,
13 }
14}
15
16impl TryFrom<Option<&ResourceConfig>> for CmdResource {
17 type Error = anyhow::Error;
18
19 fn try_from(value: Option<&ResourceConfig>) -> Result<Self, Self::Error> {
20 let Some(cfg) = value else {
21 return Ok(Self::Empty)
22 };
23 Ok(Self::Command {
24 runner: CmdRunner::build(&cfg.setup.command, &cfg.setup.args, &cfg.setup.workingdir)?,
25 warmup: Duration::from_millis(cfg.warmup_millis),
26 healthcheck: cfg.healthcheck_cmd.clone().and_then(|conf| HealthcheckCommand::new(conf).ok()),
27 })
28 }
29}
30
31impl CmdResource {
32 pub async fn ensure_running(&mut self) -> anyhow::Result<()> {
33 let Self::Command { runner, warmup, healthcheck } = self else {
34 return Ok(())
35 };
36 if !runner.is_running()? {
37 log::debug!("spawning command");
38 runner.start()?;
39 tokio::time::sleep(*warmup).await;
40 if let Some(healthcheck) = healthcheck {
41 let wait_out = healthcheck.wait_until_healthy().await;
42 if let Err(err) = wait_out {
43 log::error!("Error waiting process startup - {err}");
44 }
45 }
46 Ok(())
47 } else {
48 Ok(())
49 }
50 }
51
52 pub fn ensure_stopped(&mut self) -> anyhow::Result<()> {
53 let Self::Command { runner, .. } = self else {
54 return Ok(())
55 };
56 if runner.is_running()? {
57 log::debug!("stopping command");
58 runner.stop()
59 } else {
60 Ok(())
61 }
62 }
63}