From c6a39a39a1bbe3147e62071cbcae06cfde124ba6 Mon Sep 17 00:00:00 2001 From: Jean-Gabriel Gill-Couture Date: Tue, 17 Jun 2025 13:30:26 -0400 Subject: [PATCH] wip: Monitoring architecture becoming clearer, added adr/010-monitoring-alerting/architecture.rs that demonstrates how we designed the monitoring stack to be fully extensible by other crates --- adr/010-monitoring-alerting/architecture.rs | 73 +++++++++++++++++++ examples/lamp/src/main.rs | 21 +++++- .../alert_channel/discord_alert_channel.rs | 48 ++++++------ .../src/modules/monitoring/alert_receiver.rs | 5 +- harmony/src/modules/monitoring/alert_rule.rs | 6 +- harmony/src/modules/monitoring/mod.rs | 4 +- .../modules/monitoring/monitoring_alerting.rs | 44 ++++++----- harmony/src/modules/monitoring/prometheus.rs | 9 ++- .../src/modules/monitoring/scrape_target.rs | 5 +- 9 files changed, 163 insertions(+), 52 deletions(-) create mode 100644 adr/010-monitoring-alerting/architecture.rs diff --git a/adr/010-monitoring-alerting/architecture.rs b/adr/010-monitoring-alerting/architecture.rs new file mode 100644 index 0000000..904e5c4 --- /dev/null +++ b/adr/010-monitoring-alerting/architecture.rs @@ -0,0 +1,73 @@ +pub trait MonitoringSystem {} + +// 1. Modified AlertReceiver trait: +// - Removed the problematic `clone` method. +// - Added `box_clone` which returns a Box. +pub trait AlertReceiver { + type M: MonitoringSystem; + fn install(&self, sender: &Self::M) -> Result<(), String>; + // This method allows concrete types to clone themselves into a Box + fn box_clone(&self) -> Box>; +} +#[derive(Clone)] +struct Prometheus{} +impl MonitoringSystem for Prometheus {} + +#[derive(Clone)] // Keep derive(Clone) for DiscordWebhook itself +struct DiscordWebhook{} + +impl AlertReceiver for DiscordWebhook { + type M = Prometheus; + fn install(&self, sender: &Self::M) -> Result<(), String> { + // Placeholder for actual installation logic + println!("DiscordWebhook installed for Prometheus monitoring."); + Ok(()) + } + // 2. Implement `box_clone` for DiscordWebhook: + // This uses the derived `Clone` for DiscordWebhook to create a new boxed instance. + fn box_clone(&self) -> Box> { + Box::new(self.clone()) + } +} + +// 3. Implement `std::clone::Clone` for `Box>`: +// This allows `Box` to be cloned. +// The `+ 'static` lifetime bound is often necessary for trait objects stored in collections, +// ensuring they live long enough. +impl Clone for Box> { + fn clone(&self) -> Self { + self.box_clone() // Call the custom `box_clone` method + } +} + +// MonitoringConfig can now derive Clone because its `receivers` field +// (Vec>>) is now cloneable. +#[derive(Clone)] +struct MonitoringConfig { + receivers: Vec>> +} + +// Example usage to demonstrate compilation and functionality +fn main() { + let prometheus_instance = Prometheus{}; + let discord_webhook_instance = DiscordWebhook{}; + + let mut config = MonitoringConfig { + receivers: Vec::new() + }; + + // Create a boxed alert receiver + let boxed_receiver: Box> = Box::new(discord_webhook_instance); + config.receivers.push(boxed_receiver); + + // Clone the config, which will now correctly clone the boxed receiver + let cloned_config = config.clone(); + + println!("Original config has {} receivers.", config.receivers.len()); + println!("Cloned config has {} receivers.", cloned_config.receivers.len()); + + // Example of using the installed receiver + if let Some(receiver) = config.receivers.get(0) { + let _ = receiver.install(&prometheus_instance); + } +} diff --git a/examples/lamp/src/main.rs b/examples/lamp/src/main.rs index ea6476d..37fd0f9 100644 --- a/examples/lamp/src/main.rs +++ b/examples/lamp/src/main.rs @@ -2,7 +2,13 @@ use harmony::{ data::Version, inventory::Inventory, maestro::Maestro, - modules::{lamp::{LAMPConfig, LAMPScore}, monitoring::monitoring_alerting::MonitoringAlertingScore}, + modules::{ + lamp::{LAMPConfig, LAMPScore}, + monitoring::{ + alert_channel::discord_alert_channel::DiscordWebhook, + monitoring_alerting::MonitoringAlertingScore, + }, + }, topology::{K8sAnywhereTopology, Url}, }; @@ -29,7 +35,15 @@ async fn main() { }, }; - let monitoring = MonitoringAlertingScore { alert_channel_configs: todo!() }; + let monitoring = MonitoringAlertingScore { + alert_receivers: vec![Box::new(DiscordWebhook { + url: Url::Url(url::Url::parse("https://discord.idonotexist.com").unwrap()), + // TODO write url macro + // url: url!("https://discord.idonotexist.com"), + })], + alert_rules: vec![], + scrape_targets: vec![], + }; // You can choose the type of Topology you want, we suggest starting with the // K8sAnywhereTopology as it is the most automatic one that enables you to easily deploy @@ -42,7 +56,8 @@ async fn main() { .await .unwrap(); - maestro.register_all(vec![Box::new(lamp_stack)]); + // maestro.register_all(vec![Box::new(lamp_stack)]); + maestro.register_all(vec![Box::new(monitoring)]); // Here we bootstrap the CLI, this gives some nice features if you need them harmony_cli::init(maestro, None).await.unwrap(); } diff --git a/harmony/src/modules/monitoring/alert_channel/discord_alert_channel.rs b/harmony/src/modules/monitoring/alert_channel/discord_alert_channel.rs index 918cc7a..6f5b3c8 100644 --- a/harmony/src/modules/monitoring/alert_channel/discord_alert_channel.rs +++ b/harmony/src/modules/monitoring/alert_channel/discord_alert_channel.rs @@ -1,17 +1,17 @@ use serde::Serialize; use crate::{ - interpret::Interpret, modules::monitoring::{ - alert_receiver::{AlertReceiver, AlertReceiverInterpret}, + alert_receiver::AlertReceiver, prometheus::{Prometheus, PrometheusReceiver}, }, - score::Score, - topology::Topology, + topology::Url, }; #[derive(Debug, Clone, Serialize)] -struct DiscordWebhook; +pub struct DiscordWebhook { + pub url: Url, +} impl AlertReceiver for DiscordWebhook { type Sender = Prometheus; @@ -19,6 +19,12 @@ impl AlertReceiver for DiscordWebhook { fn install(&self, sender: &Self::Sender) -> Result<(), String> { sender.configure_receiver(Box::new(self)) } + + fn clone(&self) -> Self + where + Self: Sized { + ::clone(self) + } } impl PrometheusReceiver for DiscordWebhook { @@ -29,19 +35,19 @@ impl PrometheusReceiver for DiscordWebhook { } } -#[derive(Debug, Clone, Serialize)] -pub struct DiscordWebhookScore { - pub config: DiscordWebhook, -} - -impl Score for DiscordWebhookScore { - fn create_interpret(&self) -> Box> { - Box::new(AlertReceiverInterpret { - receiver: Box::new(self.config.clone()), - }) - } - - fn name(&self) -> String { - todo!() - } -} +// #[derive(Debug, Clone, Serialize)] +// pub struct DiscordWebhookScore { +// pub config: DiscordWebhook, +// } +// +// impl Score for DiscordWebhookScore { +// fn create_interpret(&self) -> Box> { +// Box::new(AlertReceiverInterpret { +// receiver: Box::new(self.config.clone()), +// }) +// } +// +// fn name(&self) -> String { +// todo!() +// } +// } diff --git a/harmony/src/modules/monitoring/alert_receiver.rs b/harmony/src/modules/monitoring/alert_receiver.rs index f154373..0943d6e 100644 --- a/harmony/src/modules/monitoring/alert_receiver.rs +++ b/harmony/src/modules/monitoring/alert_receiver.rs @@ -9,12 +9,13 @@ use crate::{ topology::Topology, }; -use super::{monitoring_alerting::CloneBox, prometheus::AlertSender}; +use super::prometheus::AlertSender; -pub trait AlertReceiver: Debug + Send + Sync + CloneBox { +pub trait AlertReceiver: Debug + Send + Sync { type Sender: AlertSender; fn install(&self, sender: &Self::Sender) -> Result<(), String>; + fn clone_box(&self) -> Box>; } struct AlertReceiverConfig { diff --git a/harmony/src/modules/monitoring/alert_rule.rs b/harmony/src/modules/monitoring/alert_rule.rs index d4f197c..d9fbca3 100644 --- a/harmony/src/modules/monitoring/alert_rule.rs +++ b/harmony/src/modules/monitoring/alert_rule.rs @@ -1,11 +1,11 @@ use std::fmt::Debug; +use dyn_clone::DynClone; + use super::prometheus::AlertSender; -pub trait AlertRule: Debug + Send + Sync { +pub trait AlertRule: Debug + Send + Sync + DynClone { type Sender: AlertSender; fn install(&self, sender: &Self::Sender); } - - diff --git a/harmony/src/modules/monitoring/mod.rs b/harmony/src/modules/monitoring/mod.rs index 070aa2e..270ee39 100644 --- a/harmony/src/modules/monitoring/mod.rs +++ b/harmony/src/modules/monitoring/mod.rs @@ -1,7 +1,7 @@ pub mod alert_channel; +pub mod alert_receiver; +pub mod alert_rule; pub mod kube_prometheus; pub mod monitoring_alerting; pub mod prometheus; -pub mod alert_receiver; -pub mod alert_rule; pub mod scrape_target; diff --git a/harmony/src/modules/monitoring/monitoring_alerting.rs b/harmony/src/modules/monitoring/monitoring_alerting.rs index f815ae0..b612cba 100644 --- a/harmony/src/modules/monitoring/monitoring_alerting.rs +++ b/harmony/src/modules/monitoring/monitoring_alerting.rs @@ -11,7 +11,7 @@ use crate::{ topology::Topology, }; -use super::alert_receiver::AlertReceiver; +use super::{alert_receiver::AlertReceiver, prometheus::AlertSender}; use super::alert_rule::AlertRule; use super::scrape_target::ScrapeTarget; @@ -19,15 +19,34 @@ pub trait MonitoringSystem: std::fmt::Debug + Clone + Serialize + 'static {} #[derive(Debug, Clone)] pub struct MonitoringAlertingScore { - alert_receivers: Arc>>>, - alert_rules: Arc>>>, - scrape_targets: Arc>>>, + pub alert_receivers: Vec>>, + pub alert_rules: Vec>>, + pub scrape_targets: Vec>>, } -impl Serialize for MonitoringAlertingScore { + +impl Clone for Box>{ + fn clone(&self) -> Self { + self.clone_box() + } +} +impl Clone for Box>{ + fn clone(&self) -> Self { + todo!() + } +} +impl Clone for Box>{ + fn clone(&self) -> Self { + todo!() + } +} + + +impl Serialize for MonitoringAlertingScore { fn serialize(&self, serializer: S) -> Result where - S: serde::Serializer { + S: serde::Serializer, + { todo!() } } @@ -75,16 +94,3 @@ impl Interpret for MonitoringAlertingInterp todo!() } } - -pub trait CloneBox { - fn clone_box(&self) -> Box; -} - -impl CloneBox for C -where - C: Clone + 'static, -{ - fn clone_box(&self) -> Box { - Box::new(self.clone()) - } -} diff --git a/harmony/src/modules/monitoring/prometheus.rs b/harmony/src/modules/monitoring/prometheus.rs index e1ab33a..e6d2e76 100644 --- a/harmony/src/modules/monitoring/prometheus.rs +++ b/harmony/src/modules/monitoring/prometheus.rs @@ -10,13 +10,18 @@ use crate::{ }; use std::fmt::Debug; +use super::monitoring_alerting::MonitoringSystem; + pub trait AlertSender: std::fmt::Debug {} #[derive(Debug, Clone, Serialize)] pub struct Prometheus {} impl Prometheus { - pub fn configure_receiver(&self, _receiver: Box<&dyn PrometheusReceiver>) -> Result<(), String> { + pub fn configure_receiver( + &self, + _receiver: Box<&dyn PrometheusReceiver>, + ) -> Result<(), String> { todo!() } pub fn configure_rule(&self, _rule: Box<&dyn PrometheusRule>) { @@ -27,6 +32,8 @@ impl Prometheus { } } +impl MonitoringSystem for Prometheus {} + pub trait PrometheusCapability { fn install_alert_receivers(&self, receivers: Vec>); fn install_alert_rules(&self, rules: Vec>); diff --git a/harmony/src/modules/monitoring/scrape_target.rs b/harmony/src/modules/monitoring/scrape_target.rs index 9ce73c7..37d35b7 100644 --- a/harmony/src/modules/monitoring/scrape_target.rs +++ b/harmony/src/modules/monitoring/scrape_target.rs @@ -1,9 +1,12 @@ use std::fmt::Debug; +use dyn_clone::DynClone; + use super::prometheus::AlertSender; -pub trait ScrapeTarget: Debug + Send + Sync { +pub trait ScrapeTarget: Debug + Send + Sync + DynClone { type Sender: AlertSender; fn install(&self, sender: &Self::Sender); } +