From e61ec015abdc5d9604e74671ea8716462374e4dc Mon Sep 17 00:00:00 2001 From: Willem Date: Mon, 14 Jul 2025 13:06:47 -0400 Subject: [PATCH] feat: added impl for Discordwebhook receiver to receive application alerts from namespaces from application feature --- .../alert_channel/discord_alert_channel.rs | 100 ++++++++++-------- .../kube_prometheus/alert_manager_config.rs | 7 +- .../helm_prometheus_application_alerting.rs | 4 +- 3 files changed, 63 insertions(+), 48 deletions(-) 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 b791b05..4b1f1ba 100644 --- a/harmony/src/modules/monitoring/alert_channel/discord_alert_channel.rs +++ b/harmony/src/modules/monitoring/alert_channel/discord_alert_channel.rs @@ -1,10 +1,17 @@ +use std::collections::BTreeMap; +use std::sync::Arc; + use async_trait::async_trait; +use k8s_openapi::api::core::v1::Secret; use kube::api::ObjectMeta; +use kube::{Api, Client, ResourceExt}; +use log::{debug, info}; use serde::Serialize; use serde_json::json; use serde_yaml::{Mapping, Value}; use crate::modules::monitoring::kube_prometheus::alert_manager_config::AlertmanagerConfigSpec; +use crate::topology::k8s::K8sClient; use crate::{ interpret::{InterpretError, Outcome}, modules::monitoring::{ @@ -30,35 +37,65 @@ impl CRDAlertManagerReceiver for DiscordWebhook { self.name.clone() } - async fn configure_receiver(&self) -> AlertmanagerConfig { - let spec = AlertmanagerConfigSpec { - route: Some(json!({ - "group_by": ["alertname"], - "receiver": self.name, - })), - receivers: Some(json!([ - { - "name": self.name, - "webhook_configs": [ - { - "url": self.url - } - ] - } - ])), + 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() }; - AlertmanagerConfig { + let _ = client.apply(&secret, Some(&ns)).await; + + let spec = AlertmanagerConfigSpec { + data: json!({ + "route": { + "receiver": self.name, + }, + "receivers": [ + { + "name": self.name, + "discordConfigs": [ + { + "apiURL": { + "name": secret_name, + "key": "webhook-url", + }, + "title": "{{ template \"discord.default.title\" . }}", + "message": "{{ template \"discord.default.message\" . }}" + } + ] + } + ] + }), + }; + + let am = AlertmanagerConfig { metadata: ObjectMeta { name: Some(self.name.clone()), - //TODO this cant be hardcoded - namespace: Some("monitoring".into()), + 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 { + fn clone_box(&self) -> Box { Box::new(self.clone()) } } @@ -174,27 +211,6 @@ impl DiscordWebhook { } } -impl From for AlertmanagerConfigSpec { - fn from(dw: DiscordWebhook) -> Self { - AlertmanagerConfigSpec { - route: Some(json!({ - "group_by": ["alertname"], - "receiver": dw.name, - })), - receivers: Some(json!([ - { - "name": dw.name, - "webhook_configs": [ - { - "url": dw.url - } - ] - } - ])), - } - } -} - #[cfg(test)] mod tests { use super::*; 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 b6bae51..43427dc 100644 --- a/harmony/src/modules/monitoring/kube_prometheus/alert_manager_config.rs +++ b/harmony/src/modules/monitoring/kube_prometheus/alert_manager_config.rs @@ -99,12 +99,11 @@ use crate::{ plural = "alertmanagerconfigs", namespaced )] - pub struct AlertmanagerConfigSpec { // Define the spec fields here, or use serde's `flatten` if you want to store arbitrary data // Example placeholder: - pub route: Option, - pub receivers: Option, + #[serde(flatten)] + pub data: serde_json::Value, } #[derive(Debug, Clone)] @@ -139,7 +138,7 @@ pub trait CRDAlertManagerReceiver: AlertReceiver + Send + Sync + std::fmt::Debug { fn name(&self) -> String; - async fn configure_receiver(&self) -> AlertmanagerConfig; + async fn configure_receiver(&self, client: &Arc, ns: String) -> AlertmanagerConfig; // This new method is for cloning the trait object fn clone_box(&self) -> Box; } 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 6dd19a0..7e467c5 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 @@ -48,9 +48,9 @@ impl Interpret for HelmPrometheusApplicationAlerting inventory: &Inventory, topology: &T, ) -> Result { + let client = topology.k8s_client().await.unwrap(); for receiver in self.receivers.iter() { - let alertmanager_config: AlertmanagerConfig = receiver.configure_receiver().await; - let client = topology.k8s_client().await.unwrap(); + let alertmanager_config: AlertmanagerConfig = receiver.configure_receiver(&client, self.namespace.clone()).await; client .apply(&alertmanager_config, Some(&self.namespace)) .await?;