feat/impl_installable_crd_prometheus #170

Open
wjro wants to merge 9 commits from feat/impl_installable_crd_prometheus into master
3 changed files with 45 additions and 93 deletions
Showing only changes of commit fc384599a1 - Show all commits

View File

@ -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());

View File

@ -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 {

View File

@ -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,