2023-09-27 19:50:07 +05:30
|
|
|
// SPDX-FileCopyrightText: 2023 Aravinth Manivannan <realaravinth@batsense.net>
|
|
|
|
//
|
|
|
|
// SPDX-License-Identifier: AGPL-3.0-or-later
|
|
|
|
|
2023-09-06 13:34:02 +05:30
|
|
|
use std::path::PathBuf;
|
|
|
|
use std::process::Command;
|
2023-09-27 17:50:00 +05:30
|
|
|
use std::sync::Arc;
|
2023-09-06 13:34:02 +05:30
|
|
|
|
|
|
|
use serde::{Deserialize, Serialize};
|
|
|
|
use serde_json::Value as JValue;
|
|
|
|
|
2023-10-04 23:37:48 +05:30
|
|
|
use crate::docker::Docker;
|
2023-09-26 15:38:55 +05:30
|
|
|
|
2023-09-06 13:34:02 +05:30
|
|
|
#[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,
|
2023-10-04 23:37:48 +05:30
|
|
|
docker: Arc<Docker>,
|
2023-09-06 13:34:02 +05:30
|
|
|
}
|
|
|
|
|
|
|
|
impl DockerCompose {
|
2023-10-04 23:37:48 +05:30
|
|
|
pub fn new(base_dir: PathBuf, docker: Arc<Docker>) -> Self {
|
2023-09-27 17:50:00 +05:30
|
|
|
Self { base_dir, docker }
|
2023-09-06 13:34:02 +05:30
|
|
|
}
|
|
|
|
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<Container> {
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2023-09-29 19:18:20 +05:30
|
|
|
Vec::default()
|
2023-09-06 13:34:02 +05:30
|
|
|
}
|
|
|
|
|
|
|
|
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 {
|
2023-09-27 17:50:00 +05:30
|
|
|
self.docker.get_logs(service)
|
2023-09-27 16:35:46 +05:30
|
|
|
}
|
2023-09-06 13:34:02 +05:30
|
|
|
|
|
|
|
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");
|
|
|
|
}
|
2023-09-26 15:38:55 +05:30
|
|
|
let mut child = Command::new("docker-compose")
|
2023-09-06 13:34:02 +05:30
|
|
|
.current_dir(&self.base_dir)
|
|
|
|
.args(opts)
|
|
|
|
.spawn()
|
2023-09-29 19:18:20 +05:30
|
|
|
.expect("unable to remove");
|
2023-09-26 15:38:55 +05:30
|
|
|
child.wait().unwrap();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
#[cfg(test)]
|
|
|
|
mod tests {
|
|
|
|
use super::*;
|
|
|
|
|
2023-09-29 19:18:20 +05:30
|
|
|
use crate::docker::Docker;
|
|
|
|
|
2023-09-26 15:38:55 +05:30
|
|
|
#[test]
|
|
|
|
fn test_docker_compose() {
|
|
|
|
DockerCompose::version();
|
2023-09-27 17:50:00 +05:30
|
|
|
let cmp = DockerCompose::new("./tests/".into(), Arc::new(Docker::new()));
|
2023-09-26 15:38:55 +05:30
|
|
|
assert!(cmp.services().is_empty());
|
|
|
|
cmp.up();
|
|
|
|
let services = cmp.services();
|
2023-09-27 16:35:46 +05:30
|
|
|
std::thread::sleep(std::time::Duration::new(10, 0));
|
2023-09-26 15:38:55 +05:30
|
|
|
assert_eq!(services.len(), 2);
|
|
|
|
for service in services.iter() {
|
2023-09-27 16:35:46 +05:30
|
|
|
let logs = cmp.logs(&service.name);
|
|
|
|
assert!(logs.contains(&format!("NAME={}", service.name)));
|
2023-09-26 15:38:55 +05:30
|
|
|
}
|
|
|
|
cmp.down(true, true);
|
|
|
|
assert!(cmp.services().is_empty());
|
2023-09-06 13:34:02 +05:30
|
|
|
}
|
|
|
|
}
|