From f073b7e5fb15bf7da0d4fb9fdcee60aa665c854c Mon Sep 17 00:00:00 2001 From: Willem Date: Wed, 24 Sep 2025 13:28:46 -0400 Subject: [PATCH 1/4] feat:added k8s flavour to k8s_aywhere topology to be able to get the type of cluster --- harmony/src/domain/topology/k8s.rs | 14 ++++- harmony/src/domain/topology/k8s_anywhere.rs | 62 +++++++++++++++++++++ 2 files changed, 75 insertions(+), 1 deletion(-) diff --git a/harmony/src/domain/topology/k8s.rs b/harmony/src/domain/topology/k8s.rs index 88bd2e8..144533c 100644 --- a/harmony/src/domain/topology/k8s.rs +++ b/harmony/src/domain/topology/k8s.rs @@ -2,9 +2,10 @@ use derive_new::new; use k8s_openapi::{ ClusterResourceScope, NamespaceResourceScope, api::{apps::v1::Deployment, core::v1::Pod}, + apimachinery::pkg::version::Info, }; use kube::{ - Client, Config, Error, Resource, + Client, Config, Discovery, Error, Resource, api::{Api, AttachParams, DeleteParams, ListParams, Patch, PatchParams, ResourceExt}, config::{KubeConfigOptions, Kubeconfig}, core::ErrorResponse, @@ -53,6 +54,17 @@ impl K8sClient { }) } + pub async fn get_apiserver_version(&self) -> Result { + let client: Client = self.client.clone(); + let version_info: Info = client.apiserver_version().await?; + Ok(version_info) + } + + pub async fn discovery(&self) -> Result { + let discovery: Discovery = Discovery::new(self.client.clone()).run().await?; + Ok(discovery) + } + pub async fn get_resource_json_value( &self, name: &str, diff --git a/harmony/src/domain/topology/k8s_anywhere.rs b/harmony/src/domain/topology/k8s_anywhere.rs index 53b6436..e6c37ea 100644 --- a/harmony/src/domain/topology/k8s_anywhere.rs +++ b/harmony/src/domain/topology/k8s_anywhere.rs @@ -47,6 +47,13 @@ struct K8sState { message: String, } +#[derive(Debug, Clone)] +pub enum K8sFlavour { + Okd, + K3d, + K8s, +} + #[derive(Debug, Clone)] enum K8sSource { LocalK3d, @@ -57,6 +64,7 @@ enum K8sSource { pub struct K8sAnywhereTopology { k8s_state: Arc>>, tenant_manager: Arc>, + flavour: Arc>, config: Arc, } @@ -162,6 +170,7 @@ impl K8sAnywhereTopology { Self { k8s_state: Arc::new(OnceCell::new()), tenant_manager: Arc::new(OnceCell::new()), + flavour: Arc::new(OnceCell::new()), config: Arc::new(K8sAnywhereConfig::from_env()), } } @@ -170,10 +179,63 @@ impl K8sAnywhereTopology { Self { k8s_state: Arc::new(OnceCell::new()), tenant_manager: Arc::new(OnceCell::new()), + flavour: Arc::new(OnceCell::new()), config: Arc::new(config), } } + pub async fn get_k8s_flavour(&self) -> K8sFlavour { + self.flavour + .get_or_try_init(async || { + let client = self.k8s_client().await.unwrap(); + + let discovery = client.discovery().await.map_err(|e| { + PreparationError::new(format!("Could not discover API groups: {}", e)) + })?; + + let version = client.get_apiserver_version().await.map_err(|e| { + PreparationError::new(format!("Could not get server version: {}", e)) + })?; + + let rules: &[&dyn Fn() -> Option] = &[ + // OpenShift / OKD + &|| { + discovery + .groups() + .any(|g| g.name().ends_with("openshift.io")) + .then_some(K8sFlavour::Okd) + }, + // K3d / K3s + &|| { + version + .git_version + .contains("k3s") + .then_some(K8sFlavour::K3d) + }, + // Vanilla Kubernetes + &|| { + if !discovery + .groups() + .any(|g| g.name().ends_with("openshift.io")) + && !version.git_version.contains("k3s") + && !version.git_version.contains("k3d") + { + Some(K8sFlavour::K8s) + } else { + None + } + }, + ]; + + rules.iter().find_map(|rule| rule()).ok_or_else(|| { + PreparationError::new("Unknown Kubernetes cluster flavour".to_string()) + }) + }) + .await + .unwrap() + .clone() + } + async fn get_cluster_observability_operator_prometheus_application_score( &self, sender: RHOBObservability, From 1cec398d4d4185adec39273e2b329a20da00f6f4 Mon Sep 17 00:00:00 2001 From: Willem Date: Mon, 29 Sep 2025 11:29:34 -0400 Subject: [PATCH 2/4] fix: modifed naming scheme to OpenshiftFamily, K3sFamily, and defaultswitched discovery of openshiftfamily to look for projet.openshift.io --- harmony/src/domain/topology/k8s_anywhere.rs | 28 +++++++++++---------- 1 file changed, 15 insertions(+), 13 deletions(-) diff --git a/harmony/src/domain/topology/k8s_anywhere.rs b/harmony/src/domain/topology/k8s_anywhere.rs index e6c37ea..758198f 100644 --- a/harmony/src/domain/topology/k8s_anywhere.rs +++ b/harmony/src/domain/topology/k8s_anywhere.rs @@ -48,10 +48,10 @@ struct K8sState { } #[derive(Debug, Clone)] -pub enum K8sFlavour { - Okd, - K3d, - K8s, +pub enum KubernetesDistribution { + OpenshiftFamily, + K3sFamily, + Default, } #[derive(Debug, Clone)] @@ -64,7 +64,7 @@ enum K8sSource { pub struct K8sAnywhereTopology { k8s_state: Arc>>, tenant_manager: Arc>, - flavour: Arc>, + flavour: Arc>, config: Arc, } @@ -184,7 +184,7 @@ impl K8sAnywhereTopology { } } - pub async fn get_k8s_flavour(&self) -> K8sFlavour { + pub async fn get_k8s_distribution(&self) -> KubernetesDistribution { self.flavour .get_or_try_init(async || { let client = self.k8s_client().await.unwrap(); @@ -197,22 +197,22 @@ impl K8sAnywhereTopology { PreparationError::new(format!("Could not get server version: {}", e)) })?; - let rules: &[&dyn Fn() -> Option] = &[ + let rules: &[&dyn Fn() -> Option] = &[ // OpenShift / OKD &|| { discovery .groups() - .any(|g| g.name().ends_with("openshift.io")) - .then_some(K8sFlavour::Okd) + .any(|g| g.name() == "project.openshift.io") + .then_some(KubernetesDistribution::OpenshiftFamily) }, // K3d / K3s &|| { version .git_version .contains("k3s") - .then_some(K8sFlavour::K3d) + .then_some(KubernetesDistribution::K3sFamily) }, - // Vanilla Kubernetes + // Fallback Kubernetes K8s &|| { if !discovery .groups() @@ -220,7 +220,7 @@ impl K8sAnywhereTopology { && !version.git_version.contains("k3s") && !version.git_version.contains("k3d") { - Some(K8sFlavour::K8s) + Some(KubernetesDistribution::Default) } else { None } @@ -228,7 +228,9 @@ impl K8sAnywhereTopology { ]; rules.iter().find_map(|rule| rule()).ok_or_else(|| { - PreparationError::new("Unknown Kubernetes cluster flavour".to_string()) + PreparationError::new( + "Unable to detect Kubernetes cluster distribution".to_string(), + ) }) }) .await From 2d3c32469c49107bf0e4a625b8e6f39afe27740c Mon Sep 17 00:00:00 2001 From: Jean-Gabriel Gill-Couture Date: Tue, 30 Sep 2025 22:59:50 -0400 Subject: [PATCH 3/4] chore: Simplify k8s flavour detection algorithm and do not unwrap when it cannot be detected, just return Err --- harmony/src/domain/topology/k8s_anywhere.rs | 51 ++++++--------------- 1 file changed, 14 insertions(+), 37 deletions(-) diff --git a/harmony/src/domain/topology/k8s_anywhere.rs b/harmony/src/domain/topology/k8s_anywhere.rs index 758198f..5e448b8 100644 --- a/harmony/src/domain/topology/k8s_anywhere.rs +++ b/harmony/src/domain/topology/k8s_anywhere.rs @@ -184,7 +184,7 @@ impl K8sAnywhereTopology { } } - pub async fn get_k8s_distribution(&self) -> KubernetesDistribution { + pub async fn get_k8s_distribution(&self) -> Result<&KubernetesDistribution, PreparationError> { self.flavour .get_or_try_init(async || { let client = self.k8s_client().await.unwrap(); @@ -197,45 +197,22 @@ impl K8sAnywhereTopology { PreparationError::new(format!("Could not get server version: {}", e)) })?; - let rules: &[&dyn Fn() -> Option] = &[ - // OpenShift / OKD - &|| { - discovery - .groups() - .any(|g| g.name() == "project.openshift.io") - .then_some(KubernetesDistribution::OpenshiftFamily) - }, - // K3d / K3s - &|| { - version - .git_version - .contains("k3s") - .then_some(KubernetesDistribution::K3sFamily) - }, - // Fallback Kubernetes K8s - &|| { - if !discovery - .groups() - .any(|g| g.name().ends_with("openshift.io")) - && !version.git_version.contains("k3s") - && !version.git_version.contains("k3d") - { - Some(KubernetesDistribution::Default) - } else { - None - } - }, - ]; + // OpenShift / OKD + if discovery + .groups() + .any(|g| g.name() == "project.openshift.io") + { + return Ok(KubernetesDistribution::OpenshiftFamily); + } - rules.iter().find_map(|rule| rule()).ok_or_else(|| { - PreparationError::new( - "Unable to detect Kubernetes cluster distribution".to_string(), - ) - }) + // K3d / K3s + if version.git_version.contains("k3s") { + return Ok(KubernetesDistribution::K3sFamily); + } + + return Ok(KubernetesDistribution::Default); }) .await - .unwrap() - .clone() } async fn get_cluster_observability_operator_prometheus_application_score( From 2a48d51479a5ad004aa8e9b47c0a08bd9816eb21 Mon Sep 17 00:00:00 2001 From: Willem Date: Tue, 21 Oct 2025 11:09:45 -0400 Subject: [PATCH 4/4] fix: naming of k8s distribution --- harmony/src/domain/topology/k8s_anywhere.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/harmony/src/domain/topology/k8s_anywhere.rs b/harmony/src/domain/topology/k8s_anywhere.rs index 5e448b8..1c2f764 100644 --- a/harmony/src/domain/topology/k8s_anywhere.rs +++ b/harmony/src/domain/topology/k8s_anywhere.rs @@ -64,7 +64,7 @@ enum K8sSource { pub struct K8sAnywhereTopology { k8s_state: Arc>>, tenant_manager: Arc>, - flavour: Arc>, + k8s_distribution: Arc>, config: Arc, } @@ -170,7 +170,7 @@ impl K8sAnywhereTopology { Self { k8s_state: Arc::new(OnceCell::new()), tenant_manager: Arc::new(OnceCell::new()), - flavour: Arc::new(OnceCell::new()), + k8s_distribution: Arc::new(OnceCell::new()), config: Arc::new(K8sAnywhereConfig::from_env()), } } @@ -179,13 +179,13 @@ impl K8sAnywhereTopology { Self { k8s_state: Arc::new(OnceCell::new()), tenant_manager: Arc::new(OnceCell::new()), - flavour: Arc::new(OnceCell::new()), + k8s_distribution: Arc::new(OnceCell::new()), config: Arc::new(config), } } pub async fn get_k8s_distribution(&self) -> Result<&KubernetesDistribution, PreparationError> { - self.flavour + self.k8s_distribution .get_or_try_init(async || { let client = self.k8s_client().await.unwrap();