diff --git a/examples/lamp/src/main.rs b/examples/lamp/src/main.rs index f26daaa..06d8534 100644 --- a/examples/lamp/src/main.rs +++ b/examples/lamp/src/main.rs @@ -2,7 +2,10 @@ use harmony::{ data::Version, inventory::Inventory, maestro::Maestro, - modules::lamp::{LAMPConfig, LAMPScore}, + modules::{ + lamp::{LAMPConfig, LAMPScore}, + monitoring::monitoring_alerting::MonitoringAlertingStackScore, + }, topology::{K8sAnywhereTopology, Url}, }; @@ -24,7 +27,7 @@ async fn main() { // This config can be extended as needed for more complicated configurations config: LAMPConfig { project_root: "./php".into(), - database_size: format!("2Gi").into(), + database_size: format!("4Gi").into(), ..Default::default() }, }; @@ -39,7 +42,11 @@ async fn main() { ) .await .unwrap(); - maestro.register_all(vec![Box::new(lamp_stack)]); + + let monitoring_stack_score = + MonitoringAlertingStackScore::new_with_ns(&lamp_stack.config.namespace); + + maestro.register_all(vec![Box::new(lamp_stack), Box::new(monitoring_stack_score)]); // Here we bootstrap the CLI, this gives some nice features if you need them harmony_cli::init(maestro, None).await.unwrap(); } diff --git a/harmony/src/domain/topology/k8s_anywhere.rs b/harmony/src/domain/topology/k8s_anywhere.rs index 375d887..bcd95bc 100644 --- a/harmony/src/domain/topology/k8s_anywhere.rs +++ b/harmony/src/domain/topology/k8s_anywhere.rs @@ -92,9 +92,7 @@ impl K8sAnywhereTopology { async fn try_get_or_install_k8s_client(&self) -> Result, InterpretError> { let k8s_anywhere_config = K8sAnywhereConfig { - kubeconfig: std::env::var("HARMONY_KUBECONFIG") - .ok() - .map(|v| v.to_string()), + kubeconfig: std::env::var("KUBECONFIG").ok().map(|v| v.to_string()), use_system_kubeconfig: std::env::var("HARMONY_USE_SYSTEM_KUBECONFIG") .map_or_else(|_| false, |v| v.parse().ok().unwrap_or(false)), autoinstall: std::env::var("HARMONY_AUTOINSTALL") diff --git a/harmony/src/domain/topology/mod.rs b/harmony/src/domain/topology/mod.rs index 0cbfee6..3d773ff 100644 --- a/harmony/src/domain/topology/mod.rs +++ b/harmony/src/domain/topology/mod.rs @@ -1,4 +1,3 @@ -pub mod monitoring_alerting; mod ha_cluster; mod host_binding; mod http; diff --git a/harmony/src/domain/topology/monitoring_alerting.rs b/harmony/src/domain/topology/monitoring_alerting.rs deleted file mode 100644 index 4951333..0000000 --- a/harmony/src/domain/topology/monitoring_alerting.rs +++ /dev/null @@ -1,108 +0,0 @@ -use std::sync::Arc; - -use log::warn; -use tokio::sync::OnceCell; - -use k8s_openapi::api::core::v1::Pod; -use kube::{ - Client, - api::{Api, ListParams}, -}; - -use async_trait::async_trait; - -use crate::{ - interpret::{InterpretError, Outcome}, - inventory::Inventory, - maestro::Maestro, - modules::monitoring::monitoring_alerting::MonitoringAlertingStackScore, - score::Score, -}; - -use super::{HelmCommand, K8sAnywhereTopology, Topology, k8s::K8sClient}; - -#[derive(Clone, Debug)] -struct MonitoringState { - message: String, -} - -#[derive(Debug)] -pub struct MonitoringAlertingTopology { - monitoring_state: OnceCell>, -} - -impl MonitoringAlertingTopology { - pub fn new() -> Self { - Self { - monitoring_state: OnceCell::new(), - } - } - - async fn get_monitoring_state(&self) -> Result, InterpretError> { - let client = Client::try_default() - .await - .map_err(|e| InterpretError::new(format!("Kubernetes client error: {}", e)))?; - - for ns in &["monitoring", "openshift-monitoring"] { - let pods: Api = Api::namespaced(client.clone(), ns); - //TODO hardcoding the label is a problem - //check all pods are ready - let lp = ListParams::default().labels("app.kubernetes.io/name=prometheus"); - - match pods.list(&lp).await { - Ok(pod_list) => { - for p in pod_list.items { - if let Some(status) = p.status { - if let Some(conditions) = status.conditions { - if conditions - .iter() - .any(|c| c.type_ == "Ready" && c.status == "True") - { - return Ok(Some(MonitoringState { - message: format!( - "Prometheus is ready in namespace: {}", - ns - ), - })); - } - } - } - } - } - Err(e) => { - warn!("Failed to query pods in ns {}: {}", ns, e); - } - } - } - - Ok(None) - } -} - -impl Clone for Box> { - fn clone(&self) -> Box> { - self.clone_box() - } -} - -#[async_trait] -impl Topology for MonitoringAlertingTopology { - fn name(&self) -> &str { - "MonitoringAlertingTopology" - } - - async fn ensure_ready(&self) -> Result { - if let Some(state) = self.get_monitoring_state().await? { - // Monitoring stack is already ready — stop app. - println!("{}", state.message); - std::process::exit(0); - } - - // Monitoring not found — proceed with installation. - Ok(Outcome::success( - "Monitoring stack installation started.".to_string(), - )) - } -} - -impl HelmCommand for MonitoringAlertingTopology {} diff --git a/harmony/src/modules/helm/chart.rs b/harmony/src/modules/helm/chart.rs index be15a15..ec90cd9 100644 --- a/harmony/src/modules/helm/chart.rs +++ b/harmony/src/modules/helm/chart.rs @@ -6,7 +6,7 @@ use crate::topology::{HelmCommand, Topology}; use async_trait::async_trait; use helm_wrapper_rs; use helm_wrapper_rs::blocking::{DefaultHelmExecutor, HelmExecutor}; -use log::{debug, error, info, warn}; +use log::{debug, info, warn}; pub use non_blank_string_rs::NonBlankString; use serde::Serialize; use std::collections::HashMap; @@ -104,6 +104,7 @@ impl HelmChartInterpret { fn run_helm_command(args: &[&str]) -> Result { let command_str = format!("helm {}", args.join(" ")); + debug!("Got KUBECONFIG: `{}`", std::env::var("KUBECONFIG").unwrap()); debug!("Running Helm command: `{}`", command_str); let output = Command::new("helm") @@ -159,7 +160,13 @@ impl Interpret for HelmChartInterpret { self.add_repo()?; - let helm_executor = DefaultHelmExecutor::new(); + let helm_executor = DefaultHelmExecutor::new_with_opts( + &NonBlankString::from_str("helm").unwrap(), + None, + 900, + false, + false, + ); let mut helm_options = Vec::new(); if self.score.create_namespace { diff --git a/harmony/src/modules/k8s/deployment.rs b/harmony/src/modules/k8s/deployment.rs index 019a7ac..5ddc22d 100644 --- a/harmony/src/modules/k8s/deployment.rs +++ b/harmony/src/modules/k8s/deployment.rs @@ -42,7 +42,7 @@ impl Score for K8sDeploymentScore { { "image": self.image, "name": self.name, - "imagePullPolicy": "IfNotPresent", + "imagePullPolicy": "Always", "env": self.env_vars, } ] diff --git a/harmony/src/modules/lamp.rs b/harmony/src/modules/lamp.rs index 5388001..637ba92 100644 --- a/harmony/src/modules/lamp.rs +++ b/harmony/src/modules/lamp.rs @@ -38,6 +38,7 @@ pub struct LAMPConfig { pub project_root: PathBuf, pub ssl_enabled: bool, pub database_size: Option, + pub namespace: String, } impl Default for LAMPConfig { @@ -46,6 +47,7 @@ impl Default for LAMPConfig { project_root: Path::new("./src").to_path_buf(), ssl_enabled: true, database_size: None, + namespace: "harmony-lamp".to_string(), } } } @@ -54,7 +56,6 @@ impl Score for LAMPScore { fn create_interpret(&self) -> Box> { Box::new(LAMPInterpret { score: self.clone(), - namespace: "harmony-lamp".to_string(), }) } @@ -66,7 +67,6 @@ impl Score for LAMPScore { #[derive(Debug)] pub struct LAMPInterpret { score: LAMPScore, - namespace: String, } #[async_trait] @@ -132,7 +132,9 @@ impl Interpret for LAMPInterpret { info!("LAMP deployment_score {deployment_score:?}"); - Ok(Outcome::success("Successfully deployed LAMP Stack!".to_string())) + Ok(Outcome::success( + "Successfully deployed LAMP Stack!".to_string(), + )) } fn get_name(&self) -> InterpretName { @@ -164,6 +166,10 @@ impl LAMPInterpret { NonBlankString::from_str("primary.persistence.size").unwrap(), database_size, ); + values_overrides.insert( + NonBlankString::from_str("auth.rootPassword").unwrap(), + "mariadb-changethis".to_string(), + ); } let score = HelmChartScore { namespace: self.get_namespace(), @@ -176,7 +182,7 @@ impl LAMPInterpret { chart_version: None, values_overrides: Some(values_overrides), create_namespace: true, - install_only: true, + install_only: false, values_yaml: None, repository: None, }; @@ -231,6 +237,9 @@ impl LAMPInterpret { opcache", )); + dockerfile.push(RUN::from(r#"sed -i 's/VirtualHost \*:80/VirtualHost *:8080/' /etc/apache2/sites-available/000-default.conf && \ + sed -i 's/^Listen 80$/Listen 8080/' /etc/apache2/ports.conf"#)); + // Copy PHP configuration dockerfile.push(RUN::from("mkdir -p /usr/local/etc/php/conf.d/")); @@ -296,7 +305,7 @@ opcache.fast_shutdown=1 dockerfile.push(RUN::from("chown -R appuser:appuser /var/www/html")); // Expose Apache port - dockerfile.push(EXPOSE::from("80/tcp")); + dockerfile.push(EXPOSE::from("8080/tcp")); // Set the default command dockerfile.push(CMD::from("apache2-foreground")); @@ -380,6 +389,6 @@ opcache.fast_shutdown=1 } fn get_namespace(&self) -> Option { - Some(NonBlankString::from_str(&self.namespace).unwrap()) + Some(NonBlankString::from_str(&self.score.config.namespace).unwrap()) } } diff --git a/harmony/src/modules/mod.rs b/harmony/src/modules/mod.rs index f0530a7..07f489e 100644 --- a/harmony/src/modules/mod.rs +++ b/harmony/src/modules/mod.rs @@ -9,7 +9,7 @@ pub mod k3d; pub mod k8s; pub mod lamp; pub mod load_balancer; +pub mod monitoring; pub mod okd; pub mod opnsense; pub mod tftp; -pub mod monitoring; diff --git a/harmony/src/modules/monitoring/mod.rs b/harmony/src/modules/monitoring/mod.rs index dd17cc1..3c73fad 100644 --- a/harmony/src/modules/monitoring/mod.rs +++ b/harmony/src/modules/monitoring/mod.rs @@ -1,3 +1,2 @@ -pub mod monitoring_alerting; 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 acc5969..a08b038 100644 --- a/harmony/src/modules/monitoring/monitoring_alerting.rs +++ b/harmony/src/modules/monitoring/monitoring_alerting.rs @@ -1,35 +1,27 @@ -use async_trait::async_trait; -use log::info; use serde::Serialize; use crate::{ - data::{Id, Version}, - interpret::{Interpret, InterpretError, InterpretName, InterpretStatus, Outcome}, - inventory::Inventory, - maestro::Maestro, - score::{CloneBoxScore, Score}, - topology::{HelmCommand, Topology, monitoring_alerting::MonitoringAlertingTopology}, + interpret::Interpret, + modules::helm::chart::HelmChartScore, + score::Score, + topology::{HelmCommand, Topology}, }; use super::kube_prometheus::kube_prometheus_score; -#[derive(Debug)] +#[derive(Debug, Clone, Serialize)] pub struct MonitoringAlertingStackScore { - //TODO add documenation to explain why its here - //keeps it open for the end user to specify which stack they want - //if it isnt default kube-prometheus - pub monitoring_stack: Vec>>, + // TODO Support other components in our monitoring/alerting stack instead of a single helm + // chart + pub monitoring_stack: HelmChartScore, pub namespace: String, } impl MonitoringAlertingStackScore { - pub fn new( - monitoring_stack: Vec>>, - namespace: String, - ) -> Self { + pub fn new_with_ns(ns: &str) -> Self { Self { - monitoring_stack, - namespace, + monitoring_stack: kube_prometheus_score(ns), + namespace: ns.to_string(), } } } @@ -38,107 +30,18 @@ impl Default for MonitoringAlertingStackScore { fn default() -> Self { let ns = "monitoring"; Self { - monitoring_stack: vec![Box::new(kube_prometheus_score(ns))], + monitoring_stack: kube_prometheus_score(ns), namespace: ns.to_string(), } } } -impl Clone for MonitoringAlertingStackScore { - fn clone(&self) -> Self { - Self { - monitoring_stack: self - .monitoring_stack - .iter() - .map(|s| s.clone_box()) - .collect(), - namespace: self.namespace.clone(), - } - } -} -impl Serialize for MonitoringAlertingStackScore { - fn serialize(&self, serializer: S) -> Result - where - S: serde::Serializer, - { - use serde::ser::SerializeStruct; - let mut s = serializer.serialize_struct("MonitoringAlertingStackScore", 1)?; - let monitoring_values: Vec<_> = self - .monitoring_stack - .iter() - .map(|m| m.serialize()) - .collect(); - s.serialize_field("monitoring", &monitoring_values)?; - s.end() - } -} - -impl Score for MonitoringAlertingStackScore { +impl Score for MonitoringAlertingStackScore { fn create_interpret(&self) -> Box> { - Box::new(MonitoringAlertingStackInterpret { - score: MonitoringAlertingStackScore { - monitoring_stack: self - .monitoring_stack - .iter() - .map(|s| s.clone_box()) - .collect(), - namespace: self.namespace.clone(), - }, - }) + self.monitoring_stack.create_interpret() } fn name(&self) -> String { format!("MonitoringAlertingStackScore") } } - -#[derive(Debug)] -struct MonitoringAlertingStackInterpret { - pub score: MonitoringAlertingStackScore, -} - -#[async_trait] -impl Interpret for MonitoringAlertingStackInterpret { - async fn execute( - &self, - _inventory: &Inventory, - _topology: &T, - ) -> Result { - let inventory = Inventory::autoload(); - let topology = MonitoringAlertingTopology::new(); - let maestro = match Maestro::initialize(inventory, topology).await { - Ok(m) => m, - Err(e) => { - println!("failed to initialize Maestro: {}", e); - std::process::exit(1); - } - }; - - let scores_vec = self.score.monitoring_stack.clone(); - for s in scores_vec{ - info!("Running: {}", s.name()); - maestro.interpret(s).await?; - } - - Ok(Outcome::success(format!( - "monitoring stack installed in {} namespace", - self.score.namespace - ))) - } - - fn get_name(&self) -> InterpretName { - todo!() - } - - fn get_version(&self) -> Version { - todo!() - } - - fn get_status(&self) -> InterpretStatus { - todo!() - } - - fn get_children(&self) -> Vec { - todo!() - } -}