feat: add more to the tenantmanager k8s impl #46
| @ -1,4 +1,4 @@ | |||||||
| use std::{process::Command, sync::Arc}; | use std::{io::Error, process::Command, sync::Arc}; | ||||||
| 
 | 
 | ||||||
| use async_trait::async_trait; | use async_trait::async_trait; | ||||||
| use inquire::Confirm; | use inquire::Confirm; | ||||||
| @ -6,6 +6,7 @@ use log::{info, warn}; | |||||||
| use tokio::sync::OnceCell; | use tokio::sync::OnceCell; | ||||||
| 
 | 
 | ||||||
| use crate::{ | use crate::{ | ||||||
|  |     executors::ExecutorError, | ||||||
|     interpret::{InterpretError, Outcome}, |     interpret::{InterpretError, Outcome}, | ||||||
|     inventory::Inventory, |     inventory::Inventory, | ||||||
|     maestro::Maestro, |     maestro::Maestro, | ||||||
| @ -13,7 +14,13 @@ use crate::{ | |||||||
|     topology::LocalhostTopology, |     topology::LocalhostTopology, | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| use super::{HelmCommand, K8sclient, Topology, k8s::K8sClient}; | use super::{ | ||||||
|  |     HelmCommand, K8sclient, Topology, | ||||||
|  |     k8s::K8sClient, | ||||||
|  |     tenant::{ | ||||||
|  |         ResourceLimits, TenantConfig, TenantManager, TenantNetworkPolicy, k8s::K8sTenantManager, | ||||||
|  |     }, | ||||||
|  | }; | ||||||
| 
 | 
 | ||||||
| struct K8sState { | struct K8sState { | ||||||
|     client: Arc<K8sClient>, |     client: Arc<K8sClient>, | ||||||
| @ -21,6 +28,7 @@ struct K8sState { | |||||||
|     message: String, |     message: String, | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | #[derive(Debug)] | ||||||
| enum K8sSource { | enum K8sSource { | ||||||
|     LocalK3d, |     LocalK3d, | ||||||
|     Kubeconfig, |     Kubeconfig, | ||||||
| @ -28,6 +36,7 @@ enum K8sSource { | |||||||
| 
 | 
 | ||||||
| pub struct K8sAnywhereTopology { | pub struct K8sAnywhereTopology { | ||||||
|     k8s_state: OnceCell<Option<K8sState>>, |     k8s_state: OnceCell<Option<K8sState>>, | ||||||
|  |     tenant_manager: OnceCell<K8sTenantManager>, | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| #[async_trait] | #[async_trait] | ||||||
| @ -51,6 +60,7 @@ impl K8sAnywhereTopology { | |||||||
|     pub fn new() -> Self { |     pub fn new() -> Self { | ||||||
|         Self { |         Self { | ||||||
|             k8s_state: OnceCell::new(), |             k8s_state: OnceCell::new(), | ||||||
|  |             tenant_manager: OnceCell::new(), | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
| @ -159,6 +169,15 @@ impl K8sAnywhereTopology { | |||||||
| 
 | 
 | ||||||
|         Ok(Some(state)) |         Ok(Some(state)) | ||||||
|     } |     } | ||||||
|  | 
 | ||||||
|  |     fn get_k8s_tenant_manager(&self) -> Result<&K8sTenantManager, ExecutorError> { | ||||||
|  |         match self.tenant_manager.get() { | ||||||
|  |             Some(t) => Ok(t), | ||||||
|  |             None => Err(ExecutorError::UnexpectedError( | ||||||
|  |                 "K8sTenantManager not available".to_string(), | ||||||
|  |             )), | ||||||
|  |         } | ||||||
|  |     } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| struct K8sAnywhereConfig { | struct K8sAnywhereConfig { | ||||||
| @ -209,3 +228,38 @@ impl Topology for K8sAnywhereTopology { | |||||||
| } | } | ||||||
| 
 | 
 | ||||||
| impl HelmCommand for K8sAnywhereTopology {} | impl HelmCommand for K8sAnywhereTopology {} | ||||||
|  | 
 | ||||||
|  | #[async_trait] | ||||||
|  | impl TenantManager for K8sAnywhereTopology { | ||||||
|  |     async fn provision_tenant(&self, config: &TenantConfig) -> Result<(), ExecutorError> { | ||||||
|  |         self.get_k8s_tenant_manager()? | ||||||
|  |             .provision_tenant(config) | ||||||
|  |             .await | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     async fn update_tenant_resource_limits( | ||||||
|  |         &self, | ||||||
|  |         tenant_name: &str, | ||||||
|  |         new_limits: &ResourceLimits, | ||||||
|  |     ) -> Result<(), ExecutorError> { | ||||||
|  |         self.get_k8s_tenant_manager()? | ||||||
|  |             .update_tenant_resource_limits(tenant_name, new_limits) | ||||||
|  |             .await | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     async fn update_tenant_network_policy( | ||||||
|  |         &self, | ||||||
|  |         tenant_name: &str, | ||||||
|  |         new_policy: &TenantNetworkPolicy, | ||||||
|  |     ) -> Result<(), ExecutorError> { | ||||||
|  |         self.get_k8s_tenant_manager()? | ||||||
|  |             .update_tenant_network_policy(tenant_name, new_policy) | ||||||
|  |             .await | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     async fn deprovision_tenant(&self, tenant_name: &str) -> Result<(), ExecutorError> { | ||||||
|  |         self.get_k8s_tenant_manager()? | ||||||
|  |             .deprovision_tenant(tenant_name) | ||||||
|  |             .await | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | |||||||
							
								
								
									
										95
									
								
								harmony/src/domain/topology/tenant/k8s.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										95
									
								
								harmony/src/domain/topology/tenant/k8s.rs
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,95 @@ | |||||||
|  | use std::sync::Arc; | ||||||
|  | 
 | ||||||
|  | use crate::{executors::ExecutorError, topology::k8s::K8sClient}; | ||||||
|  | use async_trait::async_trait; | ||||||
|  | use derive_new::new; | ||||||
|  | use k8s_openapi::api::core::v1::Namespace; | ||||||
|  | use serde_json::json; | ||||||
|  | 
 | ||||||
|  | use super::{ResourceLimits, TenantConfig, TenantManager, TenantNetworkPolicy}; | ||||||
|  | 
 | ||||||
|  | #[derive(new)] | ||||||
|  | pub struct K8sTenantManager { | ||||||
|  |     k8s_client: Arc<K8sClient>, | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | #[async_trait] | ||||||
|  | impl TenantManager for K8sTenantManager { | ||||||
|  |     async fn provision_tenant(&self, config: &TenantConfig) -> Result<(), ExecutorError> { | ||||||
|  |         let namespace = json!( | ||||||
|  |             { | ||||||
|  |                 "apiVersion": "v1", | ||||||
|  |                 "kind": "Namespace", | ||||||
|  |                 "metadata": { | ||||||
|  |                     "labels": { | ||||||
|  |                         "harmony.nationtech.io/tenant.id": config.id, | ||||||
|  |                         "harmony.nationtech.io/tenant.name": config.name, | ||||||
|  |                     }, | ||||||
|  |                 "name": config.name, | ||||||
|  |                 }, | ||||||
|  |             } | ||||||
|  |         ); | ||||||
|  |         todo!("Validate that when tenant already exists (by id) that name has not changed"); | ||||||
|  | 
 | ||||||
|  |         let namespace: Namespace = serde_json::from_value(namespace).unwrap(); | ||||||
|  | 
 | ||||||
|  |         let resource_quota = json!( | ||||||
|  |          { | ||||||
|  |            "apiVersion": "v1", | ||||||
|  |            "kind": "List", | ||||||
|  |            "items": [ | ||||||
|  |              { | ||||||
|  |                "apiVersion": "v1", | ||||||
|  |                "kind": "ResourceQuota", | ||||||
|  |                "metadata": { | ||||||
|  |                  "name": config.name, | ||||||
|  |                  "labels": { | ||||||
|  |                   "harmony.nationtech.io/tenant.id": config.id, | ||||||
|  |                   "harmony.nationtech.io/tenant.name": config.name, | ||||||
|  |                  }, | ||||||
|  |                  "namespace": config.name, | ||||||
|  |                }, | ||||||
|  |                "spec": { | ||||||
|  |                  "hard": { | ||||||
|  |                    "limits.cpu": format!("{:.0}",config.resource_limits.cpu_limit_cores), | ||||||
|  |                    "limits.memory": format!("{:.3}Gi", config.resource_limits.memory_limit_gb), | ||||||
|  |                    "requests.cpu": format!("{:.0}",config.resource_limits.cpu_request_cores), | ||||||
|  |                    "requests.memory": format!("{:.3}Gi", config.resource_limits.memory_request_gb), | ||||||
|  |                    "requests.storage": format!("{:.3}", config.resource_limits.storage_total_gb), | ||||||
|  |                    "pods": "20", | ||||||
|  |                    "services": "10", | ||||||
|  |                    "configmaps": "30", | ||||||
|  |                    "secrets": "30", | ||||||
|  |                    "persistentvolumeclaims": "15", | ||||||
|  |                    "services.loadbalancers": "2", | ||||||
|  |                    "services.nodeports": "5", | ||||||
|  | 
 | ||||||
|  |                  } | ||||||
|  |                } | ||||||
|  |              } | ||||||
|  |            ] | ||||||
|  |          } | ||||||
|  | 
 | ||||||
|  |         ); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     async fn update_tenant_resource_limits( | ||||||
|  |         &self, | ||||||
|  |         tenant_name: &str, | ||||||
|  |         new_limits: &ResourceLimits, | ||||||
|  |     ) -> Result<(), ExecutorError> { | ||||||
|  |         todo!() | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     async fn update_tenant_network_policy( | ||||||
|  |         &self, | ||||||
|  |         tenant_name: &str, | ||||||
|  |         new_policy: &TenantNetworkPolicy, | ||||||
|  |     ) -> Result<(), ExecutorError> { | ||||||
|  |         todo!() | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     async fn deprovision_tenant(&self, tenant_name: &str) -> Result<(), ExecutorError> { | ||||||
|  |         todo!() | ||||||
|  |     } | ||||||
|  | } | ||||||
| @ -4,7 +4,7 @@ use async_trait::async_trait; | |||||||
| use crate::executors::ExecutorError; | use crate::executors::ExecutorError; | ||||||
| 
 | 
 | ||||||
| #[async_trait] | #[async_trait] | ||||||
| pub trait TenantManager: Send + Sync + std::fmt::Debug { | pub trait TenantManager { | ||||||
|     /// Provisions a new tenant based on the provided configuration.
 |     /// Provisions a new tenant based on the provided configuration.
 | ||||||
|     /// This operation should be idempotent; if a tenant with the same `config.name`
 |     /// This operation should be idempotent; if a tenant with the same `config.name`
 | ||||||
|     /// already exists and matches the config, it will succeed without changes.
 |     /// already exists and matches the config, it will succeed without changes.
 | ||||||
|  | |||||||
| @ -1,3 +1,4 @@ | |||||||
|  | pub mod k8s; | ||||||
| mod manager; | mod manager; | ||||||
| pub use manager::*; | pub use manager::*; | ||||||
| use serde::{Deserialize, Serialize}; | use serde::{Deserialize, Serialize}; | ||||||
| @ -29,17 +30,17 @@ pub struct TenantConfig { | |||||||
| #[derive(Debug, Clone, PartialEq, Serialize, Deserialize, Default)] | #[derive(Debug, Clone, PartialEq, Serialize, Deserialize, Default)] | ||||||
| pub struct ResourceLimits { | pub struct ResourceLimits { | ||||||
|     /// Requested/guaranteed CPU cores (e.g., 2.0).
 |     /// Requested/guaranteed CPU cores (e.g., 2.0).
 | ||||||
|     pub cpu_request_cores: Option<f32>, |     pub cpu_request_cores: f32, | ||||||
|     /// Maximum CPU cores the tenant can burst to (e.g., 4.0).
 |     /// Maximum CPU cores the tenant can burst to (e.g., 4.0).
 | ||||||
|     pub cpu_limit_cores: Option<f32>, |     pub cpu_limit_cores: f32, | ||||||
| 
 | 
 | ||||||
|     /// Requested/guaranteed memory in Gigabytes (e.g., 8.0).
 |     /// Requested/guaranteed memory in Gigabytes (e.g., 8.0).
 | ||||||
|     pub memory_request_gb: Option<f32>, |     pub memory_request_gb: f32, | ||||||
|     /// Maximum memory in Gigabytes tenant can burst to (e.g., 16.0).
 |     /// Maximum memory in Gigabytes tenant can burst to (e.g., 16.0).
 | ||||||
|     pub memory_limit_gb: Option<f32>, |     pub memory_limit_gb: f32, | ||||||
| 
 | 
 | ||||||
|     /// Total persistent storage allocation in Gigabytes across all volumes.
 |     /// Total persistent storage allocation in Gigabytes across all volumes.
 | ||||||
|     pub storage_total_gb: Option<f32>, |     pub storage_total_gb: f32, | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| #[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] | #[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] | ||||||
|  | |||||||
		Loading…
	
		Reference in New Issue
	
	Block a user