feat: added a default config structure for kubeprometheus chart #33
| @ -1,4 +1,3 @@ | |||||||
| pub mod monitoring_alerting; |  | ||||||
| mod ha_cluster; | mod ha_cluster; | ||||||
| mod host_binding; | mod host_binding; | ||||||
| mod http; | mod http; | ||||||
|  | |||||||
| @ -1,108 +0,0 @@ | |||||||
| use std::sync::Arc; |  | ||||||
| 
 |  | ||||||
| use log::warn; |  | ||||||
| use tokio::sync::OnceCell; |  | ||||||
| 
 |  | ||||||
| use k8s_openapi::api::core::v1::Pod; |  | ||||||
| use kube::{ |  | ||||||
|     Client, |  | ||||||
|     api::{Api, ListParams}, |  | ||||||
| }; |  | ||||||
| 
 |  | ||||||
| use async_trait::async_trait; |  | ||||||
| 
 |  | ||||||
| use crate::{ |  | ||||||
|     interpret::{InterpretError, Outcome}, |  | ||||||
|     inventory::Inventory, |  | ||||||
|     maestro::Maestro, |  | ||||||
|     modules::monitoring::monitoring_alerting::MonitoringAlertingStackScore, |  | ||||||
|     score::Score, |  | ||||||
| }; |  | ||||||
| 
 |  | ||||||
| use super::{HelmCommand, K8sAnywhereTopology, Topology, k8s::K8sClient}; |  | ||||||
| 
 |  | ||||||
