feat: Introduce K8sAnywhereTopology and refactor Kubernetes interactions
This commit introduces a new topology, `K8sAnywhereTopology`, designed to handle Kubernetes deployments more flexibly. Key changes include: - Introduced `K8sAnywhereTopology` to encapsulate Kubernetes client management and configuration. - Refactored existing Kubernetes-related code to utilize the new topology. - Updated `OcK8sclient` to `K8sclient` across modules (k8s, lamp, deployment, resource) for consistency. - Ensured all relevant modules now interface with Kubernetes through the `K8sclient` trait. This change promotes a more modular and maintainable codebase for Kubernetes integrations within Harmony.
This commit is contained in:
parent
027114c48c
commit
6812d05849
@ -3,6 +3,8 @@ use harmony_macros::ip;
|
|||||||
use harmony_types::net::MacAddress;
|
use harmony_types::net::MacAddress;
|
||||||
|
|
||||||
use crate::executors::ExecutorError;
|
use crate::executors::ExecutorError;
|
||||||
|
use crate::interpret::InterpretError;
|
||||||
|
use crate::interpret::Outcome;
|
||||||
|
|
||||||
use super::DHCPStaticEntry;
|
use super::DHCPStaticEntry;
|
||||||
use super::DhcpServer;
|
use super::DhcpServer;
|
||||||
@ -15,13 +17,13 @@ use super::IpAddress;
|
|||||||
use super::LoadBalancer;
|
use super::LoadBalancer;
|
||||||
use super::LoadBalancerService;
|
use super::LoadBalancerService;
|
||||||
use super::LogicalHost;
|
use super::LogicalHost;
|
||||||
use super::OcK8sclient;
|
use super::K8sclient;
|
||||||
use super::Router;
|
use super::Router;
|
||||||
use super::TftpServer;
|
use super::TftpServer;
|
||||||
|
|
||||||
use super::Topology;
|
use super::Topology;
|
||||||
use super::Url;
|
use super::Url;
|
||||||
use super::openshift::OpenshiftClient;
|
use super::k8s::K8sClient;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
@ -40,16 +42,20 @@ pub struct HAClusterTopology {
|
|||||||
pub switch: Vec<LogicalHost>,
|
pub switch: Vec<LogicalHost>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[async_trait]
|
||||||
impl Topology for HAClusterTopology {
|
impl Topology for HAClusterTopology {
|
||||||
fn name(&self) -> &str {
|
fn name(&self) -> &str {
|
||||||
todo!()
|
todo!()
|
||||||
}
|
}
|
||||||
|
async fn ensure_ready(&self) -> Result<Outcome, InterpretError> {
|
||||||
|
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]
|
#[async_trait]
|
||||||
impl OcK8sclient for HAClusterTopology {
|
impl K8sclient for HAClusterTopology {
|
||||||
async fn oc_client(&self) -> Result<Arc<OpenshiftClient>, kube::Error> {
|
async fn k8s_client(&self) -> Result<Arc<K8sClient>, kube::Error> {
|
||||||
Ok(Arc::new(OpenshiftClient::try_default().await?))
|
Ok(Arc::new(K8sClient::try_default().await?))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -2,11 +2,11 @@ use k8s_openapi::NamespaceResourceScope;
|
|||||||
use kube::{Api, Client, Error, Resource, api::PostParams};
|
use kube::{Api, Client, Error, Resource, api::PostParams};
|
||||||
use serde::de::DeserializeOwned;
|
use serde::de::DeserializeOwned;
|
||||||
|
|
||||||
pub struct OpenshiftClient {
|
pub struct K8sClient {
|
||||||
client: Client,
|
client: Client,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl OpenshiftClient {
|
impl K8sClient {
|
||||||
pub async fn try_default() -> Result<Self, Error> {
|
pub async fn try_default() -> Result<Self, Error> {
|
||||||
Ok(Self {
|
Ok(Self {
|
||||||
client: Client::try_default().await?,
|
client: Client::try_default().await?,
|
||||||
119
harmony/src/domain/topology/k8s_anywhere.rs
Normal file
119
harmony/src/domain/topology/k8s_anywhere.rs
Normal file
@ -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<Option<K8sState>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl K8sAnywhereTopology {
|
||||||
|
async fn try_load_default_kubeconfig(&self) -> Option<K8sClient> {
|
||||||
|
todo!("Use kube-rs default behavior to load system kubeconfig");
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn try_load_kubeconfig(&self, path: &str) -> Option<K8sClient> {
|
||||||
|
todo!("Use kube-rs to load kubeconfig at path {path}");
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn try_install_k3d(&self) -> Result<K8sClient, InterpretError> {
|
||||||
|
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<Option<K8sState>, 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<String>,
|
||||||
|
|
||||||
|
/// 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<Outcome, InterpretError> {
|
||||||
|
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(),
|
||||||
|
)),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -1,8 +1,10 @@
|
|||||||
mod ha_cluster;
|
mod ha_cluster;
|
||||||
mod host_binding;
|
mod host_binding;
|
||||||
mod http;
|
mod http;
|
||||||
|
mod k8s_anywhere;
|
||||||
|
pub use k8s_anywhere::*;
|
||||||
mod load_balancer;
|
mod load_balancer;
|
||||||
pub mod openshift;
|
pub mod k8s;
|
||||||
mod router;
|
mod router;
|
||||||
mod tftp;
|
mod tftp;
|
||||||
use async_trait::async_trait;
|
use async_trait::async_trait;
|
||||||
|
|||||||
@ -6,7 +6,7 @@ use serde::Serialize;
|
|||||||
|
|
||||||
use crate::executors::ExecutorError;
|
use crate::executors::ExecutorError;
|
||||||
|
|
||||||
use super::{IpAddress, LogicalHost, openshift::OpenshiftClient};
|
use super::{IpAddress, LogicalHost, k8s::K8sClient};
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct DHCPStaticEntry {
|
pub struct DHCPStaticEntry {
|
||||||
@ -42,8 +42,8 @@ pub struct NetworkDomain {
|
|||||||
pub name: String,
|
pub name: String,
|
||||||
}
|
}
|
||||||
#[async_trait]
|
#[async_trait]
|
||||||
pub trait OcK8sclient: Send + Sync + std::fmt::Debug {
|
pub trait K8sclient: Send + Sync + std::fmt::Debug {
|
||||||
async fn oc_client(&self) -> Result<Arc<OpenshiftClient>, kube::Error>;
|
async fn k8s_client(&self) -> Result<Arc<K8sClient>, kube::Error>;
|
||||||
}
|
}
|
||||||
|
|
||||||
#[async_trait]
|
#[async_trait]
|
||||||
|
|||||||
@ -5,7 +5,7 @@ use serde_json::json;
|
|||||||
use crate::{
|
use crate::{
|
||||||
interpret::Interpret,
|
interpret::Interpret,
|
||||||
score::Score,
|
score::Score,
|
||||||
topology::{OcK8sclient, Topology},
|
topology::{K8sclient, Topology},
|
||||||
};
|
};
|
||||||
|
|
||||||
use super::resource::{K8sResourceInterpret, K8sResourceScore};
|
use super::resource::{K8sResourceInterpret, K8sResourceScore};
|
||||||
@ -16,7 +16,7 @@ pub struct K8sDeploymentScore {
|
|||||||
pub image: String,
|
pub image: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T: Topology + OcK8sclient> Score<T> for K8sDeploymentScore {
|
impl<T: Topology + K8sclient> Score<T> for K8sDeploymentScore {
|
||||||
fn create_interpret(&self) -> Box<dyn Interpret<T>> {
|
fn create_interpret(&self) -> Box<dyn Interpret<T>> {
|
||||||
let deployment: Deployment = serde_json::from_value(json!(
|
let deployment: Deployment = serde_json::from_value(json!(
|
||||||
{
|
{
|
||||||
|
|||||||
@ -8,7 +8,7 @@ use crate::{
|
|||||||
interpret::{Interpret, InterpretError, InterpretName, InterpretStatus, Outcome},
|
interpret::{Interpret, InterpretError, InterpretName, InterpretStatus, Outcome},
|
||||||
inventory::Inventory,
|
inventory::Inventory,
|
||||||
score::Score,
|
score::Score,
|
||||||
topology::{OcK8sclient, Topology},
|
topology::{K8sclient, Topology},
|
||||||
};
|
};
|
||||||
|
|
||||||
#[derive(Debug, Clone, Serialize)]
|
#[derive(Debug, Clone, Serialize)]
|
||||||
@ -63,7 +63,7 @@ impl<
|
|||||||
+ Default
|
+ Default
|
||||||
+ Send
|
+ Send
|
||||||
+ Sync,
|
+ Sync,
|
||||||
T: Topology + OcK8sclient,
|
T: Topology + K8sclient,
|
||||||
> Interpret<T> for K8sResourceInterpret<K>
|
> Interpret<T> for K8sResourceInterpret<K>
|
||||||
where
|
where
|
||||||
<K as kube::Resource>::DynamicType: Default,
|
<K as kube::Resource>::DynamicType: Default,
|
||||||
@ -74,7 +74,7 @@ where
|
|||||||
topology: &T,
|
topology: &T,
|
||||||
) -> Result<Outcome, InterpretError> {
|
) -> Result<Outcome, InterpretError> {
|
||||||
topology
|
topology
|
||||||
.oc_client()
|
.k8s_client()
|
||||||
.await
|
.await
|
||||||
.expect("Environment should provide enough information to instanciate a client")
|
.expect("Environment should provide enough information to instanciate a client")
|
||||||
.apply_namespaced(&self.score.resource)
|
.apply_namespaced(&self.score.resource)
|
||||||
|
|||||||
@ -9,7 +9,7 @@ use crate::{
|
|||||||
inventory::Inventory,
|
inventory::Inventory,
|
||||||
modules::k8s::deployment::K8sDeploymentScore,
|
modules::k8s::deployment::K8sDeploymentScore,
|
||||||
score::Score,
|
score::Score,
|
||||||
topology::{OcK8sclient, Topology, Url},
|
topology::{K8sclient, Topology, Url},
|
||||||
};
|
};
|
||||||
|
|
||||||
#[derive(Debug, Clone, Serialize)]
|
#[derive(Debug, Clone, Serialize)]
|
||||||
@ -51,7 +51,7 @@ pub struct LAMPInterpret {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[async_trait]
|
#[async_trait]
|
||||||
impl<T: Topology + OcK8sclient> Interpret<T> for LAMPInterpret {
|
impl<T: Topology + K8sclient> Interpret<T> for LAMPInterpret {
|
||||||
async fn execute(
|
async fn execute(
|
||||||
&self,
|
&self,
|
||||||
inventory: &Inventory,
|
inventory: &Inventory,
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user