mirror of
https://github.com/logos-blockchain/logos-blockchain-testing.git
synced 2026-03-31 08:13:48 +00:00
framework: simplify API surface
This commit is contained in:
commit
9738fad262
@ -44,7 +44,7 @@ impl Application for LbcExtEnv {
|
||||
type FeedRuntime = <LbcEnv as Application>::FeedRuntime;
|
||||
|
||||
fn external_node_client(source: &ExternalNodeSource) -> Result<Self::NodeClient, DynError> {
|
||||
let base_url = Url::parse(&source.endpoint)?;
|
||||
let base_url = Url::parse(source.endpoint())?;
|
||||
Ok(NodeHttpClient::from_urls(base_url, None))
|
||||
}
|
||||
|
||||
|
||||
@ -1,15 +1,7 @@
|
||||
use async_trait::async_trait;
|
||||
|
||||
use crate::scenario::{Application, DynError, NodeControlHandle, StartNodeOptions, StartedNode};
|
||||
use crate::scenario::{Application, ClusterWaitHandle, NodeControlHandle};
|
||||
|
||||
/// Interface for imperative, deployer-backed manual clusters.
|
||||
#[async_trait]
|
||||
pub trait ManualClusterHandle<E: Application>: NodeControlHandle<E> {
|
||||
async fn start_node_with(
|
||||
&self,
|
||||
name: &str,
|
||||
options: StartNodeOptions<E>,
|
||||
) -> Result<StartedNode<E>, DynError>;
|
||||
|
||||
async fn wait_network_ready(&self) -> Result<(), DynError>;
|
||||
}
|
||||
pub trait ManualClusterHandle<E: Application>: NodeControlHandle<E> + ClusterWaitHandle<E> {}
|
||||
|
||||
@ -6,12 +6,11 @@ use tracing::{debug, info};
|
||||
use super::{
|
||||
Application, AttachSource, DeploymentPolicy, DynError, ExternalNodeSource,
|
||||
HttpReadinessRequirement, NodeControlCapability, ObservabilityCapability, ScenarioSources,
|
||||
SourceReadinessPolicy,
|
||||
builder_ops::CoreBuilderAccess,
|
||||
expectation::Expectation,
|
||||
runtime::{
|
||||
context::RunMetrics,
|
||||
orchestration::{SourceModeName, SourceOrchestrationPlan, SourceOrchestrationPlanError},
|
||||
orchestration::{SourceOrchestrationPlan, SourceOrchestrationPlanError},
|
||||
},
|
||||
workload::Workload,
|
||||
};
|
||||
@ -44,7 +43,6 @@ pub struct Scenario<E: Application, Caps = ()> {
|
||||
expectation_cooldown: Duration,
|
||||
deployment_policy: DeploymentPolicy,
|
||||
sources: ScenarioSources,
|
||||
source_readiness_policy: SourceReadinessPolicy,
|
||||
source_orchestration_plan: SourceOrchestrationPlan,
|
||||
capabilities: Caps,
|
||||
}
|
||||
@ -58,7 +56,6 @@ impl<E: Application, Caps> Scenario<E, Caps> {
|
||||
expectation_cooldown: Duration,
|
||||
deployment_policy: DeploymentPolicy,
|
||||
sources: ScenarioSources,
|
||||
source_readiness_policy: SourceReadinessPolicy,
|
||||
source_orchestration_plan: SourceOrchestrationPlan,
|
||||
capabilities: Caps,
|
||||
) -> Self {
|
||||
@ -70,7 +67,6 @@ impl<E: Application, Caps> Scenario<E, Caps> {
|
||||
expectation_cooldown,
|
||||
deployment_policy,
|
||||
sources,
|
||||
source_readiness_policy,
|
||||
source_orchestration_plan,
|
||||
capabilities,
|
||||
}
|
||||
@ -116,15 +112,6 @@ impl<E: Application, Caps> Scenario<E, Caps> {
|
||||
self.deployment_policy
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
/// Selected source readiness policy.
|
||||
///
|
||||
/// This is currently reserved for future mixed-source orchestration and
|
||||
/// does not change runtime behavior yet.
|
||||
pub const fn source_readiness_policy(&self) -> SourceReadinessPolicy {
|
||||
self.source_readiness_policy
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub fn sources(&self) -> &ScenarioSources {
|
||||
&self.sources
|
||||
@ -151,7 +138,6 @@ pub struct Builder<E: Application, Caps = ()> {
|
||||
expectation_cooldown: Option<Duration>,
|
||||
deployment_policy: DeploymentPolicy,
|
||||
sources: ScenarioSources,
|
||||
source_readiness_policy: SourceReadinessPolicy,
|
||||
capabilities: Caps,
|
||||
}
|
||||
|
||||
@ -256,11 +242,6 @@ macro_rules! impl_common_builder_methods {
|
||||
self.map_core_builder(|builder| builder.with_external_node(node))
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub fn with_source_readiness_policy(self, policy: SourceReadinessPolicy) -> Self {
|
||||
self.map_core_builder(|builder| builder.with_source_readiness_policy(policy))
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub fn with_external_only_sources(self) -> Self {
|
||||
self.map_core_builder(|builder| builder.with_external_only_sources())
|
||||
@ -350,7 +331,6 @@ impl<E: Application, Caps: Default> Builder<E, Caps> {
|
||||
expectation_cooldown: None,
|
||||
deployment_policy: DeploymentPolicy::default(),
|
||||
sources: ScenarioSources::default(),
|
||||
source_readiness_policy: SourceReadinessPolicy::default(),
|
||||
capabilities: Caps::default(),
|
||||
}
|
||||
}
|
||||
@ -453,7 +433,6 @@ impl<E: Application, Caps> Builder<E, Caps> {
|
||||
expectation_cooldown,
|
||||
deployment_policy,
|
||||
sources,
|
||||
source_readiness_policy,
|
||||
..
|
||||
} = self;
|
||||
|
||||
@ -466,7 +445,6 @@ impl<E: Application, Caps> Builder<E, Caps> {
|
||||
expectation_cooldown,
|
||||
deployment_policy,
|
||||
sources,
|
||||
source_readiness_policy,
|
||||
capabilities,
|
||||
}
|
||||
}
|
||||
@ -569,29 +547,19 @@ impl<E: Application, Caps> Builder<E, Caps> {
|
||||
|
||||
#[must_use]
|
||||
pub fn with_attach_source(mut self, attach: AttachSource) -> Self {
|
||||
self.sources.set_attach(attach);
|
||||
self.sources = self.sources.with_attach(attach);
|
||||
self
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub fn with_external_node(mut self, node: ExternalNodeSource) -> Self {
|
||||
self.sources.add_external_node(node);
|
||||
self
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
/// Configure source readiness policy metadata.
|
||||
///
|
||||
/// This is currently reserved for future mixed-source orchestration and
|
||||
/// does not change runtime behavior yet.
|
||||
pub fn with_source_readiness_policy(mut self, policy: SourceReadinessPolicy) -> Self {
|
||||
self.source_readiness_policy = policy;
|
||||
self.sources = self.sources.with_external_node(node);
|
||||
self
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub fn with_external_only_sources(mut self) -> Self {
|
||||
self.sources.set_external_only();
|
||||
self.sources = self.sources.into_external_only();
|
||||
self
|
||||
}
|
||||
|
||||
@ -612,8 +580,7 @@ impl<E: Application, Caps> Builder<E, Caps> {
|
||||
let descriptors = parts.resolve_deployment()?;
|
||||
let run_plan = parts.run_plan();
|
||||
let run_metrics = RunMetrics::new(run_plan.duration);
|
||||
let source_orchestration_plan =
|
||||
build_source_orchestration_plan(parts.sources(), parts.source_readiness_policy)?;
|
||||
let source_orchestration_plan = build_source_orchestration_plan(parts.sources())?;
|
||||
|
||||
initialize_components(
|
||||
&descriptors,
|
||||
@ -640,7 +607,6 @@ impl<E: Application, Caps> Builder<E, Caps> {
|
||||
run_plan.expectation_cooldown,
|
||||
parts.deployment_policy,
|
||||
parts.sources,
|
||||
parts.source_readiness_policy,
|
||||
source_orchestration_plan,
|
||||
parts.capabilities,
|
||||
))
|
||||
@ -661,7 +627,6 @@ struct BuilderParts<E: Application, Caps> {
|
||||
expectation_cooldown: Option<Duration>,
|
||||
deployment_policy: DeploymentPolicy,
|
||||
sources: ScenarioSources,
|
||||
source_readiness_policy: SourceReadinessPolicy,
|
||||
capabilities: Caps,
|
||||
}
|
||||
|
||||
@ -676,7 +641,6 @@ impl<E: Application, Caps> BuilderParts<E, Caps> {
|
||||
expectation_cooldown,
|
||||
deployment_policy,
|
||||
sources,
|
||||
source_readiness_policy,
|
||||
capabilities,
|
||||
..
|
||||
} = builder;
|
||||
@ -690,7 +654,6 @@ impl<E: Application, Caps> BuilderParts<E, Caps> {
|
||||
expectation_cooldown,
|
||||
deployment_policy,
|
||||
sources,
|
||||
source_readiness_policy,
|
||||
capabilities,
|
||||
}
|
||||
}
|
||||
@ -715,28 +678,18 @@ impl<E: Application, Caps> BuilderParts<E, Caps> {
|
||||
|
||||
fn build_source_orchestration_plan(
|
||||
sources: &ScenarioSources,
|
||||
readiness_policy: SourceReadinessPolicy,
|
||||
) -> Result<SourceOrchestrationPlan, ScenarioBuildError> {
|
||||
SourceOrchestrationPlan::try_from_sources(sources, readiness_policy)
|
||||
.map_err(source_plan_error_to_build_error)
|
||||
SourceOrchestrationPlan::try_from_sources(sources).map_err(source_plan_error_to_build_error)
|
||||
}
|
||||
|
||||
fn source_plan_error_to_build_error(error: SourceOrchestrationPlanError) -> ScenarioBuildError {
|
||||
match error {
|
||||
SourceOrchestrationPlanError::SourceModeNotWiredYet { mode } => {
|
||||
ScenarioBuildError::SourceModeNotWiredYet {
|
||||
mode: source_mode_name(mode),
|
||||
}
|
||||
ScenarioBuildError::SourceModeNotWiredYet { mode }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const fn source_mode_name(mode: SourceModeName) -> &'static str {
|
||||
match mode {
|
||||
SourceModeName::Attached => "Attached",
|
||||
}
|
||||
}
|
||||
|
||||
impl<E: Application> Builder<E, ()> {
|
||||
#[must_use]
|
||||
pub fn enable_node_control(self) -> Builder<E, NodeControlCapability> {
|
||||
|
||||
@ -37,9 +37,8 @@ pub use deployment_policy::{CleanupPolicy, DeploymentPolicy, RetryPolicy};
|
||||
pub use expectation::Expectation;
|
||||
pub use observability::{ObservabilityCapabilityProvider, ObservabilityInputs};
|
||||
pub use runtime::{
|
||||
ApplicationExternalProvider, AttachProvider, AttachProviderError, AttachedNode, BorrowedNode,
|
||||
BorrowedOrigin, CleanupGuard, Deployer, Feed, FeedHandle, FeedRuntime,
|
||||
HttpReadinessRequirement, ManagedNode, ManagedSource, NodeClients, NodeHandle, NodeInventory,
|
||||
ApplicationExternalProvider, AttachProvider, AttachProviderError, AttachedNode, CleanupGuard,
|
||||
Deployer, Feed, FeedHandle, FeedRuntime, HttpReadinessRequirement, ManagedSource, NodeClients,
|
||||
ReadinessError, RunContext, RunHandle, RunMetrics, Runner, ScenarioError,
|
||||
SourceOrchestrationPlan, SourceProviders, StabilizationConfig, StaticManagedProvider,
|
||||
build_source_orchestration_plan,
|
||||
@ -52,7 +51,7 @@ pub use runtime::{
|
||||
wait_for_http_ports_with_host_and_requirement, wait_for_http_ports_with_requirement,
|
||||
wait_http_readiness, wait_until_stable,
|
||||
};
|
||||
pub use sources::{AttachSource, ExternalNodeSource, ScenarioSources, SourceReadinessPolicy};
|
||||
pub use sources::{AttachSource, ExternalNodeSource, ScenarioSources};
|
||||
pub use workload::Workload;
|
||||
|
||||
pub use crate::env::Application;
|
||||
|
||||
@ -1,10 +1,7 @@
|
||||
use std::{sync::Arc, time::Duration};
|
||||
|
||||
use super::{metrics::Metrics, node_clients::ClusterClient};
|
||||
use crate::scenario::{
|
||||
Application, BorrowedNode, ClusterWaitHandle, DynError, ManagedNode, NodeClients,
|
||||
NodeControlHandle,
|
||||
};
|
||||
use crate::scenario::{Application, ClusterWaitHandle, DynError, NodeClients, NodeControlHandle};
|
||||
|
||||
#[derive(Debug, thiserror::Error)]
|
||||
enum RunContextCapabilityError {
|
||||
@ -27,6 +24,7 @@ pub struct RunContext<E: Application> {
|
||||
impl<E: Application> RunContext<E> {
|
||||
/// Builds a run context from prepared deployment/runtime artifacts.
|
||||
#[must_use]
|
||||
#[doc(hidden)]
|
||||
pub fn new(
|
||||
descriptors: E::Deployment,
|
||||
node_clients: NodeClients<E>,
|
||||
@ -51,6 +49,7 @@ impl<E: Application> RunContext<E> {
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
#[doc(hidden)]
|
||||
pub fn with_cluster_wait(mut self, cluster_wait: Arc<dyn ClusterWaitHandle<E>>) -> Self {
|
||||
self.cluster_wait = Some(cluster_wait);
|
||||
self
|
||||
@ -71,26 +70,6 @@ impl<E: Application> RunContext<E> {
|
||||
self.node_clients.random_client()
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub fn managed_nodes(&self) -> Vec<ManagedNode<E>> {
|
||||
self.node_clients.managed_nodes()
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub fn borrowed_nodes(&self) -> Vec<BorrowedNode<E>> {
|
||||
self.node_clients.borrowed_nodes()
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub fn find_managed_node(&self, identity: &str) -> Option<ManagedNode<E>> {
|
||||
self.node_clients.find_managed(identity)
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub fn find_borrowed_node(&self, identity: &str) -> Option<BorrowedNode<E>> {
|
||||
self.node_clients.find_borrowed(identity)
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub fn feed(&self) -> <E::FeedRuntime as super::FeedRuntime>::Feed {
|
||||
self.feed.clone()
|
||||
@ -121,22 +100,12 @@ impl<E: Application> RunContext<E> {
|
||||
self.node_control.clone()
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub fn cluster_wait(&self) -> Option<Arc<dyn ClusterWaitHandle<E>>> {
|
||||
self.cluster_wait.clone()
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub const fn controls_nodes(&self) -> bool {
|
||||
self.node_control.is_some()
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub const fn can_wait_network_ready(&self) -> bool {
|
||||
self.cluster_wait.is_some()
|
||||
}
|
||||
|
||||
pub async fn wait_network_ready(&self) -> Result<(), DynError> {
|
||||
pub(crate) async fn wait_network_ready(&self) -> Result<(), DynError> {
|
||||
self.require_cluster_wait()?.wait_network_ready().await
|
||||
}
|
||||
|
||||
@ -146,7 +115,9 @@ impl<E: Application> RunContext<E> {
|
||||
}
|
||||
|
||||
fn require_cluster_wait(&self) -> Result<Arc<dyn ClusterWaitHandle<E>>, DynError> {
|
||||
self.cluster_wait()
|
||||
self.cluster_wait
|
||||
.as_ref()
|
||||
.map(Arc::clone)
|
||||
.ok_or_else(|| RunContextCapabilityError::MissingClusterWait.into())
|
||||
}
|
||||
}
|
||||
@ -168,6 +139,7 @@ impl<E: Application> Drop for RunHandle<E> {
|
||||
impl<E: Application> RunHandle<E> {
|
||||
#[must_use]
|
||||
/// Build a handle from owned context and optional cleanup guard.
|
||||
#[doc(hidden)]
|
||||
pub fn new(context: RunContext<E>, cleanup_guard: Option<Box<dyn CleanupGuard>>) -> Self {
|
||||
Self {
|
||||
run_context: Arc::new(context),
|
||||
@ -192,10 +164,6 @@ impl<E: Application> RunHandle<E> {
|
||||
pub fn context(&self) -> &RunContext<E> {
|
||||
&self.run_context
|
||||
}
|
||||
|
||||
pub async fn wait_network_ready(&self) -> Result<(), DynError> {
|
||||
self.run_context.wait_network_ready().await
|
||||
}
|
||||
}
|
||||
|
||||
/// Derived metrics about the current run timing.
|
||||
|
||||
@ -1,3 +1,3 @@
|
||||
mod node_inventory;
|
||||
|
||||
pub use node_inventory::{BorrowedNode, BorrowedOrigin, ManagedNode, NodeHandle, NodeInventory};
|
||||
pub(crate) use node_inventory::NodeInventory;
|
||||
|
||||
@ -1,91 +1,18 @@
|
||||
use std::{collections::HashMap, sync::Arc};
|
||||
use std::sync::Arc;
|
||||
|
||||
use parking_lot::RwLock;
|
||||
|
||||
use crate::scenario::{Application, DynError, NodeControlHandle, StartNodeOptions, StartedNode};
|
||||
use crate::scenario::Application;
|
||||
|
||||
/// Origin for borrowed (non-managed) nodes in the runtime inventory.
|
||||
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
|
||||
pub enum BorrowedOrigin {
|
||||
/// Node discovered from an attached cluster provider.
|
||||
Attached,
|
||||
/// Node provided explicitly as an external endpoint.
|
||||
External,
|
||||
}
|
||||
|
||||
/// Managed node handle with full lifecycle capabilities.
|
||||
pub struct ManagedNode<E: Application> {
|
||||
/// Canonical node identity used for deduplication and lookups.
|
||||
pub identity: String,
|
||||
/// Application-specific API client for this node.
|
||||
pub client: E::NodeClient,
|
||||
}
|
||||
|
||||
/// Borrowed node handle (attached or external), query-only by default.
|
||||
pub struct BorrowedNode<E: Application> {
|
||||
/// Canonical node identity used for deduplication and lookups.
|
||||
pub identity: String,
|
||||
/// Application-specific API client for this node.
|
||||
pub client: E::NodeClient,
|
||||
/// Borrowed source kind used for diagnostics and selection.
|
||||
pub origin: BorrowedOrigin,
|
||||
}
|
||||
|
||||
/// Unified node handle variant used by runtime inventory snapshots.
|
||||
pub enum NodeHandle<E: Application> {
|
||||
/// Managed node variant.
|
||||
Managed(ManagedNode<E>),
|
||||
/// Borrowed node variant.
|
||||
Borrowed(BorrowedNode<E>),
|
||||
}
|
||||
|
||||
impl<E: Application> Clone for ManagedNode<E> {
|
||||
fn clone(&self) -> Self {
|
||||
Self {
|
||||
identity: self.identity.clone(),
|
||||
client: self.client.clone(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<E: Application> Clone for BorrowedNode<E> {
|
||||
fn clone(&self) -> Self {
|
||||
Self {
|
||||
identity: self.identity.clone(),
|
||||
client: self.client.clone(),
|
||||
origin: self.origin,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<E: Application> Clone for NodeHandle<E> {
|
||||
fn clone(&self) -> Self {
|
||||
match self {
|
||||
Self::Managed(node) => Self::Managed(node.clone()),
|
||||
Self::Borrowed(node) => Self::Borrowed(node.clone()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Thread-safe node inventory with identity-based upsert semantics.
|
||||
pub struct NodeInventory<E: Application> {
|
||||
inner: Arc<RwLock<NodeInventoryInner<E>>>,
|
||||
}
|
||||
|
||||
struct NodeInventoryInner<E: Application> {
|
||||
nodes: Vec<NodeHandle<E>>,
|
||||
indices_by_identity: HashMap<String, usize>,
|
||||
next_synthetic_id: usize,
|
||||
/// Thread-safe node client storage used by runtime handles.
|
||||
pub(crate) struct NodeInventory<E: Application> {
|
||||
clients: Arc<RwLock<Vec<E::NodeClient>>>,
|
||||
}
|
||||
|
||||
impl<E: Application> Default for NodeInventory<E> {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
inner: Arc::new(RwLock::new(NodeInventoryInner {
|
||||
nodes: Vec::new(),
|
||||
indices_by_identity: HashMap::new(),
|
||||
next_synthetic_id: 0,
|
||||
})),
|
||||
clients: Arc::new(RwLock::new(Vec::new())),
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -93,243 +20,44 @@ impl<E: Application> Default for NodeInventory<E> {
|
||||
impl<E: Application> Clone for NodeInventory<E> {
|
||||
fn clone(&self) -> Self {
|
||||
Self {
|
||||
inner: Arc::clone(&self.inner),
|
||||
clients: Arc::clone(&self.clients),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<E: Application> NodeInventory<E> {
|
||||
#[must_use]
|
||||
/// Builds an inventory from managed clients.
|
||||
pub fn from_managed_clients(clients: Vec<E::NodeClient>) -> Self {
|
||||
let inventory = Self::default();
|
||||
|
||||
for client in clients {
|
||||
inventory.add_managed_node(client, None);
|
||||
}
|
||||
|
||||
inventory
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
/// Returns a cloned snapshot of all node clients.
|
||||
pub fn snapshot_clients(&self) -> Vec<E::NodeClient> {
|
||||
self.inner.read().nodes.iter().map(clone_client).collect()
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
/// Returns cloned managed node handles from the current inventory.
|
||||
pub fn managed_nodes(&self) -> Vec<ManagedNode<E>> {
|
||||
self.inner
|
||||
.read()
|
||||
.nodes
|
||||
.iter()
|
||||
.filter_map(|handle| match handle {
|
||||
NodeHandle::Managed(node) => Some(node.clone()),
|
||||
NodeHandle::Borrowed(_) => None,
|
||||
})
|
||||
.collect()
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
/// Returns cloned borrowed node handles from the current inventory.
|
||||
pub fn borrowed_nodes(&self) -> Vec<BorrowedNode<E>> {
|
||||
self.inner
|
||||
.read()
|
||||
.nodes
|
||||
.iter()
|
||||
.filter_map(|handle| match handle {
|
||||
NodeHandle::Managed(_) => None,
|
||||
NodeHandle::Borrowed(node) => Some(node.clone()),
|
||||
})
|
||||
.collect()
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
/// Finds a managed node by canonical identity.
|
||||
pub fn find_managed(&self, identity: &str) -> Option<ManagedNode<E>> {
|
||||
let guard = self.inner.read();
|
||||
match node_by_identity(&guard, identity)? {
|
||||
NodeHandle::Managed(node) => Some(node.clone()),
|
||||
NodeHandle::Borrowed(_) => None,
|
||||
pub(crate) fn from_clients(clients: Vec<E::NodeClient>) -> Self {
|
||||
Self {
|
||||
clients: Arc::new(RwLock::new(clients)),
|
||||
}
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
/// Finds a borrowed node by canonical identity.
|
||||
pub fn find_borrowed(&self, identity: &str) -> Option<BorrowedNode<E>> {
|
||||
let guard = self.inner.read();
|
||||
match node_by_identity(&guard, identity)? {
|
||||
NodeHandle::Managed(_) => None,
|
||||
NodeHandle::Borrowed(node) => Some(node.clone()),
|
||||
}
|
||||
pub(crate) fn snapshot_clients(&self) -> Vec<E::NodeClient> {
|
||||
self.clients.read().clone()
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
/// Finds any node handle by canonical identity.
|
||||
pub fn find_node(&self, identity: &str) -> Option<NodeHandle<E>> {
|
||||
let guard = self.inner.read();
|
||||
node_by_identity(&guard, identity).cloned()
|
||||
pub(crate) fn len(&self) -> usize {
|
||||
self.clients.read().len()
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
/// Returns current number of nodes in inventory.
|
||||
pub fn len(&self) -> usize {
|
||||
self.inner.read().nodes.len()
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
/// Returns true when no nodes are registered.
|
||||
pub fn is_empty(&self) -> bool {
|
||||
pub(crate) fn is_empty(&self) -> bool {
|
||||
self.len() == 0
|
||||
}
|
||||
|
||||
/// Clears all nodes and identity indexes.
|
||||
pub fn clear(&self) {
|
||||
let mut guard = self.inner.write();
|
||||
guard.nodes.clear();
|
||||
guard.indices_by_identity.clear();
|
||||
guard.next_synthetic_id = 0;
|
||||
pub(crate) fn clear(&self) {
|
||||
self.clients.write().clear();
|
||||
}
|
||||
|
||||
/// Adds or replaces a managed node entry using canonical identity
|
||||
/// resolution. Re-adding the same node identity updates the stored handle.
|
||||
pub fn add_managed_node(&self, client: E::NodeClient, identity_hint: Option<String>) {
|
||||
let mut guard = self.inner.write();
|
||||
let identity = canonical_identity::<E>(&client, identity_hint, &mut guard);
|
||||
let handle = NodeHandle::Managed(ManagedNode {
|
||||
identity: identity.clone(),
|
||||
client,
|
||||
});
|
||||
upsert_node(&mut guard, identity, handle);
|
||||
pub(crate) fn add_client(&self, client: E::NodeClient) {
|
||||
self.clients.write().push(client);
|
||||
}
|
||||
|
||||
/// Adds or replaces an attached node entry.
|
||||
pub fn add_attached_node(&self, client: E::NodeClient, identity_hint: Option<String>) {
|
||||
self.add_borrowed_node(client, BorrowedOrigin::Attached, identity_hint);
|
||||
}
|
||||
|
||||
/// Adds or replaces an external static node entry.
|
||||
pub fn add_external_node(&self, client: E::NodeClient, identity_hint: Option<String>) {
|
||||
self.add_borrowed_node(client, BorrowedOrigin::External, identity_hint);
|
||||
}
|
||||
|
||||
/// Executes a synchronous read over a cloned client slice.
|
||||
pub fn with_clients<R>(&self, f: impl FnOnce(&[E::NodeClient]) -> R) -> R {
|
||||
let guard = self.inner.read();
|
||||
let clients = guard.nodes.iter().map(clone_client).collect::<Vec<_>>();
|
||||
pub(crate) fn with_clients<R>(&self, f: impl FnOnce(&[E::NodeClient]) -> R) -> R {
|
||||
let clients = self.clients.read();
|
||||
f(&clients)
|
||||
}
|
||||
|
||||
fn add_borrowed_node(
|
||||
&self,
|
||||
client: E::NodeClient,
|
||||
origin: BorrowedOrigin,
|
||||
identity_hint: Option<String>,
|
||||
) {
|
||||
let mut guard = self.inner.write();
|
||||
let identity = canonical_identity::<E>(&client, identity_hint, &mut guard);
|
||||
let handle = NodeHandle::Borrowed(BorrowedNode {
|
||||
identity: identity.clone(),
|
||||
client,
|
||||
origin,
|
||||
});
|
||||
upsert_node(&mut guard, identity, handle);
|
||||
}
|
||||
}
|
||||
|
||||
impl<E: Application> ManagedNode<E> {
|
||||
#[must_use]
|
||||
/// Returns the node client.
|
||||
pub const fn client(&self) -> &E::NodeClient {
|
||||
&self.client
|
||||
}
|
||||
|
||||
/// Delegates restart to the deployer's control surface for this node name.
|
||||
pub async fn restart(
|
||||
&self,
|
||||
control: &dyn NodeControlHandle<E>,
|
||||
node_name: &str,
|
||||
) -> Result<(), DynError> {
|
||||
control.restart_node(node_name).await
|
||||
}
|
||||
|
||||
/// Delegates stop to the deployer's control surface for this node name.
|
||||
pub async fn stop(
|
||||
&self,
|
||||
control: &dyn NodeControlHandle<E>,
|
||||
node_name: &str,
|
||||
) -> Result<(), DynError> {
|
||||
control.stop_node(node_name).await
|
||||
}
|
||||
|
||||
/// Delegates dynamic node start with options to the control surface.
|
||||
pub async fn start_with(
|
||||
&self,
|
||||
control: &dyn NodeControlHandle<E>,
|
||||
node_name: &str,
|
||||
options: StartNodeOptions<E>,
|
||||
) -> Result<StartedNode<E>, DynError> {
|
||||
control.start_node_with(node_name, options).await
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
/// Returns process id if the backend can expose it for this node name.
|
||||
pub fn pid(&self, control: &dyn NodeControlHandle<E>, node_name: &str) -> Option<u32> {
|
||||
control.node_pid(node_name)
|
||||
}
|
||||
}
|
||||
|
||||
impl<E: Application> BorrowedNode<E> {
|
||||
#[must_use]
|
||||
/// Returns the node client.
|
||||
pub const fn client(&self) -> &E::NodeClient {
|
||||
&self.client
|
||||
}
|
||||
}
|
||||
|
||||
fn upsert_node<E: Application>(
|
||||
inner: &mut NodeInventoryInner<E>,
|
||||
identity: String,
|
||||
handle: NodeHandle<E>,
|
||||
) {
|
||||
if let Some(existing_index) = inner.indices_by_identity.get(&identity).copied() {
|
||||
inner.nodes[existing_index] = handle;
|
||||
return;
|
||||
}
|
||||
|
||||
let index = inner.nodes.len();
|
||||
inner.nodes.push(handle);
|
||||
inner.indices_by_identity.insert(identity, index);
|
||||
}
|
||||
|
||||
fn canonical_identity<E: Application>(
|
||||
_client: &E::NodeClient,
|
||||
identity_hint: Option<String>,
|
||||
inner: &mut NodeInventoryInner<E>,
|
||||
) -> String {
|
||||
// Priority: explicit hint -> synthetic.
|
||||
if let Some(identity) = identity_hint.filter(|value| !value.trim().is_empty()) {
|
||||
return identity;
|
||||
}
|
||||
|
||||
let synthetic = format!("node:{}", inner.next_synthetic_id);
|
||||
inner.next_synthetic_id += 1;
|
||||
|
||||
synthetic
|
||||
}
|
||||
|
||||
fn clone_client<E: Application>(handle: &NodeHandle<E>) -> E::NodeClient {
|
||||
match handle {
|
||||
NodeHandle::Managed(node) => node.client.clone(),
|
||||
NodeHandle::Borrowed(node) => node.client.clone(),
|
||||
}
|
||||
}
|
||||
|
||||
fn node_by_identity<'a, E: Application>(
|
||||
inner: &'a NodeInventoryInner<E>,
|
||||
identity: &str,
|
||||
) -> Option<&'a NodeHandle<E>> {
|
||||
let index = *inner.indices_by_identity.get(identity)?;
|
||||
inner.nodes.get(index)
|
||||
}
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
pub mod context;
|
||||
mod deployer;
|
||||
pub mod inventory;
|
||||
mod inventory;
|
||||
pub mod metrics;
|
||||
mod node_clients;
|
||||
pub mod orchestration;
|
||||
@ -11,7 +11,6 @@ mod runner;
|
||||
use async_trait::async_trait;
|
||||
pub use context::{CleanupGuard, RunContext, RunHandle, RunMetrics};
|
||||
pub use deployer::{Deployer, ScenarioError};
|
||||
pub use inventory::{BorrowedNode, BorrowedOrigin, ManagedNode, NodeHandle, NodeInventory};
|
||||
pub use node_clients::NodeClients;
|
||||
#[doc(hidden)]
|
||||
pub use orchestration::{
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
use rand::{seq::SliceRandom as _, thread_rng};
|
||||
|
||||
use super::inventory::{BorrowedNode, ManagedNode, NodeInventory};
|
||||
use super::inventory::NodeInventory;
|
||||
use crate::scenario::{Application, DynError};
|
||||
|
||||
/// Collection of API clients for the node set.
|
||||
@ -29,7 +29,7 @@ impl<E: Application> NodeClients<E> {
|
||||
/// Build clients from preconstructed vectors.
|
||||
pub fn new(nodes: Vec<E::NodeClient>) -> Self {
|
||||
Self {
|
||||
inventory: NodeInventory::from_managed_clients(nodes),
|
||||
inventory: NodeInventory::from_clients(nodes),
|
||||
}
|
||||
}
|
||||
|
||||
@ -72,37 +72,13 @@ impl<E: Application> NodeClients<E> {
|
||||
}
|
||||
|
||||
pub fn add_node(&self, client: E::NodeClient) {
|
||||
self.inventory.add_managed_node(client, None);
|
||||
self.inventory.add_client(client);
|
||||
}
|
||||
|
||||
pub fn clear(&self) {
|
||||
self.inventory.clear();
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
/// Returns a cloned snapshot of managed node handles.
|
||||
pub fn managed_nodes(&self) -> Vec<ManagedNode<E>> {
|
||||
self.inventory.managed_nodes()
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
/// Returns a cloned snapshot of borrowed node handles.
|
||||
pub fn borrowed_nodes(&self) -> Vec<BorrowedNode<E>> {
|
||||
self.inventory.borrowed_nodes()
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
/// Finds a managed node by canonical identity.
|
||||
pub fn find_managed(&self, identity: &str) -> Option<ManagedNode<E>> {
|
||||
self.inventory.find_managed(identity)
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
/// Finds a borrowed node by canonical identity.
|
||||
pub fn find_borrowed(&self, identity: &str) -> Option<BorrowedNode<E>> {
|
||||
self.inventory.find_borrowed(identity)
|
||||
}
|
||||
|
||||
fn shuffled_snapshot(&self) -> Vec<E::NodeClient> {
|
||||
let mut clients = self.snapshot();
|
||||
clients.shuffle(&mut thread_rng());
|
||||
|
||||
@ -3,9 +3,9 @@ mod source_orchestration_plan;
|
||||
#[allow(dead_code)]
|
||||
mod source_resolver;
|
||||
|
||||
pub(crate) use source_orchestration_plan::SourceOrchestrationMode;
|
||||
pub use source_orchestration_plan::{
|
||||
ManagedSource, SourceModeName, SourceOrchestrationMode, SourceOrchestrationPlan,
|
||||
SourceOrchestrationPlanError,
|
||||
ManagedSource, SourceOrchestrationPlan, SourceOrchestrationPlanError,
|
||||
};
|
||||
pub use source_resolver::{
|
||||
build_source_orchestration_plan, orchestrate_sources, orchestrate_sources_with_providers,
|
||||
|
||||
@ -1,6 +1,4 @@
|
||||
use std::fmt;
|
||||
|
||||
use crate::scenario::{AttachSource, ExternalNodeSource, ScenarioSources, SourceReadinessPolicy};
|
||||
use crate::scenario::{AttachSource, ExternalNodeSource, ScenarioSources};
|
||||
|
||||
/// Explicit descriptor for managed node sourcing.
|
||||
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
|
||||
@ -15,7 +13,7 @@ pub enum ManagedSource {
|
||||
/// This is scaffolding-only and is intentionally not executed by deployers
|
||||
/// yet.
|
||||
#[derive(Clone, Debug, Eq, PartialEq)]
|
||||
pub enum SourceOrchestrationMode {
|
||||
pub(crate) enum SourceOrchestrationMode {
|
||||
Managed {
|
||||
managed: ManagedSource,
|
||||
external: Vec<ExternalNodeSource>,
|
||||
@ -34,41 +32,28 @@ pub enum SourceOrchestrationMode {
|
||||
/// This captures only mapping-time source intent and readiness policy.
|
||||
#[derive(Clone, Debug, Eq, PartialEq)]
|
||||
pub struct SourceOrchestrationPlan {
|
||||
pub mode: SourceOrchestrationMode,
|
||||
pub readiness_policy: SourceReadinessPolicy,
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
|
||||
pub enum SourceModeName {
|
||||
Attached,
|
||||
}
|
||||
|
||||
impl fmt::Display for SourceModeName {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
match self {
|
||||
Self::Attached => f.write_str("Attached"),
|
||||
}
|
||||
}
|
||||
mode: SourceOrchestrationMode,
|
||||
}
|
||||
|
||||
/// Validation failure while building orchestration plan from sources.
|
||||
#[derive(Debug, thiserror::Error)]
|
||||
pub enum SourceOrchestrationPlanError {
|
||||
#[error("source mode '{mode}' is not wired into deployers yet")]
|
||||
SourceModeNotWiredYet { mode: SourceModeName },
|
||||
SourceModeNotWiredYet { mode: &'static str },
|
||||
}
|
||||
|
||||
impl SourceOrchestrationPlan {
|
||||
pub fn try_from_sources(
|
||||
sources: &ScenarioSources,
|
||||
readiness_policy: SourceReadinessPolicy,
|
||||
) -> Result<Self, SourceOrchestrationPlanError> {
|
||||
let mode = mode_from_sources(sources);
|
||||
|
||||
Ok(Self {
|
||||
mode,
|
||||
readiness_policy,
|
||||
})
|
||||
Ok(Self { mode })
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub(crate) fn mode(&self) -> &SourceOrchestrationMode {
|
||||
&self.mode
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
@ -84,17 +69,16 @@ impl SourceOrchestrationPlan {
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::{SourceOrchestrationMode, SourceOrchestrationPlan};
|
||||
use crate::scenario::{AttachSource, ScenarioSources, SourceReadinessPolicy};
|
||||
use crate::scenario::{AttachSource, ScenarioSources};
|
||||
|
||||
#[test]
|
||||
fn attached_sources_are_planned() {
|
||||
let sources = ScenarioSources::attached(AttachSource::compose(vec!["node-0".to_string()]));
|
||||
let plan =
|
||||
SourceOrchestrationPlan::try_from_sources(&sources, SourceReadinessPolicy::AllReady)
|
||||
.expect("attached sources should build a source orchestration plan");
|
||||
let plan = SourceOrchestrationPlan::try_from_sources(&sources)
|
||||
.expect("attached sources should build a source orchestration plan");
|
||||
|
||||
assert!(matches!(
|
||||
plan.mode,
|
||||
plan.mode(),
|
||||
SourceOrchestrationMode::Attached { .. }
|
||||
));
|
||||
}
|
||||
|
||||
@ -41,10 +41,7 @@ pub enum SourceResolveError {
|
||||
pub fn build_source_orchestration_plan<E: Application, Caps>(
|
||||
scenario: &Scenario<E, Caps>,
|
||||
) -> Result<SourceOrchestrationPlan, SourceOrchestrationPlanError> {
|
||||
SourceOrchestrationPlan::try_from_sources(
|
||||
scenario.sources(),
|
||||
scenario.source_readiness_policy(),
|
||||
)
|
||||
SourceOrchestrationPlan::try_from_sources(scenario.sources())
|
||||
}
|
||||
|
||||
/// Resolves runtime source nodes via unified providers from orchestration plan.
|
||||
@ -52,7 +49,7 @@ pub async fn resolve_sources<E: Application>(
|
||||
plan: &SourceOrchestrationPlan,
|
||||
providers: &SourceProviders<E>,
|
||||
) -> Result<ResolvedSources<E>, SourceResolveError> {
|
||||
match &plan.mode {
|
||||
match plan.mode() {
|
||||
SourceOrchestrationMode::Managed { managed, .. } => {
|
||||
let managed_nodes = providers.managed.provide(managed).await?;
|
||||
let external_nodes = providers.external.provide(plan.external_sources()).await?;
|
||||
@ -115,7 +112,8 @@ pub async fn orchestrate_sources_with_providers<E: Application>(
|
||||
) -> Result<NodeClients<E>, DynError> {
|
||||
let resolved = resolve_sources(plan, &providers).await?;
|
||||
|
||||
if matches!(plan.mode, SourceOrchestrationMode::Managed { .. }) && resolved.managed.is_empty() {
|
||||
if matches!(plan.mode(), SourceOrchestrationMode::Managed { .. }) && resolved.managed.is_empty()
|
||||
{
|
||||
return Err(SourceResolveError::ManagedNodesMissing.into());
|
||||
}
|
||||
|
||||
|
||||
@ -71,11 +71,11 @@ impl<E: Application> ExternalProvider<E> for ApplicationExternalProvider {
|
||||
.map(|source| {
|
||||
E::external_node_client(source)
|
||||
.map(|client| ExternalNode {
|
||||
identity_hint: Some(source.label.clone()),
|
||||
identity_hint: Some(source.label().to_string()),
|
||||
client,
|
||||
})
|
||||
.map_err(|build_error| ExternalProviderError::Build {
|
||||
source_label: source.label.clone(),
|
||||
source_label: source.label().to_string(),
|
||||
source: build_error,
|
||||
})
|
||||
})
|
||||
|
||||
@ -36,6 +36,7 @@ impl<E: Application> Drop for Runner<E> {
|
||||
impl<E: Application> Runner<E> {
|
||||
/// Construct a runner from the run context and optional cleanup guard.
|
||||
#[must_use]
|
||||
#[doc(hidden)]
|
||||
pub fn new(context: RunContext<E>, cleanup_guard: Option<Box<dyn CleanupGuard>>) -> Self {
|
||||
Self {
|
||||
context: Arc::new(context),
|
||||
@ -45,8 +46,8 @@ impl<E: Application> Runner<E> {
|
||||
|
||||
/// Access the underlying run context.
|
||||
#[must_use]
|
||||
pub fn context(&self) -> Arc<RunContext<E>> {
|
||||
Arc::clone(&self.context)
|
||||
pub fn context(&self) -> &RunContext<E> {
|
||||
self.context.as_ref()
|
||||
}
|
||||
|
||||
pub async fn wait_network_ready(&self) -> Result<(), DynError> {
|
||||
@ -71,7 +72,7 @@ impl<E: Application> Runner<E> {
|
||||
where
|
||||
Caps: Send + Sync,
|
||||
{
|
||||
let context = self.context();
|
||||
let context = Arc::clone(&self.context);
|
||||
let run_duration = scenario.duration();
|
||||
let workloads = scenario.workloads().to_vec();
|
||||
let expectation_count = scenario.expectations().len();
|
||||
|
||||
@ -1,3 +1,3 @@
|
||||
mod model;
|
||||
|
||||
pub use model::{AttachSource, ExternalNodeSource, ScenarioSources, SourceReadinessPolicy};
|
||||
pub use model::{AttachSource, ExternalNodeSource, ScenarioSources};
|
||||
|
||||
@ -21,13 +21,10 @@ impl AttachSource {
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub fn with_namespace(self, namespace: String) -> Self {
|
||||
match self {
|
||||
Self::K8s { label_selector, .. } => Self::K8s {
|
||||
namespace: Some(namespace),
|
||||
label_selector,
|
||||
},
|
||||
other => other,
|
||||
pub fn k8s_in_namespace(label_selector: String, namespace: String) -> Self {
|
||||
Self::K8s {
|
||||
namespace: Some(namespace),
|
||||
label_selector,
|
||||
}
|
||||
}
|
||||
|
||||
@ -40,13 +37,10 @@ impl AttachSource {
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub fn with_project(self, project: String) -> Self {
|
||||
match self {
|
||||
Self::Compose { services, .. } => Self::Compose {
|
||||
project: Some(project),
|
||||
services,
|
||||
},
|
||||
other => other,
|
||||
pub fn compose_in_project(services: Vec<String>, project: String) -> Self {
|
||||
Self::Compose {
|
||||
project: Some(project),
|
||||
services,
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -55,8 +49,8 @@ impl AttachSource {
|
||||
/// inventory.
|
||||
#[derive(Clone, Debug, Eq, PartialEq)]
|
||||
pub struct ExternalNodeSource {
|
||||
pub label: String,
|
||||
pub endpoint: String,
|
||||
label: String,
|
||||
endpoint: String,
|
||||
}
|
||||
|
||||
impl ExternalNodeSource {
|
||||
@ -64,19 +58,16 @@ impl ExternalNodeSource {
|
||||
pub fn new(label: String, endpoint: String) -> Self {
|
||||
Self { label, endpoint }
|
||||
}
|
||||
}
|
||||
|
||||
/// Planned readiness strategy for mixed managed/attached/external sources.
|
||||
#[derive(Clone, Copy, Debug, Eq, PartialEq, Default)]
|
||||
pub enum SourceReadinessPolicy {
|
||||
/// Phase 1 default: require every known node to pass readiness checks.
|
||||
#[default]
|
||||
AllReady,
|
||||
/// Optional relaxed policy for large/partial environments.
|
||||
Quorum,
|
||||
/// Future policy for per-source constraints (for example managed minimum
|
||||
/// plus overall quorum).
|
||||
SourceAware,
|
||||
#[must_use]
|
||||
pub fn label(&self) -> &str {
|
||||
&self.label
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub fn endpoint(&self) -> &str {
|
||||
&self.endpoint
|
||||
}
|
||||
}
|
||||
|
||||
/// Source model that makes invalid managed+attached combinations
|
||||
@ -124,22 +115,29 @@ impl ScenarioSources {
|
||||
Self::ExternalOnly { external }
|
||||
}
|
||||
|
||||
pub fn add_external_node(&mut self, node: ExternalNodeSource) {
|
||||
match self {
|
||||
#[must_use]
|
||||
pub fn with_external_node(mut self, node: ExternalNodeSource) -> Self {
|
||||
match &mut self {
|
||||
Self::Managed { external }
|
||||
| Self::Attached { external, .. }
|
||||
| Self::ExternalOnly { external } => external.push(node),
|
||||
}
|
||||
|
||||
self
|
||||
}
|
||||
|
||||
pub fn set_attach(&mut self, attach: AttachSource) {
|
||||
#[must_use]
|
||||
pub fn with_attach(self, attach: AttachSource) -> Self {
|
||||
let external = self.external_nodes().to_vec();
|
||||
*self = Self::Attached { attach, external };
|
||||
|
||||
Self::Attached { attach, external }
|
||||
}
|
||||
|
||||
pub fn set_external_only(&mut self) {
|
||||
#[must_use]
|
||||
pub fn into_external_only(self) -> Self {
|
||||
let external = self.external_nodes().to_vec();
|
||||
*self = Self::ExternalOnly { external };
|
||||
|
||||
Self::ExternalOnly { external }
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
|
||||
@ -50,7 +50,10 @@ impl ComposeDeploymentMetadata {
|
||||
.project_name()
|
||||
.ok_or(ComposeMetadataError::MissingProjectName)?;
|
||||
|
||||
Ok(AttachSource::compose(Vec::new()).with_project(project_name.to_owned()))
|
||||
Ok(AttachSource::compose_in_project(
|
||||
Vec::new(),
|
||||
project_name.to_owned(),
|
||||
))
|
||||
}
|
||||
|
||||
/// Builds an attach source for the same compose project.
|
||||
@ -62,7 +65,10 @@ impl ComposeDeploymentMetadata {
|
||||
.project_name()
|
||||
.ok_or(ComposeMetadataError::MissingProjectName)?;
|
||||
|
||||
Ok(AttachSource::compose(services).with_project(project_name.to_owned()))
|
||||
Ok(AttachSource::compose_in_project(
|
||||
services,
|
||||
project_name.to_owned(),
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -321,7 +321,7 @@ impl<E: ComposeDeployEnv> DeploymentOrchestrator<E> {
|
||||
fn managed_cluster_wait(&self, project_name: String) -> Arc<dyn ClusterWaitHandle<E>> {
|
||||
Arc::new(ComposeAttachedClusterWait::<E>::new(
|
||||
compose_runner_host(),
|
||||
AttachSource::compose(Vec::new()).with_project(project_name),
|
||||
AttachSource::compose_in_project(Vec::new(), project_name),
|
||||
))
|
||||
}
|
||||
|
||||
|
||||
@ -41,6 +41,9 @@ impl K8sDeploymentMetadata {
|
||||
.label_selector()
|
||||
.ok_or(K8sMetadataError::MissingLabelSelector)?;
|
||||
|
||||
Ok(AttachSource::k8s(label_selector.to_owned()).with_namespace(namespace.to_owned()))
|
||||
Ok(AttachSource::k8s_in_namespace(
|
||||
label_selector.to_owned(),
|
||||
namespace.to_owned(),
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
@ -35,8 +35,8 @@ pub fn build_external_client<E: LocalDeployerEnv>(
|
||||
}
|
||||
|
||||
fn resolve_api_socket(source: &ExternalNodeSource) -> Result<std::net::SocketAddr, DynError> {
|
||||
let source_label = source.label.clone();
|
||||
let endpoint = source.endpoint.trim();
|
||||
let source_label = source.label().to_string();
|
||||
let endpoint = source.endpoint().trim();
|
||||
if endpoint.is_empty() {
|
||||
return Err(ExternalClientBuildError::EmptyEndpoint {
|
||||
label: source_label,
|
||||
|
||||
@ -1,8 +1,8 @@
|
||||
use testing_framework_core::{
|
||||
manual::ManualClusterHandle,
|
||||
scenario::{
|
||||
DynError, ExternalNodeSource, NodeClients, NodeControlHandle, ReadinessError,
|
||||
StartNodeOptions, StartedNode,
|
||||
ClusterWaitHandle, DynError, ExternalNodeSource, NodeClients, NodeControlHandle,
|
||||
ReadinessError, StartNodeOptions, StartedNode,
|
||||
},
|
||||
};
|
||||
use thiserror::Error;
|
||||
@ -157,19 +157,11 @@ impl<E: LocalDeployerEnv> NodeControlHandle<E> for ManualCluster<E> {
|
||||
}
|
||||
|
||||
#[async_trait::async_trait]
|
||||
impl<E: LocalDeployerEnv> ManualClusterHandle<E> for ManualCluster<E> {
|
||||
async fn start_node_with(
|
||||
&self,
|
||||
name: &str,
|
||||
options: StartNodeOptions<E>,
|
||||
) -> Result<StartedNode<E>, DynError> {
|
||||
self.nodes
|
||||
.start_node_with(name, options)
|
||||
.await
|
||||
.map_err(|err| err.into())
|
||||
}
|
||||
|
||||
impl<E: LocalDeployerEnv> ClusterWaitHandle<E> for ManualCluster<E> {
|
||||
async fn wait_network_ready(&self) -> Result<(), DynError> {
|
||||
self.wait_network_ready().await.map_err(|err| err.into())
|
||||
}
|
||||
}
|
||||
|
||||
#[async_trait::async_trait]
|
||||
impl<E: LocalDeployerEnv> ManualClusterHandle<E> for ManualCluster<E> {}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user