wip: cluster_monitoring almost there, a kink to fix in the yaml handling
All checks were successful
Run Check Script / check (pull_request) Successful in 1m15s

This commit is contained in:
Jean-Gabriel Gill-Couture 2025-10-29 23:12:34 -04:00
parent a12d12aa4f
commit cf84f2cce8
4 changed files with 68 additions and 34 deletions

View File

@ -77,7 +77,7 @@ pub trait AlertReceiver<S: AlertSender>: std::fmt::Debug + Send + Sync {
fn name(&self) -> String;
fn clone_box(&self) -> Box<dyn AlertReceiver<S>>;
fn as_any(&self) -> &dyn Any;
fn as_alertmanager_receiver(&self) -> AlertManagerReceiver;
fn as_alertmanager_receiver(&self) -> Result<AlertManagerReceiver, String>;
}
#[derive(Debug)]

View File

@ -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<AlertManagerReceiver, String> {
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<OpenshiftClusterAlertSender> for DiscordWebhook {
todo!()
}
fn as_alertmanager_receiver(&self) -> AlertManagerReceiver {
fn as_alertmanager_receiver(&self) -> Result<AlertManagerReceiver, String> {
self.get_receiver_config()
}
}
#[async_trait]
impl AlertReceiver<RHOBObservability> for DiscordWebhook {
fn as_alertmanager_receiver(&self) -> AlertManagerReceiver {
fn as_alertmanager_receiver(&self) -> Result<AlertManagerReceiver, String> {
todo!()
}
async fn install(&self, sender: &RHOBObservability) -> Result<Outcome, InterpretError> {
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<RHOBObservability> for DiscordWebhook {
#[async_trait]
impl AlertReceiver<CRDPrometheus> for DiscordWebhook {
fn as_alertmanager_receiver(&self) -> AlertManagerReceiver {
fn as_alertmanager_receiver(&self) -> Result<AlertManagerReceiver, String> {
todo!()
}
async fn install(&self, sender: &CRDPrometheus) -> Result<Outcome, InterpretError> {
@ -252,7 +253,7 @@ impl AlertReceiver<CRDPrometheus> for DiscordWebhook {
#[async_trait]
impl AlertReceiver<Prometheus> for DiscordWebhook {
fn as_alertmanager_receiver(&self) -> AlertManagerReceiver {
fn as_alertmanager_receiver(&self) -> Result<AlertManagerReceiver, String> {
todo!()
}
async fn install(&self, sender: &Prometheus) -> Result<Outcome, InterpretError> {
@ -281,7 +282,7 @@ impl PrometheusReceiver for DiscordWebhook {
#[async_trait]
impl AlertReceiver<KubePrometheus> for DiscordWebhook {
fn as_alertmanager_receiver(&self) -> AlertManagerReceiver {
fn as_alertmanager_receiver(&self) -> Result<AlertManagerReceiver, String> {
todo!()
}
async fn install(&self, sender: &KubePrometheus) -> Result<Outcome, InterpretError> {

View File

@ -31,7 +31,7 @@ pub struct WebhookReceiver {
#[async_trait]
impl AlertReceiver<RHOBObservability> for WebhookReceiver {
fn as_alertmanager_receiver(&self) -> AlertManagerReceiver {
fn as_alertmanager_receiver(&self) -> Result<AlertManagerReceiver, String> {
todo!()
}
async fn install(&self, sender: &RHOBObservability) -> Result<Outcome, InterpretError> {
@ -100,7 +100,7 @@ impl AlertReceiver<RHOBObservability> for WebhookReceiver {
#[async_trait]
impl AlertReceiver<CRDPrometheus> for WebhookReceiver {
fn as_alertmanager_receiver(&self) -> AlertManagerReceiver {
fn as_alertmanager_receiver(&self) -> Result<AlertManagerReceiver, String> {
todo!()
}
async fn install(&self, sender: &CRDPrometheus) -> Result<Outcome, InterpretError> {
@ -164,7 +164,7 @@ impl AlertReceiver<CRDPrometheus> for WebhookReceiver {
#[async_trait]
impl AlertReceiver<Prometheus> for WebhookReceiver {
fn as_alertmanager_receiver(&self) -> AlertManagerReceiver {
fn as_alertmanager_receiver(&self) -> Result<AlertManagerReceiver, String> {
todo!()
}
async fn install(&self, sender: &Prometheus) -> Result<Outcome, InterpretError> {
@ -193,7 +193,7 @@ impl PrometheusReceiver for WebhookReceiver {
#[async_trait]
impl AlertReceiver<KubePrometheus> for WebhookReceiver {
fn as_alertmanager_receiver(&self) -> AlertManagerReceiver {
fn as_alertmanager_receiver(&self) -> Result<AlertManagerReceiver, String> {
todo!()
}
async fn install(&self, sender: &KubePrometheus) -> Result<Outcome, InterpretError> {

View File

@ -81,43 +81,76 @@ impl<T: Topology + K8sclient> Interpret<T> 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")))
}