...
This commit is contained in:
		| @@ -1,9 +1,11 @@ | ||||
| // Basic buildah operations for container management | ||||
| use std::process::Command; | ||||
| use crate::process::CommandResult; | ||||
| use super::BuildahError; | ||||
|  | ||||
|  | ||||
| /// Execute a buildah command and return the result | ||||
| pub fn execute_buildah_command(args: &[&str]) -> Dynamic { | ||||
| pub fn execute_buildah_command(args: &[&str]) -> Result<CommandResult, BuildahError> { | ||||
|     let output = Command::new("buildah") | ||||
|         .args(args) | ||||
|         .output(); | ||||
| @@ -20,10 +22,15 @@ pub fn execute_buildah_command(args: &[&str]) -> Dynamic { | ||||
|                 code: output.status.code().unwrap_or(-1), | ||||
|             }; | ||||
|              | ||||
|             result.to_dynamic() | ||||
|             if result.success { | ||||
|                 Ok(result) | ||||
|             } else { | ||||
|                 Err(BuildahError::CommandFailed(format!("Command failed with code {}: {}",  | ||||
|                     result.code, result.stderr.trim()))) | ||||
|             } | ||||
|         }, | ||||
|         Err(e) => { | ||||
|             CommandResult::error(&format!("Failed to execute buildah command: {}", e)).to_dynamic() | ||||
|             Err(BuildahError::CommandExecutionFailed(e)) | ||||
|         } | ||||
|     } | ||||
| } | ||||
|   | ||||
| @@ -1,37 +1,38 @@ | ||||
|  | ||||
| use crate::virt::buildah::execute_buildah_command; | ||||
| use crate::process::CommandResult; | ||||
| use super::BuildahError; | ||||
|  | ||||
| /// Create a container from an image | ||||
| pub fn build_from(image: &str) -> Dynamic { | ||||
| pub fn from(image: &str) -> Result<CommandResult, BuildahError> { | ||||
|     execute_buildah_command(&["from", image]) | ||||
| } | ||||
|  | ||||
| /// Run a command in a container | ||||
| pub fn build_run(container: &str, command: &str) -> Dynamic { | ||||
| pub fn run(container: &str, command: &str) -> Result<CommandResult, BuildahError> { | ||||
|     execute_buildah_command(&["run", container, "sh", "-c", command]) | ||||
| } | ||||
|  | ||||
| /// Copy files into a container | ||||
| pub fn build_copy(container: &str, source: &str, dest: &str) -> Dynamic { | ||||
| pub fn copy(container: &str, source: &str, dest: &str) -> Result<CommandResult, BuildahError> { | ||||
|     execute_buildah_command(&["copy", container, source, dest]) | ||||
| } | ||||
|  | ||||
| pub fn build_add(container: &str, source: &str, dest: &str) -> Dynamic { | ||||
| pub fn add(container: &str, source: &str, dest: &str) -> Result<CommandResult, BuildahError> { | ||||
|     execute_buildah_command(&["add", container, source, dest]) | ||||
| } | ||||
|  | ||||
| /// Commit a container to an image | ||||
| pub fn build_commit(container: &str, image_name: &str) -> Dynamic { | ||||
| pub fn commit(container: &str, image_name: &str) -> Result<CommandResult, BuildahError> { | ||||
|     execute_buildah_command(&["commit", container, image_name]) | ||||
| } | ||||
|  | ||||
|  | ||||
| /// Remove a container | ||||
| pub fn build_remove(container: &str) -> Dynamic { | ||||
| pub fn remove(container: &str) -> Result<CommandResult, BuildahError> { | ||||
|     execute_buildah_command(&["rm", container]) | ||||
| } | ||||
|  | ||||
| /// List containers | ||||
| pub fn build_list() -> Dynamic { | ||||
| pub fn list() -> Result<CommandResult, BuildahError> { | ||||
|     execute_buildah_command(&["containers"]) | ||||
| } | ||||
|   | ||||
| @@ -1,103 +1,98 @@ | ||||
| use std::process::Command; | ||||
| use std::collections::HashMap; | ||||
| use crate::virt::buildah::execute_buildah_command; | ||||
| use crate::process::CommandResult; | ||||
| use super::BuildahError; | ||||
| use serde_json::{self, Value}; | ||||
| use serde::{Deserialize, Serialize}; | ||||
|  | ||||
| /// Represents a container image | ||||
| #[derive(Debug, Clone, Serialize, Deserialize)] | ||||
| pub struct Image { | ||||
|     /// Image ID | ||||
|     pub id: String, | ||||
|     /// Image names/tags | ||||
|     pub names: Vec<String>, | ||||
|     /// Image size | ||||
|     pub size: String, | ||||
|     /// Creation timestamp | ||||
|     pub created: String, | ||||
| } | ||||
|  | ||||
| /// List images in local storage | ||||
| ///  | ||||
| /// # Returns | ||||
| /// * Array of image details on success or error details | ||||
| pub fn images() -> Dynamic { | ||||
|     let result = execute_buildah_command(&["images", "--format", "json"]); | ||||
| /// * Result with array of Image objects on success or error details | ||||
| pub fn images() -> Result<Vec<Image>, BuildahError> { | ||||
|     let result = execute_buildah_command(&["images", "--format", "json"])?; | ||||
|      | ||||
|     let result_map = match result.clone().try_cast::<Map>() { | ||||
|         Some(map) => map, | ||||
|         None => { | ||||
|             return create_result("", "Failed to convert result to map", false, -1); | ||||
|         } | ||||
|     }; | ||||
|      | ||||
|     let success = match result_map.get("success") { | ||||
|         Some(val) => val.as_bool().unwrap_or(false), | ||||
|         None => false, | ||||
|     }; | ||||
|      | ||||
|     if success { | ||||
|         let output = match result_map.get("stdout") { | ||||
|             Some(val) => val.to_string(), | ||||
|             None => "".to_string(), | ||||
|         }; | ||||
|          | ||||
|         // Try to parse the JSON output | ||||
|         match serde_json::from_str::<serde_json::Value>(&output) { | ||||
|             Ok(json) => { | ||||
|                 if let serde_json::Value::Array(images) = json { | ||||
|                     let mut image_array = Array::new(); | ||||
|     // Try to parse the JSON output | ||||
|     match serde_json::from_str::<serde_json::Value>(&result.stdout) { | ||||
|         Ok(json) => { | ||||
|             if let Value::Array(images_json) = json { | ||||
|                 let mut images = Vec::new(); | ||||
|                  | ||||
|                 for image_json in images_json { | ||||
|                     // Extract image ID | ||||
|                     let id = match image_json.get("id").and_then(|v| v.as_str()) { | ||||
|                         Some(id) => id.to_string(), | ||||
|                         None => return Err(BuildahError::ConversionError("Missing image ID".to_string())), | ||||
|                     }; | ||||
|                      | ||||
|                     for image in images { | ||||
|                         let mut image_map = Map::new(); | ||||
|                          | ||||
|                         if let Some(id) = image.get("id").and_then(|v| v.as_str()) { | ||||
|                             image_map.insert("id".into(), Dynamic::from(id.to_string())); | ||||
|                         } | ||||
|                          | ||||
|                         if let Some(name) = image.get("names").and_then(|v| v.as_array()) { | ||||
|                             let mut names_array = Array::new(); | ||||
|                             for name_value in name { | ||||
|                     // Extract image names | ||||
|                     let names = match image_json.get("names").and_then(|v| v.as_array()) { | ||||
|                         Some(names_array) => { | ||||
|                             let mut names_vec = Vec::new(); | ||||
|                             for name_value in names_array { | ||||
|                                 if let Some(name_str) = name_value.as_str() { | ||||
|                                     names_array.push(Dynamic::from(name_str.to_string())); | ||||
|                                     names_vec.push(name_str.to_string()); | ||||
|                                 } | ||||
|                             } | ||||
|                             image_map.insert("names".into(), Dynamic::from(names_array)); | ||||
|                         } | ||||
|                          | ||||
|                         if let Some(size) = image.get("size").and_then(|v| v.as_str()) { | ||||
|                             image_map.insert("size".into(), Dynamic::from(size.to_string())); | ||||
|                         } | ||||
|                          | ||||
|                         if let Some(created) = image.get("created").and_then(|v| v.as_str()) { | ||||
|                             image_map.insert("created".into(), Dynamic::from(created.to_string())); | ||||
|                         } | ||||
|                          | ||||
|                         image_array.push(Dynamic::from(image_map)); | ||||
|                     } | ||||
|                      | ||||
|                     let mut result_map = match result.clone().try_cast::<Map>() { | ||||
|                         Some(map) => map, | ||||
|                         None => Map::new(), | ||||
|                             names_vec | ||||
|                         }, | ||||
|                         None => Vec::new(), // Empty vector if no names found | ||||
|                     }; | ||||
|                     result_map.insert("images".into(), Dynamic::from(image_array)); | ||||
|                     Dynamic::from(result_map) | ||||
|                 } else { | ||||
|                     create_result( | ||||
|                         "", | ||||
|                         "Failed to parse image list: Expected JSON array", | ||||
|                         false, | ||||
|                         -1 | ||||
|                     ) | ||||
|                      | ||||
|                     // Extract image size | ||||
|                     let size = match image_json.get("size").and_then(|v| v.as_str()) { | ||||
|                         Some(size) => size.to_string(), | ||||
|                         None => "Unknown".to_string(), // Default value if size not found | ||||
|                     }; | ||||
|                      | ||||
|                     // Extract creation timestamp | ||||
|                     let created = match image_json.get("created").and_then(|v| v.as_str()) { | ||||
|                         Some(created) => created.to_string(), | ||||
|                         None => "Unknown".to_string(), // Default value if created not found | ||||
|                     }; | ||||
|                      | ||||
|                     // Create Image struct and add to vector | ||||
|                     images.push(Image { | ||||
|                         id, | ||||
|                         names, | ||||
|                         size, | ||||
|                         created, | ||||
|                     }); | ||||
|                 } | ||||
|             }, | ||||
|             Err(e) => { | ||||
|                 create_result( | ||||
|                     "", | ||||
|                     &format!("Failed to parse image list JSON: {}", e), | ||||
|                     false, | ||||
|                     -1 | ||||
|                 ) | ||||
|                  | ||||
|                 Ok(images) | ||||
|             } else { | ||||
|                 Err(BuildahError::JsonParseError("Expected JSON array".to_string())) | ||||
|             } | ||||
|         }, | ||||
|         Err(e) => { | ||||
|             Err(BuildahError::JsonParseError(format!("Failed to parse image list JSON: {}", e))) | ||||
|         } | ||||
|     } else { | ||||
|         result | ||||
|     } | ||||
| } | ||||
|  | ||||
|  | ||||
| /// Remove one or more images | ||||
| ///  | ||||
| /// # Arguments | ||||
| /// * `image` - Image ID or name | ||||
| /// | ||||
| /// # Returns | ||||
| /// * Success or error details | ||||
| pub fn image_remove(image: &str) -> Dynamic { | ||||
| /// * Result with command output or error | ||||
| pub fn image_remove(image: &str) -> Result<CommandResult, BuildahError> { | ||||
|     execute_buildah_command(&["rmi", image]) | ||||
| } | ||||
|  | ||||
| @@ -109,8 +104,8 @@ pub fn image_remove(image: &str) -> Dynamic { | ||||
| /// * `tls_verify` - Whether to verify TLS (default: true) | ||||
| /// | ||||
| /// # Returns | ||||
| /// * Success or error details | ||||
| pub fn image_push(image: &str, destination: &str, tls_verify: bool) -> Dynamic { | ||||
| /// * Result with command output or error | ||||
| pub fn image_push(image: &str, destination: &str, tls_verify: bool) -> Result<CommandResult, BuildahError> { | ||||
|     let mut args = vec!["push"]; | ||||
|      | ||||
|     if !tls_verify { | ||||
| @@ -130,8 +125,8 @@ pub fn image_push(image: &str, destination: &str, tls_verify: bool) -> Dynamic { | ||||
| /// * `new_name` - New name for the image | ||||
| /// | ||||
| /// # Returns | ||||
| /// * Success or error details | ||||
| pub fn image_tag(image: &str, new_name: &str) -> Dynamic { | ||||
| /// * Result with command output or error | ||||
| pub fn image_tag(image: &str, new_name: &str) -> Result<CommandResult, BuildahError> { | ||||
|     execute_buildah_command(&["tag", image, new_name]) | ||||
| } | ||||
|  | ||||
| @@ -142,8 +137,8 @@ pub fn image_tag(image: &str, new_name: &str) -> Dynamic { | ||||
| /// * `tls_verify` - Whether to verify TLS (default: true) | ||||
| /// | ||||
| /// # Returns | ||||
| /// * Success or error details | ||||
| pub fn image_pull(image: &str, tls_verify: bool) -> Dynamic { | ||||
| /// * Result with command output or error | ||||
| pub fn image_pull(image: &str, tls_verify: bool) -> Result<CommandResult, BuildahError> { | ||||
|     let mut args = vec!["pull"]; | ||||
|      | ||||
|     if !tls_verify { | ||||
| @@ -165,8 +160,8 @@ pub fn image_pull(image: &str, tls_verify: bool) -> Dynamic { | ||||
| /// * `rm` - Whether to remove the container after commit | ||||
| /// | ||||
| /// # Returns | ||||
| /// * Success or error details | ||||
| pub fn image_commit(container: &str, image_name: &str, format: Option<&str>, squash: bool, rm: bool) -> Dynamic { | ||||
| /// * Result with command output or error | ||||
| pub fn image_commit(container: &str, image_name: &str, format: Option<&str>, squash: bool, rm: bool) -> Result<CommandResult, BuildahError> { | ||||
|     let mut args = vec!["commit"]; | ||||
|      | ||||
|     if let Some(format_str) = format { | ||||
| @@ -195,8 +190,8 @@ pub fn image_commit(container: &str, image_name: &str, format: Option<&str>, squ | ||||
| /// * `options` - Map of configuration options | ||||
| /// | ||||
| /// # Returns | ||||
| /// * Success or error details | ||||
| pub fn config(container: &str, options: Map) -> Dynamic { | ||||
| /// * Result with command output or error | ||||
| pub fn config(container: &str, options: HashMap<String, String>) -> Result<CommandResult, BuildahError> { | ||||
|     let mut args_owned: Vec<String> = Vec::new(); | ||||
|     args_owned.push("config".to_string()); | ||||
|      | ||||
| @@ -204,10 +199,7 @@ pub fn config(container: &str, options: Map) -> Dynamic { | ||||
|     for (key, value) in options.iter() { | ||||
|         let option_name = format!("--{}", key); | ||||
|         args_owned.push(option_name); | ||||
|          | ||||
|         if !value.is_unit() { | ||||
|             args_owned.push(value.to_string()); | ||||
|         } | ||||
|         args_owned.push(value.clone()); | ||||
|     } | ||||
|      | ||||
|     args_owned.push(container.to_string()); | ||||
| @@ -217,4 +209,3 @@ pub fn config(container: &str, options: Map) -> Dynamic { | ||||
|      | ||||
|     execute_buildah_command(&args) | ||||
| } | ||||
|  | ||||
|   | ||||
| @@ -2,6 +2,46 @@ mod containers; | ||||
| mod images; | ||||
| mod cmd; | ||||
|  | ||||
| use std::fmt; | ||||
| use std::error::Error; | ||||
| use std::io; | ||||
|  | ||||
| /// Error type for buildah operations | ||||
| #[derive(Debug)] | ||||
| pub enum BuildahError { | ||||
|     /// The buildah command failed to execute | ||||
|     CommandExecutionFailed(io::Error), | ||||
|     /// The buildah command executed but returned an error | ||||
|     CommandFailed(String), | ||||
|     /// Failed to parse JSON output | ||||
|     JsonParseError(String), | ||||
|     /// Failed to convert data | ||||
|     ConversionError(String), | ||||
|     /// Generic error | ||||
|     Other(String), | ||||
| } | ||||
|  | ||||
| impl fmt::Display for BuildahError { | ||||
|     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { | ||||
|         match self { | ||||
|             BuildahError::CommandExecutionFailed(e) => write!(f, "Failed to execute buildah command: {}", e), | ||||
|             BuildahError::CommandFailed(e) => write!(f, "Buildah command failed: {}", e), | ||||
|             BuildahError::JsonParseError(e) => write!(f, "Failed to parse JSON: {}", e), | ||||
|             BuildahError::ConversionError(e) => write!(f, "Conversion error: {}", e), | ||||
|             BuildahError::Other(e) => write!(f, "{}", e), | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | ||||
| impl Error for BuildahError { | ||||
|     fn source(&self) -> Option<&(dyn Error + 'static)> { | ||||
|         match self { | ||||
|             BuildahError::CommandExecutionFailed(e) => Some(e), | ||||
|             _ => None, | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | ||||
| pub use containers::*; | ||||
| pub use images::*; | ||||
| pub use cmd::*; | ||||
|   | ||||
		Reference in New Issue
	
	Block a user