Compare commits
3 Commits
feat/multi
...
cf84f2cce8
| Author | SHA1 | Date | |
|---|---|---|---|
| cf84f2cce8 | |||
| a12d12aa4f | |||
| cefb65933a |
22
examples/okd_cluster_alerts/Cargo.toml
Normal file
22
examples/okd_cluster_alerts/Cargo.toml
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
[package]
|
||||||
|
name = "example-okd-cluster-alerts"
|
||||||
|
edition = "2024"
|
||||||
|
version.workspace = true
|
||||||
|
readme.workspace = true
|
||||||
|
license.workspace = true
|
||||||
|
publish = false
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
harmony = { path = "../../harmony" }
|
||||||
|
harmony_cli = { path = "../../harmony_cli" }
|
||||||
|
harmony_types = { path = "../../harmony_types" }
|
||||||
|
harmony_secret = { path = "../../harmony_secret" }
|
||||||
|
harmony_secret_derive = { path = "../../harmony_secret_derive" }
|
||||||
|
cidr = { workspace = true }
|
||||||
|
tokio = { workspace = true }
|
||||||
|
harmony_macros = { path = "../../harmony_macros" }
|
||||||
|
log = { workspace = true }
|
||||||
|
env_logger = { workspace = true }
|
||||||
|
url = { workspace = true }
|
||||||
|
serde.workspace = true
|
||||||
|
brocade = { path = "../../brocade" }
|
||||||
26
examples/okd_cluster_alerts/src/main.rs
Normal file
26
examples/okd_cluster_alerts/src/main.rs
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
use harmony::{
|
||||||
|
inventory::Inventory,
|
||||||
|
modules::monitoring::{
|
||||||
|
alert_channel::discord_alert_channel::DiscordWebhook,
|
||||||
|
okd::cluster_monitoring::OpenshiftClusterAlertScore,
|
||||||
|
},
|
||||||
|
topology::K8sAnywhereTopology,
|
||||||
|
};
|
||||||
|
use harmony_macros::hurl;
|
||||||
|
|
||||||
|
#[tokio::main]
|
||||||
|
async fn main() {
|
||||||
|
harmony_cli::run(
|
||||||
|
Inventory::autoload(),
|
||||||
|
K8sAnywhereTopology::from_env(),
|
||||||
|
vec![Box::new(OpenshiftClusterAlertScore {
|
||||||
|
receivers: vec![Box::new(DiscordWebhook {
|
||||||
|
name: "Webhook example".to_string(),
|
||||||
|
url: hurl!("http://something.o"),
|
||||||
|
})],
|
||||||
|
})],
|
||||||
|
None,
|
||||||
|
)
|
||||||
|
.await
|
||||||
|
.unwrap();
|
||||||
|
}
|
||||||
@@ -91,6 +91,23 @@ impl K8sClient {
|
|||||||
Ok(resource.get(name).await?)
|
Ok(resource.get(name).await?)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub async fn get_secret_json_value(
|
||||||
|
&self,
|
||||||
|
name: &str,
|
||||||
|
namespace: Option<&str>,
|
||||||
|
) -> Result<DynamicObject, Error> {
|
||||||
|
self.get_resource_json_value(
|
||||||
|
name,
|
||||||
|
namespace,
|
||||||
|
&GroupVersionKind {
|
||||||
|
group: "".to_string(),
|
||||||
|
version: "v1".to_string(),
|
||||||
|
kind: "Secret".to_string(),
|
||||||
|
},
|
||||||
|
)
|
||||||
|
.await
|
||||||
|
}
|
||||||
|
|
||||||
pub async fn get_deployment(
|
pub async fn get_deployment(
|
||||||
&self,
|
&self,
|
||||||
name: &str,
|
name: &str,
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
use std::any::Any;
|
use std::any::Any;
|
||||||
|
|
||||||
use async_trait::async_trait;
|
use async_trait::async_trait;
|
||||||
|
use kube::api::DynamicObject;
|
||||||
use log::debug;
|
use log::debug;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
@@ -76,6 +77,14 @@ pub trait AlertReceiver<S: AlertSender>: std::fmt::Debug + Send + Sync {
|
|||||||
fn name(&self) -> String;
|
fn name(&self) -> String;
|
||||||
fn clone_box(&self) -> Box<dyn AlertReceiver<S>>;
|
fn clone_box(&self) -> Box<dyn AlertReceiver<S>>;
|
||||||
fn as_any(&self) -> &dyn Any;
|
fn as_any(&self) -> &dyn Any;
|
||||||
|
fn as_alertmanager_receiver(&self) -> Result<AlertManagerReceiver, String>;
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct AlertManagerReceiver {
|
||||||
|
pub receiver_config: serde_json::Value,
|
||||||
|
// FIXME we should not leak k8s here. DynamicObject is k8s specific
|
||||||
|
pub additional_ressources: Vec<DynamicObject>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[async_trait]
|
#[async_trait]
|
||||||
|
|||||||
@@ -3,16 +3,20 @@ use std::collections::BTreeMap;
|
|||||||
|
|
||||||
use async_trait::async_trait;
|
use async_trait::async_trait;
|
||||||
use k8s_openapi::api::core::v1::Secret;
|
use k8s_openapi::api::core::v1::Secret;
|
||||||
use kube::api::ObjectMeta;
|
use kube::Resource;
|
||||||
|
use kube::api::{DynamicObject, ObjectMeta};
|
||||||
use log::debug;
|
use log::debug;
|
||||||
use serde::Serialize;
|
use serde::Serialize;
|
||||||
use serde_json::json;
|
use serde_json::json;
|
||||||
use serde_yaml::{Mapping, Value};
|
use serde_yaml::{Mapping, Value};
|
||||||
|
|
||||||
|
use crate::infra::kube::kube_resource_to_dynamic;
|
||||||
use crate::modules::monitoring::kube_prometheus::crd::crd_alertmanager_config::{
|
use crate::modules::monitoring::kube_prometheus::crd::crd_alertmanager_config::{
|
||||||
AlertmanagerConfig, AlertmanagerConfigSpec, CRDPrometheus,
|
AlertmanagerConfig, AlertmanagerConfigSpec, CRDPrometheus,
|
||||||
};
|
};
|
||||||
use crate::modules::monitoring::kube_prometheus::crd::rhob_alertmanager_config::RHOBObservability;
|
use crate::modules::monitoring::kube_prometheus::crd::rhob_alertmanager_config::RHOBObservability;
|
||||||
|
use crate::modules::monitoring::okd::OpenshiftClusterAlertSender;
|
||||||
|
use crate::topology::oberservability::monitoring::AlertManagerReceiver;
|
||||||
use crate::{
|
use crate::{
|
||||||
interpret::{InterpretError, Outcome},
|
interpret::{InterpretError, Outcome},
|
||||||
modules::monitoring::{
|
modules::monitoring::{
|
||||||
@@ -32,10 +36,8 @@ pub struct DiscordWebhook {
|
|||||||
pub url: Url,
|
pub url: Url,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[async_trait]
|
impl DiscordWebhook {
|
||||||
impl AlertReceiver<RHOBObservability> for DiscordWebhook {
|
fn get_receiver_config(&self) -> Result<AlertManagerReceiver, String> {
|
||||||
async fn install(&self, sender: &RHOBObservability) -> Result<Outcome, InterpretError> {
|
|
||||||
let ns = sender.namespace.clone();
|
|
||||||
let secret_name = format!("{}-secret", self.name.clone());
|
let secret_name = format!("{}-secret", self.name.clone());
|
||||||
let webhook_key = format!("{}", self.url.clone());
|
let webhook_key = format!("{}", self.url.clone());
|
||||||
|
|
||||||
@@ -52,26 +54,74 @@ impl AlertReceiver<RHOBObservability> for DiscordWebhook {
|
|||||||
..Default::default()
|
..Default::default()
|
||||||
};
|
};
|
||||||
|
|
||||||
let _ = sender.client.apply(&secret, Some(&ns)).await;
|
Ok(AlertManagerReceiver {
|
||||||
|
additional_ressources: vec![kube_resource_to_dynamic(&secret)?],
|
||||||
|
|
||||||
|
receiver_config: json!({
|
||||||
|
"name": self.name,
|
||||||
|
"discordConfigs": [
|
||||||
|
{
|
||||||
|
"apiURL": {
|
||||||
|
"name": secret_name,
|
||||||
|
"key": "webhook-url",
|
||||||
|
},
|
||||||
|
"title": "{{ template \"discord.default.title\" . }}",
|
||||||
|
"message": "{{ template \"discord.default.message\" . }}"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[async_trait]
|
||||||
|
impl AlertReceiver<OpenshiftClusterAlertSender> for DiscordWebhook {
|
||||||
|
async fn install(
|
||||||
|
&self,
|
||||||
|
sender: &OpenshiftClusterAlertSender,
|
||||||
|
) -> Result<Outcome, InterpretError> {
|
||||||
|
todo!()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn name(&self) -> String {
|
||||||
|
todo!()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn clone_box(&self) -> Box<dyn AlertReceiver<OpenshiftClusterAlertSender>> {
|
||||||
|
Box::new(self.clone())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn as_any(&self) -> &dyn Any {
|
||||||
|
todo!()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn as_alertmanager_receiver(&self) -> Result<AlertManagerReceiver, String> {
|
||||||
|
self.get_receiver_config()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[async_trait]
|
||||||
|
impl AlertReceiver<RHOBObservability> for DiscordWebhook {
|
||||||
|
fn as_alertmanager_receiver(&self) -> Result<AlertManagerReceiver, String> {
|
||||||
|
todo!()
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn install(&self, sender: &RHOBObservability) -> Result<Outcome, InterpretError> {
|
||||||
|
let ns = sender.namespace.clone();
|
||||||
|
|
||||||
|
let config = self.get_receiver_config()?;
|
||||||
|
for resource in config.additional_ressources.iter() {
|
||||||
|
todo!("can I apply a dynamicresource");
|
||||||
|
// sender.client.apply(resource, Some(&ns)).await;
|
||||||
|
}
|
||||||
|
|
||||||
let spec = crate::modules::monitoring::kube_prometheus::crd::rhob_alertmanager_config::AlertmanagerConfigSpec {
|
let spec = crate::modules::monitoring::kube_prometheus::crd::rhob_alertmanager_config::AlertmanagerConfigSpec {
|
||||||
data: json!({
|
data: json!({
|
||||||
"route": {
|
"route": {
|
||||||
"receiver": self.name,
|
"receiver": self.name,
|
||||||
},
|
},
|
||||||
"receivers": [
|
"receivers": [
|
||||||
{
|
config.receiver_config
|
||||||
"name": self.name,
|
|
||||||
"discordConfigs": [
|
|
||||||
{
|
|
||||||
"apiURL": {
|
|
||||||
"name": secret_name,
|
|
||||||
"key": "webhook-url",
|
|
||||||
},
|
|
||||||
"title": "{{ template \"discord.default.title\" . }}",
|
|
||||||
"message": "{{ template \"discord.default.message\" . }}"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
]
|
]
|
||||||
}),
|
}),
|
||||||
};
|
};
|
||||||
@@ -122,6 +172,9 @@ impl AlertReceiver<RHOBObservability> for DiscordWebhook {
|
|||||||
|
|
||||||
#[async_trait]
|
#[async_trait]
|
||||||
impl AlertReceiver<CRDPrometheus> for DiscordWebhook {
|
impl AlertReceiver<CRDPrometheus> for DiscordWebhook {
|
||||||
|
fn as_alertmanager_receiver(&self) -> Result<AlertManagerReceiver, String> {
|
||||||
|
todo!()
|
||||||
|
}
|
||||||
async fn install(&self, sender: &CRDPrometheus) -> Result<Outcome, InterpretError> {
|
async fn install(&self, sender: &CRDPrometheus) -> Result<Outcome, InterpretError> {
|
||||||
let ns = sender.namespace.clone();
|
let ns = sender.namespace.clone();
|
||||||
let secret_name = format!("{}-secret", self.name.clone());
|
let secret_name = format!("{}-secret", self.name.clone());
|
||||||
@@ -200,6 +253,9 @@ impl AlertReceiver<CRDPrometheus> for DiscordWebhook {
|
|||||||
|
|
||||||
#[async_trait]
|
#[async_trait]
|
||||||
impl AlertReceiver<Prometheus> for DiscordWebhook {
|
impl AlertReceiver<Prometheus> for DiscordWebhook {
|
||||||
|
fn as_alertmanager_receiver(&self) -> Result<AlertManagerReceiver, String> {
|
||||||
|
todo!()
|
||||||
|
}
|
||||||
async fn install(&self, sender: &Prometheus) -> Result<Outcome, InterpretError> {
|
async fn install(&self, sender: &Prometheus) -> Result<Outcome, InterpretError> {
|
||||||
sender.install_receiver(self).await
|
sender.install_receiver(self).await
|
||||||
}
|
}
|
||||||
@@ -226,6 +282,9 @@ impl PrometheusReceiver for DiscordWebhook {
|
|||||||
|
|
||||||
#[async_trait]
|
#[async_trait]
|
||||||
impl AlertReceiver<KubePrometheus> for DiscordWebhook {
|
impl AlertReceiver<KubePrometheus> for DiscordWebhook {
|
||||||
|
fn as_alertmanager_receiver(&self) -> Result<AlertManagerReceiver, String> {
|
||||||
|
todo!()
|
||||||
|
}
|
||||||
async fn install(&self, sender: &KubePrometheus) -> Result<Outcome, InterpretError> {
|
async fn install(&self, sender: &KubePrometheus) -> Result<Outcome, InterpretError> {
|
||||||
sender.install_receiver(self).await
|
sender.install_receiver(self).await
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -19,7 +19,7 @@ use crate::{
|
|||||||
},
|
},
|
||||||
prometheus::prometheus::{Prometheus, PrometheusReceiver},
|
prometheus::prometheus::{Prometheus, PrometheusReceiver},
|
||||||
},
|
},
|
||||||
topology::oberservability::monitoring::AlertReceiver,
|
topology::oberservability::monitoring::{AlertManagerReceiver, AlertReceiver},
|
||||||
};
|
};
|
||||||
use harmony_types::net::Url;
|
use harmony_types::net::Url;
|
||||||
|
|
||||||
@@ -31,6 +31,9 @@ pub struct WebhookReceiver {
|
|||||||
|
|
||||||
#[async_trait]
|
#[async_trait]
|
||||||
impl AlertReceiver<RHOBObservability> for WebhookReceiver {
|
impl AlertReceiver<RHOBObservability> for WebhookReceiver {
|
||||||
|
fn as_alertmanager_receiver(&self) -> Result<AlertManagerReceiver, String> {
|
||||||
|
todo!()
|
||||||
|
}
|
||||||
async fn install(&self, sender: &RHOBObservability) -> Result<Outcome, InterpretError> {
|
async fn install(&self, sender: &RHOBObservability) -> Result<Outcome, InterpretError> {
|
||||||
let spec = crate::modules::monitoring::kube_prometheus::crd::rhob_alertmanager_config::AlertmanagerConfigSpec {
|
let spec = crate::modules::monitoring::kube_prometheus::crd::rhob_alertmanager_config::AlertmanagerConfigSpec {
|
||||||
data: json!({
|
data: json!({
|
||||||
@@ -97,6 +100,9 @@ impl AlertReceiver<RHOBObservability> for WebhookReceiver {
|
|||||||
|
|
||||||
#[async_trait]
|
#[async_trait]
|
||||||
impl AlertReceiver<CRDPrometheus> for WebhookReceiver {
|
impl AlertReceiver<CRDPrometheus> for WebhookReceiver {
|
||||||
|
fn as_alertmanager_receiver(&self) -> Result<AlertManagerReceiver, String> {
|
||||||
|
todo!()
|
||||||
|
}
|
||||||
async fn install(&self, sender: &CRDPrometheus) -> Result<Outcome, InterpretError> {
|
async fn install(&self, sender: &CRDPrometheus) -> Result<Outcome, InterpretError> {
|
||||||
let spec = crate::modules::monitoring::kube_prometheus::crd::crd_alertmanager_config::AlertmanagerConfigSpec {
|
let spec = crate::modules::monitoring::kube_prometheus::crd::crd_alertmanager_config::AlertmanagerConfigSpec {
|
||||||
data: json!({
|
data: json!({
|
||||||
@@ -158,6 +164,9 @@ impl AlertReceiver<CRDPrometheus> for WebhookReceiver {
|
|||||||
|
|
||||||
#[async_trait]
|
#[async_trait]
|
||||||
impl AlertReceiver<Prometheus> for WebhookReceiver {
|
impl AlertReceiver<Prometheus> for WebhookReceiver {
|
||||||
|
fn as_alertmanager_receiver(&self) -> Result<AlertManagerReceiver, String> {
|
||||||
|
todo!()
|
||||||
|
}
|
||||||
async fn install(&self, sender: &Prometheus) -> Result<Outcome, InterpretError> {
|
async fn install(&self, sender: &Prometheus) -> Result<Outcome, InterpretError> {
|
||||||
sender.install_receiver(self).await
|
sender.install_receiver(self).await
|
||||||
}
|
}
|
||||||
@@ -184,6 +193,9 @@ impl PrometheusReceiver for WebhookReceiver {
|
|||||||
|
|
||||||
#[async_trait]
|
#[async_trait]
|
||||||
impl AlertReceiver<KubePrometheus> for WebhookReceiver {
|
impl AlertReceiver<KubePrometheus> for WebhookReceiver {
|
||||||
|
fn as_alertmanager_receiver(&self) -> Result<AlertManagerReceiver, String> {
|
||||||
|
todo!()
|
||||||
|
}
|
||||||
async fn install(&self, sender: &KubePrometheus) -> Result<Outcome, InterpretError> {
|
async fn install(&self, sender: &KubePrometheus) -> Result<Outcome, InterpretError> {
|
||||||
sender.install_receiver(self).await
|
sender.install_receiver(self).await
|
||||||
}
|
}
|
||||||
|
|||||||
172
harmony/src/modules/monitoring/okd/cluster_monitoring.rs
Normal file
172
harmony/src/modules/monitoring/okd/cluster_monitoring.rs
Normal file
@@ -0,0 +1,172 @@
|
|||||||
|
use base64::prelude::*;
|
||||||
|
use std::sync::Arc;
|
||||||
|
|
||||||
|
use async_trait::async_trait;
|
||||||
|
use harmony_types::id::Id;
|
||||||
|
use kube::api::DynamicObject;
|
||||||
|
use log::{debug, info, trace};
|
||||||
|
use serde::Serialize;
|
||||||
|
|
||||||
|
use crate::{
|
||||||
|
data::Version,
|
||||||
|
interpret::{Interpret, InterpretError, InterpretName, InterpretStatus, Outcome},
|
||||||
|
inventory::Inventory,
|
||||||
|
modules::{
|
||||||
|
application::Application,
|
||||||
|
monitoring::{
|
||||||
|
grafana::grafana::Grafana,
|
||||||
|
kube_prometheus::crd::crd_alertmanager_config::CRDPrometheus,
|
||||||
|
okd::OpenshiftClusterAlertSender,
|
||||||
|
},
|
||||||
|
prometheus::prometheus::PrometheusMonitoring,
|
||||||
|
},
|
||||||
|
score::Score,
|
||||||
|
topology::{
|
||||||
|
K8sclient, Topology,
|
||||||
|
k8s::K8sClient,
|
||||||
|
oberservability::monitoring::{AlertReceiver, AlertingInterpret, ScrapeTarget},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
impl Clone for Box<dyn AlertReceiver<OpenshiftClusterAlertSender>> {
|
||||||
|
fn clone(&self) -> Self {
|
||||||
|
self.clone_box()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Serialize for Box<dyn AlertReceiver<OpenshiftClusterAlertSender>> {
|
||||||
|
fn serialize<S>(&self, _serializer: S) -> Result<S::Ok, S::Error>
|
||||||
|
where
|
||||||
|
S: serde::Serializer,
|
||||||
|
{
|
||||||
|
todo!()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Serialize)]
|
||||||
|
pub struct OpenshiftClusterAlertScore {
|
||||||
|
pub receivers: Vec<Box<dyn AlertReceiver<OpenshiftClusterAlertSender>>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: Topology + K8sclient> Score<T> for OpenshiftClusterAlertScore {
|
||||||
|
fn name(&self) -> String {
|
||||||
|
"ClusterAlertScore".to_string()
|
||||||
|
}
|
||||||
|
|
||||||
|
#[doc(hidden)]
|
||||||
|
fn create_interpret(&self) -> Box<dyn Interpret<T>> {
|
||||||
|
Box::new(OpenshiftClusterAlertInterpret {
|
||||||
|
receivers: self.receivers.clone(),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct OpenshiftClusterAlertInterpret {
|
||||||
|
receivers: Vec<Box<dyn AlertReceiver<OpenshiftClusterAlertSender>>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[async_trait]
|
||||||
|
impl<T: Topology + K8sclient> Interpret<T> for OpenshiftClusterAlertInterpret {
|
||||||
|
async fn execute(
|
||||||
|
&self,
|
||||||
|
_inventory: &Inventory,
|
||||||
|
topology: &T,
|
||||||
|
) -> Result<Outcome, InterpretError> {
|
||||||
|
let client = topology.k8s_client().await?;
|
||||||
|
|
||||||
|
let secret: DynamicObject = client
|
||||||
|
.get_secret_json_value("alertmanager-main", Some("openshift-monitoring"))
|
||||||
|
.await?;
|
||||||
|
trace!("Got secret {secret:?}");
|
||||||
|
|
||||||
|
let data: serde_json::Value = secret.data;
|
||||||
|
trace!("Alertmanager-main secret data {data:#?}");
|
||||||
|
|
||||||
|
// TODO fix this unwrap, handle the option gracefully
|
||||||
|
let config_b64 = match data.get("data") {
|
||||||
|
Some(data_value) => match data_value.get("alertmanager.yaml") {
|
||||||
|
Some(value) => value.as_str().unwrap_or(""),
|
||||||
|
None => {
|
||||||
|
return Err(InterpretError::new(
|
||||||
|
"Missing 'alertmanager.yaml' in alertmanager-main secret".to_string(),
|
||||||
|
));
|
||||||
|
}
|
||||||
|
},
|
||||||
|
None => {
|
||||||
|
return Err(InterpretError::new(
|
||||||
|
"Missing 'data' field in alertmanager-main secret.".to_string(),
|
||||||
|
));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
trace!("Config base64 {config_b64}");
|
||||||
|
|
||||||
|
let config_bytes = BASE64_STANDARD.decode(config_b64).unwrap_or_default();
|
||||||
|
|
||||||
|
let mut am_config: serde_yaml::Value =
|
||||||
|
serde_yaml::from_str(&String::from_utf8(config_bytes).unwrap_or_default())
|
||||||
|
.unwrap_or_default();
|
||||||
|
|
||||||
|
debug!("Current alertmanager config {am_config:#?}");
|
||||||
|
|
||||||
|
let existing_receivers = if let Some(receivers) = am_config.get_mut("receivers") {
|
||||||
|
match receivers.as_mapping_mut() {
|
||||||
|
Some(recv) => recv,
|
||||||
|
None => {
|
||||||
|
return Err(InterpretError::new(format!(
|
||||||
|
"Expected alertmanager config receivers to be a mapping, got {receivers:?}"
|
||||||
|
)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
&mut serde_yaml::mapping::Mapping::default()
|
||||||
|
};
|
||||||
|
|
||||||
|
trace!("Existing receivers : {existing_receivers:#?}");
|
||||||
|
|
||||||
|
for custom_receiver in &self.receivers {
|
||||||
|
let name = &custom_receiver.name();
|
||||||
|
if let Some(recv) = existing_receivers.get(name) {
|
||||||
|
info!(
|
||||||
|
"AlertManager receiver {name} already exists and will be overwritten : {recv:#?}"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
debug!(
|
||||||
|
"Custom receiver YAML output: {:?}",
|
||||||
|
custom_receiver.as_alertmanager_receiver()
|
||||||
|
);
|
||||||
|
|
||||||
|
let json_value = custom_receiver.as_alertmanager_receiver()?.receiver_config;
|
||||||
|
let yaml_string = serde_json::to_string(&json_value).map_err(|e| {
|
||||||
|
InterpretError::new(format!("Failed to serialize receiver config: {}", e))
|
||||||
|
})?;
|
||||||
|
|
||||||
|
let yaml_value: serde_yaml::Value =
|
||||||
|
serde_yaml::from_str(&yaml_string).map_err(|e| {
|
||||||
|
InterpretError::new(format!("Failed to parse receiver config as YAML: {}", e))
|
||||||
|
})?;
|
||||||
|
|
||||||
|
existing_receivers.insert(serde_yaml::Value::from(name.as_str()), yaml_value);
|
||||||
|
}
|
||||||
|
|
||||||
|
debug!("Current alertmanager config {am_config:#?}");
|
||||||
|
|
||||||
|
Ok(Outcome::success(todo!("whats up")))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_name(&self) -> InterpretName {
|
||||||
|
InterpretName::Custom("OpenshiftClusterAlertInterpret")
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_version(&self) -> Version {
|
||||||
|
todo!()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_status(&self) -> InterpretStatus {
|
||||||
|
todo!()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_children(&self) -> Vec<Id> {
|
||||||
|
todo!()
|
||||||
|
}
|
||||||
|
}
|
||||||
90
harmony/src/modules/monitoring/okd/config.rs
Normal file
90
harmony/src/modules/monitoring/okd/config.rs
Normal file
@@ -0,0 +1,90 @@
|
|||||||
|
use std::{collections::BTreeMap, sync::Arc};
|
||||||
|
|
||||||
|
use crate::{
|
||||||
|
interpret::{InterpretError, Outcome},
|
||||||
|
topology::k8s::K8sClient,
|
||||||
|
};
|
||||||
|
use k8s_openapi::api::core::v1::ConfigMap;
|
||||||
|
use kube::api::ObjectMeta;
|
||||||
|
|
||||||
|
pub(crate) struct Config;
|
||||||
|
|
||||||
|
impl Config {
|
||||||
|
pub async fn create_cluster_monitoring_config_cm(
|
||||||
|
client: &Arc<K8sClient>,
|
||||||
|
) -> Result<Outcome, InterpretError> {
|
||||||
|
let mut data = BTreeMap::new();
|
||||||
|
data.insert(
|
||||||
|
"config.yaml".to_string(),
|
||||||
|
r#"
|
||||||
|
enableUserWorkload: true
|
||||||
|
alertmanagerMain:
|
||||||
|
enableUserAlertmanagerConfig: true
|
||||||
|
"#
|
||||||
|
.to_string(),
|
||||||
|
);
|
||||||
|
|
||||||
|
let cm = ConfigMap {
|
||||||
|
metadata: ObjectMeta {
|
||||||
|
name: Some("cluster-monitoring-config".to_string()),
|
||||||
|
namespace: Some("openshift-monitoring".to_string()),
|
||||||
|
..Default::default()
|
||||||
|
},
|
||||||
|
data: Some(data),
|
||||||
|
..Default::default()
|
||||||
|
};
|
||||||
|
client.apply(&cm, Some("openshift-monitoring")).await?;
|
||||||
|
|
||||||
|
Ok(Outcome::success(
|
||||||
|
"updated cluster-monitoring-config-map".to_string(),
|
||||||
|
))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn create_user_workload_monitoring_config_cm(
|
||||||
|
client: &Arc<K8sClient>,
|
||||||
|
) -> Result<Outcome, InterpretError> {
|
||||||
|
let mut data = BTreeMap::new();
|
||||||
|
data.insert(
|
||||||
|
"config.yaml".to_string(),
|
||||||
|
r#"
|
||||||
|
alertmanager:
|
||||||
|
enabled: true
|
||||||
|
enableAlertmanagerConfig: true
|
||||||
|
"#
|
||||||
|
.to_string(),
|
||||||
|
);
|
||||||
|
let cm = ConfigMap {
|
||||||
|
metadata: ObjectMeta {
|
||||||
|
name: Some("user-workload-monitoring-config".to_string()),
|
||||||
|
namespace: Some("openshift-user-workload-monitoring".to_string()),
|
||||||
|
..Default::default()
|
||||||
|
},
|
||||||
|
data: Some(data),
|
||||||
|
..Default::default()
|
||||||
|
};
|
||||||
|
client
|
||||||
|
.apply(&cm, Some("openshift-user-workload-monitoring"))
|
||||||
|
.await?;
|
||||||
|
|
||||||
|
Ok(Outcome::success(
|
||||||
|
"updated openshift-user-monitoring-config-map".to_string(),
|
||||||
|
))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn verify_user_workload(client: &Arc<K8sClient>) -> Result<Outcome, InterpretError> {
|
||||||
|
let namespace = "openshift-user-workload-monitoring";
|
||||||
|
let alertmanager_name = "alertmanager-user-workload-0";
|
||||||
|
let prometheus_name = "prometheus-user-workload-0";
|
||||||
|
client
|
||||||
|
.wait_for_pod_ready(alertmanager_name, Some(namespace))
|
||||||
|
.await?;
|
||||||
|
client
|
||||||
|
.wait_for_pod_ready(prometheus_name, Some(namespace))
|
||||||
|
.await?;
|
||||||
|
|
||||||
|
Ok(Outcome::success(format!(
|
||||||
|
"pods: {}, {} ready in ns: {}",
|
||||||
|
alertmanager_name, prometheus_name, namespace
|
||||||
|
)))
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,16 +1,13 @@
|
|||||||
use std::{collections::BTreeMap, sync::Arc};
|
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
data::Version,
|
data::Version,
|
||||||
interpret::{Interpret, InterpretError, InterpretName, InterpretStatus, Outcome},
|
interpret::{Interpret, InterpretError, InterpretName, InterpretStatus, Outcome},
|
||||||
inventory::Inventory,
|
inventory::Inventory,
|
||||||
|
modules::monitoring::okd::config::Config,
|
||||||
score::Score,
|
score::Score,
|
||||||
topology::{K8sclient, Topology, k8s::K8sClient},
|
topology::{K8sclient, Topology},
|
||||||
};
|
};
|
||||||
use async_trait::async_trait;
|
use async_trait::async_trait;
|
||||||
use harmony_types::id::Id;
|
use harmony_types::id::Id;
|
||||||
use k8s_openapi::api::core::v1::ConfigMap;
|
|
||||||
use kube::api::ObjectMeta;
|
|
||||||
use serde::Serialize;
|
use serde::Serialize;
|
||||||
|
|
||||||
#[derive(Clone, Debug, Serialize)]
|
#[derive(Clone, Debug, Serialize)]
|
||||||
@@ -37,10 +34,9 @@ impl<T: Topology + K8sclient> Interpret<T> for OpenshiftUserWorkloadMonitoringIn
|
|||||||
topology: &T,
|
topology: &T,
|
||||||
) -> Result<Outcome, InterpretError> {
|
) -> Result<Outcome, InterpretError> {
|
||||||
let client = topology.k8s_client().await.unwrap();
|
let client = topology.k8s_client().await.unwrap();
|
||||||
self.update_cluster_monitoring_config_cm(&client).await?;
|
Config::create_cluster_monitoring_config_cm(&client).await?;
|
||||||
self.update_user_workload_monitoring_config_cm(&client)
|
Config::create_user_workload_monitoring_config_cm(&client).await?;
|
||||||
.await?;
|
Config::verify_user_workload(&client).await?;
|
||||||
self.verify_user_workload(&client).await?;
|
|
||||||
Ok(Outcome::success(
|
Ok(Outcome::success(
|
||||||
"successfully enabled user-workload-monitoring".to_string(),
|
"successfully enabled user-workload-monitoring".to_string(),
|
||||||
))
|
))
|
||||||
@@ -62,88 +58,3 @@ impl<T: Topology + K8sclient> Interpret<T> for OpenshiftUserWorkloadMonitoringIn
|
|||||||
todo!()
|
todo!()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl OpenshiftUserWorkloadMonitoringInterpret {
|
|
||||||
pub async fn update_cluster_monitoring_config_cm(
|
|
||||||
&self,
|
|
||||||
client: &Arc<K8sClient>,
|
|
||||||
) -> Result<Outcome, InterpretError> {
|
|
||||||
let mut data = BTreeMap::new();
|
|
||||||
data.insert(
|
|
||||||
"config.yaml".to_string(),
|
|
||||||
r#"
|
|
||||||
enableUserWorkload: true
|
|
||||||
alertmanagerMain:
|
|
||||||
enableUserAlertmanagerConfig: true
|
|
||||||
"#
|
|
||||||
.to_string(),
|
|
||||||
);
|
|
||||||
|
|
||||||
let cm = ConfigMap {
|
|
||||||
metadata: ObjectMeta {
|
|
||||||
name: Some("cluster-monitoring-config".to_string()),
|
|
||||||
namespace: Some("openshift-monitoring".to_string()),
|
|
||||||
..Default::default()
|
|
||||||
},
|
|
||||||
data: Some(data),
|
|
||||||
..Default::default()
|
|
||||||
};
|
|
||||||
client.apply(&cm, Some("openshift-monitoring")).await?;
|
|
||||||
|
|
||||||
Ok(Outcome::success(
|
|
||||||
"updated cluster-monitoring-config-map".to_string(),
|
|
||||||
))
|
|
||||||
}
|
|
||||||
|
|
||||||
pub async fn update_user_workload_monitoring_config_cm(
|
|
||||||
&self,
|
|
||||||
client: &Arc<K8sClient>,
|
|
||||||
) -> Result<Outcome, InterpretError> {
|
|
||||||
let mut data = BTreeMap::new();
|
|
||||||
data.insert(
|
|
||||||
"config.yaml".to_string(),
|
|
||||||
r#"
|
|
||||||
alertmanager:
|
|
||||||
enabled: true
|
|
||||||
enableAlertmanagerConfig: true
|
|
||||||
"#
|
|
||||||
.to_string(),
|
|
||||||
);
|
|
||||||
let cm = ConfigMap {
|
|
||||||
metadata: ObjectMeta {
|
|
||||||
name: Some("user-workload-monitoring-config".to_string()),
|
|
||||||
namespace: Some("openshift-user-workload-monitoring".to_string()),
|
|
||||||
..Default::default()
|
|
||||||
},
|
|
||||||
data: Some(data),
|
|
||||||
..Default::default()
|
|
||||||
};
|
|
||||||
client
|
|
||||||
.apply(&cm, Some("openshift-user-workload-monitoring"))
|
|
||||||
.await?;
|
|
||||||
|
|
||||||
Ok(Outcome::success(
|
|
||||||
"updated openshift-user-monitoring-config-map".to_string(),
|
|
||||||
))
|
|
||||||
}
|
|
||||||
|
|
||||||
pub async fn verify_user_workload(
|
|
||||||
&self,
|
|
||||||
client: &Arc<K8sClient>,
|
|
||||||
) -> Result<Outcome, InterpretError> {
|
|
||||||
let namespace = "openshift-user-workload-monitoring";
|
|
||||||
let alertmanager_name = "alertmanager-user-workload-0";
|
|
||||||
let prometheus_name = "prometheus-user-workload-0";
|
|
||||||
client
|
|
||||||
.wait_for_pod_ready(alertmanager_name, Some(namespace))
|
|
||||||
.await?;
|
|
||||||
client
|
|
||||||
.wait_for_pod_ready(prometheus_name, Some(namespace))
|
|
||||||
.await?;
|
|
||||||
|
|
||||||
Ok(Outcome::success(format!(
|
|
||||||
"pods: {}, {} ready in ns: {}",
|
|
||||||
alertmanager_name, prometheus_name, namespace
|
|
||||||
)))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -1 +1,14 @@
|
|||||||
|
use crate::topology::oberservability::monitoring::AlertSender;
|
||||||
|
|
||||||
|
pub mod cluster_monitoring;
|
||||||
|
pub(crate) mod config;
|
||||||
pub mod enable_user_workload;
|
pub mod enable_user_workload;
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct OpenshiftClusterAlertSender;
|
||||||
|
|
||||||
|
impl AlertSender for OpenshiftClusterAlertSender {
|
||||||
|
fn name(&self) -> String {
|
||||||
|
"OpenshiftClusterAlertSender".to_string()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user