feat: add monitoring system and alerting
Some checks failed
Run Check Script / check (push) Failing after 45s
Run Check Script / check (pull_request) Failing after 44s

- Implemented a basic monitoring system with alerting capabilities.
- Introduced `MonitoringAlertingScore` and related traits (`MonitoringSystem`, `ScrapeTarget`, `AlertRule`).
- Added `MonitoringAlertingInterpret` to handle the execution of monitoring configurations.
- Defined interfaces for Prometheus integration (PrometheusReceiver, PrometheusRule, PrometheusScrapeTarget).
- Introduced `CloneBox` trait to enable cloning boxed trait objects.
- Refactored Prometheus related traits to require `Debug` implementation.
- Implemented basic placeholder logic for Prometheus configuration and alerting.
- Updated existing traits to include `Send` and `Sync` where appropriate.
This commit is contained in:
Jean-Gabriel Gill-Couture 2025-06-16 17:01:13 -04:00
parent f6c146b14b
commit 9412c0529a
8 changed files with 82 additions and 89 deletions

9
Cargo.lock generated
View File

@ -2387,6 +2387,15 @@ dependencies = [
"windows-sys 0.52.0",
]
[[package]]
name = "monitoring"
version = "0.1.0"
dependencies = [
"harmony",
"harmony_cli",
"tokio",
]
[[package]]
name = "native-tls"
version = "0.2.14"

View File

@ -2,7 +2,7 @@ use harmony::{
data::Version,
inventory::Inventory,
maestro::Maestro,
modules::lamp::{LAMPConfig, LAMPScore},
modules::{lamp::{LAMPConfig, LAMPScore}, monitoring::monitoring_alerting::MonitoringAlertingScore},
topology::{K8sAnywhereTopology, Url},
};
@ -29,6 +29,8 @@ async fn main() {
},
};
let monitoring = MonitoringAlertingScore { alert_channel_configs: todo!() };
// You can choose the type of Topology you want, we suggest starting with the
// K8sAnywhereTopology as it is the most automatic one that enables you to easily deploy
// locally, to development environment from a CI, to staging, and to production with settings

View File

@ -1,31 +1,33 @@
//#[derive(Debug, Clone)]
//pub struct DiscordWebhookAlertChannel {
// pub webhook_url: Url,
// pub name: String,
// pub send_resolved_notifications: bool,
//}
use serde::Serialize;
use crate::{interpret::Interpret, modules::monitoring::{alert_receiver::{AlertReceiver, AlertReceiverInterpret}, alert_rule::AlertRule, prometheus::{Prometheus, PrometheusReceiver, PrometheusRule, PrometheusScrapeTarget}, scrape_target::ScrapeTarget}, score::Score, topology::Topology};
use crate::{
interpret::Interpret,
modules::monitoring::{
alert_receiver::{AlertReceiver, AlertReceiverInterpret},
prometheus::{Prometheus, PrometheusReceiver},
},
score::Score,
topology::Topology,
};
#[derive(Debug, Clone, Serialize)]
struct DiscordWebhook;
impl DiscordWebhook {
fn as_prometheus_receiver(&self) -> PrometheusReceiver {
PrometheusReceiver {}
}
}
impl AlertReceiver for DiscordWebhook {
type Sender = Prometheus;
fn install(&self, sender: &Self::Sender) -> Result<(), String> {
sender.configure_receiver(self.as_prometheus_receiver())
sender.configure_receiver(Box::new(self))
}
}
impl PrometheusReceiver for DiscordWebhook {
fn get_prometheus_config(
&self,
) -> crate::modules::monitoring::prometheus::PrometheusReceiverConfig {
todo!()
}
}
#[derive(Debug, Clone, Serialize)]
pub struct DiscordWebhookScore {
@ -36,41 +38,10 @@ impl<T: Topology> Score<T> for DiscordWebhookScore {
fn create_interpret(&self) -> Box<dyn Interpret<T>> {
Box::new(AlertReceiverInterpret {
receiver: Box::new(self.config.clone()),
})
})
}
fn name(&self) -> String {
todo!()
}
}
impl ScrapeTarget for DiscordWebhook {
type Sender = Prometheus;
fn install(&self, sender: &Self::Sender) {
sender.configure_scrape_target(self.as_prometheus_scrape_target())
}
}
impl DiscordWebhook {
fn as_prometheus_scrape_target(&self) -> PrometheusScrapeTarget {
PrometheusScrapeTarget { definition: todo!() }
}
}
impl AlertRule for DiscordWebhook {
type Sender = Prometheus;
fn install(&self, sender: &Self::Sender) {
sender.configure_rule(self.as_prometheus_rule())
}
}
impl DiscordWebhook {
fn as_prometheus_rule(&self) -> PrometheusRule {
PrometheusRule { definition: todo!() }
}
}

