From 288129b0c1e8a81cdb02b9c53c61c26bbd11c98a Mon Sep 17 00:00:00 2001 From: Willem Date: Mon, 8 Sep 2025 16:16:01 -0400 Subject: [PATCH 1/6] wip: added ingress scores for install grafana and install prometheusadded ingress capability to k8s anywhere topology need to get the domain name dynamically from the topology when building the app to insert into the helm chart --- harmony/src/domain/topology/ingress.rs | 12 +++ harmony/src/domain/topology/k8s_anywhere.rs | 45 ++++++++++++ harmony/src/domain/topology/mod.rs | 1 + .../features/continuous_delivery.rs | 7 +- .../application/features/helm_argocd_score.rs | 10 +-- harmony/src/modules/application/rust.rs | 10 ++- harmony/src/modules/k8s/ingress.rs | 9 +++ harmony/src/modules/lamp.rs | 1 + .../modules/prometheus/rhob_alerting_score.rs | 73 +++++++++++++++++-- 9 files changed, 148 insertions(+), 20 deletions(-) create mode 100644 harmony/src/domain/topology/ingress.rs diff --git a/harmony/src/domain/topology/ingress.rs b/harmony/src/domain/topology/ingress.rs new file mode 100644 index 0000000..d1cd929 --- /dev/null +++ b/harmony/src/domain/topology/ingress.rs @@ -0,0 +1,12 @@ +use std::sync::Arc; + +use async_trait::async_trait; + +use crate::{ + interpret::InterpretError, + topology::{PreparationError, k8s::K8sClient}, +}; +#[async_trait] +pub trait Ingress { + async fn get_domain(&self, client: Arc) -> Result; +} diff --git a/harmony/src/domain/topology/k8s_anywhere.rs b/harmony/src/domain/topology/k8s_anywhere.rs index 119ad13..54f91ee 100644 --- a/harmony/src/domain/topology/k8s_anywhere.rs +++ b/harmony/src/domain/topology/k8s_anywhere.rs @@ -1,6 +1,7 @@ use std::{process::Command, sync::Arc}; use async_trait::async_trait; +use kube::api::GroupVersionKind; use log::{debug, info, warn}; use serde::Serialize; use tokio::sync::OnceCell; @@ -22,6 +23,7 @@ use crate::{ }, }, score::Score, + topology::ingress::Ingress, }; use super::{ @@ -198,6 +200,26 @@ impl K8sAnywhereTopology { } } + async fn openshift_ingress_operator_available(&self) -> Result<(), PreparationError> { + let client = self.k8s_client().await?; + let gvk = GroupVersionKind { + group: "operator.openshift.io".into(), + version: "v1".into(), + kind: "IngressController".into(), + }; + let ic = client + .get_resource_json_value("default", Some("openshift-ingress-operator"), &gvk) + .await?; + let ready_replicas = ic.data["status"]["availableReplicas"].as_i64().unwrap_or(0); + if ready_replicas >= 1 { + return Ok(()); + } else { + return Err(PreparationError::new( + "openshift-ingress-operator not available".to_string(), + )); + } + } + fn is_helm_available(&self) -> Result<(), String> { let version_result = Command::new("helm") .arg("version") @@ -550,3 +572,26 @@ impl TenantManager for K8sAnywhereTopology { .await } } + +#[async_trait] +impl Ingress for K8sAnywhereTopology { + //TODO this is specifically for openshift/okd which violates the k8sanywhere idea + async fn get_domain(&self, client: Arc) -> Result { + self.openshift_ingress_operator_available().await?; + + let gvk = GroupVersionKind { + group: "operator.openshift.io".into(), + version: "v1".into(), + kind: "IngressController".into(), + }; + let ic = client + .get_resource_json_value("default", Some("openshift-ingress-operator"), &gvk) + .await + .map_err(|_| PreparationError::new("Failed to fetch IngressController".to_string()))?; + + match ic.data["status"]["domain"].as_str() { + Some(domain) => Ok(domain.to_string()), + None => Err(PreparationError::new("Could not find domain".to_string())), + } + } +} diff --git a/harmony/src/domain/topology/mod.rs b/harmony/src/domain/topology/mod.rs index a1060a5..85e57d7 100644 --- a/harmony/src/domain/topology/mod.rs +++ b/harmony/src/domain/topology/mod.rs @@ -1,4 +1,5 @@ mod ha_cluster; +pub mod ingress; use harmony_types::net::IpAddress; mod host_binding; mod http; diff --git a/harmony/src/modules/application/features/continuous_delivery.rs b/harmony/src/modules/application/features/continuous_delivery.rs index 1bc2d9d..4ad06aa 100644 --- a/harmony/src/modules/application/features/continuous_delivery.rs +++ b/harmony/src/modules/application/features/continuous_delivery.rs @@ -10,11 +10,10 @@ use crate::{ data::Version, inventory::Inventory, modules::application::{ - ApplicationFeature, HelmPackage, OCICompliant, - features::{ArgoApplication, ArgoHelmScore}, + features::{ArgoApplication, ArgoHelmScore}, ApplicationFeature, HelmPackage, OCICompliant }, score::Score, - topology::{DeploymentTarget, HelmCommand, K8sclient, MultiTargetTopology, Topology}, + topology::{ingress::Ingress, DeploymentTarget, HelmCommand, K8sclient, MultiTargetTopology, Topology}, }; /// ContinuousDelivery in Harmony provides this functionality : @@ -136,7 +135,7 @@ impl ContinuousDelivery { #[async_trait] impl< A: OCICompliant + HelmPackage + Clone + 'static, - T: Topology + HelmCommand + MultiTargetTopology + K8sclient + 'static, + T: Topology + HelmCommand + MultiTargetTopology + K8sclient + Ingress + 'static, > ApplicationFeature for ContinuousDelivery { async fn ensure_installed(&self, topology: &T) -> Result<(), String> { diff --git a/harmony/src/modules/application/features/helm_argocd_score.rs b/harmony/src/modules/application/features/helm_argocd_score.rs index bfa3d8b..52ab6f6 100644 --- a/harmony/src/modules/application/features/helm_argocd_score.rs +++ b/harmony/src/modules/application/features/helm_argocd_score.rs @@ -13,7 +13,7 @@ use crate::{ modules::helm::chart::{HelmChartScore, HelmRepository}, score::Score, topology::{ - HelmCommand, K8sclient, PreparationError, PreparationOutcome, Topology, k8s::K8sClient, + ingress::Ingress, k8s::K8sClient, HelmCommand, K8sclient, PreparationError, PreparationOutcome, Topology }, }; use harmony_types::id::Id; @@ -27,7 +27,7 @@ pub struct ArgoHelmScore { pub argo_apps: Vec, } -impl Score for ArgoHelmScore { +impl Score for ArgoHelmScore { fn create_interpret(&self) -> Box> { Box::new(ArgoInterpret { score: self.clone(), @@ -47,16 +47,14 @@ pub struct ArgoInterpret { } #[async_trait] -impl Interpret for ArgoInterpret { +impl Interpret for ArgoInterpret { async fn execute( &self, inventory: &Inventory, topology: &T, ) -> Result { let k8s_client = topology.k8s_client().await?; - let domain = self - .get_host_domain(k8s_client.clone(), self.score.openshift) - .await?; + let domain = topology.get_domain(k8s_client.clone()).await?; let domain = format!("argo.{domain}"); let helm_score = argo_helm_chart_score(&self.score.namespace, self.score.openshift, &domain); diff --git a/harmony/src/modules/application/rust.rs b/harmony/src/modules/application/rust.rs index 0d204cc..8fe28d6 100644 --- a/harmony/src/modules/application/rust.rs +++ b/harmony/src/modules/application/rust.rs @@ -411,6 +411,7 @@ impl RustWebapp { fn create_helm_chart_files( &self, image_url: &str, + topology: &T, ) -> Result> { let chart_name = format!("{}-chart", self.name); let chart_dir = self @@ -422,6 +423,9 @@ impl RustWebapp { fs::create_dir_all(&templates_dir)?; let (image_repo, image_tag) = image_url.rsplit_once(':').unwrap_or((image_url, "latest")); + + //TODO need to find a way to use topology to get the domain + let domain = topology.get_domain(client.clone()).await?; // Create Chart.yaml let chart_yaml = format!( @@ -464,17 +468,17 @@ ingress: # Add other annotations like nginx ingress class if needed # kubernetes.io/ingress.class: nginx hosts: - - host: chart-example.local + - host: {} paths: - path: / pathType: ImplementationSpecific tls: - secretName: {}-tls hosts: - - chart-example.local + - {} "#, - chart_name, image_repo, image_tag, self.service_port, self.name + chart_name, image_repo, image_tag, self.service_port, domain, self.name ); fs::write(chart_dir.join("values.yaml"), values_yaml)?; diff --git a/harmony/src/modules/k8s/ingress.rs b/harmony/src/modules/k8s/ingress.rs index d07d82f..cfef9f6 100644 --- a/harmony/src/modules/k8s/ingress.rs +++ b/harmony/src/modules/k8s/ingress.rs @@ -40,6 +40,7 @@ pub struct K8sIngressScore { pub path: Option, pub path_type: Option, pub namespace: Option, + pub ingress_class_name: Option, } impl Score for K8sIngressScore { @@ -54,12 +55,20 @@ impl Score for K8sIngressScore { None => PathType::Prefix, }; + let ingress_class = match self.ingress_class_name.clone() { + Some(ingress_class_name) => { + ingress_class_name + } + None => format!("\"default\""), + }; + let ingress = json!( { "metadata": { "name": self.name.to_string(), }, "spec": { + "ingressClassName": ingress_class.as_str(), "rules": [ { "host": self.host.to_string(), "http": { diff --git a/harmony/src/modules/lamp.rs b/harmony/src/modules/lamp.rs index 66ca45e..a33fa1d 100644 --- a/harmony/src/modules/lamp.rs +++ b/harmony/src/modules/lamp.rs @@ -147,6 +147,7 @@ impl Interpret for LAMPInterpret { port: 8080, path: Some(ingress_path), path_type: None, + ingress_class_name: None, namespace: self .get_namespace() .map(|nbs| fqdn!(nbs.to_string().as_str())), diff --git a/harmony/src/modules/prometheus/rhob_alerting_score.rs b/harmony/src/modules/prometheus/rhob_alerting_score.rs index 97fa644..06aa33f 100644 --- a/harmony/src/modules/prometheus/rhob_alerting_score.rs +++ b/harmony/src/modules/prometheus/rhob_alerting_score.rs @@ -1,3 +1,4 @@ +use fqdn::fqdn; use std::fs; use std::{collections::BTreeMap, sync::Arc}; use tempfile::tempdir; @@ -8,6 +9,7 @@ use log::{debug, info}; use serde::Serialize; use std::process::Command; +use crate::modules::k8s::ingress::{K8sIngressScore, PathType}; use crate::modules::monitoring::kube_prometheus::crd::grafana_default_dashboard::build_default_dashboard; use crate::modules::monitoring::kube_prometheus::crd::rhob_alertmanager_config::RHOBObservability; use crate::modules::monitoring::kube_prometheus::crd::rhob_alertmanagers::{ @@ -29,6 +31,7 @@ use crate::modules::monitoring::kube_prometheus::crd::rhob_service_monitor::{ ServiceMonitor, ServiceMonitorSpec, }; use crate::score::Score; +use crate::topology::ingress::Ingress; use crate::topology::oberservability::monitoring::AlertReceiver; use crate::topology::{K8sclient, Topology, k8s::K8sClient}; use crate::{ @@ -48,7 +51,7 @@ pub struct RHOBAlertingScore { pub prometheus_rules: Vec, } -impl> Score +impl> Score for RHOBAlertingScore { fn create_interpret(&self) -> Box> { @@ -74,19 +77,20 @@ pub struct RHOBAlertingInterpret { } #[async_trait] -impl> Interpret +impl> Interpret for RHOBAlertingInterpret { async fn execute( &self, - _inventory: &Inventory, + inventory: &Inventory, topology: &T, ) -> Result { let client = topology.k8s_client().await.unwrap(); self.ensure_grafana_operator().await?; - self.install_prometheus(&client).await?; + self.install_prometheus(inventory, topology, &client) + .await?; self.install_client_kube_metrics().await?; - self.install_grafana(&client).await?; + self.install_grafana(inventory, topology,&client).await?; self.install_receivers(&self.sender, &self.receivers) .await?; self.install_rules(&self.prometheus_rules, &client).await?; @@ -238,7 +242,12 @@ impl RHOBAlertingInterpret { ))) } - async fn install_prometheus(&self, client: &Arc) -> Result { + async fn install_prometheus( + &self, + inventory: &Inventory, + topology: &T, + client: &Arc, + ) -> Result { debug!( "installing crd-prometheuses in namespace {}", self.sender.namespace.clone() @@ -265,6 +274,36 @@ impl RHOBAlertingInterpret { .apply(&stack, Some(&self.sender.namespace.clone())) .await .map_err(|e| InterpretError::new(e.to_string()))?; + let domain = topology.get_domain(client.clone()).await?; + let name = format!("{}-alert-manager", self.sender.namespace.clone()); + let backend_service = format!("{}-alert-manager", self.sender.namespace.clone()); + let namespace = self.sender.namespace.clone(); + let alert_manager_ingress = K8sIngressScore { + name: fqdn!(&name), + host: fqdn!(&domain), + backend_service: fqdn!(&backend_service), + port: 9093, + path: Some("/".to_string()), + path_type: Some(PathType::Prefix), + namespace: Some(fqdn!(&namespace)), + ingress_class_name: Some("openshift-default".to_string()), + }; + + let name = format!("{}-prometheus", self.sender.namespace.clone()); + let backend_service = format!("{}-prometheus", self.sender.namespace.clone()); + let prometheus_ingress = K8sIngressScore { + name: fqdn!(&name), + host: fqdn!(&domain), + backend_service: fqdn!(&backend_service), + port: 9090, + path: Some("/".to_string()), + path_type: Some(PathType::Prefix), + namespace: Some(fqdn!(&namespace)), + ingress_class_name: Some("openshift-default".to_string()), + }; + + alert_manager_ingress.interpret(inventory, topology).await?; + prometheus_ingress.interpret(inventory, topology).await?; info!("installed rhob monitoring stack",); Ok(Outcome::success(format!( "successfully deployed rhob-prometheus {:#?}", @@ -379,7 +418,12 @@ impl RHOBAlertingInterpret { ))) } - async fn install_grafana(&self, client: &Arc) -> Result { + async fn install_grafana( + &self, + inventory: &Inventory, + topology: &T, + client: &Arc, + ) -> Result { let mut label = BTreeMap::new(); label.insert("dashboards".to_string(), "grafana".to_string()); let labels = LabelSelector { @@ -465,6 +509,21 @@ impl RHOBAlertingInterpret { .apply(&grafana, Some(&self.sender.namespace.clone())) .await .map_err(|e| InterpretError::new(e.to_string()))?; + let domain = topology.get_domain(client.clone()).await?; + let name = format!("{}-grafana", self.sender.namespace.clone()); + let backend_service = format!("{}-grafana", self.sender.namespace.clone()); + let grafana_ingress = K8sIngressScore { + name: fqdn!(&name), + host: fqdn!(&domain), + backend_service: fqdn!(&backend_service), + port: 9090, + path: Some("/".to_string()), + path_type: Some(PathType::Prefix), + namespace: Some(fqdn!(&namespace)), + ingress_class_name: Some("openshift-default".to_string()), + }; + + grafana_ingress.interpret(inventory, topology).await?; Ok(Outcome::success(format!( "successfully deployed grafana instance {:#?}", grafana.metadata.name From 54803c40a2008b67aeb785f2c3faf7058aa80a22 Mon Sep 17 00:00:00 2001 From: Ian Letourneau Date: Mon, 8 Sep 2025 20:43:12 -0400 Subject: [PATCH 2/6] ingress: check whether running as local k3d or kubeconfig --- harmony/src/domain/topology/ingress.rs | 8 +--- harmony/src/domain/topology/k8s_anywhere.rs | 43 +++++++++++++------ .../application/features/helm_argocd_score.rs | 3 +- .../modules/prometheus/rhob_alerting_score.rs | 13 +++--- 4 files changed, 41 insertions(+), 26 deletions(-) diff --git a/harmony/src/domain/topology/ingress.rs b/harmony/src/domain/topology/ingress.rs index d1cd929..d27eb48 100644 --- a/harmony/src/domain/topology/ingress.rs +++ b/harmony/src/domain/topology/ingress.rs @@ -1,11 +1,7 @@ +use crate::topology::{PreparationError, k8s::K8sClient}; +use async_trait::async_trait; use std::sync::Arc; -use async_trait::async_trait; - -use crate::{ - interpret::InterpretError, - topology::{PreparationError, k8s::K8sClient}, -}; #[async_trait] pub trait Ingress { async fn get_domain(&self, client: Arc) -> Result; diff --git a/harmony/src/domain/topology/k8s_anywhere.rs b/harmony/src/domain/topology/k8s_anywhere.rs index 54f91ee..571f9ea 100644 --- a/harmony/src/domain/topology/k8s_anywhere.rs +++ b/harmony/src/domain/topology/k8s_anywhere.rs @@ -577,21 +577,38 @@ impl TenantManager for K8sAnywhereTopology { impl Ingress for K8sAnywhereTopology { //TODO this is specifically for openshift/okd which violates the k8sanywhere idea async fn get_domain(&self, client: Arc) -> Result { - self.openshift_ingress_operator_available().await?; + if let Some(Some(k8s_state)) = self.k8s_state.get() { + match k8s_state.source { + K8sSource::LocalK3d => Ok("localhost".to_string()), + K8sSource::Kubeconfig => { + self.openshift_ingress_operator_available().await?; - let gvk = GroupVersionKind { - group: "operator.openshift.io".into(), - version: "v1".into(), - kind: "IngressController".into(), - }; - let ic = client - .get_resource_json_value("default", Some("openshift-ingress-operator"), &gvk) - .await - .map_err(|_| PreparationError::new("Failed to fetch IngressController".to_string()))?; + let gvk = GroupVersionKind { + group: "operator.openshift.io".into(), + version: "v1".into(), + kind: "IngressController".into(), + }; + let ic = client + .get_resource_json_value( + "default", + Some("openshift-ingress-operator"), + &gvk, + ) + .await + .map_err(|_| { + PreparationError::new("Failed to fetch IngressController".to_string()) + })?; - match ic.data["status"]["domain"].as_str() { - Some(domain) => Ok(domain.to_string()), - None => Err(PreparationError::new("Could not find domain".to_string())), + match ic.data["status"]["domain"].as_str() { + Some(domain) => Ok(domain.to_string()), + None => Err(PreparationError::new("Could not find domain".to_string())), + } + } + } + } else { + Err(PreparationError::new( + "Cannot get domain: unable to detect K8s state".to_string(), + )) } } } diff --git a/harmony/src/modules/application/features/helm_argocd_score.rs b/harmony/src/modules/application/features/helm_argocd_score.rs index 52ab6f6..f49ca0d 100644 --- a/harmony/src/modules/application/features/helm_argocd_score.rs +++ b/harmony/src/modules/application/features/helm_argocd_score.rs @@ -13,7 +13,8 @@ use crate::{ modules::helm::chart::{HelmChartScore, HelmRepository}, score::Score, topology::{ - ingress::Ingress, k8s::K8sClient, HelmCommand, K8sclient, PreparationError, PreparationOutcome, Topology + HelmCommand, K8sclient, PreparationError, PreparationOutcome, Topology, ingress::Ingress, + k8s::K8sClient, }, }; use harmony_types::id::Id; diff --git a/harmony/src/modules/prometheus/rhob_alerting_score.rs b/harmony/src/modules/prometheus/rhob_alerting_score.rs index 06aa33f..71a3596 100644 --- a/harmony/src/modules/prometheus/rhob_alerting_score.rs +++ b/harmony/src/modules/prometheus/rhob_alerting_score.rs @@ -51,8 +51,8 @@ pub struct RHOBAlertingScore { pub prometheus_rules: Vec, } -impl> Score - for RHOBAlertingScore +impl> + Score for RHOBAlertingScore { fn create_interpret(&self) -> Box> { Box::new(RHOBAlertingInterpret { @@ -77,8 +77,8 @@ pub struct RHOBAlertingInterpret { } #[async_trait] -impl> Interpret - for RHOBAlertingInterpret +impl> + Interpret for RHOBAlertingInterpret { async fn execute( &self, @@ -90,7 +90,7 @@ impl( + async fn install_prometheus( &self, inventory: &Inventory, topology: &T, @@ -274,6 +274,7 @@ impl RHOBAlertingInterpret { .apply(&stack, Some(&self.sender.namespace.clone())) .await .map_err(|e| InterpretError::new(e.to_string()))?; + let domain = topology.get_domain(client.clone()).await?; let name = format!("{}-alert-manager", self.sender.namespace.clone()); let backend_service = format!("{}-alert-manager", self.sender.namespace.clone()); From 3bf5cb052654688b9323abb0520218f4d58f5682 Mon Sep 17 00:00:00 2001 From: Ian Letourneau Date: Mon, 8 Sep 2025 21:53:44 -0400 Subject: [PATCH 3/6] use topology domain to build & push helm package for continuous deliery --- .../src/main.rs | 1 - .../rhob_application_monitoring/src/main.rs | 1 - examples/rust/src/main.rs | 1 - examples/try_rust_webapp/src/main.rs | 1 - harmony/src/domain/topology/ingress.rs | 5 ++-- harmony/src/domain/topology/k8s_anywhere.rs | 4 ++- .../features/continuous_delivery.rs | 15 ++++++++--- .../application/features/helm_argocd_score.rs | 2 +- harmony/src/modules/application/oci.rs | 9 ++++--- harmony/src/modules/application/rust.rs | 26 ++++++++++--------- .../modules/prometheus/rhob_alerting_score.rs | 4 +-- 11 files changed, 39 insertions(+), 30 deletions(-) diff --git a/examples/application_monitoring_with_tenant/src/main.rs b/examples/application_monitoring_with_tenant/src/main.rs index f46a993..ad6e634 100644 --- a/examples/application_monitoring_with_tenant/src/main.rs +++ b/examples/application_monitoring_with_tenant/src/main.rs @@ -27,7 +27,6 @@ async fn main() { }; let application = Arc::new(RustWebapp { name: "example-monitoring".to_string(), - domain: Url::Url(url::Url::parse("https://rustapp.harmony.example.com").unwrap()), project_root: PathBuf::from("./examples/rust/webapp"), framework: Some(RustWebFramework::Leptos), service_port: 3000, diff --git a/examples/rhob_application_monitoring/src/main.rs b/examples/rhob_application_monitoring/src/main.rs index dd6a05c..fdcff48 100644 --- a/examples/rhob_application_monitoring/src/main.rs +++ b/examples/rhob_application_monitoring/src/main.rs @@ -17,7 +17,6 @@ use harmony_types::net::Url; async fn main() { let application = Arc::new(RustWebapp { name: "test-rhob-monitoring".to_string(), - domain: Url::Url(url::Url::parse("htps://some-fake-url").unwrap()), project_root: PathBuf::from("./webapp"), // Relative from 'harmony-path' param framework: Some(RustWebFramework::Leptos), service_port: 3000, diff --git a/examples/rust/src/main.rs b/examples/rust/src/main.rs index 063fdb6..ff6d769 100644 --- a/examples/rust/src/main.rs +++ b/examples/rust/src/main.rs @@ -19,7 +19,6 @@ use harmony_macros::hurl; async fn main() { let application = Arc::new(RustWebapp { name: "harmony-example-rust-webapp".to_string(), - domain: hurl!("https://rustapp.harmony.example.com"), project_root: PathBuf::from("./webapp"), framework: Some(RustWebFramework::Leptos), service_port: 3000, diff --git a/examples/try_rust_webapp/src/main.rs b/examples/try_rust_webapp/src/main.rs index 6e1ab63..84fe19b 100644 --- a/examples/try_rust_webapp/src/main.rs +++ b/examples/try_rust_webapp/src/main.rs @@ -17,7 +17,6 @@ use harmony_types::net::Url; async fn main() { let application = Arc::new(RustWebapp { name: "harmony-example-tryrust".to_string(), - domain: Url::Url(url::Url::parse("https://tryrust.harmony.example.com").unwrap()), project_root: PathBuf::from("./tryrust.org"), framework: Some(RustWebFramework::Leptos), service_port: 8080, diff --git a/harmony/src/domain/topology/ingress.rs b/harmony/src/domain/topology/ingress.rs index d27eb48..8ef3502 100644 --- a/harmony/src/domain/topology/ingress.rs +++ b/harmony/src/domain/topology/ingress.rs @@ -1,8 +1,7 @@ -use crate::topology::{PreparationError, k8s::K8sClient}; +use crate::topology::PreparationError; use async_trait::async_trait; -use std::sync::Arc; #[async_trait] pub trait Ingress { - async fn get_domain(&self, client: Arc) -> Result; + async fn get_domain(&self) -> Result; } diff --git a/harmony/src/domain/topology/k8s_anywhere.rs b/harmony/src/domain/topology/k8s_anywhere.rs index 571f9ea..0f46eca 100644 --- a/harmony/src/domain/topology/k8s_anywhere.rs +++ b/harmony/src/domain/topology/k8s_anywhere.rs @@ -576,7 +576,9 @@ impl TenantManager for K8sAnywhereTopology { #[async_trait] impl Ingress for K8sAnywhereTopology { //TODO this is specifically for openshift/okd which violates the k8sanywhere idea - async fn get_domain(&self, client: Arc) -> Result { + async fn get_domain(&self) -> Result { + let client = self.k8s_client().await?; + if let Some(Some(k8s_state)) = self.k8s_state.get() { match k8s_state.source { K8sSource::LocalK3d => Ok("localhost".to_string()), diff --git a/harmony/src/modules/application/features/continuous_delivery.rs b/harmony/src/modules/application/features/continuous_delivery.rs index 4ad06aa..33c03eb 100644 --- a/harmony/src/modules/application/features/continuous_delivery.rs +++ b/harmony/src/modules/application/features/continuous_delivery.rs @@ -1,4 +1,4 @@ -use std::{io::Write, process::Command, sync::Arc}; +use std::{io::Write, marker::PhantomData, process::Command, sync::Arc}; use async_trait::async_trait; use log::info; @@ -10,10 +10,13 @@ use crate::{ data::Version, inventory::Inventory, modules::application::{ - features::{ArgoApplication, ArgoHelmScore}, ApplicationFeature, HelmPackage, OCICompliant + ApplicationFeature, HelmPackage, OCICompliant, + features::{ArgoApplication, ArgoHelmScore}, }, score::Score, - topology::{ingress::Ingress, DeploymentTarget, HelmCommand, K8sclient, MultiTargetTopology, Topology}, + topology::{ + DeploymentTarget, HelmCommand, K8sclient, MultiTargetTopology, Topology, ingress::Ingress, + }, }; /// ContinuousDelivery in Harmony provides this functionality : @@ -140,13 +143,17 @@ impl< { async fn ensure_installed(&self, topology: &T) -> Result<(), String> { let image = self.application.image_name(); + let domain_host = topology.get_domain().await.map_err(|e| e.to_string())?; // TODO Write CI/CD workflow files // we can autotedect the CI type using the remote url (default to github action for github // url, etc..) // Or ask for it when unknown - let helm_chart = self.application.build_push_helm_package(&image).await?; + let helm_chart = self + .application + .build_push_helm_package(&image, &domain_host) + .await?; // TODO: Make building image configurable/skippable if image already exists (prompt)") // https://git.nationtech.io/NationTech/harmony/issues/104 diff --git a/harmony/src/modules/application/features/helm_argocd_score.rs b/harmony/src/modules/application/features/helm_argocd_score.rs index f49ca0d..944eedc 100644 --- a/harmony/src/modules/application/features/helm_argocd_score.rs +++ b/harmony/src/modules/application/features/helm_argocd_score.rs @@ -55,7 +55,7 @@ impl Interpret for ArgoInter topology: &T, ) -> Result { let k8s_client = topology.k8s_client().await?; - let domain = topology.get_domain(k8s_client.clone()).await?; + let domain = topology.get_domain().await?; let domain = format!("argo.{domain}"); let helm_score = argo_helm_chart_score(&self.score.namespace, self.score.openshift, &domain); diff --git a/harmony/src/modules/application/oci.rs b/harmony/src/modules/application/oci.rs index bf9f393..4085aa0 100644 --- a/harmony/src/modules/application/oci.rs +++ b/harmony/src/modules/application/oci.rs @@ -1,6 +1,5 @@ -use async_trait::async_trait; - use super::Application; +use async_trait::async_trait; #[async_trait] pub trait OCICompliant: Application { @@ -17,5 +16,9 @@ pub trait HelmPackage: Application { /// /// # Arguments /// * `image_url` - The full URL of the OCI container image to be used in the Deployment. - async fn build_push_helm_package(&self, image_url: &str) -> Result; + async fn build_push_helm_package( + &self, + image_url: &str, + domain_host: &str, + ) -> Result; } diff --git a/harmony/src/modules/application/rust.rs b/harmony/src/modules/application/rust.rs index 8fe28d6..b56ac94 100644 --- a/harmony/src/modules/application/rust.rs +++ b/harmony/src/modules/application/rust.rs @@ -1,5 +1,4 @@ -use std::fs::{self, File}; -use std::io::Read; +use std::fs::{self}; use std::path::{Path, PathBuf}; use std::process; use std::sync::Arc; @@ -13,12 +12,11 @@ use dockerfile_builder::instruction_builder::CopyBuilder; use futures_util::StreamExt; use log::{debug, info, log_enabled}; use serde::Serialize; -use tar::{Archive, Builder, Header}; +use tar::{Builder, Header}; use walkdir::WalkDir; use crate::config::{REGISTRY_PROJECT, REGISTRY_URL}; use crate::{score::Score, topology::Topology}; -use harmony_types::net::Url; use super::{Application, ApplicationFeature, ApplicationInterpret, HelmPackage, OCICompliant}; @@ -58,7 +56,6 @@ pub enum RustWebFramework { #[derive(Debug, Clone, Serialize)] pub struct RustWebapp { pub name: String, - pub domain: Url, /// The path to the root of the Rust project to be containerized. pub project_root: PathBuf, pub service_port: u32, @@ -73,12 +70,17 @@ impl Application for RustWebapp { #[async_trait] impl HelmPackage for RustWebapp { - async fn build_push_helm_package(&self, image_url: &str) -> Result { + async fn build_push_helm_package( + &self, + image_url: &str, + domain_host: &str, + ) -> Result { info!("Starting Helm chart build and push for '{}'", self.name); // 1. Create the Helm chart files on disk. let chart_dir = self - .create_helm_chart_files(image_url) + .create_helm_chart_files(image_url, domain_host) + .await .map_err(|e| format!("Failed to create Helm chart files: {}", e))?; info!("Successfully created Helm chart files in {:?}", chart_dir); @@ -408,10 +410,10 @@ impl RustWebapp { } /// Creates all necessary files for a basic Helm chart. - fn create_helm_chart_files( + async fn create_helm_chart_files( &self, image_url: &str, - topology: &T, + domain_host: &str, ) -> Result> { let chart_name = format!("{}-chart", self.name); let chart_dir = self @@ -423,9 +425,9 @@ impl RustWebapp { fs::create_dir_all(&templates_dir)?; let (image_repo, image_tag) = image_url.rsplit_once(':').unwrap_or((image_url, "latest")); - + //TODO need to find a way to use topology to get the domain - let domain = topology.get_domain(client.clone()).await?; + let domain = format!("{}.{domain_host}", self.name); // Create Chart.yaml let chart_yaml = format!( @@ -478,7 +480,7 @@ ingress: - {} "#, - chart_name, image_repo, image_tag, self.service_port, domain, self.name + chart_name, image_repo, image_tag, self.service_port, domain, self.name, domain ); fs::write(chart_dir.join("values.yaml"), values_yaml)?; diff --git a/harmony/src/modules/prometheus/rhob_alerting_score.rs b/harmony/src/modules/prometheus/rhob_alerting_score.rs index 71a3596..83e41dd 100644 --- a/harmony/src/modules/prometheus/rhob_alerting_score.rs +++ b/harmony/src/modules/prometheus/rhob_alerting_score.rs @@ -275,7 +275,7 @@ impl RHOBAlertingInterpret { .await .map_err(|e| InterpretError::new(e.to_string()))?; - let domain = topology.get_domain(client.clone()).await?; + let domain = topology.get_domain().await?; let name = format!("{}-alert-manager", self.sender.namespace.clone()); let backend_service = format!("{}-alert-manager", self.sender.namespace.clone()); let namespace = self.sender.namespace.clone(); @@ -510,7 +510,7 @@ impl RHOBAlertingInterpret { .apply(&grafana, Some(&self.sender.namespace.clone())) .await .map_err(|e| InterpretError::new(e.to_string()))?; - let domain = topology.get_domain(client.clone()).await?; + let domain = topology.get_domain().await?; let name = format!("{}-grafana", self.sender.namespace.clone()); let backend_service = format!("{}-grafana", self.sender.namespace.clone()); let grafana_ingress = K8sIngressScore { From a0884950d735216fc9d362bcf2e1fa903d575768 Mon Sep 17 00:00:00 2001 From: Ian Letourneau Date: Tue, 9 Sep 2025 08:27:43 -0400 Subject: [PATCH 4/6] remove hardcoded domain and secrets in Ntfy --- harmony/Cargo.toml | 6 +++- .../features/continuous_delivery.rs | 6 ++-- .../application/features/monitoring.rs | 32 ++++++++++++------- harmony/src/modules/application/oci.rs | 3 +- harmony/src/modules/application/rust.rs | 10 +++--- 5 files changed, 35 insertions(+), 22 deletions(-) diff --git a/harmony/Cargo.toml b/harmony/Cargo.toml index 391628b..ad57db1 100644 --- a/harmony/Cargo.toml +++ b/harmony/Cargo.toml @@ -10,7 +10,11 @@ testing = [] [dependencies] hex = "0.4" -reqwest = { version = "0.11", features = ["blocking", "json", "rustls-tls"], default-features = false } +reqwest = { version = "0.11", features = [ + "blocking", + "json", + "rustls-tls", +], default-features = false } russh = "0.45.0" rust-ipmi = "0.1.1" semver = "1.0.23" diff --git a/harmony/src/modules/application/features/continuous_delivery.rs b/harmony/src/modules/application/features/continuous_delivery.rs index 33c03eb..d6b65db 100644 --- a/harmony/src/modules/application/features/continuous_delivery.rs +++ b/harmony/src/modules/application/features/continuous_delivery.rs @@ -1,4 +1,4 @@ -use std::{io::Write, marker::PhantomData, process::Command, sync::Arc}; +use std::{io::Write, process::Command, sync::Arc}; use async_trait::async_trait; use log::info; @@ -143,7 +143,7 @@ impl< { async fn ensure_installed(&self, topology: &T) -> Result<(), String> { let image = self.application.image_name(); - let domain_host = topology.get_domain().await.map_err(|e| e.to_string())?; + let domain = topology.get_domain().await.map_err(|e| e.to_string())?; // TODO Write CI/CD workflow files // we can autotedect the CI type using the remote url (default to github action for github @@ -152,7 +152,7 @@ impl< let helm_chart = self .application - .build_push_helm_package(&image, &domain_host) + .build_push_helm_package(&image, &domain) .await?; // TODO: Make building image configurable/skippable if image already exists (prompt)") diff --git a/harmony/src/modules/application/features/monitoring.rs b/harmony/src/modules/application/features/monitoring.rs index 1c1c00b..fa3a6a3 100644 --- a/harmony/src/modules/application/features/monitoring.rs +++ b/harmony/src/modules/application/features/monitoring.rs @@ -1,10 +1,8 @@ -use std::sync::Arc; - use crate::modules::application::{Application, ApplicationFeature}; use crate::modules::monitoring::application_monitoring::application_monitoring_score::ApplicationMonitoringScore; use crate::modules::monitoring::kube_prometheus::crd::crd_alertmanager_config::CRDPrometheus; - use crate::topology::MultiTargetTopology; +use crate::topology::ingress::Ingress; use crate::{ inventory::Inventory, modules::monitoring::{ @@ -19,8 +17,12 @@ use crate::{ }; use async_trait::async_trait; use base64::{Engine as _, engine::general_purpose}; +use harmony_secret::SecretManager; +use harmony_secret_derive::Secret; use harmony_types::net::Url; use log::{debug, info}; +use serde::{Deserialize, Serialize}; +use std::sync::Arc; #[derive(Debug, Clone)] pub struct Monitoring { @@ -36,8 +38,9 @@ impl< + TenantManager + K8sclient + MultiTargetTopology - + std::fmt::Debug - + PrometheusApplicationMonitoring, + + PrometheusApplicationMonitoring + + Ingress + + std::fmt::Debug, > ApplicationFeature for Monitoring { async fn ensure_installed(&self, topology: &T) -> Result<(), String> { @@ -47,6 +50,7 @@ impl< .await .map(|ns| ns.name.clone()) .unwrap_or_else(|| self.application.name()); + let domain = topology.get_domain().await.unwrap(); let mut alerting_score = ApplicationMonitoringScore { sender: CRDPrometheus { @@ -58,19 +62,17 @@ impl< }; let ntfy = NtfyScore { namespace: namespace.clone(), - host: "ntfy.harmonydemo.apps.ncd0.harmony.mcd".to_string(), + host: format!("ntfy.{domain}"), }; ntfy.interpret(&Inventory::empty(), topology) .await .map_err(|e| e.to_string())?; - let ntfy_default_auth_username = "harmony"; - let ntfy_default_auth_password = "harmony"; + let config = SecretManager::get_or_prompt::().await.unwrap(); + let ntfy_default_auth_header = format!( "Basic {}", - general_purpose::STANDARD.encode(format!( - "{ntfy_default_auth_username}:{ntfy_default_auth_password}" - )) + general_purpose::STANDARD.encode(format!("{}:{}", config.username, config.password)) ); debug!("ntfy_default_auth_header: {ntfy_default_auth_header}"); @@ -100,9 +102,17 @@ impl< .interpret(&Inventory::empty(), topology) .await .map_err(|e| e.to_string())?; + Ok(()) } + fn name(&self) -> String { "Monitoring".to_string() } } + +#[derive(Secret, Serialize, Deserialize, Clone, Debug)] +struct NtfyAuth { + username: String, + password: String, +} diff --git a/harmony/src/modules/application/oci.rs b/harmony/src/modules/application/oci.rs index 4085aa0..8b1585c 100644 --- a/harmony/src/modules/application/oci.rs +++ b/harmony/src/modules/application/oci.rs @@ -16,9 +16,10 @@ pub trait HelmPackage: Application { /// /// # Arguments /// * `image_url` - The full URL of the OCI container image to be used in the Deployment. + /// * `domain` - The domain where the application is hosted. async fn build_push_helm_package( &self, image_url: &str, - domain_host: &str, + domain: &str, ) -> Result; } diff --git a/harmony/src/modules/application/rust.rs b/harmony/src/modules/application/rust.rs index b56ac94..c16d665 100644 --- a/harmony/src/modules/application/rust.rs +++ b/harmony/src/modules/application/rust.rs @@ -73,13 +73,13 @@ impl HelmPackage for RustWebapp { async fn build_push_helm_package( &self, image_url: &str, - domain_host: &str, + domain: &str, ) -> Result { info!("Starting Helm chart build and push for '{}'", self.name); // 1. Create the Helm chart files on disk. let chart_dir = self - .create_helm_chart_files(image_url, domain_host) + .create_helm_chart_files(image_url, domain) .await .map_err(|e| format!("Failed to create Helm chart files: {}", e))?; info!("Successfully created Helm chart files in {:?}", chart_dir); @@ -413,7 +413,7 @@ impl RustWebapp { async fn create_helm_chart_files( &self, image_url: &str, - domain_host: &str, + domain: &str, ) -> Result> { let chart_name = format!("{}-chart", self.name); let chart_dir = self @@ -425,9 +425,7 @@ impl RustWebapp { fs::create_dir_all(&templates_dir)?; let (image_repo, image_tag) = image_url.rsplit_once(':').unwrap_or((image_url, "latest")); - - //TODO need to find a way to use topology to get the domain - let domain = format!("{}.{domain_host}", self.name); + let domain = format!("{}.{domain}", self.name); // Create Chart.yaml let chart_yaml = format!( From b9e04d21dad4ff9ee93fc068cd576c30f81d9916 Mon Sep 17 00:00:00 2001 From: Ian Letourneau Date: Tue, 9 Sep 2025 09:46:00 -0400 Subject: [PATCH 5/6] get domain for a service --- examples/try_rust_webapp/src/main.rs | 7 +++---- harmony/src/domain/topology/ingress.rs | 2 +- harmony/src/domain/topology/k8s_anywhere.rs | 4 ++-- .../modules/application/features/continuous_delivery.rs | 5 ++++- .../modules/application/features/helm_argocd_score.rs | 3 +-- harmony/src/modules/application/features/monitoring.rs | 4 ++-- harmony/src/modules/prometheus/rhob_alerting_score.rs | 9 +++++---- 7 files changed, 18 insertions(+), 16 deletions(-) diff --git a/examples/try_rust_webapp/src/main.rs b/examples/try_rust_webapp/src/main.rs index 84fe19b..8a76602 100644 --- a/examples/try_rust_webapp/src/main.rs +++ b/examples/try_rust_webapp/src/main.rs @@ -1,5 +1,3 @@ -use std::{path::PathBuf, sync::Arc}; - use harmony::{ inventory::Inventory, modules::{ @@ -11,7 +9,8 @@ use harmony::{ }, topology::K8sAnywhereTopology, }; -use harmony_types::net::Url; +use harmony_macros::hurl; +use std::{path::PathBuf, sync::Arc}; #[tokio::main] async fn main() { @@ -24,7 +23,7 @@ async fn main() { let discord_receiver = DiscordWebhook { name: "test-discord".to_string(), - url: Url::Url(url::Url::parse("https://discord.doesnt.exist.com").unwrap()), + url: hurl!("https://discord.doesnt.exist.com"), }; let app = ApplicationScore { diff --git a/harmony/src/domain/topology/ingress.rs b/harmony/src/domain/topology/ingress.rs index 8ef3502..6d2a5d6 100644 --- a/harmony/src/domain/topology/ingress.rs +++ b/harmony/src/domain/topology/ingress.rs @@ -3,5 +3,5 @@ use async_trait::async_trait; #[async_trait] pub trait Ingress { - async fn get_domain(&self) -> Result; + async fn get_domain(&self, service: String) -> Result; } diff --git a/harmony/src/domain/topology/k8s_anywhere.rs b/harmony/src/domain/topology/k8s_anywhere.rs index 0f46eca..582e0f7 100644 --- a/harmony/src/domain/topology/k8s_anywhere.rs +++ b/harmony/src/domain/topology/k8s_anywhere.rs @@ -576,7 +576,7 @@ impl TenantManager for K8sAnywhereTopology { #[async_trait] impl Ingress for K8sAnywhereTopology { //TODO this is specifically for openshift/okd which violates the k8sanywhere idea - async fn get_domain(&self) -> Result { + async fn get_domain(&self, service: String) -> Result { let client = self.k8s_client().await?; if let Some(Some(k8s_state)) = self.k8s_state.get() { @@ -602,7 +602,7 @@ impl Ingress for K8sAnywhereTopology { })?; match ic.data["status"]["domain"].as_str() { - Some(domain) => Ok(domain.to_string()), + Some(domain) => Ok(format!("{service}.{domain}")), None => Err(PreparationError::new("Could not find domain".to_string())), } } diff --git a/harmony/src/modules/application/features/continuous_delivery.rs b/harmony/src/modules/application/features/continuous_delivery.rs index d6b65db..27b64f3 100644 --- a/harmony/src/modules/application/features/continuous_delivery.rs +++ b/harmony/src/modules/application/features/continuous_delivery.rs @@ -143,7 +143,10 @@ impl< { async fn ensure_installed(&self, topology: &T) -> Result<(), String> { let image = self.application.image_name(); - let domain = topology.get_domain().await.map_err(|e| e.to_string())?; + let domain = topology + .get_domain(self.application.name()) + .await + .map_err(|e| e.to_string())?; // TODO Write CI/CD workflow files // we can autotedect the CI type using the remote url (default to github action for github diff --git a/harmony/src/modules/application/features/helm_argocd_score.rs b/harmony/src/modules/application/features/helm_argocd_score.rs index 944eedc..7dcc2f2 100644 --- a/harmony/src/modules/application/features/helm_argocd_score.rs +++ b/harmony/src/modules/application/features/helm_argocd_score.rs @@ -55,8 +55,7 @@ impl Interpret for ArgoInter topology: &T, ) -> Result { let k8s_client = topology.k8s_client().await?; - let domain = topology.get_domain().await?; - let domain = format!("argo.{domain}"); + let domain = topology.get_domain("argo".into()).await?; let helm_score = argo_helm_chart_score(&self.score.namespace, self.score.openshift, &domain); diff --git a/harmony/src/modules/application/features/monitoring.rs b/harmony/src/modules/application/features/monitoring.rs index fa3a6a3..80b4a04 100644 --- a/harmony/src/modules/application/features/monitoring.rs +++ b/harmony/src/modules/application/features/monitoring.rs @@ -50,7 +50,7 @@ impl< .await .map(|ns| ns.name.clone()) .unwrap_or_else(|| self.application.name()); - let domain = topology.get_domain().await.unwrap(); + let domain = topology.get_domain("ntfy".into()).await.unwrap(); let mut alerting_score = ApplicationMonitoringScore { sender: CRDPrometheus { @@ -62,7 +62,7 @@ impl< }; let ntfy = NtfyScore { namespace: namespace.clone(), - host: format!("ntfy.{domain}"), + host: domain, }; ntfy.interpret(&Inventory::empty(), topology) .await diff --git a/harmony/src/modules/prometheus/rhob_alerting_score.rs b/harmony/src/modules/prometheus/rhob_alerting_score.rs index 83e41dd..2c8b5a4 100644 --- a/harmony/src/modules/prometheus/rhob_alerting_score.rs +++ b/harmony/src/modules/prometheus/rhob_alerting_score.rs @@ -275,13 +275,13 @@ impl RHOBAlertingInterpret { .await .map_err(|e| InterpretError::new(e.to_string()))?; - let domain = topology.get_domain().await?; + let alert_manager_domain = topology.get_domain("alert-manager".into()).await?; let name = format!("{}-alert-manager", self.sender.namespace.clone()); let backend_service = format!("{}-alert-manager", self.sender.namespace.clone()); let namespace = self.sender.namespace.clone(); let alert_manager_ingress = K8sIngressScore { name: fqdn!(&name), - host: fqdn!(&domain), + host: fqdn!(&alert_manager_domain), backend_service: fqdn!(&backend_service), port: 9093, path: Some("/".to_string()), @@ -290,11 +290,12 @@ impl RHOBAlertingInterpret { ingress_class_name: Some("openshift-default".to_string()), }; + let prometheus_domain = topology.get_domain("prometheus".into()).await?; let name = format!("{}-prometheus", self.sender.namespace.clone()); let backend_service = format!("{}-prometheus", self.sender.namespace.clone()); let prometheus_ingress = K8sIngressScore { name: fqdn!(&name), - host: fqdn!(&domain), + host: fqdn!(&prometheus_domain), backend_service: fqdn!(&backend_service), port: 9090, path: Some("/".to_string()), @@ -510,7 +511,7 @@ impl RHOBAlertingInterpret { .apply(&grafana, Some(&self.sender.namespace.clone())) .await .map_err(|e| InterpretError::new(e.to_string()))?; - let domain = topology.get_domain().await?; + let domain = topology.get_domain("grafana".into()).await?; let name = format!("{}-grafana", self.sender.namespace.clone()); let backend_service = format!("{}-grafana", self.sender.namespace.clone()); let grafana_ingress = K8sIngressScore { From cd0720f43e6a978b2851edebf7b418fa2dedcb31 Mon Sep 17 00:00:00 2001 From: Willem Date: Tue, 9 Sep 2025 13:09:05 -0400 Subject: [PATCH 6/6] connected ingress to servicemodified rust application helm chart deployment to not use tls and cert-manager annotation --- examples/try_rust_webapp/src/main.rs | 4 ++-- harmony/src/modules/application/rust.rs | 8 +------- harmony/src/modules/k8s/ingress.rs | 4 +--- .../modules/prometheus/rhob_alerting_score.rs | 20 ++++++++++++------- 4 files changed, 17 insertions(+), 19 deletions(-) diff --git a/examples/try_rust_webapp/src/main.rs b/examples/try_rust_webapp/src/main.rs index 8a76602..26a1958 100644 --- a/examples/try_rust_webapp/src/main.rs +++ b/examples/try_rust_webapp/src/main.rs @@ -3,7 +3,7 @@ use harmony::{ modules::{ application::{ ApplicationScore, RustWebFramework, RustWebapp, - features::{ContinuousDelivery, Monitoring}, + features::{ContinuousDelivery, Monitoring, rhob_monitoring::RHOBMonitoring}, }, monitoring::alert_channel::discord_alert_channel::DiscordWebhook, }, @@ -31,7 +31,7 @@ async fn main() { Box::new(ContinuousDelivery { application: application.clone(), }), - Box::new(Monitoring { + Box::new(RHOBMonitoring { application: application.clone(), alert_receiver: vec![Box::new(discord_receiver)], }), diff --git a/harmony/src/modules/application/rust.rs b/harmony/src/modules/application/rust.rs index c16d665..3205682 100644 --- a/harmony/src/modules/application/rust.rs +++ b/harmony/src/modules/application/rust.rs @@ -464,7 +464,6 @@ ingress: enabled: true # Annotations for cert-manager to handle SSL. annotations: - cert-manager.io/cluster-issuer: "letsencrypt-prod" # Add other annotations like nginx ingress class if needed # kubernetes.io/ingress.class: nginx hosts: @@ -472,13 +471,8 @@ ingress: paths: - path: / pathType: ImplementationSpecific - tls: - - secretName: {}-tls - hosts: - - {} - "#, - chart_name, image_repo, image_tag, self.service_port, domain, self.name, domain + chart_name, image_repo, image_tag, self.service_port, domain, ); fs::write(chart_dir.join("values.yaml"), values_yaml)?; diff --git a/harmony/src/modules/k8s/ingress.rs b/harmony/src/modules/k8s/ingress.rs index cfef9f6..eb5478f 100644 --- a/harmony/src/modules/k8s/ingress.rs +++ b/harmony/src/modules/k8s/ingress.rs @@ -56,9 +56,7 @@ impl Score for K8sIngressScore { }; let ingress_class = match self.ingress_class_name.clone() { - Some(ingress_class_name) => { - ingress_class_name - } + Some(ingress_class_name) => ingress_class_name, None => format!("\"default\""), }; diff --git a/harmony/src/modules/prometheus/rhob_alerting_score.rs b/harmony/src/modules/prometheus/rhob_alerting_score.rs index 2c8b5a4..2110157 100644 --- a/harmony/src/modules/prometheus/rhob_alerting_score.rs +++ b/harmony/src/modules/prometheus/rhob_alerting_score.rs @@ -275,9 +275,11 @@ impl RHOBAlertingInterpret { .await .map_err(|e| InterpretError::new(e.to_string()))?; - let alert_manager_domain = topology.get_domain("alert-manager".into()).await?; + let alert_manager_domain = topology + .get_domain(format!("alert-manager-{}", self.sender.namespace.clone())) + .await?; let name = format!("{}-alert-manager", self.sender.namespace.clone()); - let backend_service = format!("{}-alert-manager", self.sender.namespace.clone()); + let backend_service = format!("alertmanager-operated"); let namespace = self.sender.namespace.clone(); let alert_manager_ingress = K8sIngressScore { name: fqdn!(&name), @@ -290,9 +292,11 @@ impl RHOBAlertingInterpret { ingress_class_name: Some("openshift-default".to_string()), }; - let prometheus_domain = topology.get_domain("prometheus".into()).await?; + let prometheus_domain = topology + .get_domain(format!("prometheus-{}", self.sender.namespace.clone())) + .await?; let name = format!("{}-prometheus", self.sender.namespace.clone()); - let backend_service = format!("{}-prometheus", self.sender.namespace.clone()); + let backend_service = format!("prometheus-operated"); let prometheus_ingress = K8sIngressScore { name: fqdn!(&name), host: fqdn!(&prometheus_domain), @@ -511,14 +515,16 @@ impl RHOBAlertingInterpret { .apply(&grafana, Some(&self.sender.namespace.clone())) .await .map_err(|e| InterpretError::new(e.to_string()))?; - let domain = topology.get_domain("grafana".into()).await?; + let domain = topology + .get_domain(format!("grafana-{}", self.sender.namespace.clone())) + .await?; let name = format!("{}-grafana", self.sender.namespace.clone()); - let backend_service = format!("{}-grafana", self.sender.namespace.clone()); + let backend_service = format!("grafana-{}-service", self.sender.namespace.clone()); let grafana_ingress = K8sIngressScore { name: fqdn!(&name), host: fqdn!(&domain), backend_service: fqdn!(&backend_service), - port: 9090, + port: 3000, path: Some("/".to_string()), path_type: Some(PathType::Prefix), namespace: Some(fqdn!(&namespace)),