Some checks failed
Run Check Script / check (pull_request) Failing after 10s
154 lines
5.8 KiB
Rust
154 lines
5.8 KiB
Rust
//! Example of Higher-Order Topologies in Harmony.
|
|
//! Demonstrates how `FailoverTopology<T>` automatically provides failover for *any* capability
|
|
//! supported by a sub-topology `T` via blanket trait impls.
|
|
//!
|
|
//! Key insight: No manual impls per T or capability -- scales effortlessly.
|
|
//! Users can:
|
|
//! - Write new `Topology` (impl capabilities on a struct).
|
|
//! - Compose with `FailoverTopology` (gets capabilities if T has them).
|
|
//! - Compile fails if capability missing (safety).
|
|
|
|
use async_trait::async_trait;
|
|
use tokio;
|
|
|
|
/// Capability trait: Deploy and manage PostgreSQL.
|
|
#[async_trait]
|
|
pub trait PostgreSQL {
|
|
async fn deploy(&self, config: &PostgreSQLConfig) -> Result<String, String>;
|
|
async fn get_replication_certs(&self, cluster_name: &str) -> Result<ReplicationCerts, String>;
|
|
}
|
|
|
|
/// Capability trait: Deploy Docker.
|
|
#[async_trait]
|
|
pub trait Docker {
|
|
async fn deploy_docker(&self) -> Result<String, String>;
|
|
}
|
|
|
|
/// Configuration for PostgreSQL deployments.
|
|
#[derive(Clone)]
|
|
pub struct PostgreSQLConfig;
|
|
|
|
/// Replication certificates.
|
|
#[derive(Clone)]
|
|
pub struct ReplicationCerts;
|
|
|
|
/// Concrete topology: Kubernetes Anywhere (supports PostgreSQL).
|
|
#[derive(Clone)]
|
|
pub struct K8sAnywhereTopology;
|
|
|
|
#[async_trait]
|
|
impl PostgreSQL for K8sAnywhereTopology {
|
|
async fn deploy(&self, _config: &PostgreSQLConfig) -> Result<String, String> {
|
|
// Real impl: Use k8s helm chart, operator, etc.
|
|
Ok("K8sAnywhere PostgreSQL deployed".to_string())
|
|
}
|
|
|
|
async fn get_replication_certs(&self, _cluster_name: &str) -> Result<ReplicationCerts, String> {
|
|
Ok(ReplicationCerts)
|
|
}
|
|
}
|
|
|
|
/// Concrete topology: Linux Host (supports Docker).
|
|
#[derive(Clone)]
|
|
pub struct LinuxHostTopology;
|
|
|
|
#[async_trait]
|
|
impl Docker for LinuxHostTopology {
|
|
async fn deploy_docker(&self) -> Result<String, String> {
|
|
// Real impl: Install/configure Docker on host.
|
|
Ok("LinuxHost Docker deployed".to_string())
|
|
}
|
|
}
|
|
|
|
/// Higher-Order Topology: Composes multiple sub-topologies (primary + replica).
|
|
/// Automatically derives *all* capabilities of `T` with failover orchestration.
|
|
///
|
|
/// - If `T: PostgreSQL`, then `FailoverTopology<T>: PostgreSQL` (blanket impl).
|
|
/// - Same for `Docker`, etc. No boilerplate!
|
|
/// - Compile-time safe: Missing `T: Capability` → error.
|
|
#[derive(Clone)]
|
|
pub struct FailoverTopology<T> {
|
|
/// Primary sub-topology.
|
|
pub primary: T,
|
|
/// Replica sub-topology.
|
|
pub replica: T,
|
|
}
|
|
|
|
/// Blanket impl: Failover PostgreSQL if T provides PostgreSQL.
|
|
/// Delegates reads to primary; deploys to both.
|
|
#[async_trait]
|
|
impl<T: PostgreSQL + Send + Sync + Clone> PostgreSQL for FailoverTopology<T> {
|
|
async fn deploy(&self, config: &PostgreSQLConfig) -> Result<String, String> {
|
|
// Orchestrate: Deploy primary first, then replica (e.g., via pg_basebackup).
|
|
let primary_result = self.primary.deploy(config).await?;
|
|
let replica_result = self.replica.deploy(config).await?;
|
|
Ok(format!("Failover PG deployed: {} | {}", primary_result, replica_result))
|
|
}
|
|
|
|
async fn get_replication_certs(&self, cluster_name: &str) -> Result<ReplicationCerts, String> {
|
|
// Delegate to primary (replica follows).
|
|
self.primary.get_replication_certs(cluster_name).await
|
|
}
|
|
}
|
|
|
|
/// Blanket impl: Failover Docker if T provides Docker.
|
|
#[async_trait]
|
|
impl<T: Docker + Send + Sync + Clone> Docker for FailoverTopology<T> {
|
|
async fn deploy_docker(&self) -> Result<String, String> {
|
|
// Orchestrate across primary + replica.
|
|
let primary_result = self.primary.deploy_docker().await?;
|
|
let replica_result = self.replica.deploy_docker().await?;
|
|
Ok(format!("Failover Docker deployed: {} | {}", primary_result, replica_result))
|
|
}
|
|
}
|
|
|
|
#[tokio::main]
|
|
async fn main() {
|
|
let config = PostgreSQLConfig;
|
|
|
|
println!("=== ✅ PostgreSQL Failover (K8sAnywhere supports PG) ===");
|
|
let pg_failover = FailoverTopology {
|
|
primary: K8sAnywhereTopology,
|
|
replica: K8sAnywhereTopology,
|
|
};
|
|
let result = pg_failover.deploy(&config).await.unwrap();
|
|
println!("Result: {}", result);
|
|
|
|
println!("\n=== ✅ Docker Failover (LinuxHost supports Docker) ===");
|
|
let docker_failover = FailoverTopology {
|
|
primary: LinuxHostTopology,
|
|
replica: LinuxHostTopology,
|
|
};
|
|
let result = docker_failover.deploy_docker().await.unwrap();
|
|
println!("Result: {}", result);
|
|
|
|
println!("\n=== ❌ Would fail to compile (K8sAnywhere !: Docker) ===");
|
|
// let invalid = FailoverTopology {
|
|
// primary: K8sAnywhereTopology,
|
|
// replica: K8sAnywhereTopology,
|
|
// };
|
|
// invalid.deploy_docker().await.unwrap(); // Error: `K8sAnywhereTopology: Docker` not satisfied!
|
|
// Very clear error message :
|
|
// error[E0599]: the method `deploy_docker` exists for struct `FailoverTopology<K8sAnywhereTopology>`, but its trait bounds were not satisfied
|
|
// --> src/main.rs:90:9
|
|
// |
|
|
// 4 | pub struct FailoverTopology<T> {
|
|
// | ------------------------------ method `deploy_docker` not found for this struct because it doesn't satisfy `FailoverTopology<K8sAnywhereTopology>: Docker`
|
|
// ...
|
|
// 37 | struct K8sAnywhereTopology;
|
|
// | -------------------------- doesn't satisfy `K8sAnywhereTopology: Docker`
|
|
// ...
|
|
// 90 | invalid.deploy_docker(); // `T: Docker` bound unsatisfied
|
|
// | ^^^^^^^^^^^^^ method cannot be called on `FailoverTopology<K8sAnywhereTopology>` due to unsatisfied trait bounds
|
|
// |
|
|
// note: trait bound `K8sAnywhereTopology: Docker` was not satisfied
|
|
// --> src/main.rs:61:9
|
|
// |
|
|
// 61 | impl<T: Docker + Send + Sync> Docker for FailoverTopology<T> {
|
|
// | ^^^^^^ ------ -------------------
|
|
// | |
|
|
// | unsatisfied trait bound introduced here
|
|
// note: the trait `Docker` must be implemented
|
|
}
|
|
|