mirror of
https://github.com/logos-blockchain/logos-blockchain-testing.git
synced 2026-01-02 13:23:13 +00:00
133 lines
3.6 KiB
Rust
133 lines
3.6 KiB
Rust
|
|
use std::{io, path::Path, process, time::Duration};
|
||
|
|
|
||
|
|
use testing_framework_core::adjust_timeout;
|
||
|
|
use tokio::{process::Command, time::timeout};
|
||
|
|
|
||
|
|
const COMPOSE_UP_TIMEOUT: Duration = Duration::from_secs(120);
|
||
|
|
|
||
|
|
/// Errors running docker compose commands.
|
||
|
|
#[derive(Debug, thiserror::Error)]
|
||
|
|
pub enum ComposeCommandError {
|
||
|
|
#[error("{command} exited with status {status}")]
|
||
|
|
Failed {
|
||
|
|
command: String,
|
||
|
|
status: process::ExitStatus,
|
||
|
|
},
|
||
|
|
#[error("failed to spawn {command}: {source}")]
|
||
|
|
Spawn {
|
||
|
|
command: String,
|
||
|
|
#[source]
|
||
|
|
source: io::Error,
|
||
|
|
},
|
||
|
|
#[error("{command} timed out after {timeout:?}")]
|
||
|
|
Timeout { command: String, timeout: Duration },
|
||
|
|
}
|
||
|
|
|
||
|
|
/// Runs `docker compose up -d` for the generated stack.
|
||
|
|
pub async fn compose_up(
|
||
|
|
compose_path: &Path,
|
||
|
|
project_name: &str,
|
||
|
|
root: &Path,
|
||
|
|
) -> Result<(), ComposeCommandError> {
|
||
|
|
let mut cmd = Command::new("docker");
|
||
|
|
cmd.arg("compose")
|
||
|
|
.arg("-f")
|
||
|
|
.arg(compose_path)
|
||
|
|
.arg("-p")
|
||
|
|
.arg(project_name)
|
||
|
|
.arg("up")
|
||
|
|
.arg("-d")
|
||
|
|
.current_dir(root);
|
||
|
|
|
||
|
|
run_compose_command(cmd, adjust_timeout(COMPOSE_UP_TIMEOUT), "docker compose up").await
|
||
|
|
}
|
||
|
|
|
||
|
|
/// Runs `docker compose down --volumes` for the generated stack.
|
||
|
|
pub async fn compose_down(
|
||
|
|
compose_path: &Path,
|
||
|
|
project_name: &str,
|
||
|
|
root: &Path,
|
||
|
|
) -> Result<(), ComposeCommandError> {
|
||
|
|
let mut cmd = Command::new("docker");
|
||
|
|
cmd.arg("compose")
|
||
|
|
.arg("-f")
|
||
|
|
.arg(compose_path)
|
||
|
|
.arg("-p")
|
||
|
|
.arg(project_name)
|
||
|
|
.arg("down")
|
||
|
|
.arg("--volumes")
|
||
|
|
.current_dir(root);
|
||
|
|
|
||
|
|
run_compose_command(
|
||
|
|
cmd,
|
||
|
|
adjust_timeout(COMPOSE_UP_TIMEOUT),
|
||
|
|
"docker compose down",
|
||
|
|
)
|
||
|
|
.await
|
||
|
|
}
|
||
|
|
|
||
|
|
/// Dump docker compose logs to stderr for debugging failures.
|
||
|
|
pub async fn dump_compose_logs(compose_file: &Path, project: &str, root: &Path) {
|
||
|
|
let mut cmd = Command::new("docker");
|
||
|
|
cmd.arg("compose")
|
||
|
|
.arg("-f")
|
||
|
|
.arg(compose_file)
|
||
|
|
.arg("-p")
|
||
|
|
.arg(project)
|
||
|
|
.arg("logs")
|
||
|
|
.arg("--no-color")
|
||
|
|
.current_dir(root);
|
||
|
|
|
||
|
|
match cmd.output().await {
|
||
|
|
Ok(output) => print_logs(&output.stdout, &output.stderr),
|
||
|
|
Err(err) => eprintln!("[compose-runner] failed to collect docker compose logs: {err}"),
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
fn print_logs(stdout: &[u8], stderr: &[u8]) {
|
||
|
|
if !stdout.is_empty() {
|
||
|
|
eprintln!(
|
||
|
|
"[compose-runner] docker compose logs:\n{}",
|
||
|
|
String::from_utf8_lossy(stdout)
|
||
|
|
);
|
||
|
|
}
|
||
|
|
if !stderr.is_empty() {
|
||
|
|
eprintln!(
|
||
|
|
"[compose-runner] docker compose errors:\n{}",
|
||
|
|
String::from_utf8_lossy(stderr)
|
||
|
|
);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
async fn run_compose_command(
|
||
|
|
mut command: Command,
|
||
|
|
timeout_duration: Duration,
|
||
|
|
description: &str,
|
||
|
|
) -> Result<(), ComposeCommandError> {
|
||
|
|
let result = timeout(timeout_duration, command.status()).await;
|
||
|
|
match result {
|
||
|
|
Ok(status) => handle_compose_status(status, description),
|
||
|
|
Err(_) => Err(ComposeCommandError::Timeout {
|
||
|
|
command: description.to_owned(),
|
||
|
|
timeout: timeout_duration,
|
||
|
|
}),
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
fn handle_compose_status(
|
||
|
|
status: std::io::Result<std::process::ExitStatus>,
|
||
|
|
description: &str,
|
||
|
|
) -> Result<(), ComposeCommandError> {
|
||
|
|
match status {
|
||
|
|
Ok(code) if code.success() => Ok(()),
|
||
|
|
Ok(code) => Err(ComposeCommandError::Failed {
|
||
|
|
command: description.to_owned(),
|
||
|
|
status: code,
|
||
|
|
}),
|
||
|
|
Err(err) => Err(ComposeCommandError::Spawn {
|
||
|
|
command: description.to_owned(),
|
||
|
|
source: err,
|
||
|
|
}),
|
||
|
|
}
|
||
|
|
}
|