From ed70bfd2363fa9f54fcc5cf92a7fbbb23aee4b25 Mon Sep 17 00:00:00 2001 From: Willem Date: Mon, 8 Sep 2025 14:04:12 +0000 Subject: [PATCH] fix/argo (#133) * remove hardcoded value for domain name and namespace Co-authored-by: Ian Letourneau Reviewed-on: https://git.nationtech.io/NationTech/harmony/pulls/133 Co-authored-by: Willem Co-committed-by: Willem --- harmony/src/domain/topology/k8s.rs | 17 +++++- .../features/continuous_delivery.rs | 10 ++-- .../application/features/helm_argocd_score.rs | 60 ++++++++++++++++--- 3 files changed, 72 insertions(+), 15 deletions(-) diff --git a/harmony/src/domain/topology/k8s.rs b/harmony/src/domain/topology/k8s.rs index c9d0d58..88bd2e8 100644 --- a/harmony/src/domain/topology/k8s.rs +++ b/harmony/src/domain/topology/k8s.rs @@ -17,7 +17,7 @@ use kube::{ }; use log::{debug, error, trace}; use serde::{Serialize, de::DeserializeOwned}; -use serde_json::json; +use serde_json::{Value, json}; use similar::TextDiff; use tokio::io::AsyncReadExt; @@ -53,6 +53,21 @@ impl K8sClient { }) } + pub async fn get_resource_json_value( + &self, + name: &str, + namespace: Option<&str>, + gvk: &GroupVersionKind, + ) -> Result { + let gvk = ApiResource::from_gvk(gvk); + let resource: Api = if let Some(ns) = namespace { + Api::namespaced_with(self.client.clone(), ns, &gvk) + } else { + Api::default_namespaced_with(self.client.clone(), &gvk) + }; + Ok(resource.get(name).await?) + } + pub async fn get_deployment( &self, name: &str, diff --git a/harmony/src/modules/application/features/continuous_delivery.rs b/harmony/src/modules/application/features/continuous_delivery.rs index 7b447d0..1bc2d9d 100644 --- a/harmony/src/modules/application/features/continuous_delivery.rs +++ b/harmony/src/modules/application/features/continuous_delivery.rs @@ -176,18 +176,18 @@ impl< } target => { info!("Deploying {} to target {target:?}", self.application.name()); + let score = ArgoHelmScore { - namespace: "harmony-example-rust-webapp".to_string(), + namespace: format!("{}", self.application.name()), openshift: true, - domain: "argo.harmonydemo.apps.ncd0.harmony.mcd".to_string(), argo_apps: vec![ArgoApplication::from(CDApplicationConfig { // helm pull oci://hub.nationtech.io/harmony/harmony-example-rust-webapp-chart --version 0.1.0 version: Version::from("0.1.0").unwrap(), helm_chart_repo_url: "hub.nationtech.io/harmony".to_string(), - helm_chart_name: "harmony-example-rust-webapp-chart".to_string(), + helm_chart_name: format!("{}-chart", self.application.name()), values_overrides: None, - name: "harmony-demo-rust-webapp".to_string(), - namespace: "harmony-example-rust-webapp".to_string(), + name: format!("{}", self.application.name()), + namespace: format!("{}", self.application.name()), })], }; score diff --git a/harmony/src/modules/application/features/helm_argocd_score.rs b/harmony/src/modules/application/features/helm_argocd_score.rs index c439727..bfa3d8b 100644 --- a/harmony/src/modules/application/features/helm_argocd_score.rs +++ b/harmony/src/modules/application/features/helm_argocd_score.rs @@ -1,7 +1,10 @@ use async_trait::async_trait; +use kube::{Api, api::GroupVersionKind}; +use log::{debug, warn}; use non_blank_string_rs::NonBlankString; use serde::Serialize; -use std::str::FromStr; +use serde::de::DeserializeOwned; +use std::{process::Command, str::FromStr, sync::Arc}; use crate::{ data::Version, @@ -9,7 +12,9 @@ use crate::{ inventory::Inventory, modules::helm::chart::{HelmChartScore, HelmRepository}, score::Score, - topology::{HelmCommand, K8sclient, Topology}, + topology::{ + HelmCommand, K8sclient, PreparationError, PreparationOutcome, Topology, k8s::K8sClient, + }, }; use harmony_types::id::Id; @@ -19,15 +24,13 @@ use super::ArgoApplication; pub struct ArgoHelmScore { pub namespace: String, pub openshift: bool, - pub domain: String, pub argo_apps: Vec, } impl Score for ArgoHelmScore { fn create_interpret(&self) -> Box> { - let helm_score = argo_helm_chart_score(&self.namespace, self.openshift, &self.domain); Box::new(ArgoInterpret { - score: helm_score, + score: self.clone(), argo_apps: self.argo_apps.clone(), }) } @@ -39,7 +42,7 @@ impl Score for ArgoHelmScore { #[derive(Debug)] pub struct ArgoInterpret { - score: HelmChartScore, + score: ArgoHelmScore, argo_apps: Vec, } @@ -50,9 +53,16 @@ impl Interpret for ArgoInterpret { inventory: &Inventory, topology: &T, ) -> Result { - self.score.interpret(inventory, topology).await?; - let k8s_client = topology.k8s_client().await?; + let domain = self + .get_host_domain(k8s_client.clone(), self.score.openshift) + .await?; + let domain = format!("argo.{domain}"); + let helm_score = + argo_helm_chart_score(&self.score.namespace, self.score.openshift, &domain); + + helm_score.interpret(inventory, topology).await?; + k8s_client .apply_yaml_many(&self.argo_apps.iter().map(|a| a.to_yaml()).collect(), None) .await @@ -85,6 +95,38 @@ impl Interpret for ArgoInterpret { } } +impl ArgoInterpret { + pub async fn get_host_domain( + &self, + client: Arc, + openshift: bool, + ) -> Result { + //This should be the job of the topology to determine if we are in + //openshift, potentially we need on openshift topology the same way we create a + //localhosttopology + match openshift { + true => { + 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?; + + match ic.data["status"]["domain"].as_str() { + Some(domain) => return Ok(domain.to_string()), + None => return Err(InterpretError::new("Could not find domain".to_string())), + } + } + false => { + todo!() + } + }; + } +} + pub fn argo_helm_chart_score(namespace: &str, openshift: bool, domain: &str) -> HelmChartScore { let values = format!( r#" @@ -660,7 +702,7 @@ server: # nginx.ingress.kubernetes.io/ssl-passthrough: "true" # -- Defines which ingress controller will implement the resource - ingressClassName: "" + ingressClassName: "openshift-default" # -- Argo CD server hostname # @default -- `""` (defaults to global.domain)