kittynode_core/application/
start_docker_if_needed.rs

1use crate::application::is_docker_running;
2use crate::application::start_docker;
3use crate::domain::operational_state::OperationalMode;
4use crate::infra::config::ConfigStore;
5use eyre::Result;
6use std::sync::{LazyLock, Mutex};
7use tracing::info;
8
9#[derive(Clone, Copy, Debug, serde::Serialize, serde::Deserialize, PartialEq, Eq)]
10#[serde(rename_all = "snake_case")]
11pub enum DockerStartStatus {
12    Running,
13    Disabled,
14    AlreadyStarted,
15    Starting,
16}
17
18impl DockerStartStatus {
19    #[must_use]
20    pub fn as_str(self) -> &'static str {
21        match self {
22            DockerStartStatus::Running => "running",
23            DockerStartStatus::Disabled => "disabled",
24            DockerStartStatus::AlreadyStarted => "already_started",
25            DockerStartStatus::Starting => "starting",
26        }
27    }
28}
29
30static DOCKER_AUTO_STARTED: LazyLock<Mutex<bool>> = LazyLock::new(|| Mutex::new(false));
31
32/// Attempts to start Docker if the configuration allows auto start and Docker is not already running.
33pub async fn start_docker_if_needed() -> Result<DockerStartStatus> {
34    let config = ConfigStore::load()?;
35    let mode = if config.server_url.trim().is_empty() {
36        OperationalMode::Local
37    } else {
38        OperationalMode::Remote
39    };
40
41    if matches!(mode, OperationalMode::Remote) {
42        return Ok(DockerStartStatus::Running);
43    }
44
45    if is_docker_running().await {
46        let mut attempted = DOCKER_AUTO_STARTED
47            .lock()
48            .expect("docker auto-start mutex poisoned");
49        *attempted = false;
50        return Ok(DockerStartStatus::Running);
51    }
52    if !config.auto_start_docker {
53        return Ok(DockerStartStatus::Disabled);
54    }
55
56    {
57        let mut attempted = DOCKER_AUTO_STARTED
58            .lock()
59            .expect("docker auto-start mutex poisoned");
60        if *attempted {
61            return Ok(DockerStartStatus::AlreadyStarted);
62        }
63        *attempted = true;
64    }
65
66    info!("Starting Docker Desktop via auto-start preference");
67    match start_docker().await {
68        Ok(()) => Ok(DockerStartStatus::Starting),
69        Err(err) => {
70            let mut attempted = DOCKER_AUTO_STARTED
71                .lock()
72                .expect("docker auto-start mutex poisoned");
73            *attempted = false;
74            Err(err)
75        }
76    }
77}