Route source access through semantic helpers

This commit is contained in:
andrussal 2026-03-08 13:56:12 +01:00
parent 3ea3fffd1f
commit eeb0573798
6 changed files with 109 additions and 81 deletions

View File

@ -117,6 +117,16 @@ impl<E: Application, Caps> Scenario<E, Caps> {
&self.sources
}
#[must_use]
pub fn attached_source(&self) -> Option<&AttachSource> {
self.sources.attached_source()
}
#[must_use]
pub fn external_nodes(&self) -> &[ExternalNodeSource] {
self.sources.external_nodes()
}
#[must_use]
pub const fn source_orchestration_plan(&self) -> &SourceOrchestrationPlan {
&self.source_orchestration_plan

View File

@ -43,6 +43,38 @@ impl AttachSource {
services,
}
}
#[must_use]
pub fn compose_project(&self) -> Option<&str> {
match self {
Self::Compose { project, .. } => project.as_deref(),
Self::K8s { .. } => None,
}
}
#[must_use]
pub fn compose_services(&self) -> Option<&[String]> {
match self {
Self::Compose { services, .. } => Some(services),
Self::K8s { .. } => None,
}
}
#[must_use]
pub fn k8s_namespace(&self) -> Option<&str> {
match self {
Self::K8s { namespace, .. } => namespace.as_deref(),
Self::Compose { .. } => None,
}
}
#[must_use]
pub fn k8s_label_selector(&self) -> Option<&str> {
match self {
Self::K8s { label_selector, .. } => Some(label_selector),
Self::Compose { .. } => None,
}
}
}
/// Static external node endpoint that should be included in the runtime
@ -140,6 +172,14 @@ impl ScenarioSources {
Self::ExternalOnly { external }
}
#[must_use]
pub fn attached_source(&self) -> Option<&AttachSource> {
match self {
Self::Attached { attach, .. } => Some(attach),
Self::Managed { .. } | Self::ExternalOnly { .. } => None,
}
}
#[must_use]
pub fn external_nodes(&self) -> &[ExternalNodeSource] {
match self {

View File

@ -87,14 +87,15 @@ fn to_discovery_error(source: DynError) -> AttachProviderError {
fn compose_attach_request(
source: &AttachSource,
) -> Result<ComposeAttachRequest<'_>, AttachProviderError> {
let AttachSource::Compose { project, services } = source else {
return Err(AttachProviderError::UnsupportedSource {
attach_source: source.clone(),
});
};
let services =
source
.compose_services()
.ok_or_else(|| AttachProviderError::UnsupportedSource {
attach_source: source.clone(),
})?;
let project = project
.as_deref()
let project = source
.compose_project()
.ok_or_else(|| AttachProviderError::Discovery {
source: ComposeAttachDiscoveryError::MissingProjectName.into(),
})?;
@ -173,13 +174,12 @@ impl<E: ComposeDeployEnv> ClusterWaitHandle<E> for ComposeAttachedClusterWait<E>
}
fn compose_wait_request(source: &AttachSource) -> Result<ComposeAttachRequest<'_>, DynError> {
let AttachSource::Compose { project, services } = source else {
return Err("compose cluster wait requires a compose attach source".into());
};
let project = project
.as_deref()
.ok_or(ComposeAttachDiscoveryError::MissingProjectName)?;
let project = source
.compose_project()
.ok_or_else(|| DynError::from("compose cluster wait requires a compose attach source"))?;
let services = source
.compose_services()
.ok_or_else(|| DynError::from("compose cluster wait requires a compose attach source"))?;
Ok(ComposeAttachRequest { project, services })
}

View File

@ -6,9 +6,9 @@ use testing_framework_core::{
ApplicationExternalProvider, AttachSource, CleanupGuard, ClusterWaitHandle,
DeploymentPolicy, FeedHandle, FeedRuntime, HttpReadinessRequirement, Metrics, NodeClients,
NodeControlHandle, ObservabilityCapabilityProvider, ObservabilityInputs,
RequiresNodeControl, RunContext, Runner, Scenario, ScenarioSources,
SourceOrchestrationPlan, SourceProviders, StaticManagedProvider,
build_source_orchestration_plan, orchestrate_sources_with_providers,
RequiresNodeControl, RunContext, Runner, Scenario, SourceOrchestrationPlan,
SourceProviders, StaticManagedProvider, build_source_orchestration_plan,
orchestrate_sources_with_providers,
},
topology::DeploymentDescriptor,
};
@ -214,21 +214,15 @@ impl<E: ComposeDeployEnv> DeploymentOrchestrator<E> {
return Ok(None);
}
let ScenarioSources::Attached { attach, .. } = scenario.sources() else {
return Err(ComposeRunnerError::InternalInvariant {
let attach = scenario
.attached_source()
.ok_or(ComposeRunnerError::InternalInvariant {
message: "attached node control requested outside attached source mode",
});
};
})?;
let AttachSource::Compose { project, .. } = attach else {
return Err(ComposeRunnerError::InternalInvariant {
message: "compose deployer requires compose attach source for node control",
});
};
let Some(project_name) = project
.as_ref()
.map(|value| value.trim())
let Some(project_name) = attach
.compose_project()
.map(str::trim)
.filter(|value| !value.is_empty())
else {
return Err(ComposeRunnerError::InternalInvariant {
@ -248,11 +242,11 @@ impl<E: ComposeDeployEnv> DeploymentOrchestrator<E> {
where
Caps: Send + Sync,
{
let ScenarioSources::Attached { attach, .. } = scenario.sources() else {
return Err(ComposeRunnerError::InternalInvariant {
let attach = scenario
.attached_source()
.ok_or(ComposeRunnerError::InternalInvariant {
message: "compose attached cluster wait requested outside attached source mode",
});
};
})?;
Ok(Arc::new(ComposeAttachedClusterWait::<E>::new(
compose_runner_host(),
@ -378,13 +372,10 @@ where
E: ComposeDeployEnv,
Caps: Send + Sync,
{
let project_name = match scenario.sources() {
ScenarioSources::Attached {
attach: AttachSource::Compose { project, .. },
..
} => project.clone(),
_ => None,
};
let project_name = scenario
.attached_source()
.and_then(|attach| attach.compose_project())
.map(ToOwned::to_owned);
ComposeDeploymentMetadata { project_name }
}

View File

@ -91,11 +91,7 @@ fn to_discovery_error(source: DynError) -> AttachProviderError {
}
fn k8s_attach_request(source: &AttachSource) -> Result<K8sAttachRequest<'_>, AttachProviderError> {
let AttachSource::K8s {
namespace,
label_selector,
} = source
else {
let Some(label_selector) = source.k8s_label_selector() else {
return Err(AttachProviderError::UnsupportedSource {
attach_source: source.clone(),
});
@ -108,7 +104,7 @@ fn k8s_attach_request(source: &AttachSource) -> Result<K8sAttachRequest<'_>, Att
}
Ok(K8sAttachRequest {
namespace: namespace.as_deref().unwrap_or("default"),
namespace: source.k8s_namespace().unwrap_or("default"),
label_selector,
})
}
@ -247,20 +243,16 @@ impl<E: K8sDeployEnv> ClusterWaitHandle<E> for K8sAttachedClusterWait<E> {
}
fn k8s_wait_request(source: &AttachSource) -> Result<K8sAttachRequest<'_>, DynError> {
let AttachSource::K8s {
namespace,
label_selector,
} = source
else {
return Err("k8s cluster wait requires a k8s attach source".into());
};
let label_selector = source
.k8s_label_selector()
.ok_or_else(|| DynError::from("k8s cluster wait requires a k8s attach source"))?;
if label_selector.trim().is_empty() {
return Err(K8sAttachDiscoveryError::EmptyLabelSelector.into());
}
Ok(K8sAttachRequest {
namespace: namespace.as_deref().unwrap_or("default"),
namespace: source.k8s_namespace().unwrap_or("default"),
label_selector,
})
}

View File

@ -5,12 +5,11 @@ use kube::Client;
use reqwest::Url;
use testing_framework_core::{
scenario::{
Application, ApplicationExternalProvider, AttachSource, CleanupGuard, ClusterWaitHandle,
Deployer, DynError, FeedHandle, FeedRuntime, HttpReadinessRequirement, Metrics,
MetricsError, NodeClients, ObservabilityCapabilityProvider, ObservabilityInputs,
RequiresNodeControl, RunContext, Runner, Scenario, ScenarioSources,
SourceOrchestrationPlan, SourceProviders, StaticManagedProvider,
build_source_orchestration_plan, orchestrate_sources_with_providers,
Application, ApplicationExternalProvider, CleanupGuard, ClusterWaitHandle, Deployer,
DynError, FeedHandle, FeedRuntime, HttpReadinessRequirement, Metrics, MetricsError,
NodeClients, ObservabilityCapabilityProvider, ObservabilityInputs, RequiresNodeControl,
RunContext, Runner, Scenario, SourceOrchestrationPlan, SourceProviders,
StaticManagedProvider, build_source_orchestration_plan, orchestrate_sources_with_providers,
},
topology::DeploymentDescriptor,
};
@ -250,22 +249,18 @@ where
E: K8sDeployEnv,
Caps: Send + Sync,
{
match scenario.sources() {
ScenarioSources::Attached {
attach:
AttachSource::K8s {
namespace,
label_selector,
},
..
} => K8sDeploymentMetadata {
namespace: namespace.clone(),
label_selector: Some(label_selector.clone()),
},
_ => K8sDeploymentMetadata {
namespace: None,
label_selector: None,
},
let namespace = scenario
.attached_source()
.and_then(|attach| attach.k8s_namespace())
.map(ToOwned::to_owned);
let label_selector = scenario
.attached_source()
.and_then(|attach| attach.k8s_label_selector())
.map(ToOwned::to_owned);
K8sDeploymentMetadata {
namespace,
label_selector,
}
}
@ -277,11 +272,11 @@ where
E: K8sDeployEnv,
Caps: Send + Sync,
{
let ScenarioSources::Attached { attach, .. } = scenario.sources() else {
return Err(K8sRunnerError::InternalInvariant {
let attach = scenario
.attached_source()
.ok_or_else(|| K8sRunnerError::InternalInvariant {
message: "k8s attached cluster wait requested outside attached source mode".to_owned(),
});
};
})?;
Ok(Arc::new(K8sAttachedClusterWait::<E>::new(
client,