From 8ae0d6b54811d1db7ea68744f2b0572ca2a3ce2d Mon Sep 17 00:00:00 2001 From: Jean-Gabriel Gill-Couture Date: Tue, 1 Jul 2025 22:44:44 -0400 Subject: [PATCH] feat: Application Interpret still WIP but now call ensure_installed on features, also introduced a rust app example, completed work on clone_box behavior --- Cargo.lock | 14 ++++++++ examples/rust/Cargo.toml | 14 ++++++++ examples/rust/src/main.rs | 20 +++++++++++ examples/tui/Cargo.toml | 2 +- harmony/src/domain/topology/ha_cluster.rs | 2 +- .../features/continuous_delivery.rs | 2 +- .../modules/application/features/endpoint.rs | 2 +- .../application/features/monitoring.rs | 2 +- harmony/src/modules/application/mod.rs | 33 ++++++++++++++++--- harmony/src/modules/application/rust.rs | 6 ++-- 10 files changed, 86 insertions(+), 11 deletions(-) create mode 100644 examples/rust/Cargo.toml create mode 100644 examples/rust/src/main.rs diff --git a/Cargo.lock b/Cargo.lock index 2769781..cea53ec 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1194,6 +1194,20 @@ dependencies = [ "url", ] +[[package]] +name = "example-rust" +version = "0.1.0" +dependencies = [ + "env_logger", + "harmony", + "harmony_cli", + "harmony_macros", + "harmony_types", + "log", + "tokio", + "url", +] + [[package]] name = "example-tenant" version = "0.1.0" diff --git a/examples/rust/Cargo.toml b/examples/rust/Cargo.toml new file mode 100644 index 0000000..9fcfb2f --- /dev/null +++ b/examples/rust/Cargo.toml @@ -0,0 +1,14 @@ +[package] +name = "example-rust" +version = "0.1.0" +edition = "2024" + +[dependencies] +harmony = { path = "../../harmony" } +harmony_cli = { path = "../../harmony_cli" } +harmony_types = { path = "../../harmony_types" } +harmony_macros = { path = "../../harmony_macros" } +tokio = { workspace = true } +log = { workspace = true } +env_logger = { workspace = true } +url = { workspace = true } diff --git a/examples/rust/src/main.rs b/examples/rust/src/main.rs new file mode 100644 index 0000000..28cd6f1 --- /dev/null +++ b/examples/rust/src/main.rs @@ -0,0 +1,20 @@ +use harmony::{ + inventory::Inventory, + maestro::Maestro, + modules::application::{RustWebappScore, features::ContinuousDelivery}, + topology::{K8sAnywhereTopology, Url}, +}; + +#[tokio::main] +async fn main() { + let app = RustWebappScore { + name: "Example Rust Webapp".to_string(), + domain: Url::Url(url::Url::parse("https://rustapp.harmony.example.com").unwrap()), + features: vec![Box::new(ContinuousDelivery {})], + }; + + let topology = K8sAnywhereTopology::from_env(); + let mut maestro = Maestro::new(Inventory::autoload(), topology); + maestro.register_all(vec![Box::new(app)]); + harmony_cli::init(maestro, None).await.unwrap(); +} diff --git a/examples/tui/Cargo.toml b/examples/tui/Cargo.toml index d9390ca..9e5dc2a 100644 --- a/examples/tui/Cargo.toml +++ b/examples/tui/Cargo.toml @@ -10,9 +10,9 @@ publish = false harmony = { path = "../../harmony" } harmony_tui = { path = "../../harmony_tui" } harmony_types = { path = "../../harmony_types" } +harmony_macros = { path = "../../harmony_macros" } cidr = { workspace = true } tokio = { workspace = true } -harmony_macros = { path = "../../harmony_macros" } log = { workspace = true } env_logger = { workspace = true } url = { workspace = true } diff --git a/harmony/src/domain/topology/ha_cluster.rs b/harmony/src/domain/topology/ha_cluster.rs index 6ee1844..a114e18 100644 --- a/harmony/src/domain/topology/ha_cluster.rs +++ b/harmony/src/domain/topology/ha_cluster.rs @@ -46,7 +46,7 @@ pub struct HAClusterTopology { #[async_trait] impl Topology for HAClusterTopology { fn name(&self) -> &str { - todo!() + "HAClusterTopology" } async fn ensure_ready(&self) -> Result { todo!( diff --git a/harmony/src/modules/application/features/continuous_delivery.rs b/harmony/src/modules/application/features/continuous_delivery.rs index 3530142..14ce255 100644 --- a/harmony/src/modules/application/features/continuous_delivery.rs +++ b/harmony/src/modules/application/features/continuous_delivery.rs @@ -30,7 +30,7 @@ use crate::{modules::application::ApplicationFeature, topology::Topology}; /// - Harbor as artifact registru /// - ArgoCD to install/upgrade/rollback/inspect k8s resources /// - Kubernetes for runtime orchestration -#[derive(Debug, Default)] +#[derive(Debug, Default, Clone)] pub struct ContinuousDelivery {} #[async_trait] diff --git a/harmony/src/modules/application/features/endpoint.rs b/harmony/src/modules/application/features/endpoint.rs index f4940ed..83cc215 100644 --- a/harmony/src/modules/application/features/endpoint.rs +++ b/harmony/src/modules/application/features/endpoint.rs @@ -6,7 +6,7 @@ use crate::{ topology::{K8sclient, Topology}, }; -#[derive(Debug)] +#[derive(Debug, Clone)] pub struct PublicEndpoint { application_port: u16, } diff --git a/harmony/src/modules/application/features/monitoring.rs b/harmony/src/modules/application/features/monitoring.rs index 91ad72d..a343e44 100644 --- a/harmony/src/modules/application/features/monitoring.rs +++ b/harmony/src/modules/application/features/monitoring.rs @@ -6,7 +6,7 @@ use crate::{ topology::{HelmCommand, Topology}, }; -#[derive(Debug, Default)] +#[derive(Debug, Default, Clone)] pub struct Monitoring {} #[async_trait] diff --git a/harmony/src/modules/application/mod.rs b/harmony/src/modules/application/mod.rs index 2a3ee24..ddbfe49 100644 --- a/harmony/src/modules/application/mod.rs +++ b/harmony/src/modules/application/mod.rs @@ -22,9 +22,19 @@ impl Interpret for ApplicationInterpret { async fn execute( &self, _inventory: &Inventory, - _topology: &T, + topology: &T, ) -> Result { - todo!() + for feature in self.features.iter() { + let _ = match feature.ensure_installed(topology).await { + Ok(()) => (), + Err(msg) => { + return Err(InterpretError::new(format!( + "Application Interpret failed to install feature : {msg}" + ))); + } + }; + } + todo!("Do I need to do anything more than this here??") } fn get_name(&self) -> InterpretName { @@ -47,10 +57,25 @@ impl Interpret for ApplicationInterpret { /// An ApplicationFeature provided by harmony, such as Backups, Monitoring, MultisiteAvailability, /// ContinuousIntegration, ContinuousDelivery #[async_trait] -pub trait ApplicationFeature: std::fmt::Debug + Send + Sync { +pub trait ApplicationFeature: + std::fmt::Debug + Send + Sync + ApplicationFeatureClone +{ async fn ensure_installed(&self, topology: &T) -> Result<(), String>; } +trait ApplicationFeatureClone { + fn clone_box(&self) -> Box>; +} + +impl ApplicationFeatureClone for A +where + A: ApplicationFeature + Clone + 'static, +{ + fn clone_box(&self) -> Box> { + Box::new(self.clone()) + } +} + impl Serialize for Box> { fn serialize(&self, serializer: S) -> Result where @@ -62,6 +87,6 @@ impl Serialize for Box> { impl Clone for Box> { fn clone(&self) -> Self { - todo!() + self.clone_box() } } diff --git a/harmony/src/modules/application/rust.rs b/harmony/src/modules/application/rust.rs index 0ef41b3..231b821 100644 --- a/harmony/src/modules/application/rust.rs +++ b/harmony/src/modules/application/rust.rs @@ -5,7 +5,7 @@ use crate::{ topology::{Topology, Url}, }; -use super::{ApplicationFeature, ApplicationInterpret}; +use super::{ApplicationFeature, ApplicationInterpret, features::ContinuousDelivery}; #[derive(Debug, Serialize, Clone)] pub struct RustWebappScore { @@ -16,7 +16,9 @@ pub struct RustWebappScore { impl Score for RustWebappScore { fn create_interpret(&self) -> Box> { - Box::new(ApplicationInterpret { features: todo!() }) + Box::new(ApplicationInterpret { + features: self.features.clone(), + }) } fn name(&self) -> String {