feat/impl_installable_crd_prometheus #170

Open
wjro wants to merge 9 commits from feat/impl_installable_crd_prometheus into master
2 changed files with 159 additions and 15 deletions
Showing only changes of commit 85bec66e58 - Show all commits

View File

@ -1,7 +1,15 @@
use std::{collections::BTreeMap, process::Command, sync::Arc}; use std::{collections::BTreeMap, process::Command, sync::Arc};
use async_trait::async_trait; 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 log::{debug, info, warn};
use serde::Serialize; use serde::Serialize;
use tokio::sync::OnceCell; use tokio::sync::OnceCell;
@ -19,12 +27,14 @@ use crate::{
crd_alertmanager_config::CRDPrometheus, crd_alertmanager_config::CRDPrometheus,
crd_grafana::{ crd_grafana::{
Grafana as GrafanaCRD, GrafanaDashboard, GrafanaDashboardSpec, Grafana as GrafanaCRD, GrafanaDashboard, GrafanaDashboardSpec,
GrafanaDatasource, GrafanaDatasourceConfig, GrafanaDatasourceSpec, GrafanaSpec, GrafanaDatasource, GrafanaDatasourceConfig, GrafanaDatasourceJsonData,
GrafanaDatasourceSecureJsonData, GrafanaDatasourceSpec, GrafanaSpec,
}, },
crd_prometheuses::LabelSelector, crd_prometheuses::LabelSelector,
grafana_default_dashboard::build_default_dashboard, grafana_default_dashboard::build_default_dashboard,
prometheus_operator::prometheus_operator_helm_chart_score, prometheus_operator::prometheus_operator_helm_chart_score,
rhob_alertmanager_config::RHOBObservability, rhob_alertmanager_config::RHOBObservability,
role::build_prom_service_account,
service_monitor::ServiceMonitor, service_monitor::ServiceMonitor,
}, },
}, },
@ -142,8 +152,26 @@ impl Grafana for K8sAnywhereTopology {
}; };
let client = self.k8s_client().await?; 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<Secret> 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?; client.apply(&datasource, Some(ns)).await?;
@ -334,35 +362,121 @@ impl K8sAnywhereTopology {
.clone() .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( fn build_grafana_datasource(
&self, &self,
ns: &str, ns: &str,
label_selector: &LabelSelector, label_selector: &LabelSelector,
url: &str,
) -> GrafanaDatasource { ) -> GrafanaDatasource {
let mut json_data = BTreeMap::new(); let mut json_data = BTreeMap::new();
json_data.insert("timeInterval".to_string(), "5s".to_string()); 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 { metadata: ObjectMeta {
name: Some(format!("grafana-datasource-{}", ns)), name: Some("thanos-prometheus".to_string()),
namespace: Some(ns.to_string()), namespace: Some(ns.to_string()),
..Default::default() ..Default::default()
}, },
spec: GrafanaDatasourceSpec { spec: GrafanaDatasourceSpec {
instance_selector: label_selector.clone(), instance_selector: label_selector.clone(),
allow_cross_namespace_import: Some(false), allow_cross_namespace_import: Some(true),
datasource: GrafanaDatasourceConfig { datasource: GrafanaDatasourceConfig {
access: "proxy".to_string(), access: "proxy".to_string(),
database: Some("prometheus".to_string()), name: "OpenShift-Thanos".to_string(),
json_data: Some(json_data),
//this is fragile
name: format!("prometheus-{}-0", ns),
r#type: "prometheus".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( fn build_grafana_dashboard(

View File

@ -133,13 +133,43 @@ pub struct GrafanaDatasourceSpec {
pub struct GrafanaDatasourceConfig { pub struct GrafanaDatasourceConfig {
pub access: String, pub access: String,
pub database: Option<String>, pub database: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub json_data: Option<BTreeMap<String, String>>,
pub name: String, pub name: String,
pub r#type: String, pub r#type: String,
pub url: String, pub url: String,
/// Represents jsonData in the GrafanaDatasource spec
#[serde(default, skip_serializing_if = "Option::is_none")]
pub json_data: Option<GrafanaDatasourceJsonData>,
/// Represents secureJsonData (secrets)
#[serde(default, skip_serializing_if = "Option::is_none")]
pub secure_json_data: Option<GrafanaDatasourceSecureJsonData>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub is_default: Option<bool>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub editable: Option<bool>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub version: Option<i32>,
} }
#[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<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub http_header_name1: Option<String>,
}
#[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<String>,
}
// ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------
#[derive(Serialize, Deserialize, Debug, Clone, JsonSchema, Default)] #[derive(Serialize, Deserialize, Debug, Clone, JsonSchema, Default)]