Add shared cluster control profile semantics

This commit is contained in:
andrussal 2026-03-08 14:44:58 +01:00
parent 4c6aea1358
commit cf1e6185fa
5 changed files with 84 additions and 6 deletions

View File

@ -1,7 +1,11 @@
use async_trait::async_trait;
use crate::scenario::{Application, ClusterWaitHandle, NodeControlHandle};
use crate::scenario::{Application, ClusterControlProfile, ClusterWaitHandle, NodeControlHandle};
/// Interface for imperative, deployer-backed manual clusters.
#[async_trait]
pub trait ManualClusterHandle<E: Application>: NodeControlHandle<E> + ClusterWaitHandle<E> {}
pub trait ManualClusterHandle<E: Application>: NodeControlHandle<E> + ClusterWaitHandle<E> {
fn cluster_control_profile(&self) -> ClusterControlProfile {
ClusterControlProfile::ManualControlled
}
}

View File

@ -4,8 +4,8 @@ use thiserror::Error;
use tracing::{debug, info};
use super::{
Application, ClusterMode, DeploymentPolicy, DynError, ExistingCluster, ExternalNodeSource,
HttpReadinessRequirement, NodeControlCapability, ObservabilityCapability,
Application, ClusterControlProfile, ClusterMode, DeploymentPolicy, DynError, ExistingCluster,
ExternalNodeSource, HttpReadinessRequirement, NodeControlCapability, ObservabilityCapability,
builder_ops::CoreBuilderAccess,
expectation::Expectation,
runtime::{
@ -123,6 +123,11 @@ impl<E: Application, Caps> Scenario<E, Caps> {
self.sources.cluster_mode()
}
#[must_use]
pub const fn cluster_control_profile(&self) -> ClusterControlProfile {
self.sources.control_profile()
}
#[must_use]
#[doc(hidden)]
pub fn attached_source(&self) -> Option<&ExistingCluster> {

View File

@ -54,7 +54,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::{ClusterMode, ExistingCluster, ExternalNodeSource};
pub use sources::{ClusterControlProfile, ClusterMode, ExistingCluster, ExternalNodeSource};
pub use workload::Workload;
pub use crate::env::Application;

View File

@ -2,4 +2,4 @@ mod model;
pub(crate) use model::ScenarioSources;
#[doc(hidden)]
pub use model::{ClusterMode, ExistingCluster, ExternalNodeSource};
pub use model::{ClusterControlProfile, ClusterMode, ExistingCluster, ExternalNodeSource};

View File

@ -127,6 +127,27 @@ pub enum ClusterMode {
ExternalOnly,
}
/// High-level control/lifecycle expectation for a cluster surface.
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub enum ClusterControlProfile {
FrameworkManaged,
ExistingClusterControlled,
ExternalUncontrolled,
ManualControlled,
}
impl ClusterControlProfile {
#[must_use]
pub const fn framework_owns_lifecycle(self) -> bool {
matches!(self, Self::FrameworkManaged)
}
#[must_use]
pub const fn supports_node_control(self) -> bool {
!matches!(self, Self::ExternalUncontrolled)
}
}
/// Source model that makes invalid managed+attached combinations
/// unrepresentable by type.
#[derive(Clone, Debug, Eq, PartialEq)]
@ -202,4 +223,52 @@ impl ScenarioSources {
Self::ExternalOnly { .. } => ClusterMode::ExternalOnly,
}
}
#[must_use]
pub(crate) const fn control_profile(&self) -> ClusterControlProfile {
match self.cluster_mode() {
ClusterMode::Managed => ClusterControlProfile::FrameworkManaged,
ClusterMode::ExistingCluster => ClusterControlProfile::ExistingClusterControlled,
ClusterMode::ExternalOnly => ClusterControlProfile::ExternalUncontrolled,
}
}
}
#[cfg(test)]
mod tests {
use super::{ClusterControlProfile, ExistingCluster, ExternalNodeSource, ScenarioSources};
#[test]
fn managed_sources_map_to_framework_managed_control() {
assert_eq!(
ScenarioSources::default().control_profile(),
ClusterControlProfile::FrameworkManaged,
);
}
#[test]
fn attached_sources_map_to_existing_cluster_control() {
let sources = ScenarioSources::default()
.with_attach(ExistingCluster::for_compose_project("project".to_owned()));
assert_eq!(
sources.control_profile(),
ClusterControlProfile::ExistingClusterControlled,
);
}
#[test]
fn external_only_sources_map_to_uncontrolled_profile() {
let sources = ScenarioSources::default()
.with_external_node(ExternalNodeSource::new(
"node".to_owned(),
"http://node".to_owned(),
))
.into_external_only();
assert_eq!(
sources.control_profile(),
ClusterControlProfile::ExternalUncontrolled,
);
}
}