feat: implementation of Installable for CRDPrometheusIntroduction of Grafana trait and its impl for k8sanywhereallows for CRDPrometheus to be installed via AlertingInterpret which standardizes the installation of alert receivers, alerting rules, and alert senders
This commit is contained in:
parent
7dff70edcf
commit
fc384599a1
@ -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<PreparationOutcome, PreparationError> {
|
||||
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<String> {
|
||||
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<TokenRequestStatus, PreparationError> {
|
||||
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::<TokenRequest>("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());
|
||||
|
||||
@ -105,7 +105,7 @@ pub struct GrafanaDashboardSpec {
|
||||
|
||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||
pub datasources: Option<Vec<GrafanaDashboardDatasource>>,
|
||||
|
||||
|
||||
pub instance_selector: LabelSelector,
|
||||
|
||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||
@ -156,7 +156,6 @@ pub struct GrafanaDatasourceSpec {
|
||||
pub values_from: Option<Vec<GrafanaValueFrom>>,
|
||||
}
|
||||
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug, Clone, JsonSchema)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct GrafanaValueFrom {
|
||||
|
||||
@ -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,
|
||||
|
||||
Loading…
Reference in New Issue
Block a user