Compare commits
	
		
			No commits in common. "1f3796f50301b38746366c4d5e4909332db203dd" and "f073b7e5fb15bf7da0d4fb9fdcee60aa665c854c" have entirely different histories.
		
	
	
		
			1f3796f503
			...
			f073b7e5fb
		
	
		
| @ -3,7 +3,7 @@ use harmony::{ | |||||||
|     modules::{ |     modules::{ | ||||||
|         application::{ |         application::{ | ||||||
|             ApplicationScore, RustWebFramework, RustWebapp, |             ApplicationScore, RustWebFramework, RustWebapp, | ||||||
|             features::{Monitoring, PackagingDeployment}, |             features::{PackagingDeployment, rhob_monitoring::Monitoring}, | ||||||
|         }, |         }, | ||||||
|         monitoring::alert_channel::discord_alert_channel::DiscordWebhook, |         monitoring::alert_channel::discord_alert_channel::DiscordWebhook, | ||||||
|     }, |     }, | ||||||
|  | |||||||
| @ -1,7 +1,7 @@ | |||||||
| use std::{collections::BTreeMap, process::Command, sync::Arc}; | use std::{process::Command, sync::Arc}; | ||||||
| 
 | 
 | ||||||
| use async_trait::async_trait; | use async_trait::async_trait; | ||||||
| use kube::api::{GroupVersionKind, ObjectMeta}; | use kube::api::GroupVersionKind; | ||||||
| use log::{debug, info, warn}; | use log::{debug, info, warn}; | ||||||
| use serde::Serialize; | use serde::Serialize; | ||||||
| use tokio::sync::OnceCell; | use tokio::sync::OnceCell; | ||||||
| @ -12,25 +12,14 @@ use crate::{ | |||||||
|     inventory::Inventory, |     inventory::Inventory, | ||||||
|     modules::{ |     modules::{ | ||||||
|         k3d::K3DInstallationScore, |         k3d::K3DInstallationScore, | ||||||
|         k8s::ingress::{K8sIngressScore, PathType}, |         monitoring::kube_prometheus::crd::{ | ||||||
|         monitoring::{ |             crd_alertmanager_config::CRDPrometheus, | ||||||
|             grafana::{grafana::Grafana, helm::helm_grafana::grafana_helm_chart_score}, |             prometheus_operator::prometheus_operator_helm_chart_score, | ||||||
|             kube_prometheus::crd::{ |             rhob_alertmanager_config::RHOBObservability, | ||||||
|                 crd_alertmanager_config::CRDPrometheus, |  | ||||||
|                 crd_grafana::{ |  | ||||||
|                     Grafana as GrafanaCRD, GrafanaDashboard, GrafanaDashboardSpec, |  | ||||||
|                     GrafanaDatasource, GrafanaDatasourceConfig, GrafanaDatasourceSpec, GrafanaSpec, |  | ||||||
|                 }, |  | ||||||
|                 crd_prometheuses::LabelSelector, |  | ||||||
|                 grafana_default_dashboard::build_default_dashboard, |  | ||||||
|                 prometheus_operator::prometheus_operator_helm_chart_score, |  | ||||||
|                 rhob_alertmanager_config::RHOBObservability, |  | ||||||
|                 service_monitor::ServiceMonitor, |  | ||||||
|             }, |  | ||||||
|         }, |         }, | ||||||
|         prometheus::{ |         prometheus::{ | ||||||
|             k8s_prometheus_alerting_score::K8sPrometheusCRDAlertingScore, |             k8s_prometheus_alerting_score::K8sPrometheusCRDAlertingScore, | ||||||
|             prometheus::PrometheusMonitoring, rhob_alerting_score::RHOBAlertingScore, |             prometheus::PrometheusApplicationMonitoring, rhob_alerting_score::RHOBAlertingScore, | ||||||
|         }, |         }, | ||||||
|     }, |     }, | ||||||
|     score::Score, |     score::Score, | ||||||
| @ -97,120 +86,41 @@ impl K8sclient for K8sAnywhereTopology { | |||||||
| } | } | ||||||
| 
 | 
 | ||||||
| #[async_trait] | #[async_trait] | ||||||
| impl Grafana for K8sAnywhereTopology { | impl PrometheusApplicationMonitoring<CRDPrometheus> for K8sAnywhereTopology { | ||||||
|     async fn ensure_grafana_operator( |  | ||||||
|         &self, |  | ||||||
|         inventory: &Inventory, |  | ||||||
|     ) -> Result<PreparationOutcome, PreparationError> { |  | ||||||
|         debug!("ensure grafana operator"); |  | ||||||
|         let client = self.k8s_client().await.unwrap(); |  | ||||||
|         let grafana_gvk = GroupVersionKind { |  | ||||||
|             group: "grafana.integreatly.org".to_string(), |  | ||||||
|             version: "v1beta1".to_string(), |  | ||||||
|             kind: "Grafana".to_string(), |  | ||||||
|         }; |  | ||||||
|         let name = "grafanas.grafana.integreatly.org"; |  | ||||||
|         let ns = "grafana"; |  | ||||||
| 
 |  | ||||||
|         let grafana_crd = client |  | ||||||
|             .get_resource_json_value(name, Some(ns), &grafana_gvk) |  | ||||||
|             .await; |  | ||||||
|         match grafana_crd { |  | ||||||
|             Ok(_) => { |  | ||||||
|                 return Ok(PreparationOutcome::Success { |  | ||||||
|                     details: "Found grafana CRDs in cluster".to_string(), |  | ||||||
|                 }); |  | ||||||
|             } |  | ||||||
| 
 |  | ||||||
|             Err(_) => { |  | ||||||
|                 return self |  | ||||||
|                     .install_grafana_operator(inventory, Some("grafana")) |  | ||||||
|                     .await; |  | ||||||
|             } |  | ||||||
|         }; |  | ||||||
|     } |  | ||||||
|     async fn install_grafana(&self) -> Result<PreparationOutcome, PreparationError> { |  | ||||||
|         debug!("install grafana"); |  | ||||||
|         let ns = "grafana"; |  | ||||||
| 
 |  | ||||||
|         let mut label = BTreeMap::new(); |  | ||||||
| 
 |  | ||||||
|         label.insert("dashboards".to_string(), "grafana".to_string()); |  | ||||||
|         let label_selector = LabelSelector { |  | ||||||
|             match_labels: label.clone(), |  | ||||||
|             match_expressions: vec![], |  | ||||||
|         }; |  | ||||||
| 
 |  | ||||||
|         let client = self.k8s_client().await?; |  | ||||||
| 
 |  | ||||||
|         let datasource = self.build_grafana_datasource(ns, &label_selector); |  | ||||||
| 
 |  | ||||||
|         client.apply(&datasource, Some(ns)).await?; |  | ||||||
| 
 |  | ||||||
|         let dashboard = self.build_grafana_dashboard(ns, &label_selector); |  | ||||||
| 
 |  | ||||||
|         client.apply(&dashboard, Some(ns)).await?; |  | ||||||
| 
 |  | ||||||
|         let grafana = self.build_grafana(ns, &label); |  | ||||||
| 
 |  | ||||||
|         client.apply(&grafana, Some(ns)).await?; |  | ||||||
| 
 |  | ||||||
|         let grafana_ingress = self.build_grafana_ingress(ns).await; |  | ||||||
| 
 |  | ||||||
|         grafana_ingress |  | ||||||
|             .interpret(&Inventory::empty(), self) |  | ||||||
|             .await |  | ||||||
|             .map_err(|e| PreparationError::new(e.to_string()))?; |  | ||||||
| 
 |  | ||||||
|         Ok(PreparationOutcome::Success { |  | ||||||
|             details: "Installed grafana composants".to_string(), |  | ||||||
|         }) |  | ||||||
|     } |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| #[async_trait] |  | ||||||
| impl PrometheusMonitoring<CRDPrometheus> for K8sAnywhereTopology { |  | ||||||
|     async fn install_prometheus( |     async fn install_prometheus( | ||||||
|         &self, |         &self, | ||||||
|         sender: &CRDPrometheus, |         sender: &CRDPrometheus, | ||||||
|         _inventory: &Inventory, |         inventory: &Inventory, | ||||||
|         _receivers: Option<Vec<Box<dyn AlertReceiver<CRDPrometheus>>>>, |         receivers: Option<Vec<Box<dyn AlertReceiver<CRDPrometheus>>>>, | ||||||
|     ) -> Result<PreparationOutcome, PreparationError> { |  | ||||||
|         let client = self.k8s_client().await?; |  | ||||||
| 
 |  | ||||||
|         for monitor in sender.service_monitor.iter() { |  | ||||||
|             client |  | ||||||
|                 .apply(monitor, Some(&sender.namespace)) |  | ||||||
|                 .await |  | ||||||
|                 .map_err(|e| PreparationError::new(e.to_string()))?; |  | ||||||
|         } |  | ||||||
|         Ok(PreparationOutcome::Success { |  | ||||||
|             details: "successfuly installed prometheus components".to_string(), |  | ||||||
|         }) |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     async fn ensure_prometheus_operator( |  | ||||||
|         &self, |  | ||||||
|         sender: &CRDPrometheus, |  | ||||||
|         _inventory: &Inventory, |  | ||||||
|     ) -> Result<PreparationOutcome, PreparationError> { |     ) -> Result<PreparationOutcome, PreparationError> { | ||||||
|         let po_result = self.ensure_prometheus_operator(sender).await?; |         let po_result = self.ensure_prometheus_operator(sender).await?; | ||||||
| 
 | 
 | ||||||
|         match po_result { |         if po_result == PreparationOutcome::Noop { | ||||||
|             PreparationOutcome::Success { details: _ } => { |             debug!("Skipping Prometheus CR installation due to missing operator."); | ||||||
|                 debug!("Detected prometheus crds operator present in cluster."); |             return Ok(po_result); | ||||||
|                 return Ok(po_result); |         } | ||||||
|             } | 
 | ||||||
|             PreparationOutcome::Noop => { |         let result = self | ||||||
|                 debug!("Skipping Prometheus CR installation due to missing operator."); |             .get_k8s_prometheus_application_score(sender.clone(), receivers) | ||||||
|                 return Ok(po_result); |             .await | ||||||
|             } |             .interpret(inventory, self) | ||||||
|  |             .await; | ||||||
|  | 
 | ||||||
|  |         match result { | ||||||
|  |             Ok(outcome) => match outcome.status { | ||||||
|  |                 InterpretStatus::SUCCESS => Ok(PreparationOutcome::Success { | ||||||
|  |                     details: outcome.message, | ||||||
|  |                 }), | ||||||
|  |                 InterpretStatus::NOOP => Ok(PreparationOutcome::Noop), | ||||||
|  |                 _ => Err(PreparationError::new(outcome.message)), | ||||||
|  |             }, | ||||||
|  |             Err(err) => Err(PreparationError::new(err.to_string())), | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| #[async_trait] | #[async_trait] | ||||||
| impl PrometheusMonitoring<RHOBObservability> for K8sAnywhereTopology { | impl PrometheusApplicationMonitoring<RHOBObservability> for K8sAnywhereTopology { | ||||||
|     async fn install_prometheus( |     async fn install_prometheus( | ||||||
|         &self, |         &self, | ||||||
|         sender: &RHOBObservability, |         sender: &RHOBObservability, | ||||||
| @ -244,14 +154,6 @@ impl PrometheusMonitoring<RHOBObservability> for K8sAnywhereTopology { | |||||||
|             Err(err) => Err(PreparationError::new(err.to_string())), |             Err(err) => Err(PreparationError::new(err.to_string())), | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
| 
 |  | ||||||
|     async fn ensure_prometheus_operator( |  | ||||||
|         &self, |  | ||||||
|         sender: &RHOBObservability, |  | ||||||
|         inventory: &Inventory, |  | ||||||
|     ) -> Result<PreparationOutcome, PreparationError> { |  | ||||||
|         todo!() |  | ||||||
|     } |  | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| impl Serialize for K8sAnywhereTopology { | impl Serialize for K8sAnywhereTopology { | ||||||
| @ -334,95 +236,6 @@ impl K8sAnywhereTopology { | |||||||
|             .clone() |             .clone() | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     fn build_grafana_datasource( |  | ||||||
|         &self, |  | ||||||
|         ns: &str, |  | ||||||
|         label_selector: &LabelSelector, |  | ||||||
|     ) -> GrafanaDatasource { |  | ||||||
|         let mut json_data = BTreeMap::new(); |  | ||||||
|         json_data.insert("timeInterval".to_string(), "5s".to_string()); |  | ||||||
| 
 |  | ||||||
|         let graf_data_source = GrafanaDatasource { |  | ||||||
|             metadata: ObjectMeta { |  | ||||||
|                 name: Some(format!("grafana-datasource-{}", ns)), |  | ||||||
|                 namespace: Some(ns.to_string()), |  | ||||||
|                 ..Default::default() |  | ||||||
|             }, |  | ||||||
|             spec: GrafanaDatasourceSpec { |  | ||||||
|                 instance_selector: label_selector.clone(), |  | ||||||
|                 allow_cross_namespace_import: Some(false), |  | ||||||
|                 datasource: GrafanaDatasourceConfig { |  | ||||||
|                     access: "proxy".to_string(), |  | ||||||
|                     database: Some("prometheus".to_string()), |  | ||||||
|                     json_data: Some(json_data), |  | ||||||
|                     //this is fragile
 |  | ||||||
|                     name: format!("prometheus-{}-0", ns), |  | ||||||
|                     r#type: "prometheus".to_string(), |  | ||||||
|                     url: format!("http://prometheus-operated.{}.svc.cluster.local:9090", ns), |  | ||||||
|                 }, |  | ||||||
|             }, |  | ||||||
|         }; |  | ||||||
|         graf_data_source |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     fn build_grafana_dashboard( |  | ||||||
|         &self, |  | ||||||
|         ns: &str, |  | ||||||
|         label_selector: &LabelSelector, |  | ||||||
|     ) -> GrafanaDashboard { |  | ||||||
|         let json = build_default_dashboard(ns); |  | ||||||
|         let graf_dashboard = GrafanaDashboard { |  | ||||||
|             metadata: ObjectMeta { |  | ||||||
|                 name: Some(format!("grafana-dashboard-{}", ns)), |  | ||||||
|                 namespace: Some(ns.to_string()), |  | ||||||
|                 ..Default::default() |  | ||||||
|             }, |  | ||||||
|             spec: GrafanaDashboardSpec { |  | ||||||
|                 resync_period: Some("30s".to_string()), |  | ||||||
|                 instance_selector: label_selector.clone(), |  | ||||||
|                 json, |  | ||||||
|             }, |  | ||||||
|         }; |  | ||||||
|         graf_dashboard |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     fn build_grafana(&self, ns: &str, labels: &BTreeMap<String, String>) -> GrafanaCRD { |  | ||||||
|         let grafana = GrafanaCRD { |  | ||||||
|             metadata: ObjectMeta { |  | ||||||
|                 name: Some(format!("grafana-{}", ns)), |  | ||||||
|                 namespace: Some(ns.to_string()), |  | ||||||
|                 labels: Some(labels.clone()), |  | ||||||
|                 ..Default::default() |  | ||||||
|             }, |  | ||||||
|             spec: GrafanaSpec { |  | ||||||
|                 config: None, |  | ||||||
|                 admin_user: None, |  | ||||||
|                 admin_password: None, |  | ||||||
|                 ingress: None, |  | ||||||
|                 persistence: None, |  | ||||||
|                 resources: None, |  | ||||||
|             }, |  | ||||||
|         }; |  | ||||||
|         grafana |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     async fn build_grafana_ingress(&self, ns: &str) -> K8sIngressScore { |  | ||||||
|         let domain = self.get_domain(&format!("grafana-{}", ns)).await.unwrap(); |  | ||||||
|         let name = format!("{}-grafana", ns); |  | ||||||
|         let backend_service = format!("grafana-{}-service", ns); |  | ||||||
| 
 |  | ||||||
|         K8sIngressScore { |  | ||||||
|             name: fqdn::fqdn!(&name), |  | ||||||
|             host: fqdn::fqdn!(&domain), |  | ||||||
|             backend_service: fqdn::fqdn!(&backend_service), |  | ||||||
|             port: 3000, |  | ||||||
|             path: Some("/".to_string()), |  | ||||||
|             path_type: Some(PathType::Prefix), |  | ||||||
|             namespace: Some(fqdn::fqdn!(&ns)), |  | ||||||
|             ingress_class_name: Some("openshift-default".to_string()), |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     async fn get_cluster_observability_operator_prometheus_application_score( |     async fn get_cluster_observability_operator_prometheus_application_score( | ||||||
|         &self, |         &self, | ||||||
|         sender: RHOBObservability, |         sender: RHOBObservability, | ||||||
| @ -440,22 +253,12 @@ impl K8sAnywhereTopology { | |||||||
|         &self, |         &self, | ||||||
|         sender: CRDPrometheus, |         sender: CRDPrometheus, | ||||||
|         receivers: Option<Vec<Box<dyn AlertReceiver<CRDPrometheus>>>>, |         receivers: Option<Vec<Box<dyn AlertReceiver<CRDPrometheus>>>>, | ||||||
|         service_monitors: Option<Vec<ServiceMonitor>>, |  | ||||||
|     ) -> K8sPrometheusCRDAlertingScore { |     ) -> K8sPrometheusCRDAlertingScore { | ||||||
|         if let Some(sm) = service_monitors { |         K8sPrometheusCRDAlertingScore { | ||||||
|             return K8sPrometheusCRDAlertingScore { |             sender, | ||||||
|                 sender, |             receivers: receivers.unwrap_or_default(), | ||||||
|                 receivers: receivers.unwrap_or_default(), |             service_monitors: vec![], | ||||||
|                 service_monitors: sm, |             prometheus_rules: vec![], | ||||||
|                 prometheus_rules: vec![], |  | ||||||
|             }; |  | ||||||
|         } else { |  | ||||||
|             return K8sPrometheusCRDAlertingScore { |  | ||||||
|                 sender, |  | ||||||
|                 receivers: receivers.unwrap_or_default(), |  | ||||||
|                 service_monitors: vec![], |  | ||||||
|                 prometheus_rules: vec![], |  | ||||||
|             }; |  | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
| @ -724,29 +527,6 @@ impl K8sAnywhereTopology { | |||||||
|             details: "prometheus operator present in cluster".into(), |             details: "prometheus operator present in cluster".into(), | ||||||
|         }) |         }) | ||||||
|     } |     } | ||||||
| 
 |  | ||||||
|     async fn install_grafana_operator( |  | ||||||
|         &self, |  | ||||||
|         inventory: &Inventory, |  | ||||||
|         ns: Option<&str>, |  | ||||||
|     ) -> Result<PreparationOutcome, PreparationError> { |  | ||||||
|         let namespace = ns.unwrap_or("grafana"); |  | ||||||
|         info!("installing grafana operator in ns {namespace}"); |  | ||||||
|         let tenant = self.get_k8s_tenant_manager()?.get_tenant_config().await; |  | ||||||
|         let mut namespace_scope = false; |  | ||||||
|         if tenant.is_some() { |  | ||||||
|             namespace_scope = true; |  | ||||||
|         } |  | ||||||
|         let _grafana_operator_score = grafana_helm_chart_score(namespace, namespace_scope) |  | ||||||
|             .interpret(inventory, self) |  | ||||||
|             .await; |  | ||||||
|         Ok(PreparationOutcome::Success { |  | ||||||
|             details: format!( |  | ||||||
|                 "Successfully installed grafana operator in ns {}", |  | ||||||
|                 ns.unwrap() |  | ||||||
|             ), |  | ||||||
|         }) |  | ||||||
|     } |  | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| #[derive(Clone, Debug)] | #[derive(Clone, Debug)] | ||||||
|  | |||||||
| @ -30,7 +30,6 @@ impl<S: AlertSender + Installable<T>, T: Topology> Interpret<T> for AlertingInte | |||||||
|         inventory: &Inventory, |         inventory: &Inventory, | ||||||
|         topology: &T, |         topology: &T, | ||||||
|     ) -> Result<Outcome, InterpretError> { |     ) -> Result<Outcome, InterpretError> { | ||||||
|         debug!("hit sender configure for AlertingInterpret"); |  | ||||||
|         self.sender.configure(inventory, topology).await?; |         self.sender.configure(inventory, topology).await?; | ||||||
|         for receiver in self.receivers.iter() { |         for receiver in self.receivers.iter() { | ||||||
|             receiver.install(&self.sender).await?; |             receiver.install(&self.sender).await?; | ||||||
| @ -39,7 +38,6 @@ impl<S: AlertSender + Installable<T>, T: Topology> Interpret<T> for AlertingInte | |||||||
|             debug!("installing rule: {:#?}", rule); |             debug!("installing rule: {:#?}", rule); | ||||||
|             rule.install(&self.sender).await?; |             rule.install(&self.sender).await?; | ||||||
|         } |         } | ||||||
|         debug!("hit sender ensure installed for AlertingInterpret"); |  | ||||||
|         self.sender.ensure_installed(inventory, topology).await?; |         self.sender.ensure_installed(inventory, topology).await?; | ||||||
|         Ok(Outcome::success(format!( |         Ok(Outcome::success(format!( | ||||||
|             "successfully installed alert sender {}", |             "successfully installed alert sender {}", | ||||||
|  | |||||||
| @ -2,11 +2,7 @@ use crate::modules::application::{ | |||||||
|     Application, ApplicationFeature, InstallationError, InstallationOutcome, |     Application, ApplicationFeature, InstallationError, InstallationOutcome, | ||||||
| }; | }; | ||||||
| use crate::modules::monitoring::application_monitoring::application_monitoring_score::ApplicationMonitoringScore; | use crate::modules::monitoring::application_monitoring::application_monitoring_score::ApplicationMonitoringScore; | ||||||
| use crate::modules::monitoring::grafana::grafana::Grafana; |  | ||||||
| use crate::modules::monitoring::kube_prometheus::crd::crd_alertmanager_config::CRDPrometheus; | use crate::modules::monitoring::kube_prometheus::crd::crd_alertmanager_config::CRDPrometheus; | ||||||
| use crate::modules::monitoring::kube_prometheus::crd::service_monitor::{ |  | ||||||
|     ServiceMonitor, ServiceMonitorSpec, |  | ||||||
| }; |  | ||||||
| use crate::topology::MultiTargetTopology; | use crate::topology::MultiTargetTopology; | ||||||
| use crate::topology::ingress::Ingress; | use crate::topology::ingress::Ingress; | ||||||
| use crate::{ | use crate::{ | ||||||
| @ -18,7 +14,7 @@ use crate::{ | |||||||
|     topology::{HelmCommand, K8sclient, Topology, tenant::TenantManager}, |     topology::{HelmCommand, K8sclient, Topology, tenant::TenantManager}, | ||||||
| }; | }; | ||||||
| use crate::{ | use crate::{ | ||||||
|     modules::prometheus::prometheus::PrometheusMonitoring, |     modules::prometheus::prometheus::PrometheusApplicationMonitoring, | ||||||
|     topology::oberservability::monitoring::AlertReceiver, |     topology::oberservability::monitoring::AlertReceiver, | ||||||
| }; | }; | ||||||
| use async_trait::async_trait; | use async_trait::async_trait; | ||||||
| @ -26,7 +22,6 @@ use base64::{Engine as _, engine::general_purpose}; | |||||||
| use harmony_secret::SecretManager; | use harmony_secret::SecretManager; | ||||||
| use harmony_secret_derive::Secret; | use harmony_secret_derive::Secret; | ||||||
| use harmony_types::net::Url; | use harmony_types::net::Url; | ||||||
| use kube::api::ObjectMeta; |  | ||||||
| use log::{debug, info}; | use log::{debug, info}; | ||||||
| use serde::{Deserialize, Serialize}; | use serde::{Deserialize, Serialize}; | ||||||
| use std::sync::Arc; | use std::sync::Arc; | ||||||
| @ -45,8 +40,7 @@ impl< | |||||||
|         + TenantManager |         + TenantManager | ||||||
|         + K8sclient |         + K8sclient | ||||||
|         + MultiTargetTopology |         + MultiTargetTopology | ||||||
|         + PrometheusMonitoring<CRDPrometheus> |         + PrometheusApplicationMonitoring<CRDPrometheus> | ||||||
|         + Grafana |  | ||||||
|         + Ingress |         + Ingress | ||||||
|         + std::fmt::Debug, |         + std::fmt::Debug, | ||||||
| > ApplicationFeature<T> for Monitoring | > ApplicationFeature<T> for Monitoring | ||||||
| @ -63,20 +57,10 @@ impl< | |||||||
|             .unwrap_or_else(|| self.application.name()); |             .unwrap_or_else(|| self.application.name()); | ||||||
|         let domain = topology.get_domain("ntfy").await.unwrap(); |         let domain = topology.get_domain("ntfy").await.unwrap(); | ||||||
| 
 | 
 | ||||||
|         let app_service_monitor = ServiceMonitor { |  | ||||||
|             metadata: ObjectMeta { |  | ||||||
|                 name: Some(self.application.name()), |  | ||||||
|                 namespace: Some(namespace.clone()), |  | ||||||
|                 ..Default::default() |  | ||||||
|             }, |  | ||||||
|             spec: ServiceMonitorSpec::default(), |  | ||||||
|         }; |  | ||||||
| 
 |  | ||||||
|         let mut alerting_score = ApplicationMonitoringScore { |         let mut alerting_score = ApplicationMonitoringScore { | ||||||
|             sender: CRDPrometheus { |             sender: CRDPrometheus { | ||||||
|                 namespace: namespace.clone(), |                 namespace: namespace.clone(), | ||||||
|                 client: topology.k8s_client().await.unwrap(), |                 client: topology.k8s_client().await.unwrap(), | ||||||
|                 service_monitor: vec![app_service_monitor], |  | ||||||
|             }, |             }, | ||||||
|             application: self.application.clone(), |             application: self.application.clone(), | ||||||
|             receivers: self.alert_receiver.clone(), |             receivers: self.alert_receiver.clone(), | ||||||
|  | |||||||
| @ -18,7 +18,7 @@ use crate::{ | |||||||
|     topology::{HelmCommand, K8sclient, Topology, tenant::TenantManager}, |     topology::{HelmCommand, K8sclient, Topology, tenant::TenantManager}, | ||||||
| }; | }; | ||||||
| use crate::{ | use crate::{ | ||||||
|     modules::prometheus::prometheus::PrometheusMonitoring, |     modules::prometheus::prometheus::PrometheusApplicationMonitoring, | ||||||
|     topology::oberservability::monitoring::AlertReceiver, |     topology::oberservability::monitoring::AlertReceiver, | ||||||
| }; | }; | ||||||
| use async_trait::async_trait; | use async_trait::async_trait; | ||||||
| @ -42,7 +42,7 @@ impl< | |||||||
|         + MultiTargetTopology |         + MultiTargetTopology | ||||||
|         + Ingress |         + Ingress | ||||||
|         + std::fmt::Debug |         + std::fmt::Debug | ||||||
|         + PrometheusMonitoring<RHOBObservability>, |         + PrometheusApplicationMonitoring<RHOBObservability>, | ||||||
| > ApplicationFeature<T> for Monitoring | > ApplicationFeature<T> for Monitoring | ||||||
| { | { | ||||||
|     async fn ensure_installed( |     async fn ensure_installed( | ||||||
|  | |||||||
| @ -1,23 +1,21 @@ | |||||||
| use std::sync::Arc; | use std::sync::Arc; | ||||||
| 
 | 
 | ||||||
| use log::debug; | use async_trait::async_trait; | ||||||
| use serde::Serialize; | use serde::Serialize; | ||||||
| 
 | 
 | ||||||
| use crate::{ | use crate::{ | ||||||
|     interpret::Interpret, |     data::Version, | ||||||
|  |     interpret::{Interpret, InterpretError, InterpretName, InterpretStatus, Outcome}, | ||||||
|  |     inventory::Inventory, | ||||||
|     modules::{ |     modules::{ | ||||||
|         application::Application, |         application::Application, | ||||||
|         monitoring::{ |         monitoring::kube_prometheus::crd::crd_alertmanager_config::CRDPrometheus, | ||||||
|             grafana::grafana::Grafana, kube_prometheus::crd::crd_alertmanager_config::CRDPrometheus, |         prometheus::prometheus::PrometheusApplicationMonitoring, | ||||||
|         }, |  | ||||||
|         prometheus::prometheus::PrometheusMonitoring, |  | ||||||
|     }, |     }, | ||||||
|     score::Score, |     score::Score, | ||||||
|     topology::{ |     topology::{PreparationOutcome, Topology, oberservability::monitoring::AlertReceiver}, | ||||||
|         K8sclient, Topology, |  | ||||||
|         oberservability::monitoring::{AlertReceiver, AlertingInterpret}, |  | ||||||
|     }, |  | ||||||
| }; | }; | ||||||
|  | use harmony_types::id::Id; | ||||||
| 
 | 
 | ||||||
| #[derive(Debug, Clone, Serialize)] | #[derive(Debug, Clone, Serialize)] | ||||||
| pub struct ApplicationMonitoringScore { | pub struct ApplicationMonitoringScore { | ||||||
| @ -26,15 +24,12 @@ pub struct ApplicationMonitoringScore { | |||||||
|     pub receivers: Vec<Box<dyn AlertReceiver<CRDPrometheus>>>, |     pub receivers: Vec<Box<dyn AlertReceiver<CRDPrometheus>>>, | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| impl<T: Topology + PrometheusMonitoring<CRDPrometheus> + K8sclient + Grafana> Score<T> | impl<T: Topology + PrometheusApplicationMonitoring<CRDPrometheus>> Score<T> | ||||||
|     for ApplicationMonitoringScore |     for ApplicationMonitoringScore | ||||||
| { | { | ||||||
|     fn create_interpret(&self) -> Box<dyn Interpret<T>> { |     fn create_interpret(&self) -> Box<dyn Interpret<T>> { | ||||||
|         debug!("creating alerting interpret"); |         Box::new(ApplicationMonitoringInterpret { | ||||||
|         Box::new(AlertingInterpret { |             score: self.clone(), | ||||||
|             sender: self.sender.clone(), |  | ||||||
|             receivers: self.receivers.clone(), |  | ||||||
|             rules: vec![], |  | ||||||
|         }) |         }) | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
| @ -45,3 +40,55 @@ impl<T: Topology + PrometheusMonitoring<CRDPrometheus> + K8sclient + Grafana> Sc | |||||||
|         ) |         ) | ||||||
|     } |     } | ||||||
| } | } | ||||||
|  | 
 | ||||||
|  | #[derive(Debug)] | ||||||
|  | pub struct ApplicationMonitoringInterpret { | ||||||
|  |     score: ApplicationMonitoringScore, | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | #[async_trait] | ||||||
|  | impl<T: Topology + PrometheusApplicationMonitoring<CRDPrometheus>> Interpret<T> | ||||||
|  |     for ApplicationMonitoringInterpret | ||||||
|  | { | ||||||
|  |     async fn execute( | ||||||
|  |         &self, | ||||||
|  |         inventory: &Inventory, | ||||||
|  |         topology: &T, | ||||||
|  |     ) -> Result<Outcome, InterpretError> { | ||||||
|  |         let result = topology | ||||||
|  |             .install_prometheus( | ||||||
|  |                 &self.score.sender, | ||||||
|  |                 inventory, | ||||||
|  |                 Some(self.score.receivers.clone()), | ||||||
|  |             ) | ||||||
|  |             .await; | ||||||
|  | 
 | ||||||
|  |         match result { | ||||||
|  |             Ok(outcome) => match outcome { | ||||||
|  |                 PreparationOutcome::Success { details: _ } => { | ||||||
|  |                     Ok(Outcome::success("Prometheus installed".into())) | ||||||
|  |                 } | ||||||
|  |                 PreparationOutcome::Noop => { | ||||||
|  |                     Ok(Outcome::noop("Prometheus installation skipped".into())) | ||||||
|  |                 } | ||||||
|  |             }, | ||||||
|  |             Err(err) => Err(InterpretError::from(err)), | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     fn get_name(&self) -> InterpretName { | ||||||
|  |         InterpretName::ApplicationMonitoring | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     fn get_version(&self) -> Version { | ||||||
|  |         todo!() | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     fn get_status(&self) -> InterpretStatus { | ||||||
|  |         todo!() | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     fn get_children(&self) -> Vec<Id> { | ||||||
|  |         todo!() | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | |||||||
| @ -12,7 +12,7 @@ use crate::{ | |||||||
|         monitoring::kube_prometheus::crd::{ |         monitoring::kube_prometheus::crd::{ | ||||||
|             crd_alertmanager_config::CRDPrometheus, rhob_alertmanager_config::RHOBObservability, |             crd_alertmanager_config::CRDPrometheus, rhob_alertmanager_config::RHOBObservability, | ||||||
|         }, |         }, | ||||||
|         prometheus::prometheus::PrometheusMonitoring, |         prometheus::prometheus::PrometheusApplicationMonitoring, | ||||||
|     }, |     }, | ||||||
|     score::Score, |     score::Score, | ||||||
|     topology::{PreparationOutcome, Topology, oberservability::monitoring::AlertReceiver}, |     topology::{PreparationOutcome, Topology, oberservability::monitoring::AlertReceiver}, | ||||||
| @ -26,7 +26,7 @@ pub struct ApplicationRHOBMonitoringScore { | |||||||
|     pub receivers: Vec<Box<dyn AlertReceiver<RHOBObservability>>>, |     pub receivers: Vec<Box<dyn AlertReceiver<RHOBObservability>>>, | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| impl<T: Topology + PrometheusMonitoring<RHOBObservability>> Score<T> | impl<T: Topology + PrometheusApplicationMonitoring<RHOBObservability>> Score<T> | ||||||
|     for ApplicationRHOBMonitoringScore |     for ApplicationRHOBMonitoringScore | ||||||
| { | { | ||||||
|     fn create_interpret(&self) -> Box<dyn Interpret<T>> { |     fn create_interpret(&self) -> Box<dyn Interpret<T>> { | ||||||
| @ -49,7 +49,7 @@ pub struct ApplicationRHOBMonitoringInterpret { | |||||||
| } | } | ||||||
| 
 | 
 | ||||||
| #[async_trait] | #[async_trait] | ||||||
| impl<T: Topology + PrometheusMonitoring<RHOBObservability>> Interpret<T> | impl<T: Topology + PrometheusApplicationMonitoring<RHOBObservability>> Interpret<T> | ||||||
|     for ApplicationRHOBMonitoringInterpret |     for ApplicationRHOBMonitoringInterpret | ||||||
| { | { | ||||||
|     async fn execute( |     async fn execute( | ||||||
|  | |||||||
| @ -1,17 +0,0 @@ | |||||||
| use async_trait::async_trait; |  | ||||||
| use k8s_openapi::Resource; |  | ||||||
| 
 |  | ||||||
| use crate::{ |  | ||||||
|     inventory::Inventory, |  | ||||||
|     topology::{PreparationError, PreparationOutcome}, |  | ||||||
| }; |  | ||||||
| 
 |  | ||||||
| #[async_trait] |  | ||||||
| pub trait Grafana { |  | ||||||
|     async fn ensure_grafana_operator( |  | ||||||
|         &self, |  | ||||||
|         inventory: &Inventory, |  | ||||||
|     ) -> Result<PreparationOutcome, PreparationError>; |  | ||||||
| 
 |  | ||||||
|     async fn install_grafana(&self) -> Result<PreparationOutcome, PreparationError>; |  | ||||||
| } |  | ||||||
| @ -1,27 +1,27 @@ | |||||||
| use non_blank_string_rs::NonBlankString; | use non_blank_string_rs::NonBlankString; | ||||||
| use std::{collections::HashMap, str::FromStr}; | use std::str::FromStr; | ||||||
| 
 | 
 | ||||||
| use crate::modules::helm::chart::{HelmChartScore, HelmRepository}; | use crate::modules::helm::chart::HelmChartScore; | ||||||
|  | 
 | ||||||
|  | pub fn grafana_helm_chart_score(ns: &str) -> HelmChartScore { | ||||||
|  |     let values = r#" | ||||||
|  | rbac: | ||||||
|  |   namespaced: true | ||||||
|  | sidecar: | ||||||
|  |   dashboards: | ||||||
|  |     enabled: true | ||||||
|  |         "#
 | ||||||
|  |     .to_string(); | ||||||
| 
 | 
 | ||||||
| pub fn grafana_helm_chart_score(ns: &str, namespace_scope: bool) -> HelmChartScore { |  | ||||||
|     let mut values_overrides = HashMap::new(); |  | ||||||
|     values_overrides.insert( |  | ||||||
|         NonBlankString::from_str("namespaceScope").unwrap(), |  | ||||||
|         namespace_scope.to_string(), |  | ||||||
|     ); |  | ||||||
|     HelmChartScore { |     HelmChartScore { | ||||||
|         namespace: Some(NonBlankString::from_str(ns).unwrap()), |         namespace: Some(NonBlankString::from_str(ns).unwrap()), | ||||||
|         release_name: NonBlankString::from_str("grafana-operator").unwrap(), |         release_name: NonBlankString::from_str("grafana").unwrap(), | ||||||
|         chart_name: NonBlankString::from_str("grafana/grafana-operator").unwrap(), |         chart_name: NonBlankString::from_str("oci://ghcr.io/grafana/helm-charts/grafana").unwrap(), | ||||||
|         chart_version: None, |         chart_version: None, | ||||||
|         values_overrides: Some(values_overrides), |         values_overrides: None, | ||||||
|         values_yaml: None, |         values_yaml: Some(values.to_string()), | ||||||
|         create_namespace: true, |         create_namespace: true, | ||||||
|         install_only: true, |         install_only: true, | ||||||
|         repository: Some(HelmRepository::new( |         repository: None, | ||||||
|             "grafana".to_string(), |  | ||||||
|             url::Url::parse("https://grafana.github.io/helm-charts").unwrap(), |  | ||||||
|             true, |  | ||||||
|         )), |  | ||||||
|     } |     } | ||||||
| } | } | ||||||
|  | |||||||
| @ -1,2 +1 @@ | |||||||
| pub mod grafana; |  | ||||||
| pub mod helm; | pub mod helm; | ||||||
|  | |||||||
| @ -1,25 +1,12 @@ | |||||||
| 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::{ | use crate::topology::{ | ||||||
|     interpret::{InterpretError, Outcome}, |     k8s::K8sClient, | ||||||
|     inventory::Inventory, |     oberservability::monitoring::{AlertReceiver, AlertSender}, | ||||||
|     modules::{ |  | ||||||
|         monitoring::{ |  | ||||||
|             grafana::grafana::Grafana, kube_prometheus::crd::service_monitor::ServiceMonitor, |  | ||||||
|         }, |  | ||||||
|         prometheus::prometheus::PrometheusMonitoring, |  | ||||||
|     }, |  | ||||||
|     topology::{ |  | ||||||
|         K8sclient, Topology, |  | ||||||
|         installable::Installable, |  | ||||||
|         k8s::K8sClient, |  | ||||||
|         oberservability::monitoring::{AlertReceiver, AlertSender}, |  | ||||||
|     }, |  | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| #[derive(CustomResource, Serialize, Deserialize, Debug, Clone, JsonSchema)] | #[derive(CustomResource, Serialize, Deserialize, Debug, Clone, JsonSchema)] | ||||||
| @ -39,7 +26,6 @@ pub struct AlertmanagerConfigSpec { | |||||||
| pub struct CRDPrometheus { | pub struct CRDPrometheus { | ||||||
|     pub namespace: String, |     pub namespace: String, | ||||||
|     pub client: Arc<K8sClient>, |     pub client: Arc<K8sClient>, | ||||||
|     pub service_monitor: Vec<ServiceMonitor>, |  | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| impl AlertSender for CRDPrometheus { | impl AlertSender for CRDPrometheus { | ||||||
| @ -62,24 +48,3 @@ impl Serialize for Box<dyn AlertReceiver<CRDPrometheus>> { | |||||||
|         todo!() |         todo!() | ||||||
|     } |     } | ||||||
| } | } | ||||||
| 
 |  | ||||||
| #[async_trait] |  | ||||||
| impl<T: Topology + K8sclient + PrometheusMonitoring<CRDPrometheus> + Grafana> Installable<T> |  | ||||||
|     for CRDPrometheus |  | ||||||
| { |  | ||||||
|     async fn configure(&self, inventory: &Inventory, topology: &T) -> Result<(), InterpretError> { |  | ||||||
|         topology.ensure_grafana_operator(inventory).await?; |  | ||||||
|         topology.ensure_prometheus_operator(self, inventory).await?; |  | ||||||
|         Ok(()) |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     async fn ensure_installed( |  | ||||||
|         &self, |  | ||||||
|         inventory: &Inventory, |  | ||||||
|         topology: &T, |  | ||||||
|     ) -> Result<(), InterpretError> { |  | ||||||
|         topology.install_grafana().await?; |  | ||||||
|         topology.install_prometheus(&self, inventory, None).await?; |  | ||||||
|         Ok(()) |  | ||||||
|     } |  | ||||||
| } |  | ||||||
|  | |||||||
| @ -114,7 +114,7 @@ impl Prometheus { | |||||||
|         }; |         }; | ||||||
| 
 | 
 | ||||||
|         if let Some(ns) = namespace.as_deref() { |         if let Some(ns) = namespace.as_deref() { | ||||||
|             grafana_helm_chart_score(ns, false) |             grafana_helm_chart_score(ns) | ||||||
|                 .interpret(inventory, topology) |                 .interpret(inventory, topology) | ||||||
|                 .await |                 .await | ||||||
|         } else { |         } else { | ||||||
|  | |||||||
| @ -39,7 +39,7 @@ use crate::{ | |||||||
| }; | }; | ||||||
| use harmony_types::id::Id; | use harmony_types::id::Id; | ||||||
| 
 | 
 | ||||||
| use super::prometheus::PrometheusMonitoring; | use super::prometheus::PrometheusApplicationMonitoring; | ||||||
| 
 | 
 | ||||||
| #[derive(Clone, Debug, Serialize)] | #[derive(Clone, Debug, Serialize)] | ||||||
| pub struct K8sPrometheusCRDAlertingScore { | pub struct K8sPrometheusCRDAlertingScore { | ||||||
| @ -49,7 +49,7 @@ pub struct K8sPrometheusCRDAlertingScore { | |||||||
|     pub prometheus_rules: Vec<RuleGroup>, |     pub prometheus_rules: Vec<RuleGroup>, | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| impl<T: Topology + K8sclient + PrometheusMonitoring<CRDPrometheus>> Score<T> | impl<T: Topology + K8sclient + PrometheusApplicationMonitoring<CRDPrometheus>> Score<T> | ||||||
|     for K8sPrometheusCRDAlertingScore |     for K8sPrometheusCRDAlertingScore | ||||||
| { | { | ||||||
|     fn create_interpret(&self) -> Box<dyn crate::interpret::Interpret<T>> { |     fn create_interpret(&self) -> Box<dyn crate::interpret::Interpret<T>> { | ||||||
| @ -75,7 +75,7 @@ pub struct K8sPrometheusCRDAlertingInterpret { | |||||||
| } | } | ||||||
| 
 | 
 | ||||||
| #[async_trait] | #[async_trait] | ||||||
| impl<T: Topology + K8sclient + PrometheusMonitoring<CRDPrometheus>> Interpret<T> | impl<T: Topology + K8sclient + PrometheusApplicationMonitoring<CRDPrometheus>> Interpret<T> | ||||||
|     for K8sPrometheusCRDAlertingInterpret |     for K8sPrometheusCRDAlertingInterpret | ||||||
| { | { | ||||||
|     async fn execute( |     async fn execute( | ||||||
|  | |||||||
| @ -9,17 +9,11 @@ use crate::{ | |||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| #[async_trait] | #[async_trait] | ||||||
| pub trait PrometheusMonitoring<S: AlertSender> { | pub trait PrometheusApplicationMonitoring<S: AlertSender> { | ||||||
|     async fn install_prometheus( |     async fn install_prometheus( | ||||||
|         &self, |         &self, | ||||||
|         sender: &S, |         sender: &S, | ||||||
|         inventory: &Inventory, |         inventory: &Inventory, | ||||||
|         receivers: Option<Vec<Box<dyn AlertReceiver<S>>>>, |         receivers: Option<Vec<Box<dyn AlertReceiver<S>>>>, | ||||||
|     ) -> Result<PreparationOutcome, PreparationError>; |     ) -> Result<PreparationOutcome, PreparationError>; | ||||||
| 
 |  | ||||||
|     async fn ensure_prometheus_operator( |  | ||||||
|         &self, |  | ||||||
|         sender: &S, |  | ||||||
|         inventory: &Inventory, |  | ||||||
|     ) -> Result<PreparationOutcome, PreparationError>; |  | ||||||
| } | } | ||||||
|  | |||||||
| @ -38,7 +38,7 @@ use crate::{ | |||||||
| }; | }; | ||||||
| use harmony_types::id::Id; | use harmony_types::id::Id; | ||||||
| 
 | 
 | ||||||
| use super::prometheus::PrometheusMonitoring; | use super::prometheus::PrometheusApplicationMonitoring; | ||||||
| 
 | 
 | ||||||
| #[derive(Clone, Debug, Serialize)] | #[derive(Clone, Debug, Serialize)] | ||||||
| pub struct RHOBAlertingScore { | pub struct RHOBAlertingScore { | ||||||
| @ -48,8 +48,8 @@ pub struct RHOBAlertingScore { | |||||||
|     pub prometheus_rules: Vec<RuleGroup>, |     pub prometheus_rules: Vec<RuleGroup>, | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| impl<T: Topology + K8sclient + Ingress + PrometheusMonitoring<RHOBObservability>> Score<T> | impl<T: Topology + K8sclient + Ingress + PrometheusApplicationMonitoring<RHOBObservability>> | ||||||
|     for RHOBAlertingScore |     Score<T> for RHOBAlertingScore | ||||||
| { | { | ||||||
|     fn create_interpret(&self) -> Box<dyn crate::interpret::Interpret<T>> { |     fn create_interpret(&self) -> Box<dyn crate::interpret::Interpret<T>> { | ||||||
|         Box::new(RHOBAlertingInterpret { |         Box::new(RHOBAlertingInterpret { | ||||||
| @ -74,8 +74,8 @@ pub struct RHOBAlertingInterpret { | |||||||
| } | } | ||||||
| 
 | 
 | ||||||
| #[async_trait] | #[async_trait] | ||||||
| impl<T: Topology + K8sclient + Ingress + PrometheusMonitoring<RHOBObservability>> Interpret<T> | impl<T: Topology + K8sclient + Ingress + PrometheusApplicationMonitoring<RHOBObservability>> | ||||||
|     for RHOBAlertingInterpret |     Interpret<T> for RHOBAlertingInterpret | ||||||
| { | { | ||||||
|     async fn execute( |     async fn execute( | ||||||
|         &self, |         &self, | ||||||
|  | |||||||
		Loading…
	
		Reference in New Issue
	
	Block a user