diff --git a/harmony/src/domain/interpret/mod.rs b/harmony/src/domain/interpret/mod.rs index f9a509d..4a9855a 100644 --- a/harmony/src/domain/interpret/mod.rs +++ b/harmony/src/domain/interpret/mod.rs @@ -152,6 +152,12 @@ pub struct InterpretError { msg: String, } +impl From for String { + fn from(e: InterpretError) -> String { + format!("InterpretError : {}", e.msg) + } +} + impl std::fmt::Display for InterpretError { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { f.write_str(&self.msg) diff --git a/harmony/src/domain/topology/k8s_anywhere.rs b/harmony/src/domain/topology/k8s_anywhere.rs index 226860b..caeb576 100644 --- a/harmony/src/domain/topology/k8s_anywhere.rs +++ b/harmony/src/domain/topology/k8s_anywhere.rs @@ -34,13 +34,14 @@ use crate::{ service_monitor::ServiceMonitor, }, }, + network::TlsPassthroughScore, prometheus::{ k8s_prometheus_alerting_score::K8sPrometheusCRDAlertingScore, prometheus::PrometheusMonitoring, rhob_alerting_score::RHOBAlertingScore, }, }, score::Score, - topology::ingress::Ingress, + topology::{TlsRoute, TlsRouter, ingress::Ingress}, }; use super::{ @@ -102,6 +103,29 @@ impl K8sclient for K8sAnywhereTopology { } } +#[async_trait] +impl TlsRouter for K8sAnywhereTopology { + async fn install_route(&self, route: TlsRoute) -> Result<(), String> { + if let Some(distro) = self.k8s_distribution.get() { + match distro { + KubernetesDistribution::OpenshiftFamily => { + TlsPassthroughScore { route } + .interpret(&Inventory::empty(), self) + .await?; + Ok(()) + } + KubernetesDistribution::K3sFamily | KubernetesDistribution::Default => Err( + format!("Distribution not supported yet for Tlsrouter {distro:?}"), + ), + } + } else { + Err(format!( + "Could not find a k8s distribution, TlsRouter in K8sAnywhereTopology requires it" + )) + } + } +} + #[async_trait] impl Grafana for K8sAnywhereTopology { async fn ensure_grafana_operator( diff --git a/harmony/src/domain/topology/router.rs b/harmony/src/domain/topology/router.rs index 66ae8f9..b453e99 100644 --- a/harmony/src/domain/topology/router.rs +++ b/harmony/src/domain/topology/router.rs @@ -1,6 +1,7 @@ use async_trait::async_trait; use cidr::Ipv4Cidr; use derive_new::new; +use serde::Serialize; use super::{IpAddress, LogicalHost}; @@ -47,8 +48,6 @@ impl Router for UnmanagedRouter { } } -#[derive(Clone, Debug)] - /// Desired state config for a TLS passthrough route. /// Forwards external TLS (port 443) → backend service:target_port (no termination at router). /// Inspired by CNPG multisite: exposes `-rw`/`-ro` services publicly via OKD Route/HAProxy/K8s @@ -63,6 +62,7 @@ impl Router for UnmanagedRouter { /// target_port: 5432, /// }; /// ``` +#[derive(Clone, Debug, Serialize)] pub struct TlsRoute { /// Public hostname clients connect to (TLS SNI, port 443 implicit). /// Router matches this for passthrough forwarding. @@ -95,24 +95,4 @@ pub trait TlsRouter: Send + Sync { /// Example: OKD Route{ host, to: backend:target_port, tls: {passthrough} }; /// HAProxy frontend→backend \"postgres-upstream\". async fn install_route(&self, config: TlsRoute) -> Result<(), String>; - - /// Installed route's public hostname. - fn hostname(&self) -> String; - - /// Installed route's backend identifier. - fn backend(&self) -> String; - - /// Installed route's backend port. - fn target_port(&self) -> u16; -} - -impl std::fmt::Debug for dyn TlsRouter { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - f.write_fmt(format_args!( - "TlsRouter[hostname={}, backend={}:{}]", - self.hostname(), - self.backend(), - self.target_port() - )) - } } diff --git a/harmony/src/modules/network/tls_router.rs b/harmony/src/modules/network/tls_router.rs index 5f43df3..75f1a00 100644 --- a/harmony/src/modules/network/tls_router.rs +++ b/harmony/src/modules/network/tls_router.rs @@ -19,10 +19,13 @@ use crate::topology::{K8sclient, Topology}; /// # Usage /// ``` /// use harmony::modules::network::TlsPassthroughScore; +/// use harmony::topology::router::TlsRoute; /// let score = TlsPassthroughScore { +/// route: TlsRoute { /// backend: "postgres-cluster-rw".to_string(), /// hostname: "postgres-rw.example.com".to_string(), /// target_port: 5432, +/// }, /// }; /// ``` /// @@ -37,28 +40,20 @@ use crate::topology::{K8sclient, Topology}; /// to use through this high level TlsPassthroughScore. #[derive(Debug, Clone, Serialize)] pub struct TlsPassthroughScore { - /// Backend identifier (k8s Service, HAProxy upstream, IP/FQDN, etc.). - pub backend: String, - /// Public hostname clients connect to (TLS SNI, port 443 implicit). - pub hostname: String, - /// Backend TCP port. - pub target_port: u16, + pub route: TlsRoute, } impl Score for TlsPassthroughScore { fn create_interpret(&self) -> Box> { - let tls_route = TlsRoute { - hostname: self.hostname.clone(), - backend: self.backend.clone(), - target_port: self.target_port, - }; - Box::new(TlsPassthroughInterpret { tls_route }) + Box::new(TlsPassthroughInterpret { + tls_route: self.route.clone(), + }) } fn name(&self) -> String { format!( "TlsRouterScore({}:{} → {})", - self.backend, self.target_port, self.hostname + self.route.backend, self.route.target_port, self.route.hostname ) } }