diff --git a/harmony/src/domain/topology/oberservability/monitoring.rs b/harmony/src/domain/topology/oberservability/monitoring.rs index 1bad370..9ef77f4 100644 --- a/harmony/src/domain/topology/oberservability/monitoring.rs +++ b/harmony/src/domain/topology/oberservability/monitoring.rs @@ -77,7 +77,7 @@ pub trait AlertReceiver: std::fmt::Debug + Send + Sync { fn name(&self) -> String; fn clone_box(&self) -> Box>; fn as_any(&self) -> &dyn Any; - fn as_alertmanager_receiver(&self) -> AlertManagerReceiver; + fn as_alertmanager_receiver(&self) -> Result; } #[derive(Debug)] 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 550ce46..e455e76 100644 --- a/harmony/src/modules/monitoring/alert_channel/discord_alert_channel.rs +++ b/harmony/src/modules/monitoring/alert_channel/discord_alert_channel.rs @@ -10,6 +10,7 @@ use serde::Serialize; use serde_json::json; use serde_yaml::{Mapping, Value}; +use crate::infra::kube::kube_resource_to_dynamic; use crate::modules::monitoring::kube_prometheus::crd::crd_alertmanager_config::{ AlertmanagerConfig, AlertmanagerConfigSpec, CRDPrometheus, }; @@ -36,7 +37,7 @@ pub struct DiscordWebhook { } impl DiscordWebhook { - fn get_receiver_config(&self) -> AlertManagerReceiver { + fn get_receiver_config(&self) -> Result { let secret_name = format!("{}-secret", self.name.clone()); let webhook_key = format!("{}", self.url.clone()); @@ -53,8 +54,8 @@ impl DiscordWebhook { ..Default::default() }; - AlertManagerReceiver { - additional_ressources: vec![], + Ok(AlertManagerReceiver { + additional_ressources: vec![kube_resource_to_dynamic(&secret)?], receiver_config: json!({ "name": self.name, @@ -69,7 +70,7 @@ impl DiscordWebhook { } ] }), - } + }) } } @@ -94,21 +95,21 @@ impl AlertReceiver for DiscordWebhook { todo!() } - fn as_alertmanager_receiver(&self) -> AlertManagerReceiver { + fn as_alertmanager_receiver(&self) -> Result { self.get_receiver_config() } } #[async_trait] impl AlertReceiver for DiscordWebhook { - fn as_alertmanager_receiver(&self) -> AlertManagerReceiver { + fn as_alertmanager_receiver(&self) -> Result { todo!() } async fn install(&self, sender: &RHOBObservability) -> Result { let ns = sender.namespace.clone(); - let config = self.get_receiver_config(); + let config = self.get_receiver_config()?; for resource in config.additional_ressources.iter() { todo!("can I apply a dynamicresource"); // sender.client.apply(resource, Some(&ns)).await; @@ -171,7 +172,7 @@ impl AlertReceiver for DiscordWebhook { #[async_trait] impl AlertReceiver for DiscordWebhook { - fn as_alertmanager_receiver(&self) -> AlertManagerReceiver { + fn as_alertmanager_receiver(&self) -> Result { todo!() } async fn install(&self, sender: &CRDPrometheus) -> Result { @@ -252,7 +253,7 @@ impl AlertReceiver for DiscordWebhook { #[async_trait] impl AlertReceiver for DiscordWebhook { - fn as_alertmanager_receiver(&self) -> AlertManagerReceiver { + fn as_alertmanager_receiver(&self) -> Result { todo!() } async fn install(&self, sender: &Prometheus) -> Result { @@ -281,7 +282,7 @@ impl PrometheusReceiver for DiscordWebhook { #[async_trait] impl AlertReceiver for DiscordWebhook { - fn as_alertmanager_receiver(&self) -> AlertManagerReceiver { + fn as_alertmanager_receiver(&self) -> Result { todo!() } async fn install(&self, sender: &KubePrometheus) -> Result { diff --git a/harmony/src/modules/monitoring/alert_channel/webhook_receiver.rs b/harmony/src/modules/monitoring/alert_channel/webhook_receiver.rs index c1d32b2..a141df0 100644 --- a/harmony/src/modules/monitoring/alert_channel/webhook_receiver.rs +++ b/harmony/src/modules/monitoring/alert_channel/webhook_receiver.rs @@ -31,7 +31,7 @@ pub struct WebhookReceiver { #[async_trait] impl AlertReceiver for WebhookReceiver { - fn as_alertmanager_receiver(&self) -> AlertManagerReceiver { + fn as_alertmanager_receiver(&self) -> Result { todo!() } async fn install(&self, sender: &RHOBObservability) -> Result { @@ -100,7 +100,7 @@ impl AlertReceiver for WebhookReceiver { #[async_trait] impl AlertReceiver for WebhookReceiver { - fn as_alertmanager_receiver(&self) -> AlertManagerReceiver { + fn as_alertmanager_receiver(&self) -> Result { todo!() } async fn install(&self, sender: &CRDPrometheus) -> Result { @@ -164,7 +164,7 @@ impl AlertReceiver for WebhookReceiver { #[async_trait] impl AlertReceiver for WebhookReceiver { - fn as_alertmanager_receiver(&self) -> AlertManagerReceiver { + fn as_alertmanager_receiver(&self) -> Result { todo!() } async fn install(&self, sender: &Prometheus) -> Result { @@ -193,7 +193,7 @@ impl PrometheusReceiver for WebhookReceiver { #[async_trait] impl AlertReceiver for WebhookReceiver { - fn as_alertmanager_receiver(&self) -> AlertManagerReceiver { + fn as_alertmanager_receiver(&self) -> Result { todo!() } async fn install(&self, sender: &KubePrometheus) -> Result { diff --git a/harmony/src/modules/monitoring/okd/cluster_monitoring.rs b/harmony/src/modules/monitoring/okd/cluster_monitoring.rs index 05da341..2dfc169 100644 --- a/harmony/src/modules/monitoring/okd/cluster_monitoring.rs +++ b/harmony/src/modules/monitoring/okd/cluster_monitoring.rs @@ -81,43 +81,76 @@ impl Interpret for OpenshiftClusterAlertInterpret { trace!("Got secret {secret:?}"); let data: serde_json::Value = secret.data; + trace!("Alertmanager-main secret data {data:#?}"); - // TODO : get config in base64 by drilling into the value - let config_b64 = match data.get("alertmanager.yaml") { - Some(value) => value.as_str().unwrap_or(""), - None => "", + // TODO fix this unwrap, handle the option gracefully + let config_b64 = match data.get("data") { + Some(data_value) => match data_value.get("alertmanager.yaml") { + Some(value) => value.as_str().unwrap_or(""), + None => { + return Err(InterpretError::new( + "Missing 'alertmanager.yaml' in alertmanager-main secret".to_string(), + )); + } + }, + None => { + return Err(InterpretError::new( + "Missing 'data' field in alertmanager-main secret.".to_string(), + )); + } }; + trace!("Config base64 {config_b64}"); - // TODO : base64 decode it let config_bytes = BASE64_STANDARD.decode(config_b64).unwrap_or_default(); - // TODO : use serde_yaml to deserialize the string - let am_config: serde_yaml::Value = + let mut am_config: serde_yaml::Value = serde_yaml::from_str(&String::from_utf8(config_bytes).unwrap_or_default()) .unwrap_or_default(); - // Merge current alert receivers from this config with self.receivers - if let Some(existing_receivers) = am_config.get("receivers") { - for receiver in existing_receivers.as_sequence().unwrap_or(&vec![]) { - match serde_json::to_string(receiver) { - Ok(yaml_str) => { - // TODO: validate that each receiver implements to_alertmanager_yaml() - // and compare with our receivers - info!("Found existing receiver config: {}", yaml_str); - } - Err(e) => debug!("Failed to serialize receiver: {}", e), + debug!("Current alertmanager config {am_config:#?}"); + + let existing_receivers = if let Some(receivers) = am_config.get_mut("receivers") { + match receivers.as_mapping_mut() { + Some(recv) => recv, + None => { + return Err(InterpretError::new(format!( + "Expected alertmanager config receivers to be a mapping, got {receivers:?}" + ))); } } - } + } else { + &mut serde_yaml::mapping::Mapping::default() + }; + + trace!("Existing receivers : {existing_receivers:#?}"); for custom_receiver in &self.receivers { - trace!("Processing custom receiver"); + let name = &custom_receiver.name(); + if let Some(recv) = existing_receivers.get(name) { + info!( + "AlertManager receiver {name} already exists and will be overwritten : {recv:#?}" + ); + } debug!( "Custom receiver YAML output: {:?}", custom_receiver.as_alertmanager_receiver() ); + + let json_value = custom_receiver.as_alertmanager_receiver()?.receiver_config; + let yaml_string = serde_json::to_string(&json_value).map_err(|e| { + InterpretError::new(format!("Failed to serialize receiver config: {}", e)) + })?; + + let yaml_value: serde_yaml::Value = + serde_yaml::from_str(&yaml_string).map_err(|e| { + InterpretError::new(format!("Failed to parse receiver config as YAML: {}", e)) + })?; + + existing_receivers.insert(serde_yaml::Value::from(name.as_str()), yaml_value); } + debug!("Current alertmanager config {am_config:#?}"); + Ok(Outcome::success(todo!("whats up"))) }