| #[derive(Clone, Debug)] |  | ||||||
| struct MonitoringState { |  | ||||||
|     message: String, |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| #[derive(Debug)] |  | ||||||
| pub struct MonitoringAlertingTopology { |  | ||||||
|     monitoring_state: OnceCell<Option<MonitoringState>>, |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| impl MonitoringAlertingTopology { |  | ||||||
|     pub fn new() -> Self { |  | ||||||
|         Self { |  | ||||||
|             monitoring_state: OnceCell::new(), |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     async fn get_monitoring_state(&self) -> Result<Option<MonitoringState>, InterpretError> { |  | ||||||
|         let client = Client::try_default() |  | ||||||
|             .await |  | ||||||
|             .map_err(|e| InterpretError::new(format!("Kubernetes client error: {}", e)))?; |  | ||||||
| 
 |  | ||||||
|         for ns in &["monitoring", "openshift-monitoring"] { |  | ||||||
|             let pods: Api<Pod> = Api::namespaced(client.clone(), ns); |  | ||||||
|             //TODO hardcoding the label is a problem
 |  | ||||||
|             //check all pods are ready
 |  | ||||||
|             let lp = ListParams::default().labels("app.kubernetes.io/name=prometheus"); |  | ||||||
| 
 |  | ||||||
|             match pods.list(&lp).await { |  | ||||||
|                 Ok(pod_list) => { |  | ||||||
|                     for p in pod_list.items { |  | ||||||
|                         if let Some(status) = p.status { |  | ||||||
|                             if let Some(conditions) = status.conditions { |  | ||||||
|                                 if conditions |  | ||||||
|                                     .iter() |  | ||||||
|                                     .any(|c| c.type_ == "Ready" && c.status == "True") |  | ||||||
|                                 { |  | ||||||
|                                     return Ok(Some(MonitoringState { |  | ||||||
|                                         message: format!( |  | ||||||
|                                             "Prometheus is ready in namespace: {}", |  | ||||||
|                                             ns |  | ||||||
|                                         ), |  | ||||||
|                                     })); |  | ||||||
|                                 } |  | ||||||
|                             } |  | ||||||
|                         } |  | ||||||
|                     } |  | ||||||
|                 } |  | ||||||
|                 Err(e) => { |  | ||||||
|                     warn!("Failed to query pods in ns {}: {}", ns, e); |  | ||||||
|                 } |  | ||||||
|             } |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         Ok(None) |  | ||||||
|     } |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| impl<T: Topology> Clone for Box<dyn Score<T>> { |  | ||||||
|     fn clone(&self) -> Box<dyn Score<T>> { |  | ||||||
|         self.clone_box() |  | ||||||
|     } |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| #[async_trait] |  | ||||||
| impl Topology for MonitoringAlertingTopology { |  | ||||||
|     fn name(&self) -> &str { |  | ||||||
|         "MonitoringAlertingTopology" |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     async fn ensure_ready(&self) -> Result<Outcome, InterpretError> { |  | ||||||
|         if let Some(state) = self.get_monitoring_state().await? { |  | ||||||
|             // Monitoring stack is already ready — stop app.
 |  | ||||||
|             println!("{}", state.message); |  | ||||||
|             std::process::exit(0); |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         // Monitoring not found — proceed with installation.
 |  | ||||||
|         Ok(Outcome::success( |  | ||||||
|             "Monitoring stack installation started.".to_string(), |  | ||||||
|         )) |  | ||||||
|     } |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| impl HelmCommand for MonitoringAlertingTopology {} |  | ||||||
| @ -159,7 +159,8 @@ impl<T: Topology + HelmCommand> Interpret<T> for HelmChartInterpret { | |||||||
| 
 | 
 | ||||||
|         self.add_repo()?; |         self.add_repo()?; | ||||||
| 
 | 
 | ||||||
|         let helm_executor = DefaultHelmExecutor::new(); |         let helm_path = NonBlankString::from_str("helm").unwrap(); | ||||||
|  |         let helm_executor = DefaultHelmExecutor::new_with_opts(&helm_path, None, 9000, false, false); | ||||||
| 
 | 
 | ||||||
|         let mut helm_options = Vec::new(); |         let mut helm_options = Vec::new(); | ||||||
|         if self.score.create_namespace { |         if self.score.create_namespace { | ||||||
|  | |||||||
							
								
								
									
										37
									
								
								harmony/src/modules/monitoring/config.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										37
									
								
								harmony/src/modules/monitoring/config.rs
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,37 @@ | |||||||
|  | #[derive(Debug, Clone)] | ||||||
|  | pub struct KubePrometheusConfig { | ||||||
|  |     pub namespace: String, | ||||||
|  |     pub node_exporter: bool, | ||||||
|  |     pub alert_manager: bool, | ||||||
|  |     pub prometheus: bool, | ||||||
|  |     pub grafana: bool, | ||||||
|  |     pub windows_monitoring: bool, | ||||||
|  |     pub kubernetes_service_monitors: bool, | ||||||
|  |     pub kubelet: bool, | ||||||
|  |     pub kube_controller_manager: bool, | ||||||
|  |     pub kube_etcd: bool, | ||||||
|  |     pub kube_proxy: bool, | ||||||
|  |     pub kube_state_metrics: bool, | ||||||
|  |     pub prometheus_operator: bool, | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | impl Default for KubePrometheusConfig { | ||||||
|  |     fn default() -> Self { | ||||||
|  |         Self { | ||||||
|  |             namespace: "monitoring".into(), | ||||||
|  |             node_exporter: false, | ||||||
|  |             alert_manager: false, | ||||||
|  |             prometheus: true, | ||||||
|  |             grafana: true, | ||||||
|  |             windows_monitoring: false, | ||||||
|  |             kubernetes_service_monitors: true, | ||||||
|  |             kubelet: true, | ||||||
|  |             kube_controller_manager: true, | ||||||
|  |             kube_etcd: true, | ||||||
|  |             kube_proxy: true, | ||||||
|  |             kube_state_metrics: true, | ||||||
|  |             prometheus_operator: true, | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
| @ -1,10 +1,12 @@ | |||||||
| use std::str::FromStr; | use std::{collections::HashMap, str::FromStr}; | ||||||
| 
 | 
 | ||||||
| use non_blank_string_rs::NonBlankString; | use non_blank_string_rs::NonBlankString; | ||||||
| 
 | 
 | ||||||
| use crate::modules::helm::chart::HelmChartScore; | use crate::modules::helm::chart::HelmChartScore; | ||||||
| 
 | 
 | ||||||
| pub fn kube_prometheus_score(ns: &str) -> HelmChartScore { | use super::config::KubePrometheusConfig; | ||||||
|  | 
 | ||||||
|  | pub fn kube_prometheus_score(config: KubePrometheusConfig) -> HelmChartScore { | ||||||
|     //TODO this should be make into a rule with default formatting that can be easily passed as a vec
 |     //TODO this should be make into a rule with default formatting that can be easily passed as a vec
 | ||||||
|     //to the overrides or something leaving the user to deal with formatting here seems bad
 |     //to the overrides or something leaving the user to deal with formatting here seems bad
 | ||||||
|     let values = r#" |     let values = r#" | ||||||
| @ -32,8 +34,31 @@ additionalPrometheusRulesMap: | |||||||
|               description: The PVC {{ $labels.persistentvolumeclaim }} in namespace {{ $labels.namespace }} is predicted to fill over 95% in less than 2 days. |               description: The PVC {{ $labels.persistentvolumeclaim }} in namespace {{ $labels.namespace }} is predicted to fill over 95% in less than 2 days. | ||||||
|               title: PVC {{ $labels.persistentvolumeclaim }} in namespace {{ $labels.namespace }} will fill over 95% in less than 2 days |               title: PVC {{ $labels.persistentvolumeclaim }} in namespace {{ $labels.namespace }} will fill over 95% in less than 2 days | ||||||
| "#;
 | "#;
 | ||||||
|  |     let mut values_overrides: HashMap<NonBlankString, String> = HashMap::new(); | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  |     macro_rules! insert_flag { | ||||||
|  |         ($key:expr, $val:expr) => { | ||||||
|  |             values_overrides.insert(NonBlankString::from_str($key).unwrap(), $val.to_string()); | ||||||
|  |         }; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     insert_flag!("nodeExporter.enabled", config.node_exporter); | ||||||
|  |     insert_flag!("windowsMonitoring.enabled", config.windows_monitoring); | ||||||
|  |     insert_flag!("grafana.enabled", config.grafana); | ||||||
|  |     insert_flag!("alertmanager.enabled", config.alert_manager); | ||||||
|  |     insert_flag!("prometheus.enabled", config.prometheus); | ||||||
|  |     insert_flag!("kubernetes_service_monitors.enabled", config.kubernetes_service_monitors); | ||||||
|  |     insert_flag!("kubelet.enabled", config.kubelet); | ||||||
|  |     insert_flag!("kubeControllerManager.enabled", config.kube_controller_manager); | ||||||
|  |     insert_flag!("kubeProxy.enabled", config.kube_proxy); | ||||||
|  |     insert_flag!("kubeEtcd.enabled", config.kube_etcd); | ||||||
|  |     insert_flag!("kubeStateMetrics.enabled", config.kube_state_metrics); | ||||||
|  |     insert_flag!("prometheusOperator.enabled", config.prometheus_operator); | ||||||
|  | 
 | ||||||
|  |     
 | ||||||
|     HelmChartScore { |     HelmChartScore { | ||||||
|         namespace: Some(NonBlankString::from_str(ns).unwrap()), |         namespace: Some(NonBlankString::from_str(&config.namespace).unwrap()), | ||||||
|         release_name: NonBlankString::from_str("kube-prometheus").unwrap(), |         release_name: NonBlankString::from_str("kube-prometheus").unwrap(), | ||||||
|         chart_name: NonBlankString::from_str( |         chart_name: NonBlankString::from_str( | ||||||
|             "oci://ghcr.io/prometheus-community/charts/kube-prometheus-stack", //use kube prometheus chart which includes grafana, prometheus, alert
 |             "oci://ghcr.io/prometheus-community/charts/kube-prometheus-stack", //use kube prometheus chart which includes grafana, prometheus, alert
 | ||||||
| @ -41,7 +66,7 @@ additionalPrometheusRulesMap: | |||||||
|         ) |         ) | ||||||
|         .unwrap(), |         .unwrap(), | ||||||
|         chart_version: None, |         chart_version: None, | ||||||
|         values_overrides: None, |         values_overrides: Some(values_overrides), | ||||||
|         values_yaml: Some(values.to_string()), |         values_yaml: Some(values.to_string()), | ||||||
|         create_namespace: true, |         create_namespace: true, | ||||||
|         install_only: true, |         install_only: true, | ||||||
|  | |||||||
| @ -1,3 +1,4 @@ | |||||||
| pub mod monitoring_alerting; | pub mod monitoring_alerting; | ||||||
|  | mod config; | ||||||
| mod kube_prometheus; | mod kube_prometheus; | ||||||
| 
 | 
 | ||||||
|  | |||||||
| @ -3,142 +3,45 @@ use log::info; | |||||||
| use serde::Serialize; | use serde::Serialize; | ||||||
| 
 | 
 | ||||||
| use crate::{ | use crate::{ | ||||||
|     data::{Id, Version}, |     interpret::Interpret, | ||||||
|     interpret::{Interpret, InterpretError, InterpretName, InterpretStatus, Outcome}, |     modules::helm::chart::HelmChartScore, | ||||||
|     inventory::Inventory, |     score::{CloneBoxScore, Score, SerializeScore}, | ||||||
|     maestro::Maestro, |     topology::{HelmCommand, Topology}, | ||||||
|     score::{CloneBoxScore, Score}, |  | ||||||
|     topology::{HelmCommand, Topology, monitoring_alerting::MonitoringAlertingTopology}, |  | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| use super::kube_prometheus::kube_prometheus_score; | use super::{config::KubePrometheusConfig, kube_prometheus::kube_prometheus_score}; | ||||||
| 
 | 
 | ||||||
| #[derive(Debug)] | #[derive(Debug, Clone, Serialize)] | ||||||
| pub struct MonitoringAlertingStackScore { | pub struct KubePrometheusStackScore { | ||||||
|     //TODO add documenation to explain why its here
 |     pub monitoring_stack: HelmChartScore, | ||||||
|     //keeps it open for the end user to specify which stack they want 
 |     
 | ||||||
|     //if it isnt default kube-prometheus
 |  | ||||||
|     pub monitoring_stack: Vec<Box<dyn Score<MonitoringAlertingTopology>>>, |  | ||||||
|     pub namespace: String, |  | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| impl MonitoringAlertingStackScore { | impl KubePrometheusStackScore { | ||||||
|     pub fn new( |     pub fn new(monitoring_stack: HelmChartScore) -> Self { | ||||||
|         monitoring_stack: Vec<Box<dyn Score<MonitoringAlertingTopology>>>, |  | ||||||
|         namespace: String, |  | ||||||
|     ) -> Self { |  | ||||||
|         Self { |         Self { | ||||||
|             monitoring_stack, |             monitoring_stack, | ||||||
|             namespace, |  | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| impl Default for MonitoringAlertingStackScore { | impl Default for KubePrometheusStackScore { | ||||||
|     fn default() -> Self { |     fn default() -> Self { | ||||||
|         let ns = "monitoring"; |         let config = KubePrometheusConfig::default(); | ||||||
|  |         let monitoring_stack = kube_prometheus_score(config); | ||||||
|  |         
 | ||||||
|         Self { |         Self { | ||||||
|             monitoring_stack: vec![Box::new(kube_prometheus_score(ns))], |            monitoring_stack 
 | ||||||
|             namespace: ns.to_string(), |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
| } |  | ||||||
| impl Clone for MonitoringAlertingStackScore { |  | ||||||
|     fn clone(&self) -> Self { |  | ||||||
|         Self { |  | ||||||
|             monitoring_stack: self |  | ||||||
|                 .monitoring_stack |  | ||||||
|                 .iter() |  | ||||||
|                 .map(|s| s.clone_box()) |  | ||||||
|                 .collect(), |  | ||||||
|             namespace: self.namespace.clone(), |  | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| impl Serialize for MonitoringAlertingStackScore { | impl<T: Topology + HelmCommand> Score<T> for KubePrometheusStackScore { | ||||||
|     fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error> |  | ||||||
|     where |  | ||||||
|         S: serde::Serializer, |  | ||||||
|     { |  | ||||||
|         use serde::ser::SerializeStruct; |  | ||||||
|         let mut s = serializer.serialize_struct("MonitoringAlertingStackScore", 1)?; |  | ||||||
|         let monitoring_values: Vec<_> = self |  | ||||||
|             .monitoring_stack |  | ||||||
|             .iter() |  | ||||||
|             .map(|m| m.serialize()) |  | ||||||
|             .collect(); |  | ||||||
|         s.serialize_field("monitoring", &monitoring_values)?; |  | ||||||
|         s.end() |  | ||||||
|     } |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| impl<T:Topology> Score<T> for MonitoringAlertingStackScore { |  | ||||||
|     fn create_interpret(&self) -> Box<dyn Interpret<T>> { |  | ||||||
|         Box::new(MonitoringAlertingStackInterpret { |  | ||||||
|             score: MonitoringAlertingStackScore { |  | ||||||
|                 monitoring_stack: self |  | ||||||
|                     .monitoring_stack |  | ||||||
|                     .iter() |  | ||||||
|                     .map(|s| s.clone_box()) |  | ||||||
|                     .collect(), |  | ||||||
|                 namespace: self.namespace.clone(), |  | ||||||
|             }, |  | ||||||
|         }) |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     fn name(&self) -> String { |     fn name(&self) -> String { | ||||||
|         format!("MonitoringAlertingStackScore") |         format!("MonitoringAlertingStackScore") | ||||||
|     } |     } | ||||||
| } |  | ||||||
| 
 | 
 | ||||||
| #[derive(Debug)] |     fn create_interpret(&self) -> Box<dyn Interpret<T>> { | ||||||
| struct MonitoringAlertingStackInterpret { |         self.monitoring_stack.create_interpret() | ||||||
|     pub score: MonitoringAlertingStackScore, |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| #[async_trait] |  | ||||||
| impl <T: Topology> Interpret<T> for MonitoringAlertingStackInterpret { |  | ||||||
|     async fn execute( |  | ||||||
|         &self, |  | ||||||
|         _inventory: &Inventory, |  | ||||||
|         _topology: &T, |  | ||||||
|     ) -> Result<Outcome, InterpretError> { |  | ||||||
|         let inventory = Inventory::autoload(); |  | ||||||
|         let topology = MonitoringAlertingTopology::new(); |  | ||||||
|         let maestro = match Maestro::initialize(inventory, topology).await { |  | ||||||
|             Ok(m) => m, |  | ||||||
|             Err(e) => { |  | ||||||
|                 println!("failed to initialize Maestro: {}", e); |  | ||||||
|                 std::process::exit(1); |  | ||||||
|             } |  | ||||||
|         }; |  | ||||||
|         
 |  | ||||||
|         let scores_vec = self.score.monitoring_stack.clone(); |  | ||||||
|         for s in scores_vec{ |  | ||||||
|             info!("Running: {}", s.name()); |  | ||||||
|             maestro.interpret(s).await?; |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         Ok(Outcome::success(format!( |  | ||||||
|             "monitoring stack installed in {} namespace", |  | ||||||
|             self.score.namespace |  | ||||||
|         ))) |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     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!() |  | ||||||
|     } |     } | ||||||
| } | } | ||||||
|  | |||||||
		Loading…
	
		Reference in New Issue
	
	Block a user