Restructure compose runner modules

This commit is contained in:
andrussal 2025-12-10 15:26:34 +01:00
parent e7c4bccaa6
commit daa8f6a129
24 changed files with 143 additions and 118 deletions

View File

@ -5,8 +5,11 @@ use testing_framework_core::{
use tracing::info;
use crate::{
block_feed::spawn_block_feed_with_retry, environment::StackEnvironment,
errors::ComposeRunnerError, ports::HostPortMapping, readiness::build_node_clients_with_ports,
errors::ComposeRunnerError,
infrastructure::{environment::StackEnvironment, ports::HostPortMapping},
lifecycle::{
block_feed::spawn_block_feed_with_retry, readiness::build_node_clients_with_ports,
},
};
pub struct ClientBuilder;

View File

@ -9,7 +9,7 @@ use testing_framework_core::scenario::{
BlockFeedTask, CleanupGuard, Deployer, RequiresNodeControl, Runner, Scenario,
};
use crate::{cleanup::RunnerCleanup, errors::ComposeRunnerError};
use crate::{errors::ComposeRunnerError, lifecycle::cleanup::RunnerCleanup};
/// Docker Compose-based deployer for Nomos test scenarios.
#[derive(Clone, Copy)]

View File

@ -14,8 +14,10 @@ use super::{
setup::{DeploymentContext, DeploymentSetup},
};
use crate::{
control::ComposeNodeControl, errors::ComposeRunnerError, ports::compose_runner_host,
readiness::metrics_handle_from_port,
docker::control::ComposeNodeControl,
errors::ComposeRunnerError,
infrastructure::{environment::StackEnvironment, ports::compose_runner_host},
lifecycle::readiness::metrics_handle_from_port,
};
pub struct DeploymentOrchestrator {
@ -48,7 +50,7 @@ impl DeploymentOrchestrator {
ReadinessChecker::wait_all(&descriptors, &host_ports, &mut environment).await?;
} else {
info!("readiness checks disabled; giving the stack a short grace period");
crate::readiness::maybe_sleep_for_disabled_readiness(false).await;
crate::lifecycle::readiness::maybe_sleep_for_disabled_readiness(false).await;
}
let host = compose_runner_host();
@ -79,7 +81,7 @@ impl DeploymentOrchestrator {
fn maybe_node_control<Caps>(
&self,
environment: &crate::environment::StackEnvironment,
environment: &StackEnvironment,
) -> Option<Arc<dyn NodeControlHandle>>
where
Caps: RequiresNodeControl + Send + Sync,

View File

@ -2,9 +2,11 @@ use testing_framework_core::topology::generation::GeneratedTopology;
use tracing::{debug, info};
use crate::{
environment::StackEnvironment,
errors::ComposeRunnerError,
ports::{HostPortMapping, discover_host_ports},
infrastructure::{
environment::StackEnvironment,
ports::{HostPortMapping, discover_host_ports},
},
};
pub struct PortManager;

View File

@ -2,10 +2,12 @@ use testing_framework_core::topology::generation::GeneratedTopology;
use tracing::info;
use crate::{
environment::StackEnvironment,
errors::ComposeRunnerError,
ports::{HostPortMapping, ensure_remote_readiness_with_ports},
readiness::{ensure_executors_ready_with_ports, ensure_validators_ready_with_ports},
infrastructure::{
environment::StackEnvironment,
ports::{HostPortMapping, ensure_remote_readiness_with_ports},
},
lifecycle::readiness::{ensure_executors_ready_with_ports, ensure_validators_ready_with_ports},
};
pub struct ReadinessChecker;

View File

@ -8,10 +8,10 @@ use tracing::info;
use crate::{
docker::ensure_docker_available,
environment::{
errors::ComposeRunnerError,
infrastructure::environment::{
PortReservation, StackEnvironment, ensure_supported_topology, prepare_environment,
},
errors::ComposeRunnerError,
};
pub const PROMETHEUS_PORT_ENV: &str = "TEST_FRAMEWORK_PROMETHEUS_PORT";

View File

@ -4,7 +4,7 @@ use testing_framework_core::{
topology::generation::{GeneratedNodeConfig, GeneratedTopology},
};
use crate::platform::{host_gateway_entry, resolve_image};
use crate::docker::platform::{host_gateway_entry, resolve_image};
mod node;

View File

@ -23,6 +23,22 @@ pub enum ComposeCommandError {
Timeout { command: String, timeout: Duration },
}
/// Run an arbitrary docker command with a timeout.
pub async fn run_docker_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,
}),
}
}
/// Runs `docker compose up -d` for the generated stack.
pub async fn compose_up(
compose_path: &Path,

View File

@ -3,7 +3,7 @@ use std::path::{Path, PathBuf};
use testing_framework_core::scenario::{DynError, NodeControlHandle};
use tokio::process::Command;
use crate::{docker::run_docker_command, errors::ComposeRunnerError};
use crate::{docker::commands::run_docker_command, errors::ComposeRunnerError};
pub async fn restart_compose_service(
compose_file: &Path,
@ -23,10 +23,11 @@ pub async fn restart_compose_service(
let description = "docker compose restart";
run_docker_command(
command,
description,
testing_framework_core::adjust_timeout(std::time::Duration::from_secs(120)),
description,
)
.await
.map_err(ComposeRunnerError::Compose)
}
/// Compose-specific node control handle for restarting nodes.

View File

@ -1,13 +1,16 @@
use std::{
env,
process::{Command as StdCommand, Stdio},
time::Duration,
};
pub mod commands;
pub mod control;
pub mod platform;
pub mod workspace;
use std::{env, process::Stdio, time::Duration};
use tokio::{process::Command, time::timeout};
use tracing::warn;
use crate::{commands::ComposeCommandError, errors::ComposeRunnerError, template::repository_root};
use crate::{
docker::commands::ComposeCommandError, errors::ComposeRunnerError,
infrastructure::template::repository_root,
};
const IMAGE_BUILD_TIMEOUT: Duration = Duration::from_secs(600);
const DOCKER_INFO_TIMEOUT: Duration = Duration::from_secs(15);
@ -40,7 +43,7 @@ pub async fn ensure_docker_available() -> Result<(), ComposeRunnerError> {
/// Ensure the configured compose image exists, building a local one if needed.
pub async fn ensure_compose_image() -> Result<(), ComposeRunnerError> {
let (image, platform) = crate::platform::resolve_image();
let (image, platform) = crate::docker::platform::resolve_image();
ensure_image_present(&image, platform.as_deref()).await
}
@ -123,73 +126,60 @@ pub async fn build_local_image(
.arg(format!("CIRCUITS_OVERRIDE={value}"));
}
let node_rev = std::env::var("NOMOS_NODE_REV")
.unwrap_or_else(|_| String::from("d2dd5a5084e1daef4032562c77d41de5e4d495f8"));
cmd.arg("--build-arg")
.arg(format!("NOMOS_NODE_REV={node_rev}"));
if let Some(value) = env::var("NOMOS_CIRCUITS_VERSION")
.ok()
.filter(|val| !val.is_empty())
{
cmd.arg("--build-arg")
.arg(format!("NOMOS_CIRCUITS_VERSION={value}"));
}
if env::var("NOMOS_CIRCUITS_REBUILD_RAPIDSNARK").is_ok() {
cmd.arg("--build-arg").arg("RAPIDSNARK_REBUILD=1");
}
cmd.arg("-t")
.arg(image)
.arg("-f")
.arg(&dockerfile)
.arg(dockerfile)
.arg(&repo_root);
run_docker_command(cmd, "docker build compose image", IMAGE_BUILD_TIMEOUT).await
}
cmd.current_dir(&repo_root);
/// Run a docker command with a timeout, mapping errors into runner errors.
pub async fn run_docker_command(
mut command: Command,
description: &str,
timeout_duration: Duration,
) -> Result<(), ComposeRunnerError> {
match timeout(timeout_duration, command.status()).await {
Ok(Ok(status)) if status.success() => Ok(()),
Ok(Ok(status)) => Err(ComposeRunnerError::Compose(ComposeCommandError::Failed {
command: description.to_owned(),
status,
})),
Ok(Err(source)) => Err(ComposeRunnerError::Compose(ComposeCommandError::Spawn {
command: description.to_owned(),
source,
})),
Err(_) => Err(ComposeRunnerError::Compose(ComposeCommandError::Timeout {
command: description.to_owned(),
timeout: timeout_duration,
})),
}
}
fn detect_docker_platform() -> Result<Option<String>, ComposeRunnerError> {
let output = StdCommand::new("docker")
.arg("info")
.arg("-f")
.arg("{{.Architecture}}")
.output()
.map_err(|source| ComposeRunnerError::ImageBuild {
source: source.into(),
})?;
if !output.status.success() {
return Ok(None);
}
let arch = String::from_utf8_lossy(&output.stdout).trim().to_owned();
if arch.is_empty() {
return Ok(None);
}
Ok(Some(format!("linux/{arch}")))
}
/// Choose the build platform from user override or docker host architecture.
pub fn select_build_platform(
requested: Option<&str>,
) -> Result<Option<String>, ComposeRunnerError> {
if let Some(value) = requested {
return Ok(Some(value.to_owned()));
}
detect_docker_platform()?.map_or_else(
|| {
warn!("docker host architecture unavailable; letting docker choose default platform");
Ok(None)
},
|host_platform| Ok(Some(host_platform)),
let status = timeout(
testing_framework_core::adjust_timeout(IMAGE_BUILD_TIMEOUT),
cmd.status(),
)
.await
.map_err(|_| {
ComposeRunnerError::Compose(ComposeCommandError::Timeout {
command: String::from("docker build"),
timeout: testing_framework_core::adjust_timeout(IMAGE_BUILD_TIMEOUT),
})
})?;
match status {
Ok(code) if code.success() => Ok(()),
Ok(code) => Err(ComposeRunnerError::Compose(ComposeCommandError::Failed {
command: String::from("docker build"),
status: code,
})),
Err(err) => Err(ComposeRunnerError::ImageBuild { source: err.into() }),
}
}
fn select_build_platform(platform: Option<&str>) -> Result<Option<String>, ComposeRunnerError> {
Ok(platform.map(String::from).or_else(|| {
let host_arch = std::env::consts::ARCH;
match host_arch {
"aarch64" | "arm64" => Some(String::from("linux/arm64")),
"x86_64" => Some(String::from("linux/amd64")),
_ => None,
}
}))
}

View File

@ -10,7 +10,8 @@ use testing_framework_core::{
use url::ParseError;
use crate::{
commands::ComposeCommandError, descriptor::DescriptorBuildError, template::TemplateError,
descriptor::DescriptorBuildError, docker::commands::ComposeCommandError,
infrastructure::template::TemplateError,
};
#[derive(Debug, thiserror::Error)]

View File

@ -13,16 +13,20 @@ use tracing::{debug, info, warn};
use uuid::Uuid;
use crate::{
cfgsync::{CfgsyncServerHandle, update_cfgsync_config},
cleanup::RunnerCleanup,
commands::{compose_up, dump_compose_logs},
deployer::setup::DEFAULT_PROMETHEUS_PORT,
descriptor::ComposeDescriptor,
docker::{ensure_compose_image, run_docker_command},
docker::{
commands::{compose_up, dump_compose_logs, run_docker_command},
ensure_compose_image,
platform::resolve_image,
workspace::ComposeWorkspace,
},
errors::{ComposeRunnerError, ConfigError, WorkspaceError},
platform::resolve_image,
template::write_compose_file,
workspace::ComposeWorkspace,
infrastructure::{
cfgsync::{CfgsyncServerHandle, update_cfgsync_config},
template::write_compose_file,
},
lifecycle::cleanup::RunnerCleanup,
};
const CFGSYNC_START_TIMEOUT: Duration = Duration::from_secs(180);
@ -274,8 +278,8 @@ pub async fn launch_cfgsync(
run_docker_command(
command,
"docker run cfgsync server",
adjust_timeout(CFGSYNC_START_TIMEOUT),
"docker run cfgsync server",
)
.await
.map_err(|source| ConfigError::CfgsyncStart {

View File

@ -0,0 +1,4 @@
pub mod cfgsync;
pub mod environment;
pub mod ports;
pub mod template;

View File

@ -11,8 +11,8 @@ use tokio::{process::Command, time::timeout};
use url::ParseError;
use crate::{
environment::StackEnvironment,
errors::{ComposeRunnerError, StackReadinessError},
infrastructure::environment::StackEnvironment,
};
/// Host ports mapped for a single node.

View File

@ -1,24 +1,18 @@
pub mod block_feed;
pub mod cfgsync;
pub mod cleanup;
pub mod commands;
pub mod control;
pub mod deployer;
pub mod descriptor;
pub mod docker;
pub mod environment;
pub mod errors;
pub mod platform;
pub mod ports;
pub mod readiness;
pub mod template;
pub mod wait;
pub mod workspace;
pub mod infrastructure;
pub mod lifecycle;
pub use commands::{ComposeCommandError, compose_down, compose_up, dump_compose_logs};
pub use deployer::ComposeDeployer;
pub use descriptor::{ComposeDescriptor, ComposeDescriptorBuilder, EnvEntry, NodeDescriptor};
pub use docker::{
commands::{ComposeCommandError, compose_down, compose_up, dump_compose_logs},
platform::{host_gateway_entry, resolve_image},
};
pub use errors::ComposeRunnerError;
pub use platform::{host_gateway_entry, resolve_image};
pub use ports::{HostPortMapping, NodeHostPorts};
pub use template::{TemplateError, repository_root, write_compose_file};
pub use infrastructure::{
ports::{HostPortMapping, NodeHostPorts},
template::{TemplateError, repository_root, write_compose_file},
};

View File

@ -3,9 +3,11 @@ use std::{env, path::PathBuf, thread};
use testing_framework_core::scenario::CleanupGuard;
use crate::{
cfgsync::CfgsyncServerHandle,
commands::{ComposeCommandError, compose_down},
workspace::ComposeWorkspace,
docker::{
commands::{ComposeCommandError, compose_down},
workspace::ComposeWorkspace,
},
infrastructure::cfgsync::CfgsyncServerHandle,
};
/// Cleans up a compose deployment and associated cfgsync container.

View File

@ -0,0 +1,4 @@
pub mod block_feed;
pub mod cleanup;
pub mod readiness;
pub mod wait;

View File

@ -10,8 +10,8 @@ use tokio::time::sleep;
use crate::{
errors::{NodeClientError, StackReadinessError},
ports::{HostPortMapping, NodeHostPorts},
wait::{wait_for_executors, wait_for_validators},
infrastructure::ports::{HostPortMapping, NodeHostPorts},
lifecycle::wait::{wait_for_executors, wait_for_validators},
};
const DISABLED_READINESS_SLEEP: Duration = Duration::from_secs(5);