// SPDX-FileCopyrightText: 2023 Aravinth Manivannan // // SPDX-License-Identifier: AGPL-3.0-or-later use std::path::PathBuf; use std::process::Command; use std::sync::Arc; use serde::{Deserialize, Serialize}; use serde_json::Value as JValue; use crate::docker::Docker; use crate::docker::DockerLike; #[derive(Debug, Eq, PartialEq, Clone, Serialize, Deserialize)] pub struct Container { pub service: String, pub name: String, pub image: String, } pub struct DockerCompose { base_dir: PathBuf, docker: Arc, } impl DockerCompose { pub fn new(base_dir: PathBuf, docker: Arc) -> Self { Self { base_dir, docker } } pub fn version() -> String { let version = Command::new("docker-compose") .arg("--version") .output() .expect("unable to obtain DockerCompose version"); let x = String::from_utf8(version.stdout).unwrap(); let x: Vec<&str> = x.split("Docker Compose version ").collect(); x.get(1).unwrap().trim().to_string() } pub fn services(&self) -> Vec { let ps = Command::new("docker-compose") .current_dir(&self.base_dir) .args(["ps", "--format", "json"]) .output() .expect("unable to obtain DockerCompose version"); let ps = String::from_utf8(ps.stdout).unwrap(); let x: serde_json::Value = serde_json::from_str(&ps).unwrap(); if let JValue::Array(val) = x { let mut containers = Vec::with_capacity(val.len()); for item in val.iter() { if let JValue::Object(val) = item { containers.push(Container { name: val["Name"].as_str().unwrap().to_string(), service: val["Service"].as_str().unwrap().to_string(), image: val["Image"].as_str().unwrap().to_string(), }) } } return containers; } return Vec::default(); } pub fn up(&self) { let mut child = Command::new("docker-compose") .current_dir(&self.base_dir) .args(["up", "--detach", "--remove-orphans"]) .spawn() .expect("unable to run DockerCompose"); child.wait().unwrap(); } pub fn logs(&self, service: &str) -> String { self.docker.get_logs(service) } pub fn down(&self, remove_orphans: bool, volumes: bool) { let mut opts = vec!["down"]; if remove_orphans { opts.push("--remove-orphans"); } if volumes { opts.push("--volumes"); } let mut child = Command::new("docker-compose") .current_dir(&self.base_dir) .args(opts) .spawn() .expect(&format!("unable to remove")); child.wait().unwrap(); } } #[cfg(test)] mod tests { use super::*; #[test] fn test_docker_compose() { DockerCompose::version(); let cmp = DockerCompose::new("./tests/".into(), Arc::new(Docker::new())); assert!(cmp.services().is_empty()); cmp.up(); let services = cmp.services(); std::thread::sleep(std::time::Duration::new(10, 0)); assert_eq!(services.len(), 2); for service in services.iter() { let logs = cmp.logs(&service.name); assert!(logs.contains(&format!("NAME={}", service.name))); } cmp.down(true, true); assert!(cmp.services().is_empty()); } }