port_plumber/
cmd_resource.rs

1use 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}