Compare commits
	
		
			No commits in common. "23971ecd7c7f941499e02ec4f5261e9a22a82c21" and "d307893f1577c99dd46c230dae15e4e8bdf532f1" have entirely different histories.
		
	
	
		
			23971ecd7c
			...
			d307893f15
		
	
		
							
								
								
									
										52
									
								
								Cargo.lock
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										52
									
								
								Cargo.lock
									
									
									
										generated
									
									
									
								
							| @ -795,27 +795,6 @@ dependencies = [ | ||||
|  "subtle", | ||||
| ] | ||||
| 
 | ||||
| [[package]] | ||||
| name = "directories" | ||||
| version = "6.0.0" | ||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||||
| checksum = "16f5094c54661b38d03bd7e50df373292118db60b585c08a411c6d840017fe7d" | ||||
| dependencies = [ | ||||
|  "dirs-sys", | ||||
| ] | ||||
| 
 | ||||
| [[package]] | ||||
| name = "dirs-sys" | ||||
| version = "0.5.0" | ||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||||
| checksum = "e01a3366d27ee9890022452ee61b2b63a67e6f13f58900b651ff5665f0bb1fab" | ||||
| dependencies = [ | ||||
|  "libc", | ||||
|  "option-ext", | ||||
|  "redox_users", | ||||
|  "windows-sys 0.59.0", | ||||
| ] | ||||
| 
 | ||||
