Merge remote-tracking branch 'origin/master' into TenantManager_impl_k8s_anywhere
All checks were successful
Run Check Script / check (push) Successful in 1m48s
Run Check Script / check (pull_request) Successful in 1m47s

This commit is contained in:
Jean-Gabriel Gill-Couture 2025-06-04 16:14:21 -04:00
commit e17ac1af83
13 changed files with 197 additions and 43 deletions

1
Cargo.lock generated
View File

@ -1409,6 +1409,7 @@ dependencies = [
"derive-new", "derive-new",
"directories", "directories",
"dockerfile_builder", "dockerfile_builder",
"dyn-clone",
"email_address", "email_address",
"env_logger", "env_logger",
"fqdn", "fqdn",

View File

@ -49,3 +49,4 @@ fqdn = { version = "0.4.6", features = [
"serde", "serde",
] } ] }
temp-dir = "0.1.14" temp-dir = "0.1.14"
dyn-clone = "1.0.19"

View File

@ -10,3 +10,9 @@ impl Id {
Self { value } Self { value }
} }
} }
impl std::fmt::Display for Id {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.write_str(&self.value)
}
}

View File

@ -20,6 +20,7 @@ pub enum InterpretName {
Panic, Panic,
OPNSense, OPNSense,
K3dInstallation, K3dInstallation,
TenantInterpret,
} }
impl std::fmt::Display for InterpretName { impl std::fmt::Display for InterpretName {
@ -35,6 +36,7 @@ impl std::fmt::Display for InterpretName {
InterpretName::Panic => f.write_str("Panic"), InterpretName::Panic => f.write_str("Panic"),
InterpretName::OPNSense => f.write_str("OPNSense"), InterpretName::OPNSense => f.write_str("OPNSense"),
InterpretName::K3dInstallation => f.write_str("K3dInstallation"), InterpretName::K3dInstallation => f.write_str("K3dInstallation"),
InterpretName::TenantInterpret => f.write_str("Tenant"),
} }
} }
} }

View File

@ -3,6 +3,7 @@ mod host_binding;
mod http; mod http;
mod k8s_anywhere; mod k8s_anywhere;
mod localhost; mod localhost;
pub mod oberservability;
pub mod tenant; pub mod tenant;
pub use k8s_anywhere::*; pub use k8s_anywhere::*;
pub use localhost::*; pub use localhost::*;

View File

@ -0,0 +1 @@
pub mod monitoring;

View File

@ -0,0 +1,31 @@
use async_trait::async_trait;
use std::fmt::Debug;
use url::Url;
use crate::interpret::InterpretError;
use crate::{interpret::Outcome, topology::Topology};
/// Represents an entity responsible for collecting and organizing observability data
/// from various telemetry sources
/// A `Monitor` abstracts the logic required to scrape, aggregate, and structure
/// monitoring data, enabling consistent processing regardless of the underlying data source.
#[async_trait]
pub trait Monitor<T: Topology>: Debug + Send + Sync {
async fn deploy_monitor(
&self,
topology: &T,
alert_receivers: Vec<AlertReceiver>,
) -> Result<Outcome, InterpretError>;
async fn delete_monitor(
&self,
topolgy: &T,
alert_receivers: Vec<AlertReceiver>,
) -> Result<Outcome, InterpretError>;
}
pub struct AlertReceiver {
pub receiver_id: String,
}

View File

@ -12,4 +12,5 @@ pub mod load_balancer;
pub mod monitoring; pub mod monitoring;
pub mod okd; pub mod okd;
pub mod opnsense; pub mod opnsense;
pub mod tenant;
pub mod tftp; pub mod tftp;

View File

