164 lines
4.7 KiB
Rust
164 lines
4.7 KiB
Rust
// SPDX-FileCopyrightText: 2023 Aravinth Manivannan <realaravinth@batsense.net>
|
|
//
|
|
// SPDX-License-Identifier: AGPL-3.0-or-later
|
|
|
|
use std::collections::HashMap;
|
|
use std::process::{Command, Stdio};
|
|
|
|
#[derive(Default, Clone, Eq, PartialEq)]
|
|
pub struct Docker;
|
|
|
|
impl Docker {
|
|
pub fn new() -> Self {
|
|
Docker
|
|
}
|
|
}
|
|
|
|
impl Docker {
|
|
pub fn version(&self) -> String {
|
|
let version = Command::new("docker")
|
|
.arg("--version")
|
|
.output()
|
|
.expect("unable to obtain Docker version");
|
|
let x = String::from_utf8(version.stdout).unwrap();
|
|
let x: Vec<&str> = x.split("Docker version ").collect();
|
|
x.get(1).unwrap().trim().to_string()
|
|
}
|
|
|
|
pub fn run_container(
|
|
&self,
|
|
name: &str,
|
|
img: &str,
|
|
detached: bool,
|
|
env: &HashMap<String, String>,
|
|
network: Option<String>,
|
|
pull: bool,
|
|
) {
|
|
let mut env_args = Vec::with_capacity(env.len() * 2 + 6);
|
|
env_args.push("run".to_string());
|
|
if detached {
|
|
env_args.push("-d".to_string());
|
|
}
|
|
|
|
if pull {
|
|
env_args.push("--pull=always".into());
|
|
}
|
|
env_args.push("--name".to_string());
|
|
env_args.push(name.to_string());
|
|
for (k, v) in env.iter() {
|
|
env_args.push("-e".to_string());
|
|
env_args.push(format!("{k}={v}"));
|
|
}
|
|
|
|
if let Some(network) = network {
|
|
env_args.push("--network".into());
|
|
env_args.push(network);
|
|
}
|
|
|
|
env_args.push(img.to_string());
|
|
let mut child = Command::new("docker")
|
|
.args(&env_args)
|
|
.stdout(Stdio::null())
|
|
.stderr(Stdio::null())
|
|
.spawn()
|
|
.expect("unable to obtain Docker version");
|
|
child.wait().unwrap();
|
|
}
|
|
|
|
pub fn get_exit_status(&self, name: &str) -> isize {
|
|
let output = Command::new("docker")
|
|
.args(["inspect", name, "--format={{.State.ExitCode}}"])
|
|
.output()
|
|
.expect("unable to exit status");
|
|
let out = String::from_utf8(output.stdout).unwrap();
|
|
let out = out.trim();
|
|
out.parse::<isize>().unwrap()
|
|
}
|
|
pub fn get_logs(&self, name: &str) -> String {
|
|
let output = Command::new("docker")
|
|
.args(["logs", name])
|
|
.output()
|
|
.expect("unable to get logs");
|
|
String::from_utf8(output.stdout).unwrap()
|
|
}
|
|
|
|
pub async fn block_till_container_exists(&self, name: &str, mut timeout: usize) -> bool {
|
|
let args = ["container", "inspect", name, "--format={{.State.Status}}"];
|
|
loop {
|
|
let out = tokio::process::Command::new("docker")
|
|
.args(args)
|
|
.output()
|
|
.await
|
|
.unwrap_or_else(|_| panic!("unable to run docker command on container {name}"));
|
|
let out = String::from_utf8(out.stdout).unwrap();
|
|
let out = out.trim();
|
|
if out == "exited" {
|
|
return true;
|
|
}
|
|
|
|
if timeout == 0 {
|
|
return false;
|
|
}
|
|
tokio::time::sleep(std::time::Duration::new(1, 0)).await;
|
|
timeout -= 1;
|
|
}
|
|
}
|
|
|
|
pub fn rm_container(&self, name: &str, force: bool) {
|
|
let args = if force {
|
|
vec!["rm", "--force", name]
|
|
} else {
|
|
vec!["rm", name]
|
|
};
|
|
Command::new("docker")
|
|
.args(args)
|
|
.spawn()
|
|
.unwrap_or_else(|_| panic!("unable to remove docker container {name}"));
|
|
}
|
|
}
|
|
|
|
#[cfg(test)]
|
|
mod tests {
|
|
use crate::utils::get_random;
|
|
|
|
use super::*;
|
|
|
|
#[actix_rt::test]
|
|
async fn test_docker_util() {
|
|
let d = Docker::new();
|
|
d.version();
|
|
let name = format!("test_sleep__{}", get_random(4));
|
|
let mut env = HashMap::new();
|
|
env.insert("FOO".to_string(), "BAR".to_string());
|
|
env.insert("BAZ".to_string(), "BOO".to_string());
|
|
d.run_container(
|
|
&name,
|
|
"forgeflux/ftest-docker-cmd-tester",
|
|
true,
|
|
&env,
|
|
None,
|
|
true,
|
|
);
|
|
let out = Command::new("docker")
|
|
.args(["container", "inspect", "-f", "'{{.State.Running}}'", &name])
|
|
.output()
|
|
.unwrap();
|
|
let out = String::from_utf8(out.stdout).unwrap();
|
|
assert!(out.contains("true"));
|
|
|
|
loop {
|
|
if d.block_till_container_exists(&name, 13).await {
|
|
break;
|
|
}
|
|
}
|
|
|
|
let logs = d.get_logs(&name);
|
|
println!("{logs}");
|
|
assert!(logs.contains("running"));
|
|
assert!(logs.contains("FOO=BAR"));
|
|
assert!(logs.contains("BAZ=BOO"));
|
|
assert_eq!(d.get_exit_status(&name), 0);
|
|
d.rm_container(&name, true);
|
|
}
|
|
}
|