feat/impl_installable_crd_prometheus #170
@ -1,21 +1,12 @@
|
|||||||
use std::{
|
use std::{collections::BTreeMap, process::Command, sync::Arc};
|
||||||
collections::{BTreeMap, HashMap},
|
|
||||||
process::Command,
|
|
||||||
sync::Arc,
|
|
||||||
};
|
|
||||||
|
|
||||||
use async_trait::async_trait;
|
use async_trait::async_trait;
|
||||||
|
use base64::{Engine, engine::general_purpose};
|
||||||
use k8s_openapi::api::{
|
use k8s_openapi::api::{
|
||||||
authentication::v1::{
|
core::v1::Secret,
|
||||||
BoundObjectReference, TokenRequest, TokenRequestSpec, TokenRequestStatus,
|
|
||||||
},
|
|
||||||
core::v1::{Secret, ServiceAccount},
|
|
||||||
rbac::v1::{ClusterRoleBinding, RoleRef, Subject},
|
rbac::v1::{ClusterRoleBinding, RoleRef, Subject},
|
||||||
};
|
};
|
||||||
use kube::{
|
use kube::api::{DynamicObject, GroupVersionKind, ObjectMeta};
|
||||||
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;
|
||||||
@ -35,14 +26,11 @@ use crate::{
|
|||||||
Grafana as GrafanaCRD, GrafanaCom, GrafanaDashboard,
|
Grafana as GrafanaCRD, GrafanaCom, GrafanaDashboard,
|
||||||
GrafanaDashboardDatasource, GrafanaDashboardSpec, GrafanaDatasource,
|
GrafanaDashboardDatasource, GrafanaDashboardSpec, GrafanaDatasource,
|
||||||
GrafanaDatasourceConfig, GrafanaDatasourceJsonData,
|
GrafanaDatasourceConfig, GrafanaDatasourceJsonData,
|
||||||
GrafanaDatasourceSecureJsonData, GrafanaDatasourceSpec, GrafanaSecretKeyRef,
|
GrafanaDatasourceSecureJsonData, GrafanaDatasourceSpec, GrafanaSpec,
|
||||||
GrafanaSpec, GrafanaValueFrom, GrafanaValueSource,
|
|
||||||
},
|
},
|
||||||
crd_prometheuses::LabelSelector,
|
crd_prometheuses::LabelSelector,
|
||||||
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,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@ -148,24 +136,23 @@ impl Grafana for K8sAnywhereTopology {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
async fn install_grafana(&self) -> Result<PreparationOutcome, PreparationError> {
|
async fn install_grafana(&self) -> Result<PreparationOutcome, PreparationError> {
|
||||||
debug!("install grafana");
|
|
||||||
let ns = "grafana";
|
let ns = "grafana";
|
||||||
|
|
||||||
let mut label = BTreeMap::new();
|
let mut label = BTreeMap::new();
|
||||||
|
|
||||||
label.insert("dashboards".to_string(), "grafana".to_string());
|
label.insert("dashboards".to_string(), "grafana".to_string());
|
||||||
|
|
||||||
let label_selector = LabelSelector {
|
let label_selector = LabelSelector {
|
||||||
match_labels: label.clone(),
|
match_labels: label.clone(),
|
||||||
match_expressions: vec![],
|
match_expressions: vec![],
|
||||||
};
|
};
|
||||||
debug!("getting client");
|
|
||||||
let client = self.k8s_client().await?;
|
let client = self.k8s_client().await?;
|
||||||
|
|
||||||
info!("creating grafanas crd");
|
|
||||||
let grafana = self.build_grafana(ns, &label);
|
let grafana = self.build_grafana(ns, &label);
|
||||||
|
|
||||||
client.apply(&grafana, Some(ns)).await?;
|
client.apply(&grafana, Some(ns)).await?;
|
||||||
|
//TODO change this to a ensure ready or something better than just a timeout
|
||||||
client
|
client
|
||||||
.wait_until_deployment_ready(
|
.wait_until_deployment_ready(
|
||||||
"grafana-grafana-deployment".to_string(),
|
"grafana-grafana-deployment".to_string(),
|
||||||
@ -175,16 +162,25 @@ impl Grafana for K8sAnywhereTopology {
|
|||||||
.await?;
|
.await?;
|
||||||
|
|
||||||
let sa_name = "grafana-grafana-sa";
|
let sa_name = "grafana-grafana-sa";
|
||||||
|
|
||||||
let token_secret_name = "grafana-sa-token-secret";
|
let token_secret_name = "grafana-sa-token-secret";
|
||||||
|
|
||||||
// let sa_token_secret = self.build_sa_token_secret(token_secret_name, sa_name, ns);
|
let sa_token_secret = self.build_sa_token_secret(token_secret_name, sa_name, ns);
|
||||||
//
|
|
||||||
// client.apply(&sa_token_secret, Some(ns)).await?;
|
|
||||||
|
|
||||||
let secret = self.build_token_secret(token_secret_name, ns).await;
|
client.apply(&sa_token_secret, Some(ns)).await?;
|
||||||
client.apply(&secret, Some(ns)).await?;
|
let secret_gvk = GroupVersionKind {
|
||||||
let token_request_status = self.create_service_account_token(sa_name, ns).await?;
|
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");
|
debug!("creating grafana clusterrole binding");
|
||||||
|
|
||||||
@ -195,8 +191,6 @@ impl Grafana for K8sAnywhereTopology {
|
|||||||
|
|
||||||
debug!("creating grafana datasource crd");
|
debug!("creating grafana datasource crd");
|
||||||
|
|
||||||
// let token_str = format!("Bearer {}", token.token);
|
|
||||||
|
|
||||||
let thanos_url = format!(
|
let thanos_url = format!(
|
||||||
"https://{}",
|
"https://{}",
|
||||||
self.get_domain("thanos-querier-openshift-monitoring")
|
self.get_domain("thanos-querier-openshift-monitoring")
|
||||||
@ -209,7 +203,7 @@ impl Grafana for K8sAnywhereTopology {
|
|||||||
ns,
|
ns,
|
||||||
&label_selector,
|
&label_selector,
|
||||||
&thanos_url,
|
&thanos_url,
|
||||||
&token_request_status.token, // Pass the secret name here
|
&token,
|
||||||
);
|
);
|
||||||
|
|
||||||
client.apply(&thanos_openshift_datasource, Some(ns)).await?;
|
client.apply(&thanos_openshift_datasource, Some(ns)).await?;
|
||||||
@ -398,8 +392,21 @@ impl K8sAnywhereTopology {
|
|||||||
.clone()
|
.clone()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn build_service_account(&self, name: &str, namespace: &str) -> ServiceAccount {
|
fn extract_and_normalize_token(&self, secret: &DynamicObject) -> Option<String> {
|
||||||
build_prom_service_account(name.to_string(), namespace.to_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(
|
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(
|
fn build_grafana_datasource(
|
||||||
&self,
|
&self,
|
||||||
name: &str,
|
name: &str,
|
||||||
ns: &str,
|
ns: &str,
|
||||||
label_selector: &LabelSelector,
|
label_selector: &LabelSelector,
|
||||||
url: &str,
|
url: &str,
|
||||||
token: &str, // Pass in the secret name
|
token: &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());
|
||||||
|
|||||||
@ -156,7 +156,6 @@ pub struct GrafanaDatasourceSpec {
|
|||||||
pub values_from: Option<Vec<GrafanaValueFrom>>,
|
pub values_from: Option<Vec<GrafanaValueFrom>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize, Debug, Clone, JsonSchema)]
|
#[derive(Serialize, Deserialize, Debug, Clone, JsonSchema)]
|
||||||
#[serde(rename_all = "camelCase")]
|
#[serde(rename_all = "camelCase")]
|
||||||
pub struct GrafanaValueFrom {
|
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_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_default_rules::build_default_application_rules;
|
||||||
use crate::modules::monitoring::kube_prometheus::crd::crd_grafana::{
|
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::{
|
use crate::modules::monitoring::kube_prometheus::crd::crd_prometheus_rules::{
|
||||||
PrometheusRule, PrometheusRuleSpec, RuleGroup,
|
PrometheusRule, PrometheusRuleSpec, RuleGroup,
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user