2026-01-19 08:34:17 +01:00
|
|
|
use testing_framework_core::{
|
2026-01-19 13:27:01 +01:00
|
|
|
manual::ManualClusterHandle,
|
2026-02-19 11:53:46 +01:00
|
|
|
scenario::{
|
|
|
|
|
DynError, ExternalNodeSource, NodeClients, NodeControlHandle, ReadinessError,
|
|
|
|
|
StartNodeOptions, StartedNode,
|
|
|
|
|
},
|
2026-01-19 08:34:17 +01:00
|
|
|
};
|
|
|
|
|
use thiserror::Error;
|
|
|
|
|
|
2026-02-02 07:19:22 +01:00
|
|
|
use crate::{
|
|
|
|
|
env::LocalDeployerEnv,
|
2026-02-20 10:30:36 +01:00
|
|
|
external::build_external_client,
|
2026-02-17 10:28:50 +01:00
|
|
|
keep_tempdir_from_env,
|
|
|
|
|
node_control::{NodeManager, NodeManagerError, NodeManagerSeed},
|
2026-02-02 07:19:22 +01:00
|
|
|
};
|
2026-01-19 08:34:17 +01:00
|
|
|
|
|
|
|
|
#[derive(Debug, Error)]
|
|
|
|
|
pub enum ManualClusterError {
|
|
|
|
|
#[error(transparent)]
|
2026-02-02 07:19:22 +01:00
|
|
|
Dynamic(#[from] NodeManagerError),
|
2026-01-19 08:34:17 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// Imperative, in-process cluster that can start nodes on demand.
|
2026-02-02 07:19:22 +01:00
|
|
|
pub struct ManualCluster<E: LocalDeployerEnv> {
|
|
|
|
|
nodes: NodeManager<E>,
|
2026-01-19 08:34:17 +01:00
|
|
|
}
|
|
|
|
|
|
2026-02-02 07:19:22 +01:00
|
|
|
impl<E: LocalDeployerEnv> ManualCluster<E> {
|
|
|
|
|
pub fn from_topology(descriptors: E::Deployment) -> Self {
|
2026-02-17 10:28:50 +01:00
|
|
|
let nodes = NodeManager::new_with_seed(
|
2026-01-19 08:34:17 +01:00
|
|
|
descriptors,
|
|
|
|
|
testing_framework_core::scenario::NodeClients::default(),
|
2026-02-17 10:28:50 +01:00
|
|
|
keep_tempdir_from_env(),
|
|
|
|
|
NodeManagerSeed::default(),
|
2026-01-19 08:34:17 +01:00
|
|
|
);
|
2026-02-05 08:23:14 +02:00
|
|
|
|
2026-02-02 07:19:22 +01:00
|
|
|
Self { nodes }
|
2026-02-05 08:23:14 +02:00
|
|
|
}
|
|
|
|
|
|
2026-01-19 08:34:17 +01:00
|
|
|
#[must_use]
|
2026-02-02 07:19:22 +01:00
|
|
|
pub fn node_client(&self, name: &str) -> Option<E::NodeClient> {
|
2026-01-19 08:34:17 +01:00
|
|
|
self.nodes.node_client(name)
|
|
|
|
|
}
|
|
|
|
|
|
2026-02-05 08:23:14 +02:00
|
|
|
#[must_use]
|
|
|
|
|
pub fn node_pid(&self, name: &str) -> Option<u32> {
|
|
|
|
|
self.nodes.node_pid(name)
|
|
|
|
|
}
|
|
|
|
|
|
2026-02-02 07:19:22 +01:00
|
|
|
pub async fn start_node(&self, name: &str) -> Result<StartedNode<E>, ManualClusterError> {
|
2026-01-19 08:34:17 +01:00
|
|
|
Ok(self
|
|
|
|
|
.nodes
|
2026-02-02 07:19:22 +01:00
|
|
|
.start_node_with(name, StartNodeOptions::<E>::default())
|
2026-01-19 08:34:17 +01:00
|
|
|
.await?)
|
|
|
|
|
}
|
|
|
|
|
|
2026-01-26 08:26:15 +01:00
|
|
|
pub async fn start_node_with(
|
2026-01-19 08:34:17 +01:00
|
|
|
&self,
|
|
|
|
|
name: &str,
|
2026-02-02 07:19:22 +01:00
|
|
|
options: StartNodeOptions<E>,
|
|
|
|
|
) -> Result<StartedNode<E>, ManualClusterError> {
|
2026-01-26 08:26:15 +01:00
|
|
|
Ok(self.nodes.start_node_with(name, options).await?)
|
2026-01-19 08:34:17 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pub fn stop_all(&self) {
|
|
|
|
|
self.nodes.stop_all();
|
|
|
|
|
}
|
|
|
|
|
|
2026-02-05 08:23:14 +02:00
|
|
|
pub async fn restart_node(&self, name: &str) -> Result<(), ManualClusterError> {
|
|
|
|
|
Ok(self.nodes.restart_node(name).await?)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pub async fn stop_node(&self, name: &str) -> Result<(), ManualClusterError> {
|
|
|
|
|
Ok(self.nodes.stop_node(name).await?)
|
|
|
|
|
}
|
|
|
|
|
|
2026-01-19 08:34:17 +01:00
|
|
|
pub async fn wait_network_ready(&self) -> Result<(), ReadinessError> {
|
2026-02-02 07:19:22 +01:00
|
|
|
self.nodes.wait_network_ready().await
|
2026-01-19 08:34:17 +01:00
|
|
|
}
|
2026-02-17 10:54:36 +01:00
|
|
|
|
|
|
|
|
pub async fn wait_node_ready(&self, name: &str) -> Result<(), ManualClusterError> {
|
|
|
|
|
self.nodes.wait_node_ready(name).await?;
|
|
|
|
|
Ok(())
|
|
|
|
|
}
|
2026-02-19 11:53:46 +01:00
|
|
|
|
|
|
|
|
#[must_use]
|
|
|
|
|
pub fn node_clients(&self) -> NodeClients<E> {
|
|
|
|
|
self.nodes.node_clients()
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pub fn add_external_sources(
|
|
|
|
|
&self,
|
|
|
|
|
external_sources: impl IntoIterator<Item = ExternalNodeSource>,
|
|
|
|
|
) -> Result<(), DynError> {
|
|
|
|
|
let node_clients = self.nodes.node_clients();
|
|
|
|
|
for source in external_sources {
|
2026-02-20 10:30:36 +01:00
|
|
|
let client = E::external_node_client(&source)
|
|
|
|
|
.or_else(|_| build_external_client::<E>(&source))?;
|
2026-02-19 11:53:46 +01:00
|
|
|
node_clients.add_node(client);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Ok(())
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pub fn add_external_clients(&self, clients: impl IntoIterator<Item = E::NodeClient>) {
|
|
|
|
|
let node_clients = self.nodes.node_clients();
|
|
|
|
|
for client in clients {
|
|
|
|
|
node_clients.add_node(client);
|
|
|
|
|
}
|
|
|
|
|
}
|
2026-01-19 08:34:17 +01:00
|
|
|
}
|
2026-01-19 10:05:58 +01:00
|
|
|
|
2026-02-02 07:19:22 +01:00
|
|
|
impl<E: LocalDeployerEnv> Drop for ManualCluster<E> {
|
2026-01-19 10:05:58 +01:00
|
|
|
fn drop(&mut self) {
|
|
|
|
|
self.stop_all();
|
|
|
|
|
}
|
|
|
|
|
}
|
2026-01-19 13:27:01 +01:00
|
|
|
|
2026-02-05 08:23:14 +02:00
|
|
|
#[async_trait::async_trait]
|
2026-02-02 07:19:22 +01:00
|
|
|
impl<E: LocalDeployerEnv> NodeControlHandle<E> for ManualCluster<E> {
|
2026-02-05 08:23:14 +02:00
|
|
|
async fn restart_node(&self, name: &str) -> Result<(), DynError> {
|
|
|
|
|
self.nodes
|
|
|
|
|
.restart_node(name)
|
|
|
|
|
.await
|
|
|
|
|
.map_err(|err| err.into())
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
async fn stop_node(&self, name: &str) -> Result<(), DynError> {
|
|
|
|
|
self.nodes.stop_node(name).await.map_err(|err| err.into())
|
|
|
|
|
}
|
|
|
|
|
|
2026-02-02 07:19:22 +01:00
|
|
|
async fn start_node(&self, name: &str) -> Result<StartedNode<E>, DynError> {
|
|
|
|
|
self.nodes
|
|
|
|
|
.start_node_with(name, StartNodeOptions::<E>::default())
|
2026-02-05 08:23:14 +02:00
|
|
|
.await
|
|
|
|
|
.map_err(|err| err.into())
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
async fn start_node_with(
|
|
|
|
|
&self,
|
|
|
|
|
name: &str,
|
2026-02-02 07:19:22 +01:00
|
|
|
options: StartNodeOptions<E>,
|
|
|
|
|
) -> Result<StartedNode<E>, DynError> {
|
|
|
|
|
self.nodes
|
|
|
|
|
.start_node_with(name, options)
|
2026-02-05 08:23:14 +02:00
|
|
|
.await
|
|
|
|
|
.map_err(|err| err.into())
|
|
|
|
|
}
|
|
|
|
|
|
2026-02-02 07:19:22 +01:00
|
|
|
fn node_client(&self, name: &str) -> Option<E::NodeClient> {
|
2026-02-05 08:23:14 +02:00
|
|
|
self.node_client(name)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn node_pid(&self, name: &str) -> Option<u32> {
|
|
|
|
|
self.node_pid(name)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2026-01-19 13:27:01 +01:00
|
|
|
#[async_trait::async_trait]
|
2026-02-02 07:19:22 +01:00
|
|
|
impl<E: LocalDeployerEnv> ManualClusterHandle<E> for ManualCluster<E> {
|
2026-01-26 08:26:15 +01:00
|
|
|
async fn start_node_with(
|
2026-01-19 13:27:01 +01:00
|
|
|
&self,
|
|
|
|
|
name: &str,
|
2026-02-02 07:19:22 +01:00
|
|
|
options: StartNodeOptions<E>,
|
|
|
|
|
) -> Result<StartedNode<E>, DynError> {
|
|
|
|
|
self.nodes
|
|
|
|
|
.start_node_with(name, options)
|
2026-01-19 13:27:01 +01:00
|
|
|
.await
|
|
|
|
|
.map_err(|err| err.into())
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
async fn wait_network_ready(&self) -> Result<(), DynError> {
|
|
|
|
|
self.wait_network_ready().await.map_err(|err| err.into())
|
|
|
|
|
}
|
|
|
|
|
}
|