diff --git a/harmony/src/domain/topology/ha_cluster.rs b/harmony/src/domain/topology/ha_cluster.rs index 0e9230b..d2af961 100644 --- a/harmony/src/domain/topology/ha_cluster.rs +++ b/harmony/src/domain/topology/ha_cluster.rs @@ -3,6 +3,8 @@ use harmony_macros::ip; use harmony_types::net::MacAddress; use crate::executors::ExecutorError; +use crate::interpret::InterpretError; +use crate::interpret::Outcome; use super::DHCPStaticEntry; use super::DhcpServer; @@ -15,13 +17,13 @@ use super::IpAddress; use super::LoadBalancer; use super::LoadBalancerService; use super::LogicalHost; -use super::OcK8sclient; +use super::K8sclient; use super::Router; use super::TftpServer; use super::Topology; use super::Url; -use super::openshift::OpenshiftClient; +use super::k8s::K8sClient; use std::sync::Arc; #[derive(Debug, Clone)] @@ -40,16 +42,20 @@ pub struct HAClusterTopology { pub switch: Vec, } +#[async_trait] impl Topology for HAClusterTopology { fn name(&self) -> &str { todo!() } + async fn ensure_ready(&self) -> Result { + todo!("ensure_ready, not entirely sure what it should do here, probably something like verify that the hosts are reachable and all services are up and ready.") + } } #[async_trait] -impl OcK8sclient for HAClusterTopology { - async fn oc_client(&self) -> Result, kube::Error> { - Ok(Arc::new(OpenshiftClient::try_default().await?)) +impl K8sclient for HAClusterTopology { + async fn k8s_client(&self) -> Result, kube::Error> { + Ok(Arc::new(K8sClient::try_default().await?)) } } diff --git a/harmony/src/domain/topology/openshift.rs b/harmony/src/domain/topology/k8s.rs similarity index 96% rename from harmony/src/domain/topology/openshift.rs rename to harmony/src/domain/topology/k8s.rs index 1790a06..ed345ee 100644 --- a/harmony/src/domain/topology/openshift.rs +++ b/harmony/src/domain/topology/k8s.rs @@ -2,11 +2,11 @@ use k8s_openapi::NamespaceResourceScope; use kube::{Api, Client, Error, Resource, api::PostParams}; use serde::de::DeserializeOwned; -pub struct OpenshiftClient { +pub struct K8sClient { client: Client, } -impl OpenshiftClient { +impl K8sClient { pub async fn try_default() -> Result { Ok(Self { client: Client::try_default().await?, diff --git a/harmony/src/domain/topology/k8s_anywhere.rs b/harmony/src/domain/topology/k8s_anywhere.rs new file mode 100644 index 0000000..78e4a0e --- /dev/null +++ b/harmony/src/domain/topology/k8s_anywhere.rs @@ -0,0 +1,119 @@ +use async_trait::async_trait; +use log::info; +use tokio::sync::OnceCell; + +use crate::interpret::{InterpretError, Outcome}; + +use super::{Topology, k8s::K8sClient}; + +struct K8sState { + client: K8sClient, + source: K8sSource, + message: String, +} + +enum K8sSource { + Existing, + K3d, + // TODO: Add variants for cloud providers like AwsEks, Gke, Aks +} + +pub struct K8sAnywhereTopology { + k8s_state: OnceCell>, +} + +impl K8sAnywhereTopology { + async fn try_load_default_kubeconfig(&self) -> Option { + todo!("Use kube-rs default behavior to load system kubeconfig"); + } + + async fn try_load_kubeconfig(&self, path: &str) -> Option { + todo!("Use kube-rs to load kubeconfig at path {path}"); + } + + async fn try_install_k3d(&self) -> Result { + todo!( + "Create Maestro with LocalDockerTopology or something along these lines and run a K3dInstallationScore on it" + ) + } + + 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()), + 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") + .map_or_else(|_| false, |v| v.parse().ok().unwrap_or(false)), + }; + + if k8s_anywhere_config.use_system_kubeconfig { + match self.try_load_default_kubeconfig().await { + Some(client) => todo!(), + None => todo!(), + } + } + + if let Some(kubeconfig) = k8s_anywhere_config.kubeconfig { + match self.try_load_kubeconfig(&kubeconfig).await { + Some(client) => todo!(), + None => todo!(), + } + } + info!("No kubernetes configuration found"); + + if !k8s_anywhere_config.autoinstall { + info!( + "Harmony autoinstallation is not activated, do you wish to launch autoinstallation?" + ); + todo!("Prompt user"); + } + + match self.try_install_k3d().await { + Ok(client) => todo!(), + Err(_) => todo!(), + } + } +} + +struct K8sAnywhereConfig { + /// The path of the KUBECONFIG file that Harmony should use to interact with the Kubernetes + /// cluster + /// + /// Default : None + kubeconfig: Option, + + /// Whether to use the system KUBECONFIG, either the environment variable or the file in the + /// default or configured location + /// + /// Default : false + use_system_kubeconfig: bool, + + /// Whether to install automatically a kubernetes cluster + /// + /// When enabled, autoinstall will setup a K3D cluster on the localhost. https://k3d.io/stable/ + /// + /// Default: true + autoinstall: bool, +} + +#[async_trait] +impl Topology for K8sAnywhereTopology { + fn name(&self) -> &str { + todo!() + } + + async fn ensure_ready(&self) -> Result { + match self + .k8s_state + .get_or_try_init(|| self.try_get_or_install_k8s_client()) + .await? + { + Some(k8s_state) => Ok(Outcome::success(k8s_state.message.clone())), + None => Err(InterpretError::new( + "No K8s client could be found or installed".to_string(), + )), + } + } +} diff --git a/harmony/src/domain/topology/mod.rs b/harmony/src/domain/topology/mod.rs index 16525f5..29e12fc 100644 --- a/harmony/src/domain/topology/mod.rs +++ b/harmony/src/domain/topology/mod.rs @@ -1,8 +1,10 @@ mod ha_cluster; mod host_binding; mod http; +mod k8s_anywhere; +pub use k8s_anywhere::*; mod load_balancer; -pub mod openshift; +pub mod k8s; mod router; mod tftp; use async_trait::async_trait; diff --git a/harmony/src/domain/topology/network.rs b/harmony/src/domain/topology/network.rs index 13d1902..d4463ae 100644 --- a/harmony/src/domain/topology/network.rs +++ b/harmony/src/domain/topology/network.rs @@ -6,7 +6,7 @@ use serde::Serialize; use crate::executors::ExecutorError; -use super::{IpAddress, LogicalHost, openshift::OpenshiftClient}; +use super::{IpAddress, LogicalHost, k8s::K8sClient}; #[derive(Debug)] pub struct DHCPStaticEntry { @@ -42,8 +42,8 @@ pub struct NetworkDomain { pub name: String, } #[async_trait] -pub trait OcK8sclient: Send + Sync + std::fmt::Debug { - async fn oc_client(&self) -> Result, kube::Error>; +pub trait K8sclient: Send + Sync + std::fmt::Debug { + async fn k8s_client(&self) -> Result, kube::Error>; } #[async_trait] diff --git a/harmony/src/modules/k8s/deployment.rs b/harmony/src/modules/k8s/deployment.rs index cd2ad90..9e7178f 100644 --- a/harmony/src/modules/k8s/deployment.rs +++ b/harmony/src/modules/k8s/deployment.rs @@ -5,7 +5,7 @@ use serde_json::json; use crate::{ interpret::Interpret, score::Score, - topology::{OcK8sclient, Topology}, + topology::{K8sclient, Topology}, }; use super::resource::{K8sResourceInterpret, K8sResourceScore}; @@ -16,7 +16,7 @@ pub struct K8sDeploymentScore { pub image: String, } -impl Score for K8sDeploymentScore { +impl Score for K8sDeploymentScore { fn create_interpret(&self) -> Box> { let deployment: Deployment = serde_json::from_value(json!( { diff --git a/harmony/src/modules/k8s/resource.rs b/harmony/src/modules/k8s/resource.rs index 505c4a4..4e54be7 100644 --- a/harmony/src/modules/k8s/resource.rs +++ b/harmony/src/modules/k8s/resource.rs @@ -8,7 +8,7 @@ use crate::{ interpret::{Interpret, InterpretError, InterpretName, InterpretStatus, Outcome}, inventory::Inventory, score::Score, - topology::{OcK8sclient, Topology}, + topology::{K8sclient, Topology}, }; #[derive(Debug, Clone, Serialize)] @@ -63,7 +63,7 @@ impl< + Default + Send + Sync, - T: Topology + OcK8sclient, + T: Topology + K8sclient, > Interpret for K8sResourceInterpret where ::DynamicType: Default, @@ -74,7 +74,7 @@ where topology: &T, ) -> Result { topology - .oc_client() + .k8s_client() .await .expect("Environment should provide enough information to instanciate a client") .apply_namespaced(&self.score.resource) diff --git a/harmony/src/modules/lamp.rs b/harmony/src/modules/lamp.rs index ef7227c..55eefdd 100644 --- a/harmony/src/modules/lamp.rs +++ b/harmony/src/modules/lamp.rs @@ -9,7 +9,7 @@ use crate::{ inventory::Inventory, modules::k8s::deployment::K8sDeploymentScore, score::Score, - topology::{OcK8sclient, Topology, Url}, + topology::{K8sclient, Topology, Url}, }; #[derive(Debug, Clone, Serialize)] @@ -51,7 +51,7 @@ pub struct LAMPInterpret { } #[async_trait] -impl Interpret for LAMPInterpret { +impl Interpret for LAMPInterpret { async fn execute( &self, inventory: &Inventory,