From 7b91088828e2815c37f2fc36ed95a92f22be6a8d Mon Sep 17 00:00:00 2001 From: Willem Date: Mon, 14 Jul 2025 13:41:48 -0400 Subject: [PATCH] feat: added impl for webhook receiver for crd alertmanagerconfigs --- examples/rust/src/main.rs | 27 ++++-- .../application/features/monitoring.rs | 21 ++-- .../alert_channel/discord_alert_channel.rs | 11 ++- .../alert_channel/webhook_receiver.rs | 96 ++++++++++++++++++- .../kube_prometheus/alert_manager_config.rs | 7 +- .../helm_prometheus_application_alerting.rs | 19 +++- .../modules/monitoring/kube_prometheus/mod.rs | 4 +- 7 files changed, 156 insertions(+), 29 deletions(-) diff --git a/examples/rust/src/main.rs b/examples/rust/src/main.rs index 3f13371..25d41b3 100644 --- a/examples/rust/src/main.rs +++ b/examples/rust/src/main.rs @@ -3,9 +3,15 @@ use std::{path::PathBuf, sync::Arc}; use harmony::{ inventory::Inventory, maestro::Maestro, - modules::{application::{ - features::{ContinuousDelivery, PrometheusApplicationMonitoring}, ApplicationScore, RustWebFramework, RustWebapp - }, monitoring::alert_channel::discord_alert_channel::DiscordWebhook}, + modules::{ + application::{ + ApplicationScore, RustWebFramework, RustWebapp, + features::{ContinuousDelivery, PrometheusApplicationMonitoring}, + }, + monitoring::alert_channel::{ + discord_alert_channel::DiscordWebhook, webhook_receiver::WebhookReceiver, + }, + }, topology::{K8sAnywhereTopology, Url}, }; @@ -24,16 +30,21 @@ async fn main() { url: Url::Url(url::Url::parse("https://discord.doesnt.exist.com").unwrap()), }; + let webhook_receiver = WebhookReceiver { + name: "sample-webhook-receiver".to_string(), + url: Url::Url(url::Url::parse("https://webhook-doesnt-exist.com").unwrap()), + }; + let app = ApplicationScore { features: vec![ - // Box::new(ContinuousDelivery { - // application: application.clone(), - // }), + Box::new(ContinuousDelivery { + application: application.clone(), + }), Box::new(PrometheusApplicationMonitoring { application: application.clone(), - alert_receiver: vec![Box::new(discord_receiver),] + alert_receiver: vec![Box::new(discord_receiver), Box::new(webhook_receiver)], }), - // TODO add monitoring, backups, multisite ha, etc + // TODO add backups, multisite ha, etc ], application, }; diff --git a/harmony/src/modules/application/features/monitoring.rs b/harmony/src/modules/application/features/monitoring.rs index 528e721..a1c9754 100644 --- a/harmony/src/modules/application/features/monitoring.rs +++ b/harmony/src/modules/application/features/monitoring.rs @@ -7,23 +7,28 @@ use crate::{ inventory::Inventory, modules::{ application::{Application, ApplicationFeature}, - monitoring:: - kube_prometheus::{ - alert_manager_config::{CRDAlertManager, CRDAlertManagerReceiver}, helm_prometheus_application_alerting::HelmPrometheusApplicationAlertingScore - } - , + monitoring::kube_prometheus::{ + alert_manager_config::{CRDAlertManager, CRDAlertManagerReceiver}, + helm_prometheus_application_alerting::HelmPrometheusApplicationAlertingScore, + }, }, score::Score, - topology::{oberservability::monitoring::AlertReceiver, tenant::TenantManager, HelmCommand, K8sclient, Topology}, + topology::{ + HelmCommand, K8sclient, Topology, oberservability::monitoring::AlertReceiver, + tenant::TenantManager, + }, }; #[derive(Debug, Clone)] pub struct PrometheusApplicationMonitoring { pub application: Arc, - pub alert_receiver: Vec>,} + pub alert_receiver: Vec>, +} #[async_trait] -impl ApplicationFeature for PrometheusApplicationMonitoring { +impl + ApplicationFeature for PrometheusApplicationMonitoring +{ async fn ensure_installed(&self, topology: &T) -> Result<(), String> { info!("Ensuring monitoring is available for application"); 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 4b1f1ba..9177052 100644 --- a/harmony/src/modules/monitoring/alert_channel/discord_alert_channel.rs +++ b/harmony/src/modules/monitoring/alert_channel/discord_alert_channel.rs @@ -91,8 +91,6 @@ impl CRDAlertManagerReceiver for DiscordWebhook { }, spec, }; - debug!(" am: \n{:#?}", am.clone()); - am } fn clone_box(&self) -> Box { @@ -103,7 +101,14 @@ impl CRDAlertManagerReceiver for DiscordWebhook { #[async_trait] impl AlertReceiver for DiscordWebhook { async fn install(&self, sender: &CRDAlertManager) -> Result { - todo!() + sender + .client + .apply(&sender.alertmanager_configs, Some(&sender.namespace)) + .await?; + Ok(Outcome::success(format!( + "installed crd-alertmanagerconfigs for {}", + self.name + ))) } fn name(&self) -> String { "discord-webhook".to_string() diff --git a/harmony/src/modules/monitoring/alert_channel/webhook_receiver.rs b/harmony/src/modules/monitoring/alert_channel/webhook_receiver.rs index d02be82..a3c5887 100644 --- a/harmony/src/modules/monitoring/alert_channel/webhook_receiver.rs +++ b/harmony/src/modules/monitoring/alert_channel/webhook_receiver.rs @@ -1,17 +1,27 @@ +use std::{collections::BTreeMap, sync::Arc}; + use async_trait::async_trait; +use k8s_openapi::api::core::v1::Secret; +use kube::api::ObjectMeta; +use log::debug; use serde::Serialize; +use serde_json::json; use serde_yaml::{Mapping, Value}; use crate::{ interpret::{InterpretError, Outcome}, modules::monitoring::{ kube_prometheus::{ + alert_manager_config::{ + AlertmanagerConfig, AlertmanagerConfigSpec, CRDAlertManager, + CRDAlertManagerReceiver, + }, prometheus::{KubePrometheus, KubePrometheusReceiver}, types::{AlertChannelConfig, AlertManagerChannelConfig}, }, prometheus::prometheus::{Prometheus, PrometheusReceiver}, }, - topology::{Url, oberservability::monitoring::AlertReceiver}, + topology::{Url, k8s::K8sClient, oberservability::monitoring::AlertReceiver}, }; #[derive(Debug, Clone, Serialize)] @@ -20,6 +30,90 @@ pub struct WebhookReceiver { pub url: Url, } +#[async_trait] +impl CRDAlertManagerReceiver for WebhookReceiver { + fn name(&self) -> String { + self.name.clone() + } + + async fn configure_receiver(&self, client: &Arc, ns: String) -> AlertmanagerConfig { + // let secret_name = format!("{}-secret", self.name.clone()); + // let webhook_key = format!("{}", self.url.clone()); + // + // let mut string_data = BTreeMap::new(); + // string_data.insert("webhook-url".to_string(), webhook_key.clone()); + // + // let secret = Secret { + // metadata: kube::core::ObjectMeta { + // name: Some(secret_name.clone()), + // ..Default::default() + // }, + // string_data: Some(string_data), + // type_: Some("Opaque".to_string()), + // ..Default::default() + // }; + // + // let _ = client.apply(&secret, Some(&ns)).await; + + let spec = AlertmanagerConfigSpec { + data: json!({ + "route": { + "receiver": self.name, + }, + "receivers": [ + { + "name": self.name, + "webhookConfigs": [ + { + "url": self.url, + } + ] + } + ] + }), + }; + + let am = AlertmanagerConfig { + metadata: ObjectMeta { + name: Some(self.name.clone()), + labels: Some(std::collections::BTreeMap::from([( + "alertmanagerConfig".to_string(), + "enabled".to_string(), + )])), + namespace: Some(ns), + ..Default::default() + }, + spec, + }; + debug!(" am: \n{:#?}", am.clone()); + + am + } + fn clone_box(&self) -> Box { + Box::new(self.clone()) + } +} + +#[async_trait] +impl AlertReceiver for WebhookReceiver { + async fn install(&self, sender: &CRDAlertManager) -> Result { + sender + .client + .apply(&sender.alertmanager_configs, Some(&sender.namespace)) + .await?; + Ok(Outcome::success(format!( + "installed crd-alertmanagerconfigs for {}", + self.name + ))) + } + fn name(&self) -> String { + "webhook-receiver".to_string() + } + fn clone_box(&self) -> Box> { + Box::new(self.clone()) + } +} + #[async_trait] impl AlertReceiver for WebhookReceiver { async fn install(&self, sender: &Prometheus) -> Result { diff --git a/harmony/src/modules/monitoring/kube_prometheus/alert_manager_config.rs b/harmony/src/modules/monitoring/kube_prometheus/alert_manager_config.rs index 43427dc..254cd39 100644 --- a/harmony/src/modules/monitoring/kube_prometheus/alert_manager_config.rs +++ b/harmony/src/modules/monitoring/kube_prometheus/alert_manager_config.rs @@ -91,6 +91,8 @@ use crate::{ }, }; +use super::types::AlertManagerConfig; + #[derive(CustomResource, Serialize, Deserialize, Debug, Clone, JsonSchema)] #[kube( group = "monitoring.coreos.com", @@ -108,8 +110,9 @@ pub struct AlertmanagerConfigSpec { #[derive(Debug, Clone)] pub struct CRDAlertManager { - namespace: String, - client: K8sClient, + pub alertmanager_configs: AlertmanagerConfig, + pub namespace: String, + pub client: Arc, } impl AlertSender for CRDAlertManager { diff --git a/harmony/src/modules/monitoring/kube_prometheus/helm_prometheus_application_alerting.rs b/harmony/src/modules/monitoring/kube_prometheus/helm_prometheus_application_alerting.rs index 7e467c5..bbc59d7 100644 --- a/harmony/src/modules/monitoring/kube_prometheus/helm_prometheus_application_alerting.rs +++ b/harmony/src/modules/monitoring/kube_prometheus/helm_prometheus_application_alerting.rs @@ -12,7 +12,9 @@ use crate::{ }; use super::{ - alert_manager_config::{AlertmanagerConfig, AlertmanagerConfigSpec, CRDAlertManager, CRDAlertManagerReceiver}, + alert_manager_config::{ + AlertmanagerConfig, AlertmanagerConfigSpec, CRDAlertManager, CRDAlertManagerReceiver, + }, prometheus::KubePrometheus, }; @@ -50,10 +52,17 @@ impl Interpret for HelmPrometheusApplicationAlerting ) -> Result { let client = topology.k8s_client().await.unwrap(); for receiver in self.receivers.iter() { - let alertmanager_config: AlertmanagerConfig = receiver.configure_receiver(&client, self.namespace.clone()).await; - client - .apply(&alertmanager_config, Some(&self.namespace)) - .await?; + let alertmanager_config: AlertmanagerConfig = receiver + .configure_receiver(&client, self.namespace.clone()) + .await; + let sender = CRDAlertManager { + alertmanager_configs: alertmanager_config, + namespace: self.namespace.clone(), + client: client.clone(), + }; + receiver.install(&sender).await.map_err(|err| { + InterpretError::new(format!("failed to install receiver: {}", err)) + })?; } Ok(Outcome::success(format!("deployed alert channels"))) } diff --git a/harmony/src/modules/monitoring/kube_prometheus/mod.rs b/harmony/src/modules/monitoring/kube_prometheus/mod.rs index 2a5974c..4843509 100644 --- a/harmony/src/modules/monitoring/kube_prometheus/mod.rs +++ b/harmony/src/modules/monitoring/kube_prometheus/mod.rs @@ -1,6 +1,6 @@ +pub mod alert_manager_config; pub mod helm; pub mod helm_prometheus_alert_score; +pub mod helm_prometheus_application_alerting; pub mod prometheus; pub mod types; -pub mod alert_manager_config; -pub mod helm_prometheus_application_alerting;