diff --git a/harmony/src/domain/topology/k8s_anywhere.rs b/harmony/src/domain/topology/k8s_anywhere.rs index cb4ab2d..e45b65f 100644 --- a/harmony/src/domain/topology/k8s_anywhere.rs +++ b/harmony/src/domain/topology/k8s_anywhere.rs @@ -1,21 +1,12 @@ -use std::{ - collections::{BTreeMap, HashMap}, - process::Command, - sync::Arc, -}; +use std::{collections::BTreeMap, process::Command, sync::Arc}; use async_trait::async_trait; +use base64::{Engine, engine::general_purpose}; use k8s_openapi::api::{ - authentication::v1::{ - BoundObjectReference, TokenRequest, TokenRequestSpec, TokenRequestStatus, - }, - core::v1::{Secret, ServiceAccount}, + core::v1::Secret, rbac::v1::{ClusterRoleBinding, RoleRef, Subject}, }; -use kube::{ - Api, - api::{GroupVersionKind, ObjectMeta, PostParams}, -}; +use kube::api::{DynamicObject, GroupVersionKind, ObjectMeta}; use log::{debug, info, warn}; use serde::Serialize; use tokio::sync::OnceCell; @@ -35,14 +26,11 @@ use crate::{ Grafana as GrafanaCRD, GrafanaCom, GrafanaDashboard, GrafanaDashboardDatasource, GrafanaDashboardSpec, GrafanaDatasource, GrafanaDatasourceConfig, GrafanaDatasourceJsonData, - GrafanaDatasourceSecureJsonData, GrafanaDatasourceSpec, GrafanaSecretKeyRef, - GrafanaSpec, GrafanaValueFrom, GrafanaValueSource, + GrafanaDatasourceSecureJsonData, GrafanaDatasourceSpec, GrafanaSpec, }, crd_prometheuses::LabelSelector, - grafana_default_dashboard::build_default_dashboard, prometheus_operator::prometheus_operator_helm_chart_score, rhob_alertmanager_config::RHOBObservability, - role::build_prom_service_account, service_monitor::ServiceMonitor, }, }, @@ -148,24 +136,23 @@ impl Grafana for K8sAnywhereTopology { }; } async fn install_grafana(&self) -> Result { - 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![], }; - debug!("getting client"); + let client = self.k8s_client().await?; - info!("creating grafanas crd"); let grafana = self.build_grafana(ns, &label); client.apply(&grafana, Some(ns)).await?; - + //TODO change this to a ensure ready or something better than just a timeout client .wait_until_deployment_ready( "grafana-grafana-deployment".to_string(), @@ -175,16 +162,25 @@ impl Grafana for K8sAnywhereTopology { .await?; let sa_name = "grafana-grafana-sa"; - let token_secret_name = "grafana-sa-token-secret"; - // let sa_token_secret = self.build_sa_token_secret(token_secret_name, sa_name, ns); - // - // client.apply(&sa_token_secret, Some(ns)).await?; + let sa_token_secret = self.build_sa_token_secret(token_secret_name, sa_name, ns); - let secret = self.build_token_secret(token_secret_name, ns).await; - client.apply(&secret, Some(ns)).await?; - let token_request_status = self.create_service_account_token(sa_name, ns).await?; + client.apply(&sa_token_secret, Some(ns)).await?; + let secret_gvk = GroupVersionKind { + group: "".to_string(), + version: "v1".to_string(), + kind: "Secret".to_string(), + }; + + let secret = client + .get_resource_json_value(token_secret_name, Some(ns), &secret_gvk) + .await?; + + let token = format!( + "Bearer {}", + self.extract_and_normalize_token(&secret).unwrap() + ); debug!("creating grafana clusterrole binding"); @@ -195,8 +191,6 @@ impl Grafana for K8sAnywhereTopology { debug!("creating grafana datasource crd"); - // let token_str = format!("Bearer {}", token.token); - let thanos_url = format!( "https://{}", self.get_domain("thanos-querier-openshift-monitoring") @@ -209,7 +203,7 @@ impl Grafana for K8sAnywhereTopology { ns, &label_selector, &thanos_url, - &token_request_status.token, // Pass the secret name here + &token, ); client.apply(&thanos_openshift_datasource, Some(ns)).await?; @@ -398,8 +392,21 @@ impl K8sAnywhereTopology { .clone() } - pub fn build_service_account(&self, name: &str, namespace: &str) -> ServiceAccount { - build_prom_service_account(name.to_string(), namespace.to_string()) + fn extract_and_normalize_token(&self, secret: &DynamicObject) -> Option { + let token_b64 = secret + .data + .get("token") + .or_else(|| secret.data.get("data").and_then(|d| d.get("token"))) + .and_then(|v| v.as_str())?; + + let bytes = general_purpose::STANDARD.decode(token_b64).ok()?; + + let s = String::from_utf8(bytes).ok()?; + + let cleaned = s + .trim_matches(|c: char| c.is_whitespace() || c == '\0') + .to_string(); + Some(cleaned) } pub fn build_cluster_rolebinding( @@ -451,69 +458,13 @@ impl K8sAnywhereTopology { } } - pub fn get_token_request(&self, ns: &str) -> TokenRequest { - debug!("building token request"); - TokenRequest { - metadata: ObjectMeta { - namespace: Some(ns.to_string()), - ..Default::default() - }, - spec: TokenRequestSpec { - audiences: vec!["https://kubernetes.default.svc".to_string()], - expiration_seconds: Some(3600), - bound_object_ref: Some(BoundObjectReference { - kind: Some("Secret".to_string()), - name: Some("grafana-sa-token-secret".to_string()), - ..Default::default() - }), - }, - ..Default::default() - } - } - - pub async fn create_service_account_token( - &self, - service_account_name: &str, - ns: &str, - ) -> Result { - debug!("creating service account token"); - let token_request = self.get_token_request(ns); - let client = self.k8s_client().await?; - let pp = PostParams::default(); - let token_requests_api = client.service_account_api(ns).await; - - let data = serde_json::to_vec(&token_request).unwrap(); - - let created_token_request = token_requests_api - .create_subresource::("token", service_account_name, &pp, data) - .await?; - - let status = created_token_request - .status - .ok_or_else(|| PreparationError::new("missing token request status".to_string()))?; - - Ok(status) - } - - pub async fn build_token_secret(&self, secret_name: &str, ns: &str) -> Secret { - Secret { - metadata: ObjectMeta { - name: Some(secret_name.into()), - namespace: Some(ns.into()), - ..Default::default() - }, - string_data: None, - ..Default::default() - } - } - fn build_grafana_datasource( &self, name: &str, ns: &str, label_selector: &LabelSelector, url: &str, - token: &str, // Pass in the secret name + token: &str, ) -> GrafanaDatasource { let mut json_data = BTreeMap::new(); json_data.insert("timeInterval".to_string(), "5s".to_string()); diff --git a/harmony/src/modules/monitoring/kube_prometheus/crd/crd_grafana.rs b/harmony/src/modules/monitoring/kube_prometheus/crd/crd_grafana.rs index c99adc1..386890e 100644 --- a/harmony/src/modules/monitoring/kube_prometheus/crd/crd_grafana.rs +++ b/harmony/src/modules/monitoring/kube_prometheus/crd/crd_grafana.rs @@ -105,7 +105,7 @@ pub struct GrafanaDashboardSpec { #[serde(default, skip_serializing_if = "Option::is_none")] pub datasources: Option>, - + pub instance_selector: LabelSelector, #[serde(default, skip_serializing_if = "Option::is_none")] @@ -156,7 +156,6 @@ pub struct GrafanaDatasourceSpec { pub values_from: Option>, } - #[derive(Serialize, Deserialize, Debug, Clone, JsonSchema)] #[serde(rename_all = "camelCase")] pub struct GrafanaValueFrom { diff --git a/harmony/src/modules/prometheus/k8s_prometheus_alerting_score.rs b/harmony/src/modules/prometheus/k8s_prometheus_alerting_score.rs index 7873235..d7dca5e 100644 --- a/harmony/src/modules/prometheus/k8s_prometheus_alerting_score.rs +++ b/harmony/src/modules/prometheus/k8s_prometheus_alerting_score.rs @@ -11,7 +11,9 @@ use std::process::Command; use crate::modules::monitoring::kube_prometheus::crd::crd_alertmanager_config::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, GrafanaDatasourceJsonData, GrafanaDatasourceSpec, GrafanaSecretKeyRef, GrafanaSpec, GrafanaValueFrom, GrafanaValueSource + Grafana, GrafanaDashboard, GrafanaDashboardSpec, GrafanaDatasource, GrafanaDatasourceConfig, + GrafanaDatasourceJsonData, GrafanaDatasourceSpec, GrafanaSecretKeyRef, GrafanaSpec, + GrafanaValueFrom, GrafanaValueSource, }; use crate::modules::monitoring::kube_prometheus::crd::crd_prometheus_rules::{ PrometheusRule, PrometheusRuleSpec, RuleGroup,