pub mod compose;
pub mod containers;
pub mod images;
pub mod inventory_cache;
pub mod networks;
use std::sync::OnceLock;
use anyhow::Result;
use bollard::Docker;
use tokio::process::Command;
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum Engine {
Docker,
Podman,
}
impl Engine {
pub fn detect(socket_path: &str) -> Self {
match std::env::var("RUSTPLOY_CONTAINER_ENGINE").as_deref() {
Ok("podman") => Engine::Podman,
Ok("docker") => Engine::Docker,
_ if socket_path.contains("podman") => Engine::Podman,
_ => Engine::Docker,
}
}
pub fn binary(self) -> &'static str {
match self {
Engine::Docker => "docker",
Engine::Podman => "podman",
}
}
}
static ENGINE: OnceLock<(Engine, String)> = OnceLock::new();
pub fn engine() -> Engine {
ENGINE.get().map_or(Engine::Docker, |(e, _)| *e)
}
pub fn cli() -> Command {
let mut cmd = Command::new(engine().binary());
if let Some((engine, socket)) = ENGINE.get() {
if *engine == Engine::Podman {
cmd.args(["--remote", "--url", &format!("unix://{socket}")]);
}
cmd.env("DOCKER_HOST", format!("unix://{socket}"));
}
cmd
}
pub struct DockerClient {
pub inner: Docker,
pub _socket_path: String,
}
impl DockerClient {
pub fn connect(socket_path: &str) -> Result<Self> {
let _ = ENGINE.set((Engine::detect(socket_path), socket_path.to_string()));
let docker = Docker::connect_with_unix(socket_path, 120, bollard::API_DEFAULT_VERSION)?;
Ok(Self {
inner: docker,
_socket_path: socket_path.to_string(),
})
}
#[cfg(test)]
pub fn for_tests() -> Self {
let path = std::env::temp_dir().join("rustploy-test-engine.sock");
let _ = std::fs::File::create(&path);
Self::connect(path.to_str().expect("caminho temporário em UTF-8"))
.expect("cliente de teste")
}
pub async fn ping(&self) -> Result<()> {
self.inner.ping().await?;
Ok(())
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn detect_pelo_caminho_do_socket() {
assert_eq!(Engine::detect("/var/run/docker.sock"), Engine::Docker);
assert_eq!(Engine::detect("/run/podman/podman.sock"), Engine::Podman);
}
}