autoschematic-core 0.8.2

Core shared functionality for Autoschematic: workflow engine, state management, and Git integrations
Documentation
use std::{
    collections::HashMap,
    path::{Path, PathBuf},
    sync::Arc,
    time::Duration,
};

use crate::{
    config::Spec,
    connector::{ConnectorInbox, handle::ConnectorHandle},
    keystore::KeyStore,
};
use anyhow::{Context, bail};
use rand::{Rng, distr::Alphanumeric};

#[cfg(target_os = "linux")]
pub mod sandbox;

pub mod unsandbox;

#[cfg(target_os = "linux")]
/// On Linux, the sandbox can be opted into by setting AUTOSCHEMATIC_SANDBOX=true
/// See autoschematic-core/src/connector/spawn/sandbox.rs for the sandboxing implementation.
pub fn is_sandbox_enabled() -> bool {
    match std::env::var("AUTOSCHEMATIC_SANDBOX") {
        Ok(s) if s == "true" => true,
        Ok(s) if s == "false" => false,
        Ok(_) => true,
        Err(_) => false,
    }
}

pub async fn spawn_connector(
    shortname: &str,
    spec: &Spec,
    prefix: &Path,
    env: &HashMap<String, String>,
    keystore: Option<Arc<dyn KeyStore>>,
) -> Result<(Arc<dyn ConnectorHandle>, ConnectorInbox), anyhow::Error> {
    let (outbox, inbox) = tokio::sync::broadcast::channel(64);

    #[cfg(target_os = "linux")]
    return Ok((
        if is_sandbox_enabled() {
            Arc::new(
                sandbox::launch_server_binary_sandboxed(spec, shortname, prefix, env, outbox, keystore)
                    .await
                    .context("launch_server_binary_sandboxed()")?,
            ) as Arc<dyn ConnectorHandle>
        } else {
            Arc::new(
                unsandbox::launch_server_binary(spec, shortname, prefix, env, outbox, keystore)
                    .await
                    .context("launch_server_binary()")?,
            ) as Arc<dyn ConnectorHandle>
        },
        inbox,
    ));

    #[cfg(not(target_os = "linux"))]
    return Ok((
        Arc::new(
            unsandbox::launch_server_binary(spec, shortname, prefix, env, outbox, keystore)
                .await
                .context("launch_server_binary()")?,
        ) as Arc<dyn ConnectorHandle>,
        inbox,
    ));
}

pub async fn wait_for_socket(socket: &Path, timeout: Duration) -> anyhow::Result<()> {
    let start_time = std::time::Instant::now();

    loop {
        if std::time::Instant::now() - start_time > timeout {
            bail!("Timed out waiting for socket after {:?}", timeout)
        }
        if socket.exists() {
            break;
        }
        std::thread::sleep(Duration::from_millis(10));
    }

    Ok(())
}

fn random_socket_path() -> PathBuf {
    loop {
        let socket_s: String = rand::rng().sample_iter(&Alphanumeric).take(20).map(char::from).collect();

        let mut socket = PathBuf::from("/tmp/").join(socket_s);

        socket.set_extension("sock");

        if let Ok(false) = socket.try_exists() {
            tracing::info!("Creating socket at {:?}", socket);
            return socket;
        }
    }
}

fn random_error_dump_path() -> PathBuf {
    loop {
        let dump_s: String = rand::rng().sample_iter(&Alphanumeric).take(20).map(char::from).collect();

        let mut dump = PathBuf::from("/tmp/").join(dump_s);

        dump.set_extension("dump");

        if let Ok(false) = dump.try_exists() {
            return dump;
        }
    }
}