diff --git a/Cargo.lock b/Cargo.lock index 63c8897..7c721b1 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1409,6 +1409,7 @@ dependencies = [ "derive-new", "directories", "dockerfile_builder", + "dyn-clone", "email_address", "env_logger", "fqdn", diff --git a/harmony/Cargo.toml b/harmony/Cargo.toml index 5bc88b1..fcf69cf 100644 --- a/harmony/Cargo.toml +++ b/harmony/Cargo.toml @@ -49,3 +49,4 @@ fqdn = { version = "0.4.6", features = [ "serde", ] } temp-dir = "0.1.14" +dyn-clone = "1.0.19" diff --git a/harmony/src/domain/data/id.rs b/harmony/src/domain/data/id.rs index a710721..e215eb4 100644 --- a/harmony/src/domain/data/id.rs +++ b/harmony/src/domain/data/id.rs @@ -10,3 +10,9 @@ impl Id { Self { value } } } + +impl std::fmt::Display for Id { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.write_str(&self.value) + } +} diff --git a/harmony/src/domain/interpret/mod.rs b/harmony/src/domain/interpret/mod.rs index d0e00d6..5e928cb 100644 --- a/harmony/src/domain/interpret/mod.rs +++ b/harmony/src/domain/interpret/mod.rs @@ -20,6 +20,7 @@ pub enum InterpretName { Panic, OPNSense, K3dInstallation, + TenantInterpret, } impl std::fmt::Display for InterpretName { @@ -35,6 +36,7 @@ impl std::fmt::Display for InterpretName { InterpretName::Panic => f.write_str("Panic"), InterpretName::OPNSense => f.write_str("OPNSense"), InterpretName::K3dInstallation => f.write_str("K3dInstallation"), + InterpretName::TenantInterpret => f.write_str("Tenant"), } } } diff --git a/harmony/src/domain/topology/mod.rs b/harmony/src/domain/topology/mod.rs index abf317d..faa7fee 100644 --- a/harmony/src/domain/topology/mod.rs +++ b/harmony/src/domain/topology/mod.rs @@ -3,6 +3,7 @@ mod host_binding; mod http; mod k8s_anywhere; mod localhost; +pub mod oberservability; pub mod tenant; pub use k8s_anywhere::*; pub use localhost::*; diff --git a/harmony/src/domain/topology/oberservability/mod.rs b/harmony/src/domain/topology/oberservability/mod.rs new file mode 100644 index 0000000..7f2ac95 --- /dev/null +++ b/harmony/src/domain/topology/oberservability/mod.rs @@ -0,0 +1 @@ +pub mod monitoring; diff --git a/harmony/src/domain/topology/oberservability/monitoring.rs b/harmony/src/domain/topology/oberservability/monitoring.rs new file mode 100644 index 0000000..4603eba --- /dev/null +++ b/harmony/src/domain/topology/oberservability/monitoring.rs @@ -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: Debug + Send + Sync { + async fn deploy_monitor( + &self, + topology: &T, + alert_receivers: Vec, + ) -> Result; + + async fn delete_monitor( + &self, + topolgy: &T, + alert_receivers: Vec, + ) -> Result; +} + +pub struct AlertReceiver { + pub receiver_id: String, +} diff --git a/harmony/src/modules/mod.rs b/harmony/src/modules/mod.rs index 07f489e..1427515 100644 --- a/harmony/src/modules/mod.rs +++ b/harmony/src/modules/mod.rs @@ -12,4 +12,5 @@ pub mod load_balancer; pub mod monitoring; pub mod okd; pub mod opnsense; +pub mod tenant; pub mod tftp; diff --git a/harmony/src/modules/monitoring/discord_alert_manager.rs b/harmony/src/modules/monitoring/discord_alert_manager.rs index a3519e8..7765505 100644 --- a/harmony/src/modules/monitoring/discord_alert_manager.rs +++ b/harmony/src/modules/monitoring/discord_alert_manager.rs @@ -1,30 +1,25 @@ use std::str::FromStr; use non_blank_string_rs::NonBlankString; +use url::Url; use crate::modules::helm::chart::HelmChartScore; -use super::{config::KubePrometheusConfig, monitoring_alerting::AlertChannel}; - -fn get_discord_alert_manager_score(config: &KubePrometheusConfig) -> Option { - let (url, name) = config.alert_channel.iter().find_map(|channel| { - if let AlertChannel::Discord { webhook_url, name } = channel { - Some((webhook_url, name)) - } else { - None - } - })?; - +pub fn discord_alert_manager_score( + webhook_url: Url, + namespace: String, + name: String, +) -> HelmChartScore { let values = format!( r#" environment: - name: "DISCORD_WEBHOOK" - value: "{url}" + value: "{webhook_url}" "#, ); - Some(HelmChartScore { - namespace: Some(NonBlankString::from_str(&config.namespace).unwrap()), + HelmChartScore { + namespace: Some(NonBlankString::from_str(&namespace).unwrap()), release_name: NonBlankString::from_str(&name).unwrap(), chart_name: NonBlankString::from_str( "oci://hub.nationtech.io/library/alertmanager-discord", @@ -36,13 +31,5 @@ environment: create_namespace: true, install_only: true, 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"); } } diff --git a/harmony/src/modules/monitoring/discord_webhook_sender.rs b/harmony/src/modules/monitoring/discord_webhook_sender.rs new file mode 100644 index 0000000..bad6402 --- /dev/null +++ b/harmony/src/modules/monitoring/discord_webhook_sender.rs @@ -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; + + fn delete_discord_webhook_receiver( + &self, + _notification_adapter_id: &str, + ) -> Result; +} + +// trait used to generate alert manager config values impl Monitor for KubePrometheus +pub trait AlertManagerConfig { + fn get_alert_manager_config(&self) -> Result; +} + +#[async_trait] +impl AlertManagerConfig for DiscordWebhookConfig { + fn get_alert_manager_config(&self) -> Result { + todo!() + } +} + +#[async_trait] +impl DiscordWebhookReceiver for K8sAnywhereTopology { + fn deploy_discord_webhook_receiver( + &self, + _notification_adapter_id: &str, + ) -> Result { + todo!() + } + fn delete_discord_webhook_receiver( + &self, + _notification_adapter_id: &str, + ) -> Result { + todo!() + } +} diff --git a/harmony/src/modules/monitoring/mod.rs b/harmony/src/modules/monitoring/mod.rs index 914ae07..d3eb288 100644 --- a/harmony/src/modules/monitoring/mod.rs +++ b/harmony/src/modules/monitoring/mod.rs @@ -1,4 +1,5 @@ mod config; mod discord_alert_manager; +pub mod discord_webhook_sender; mod kube_prometheus; pub mod monitoring_alerting; diff --git a/harmony/src/modules/monitoring/monitoring_alerting.rs b/harmony/src/modules/monitoring/monitoring_alerting.rs index 325f9d3..6d2db38 100644 --- a/harmony/src/modules/monitoring/monitoring_alerting.rs +++ b/harmony/src/modules/monitoring/monitoring_alerting.rs @@ -96,28 +96,28 @@ impl MonitoringAlertingStackInterpret { topology: &T, config: &KubePrometheusConfig, ) -> Result { - let mut outcomes = vec![]; + //let mut outcomes = vec![]; - for channel in &self.score.alert_channel { - let outcome = match channel { - AlertChannel::Discord { .. } => { - discord_alert_manager_score(config) - .create_interpret() - .execute(inventory, topology) - .await - } - AlertChannel::Slack { .. } => Ok(Outcome::success( - "No extra configs for slack alerting".to_string(), - )), - AlertChannel::Smpt { .. } => { - todo!() - } - }; - outcomes.push(outcome); - } - for result in outcomes { - result?; - } + //for channel in &self.score.alert_channel { + // let outcome = match channel { + // AlertChannel::Discord { .. } => { + // discord_alert_manager_score(config) + // .create_interpret() + // .execute(inventory, topology) + // .await + // } + // AlertChannel::Slack { .. } => Ok(Outcome::success( + // "No extra configs for slack alerting".to_string(), + // )), + // AlertChannel::Smpt { .. } => { + // todo!() + // } + // }; + // outcomes.push(outcome); + //} + //for result in outcomes { + // result?; + //} Ok(Outcome::success("All alert channels deployed".to_string())) } diff --git a/harmony/src/modules/tenant/mod.rs b/harmony/src/modules/tenant/mod.rs new file mode 100644 index 0000000..5ee212c --- /dev/null +++ b/harmony/src/modules/tenant/mod.rs @@ -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 Score for TenantScore { + fn create_interpret(&self) -> Box> { + 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 Interpret for TenantInterpret { + async fn execute( + &self, + _inventory: &Inventory, + topology: &T, + ) -> Result { + 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 { + todo!() + } +}