View File

@ -9,9 +9,9 @@ use crate::{
topology::Topology,
};
use super::prometheus::AlertSender;
use super::{monitoring_alerting::CloneBox, prometheus::AlertSender};
pub trait AlertReceiver: Debug + Send + Sync {
pub trait AlertReceiver: Debug + Send + Sync + CloneBox {
type Sender: AlertSender;
fn install(&self, sender: &Self::Sender) -> Result<(), String>;
@ -23,16 +23,16 @@ struct AlertReceiverConfig<S: AlertSender> {
}
#[derive(Debug)]
pub struct AlertReceiverInterpret {
pub receiver: Box<dyn AlertReceiver<Sender = dyn AlertSender>>,
pub struct AlertReceiverInterpret<S: AlertSender> {
pub receiver: Box<dyn AlertReceiver<Sender = S>>,
}
#[async_trait]
impl<T: Topology> Interpret<T> for AlertReceiverInterpret {
impl<T: Topology, S: AlertSender> Interpret<T> for AlertReceiverInterpret<S> {
async fn execute(
&self,
inventory: &Inventory,
topology: &T,
_inventory: &Inventory,
_topology: &T,
) -> Result<Outcome, InterpretError> {
todo!()
}

View File

@ -2,7 +2,7 @@ use std::fmt::Debug;
use super::prometheus::AlertSender;
pub trait AlertRule: Debug {
pub trait AlertRule: Debug + Send + Sync {
type Sender: AlertSender;
fn install(&self, sender: &Self::Sender);

View File

@ -1,35 +1,41 @@
use std::sync::Arc;
use async_trait::async_trait;
use serde::Serialize;
use serde_value::Value;
use crate::topology::oberservability::monitoring::{AlertChannelConfig, Monitor};
use crate::{
data::{Id, Version},
interpret::{Interpret, InterpretError, InterpretName, InterpretStatus, Outcome},
inventory::Inventory,
score::Score,
topology::{HelmCommand, Topology},
topology::Topology,
};
use super::alert_receiver::AlertReceiver;
use super::alert_rule::AlertRule;
use super::scrape_target::ScrapeTarget;
pub trait MonitoringSystem: std::fmt::Debug + Clone + Serialize + 'static {}
pub trait MonitoringSystem {}
#[derive(Debug, Clone, Serialize)]
#[derive(Debug, Clone)]
pub struct MonitoringAlertingScore<M: MonitoringSystem> {
alert_receivers: Vec<Box<dyn AlertReceiver<Sender = M>>>,
alert_rules: Vec<Box<dyn AlertRule<Sender = M>>>,
scrape_targets: Vec<Box<dyn ScrapeTarget<Sender = M>>>,
alert_receivers: Arc<Vec<Box<dyn AlertReceiver<Sender = M>>>>,
alert_rules: Arc<Vec<Box<dyn AlertRule<Sender = M>>>>,
scrape_targets: Arc<Vec<Box<dyn ScrapeTarget<Sender = M>>>>,
}
impl<T: Topology, M: MonitoringSystem> Score<T> for MonitoringAlertingScore<M> {
impl <M: MonitoringSystem> Serialize for MonitoringAlertingScore<M> {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: serde::Serializer {
todo!()
}
}
impl<T: Topology, M: MonitoringSystem + Serialize> Score<T> for MonitoringAlertingScore<M> {
fn create_interpret(&self) -> Box<dyn Interpret<T>> {
Box::new(MonitoringAlertingInterpret {
score: self.clone(),
score: Arc::new(self.clone()),
})
}
@ -39,24 +45,18 @@ impl<T: Topology, M: MonitoringSystem> Score<T> for MonitoringAlertingScore<M> {
}
#[derive(Debug)]
struct MonitoringAlertingInterpret {
score: MonitoringAlertingScore,
struct MonitoringAlertingInterpret<M: MonitoringSystem> {
score: Arc<MonitoringAlertingScore<M>>,
}
#[async_trait]
impl<T: Topology> Interpret<T> for MonitoringAlertingInterpret {
impl<M: MonitoringSystem, T: Topology> Interpret<T> for MonitoringAlertingInterpret<M> {
async fn execute(
&self,
inventory: &Inventory,
topology: &T,
) -> Result<Outcome, InterpretError> {
topology
.provision_monitor(
inventory,
topology,
self.score.alert_channel_configs.clone(),
)
.await
todo!()
}
fn get_name(&self) -> InterpretName {
@ -75,3 +75,16 @@ impl<T: Topology> Interpret<T> for MonitoringAlertingInterpret {
todo!()
}
}
pub trait CloneBox {
fn clone_box(&self) -> Box<dyn CloneBox>;
}
impl<C> CloneBox for C
where
C: Clone + 'static,
{
fn clone_box(&self) -> Box<dyn CloneBox> {
Box::new(self.clone())
}
}

View File

@ -6,24 +6,23 @@ use crate::{
data::{Id, Version},
interpret::{Interpret, InterpretError, InterpretName, InterpretStatus, Outcome},
inventory::Inventory,
score::Score,
topology::Topology,
};
use std::fmt::Debug;
pub trait AlertSender {}
pub trait AlertSender: std::fmt::Debug {}
#[derive(Debug, Clone, Serialize)]
pub struct Prometheus {}
impl Prometheus {
pub fn configure_receiver(&self, receiver: PrometheusReceiver) -> Result<(), String> {
pub fn configure_receiver(&self, _receiver: Box<&dyn PrometheusReceiver>) -> Result<(), String> {
todo!()
}
pub fn configure_rule(&self, rule: PrometheusRule) {
pub fn configure_rule(&self, _rule: Box<&dyn PrometheusRule>) {
todo!()
}
pub fn configure_scrape_target(&self, target: PrometheusScrapeTarget) {
pub fn configure_scrape_target(&self, _target: Box<&dyn PrometheusScrapeTarget>) {
todo!()
}
}
@ -36,7 +35,7 @@ pub trait PrometheusCapability {
impl AlertSender for Prometheus {}
pub trait PrometheusReceiver{
pub trait PrometheusReceiver {
fn get_prometheus_config(&self) -> PrometheusReceiverConfig;
}
@ -45,7 +44,7 @@ pub struct PrometheusReceiverConfig {
// config
}
pub trait PrometheusRule{
pub trait PrometheusRule {
fn get_prometheus_config(&self) -> PrometheusRuleConfig;
}
@ -74,8 +73,8 @@ pub struct PrometheusMonitoringInterpret {}
impl<T: Topology + PrometheusCapability> Interpret<T> for PrometheusMonitoringInterpret {
async fn execute(
&self,
inventory: &Inventory,
topology: &T,
_inventory: &Inventory,
_topology: &T,
) -> Result<Outcome, InterpretError> {
todo!()
}
@ -96,4 +95,3 @@ impl<T: Topology + PrometheusCapability> Interpret<T> for PrometheusMonitoringIn
todo!()
}
}

View File

@ -2,7 +2,7 @@ use std::fmt::Debug;
use super::prometheus::AlertSender;
pub trait ScrapeTarget: Debug {
pub trait ScrapeTarget: Debug + Send + Sync {
type Sender: AlertSender;
fn install(&self, sender: &Self::Sender);