From 8d219c648ab33cff20ee5eebd36b1d19ffb68faf Mon Sep 17 00:00:00 2001 From: Willem Date: Wed, 11 Jun 2025 15:40:58 -0400 Subject: [PATCH 01/11] feat: deploys cluster monitoring from MonitoringAlertingScore --- examples/monitoring/Cargo.toml | 11 ++ examples/monitoring/src/main.rs | 23 +++ harmony/src/domain/topology/k8s_anywhere.rs | 56 +++++++- .../domain/topology/oberservability/k8s.rs | 71 ++++++++++ .../domain/topology/oberservability/mod.rs | 22 +++ .../topology/oberservability/monitoring.rs | 33 +++-- .../alert_channel/discord_alert_channel.rs | 8 ++ .../modules/monitoring/alert_channel/mod.rs | 1 + .../monitoring/discord_alert_manager.rs | 35 ----- .../monitoring/discord_webhook_sender.rs | 55 -------- .../{ => kube_prometheus}/config.rs | 6 +- .../kube_prometheus_helm_chart_score.rs} | 110 +-------------- .../modules/monitoring/kube_prometheus/mod.rs | 3 + .../monitoring/kube_prometheus/types.rs | 14 ++ harmony/src/modules/monitoring/mod.rs | 6 +- .../modules/monitoring/monitoring_alerting.rs | 132 +++--------------- 16 files changed, 256 insertions(+), 330 deletions(-) create mode 100644 examples/monitoring/Cargo.toml create mode 100644 examples/monitoring/src/main.rs create mode 100644 harmony/src/domain/topology/oberservability/k8s.rs create mode 100644 harmony/src/modules/monitoring/alert_channel/discord_alert_channel.rs create mode 100644 harmony/src/modules/monitoring/alert_channel/mod.rs delete mode 100644 harmony/src/modules/monitoring/discord_alert_manager.rs delete mode 100644 harmony/src/modules/monitoring/discord_webhook_sender.rs rename harmony/src/modules/monitoring/{ => kube_prometheus}/config.rs (90%) rename harmony/src/modules/monitoring/{kube_prometheus.rs => kube_prometheus/kube_prometheus_helm_chart_score.rs} (62%) create mode 100644 harmony/src/modules/monitoring/kube_prometheus/mod.rs create mode 100644 harmony/src/modules/monitoring/kube_prometheus/types.rs diff --git a/examples/monitoring/Cargo.toml b/examples/monitoring/Cargo.toml new file mode 100644 index 0000000..57f291c --- /dev/null +++ b/examples/monitoring/Cargo.toml @@ -0,0 +1,11 @@ +[package] +name = "monitoring" +edition = "2024" +version.workspace = true +readme.workspace = true +license.workspace = true + +[dependencies] +harmony = { version = "0.1.0", path = "../../harmony" } +harmony_cli = { version = "0.1.0", path = "../../harmony_cli" } +tokio.workspace = true diff --git a/examples/monitoring/src/main.rs b/examples/monitoring/src/main.rs new file mode 100644 index 0000000..8b6eeb4 --- /dev/null +++ b/examples/monitoring/src/main.rs @@ -0,0 +1,23 @@ +use harmony::{ + inventory::Inventory, + maestro::Maestro, + modules::monitoring::monitoring_alerting::MonitoringAlertingScore, + topology::K8sAnywhereTopology, +}; + +#[tokio::main] +async fn main() { + let mut maestro = Maestro::::initialize( + Inventory::autoload(), + K8sAnywhereTopology::from_env(), + ) + .await + .unwrap(); + + let monitoring = MonitoringAlertingScore { + alert_channel_configs: None, + }; + + maestro.register_all(vec![Box::new(monitoring)]); + harmony_cli::init(maestro, None).await.unwrap(); +} diff --git a/harmony/src/domain/topology/k8s_anywhere.rs b/harmony/src/domain/topology/k8s_anywhere.rs index 044729f..87d47bd 100644 --- a/harmony/src/domain/topology/k8s_anywhere.rs +++ b/harmony/src/domain/topology/k8s_anywhere.rs @@ -6,7 +6,6 @@ use log::{debug, info, warn}; use tokio::sync::OnceCell; use crate::{ - data::Id, executors::ExecutorError, interpret::{InterpretError, Outcome}, inventory::Inventory, @@ -18,9 +17,12 @@ use crate::{ use super::{ HelmCommand, K8sclient, Topology, k8s::K8sClient, - tenant::{ - ResourceLimits, TenantConfig, TenantManager, TenantNetworkPolicy, k8s::K8sTenantManager, + oberservability::{ + K8sMonitorConfig, + k8s::K8sMonitor, + monitoring::{AlertChannelConfig, Monitor}, }, + tenant::{TenantConfig, TenantManager, k8s::K8sTenantManager}, }; struct K8sState { @@ -38,6 +40,7 @@ enum K8sSource { pub struct K8sAnywhereTopology { k8s_state: OnceCell>, tenant_manager: OnceCell, + k8s_monitor: OnceCell, config: K8sAnywhereConfig, } @@ -63,6 +66,7 @@ impl K8sAnywhereTopology { Self { k8s_state: OnceCell::new(), tenant_manager: OnceCell::new(), + k8s_monitor: OnceCell::new(), config: K8sAnywhereConfig::from_env(), } } @@ -71,6 +75,7 @@ impl K8sAnywhereTopology { Self { k8s_state: OnceCell::new(), tenant_manager: OnceCell::new(), + k8s_monitor: OnceCell::new(), config, } } @@ -201,6 +206,30 @@ impl K8sAnywhereTopology { )), } } + + async fn ensure_k8s_monitor(&self) -> Result<(), String> { + if let Some(_) = self.k8s_monitor.get() { + return Ok(()); + } + + self.k8s_monitor + .get_or_try_init(async || -> Result { + let config = K8sMonitorConfig::cluster_monitor(); + Ok(K8sMonitor { config }) + }) + .await + .unwrap(); + Ok(()) + } + + fn get_k8s_monitor(&self) -> Result<&K8sMonitor, ExecutorError> { + match self.k8s_monitor.get() { + Some(k) => Ok(k), + None => Err(ExecutorError::UnexpectedError( + "K8sMonitor not available".to_string(), + )), + } + } } pub struct K8sAnywhereConfig { @@ -252,6 +281,10 @@ impl Topology for K8sAnywhereTopology { "No K8s client could be found or installed".to_string(), ))?; + self.ensure_k8s_monitor() + .await + .map_err(|e| InterpretError::new(e))?; + self.ensure_k8s_tenant_manager() .await .map_err(|e| InterpretError::new(e))?; @@ -276,3 +309,20 @@ impl TenantManager for K8sAnywhereTopology { .await } } +#[async_trait] +impl Monitor for K8sAnywhereTopology { + async fn provision_monitor( + &self, + inventory: &Inventory, + topology: &T, + alert_receivers: Option>>, + ) -> Result { + self.get_k8s_monitor()? + .provision_monitor(inventory, topology, alert_receivers) + .await + } + + fn delete_monitor(&self) -> Result { + todo!() + } +} diff --git a/harmony/src/domain/topology/oberservability/k8s.rs b/harmony/src/domain/topology/oberservability/k8s.rs new file mode 100644 index 0000000..004f9ec --- /dev/null +++ b/harmony/src/domain/topology/oberservability/k8s.rs @@ -0,0 +1,71 @@ +use std::sync::Arc; + +use async_trait::async_trait; +use serde::Serialize; + +use crate::score::Score; + +use crate::topology::HelmCommand; +use crate::{ + interpret::{InterpretError, Outcome}, + inventory::Inventory, + topology::Topology, +}; + +use super::{ + K8sMonitorConfig, + monitoring::{AlertChannel, AlertChannelConfig, Monitor}, +}; + +#[derive(Debug, Clone, Serialize)] +pub struct K8sMonitor { + pub config: K8sMonitorConfig, +} + +#[async_trait] +impl Monitor for K8sMonitor { + async fn provision_monitor( + &self, + inventory: &Inventory, + topology: &T, + alert_channels: Option>>, + ) -> Result { + if let Some(channels) = alert_channels { + let alert_channels = self.build_alert_channels(channels).await?; + for channel in alert_channels { + channel.register_alert_channel().await?; + } + } + let chart = self.config.chart.clone(); + chart + .create_interpret() + .execute(inventory, topology) + .await?; + Ok(Outcome::success("installed monitor".to_string())) + } + + fn delete_monitor(&self) -> Result { + todo!() + } +} + +#[async_trait] +impl AlertChannelConfig for K8sMonitor { + async fn build_alert_channel(&self) -> Result, InterpretError> { + todo!() + } +} + +impl K8sMonitor { + pub async fn build_alert_channels( + &self, + alert_channel_configs: Vec>, + ) -> Result>, InterpretError> { + let mut alert_channels = Vec::new(); + for config in alert_channel_configs { + let channel = config.build_alert_channel().await?; + alert_channels.push(channel) + } + Ok(alert_channels) + } +} diff --git a/harmony/src/domain/topology/oberservability/mod.rs b/harmony/src/domain/topology/oberservability/mod.rs index 7f2ac95..387709a 100644 --- a/harmony/src/domain/topology/oberservability/mod.rs +++ b/harmony/src/domain/topology/oberservability/mod.rs @@ -1 +1,23 @@ +use serde::Serialize; + +use crate::modules::{ + helm::chart::HelmChartScore, + monitoring::kube_prometheus::kube_prometheus_helm_chart_score::kube_prometheus_helm_chart_score, +}; + +pub mod k8s; pub mod monitoring; + +#[derive(Debug, Clone, Serialize)] +pub struct K8sMonitorConfig { + //probably need to do something better here + pub chart: HelmChartScore, +} + +impl K8sMonitorConfig { + pub fn cluster_monitor() -> Self { + Self { + chart: kube_prometheus_helm_chart_score(), + } + } +} diff --git a/harmony/src/domain/topology/oberservability/monitoring.rs b/harmony/src/domain/topology/oberservability/monitoring.rs index 51ec38e..1d6a159 100644 --- a/harmony/src/domain/topology/oberservability/monitoring.rs +++ b/harmony/src/domain/topology/oberservability/monitoring.rs @@ -1,30 +1,39 @@ use async_trait::async_trait; - +use dyn_clone::DynClone; use std::fmt::Debug; +use crate::executors::ExecutorError; use crate::interpret::InterpretError; +use crate::inventory::Inventory; +use crate::topology::HelmCommand; use crate::{interpret::Outcome, topology::Topology}; /// Represents an entity responsible for collecting and organizing observability data -/// from various telemetry sources +/// from various telemetry sources such as Prometheus or Datadog /// A `Monitor` abstracts the logic required to scrape, aggregate, and structure /// monitoring data, enabling consistent processing regardless of the underlying data source. #[async_trait] -pub trait Monitor: Debug + Send + Sync { - async fn deploy_monitor( +pub trait Monitor { + async fn provision_monitor( &self, + inventory: &Inventory, topology: &T, - alert_receivers: Vec, + alert_receivers: Option>>, ) -> Result; - async fn delete_monitor( - &self, - topolgy: &T, - alert_receivers: Vec, - ) -> Result; + fn delete_monitor(&self) -> Result; } -pub struct AlertReceiver { - pub receiver_id: String, +#[async_trait] +pub trait AlertChannel: Debug + Send + Sync { + async fn register_alert_channel(&self) -> Result; + //async fn get_channel_id(&self) -> String; } + +#[async_trait] +pub trait AlertChannelConfig: Debug + Send + Sync + DynClone { + async fn build_alert_channel(&self) -> Result, InterpretError>; +} + +dyn_clone::clone_trait_object!(AlertChannelConfig); diff --git a/harmony/src/modules/monitoring/alert_channel/discord_alert_channel.rs b/harmony/src/modules/monitoring/alert_channel/discord_alert_channel.rs new file mode 100644 index 0000000..9dc11f3 --- /dev/null +++ b/harmony/src/modules/monitoring/alert_channel/discord_alert_channel.rs @@ -0,0 +1,8 @@ +use url::Url; + +#[derive(Debug, Clone)] +pub struct DiscordWebhookAlertChannel { + pub webhook_url: Url, + pub name: String, + pub send_resolved_notifications: bool, +} diff --git a/harmony/src/modules/monitoring/alert_channel/mod.rs b/harmony/src/modules/monitoring/alert_channel/mod.rs new file mode 100644 index 0000000..fabc6dd --- /dev/null +++ b/harmony/src/modules/monitoring/alert_channel/mod.rs @@ -0,0 +1 @@ +pub mod discord_alert_channel; diff --git a/harmony/src/modules/monitoring/discord_alert_manager.rs b/harmony/src/modules/monitoring/discord_alert_manager.rs deleted file mode 100644 index 7765505..0000000 --- a/harmony/src/modules/monitoring/discord_alert_manager.rs +++ /dev/null @@ -1,35 +0,0 @@ -use std::str::FromStr; - -use non_blank_string_rs::NonBlankString; -use url::Url; - -use crate::modules::helm::chart::HelmChartScore; - -pub fn discord_alert_manager_score( - webhook_url: Url, - namespace: String, - name: String, -) -> HelmChartScore { - let values = format!( - r#" -environment: - - name: "DISCORD_WEBHOOK" - value: "{webhook_url}" -"#, - ); - - HelmChartScore { - namespace: Some(NonBlankString::from_str(&namespace).unwrap()), - release_name: NonBlankString::from_str(&name).unwrap(), - chart_name: NonBlankString::from_str( - "oci://hub.nationtech.io/library/alertmanager-discord", - ) - .unwrap(), - chart_version: None, - values_overrides: None, - values_yaml: Some(values.to_string()), - create_namespace: true, - install_only: true, - repository: None, - } -} diff --git a/harmony/src/modules/monitoring/discord_webhook_sender.rs b/harmony/src/modules/monitoring/discord_webhook_sender.rs deleted file mode 100644 index bad6402..0000000 --- a/harmony/src/modules/monitoring/discord_webhook_sender.rs +++ /dev/null @@ -1,55 +0,0 @@ -use async_trait::async_trait; -use serde_json::Value; -use url::Url; - -use crate::{ - interpret::{InterpretError, Outcome}, - topology::K8sAnywhereTopology, -}; - -#[derive(Debug, Clone)] -pub struct DiscordWebhookConfig { - pub webhook_url: Url, - pub name: String, - pub send_resolved_notifications: bool, -} - -pub trait DiscordWebhookReceiver { - fn deploy_discord_webhook_receiver( - &self, - _notification_adapter_id: &str, - ) -> Result; - - fn delete_discord_webhook_receiver( - &self, - _notification_adapter_id: &str, - ) -> Result; -} - -// trait used to generate alert manager config values impl Monitor for KubePrometheus -pub trait AlertManagerConfig { - fn get_alert_manager_config(&self) -> Result; -} - -#[async_trait] -impl AlertManagerConfig for DiscordWebhookConfig { - fn get_alert_manager_config(&self) -> Result { - todo!() - } -} - -#[async_trait] -impl DiscordWebhookReceiver for K8sAnywhereTopology { - fn deploy_discord_webhook_receiver( - &self, - _notification_adapter_id: &str, - ) -> Result { - todo!() - } - fn delete_discord_webhook_receiver( - &self, - _notification_adapter_id: &str, - ) -> Result { - todo!() - } -} diff --git a/harmony/src/modules/monitoring/config.rs b/harmony/src/modules/monitoring/kube_prometheus/config.rs similarity index 90% rename from harmony/src/modules/monitoring/config.rs rename to harmony/src/modules/monitoring/kube_prometheus/config.rs index 1477905..74fdf6f 100644 --- a/harmony/src/modules/monitoring/config.rs +++ b/harmony/src/modules/monitoring/kube_prometheus/config.rs @@ -1,6 +1,6 @@ use serde::Serialize; -use super::monitoring_alerting::AlertChannel; +use super::types::AlertManagerChannelConfig; #[derive(Debug, Clone, Serialize)] pub struct KubePrometheusConfig { @@ -21,7 +21,7 @@ pub struct KubePrometheusConfig { pub kube_proxy: bool, pub kube_state_metrics: bool, pub prometheus_operator: bool, - pub alert_channel: Vec, + pub alert_channels: Vec, } impl KubePrometheusConfig { pub fn new() -> Self { @@ -30,7 +30,6 @@ impl KubePrometheusConfig { default_rules: true, windows_monitoring: false, alert_manager: true, - alert_channel: Vec::new(), grafana: true, node_exporter: false, prometheus: true, @@ -44,6 +43,7 @@ impl KubePrometheusConfig { prometheus_operator: true, core_dns: false, kube_scheduler: false, + alert_channels: Vec::new(), } } } diff --git a/harmony/src/modules/monitoring/kube_prometheus.rs b/harmony/src/modules/monitoring/kube_prometheus/kube_prometheus_helm_chart_score.rs similarity index 62% rename from harmony/src/modules/monitoring/kube_prometheus.rs rename to harmony/src/modules/monitoring/kube_prometheus/kube_prometheus_helm_chart_score.rs index b694f51..14680d9 100644 --- a/harmony/src/modules/monitoring/kube_prometheus.rs +++ b/harmony/src/modules/monitoring/kube_prometheus/kube_prometheus_helm_chart_score.rs @@ -1,12 +1,13 @@ -use super::{config::KubePrometheusConfig, monitoring_alerting::AlertChannel}; +use super::config::KubePrometheusConfig; use log::info; use non_blank_string_rs::NonBlankString; -use std::{collections::HashMap, str::FromStr}; -use url::Url; +use std::str::FromStr; use crate::modules::helm::chart::HelmChartScore; -pub fn kube_prometheus_helm_chart_score(config: &KubePrometheusConfig) -> HelmChartScore { +pub fn kube_prometheus_helm_chart_score() -> HelmChartScore { + let config = KubePrometheusConfig::new(); + //TODO this should be make into a rule with default formatting that can be easily passed as a vec //to the overrides or something leaving the user to deal with formatting here seems bad let default_rules = config.default_rules.to_string(); @@ -144,67 +145,6 @@ prometheus: enabled: {prometheus} "#, ); - - let alertmanager_config = alert_manager_yaml_builder(&config); - values.push_str(&alertmanager_config); - - fn alert_manager_yaml_builder(config: &KubePrometheusConfig) -> String { - let mut receivers = String::new(); - let mut routes = String::new(); - let mut global_configs = String::new(); - let alert_manager = config.alert_manager; - for alert_channel in &config.alert_channel { - match alert_channel { - AlertChannel::Discord { name, .. } => { - let (receiver, route) = discord_alert_builder(name); - info!("discord receiver: {} \nroute: {}", receiver, route); - receivers.push_str(&receiver); - routes.push_str(&route); - } - AlertChannel::Slack { - slack_channel, - webhook_url, - } => { - let (receiver, route) = slack_alert_builder(slack_channel); - info!("slack receiver: {} \nroute: {}", receiver, route); - receivers.push_str(&receiver); - - routes.push_str(&route); - let global_config = format!( - r#" - global: - slack_api_url: {webhook_url}"# - ); - - global_configs.push_str(&global_config); - } - AlertChannel::Smpt { .. } => todo!(), - } - } - info!("after alert receiver: {}", receivers); - info!("after alert routes: {}", routes); - - let alertmanager_config = format!( - r#" -alertmanager: - enabled: {alert_manager} - config: {global_configs} - route: - group_by: ['job'] - group_wait: 30s - group_interval: 5m - repeat_interval: 12h - routes: -{routes} - receivers: - - name: 'null' -{receivers}"# - ); - - info!("alert manager config: {}", alertmanager_config); - alertmanager_config - } - HelmChartScore { namespace: Some(NonBlankString::from_str(&config.namespace).unwrap()), release_name: NonBlankString::from_str("kube-prometheus").unwrap(), @@ -220,43 +160,3 @@ alertmanager: repository: None, } } - -fn discord_alert_builder(release_name: &String) -> (String, String) { - let discord_receiver_name = format!("Discord-{}", release_name); - let receiver = format!( - r#" - - name: '{discord_receiver_name}' - webhook_configs: - - url: 'http://{release_name}-alertmanager-discord:9094' - send_resolved: true"#, - ); - let route = format!( - r#" - - receiver: '{discord_receiver_name}' - matchers: - - alertname!=Watchdog - continue: true"#, - ); - (receiver, route) -} - -fn slack_alert_builder(slack_channel: &String) -> (String, String) { - let slack_receiver_name = format!("Slack-{}", slack_channel); - let receiver = format!( - r#" - - name: '{slack_receiver_name}' - slack_configs: - - channel: '{slack_channel}' - send_resolved: true - title: '{{{{ .CommonAnnotations.title }}}}' - text: '{{{{ .CommonAnnotations.description }}}}'"#, - ); - let route = format!( - r#" - - receiver: '{slack_receiver_name}' - matchers: - - alertname!=Watchdog - continue: true"#, - ); - (receiver, route) -} diff --git a/harmony/src/modules/monitoring/kube_prometheus/mod.rs b/harmony/src/modules/monitoring/kube_prometheus/mod.rs new file mode 100644 index 0000000..8ad49f0 --- /dev/null +++ b/harmony/src/modules/monitoring/kube_prometheus/mod.rs @@ -0,0 +1,3 @@ +pub mod config; +pub mod kube_prometheus_helm_chart_score; +pub mod types; diff --git a/harmony/src/modules/monitoring/kube_prometheus/types.rs b/harmony/src/modules/monitoring/kube_prometheus/types.rs new file mode 100644 index 0000000..fa66f99 --- /dev/null +++ b/harmony/src/modules/monitoring/kube_prometheus/types.rs @@ -0,0 +1,14 @@ +use serde::Serialize; + +#[derive(Debug, Clone, Serialize)] +pub struct AlertManagerChannelConfig { + pub global_configs: AlertManagerChannelGlobalConfigs, + pub route: AlertManagerChannelRoute, + pub receiver: AlertManagerChannelReceiver, +} +#[derive(Debug, Clone, Serialize)] +pub struct AlertManagerChannelGlobalConfigs {} +#[derive(Debug, Clone, Serialize)] +pub struct AlertManagerChannelReceiver {} +#[derive(Debug, Clone, Serialize)] +pub struct AlertManagerChannelRoute {} diff --git a/harmony/src/modules/monitoring/mod.rs b/harmony/src/modules/monitoring/mod.rs index d3eb288..a84bd9f 100644 --- a/harmony/src/modules/monitoring/mod.rs +++ b/harmony/src/modules/monitoring/mod.rs @@ -1,5 +1,3 @@ -mod config; -mod discord_alert_manager; -pub mod discord_webhook_sender; -mod kube_prometheus; +pub mod alert_channel; +pub mod kube_prometheus; pub mod monitoring_alerting; diff --git a/harmony/src/modules/monitoring/monitoring_alerting.rs b/harmony/src/modules/monitoring/monitoring_alerting.rs index 6d2db38..df1c8a2 100644 --- a/harmony/src/modules/monitoring/monitoring_alerting.rs +++ b/harmony/src/modules/monitoring/monitoring_alerting.rs @@ -1,10 +1,7 @@ use async_trait::async_trait; -use email_address::EmailAddress; - -use log::info; use serde::Serialize; -use url::Url; +use crate::topology::oberservability::monitoring::{AlertChannelConfig, Monitor}; use crate::{ data::{Id, Version}, interpret::{Interpret, InterpretError, InterpretName, InterpretStatus, Outcome}, @@ -13,134 +10,43 @@ use crate::{ topology::{HelmCommand, Topology}, }; -use super::{ - config::KubePrometheusConfig, discord_alert_manager::discord_alert_manager_score, - kube_prometheus::kube_prometheus_helm_chart_score, -}; - #[derive(Debug, Clone, Serialize)] -pub enum AlertChannel { - Discord { - name: String, - webhook_url: Url, - }, - Slack { - slack_channel: String, - webhook_url: Url, - }, - //TODO test and implement in helm chart - //currently does not work - Smpt { - email_address: EmailAddress, - service_name: String, - }, +pub struct MonitoringAlertingScore { + #[serde(skip)] + pub alert_channel_configs: Option>>, } -#[derive(Debug, Clone, Serialize)] -pub struct MonitoringAlertingStackScore { - pub alert_channel: Vec, - pub namespace: Option, -} - -impl MonitoringAlertingStackScore { - pub fn new() -> Self { - Self { - alert_channel: Vec::new(), - namespace: None, - } - } -} - -impl Score for MonitoringAlertingStackScore { +impl Score for MonitoringAlertingScore { fn create_interpret(&self) -> Box> { - Box::new(MonitoringAlertingStackInterpret { + Box::new(MonitoringAlertingInterpret { score: self.clone(), }) } + fn name(&self) -> String { - format!("MonitoringAlertingStackScore") + "MonitoringAlertingScore".to_string() } } -#[derive(Debug, Clone, Serialize)] -struct MonitoringAlertingStackInterpret { - score: MonitoringAlertingStackScore, -} - -impl MonitoringAlertingStackInterpret { - async fn build_kube_prometheus_helm_chart_config(&self) -> KubePrometheusConfig { - let mut config = KubePrometheusConfig::new(); - if let Some(ns) = &self.score.namespace { - config.namespace = ns.clone(); - } - config.alert_channel = self.score.alert_channel.clone(); - config - } - - async fn deploy_kube_prometheus_helm_chart_score( - &self, - inventory: &Inventory, - topology: &T, - config: &KubePrometheusConfig, - ) -> Result { - let helm_chart = kube_prometheus_helm_chart_score(config); - helm_chart - .create_interpret() - .execute(inventory, topology) - .await - } - - async fn deploy_alert_channel_service( - &self, - inventory: &Inventory, - topology: &T, - config: &KubePrometheusConfig, - ) -> Result { - //let mut outcomes = vec![]; - - //for channel in &self.score.alert_channel { - // let outcome = match channel { - // AlertChannel::Discord { .. } => { - // discord_alert_manager_score(config) - // .create_interpret() - // .execute(inventory, topology) - // .await - // } - // AlertChannel::Slack { .. } => Ok(Outcome::success( - // "No extra configs for slack alerting".to_string(), - // )), - // AlertChannel::Smpt { .. } => { - // todo!() - // } - // }; - // outcomes.push(outcome); - //} - //for result in outcomes { - // result?; - //} - - Ok(Outcome::success("All alert channels deployed".to_string())) - } +#[derive(Debug)] +struct MonitoringAlertingInterpret { + score: MonitoringAlertingScore, } #[async_trait] -impl Interpret for MonitoringAlertingStackInterpret { +impl Interpret for MonitoringAlertingInterpret { async fn execute( &self, inventory: &Inventory, topology: &T, ) -> Result { - let config = self.build_kube_prometheus_helm_chart_config().await; - info!("Built kube prometheus config"); - info!("Installing kube prometheus chart"); - self.deploy_kube_prometheus_helm_chart_score(inventory, topology, &config) - .await?; - info!("Installing alert channel service"); - self.deploy_alert_channel_service(inventory, topology, &config) - .await?; - Ok(Outcome::success(format!( - "succesfully deployed monitoring and alerting stack" - ))) + topology + .provision_monitor( + inventory, + topology, + self.score.alert_channel_configs.clone(), + ) + .await } fn get_name(&self) -> InterpretName { -- 2.39.5 From 7c92a081325dfff5457186f79d469139ec10e31d Mon Sep 17 00:00:00 2001 From: Willem Date: Wed, 11 Jun 2025 15:44:38 -0400 Subject: [PATCH 02/11] fix: cargo fmt, remove old use from lamp example --- examples/lamp/src/main.rs | 10 ++-------- examples/monitoring/src/main.rs | 3 +-- 2 files changed, 3 insertions(+), 10 deletions(-) diff --git a/examples/lamp/src/main.rs b/examples/lamp/src/main.rs index 4433d96..eaeb0f5 100644 --- a/examples/lamp/src/main.rs +++ b/examples/lamp/src/main.rs @@ -2,10 +2,7 @@ use harmony::{ data::Version, inventory::Inventory, maestro::Maestro, - modules::{ - lamp::{LAMPConfig, LAMPScore}, - monitoring::monitoring_alerting::MonitoringAlertingStackScore, - }, + modules::lamp::{LAMPConfig, LAMPScore}, topology::{K8sAnywhereTopology, Url}, }; @@ -43,10 +40,7 @@ async fn main() { .await .unwrap(); - let mut monitoring_stack_score = MonitoringAlertingStackScore::new(); - monitoring_stack_score.namespace = Some(lamp_stack.config.namespace.clone()); - - maestro.register_all(vec![Box::new(lamp_stack), Box::new(monitoring_stack_score)]); + maestro.register_all(vec![Box::new(lamp_stack)]); // Here we bootstrap the CLI, this gives some nice features if you need them harmony_cli::init(maestro, None).await.unwrap(); } diff --git a/examples/monitoring/src/main.rs b/examples/monitoring/src/main.rs index 8b6eeb4..91ddfd3 100644 --- a/examples/monitoring/src/main.rs +++ b/examples/monitoring/src/main.rs @@ -1,6 +1,5 @@ use harmony::{ - inventory::Inventory, - maestro::Maestro, + inventory::Inventory, maestro::Maestro, modules::monitoring::monitoring_alerting::MonitoringAlertingScore, topology::K8sAnywhereTopology, }; -- 2.39.5 From c16276b62b6fd0d5a7099fe34b32c361156a31d6 Mon Sep 17 00:00:00 2001 From: Jean-Gabriel Gill-Couture Date: Fri, 13 Jun 2025 12:36:02 -0400 Subject: [PATCH 03/11] wip Alerting abstractions architecture --- harmony/src/modules/monitoring/mod.rs | 1 + .../src/modules/monitoring/monitoring_2.rs | 59 +++++++++++++++ .../modules/monitoring/monitoring_alerting.rs | 73 +++++++++++++++++-- 3 files changed, 128 insertions(+), 5 deletions(-) create mode 100644 harmony/src/modules/monitoring/monitoring_2.rs diff --git a/harmony/src/modules/monitoring/mod.rs b/harmony/src/modules/monitoring/mod.rs index a84bd9f..8baa019 100644 --- a/harmony/src/modules/monitoring/mod.rs +++ b/harmony/src/modules/monitoring/mod.rs @@ -1,3 +1,4 @@ pub mod alert_channel; pub mod kube_prometheus; pub mod monitoring_alerting; +mod monitoring_2; diff --git a/harmony/src/modules/monitoring/monitoring_2.rs b/harmony/src/modules/monitoring/monitoring_2.rs new file mode 100644 index 0000000..b8e1dfc --- /dev/null +++ b/harmony/src/modules/monitoring/monitoring_2.rs @@ -0,0 +1,59 @@ +use crate::{score::Score, topology::Topology}; + +pub trait AlertSender {} + +struct Prometheus {} + +impl Prometheus { + fn configure_receiver(&self, receiver: PrometheusReceiver) -> Result<(), String> { + todo!() + } +} + +impl AlertSender for Prometheus {} + +struct DiscordWebhook; +struct PrometheusReceiver; + +impl DiscordWebhook { + fn as_prometheus_receiver(&self) -> PrometheusReceiver { + PrometheusReceiver {} + } +} + +impl AlertReceiver for DiscordWebhook { + type Sender = Prometheus; + + fn install(&self, sender: &Self::Sender) -> Result<(), String> { + sender.configure_receiver(self.as_prometheus_receiver()) + } +} + +pub trait AlertReceiver { + type Sender: AlertSender; + + fn install(&self) -> Result<(), String>; +} + +struct AlertReceiverConfig { + config: String, // Or whatever + sender: S, +} + +struct DiscordWebhookScore { + config: DiscordWebhook, +} + +impl Score for DiscordWebhookScore { + fn create_interpret(&self) -> Box { + AlertReceiverInterpret { receiver: Box::new(self.config.clone())} + } + + fn name(&self) -> String { + todo!() + } +} + +struct AlertReceiverInterpret { + receiver: Box, +} diff --git a/harmony/src/modules/monitoring/monitoring_alerting.rs b/harmony/src/modules/monitoring/monitoring_alerting.rs index df1c8a2..1d0d48e 100644 --- a/harmony/src/modules/monitoring/monitoring_alerting.rs +++ b/harmony/src/modules/monitoring/monitoring_alerting.rs @@ -1,5 +1,6 @@ use async_trait::async_trait; use serde::Serialize; +use serde_value::Value; use crate::topology::oberservability::monitoring::{AlertChannelConfig, Monitor}; use crate::{ @@ -10,13 +11,74 @@ use crate::{ topology::{HelmCommand, Topology}, }; -#[derive(Debug, Clone, Serialize)] -pub struct MonitoringAlertingScore { - #[serde(skip)] - pub alert_channel_configs: Option>>, +pub trait MonitoringSystem {} + +trait PrometheusReceiver { + fn get_config(&self) -> PrometheusReceiverConfig; } -impl Score for MonitoringAlertingScore { +struct PrometheusReceiverConfig { + config: Value, // either a serde Value or a more specific type that understands prometheus + // config +} + +struct PrometheusRule { + definition: String, // Not a string but an actual prometheus rule config +} + +struct PrometheusScrapeTarget { + definition: String, // Not a string but an actual prometheus scraping config +} + +pub struct PrometheusMonitoringScore { + alert_receivers: Vec>, + alert_rules: Vec>, + scrape_targets: Vec>, +} + +pub struct PrometheusMonitoringInterpret {} + +#[async_trait] +impl Interpret for PrometheusMonitoringInterpret { + async fn execute( + &self, + inventory: &Inventory, + topology: &T, + ) -> Result { + todo!() + } + + fn get_name(&self) -> InterpretName { + todo!() + } + + fn get_version(&self) -> Version { + todo!() + } + + fn get_status(&self) -> InterpretStatus { + todo!() + } + + fn get_children(&self) -> Vec { + todo!() + } +} + +pub trait PrometheusCapability { + fn install_alert_receivers(&self, receivers: Vec>); + fn install_alert_rules(&self, rules: Vec>); + fn install_scrape_targets(&self, receivers: Vec>); +} + +#[derive(Debug, Clone, Serialize)] +pub struct MonitoringAlertingScore { + alert_receivers: Vec>>, + alert_rules: Vec>>, + monitoring_targets: Vec>>, +} + +impl Score for MonitoringAlertingScore { fn create_interpret(&self) -> Box> { Box::new(MonitoringAlertingInterpret { score: self.clone(), @@ -65,3 +127,4 @@ impl Interpret for MonitoringAlertingInt todo!() } } + -- 2.39.5 From f6c146b14bd01fdefc8ca26d8fa096e2faa720a5 Mon Sep 17 00:00:00 2001 From: Willem Date: Mon, 16 Jun 2025 08:45:29 -0400 Subject: [PATCH 04/11] wip: setting up base for monitoring --- harmony/src/domain/topology/k8s_anywhere.rs | 48 --------- .../alert_channel/discord_alert_channel.rs | 80 +++++++++++++-- .../src/modules/monitoring/alert_receiver.rs | 55 +++++++++++ harmony/src/modules/monitoring/alert_rule.rs | 11 +++ harmony/src/modules/monitoring/mod.rs | 5 +- .../src/modules/monitoring/monitoring_2.rs | 59 ----------- .../modules/monitoring/monitoring_alerting.rs | 73 ++------------ harmony/src/modules/monitoring/prometheus.rs | 99 +++++++++++++++++++ .../src/modules/monitoring/scrape_target.rs | 9 ++ 9 files changed, 262 insertions(+), 177 deletions(-) create mode 100644 harmony/src/modules/monitoring/alert_receiver.rs create mode 100644 harmony/src/modules/monitoring/alert_rule.rs delete mode 100644 harmony/src/modules/monitoring/monitoring_2.rs create mode 100644 harmony/src/modules/monitoring/prometheus.rs create mode 100644 harmony/src/modules/monitoring/scrape_target.rs diff --git a/harmony/src/domain/topology/k8s_anywhere.rs b/harmony/src/domain/topology/k8s_anywhere.rs index 87d47bd..c143805 100644 --- a/harmony/src/domain/topology/k8s_anywhere.rs +++ b/harmony/src/domain/topology/k8s_anywhere.rs @@ -40,7 +40,6 @@ enum K8sSource { pub struct K8sAnywhereTopology { k8s_state: OnceCell>, tenant_manager: OnceCell, - k8s_monitor: OnceCell, config: K8sAnywhereConfig, } @@ -66,7 +65,6 @@ impl K8sAnywhereTopology { Self { k8s_state: OnceCell::new(), tenant_manager: OnceCell::new(), - k8s_monitor: OnceCell::new(), config: K8sAnywhereConfig::from_env(), } } @@ -75,7 +73,6 @@ impl K8sAnywhereTopology { Self { k8s_state: OnceCell::new(), tenant_manager: OnceCell::new(), - k8s_monitor: OnceCell::new(), config, } } @@ -206,30 +203,6 @@ impl K8sAnywhereTopology { )), } } - - async fn ensure_k8s_monitor(&self) -> Result<(), String> { - if let Some(_) = self.k8s_monitor.get() { - return Ok(()); - } - - self.k8s_monitor - .get_or_try_init(async || -> Result { - let config = K8sMonitorConfig::cluster_monitor(); - Ok(K8sMonitor { config }) - }) - .await - .unwrap(); - Ok(()) - } - - fn get_k8s_monitor(&self) -> Result<&K8sMonitor, ExecutorError> { - match self.k8s_monitor.get() { - Some(k) => Ok(k), - None => Err(ExecutorError::UnexpectedError( - "K8sMonitor not available".to_string(), - )), - } - } } pub struct K8sAnywhereConfig { @@ -281,10 +254,6 @@ impl Topology for K8sAnywhereTopology { "No K8s client could be found or installed".to_string(), ))?; - self.ensure_k8s_monitor() - .await - .map_err(|e| InterpretError::new(e))?; - self.ensure_k8s_tenant_manager() .await .map_err(|e| InterpretError::new(e))?; @@ -309,20 +278,3 @@ impl TenantManager for K8sAnywhereTopology { .await } } -#[async_trait] -impl Monitor for K8sAnywhereTopology { - async fn provision_monitor( - &self, - inventory: &Inventory, - topology: &T, - alert_receivers: Option>>, - ) -> Result { - self.get_k8s_monitor()? - .provision_monitor(inventory, topology, alert_receivers) - .await - } - - fn delete_monitor(&self) -> Result { - todo!() - } -} 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 9dc11f3..e8bca1d 100644 --- a/harmony/src/modules/monitoring/alert_channel/discord_alert_channel.rs +++ b/harmony/src/modules/monitoring/alert_channel/discord_alert_channel.rs @@ -1,8 +1,76 @@ -use url::Url; +//#[derive(Debug, Clone)] +//pub struct DiscordWebhookAlertChannel { +// pub webhook_url: Url, +// pub name: String, +// pub send_resolved_notifications: bool, +//} -#[derive(Debug, Clone)] -pub struct DiscordWebhookAlertChannel { - pub webhook_url: Url, - pub name: String, - pub send_resolved_notifications: bool, +use serde::Serialize; + +use crate::{interpret::Interpret, modules::monitoring::{alert_receiver::{AlertReceiver, AlertReceiverInterpret}, alert_rule::AlertRule, prometheus::{Prometheus, PrometheusReceiver, PrometheusRule, PrometheusScrapeTarget}, scrape_target::ScrapeTarget}, score::Score, topology::Topology}; + +#[derive(Debug, Clone, Serialize)] +struct DiscordWebhook; + +impl DiscordWebhook { + fn as_prometheus_receiver(&self) -> PrometheusReceiver { + PrometheusReceiver {} + } +} + +impl AlertReceiver for DiscordWebhook { + type Sender = Prometheus; + + fn install(&self, sender: &Self::Sender) -> Result<(), String> { + sender.configure_receiver(self.as_prometheus_receiver()) + } +} + + +#[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!() + } +} + +impl ScrapeTarget for DiscordWebhook { + type Sender = Prometheus; + + fn install(&self, sender: &Self::Sender) { + sender.configure_scrape_target(self.as_prometheus_scrape_target()) + } +} + + +impl DiscordWebhook { + fn as_prometheus_scrape_target(&self) -> PrometheusScrapeTarget { + PrometheusScrapeTarget { definition: todo!() } + } +} + + +impl AlertRule for DiscordWebhook { + type Sender = Prometheus; + + fn install(&self, sender: &Self::Sender) { + sender.configure_rule(self.as_prometheus_rule()) + } +} + + +impl DiscordWebhook { + fn as_prometheus_rule(&self) -> PrometheusRule { + PrometheusRule { definition: todo!() } + } } diff --git a/harmony/src/modules/monitoring/alert_receiver.rs b/harmony/src/modules/monitoring/alert_receiver.rs new file mode 100644 index 0000000..c8a7028 --- /dev/null +++ b/harmony/src/modules/monitoring/alert_receiver.rs @@ -0,0 +1,55 @@ +use std::fmt::Debug; + +use async_trait::async_trait; + +use crate::{ + data::{Id, Version}, + interpret::{Interpret, InterpretError, InterpretName, InterpretStatus, Outcome}, + inventory::Inventory, + topology::Topology, +}; + +use super::prometheus::AlertSender; + +pub trait AlertReceiver: Debug + Send + Sync { + type Sender: AlertSender; + + fn install(&self, sender: &Self::Sender) -> Result<(), String>; +} + +struct AlertReceiverConfig { + config: String, // Or whatever + sender: S, +} + +#[derive(Debug)] +pub struct AlertReceiverInterpret { + pub receiver: Box>, +} + +#[async_trait] +impl Interpret for AlertReceiverInterpret { + async fn execute( + &self, + inventory: &Inventory, + topology: &T, + ) -> Result { + todo!() + } + + fn get_name(&self) -> InterpretName { + todo!() + } + + fn get_version(&self) -> Version { + todo!() + } + + fn get_status(&self) -> InterpretStatus { + todo!() + } + + fn get_children(&self) -> Vec { + todo!() + } +} diff --git a/harmony/src/modules/monitoring/alert_rule.rs b/harmony/src/modules/monitoring/alert_rule.rs new file mode 100644 index 0000000..c64014c --- /dev/null +++ b/harmony/src/modules/monitoring/alert_rule.rs @@ -0,0 +1,11 @@ +use std::fmt::Debug; + +use super::prometheus::AlertSender; + +pub trait AlertRule: Debug { + 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 8baa019..070aa2e 100644 --- a/harmony/src/modules/monitoring/mod.rs +++ b/harmony/src/modules/monitoring/mod.rs @@ -1,4 +1,7 @@ pub mod alert_channel; pub mod kube_prometheus; pub mod monitoring_alerting; -mod monitoring_2; +pub mod prometheus; +pub mod alert_receiver; +pub mod alert_rule; +pub mod scrape_target; diff --git a/harmony/src/modules/monitoring/monitoring_2.rs b/harmony/src/modules/monitoring/monitoring_2.rs deleted file mode 100644 index b8e1dfc..0000000 --- a/harmony/src/modules/monitoring/monitoring_2.rs +++ /dev/null @@ -1,59 +0,0 @@ -use crate::{score::Score, topology::Topology}; - -pub trait AlertSender {} - -struct Prometheus {} - -impl Prometheus { - fn configure_receiver(&self, receiver: PrometheusReceiver) -> Result<(), String> { - todo!() - } -} - -impl AlertSender for Prometheus {} - -struct DiscordWebhook; -struct PrometheusReceiver; - -impl DiscordWebhook { - fn as_prometheus_receiver(&self) -> PrometheusReceiver { - PrometheusReceiver {} - } -} - -impl AlertReceiver for DiscordWebhook { - type Sender = Prometheus; - - fn install(&self, sender: &Self::Sender) -> Result<(), String> { - sender.configure_receiver(self.as_prometheus_receiver()) - } -} - -pub trait AlertReceiver { - type Sender: AlertSender; - - fn install(&self) -> Result<(), String>; -} - -struct AlertReceiverConfig { - config: String, // Or whatever - sender: S, -} - -struct DiscordWebhookScore { - config: DiscordWebhook, -} - -impl Score for DiscordWebhookScore { - fn create_interpret(&self) -> Box { - AlertReceiverInterpret { receiver: Box::new(self.config.clone())} - } - - fn name(&self) -> String { - todo!() - } -} - -struct AlertReceiverInterpret { - receiver: Box, -} diff --git a/harmony/src/modules/monitoring/monitoring_alerting.rs b/harmony/src/modules/monitoring/monitoring_alerting.rs index 1d0d48e..d170a93 100644 --- a/harmony/src/modules/monitoring/monitoring_alerting.rs +++ b/harmony/src/modules/monitoring/monitoring_alerting.rs @@ -11,74 +11,22 @@ use crate::{ topology::{HelmCommand, Topology}, }; +use super::alert_receiver::AlertReceiver; +use super::alert_rule::AlertRule; +use super::scrape_target::ScrapeTarget; + + pub trait MonitoringSystem {} -trait PrometheusReceiver { - fn get_config(&self) -> PrometheusReceiverConfig; -} - -struct PrometheusReceiverConfig { - config: Value, // either a serde Value or a more specific type that understands prometheus - // config -} - -struct PrometheusRule { - definition: String, // Not a string but an actual prometheus rule config -} - -struct PrometheusScrapeTarget { - definition: String, // Not a string but an actual prometheus scraping config -} - -pub struct PrometheusMonitoringScore { - alert_receivers: Vec>, - alert_rules: Vec>, - scrape_targets: Vec>, -} - -pub struct PrometheusMonitoringInterpret {} - -#[async_trait] -impl Interpret for PrometheusMonitoringInterpret { - async fn execute( - &self, - inventory: &Inventory, - topology: &T, - ) -> Result { - todo!() - } - - fn get_name(&self) -> InterpretName { - todo!() - } - - fn get_version(&self) -> Version { - todo!() - } - - fn get_status(&self) -> InterpretStatus { - todo!() - } - - fn get_children(&self) -> Vec { - todo!() - } -} - -pub trait PrometheusCapability { - fn install_alert_receivers(&self, receivers: Vec>); - fn install_alert_rules(&self, rules: Vec>); - fn install_scrape_targets(&self, receivers: Vec>); -} #[derive(Debug, Clone, Serialize)] pub struct MonitoringAlertingScore { - alert_receivers: Vec>>, - alert_rules: Vec>>, - monitoring_targets: Vec>>, + alert_receivers: Vec>>, + alert_rules: Vec>>, + scrape_targets: Vec>>, } -impl Score for MonitoringAlertingScore { +impl Score for MonitoringAlertingScore { fn create_interpret(&self) -> Box> { Box::new(MonitoringAlertingInterpret { score: self.clone(), @@ -96,7 +44,7 @@ struct MonitoringAlertingInterpret { } #[async_trait] -impl Interpret for MonitoringAlertingInterpret { +impl Interpret for MonitoringAlertingInterpret { async fn execute( &self, inventory: &Inventory, @@ -127,4 +75,3 @@ impl Interpret for MonitoringAlertingInt todo!() } } - diff --git a/harmony/src/modules/monitoring/prometheus.rs b/harmony/src/modules/monitoring/prometheus.rs new file mode 100644 index 0000000..270fd84 --- /dev/null +++ b/harmony/src/modules/monitoring/prometheus.rs @@ -0,0 +1,99 @@ +use async_trait::async_trait; +use serde::Serialize; +use serde_value::Value; + +use crate::{ + data::{Id, Version}, + interpret::{Interpret, InterpretError, InterpretName, InterpretStatus, Outcome}, + inventory::Inventory, + score::Score, + topology::Topology, +}; +use std::fmt::Debug; + +pub trait AlertSender {} + +#[derive(Debug, Clone, Serialize)] +pub struct Prometheus {} + +impl Prometheus { + pub fn configure_receiver(&self, receiver: PrometheusReceiver) -> Result<(), String> { + todo!() + } + pub fn configure_rule(&self, rule: PrometheusRule) { + todo!() + } + pub fn configure_scrape_target(&self, target: PrometheusScrapeTarget) { + todo!() + } +} + +pub trait PrometheusCapability { + fn install_alert_receivers(&self, receivers: Vec>); + fn install_alert_rules(&self, rules: Vec>); + fn install_scrape_targets(&self, receivers: Vec>); +} + +impl AlertSender for Prometheus {} + +pub trait PrometheusReceiver{ + fn get_prometheus_config(&self) -> PrometheusReceiverConfig; +} + +pub struct PrometheusReceiverConfig { + config: Value, // either a serde Value or a more specific type that understands prometheus + // config +} + +pub trait PrometheusRule{ + fn get_prometheus_config(&self) -> PrometheusRuleConfig; +} + +pub struct PrometheusRuleConfig { + pub definition: String, // Not a string but an actual prometheus rule config +} + +pub trait PrometheusScrapeTarget { + fn get_prometheus_config(&self) -> PrometheusScrapeTargetConfig; +} + +pub struct PrometheusScrapeTargetConfig { + pub definition: String, // Not a string but an actual prometheus scraping config +} + +pub struct PrometheusMonitoringScore { + alert_receivers: Vec>, + alert_rules: Vec>, + scrape_targets: Vec>, +} + +#[derive(Debug)] +pub struct PrometheusMonitoringInterpret {} + +#[async_trait] +impl Interpret for PrometheusMonitoringInterpret { + async fn execute( + &self, + inventory: &Inventory, + topology: &T, + ) -> Result { + todo!() + } + + fn get_name(&self) -> InterpretName { + todo!() + } + + fn get_version(&self) -> Version { + todo!() + } + + fn get_status(&self) -> InterpretStatus { + todo!() + } + + fn get_children(&self) -> Vec { + todo!() + } +} + diff --git a/harmony/src/modules/monitoring/scrape_target.rs b/harmony/src/modules/monitoring/scrape_target.rs new file mode 100644 index 0000000..954bf0c --- /dev/null +++ b/harmony/src/modules/monitoring/scrape_target.rs @@ -0,0 +1,9 @@ +use std::fmt::Debug; + +use super::prometheus::AlertSender; + +pub trait ScrapeTarget: Debug { + type Sender: AlertSender; + + fn install(&self, sender: &Self::Sender); +} -- 2.39.5 From 9412c0529ad10682b15a48f2068f8518ce708de9 Mon Sep 17 00:00:00 2001 From: Jean-Gabriel Gill-Couture Date: Mon, 16 Jun 2025 17:01:13 -0400 Subject: [PATCH 05/11] feat: add monitoring system and alerting - Implemented a basic monitoring system with alerting capabilities. - Introduced `MonitoringAlertingScore` and related traits (`MonitoringSystem`, `ScrapeTarget`, `AlertRule`). - Added `MonitoringAlertingInterpret` to handle the execution of monitoring configurations. - Defined interfaces for Prometheus integration (PrometheusReceiver, PrometheusRule, PrometheusScrapeTarget). - Introduced `CloneBox` trait to enable cloning boxed trait objects. - Refactored Prometheus related traits to require `Debug` implementation. - Implemented basic placeholder logic for Prometheus configuration and alerting. - Updated existing traits to include `Send` and `Sync` where appropriate. --- Cargo.lock | 9 +++ examples/lamp/src/main.rs | 4 +- .../alert_channel/discord_alert_channel.rs | 65 +++++-------------- .../src/modules/monitoring/alert_receiver.rs | 14 ++-- harmony/src/modules/monitoring/alert_rule.rs | 2 +- .../modules/monitoring/monitoring_alerting.rs | 57 +++++++++------- harmony/src/modules/monitoring/prometheus.rs | 18 +++-- .../src/modules/monitoring/scrape_target.rs | 2 +- 8 files changed, 82 insertions(+), 89 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index a2d8c40..426f5f9 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2387,6 +2387,15 @@ dependencies = [ "windows-sys 0.52.0", ] +[[package]] +name = "monitoring" +version = "0.1.0" +dependencies = [ + "harmony", + "harmony_cli", + "tokio", +] + [[package]] name = "native-tls" version = "0.2.14" diff --git a/examples/lamp/src/main.rs b/examples/lamp/src/main.rs index eaeb0f5..ea6476d 100644 --- a/examples/lamp/src/main.rs +++ b/examples/lamp/src/main.rs @@ -2,7 +2,7 @@ use harmony::{ data::Version, inventory::Inventory, maestro::Maestro, - modules::lamp::{LAMPConfig, LAMPScore}, + modules::{lamp::{LAMPConfig, LAMPScore}, monitoring::monitoring_alerting::MonitoringAlertingScore}, topology::{K8sAnywhereTopology, Url}, }; @@ -29,6 +29,8 @@ async fn main() { }, }; + let monitoring = MonitoringAlertingScore { alert_channel_configs: todo!() }; + // 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 // locally, to development environment from a CI, to staging, and to production with settings 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 e8bca1d..918cc7a 100644 --- a/harmony/src/modules/monitoring/alert_channel/discord_alert_channel.rs +++ b/harmony/src/modules/monitoring/alert_channel/discord_alert_channel.rs @@ -1,31 +1,33 @@ -//#[derive(Debug, Clone)] -//pub struct DiscordWebhookAlertChannel { -// pub webhook_url: Url, -// pub name: String, -// pub send_resolved_notifications: bool, -//} - use serde::Serialize; -use crate::{interpret::Interpret, modules::monitoring::{alert_receiver::{AlertReceiver, AlertReceiverInterpret}, alert_rule::AlertRule, prometheus::{Prometheus, PrometheusReceiver, PrometheusRule, PrometheusScrapeTarget}, scrape_target::ScrapeTarget}, score::Score, topology::Topology}; +use crate::{ + interpret::Interpret, + modules::monitoring::{ + alert_receiver::{AlertReceiver, AlertReceiverInterpret}, + prometheus::{Prometheus, PrometheusReceiver}, + }, + score::Score, + topology::Topology, +}; #[derive(Debug, Clone, Serialize)] struct DiscordWebhook; -impl DiscordWebhook { - fn as_prometheus_receiver(&self) -> PrometheusReceiver { - PrometheusReceiver {} - } -} - impl AlertReceiver for DiscordWebhook { type Sender = Prometheus; fn install(&self, sender: &Self::Sender) -> Result<(), String> { - sender.configure_receiver(self.as_prometheus_receiver()) + sender.configure_receiver(Box::new(self)) } } +impl PrometheusReceiver for DiscordWebhook { + fn get_prometheus_config( + &self, + ) -> crate::modules::monitoring::prometheus::PrometheusReceiverConfig { + todo!() + } +} #[derive(Debug, Clone, Serialize)] pub struct DiscordWebhookScore { @@ -36,41 +38,10 @@ impl Score for DiscordWebhookScore { fn create_interpret(&self) -> Box> { Box::new(AlertReceiverInterpret { receiver: Box::new(self.config.clone()), - }) + }) } fn name(&self) -> String { todo!() } } - -impl ScrapeTarget for DiscordWebhook { - type Sender = Prometheus; - - fn install(&self, sender: &Self::Sender) { - sender.configure_scrape_target(self.as_prometheus_scrape_target()) - } -} - - -impl DiscordWebhook { - fn as_prometheus_scrape_target(&self) -> PrometheusScrapeTarget { - PrometheusScrapeTarget { definition: todo!() } - } -} - - -impl AlertRule for DiscordWebhook { - type Sender = Prometheus; - - fn install(&self, sender: &Self::Sender) { - sender.configure_rule(self.as_prometheus_rule()) - } -} - - -impl DiscordWebhook { - fn as_prometheus_rule(&self) -> PrometheusRule { - PrometheusRule { definition: todo!() } - } -} diff --git a/harmony/src/modules/monitoring/alert_receiver.rs b/harmony/src/modules/monitoring/alert_receiver.rs index c8a7028..f154373 100644 --- a/harmony/src/modules/monitoring/alert_receiver.rs +++ b/harmony/src/modules/monitoring/alert_receiver.rs @@ -9,9 +9,9 @@ use crate::{ topology::Topology, }; -use super::prometheus::AlertSender; +use super::{monitoring_alerting::CloneBox, prometheus::AlertSender}; -pub trait AlertReceiver: Debug + Send + Sync { +pub trait AlertReceiver: Debug + Send + Sync + CloneBox { type Sender: AlertSender; fn install(&self, sender: &Self::Sender) -> Result<(), String>; @@ -23,16 +23,16 @@ struct AlertReceiverConfig { } #[derive(Debug)] -pub struct AlertReceiverInterpret { - pub receiver: Box>, +pub struct AlertReceiverInterpret { + pub receiver: Box>, } #[async_trait] -impl Interpret for AlertReceiverInterpret { +impl Interpret for AlertReceiverInterpret { async fn execute( &self, - inventory: &Inventory, - topology: &T, + _inventory: &Inventory, + _topology: &T, ) -> Result { todo!() } diff --git a/harmony/src/modules/monitoring/alert_rule.rs b/harmony/src/modules/monitoring/alert_rule.rs index c64014c..d4f197c 100644 --- a/harmony/src/modules/monitoring/alert_rule.rs +++ b/harmony/src/modules/monitoring/alert_rule.rs @@ -2,7 +2,7 @@ use std::fmt::Debug; use super::prometheus::AlertSender; -pub trait AlertRule: Debug { +pub trait AlertRule: Debug + Send + Sync { type Sender: AlertSender; fn install(&self, sender: &Self::Sender); diff --git a/harmony/src/modules/monitoring/monitoring_alerting.rs b/harmony/src/modules/monitoring/monitoring_alerting.rs index d170a93..f815ae0 100644 --- a/harmony/src/modules/monitoring/monitoring_alerting.rs +++ b/harmony/src/modules/monitoring/monitoring_alerting.rs @@ -1,35 +1,41 @@ +use std::sync::Arc; + use async_trait::async_trait; use serde::Serialize; -use serde_value::Value; -use crate::topology::oberservability::monitoring::{AlertChannelConfig, Monitor}; use crate::{ data::{Id, Version}, interpret::{Interpret, InterpretError, InterpretName, InterpretStatus, Outcome}, inventory::Inventory, score::Score, - topology::{HelmCommand, Topology}, + topology::Topology, }; use super::alert_receiver::AlertReceiver; use super::alert_rule::AlertRule; use super::scrape_target::ScrapeTarget; +pub trait MonitoringSystem: std::fmt::Debug + Clone + Serialize + 'static {} -pub trait MonitoringSystem {} - - -#[derive(Debug, Clone, Serialize)] +#[derive(Debug, Clone)] pub struct MonitoringAlertingScore { - alert_receivers: Vec>>, - alert_rules: Vec>>, - scrape_targets: Vec>>, + alert_receivers: Arc>>>, + alert_rules: Arc>>>, + scrape_targets: Arc>>>, } -impl Score for MonitoringAlertingScore { +impl Serialize for MonitoringAlertingScore { + fn serialize(&self, serializer: S) -> Result + where + S: serde::Serializer { + todo!() + } +} + +impl Score for MonitoringAlertingScore { fn create_interpret(&self) -> Box> { Box::new(MonitoringAlertingInterpret { - score: self.clone(), + score: Arc::new(self.clone()), }) } @@ -39,24 +45,18 @@ impl Score for MonitoringAlertingScore { } #[derive(Debug)] -struct MonitoringAlertingInterpret { - score: MonitoringAlertingScore, +struct MonitoringAlertingInterpret { + score: Arc>, } #[async_trait] -impl Interpret for MonitoringAlertingInterpret { +impl Interpret for MonitoringAlertingInterpret { async fn execute( &self, inventory: &Inventory, topology: &T, ) -> Result { - topology - .provision_monitor( - inventory, - topology, - self.score.alert_channel_configs.clone(), - ) - .await + todo!() } fn get_name(&self) -> InterpretName { @@ -75,3 +75,16 @@ impl Interpret for MonitoringAlertingInterpret { 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 270fd84..e1ab33a 100644 --- a/harmony/src/modules/monitoring/prometheus.rs +++ b/harmony/src/modules/monitoring/prometheus.rs @@ -6,24 +6,23 @@ use crate::{ data::{Id, Version}, interpret::{Interpret, InterpretError, InterpretName, InterpretStatus, Outcome}, inventory::Inventory, - score::Score, topology::Topology, }; use std::fmt::Debug; -pub trait AlertSender {} +pub trait AlertSender: std::fmt::Debug {} #[derive(Debug, Clone, Serialize)] pub struct Prometheus {} impl Prometheus { - pub fn configure_receiver(&self, receiver: PrometheusReceiver) -> Result<(), String> { + pub fn configure_receiver(&self, _receiver: Box<&dyn PrometheusReceiver>) -> Result<(), String> { todo!() } - pub fn configure_rule(&self, rule: PrometheusRule) { + pub fn configure_rule(&self, _rule: Box<&dyn PrometheusRule>) { todo!() } - pub fn configure_scrape_target(&self, target: PrometheusScrapeTarget) { + pub fn configure_scrape_target(&self, _target: Box<&dyn PrometheusScrapeTarget>) { todo!() } } @@ -36,7 +35,7 @@ pub trait PrometheusCapability { impl AlertSender for Prometheus {} -pub trait PrometheusReceiver{ +pub trait PrometheusReceiver { fn get_prometheus_config(&self) -> PrometheusReceiverConfig; } @@ -45,7 +44,7 @@ pub struct PrometheusReceiverConfig { // config } -pub trait PrometheusRule{ +pub trait PrometheusRule { fn get_prometheus_config(&self) -> PrometheusRuleConfig; } @@ -74,8 +73,8 @@ pub struct PrometheusMonitoringInterpret {} impl Interpret for PrometheusMonitoringInterpret { async fn execute( &self, - inventory: &Inventory, - topology: &T, + _inventory: &Inventory, + _topology: &T, ) -> Result { todo!() } @@ -96,4 +95,3 @@ impl Interpret for PrometheusMonitoringIn todo!() } } - diff --git a/harmony/src/modules/monitoring/scrape_target.rs b/harmony/src/modules/monitoring/scrape_target.rs index 954bf0c..9ce73c7 100644 --- a/harmony/src/modules/monitoring/scrape_target.rs +++ b/harmony/src/modules/monitoring/scrape_target.rs @@ -2,7 +2,7 @@ use std::fmt::Debug; use super::prometheus::AlertSender; -pub trait ScrapeTarget: Debug { +pub trait ScrapeTarget: Debug + Send + Sync { type Sender: AlertSender; fn install(&self, sender: &Self::Sender); -- 2.39.5 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 06/11] 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); } + -- 2.39.5 From ba536885bdeb5a586609598c7a7d9bedbaf48f4c Mon Sep 17 00:00:00 2001 From: Willem Date: Tue, 17 Jun 2025 16:28:35 -0400 Subject: [PATCH 07/11] wip:installable/installer --- examples/monitoring/src/main.rs | 13 ++--- .../alert_channel/discord_alert_channel.rs | 15 +++--- .../src/modules/monitoring/alert_receiver.rs | 19 ++++---- harmony/src/modules/monitoring/alert_rule.rs | 8 ++-- .../modules/monitoring/monitoring_alerting.rs | 47 +++++++++++++------ harmony/src/modules/monitoring/prometheus.rs | 28 +++++++---- .../src/modules/monitoring/scrape_target.rs | 8 ++-- 7 files changed, 86 insertions(+), 52 deletions(-) diff --git a/examples/monitoring/src/main.rs b/examples/monitoring/src/main.rs index 91ddfd3..18ba474 100644 --- a/examples/monitoring/src/main.rs +++ b/examples/monitoring/src/main.rs @@ -1,6 +1,6 @@ use harmony::{ inventory::Inventory, maestro::Maestro, - modules::monitoring::monitoring_alerting::MonitoringAlertingScore, + modules::monitoring::monitoring_alerting::{MonitoringAlertingScore, MonitoringSystem}, topology::K8sAnywhereTopology, }; @@ -13,10 +13,11 @@ async fn main() { .await .unwrap(); - let monitoring = MonitoringAlertingScore { - alert_channel_configs: None, - }; - - maestro.register_all(vec![Box::new(monitoring)]); + //let monitoring = MonitoringAlertingScore { + // alert_receivers: vec![], + // alert_rules: vec![], + // scrape_targets: vec![], + //}; + //maestro.register_all(vec![Box::new(monitoring)]); 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 6f5b3c8..7556c54 100644 --- a/harmony/src/modules/monitoring/alert_channel/discord_alert_channel.rs +++ b/harmony/src/modules/monitoring/alert_channel/discord_alert_channel.rs @@ -1,3 +1,4 @@ +use log::debug; use serde::Serialize; use crate::{ @@ -14,16 +15,16 @@ pub struct DiscordWebhook { } impl AlertReceiver for DiscordWebhook { - type Sender = Prometheus; + type M = Prometheus; - fn install(&self, sender: &Self::Sender) -> Result<(), String> { - sender.configure_receiver(Box::new(self)) + fn install(&self, sender: &Self::M) -> Result<(), String> { + sender.configure_receiver(Box::new(self))?; + debug!("DiscordWebhook installed for Prometheus"); + Ok(()) } - fn clone(&self) -> Self - where - Self: Sized { - ::clone(self) + fn clone_box(&self) -> Box> { + Box::new(self.clone()) } } diff --git a/harmony/src/modules/monitoring/alert_receiver.rs b/harmony/src/modules/monitoring/alert_receiver.rs index 0943d6e..53251cd 100644 --- a/harmony/src/modules/monitoring/alert_receiver.rs +++ b/harmony/src/modules/monitoring/alert_receiver.rs @@ -9,27 +9,28 @@ use crate::{ topology::Topology, }; -use super::prometheus::AlertSender; +use super::monitoring_alerting::MonitoringSystem; + pub trait AlertReceiver: Debug + Send + Sync { - type Sender: AlertSender; + type M: MonitoringSystem; - fn install(&self, sender: &Self::Sender) -> Result<(), String>; - fn clone_box(&self) -> Box>; + fn install(&self, sender: &Self::M) -> Result<(), String>; + fn clone_box(&self) -> Box>; } -struct AlertReceiverConfig { +struct AlertReceiverConfig { config: String, // Or whatever - sender: S, + sender: M, } #[derive(Debug)] -pub struct AlertReceiverInterpret { - pub receiver: Box>, +pub struct AlertReceiverInterpret { + pub receiver: Box>, } #[async_trait] -impl Interpret for AlertReceiverInterpret { +impl Interpret for AlertReceiverInterpret { async fn execute( &self, _inventory: &Inventory, diff --git a/harmony/src/modules/monitoring/alert_rule.rs b/harmony/src/modules/monitoring/alert_rule.rs index d9fbca3..f6fd535 100644 --- a/harmony/src/modules/monitoring/alert_rule.rs +++ b/harmony/src/modules/monitoring/alert_rule.rs @@ -2,10 +2,12 @@ use std::fmt::Debug; use dyn_clone::DynClone; -use super::prometheus::AlertSender; +use super::monitoring_alerting::MonitoringSystem; + pub trait AlertRule: Debug + Send + Sync + DynClone { - type Sender: AlertSender; + type M: MonitoringSystem; - fn install(&self, sender: &Self::Sender); + fn install(&self, sender: &Self::M); + fn clone_box(&self) -> Box>; } diff --git a/harmony/src/modules/monitoring/monitoring_alerting.rs b/harmony/src/modules/monitoring/monitoring_alerting.rs index b612cba..cbf830e 100644 --- a/harmony/src/modules/monitoring/monitoring_alerting.rs +++ b/harmony/src/modules/monitoring/monitoring_alerting.rs @@ -1,6 +1,7 @@ use std::sync::Arc; use async_trait::async_trait; +use log::debug; use serde::Serialize; use crate::{ @@ -11,37 +12,42 @@ use crate::{ topology::Topology, }; -use super::{alert_receiver::AlertReceiver, prometheus::AlertSender}; +use super::{alert_receiver::AlertReceiver, prometheus::Installable}; use super::alert_rule::AlertRule; use super::scrape_target::ScrapeTarget; -pub trait MonitoringSystem: std::fmt::Debug + Clone + Serialize + 'static {} +#[async_trait] +pub trait MonitoringSystem: std::fmt::Debug + Clone + Serialize + 'static + Send + Sync { +} #[derive(Debug, Clone)] pub struct MonitoringAlertingScore { - pub alert_receivers: Vec>>, - pub alert_rules: Vec>>, - pub scrape_targets: Vec>>, + pub alert_receivers: Vec>>, + pub alert_rules: Vec>>, + pub scrape_targets: Vec>>, } +#[derive(Clone)] +struct MonitoringConfig { + receivers: Vec>>, +} -impl Clone for Box>{ +impl Clone for Box> { fn clone(&self) -> Self { self.clone_box() } } -impl Clone for Box>{ +impl Clone for Box> { fn clone(&self) -> Self { - todo!() + self.clone_box() } } -impl Clone for Box>{ +impl Clone for Box> { fn clone(&self) -> Self { - todo!() + self.clone_box() } } - impl Serialize for MonitoringAlertingScore { fn serialize(&self, serializer: S) -> Result where @@ -51,7 +57,9 @@ impl Serialize for MonitoringAlertingScore { } } -impl Score for MonitoringAlertingScore { +impl Score + for MonitoringAlertingScore +{ fn create_interpret(&self) -> Box> { Box::new(MonitoringAlertingInterpret { score: Arc::new(self.clone()), @@ -68,14 +76,19 @@ struct MonitoringAlertingInterpret { score: Arc>, } + + #[async_trait] -impl Interpret for MonitoringAlertingInterpret { +impl Interpret + for MonitoringAlertingInterpret +{ async fn execute( &self, inventory: &Inventory, topology: &T, ) -> Result { - todo!() + debug!("score {:#?}", self.score); + monitoring_system.install().await } fn get_name(&self) -> InterpretName { @@ -94,3 +107,9 @@ impl Interpret for MonitoringAlertingInterp todo!() } } + +impl MonitoringAlertingInterpret { + fn build_config(&self) -> MonitoringConfig { + todo!() + } +} diff --git a/harmony/src/modules/monitoring/prometheus.rs b/harmony/src/modules/monitoring/prometheus.rs index e6d2e76..b849d5d 100644 --- a/harmony/src/modules/monitoring/prometheus.rs +++ b/harmony/src/modules/monitoring/prometheus.rs @@ -6,22 +6,18 @@ use crate::{ data::{Id, Version}, interpret::{Interpret, InterpretError, InterpretName, InterpretStatus, Outcome}, inventory::Inventory, - topology::Topology, + topology::{HelmCommand, K8sAnywhereTopology, Topology}, }; use std::fmt::Debug; -use super::monitoring_alerting::MonitoringSystem; - -pub trait AlertSender: std::fmt::Debug {} +use super::monitoring_alerting::{MonitoringSystem, MonitoringSystemInstaller}; #[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> { + let receiver_config = receiver.get_prometheus_config(); todo!() } pub fn configure_rule(&self, _rule: Box<&dyn PrometheusRule>) { @@ -40,8 +36,6 @@ pub trait PrometheusCapability { fn install_scrape_targets(&self, receivers: Vec>); } -impl AlertSender for Prometheus {} - pub trait PrometheusReceiver { fn get_prometheus_config(&self) -> PrometheusReceiverConfig; } @@ -102,3 +96,17 @@ impl Interpret for PrometheusMonitoringIn todo!() } } + +#[async_trait] +pub trait Installable { + async fn install(&self) -> Result; + type Installer; +} + +#[async_trait] +impl Installable for Prometheus { + type Installer = K8sAnywhereTopology; + async fn install(&self) -> Result { + todo!() + } +} diff --git a/harmony/src/modules/monitoring/scrape_target.rs b/harmony/src/modules/monitoring/scrape_target.rs index 37d35b7..86e3b2d 100644 --- a/harmony/src/modules/monitoring/scrape_target.rs +++ b/harmony/src/modules/monitoring/scrape_target.rs @@ -2,11 +2,13 @@ use std::fmt::Debug; use dyn_clone::DynClone; -use super::prometheus::AlertSender; +use super::monitoring_alerting::MonitoringSystem; + pub trait ScrapeTarget: Debug + Send + Sync + DynClone { - type Sender: AlertSender; + type M: MonitoringSystem; - fn install(&self, sender: &Self::Sender); + fn install(&self, sender: &Self::M); + fn clone_box(&self) -> Box>; } -- 2.39.5 From 527b88ce3b4fc561936e7b7769dfe3f82ee425f2 Mon Sep 17 00:00:00 2001 From: Jean-Gabriel Gill-Couture Date: Wed, 18 Jun 2025 13:09:11 -0400 Subject: [PATCH 08/11] wip : new architecture test for Alerting, this one binds everything at the Score level. The score itself knows exactly what kind of sender is deployed on what such as Prometheus Sender deployed on Kubernetes. This still allows for extensibility where users can create new scores for new combinations easily, and allows for a generic AlertInterpret that handles reusable core logic to manage alerting concerns within Harmony --- Cargo.lock | 18 ++-- examples/monitoring/Cargo.toml | 2 +- examples/monitoring/src/main.rs | 7 +- harmony/src/domain/topology/k8s_anywhere.rs | 5 -- harmony/src/modules/monitoring/alert_score.rs | 86 +++++++++++++++++++ .../kube_prometheus_helm_chart_score.rs | 3 +- harmony/src/modules/monitoring/mod.rs | 1 + .../modules/monitoring/monitoring_alerting.rs | 28 +++--- harmony/src/modules/monitoring/prometheus.rs | 6 +- 9 files changed, 119 insertions(+), 37 deletions(-) create mode 100644 harmony/src/modules/monitoring/alert_score.rs diff --git a/Cargo.lock b/Cargo.lock index 426f5f9..a9c8654 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1040,6 +1040,15 @@ dependencies = [ "url", ] +[[package]] +name = "example-monitoring" +version = "0.1.0" +dependencies = [ + "harmony", + "harmony_cli", + "tokio", +] + [[package]] name = "example-nanodc" version = "0.1.0" @@ -2387,15 +2396,6 @@ dependencies = [ "windows-sys 0.52.0", ] -[[package]] -name = "monitoring" -version = "0.1.0" -dependencies = [ - "harmony", - "harmony_cli", - "tokio", -] - [[package]] name = "native-tls" version = "0.2.14" diff --git a/examples/monitoring/Cargo.toml b/examples/monitoring/Cargo.toml index 57f291c..af42491 100644 --- a/examples/monitoring/Cargo.toml +++ b/examples/monitoring/Cargo.toml @@ -1,5 +1,5 @@ [package] -name = "monitoring" +name = "example-monitoring" edition = "2024" version.workspace = true readme.workspace = true diff --git a/examples/monitoring/src/main.rs b/examples/monitoring/src/main.rs index 18ba474..3890c11 100644 --- a/examples/monitoring/src/main.rs +++ b/examples/monitoring/src/main.rs @@ -1,11 +1,11 @@ use harmony::{ inventory::Inventory, maestro::Maestro, - modules::monitoring::monitoring_alerting::{MonitoringAlertingScore, MonitoringSystem}, - topology::K8sAnywhereTopology, + modules::monitoring::alert_score::PrometheusAlertingScore, topology::K8sAnywhereTopology, }; #[tokio::main] async fn main() { + let alerting_score = PrometheusAlertingScore { receivers: vec![] }; let mut maestro = Maestro::::initialize( Inventory::autoload(), K8sAnywhereTopology::from_env(), @@ -14,10 +14,11 @@ async fn main() { .unwrap(); //let monitoring = MonitoringAlertingScore { - // alert_receivers: vec![], + // alert_receivers: vec![], // alert_rules: vec![], // scrape_targets: vec![], //}; //maestro.register_all(vec![Box::new(monitoring)]); + maestro.register_all(vec![Box::new(alerting_score)]); harmony_cli::init(maestro, None).await.unwrap(); } diff --git a/harmony/src/domain/topology/k8s_anywhere.rs b/harmony/src/domain/topology/k8s_anywhere.rs index c143805..6742b5a 100644 --- a/harmony/src/domain/topology/k8s_anywhere.rs +++ b/harmony/src/domain/topology/k8s_anywhere.rs @@ -17,11 +17,6 @@ use crate::{ use super::{ HelmCommand, K8sclient, Topology, k8s::K8sClient, - oberservability::{ - K8sMonitorConfig, - k8s::K8sMonitor, - monitoring::{AlertChannelConfig, Monitor}, - }, tenant::{TenantConfig, TenantManager, k8s::K8sTenantManager}, }; diff --git a/harmony/src/modules/monitoring/alert_score.rs b/harmony/src/modules/monitoring/alert_score.rs new file mode 100644 index 0000000..1971e55 --- /dev/null +++ b/harmony/src/modules/monitoring/alert_score.rs @@ -0,0 +1,86 @@ +use async_trait::async_trait; +use serde::Serialize; + +use crate::{ + data::{Id, Version}, interpret::{Interpret, InterpretError, InterpretName, InterpretStatus, Outcome}, inventory::Inventory, score::Score, topology::{HelmCommand, Topology} +}; + +#[async_trait] +pub trait Installable { + async fn ensure_installed(&self) -> Result<(), InterpretError>; +} + +pub trait AlertSender: Send + Sync + std::fmt::Debug + Installable {} + +pub trait AlertReceiver: std::fmt::Debug + Send + Sync { + fn install(&self, sender: &S) -> Result<(), InterpretError>; +} + +#[derive(Clone, Debug, Serialize)] +pub struct PrometheusAlertingScore { + pub receivers: Vec>>, +} + +impl Serialize for Box> { + fn serialize(&self, _serializer: S) -> Result + where + S: serde::Serializer, + { + todo!() + } +} +impl Clone for Box> { + fn clone(&self) -> Self { + todo!() + } +} + +impl Score for PrometheusAlertingScore { + fn create_interpret(&self) -> Box> { + Box::new(AlertingInterpret { sender: Prometheus {}, receivers: todo!() }) + } + + fn name(&self) -> String { + "PrometheusAlertingScore".to_string() + } +} + +pub struct Prometheus; + +#[derive(Debug)] +pub struct AlertingInterpret { + pub sender: S, + pub receivers: Vec>>, +} + +#[async_trait] +impl Interpret for AlertingInterpret { + #[must_use] + async fn execute( + &self, + inventory: &Inventory, + topology: &T, + ) -> Result { + + self.receivers.iter().try_for_each(|r| { + r.install(&self.sender) + })?; + todo!(); + } + + fn get_name(&self) -> InterpretName { + todo!() + } + + fn get_version(&self) -> Version { + todo!() + } + + fn get_status(&self) -> InterpretStatus { + todo!() + } + + fn get_children(&self) -> Vec { + todo!() + } +} diff --git a/harmony/src/modules/monitoring/kube_prometheus/kube_prometheus_helm_chart_score.rs b/harmony/src/modules/monitoring/kube_prometheus/kube_prometheus_helm_chart_score.rs index 14680d9..2377627 100644 --- a/harmony/src/modules/monitoring/kube_prometheus/kube_prometheus_helm_chart_score.rs +++ b/harmony/src/modules/monitoring/kube_prometheus/kube_prometheus_helm_chart_score.rs @@ -1,5 +1,4 @@ use super::config::KubePrometheusConfig; -use log::info; use non_blank_string_rs::NonBlankString; use std::str::FromStr; @@ -26,7 +25,7 @@ pub fn kube_prometheus_helm_chart_score() -> HelmChartScore { let node_exporter = config.node_exporter.to_string(); let prometheus_operator = config.prometheus_operator.to_string(); let prometheus = config.prometheus.to_string(); - let mut values = format!( + let values = format!( r#" additionalPrometheusRulesMap: pods-status-alerts: diff --git a/harmony/src/modules/monitoring/mod.rs b/harmony/src/modules/monitoring/mod.rs index 270ee39..5f5fc19 100644 --- a/harmony/src/modules/monitoring/mod.rs +++ b/harmony/src/modules/monitoring/mod.rs @@ -5,3 +5,4 @@ pub mod kube_prometheus; pub mod monitoring_alerting; pub mod prometheus; pub mod scrape_target; +pub mod alert_score; diff --git a/harmony/src/modules/monitoring/monitoring_alerting.rs b/harmony/src/modules/monitoring/monitoring_alerting.rs index cbf830e..a847a04 100644 --- a/harmony/src/modules/monitoring/monitoring_alerting.rs +++ b/harmony/src/modules/monitoring/monitoring_alerting.rs @@ -27,11 +27,6 @@ pub struct MonitoringAlertingScore { pub scrape_targets: Vec>>, } -#[derive(Clone)] -struct MonitoringConfig { - receivers: Vec>>, -} - impl Clone for Box> { fn clone(&self) -> Self { self.clone_box() @@ -49,7 +44,7 @@ impl Clone for Box> { } impl Serialize for MonitoringAlertingScore { - fn serialize(&self, serializer: S) -> Result + fn serialize(&self, _serializer: S) -> Result where S: serde::Serializer, { @@ -84,11 +79,21 @@ impl Interpret { async fn execute( &self, - inventory: &Inventory, - topology: &T, + _inventory: &Inventory, + _topology: &T, ) -> Result { debug!("score {:#?}", self.score); - monitoring_system.install().await + todo!("Figure out a clean way to have things come together here : + We want Prometheus to be bound like this : + + DiscordWebhook (AlertReceiver) + | + Prometheus (AlertSender + Scraper + Installable) + ^ + MonitoringScore {{ alert_rules, alert_receivers, scrape_endpoints }} + (Interpret InterpretName { @@ -108,8 +113,3 @@ impl Interpret } } -impl MonitoringAlertingInterpret { - fn build_config(&self) -> MonitoringConfig { - todo!() - } -} diff --git a/harmony/src/modules/monitoring/prometheus.rs b/harmony/src/modules/monitoring/prometheus.rs index b849d5d..4849a98 100644 --- a/harmony/src/modules/monitoring/prometheus.rs +++ b/harmony/src/modules/monitoring/prometheus.rs @@ -6,18 +6,18 @@ use crate::{ data::{Id, Version}, interpret::{Interpret, InterpretError, InterpretName, InterpretStatus, Outcome}, inventory::Inventory, - topology::{HelmCommand, K8sAnywhereTopology, Topology}, + topology::{K8sAnywhereTopology, Topology}, }; use std::fmt::Debug; -use super::monitoring_alerting::{MonitoringSystem, MonitoringSystemInstaller}; +use super::monitoring_alerting::MonitoringSystem; #[derive(Debug, Clone, Serialize)] pub struct Prometheus {} impl Prometheus { pub fn configure_receiver(&self, receiver: Box<&dyn PrometheusReceiver>) -> Result<(), String> { - let receiver_config = receiver.get_prometheus_config(); + let _receiver_config = receiver.get_prometheus_config(); todo!() } pub fn configure_rule(&self, _rule: Box<&dyn PrometheusRule>) { -- 2.39.5 From 3ad8cd60f8f9794ecc74234172bdb190d96d47b0 Mon Sep 17 00:00:00 2001 From: Willem Date: Wed, 18 Jun 2025 16:12:29 -0400 Subject: [PATCH 09/11] WIP: working on implementation of monitoring alert score for HelmKubePrometheus --- examples/monitoring/src/main.rs | 4 +- harmony/src/modules/monitoring/alert_score.rs | 86 ++++++++++++++++--- 2 files changed, 76 insertions(+), 14 deletions(-) diff --git a/examples/monitoring/src/main.rs b/examples/monitoring/src/main.rs index 3890c11..cccc999 100644 --- a/examples/monitoring/src/main.rs +++ b/examples/monitoring/src/main.rs @@ -1,11 +1,11 @@ use harmony::{ inventory::Inventory, maestro::Maestro, - modules::monitoring::alert_score::PrometheusAlertingScore, topology::K8sAnywhereTopology, + modules::monitoring::alert_score::HelmPrometheusAlertingScore, topology::K8sAnywhereTopology, }; #[tokio::main] async fn main() { - let alerting_score = PrometheusAlertingScore { receivers: vec![] }; + let alerting_score = HelmPrometheusAlertingScore { receivers: vec![] }; let mut maestro = Maestro::::initialize( Inventory::autoload(), K8sAnywhereTopology::from_env(), diff --git a/harmony/src/modules/monitoring/alert_score.rs b/harmony/src/modules/monitoring/alert_score.rs index 1971e55..6197128 100644 --- a/harmony/src/modules/monitoring/alert_score.rs +++ b/harmony/src/modules/monitoring/alert_score.rs @@ -1,8 +1,12 @@ use async_trait::async_trait; -use serde::Serialize; +use serde::{Serializer, Serialize}; use crate::{ - data::{Id, Version}, interpret::{Interpret, InterpretError, InterpretName, InterpretStatus, Outcome}, inventory::Inventory, score::Score, topology::{HelmCommand, Topology} + data::{Id, Version}, + interpret::{Interpret, InterpretError, InterpretName, InterpretStatus, Outcome}, + inventory::Inventory, + score::Score, + topology::{HelmCommand, Topology, Url}, }; #[async_trait] @@ -12,12 +16,29 @@ pub trait Installable { pub trait AlertSender: Send + Sync + std::fmt::Debug + Installable {} +#[async_trait] pub trait AlertReceiver: std::fmt::Debug + Send + Sync { - fn install(&self, sender: &S) -> Result<(), InterpretError>; + async fn install(&self, sender: &S) -> Result<(), InterpretError>; +} + +#[derive(Debug)] +pub struct DiscordWebhook { + name: String, + url: Url, +} + +#[async_trait] +impl AlertReceiver for DiscordWebhook { + async fn install(&self, sender: &Prometheus) -> Result<(), InterpretError> { + sender + .install_receiver(PrometheusReceiver { + }) + .await + } } #[derive(Clone, Debug, Serialize)] -pub struct PrometheusAlertingScore { +pub struct HelmPrometheusAlertingScore { pub receivers: Vec>>, } @@ -35,18 +56,60 @@ impl Clone for Box> { } } -impl Score for PrometheusAlertingScore { +impl Score for HelmPrometheusAlertingScore { fn create_interpret(&self) -> Box> { - Box::new(AlertingInterpret { sender: Prometheus {}, receivers: todo!() }) + Box::new(AlertingInterpret { + sender: Prometheus {}, + receivers: vec![Box::new(DiscordWebhook {url:todo!(), name: todo!() })], + }) } fn name(&self) -> String { - "PrometheusAlertingScore".to_string() + "HelmPrometheusAlertingScore".to_string() } } +#[derive(Debug)] pub struct Prometheus; +impl Prometheus { + async fn install_receiver( + &self, + prometheus_receiver: PrometheusReceiver, + ) -> Result<(), InterpretError> { + todo!() + } +} + +pub struct PrometheusReceiver { +} + +impl PrometheusReceiver { + fn get_prometheus_receiver_config(&self) {} +} + +pub struct AlertChannelGlobalConfig {} + +#[derive(Serialize)] +pub struct AlertReceiverRoute { + pub receiver: String, + pub matchers: Vec, + #[serde(default)] + pub r#continue: bool, +} +pub struct AlertChannelReceiver { + pub name: String, +} + +impl AlertSender for Prometheus {} + +#[async_trait] +impl Installable for Prometheus { + async fn ensure_installed(&self) -> Result<(), InterpretError> { + todo!() + } +} + #[derive(Debug)] pub struct AlertingInterpret { pub sender: S, @@ -61,11 +124,10 @@ impl Interpret for AlertingInterpret { inventory: &Inventory, topology: &T, ) -> Result { - - self.receivers.iter().try_for_each(|r| { - r.install(&self.sender) - })?; - todo!(); + for receiver in self.receivers.iter() { + receiver.install(&self.sender).await?; + } + todo!() } fn get_name(&self) -> InterpretName { -- 2.39.5 From 1771f47eca86a1fee8afae8efc5ff3cf8681061a Mon Sep 17 00:00:00 2001 From: Willem Date: Wed, 18 Jun 2025 16:54:28 -0400 Subject: [PATCH 10/11] chore: clean up unused old code, organized traits, impls, etc into logical locations --- examples/lamp/src/main.rs | 24 ++- examples/monitoring/src/main.rs | 3 +- harmony/src/domain/topology/installable.rs | 8 + harmony/src/domain/topology/mod.rs | 1 + .../domain/topology/oberservability/k8s.rs | 71 --------- .../domain/topology/oberservability/mod.rs | 22 --- .../topology/oberservability/monitoring.rs | 66 +++++--- .../alert_channel/discord_alert_channel.rs | 54 ++----- .../src/modules/monitoring/alert_receiver.rs | 57 ------- harmony/src/modules/monitoring/alert_rule.rs | 13 -- harmony/src/modules/monitoring/alert_score.rs | 148 ------------------ .../kube_prometheus/{ => helm}/config.rs | 4 - .../kube_prometheus_helm_chart.rs} | 0 .../monitoring/kube_prometheus/helm/mod.rs | 2 + .../helm_prometheus_alert_score.rs | 47 ++++++ .../modules/monitoring/kube_prometheus/mod.rs | 5 +- .../monitoring/kube_prometheus/prometheus.rs | 34 ++++ .../monitoring/kube_prometheus/types.rs | 20 ++- harmony/src/modules/monitoring/mod.rs | 6 - .../modules/monitoring/monitoring_alerting.rs | 115 -------------- harmony/src/modules/monitoring/prometheus.rs | 112 ------------- .../src/modules/monitoring/scrape_target.rs | 14 -- 22 files changed, 170 insertions(+), 656 deletions(-) create mode 100644 harmony/src/domain/topology/installable.rs delete mode 100644 harmony/src/domain/topology/oberservability/k8s.rs delete mode 100644 harmony/src/modules/monitoring/alert_receiver.rs delete mode 100644 harmony/src/modules/monitoring/alert_rule.rs delete mode 100644 harmony/src/modules/monitoring/alert_score.rs rename harmony/src/modules/monitoring/kube_prometheus/{ => helm}/config.rs (90%) rename harmony/src/modules/monitoring/kube_prometheus/{kube_prometheus_helm_chart_score.rs => helm/kube_prometheus_helm_chart.rs} (100%) create mode 100644 harmony/src/modules/monitoring/kube_prometheus/helm/mod.rs create mode 100644 harmony/src/modules/monitoring/kube_prometheus/helm_prometheus_alert_score.rs create mode 100644 harmony/src/modules/monitoring/kube_prometheus/prometheus.rs delete mode 100644 harmony/src/modules/monitoring/monitoring_alerting.rs delete mode 100644 harmony/src/modules/monitoring/prometheus.rs delete mode 100644 harmony/src/modules/monitoring/scrape_target.rs diff --git a/examples/lamp/src/main.rs b/examples/lamp/src/main.rs index 37fd0f9..0b1f93c 100644 --- a/examples/lamp/src/main.rs +++ b/examples/lamp/src/main.rs @@ -4,10 +4,7 @@ use harmony::{ maestro::Maestro, modules::{ lamp::{LAMPConfig, LAMPScore}, - monitoring::{ - alert_channel::discord_alert_channel::DiscordWebhook, - monitoring_alerting::MonitoringAlertingScore, - }, + monitoring::alert_channel::discord_alert_channel::DiscordWebhook, }, topology::{K8sAnywhereTopology, Url}, }; @@ -35,15 +32,15 @@ async fn main() { }, }; - 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![], - }; + //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 @@ -57,7 +54,6 @@ async fn main() { .unwrap(); // 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/examples/monitoring/src/main.rs b/examples/monitoring/src/main.rs index cccc999..d52c649 100644 --- a/examples/monitoring/src/main.rs +++ b/examples/monitoring/src/main.rs @@ -1,6 +1,7 @@ use harmony::{ inventory::Inventory, maestro::Maestro, - modules::monitoring::alert_score::HelmPrometheusAlertingScore, topology::K8sAnywhereTopology, + modules::monitoring::kube_prometheus::helm_prometheus_alert_score::HelmPrometheusAlertingScore, + topology::K8sAnywhereTopology, }; #[tokio::main] diff --git a/harmony/src/domain/topology/installable.rs b/harmony/src/domain/topology/installable.rs new file mode 100644 index 0000000..9b9054f --- /dev/null +++ b/harmony/src/domain/topology/installable.rs @@ -0,0 +1,8 @@ +use async_trait::async_trait; + +use crate::interpret::InterpretError; + +#[async_trait] +pub trait Installable { + async fn ensure_installed(&self) -> Result<(), InterpretError>; +} diff --git a/harmony/src/domain/topology/mod.rs b/harmony/src/domain/topology/mod.rs index faa7fee..7d3830d 100644 --- a/harmony/src/domain/topology/mod.rs +++ b/harmony/src/domain/topology/mod.rs @@ -1,6 +1,7 @@ mod ha_cluster; mod host_binding; mod http; +pub mod installable; mod k8s_anywhere; mod localhost; pub mod oberservability; diff --git a/harmony/src/domain/topology/oberservability/k8s.rs b/harmony/src/domain/topology/oberservability/k8s.rs deleted file mode 100644 index 004f9ec..0000000 --- a/harmony/src/domain/topology/oberservability/k8s.rs +++ /dev/null @@ -1,71 +0,0 @@ -use std::sync::Arc; - -use async_trait::async_trait; -use serde::Serialize; - -use crate::score::Score; - -use crate::topology::HelmCommand; -use crate::{ - interpret::{InterpretError, Outcome}, - inventory::Inventory, - topology::Topology, -}; - -use super::{ - K8sMonitorConfig, - monitoring::{AlertChannel, AlertChannelConfig, Monitor}, -}; - -#[derive(Debug, Clone, Serialize)] -pub struct K8sMonitor { - pub config: K8sMonitorConfig, -} - -#[async_trait] -impl Monitor for K8sMonitor { - async fn provision_monitor( - &self, - inventory: &Inventory, - topology: &T, - alert_channels: Option>>, - ) -> Result { - if let Some(channels) = alert_channels { - let alert_channels = self.build_alert_channels(channels).await?; - for channel in alert_channels { - channel.register_alert_channel().await?; - } - } - let chart = self.config.chart.clone(); - chart - .create_interpret() - .execute(inventory, topology) - .await?; - Ok(Outcome::success("installed monitor".to_string())) - } - - fn delete_monitor(&self) -> Result { - todo!() - } -} - -#[async_trait] -impl AlertChannelConfig for K8sMonitor { - async fn build_alert_channel(&self) -> Result, InterpretError> { - todo!() - } -} - -impl K8sMonitor { - pub async fn build_alert_channels( - &self, - alert_channel_configs: Vec>, - ) -> Result>, InterpretError> { - let mut alert_channels = Vec::new(); - for config in alert_channel_configs { - let channel = config.build_alert_channel().await?; - alert_channels.push(channel) - } - Ok(alert_channels) - } -} diff --git a/harmony/src/domain/topology/oberservability/mod.rs b/harmony/src/domain/topology/oberservability/mod.rs index 387709a..7f2ac95 100644 --- a/harmony/src/domain/topology/oberservability/mod.rs +++ b/harmony/src/domain/topology/oberservability/mod.rs @@ -1,23 +1 @@ -use serde::Serialize; - -use crate::modules::{ - helm::chart::HelmChartScore, - monitoring::kube_prometheus::kube_prometheus_helm_chart_score::kube_prometheus_helm_chart_score, -}; - -pub mod k8s; pub mod monitoring; - -#[derive(Debug, Clone, Serialize)] -pub struct K8sMonitorConfig { - //probably need to do something better here - pub chart: HelmChartScore, -} - -impl K8sMonitorConfig { - pub fn cluster_monitor() -> Self { - Self { - chart: kube_prometheus_helm_chart_score(), - } - } -} diff --git a/harmony/src/domain/topology/oberservability/monitoring.rs b/harmony/src/domain/topology/oberservability/monitoring.rs index 1d6a159..a3a6164 100644 --- a/harmony/src/domain/topology/oberservability/monitoring.rs +++ b/harmony/src/domain/topology/oberservability/monitoring.rs @@ -1,39 +1,61 @@ use async_trait::async_trait; -use dyn_clone::DynClone; -use std::fmt::Debug; -use crate::executors::ExecutorError; -use crate::interpret::InterpretError; +use crate::{ + data::{Id, Version}, + interpret::{Interpret, InterpretError, InterpretName, InterpretStatus, Outcome}, + inventory::Inventory, + topology::{Topology, installable::Installable}, +}; -use crate::inventory::Inventory; -use crate::topology::HelmCommand; -use crate::{interpret::Outcome, topology::Topology}; +pub trait AlertSender: Send + Sync + std::fmt::Debug + Installable {} + +#[derive(Debug)] +pub struct AlertingInterpret { + pub sender: S, + pub receivers: Vec>>, +} -/// Represents an entity responsible for collecting and organizing observability data -/// from various telemetry sources such as Prometheus or Datadog -/// A `Monitor` abstracts the logic required to scrape, aggregate, and structure -/// monitoring data, enabling consistent processing regardless of the underlying data source. #[async_trait] -pub trait Monitor { - async fn provision_monitor( +impl Interpret for AlertingInterpret { + async fn execute( &self, inventory: &Inventory, topology: &T, - alert_receivers: Option>>, - ) -> Result; + ) -> Result { + for receiver in self.receivers.iter() { + receiver.install(&self.sender).await?; + } + todo!() + } - fn delete_monitor(&self) -> Result; + fn get_name(&self) -> InterpretName { + todo!() + } + + fn get_version(&self) -> Version { + todo!() + } + + fn get_status(&self) -> InterpretStatus { + todo!() + } + + fn get_children(&self) -> Vec { + todo!() + } } #[async_trait] -pub trait AlertChannel: Debug + Send + Sync { - async fn register_alert_channel(&self) -> Result; - //async fn get_channel_id(&self) -> String; +pub trait AlertReceiver: std::fmt::Debug + Send + Sync { + async fn install(&self, sender: &S) -> Result<(), InterpretError>; } #[async_trait] -pub trait AlertChannelConfig: Debug + Send + Sync + DynClone { - async fn build_alert_channel(&self) -> Result, InterpretError>; +pub trait AlertRule { + async fn install(&self, sender: &S) -> Result<(), InterpretError>; } -dyn_clone::clone_trait_object!(AlertChannelConfig); +#[async_trait] +pub trait ScrapeTarger { + async fn install(&self, sender: &S) -> Result<(), InterpretError>; +} 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 7556c54..42f4450 100644 --- a/harmony/src/modules/monitoring/alert_channel/discord_alert_channel.rs +++ b/harmony/src/modules/monitoring/alert_channel/discord_alert_channel.rs @@ -1,54 +1,20 @@ -use log::debug; -use serde::Serialize; +use async_trait::async_trait; use crate::{ - modules::monitoring::{ - alert_receiver::AlertReceiver, - prometheus::{Prometheus, PrometheusReceiver}, - }, - topology::Url, + interpret::InterpretError, + modules::monitoring::kube_prometheus::prometheus::{Prometheus, PrometheusReceiver}, + topology::{Url, oberservability::monitoring::AlertReceiver}, }; -#[derive(Debug, Clone, Serialize)] +#[derive(Debug)] pub struct DiscordWebhook { + pub name: String, pub url: Url, } -impl AlertReceiver for DiscordWebhook { - type M = Prometheus; - - fn install(&self, sender: &Self::M) -> Result<(), String> { - sender.configure_receiver(Box::new(self))?; - debug!("DiscordWebhook installed for Prometheus"); - Ok(()) - } - - fn clone_box(&self) -> Box> { - Box::new(self.clone()) +#[async_trait] +impl AlertReceiver for DiscordWebhook { + async fn install(&self, sender: &Prometheus) -> Result<(), InterpretError> { + sender.install_receiver(PrometheusReceiver {}).await } } - -impl PrometheusReceiver for DiscordWebhook { - fn get_prometheus_config( - &self, - ) -> crate::modules::monitoring::prometheus::PrometheusReceiverConfig { - 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 deleted file mode 100644 index 53251cd..0000000 --- a/harmony/src/modules/monitoring/alert_receiver.rs +++ /dev/null @@ -1,57 +0,0 @@ -use std::fmt::Debug; - -use async_trait::async_trait; - -use crate::{ - data::{Id, Version}, - interpret::{Interpret, InterpretError, InterpretName, InterpretStatus, Outcome}, - inventory::Inventory, - topology::Topology, -}; - -use super::monitoring_alerting::MonitoringSystem; - - -pub trait AlertReceiver: Debug + Send + Sync { - type M: MonitoringSystem; - - fn install(&self, sender: &Self::M) -> Result<(), String>; - fn clone_box(&self) -> Box>; -} - -struct AlertReceiverConfig { - config: String, // Or whatever - sender: M, -} - -#[derive(Debug)] -pub struct AlertReceiverInterpret { - pub receiver: Box>, -} - -#[async_trait] -impl Interpret for AlertReceiverInterpret { - async fn execute( - &self, - _inventory: &Inventory, - _topology: &T, - ) -> Result { - todo!() - } - - fn get_name(&self) -> InterpretName { - todo!() - } - - fn get_version(&self) -> Version { - todo!() - } - - fn get_status(&self) -> InterpretStatus { - todo!() - } - - fn get_children(&self) -> Vec { - todo!() - } -} diff --git a/harmony/src/modules/monitoring/alert_rule.rs b/harmony/src/modules/monitoring/alert_rule.rs deleted file mode 100644 index f6fd535..0000000 --- a/harmony/src/modules/monitoring/alert_rule.rs +++ /dev/null @@ -1,13 +0,0 @@ -use std::fmt::Debug; - -use dyn_clone::DynClone; - -use super::monitoring_alerting::MonitoringSystem; - - -pub trait AlertRule: Debug + Send + Sync + DynClone { - type M: MonitoringSystem; - - fn install(&self, sender: &Self::M); - fn clone_box(&self) -> Box>; -} diff --git a/harmony/src/modules/monitoring/alert_score.rs b/harmony/src/modules/monitoring/alert_score.rs deleted file mode 100644 index 6197128..0000000 --- a/harmony/src/modules/monitoring/alert_score.rs +++ /dev/null @@ -1,148 +0,0 @@ -use async_trait::async_trait; -use serde::{Serializer, Serialize}; - -use crate::{ - data::{Id, Version}, - interpret::{Interpret, InterpretError, InterpretName, InterpretStatus, Outcome}, - inventory::Inventory, - score::Score, - topology::{HelmCommand, Topology, Url}, -}; - -#[async_trait] -pub trait Installable { - async fn ensure_installed(&self) -> Result<(), InterpretError>; -} - -pub trait AlertSender: Send + Sync + std::fmt::Debug + Installable {} - -#[async_trait] -pub trait AlertReceiver: std::fmt::Debug + Send + Sync { - async fn install(&self, sender: &S) -> Result<(), InterpretError>; -} - -#[derive(Debug)] -pub struct DiscordWebhook { - name: String, - url: Url, -} - -#[async_trait] -impl AlertReceiver for DiscordWebhook { - async fn install(&self, sender: &Prometheus) -> Result<(), InterpretError> { - sender - .install_receiver(PrometheusReceiver { - }) - .await - } -} - -#[derive(Clone, Debug, Serialize)] -pub struct HelmPrometheusAlertingScore { - pub receivers: Vec>>, -} - -impl Serialize for Box> { - fn serialize(&self, _serializer: S) -> Result - where - S: serde::Serializer, - { - todo!() - } -} -impl Clone for Box> { - fn clone(&self) -> Self { - todo!() - } -} - -impl Score for HelmPrometheusAlertingScore { - fn create_interpret(&self) -> Box> { - Box::new(AlertingInterpret { - sender: Prometheus {}, - receivers: vec![Box::new(DiscordWebhook {url:todo!(), name: todo!() })], - }) - } - - fn name(&self) -> String { - "HelmPrometheusAlertingScore".to_string() - } -} - -#[derive(Debug)] -pub struct Prometheus; - -impl Prometheus { - async fn install_receiver( - &self, - prometheus_receiver: PrometheusReceiver, - ) -> Result<(), InterpretError> { - todo!() - } -} - -pub struct PrometheusReceiver { -} - -impl PrometheusReceiver { - fn get_prometheus_receiver_config(&self) {} -} - -pub struct AlertChannelGlobalConfig {} - -#[derive(Serialize)] -pub struct AlertReceiverRoute { - pub receiver: String, - pub matchers: Vec, - #[serde(default)] - pub r#continue: bool, -} -pub struct AlertChannelReceiver { - pub name: String, -} - -impl AlertSender for Prometheus {} - -#[async_trait] -impl Installable for Prometheus { - async fn ensure_installed(&self) -> Result<(), InterpretError> { - todo!() - } -} - -#[derive(Debug)] -pub struct AlertingInterpret { - pub sender: S, - pub receivers: Vec>>, -} - -#[async_trait] -impl Interpret for AlertingInterpret { - #[must_use] - async fn execute( - &self, - inventory: &Inventory, - topology: &T, - ) -> Result { - for receiver in self.receivers.iter() { - receiver.install(&self.sender).await?; - } - todo!() - } - - fn get_name(&self) -> InterpretName { - todo!() - } - - fn get_version(&self) -> Version { - todo!() - } - - fn get_status(&self) -> InterpretStatus { - todo!() - } - - fn get_children(&self) -> Vec { - todo!() - } -} diff --git a/harmony/src/modules/monitoring/kube_prometheus/config.rs b/harmony/src/modules/monitoring/kube_prometheus/helm/config.rs similarity index 90% rename from harmony/src/modules/monitoring/kube_prometheus/config.rs rename to harmony/src/modules/monitoring/kube_prometheus/helm/config.rs index 74fdf6f..0e62c0f 100644 --- a/harmony/src/modules/monitoring/kube_prometheus/config.rs +++ b/harmony/src/modules/monitoring/kube_prometheus/helm/config.rs @@ -1,7 +1,5 @@ use serde::Serialize; -use super::types::AlertManagerChannelConfig; - #[derive(Debug, Clone, Serialize)] pub struct KubePrometheusConfig { pub namespace: String, @@ -21,7 +19,6 @@ pub struct KubePrometheusConfig { pub kube_proxy: bool, pub kube_state_metrics: bool, pub prometheus_operator: bool, - pub alert_channels: Vec, } impl KubePrometheusConfig { pub fn new() -> Self { @@ -43,7 +40,6 @@ impl KubePrometheusConfig { prometheus_operator: true, core_dns: false, kube_scheduler: false, - alert_channels: Vec::new(), } } } diff --git a/harmony/src/modules/monitoring/kube_prometheus/kube_prometheus_helm_chart_score.rs b/harmony/src/modules/monitoring/kube_prometheus/helm/kube_prometheus_helm_chart.rs similarity index 100% rename from harmony/src/modules/monitoring/kube_prometheus/kube_prometheus_helm_chart_score.rs rename to harmony/src/modules/monitoring/kube_prometheus/helm/kube_prometheus_helm_chart.rs diff --git a/harmony/src/modules/monitoring/kube_prometheus/helm/mod.rs b/harmony/src/modules/monitoring/kube_prometheus/helm/mod.rs new file mode 100644 index 0000000..4b07750 --- /dev/null +++ b/harmony/src/modules/monitoring/kube_prometheus/helm/mod.rs @@ -0,0 +1,2 @@ +pub mod config; +pub mod kube_prometheus_helm_chart; diff --git a/harmony/src/modules/monitoring/kube_prometheus/helm_prometheus_alert_score.rs b/harmony/src/modules/monitoring/kube_prometheus/helm_prometheus_alert_score.rs new file mode 100644 index 0000000..c090f13 --- /dev/null +++ b/harmony/src/modules/monitoring/kube_prometheus/helm_prometheus_alert_score.rs @@ -0,0 +1,47 @@ +use serde::Serialize; + +use crate::{ + modules::monitoring::alert_channel::discord_alert_channel::DiscordWebhook, + score::Score, + topology::{ + HelmCommand, Topology, + oberservability::monitoring::{AlertReceiver, AlertingInterpret}, + }, +}; + +use super::prometheus::Prometheus; + +#[derive(Clone, Debug, Serialize)] +pub struct HelmPrometheusAlertingScore { + pub receivers: Vec>>, +} + +impl Score for HelmPrometheusAlertingScore { + fn create_interpret(&self) -> Box> { + Box::new(AlertingInterpret { + sender: Prometheus {}, + receivers: vec![Box::new(DiscordWebhook { + url: todo!(), + name: todo!(), + })], + }) + } + + fn name(&self) -> String { + "HelmPrometheusAlertingScore".to_string() + } +} + +impl Serialize for Box> { + fn serialize(&self, _serializer: S) -> Result + where + S: serde::Serializer, + { + todo!() + } +} +impl Clone for Box> { + fn clone(&self) -> Self { + todo!() + } +} diff --git a/harmony/src/modules/monitoring/kube_prometheus/mod.rs b/harmony/src/modules/monitoring/kube_prometheus/mod.rs index 8ad49f0..7c8233a 100644 --- a/harmony/src/modules/monitoring/kube_prometheus/mod.rs +++ b/harmony/src/modules/monitoring/kube_prometheus/mod.rs @@ -1,3 +1,4 @@ -pub mod config; -pub mod kube_prometheus_helm_chart_score; +pub mod helm; +pub mod helm_prometheus_alert_score; +pub mod prometheus; pub mod types; diff --git a/harmony/src/modules/monitoring/kube_prometheus/prometheus.rs b/harmony/src/modules/monitoring/kube_prometheus/prometheus.rs new file mode 100644 index 0000000..c5be07e --- /dev/null +++ b/harmony/src/modules/monitoring/kube_prometheus/prometheus.rs @@ -0,0 +1,34 @@ +use async_trait::async_trait; + +use crate::{ + interpret::InterpretError, + topology::{installable::Installable, oberservability::monitoring::AlertSender}, +}; + +impl AlertSender for Prometheus {} + +#[async_trait] +impl Installable for Prometheus { + async fn ensure_installed(&self) -> Result<(), InterpretError> { + todo!() + } +} +#[derive(Debug)] +pub struct Prometheus; + +impl Prometheus { + pub async fn install_receiver( + &self, + prometheus_receiver: PrometheusReceiver, + ) -> Result<(), InterpretError> { + todo!() + } +} + +pub struct PrometheusReceiver {} + +impl PrometheusReceiver { + fn get_prometheus_receiver_config(&self) {} +} + +pub struct AlertChannelGlobalConfig {} diff --git a/harmony/src/modules/monitoring/kube_prometheus/types.rs b/harmony/src/modules/monitoring/kube_prometheus/types.rs index fa66f99..224b125 100644 --- a/harmony/src/modules/monitoring/kube_prometheus/types.rs +++ b/harmony/src/modules/monitoring/kube_prometheus/types.rs @@ -1,14 +1,12 @@ use serde::Serialize; -#[derive(Debug, Clone, Serialize)] -pub struct AlertManagerChannelConfig { - pub global_configs: AlertManagerChannelGlobalConfigs, - pub route: AlertManagerChannelRoute, - pub receiver: AlertManagerChannelReceiver, +#[derive(Serialize)] +pub struct AlertReceiverRoute { + pub receiver: String, + pub matchers: Vec, + #[serde(default)] + pub r#continue: bool, +} +pub struct AlertChannelReceiver { + pub name: String, } -#[derive(Debug, Clone, Serialize)] -pub struct AlertManagerChannelGlobalConfigs {} -#[derive(Debug, Clone, Serialize)] -pub struct AlertManagerChannelReceiver {} -#[derive(Debug, Clone, Serialize)] -pub struct AlertManagerChannelRoute {} diff --git a/harmony/src/modules/monitoring/mod.rs b/harmony/src/modules/monitoring/mod.rs index 5f5fc19..7cdb3a9 100644 --- a/harmony/src/modules/monitoring/mod.rs +++ b/harmony/src/modules/monitoring/mod.rs @@ -1,8 +1,2 @@ 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 scrape_target; -pub mod alert_score; diff --git a/harmony/src/modules/monitoring/monitoring_alerting.rs b/harmony/src/modules/monitoring/monitoring_alerting.rs deleted file mode 100644 index a847a04..0000000 --- a/harmony/src/modules/monitoring/monitoring_alerting.rs +++ /dev/null @@ -1,115 +0,0 @@ -use std::sync::Arc; - -use async_trait::async_trait; -use log::debug; -use serde::Serialize; - -use crate::{ - data::{Id, Version}, - interpret::{Interpret, InterpretError, InterpretName, InterpretStatus, Outcome}, - inventory::Inventory, - score::Score, - topology::Topology, -}; - -use super::{alert_receiver::AlertReceiver, prometheus::Installable}; -use super::alert_rule::AlertRule; -use super::scrape_target::ScrapeTarget; - -#[async_trait] -pub trait MonitoringSystem: std::fmt::Debug + Clone + Serialize + 'static + Send + Sync { -} - -#[derive(Debug, Clone)] -pub struct MonitoringAlertingScore { - pub alert_receivers: Vec>>, - pub alert_rules: Vec>>, - pub scrape_targets: Vec>>, -} - -impl Clone for Box> { - fn clone(&self) -> Self { - self.clone_box() - } -} -impl Clone for Box> { - fn clone(&self) -> Self { - self.clone_box() - } -} -impl Clone for Box> { - fn clone(&self) -> Self { - self.clone_box() - } -} - -impl Serialize for MonitoringAlertingScore { - fn serialize(&self, _serializer: S) -> Result - where - S: serde::Serializer, - { - todo!() - } -} - -impl Score - for MonitoringAlertingScore -{ - fn create_interpret(&self) -> Box> { - Box::new(MonitoringAlertingInterpret { - score: Arc::new(self.clone()), - }) - } - - fn name(&self) -> String { - "MonitoringAlertingScore".to_string() - } -} - -#[derive(Debug)] -struct MonitoringAlertingInterpret { - score: Arc>, -} - - - -#[async_trait] -impl Interpret - for MonitoringAlertingInterpret -{ - async fn execute( - &self, - _inventory: &Inventory, - _topology: &T, - ) -> Result { - debug!("score {:#?}", self.score); - todo!("Figure out a clean way to have things come together here : - We want Prometheus to be bound like this : - - DiscordWebhook (AlertReceiver) - | - Prometheus (AlertSender + Scraper + Installable) - ^ - MonitoringScore {{ alert_rules, alert_receivers, scrape_endpoints }} - (Interpret InterpretName { - todo!() - } - - fn get_version(&self) -> Version { - todo!() - } - - fn get_status(&self) -> InterpretStatus { - todo!() - } - - fn get_children(&self) -> Vec { - todo!() - } -} - diff --git a/harmony/src/modules/monitoring/prometheus.rs b/harmony/src/modules/monitoring/prometheus.rs deleted file mode 100644 index 4849a98..0000000 --- a/harmony/src/modules/monitoring/prometheus.rs +++ /dev/null @@ -1,112 +0,0 @@ -use async_trait::async_trait; -use serde::Serialize; -use serde_value::Value; - -use crate::{ - data::{Id, Version}, - interpret::{Interpret, InterpretError, InterpretName, InterpretStatus, Outcome}, - inventory::Inventory, - topology::{K8sAnywhereTopology, Topology}, -}; -use std::fmt::Debug; - -use super::monitoring_alerting::MonitoringSystem; - -#[derive(Debug, Clone, Serialize)] -pub struct Prometheus {} - -impl Prometheus { - pub fn configure_receiver(&self, receiver: Box<&dyn PrometheusReceiver>) -> Result<(), String> { - let _receiver_config = receiver.get_prometheus_config(); - todo!() - } - pub fn configure_rule(&self, _rule: Box<&dyn PrometheusRule>) { - todo!() - } - pub fn configure_scrape_target(&self, _target: Box<&dyn PrometheusScrapeTarget>) { - todo!() - } -} - -impl MonitoringSystem for Prometheus {} - -pub trait PrometheusCapability { - fn install_alert_receivers(&self, receivers: Vec>); - fn install_alert_rules(&self, rules: Vec>); - fn install_scrape_targets(&self, receivers: Vec>); -} - -pub trait PrometheusReceiver { - fn get_prometheus_config(&self) -> PrometheusReceiverConfig; -} - -pub struct PrometheusReceiverConfig { - config: Value, // either a serde Value or a more specific type that understands prometheus - // config -} - -pub trait PrometheusRule { - fn get_prometheus_config(&self) -> PrometheusRuleConfig; -} - -pub struct PrometheusRuleConfig { - pub definition: String, // Not a string but an actual prometheus rule config -} - -pub trait PrometheusScrapeTarget { - fn get_prometheus_config(&self) -> PrometheusScrapeTargetConfig; -} - -pub struct PrometheusScrapeTargetConfig { - pub definition: String, // Not a string but an actual prometheus scraping config -} - -pub struct PrometheusMonitoringScore { - alert_receivers: Vec>, - alert_rules: Vec>, - scrape_targets: Vec>, -} - -#[derive(Debug)] -pub struct PrometheusMonitoringInterpret {} - -#[async_trait] -impl Interpret for PrometheusMonitoringInterpret { - async fn execute( - &self, - _inventory: &Inventory, - _topology: &T, - ) -> Result { - todo!() - } - - fn get_name(&self) -> InterpretName { - todo!() - } - - fn get_version(&self) -> Version { - todo!() - } - - fn get_status(&self) -> InterpretStatus { - todo!() - } - - fn get_children(&self) -> Vec { - todo!() - } -} - -#[async_trait] -pub trait Installable { - async fn install(&self) -> Result; - type Installer; -} - -#[async_trait] -impl Installable for Prometheus { - type Installer = K8sAnywhereTopology; - async fn install(&self) -> Result { - todo!() - } -} diff --git a/harmony/src/modules/monitoring/scrape_target.rs b/harmony/src/modules/monitoring/scrape_target.rs deleted file mode 100644 index 86e3b2d..0000000 --- a/harmony/src/modules/monitoring/scrape_target.rs +++ /dev/null @@ -1,14 +0,0 @@ -use std::fmt::Debug; - -use dyn_clone::DynClone; - -use super::monitoring_alerting::MonitoringSystem; - - -pub trait ScrapeTarget: Debug + Send + Sync + DynClone { - type M: MonitoringSystem; - - fn install(&self, sender: &Self::M); - fn clone_box(&self) -> Box>; -} - -- 2.39.5 From 273befc61cac39e58b747cf2087a5663e40abfec Mon Sep 17 00:00:00 2001 From: Willem Date: Thu, 19 Jun 2025 09:03:39 -0400 Subject: [PATCH 11/11] fix: deleted outdated monitoring and alerting score --- .../modules/monitoring/monitoring_alerting.rs | 158 ------------------ 1 file changed, 158 deletions(-) delete mode 100644 harmony/src/modules/monitoring/monitoring_alerting.rs diff --git a/harmony/src/modules/monitoring/monitoring_alerting.rs b/harmony/src/modules/monitoring/monitoring_alerting.rs deleted file mode 100644 index bdddb7d..0000000 --- a/harmony/src/modules/monitoring/monitoring_alerting.rs +++ /dev/null @@ -1,158 +0,0 @@ -use async_trait::async_trait; -use email_address::EmailAddress; - -use log::info; -use serde::Serialize; -use url::Url; - -use crate::{ - data::{Id, Version}, - interpret::{Interpret, InterpretError, InterpretName, InterpretStatus, Outcome}, - inventory::Inventory, - score::Score, - topology::{HelmCommand, Topology}, -}; - -use super::{config::KubePrometheusConfig, kube_prometheus::kube_prometheus_helm_chart_score}; - -#[derive(Debug, Clone, Serialize)] -pub enum AlertChannel { - Discord { - name: String, - webhook_url: Url, - }, - Slack { - slack_channel: String, - webhook_url: Url, - }, - //TODO test and implement in helm chart - //currently does not work - Smpt { - email_address: EmailAddress, - service_name: String, - }, -} - -#[derive(Debug, Clone, Serialize)] -pub struct MonitoringAlertingStackScore { - pub alert_channel: Vec, - pub namespace: Option, -} - -impl MonitoringAlertingStackScore { - pub fn new() -> Self { - Self { - alert_channel: Vec::new(), - namespace: None, - } - } -} - -impl Score for MonitoringAlertingStackScore { - fn create_interpret(&self) -> Box> { - Box::new(MonitoringAlertingStackInterpret { - score: self.clone(), - }) - } - fn name(&self) -> String { - format!("MonitoringAlertingStackScore") - } -} - -#[derive(Debug, Clone, Serialize)] -struct MonitoringAlertingStackInterpret { - score: MonitoringAlertingStackScore, -} - -impl MonitoringAlertingStackInterpret { - async fn build_kube_prometheus_helm_chart_config(&self) -> KubePrometheusConfig { - let mut config = KubePrometheusConfig::new(); - if let Some(ns) = &self.score.namespace { - config.namespace = ns.clone(); - } - config.alert_channel = self.score.alert_channel.clone(); - config - } - - async fn deploy_kube_prometheus_helm_chart_score( - &self, - inventory: &Inventory, - topology: &T, - config: &KubePrometheusConfig, - ) -> Result { - let helm_chart = kube_prometheus_helm_chart_score(config); - helm_chart - .create_interpret() - .execute(inventory, topology) - .await - } - - async fn deploy_alert_channel_service( - &self, - inventory: &Inventory, - topology: &T, - config: &KubePrometheusConfig, - ) -> Result { - //let mut outcomes = vec![]; - - //for channel in &self.score.alert_channel { - // let outcome = match channel { - // AlertChannel::Discord { .. } => { - // discord_alert_manager_score(config) - // .create_interpret() - // .execute(inventory, topology) - // .await - // } - // AlertChannel::Slack { .. } => Ok(Outcome::success( - // "No extra configs for slack alerting".to_string(), - // )), - // AlertChannel::Smpt { .. } => { - // todo!() - // } - // }; - // outcomes.push(outcome); - //} - //for result in outcomes { - // result?; - //} - - Ok(Outcome::success("All alert channels deployed".to_string())) - } -} - -#[async_trait] -impl Interpret for MonitoringAlertingStackInterpret { - async fn execute( - &self, - inventory: &Inventory, - topology: &T, - ) -> Result { - let config = self.build_kube_prometheus_helm_chart_config().await; - info!("Built kube prometheus config"); - info!("Installing kube prometheus chart"); - self.deploy_kube_prometheus_helm_chart_score(inventory, topology, &config) - .await?; - info!("Installing alert channel service"); - self.deploy_alert_channel_service(inventory, topology, &config) - .await?; - Ok(Outcome::success(format!( - "succesfully deployed monitoring and alerting stack" - ))) - } - - fn get_name(&self) -> InterpretName { - todo!() - } - - fn get_version(&self) -> Version { - todo!() - } - - fn get_status(&self) -> InterpretStatus { - todo!() - } - - fn get_children(&self) -> Vec { - todo!() - } -} -- 2.39.5