mirror of
https://github.com/logos-blockchain/logos-blockchain-testing.git
synced 2026-02-23 14:43:07 +00:00
feat(local): support external URL clients in local deployer orchestration
This commit is contained in:
parent
b3ecc5acf7
commit
674702bd62
@ -12,8 +12,8 @@ use testing_framework_core::{
|
||||
scenario::{
|
||||
Application, CleanupGuard, Deployer, DeploymentPolicy, DynError, FeedHandle, FeedRuntime,
|
||||
HttpReadinessRequirement, Metrics, NodeClients, NodeControlCapability, NodeControlHandle,
|
||||
RetryPolicy, RunContext, Runner, Scenario, ScenarioError, build_source_orchestration_plan,
|
||||
orchestrate_sources, spawn_feed,
|
||||
RetryPolicy, RunContext, Runner, Scenario, ScenarioError, SourceOrchestrationPlan,
|
||||
build_source_orchestration_plan, spawn_feed,
|
||||
},
|
||||
topology::DeploymentDescriptor,
|
||||
};
|
||||
@ -26,6 +26,7 @@ use tracing::{debug, info, warn};
|
||||
|
||||
use crate::{
|
||||
env::{LocalDeployerEnv, Node, wait_local_http_readiness},
|
||||
external::build_external_client,
|
||||
keep_tempdir_from_env,
|
||||
manual::ManualCluster,
|
||||
node_control::{NodeManager, NodeManagerSeed},
|
||||
@ -202,9 +203,7 @@ impl<E: LocalDeployerEnv> ProcessDeployer<E> {
|
||||
let nodes = Self::spawn_nodes_for_scenario(scenario, self.membership_check).await?;
|
||||
let node_clients = NodeClients::<E>::new(nodes.iter().map(|node| node.client()).collect());
|
||||
|
||||
// Source orchestration currently runs here after managed clients are prepared.
|
||||
let node_clients = orchestrate_sources(&source_plan, node_clients)
|
||||
.await
|
||||
let node_clients = merge_source_clients_for_local::<E>(&source_plan, node_clients)
|
||||
.map_err(|source| ProcessDeployerError::SourceOrchestration { source })?;
|
||||
|
||||
let runtime = run_context_for(
|
||||
@ -241,10 +240,9 @@ impl<E: LocalDeployerEnv> ProcessDeployer<E> {
|
||||
|
||||
let nodes = Self::spawn_nodes_for_scenario(scenario, self.membership_check).await?;
|
||||
let node_control = self.node_control_from(scenario, nodes);
|
||||
// Source orchestration currently runs here after managed clients are prepared.
|
||||
let node_clients = orchestrate_sources(&source_plan, node_control.node_clients())
|
||||
.await
|
||||
.map_err(|source| ProcessDeployerError::SourceOrchestration { source })?;
|
||||
let node_clients =
|
||||
merge_source_clients_for_local::<E>(&source_plan, node_control.node_clients())
|
||||
.map_err(|source| ProcessDeployerError::SourceOrchestration { source })?;
|
||||
let runtime = run_context_for(
|
||||
scenario.deployment().clone(),
|
||||
node_clients,
|
||||
@ -314,6 +312,18 @@ impl<E: LocalDeployerEnv> ProcessDeployer<E> {
|
||||
}
|
||||
}
|
||||
|
||||
fn merge_source_clients_for_local<E: LocalDeployerEnv>(
|
||||
source_plan: &SourceOrchestrationPlan,
|
||||
node_clients: NodeClients<E>,
|
||||
) -> Result<NodeClients<E>, DynError> {
|
||||
for source in source_plan.external_sources() {
|
||||
let client =
|
||||
E::external_node_client(source).or_else(|_| build_external_client::<E>(source))?;
|
||||
node_clients.add_node(client);
|
||||
}
|
||||
Ok(node_clients)
|
||||
}
|
||||
|
||||
fn build_retry_execution_config(
|
||||
deployment_policy: DeploymentPolicy,
|
||||
membership_check: bool,
|
||||
|
||||
90
testing-framework/deployers/local/src/external.rs
Normal file
90
testing-framework/deployers/local/src/external.rs
Normal file
@ -0,0 +1,90 @@
|
||||
use std::net::ToSocketAddrs;
|
||||
|
||||
use testing_framework_core::scenario::{DynError, ExternalNodeSource};
|
||||
|
||||
use crate::{LocalDeployerEnv, NodeEndpoints};
|
||||
|
||||
#[derive(Debug, thiserror::Error)]
|
||||
pub enum ExternalClientBuildError {
|
||||
#[error("external source '{label}' endpoint is empty")]
|
||||
EmptyEndpoint { label: String },
|
||||
#[error("external source '{label}' endpoint '{endpoint}' has unsupported scheme")]
|
||||
UnsupportedScheme { label: String, endpoint: String },
|
||||
#[error("external source '{label}' endpoint '{endpoint}' is missing host")]
|
||||
MissingHost { label: String, endpoint: String },
|
||||
#[error("external source '{label}' endpoint '{endpoint}' is missing port")]
|
||||
MissingPort { label: String, endpoint: String },
|
||||
#[error("external source '{label}' endpoint '{endpoint}' failed to resolve: {source}")]
|
||||
Resolve {
|
||||
label: String,
|
||||
endpoint: String,
|
||||
#[source]
|
||||
source: std::io::Error,
|
||||
},
|
||||
#[error("external source '{label}' endpoint '{endpoint}' resolved to no socket addresses")]
|
||||
NoResolvedAddress { label: String, endpoint: String },
|
||||
}
|
||||
|
||||
pub fn build_external_client<E: LocalDeployerEnv>(
|
||||
source: &ExternalNodeSource,
|
||||
) -> Result<E::NodeClient, DynError> {
|
||||
let api = resolve_api_socket(source)?;
|
||||
let mut endpoints = NodeEndpoints::default();
|
||||
endpoints.api = api;
|
||||
Ok(E::node_client(&endpoints))
|
||||
}
|
||||
|
||||
fn resolve_api_socket(source: &ExternalNodeSource) -> Result<std::net::SocketAddr, DynError> {
|
||||
let source_label = source.label.clone();
|
||||
let endpoint = source.endpoint.trim();
|
||||
if endpoint.is_empty() {
|
||||
return Err(ExternalClientBuildError::EmptyEndpoint {
|
||||
label: source_label,
|
||||
}
|
||||
.into());
|
||||
}
|
||||
|
||||
let without_scheme = endpoint
|
||||
.strip_prefix("http://")
|
||||
.or_else(|| endpoint.strip_prefix("https://"))
|
||||
.ok_or_else(|| ExternalClientBuildError::UnsupportedScheme {
|
||||
label: source_label.clone(),
|
||||
endpoint: endpoint.to_owned(),
|
||||
})?;
|
||||
|
||||
let authority = without_scheme.trim_end_matches('/');
|
||||
let (host, port) =
|
||||
split_host_port(authority).ok_or_else(|| ExternalClientBuildError::MissingPort {
|
||||
label: source_label.clone(),
|
||||
endpoint: endpoint.to_owned(),
|
||||
})?;
|
||||
|
||||
if host.is_empty() {
|
||||
return Err(ExternalClientBuildError::MissingHost {
|
||||
label: source_label.clone(),
|
||||
endpoint: endpoint.to_owned(),
|
||||
}
|
||||
.into());
|
||||
}
|
||||
|
||||
let resolved = (host, port)
|
||||
.to_socket_addrs()
|
||||
.map_err(|source| ExternalClientBuildError::Resolve {
|
||||
label: source_label.clone(),
|
||||
endpoint: endpoint.to_owned(),
|
||||
source,
|
||||
})?
|
||||
.next()
|
||||
.ok_or_else(|| ExternalClientBuildError::NoResolvedAddress {
|
||||
label: source_label,
|
||||
endpoint: endpoint.to_owned(),
|
||||
})?;
|
||||
|
||||
Ok(resolved)
|
||||
}
|
||||
|
||||
fn split_host_port(authority: &str) -> Option<(&str, u16)> {
|
||||
let (host, port) = authority.rsplit_once(':')?;
|
||||
let port = port.parse::<u16>().ok()?;
|
||||
Some((host.trim_matches(['[', ']']), port))
|
||||
}
|
||||
@ -1,6 +1,7 @@
|
||||
pub mod binary;
|
||||
mod deployer;
|
||||
pub mod env;
|
||||
mod external;
|
||||
mod manual;
|
||||
mod node_control;
|
||||
pub mod process;
|
||||
|
||||
@ -9,6 +9,7 @@ use thiserror::Error;
|
||||
|
||||
use crate::{
|
||||
env::LocalDeployerEnv,
|
||||
external::build_external_client,
|
||||
keep_tempdir_from_env,
|
||||
node_control::{NodeManager, NodeManagerError, NodeManagerSeed},
|
||||
};
|
||||
@ -93,7 +94,8 @@ impl<E: LocalDeployerEnv> ManualCluster<E> {
|
||||
) -> Result<(), DynError> {
|
||||
let node_clients = self.nodes.node_clients();
|
||||
for source in external_sources {
|
||||
let client = E::external_node_client(&source)?;
|
||||
let client = E::external_node_client(&source)
|
||||
.or_else(|_| build_external_client::<E>(&source))?;
|
||||
node_clients.add_node(client);
|
||||
}
|
||||
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user