From 21e51b8d809c2cd379e4d0d44f8d5095c2af63a2 Mon Sep 17 00:00:00 2001 From: Willem Date: Fri, 19 Sep 2025 10:00:13 -0400 Subject: [PATCH] feat(monitoring): score to enable user-workload-monitoring within okd --- harmony/src/modules/monitoring/mod.rs | 1 + .../monitoring/okd/enable_user_workload.rs | 181 ++++++++++++++++++ harmony/src/modules/monitoring/okd/mod.rs | 1 + 3 files changed, 183 insertions(+) create mode 100644 harmony/src/modules/monitoring/okd/enable_user_workload.rs create mode 100644 harmony/src/modules/monitoring/okd/mod.rs diff --git a/harmony/src/modules/monitoring/mod.rs b/harmony/src/modules/monitoring/mod.rs index b93f0c6..edda516 100644 --- a/harmony/src/modules/monitoring/mod.rs +++ b/harmony/src/modules/monitoring/mod.rs @@ -4,4 +4,5 @@ pub mod application_monitoring; pub mod grafana; pub mod kube_prometheus; pub mod ntfy; +pub mod okd; pub mod prometheus; diff --git a/harmony/src/modules/monitoring/okd/enable_user_workload.rs b/harmony/src/modules/monitoring/okd/enable_user_workload.rs new file mode 100644 index 0000000..d3a5998 --- /dev/null +++ b/harmony/src/modules/monitoring/okd/enable_user_workload.rs @@ -0,0 +1,181 @@ +use std::sync::Arc; + +use async_trait::async_trait; +use harmony_types::id::Id; +use k8s_openapi::api::core::v1::Pod; +use kube::api::GroupVersionKind; +use serde::Serialize; + +use crate::{ + data::Version, + interpret::{Interpret, InterpretError, InterpretName, InterpretStatus, Outcome}, + inventory::Inventory, + score::Score, + topology::{K8sclient, Topology, k8s::K8sClient}, +}; + +#[derive(Clone, Debug, Serialize)] +pub struct OpenshiftUserWorkloadMonitoring {} + +impl Score for OpenshiftUserWorkloadMonitoring { + fn name(&self) -> String { + "OpenshiftUserWorkloadMonitoringScore".to_string() + } + + fn create_interpret(&self) -> Box> { + Box::new(OpenshiftUserWorkloadMonitoringInterpret {}) + } +} + +#[derive(Clone, Debug, Serialize)] +pub struct OpenshiftUserWorkloadMonitoringInterpret {} + +#[async_trait] +impl Interpret for OpenshiftUserWorkloadMonitoringInterpret { + async fn execute( + &self, + inventory: &Inventory, + topology: &T, + ) -> Result { + let client = topology.k8s_client().await.unwrap(); + self.update_cluster_monitoring_config_cm(&client).await?; + self.update_user_workload_monitoring_config_cm(&client) + .await?; + self.verify_user_workload(&client).await?; + Ok(Outcome::success( + "successfully enabled user-workload-monitoring".to_string(), + )) + } + + fn get_name(&self) -> InterpretName { + InterpretName::Custom("OpenshiftUserWorkloadMonitoring") + } + + fn get_version(&self) -> Version { + todo!() + } + + fn get_status(&self) -> InterpretStatus { + todo!() + } + + fn get_children(&self) -> Vec { + todo!() + } +} + +impl OpenshiftUserWorkloadMonitoringInterpret { + pub async fn update_cluster_monitoring_config_cm( + &self, + client: &Arc, + ) -> Result { + let cm = format!( + r#" +apiVersion: v1 +kind: ConfigMap +metadata: + name: cluster-monitoring-config + namespace: openshift-monitoring +data: + config.yaml: | + enableUserWorkload: true + alertmanagerMain: + enableUserAlertmanagerConfig: true + "# + ); + + let cm_yaml = serde_yaml::to_value(cm).unwrap(); + + client + .apply_yaml(&cm_yaml, Some("openshift-monitoring")) + .await?; + Ok(Outcome::success( + "updated cluster-monitoring-config-map".to_string(), + )) + } + pub async fn update_user_workload_monitoring_config_cm( + &self, + client: &Arc, + ) -> Result { + let cm = format!( + r#" +apiVersion: v1 +kind: ConfigMap +metadata: + name: user-workload-monitoring-config + namespace: openshift-user-workload-monitoring +data: + config.yaml: | + alertmanager: + enabled: true + enableAlertmanagerConfig: true + "# + ); + + let cm_yaml = serde_yaml::to_value(cm).unwrap(); + + client + .apply_yaml(&cm_yaml, Some("openshift-user-workload-monitoring")) + .await?; + Ok(Outcome::success( + "updated openshift-user-monitoring-config-map".to_string(), + )) + } + + pub async fn verify_user_workload( + &self, + client: &Arc, + ) -> Result { + let namespace = "openshift-user-workload-monitoring"; + let alertmanager_name = "alertmanager-user-workload-0"; + let alertmanager = client + .get_pod(alertmanager_name, Some(namespace)) + .await + .unwrap(); + let prometheus_name = "prometheus-user-workload-0"; + let prometheus = client + .get_pod(prometheus_name, Some(namespace)) + .await + .unwrap(); + self.ensure_pod(alertmanager, alertmanager_name, namespace) + .await?; + self.ensure_pod(prometheus, prometheus_name, namespace) + .await + } + + async fn ensure_pod( + &self, + pod: Option, + pod_name: &str, + namespace: &str, + ) -> Result { + match pod { + Some(pod) => { + if let Some(status) = pod.status { + let phase = status.phase.unwrap_or("failed".to_string()); + if phase == "running" { + Ok(Outcome::success(format!( + "'{}' is ready with status.phase '{}'.", + pod.metadata.name.unwrap(), + phase + ))) + } else { + Err(InterpretError::new(format!( + "'{}' in namespace '{}' has status.phase '{}'.", + pod_name, namespace, phase + ))) + } + } else { + Err(InterpretError::new(format!( + "{} not found in ns: {}", + pod_name, namespace + ))) + } + } + None => Err(InterpretError::new(format!( + "'{}' not found in namespace '{}'", + pod_name, namespace + ))), + } + } +} diff --git a/harmony/src/modules/monitoring/okd/mod.rs b/harmony/src/modules/monitoring/okd/mod.rs new file mode 100644 index 0000000..50339ba --- /dev/null +++ b/harmony/src/modules/monitoring/okd/mod.rs @@ -0,0 +1 @@ +pub mod enable_user_workload;