fix: Tests, doctests, formatting
All checks were successful
Run Check Script / check (pull_request) Successful in 1m38s

This commit is contained in:
2025-12-13 17:56:53 -05:00
parent b61e4f9a96
commit 2254641f3d
4 changed files with 50 additions and 25 deletions

View File

@@ -15,7 +15,7 @@ pub use k8s_anywhere::*;
pub use localhost::*; pub use localhost::*;
pub mod k8s; pub mod k8s;
mod load_balancer; mod load_balancer;
mod router; pub mod router;
mod tftp; mod tftp;
use async_trait::async_trait; use async_trait::async_trait;
pub use ha_cluster::*; pub use ha_cluster::*;

View File

@@ -47,17 +47,16 @@ impl Router for UnmanagedRouter {
} }
} }
#[derive(Clone, Debug)] #[derive(Clone, Debug)]
/// Desired state config for a TLS passthrough route. /// Desired state config for a TLS passthrough route.
/// Forwards external TLS (port 443) → backend service:target_port (no termination at router). /// 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 /// Inspired by CNPG multisite: exposes `-rw`/`-ro` services publicly via OKD Route/HAProxy/K8s
/// Gateway etc. /// Gateway etc.
/// ///
/// # Example /// # Example
/// ``` /// ```
/// use harmony::domain::topology::router::TlsRoute; /// use harmony::topology::router::TlsRoute;
/// let postgres_rw = TlsRoute { /// let postgres_rw = TlsRoute {
/// hostname: "postgres-cluster-example.public.domain.io".to_string(), /// hostname: "postgres-cluster-example.public.domain.io".to_string(),
/// backend: "postgres-cluster-example-rw".to_string(), // k8s Service or HAProxy upstream /// backend: "postgres-cluster-example-rw".to_string(), // k8s Service or HAProxy upstream
@@ -81,19 +80,19 @@ pub struct TlsRoute {
/// Installs and queries TLS passthrough routes (L4 TCP/SNI forwarding, no TLS termination). /// Installs and queries TLS passthrough routes (L4 TCP/SNI forwarding, no TLS termination).
/// Agnostic to impl: OKD Route, AWS NLB+HAProxy, k3s Envoy Gateway, Apache ProxyPass. /// Agnostic to impl: OKD Route, AWS NLB+HAProxy, k3s Envoy Gateway, Apache ProxyPass.
/// Used by PostgreSQL capability to expose CNPG clusters multisite (site1 → site2 replication). /// Used by PostgreSQL capability to expose CNPG clusters multisite (site1 → site2 replication).
/// ///
/// # Usage /// # Usage
/// ```rust,no_run /// ```ignore
/// use harmony::topology::router::TlsRoute;
/// // After CNPG deploy, expose RW endpoint /// // After CNPG deploy, expose RW endpoint
/// let topology = okd_topology(); /// async fn route() {
/// let route = TlsRoute { /* ... */ }; /// let topology = okd_topology();
/// topology.install_route(route).await?; // OKD Route, HAProxy reload, etc. /// let route = TlsRoute { /* ... */ };
/// /// topology.install_route(route).await; // OKD Route, HAProxy reload, etc.
/// // Client: psql \\"host={route.hostname} port=443 sslmode=verify-ca sslnegotiation=direct\\" /// }
/// let public_ep = Endpoint { host: topology.hostname(), port: 443 };
/// ``` /// ```
pub trait TlsRouter: Send + Sync { pub trait TlsRouter: Send + Sync {
/// Provisions the route (idempotent where possible). /// Provisions the route (idempotent where possible).
/// Example: OKD Route{ host, to: backend:target_port, tls: {passthrough} }; /// Example: OKD Route{ host, to: backend:target_port, tls: {passthrough} };
/// HAProxy frontend→backend \"postgres-upstream\". /// HAProxy frontend→backend \"postgres-upstream\".
async fn install_route(&self, config: TlsRoute) -> Result<(), String>; async fn install_route(&self, config: TlsRoute) -> Result<(), String>;
@@ -118,4 +117,3 @@ impl std::fmt::Debug for dyn TlsRouter {
)) ))
} }
} }

View File

@@ -1,4 +1,4 @@
use kube::{api::ObjectMeta, CustomResource}; use kube::{CustomResource, api::ObjectMeta};
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
#[derive(CustomResource, Deserialize, Serialize, Clone, Debug)] #[derive(CustomResource, Deserialize, Serialize, Clone, Debug)]