| [[package]] | ||||
| name = "displaydoc" | ||||
| version = "0.2.5" | ||||
| @ -1362,17 +1341,14 @@ dependencies = [ | ||||
|  "async-trait", | ||||
|  "cidr", | ||||
|  "derive-new", | ||||
|  "directories", | ||||
|  "env_logger", | ||||
|  "harmony_macros", | ||||
|  "harmony_types", | ||||
|  "helm-wrapper-rs", | ||||
|  "http 1.3.1", | ||||
|  "inquire", | ||||
|  "k3d-rs", | ||||
|  "k8s-openapi", | ||||
|  "kube", | ||||
|  "lazy_static", | ||||
|  "libredfish", | ||||
|  "log", | ||||
|  "non-blank-string-rs", | ||||
| @ -2109,7 +2085,6 @@ dependencies = [ | ||||
|  "env_logger", | ||||
|  "futures-util", | ||||
|  "httptest", | ||||
|  "kube", | ||||
|  "log", | ||||
|  "octocrab", | ||||
|  "pretty_assertions", | ||||
| @ -2232,16 +2207,6 @@ dependencies = [ | ||||
|  "serde_json", | ||||
| ] | ||||
| 
 | ||||
| [[package]] | ||||
| name = "libredox" | ||||
| version = "0.1.3" | ||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||||
| checksum = "c0ff37bd590ca25063e35af745c343cb7a0271906fb7b37e4813e8f79f00268d" | ||||
| dependencies = [ | ||||
|  "bitflags 2.9.0", | ||||
|  "libc", | ||||
| ] | ||||
| 
 | ||||
| [[package]] | ||||
| name = "linux-raw-sys" | ||||
| version = "0.4.15" | ||||
| @ -2607,12 +2572,6 @@ dependencies = [ | ||||
|  "yaserde_derive", | ||||
| ] | ||||
| 
 | ||||
| [[package]] | ||||
| name = "option-ext" | ||||
| version = "0.2.0" | ||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||||
| checksum = "04744f49eae99ab78e0d5c0b603ab218f515ea8cfe5a456d7629ad883a3b6e7d" | ||||
| 
 | ||||
| [[package]] | ||||
| name = "ordered-float" | ||||
| version = "2.10.1" | ||||
| @ -3092,17 +3051,6 @@ dependencies = [ | ||||
|  "bitflags 2.9.0", | ||||
| ] | ||||
| 
 | ||||
| [[package]] | ||||
| name = "redox_users" | ||||
| version = "0.5.0" | ||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||||
| checksum = "dd6f9d3d47bdd2ad6945c5015a226ec6155d0bcdfd8f7cd29f86b71f8de99d2b" | ||||
| dependencies = [ | ||||
|  "getrandom 0.2.15", | ||||
|  "libredox", | ||||
|  "thiserror 2.0.12", | ||||
| ] | ||||
| 
 | ||||
| [[package]] | ||||
| name = "regex" | ||||
| version = "1.11.1" | ||||
|  | ||||
| @ -1,6 +1,5 @@ | ||||
| use harmony::{ | ||||
|     data::Version, | ||||
|     inventory::Inventory, | ||||
|     maestro::Maestro, | ||||
|     modules::lamp::{LAMPConfig, LAMPScore}, | ||||
|     topology::{K8sAnywhereTopology, Url}, | ||||
| @ -18,12 +17,7 @@ async fn main() { | ||||
|         }, | ||||
|     }; | ||||
| 
 | ||||
|     let mut maestro = Maestro::<K8sAnywhereTopology>::initialize( | ||||
|         Inventory::autoload(), | ||||
|         K8sAnywhereTopology::new(), | ||||
|     ) | ||||
|     .await | ||||
|     .unwrap(); | ||||
|     let mut maestro = Maestro::<K8sAnywhereTopology>::load_from_env(); | ||||
|     maestro.register_all(vec![Box::new(lamp_stack)]); | ||||
|     harmony_tui::init(maestro).await.unwrap(); | ||||
| } | ||||
|  | ||||
| @ -33,6 +33,3 @@ serde-value = { workspace = true } | ||||
| inquire.workspace = true | ||||
| helm-wrapper-rs = "0.4.0" | ||||
| non-blank-string-rs = "1.0.4" | ||||
| k3d-rs = { path = "../k3d" } | ||||
| directories = "6.0.0" | ||||
| lazy_static = "1.5.0" | ||||
|  | ||||
| @ -1,9 +0,0 @@ | ||||
| use lazy_static::lazy_static; | ||||
| use std::path::PathBuf; | ||||
| 
 | ||||
| lazy_static! { | ||||
|     pub static ref HARMONY_CONFIG_DIR: PathBuf = directories::BaseDirs::new() | ||||
|         .unwrap() | ||||
|         .data_dir() | ||||
|         .join("harmony"); | ||||
| } | ||||
| @ -52,6 +52,27 @@ impl<T: Topology> Maestro<T> { | ||||
|         Ok(outcome) | ||||
|     } | ||||
| 
 | ||||
|     // Load the inventory and inventory from environment.
 | ||||
|     // This function is able to discover the context that it is running in, such as k8s clusters, aws cloud, linux host, etc.
 | ||||
|     // When the HARMONY_TOPOLOGY environment variable is not set, it will default to install k3s
 | ||||
|     // locally (lazily, if not installed yet, when the first execution occurs) and use that as a topology
 | ||||
|     // So, by default, the inventory is a single host that the binary is running on, and the
 | ||||
|     // topology is a single node k3s
 | ||||
|     //
 | ||||
|     // By default :
 | ||||
|     // - Linux => k3s
 | ||||
|     // - macos, windows => docker compose
 | ||||
|     //
 | ||||
|     // To run more complex cases like OKDHACluster, either provide the default target in the
 | ||||
|     // harmony infrastructure as code or as an environment variable
 | ||||
|     pub fn load_from_env() -> Self { | ||||
|         // Load env var HARMONY_TOPOLOGY
 | ||||
|         match std::env::var("HARMONY_TOPOLOGY") { | ||||
|             Ok(_) => todo!(), | ||||
|             Err(_) => todo!(), | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     pub fn register_all(&mut self, mut scores: ScoreVec<T>) { | ||||
|         let mut score_mut = self.scores.write().expect("Should acquire lock"); | ||||
|         score_mut.append(&mut scores); | ||||
|  | ||||
| @ -1,4 +1,3 @@ | ||||
| pub mod config; | ||||
| pub mod data; | ||||
| pub mod executors; | ||||
| pub mod filter; | ||||
|  | ||||
| @ -1,9 +1,7 @@ | ||||
| use derive_new::new; | ||||
| use k8s_openapi::NamespaceResourceScope; | ||||
| use kube::{Api, Client, Error, Resource, api::PostParams}; | ||||
| use serde::de::DeserializeOwned; | ||||
| 
 | ||||
| #[derive(new)] | ||||
| pub struct K8sClient { | ||||
|     client: Client, | ||||
| } | ||||
|  | ||||
| @ -61,15 +61,13 @@ impl K8sAnywhereTopology { | ||||
|         todo!("Use kube-rs to load kubeconfig at path {path}"); | ||||
|     } | ||||
| 
 | ||||
|     fn get_k3d_installation_score(&self) -> K3DInstallationScore { | ||||
|         K3DInstallationScore::default() | ||||
|     } | ||||
| 
 | ||||
|     async fn try_install_k3d(&self) -> Result<(), InterpretError> { | ||||
|     async fn try_install_k3d(&self) -> Result<K8sClient, InterpretError> { | ||||
|         let maestro = Maestro::initialize(Inventory::autoload(), LocalhostTopology::new()).await?; | ||||
|         let k3d_score = self.get_k3d_installation_score(); | ||||
|         let k3d_score = K3DInstallationScore::new(); | ||||
|         maestro.interpret(Box::new(k3d_score)).await?; | ||||
|         Ok(()) | ||||
|         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> { | ||||
| @ -114,14 +112,9 @@ impl K8sAnywhereTopology { | ||||
|         } | ||||
| 
 | ||||
|         info!("Starting K8sAnywhere installation"); | ||||
|         self.try_install_k3d().await?; | ||||
|         let k3d_score = self.get_k3d_installation_score(); | ||||
|         match k3d_rs::K3d::new(k3d_score.installation_path, Some(k3d_score.cluster_name)) | ||||
|             .get_client() | ||||
|             .await | ||||
|         { | ||||
|         match self.try_install_k3d().await { | ||||
|             Ok(client) => Ok(Some(K8sState { | ||||
|                 _client: K8sClient::new(client), | ||||
|                 _client: client, | ||||
|                 _source: K8sSource::LocalK3d, | ||||
|                 message: "Successfully installed K3D cluster and acquired client".to_string(), | ||||
|             })), | ||||
|  | ||||
| @ -1,11 +1,7 @@ | ||||
| use std::path::PathBuf; | ||||
| 
 | ||||
| use async_trait::async_trait; | ||||
| use log::info; | ||||
| use serde::Serialize; | ||||
| 
 | ||||
| use crate::{ | ||||
|     config::HARMONY_CONFIG_DIR, | ||||
|     data::{Id, Version}, | ||||
|     interpret::{Interpret, InterpretError, InterpretName, InterpretStatus, Outcome}, | ||||
|     inventory::Inventory, | ||||
| @ -14,25 +10,26 @@ use crate::{ | ||||
| }; | ||||
| 
 | ||||
| #[derive(Debug, Clone, Serialize)] | ||||
| pub struct K3DInstallationScore { | ||||
|     pub installation_path: PathBuf, | ||||
|     pub cluster_name: String, | ||||
| } | ||||
| pub struct K3DInstallationScore {} | ||||
| 
 | ||||
| impl Default for K3DInstallationScore { | ||||
|     fn default() -> Self { | ||||
|         Self { | ||||
|             installation_path: HARMONY_CONFIG_DIR.join("k3d"), | ||||
|             cluster_name: "harmony".to_string(), | ||||
|         } | ||||
| impl K3DInstallationScore { | ||||
|     pub fn new() -> Self { | ||||
|         Self {} | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| impl<T: Topology> Score<T> for K3DInstallationScore { | ||||
|     fn create_interpret(&self) -> Box<dyn crate::interpret::Interpret<T>> { | ||||
|         Box::new(K3dInstallationInterpret { | ||||
|             score: self.clone(), | ||||
|         }) | ||||
|         todo!(" | ||||
|         1. Decide if I create a new crate for k3d management, especially to avoid the ocrtograb dependency | ||||
|         2. Implement k3d management | ||||
|         3. Find latest tag | ||||
|         4. Download k3d to some path managed by harmony (or not?) | ||||
|         5. Bootstrap cluster | ||||
|         6. Get kubeconfig | ||||
|         7. Load kubeconfig in k8s anywhere | ||||
|         8. Complete k8sanywhere setup | ||||
|         ")
 | ||||
|     } | ||||
| 
 | ||||
|     fn name(&self) -> String { | ||||
| @ -41,9 +38,7 @@ impl<T: Topology> Score<T> for K3DInstallationScore { | ||||
| } | ||||
| 
 | ||||
| #[derive(Debug)] | ||||
| pub struct K3dInstallationInterpret { | ||||
|     score: K3DInstallationScore, | ||||
| } | ||||
| pub struct K3dInstallationInterpret {} | ||||
| 
 | ||||
| #[async_trait] | ||||
| impl<T: Topology> Interpret<T> for K3dInstallationInterpret { | ||||
| @ -52,20 +47,7 @@ impl<T: Topology> Interpret<T> for K3dInstallationInterpret { | ||||
|         _inventory: &Inventory, | ||||
|         _topology: &T, | ||||
|     ) -> Result<Outcome, InterpretError> { | ||||
|         let k3d = k3d_rs::K3d::new( | ||||
|             self.score.installation_path.clone(), | ||||
|             Some(self.score.cluster_name.clone()), | ||||
|         ); | ||||
|         match k3d.ensure_installed().await { | ||||
|             Ok(_client) => { | ||||
|                 let msg = format!("k3d cluster {} is installed ", self.score.cluster_name); | ||||
|                 info!("{msg}"); | ||||
|                 Ok(Outcome::success(msg)) | ||||
|             } | ||||
|             Err(msg) => Err(InterpretError::new(format!( | ||||
|                 "K3dInstallationInterpret failed to ensure k3d is installed : {msg}" | ||||
|             ))), | ||||
|         } | ||||
|         todo!() | ||||
|     } | ||||
|     fn get_name(&self) -> InterpretName { | ||||
|         InterpretName::K3dInstallation | ||||
|  | ||||
| @ -99,7 +99,7 @@ pub async fn init<T: Topology + Send + Sync + 'static>( | ||||
|         return Err("Not compiled with interactive support".into()); | ||||
|     } | ||||
| 
 | ||||
|     let _ = env_logger::builder().try_init(); | ||||
|     env_logger::builder().init(); | ||||
| 
 | ||||
|     let scores_vec = maestro_scores_filter(&maestro, args.all, args.filter, args.number); | ||||
| 
 | ||||
|  | ||||
| @ -15,7 +15,6 @@ reqwest = { version = "0.12", features = ["stream"]  } | ||||
| url.workspace = true | ||||
| sha2 = "0.10.8" | ||||
| futures-util = "0.3.31" | ||||
| kube.workspace = true | ||||
| 
 | ||||
| [dev-dependencies] | ||||
| env_logger = { workspace = true } | ||||
|  | ||||
							
								
								
									
										221
									
								
								k3d/src/lib.rs
									
									
									
									
									
								
							
							
						
						
									
										221
									
								
								k3d/src/lib.rs
									
									
									
									
									
								
							| @ -1,23 +1,18 @@ | ||||
| mod downloadable_asset; | ||||
| use downloadable_asset::*; | ||||
| 
 | ||||
| use kube::Client; | ||||
| use log::{debug, info, warn}; | ||||
| use log::{debug, info}; | ||||
| use std::path::PathBuf; | ||||
| 
 | ||||
| const K3D_BIN_FILE_NAME: &str = "k3d"; | ||||
| 
 | ||||
| pub struct K3d { | ||||
|     base_dir: PathBuf, | ||||
|     cluster_name: Option<String>, | ||||
| } | ||||
| 
 | ||||
| impl K3d { | ||||
|     pub fn new(base_dir: PathBuf, cluster_name: Option<String>) -> Self { | ||||
|         Self { | ||||
|             base_dir, | ||||
|             cluster_name, | ||||
|         } | ||||
|     pub fn new(base_dir: PathBuf) -> Self { | ||||
|         Self { base_dir } | ||||
|     } | ||||
| 
 | ||||
|     async fn get_binary_for_current_platform( | ||||
| @ -29,6 +24,7 @@ impl K3d { | ||||
| 
 | ||||
|         debug!("Detecting platform: OS={}, ARCH={}", os, arch); | ||||
| 
 | ||||
|         // 2. Construct the binary name pattern based on platform
 | ||||
|         let binary_pattern = match (os, arch) { | ||||
|             ("linux", "x86") => "k3d-linux-386", | ||||
|             ("linux", "x86_64") => "k3d-linux-amd64", | ||||
| @ -42,6 +38,7 @@ impl K3d { | ||||
| 
 | ||||
|         debug!("Looking for binary matching pattern: {}", binary_pattern); | ||||
| 
 | ||||
|         // 3. Find the matching binary in release assets
 | ||||
|         let binary_asset = latest_release | ||||
|             .assets | ||||
|             .iter() | ||||
| @ -50,12 +47,14 @@ impl K3d { | ||||
| 
 | ||||
|         let binary_url = binary_asset.browser_download_url.clone(); | ||||
| 
 | ||||
|         // 4. Find and parse the checksums file
 | ||||
|         let checksums_asset = latest_release | ||||
|             .assets | ||||
|             .iter() | ||||
|             .find(|asset| asset.name == "checksums.txt") | ||||
|             .expect("Checksums file not found in release assets"); | ||||
| 
 | ||||
|         // 5. Download and parse checksums file
 | ||||
|         let checksums_url = checksums_asset.browser_download_url.clone(); | ||||
| 
 | ||||
|         let body = reqwest::get(checksums_url) | ||||
| @ -66,6 +65,7 @@ impl K3d { | ||||
|             .unwrap(); | ||||
|         println!("body: {body}"); | ||||
| 
 | ||||
|         // 6. Find the checksum for our binary
 | ||||
|         let checksum = body | ||||
|             .lines() | ||||
|             .find_map(|line| { | ||||
| @ -109,207 +109,6 @@ impl K3d { | ||||
| 
 | ||||
|         Ok(latest_release) | ||||
|     } | ||||
| 
 | ||||
|     /// Checks if k3d binary exists and is executable
 | ||||
|     ///
 | ||||
|     /// Verifies that:
 | ||||
|     /// 1. The k3d binary exists in the base directory
 | ||||
|     /// 2. It has proper executable permissions (on Unix systems)
 | ||||
|     /// 3. It responds correctly to a simple command (`k3d --version`)
 | ||||
|     pub fn is_installed(&self) -> bool { | ||||
|         let binary_path = self.base_dir.join(K3D_BIN_FILE_NAME); | ||||
| 
 | ||||
|         if !binary_path.exists() { | ||||
|             debug!("K3d binary not found at {:?}", binary_path); | ||||
|             return false; | ||||
|         } | ||||
| 
 | ||||
|         if !self.ensure_binary_executable(&binary_path) { | ||||
|             return false; | ||||
|         } | ||||
| 
 | ||||
|         self.can_execute_binary_check(&binary_path) | ||||
|     } | ||||
| 
 | ||||
|     /// Verifies if the specified cluster is already running
 | ||||
|     ///
 | ||||
|     /// Executes `k3d cluster list <cluster_name>` and checks for a successful response,
 | ||||
|     /// indicating that the cluster exists and is registered with k3d.
 | ||||
|     pub fn is_cluster_initialized(&self) -> bool { | ||||
|         let cluster_name = match &self.cluster_name { | ||||
|             Some(name) => name, | ||||
|             None => { | ||||
|                 debug!("No cluster name specified, can't verify if cluster is initialized"); | ||||
|                 return false; | ||||
|             } | ||||
|         }; | ||||
| 
 | ||||
|         let binary_path = self.base_dir.join(K3D_BIN_FILE_NAME); | ||||
|         if !binary_path.exists() { | ||||
|             return false; | ||||
|         } | ||||
| 
 | ||||
|         self.verify_cluster_exists(&binary_path, cluster_name) | ||||
|     } | ||||
| 
 | ||||
|     /// Creates a new k3d cluster with the specified name
 | ||||
|     ///
 | ||||
|     /// This method:
 | ||||
|     /// 1. Creates a new k3d cluster using `k3d cluster create <cluster_name>`
 | ||||
|     /// 2. Waits for the cluster to initialize
 | ||||
|     /// 3. Returns a configured Kubernetes client connected to the cluster
 | ||||
|     ///
 | ||||
|     /// # Returns
 | ||||
|     /// - `Ok(Client)` - Successfully created cluster and connected client
 | ||||
|     /// - `Err(String)` - Error message detailing what went wrong
 | ||||
|     pub async fn initialize_cluster(&self) -> Result<Client, String> { | ||||
|         let cluster_name = match &self.cluster_name { | ||||
|             Some(name) => name, | ||||
|             None => return Err("No cluster name specified for initialization".to_string()), | ||||
|         }; | ||||
| 
 | ||||
|         let binary_path = self.base_dir.join(K3D_BIN_FILE_NAME); | ||||
|         if !binary_path.exists() { | ||||
|             return Err(format!("K3d binary not found at {:?}", binary_path)); | ||||
|         } | ||||
| 
 | ||||
|         info!("Initializing k3d cluster '{}'", cluster_name); | ||||
| 
 | ||||
|         self.create_cluster(&binary_path, cluster_name)?; | ||||
|         self.create_kubernetes_client().await | ||||
|     } | ||||
| 
 | ||||
|     /// Ensures k3d is installed and the cluster is initialized
 | ||||
|     ///
 | ||||
|     /// This method provides a complete setup flow:
 | ||||
|     /// 1. Checks if k3d is installed, downloads and installs it if needed
 | ||||
|     /// 2. Verifies if the specified cluster exists, creates it if not
 | ||||
|     /// 3. Returns a Kubernetes client connected to the cluster
 | ||||
|     ///
 | ||||
|     /// # Returns
 | ||||
|     /// - `Ok(Client)` - Successfully ensured k3d and cluster are ready
 | ||||
|     /// - `Err(String)` - Error message if any step failed
 | ||||
|     pub async fn ensure_installed(&self) -> Result<Client, String> { | ||||
|         if !self.is_installed() { | ||||
|             info!("K3d is not installed, downloading latest release"); | ||||
|             self.download_latest_release() | ||||
|                 .await | ||||
|                 .map_err(|e| format!("Failed to download k3d: {}", e))?; | ||||
| 
 | ||||
|             if !self.is_installed() { | ||||
|                 return Err("Failed to install k3d properly".to_string()); | ||||
|             } | ||||
|         } | ||||
| 
 | ||||
|         if !self.is_cluster_initialized() { | ||||
|             info!("Cluster is not initialized, initializing now"); | ||||
|             return self.initialize_cluster().await; | ||||
|         } | ||||
| 
 | ||||
|         info!("K3d and cluster are already properly set up"); | ||||
|         self.create_kubernetes_client().await | ||||
|     } | ||||
| 
 | ||||
|     // Private helper methods
 | ||||
| 
 | ||||
|     #[cfg(not(target_os = "windows"))] | ||||
|     fn ensure_binary_executable(&self, binary_path: &PathBuf) -> bool { | ||||
|         use std::os::unix::fs::PermissionsExt; | ||||
| 
 | ||||
|         let mut perms = match std::fs::metadata(binary_path) { | ||||
|             Ok(metadata) => metadata.permissions(), | ||||
|             Err(e) => { | ||||
|                 debug!("Failed to get binary metadata: {}", e); | ||||
|                 return false; | ||||
|             } | ||||
|         }; | ||||
| 
 | ||||
|         perms.set_mode(0o755); | ||||
| 
 | ||||
|         if let Err(e) = std::fs::set_permissions(binary_path, perms) { | ||||
|             debug!("Failed to set executable permissions on k3d binary: {}", e); | ||||
|             return false; | ||||
|         } | ||||
| 
 | ||||
|         true | ||||
|     } | ||||
| 
 | ||||
|     #[cfg(target_os = "windows")] | ||||
|     fn ensure_binary_executable(&self, _binary_path: &PathBuf) -> bool { | ||||
|         // Windows doesn't use executable file permissions
 | ||||
|         true | ||||
|     } | ||||
| 
 | ||||
|     fn can_execute_binary_check(&self, binary_path: &PathBuf) -> bool { | ||||
|         match std::process::Command::new(binary_path) | ||||
|             .arg("--version") | ||||
|             .output() | ||||
|         { | ||||
|             Ok(output) => { | ||||
|                 if output.status.success() { | ||||
|                     debug!("K3d binary is installed and working"); | ||||
|                     true | ||||
|                 } else { | ||||
|                     debug!("K3d binary check failed: {:?}", output); | ||||
|                     false | ||||
|                 } | ||||
|             } | ||||
|             Err(e) => { | ||||
|                 debug!("Failed to execute K3d binary: {}", e); | ||||
|                 false | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     fn verify_cluster_exists(&self, binary_path: &PathBuf, cluster_name: &str) -> bool { | ||||
|         match std::process::Command::new(binary_path) | ||||
|             .args(["cluster", "list", cluster_name, "--no-headers"]) | ||||
|             .output() | ||||
|         { | ||||
|             Ok(output) => { | ||||
|                 if output.status.success() && !output.stdout.is_empty() { | ||||
|                     debug!("Cluster '{}' is initialized", cluster_name); | ||||
|                     true | ||||
|                 } else { | ||||
|                     debug!("Cluster '{}' is not initialized", cluster_name); | ||||
|                     false | ||||
|                 } | ||||
|             } | ||||
|             Err(e) => { | ||||
|                 debug!("Failed to check cluster initialization: {}", e); | ||||
|                 false | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     fn create_cluster(&self, binary_path: &PathBuf, cluster_name: &str) -> Result<(), String> { | ||||
|         let output = std::process::Command::new(binary_path) | ||||
|             .args(["cluster", "create", cluster_name]) | ||||
|             .output() | ||||
|             .map_err(|e| format!("Failed to execute k3d command: {}", e))?; | ||||
| 
 | ||||
|         if !output.status.success() { | ||||
|             let stderr = String::from_utf8_lossy(&output.stderr); | ||||
|             return Err(format!("Failed to create cluster: {}", stderr)); | ||||
|         } | ||||
| 
 | ||||
|         info!("Successfully created k3d cluster '{}'", cluster_name); | ||||
|         Ok(()) | ||||
|     } | ||||
| 
 | ||||
|     async fn create_kubernetes_client(&self) -> Result<Client, String> { | ||||
|         warn!("TODO this method is way too dumb, it should make sure that the client is connected to the k3d cluster actually represented by this instance, not just any default  client"); | ||||
|         Client::try_default() | ||||
|             .await | ||||
|             .map_err(|e| format!("Failed to create Kubernetes client: {}", e)) | ||||
|     } | ||||
| 
 | ||||
|     pub async fn get_client(&self) -> Result<Client, String> { | ||||
|         match self.is_cluster_initialized() { | ||||
|             true => Ok(self.create_kubernetes_client().await?), | ||||
|             false => Err("Cannot get client! Cluster not initialized yet".to_string()), | ||||
|         } | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| #[cfg(test)] | ||||
| @ -325,7 +124,7 @@ mod test { | ||||
| 
 | ||||
|         assert_eq!(dir.join(K3D_BIN_FILE_NAME).exists(), false); | ||||
| 
 | ||||
|         let k3d = K3d::new(dir.clone(), None); | ||||
|         let k3d = K3d::new(dir.clone()); | ||||
|         let latest_release = k3d.get_latest_release_tag().await.unwrap(); | ||||
| 
 | ||||
|         let tag_regex = Regex::new(r"^v\d+\.\d+\.\d+$").unwrap(); | ||||
| @ -339,7 +138,7 @@ mod test { | ||||
| 
 | ||||
|         assert_eq!(dir.join(K3D_BIN_FILE_NAME).exists(), false); | ||||
| 
 | ||||
|         let k3d = K3d::new(dir.clone(), None); | ||||
|         let k3d = K3d::new(dir.clone()); | ||||
|         let bin_file_path = k3d.download_latest_release().await.unwrap(); | ||||
|         assert_eq!(bin_file_path, dir.join(K3D_BIN_FILE_NAME)); | ||||
|         assert_eq!(dir.join(K3D_BIN_FILE_NAME).exists(), true); | ||||
|  | ||||
		Loading…
	
		Reference in New Issue
	
	Block a user