diff --git a/harmony/src/domain/topology/k8s_anywhere.rs b/harmony/src/domain/topology/k8s_anywhere.rs index 895f7da..efbe33f 100644 --- a/harmony/src/domain/topology/k8s_anywhere.rs +++ b/harmony/src/domain/topology/k8s_anywhere.rs @@ -1,7 +1,15 @@ use std::{collections::BTreeMap, process::Command, sync::Arc}; use async_trait::async_trait; -use kube::api::{GroupVersionKind, ObjectMeta}; +use k8s_openapi::api::{ + authentication::v1::{TokenRequest, TokenRequestSpec}, + core::v1::{Secret, ServiceAccount}, + rbac::v1::{ClusterRoleBinding, RoleRef, Subject}, +}; +use kube::{ + Api, + api::{GroupVersionKind, ObjectMeta, PostParams}, +}; use log::{debug, info, warn}; use serde::Serialize; use tokio::sync::OnceCell; @@ -19,12 +27,14 @@ use crate::{ crd_alertmanager_config::CRDPrometheus, crd_grafana::{ Grafana as GrafanaCRD, GrafanaDashboard, GrafanaDashboardSpec, - GrafanaDatasource, GrafanaDatasourceConfig, GrafanaDatasourceSpec, GrafanaSpec, + GrafanaDatasource, GrafanaDatasourceConfig, GrafanaDatasourceJsonData, + 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, }, }, @@ -142,8 +152,26 @@ impl Grafana for K8sAnywhereTopology { }; let client = self.k8s_client().await?; + let url = format!("{}:9091", self.get_domain("thanos-querier").await.unwrap()); + + let sa = self.build_service_account(); + //TODO finish this section + //needs apply Api or something + client.apply(&sa, Some(ns)).await?; - let datasource = self.build_grafana_datasource(ns, &label_selector); + let token_request =self.get_token_request(); + //this wont work needs a new function for apply secret + client.apply(&token_request, Some(ns)).await?; + + let clusterrolebinding = self.build_cluster_rolebinding(); + + client.apply(&clusterrolebinding, Some(ns)).await?; + + let secret = self.build_token_secret(); + + client.apply(&secret, Some(ns)).await?; + + let datasource = self.build_grafana_datasource(ns, &label_selector, &url); client.apply(&datasource, Some(ns)).await?; @@ -334,35 +362,121 @@ impl K8sAnywhereTopology { .clone() } + pub fn build_service_account(&self, name: &str, namespace: &str) -> ServiceAccount { + build_prom_service_account(name.to_string(), namespace.to_string()) + } + + pub fn build_cluster_rolebinding( + &self, + ns: &str, + account_name: &str, + role: &str, + ) -> ClusterRoleBinding { + ClusterRoleBinding { + metadata: ObjectMeta { + name: Some(format!("{}-view-binding", account_name)), + ..Default::default() + }, + role_ref: RoleRef { + api_group: "rbac.authorization.k8s.io".into(), + kind: "ClusterRole".into(), + name: role.into(), + }, + subjects: Some(vec![Subject { + kind: "ServiceAccount".into(), + name: account_name.into(), + namespace: Some(ns.into()), + ..Default::default() + }]), + } + } + + pub fn get_token_request(&self) -> TokenRequest { + TokenRequest { + spec: TokenRequestSpec { + audiences: vec!["https://kubernetes.default.svc".to_string()], + expiration_seconds: Some(3600), + ..Default::default() + }, + ..Default::default() + } + } + + pub fn build_token_secret(&self, token: &str, ns: &str) -> Secret { + Secret { + metadata: ObjectMeta { + name: Some("grafana-credentials".into()), + namespace: Some(ns.into()), + ..Default::default() + }, + string_data: Some(std::collections::BTreeMap::from([( + "PROMETHEUS_TOKEN".into(), + format!("Bearer {}", token), + )])), + ..Default::default() + } + } + fn build_grafana_datasource( &self, ns: &str, label_selector: &LabelSelector, + url: &str, ) -> 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: url.to_string(), + // //url: format!("http://prometheus-operated.{}.svc.cluster.local:9090", ns), + // }, + // }, + // }; + // graf_data_source - let graf_data_source = GrafanaDatasource { + GrafanaDatasource { metadata: ObjectMeta { - name: Some(format!("grafana-datasource-{}", ns)), + name: Some("thanos-prometheus".to_string()), namespace: Some(ns.to_string()), ..Default::default() }, spec: GrafanaDatasourceSpec { instance_selector: label_selector.clone(), - allow_cross_namespace_import: Some(false), + allow_cross_namespace_import: Some(true), datasource: GrafanaDatasourceConfig { access: "proxy".to_string(), - database: Some("prometheus".to_string()), - json_data: Some(json_data), - //this is fragile - name: format!("prometheus-{}-0", ns), + name: "OpenShift-Thanos".to_string(), r#type: "prometheus".to_string(), - url: format!("http://prometheus-operated.{}.svc.cluster.local:9090", ns), + url: url.to_string(), + database: None, + json_data: Some(GrafanaDatasourceJsonData { + time_interval: Some("60s".to_string()), + http_header_name1: Some("Authorization".to_string()), + }), + secure_json_data: Some(GrafanaDatasourceSecureJsonData { + http_header_value1: Some("Bearer eyJhbGc...".to_string()), + }), + is_default: Some(false), + editable: Some(true), + version: Some(1), }, }, - }; - graf_data_source + } } fn build_grafana_dashboard( 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 793f639..4134670 100644 --- a/harmony/src/modules/monitoring/kube_prometheus/crd/crd_grafana.rs +++ b/harmony/src/modules/monitoring/kube_prometheus/crd/crd_grafana.rs @@ -133,13 +133,43 @@ pub struct GrafanaDatasourceSpec { pub struct GrafanaDatasourceConfig { pub access: String, pub database: Option, - #[serde(default, skip_serializing_if = "Option::is_none")] - pub json_data: Option>, pub name: String, pub r#type: String, pub url: String, + /// Represents jsonData in the GrafanaDatasource spec + #[serde(default, skip_serializing_if = "Option::is_none")] + pub json_data: Option, + + /// Represents secureJsonData (secrets) + #[serde(default, skip_serializing_if = "Option::is_none")] + pub secure_json_data: Option, + + #[serde(default, skip_serializing_if = "Option::is_none")] + pub is_default: Option, + + #[serde(default, skip_serializing_if = "Option::is_none")] + pub editable: Option, + + #[serde(default, skip_serializing_if = "Option::is_none")] + pub version: Option, } +#[derive(Serialize, Deserialize, Debug, Clone, JsonSchema)] +#[serde(rename_all = "camelCase")] +pub struct GrafanaDatasourceJsonData { + #[serde(default, skip_serializing_if = "Option::is_none")] + pub time_interval: Option, + + #[serde(default, skip_serializing_if = "Option::is_none")] + pub http_header_name1: Option, +} + +#[derive(Serialize, Deserialize, Debug, Clone, JsonSchema)] +#[serde(rename_all = "camelCase")] +pub struct GrafanaDatasourceSecureJsonData { + #[serde(default, skip_serializing_if = "Option::is_none")] + pub http_header_value1: Option, +} // ------------------------------------------------------------------------------------------------ #[derive(Serialize, Deserialize, Debug, Clone, JsonSchema, Default)]