From ec1df0413af59dbe73fb37bdec7579c784d2b724 Mon Sep 17 00:00:00 2001 From: Willem Date: Thu, 19 Jun 2025 11:06:20 -0400 Subject: [PATCH 1/9] wip: implentation for kube prometheus monitoring alert sender --- examples/lamp/php/Dockerfile | 18 ++++ examples/lamp/php/docker-php.ini | 16 ++++ examples/lamp/src/main.rs | 5 +- .../alert_channel/discord_alert_channel.rs | 87 ++++++++++++++++++- .../helm_prometheus_alert_score.rs | 5 +- .../monitoring/kube_prometheus/types.rs | 15 ++-- 6 files changed, 127 insertions(+), 19 deletions(-) create mode 100644 examples/lamp/php/Dockerfile create mode 100644 examples/lamp/php/docker-php.ini diff --git a/examples/lamp/php/Dockerfile b/examples/lamp/php/Dockerfile new file mode 100644 index 0000000..df5f4fa --- /dev/null +++ b/examples/lamp/php/Dockerfile @@ -0,0 +1,18 @@ +FROM php:8.4-apache +ENV PHP_MEMORY_LIMIT=256M +ENV PHP_MAX_EXECUTION_TIME=30 +ENV PHP_ERROR_REPORTING="E_ERROR | E_WARNING | E_PARSE" +RUN apt-get update && apt-get install -y --no-install-recommends libfreetype6-dev libjpeg62-turbo-dev libpng-dev libzip-dev unzip && apt-get clean && rm -rf /var/lib/apt/lists/* +RUN docker-php-ext-configure gd --with-freetype --with-jpeg && docker-php-ext-install -j$(nproc) gd mysqli pdo_mysql zip opcache +RUN sed -i 's/VirtualHost \*:80/VirtualHost *:8080/' /etc/apache2/sites-available/000-default.conf && \ + sed -i 's/^Listen 80$/Listen 8080/' /etc/apache2/ports.conf +RUN mkdir -p /usr/local/etc/php/conf.d/ +COPY docker-php.ini /usr/local/etc/php/conf.d/docker-php.ini +RUN a2enmod headers && a2enmod rewrite && sed -i 's/ServerTokens OS/ServerTokens Prod/' /etc/apache2/conf-enabled/security.conf && sed -i 's/ServerSignature On/ServerSignature Off/' /etc/apache2/conf-enabled/security.conf +RUN echo 'PassEnv MYSQL_PASSWORD' >> /etc/apache2/sites-available/000-default.conf && echo 'PassEnv MYSQL_USER' >> /etc/apache2/sites-available/000-default.conf && echo 'PassEnv MYSQL_HOST' >> /etc/apache2/sites-available/000-default.conf +RUN groupadd -g 1000 appuser && useradd -u 1000 -g appuser -m -s /bin/bash appuser && chown -R appuser:appuser /var/www/html +WORKDIR /var/www/html +COPY . /var/www/html +RUN chown -R appuser:appuser /var/www/html +EXPOSE 8080/tcp +CMD apache2-foreground \ No newline at end of file diff --git a/examples/lamp/php/docker-php.ini b/examples/lamp/php/docker-php.ini new file mode 100644 index 0000000..3745ad5 --- /dev/null +++ b/examples/lamp/php/docker-php.ini @@ -0,0 +1,16 @@ + +memory_limit = ${PHP_MEMORY_LIMIT} +max_execution_time = ${PHP_MAX_EXECUTION_TIME} +error_reporting = ${PHP_ERROR_REPORTING} +display_errors = Off +log_errors = On +error_log = /dev/stderr +date.timezone = UTC + +; Opcache configuration for production +opcache.enable=1 +opcache.memory_consumption=128 +opcache.interned_strings_buffer=8 +opcache.max_accelerated_files=4000 +opcache.revalidate_freq=2 +opcache.fast_shutdown=1 diff --git a/examples/lamp/src/main.rs b/examples/lamp/src/main.rs index 0b1f93c..4518165 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::alert_channel::discord_alert_channel::DiscordWebhook, - }, + modules::lamp::{LAMPConfig, LAMPScore}, topology::{K8sAnywhereTopology, Url}, }; 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 42f4450..10f4d72 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,12 @@ use async_trait::async_trait; +use serde_yaml::{Mapping, Value}; use crate::{ interpret::InterpretError, - modules::monitoring::kube_prometheus::prometheus::{Prometheus, PrometheusReceiver}, + modules::monitoring::kube_prometheus::{ + prometheus::{Prometheus, PrometheusReceiver}, + types::AlertChannelConfig, + }, topology::{Url, oberservability::monitoring::AlertReceiver}, }; @@ -18,3 +22,84 @@ impl AlertReceiver for DiscordWebhook { sender.install_receiver(PrometheusReceiver {}).await } } + +impl AlertChannelConfig for DiscordWebhook { + fn alert_channel_global_config(&self) -> Option { + None + } + + fn alert_channel_route(&self) -> serde_yaml::Value { + let mut route = Mapping::new(); + route.insert( + Value::String("receiver".to_string()), + Value::String(self.name.clone()), + ); + route.insert( + Value::String("matchers".to_string()), + Value::Sequence(vec![Value::String("alertname!=Watchdog".to_string())]), + ); + route.insert(Value::String("continue".to_string()), Value::Bool(true)); + Value::Mapping(route) + } + + fn alert_channel_receiver(&self) -> serde_yaml::Value { + let mut receiver = Mapping::new(); + receiver.insert( + Value::String("name".to_string()), + Value::String(self.name.clone()), + ); + + let mut discord_config = Mapping::new(); + discord_config.insert( + Value::String("webhook_url".to_string()), + Value::String(self.url.to_string()), + ); + + receiver.insert( + Value::String("discord_configs".to_string()), + Value::Sequence(vec![Value::Mapping(discord_config)]), + ); + + Value::Mapping(receiver) + } +} + +#[cfg(test)] +mod tests { + use log::debug; + + use super::*; + + #[test] + fn discord_serialize_should_match() { + let discord_receiver = DiscordWebhook { + name: "test-discord".to_string(), + url: Url::Url(url::Url::parse("https://discord.i.dont.exist.com").unwrap()), + }; + + let discord_receiver_global = + serde_yaml::to_string(&discord_receiver.alert_channel_global_config()).unwrap(); + println!("global receiver \n{:#}", discord_receiver_global); + let discord_receiver_global_yaml = "".to_string(); + + let discord_receiver_receiver = + serde_yaml::to_string(&discord_receiver.alert_channel_receiver()).unwrap(); + println!("receiver \n{:#}", discord_receiver_receiver); + let discord_receiver_receiver_yaml = r#"name: test-discord +discord_configs: +- webhook_url: https://discord.i.dont.exist.com/"# + .to_string(); + + let discord_receiver_route = + serde_yaml::to_string(&discord_receiver.alert_channel_route()).unwrap(); + println!("route \n{:#}", discord_receiver_route); + let discord_receiver_route_yaml = r#"receiver: test-discord +matchers: +- alertname!=Watchdog +continue: true"# + .to_string(); + + assert_eq!(discord_receiver_receiver, discord_receiver_receiver_yaml); + assert_eq!(discord_receiver_route, discord_receiver_route_yaml); + } +} 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 index c090f13..3cfd447 100644 --- a/harmony/src/modules/monitoring/kube_prometheus/helm_prometheus_alert_score.rs +++ b/harmony/src/modules/monitoring/kube_prometheus/helm_prometheus_alert_score.rs @@ -20,10 +20,7 @@ impl Score for HelmPrometheusAlertingScore { fn create_interpret(&self) -> Box> { Box::new(AlertingInterpret { sender: Prometheus {}, - receivers: vec![Box::new(DiscordWebhook { - url: todo!(), - name: todo!(), - })], + receivers: self.receivers.clone(), }) } diff --git a/harmony/src/modules/monitoring/kube_prometheus/types.rs b/harmony/src/modules/monitoring/kube_prometheus/types.rs index 224b125..08d1b7d 100644 --- a/harmony/src/modules/monitoring/kube_prometheus/types.rs +++ b/harmony/src/modules/monitoring/kube_prometheus/types.rs @@ -1,12 +1,7 @@ -use serde::Serialize; +use serde_yaml::Value; -#[derive(Serialize)] -pub struct AlertReceiverRoute { - pub receiver: String, - pub matchers: Vec, - #[serde(default)] - pub r#continue: bool, -} -pub struct AlertChannelReceiver { - pub name: String, +pub trait AlertChannelConfig { + fn alert_channel_global_config(&self) -> Option; + fn alert_channel_route(&self) -> Value; + fn alert_channel_receiver(&self) -> Value; } -- 2.39.5 From 50e38fed0a1ea661cdc42b0dbe7f2a95074a1531 Mon Sep 17 00:00:00 2001 From: Willem Date: Thu, 19 Jun 2025 12:32:46 -0400 Subject: [PATCH 2/9] wip: monitoring impl --- examples/monitoring/Cargo.toml | 1 + examples/monitoring/src/main.rs | 10 +++-- .../topology/oberservability/monitoring.rs | 1 + .../alert_channel/discord_alert_channel.rs | 39 +++++++++++-------- .../monitoring/kube_prometheus/helm/config.rs | 5 +++ .../helm_prometheus_alert_score.rs | 12 +++--- .../monitoring/kube_prometheus/prometheus.rs | 29 ++++++++++---- .../monitoring/kube_prometheus/types.rs | 8 ++-- 8 files changed, 69 insertions(+), 36 deletions(-) diff --git a/examples/monitoring/Cargo.toml b/examples/monitoring/Cargo.toml index af42491..d188b78 100644 --- a/examples/monitoring/Cargo.toml +++ b/examples/monitoring/Cargo.toml @@ -9,3 +9,4 @@ license.workspace = true 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 index d52c649..de81303 100644 --- a/examples/monitoring/src/main.rs +++ b/examples/monitoring/src/main.rs @@ -1,12 +1,16 @@ use harmony::{ inventory::Inventory, maestro::Maestro, - modules::monitoring::kube_prometheus::helm_prometheus_alert_score::HelmPrometheusAlertingScore, - topology::K8sAnywhereTopology, + modules::monitoring::{alert_channel::discord_alert_channel::DiscordWebhook, kube_prometheus::helm_prometheus_alert_score::HelmPrometheusAlertingScore}, + topology::{K8sAnywhereTopology, Url}, }; #[tokio::main] async fn main() { - let alerting_score = HelmPrometheusAlertingScore { receivers: vec![] }; + let discord_receiver = DiscordWebhook { + name: "test-discord".to_string(), + url: Url::Url(url::Url::parse("https://discord.i.dont.exist.com").unwrap()), + }; + let alerting_score = HelmPrometheusAlertingScore { receivers: vec![Box::new(discord_receiver)] }; let mut maestro = Maestro::::initialize( Inventory::autoload(), K8sAnywhereTopology::from_env(), diff --git a/harmony/src/domain/topology/oberservability/monitoring.rs b/harmony/src/domain/topology/oberservability/monitoring.rs index a3a6164..ab71f5b 100644 --- a/harmony/src/domain/topology/oberservability/monitoring.rs +++ b/harmony/src/domain/topology/oberservability/monitoring.rs @@ -48,6 +48,7 @@ impl Interpret for AlertingInterpret { #[async_trait] pub trait AlertReceiver: std::fmt::Debug + Send + Sync { async fn install(&self, sender: &S) -> Result<(), InterpretError>; + fn clone_box(&self) -> Box>; } #[async_trait] 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 10f4d72..72a95f5 100644 --- a/harmony/src/modules/monitoring/alert_channel/discord_alert_channel.rs +++ b/harmony/src/modules/monitoring/alert_channel/discord_alert_channel.rs @@ -10,7 +10,7 @@ use crate::{ topology::{Url, oberservability::monitoring::AlertReceiver}, }; -#[derive(Debug)] +#[derive(Debug, Clone)] pub struct DiscordWebhook { pub name: String, pub url: Url, @@ -19,16 +19,28 @@ pub struct DiscordWebhook { #[async_trait] impl AlertReceiver for DiscordWebhook { async fn install(&self, sender: &Prometheus) -> Result<(), InterpretError> { - sender.install_receiver(PrometheusReceiver {}).await + sender.install_receiver(self).await + } + fn clone_box(&self) -> Box> { + Box::new(self.clone()) } } +#[async_trait] +impl PrometheusReceiver for DiscordWebhook { + //TODO not return a tuple + async fn receiver_config(&self) -> (Option, Value, Value) { + (self.alert_channel_global_config().await, self.alert_channel_receiver().await, self.alert_channel_route().await) + } +} + +#[async_trait] impl AlertChannelConfig for DiscordWebhook { - fn alert_channel_global_config(&self) -> Option { + async fn alert_channel_global_config(&self) -> Option { None } - fn alert_channel_route(&self) -> serde_yaml::Value { + async fn alert_channel_route(&self) -> serde_yaml::Value { let mut route = Mapping::new(); route.insert( Value::String("receiver".to_string()), @@ -42,7 +54,7 @@ impl AlertChannelConfig for DiscordWebhook { Value::Mapping(route) } - fn alert_channel_receiver(&self) -> serde_yaml::Value { + async fn alert_channel_receiver(&self) -> serde_yaml::Value { let mut receiver = Mapping::new(); receiver.insert( Value::String("name".to_string()), @@ -66,8 +78,6 @@ impl AlertChannelConfig for DiscordWebhook { #[cfg(test)] mod tests { - use log::debug; - use super::*; #[test] @@ -77,18 +87,14 @@ mod tests { url: Url::Url(url::Url::parse("https://discord.i.dont.exist.com").unwrap()), }; - let discord_receiver_global = - serde_yaml::to_string(&discord_receiver.alert_channel_global_config()).unwrap(); - println!("global receiver \n{:#}", discord_receiver_global); - let discord_receiver_global_yaml = "".to_string(); - let discord_receiver_receiver = serde_yaml::to_string(&discord_receiver.alert_channel_receiver()).unwrap(); println!("receiver \n{:#}", discord_receiver_receiver); let discord_receiver_receiver_yaml = r#"name: test-discord discord_configs: -- webhook_url: https://discord.i.dont.exist.com/"# - .to_string(); +- webhook_url: https://discord.i.dont.exist.com/ +"# + .to_string(); let discord_receiver_route = serde_yaml::to_string(&discord_receiver.alert_channel_route()).unwrap(); @@ -96,8 +102,9 @@ discord_configs: let discord_receiver_route_yaml = r#"receiver: test-discord matchers: - alertname!=Watchdog -continue: true"# - .to_string(); +continue: true +"# + .to_string(); assert_eq!(discord_receiver_receiver, discord_receiver_receiver_yaml); assert_eq!(discord_receiver_route, discord_receiver_route_yaml); diff --git a/harmony/src/modules/monitoring/kube_prometheus/helm/config.rs b/harmony/src/modules/monitoring/kube_prometheus/helm/config.rs index 0e62c0f..39d449f 100644 --- a/harmony/src/modules/monitoring/kube_prometheus/helm/config.rs +++ b/harmony/src/modules/monitoring/kube_prometheus/helm/config.rs @@ -1,4 +1,7 @@ use serde::Serialize; +use serde_yaml::Value; + +use crate::modules::monitoring::kube_prometheus::prometheus::PrometheusReceiver; #[derive(Debug, Clone, Serialize)] pub struct KubePrometheusConfig { @@ -19,6 +22,7 @@ pub struct KubePrometheusConfig { pub kube_proxy: bool, pub kube_state_metrics: bool, pub prometheus_operator: bool, + pub alert_receiver_configs: Vec, } impl KubePrometheusConfig { pub fn new() -> Self { @@ -40,6 +44,7 @@ impl KubePrometheusConfig { prometheus_operator: true, core_dns: false, kube_scheduler: false, + alert_receiver_configs: vec![], } } } 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 index 3cfd447..284f8fb 100644 --- a/harmony/src/modules/monitoring/kube_prometheus/helm_prometheus_alert_score.rs +++ b/harmony/src/modules/monitoring/kube_prometheus/helm_prometheus_alert_score.rs @@ -1,7 +1,6 @@ use serde::Serialize; use crate::{ - modules::monitoring::alert_channel::discord_alert_channel::DiscordWebhook, score::Score, topology::{ HelmCommand, Topology, @@ -9,7 +8,7 @@ use crate::{ }, }; -use super::prometheus::Prometheus; +use super::{helm::config::KubePrometheusConfig, prometheus::Prometheus}; #[derive(Clone, Debug, Serialize)] pub struct HelmPrometheusAlertingScore { @@ -19,12 +18,10 @@ pub struct HelmPrometheusAlertingScore { impl Score for HelmPrometheusAlertingScore { fn create_interpret(&self) -> Box> { Box::new(AlertingInterpret { - sender: Prometheus {}, + sender: Prometheus { config: KubePrometheusConfig::new() }, receivers: self.receivers.clone(), }) - } - - fn name(&self) -> String { + } fn name(&self) -> String { "HelmPrometheusAlertingScore".to_string() } } @@ -37,8 +34,9 @@ impl Serialize for Box> { todo!() } } + impl Clone for Box> { fn clone(&self) -> Self { - todo!() + self.clone_box() } } diff --git a/harmony/src/modules/monitoring/kube_prometheus/prometheus.rs b/harmony/src/modules/monitoring/kube_prometheus/prometheus.rs index c5be07e..de666e3 100644 --- a/harmony/src/modules/monitoring/kube_prometheus/prometheus.rs +++ b/harmony/src/modules/monitoring/kube_prometheus/prometheus.rs @@ -1,10 +1,14 @@ use async_trait::async_trait; +use log::debug; +use serde_yaml::Value; use crate::{ interpret::InterpretError, topology::{installable::Installable, oberservability::monitoring::AlertSender}, }; +use super::helm::config::KubePrometheusConfig; + impl AlertSender for Prometheus {} #[async_trait] @@ -13,22 +17,33 @@ impl Installable for Prometheus { todo!() } } + #[derive(Debug)] -pub struct Prometheus; +pub struct Prometheus{ + pub config: KubePrometheusConfig, +} impl Prometheus { pub async fn install_receiver( &self, - prometheus_receiver: PrometheusReceiver, + prometheus_receiver: &dyn PrometheusReceiver, ) -> Result<(), InterpretError> { + + + debug!("adding alert receiver to prometheus config: {:#?}", prometheus_receiver.receiver_config()); + self.config.alert_receiver_configs.push(prometheus_receiver.receiver_config()); todo!() } } -pub struct PrometheusReceiver {} - -impl PrometheusReceiver { - fn get_prometheus_receiver_config(&self) {} +#[async_trait] +pub trait PrometheusReceiver: Send + Sync + std::fmt::Debug{ + async fn receiver_config(&self) -> (Option, Value, Value); + //this probably needs to be a type + //that + //represents + //a + //promtheusreceiver } -pub struct AlertChannelGlobalConfig {} + diff --git a/harmony/src/modules/monitoring/kube_prometheus/types.rs b/harmony/src/modules/monitoring/kube_prometheus/types.rs index 08d1b7d..d6f28d2 100644 --- a/harmony/src/modules/monitoring/kube_prometheus/types.rs +++ b/harmony/src/modules/monitoring/kube_prometheus/types.rs @@ -1,7 +1,9 @@ +use async_trait::async_trait; use serde_yaml::Value; +#[async_trait] pub trait AlertChannelConfig { - fn alert_channel_global_config(&self) -> Option; - fn alert_channel_route(&self) -> Value; - fn alert_channel_receiver(&self) -> Value; + async fn alert_channel_global_config(&self) -> Option; + async fn alert_channel_route(&self) -> Value; + async fn alert_channel_receiver(&self) -> Value; } -- 2.39.5 From a41230ac7b2541eae7a5a5d4b0e780feee18f767 Mon Sep 17 00:00:00 2001 From: Willem Date: Thu, 19 Jun 2025 16:40:00 -0400 Subject: [PATCH 3/9] wip: trying to install the helm chart for HelmKubePrometheus --- examples/monitoring/src/main.rs | 18 ++-- harmony/src/domain/topology/installable.rs | 2 + .../topology/oberservability/monitoring.rs | 16 +++- .../alert_channel/discord_alert_channel.rs | 11 ++- .../helm/kube_prometheus_helm_chart.rs | 4 +- .../helm_prometheus_alert_score.rs | 12 ++- .../monitoring/kube_prometheus/prometheus.rs | 92 +++++++++++++++---- 7 files changed, 113 insertions(+), 42 deletions(-) diff --git a/examples/monitoring/src/main.rs b/examples/monitoring/src/main.rs index de81303..d3eb3b8 100644 --- a/examples/monitoring/src/main.rs +++ b/examples/monitoring/src/main.rs @@ -1,6 +1,10 @@ use harmony::{ - inventory::Inventory, maestro::Maestro, - modules::monitoring::{alert_channel::discord_alert_channel::DiscordWebhook, kube_prometheus::helm_prometheus_alert_score::HelmPrometheusAlertingScore}, + inventory::Inventory, + maestro::Maestro, + modules::monitoring::{ + alert_channel::discord_alert_channel::DiscordWebhook, + kube_prometheus::helm_prometheus_alert_score::HelmPrometheusAlertingScore, + }, topology::{K8sAnywhereTopology, Url}, }; @@ -10,7 +14,9 @@ async fn main() { name: "test-discord".to_string(), url: Url::Url(url::Url::parse("https://discord.i.dont.exist.com").unwrap()), }; - let alerting_score = HelmPrometheusAlertingScore { receivers: vec![Box::new(discord_receiver)] }; + let alerting_score = HelmPrometheusAlertingScore { + receivers: vec![Box::new(discord_receiver)], + }; let mut maestro = Maestro::::initialize( Inventory::autoload(), K8sAnywhereTopology::from_env(), @@ -18,12 +24,6 @@ async fn main() { .await .unwrap(); - //let monitoring = MonitoringAlertingScore { - // 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/installable.rs b/harmony/src/domain/topology/installable.rs index 9b9054f..0092065 100644 --- a/harmony/src/domain/topology/installable.rs +++ b/harmony/src/domain/topology/installable.rs @@ -2,6 +2,8 @@ use async_trait::async_trait; use crate::interpret::InterpretError; +use super::oberservability::monitoring::AlertSender; + #[async_trait] pub trait Installable { async fn ensure_installed(&self) -> Result<(), InterpretError>; diff --git a/harmony/src/domain/topology/oberservability/monitoring.rs b/harmony/src/domain/topology/oberservability/monitoring.rs index ab71f5b..61e494a 100644 --- a/harmony/src/domain/topology/oberservability/monitoring.rs +++ b/harmony/src/domain/topology/oberservability/monitoring.rs @@ -7,7 +7,11 @@ use crate::{ topology::{Topology, installable::Installable}, }; -pub trait AlertSender: Send + Sync + std::fmt::Debug + Installable {} +#[async_trait] +pub trait AlertSender: Send + Sync + std::fmt::Debug + Installable { + fn name(&self) -> String; + async fn install(&self) -> Result; +} #[derive(Debug)] pub struct AlertingInterpret { @@ -16,7 +20,7 @@ pub struct AlertingInterpret { } #[async_trait] -impl Interpret for AlertingInterpret { +impl Interpret for AlertingInterpret { async fn execute( &self, inventory: &Inventory, @@ -25,7 +29,11 @@ impl Interpret for AlertingInterpret { for receiver in self.receivers.iter() { receiver.install(&self.sender).await?; } - todo!() + self.sender.ensure_installed(); + Ok(Outcome::success(format!( + "successfully installed alert sender {}", + self.sender.name() + ))) } fn get_name(&self) -> InterpretName { @@ -47,7 +55,7 @@ impl Interpret for AlertingInterpret { #[async_trait] pub trait AlertReceiver: std::fmt::Debug + Send + Sync { - async fn install(&self, sender: &S) -> Result<(), InterpretError>; + async fn install(&self, sender: &S) -> Result; fn clone_box(&self) -> Box>; } 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 72a95f5..8fa7523 100644 --- a/harmony/src/modules/monitoring/alert_channel/discord_alert_channel.rs +++ b/harmony/src/modules/monitoring/alert_channel/discord_alert_channel.rs @@ -2,7 +2,7 @@ use async_trait::async_trait; use serde_yaml::{Mapping, Value}; use crate::{ - interpret::InterpretError, + interpret::{InterpretError, Outcome}, modules::monitoring::kube_prometheus::{ prometheus::{Prometheus, PrometheusReceiver}, types::AlertChannelConfig, @@ -18,7 +18,7 @@ pub struct DiscordWebhook { #[async_trait] impl AlertReceiver for DiscordWebhook { - async fn install(&self, sender: &Prometheus) -> Result<(), InterpretError> { + async fn install(&self, sender: &Prometheus) -> Result { sender.install_receiver(self).await } fn clone_box(&self) -> Box> { @@ -29,8 +29,11 @@ impl AlertReceiver for DiscordWebhook { #[async_trait] impl PrometheusReceiver for DiscordWebhook { //TODO not return a tuple - async fn receiver_config(&self) -> (Option, Value, Value) { - (self.alert_channel_global_config().await, self.alert_channel_receiver().await, self.alert_channel_route().await) + fn name(&self) -> String { + self.name.clone() + } + async fn receiver_config(&self) -> Value { + self.alert_channel_receiver().await } } diff --git a/harmony/src/modules/monitoring/kube_prometheus/helm/kube_prometheus_helm_chart.rs b/harmony/src/modules/monitoring/kube_prometheus/helm/kube_prometheus_helm_chart.rs index 2377627..e8c2dfa 100644 --- a/harmony/src/modules/monitoring/kube_prometheus/helm/kube_prometheus_helm_chart.rs +++ b/harmony/src/modules/monitoring/kube_prometheus/helm/kube_prometheus_helm_chart.rs @@ -4,9 +4,7 @@ use std::str::FromStr; use crate::modules::helm::chart::HelmChartScore; -pub fn kube_prometheus_helm_chart_score() -> HelmChartScore { - let config = KubePrometheusConfig::new(); - +pub fn kube_prometheus_helm_chart_score(config: KubePrometheusConfig) -> HelmChartScore { //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(); 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 index 284f8fb..04853fd 100644 --- a/harmony/src/modules/monitoring/kube_prometheus/helm_prometheus_alert_score.rs +++ b/harmony/src/modules/monitoring/kube_prometheus/helm_prometheus_alert_score.rs @@ -1,9 +1,12 @@ +use std::sync::Mutex; + use serde::Serialize; use crate::{ score::Score, topology::{ HelmCommand, Topology, + installable::Installable, oberservability::monitoring::{AlertReceiver, AlertingInterpret}, }, }; @@ -15,13 +18,16 @@ pub struct HelmPrometheusAlertingScore { pub receivers: Vec>>, } -impl Score for HelmPrometheusAlertingScore { +impl Score for HelmPrometheusAlertingScore { fn create_interpret(&self) -> Box> { Box::new(AlertingInterpret { - sender: Prometheus { config: KubePrometheusConfig::new() }, + sender: Prometheus { + config: Mutex::new(KubePrometheusConfig::new()), + }, receivers: self.receivers.clone(), }) - } fn name(&self) -> String { + } + fn name(&self) -> String { "HelmPrometheusAlertingScore".to_string() } } diff --git a/harmony/src/modules/monitoring/kube_prometheus/prometheus.rs b/harmony/src/modules/monitoring/kube_prometheus/prometheus.rs index de666e3..222245a 100644 --- a/harmony/src/modules/monitoring/kube_prometheus/prometheus.rs +++ b/harmony/src/modules/monitoring/kube_prometheus/prometheus.rs @@ -1,15 +1,38 @@ +use std::sync::Mutex; + use async_trait::async_trait; use log::debug; use serde_yaml::Value; use crate::{ - interpret::InterpretError, - topology::{installable::Installable, oberservability::monitoring::AlertSender}, + interpret::{InterpretError, Outcome}, + inventory::Inventory, + score, + topology::{ + HelmCommand, K8sAnywhereTopology, Topology, installable::Installable, + oberservability::monitoring::AlertSender, + }, }; -use super::helm::config::KubePrometheusConfig; +use score::Score; -impl AlertSender for Prometheus {} +use super::helm::{ + config::KubePrometheusConfig, kube_prometheus_helm_chart::kube_prometheus_helm_chart_score, +}; + +#[async_trait] +impl AlertSender for Prometheus { + fn name(&self) -> String { + "HelmKubePrometheus".to_string() + } + async fn install(&self) -> Result { + self.install_prometheus(); + todo!() + } +} + +//im not totally sure what to do in the impl installable +//should we have a oncecell that checks insured is true? #[async_trait] impl Installable for Prometheus { @@ -18,32 +41,63 @@ impl Installable for Prometheus { } } +//before we talked about having a trait installable and a trait installer for the topology +// i feel like that might still be necessary to meet the requirement of inventory and topology on +// the score.create_interpret().execute(inventory, topology) method +#[async_trait] +trait Installer { + async fn install(&self, sender: Box<&dyn Installable>) -> Result<(), InterpretError>; +} + +#[async_trait] +impl Installer for K8sAnywhereTopology { + async fn install(&self, installable: Box<&dyn Installable>) -> Result<(), InterpretError> { + installable.install().await?; + Ok(()) + } +} + #[derive(Debug)] -pub struct Prometheus{ - pub config: KubePrometheusConfig, +pub struct Prometheus { + pub config: Mutex, } impl Prometheus { pub async fn install_receiver( &self, prometheus_receiver: &dyn PrometheusReceiver, - ) -> Result<(), InterpretError> { + ) -> Result { + let prom_receiver = prometheus_receiver.receiver_config().await; + debug!( + "adding alert receiver to prometheus config: {:#?}", + &prom_receiver + ); + let mut config = self.config.lock().unwrap(); - - debug!("adding alert receiver to prometheus config: {:#?}", prometheus_receiver.receiver_config()); - self.config.alert_receiver_configs.push(prometheus_receiver.receiver_config()); - todo!() + config.alert_receiver_configs.push(prom_receiver); + let prom_receiver_name = prometheus_receiver.name(); + debug!("installed alert receiver {}", &prom_receiver_name); + Ok(Outcome::success(format!( + "Sucessfully installed receiver {}", + prom_receiver_name + ))) + } + + pub async fn install_prometheus(&self) -> Result { + kube_prometheus_helm_chart_score(self.config.lock().unwrap().clone()) + .create_interpret() + .execute() + .await } } #[async_trait] -pub trait PrometheusReceiver: Send + Sync + std::fmt::Debug{ - async fn receiver_config(&self) -> (Option, Value, Value); +pub trait PrometheusReceiver: Send + Sync + std::fmt::Debug { + fn name(&self) -> String; + async fn receiver_config(&self) -> Value; //this probably needs to be a type - //that - //represents - //a - //promtheusreceiver + //that + //represents + //a + //promtheusreceiver } - - -- 2.39.5 From 3c20574e6fa7b6d5c474d4ae98940b3139943e9f Mon Sep 17 00:00:00 2001 From: tahahawa Date: Fri, 20 Jun 2025 00:25:29 -0400 Subject: [PATCH 4/9] make it compile, need to think about Installer/Installable concept a bit more --- Cargo.lock | 1 + .../topology/oberservability/monitoring.rs | 8 +++-- .../helm/kube_prometheus_helm_chart.rs | 18 +++++++---- .../helm_prometheus_alert_score.rs | 6 ++-- .../monitoring/kube_prometheus/prometheus.rs | 31 ++++++++++++------- 5 files changed, 42 insertions(+), 22 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 0ee6318..18f8abf 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1161,6 +1161,7 @@ dependencies = [ "harmony", "harmony_cli", "tokio", + "url", ] [[package]] diff --git a/harmony/src/domain/topology/oberservability/monitoring.rs b/harmony/src/domain/topology/oberservability/monitoring.rs index 61e494a..51e3053 100644 --- a/harmony/src/domain/topology/oberservability/monitoring.rs +++ b/harmony/src/domain/topology/oberservability/monitoring.rs @@ -4,13 +4,17 @@ use crate::{ data::{Id, Version}, interpret::{Interpret, InterpretError, InterpretName, InterpretStatus, Outcome}, inventory::Inventory, - topology::{Topology, installable::Installable}, + modules::monitoring::kube_prometheus::prometheus::Installer, + topology::{HelmCommand, Topology, installable::Installable}, }; #[async_trait] pub trait AlertSender: Send + Sync + std::fmt::Debug + Installable { fn name(&self) -> String; - async fn install(&self) -> Result; + async fn install( + &self, + topology: T, + ) -> Result; } #[derive(Debug)] diff --git a/harmony/src/modules/monitoring/kube_prometheus/helm/kube_prometheus_helm_chart.rs b/harmony/src/modules/monitoring/kube_prometheus/helm/kube_prometheus_helm_chart.rs index e8c2dfa..5f2baaf 100644 --- a/harmony/src/modules/monitoring/kube_prometheus/helm/kube_prometheus_helm_chart.rs +++ b/harmony/src/modules/monitoring/kube_prometheus/helm/kube_prometheus_helm_chart.rs @@ -1,10 +1,16 @@ use super::config::KubePrometheusConfig; use non_blank_string_rs::NonBlankString; -use std::str::FromStr; +use std::{ + str::FromStr, + sync::{Arc, Mutex}, +}; use crate::modules::helm::chart::HelmChartScore; -pub fn kube_prometheus_helm_chart_score(config: KubePrometheusConfig) -> HelmChartScore { +pub fn kube_prometheus_helm_chart_score( + config: Arc>, +) -> HelmChartScore { + let config = config.lock().unwrap(); //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(); @@ -60,14 +66,14 @@ additionalPrometheusRulesMap: - alert: 'PVC Fill Over 95 Percent In 2 Days' expr: | ( - kubelet_volume_stats_used_bytes - / + kubelet_volume_stats_used_bytes + / kubelet_volume_stats_capacity_bytes ) > 0.95 AND predict_linear(kubelet_volume_stats_used_bytes[2d], 2 * 24 * 60 * 60) - / - kubelet_volume_stats_capacity_bytes + / + kubelet_volume_stats_capacity_bytes > 0.95 for: 1m labels: 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 index 04853fd..1d8cbaf 100644 --- a/harmony/src/modules/monitoring/kube_prometheus/helm_prometheus_alert_score.rs +++ b/harmony/src/modules/monitoring/kube_prometheus/helm_prometheus_alert_score.rs @@ -1,4 +1,4 @@ -use std::sync::Mutex; +use std::sync::{Arc, Mutex}; use serde::Serialize; @@ -18,11 +18,11 @@ pub struct HelmPrometheusAlertingScore { pub receivers: Vec>>, } -impl Score for HelmPrometheusAlertingScore { +impl Score for HelmPrometheusAlertingScore { fn create_interpret(&self) -> Box> { Box::new(AlertingInterpret { sender: Prometheus { - config: Mutex::new(KubePrometheusConfig::new()), + config: Arc::new(Mutex::new(KubePrometheusConfig::new())), }, receivers: self.receivers.clone(), }) diff --git a/harmony/src/modules/monitoring/kube_prometheus/prometheus.rs b/harmony/src/modules/monitoring/kube_prometheus/prometheus.rs index 222245a..a45ec94 100644 --- a/harmony/src/modules/monitoring/kube_prometheus/prometheus.rs +++ b/harmony/src/modules/monitoring/kube_prometheus/prometheus.rs @@ -1,4 +1,4 @@ -use std::sync::Mutex; +use std::sync::{Arc, Mutex}; use async_trait::async_trait; use log::debug; @@ -25,8 +25,11 @@ impl AlertSender for Prometheus { fn name(&self) -> String { "HelmKubePrometheus".to_string() } - async fn install(&self) -> Result { - self.install_prometheus(); + async fn install( + &self, + topology: T, + ) -> Result { + let _ = self.install_prometheus(topology).await; todo!() } } @@ -45,21 +48,24 @@ impl Installable for Prometheus { // i feel like that might still be necessary to meet the requirement of inventory and topology on // the score.create_interpret().execute(inventory, topology) method #[async_trait] -trait Installer { - async fn install(&self, sender: Box<&dyn Installable>) -> Result<(), InterpretError>; +pub trait Installer { + async fn install(&self, sender: I) -> Result<(), InterpretError>; } #[async_trait] impl Installer for K8sAnywhereTopology { - async fn install(&self, installable: Box<&dyn Installable>) -> Result<(), InterpretError> { - installable.install().await?; + async fn install( + &self, + installable: I, + ) -> Result<(), InterpretError> { + installable.ensure_installed().await?; Ok(()) } } #[derive(Debug)] pub struct Prometheus { - pub config: Mutex, + pub config: Arc>, } impl Prometheus { @@ -83,10 +89,13 @@ impl Prometheus { ))) } - pub async fn install_prometheus(&self) -> Result { - kube_prometheus_helm_chart_score(self.config.lock().unwrap().clone()) + pub async fn install_prometheus( + &self, + topology: T, + ) -> Result { + kube_prometheus_helm_chart_score(self.config.clone()) .create_interpret() - .execute() + .execute(&Inventory::autoload(), &topology) .await } } -- 2.39.5 From b1755c183d21a69955131ebc0ba8c7ee33a8b074 Mon Sep 17 00:00:00 2001 From: Willem Date: Fri, 20 Jun 2025 09:12:54 -0400 Subject: [PATCH 5/9] fix: install_prometheus takes inventory from the maestro rather than inventory::autoload --- .../src/domain/topology/oberservability/monitoring.rs | 5 +++-- .../modules/monitoring/kube_prometheus/prometheus.rs | 10 ++++++---- 2 files changed, 9 insertions(+), 6 deletions(-) diff --git a/harmony/src/domain/topology/oberservability/monitoring.rs b/harmony/src/domain/topology/oberservability/monitoring.rs index 51e3053..c4d19d5 100644 --- a/harmony/src/domain/topology/oberservability/monitoring.rs +++ b/harmony/src/domain/topology/oberservability/monitoring.rs @@ -13,7 +13,8 @@ pub trait AlertSender: Send + Sync + std::fmt::Debug + Installable { fn name(&self) -> String; async fn install( &self, - topology: T, + inventory: &Inventory, + topology: &T, ) -> Result; } @@ -33,7 +34,7 @@ impl Interpret for AlertingInterpr for receiver in self.receivers.iter() { receiver.install(&self.sender).await?; } - self.sender.ensure_installed(); + self.sender.ensure_installed().await?; Ok(Outcome::success(format!( "successfully installed alert sender {}", self.sender.name() diff --git a/harmony/src/modules/monitoring/kube_prometheus/prometheus.rs b/harmony/src/modules/monitoring/kube_prometheus/prometheus.rs index a45ec94..68e7d46 100644 --- a/harmony/src/modules/monitoring/kube_prometheus/prometheus.rs +++ b/harmony/src/modules/monitoring/kube_prometheus/prometheus.rs @@ -27,9 +27,10 @@ impl AlertSender for Prometheus { } async fn install( &self, - topology: T, + inventory: &Inventory, + topology: &T, ) -> Result { - let _ = self.install_prometheus(topology).await; + let _ = self.install_prometheus(inventory, topology).await; todo!() } } @@ -91,11 +92,12 @@ impl Prometheus { pub async fn install_prometheus( &self, - topology: T, + inventory: &Inventory, + topology: &T, ) -> Result { kube_prometheus_helm_chart_score(self.config.clone()) .create_interpret() - .execute(&Inventory::autoload(), &topology) + .execute(inventory, topology) .await } } -- 2.39.5 From 2cb95151aeb508a6cfe1cba017d893003d17b8d7 Mon Sep 17 00:00:00 2001 From: Willem Date: Fri, 20 Jun 2025 15:48:54 -0400 Subject: [PATCH 6/9] feat: deploys kube-prometheus with discord alert receiver --- examples/monitoring/src/main.rs | 2 +- harmony/src/domain/topology/installable.rs | 12 ++-- .../topology/oberservability/monitoring.rs | 12 +--- .../alert_channel/discord_alert_channel.rs | 22 +++++-- .../monitoring/kube_prometheus/helm/config.rs | 4 +- .../helm/kube_prometheus_helm_chart.rs | 62 +++++++++++++++++- .../monitoring/kube_prometheus/prometheus.rs | 64 ++++++++----------- .../monitoring/kube_prometheus/types.rs | 39 +++++++++-- 8 files changed, 151 insertions(+), 66 deletions(-) diff --git a/examples/monitoring/src/main.rs b/examples/monitoring/src/main.rs index d3eb3b8..c0fcf33 100644 --- a/examples/monitoring/src/main.rs +++ b/examples/monitoring/src/main.rs @@ -12,7 +12,7 @@ use harmony::{ async fn main() { let discord_receiver = DiscordWebhook { name: "test-discord".to_string(), - url: Url::Url(url::Url::parse("https://discord.i.dont.exist.com").unwrap()), + url: Url::Url(url::Url::parse("discord.doesnt.exist.com").unwrap()), }; let alerting_score = HelmPrometheusAlertingScore { receivers: vec![Box::new(discord_receiver)], diff --git a/harmony/src/domain/topology/installable.rs b/harmony/src/domain/topology/installable.rs index 0092065..8d8178c 100644 --- a/harmony/src/domain/topology/installable.rs +++ b/harmony/src/domain/topology/installable.rs @@ -1,10 +1,12 @@ use async_trait::async_trait; -use crate::interpret::InterpretError; - -use super::oberservability::monitoring::AlertSender; +use crate::{interpret::InterpretError, inventory::Inventory}; #[async_trait] -pub trait Installable { - async fn ensure_installed(&self) -> Result<(), InterpretError>; +pub trait Installable: Send + Sync { + async fn ensure_installed( + &self, + inventory: &Inventory, + topology: &T, + ) -> Result<(), InterpretError>; } diff --git a/harmony/src/domain/topology/oberservability/monitoring.rs b/harmony/src/domain/topology/oberservability/monitoring.rs index c4d19d5..c66cfc2 100644 --- a/harmony/src/domain/topology/oberservability/monitoring.rs +++ b/harmony/src/domain/topology/oberservability/monitoring.rs @@ -4,18 +4,12 @@ use crate::{ data::{Id, Version}, interpret::{Interpret, InterpretError, InterpretName, InterpretStatus, Outcome}, inventory::Inventory, - modules::monitoring::kube_prometheus::prometheus::Installer, topology::{HelmCommand, Topology, installable::Installable}, }; #[async_trait] -pub trait AlertSender: Send + Sync + std::fmt::Debug + Installable { +pub trait AlertSender: Send + Sync + std::fmt::Debug { fn name(&self) -> String; - async fn install( - &self, - inventory: &Inventory, - topology: &T, - ) -> Result; } #[derive(Debug)] @@ -25,7 +19,7 @@ pub struct AlertingInterpret { } #[async_trait] -impl Interpret for AlertingInterpret { +impl, T: Topology + HelmCommand> Interpret for AlertingInterpret { async fn execute( &self, inventory: &Inventory, @@ -34,7 +28,7 @@ impl Interpret for AlertingInterpr for receiver in self.receivers.iter() { receiver.install(&self.sender).await?; } - self.sender.ensure_installed().await?; + self.sender.ensure_installed(inventory, topology).await?; Ok(Outcome::success(format!( "successfully installed alert sender {}", self.sender.name() 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 8fa7523..05e313e 100644 --- a/harmony/src/modules/monitoring/alert_channel/discord_alert_channel.rs +++ b/harmony/src/modules/monitoring/alert_channel/discord_alert_channel.rs @@ -5,7 +5,7 @@ use crate::{ interpret::{InterpretError, Outcome}, modules::monitoring::kube_prometheus::{ prometheus::{Prometheus, PrometheusReceiver}, - types::AlertChannelConfig, + types::{AlertChannelConfig, AlertManagerChannelConfig}, }, topology::{Url, oberservability::monitoring::AlertReceiver}, }; @@ -28,20 +28,30 @@ impl AlertReceiver for DiscordWebhook { #[async_trait] impl PrometheusReceiver for DiscordWebhook { - //TODO not return a tuple fn name(&self) -> String { self.name.clone() } - async fn receiver_config(&self) -> Value { - self.alert_channel_receiver().await + async fn configure_receiver(&self) -> AlertManagerChannelConfig { + self.get_config().await } } #[async_trait] impl AlertChannelConfig for DiscordWebhook { - async fn alert_channel_global_config(&self) -> Option { - None + async fn get_config(&self) -> AlertManagerChannelConfig { + let channel_global_config = None; + let channel_receiver = self.alert_channel_receiver().await; + let channel_route = self.alert_channel_route().await; + + AlertManagerChannelConfig { + channel_global_config, + channel_receiver, + channel_route, + } } +} + +impl DiscordWebhook { async fn alert_channel_route(&self) -> serde_yaml::Value { let mut route = Mapping::new(); diff --git a/harmony/src/modules/monitoring/kube_prometheus/helm/config.rs b/harmony/src/modules/monitoring/kube_prometheus/helm/config.rs index 39d449f..631d9d8 100644 --- a/harmony/src/modules/monitoring/kube_prometheus/helm/config.rs +++ b/harmony/src/modules/monitoring/kube_prometheus/helm/config.rs @@ -1,7 +1,7 @@ use serde::Serialize; use serde_yaml::Value; -use crate::modules::monitoring::kube_prometheus::prometheus::PrometheusReceiver; +use crate::modules::monitoring::kube_prometheus::{prometheus::PrometheusReceiver, types::AlertManagerChannelConfig}; #[derive(Debug, Clone, Serialize)] pub struct KubePrometheusConfig { @@ -22,7 +22,7 @@ pub struct KubePrometheusConfig { pub kube_proxy: bool, pub kube_state_metrics: bool, pub prometheus_operator: bool, - pub alert_receiver_configs: Vec, + pub alert_receiver_configs: Vec, } impl KubePrometheusConfig { pub fn new() -> Self { diff --git a/harmony/src/modules/monitoring/kube_prometheus/helm/kube_prometheus_helm_chart.rs b/harmony/src/modules/monitoring/kube_prometheus/helm/kube_prometheus_helm_chart.rs index 5f2baaf..8420fca 100644 --- a/harmony/src/modules/monitoring/kube_prometheus/helm/kube_prometheus_helm_chart.rs +++ b/harmony/src/modules/monitoring/kube_prometheus/helm/kube_prometheus_helm_chart.rs @@ -1,11 +1,19 @@ use super::config::KubePrometheusConfig; +use log::debug; use non_blank_string_rs::NonBlankString; +use serde_yaml::{Mapping, Value}; use std::{ str::FromStr, sync::{Arc, Mutex}, }; -use crate::modules::helm::chart::HelmChartScore; +use crate::modules::{ + helm::chart::HelmChartScore, + monitoring::kube_prometheus::types::{ + AlertManager, AlertManagerChannelConfig, AlertManagerConfig, AlertManagerRoute, + AlertManagerValues, + }, +}; pub fn kube_prometheus_helm_chart_score( config: Arc>, @@ -15,11 +23,11 @@ pub fn kube_prometheus_helm_chart_score( //to the overrides or something leaving the user to deal with formatting here seems bad let default_rules = config.default_rules.to_string(); let windows_monitoring = config.windows_monitoring.to_string(); - let alert_manager = config.alert_manager.to_string(); let grafana = config.grafana.to_string(); let kubernetes_service_monitors = config.kubernetes_service_monitors.to_string(); let kubernetes_api_server = config.kubernetes_api_server.to_string(); let kubelet = config.kubelet.to_string(); + let alert_manager = config.alert_manager.to_string(); let kube_controller_manager = config.kube_controller_manager.to_string(); let core_dns = config.core_dns.to_string(); let kube_etcd = config.kube_etcd.to_string(); @@ -29,7 +37,7 @@ pub fn kube_prometheus_helm_chart_score( let node_exporter = config.node_exporter.to_string(); let prometheus_operator = config.prometheus_operator.to_string(); let prometheus = config.prometheus.to_string(); - let values = format!( + let mut values = format!( r#" additionalPrometheusRulesMap: pods-status-alerts: @@ -148,6 +156,54 @@ prometheus: enabled: {prometheus} "#, ); + + + let mut null_receiver = Mapping::new(); + null_receiver.insert( + Value::String("receiver".to_string()), + Value::String("null".to_string()), + ); + null_receiver.insert( + Value::String("matchers".to_string()), + Value::Sequence(vec![Value::String("alertname!=Watchdog".to_string())]), + ); + null_receiver.insert(Value::String("continue".to_string()), Value::Bool(true)); + + + let mut alert_manager_channel_config = AlertManagerConfig { + global: Mapping::new(), + route: AlertManagerRoute { + routes: vec![Value::Mapping(null_receiver)], + }, + receivers: vec![serde_yaml::from_str("name: 'null'").unwrap()], + }; + for receiver in config.alert_receiver_configs.iter() { + if let Some(global) = receiver.channel_global_config.clone() { + alert_manager_channel_config + .global + .insert(global.0, global.1); + } + alert_manager_channel_config + .route + .routes + .push(receiver.channel_route.clone()); + alert_manager_channel_config + .receivers + .push(receiver.channel_receiver.clone()); + } + + let alert_manager_values = AlertManagerValues { + alertmanager: AlertManager { + enabled: config.alert_manager, + config: alert_manager_channel_config, + }, + }; + + let alert_manager_yaml = + serde_yaml::to_string(&alert_manager_values).expect("Failed to serialize YAML"); + debug!("serialed alert manager: \n {:#}", alert_manager_yaml); + values.push_str(&alert_manager_yaml); + debug!("full values.yaml: \n {:#}", values); HelmChartScore { namespace: Some(NonBlankString::from_str(&config.namespace).unwrap()), release_name: NonBlankString::from_str("kube-prometheus").unwrap(), diff --git a/harmony/src/modules/monitoring/kube_prometheus/prometheus.rs b/harmony/src/modules/monitoring/kube_prometheus/prometheus.rs index 68e7d46..f4a81d8 100644 --- a/harmony/src/modules/monitoring/kube_prometheus/prometheus.rs +++ b/harmony/src/modules/monitoring/kube_prometheus/prometheus.rs @@ -16,54 +16,46 @@ use crate::{ use score::Score; -use super::helm::{ +use super::{helm::{ config::KubePrometheusConfig, kube_prometheus_helm_chart::kube_prometheus_helm_chart_score, -}; +}, types::AlertManagerChannelConfig}; #[async_trait] impl AlertSender for Prometheus { fn name(&self) -> String { "HelmKubePrometheus".to_string() } - async fn install( - &self, - inventory: &Inventory, - topology: &T, - ) -> Result { - let _ = self.install_prometheus(inventory, topology).await; - todo!() - } -} - -//im not totally sure what to do in the impl installable -//should we have a oncecell that checks insured is true? - -#[async_trait] -impl Installable for Prometheus { - async fn ensure_installed(&self) -> Result<(), InterpretError> { - todo!() - } -} - -//before we talked about having a trait installable and a trait installer for the topology -// i feel like that might still be necessary to meet the requirement of inventory and topology on -// the score.create_interpret().execute(inventory, topology) method -#[async_trait] -pub trait Installer { - async fn install(&self, sender: I) -> Result<(), InterpretError>; } #[async_trait] -impl Installer for K8sAnywhereTopology { - async fn install( - &self, - installable: I, - ) -> Result<(), InterpretError> { - installable.ensure_installed().await?; +impl Installable for Prometheus { + async fn ensure_installed(&self, inventory: &Inventory, topology: &T) -> Result<(), InterpretError> { + //install_prometheus + self.install_prometheus(inventory, topology).await?; Ok(()) } } +// //before we talked about having a trait installable and a trait installer for the topology +// // i feel like that might still be necessary to meet the requirement of inventory and topology on +// // the score.create_interpret().execute(inventory, topology) method +// #[async_trait] +// pub trait Installer { +// async fn install(&self, inventory: &Inventory, sender: Box) -> Result<(), InterpretError>; +// } +// +// #[async_trait] +// impl Installer for K8sAnywhereTopology { +// async fn install( +// &self, +// inventory: &Inventory, +// installable: Box>, +// ) -> Result<(), InterpretError> { +// installable.ensure_installed(inventory, self).await?; +// Ok(()) +// } +// } + #[derive(Debug)] pub struct Prometheus { pub config: Arc>, @@ -74,7 +66,7 @@ impl Prometheus { &self, prometheus_receiver: &dyn PrometheusReceiver, ) -> Result { - let prom_receiver = prometheus_receiver.receiver_config().await; + let prom_receiver = prometheus_receiver.configure_receiver().await; debug!( "adding alert receiver to prometheus config: {:#?}", &prom_receiver @@ -105,7 +97,7 @@ impl Prometheus { #[async_trait] pub trait PrometheusReceiver: Send + Sync + std::fmt::Debug { fn name(&self) -> String; - async fn receiver_config(&self) -> Value; + async fn configure_receiver(&self) -> AlertManagerChannelConfig; //this probably needs to be a type //that //represents diff --git a/harmony/src/modules/monitoring/kube_prometheus/types.rs b/harmony/src/modules/monitoring/kube_prometheus/types.rs index d6f28d2..f237bba 100644 --- a/harmony/src/modules/monitoring/kube_prometheus/types.rs +++ b/harmony/src/modules/monitoring/kube_prometheus/types.rs @@ -1,9 +1,40 @@ use async_trait::async_trait; -use serde_yaml::Value; +use serde::Serialize; +use serde_yaml::{Mapping, Sequence, Value}; #[async_trait] pub trait AlertChannelConfig { - async fn alert_channel_global_config(&self) -> Option; - async fn alert_channel_route(&self) -> Value; - async fn alert_channel_receiver(&self) -> Value; + async fn get_config(&self) -> AlertManagerChannelConfig; +} + +#[derive(Debug, Clone, Serialize)] +pub struct AlertManagerValues { + pub alertmanager: AlertManager, +} +#[derive(Debug, Clone, Serialize)] +pub struct AlertManager { + pub enabled: bool, + pub config: AlertManagerConfig, +} + +#[derive(Debug, Clone, Serialize)] +pub struct AlertManagerConfig { + pub global: Mapping, + pub route: AlertManagerRoute, + pub receivers: Sequence, +} + +#[derive(Debug, Clone, Serialize)] +pub struct AlertManagerRoute { + pub routes: Sequence, +} + +#[derive(Debug, Clone, Serialize)] +pub struct AlertManagerChannelConfig { + ///expecting an option that contains two values + ///if necessary for the alertchannel + ///[ jira_api_url: ] + pub channel_global_config: Option<(Value, Value)>, + pub channel_route: Value, + pub channel_receiver: Value, } -- 2.39.5 From dec4b76b577530babd153a3c37003648ffb66c1a Mon Sep 17 00:00:00 2001 From: tahahawa Date: Fri, 20 Jun 2025 16:10:16 -0400 Subject: [PATCH 7/9] fix checks --- examples/lamp/src/main.rs | 2 +- .../topology/oberservability/monitoring.rs | 4 +++- .../alert_channel/discord_alert_channel.rs | 12 +++++----- .../monitoring/kube_prometheus/helm/config.rs | 3 +-- .../helm/kube_prometheus_helm_chart.rs | 23 ++++++++----------- .../helm_prometheus_alert_score.rs | 1 - .../monitoring/kube_prometheus/prometheus.rs | 19 +++++++++------ 7 files changed, 33 insertions(+), 31 deletions(-) diff --git a/examples/lamp/src/main.rs b/examples/lamp/src/main.rs index 4518165..276e255 100644 --- a/examples/lamp/src/main.rs +++ b/examples/lamp/src/main.rs @@ -43,7 +43,7 @@ async fn main() { // 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 // that automatically adapt to each environment grade. - let mut maestro = Maestro::::initialize( + let maestro = Maestro::::initialize( Inventory::autoload(), K8sAnywhereTopology::from_env(), ) diff --git a/harmony/src/domain/topology/oberservability/monitoring.rs b/harmony/src/domain/topology/oberservability/monitoring.rs index c66cfc2..3cdd1bd 100644 --- a/harmony/src/domain/topology/oberservability/monitoring.rs +++ b/harmony/src/domain/topology/oberservability/monitoring.rs @@ -19,7 +19,9 @@ pub struct AlertingInterpret { } #[async_trait] -impl, T: Topology + HelmCommand> Interpret for AlertingInterpret { +impl, T: Topology + HelmCommand> Interpret + for AlertingInterpret +{ async fn execute( &self, inventory: &Inventory, 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 05e313e..fb85a98 100644 --- a/harmony/src/modules/monitoring/alert_channel/discord_alert_channel.rs +++ b/harmony/src/modules/monitoring/alert_channel/discord_alert_channel.rs @@ -1,4 +1,5 @@ use async_trait::async_trait; +use serde::Serialize; use serde_yaml::{Mapping, Value}; use crate::{ @@ -10,7 +11,7 @@ use crate::{ topology::{Url, oberservability::monitoring::AlertReceiver}, }; -#[derive(Debug, Clone)] +#[derive(Debug, Clone, Serialize)] pub struct DiscordWebhook { pub name: String, pub url: Url, @@ -52,7 +53,6 @@ impl AlertChannelConfig for DiscordWebhook { } impl DiscordWebhook { - async fn alert_channel_route(&self) -> serde_yaml::Value { let mut route = Mapping::new(); route.insert( @@ -93,15 +93,15 @@ impl DiscordWebhook { mod tests { use super::*; - #[test] - fn discord_serialize_should_match() { + #[tokio::test] + async fn discord_serialize_should_match() { let discord_receiver = DiscordWebhook { name: "test-discord".to_string(), url: Url::Url(url::Url::parse("https://discord.i.dont.exist.com").unwrap()), }; let discord_receiver_receiver = - serde_yaml::to_string(&discord_receiver.alert_channel_receiver()).unwrap(); + serde_yaml::to_string(&discord_receiver.alert_channel_receiver().await).unwrap(); println!("receiver \n{:#}", discord_receiver_receiver); let discord_receiver_receiver_yaml = r#"name: test-discord discord_configs: @@ -110,7 +110,7 @@ discord_configs: .to_string(); let discord_receiver_route = - serde_yaml::to_string(&discord_receiver.alert_channel_route()).unwrap(); + serde_yaml::to_string(&discord_receiver.alert_channel_route().await).unwrap(); println!("route \n{:#}", discord_receiver_route); let discord_receiver_route_yaml = r#"receiver: test-discord matchers: diff --git a/harmony/src/modules/monitoring/kube_prometheus/helm/config.rs b/harmony/src/modules/monitoring/kube_prometheus/helm/config.rs index 631d9d8..741cd1b 100644 --- a/harmony/src/modules/monitoring/kube_prometheus/helm/config.rs +++ b/harmony/src/modules/monitoring/kube_prometheus/helm/config.rs @@ -1,7 +1,6 @@ use serde::Serialize; -use serde_yaml::Value; -use crate::modules::monitoring::kube_prometheus::{prometheus::PrometheusReceiver, types::AlertManagerChannelConfig}; +use crate::modules::monitoring::kube_prometheus::types::AlertManagerChannelConfig; #[derive(Debug, Clone, Serialize)] pub struct KubePrometheusConfig { diff --git a/harmony/src/modules/monitoring/kube_prometheus/helm/kube_prometheus_helm_chart.rs b/harmony/src/modules/monitoring/kube_prometheus/helm/kube_prometheus_helm_chart.rs index 8420fca..c262cae 100644 --- a/harmony/src/modules/monitoring/kube_prometheus/helm/kube_prometheus_helm_chart.rs +++ b/harmony/src/modules/monitoring/kube_prometheus/helm/kube_prometheus_helm_chart.rs @@ -10,8 +10,7 @@ use std::{ use crate::modules::{ helm::chart::HelmChartScore, monitoring::kube_prometheus::types::{ - AlertManager, AlertManagerChannelConfig, AlertManagerConfig, AlertManagerRoute, - AlertManagerValues, + AlertManager, AlertManagerConfig, AlertManagerRoute, AlertManagerValues, }, }; @@ -157,18 +156,16 @@ prometheus: "#, ); - let mut null_receiver = Mapping::new(); - null_receiver.insert( - Value::String("receiver".to_string()), - Value::String("null".to_string()), - ); - null_receiver.insert( - Value::String("matchers".to_string()), - Value::Sequence(vec![Value::String("alertname!=Watchdog".to_string())]), - ); - null_receiver.insert(Value::String("continue".to_string()), Value::Bool(true)); - + null_receiver.insert( + Value::String("receiver".to_string()), + Value::String("null".to_string()), + ); + null_receiver.insert( + Value::String("matchers".to_string()), + Value::Sequence(vec![Value::String("alertname!=Watchdog".to_string())]), + ); + null_receiver.insert(Value::String("continue".to_string()), Value::Bool(true)); let mut alert_manager_channel_config = AlertManagerConfig { global: Mapping::new(), 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 index 1d8cbaf..f1f5322 100644 --- a/harmony/src/modules/monitoring/kube_prometheus/helm_prometheus_alert_score.rs +++ b/harmony/src/modules/monitoring/kube_prometheus/helm_prometheus_alert_score.rs @@ -6,7 +6,6 @@ use crate::{ score::Score, topology::{ HelmCommand, Topology, - installable::Installable, oberservability::monitoring::{AlertReceiver, AlertingInterpret}, }, }; diff --git a/harmony/src/modules/monitoring/kube_prometheus/prometheus.rs b/harmony/src/modules/monitoring/kube_prometheus/prometheus.rs index f4a81d8..e7ddb9a 100644 --- a/harmony/src/modules/monitoring/kube_prometheus/prometheus.rs +++ b/harmony/src/modules/monitoring/kube_prometheus/prometheus.rs @@ -2,23 +2,24 @@ use std::sync::{Arc, Mutex}; use async_trait::async_trait; use log::debug; -use serde_yaml::Value; use crate::{ interpret::{InterpretError, Outcome}, inventory::Inventory, score, topology::{ - HelmCommand, K8sAnywhereTopology, Topology, installable::Installable, - oberservability::monitoring::AlertSender, + HelmCommand, Topology, installable::Installable, oberservability::monitoring::AlertSender, }, }; use score::Score; -use super::{helm::{ - config::KubePrometheusConfig, kube_prometheus_helm_chart::kube_prometheus_helm_chart_score, -}, types::AlertManagerChannelConfig}; +use super::{ + helm::{ + config::KubePrometheusConfig, kube_prometheus_helm_chart::kube_prometheus_helm_chart_score, + }, + types::AlertManagerChannelConfig, +}; #[async_trait] impl AlertSender for Prometheus { @@ -29,7 +30,11 @@ impl AlertSender for Prometheus { #[async_trait] impl Installable for Prometheus { - async fn ensure_installed(&self, inventory: &Inventory, topology: &T) -> Result<(), InterpretError> { + async fn ensure_installed( + &self, + inventory: &Inventory, + topology: &T, + ) -> Result<(), InterpretError> { //install_prometheus self.install_prometheus(inventory, topology).await?; Ok(()) -- 2.39.5 From 55da0cb1ea738df7b4dcbf26432b938cc730e128 Mon Sep 17 00:00:00 2001 From: Willem Date: Tue, 24 Jun 2025 09:45:39 -0400 Subject: [PATCH 8/9] removed unnecessary files --- examples/lamp/php/Dockerfile | 18 ------------------ examples/lamp/php/docker-php.ini | 16 ---------------- 2 files changed, 34 deletions(-) delete mode 100644 examples/lamp/php/Dockerfile delete mode 100644 examples/lamp/php/docker-php.ini diff --git a/examples/lamp/php/Dockerfile b/examples/lamp/php/Dockerfile deleted file mode 100644 index df5f4fa..0000000 --- a/examples/lamp/php/Dockerfile +++ /dev/null @@ -1,18 +0,0 @@ -FROM php:8.4-apache -ENV PHP_MEMORY_LIMIT=256M -ENV PHP_MAX_EXECUTION_TIME=30 -ENV PHP_ERROR_REPORTING="E_ERROR | E_WARNING | E_PARSE" -RUN apt-get update && apt-get install -y --no-install-recommends libfreetype6-dev libjpeg62-turbo-dev libpng-dev libzip-dev unzip && apt-get clean && rm -rf /var/lib/apt/lists/* -RUN docker-php-ext-configure gd --with-freetype --with-jpeg && docker-php-ext-install -j$(nproc) gd mysqli pdo_mysql zip opcache -RUN sed -i 's/VirtualHost \*:80/VirtualHost *:8080/' /etc/apache2/sites-available/000-default.conf && \ - sed -i 's/^Listen 80$/Listen 8080/' /etc/apache2/ports.conf -RUN mkdir -p /usr/local/etc/php/conf.d/ -COPY docker-php.ini /usr/local/etc/php/conf.d/docker-php.ini -RUN a2enmod headers && a2enmod rewrite && sed -i 's/ServerTokens OS/ServerTokens Prod/' /etc/apache2/conf-enabled/security.conf && sed -i 's/ServerSignature On/ServerSignature Off/' /etc/apache2/conf-enabled/security.conf -RUN echo 'PassEnv MYSQL_PASSWORD' >> /etc/apache2/sites-available/000-default.conf && echo 'PassEnv MYSQL_USER' >> /etc/apache2/sites-available/000-default.conf && echo 'PassEnv MYSQL_HOST' >> /etc/apache2/sites-available/000-default.conf -RUN groupadd -g 1000 appuser && useradd -u 1000 -g appuser -m -s /bin/bash appuser && chown -R appuser:appuser /var/www/html -WORKDIR /var/www/html -COPY . /var/www/html -RUN chown -R appuser:appuser /var/www/html -EXPOSE 8080/tcp -CMD apache2-foreground \ No newline at end of file diff --git a/examples/lamp/php/docker-php.ini b/examples/lamp/php/docker-php.ini deleted file mode 100644 index 3745ad5..0000000 --- a/examples/lamp/php/docker-php.ini +++ /dev/null @@ -1,16 +0,0 @@ - -memory_limit = ${PHP_MEMORY_LIMIT} -max_execution_time = ${PHP_MAX_EXECUTION_TIME} -error_reporting = ${PHP_ERROR_REPORTING} -display_errors = Off -log_errors = On -error_log = /dev/stderr -date.timezone = UTC - -; Opcache configuration for production -opcache.enable=1 -opcache.memory_consumption=128 -opcache.interned_strings_buffer=8 -opcache.max_accelerated_files=4000 -opcache.revalidate_freq=2 -opcache.fast_shutdown=1 -- 2.39.5 From 61c1d22b55afa4173784e05e00b322afe2fe1590 Mon Sep 17 00:00:00 2001 From: Willem Date: Tue, 24 Jun 2025 14:50:56 -0400 Subject: [PATCH 9/9] fix: change request from pr --- .../topology/oberservability/monitoring.rs | 4 +-- .../helm/kube_prometheus_helm_chart.rs | 2 +- .../monitoring/kube_prometheus/prometheus.rs | 25 ------------------- 3 files changed, 2 insertions(+), 29 deletions(-) diff --git a/harmony/src/domain/topology/oberservability/monitoring.rs b/harmony/src/domain/topology/oberservability/monitoring.rs index 3cdd1bd..7d65bf2 100644 --- a/harmony/src/domain/topology/oberservability/monitoring.rs +++ b/harmony/src/domain/topology/oberservability/monitoring.rs @@ -19,9 +19,7 @@ pub struct AlertingInterpret { } #[async_trait] -impl, T: Topology + HelmCommand> Interpret - for AlertingInterpret -{ +impl, T: Topology> Interpret for AlertingInterpret { async fn execute( &self, inventory: &Inventory, diff --git a/harmony/src/modules/monitoring/kube_prometheus/helm/kube_prometheus_helm_chart.rs b/harmony/src/modules/monitoring/kube_prometheus/helm/kube_prometheus_helm_chart.rs index c262cae..94440c0 100644 --- a/harmony/src/modules/monitoring/kube_prometheus/helm/kube_prometheus_helm_chart.rs +++ b/harmony/src/modules/monitoring/kube_prometheus/helm/kube_prometheus_helm_chart.rs @@ -198,7 +198,7 @@ prometheus: let alert_manager_yaml = serde_yaml::to_string(&alert_manager_values).expect("Failed to serialize YAML"); - debug!("serialed alert manager: \n {:#}", alert_manager_yaml); + debug!("serialized alert manager: \n {:#}", alert_manager_yaml); values.push_str(&alert_manager_yaml); debug!("full values.yaml: \n {:#}", values); HelmChartScore { diff --git a/harmony/src/modules/monitoring/kube_prometheus/prometheus.rs b/harmony/src/modules/monitoring/kube_prometheus/prometheus.rs index e7ddb9a..554d319 100644 --- a/harmony/src/modules/monitoring/kube_prometheus/prometheus.rs +++ b/harmony/src/modules/monitoring/kube_prometheus/prometheus.rs @@ -41,26 +41,6 @@ impl Installable for Prometheus { } } -// //before we talked about having a trait installable and a trait installer for the topology -// // i feel like that might still be necessary to meet the requirement of inventory and topology on -// // the score.create_interpret().execute(inventory, topology) method -// #[async_trait] -// pub trait Installer { -// async fn install(&self, inventory: &Inventory, sender: Box) -> Result<(), InterpretError>; -// } -// -// #[async_trait] -// impl Installer for K8sAnywhereTopology { -// async fn install( -// &self, -// inventory: &Inventory, -// installable: Box>, -// ) -> Result<(), InterpretError> { -// installable.ensure_installed(inventory, self).await?; -// Ok(()) -// } -// } - #[derive(Debug)] pub struct Prometheus { pub config: Arc>, @@ -103,9 +83,4 @@ impl Prometheus { pub trait PrometheusReceiver: Send + Sync + std::fmt::Debug { fn name(&self) -> String; async fn configure_receiver(&self) -> AlertManagerChannelConfig; - //this probably needs to be a type - //that - //represents - //a - //promtheusreceiver } -- 2.39.5