@ -1,30 +1,25 @@
use std::str::FromStr; use std::str::FromStr;
use non_blank_string_rs::NonBlankString; use non_blank_string_rs::NonBlankString;
use url::Url;
use crate::modules::helm::chart::HelmChartScore; use crate::modules::helm::chart::HelmChartScore;
use super::{config::KubePrometheusConfig, monitoring_alerting::AlertChannel}; pub fn discord_alert_manager_score(
webhook_url: Url,
fn get_discord_alert_manager_score(config: &KubePrometheusConfig) -> Option<HelmChartScore> { namespace: String,
let (url, name) = config.alert_channel.iter().find_map(|channel| { name: String,
if let AlertChannel::Discord { webhook_url, name } = channel { ) -> HelmChartScore {
Some((webhook_url, name))
} else {
None
}
})?;
let values = format!( let values = format!(
r#" r#"
environment: environment:
- name: "DISCORD_WEBHOOK" - name: "DISCORD_WEBHOOK"
value: "{url}" value: "{webhook_url}"
"#, "#,
); );
Some(HelmChartScore { HelmChartScore {
namespace: Some(NonBlankString::from_str(&config.namespace).unwrap()), namespace: Some(NonBlankString::from_str(&namespace).unwrap()),
release_name: NonBlankString::from_str(&name).unwrap(), release_name: NonBlankString::from_str(&name).unwrap(),
chart_name: NonBlankString::from_str( chart_name: NonBlankString::from_str(
"oci://hub.nationtech.io/library/alertmanager-discord", "oci://hub.nationtech.io/library/alertmanager-discord",
@ -36,13 +31,5 @@ environment:
create_namespace: true, create_namespace: true,
install_only: true, install_only: true,
repository: None, repository: None,
})
}
pub fn discord_alert_manager_score(config: &KubePrometheusConfig) -> HelmChartScore {
if let Some(chart) = get_discord_alert_manager_score(config) {
chart
} else {
panic!("Expected discord alert manager helm chart");
} }
} }

View File

@ -0,0 +1,55 @@
use async_trait::async_trait;
use serde_json::Value;
use url::Url;
use crate::{
interpret::{InterpretError, Outcome},
topology::K8sAnywhereTopology,
};
#[derive(Debug, Clone)]
pub struct DiscordWebhookConfig {
pub webhook_url: Url,
pub name: String,
pub send_resolved_notifications: bool,
}
pub trait DiscordWebhookReceiver {
fn deploy_discord_webhook_receiver(
&self,
_notification_adapter_id: &str,
) -> Result<Outcome, InterpretError>;
fn delete_discord_webhook_receiver(
&self,
_notification_adapter_id: &str,
) -> Result<Outcome, InterpretError>;
}
// trait used to generate alert manager config values impl<T: Topology + AlertManagerConfig> Monitor for KubePrometheus
pub trait AlertManagerConfig<T> {
fn get_alert_manager_config(&self) -> Result<Value, InterpretError>;
}
#[async_trait]
impl<T: DiscordWebhookReceiver> AlertManagerConfig<T> for DiscordWebhookConfig {
fn get_alert_manager_config(&self) -> Result<Value, InterpretError> {
todo!()
}
}
#[async_trait]
impl DiscordWebhookReceiver for K8sAnywhereTopology {
fn deploy_discord_webhook_receiver(
&self,
_notification_adapter_id: &str,
) -> Result<Outcome, InterpretError> {
todo!()
}
fn delete_discord_webhook_receiver(
&self,
_notification_adapter_id: &str,
) -> Result<Outcome, InterpretError> {
todo!()
}
}

View File

@ -1,4 +1,5 @@
mod config; mod config;
mod discord_alert_manager; mod discord_alert_manager;
pub mod discord_webhook_sender;
mod kube_prometheus; mod kube_prometheus;
pub mod monitoring_alerting; pub mod monitoring_alerting;

View File

@ -96,28 +96,28 @@ impl MonitoringAlertingStackInterpret {
topology: &T, topology: &T,
config: &KubePrometheusConfig, config: &KubePrometheusConfig,
) -> Result<Outcome, InterpretError> { ) -> Result<Outcome, InterpretError> {
let mut outcomes = vec![]; //let mut outcomes = vec![];
for channel in &self.score.alert_channel { //for channel in &self.score.alert_channel {
let outcome = match channel { // let outcome = match channel {
AlertChannel::Discord { .. } => { // AlertChannel::Discord { .. } => {
discord_alert_manager_score(config) // discord_alert_manager_score(config)
.create_interpret() // .create_interpret()
.execute(inventory, topology) // .execute(inventory, topology)
.await // .await
} // }
AlertChannel::Slack { .. } => Ok(Outcome::success( // AlertChannel::Slack { .. } => Ok(Outcome::success(
"No extra configs for slack alerting".to_string(), // "No extra configs for slack alerting".to_string(),
)), // )),
AlertChannel::Smpt { .. } => { // AlertChannel::Smpt { .. } => {
todo!() // todo!()
} // }
}; // };
outcomes.push(outcome); // outcomes.push(outcome);
} //}
for result in outcomes { //for result in outcomes {
result?; // result?;
} //}
Ok(Outcome::success("All alert channels deployed".to_string())) Ok(Outcome::success("All alert channels deployed".to_string()))
} }

View File

@ -0,0 +1,67 @@
use async_trait::async_trait;
use serde::Serialize;
use crate::{
data::{Id, Version},
interpret::{Interpret, InterpretError, InterpretName, InterpretStatus, Outcome},
inventory::Inventory,
score::Score,
topology::{
Topology,
tenant::{TenantConfig, TenantManager},
},
};
#[derive(Debug, Serialize, Clone)]
pub struct TenantScore {
config: TenantConfig,
}
impl<T: Topology + TenantManager> Score<T> for TenantScore {
fn create_interpret(&self) -> Box<dyn crate::interpret::Interpret<T>> {
Box::new(TenantInterpret {
tenant_config: self.config.clone(),
})
}
fn name(&self) -> String {
format!("{} TenantScore", self.config.name)
}
}
#[derive(Debug)]
pub struct TenantInterpret {
tenant_config: TenantConfig,
}
#[async_trait]
impl<T: Topology + TenantManager> Interpret<T> for TenantInterpret {
async fn execute(
&self,
_inventory: &Inventory,
topology: &T,
) -> Result<Outcome, InterpretError> {
topology.provision_tenant(&self.tenant_config).await?;
Ok(Outcome::success(format!(
"Successfully provisioned tenant {} with id {}",
self.tenant_config.name, self.tenant_config.id
)))
}
fn get_name(&self) -> InterpretName {
InterpretName::TenantInterpret
}
fn get_version(&self) -> Version {
todo!()
}
fn get_status(&self) -> InterpretStatus {
todo!()
}
fn get_children(&self) -> Vec<Id> {
todo!()
}
}