From f073b7e5fb15bf7da0d4fb9fdcee60aa665c854c Mon Sep 17 00:00:00 2001 From: Willem Date: Wed, 24 Sep 2025 13:28:46 -0400 Subject: [PATCH] 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,