diff --git a/examples/lamp/src/main.rs b/examples/lamp/src/main.rs index 74df0d8..f8bc6c5 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::{AlertChannel, MonitoringAlertingStackScore}, - }, + modules::lamp::{LAMPConfig, LAMPScore}, topology::{K8sAnywhereTopology, Url}, }; @@ -43,13 +40,7 @@ async fn main() { .await .unwrap(); - let url = url::Url::parse("https://discord.com/api/webhooks/dummy_channel/dummy_token") - .expect("invalid URL"); - - 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/Cargo.toml b/examples/monitoring/Cargo.toml new file mode 100644 index 0000000..5eee2fe --- /dev/null +++ b/examples/monitoring/Cargo.toml @@ -0,0 +1,12 @@ +[package] +name = "webhook_sender" +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 +url.workspace = true diff --git a/examples/monitoring/src/main.rs b/examples/monitoring/src/main.rs new file mode 100644 index 0000000..1f553fa --- /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, oberservability::K8sMonitorConfig}, +}; + +#[tokio::main] +async fn main() { + let mut maestro = Maestro::::initialize( + Inventory::autoload(), + K8sAnywhereTopology::new(), + ) + .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 ef11f36..8888dd9 100644 --- a/harmony/src/domain/topology/k8s_anywhere.rs +++ b/harmony/src/domain/topology/k8s_anywhere.rs @@ -10,13 +10,21 @@ use crate::{ interpret::{InterpretError, Outcome}, inventory::Inventory, maestro::Maestro, - modules::k3d::K3DInstallationScore, + modules::{ + k3d::K3DInstallationScore, + monitoring::kube_prometheus::kube_prometheus_helm_chart_score::kube_prometheus_helm_chart_score, + }, topology::LocalhostTopology, }; use super::{ HelmCommand, K8sclient, Topology, k8s::K8sClient, + oberservability::{ + K8sMonitorConfig, + k8s::K8sMonitor, + monitoring::{AlertChannel, AlertChannelConfig, Monitor}, + }, tenant::{ ResourceLimits, TenantConfig, TenantManager, TenantNetworkPolicy, k8s::K8sTenantManager, }, @@ -37,6 +45,7 @@ enum K8sSource { pub struct K8sAnywhereTopology { k8s_state: OnceCell>, tenant_manager: OnceCell, + k8s_monitor: OnceCell, } #[async_trait] @@ -61,6 +70,7 @@ impl K8sAnywhereTopology { Self { k8s_state: OnceCell::new(), tenant_manager: OnceCell::new(), + k8s_monitor: OnceCell::new(), } } @@ -178,6 +188,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(), + )), + } + } } struct K8sAnywhereConfig { @@ -217,6 +251,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))?; + match self.is_helm_available() { Ok(()) => Ok(Outcome::success(format!( "{} + helm available", @@ -263,3 +301,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 4603eba..1d6a159 100644 --- a/harmony/src/domain/topology/oberservability/monitoring.rs +++ b/harmony/src/domain/topology/oberservability/monitoring.rs @@ -1,31 +1,39 @@ use async_trait::async_trait; - +use dyn_clone::DynClone; use std::fmt::Debug; -use url::Url; +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..76a6498 --- /dev/null +++ b/harmony/src/modules/monitoring/alert_channel/discord_alert_channel.rs @@ -0,0 +1,42 @@ +use url::Url; + +#[derive(Debug, Clone)] +pub struct DiscordWebhookAlertChannel { + pub webhook_url: Url, + pub name: String, + pub send_resolved_notifications: bool, +} + +//impl AlertChannelConfig for DiscordWebhookAlertChannel { +// fn build_alert_channel(&self) -> Box { +// Box::new(DiscordWebhookAlertChannel { +// webhook_url: self.webhook_url.clone(), +// name: self.name.clone(), +// send_resolved_notifications: self.send_resolved_notifications.clone(), +// }) +// } +// fn channel_type(&self) -> String { +// "discord".to_string() +// } +//} +// +//#[async_trait] +//impl AlertChannel for DiscordWebhookAlertChannel { +// async fn get_channel_id(&self) -> String { +// self.name.clone() +// } +//} +// +//impl PrometheusAlertChannel for DiscordWebhookAlertChannel { +// fn get_alert_channel_global_settings(&self) -> Option { +// None +// } +// +// fn get_alert_channel_route(&self) -> AlertManagerChannelRoute { +// todo!() +// } +// +// fn get_alert_channel_receiver(&self) -> AlertManagerChannelReceiver { +// todo!() +// } +//} 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 61% 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..fa9eb47 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,102 @@ alertmanager: repository: None, } } +// 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 +// } -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) -} +//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/kube_prometheus_monitor.rs b/harmony/src/modules/monitoring/kube_prometheus/kube_prometheus_monitor.rs new file mode 100644 index 0000000..f66ab8a --- /dev/null +++ b/harmony/src/modules/monitoring/kube_prometheus/kube_prometheus_monitor.rs @@ -0,0 +1,85 @@ +//#[derive(Debug, Clone, Serialize)] +//pub struct KubePrometheusMonitorScore { +// pub kube_prometheus_config: KubePrometheusConfig, +// pub alert_channel_configs: Vec, +//} + +//impl> MonitorConfig +// for KubePrometheusMonitorScore +//{ +// fn build_monitor(&self) -> Box> { +// Box::new(self.clone()) +// } +//} + +//impl> Score +// for KubePrometheusMonitorScore +//{ +// fn create_interpret(&self) -> Box> { +// Box::new(KubePrometheusMonitorInterpret { +// score: self.clone(), +// }) +// } +// +// fn name(&self) -> String { +// "KubePrometheusMonitorScore".to_string() +// } +//} + +//#[derive(Debug, Clone)] +//pub struct KubePrometheusMonitorInterpret { +// score: KubePrometheusMonitorScore, +//} + +//#[async_trait] +//impl AlertChannelConfig for KubePrometheusMonitorInterpret { +// async fn build_alert_channel( +// &self, +// ) -> Box { +// todo!() +// } +//} +//#[async_trait] +//impl> Interpret +// for KubePrometheusMonitorInterpret +//{ +// async fn execute( +// &self, +// inventory: &Inventory, +// topology: &T, +// ) -> Result { +// let monitor = self.score.build_monitor(); +// +// let mut alert_channels = Vec::new(); +// //for config in self.score.alert_channel_configs { +// // alert_channels.push(self.build_alert_channel()); +// //} +// +// monitor +// .deploy_monitor(inventory, topology, alert_channels) +// .await +// } +// +// 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 PrometheusAlertChannel { +// fn get_alert_channel_global_settings(&self) -> Option; +// fn get_alert_channel_route(&self) -> AlertManagerChannelRoute; +// fn get_alert_channel_receiver(&self) -> AlertManagerChannelReceiver; +//} 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..b0b9985 --- /dev/null +++ b/harmony/src/modules/monitoring/kube_prometheus/mod.rs @@ -0,0 +1,4 @@ +pub mod config; +pub mod kube_prometheus_helm_chart_score; +pub mod kube_prometheus_monitor; +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..d4fcecb 100644 --- a/harmony/src/modules/monitoring/monitoring_alerting.rs +++ b/harmony/src/modules/monitoring/monitoring_alerting.rs @@ -1,146 +1,54 @@ 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, discord_alert_manager::discord_alert_manager_score, - kube_prometheus::kube_prometheus_helm_chart_score, + topology::{ + HelmCommand, Topology, + oberservability::monitoring::{AlertChannelConfig, Monitor}, + }, }; #[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 {