View File

@@ -1,20 +1,23 @@
use async_trait::async_trait; use async_trait::async_trait;
use harmony_types::id::Id;
use serde::Serialize; use serde::Serialize;
use crate::data::Version;
use crate::domain::topology::router::{TlsRoute, TlsRouter}; use crate::domain::topology::router::{TlsRoute, TlsRouter};
use crate::interpret::Interpret; use crate::interpret::{Interpret, InterpretError, InterpretName, InterpretStatus, Outcome};
use crate::inventory::Inventory;
use crate::modules::k8s::resource::K8sResourceScore; use crate::modules::k8s::resource::K8sResourceScore;
use crate::modules::postgresql::cnpg::{Bootstrap, Cluster, ClusterSpec, Initdb, Storage};
use crate::modules::postgresql::PostgreSQLScore; use crate::modules::postgresql::PostgreSQLScore;
use crate::modules::postgresql::cnpg::{Bootstrap, Cluster, ClusterSpec, Initdb, Storage};
use crate::score::Score; use crate::score::Score;
use crate::topology::{K8sclient, Topology}; use crate::topology::{K8sclient, Topology};
use k8s_openapi::apimachinery::pkg::apis::meta::v1::ObjectMeta; use k8s_openapi::apimachinery::pkg::apis::meta::v1::ObjectMeta;
/// Deploys a public PostgreSQL cluster: CNPG + TLS passthrough route for RW endpoint. /// Deploys a public PostgreSQL cluster: CNPG + TLS passthrough route for RW endpoint.
/// For failover/multisite: exposes single-instance or small HA Postgres publicly. /// For failover/multisite: exposes single-instance or small HA Postgres publicly.
/// ///
/// Sequence: PostgreSQLScore → TlsRouter::install_route (RW backend). /// Sequence: PostgreSQLScore → TlsRouter::install_route (RW backend).
/// ///
/// # Usage /// # Usage
/// ``` /// ```
/// use harmony::modules::postgresql::PublicPostgreSQLScore; /// use harmony::modules::postgresql::PublicPostgreSQLScore;
@@ -55,14 +58,35 @@ struct PublicPostgreSQLInterpret {
#[async_trait] #[async_trait]
impl<T: Topology + K8sclient + TlsRouter + Send + Sync> Interpret<T> for PublicPostgreSQLInterpret { impl<T: Topology + K8sclient + TlsRouter + Send + Sync> Interpret<T> for PublicPostgreSQLInterpret {
async fn interpret(&self, topo: &T) -> Result<(), Box<dyn std::error::Error>> { fn get_name(&self) -> InterpretName {
InterpretName::Custom("PublicPostgreSQLInterpret")
}
fn get_version(&self) -> Version {
todo!()
}
fn get_status(&self) -> InterpretStatus {
todo!()
}
fn get_children(&self) -> Vec<Id> {
todo!()
}
async fn execute(&self, inventory: &Inventory, topo: &T) -> Result<Outcome, InterpretError> {
// Deploy CNPG cluster first (creates -rw service) // Deploy CNPG cluster first (creates -rw service)
self.postgres_score.create_interpret().interpret(topo).await?; self.postgres_score
.create_interpret()
.execute(inventory, topo)
.await?;
// Expose RW publicly via TLS passthrough // Expose RW publicly via TLS passthrough
topo.install_route(self.tls_route.clone()).await.map_err(|e| Box::new(std::io::Error::new(std::io::ErrorKind::Other, e)) as Box<dyn std::error::Error>)?; topo.install_route(self.tls_route.clone())
.await
.map_err(|e| InterpretError::new(e))?;
Ok(()) Ok(Outcome::success(format!(
"Public CNPG cluster '{}' deployed with TLS passthrough route '{}'",
self.postgres_score.name.clone(),
self.tls_route.hostname
)))
} }
} }
@@ -82,8 +106,11 @@ impl<T: Topology + K8sclient + TlsRouter + Send + Sync> Score<T> for PublicPostg
} }
fn name(&self) -> String { fn name(&self) -> String {
format!("PublicPostgreSQLScore({}:{})", self.inner.namespace, self.hostname) format!(
"PublicPostgreSQLScore({}:{})",
self.inner.namespace, self.hostname
)
} }
} }
// TODO: Add RO route (separate hostname/backend="cluster-ro"), backups, failover logic. // TODO: Add RO route (separate hostname/backend="cluster-ro"), backups, failover logic.