Compare commits
	
		
			No commits in common. "c6b255d0bd85f123af0968d92b16bd7739d13531" and "a5deda647b030be15af94e49c9781a7b06b586fa" have entirely different histories.
		
	
	
		
			c6b255d0bd
			...
			a5deda647b
		
	
		
| @ -43,9 +43,9 @@ async fn main() { | |||||||
| 
 | 
 | ||||||
|     let app = ApplicationScore { |     let app = ApplicationScore { | ||||||
|         features: vec![ |         features: vec![ | ||||||
|             Box::new(ContinuousDelivery { |             // Box::new(ContinuousDelivery {
 | ||||||
|                 application: application.clone(), |             //     application: application.clone(),
 | ||||||
|             }), |             // }),
 | ||||||
|             Box::new(Monitoring { |             Box::new(Monitoring { | ||||||
|                 application: application.clone(), |                 application: application.clone(), | ||||||
|                 alert_receiver: vec![Box::new(discord_receiver), Box::new(webhook_receiver)], |                 alert_receiver: vec![Box::new(discord_receiver), Box::new(webhook_receiver)], | ||||||
|  | |||||||
| @ -12,14 +12,16 @@ use crate::{ | |||||||
|     inventory::Inventory, |     inventory::Inventory, | ||||||
|     maestro::Maestro, |     maestro::Maestro, | ||||||
|     modules::{ |     modules::{ | ||||||
|  |         application::Application, | ||||||
|         k3d::K3DInstallationScore, |         k3d::K3DInstallationScore, | ||||||
|         monitoring::kube_prometheus::crd::{ |         monitoring::kube_prometheus::crd::{ | ||||||
|             crd_alertmanager_config::CRDPrometheus, |             crd_alertmanager_config::{CRDAlertManagerReceiver, CRDPrometheus}, | ||||||
|             prometheus_operator::prometheus_operator_helm_chart_score, |             prometheus_operator::prometheus_operator_helm_chart_score, | ||||||
|         }, |         }, | ||||||
|         prometheus::{ |         prometheus::{ | ||||||
|  |             k3d_prometheus_alerting_score::K3dPrometheusCRDAlertingScore, | ||||||
|             k8s_prometheus_alerting_score::K8sPrometheusCRDAlertingScore, |             k8s_prometheus_alerting_score::K8sPrometheusCRDAlertingScore, | ||||||
|             prometheus::PrometheusApplicationMonitoring, |             prometheus::{PrometheusApplicationMonitoring, PrometheusMonitoring}, | ||||||
|         }, |         }, | ||||||
|     }, |     }, | ||||||
|     score::Score, |     score::Score, | ||||||
| @ -29,7 +31,7 @@ use crate::{ | |||||||
| use super::{ | use super::{ | ||||||
|     DeploymentTarget, HelmCommand, K8sclient, MultiTargetTopology, Topology, |     DeploymentTarget, HelmCommand, K8sclient, MultiTargetTopology, Topology, | ||||||
|     k8s::K8sClient, |     k8s::K8sClient, | ||||||
|     oberservability::monitoring::AlertReceiver, |     oberservability::monitoring::{AlertReceiver, AlertSender}, | ||||||
|     tenant::{TenantConfig, TenantManager, k8s::K8sTenantManager}, |     tenant::{TenantConfig, TenantManager, k8s::K8sTenantManager}, | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| @ -70,8 +72,59 @@ impl K8sclient for K8sAnywhereTopology { | |||||||
|     } |     } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | #[async_trait] | ||||||
|  | impl PrometheusMonitoring<CRDPrometheus> for K8sAnywhereTopology { | ||||||
|  |     async fn ensure_prometheus_operator( | ||||||
|  |         &self, | ||||||
|  |         sender: &CRDPrometheus, | ||||||
|  |     ) -> Result<Outcome, InterpretError> { | ||||||
|  |         let output = Command::new("sh") | ||||||
|  |             .args(["-c", "kubectl get crd -A | grep -i prometheuses"]) | ||||||
|  |             .output() | ||||||
|  |             .map_err(|e| InterpretError::new(format!("could not connect to cluster: {}", e)))?; | ||||||
|  |         if output.status.success() && output.stdout.is_empty() { | ||||||
|  |             if let Some(Some(k8s_state)) = self.k8s_state.get() { | ||||||
|  |                 match k8s_state.source { | ||||||
|  |                     K8sSource::LocalK3d => { | ||||||
|  |                         debug!("installing prometheus operator"); | ||||||
|  |                         let op_score = | ||||||
|  |                             prometheus_operator_helm_chart_score(sender.namespace.clone()); | ||||||
|  |                         op_score | ||||||
|  |                             .create_interpret() | ||||||
|  |                             .execute(&Inventory::empty(), self) | ||||||
|  |                             .await?; | ||||||
|  |                         return Ok(Outcome::success( | ||||||
|  |                             "installed prometheus operator".to_string(), | ||||||
|  |                         )); | ||||||
|  |                     } | ||||||
|  |                     K8sSource::Kubeconfig => { | ||||||
|  |                         debug!("unable to install prometheus operator, contact cluster admin"); | ||||||
|  |                         return Ok(Outcome::noop()); | ||||||
|  |                     } | ||||||
|  |                 } | ||||||
|  |             } else { | ||||||
|  |                 warn!("Unable to detect k8s_state. Skipping Prometheus Operator install."); | ||||||
|  |                 return Ok(Outcome::noop()); | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |         debug!("Prometheus operator is already present, skipping install"); | ||||||
|  |         Ok(Outcome::success( | ||||||
|  |             "prometheus operator present in cluster".to_string(), | ||||||
|  |         )) | ||||||
|  |     } | ||||||
|  | } | ||||||
| #[async_trait] | #[async_trait] | ||||||
| impl PrometheusApplicationMonitoring<CRDPrometheus> for K8sAnywhereTopology { | impl PrometheusApplicationMonitoring<CRDPrometheus> for K8sAnywhereTopology { | ||||||
|  |     async fn configure_receivers( | ||||||
|  |         &self, | ||||||
|  |         receivers: Option<Vec<Box<dyn AlertReceiver<CRDPrometheus>>>>, | ||||||
|  |     ) -> Option<Vec<Box<dyn CRDAlertManagerReceiver>>> { | ||||||
|  |         let Some(receivers) = receivers else { | ||||||
|  |             return None; | ||||||
|  |         }; | ||||||
|  | 
 | ||||||
|  |         todo!() | ||||||
|  |     } | ||||||
|     async fn install_prometheus( |     async fn install_prometheus( | ||||||
|         &self, |         &self, | ||||||
|         sender: &CRDPrometheus, |         sender: &CRDPrometheus, | ||||||
| @ -127,7 +180,10 @@ impl K8sAnywhereTopology { | |||||||
|     ) -> K8sPrometheusCRDAlertingScore { |     ) -> K8sPrometheusCRDAlertingScore { | ||||||
|         K8sPrometheusCRDAlertingScore { |         K8sPrometheusCRDAlertingScore { | ||||||
|             sender, |             sender, | ||||||
|             receivers: receivers.unwrap_or_else(Vec::new), |             receivers: self | ||||||
|  |                 .configure_receivers(receivers) | ||||||
|  |                 .await | ||||||
|  |                 .unwrap_or_else(|| vec![]), | ||||||
|             service_monitors: vec![], |             service_monitors: vec![], | ||||||
|             prometheus_rules: vec![], |             prometheus_rules: vec![], | ||||||
|         } |         } | ||||||
| @ -266,45 +322,6 @@ impl K8sAnywhereTopology { | |||||||
|             )), |             )), | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
| 
 |  | ||||||
|     async fn ensure_prometheus_operator( |  | ||||||
|         &self, |  | ||||||
|         sender: &CRDPrometheus, |  | ||||||
|     ) -> Result<Outcome, InterpretError> { |  | ||||||
|         let output = Command::new("sh") |  | ||||||
|             .args(["-c", "kubectl get crd -A | grep -i prometheuses"]) |  | ||||||
|             .output() |  | ||||||
|             .map_err(|e| InterpretError::new(format!("could not connect to cluster: {}", e)))?; |  | ||||||
|         if output.status.success() && output.stdout.is_empty() { |  | ||||||
|             if let Some(Some(k8s_state)) = self.k8s_state.get() { |  | ||||||
|                 match k8s_state.source { |  | ||||||
|                     K8sSource::LocalK3d => { |  | ||||||
|                         debug!("installing prometheus operator"); |  | ||||||
|                         let op_score = |  | ||||||
|                             prometheus_operator_helm_chart_score(sender.namespace.clone()); |  | ||||||
|                         op_score |  | ||||||
|                             .create_interpret() |  | ||||||
|                             .execute(&Inventory::empty(), self) |  | ||||||
|                             .await?; |  | ||||||
|                         return Ok(Outcome::success( |  | ||||||
|                             "installed prometheus operator".to_string(), |  | ||||||
|                         )); |  | ||||||
|                     } |  | ||||||
|                     K8sSource::Kubeconfig => { |  | ||||||
|                         debug!("unable to install prometheus operator, contact cluster admin"); |  | ||||||
|                         return Ok(Outcome::noop()); |  | ||||||
|                     } |  | ||||||
|                 } |  | ||||||
|             } else { |  | ||||||
|                 warn!("Unable to detect k8s_state. Skipping Prometheus Operator install."); |  | ||||||
|                 return Ok(Outcome::noop()); |  | ||||||
|             } |  | ||||||
|         } |  | ||||||
|         debug!("Prometheus operator is already present, skipping install"); |  | ||||||
|         Ok(Outcome::success( |  | ||||||
|             "prometheus operator present in cluster".to_string(), |  | ||||||
|         )) |  | ||||||
|     } |  | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| #[derive(Clone, Debug)] | #[derive(Clone, Debug)] | ||||||
|  | |||||||
| @ -2,7 +2,9 @@ use std::sync::Arc; | |||||||
| 
 | 
 | ||||||
| use crate::modules::application::{Application, ApplicationFeature}; | use crate::modules::application::{Application, ApplicationFeature}; | ||||||
| use crate::modules::monitoring::application_monitoring::application_monitoring_score::ApplicationMonitoringScore; | use crate::modules::monitoring::application_monitoring::application_monitoring_score::ApplicationMonitoringScore; | ||||||
| use crate::modules::monitoring::kube_prometheus::crd::crd_alertmanager_config::CRDPrometheus; | use crate::modules::monitoring::kube_prometheus::crd::crd_alertmanager_config::{ | ||||||
|  |     AlertmanagerConfig, AlertmanagerConfigSpec, CRDPrometheus, | ||||||
|  | }; | ||||||
| 
 | 
 | ||||||
| use crate::{ | use crate::{ | ||||||
|     inventory::Inventory, |     inventory::Inventory, | ||||||
| @ -18,7 +20,9 @@ use crate::{ | |||||||
| }; | }; | ||||||
| use async_trait::async_trait; | use async_trait::async_trait; | ||||||
| use base64::{Engine as _, engine::general_purpose}; | use base64::{Engine as _, engine::general_purpose}; | ||||||
|  | use kube::api::ObjectMeta; | ||||||
| use log::{debug, info}; | use log::{debug, info}; | ||||||
|  | use serde_json::json; | ||||||
| 
 | 
 | ||||||
| #[derive(Debug, Clone)] | #[derive(Debug, Clone)] | ||||||
| pub struct Monitoring { | pub struct Monitoring { | ||||||
| @ -47,6 +51,12 @@ impl< | |||||||
| 
 | 
 | ||||||
|         let mut alerting_score = ApplicationMonitoringScore { |         let mut alerting_score = ApplicationMonitoringScore { | ||||||
|             sender: CRDPrometheus { |             sender: CRDPrometheus { | ||||||
|  |                 alertmanager_configs: AlertmanagerConfig { | ||||||
|  |                     metadata: ObjectMeta { | ||||||
|  |                         ..Default::default() | ||||||
|  |                     }, | ||||||
|  |                     spec: AlertmanagerConfigSpec { data: json! {""} }, | ||||||
|  |                 }, | ||||||
|                 namespace: namespace.clone(), |                 namespace: namespace.clone(), | ||||||
|                 client: topology.k8s_client().await.unwrap(), |                 client: topology.k8s_client().await.unwrap(), | ||||||
|             }, |             }, | ||||||
|  | |||||||
| @ -1,16 +1,20 @@ | |||||||
| use std::any::Any; | use std::any::Any; | ||||||
| use std::collections::BTreeMap; | use std::collections::BTreeMap; | ||||||
|  | use std::sync::Arc; | ||||||
| 
 | 
 | ||||||
| use async_trait::async_trait; | use async_trait::async_trait; | ||||||
| use k8s_openapi::api::core::v1::Secret; | use k8s_openapi::api::core::v1::Secret; | ||||||
| use kube::api::ObjectMeta; | use kube::api::ObjectMeta; | ||||||
|  | use kube::{Api, Client, ResourceExt}; | ||||||
|  | use log::{debug, info}; | ||||||
| use serde::Serialize; | use serde::Serialize; | ||||||
| use serde_json::json; | use serde_json::json; | ||||||
| use serde_yaml::{Mapping, Value}; | use serde_yaml::{Mapping, Value}; | ||||||
| 
 | 
 | ||||||
| use crate::modules::monitoring::kube_prometheus::crd::crd_alertmanager_config::{ | use crate::modules::monitoring::kube_prometheus::crd::crd_alertmanager_config::{ | ||||||
|     AlertmanagerConfig, AlertmanagerConfigSpec, CRDPrometheus, |     AlertmanagerConfig, AlertmanagerConfigSpec, CRDAlertManagerReceiver, CRDPrometheus, | ||||||
| }; | }; | ||||||
|  | use crate::topology::k8s::K8sClient; | ||||||
| use crate::{ | use crate::{ | ||||||
|     interpret::{InterpretError, Outcome}, |     interpret::{InterpretError, Outcome}, | ||||||
|     modules::monitoring::{ |     modules::monitoring::{ | ||||||
| @ -30,9 +34,12 @@ pub struct DiscordWebhook { | |||||||
| } | } | ||||||
| 
 | 
 | ||||||
| #[async_trait] | #[async_trait] | ||||||
| impl AlertReceiver<CRDPrometheus> for DiscordWebhook { | impl CRDAlertManagerReceiver for DiscordWebhook { | ||||||
|     async fn install(&self, sender: &CRDPrometheus) -> Result<Outcome, InterpretError> { |     fn name(&self) -> String { | ||||||
|         let ns = sender.namespace.clone(); |         self.name.clone() | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     async fn configure_receiver(&self, client: &Arc<K8sClient>, ns: String) -> AlertmanagerConfig { | ||||||
|         let secret_name = format!("{}-secret", self.name.clone()); |         let secret_name = format!("{}-secret", self.name.clone()); | ||||||
|         let webhook_key = format!("{}", self.url.clone()); |         let webhook_key = format!("{}", self.url.clone()); | ||||||
| 
 | 
 | ||||||
| @ -49,7 +56,7 @@ impl AlertReceiver<CRDPrometheus> for DiscordWebhook { | |||||||
|             ..Default::default() |             ..Default::default() | ||||||
|         }; |         }; | ||||||
| 
 | 
 | ||||||
|         let _ = sender.client.apply(&secret, Some(&ns)).await; |         let _ = client.apply(&secret, Some(&ns)).await; | ||||||
| 
 | 
 | ||||||
|         let spec = AlertmanagerConfigSpec { |         let spec = AlertmanagerConfigSpec { | ||||||
|             data: json!({ |             data: json!({ | ||||||
| @ -74,7 +81,7 @@ impl AlertReceiver<CRDPrometheus> for DiscordWebhook { | |||||||
|             }), |             }), | ||||||
|         }; |         }; | ||||||
| 
 | 
 | ||||||
|         let alertmanager_configs = AlertmanagerConfig { |         let am = AlertmanagerConfig { | ||||||
|             metadata: ObjectMeta { |             metadata: ObjectMeta { | ||||||
|                 name: Some(self.name.clone()), |                 name: Some(self.name.clone()), | ||||||
|                 labels: Some(std::collections::BTreeMap::from([( |                 labels: Some(std::collections::BTreeMap::from([( | ||||||
| @ -86,10 +93,19 @@ impl AlertReceiver<CRDPrometheus> for DiscordWebhook { | |||||||
|             }, |             }, | ||||||
|             spec, |             spec, | ||||||
|         }; |         }; | ||||||
|  |         am | ||||||
|  |     } | ||||||
|  |     fn clone_box(&self) -> Box<dyn CRDAlertManagerReceiver> { | ||||||
|  |         Box::new(self.clone()) | ||||||
|  |     } | ||||||
|  | } | ||||||
| 
 | 
 | ||||||
|  | #[async_trait] | ||||||
|  | impl AlertReceiver<CRDPrometheus> for DiscordWebhook { | ||||||
|  |     async fn install(&self, sender: &CRDPrometheus) -> Result<Outcome, InterpretError> { | ||||||
|         sender |         sender | ||||||
|             .client |             .client | ||||||
|             .apply(&alertmanager_configs, Some(&sender.namespace)) |             .apply(&sender.alertmanager_configs, Some(&sender.namespace)) | ||||||
|             .await?; |             .await?; | ||||||
|         Ok(Outcome::success(format!( |         Ok(Outcome::success(format!( | ||||||
|             "installed crd-alertmanagerconfigs for {}", |             "installed crd-alertmanagerconfigs for {}", | ||||||
|  | |||||||
| @ -1,6 +1,7 @@ | |||||||
| use std::any::Any; | use std::{any::Any, sync::Arc}; | ||||||
| 
 | 
 | ||||||
| use async_trait::async_trait; | use async_trait::async_trait; | ||||||
|  | use k8s_openapi::api::core::v1::Secret; | ||||||
| use kube::api::ObjectMeta; | use kube::api::ObjectMeta; | ||||||
| use log::debug; | use log::debug; | ||||||
| use serde::Serialize; | use serde::Serialize; | ||||||
| @ -12,14 +13,14 @@ use crate::{ | |||||||
|     modules::monitoring::{ |     modules::monitoring::{ | ||||||
|         kube_prometheus::{ |         kube_prometheus::{ | ||||||
|             crd::crd_alertmanager_config::{ |             crd::crd_alertmanager_config::{ | ||||||
|                 AlertmanagerConfig, AlertmanagerConfigSpec, CRDPrometheus, |                 AlertmanagerConfig, AlertmanagerConfigSpec, CRDAlertManagerReceiver, CRDPrometheus, | ||||||
|             }, |             }, | ||||||
|             prometheus::{KubePrometheus, KubePrometheusReceiver}, |             prometheus::{KubePrometheus, KubePrometheusReceiver}, | ||||||
|             types::{AlertChannelConfig, AlertManagerChannelConfig}, |             types::{AlertChannelConfig, AlertManagerChannelConfig}, | ||||||
|         }, |         }, | ||||||
|         prometheus::prometheus::{Prometheus, PrometheusReceiver}, |         prometheus::prometheus::{Prometheus, PrometheusReceiver}, | ||||||
|     }, |     }, | ||||||
|     topology::{Url, oberservability::monitoring::AlertReceiver}, |     topology::{Url, k8s::K8sClient, oberservability::monitoring::AlertReceiver}, | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| #[derive(Debug, Clone, Serialize)] | #[derive(Debug, Clone, Serialize)] | ||||||
| @ -29,8 +30,12 @@ pub struct WebhookReceiver { | |||||||
| } | } | ||||||
| 
 | 
 | ||||||
| #[async_trait] | #[async_trait] | ||||||
| impl AlertReceiver<CRDPrometheus> for WebhookReceiver { | impl CRDAlertManagerReceiver for WebhookReceiver { | ||||||
|     async fn install(&self, sender: &CRDPrometheus) -> Result<Outcome, InterpretError> { |     fn name(&self) -> String { | ||||||
|  |         self.name.clone() | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     async fn configure_receiver(&self, client: &Arc<K8sClient>, ns: String) -> AlertmanagerConfig { | ||||||
|         let spec = AlertmanagerConfigSpec { |         let spec = AlertmanagerConfigSpec { | ||||||
|             data: json!({ |             data: json!({ | ||||||
|                 "route": { |                 "route": { | ||||||
| @ -49,41 +54,45 @@ impl AlertReceiver<CRDPrometheus> for WebhookReceiver { | |||||||
|             }), |             }), | ||||||
|         }; |         }; | ||||||
| 
 | 
 | ||||||
|         let alertmanager_configs = AlertmanagerConfig { |         let am = AlertmanagerConfig { | ||||||
|             metadata: ObjectMeta { |             metadata: ObjectMeta { | ||||||
|                 name: Some(self.name.clone()), |                 name: Some(self.name.clone()), | ||||||
|                 labels: Some(std::collections::BTreeMap::from([( |                 labels: Some(std::collections::BTreeMap::from([( | ||||||
|                     "alertmanagerConfig".to_string(), |                     "alertmanagerConfig".to_string(), | ||||||
|                     "enabled".to_string(), |                     "enabled".to_string(), | ||||||
|                 )])), |                 )])), | ||||||
|                 namespace: Some(sender.namespace.clone()), |                 namespace: Some(ns), | ||||||
|                 ..Default::default() |                 ..Default::default() | ||||||
|             }, |             }, | ||||||
|             spec, |             spec, | ||||||
|         }; |         }; | ||||||
|         debug!( |         debug!(" am: \n{:#?}", am.clone()); | ||||||
|             "alert manager configs: \n{:#?}", |  | ||||||
|             alertmanager_configs.clone() |  | ||||||
|         ); |  | ||||||
| 
 | 
 | ||||||
|  |         am | ||||||
|  |     } | ||||||
|  |     fn clone_box(&self) -> Box<dyn CRDAlertManagerReceiver> { | ||||||
|  |         Box::new(self.clone()) | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | #[async_trait] | ||||||
|  | impl AlertReceiver<CRDPrometheus> for WebhookReceiver { | ||||||
|  |     async fn install(&self, sender: &CRDPrometheus) -> Result<Outcome, InterpretError> { | ||||||
|         sender |         sender | ||||||
|             .client |             .client | ||||||
|             .apply(&alertmanager_configs, Some(&sender.namespace)) |             .apply(&sender.alertmanager_configs, Some(&sender.namespace)) | ||||||
|             .await?; |             .await?; | ||||||
|         Ok(Outcome::success(format!( |         Ok(Outcome::success(format!( | ||||||
|             "installed crd-alertmanagerconfigs for {}", |             "installed crd-alertmanagerconfigs for {}", | ||||||
|             self.name |             self.name | ||||||
|         ))) |         ))) | ||||||
|     } |     } | ||||||
| 
 |  | ||||||
|     fn name(&self) -> String { |     fn name(&self) -> String { | ||||||
|         "webhook-receiver".to_string() |         "webhook-receiver".to_string() | ||||||
|     } |     } | ||||||
| 
 |  | ||||||
|     fn clone_box(&self) -> Box<dyn AlertReceiver<CRDPrometheus>> { |     fn clone_box(&self) -> Box<dyn AlertReceiver<CRDPrometheus>> { | ||||||
|         Box::new(self.clone()) |         Box::new(self.clone()) | ||||||
|     } |     } | ||||||
| 
 |  | ||||||
|     fn as_any(&self) -> &dyn Any { |     fn as_any(&self) -> &dyn Any { | ||||||
|         self |         self | ||||||
|     } |     } | ||||||
|  | |||||||
| @ -9,11 +9,17 @@ use crate::{ | |||||||
|     inventory::Inventory, |     inventory::Inventory, | ||||||
|     modules::{ |     modules::{ | ||||||
|         application::Application, |         application::Application, | ||||||
|         monitoring::kube_prometheus::crd::crd_alertmanager_config::CRDPrometheus, |         monitoring::kube_prometheus::crd::crd_alertmanager_config::{ | ||||||
|         prometheus::prometheus::PrometheusApplicationMonitoring, |             CRDAlertManagerReceiver, CRDPrometheus, | ||||||
|  |         }, | ||||||
|  |         prometheus::prometheus::{PrometheusApplicationMonitoring, PrometheusMonitoring}, | ||||||
|     }, |     }, | ||||||
|     score::Score, |     score::Score, | ||||||
|     topology::{Topology, oberservability::monitoring::AlertReceiver}, |     topology::{ | ||||||
|  |         Topology, | ||||||
|  |         oberservability::monitoring::{AlertReceiver, AlertSender}, | ||||||
|  |         tenant::TenantManager, | ||||||
|  |     }, | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| #[derive(Debug, Clone, Serialize)] | #[derive(Debug, Clone, Serialize)] | ||||||
|  | |||||||
| @ -0,0 +1,44 @@ | |||||||
|  | use std::sync::{Arc, Mutex}; | ||||||
|  | 
 | ||||||
|  | use serde::Serialize; | ||||||
|  | 
 | ||||||
|  | use crate::{ | ||||||
|  |     modules::monitoring::{ | ||||||
|  |         kube_prometheus::types::ServiceMonitor, | ||||||
|  |         prometheus::{prometheus::Prometheus, prometheus_config::PrometheusConfig}, | ||||||
|  |     }, | ||||||
|  |     score::Score, | ||||||
|  |     topology::{ | ||||||
|  |         HelmCommand, Topology, | ||||||
|  |         oberservability::monitoring::{AlertReceiver, AlertRule, AlertingInterpret}, | ||||||
|  |         tenant::TenantManager, | ||||||
|  |     }, | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | #[derive(Clone, Debug, Serialize)] | ||||||
|  | pub struct ApplicationPrometheusMonitoringScore { | ||||||
|  |     pub receivers: Vec<Box<dyn AlertReceiver<Prometheus>>>, | ||||||
|  |     pub rules: Vec<Box<dyn AlertRule<Prometheus>>>, | ||||||
|  |     pub service_monitors: Vec<ServiceMonitor>, | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | impl<T: Topology + HelmCommand + TenantManager> Score<T> for ApplicationPrometheusMonitoringScore { | ||||||
|  |     fn create_interpret(&self) -> Box<dyn crate::interpret::Interpret<T>> { | ||||||
|  |         let mut prom_config = PrometheusConfig::new(); | ||||||
|  |         prom_config.alert_manager = true; | ||||||
|  | 
 | ||||||
|  |         let config = Arc::new(Mutex::new(prom_config)); | ||||||
|  |         config | ||||||
|  |             .try_lock() | ||||||
|  |             .expect("couldn't lock config") | ||||||
|  |             .additional_service_monitors = self.service_monitors.clone(); | ||||||
|  |         Box::new(AlertingInterpret { | ||||||
|  |             sender: Prometheus::new(), | ||||||
|  |             receivers: self.receivers.clone(), | ||||||
|  |             rules: self.rules.clone(), | ||||||
|  |         }) | ||||||
|  |     } | ||||||
|  |     fn name(&self) -> String { | ||||||
|  |         "ApplicationPrometheusMonitoringScore".to_string() | ||||||
|  |     } | ||||||
|  | } | ||||||
| @ -1 +1,2 @@ | |||||||
| pub mod application_monitoring_score; | pub mod application_monitoring_score; | ||||||
|  | pub mod k8s_application_monitoring_score; | ||||||
|  | |||||||
| @ -1,10 +1,13 @@ | |||||||
| use std::sync::Arc; | use std::sync::Arc; | ||||||
| 
 | 
 | ||||||
|  | use async_trait::async_trait; | ||||||
| use kube::CustomResource; | use kube::CustomResource; | ||||||
| use schemars::JsonSchema; | use schemars::JsonSchema; | ||||||
| use serde::{Deserialize, Serialize}; | use serde::{Deserialize, Serialize}; | ||||||
| 
 | 
 | ||||||
| use crate::topology::{ | use crate::topology::{ | ||||||
|  |     Topology, | ||||||
|  |     installable::Installable, | ||||||
|     k8s::K8sClient, |     k8s::K8sClient, | ||||||
|     oberservability::monitoring::{AlertReceiver, AlertSender}, |     oberservability::monitoring::{AlertReceiver, AlertSender}, | ||||||
| }; | }; | ||||||
| @ -24,6 +27,7 @@ pub struct AlertmanagerConfigSpec { | |||||||
| 
 | 
 | ||||||
| #[derive(Debug, Clone, Serialize)] | #[derive(Debug, Clone, Serialize)] | ||||||
| pub struct CRDPrometheus { | pub struct CRDPrometheus { | ||||||
|  |     pub alertmanager_configs: AlertmanagerConfig, | ||||||
|     pub namespace: String, |     pub namespace: String, | ||||||
|     pub client: Arc<K8sClient>, |     pub client: Arc<K8sClient>, | ||||||
| } | } | ||||||
| @ -40,6 +44,31 @@ impl Clone for Box<dyn AlertReceiver<CRDPrometheus>> { | |||||||
|     } |     } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | #[async_trait] | ||||||
|  | pub trait CRDAlertManagerReceiver: | ||||||
|  |     AlertReceiver<CRDPrometheus> + Send + Sync + std::fmt::Debug | ||||||
|  | { | ||||||
|  |     fn name(&self) -> String; | ||||||
|  |     async fn configure_receiver(&self, client: &Arc<K8sClient>, ns: String) -> AlertmanagerConfig; | ||||||
|  |     // This new method is for cloning the trait object
 | ||||||
|  |     fn clone_box(&self) -> Box<dyn CRDAlertManagerReceiver>; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | impl Clone for Box<dyn CRDAlertManagerReceiver> { | ||||||
|  |     fn clone(&self) -> Self { | ||||||
|  |         CRDAlertManagerReceiver::clone_box(self.as_ref()) | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | impl Serialize for Box<dyn CRDAlertManagerReceiver> { | ||||||
|  |     fn serialize<S>(&self, _serializer: S) -> Result<S::Ok, S::Error> | ||||||
|  |     where | ||||||
|  |         S: serde::Serializer, | ||||||
|  |     { | ||||||
|  |         todo!() | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
| impl Serialize for Box<dyn AlertReceiver<CRDPrometheus>> { | impl Serialize for Box<dyn AlertReceiver<CRDPrometheus>> { | ||||||
|     fn serialize<S>(&self, _serializer: S) -> Result<S::Ok, S::Error> |     fn serialize<S>(&self, _serializer: S) -> Result<S::Ok, S::Error> | ||||||
|     where |     where | ||||||
|  | |||||||
							
								
								
									
										582
									
								
								harmony/src/modules/prometheus/k3d_prometheus_alerting_score.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										582
									
								
								harmony/src/modules/prometheus/k3d_prometheus_alerting_score.rs
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,582 @@ | |||||||
|  | use std::fs; | ||||||
|  | use std::{collections::BTreeMap, sync::Arc}; | ||||||
|  | use tempfile::tempdir; | ||||||
|  | 
 | ||||||
|  | use async_trait::async_trait; | ||||||
|  | use kube::api::ObjectMeta; | ||||||
|  | use log::{debug, info}; | ||||||
|  | use serde::Serialize; | ||||||
|  | use tokio::process::Command; | ||||||
|  | 
 | ||||||
|  | use crate::modules::monitoring::kube_prometheus::crd::crd_alertmanager_config::{ | ||||||
|  |     AlertmanagerConfig, CRDAlertManagerReceiver, CRDPrometheus, | ||||||
|  | }; | ||||||
|  | use crate::modules::monitoring::kube_prometheus::crd::crd_default_rules::build_default_application_rules; | ||||||
|  | use crate::modules::monitoring::kube_prometheus::crd::crd_grafana::{ | ||||||
|  |     Grafana, GrafanaDashboard, GrafanaDashboardSpec, GrafanaDatasource, GrafanaDatasourceConfig, | ||||||
|  |     GrafanaDatasourceSpec, GrafanaSpec, | ||||||
|  | }; | ||||||
|  | use crate::modules::monitoring::kube_prometheus::crd::crd_prometheus_rules::{ | ||||||
|  |     PrometheusRule, PrometheusRuleSpec, RuleGroup, | ||||||
|  | }; | ||||||
|  | use crate::modules::monitoring::kube_prometheus::crd::grafana_default_dashboard::build_default_dashboard; | ||||||
|  | use crate::modules::monitoring::kube_prometheus::crd::service_monitor::{ | ||||||
|  |     ServiceMonitor, ServiceMonitorSpec, | ||||||
|  | }; | ||||||
|  | use crate::topology::{K8sclient, Topology, k8s::K8sClient}; | ||||||
|  | use crate::{ | ||||||
|  |     data::{Id, Version}, | ||||||
|  |     interpret::{Interpret, InterpretError, InterpretName, InterpretStatus, Outcome}, | ||||||
|  |     inventory::Inventory, | ||||||
|  |     modules::monitoring::kube_prometheus::crd::{ | ||||||
|  |         crd_alertmanagers::{Alertmanager, AlertmanagerSpec}, | ||||||
|  |         crd_prometheuses::{ | ||||||
|  |             AlertmanagerEndpoints, LabelSelector, Prometheus, PrometheusSpec, | ||||||
|  |             PrometheusSpecAlerting, | ||||||
|  |         }, | ||||||
|  |         role::{build_prom_role, build_prom_rolebinding, build_prom_service_account}, | ||||||
|  |     }, | ||||||
|  |     score::Score, | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | use super::prometheus::PrometheusMonitoring; | ||||||
|  | 
 | ||||||
|  | #[derive(Clone, Debug, Serialize)] | ||||||
|  | pub struct K3dPrometheusCRDAlertingScore { | ||||||
|  |     pub namespace: String, | ||||||
|  |     pub receivers: Vec<Box<dyn CRDAlertManagerReceiver>>, | ||||||
|  |     pub service_monitors: Vec<ServiceMonitor>, | ||||||
|  |     pub prometheus_rules: Vec<RuleGroup>, | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | impl<T: Topology + K8sclient + PrometheusMonitoring<CRDPrometheus>> Score<T> | ||||||
|  |     for K3dPrometheusCRDAlertingScore | ||||||
|  | { | ||||||
|  |     fn create_interpret(&self) -> Box<dyn crate::interpret::Interpret<T>> { | ||||||
|  |         Box::new(K3dPrometheusCRDAlertingInterpret { | ||||||
|  |             namespace: self.namespace.clone(), | ||||||
|  |             receivers: self.receivers.clone(), | ||||||
|  |             service_monitors: self.service_monitors.clone(), | ||||||
|  |             prometheus_rules: self.prometheus_rules.clone(), | ||||||
|  |         }) | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     fn name(&self) -> String { | ||||||
|  |         "CRDApplicationAlertingScore".into() | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | #[derive(Clone, Debug)] | ||||||
|  | pub struct K3dPrometheusCRDAlertingInterpret { | ||||||
|  |     pub namespace: String, | ||||||
|  |     pub receivers: Vec<Box<dyn CRDAlertManagerReceiver>>, | ||||||
|  |     pub service_monitors: Vec<ServiceMonitor>, | ||||||
|  |     pub prometheus_rules: Vec<RuleGroup>, | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | #[async_trait] | ||||||
|  | impl<T: Topology + K8sclient + PrometheusMonitoring<CRDPrometheus>> Interpret<T> | ||||||
|  |     for K3dPrometheusCRDAlertingInterpret | ||||||
|  | { | ||||||
|  |     async fn execute( | ||||||
|  |         &self, | ||||||
|  |         _inventory: &Inventory, | ||||||
|  |         topology: &T, | ||||||
|  |     ) -> Result<Outcome, InterpretError> { | ||||||
|  |         let client = topology.k8s_client().await.unwrap(); | ||||||
|  |         self.ensure_prometheus_operator().await?; | ||||||
|  |         self.ensure_grafana_operator().await?; | ||||||
|  |         self.install_prometheus(&client).await?; | ||||||
|  |         self.install_alert_manager(&client).await?; | ||||||
|  |         self.install_client_kube_metrics().await?; | ||||||
|  |         self.install_grafana(&client).await?; | ||||||
|  |         self.install_receivers(&self.receivers, &client).await?; | ||||||
|  |         self.install_rules(&self.prometheus_rules, &client).await?; | ||||||
|  |         self.install_monitors(self.service_monitors.clone(), &client) | ||||||
|  |             .await?; | ||||||
|  |         Ok(Outcome::success(format!( | ||||||
|  |             "deployed application monitoring composants" | ||||||
|  |         ))) | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     fn get_name(&self) -> InterpretName { | ||||||
|  |         todo!() | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     fn get_version(&self) -> Version { | ||||||
|  |         todo!() | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     fn get_status(&self) -> InterpretStatus { | ||||||
|  |         todo!() | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     fn get_children(&self) -> Vec<Id> { | ||||||
|  |         todo!() | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | impl K3dPrometheusCRDAlertingInterpret { | ||||||
|  |     async fn crd_exists(&self, crd: &str) -> bool { | ||||||
|  |         let output = Command::new("kubectl") | ||||||
|  |             .args(["get", "crd", crd]) | ||||||
|  |             .output() | ||||||
|  |             .await; | ||||||
|  | 
 | ||||||
|  |         matches!(output, Ok(o) if o.status.success()) | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     async fn install_chart( | ||||||
|  |         &self, | ||||||
|  |         chart_path: String, | ||||||
|  |         chart_name: String, | ||||||
|  |     ) -> Result<(), InterpretError> { | ||||||
|  |         let temp_dir = | ||||||
|  |             tempdir().map_err(|e| InterpretError::new(format!("Tempdir error: {}", e)))?; | ||||||
|  |         let temp_path = temp_dir.path().to_path_buf(); | ||||||
|  |         debug!("Using temp directory: {}", temp_path.display()); | ||||||
|  |         let chart = format!("{}/{}", chart_path, chart_name); | ||||||
|  |         let pull_output = Command::new("helm") | ||||||
|  |             .args(&["pull", &chart, "--destination", temp_path.to_str().unwrap()]) | ||||||
|  |             .output() | ||||||
|  |             .await | ||||||
|  |             .map_err(|e| InterpretError::new(format!("Helm pull error: {}", e)))?; | ||||||
|  | 
 | ||||||
|  |         if !pull_output.status.success() { | ||||||
|  |             return Err(InterpretError::new(format!( | ||||||
|  |                 "Helm pull failed: {}", | ||||||
|  |                 String::from_utf8_lossy(&pull_output.stderr) | ||||||
|  |             ))); | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         let tgz_path = fs::read_dir(&temp_path) | ||||||
|  |             .unwrap() | ||||||
|  |             .filter_map(|entry| { | ||||||
|  |                 let entry = entry.ok()?; | ||||||
|  |                 let path = entry.path(); | ||||||
|  |                 if path.extension()? == "tgz" { | ||||||
|  |                     Some(path) | ||||||
|  |                 } else { | ||||||
|  |                     None | ||||||
|  |                 } | ||||||
|  |             }) | ||||||
|  |             .next() | ||||||
|  |             .ok_or_else(|| InterpretError::new("Could not find pulled Helm chart".into()))?; | ||||||
|  | 
 | ||||||
|  |         debug!("Installing chart from: {}", tgz_path.display()); | ||||||
|  | 
 | ||||||
|  |         let install_output = Command::new("helm") | ||||||
|  |             .args(&[ | ||||||
|  |                 "install", | ||||||
|  |                 &chart_name, | ||||||
|  |                 tgz_path.to_str().unwrap(), | ||||||
|  |                 "--namespace", | ||||||
|  |                 &self.namespace, | ||||||
|  |                 "--create-namespace", | ||||||
|  |                 "--wait", | ||||||
|  |                 "--atomic", | ||||||
|  |             ]) | ||||||
|  |             .output() | ||||||
|  |             .await | ||||||
|  |             .map_err(|e| InterpretError::new(format!("Helm install error: {}", e)))?; | ||||||
|  | 
 | ||||||
|  |         if !install_output.status.success() { | ||||||
|  |             return Err(InterpretError::new(format!( | ||||||
|  |                 "Helm install failed: {}", | ||||||
|  |                 String::from_utf8_lossy(&install_output.stderr) | ||||||
|  |             ))); | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         debug!( | ||||||
|  |             "Installed chart {}/{} in namespace: {}", | ||||||
|  |             &chart_path, &chart_name, self.namespace | ||||||
|  |         ); | ||||||
|  |         Ok(()) | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     async fn ensure_prometheus_operator(&self) -> Result<Outcome, InterpretError> { | ||||||
|  |         self.install_chart( | ||||||
|  |             "oci://hub.nationtech.io/harmony".to_string(), | ||||||
|  |             "nt-prometheus-operator".to_string(), | ||||||
|  |         ) | ||||||
|  |         .await?; | ||||||
|  |         Ok(Outcome::success(format!( | ||||||
|  |             "installed prometheus operator to ns {}", | ||||||
|  |             self.namespace.clone() | ||||||
|  |         ))) | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     async fn ensure_grafana_operator(&self) -> Result<Outcome, InterpretError> { | ||||||
|  |         if self.crd_exists("grafanas.grafana.integreatly.org").await { | ||||||
|  |             debug!("grafana CRDs already exist — skipping install."); | ||||||
|  |             return Ok(Outcome::success("Grafana CRDs already exist".to_string())); | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         let _ = Command::new("helm") | ||||||
|  |             .args(&[ | ||||||
|  |                 "repo", | ||||||
|  |                 "add", | ||||||
|  |                 "grafana-operator", | ||||||
|  |                 "https://grafana.github.io/helm-charts", | ||||||
|  |             ]) | ||||||
|  |             .output() | ||||||
|  |             .await | ||||||
|  |             .unwrap(); | ||||||
|  | 
 | ||||||
|  |         let _ = Command::new("helm") | ||||||
|  |             .args(&["repo", "update"]) | ||||||
|  |             .output() | ||||||
|  |             .await | ||||||
|  |             .unwrap(); | ||||||
|  | 
 | ||||||
|  |         let output = Command::new("helm") | ||||||
|  |             .args(&[ | ||||||
|  |                 "install", | ||||||
|  |                 "grafana-operator", | ||||||
|  |                 "grafana-operator/grafana-operator", | ||||||
|  |                 "--namespace", | ||||||
|  |                 &self.namespace, | ||||||
|  |                 "--create-namespace", | ||||||
|  |                 "--set", | ||||||
|  |                 "namespaceScope=true", | ||||||
|  |             ]) | ||||||
|  |             .output() | ||||||
|  |             .await | ||||||
|  |             .unwrap(); | ||||||
|  | 
 | ||||||
|  |         if !output.status.success() { | ||||||
|  |             return Err(InterpretError::new(format!( | ||||||
|  |                 "helm install failed:\nstdout: {}\nstderr: {}", | ||||||
|  |                 String::from_utf8_lossy(&output.stdout), | ||||||
|  |                 String::from_utf8_lossy(&output.stderr) | ||||||
|  |             ))); | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         Ok(Outcome::success(format!( | ||||||
|  |             "installed grafana operator in ns {}", | ||||||
|  |             self.namespace.clone() | ||||||
|  |         ))) | ||||||
|  |     } | ||||||
|  |     async fn install_prometheus(&self, client: &Arc<K8sClient>) -> Result<Outcome, InterpretError> { | ||||||
|  |         debug!( | ||||||
|  |             "installing crd-prometheuses in namespace {}", | ||||||
|  |             self.namespace.clone() | ||||||
|  |         ); | ||||||
|  |         debug!("building role/rolebinding/serviceaccount for crd-prometheus"); | ||||||
|  |         let rolename = format!("{}-prom", self.namespace.clone()); | ||||||
|  |         let sa_name = format!("{}-prom-sa", self.namespace.clone()); | ||||||
|  |         let role = build_prom_role(rolename.clone(), self.namespace.clone()); | ||||||
|  |         let rolebinding = | ||||||
|  |             build_prom_rolebinding(rolename.clone(), self.namespace.clone(), sa_name.clone()); | ||||||
|  |         let sa = build_prom_service_account(sa_name.clone(), self.namespace.clone()); | ||||||
|  |         let prom_spec = PrometheusSpec { | ||||||
|  |             alerting: Some(PrometheusSpecAlerting { | ||||||
|  |                 alertmanagers: Some(vec![AlertmanagerEndpoints { | ||||||
|  |                     name: Some(format!("alertmanager-operated")), | ||||||
|  |                     namespace: Some(format!("{}", self.namespace.clone())), | ||||||
|  |                     port: Some("web".into()), | ||||||
|  |                     scheme: Some("http".into()), | ||||||
|  |                 }]), | ||||||
|  |             }), | ||||||
|  |             service_account_name: sa_name.clone(), | ||||||
|  |             service_monitor_namespace_selector: Some(LabelSelector { | ||||||
|  |                 match_labels: BTreeMap::from([( | ||||||
|  |                     "kubernetes.io/metadata.name".to_string(), | ||||||
|  |                     format!("{}", self.namespace.clone()), | ||||||
|  |                 )]), | ||||||
|  |                 match_expressions: vec![], | ||||||
|  |             }), | ||||||
|  |             service_monitor_selector: Some(LabelSelector { | ||||||
|  |                 match_labels: BTreeMap::from([("client".to_string(), "prometheus".to_string())]), | ||||||
|  |                 ..Default::default() | ||||||
|  |             }), | ||||||
|  | 
 | ||||||
|  |             service_discovery_role: Some("Endpoints".into()), | ||||||
|  | 
 | ||||||
|  |             pod_monitor_selector: Some(LabelSelector { | ||||||
|  |                 match_labels: BTreeMap::from([("client".to_string(), "prometheus".to_string())]), | ||||||
|  |                 ..Default::default() | ||||||
|  |             }), | ||||||
|  | 
 | ||||||
|  |             rule_selector: Some(LabelSelector { | ||||||
|  |                 match_labels: BTreeMap::from([("role".to_string(), "prometheus-rule".to_string())]), | ||||||
|  |                 ..Default::default() | ||||||
|  |             }), | ||||||
|  | 
 | ||||||
|  |             rule_namespace_selector: Some(LabelSelector { | ||||||
|  |                 match_labels: BTreeMap::from([( | ||||||
|  |                     "kubernetes.io/metadata.name".to_string(), | ||||||
|  |                     format!("{}", self.namespace.clone()), | ||||||
|  |                 )]), | ||||||
|  |                 match_expressions: vec![], | ||||||
|  |             }), | ||||||
|  |         }; | ||||||
|  |         let prom = Prometheus { | ||||||
|  |             metadata: ObjectMeta { | ||||||
|  |                 name: Some(self.namespace.clone()), | ||||||
|  |                 labels: Some(std::collections::BTreeMap::from([ | ||||||
|  |                     ("alertmanagerConfig".to_string(), "enabled".to_string()), | ||||||
|  |                     ("client".to_string(), "prometheus".to_string()), | ||||||
|  |                 ])), | ||||||
|  |                 namespace: Some(self.namespace.clone()), | ||||||
|  |                 ..Default::default() | ||||||
|  |             }, | ||||||
|  |             spec: prom_spec, | ||||||
|  |         }; | ||||||
|  |         client | ||||||
|  |             .apply(&role, Some(&self.namespace.clone())) | ||||||
|  |             .await | ||||||
|  |             .map_err(|e| InterpretError::new(e.to_string()))?; | ||||||
|  |         info!( | ||||||
|  |             "installed prometheus role: {:#?} in ns {:#?}", | ||||||
|  |             role.metadata.name.unwrap(), | ||||||
|  |             role.metadata.namespace.unwrap() | ||||||
|  |         ); | ||||||
|  |         client | ||||||
|  |             .apply(&rolebinding, Some(&self.namespace.clone())) | ||||||
|  |             .await | ||||||
|  |             .map_err(|e| InterpretError::new(e.to_string()))?; | ||||||
|  |         info!( | ||||||
|  |             "installed prometheus rolebinding: {:#?} in ns {:#?}", | ||||||
|  |             rolebinding.metadata.name.unwrap(), | ||||||
|  |             rolebinding.metadata.namespace.unwrap() | ||||||
|  |         ); | ||||||
|  |         client | ||||||
|  |             .apply(&sa, Some(&self.namespace.clone())) | ||||||
|  |             .await | ||||||
|  |             .map_err(|e| InterpretError::new(e.to_string()))?; | ||||||
|  |         info!( | ||||||
|  |             "installed prometheus service account: {:#?} in ns {:#?}", | ||||||
|  |             sa.metadata.name.unwrap(), | ||||||
|  |             sa.metadata.namespace.unwrap() | ||||||
|  |         ); | ||||||
|  |         client | ||||||
|  |             .apply(&prom, Some(&self.namespace.clone())) | ||||||
|  |             .await | ||||||
|  |             .map_err(|e| InterpretError::new(e.to_string()))?; | ||||||
|  |         info!( | ||||||
|  |             "installed prometheus: {:#?} in ns {:#?}", | ||||||
|  |             &prom.metadata.name.clone().unwrap(), | ||||||
|  |             &prom.metadata.namespace.clone().unwrap() | ||||||
|  |         ); | ||||||
|  | 
 | ||||||
|  |         Ok(Outcome::success(format!( | ||||||
|  |             "successfully deployed crd-prometheus {:#?}", | ||||||
|  |             prom | ||||||
|  |         ))) | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     async fn install_alert_manager( | ||||||
|  |         &self, | ||||||
|  |         client: &Arc<K8sClient>, | ||||||
|  |     ) -> Result<Outcome, InterpretError> { | ||||||
|  |         let am = Alertmanager { | ||||||
|  |             metadata: ObjectMeta { | ||||||
|  |                 name: Some(self.namespace.clone()), | ||||||
|  |                 labels: Some(std::collections::BTreeMap::from([( | ||||||
|  |                     "alertmanagerConfig".to_string(), | ||||||
|  |                     "enabled".to_string(), | ||||||
|  |                 )])), | ||||||
|  |                 namespace: Some(self.namespace.clone()), | ||||||
|  |                 ..Default::default() | ||||||
|  |             }, | ||||||
|  |             spec: AlertmanagerSpec::default(), | ||||||
|  |         }; | ||||||
|  |         client | ||||||
|  |             .apply(&am, Some(&self.namespace.clone())) | ||||||
|  |             .await | ||||||
|  |             .map_err(|e| InterpretError::new(e.to_string()))?; | ||||||
|  |         Ok(Outcome::success(format!( | ||||||
|  |             "successfully deployed service monitor {:#?}", | ||||||
|  |             am.metadata.name | ||||||
|  |         ))) | ||||||
|  |     } | ||||||
|  |     async fn install_monitors( | ||||||
|  |         &self, | ||||||
|  |         mut monitors: Vec<ServiceMonitor>, | ||||||
|  |         client: &Arc<K8sClient>, | ||||||
|  |     ) -> Result<Outcome, InterpretError> { | ||||||
|  |         let default_service_monitor = ServiceMonitor { | ||||||
|  |             metadata: ObjectMeta { | ||||||
|  |                 name: Some(self.namespace.clone()), | ||||||
|  |                 labels: Some(std::collections::BTreeMap::from([ | ||||||
|  |                     ("alertmanagerConfig".to_string(), "enabled".to_string()), | ||||||
|  |                     ("client".to_string(), "prometheus".to_string()), | ||||||
|  |                     ( | ||||||
|  |                         "app.kubernetes.io/name".to_string(), | ||||||
|  |                         "kube-state-metrics".to_string(), | ||||||
|  |                     ), | ||||||
|  |                 ])), | ||||||
|  |                 namespace: Some(self.namespace.clone()), | ||||||
|  |                 ..Default::default() | ||||||
|  |             }, | ||||||
|  |             spec: ServiceMonitorSpec::default(), | ||||||
|  |         }; | ||||||
|  |         monitors.push(default_service_monitor); | ||||||
|  |         for monitor in monitors.iter() { | ||||||
|  |             client | ||||||
|  |                 .apply(monitor, Some(&self.namespace.clone())) | ||||||
|  |                 .await | ||||||
|  |                 .map_err(|e| InterpretError::new(e.to_string()))?; | ||||||
|  |         } | ||||||
|  |         Ok(Outcome::success( | ||||||
|  |             "succesfully deployed service monitors".to_string(), | ||||||
|  |         )) | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     async fn install_rules( | ||||||
|  |         &self, | ||||||
|  |         rules: &Vec<RuleGroup>, | ||||||
|  |         client: &Arc<K8sClient>, | ||||||
|  |     ) -> Result<Outcome, InterpretError> { | ||||||
|  |         let mut prom_rule_spec = PrometheusRuleSpec { | ||||||
|  |             groups: rules.clone(), | ||||||
|  |         }; | ||||||
|  | 
 | ||||||
|  |         let default_rules_group = RuleGroup { | ||||||
|  |             name: format!("default-rules"), | ||||||
|  |             rules: build_default_application_rules(), | ||||||
|  |         }; | ||||||
|  | 
 | ||||||
|  |         prom_rule_spec.groups.push(default_rules_group); | ||||||
|  |         let prom_rules = PrometheusRule { | ||||||
|  |             metadata: ObjectMeta { | ||||||
|  |                 name: Some(self.namespace.clone()), | ||||||
|  |                 labels: Some(std::collections::BTreeMap::from([ | ||||||
|  |                     ("alertmanagerConfig".to_string(), "enabled".to_string()), | ||||||
|  |                     ("role".to_string(), "prometheus-rule".to_string()), | ||||||
|  |                 ])), | ||||||
|  |                 namespace: Some(self.namespace.clone()), | ||||||
|  |                 ..Default::default() | ||||||
|  |             }, | ||||||
|  |             spec: prom_rule_spec, | ||||||
|  |         }; | ||||||
|  |         client | ||||||
|  |             .apply(&prom_rules, Some(&self.namespace)) | ||||||
|  |             .await | ||||||
|  |             .map_err(|e| InterpretError::new(e.to_string()))?; | ||||||
|  |         Ok(Outcome::success(format!( | ||||||
|  |             "successfully deployed rules {:#?}", | ||||||
|  |             prom_rules.metadata.name | ||||||
|  |         ))) | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     async fn install_client_kube_metrics(&self) -> Result<Outcome, InterpretError> { | ||||||
|  |         self.install_chart( | ||||||
|  |             "oci://hub.nationtech.io/harmony".to_string(), | ||||||
|  |             "nt-kube-metrics".to_string(), | ||||||
|  |         ) | ||||||
|  |         .await?; | ||||||
|  |         Ok(Outcome::success(format!( | ||||||
|  |             "Installed client kube metrics in ns {}", | ||||||
|  |             &self.namespace | ||||||
|  |         ))) | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     async fn install_grafana(&self, client: &Arc<K8sClient>) -> Result<Outcome, InterpretError> { | ||||||
|  |         let mut label = BTreeMap::new(); | ||||||
|  |         label.insert("dashboards".to_string(), "grafana".to_string()); | ||||||
|  |         let labels = LabelSelector { | ||||||
|  |             match_labels: label.clone(), | ||||||
|  |             match_expressions: vec![], | ||||||
|  |         }; | ||||||
|  |         let mut json_data = BTreeMap::new(); | ||||||
|  |         json_data.insert("timeInterval".to_string(), "5s".to_string()); | ||||||
|  |         let namespace = self.namespace.clone(); | ||||||
|  | 
 | ||||||
|  |         let json = build_default_dashboard(&namespace); | ||||||
|  | 
 | ||||||
|  |         let graf_data_source = GrafanaDatasource { | ||||||
|  |             metadata: ObjectMeta { | ||||||
|  |                 name: Some(format!("grafana-datasource-{}", self.namespace.clone())), | ||||||
|  |                 namespace: Some(self.namespace.clone()), | ||||||
|  |                 ..Default::default() | ||||||
|  |             }, | ||||||
|  |             spec: GrafanaDatasourceSpec { | ||||||
|  |                 instance_selector: labels.clone(), | ||||||
|  |                 allow_cross_namespace_import: Some(false), | ||||||
|  |                 datasource: GrafanaDatasourceConfig { | ||||||
|  |                     access: "proxy".to_string(), | ||||||
|  |                     database: Some("prometheus".to_string()), | ||||||
|  |                     json_data: Some(json_data), | ||||||
|  |                     //TODO this is fragile
 | ||||||
|  |                     name: format!("prometheus-{}-0", self.namespace.clone()), | ||||||
|  |                     r#type: "prometheus".to_string(), | ||||||
|  |                     url: format!( | ||||||
|  |                         "http://prometheus-operated.{}.svc.cluster.local:9090", | ||||||
|  |                         self.namespace.clone() | ||||||
|  |                     ), | ||||||
|  |                 }, | ||||||
|  |             }, | ||||||
|  |         }; | ||||||
|  | 
 | ||||||
|  |         client | ||||||
|  |             .apply(&graf_data_source, Some(&self.namespace)) | ||||||
|  |             .await | ||||||
|  |             .map_err(|e| InterpretError::new(e.to_string()))?; | ||||||
|  | 
 | ||||||
|  |         let graf_dashboard = GrafanaDashboard { | ||||||
|  |             metadata: ObjectMeta { | ||||||
|  |                 name: Some(format!("grafana-dashboard-{}", self.namespace.clone())), | ||||||
|  |                 namespace: Some(self.namespace.clone()), | ||||||
|  |                 ..Default::default() | ||||||
|  |             }, | ||||||
|  |             spec: GrafanaDashboardSpec { | ||||||
|  |                 resync_period: Some("30s".to_string()), | ||||||
|  |                 instance_selector: labels.clone(), | ||||||
|  |                 json, | ||||||
|  |             }, | ||||||
|  |         }; | ||||||
|  | 
 | ||||||
|  |         client | ||||||
|  |             .apply(&graf_dashboard, Some(&self.namespace)) | ||||||
|  |             .await | ||||||
|  |             .map_err(|e| InterpretError::new(e.to_string()))?; | ||||||
|  | 
 | ||||||
|  |         let grafana = Grafana { | ||||||
|  |             metadata: ObjectMeta { | ||||||
|  |                 name: Some(format!("grafana-{}", self.namespace.clone())), | ||||||
|  |                 namespace: Some(self.namespace.clone()), | ||||||
|  |                 labels: Some(label.clone()), | ||||||
|  |                 ..Default::default() | ||||||
|  |             }, | ||||||
|  |             spec: GrafanaSpec { | ||||||
|  |                 config: None, | ||||||
|  |                 admin_user: None, | ||||||
|  |                 admin_password: None, | ||||||
|  |                 ingress: None, | ||||||
|  |                 persistence: None, | ||||||
|  |                 resources: None, | ||||||
|  |             }, | ||||||
|  |         }; | ||||||
|  |         client | ||||||
|  |             .apply(&grafana, Some(&self.namespace)) | ||||||
|  |             .await | ||||||
|  |             .map_err(|e| InterpretError::new(e.to_string()))?; | ||||||
|  |         Ok(Outcome::success(format!( | ||||||
|  |             "successfully deployed grafana instance {:#?}", | ||||||
|  |             grafana.metadata.name | ||||||
|  |         ))) | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     async fn install_receivers( | ||||||
|  |         &self, | ||||||
|  |         receivers: &Vec<Box<dyn CRDAlertManagerReceiver>>, | ||||||
|  |         client: &Arc<K8sClient>, | ||||||
|  |     ) -> Result<Outcome, InterpretError> { | ||||||
|  |         for receiver in receivers.iter() { | ||||||
|  |             let alertmanager_config: AlertmanagerConfig = receiver | ||||||
|  |                 .configure_receiver(&client, self.namespace.clone()) | ||||||
|  |                 .await; | ||||||
|  |             let sender = CRDPrometheus { | ||||||
|  |                 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!("successfully deployed receivers"))) | ||||||
|  |     } | ||||||
|  | } | ||||||
| @ -8,7 +8,9 @@ use log::{debug, info}; | |||||||
| use serde::Serialize; | use serde::Serialize; | ||||||
| use tokio::process::Command; | use tokio::process::Command; | ||||||
| 
 | 
 | ||||||
| use crate::modules::monitoring::kube_prometheus::crd::crd_alertmanager_config::CRDPrometheus; | use crate::modules::monitoring::kube_prometheus::crd::crd_alertmanager_config::{ | ||||||
|  |     AlertmanagerConfig, CRDAlertManagerReceiver, CRDPrometheus, | ||||||
|  | }; | ||||||
| use crate::modules::monitoring::kube_prometheus::crd::crd_default_rules::build_default_application_rules; | use crate::modules::monitoring::kube_prometheus::crd::crd_default_rules::build_default_application_rules; | ||||||
| use crate::modules::monitoring::kube_prometheus::crd::crd_grafana::{ | use crate::modules::monitoring::kube_prometheus::crd::crd_grafana::{ | ||||||
|     Grafana, GrafanaDashboard, GrafanaDashboardSpec, GrafanaDatasource, GrafanaDatasourceConfig, |     Grafana, GrafanaDashboard, GrafanaDashboardSpec, GrafanaDatasource, GrafanaDatasourceConfig, | ||||||
| @ -21,7 +23,6 @@ use crate::modules::monitoring::kube_prometheus::crd::grafana_default_dashboard: | |||||||
| use crate::modules::monitoring::kube_prometheus::crd::service_monitor::{ | use crate::modules::monitoring::kube_prometheus::crd::service_monitor::{ | ||||||
|     ServiceMonitor, ServiceMonitorSpec, |     ServiceMonitor, ServiceMonitorSpec, | ||||||
| }; | }; | ||||||
| use crate::topology::oberservability::monitoring::AlertReceiver; |  | ||||||
| use crate::topology::{K8sclient, Topology, k8s::K8sClient}; | use crate::topology::{K8sclient, Topology, k8s::K8sClient}; | ||||||
| use crate::{ | use crate::{ | ||||||
|     data::{Id, Version}, |     data::{Id, Version}, | ||||||
| @ -38,12 +39,12 @@ use crate::{ | |||||||
|     score::Score, |     score::Score, | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| use super::prometheus::PrometheusApplicationMonitoring; | use super::prometheus::{PrometheusApplicationMonitoring, PrometheusMonitoring}; | ||||||
| 
 | 
 | ||||||
| #[derive(Clone, Debug, Serialize)] | #[derive(Clone, Debug, Serialize)] | ||||||
| pub struct K8sPrometheusCRDAlertingScore { | pub struct K8sPrometheusCRDAlertingScore { | ||||||
|     pub sender: CRDPrometheus, |     pub sender: CRDPrometheus, | ||||||
|     pub receivers: Vec<Box<dyn AlertReceiver<CRDPrometheus>>>, |     pub receivers: Vec<Box<dyn CRDAlertManagerReceiver>>, | ||||||
|     pub service_monitors: Vec<ServiceMonitor>, |     pub service_monitors: Vec<ServiceMonitor>, | ||||||
|     pub prometheus_rules: Vec<RuleGroup>, |     pub prometheus_rules: Vec<RuleGroup>, | ||||||
| } | } | ||||||
| @ -68,7 +69,7 @@ impl<T: Topology + K8sclient + PrometheusApplicationMonitoring<CRDPrometheus>> S | |||||||
| #[derive(Clone, Debug)] | #[derive(Clone, Debug)] | ||||||
| pub struct K8sPrometheusCRDAlertingInterpret { | pub struct K8sPrometheusCRDAlertingInterpret { | ||||||
|     pub sender: CRDPrometheus, |     pub sender: CRDPrometheus, | ||||||
|     pub receivers: Vec<Box<dyn AlertReceiver<CRDPrometheus>>>, |     pub receivers: Vec<Box<dyn CRDAlertManagerReceiver>>, | ||||||
|     pub service_monitors: Vec<ServiceMonitor>, |     pub service_monitors: Vec<ServiceMonitor>, | ||||||
|     pub prometheus_rules: Vec<RuleGroup>, |     pub prometheus_rules: Vec<RuleGroup>, | ||||||
| } | } | ||||||
| @ -88,8 +89,7 @@ impl<T: Topology + K8sclient + PrometheusApplicationMonitoring<CRDPrometheus>> I | |||||||
|         self.install_alert_manager(&client).await?; |         self.install_alert_manager(&client).await?; | ||||||
|         self.install_client_kube_metrics().await?; |         self.install_client_kube_metrics().await?; | ||||||
|         self.install_grafana(&client).await?; |         self.install_grafana(&client).await?; | ||||||
|         self.install_receivers(&self.sender, &self.receivers) |         self.install_receivers(&self.receivers, &client).await?; | ||||||
|             .await?; |  | ||||||
|         self.install_rules(&self.prometheus_rules, &client).await?; |         self.install_rules(&self.prometheus_rules, &client).await?; | ||||||
|         self.install_monitors(self.service_monitors.clone(), &client) |         self.install_monitors(self.service_monitors.clone(), &client) | ||||||
|             .await?; |             .await?; | ||||||
| @ -246,7 +246,6 @@ impl K8sPrometheusCRDAlertingInterpret { | |||||||
|             self.sender.namespace.clone().clone() |             self.sender.namespace.clone().clone() | ||||||
|         ))) |         ))) | ||||||
|     } |     } | ||||||
| 
 |  | ||||||
|     async fn install_prometheus(&self, client: &Arc<K8sClient>) -> Result<Outcome, InterpretError> { |     async fn install_prometheus(&self, client: &Arc<K8sClient>) -> Result<Outcome, InterpretError> { | ||||||
|         debug!( |         debug!( | ||||||
|             "installing crd-prometheuses in namespace {}", |             "installing crd-prometheuses in namespace {}", | ||||||
| @ -560,14 +559,22 @@ impl K8sPrometheusCRDAlertingInterpret { | |||||||
| 
 | 
 | ||||||
|     async fn install_receivers( |     async fn install_receivers( | ||||||
|         &self, |         &self, | ||||||
|         sender: &CRDPrometheus, |         receivers: &Vec<Box<dyn CRDAlertManagerReceiver>>, | ||||||
|         receivers: &Vec<Box<dyn AlertReceiver<CRDPrometheus>>>, |         client: &Arc<K8sClient>, | ||||||
|     ) -> Result<Outcome, InterpretError> { |     ) -> Result<Outcome, InterpretError> { | ||||||
|         for receiver in receivers.iter() { |         for receiver in receivers.iter() { | ||||||
|             receiver.install(sender).await.map_err(|err| { |             let alertmanager_config: AlertmanagerConfig = receiver | ||||||
|  |                 .configure_receiver(&client, self.sender.namespace.clone().clone()) | ||||||
|  |                 .await; | ||||||
|  |             let sender = CRDPrometheus { | ||||||
|  |                 alertmanager_configs: alertmanager_config, | ||||||
|  |                 namespace: self.sender.namespace.clone().clone(), | ||||||
|  |                 client: self.sender.client.clone(), | ||||||
|  |             }; | ||||||
|  |             receiver.install(&sender).await.map_err(|err| { | ||||||
|                 InterpretError::new(format!("failed to install receiver: {}", err)) |                 InterpretError::new(format!("failed to install receiver: {}", err)) | ||||||
|             })?; |             })?; | ||||||
|         } |         } | ||||||
|         Ok(Outcome::success("successfully deployed receivers".into())) |         Ok(Outcome::success(format!("successfully deployed receivers"))) | ||||||
|     } |     } | ||||||
| } | } | ||||||
|  | |||||||
| @ -1,3 +1,4 @@ | |||||||
| pub mod alerts; | pub mod alerts; | ||||||
|  | pub mod k3d_prometheus_alerting_score; | ||||||
| pub mod k8s_prometheus_alerting_score; | pub mod k8s_prometheus_alerting_score; | ||||||
| pub mod prometheus; | pub mod prometheus; | ||||||
|  | |||||||
| @ -3,11 +3,21 @@ use async_trait::async_trait; | |||||||
| use crate::{ | use crate::{ | ||||||
|     interpret::{InterpretError, Outcome}, |     interpret::{InterpretError, Outcome}, | ||||||
|     inventory::Inventory, |     inventory::Inventory, | ||||||
|  |     modules::monitoring::kube_prometheus::crd::crd_alertmanager_config::CRDAlertManagerReceiver, | ||||||
|     topology::oberservability::monitoring::{AlertReceiver, AlertSender}, |     topology::oberservability::monitoring::{AlertReceiver, AlertSender}, | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| #[async_trait] | #[async_trait] | ||||||
| pub trait PrometheusApplicationMonitoring<S: AlertSender> { | pub trait PrometheusMonitoring<S: AlertSender> { | ||||||
|  |     async fn ensure_prometheus_operator(&self, sender: &S) -> Result<Outcome, InterpretError>; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | #[async_trait] | ||||||
|  | pub trait PrometheusApplicationMonitoring<S: AlertSender>: PrometheusMonitoring<S> { | ||||||
|  |     async fn configure_receivers( | ||||||
|  |         &self, | ||||||
|  |         receivers: Option<Vec<Box<dyn AlertReceiver<S>>>>, | ||||||
|  |     ) -> Option<Vec<Box<dyn CRDAlertManagerReceiver>>>; | ||||||
|     async fn install_prometheus( |     async fn install_prometheus( | ||||||
|         &self, |         &self, | ||||||
|         sender: &S, |         sender: &S, | ||||||
|  | |||||||
		Loading…
	
		Reference in New Issue
	
	Block a user