use std::path::Path;
use crate::error::Error;
use crate::podman::setup::{self, MACHINE_NAME};
#[derive(Clone, Copy, PartialEq, Eq)]
enum MachineState {
Absent,
Stopped,
Running,
}
pub async fn ensure_running(
machine_lock: &tokio::sync::Mutex<()>,
bin_dir: &Path,
exe: &Path,
) -> Result<(), Error> {
if std::env::consts::OS == "linux" {
return Ok(());
}
let helper_dir = exe.parent();
if machine_state(exe, helper_dir).await == MachineState::Running {
return Ok(());
}
let _in_process = machine_lock.lock().await;
let claim = objectiveai_sdk::lockfile::wait_acquire(
&bin_dir.join("locks"),
"podman-machine",
&format!("pid {}", std::process::id()),
)
.await
.map_err(|e| Error::Podman(format!("machine lock: {e}")))?;
let result = reconcile(exe, helper_dir).await;
claim
.release()
.map_err(|e| Error::Podman(format!("machine lock release: {e}")))?;
result
}
async fn reconcile(exe: &Path, helper_dir: Option<&Path>) -> Result<(), Error> {
match machine_state(exe, helper_dir).await {
MachineState::Running => Ok(()),
MachineState::Stopped => machine_start(exe, helper_dir).await,
MachineState::Absent => {
setup::machine_init(exe, helper_dir).await?;
machine_start(exe, helper_dir).await
}
}
}
async fn machine_state(exe: &Path, helper_dir: Option<&Path>) -> MachineState {
let output = setup::command(exe, helper_dir)
.arg("machine")
.arg("inspect")
.arg(MACHINE_NAME)
.arg("--format")
.arg("{{.State}}")
.output()
.await;
let Ok(output) = output else {
return MachineState::Stopped;
};
if !output.status.success() {
return MachineState::Absent;
}
if String::from_utf8_lossy(&output.stdout).trim() == "running" {
MachineState::Running
} else {
MachineState::Stopped
}
}
async fn machine_start(exe: &Path, helper_dir: Option<&Path>) -> Result<(), Error> {
let output = setup::command(exe, helper_dir)
.arg("machine")
.arg("start")
.arg(MACHINE_NAME)
.output()
.await
.map_err(|e| Error::Podman(format!("spawn podman machine start: {e}")))?;
if output.status.success() {
return Ok(());
}
let stderr = String::from_utf8_lossy(&output.stderr).to_ascii_lowercase();
if stderr.contains("already running") || stderr.contains("already started") {
return Ok(());
}
Err(Error::Podman(format!(
"podman machine start failed: {}",
String::from_utf8_lossy(&output.stderr).trim()
)))
}