feat: rely on DockerLike trait for container ops
This commit is contained in:
parent
44a97e8928
commit
8ad1be03a8
4 changed files with 88 additions and 29 deletions
23
src/ctx.rs
23
src/ctx.rs
|
@ -1,3 +1,4 @@
|
||||||
|
use std::collections::HashMap;
|
||||||
/*
|
/*
|
||||||
* Copyright (C) 2022 Aravinth Manivannan <realaravinth@batsense.net>
|
* Copyright (C) 2022 Aravinth Manivannan <realaravinth@batsense.net>
|
||||||
*
|
*
|
||||||
|
@ -14,13 +15,17 @@
|
||||||
* You should have received a copy of the GNU Affero General Public License
|
* You should have received a copy of the GNU Affero General Public License
|
||||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
use std::sync::Arc;
|
use std::sync::{Arc, RwLock};
|
||||||
use std::thread;
|
|
||||||
|
use reqwest::Client;
|
||||||
|
use tokio::sync::mpsc::Sender;
|
||||||
|
|
||||||
use crate::db::*;
|
use crate::db::*;
|
||||||
|
use crate::docker::Docker;
|
||||||
|
use crate::docker::DockerLike;
|
||||||
use crate::settings::Settings;
|
use crate::settings::Settings;
|
||||||
use reqwest::Client;
|
|
||||||
use tracing::info;
|
use super::complaince::result::Result as CResult;
|
||||||
|
|
||||||
pub type ArcCtx = Arc<Ctx>;
|
pub type ArcCtx = Arc<Ctx>;
|
||||||
|
|
||||||
|
@ -28,19 +33,23 @@ pub type ArcCtx = Arc<Ctx>;
|
||||||
pub struct Ctx {
|
pub struct Ctx {
|
||||||
pub settings: Settings,
|
pub settings: Settings,
|
||||||
pub db: Database,
|
pub db: Database,
|
||||||
client: Client,
|
pub client: Client,
|
||||||
|
pub results: Arc<RwLock<HashMap<String, Sender<CResult>>>>,
|
||||||
|
pub docker: Arc<dyn DockerLike>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Ctx {
|
impl Ctx {
|
||||||
pub async fn new(settings: Settings) -> Arc<Self> {
|
pub async fn new(settings: Settings) -> Arc<Self> {
|
||||||
|
let results = HashMap::default();
|
||||||
|
let results = Arc::new(RwLock::new(results));
|
||||||
let client = Client::default();
|
let client = Client::default();
|
||||||
let db = get_db(&settings).await;
|
let db = get_db(&settings).await;
|
||||||
#[cfg(not(debug_assertions))]
|
|
||||||
init.join();
|
|
||||||
Arc::new(Self {
|
Arc::new(Self {
|
||||||
settings,
|
settings,
|
||||||
client,
|
client,
|
||||||
db,
|
db,
|
||||||
|
results,
|
||||||
|
docker: Arc::new(Docker::new()),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,10 +1,33 @@
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
use std::process::Command;
|
use std::process::Command;
|
||||||
|
|
||||||
|
#[derive(Default, Clone, Eq, PartialEq)]
|
||||||
pub struct Docker;
|
pub struct Docker;
|
||||||
|
|
||||||
impl Docker {
|
impl Docker {
|
||||||
pub fn version() -> String {
|
pub fn new() -> Self {
|
||||||
|
Docker
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub trait DockerLike: std::marker::Send + std::marker::Sync + CloneDockerLike {
|
||||||
|
fn version(&self) -> String;
|
||||||
|
fn run_container(
|
||||||
|
&self,
|
||||||
|
name: &str,
|
||||||
|
img: &str,
|
||||||
|
detached: bool,
|
||||||
|
env: &HashMap<String, String>,
|
||||||
|
network: Option<String>,
|
||||||
|
pull: bool,
|
||||||
|
);
|
||||||
|
fn get_exit_status(&self, name: &str) -> isize;
|
||||||
|
fn get_logs(&self, name: &str) -> String;
|
||||||
|
fn rm_container(&self, name: &str, force: bool);
|
||||||
|
}
|
||||||
|
|
||||||
|
impl DockerLike for Docker {
|
||||||
|
fn version(&self) -> String {
|
||||||
let version = Command::new("docker")
|
let version = Command::new("docker")
|
||||||
.arg("--version")
|
.arg("--version")
|
||||||
.output()
|
.output()
|
||||||
|
@ -14,7 +37,8 @@ impl Docker {
|
||||||
x.get(1).unwrap().trim().to_string()
|
x.get(1).unwrap().trim().to_string()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn run_container(
|
fn run_container(
|
||||||
|
&self,
|
||||||
name: &str,
|
name: &str,
|
||||||
img: &str,
|
img: &str,
|
||||||
detached: bool,
|
detached: bool,
|
||||||
|
@ -51,7 +75,7 @@ impl Docker {
|
||||||
child.wait().unwrap();
|
child.wait().unwrap();
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_exit_status(name: &str) -> isize {
|
fn get_exit_status(&self, name: &str) -> isize {
|
||||||
let output = Command::new("docker")
|
let output = Command::new("docker")
|
||||||
.args(["inspect", name, "--format={{.State.ExitCode}}"])
|
.args(["inspect", name, "--format={{.State.ExitCode}}"])
|
||||||
.output()
|
.output()
|
||||||
|
@ -60,7 +84,7 @@ impl Docker {
|
||||||
let out = out.trim();
|
let out = out.trim();
|
||||||
out.parse::<isize>().unwrap()
|
out.parse::<isize>().unwrap()
|
||||||
}
|
}
|
||||||
pub fn get_logs(name: &str) -> String {
|
fn get_logs(&self, name: &str) -> String {
|
||||||
let output = Command::new("docker")
|
let output = Command::new("docker")
|
||||||
.args(["logs", name])
|
.args(["logs", name])
|
||||||
.output()
|
.output()
|
||||||
|
@ -68,7 +92,7 @@ impl Docker {
|
||||||
String::from_utf8(output.stdout).unwrap()
|
String::from_utf8(output.stdout).unwrap()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn rm_container(name: &str, force: bool) {
|
fn rm_container(&self, name: &str, force: bool) {
|
||||||
let args = if force {
|
let args = if force {
|
||||||
vec!["rm", name]
|
vec!["rm", name]
|
||||||
} else {
|
} else {
|
||||||
|
@ -81,6 +105,26 @@ impl Docker {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub trait CloneDockerLike {
|
||||||
|
/// clone DB
|
||||||
|
fn clone_docker_like(&self) -> Box<dyn DockerLike>;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T> CloneDockerLike for T
|
||||||
|
where
|
||||||
|
T: DockerLike + Clone + 'static,
|
||||||
|
{
|
||||||
|
fn clone_docker_like(&self) -> Box<dyn DockerLike> {
|
||||||
|
Box::new(self.clone())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Clone for Box<dyn DockerLike> {
|
||||||
|
fn clone(&self) -> Self {
|
||||||
|
(**self).clone_docker_like()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use crate::utils::get_random;
|
use crate::utils::get_random;
|
||||||
|
@ -89,12 +133,13 @@ mod tests {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_docker_util() {
|
fn test_docker_util() {
|
||||||
Docker::version();
|
let d = Docker::new();
|
||||||
|
d.version();
|
||||||
let name = format!("test_sleep__{}", get_random(4));
|
let name = format!("test_sleep__{}", get_random(4));
|
||||||
let mut env = HashMap::new();
|
let mut env = HashMap::new();
|
||||||
env.insert("FOO".to_string(), "BAR".to_string());
|
env.insert("FOO".to_string(), "BAR".to_string());
|
||||||
env.insert("BAZ".to_string(), "BOO".to_string());
|
env.insert("BAZ".to_string(), "BOO".to_string());
|
||||||
Docker::run_container(
|
d.run_container(
|
||||||
&name,
|
&name,
|
||||||
"forgeflux/ftest-dev-docker-cmd",
|
"forgeflux/ftest-dev-docker-cmd",
|
||||||
true,
|
true,
|
||||||
|
@ -109,12 +154,12 @@ mod tests {
|
||||||
let out = String::from_utf8(out.stdout).unwrap();
|
let out = String::from_utf8(out.stdout).unwrap();
|
||||||
assert!(out.contains("true"));
|
assert!(out.contains("true"));
|
||||||
std::thread::sleep(std::time::Duration::new(10, 0));
|
std::thread::sleep(std::time::Duration::new(10, 0));
|
||||||
let logs = Docker::get_logs(&name);
|
let logs = d.get_logs(&name);
|
||||||
println!("{logs}");
|
println!("{logs}");
|
||||||
assert!(logs.contains("running"));
|
assert!(logs.contains("running"));
|
||||||
assert!(logs.contains("FOO=BAR"));
|
assert!(logs.contains("FOO=BAR"));
|
||||||
assert!(logs.contains("BAZ=BOO"));
|
assert!(logs.contains("BAZ=BOO"));
|
||||||
assert_eq!(Docker::get_exit_status(&name), 0);
|
assert_eq!(d.get_exit_status(&name), 0);
|
||||||
Docker::rm_container(&name, true);
|
d.rm_container(&name, true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,10 +1,12 @@
|
||||||
use std::path::PathBuf;
|
use std::path::PathBuf;
|
||||||
use std::process::Command;
|
use std::process::Command;
|
||||||
|
use std::sync::Arc;
|
||||||
|
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use serde_json::Value as JValue;
|
use serde_json::Value as JValue;
|
||||||
|
|
||||||
use crate::docker::Docker;
|
use crate::docker::Docker;
|
||||||
|
use crate::docker::DockerLike;
|
||||||
|
|
||||||
#[derive(Debug, Eq, PartialEq, Clone, Serialize, Deserialize)]
|
#[derive(Debug, Eq, PartialEq, Clone, Serialize, Deserialize)]
|
||||||
pub struct Container {
|
pub struct Container {
|
||||||
|
@ -15,11 +17,12 @@ pub struct Container {
|
||||||
|
|
||||||
pub struct DockerCompose {
|
pub struct DockerCompose {
|
||||||
base_dir: PathBuf,
|
base_dir: PathBuf,
|
||||||
|
docker: Arc<dyn DockerLike>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl DockerCompose {
|
impl DockerCompose {
|
||||||
pub fn new(base_dir: PathBuf) -> Self {
|
pub fn new(base_dir: PathBuf, docker: Arc<dyn DockerLike>) -> Self {
|
||||||
Self { base_dir }
|
Self { base_dir, docker }
|
||||||
}
|
}
|
||||||
pub fn version() -> String {
|
pub fn version() -> String {
|
||||||
let version = Command::new("docker-compose")
|
let version = Command::new("docker-compose")
|
||||||
|
@ -67,7 +70,7 @@ impl DockerCompose {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn logs(&self, service: &str) -> String {
|
pub fn logs(&self, service: &str) -> String {
|
||||||
Docker::get_logs(service)
|
self.docker.get_logs(service)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn down(&self, remove_orphans: bool, volumes: bool) {
|
pub fn down(&self, remove_orphans: bool, volumes: bool) {
|
||||||
|
@ -95,7 +98,7 @@ mod tests {
|
||||||
#[test]
|
#[test]
|
||||||
fn test_docker_compose() {
|
fn test_docker_compose() {
|
||||||
DockerCompose::version();
|
DockerCompose::version();
|
||||||
let cmp = DockerCompose::new("./tests/".into());
|
let cmp = DockerCompose::new("./tests/".into(), Arc::new(Docker::new()));
|
||||||
assert!(cmp.services().is_empty());
|
assert!(cmp.services().is_empty());
|
||||||
cmp.up();
|
cmp.up();
|
||||||
let services = cmp.services();
|
let services = cmp.services();
|
||||||
|
|
|
@ -2,11 +2,13 @@ use std::collections::HashMap;
|
||||||
|
|
||||||
use crate::complaince::target::Target;
|
use crate::complaince::target::Target;
|
||||||
use crate::docker::Docker;
|
use crate::docker::Docker;
|
||||||
|
use crate::docker::DockerLike;
|
||||||
use crate::utils::get_random;
|
use crate::utils::get_random;
|
||||||
|
use crate::AppCtx;
|
||||||
|
|
||||||
use super::results::{ArchivableContainer, ArchivableInitResult};
|
use super::results::{ArchivableContainer, ArchivableInitResult};
|
||||||
|
|
||||||
pub fn launch_init_containers(target: &Target) -> Option<Vec<ArchivableInitResult>> {
|
pub fn launch_init_containers(ctx: &AppCtx, target: &Target) -> Option<Vec<ArchivableInitResult>> {
|
||||||
if let Some(init_scripts) = target.init_scripts.as_ref() {
|
if let Some(init_scripts) = target.init_scripts.as_ref() {
|
||||||
let mut init_results = Vec::with_capacity(init_scripts.len());
|
let mut init_results = Vec::with_capacity(init_scripts.len());
|
||||||
for init in init_scripts.iter() {
|
for init in init_scripts.iter() {
|
||||||
|
@ -17,7 +19,7 @@ pub fn launch_init_containers(target: &Target) -> Option<Vec<ArchivableInitResul
|
||||||
env.extend(custom_vars);
|
env.extend(custom_vars);
|
||||||
}
|
}
|
||||||
let name = format!("{}--{}", init.name, &auth[0..5]);
|
let name = format!("{}--{}", init.name, &auth[0..5]);
|
||||||
Docker::run_container(
|
ctx.docker.run_container(
|
||||||
&name,
|
&name,
|
||||||
&init.container,
|
&init.container,
|
||||||
false,
|
false,
|
||||||
|
@ -25,9 +27,9 @@ pub fn launch_init_containers(target: &Target) -> Option<Vec<ArchivableInitResul
|
||||||
Some("ftest".to_string()),
|
Some("ftest".to_string()),
|
||||||
true,
|
true,
|
||||||
);
|
);
|
||||||
let logs = Docker::get_logs(&name);
|
let logs = ctx.docker.get_logs(&name);
|
||||||
let exit_code = Docker::get_exit_status(&name);
|
let exit_code = ctx.docker.get_exit_status(&name);
|
||||||
Docker::rm_container(&name, true);
|
ctx.docker.rm_container(&name, true);
|
||||||
let c = ArchivableInitResult {
|
let c = ArchivableInitResult {
|
||||||
success: exit_code == 0,
|
success: exit_code == 0,
|
||||||
exit_code,
|
exit_code,
|
||||||
|
@ -56,8 +58,8 @@ mod tests {
|
||||||
|
|
||||||
#[actix_rt::test]
|
#[actix_rt::test]
|
||||||
async fn launch_init_containers_works() {
|
async fn launch_init_containers_works() {
|
||||||
// let settings = Settings::new().unwrap();
|
let settings = Settings::new().unwrap();
|
||||||
// let ctx = AppCtx::new(Ctx::new(settings.clone()).await);
|
let ctx = AppCtx::new(Ctx::new(settings.clone()).await);
|
||||||
// let base_dir = Path::new(&ctx.settings.repository.base_dir);
|
// let base_dir = Path::new(&ctx.settings.repository.base_dir);
|
||||||
// let control = base_dir.join("control");
|
// let control = base_dir.join("control");
|
||||||
|
|
||||||
|
@ -84,7 +86,7 @@ mod tests {
|
||||||
suites: Vec::default(),
|
suites: Vec::default(),
|
||||||
};
|
};
|
||||||
|
|
||||||
let init = launch_init_containers(&target);
|
let init = launch_init_containers(&ctx, &target);
|
||||||
assert!(init.is_some());
|
assert!(init.is_some());
|
||||||
let init = init.unwrap();
|
let init = init.unwrap();
|
||||||
assert_eq!(init.len(), 1);
|
assert_eq!(init.len(), 1);
|
||||||
|
|
Loading…
Add table
Reference in a new issue