...
This commit is contained in:
		| @@ -24,6 +24,7 @@ glob = "0.3.1"     # For file pattern matching | |||||||
| tempfile = "3.5"   # For temporary file operations | tempfile = "3.5"   # For temporary file operations | ||||||
| log = "0.4"        # Logging facade | log = "0.4"        # Logging facade | ||||||
| rhai = { version = "1.12.0", features = ["sync"] } # Embedded scripting language | rhai = { version = "1.12.0", features = ["sync"] } # Embedded scripting language | ||||||
|  | clap = "2.33"      # Command-line argument parsing | ||||||
|  |  | ||||||
| # Optional features for specific OS functionality | # Optional features for specific OS functionality | ||||||
| [target.'cfg(unix)'.dependencies] | [target.'cfg(unix)'.dependencies] | ||||||
| @@ -34,3 +35,7 @@ windows = { version = "0.48", features = ["Win32_Foundation", "Win32_System_Thre | |||||||
|  |  | ||||||
| [dev-dependencies] | [dev-dependencies] | ||||||
| tempfile = "3.5"   # For tests that need temporary files/directories | tempfile = "3.5"   # For tests that need temporary files/directories | ||||||
|  |  | ||||||
|  | [[bin]] | ||||||
|  | name = "herodo" | ||||||
|  | path = "src/bin/herodo.rs" | ||||||
|   | |||||||
							
								
								
									
										74
									
								
								examples/rhai_buildah_example.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										74
									
								
								examples/rhai_buildah_example.rs
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,74 @@ | |||||||
|  | //! Example of using the Rhai integration with SAL Buildah module | ||||||
|  | //! | ||||||
|  | //! This example demonstrates how to use the Rhai scripting language | ||||||
|  | //! with the System Abstraction Layer (SAL) Buildah module. | ||||||
|  |  | ||||||
|  | use rhai::Engine; | ||||||
|  | use std::error::Error; | ||||||
|  |  | ||||||
|  | fn main() -> Result<(), Box<dyn Error>> { | ||||||
|  |     // Create a new Rhai engine | ||||||
|  |     let mut engine = Engine::new(); | ||||||
|  |      | ||||||
|  |     // Register println function | ||||||
|  |     engine.register_fn("println", |s: &str| println!("{}", s)); | ||||||
|  |      | ||||||
|  |     // Register SAL functions with the engine | ||||||
|  |     sal::rhai::register(&mut engine)?; | ||||||
|  |      | ||||||
|  |     // Run a Rhai script that uses SAL Buildah functions | ||||||
|  |     let script = r#" | ||||||
|  |         // List available images | ||||||
|  |         println("Listing available images:"); | ||||||
|  |         let images = buildah_images(); | ||||||
|  |         println("Found " + images.len() + " images"); | ||||||
|  |          | ||||||
|  |         // Create a container from an image (uncomment if you have a valid image) | ||||||
|  |         // let container = buildah_from("alpine:latest"); | ||||||
|  |         // println("Created container: " + container.stdout.trim()); | ||||||
|  |          | ||||||
|  |         // Build an image using options | ||||||
|  |         let build_options = buildah_new_build_options(); | ||||||
|  |         build_options.tag = "example-image:latest"; | ||||||
|  |         build_options.context_dir = "."; | ||||||
|  |         build_options.file = "example_Dockerfile"; | ||||||
|  |          | ||||||
|  |         println("Building image with options:"); | ||||||
|  |         println("  Tag: " + build_options.tag); | ||||||
|  |         println("  Context: " + build_options.context_dir); | ||||||
|  |         println("  Dockerfile: " + build_options.file); | ||||||
|  |          | ||||||
|  |         // Uncomment to actually build the image | ||||||
|  |         // let build_result = buildah_build(build_options); | ||||||
|  |         // println("Build result: " + build_result.success); | ||||||
|  |          | ||||||
|  |         // Create a container configuration | ||||||
|  |         let config_options = buildah_new_config_options(); | ||||||
|  |         config_options.author = "Rhai Example"; | ||||||
|  |         config_options.cmd = "/bin/sh -c 'echo Hello from Buildah'"; | ||||||
|  |          | ||||||
|  |         println("Container config options:"); | ||||||
|  |         println("  Author: " + config_options.author); | ||||||
|  |         println("  Command: " + config_options.cmd); | ||||||
|  |          | ||||||
|  |         // Commit options | ||||||
|  |         let commit_options = buildah_new_commit_options(); | ||||||
|  |         commit_options.format = "docker"; | ||||||
|  |         commit_options.squash = true; | ||||||
|  |         commit_options.rm = true; | ||||||
|  |          | ||||||
|  |         println("Commit options:"); | ||||||
|  |         println("  Format: " + commit_options.format); | ||||||
|  |         println("  Squash: " + commit_options.squash); | ||||||
|  |         println("  Remove container: " + commit_options.rm); | ||||||
|  |          | ||||||
|  |         // Return success | ||||||
|  |         true | ||||||
|  |     "#; | ||||||
|  |      | ||||||
|  |     // Evaluate the script | ||||||
|  |     let result = engine.eval::<bool>(script)?; | ||||||
|  |     println!("Script execution successful: {}", result); | ||||||
|  |      | ||||||
|  |     Ok(()) | ||||||
|  | } | ||||||
| @@ -2,9 +2,9 @@ | |||||||
| //! | //! | ||||||
| //! This example demonstrates how to use the Rhai scripting language | //! This example demonstrates how to use the Rhai scripting language | ||||||
| //! with the System Abstraction Layer (SAL) library. | //! with the System Abstraction Layer (SAL) library. | ||||||
|  |  | ||||||
| use rhai::Engine; | use rhai::Engine; | ||||||
| use sal::rhai; | use sal::rhai; | ||||||
|  | use sal::rhai::{self, Engine}; | ||||||
| use std::fs; | use std::fs; | ||||||
|  |  | ||||||
| fn main() -> Result<(), Box<dyn std::error::Error>> { | fn main() -> Result<(), Box<dyn std::error::Error>> { | ||||||
|   | |||||||
| @@ -3,65 +3,52 @@ | |||||||
| //! This example demonstrates how to use the Rhai scripting language | //! This example demonstrates how to use the Rhai scripting language | ||||||
| //! with the System Abstraction Layer (SAL) Process module. | //! with the System Abstraction Layer (SAL) Process module. | ||||||
|  |  | ||||||
| use rhai::{Engine, Map, Dynamic}; | use rhai::Engine; | ||||||
| use sal::rhai; | use std::error::Error; | ||||||
|  |  | ||||||
| fn main() -> Result<(), Box<dyn std::error::Error>> { | fn main() -> Result<(), Box<dyn Error>> { | ||||||
|     // Create a new Rhai engine |     // Create a new Rhai engine | ||||||
|     let mut engine = Engine::new(); |     let mut engine = Engine::new(); | ||||||
|      |      | ||||||
|     // Register SAL functions with the engine |     // Register SAL functions with the engine | ||||||
|     rhai::register(&mut engine)?; |     sal::rhai::register(&mut engine)?; | ||||||
|      |      | ||||||
|     // Run a Rhai script that uses SAL Process functions |     // Run a Rhai script that uses SAL Process functions | ||||||
|     let script = r#" |     let script = r#" | ||||||
|         // Check if a command exists |         // Check if a command exists | ||||||
|         let ls_exists = which("ls"); |         let ls_exists = which("ls"); | ||||||
|  |         println("ls command exists: " + ls_exists); | ||||||
|          |          | ||||||
|         // Run a simple command |         // Run a simple command | ||||||
|         let echo_result = run_command("echo 'Hello from Rhai!'"); |         let echo_result = run_command("echo 'Hello from Rhai!'"); | ||||||
|  |         println("Echo command output: " + echo_result.stdout); | ||||||
|  |         println("Echo command success: " + echo_result.success); | ||||||
|          |          | ||||||
|         // Run a command silently |         // Run a command silently | ||||||
|         let silent_result = run_silent("ls -la"); |         let silent_result = run_silent("ls -la"); | ||||||
|  |         println("Silent command success: " + silent_result.success); | ||||||
|  |          | ||||||
|  |         // Run a command with custom options using a Map | ||||||
|  |         let options = new_run_options(); | ||||||
|  |         options["die"] = false;      // Don't return error if command fails | ||||||
|  |         options["silent"] = true;     // Suppress output to stdout/stderr | ||||||
|  |         options["async_exec"] = false; // Run synchronously | ||||||
|  |         options["log"] = true;        // Log command execution | ||||||
|  |          | ||||||
|  |         let custom_result = run("echo 'Custom options'", options); | ||||||
|  |         println("Custom command success: " + custom_result.success); | ||||||
|          |          | ||||||
|         // List processes |         // List processes | ||||||
|         let processes = process_list(""); |         let processes = process_list(""); | ||||||
|  |         println("Number of processes: " + processes.len()); | ||||||
|          |          | ||||||
|         // Return a map with all the results |         // Return success | ||||||
|         { |         true | ||||||
|             ls_exists: ls_exists, |  | ||||||
|             echo_stdout: echo_result.stdout, |  | ||||||
|             echo_success: echo_result.success, |  | ||||||
|             silent_success: silent_result.success, |  | ||||||
|             process_count: processes.len() |  | ||||||
|         } |  | ||||||
|     "#; |     "#; | ||||||
|      |      | ||||||
|     // Evaluate the script and get the results |     // Evaluate the script | ||||||
|     let result = engine.eval::<Map>(script)?; |     let result = engine.eval::<bool>(script)?; | ||||||
|      |     println!("Script execution successful: {}", result); | ||||||
|     // Print the results |  | ||||||
|     println!("Script results:"); |  | ||||||
|      |  | ||||||
|     if let Some(ls_exists) = result.get("ls_exists") { |  | ||||||
|         println!("  ls command exists: {}", ls_exists); |  | ||||||
|     } |  | ||||||
|      |  | ||||||
|     if let Some(echo_stdout) = result.get("echo_stdout") { |  | ||||||
|         println!("  Echo command output: {}", echo_stdout.clone().into_string().unwrap()); |  | ||||||
|     } |  | ||||||
|      |  | ||||||
|     if let Some(echo_success) = result.get("echo_success") { |  | ||||||
|         println!("  Echo command success: {}", echo_success.clone().as_bool().unwrap()); |  | ||||||
|     } |  | ||||||
|      |  | ||||||
|     if let Some(silent_success) = result.get("silent_success") { |  | ||||||
|         println!("  Silent command success: {}", silent_success.clone().as_bool().unwrap()); |  | ||||||
|     } |  | ||||||
|      |  | ||||||
|     if let Some(process_count) = result.get("process_count") { |  | ||||||
|         println!("  Number of processes: {}", process_count.clone().as_int().unwrap()); |  | ||||||
|     } |  | ||||||
|      |      | ||||||
|     Ok(()) |     Ok(()) | ||||||
| } | } | ||||||
							
								
								
									
										39
									
								
								rhaiexamples/01_hello_world.rhai
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										39
									
								
								rhaiexamples/01_hello_world.rhai
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,39 @@ | |||||||
|  | // 01_hello_world.rhai | ||||||
|  | // A simple hello world script to demonstrate basic Rhai functionality | ||||||
|  |  | ||||||
|  | // Print a message | ||||||
|  | println("Hello from Rhai!"); | ||||||
|  |  | ||||||
|  | // Define a function | ||||||
|  | fn greet(name) { | ||||||
|  |     "Hello, " + name + "!" | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // Call the function and print the result | ||||||
|  | let greeting = greet("SAL User"); | ||||||
|  | println(greeting); | ||||||
|  |  | ||||||
|  | // Do some basic calculations | ||||||
|  | let a = 5; | ||||||
|  | let b = 7; | ||||||
|  | println(`${a} + ${b} = ${a + b}`); | ||||||
|  | println(`${a} * ${b} = ${a * b}`); | ||||||
|  |  | ||||||
|  | // Create and use an array | ||||||
|  | let numbers = [1, 2, 3, 4, 5]; | ||||||
|  | println("Numbers: " + numbers); | ||||||
|  | println("Sum of numbers: " + numbers.reduce(|sum, n| sum + n, 0)); | ||||||
|  |  | ||||||
|  | // Create and use a map | ||||||
|  | let person = #{ | ||||||
|  |     name: "John Doe", | ||||||
|  |     age: 30, | ||||||
|  |     occupation: "Developer" | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | println("Person: " + person); | ||||||
|  | println("Name: " + person.name); | ||||||
|  | println("Age: " + person.age); | ||||||
|  |  | ||||||
|  | // Return a success message | ||||||
|  | "Hello world script completed successfully!" | ||||||
							
								
								
									
										61
									
								
								rhaiexamples/02_file_operations.rhai
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										61
									
								
								rhaiexamples/02_file_operations.rhai
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,61 @@ | |||||||
|  | // 02_file_operations.rhai | ||||||
|  | // Demonstrates file system operations using SAL | ||||||
|  |  | ||||||
|  | // Create a test directory | ||||||
|  | let test_dir = "rhai_test_dir"; | ||||||
|  | println(`Creating directory: ${test_dir}`); | ||||||
|  | let mkdir_result = mkdir(test_dir); | ||||||
|  | println(`Directory creation result: ${mkdir_result}`); | ||||||
|  |  | ||||||
|  | // Check if the directory exists | ||||||
|  | let dir_exists = exist(test_dir); | ||||||
|  | println(`Directory exists: ${dir_exists}`); | ||||||
|  |  | ||||||
|  | // Create a test file | ||||||
|  | let test_file = test_dir + "/test_file.txt"; | ||||||
|  | let file_content = "This is a test file created by Rhai script."; | ||||||
|  |  | ||||||
|  | // Create the file using a different approach | ||||||
|  | println(`Creating file: ${test_file}`); | ||||||
|  | // First ensure the directory exists | ||||||
|  | run_command(`mkdir -p ${test_dir}`); | ||||||
|  | // Then create the file using a different approach | ||||||
|  | // Use run_command with sh -c to properly handle shell features | ||||||
|  | let write_cmd = `sh -c 'touch ${test_file} && echo "${file_content}" > ${test_file}'`; | ||||||
|  | let write_result = run_command(write_cmd); | ||||||
|  | println(`File creation result: ${write_result.success}`); | ||||||
|  |  | ||||||
|  | // Wait a moment to ensure the file is created | ||||||
|  | run_command("sleep 1"); | ||||||
|  |  | ||||||
|  | // Check if the file exists | ||||||
|  | let file_exists = exist(test_file); | ||||||
|  | println(`File exists: ${file_exists}`); | ||||||
|  |  | ||||||
|  | // Get file size | ||||||
|  | if file_exists { | ||||||
|  |     let size = file_size(test_file); | ||||||
|  |     println(`File size: ${size} bytes`); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // Copy the file | ||||||
|  | let copied_file = test_dir + "/copied_file.txt"; | ||||||
|  | println(`Copying file to: ${copied_file}`); | ||||||
|  | let copy_result = copy(test_file, copied_file); | ||||||
|  | println(`File copy result: ${copy_result}`); | ||||||
|  |  | ||||||
|  | // Find files in the directory | ||||||
|  | println("Finding files in the test directory:"); | ||||||
|  | let files = find_files(test_dir, "*.txt"); | ||||||
|  | for file in files { | ||||||
|  |     println(`  - ${file}`); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // Clean up (uncomment to actually delete the files) | ||||||
|  | // println("Cleaning up..."); | ||||||
|  | // delete(copied_file); | ||||||
|  | // delete(test_file); | ||||||
|  | // delete(test_dir); | ||||||
|  | // println("Cleanup complete"); | ||||||
|  |  | ||||||
|  | "File operations script completed successfully!" | ||||||
							
								
								
									
										64
									
								
								rhaiexamples/03_process_management.rhai
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										64
									
								
								rhaiexamples/03_process_management.rhai
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,64 @@ | |||||||
|  | // 03_process_management.rhai | ||||||
|  | // Demonstrates process management operations using SAL | ||||||
|  |  | ||||||
|  | // Check if common commands exist | ||||||
|  | println("Checking if common commands exist:"); | ||||||
|  | let commands = ["ls", "echo", "cat", "grep"]; | ||||||
|  | for cmd in commands { | ||||||
|  |     let exists = which(cmd); | ||||||
|  |     println(`  - ${cmd}: ${exists}`); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // Run a simple command | ||||||
|  | println("\nRunning a simple echo command:"); | ||||||
|  | let echo_result = run_command("echo 'Hello from Rhai process management!'"); | ||||||
|  | println(`Command output: ${echo_result.stdout}`); | ||||||
|  | // The CommandResult type doesn't have an exit_code property | ||||||
|  | println(`Success: ${echo_result.success}`); | ||||||
|  |  | ||||||
|  | // Run a command silently (no output to console) | ||||||
|  | println("\nRunning a command silently:"); | ||||||
|  | let silent_result = run_silent("ls -la"); | ||||||
|  | println(`Command success: ${silent_result.success}`); | ||||||
|  | println(`Command output length: ${silent_result.stdout.len()} characters`); | ||||||
|  |  | ||||||
|  | // Create custom run options | ||||||
|  | println("\nRunning a command with custom options:"); | ||||||
|  | let options = new_run_options(); | ||||||
|  | options["die"] = false;       // Don't return error if command fails | ||||||
|  | options["silent"] = true;     // Suppress output to stdout/stderr | ||||||
|  | options["async_exec"] = false; // Run synchronously | ||||||
|  | options["log"] = true;        // Log command execution | ||||||
|  |  | ||||||
|  | let custom_result = run("echo 'Custom options test'", options); | ||||||
|  | println(`Command success: ${custom_result.success}`); | ||||||
|  | println(`Command output: ${custom_result.stdout}`); | ||||||
|  |  | ||||||
|  | // List processes | ||||||
|  | println("\nListing processes (limited to 5):"); | ||||||
|  | let processes = process_list(""); | ||||||
|  | let count = 0; | ||||||
|  | for proc in processes { | ||||||
|  |     if count >= 5 { | ||||||
|  |         break; | ||||||
|  |     } | ||||||
|  |     // Just print the PID since we're not sure what other properties are available | ||||||
|  |     println(`  - PID: ${proc.pid}`); | ||||||
|  |     count += 1; | ||||||
|  | } | ||||||
|  | println(`Total processes: ${processes.len()}`); | ||||||
|  |  | ||||||
|  | // Run a command that will create a background process | ||||||
|  | // Note: This is just for demonstration, the process will be short-lived | ||||||
|  | println("\nRunning a background process:"); | ||||||
|  | let bg_options = new_run_options(); | ||||||
|  | bg_options["async_exec"] = true; | ||||||
|  | // Fix the command to avoid issues with shell interpretation | ||||||
|  | let bg_result = run("sleep 1", bg_options); | ||||||
|  | println("Background process started"); | ||||||
|  |  | ||||||
|  | // Wait a moment to let the background process run | ||||||
|  | run_command("sleep 0.5"); | ||||||
|  | println("Main script continuing while background process runs"); | ||||||
|  |  | ||||||
|  | "Process management script completed successfully!" | ||||||
							
								
								
									
										112
									
								
								rhaiexamples/04_buildah_operations.rhai
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										112
									
								
								rhaiexamples/04_buildah_operations.rhai
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,112 @@ | |||||||
|  | // 04_buildah_operations.rhai | ||||||
|  | // Demonstrates container operations using SAL's buildah integration | ||||||
|  | // Note: This script requires buildah to be installed and may need root privileges | ||||||
|  |  | ||||||
|  | // Check if buildah is installed | ||||||
|  | let buildah_exists = which("buildah"); | ||||||
|  | println(`Buildah exists: ${buildah_exists}`); | ||||||
|  |  | ||||||
|  | // List available images (only if buildah is installed) | ||||||
|  | println("Listing available container images:"); | ||||||
|  | if buildah_exists != "" { | ||||||
|  |     // This try/catch block will handle any errors that might occur | ||||||
|  |     // when trying to use buildah functions | ||||||
|  |     try { | ||||||
|  |         let images = buildah_images(); | ||||||
|  |         println(`Found ${images.len()} images`); | ||||||
|  |          | ||||||
|  |         // Print image details (limited to 3) | ||||||
|  |         let count = 0; | ||||||
|  |         for img in images { | ||||||
|  |             if count >= 3 { | ||||||
|  |                 break; | ||||||
|  |             } | ||||||
|  |             println(`  - ID: ${img.id}, Name: ${img.name}, Created: ${img.created}`); | ||||||
|  |             count += 1; | ||||||
|  |         } | ||||||
|  |     } catch(err) { | ||||||
|  |         println(`Error accessing buildah: ${err}`); | ||||||
|  |     } | ||||||
|  | } else { | ||||||
|  |     println("Buildah is not installed. Skipping container image operations."); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // Remove the duplicate code that was causing the error | ||||||
|  |  | ||||||
|  | // The following operations are commented out as they require buildah to be installed | ||||||
|  | // and may need root privileges. Uncomment them if you want to try them out. | ||||||
|  |  | ||||||
|  | // Create a container from an image | ||||||
|  | // println("\nCreating a container from alpine image:"); | ||||||
|  | // let container = from("alpine:latest"); | ||||||
|  | // println(`Container ID: ${container.stdout.trim()}`); | ||||||
|  |  | ||||||
|  | // Run a command in the container | ||||||
|  | // println("\nRunning a command in the container:"); | ||||||
|  | // let run_result = run(container.stdout.trim(), "echo 'Hello from container'"); | ||||||
|  | // println(`Command output: ${run_result.stdout}`); | ||||||
|  |  | ||||||
|  | // Add a file to the container | ||||||
|  | // println("\nAdding a file to the container:"); | ||||||
|  | // let test_file = "test_file.txt"; | ||||||
|  | // run_command(`echo "Test content" > ${test_file}`); | ||||||
|  | // let add_result = add(container.stdout.trim(), test_file, "/"); | ||||||
|  | // println(`Add result: ${add_result.success}`); | ||||||
|  |  | ||||||
|  | // Commit the container to create a new image | ||||||
|  | // println("\nCommitting the container to create a new image:"); | ||||||
|  | // let commit_result = commit(container.stdout.trim(), "my-custom-image:latest"); | ||||||
|  | // println(`Commit result: ${commit_result.success}`); | ||||||
|  |  | ||||||
|  | // Remove the container | ||||||
|  | // println("\nRemoving the container:"); | ||||||
|  | // let remove_result = remove(container.stdout.trim()); | ||||||
|  | // println(`Remove result: ${remove_result.success}`); | ||||||
|  |  | ||||||
|  | // Clean up the test file | ||||||
|  | // delete(test_file); | ||||||
|  |  | ||||||
|  | // Only demonstrate buildah options if buildah is installed | ||||||
|  | if buildah_exists != "" { | ||||||
|  |     try { | ||||||
|  |         // Demonstrate build options | ||||||
|  |         println("\nDemonstrating build options:"); | ||||||
|  |         let build_options = buildah_new_build_options(); | ||||||
|  |         build_options.tag = "example-image:latest"; | ||||||
|  |         build_options.context_dir = "."; | ||||||
|  |         build_options.file = "example_Dockerfile"; | ||||||
|  |  | ||||||
|  |         println("Build options configured:"); | ||||||
|  |         println(`  - Tag: ${build_options.tag}`); | ||||||
|  |         println(`  - Context: ${build_options.context_dir}`); | ||||||
|  |         println(`  - Dockerfile: ${build_options.file}`); | ||||||
|  |  | ||||||
|  |         // Demonstrate commit options | ||||||
|  |         println("\nDemonstrating commit options:"); | ||||||
|  |         let commit_options = buildah_new_commit_options(); | ||||||
|  |         commit_options.format = "docker"; | ||||||
|  |         commit_options.squash = true; | ||||||
|  |         commit_options.rm = true; | ||||||
|  |  | ||||||
|  |         println("Commit options configured:"); | ||||||
|  |         println(`  - Format: ${commit_options.format}`); | ||||||
|  |         println(`  - Squash: ${commit_options.squash}`); | ||||||
|  |         println(`  - Remove container: ${commit_options.rm}`); | ||||||
|  |  | ||||||
|  |         // Demonstrate config options | ||||||
|  |         println("\nDemonstrating config options:"); | ||||||
|  |         let config_options = buildah_new_config_options(); | ||||||
|  |         config_options.author = "Rhai Example"; | ||||||
|  |         config_options.cmd = "/bin/sh -c 'echo Hello from Buildah'"; | ||||||
|  |  | ||||||
|  |         println("Config options configured:"); | ||||||
|  |         println(`  - Author: ${config_options.author}`); | ||||||
|  |         println(`  - Command: ${config_options.cmd}`); | ||||||
|  |     } catch(err) { | ||||||
|  |         println(`Error accessing buildah options: ${err}`); | ||||||
|  |     } | ||||||
|  | } else { | ||||||
|  |     println("\nSkipping buildah options demonstration since buildah is not installed."); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | "Buildah operations script completed successfully!" | ||||||
							
								
								
									
										30
									
								
								src/bin/herodo.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										30
									
								
								src/bin/herodo.rs
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,30 @@ | |||||||
|  | //! Herodo binary entry point | ||||||
|  | //! | ||||||
|  | //! This is the main entry point for the herodo binary. | ||||||
|  | //! It parses command line arguments and calls into the implementation in the cmd module. | ||||||
|  |  | ||||||
|  | use clap::{App, Arg}; | ||||||
|  |  | ||||||
|  | fn main() -> Result<(), Box<dyn std::error::Error>> { | ||||||
|  |     // Parse command line arguments | ||||||
|  |     let matches = App::new("herodo") | ||||||
|  |         .version("0.1.0") | ||||||
|  |         .author("SAL Team") | ||||||
|  |         .about("Executes Rhai scripts for SAL") | ||||||
|  |         .arg( | ||||||
|  |             Arg::with_name("path") | ||||||
|  |                 .short("p") | ||||||
|  |                 .long("path") | ||||||
|  |                 .value_name("PATH") | ||||||
|  |                 .help("Path to directory containing Rhai scripts") | ||||||
|  |                 .required(true) | ||||||
|  |                 .takes_value(true), | ||||||
|  |         ) | ||||||
|  |         .get_matches(); | ||||||
|  |  | ||||||
|  |     // Get the script path from arguments | ||||||
|  |     let script_path = matches.value_of("path").unwrap(); | ||||||
|  |      | ||||||
|  |     // Call the run function from the cmd module | ||||||
|  |     sal::cmd::herodo::run(script_path) | ||||||
|  | } | ||||||
							
								
								
									
										84
									
								
								src/cmd/herodo.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										84
									
								
								src/cmd/herodo.rs
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,84 @@ | |||||||
|  | //! Herodo - A Rhai script executor for SAL | ||||||
|  | //! | ||||||
|  | //! This binary loads the Rhai engine, registers all SAL modules, | ||||||
|  | //! and executes Rhai scripts from a specified directory in sorted order. | ||||||
|  |  | ||||||
|  | // Removed unused imports | ||||||
|  | use rhai::Engine; | ||||||
|  | use std::error::Error; | ||||||
|  | use std::fs; | ||||||
|  | use std::path::{Path, PathBuf}; | ||||||
|  | use std::process; | ||||||
|  |  | ||||||
|  | /// Run the herodo script executor with the given script path | ||||||
|  | /// | ||||||
|  | /// # Arguments | ||||||
|  | /// | ||||||
|  | /// * `script_path` - Path to the directory containing Rhai scripts | ||||||
|  | /// | ||||||
|  | /// # Returns | ||||||
|  | /// | ||||||
|  | /// Result indicating success or failure | ||||||
|  | pub fn run(script_path: &str) -> Result<(), Box<dyn Error>> { | ||||||
|  |     let script_dir = Path::new(script_path); | ||||||
|  |  | ||||||
|  |     // Check if the directory exists | ||||||
|  |     if !script_dir.exists() || !script_dir.is_dir() { | ||||||
|  |         eprintln!("Error: '{}' is not a valid directory", script_path); | ||||||
|  |         process::exit(1); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     // Create a new Rhai engine | ||||||
|  |     let mut engine = Engine::new(); | ||||||
|  |  | ||||||
|  |     // Register println function for output | ||||||
|  |     engine.register_fn("println", |s: &str| println!("{}", s)); | ||||||
|  |  | ||||||
|  |     // Register all SAL modules with the engine | ||||||
|  |     crate::rhai::register(&mut engine)?; | ||||||
|  |  | ||||||
|  |     // Find all .rhai files in the directory | ||||||
|  |     let mut script_files: Vec<PathBuf> = fs::read_dir(script_dir)? | ||||||
|  |         .filter_map(Result::ok) | ||||||
|  |         .filter(|entry| { | ||||||
|  |             entry.path().is_file() && | ||||||
|  |             entry.path().extension().map_or(false, |ext| ext == "rhai") | ||||||
|  |         }) | ||||||
|  |         .map(|entry| entry.path()) | ||||||
|  |         .collect(); | ||||||
|  |  | ||||||
|  |     // Sort the script files by name | ||||||
|  |     script_files.sort(); | ||||||
|  |  | ||||||
|  |     if script_files.is_empty() { | ||||||
|  |         println!("No Rhai scripts found in '{}'", script_path); | ||||||
|  |         return Ok(()); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     println!("Found {} Rhai scripts to execute:", script_files.len()); | ||||||
|  |      | ||||||
|  |     // Execute each script in sorted order | ||||||
|  |     for script_file in script_files { | ||||||
|  |         println!("\nExecuting: {}", script_file.display()); | ||||||
|  |          | ||||||
|  |         // Read the script content | ||||||
|  |         let script = fs::read_to_string(&script_file)?; | ||||||
|  |          | ||||||
|  |         // Execute the script | ||||||
|  |         match engine.eval::<rhai::Dynamic>(&script) { | ||||||
|  |             Ok(result) => { | ||||||
|  |                 println!("Script executed successfully"); | ||||||
|  |                 if !result.is_unit() { | ||||||
|  |                     println!("Result: {}", result); | ||||||
|  |                 } | ||||||
|  |             }, | ||||||
|  |             Err(err) => { | ||||||
|  |                 eprintln!("Error executing script: {}", err); | ||||||
|  |                 // Continue with the next script instead of stopping | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     println!("\nAll scripts executed"); | ||||||
|  |     Ok(()) | ||||||
|  | } | ||||||
							
								
								
									
										5
									
								
								src/cmd/mod.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										5
									
								
								src/cmd/mod.rs
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,5 @@ | |||||||
|  | //! Command-line tools for SAL | ||||||
|  | //! | ||||||
|  | //! This module contains command-line tools built on top of the SAL library. | ||||||
|  |  | ||||||
|  | pub mod herodo; | ||||||
| @@ -43,6 +43,7 @@ pub mod redisclient; | |||||||
| pub mod text; | pub mod text; | ||||||
| pub mod virt; | pub mod virt; | ||||||
| pub mod rhai; | pub mod rhai; | ||||||
|  | pub mod cmd; | ||||||
|  |  | ||||||
| // Version information | // Version information | ||||||
| /// Returns the version of the SAL library | /// Returns the version of the SAL library | ||||||
|   | |||||||
							
								
								
									
										391
									
								
								src/rhai/buildah.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										391
									
								
								src/rhai/buildah.rs
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,391 @@ | |||||||
|  | //! Rhai wrappers for Buildah module functions | ||||||
|  | //! | ||||||
|  | //! This module provides Rhai wrappers for the functions in the Buildah module. | ||||||
|  |  | ||||||
|  | use rhai::{Engine, EvalAltResult, Array, Dynamic, Map}; | ||||||
|  | use std::collections::HashMap; | ||||||
|  | use crate::virt::buildah::{self, BuildahError, Image}; | ||||||
|  | use crate::process::CommandResult; | ||||||
|  |  | ||||||
|  | /// Register Buildah module functions with the Rhai engine | ||||||
|  | /// | ||||||
|  | /// # Arguments | ||||||
|  | /// | ||||||
|  | /// * `engine` - The Rhai engine to register the functions with | ||||||
|  | /// | ||||||
|  | /// # Returns | ||||||
|  | /// | ||||||
|  | /// * `Result<(), Box<EvalAltResult>>` - Ok if registration was successful, Err otherwise | ||||||
|  | pub fn register_buildah_module(engine: &mut Engine) -> Result<(), Box<EvalAltResult>> { | ||||||
|  |     // Register types | ||||||
|  |     register_buildah_types(engine)?; | ||||||
|  |      | ||||||
|  |     // Register container functions | ||||||
|  |     engine.register_fn("buildah_from", from); | ||||||
|  |     engine.register_fn("buildah_run", run); | ||||||
|  |     engine.register_fn("buildah_run_with_isolation", run_with_isolation); | ||||||
|  |     engine.register_fn("buildah_copy", copy); | ||||||
|  |     engine.register_fn("buildah_add", add); | ||||||
|  |     engine.register_fn("buildah_commit", commit); | ||||||
|  |     engine.register_fn("buildah_remove", remove); | ||||||
|  |     engine.register_fn("buildah_list", list); | ||||||
|  |     engine.register_fn("buildah_build", build_with_options); | ||||||
|  |     engine.register_fn("buildah_new_build_options", new_build_options); | ||||||
|  |      | ||||||
|  |     // Register image functions | ||||||
|  |     engine.register_fn("buildah_images", images); | ||||||
|  |     engine.register_fn("buildah_image_remove", image_remove); | ||||||
|  |     engine.register_fn("buildah_image_push", image_push); | ||||||
|  |     engine.register_fn("buildah_image_tag", image_tag); | ||||||
|  |     engine.register_fn("buildah_image_pull", image_pull); | ||||||
|  |     engine.register_fn("buildah_image_commit", image_commit_with_options); | ||||||
|  |     engine.register_fn("buildah_new_commit_options", new_commit_options); | ||||||
|  |     engine.register_fn("buildah_config", config_with_options); | ||||||
|  |     engine.register_fn("buildah_new_config_options", new_config_options); | ||||||
|  |      | ||||||
|  |     Ok(()) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | /// Register Buildah module types with the Rhai engine | ||||||
|  | fn register_buildah_types(engine: &mut Engine) -> Result<(), Box<EvalAltResult>> { | ||||||
|  |     // Register Image type and methods | ||||||
|  |     engine.register_type_with_name::<Image>("BuildahImage"); | ||||||
|  |      | ||||||
|  |     // Register getters for Image properties | ||||||
|  |     engine.register_get("id", |img: &mut Image| img.id.clone()); | ||||||
|  |     engine.register_get("names", |img: &mut Image| { | ||||||
|  |         let mut array = Array::new(); | ||||||
|  |         for name in &img.names { | ||||||
|  |             array.push(Dynamic::from(name.clone())); | ||||||
|  |         } | ||||||
|  |         array | ||||||
|  |     }); | ||||||
|  |     engine.register_get("size", |img: &mut Image| img.size.clone()); | ||||||
|  |     engine.register_get("created", |img: &mut Image| img.created.clone()); | ||||||
|  |      | ||||||
|  |     Ok(()) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // Helper functions for error conversion | ||||||
|  | fn buildah_error_to_rhai_error<T>(result: Result<T, BuildahError>) -> Result<T, Box<EvalAltResult>> { | ||||||
|  |     result.map_err(|e| { | ||||||
|  |         Box::new(EvalAltResult::ErrorRuntime( | ||||||
|  |             format!("Buildah error: {}", e).into(), | ||||||
|  |             rhai::Position::NONE | ||||||
|  |         )) | ||||||
|  |     }) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | /// Create a new Map with default build options | ||||||
|  | pub fn new_build_options() -> Map { | ||||||
|  |     let mut map = Map::new(); | ||||||
|  |     map.insert("tag".into(), Dynamic::UNIT); | ||||||
|  |     map.insert("context_dir".into(), Dynamic::from(".")); | ||||||
|  |     map.insert("file".into(), Dynamic::from("Dockerfile")); | ||||||
|  |     map.insert("isolation".into(), Dynamic::UNIT); | ||||||
|  |     map | ||||||
|  | } | ||||||
|  |  | ||||||
|  | /// Create a new Map with default commit options | ||||||
|  | pub fn new_commit_options() -> Map { | ||||||
|  |     let mut map = Map::new(); | ||||||
|  |     map.insert("format".into(), Dynamic::UNIT); | ||||||
|  |     map.insert("squash".into(), Dynamic::from(false)); | ||||||
|  |     map.insert("rm".into(), Dynamic::from(false)); | ||||||
|  |     map | ||||||
|  | } | ||||||
|  |  | ||||||
|  | /// Create a new Map for config options | ||||||
|  | pub fn new_config_options() -> Map { | ||||||
|  |     Map::new() | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // | ||||||
|  | // Container Function Wrappers | ||||||
|  | // | ||||||
|  |  | ||||||
|  | /// Wrapper for buildah::from | ||||||
|  | /// | ||||||
|  | /// Create a container from an image. | ||||||
|  | pub fn from(image: &str) -> Result<CommandResult, Box<EvalAltResult>> { | ||||||
|  |     buildah_error_to_rhai_error(buildah::from(image)) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | /// Wrapper for buildah::run | ||||||
|  | /// | ||||||
|  | /// Run a command in a container. | ||||||
|  | pub fn run(container: &str, command: &str) -> Result<CommandResult, Box<EvalAltResult>> { | ||||||
|  |     buildah_error_to_rhai_error(buildah::run(container, command)) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | /// Wrapper for buildah::run_with_isolation | ||||||
|  | /// | ||||||
|  | /// Run a command in a container with specified isolation. | ||||||
|  | pub fn run_with_isolation(container: &str, command: &str, isolation: &str) -> Result<CommandResult, Box<EvalAltResult>> { | ||||||
|  |     buildah_error_to_rhai_error(buildah::run_with_isolation(container, command, isolation)) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | /// Wrapper for buildah::copy | ||||||
|  | /// | ||||||
|  | /// Copy files into a container. | ||||||
|  | pub fn copy(container: &str, source: &str, dest: &str) -> Result<CommandResult, Box<EvalAltResult>> { | ||||||
|  |     buildah_error_to_rhai_error(buildah::copy(container, source, dest)) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | /// Wrapper for buildah::add | ||||||
|  | /// | ||||||
|  | /// Add files into a container. | ||||||
|  | pub fn add(container: &str, source: &str, dest: &str) -> Result<CommandResult, Box<EvalAltResult>> { | ||||||
|  |     buildah_error_to_rhai_error(buildah::add(container, source, dest)) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | /// Wrapper for buildah::commit | ||||||
|  | /// | ||||||
|  | /// Commit a container to an image. | ||||||
|  | pub fn commit(container: &str, image_name: &str) -> Result<CommandResult, Box<EvalAltResult>> { | ||||||
|  |     buildah_error_to_rhai_error(buildah::commit(container, image_name)) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | /// Wrapper for buildah::remove | ||||||
|  | /// | ||||||
|  | /// Remove a container. | ||||||
|  | pub fn remove(container: &str) -> Result<CommandResult, Box<EvalAltResult>> { | ||||||
|  |     buildah_error_to_rhai_error(buildah::remove(container)) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | /// Wrapper for buildah::list | ||||||
|  | /// | ||||||
|  | /// List containers. | ||||||
|  | pub fn list() -> Result<CommandResult, Box<EvalAltResult>> { | ||||||
|  |     buildah_error_to_rhai_error(buildah::list()) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | /// Build an image with options specified in a Map | ||||||
|  | /// | ||||||
|  | /// This provides a builder-style interface for Rhai scripts. | ||||||
|  | /// | ||||||
|  | /// # Example | ||||||
|  | /// | ||||||
|  | /// ```rhai | ||||||
|  | /// let options = buildah_new_build_options(); | ||||||
|  | /// options.tag = "my-image:latest"; | ||||||
|  | /// options.context_dir = "."; | ||||||
|  | /// options.file = "Dockerfile"; | ||||||
|  | /// options.isolation = "chroot"; | ||||||
|  | /// let result = buildah_build(options); | ||||||
|  | /// ``` | ||||||
|  | pub fn build_with_options(options: Map) -> Result<CommandResult, Box<EvalAltResult>> { | ||||||
|  |     // Extract options from the map | ||||||
|  |     let tag_option = match options.get("tag") { | ||||||
|  |         Some(tag) => { | ||||||
|  |             if tag.is_unit() { | ||||||
|  |                 None | ||||||
|  |             } else if let Ok(tag_str) = tag.clone().into_string() { | ||||||
|  |                 Some(tag_str) | ||||||
|  |             } else { | ||||||
|  |                 return Err(Box::new(EvalAltResult::ErrorRuntime( | ||||||
|  |                     "tag must be a string".into(), | ||||||
|  |                     rhai::Position::NONE | ||||||
|  |                 ))); | ||||||
|  |             } | ||||||
|  |         }, | ||||||
|  |         None => None | ||||||
|  |     }; | ||||||
|  |      | ||||||
|  |     let context_dir = match options.get("context_dir") { | ||||||
|  |         Some(dir) => { | ||||||
|  |             if let Ok(dir_str) = dir.clone().into_string() { | ||||||
|  |                 dir_str | ||||||
|  |             } else { | ||||||
|  |                 return Err(Box::new(EvalAltResult::ErrorRuntime( | ||||||
|  |                     "context_dir must be a string".into(), | ||||||
|  |                     rhai::Position::NONE | ||||||
|  |                 ))); | ||||||
|  |             } | ||||||
|  |         }, | ||||||
|  |         None => String::from(".") | ||||||
|  |     }; | ||||||
|  |      | ||||||
|  |     let file = match options.get("file") { | ||||||
|  |         Some(file) => { | ||||||
|  |             if let Ok(file_str) = file.clone().into_string() { | ||||||
|  |                 file_str | ||||||
|  |             } else { | ||||||
|  |                 return Err(Box::new(EvalAltResult::ErrorRuntime( | ||||||
|  |                     "file must be a string".into(), | ||||||
|  |                     rhai::Position::NONE | ||||||
|  |                 ))); | ||||||
|  |             } | ||||||
|  |         }, | ||||||
|  |         None => String::from("Dockerfile") | ||||||
|  |     }; | ||||||
|  |      | ||||||
|  |     let isolation_option = match options.get("isolation") { | ||||||
|  |         Some(isolation) => { | ||||||
|  |             if isolation.is_unit() { | ||||||
|  |                 None | ||||||
|  |             } else if let Ok(isolation_str) = isolation.clone().into_string() { | ||||||
|  |                 Some(isolation_str) | ||||||
|  |             } else { | ||||||
|  |                 return Err(Box::new(EvalAltResult::ErrorRuntime( | ||||||
|  |                     "isolation must be a string".into(), | ||||||
|  |                     rhai::Position::NONE | ||||||
|  |                 ))); | ||||||
|  |             } | ||||||
|  |         }, | ||||||
|  |         None => None | ||||||
|  |     }; | ||||||
|  |      | ||||||
|  |     // Convert String to &str for the function call | ||||||
|  |     let tag_ref = tag_option.as_deref(); | ||||||
|  |     let isolation_ref = isolation_option.as_deref(); | ||||||
|  |      | ||||||
|  |     // Call the buildah build function | ||||||
|  |     buildah_error_to_rhai_error(buildah::build(tag_ref, &context_dir, &file, isolation_ref)) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // | ||||||
|  | // Image Function Wrappers | ||||||
|  | // | ||||||
|  |  | ||||||
|  | /// Wrapper for buildah::images | ||||||
|  | /// | ||||||
|  | /// List images in local storage. | ||||||
|  | pub fn images() -> Result<Array, Box<EvalAltResult>> { | ||||||
|  |     let images = buildah_error_to_rhai_error(buildah::images())?; | ||||||
|  |      | ||||||
|  |     // Convert Vec<Image> to Rhai Array | ||||||
|  |     let mut array = Array::new(); | ||||||
|  |     for image in images { | ||||||
|  |         array.push(Dynamic::from(image)); | ||||||
|  |     } | ||||||
|  |      | ||||||
|  |     Ok(array) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | /// Wrapper for buildah::image_remove | ||||||
|  | /// | ||||||
|  | /// Remove one or more images. | ||||||
|  | pub fn image_remove(image: &str) -> Result<CommandResult, Box<EvalAltResult>> { | ||||||
|  |     buildah_error_to_rhai_error(buildah::image_remove(image)) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | /// Wrapper for buildah::image_push | ||||||
|  | /// | ||||||
|  | /// Push an image to a registry. | ||||||
|  | pub fn image_push(image: &str, destination: &str, tls_verify: bool) -> Result<CommandResult, Box<EvalAltResult>> { | ||||||
|  |     buildah_error_to_rhai_error(buildah::image_push(image, destination, tls_verify)) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | /// Wrapper for buildah::image_tag | ||||||
|  | /// | ||||||
|  | /// Add an additional name to a local image. | ||||||
|  | pub fn image_tag(image: &str, new_name: &str) -> Result<CommandResult, Box<EvalAltResult>> { | ||||||
|  |     buildah_error_to_rhai_error(buildah::image_tag(image, new_name)) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | /// Wrapper for buildah::image_pull | ||||||
|  | /// | ||||||
|  | /// Pull an image from a registry. | ||||||
|  | pub fn image_pull(image: &str, tls_verify: bool) -> Result<CommandResult, Box<EvalAltResult>> { | ||||||
|  |     buildah_error_to_rhai_error(buildah::image_pull(image, tls_verify)) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | /// Commit a container to an image with options specified in a Map | ||||||
|  | /// | ||||||
|  | /// This provides a builder-style interface for Rhai scripts. | ||||||
|  | /// | ||||||
|  | /// # Example | ||||||
|  | /// | ||||||
|  | /// ```rhai | ||||||
|  | /// let options = buildah_new_commit_options(); | ||||||
|  | /// options.format = "docker"; | ||||||
|  | /// options.squash = true; | ||||||
|  | /// options.rm = true; | ||||||
|  | /// let result = buildah_image_commit("my-container", "my-image:latest", options); | ||||||
|  | /// ``` | ||||||
|  | pub fn image_commit_with_options(container: &str, image_name: &str, options: Map) -> Result<CommandResult, Box<EvalAltResult>> { | ||||||
|  |     // Extract options from the map | ||||||
|  |     let format_option = match options.get("format") { | ||||||
|  |         Some(format) => { | ||||||
|  |             if format.is_unit() { | ||||||
|  |                 None | ||||||
|  |             } else if let Ok(format_str) = format.clone().into_string() { | ||||||
|  |                 Some(format_str) | ||||||
|  |             } else { | ||||||
|  |                 return Err(Box::new(EvalAltResult::ErrorRuntime( | ||||||
|  |                     "format must be a string".into(), | ||||||
|  |                     rhai::Position::NONE | ||||||
|  |                 ))); | ||||||
|  |             } | ||||||
|  |         }, | ||||||
|  |         None => None | ||||||
|  |     }; | ||||||
|  |      | ||||||
|  |     let squash = match options.get("squash") { | ||||||
|  |         Some(squash) => { | ||||||
|  |             if let Ok(squash_val) = squash.clone().as_bool() { | ||||||
|  |                 squash_val | ||||||
|  |             } else { | ||||||
|  |                 return Err(Box::new(EvalAltResult::ErrorRuntime( | ||||||
|  |                     "squash must be a boolean".into(), | ||||||
|  |                     rhai::Position::NONE | ||||||
|  |                 ))); | ||||||
|  |             } | ||||||
|  |         }, | ||||||
|  |         None => false | ||||||
|  |     }; | ||||||
|  |      | ||||||
|  |     let rm = match options.get("rm") { | ||||||
|  |         Some(rm) => { | ||||||
|  |             if let Ok(rm_val) = rm.clone().as_bool() { | ||||||
|  |                 rm_val | ||||||
|  |             } else { | ||||||
|  |                 return Err(Box::new(EvalAltResult::ErrorRuntime( | ||||||
|  |                     "rm must be a boolean".into(), | ||||||
|  |                     rhai::Position::NONE | ||||||
|  |                 ))); | ||||||
|  |             } | ||||||
|  |         }, | ||||||
|  |         None => false | ||||||
|  |     }; | ||||||
|  |      | ||||||
|  |     // Convert String to &str for the function call | ||||||
|  |     let format_ref = format_option.as_deref(); | ||||||
|  |      | ||||||
|  |     // Call the buildah image_commit function | ||||||
|  |     buildah_error_to_rhai_error(buildah::image_commit(container, image_name, format_ref, squash, rm)) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | /// Configure a container with options specified in a Map | ||||||
|  | /// | ||||||
|  | /// This provides a builder-style interface for Rhai scripts. | ||||||
|  | /// | ||||||
|  | /// # Example | ||||||
|  | /// | ||||||
|  | /// ```rhai | ||||||
|  | /// let options = buildah_new_config_options(); | ||||||
|  | /// options.author = "John Doe"; | ||||||
|  | /// options.cmd = "echo Hello"; | ||||||
|  | /// options.entrypoint = "/bin/sh -c"; | ||||||
|  | /// let result = buildah_config("my-container", options); | ||||||
|  | /// ``` | ||||||
|  | pub fn config_with_options(container: &str, options: Map) -> Result<CommandResult, Box<EvalAltResult>> { | ||||||
|  |     // Convert Rhai Map to Rust HashMap | ||||||
|  |     let mut config_options = HashMap::<String, String>::new(); | ||||||
|  |      | ||||||
|  |     for (key, value) in options.iter() { | ||||||
|  |         if let Ok(value_str) = value.clone().into_string() { | ||||||
|  |             // Convert SmartString to String | ||||||
|  |             config_options.insert(key.to_string(), value_str); | ||||||
|  |         } else { | ||||||
|  |             return Err(Box::new(EvalAltResult::ErrorRuntime( | ||||||
|  |                 format!("Option '{}' must be a string", key).into(), | ||||||
|  |                 rhai::Position::NONE | ||||||
|  |             ))); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |      | ||||||
|  |     // Call the buildah config function | ||||||
|  |     buildah_error_to_rhai_error(buildah::config(container, config_options)) | ||||||
|  | } | ||||||
| @@ -6,15 +6,49 @@ | |||||||
| mod error; | mod error; | ||||||
| mod os; | mod os; | ||||||
| mod process; | mod process; | ||||||
|  | mod buildah; | ||||||
|  |  | ||||||
| #[cfg(test)] | #[cfg(test)] | ||||||
| mod tests; | mod tests; | ||||||
|  |  | ||||||
| use rhai::Engine; | // Re-export common Rhai types for convenience | ||||||
|  | pub use rhai::{Array, Dynamic, Map, EvalAltResult, Engine}; | ||||||
|  |  | ||||||
|  | // Re-export error module | ||||||
| pub use error::*; | pub use error::*; | ||||||
| pub use os::*; |  | ||||||
| pub use process::*; | // Re-export specific functions from modules to avoid name conflicts | ||||||
|  | pub use os::{ | ||||||
|  |     register_os_module, | ||||||
|  |     // File system functions | ||||||
|  |     exist, find_file, find_files, find_dir, find_dirs,  | ||||||
|  |     delete, mkdir, file_size, rsync, | ||||||
|  |     // Download functions | ||||||
|  |     download, download_install | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | pub use process::{ | ||||||
|  |     register_process_module, | ||||||
|  |     // Run functions | ||||||
|  |     run_command, run_silent, run_with_options, new_run_options, | ||||||
|  |     // Process management functions | ||||||
|  |     which, kill, process_list, process_get | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | pub use buildah::{ | ||||||
|  |     register_buildah_module, | ||||||
|  |     // Container functions | ||||||
|  |     from, run, run_with_isolation, add, commit, remove, list, | ||||||
|  |     build_with_options, new_build_options, | ||||||
|  |     // Image functions | ||||||
|  |     images, image_remove, image_push, image_tag, image_pull, | ||||||
|  |     image_commit_with_options, new_commit_options, | ||||||
|  |     config_with_options, new_config_options | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | // Rename copy functions to avoid conflicts | ||||||
|  | pub use os::copy as os_copy; | ||||||
|  | pub use buildah::copy as buildah_copy; | ||||||
|  |  | ||||||
| /// Register all SAL modules with the Rhai engine | /// Register all SAL modules with the Rhai engine | ||||||
| /// | /// | ||||||
| @@ -41,6 +75,9 @@ pub fn register(engine: &mut Engine) -> Result<(), Box<rhai::EvalAltResult>> { | |||||||
|     // Register Process module functions |     // Register Process module functions | ||||||
|     process::register_process_module(engine)?; |     process::register_process_module(engine)?; | ||||||
|      |      | ||||||
|  |     // Register Buildah module functions | ||||||
|  |     buildah::register_buildah_module(engine)?; | ||||||
|  |      | ||||||
|     // Future modules can be registered here |     // Future modules can be registered here | ||||||
|      |      | ||||||
|     Ok(()) |     Ok(()) | ||||||
|   | |||||||
| @@ -2,7 +2,7 @@ | |||||||
| //! | //! | ||||||
| //! This module provides Rhai wrappers for the functions in the Process module. | //! This module provides Rhai wrappers for the functions in the Process module. | ||||||
|  |  | ||||||
| use rhai::{Engine, EvalAltResult, Array, Dynamic}; | use rhai::{Engine, EvalAltResult, Array, Dynamic, Map}; | ||||||
| use crate::process::{self, CommandResult, ProcessInfo, RunError, ProcessError}; | use crate::process::{self, CommandResult, ProcessInfo, RunError, ProcessError}; | ||||||
|  |  | ||||||
| /// Register Process module functions with the Rhai engine | /// Register Process module functions with the Rhai engine | ||||||
| @@ -21,6 +21,8 @@ pub fn register_process_module(engine: &mut Engine) -> Result<(), Box<EvalAltRes | |||||||
|     // Register run functions |     // Register run functions | ||||||
|     engine.register_fn("run_command", run_command); |     engine.register_fn("run_command", run_command); | ||||||
|     engine.register_fn("run_silent", run_silent); |     engine.register_fn("run_silent", run_silent); | ||||||
|  |     engine.register_fn("run", run_with_options); | ||||||
|  |     engine.register_fn("new_run_options", new_run_options); | ||||||
|      |      | ||||||
|     // Register process management functions |     // Register process management functions | ||||||
|     engine.register_fn("which", which); |     engine.register_fn("which", which); | ||||||
| @@ -51,9 +53,6 @@ fn register_process_types(engine: &mut Engine) -> Result<(), Box<EvalAltResult>> | |||||||
|     engine.register_get("memory", |p: &mut ProcessInfo| p.memory); |     engine.register_get("memory", |p: &mut ProcessInfo| p.memory); | ||||||
|     engine.register_get("cpu", |p: &mut ProcessInfo| p.cpu); |     engine.register_get("cpu", |p: &mut ProcessInfo| p.cpu); | ||||||
|      |      | ||||||
|     // Register error conversion functions |  | ||||||
|     engine.register_fn("to_string", |err: &str| err.to_string()); |  | ||||||
|      |  | ||||||
|     Ok(()) |     Ok(()) | ||||||
| } | } | ||||||
|  |  | ||||||
| @@ -76,6 +75,16 @@ fn process_error_to_rhai_error<T>(result: Result<T, ProcessError>) -> Result<T, | |||||||
|     }) |     }) | ||||||
| } | } | ||||||
|  |  | ||||||
|  | /// Create a new Map with default run options | ||||||
|  | pub fn new_run_options() -> Map { | ||||||
|  |     let mut map = Map::new(); | ||||||
|  |     map.insert("die".into(), Dynamic::from(true)); | ||||||
|  |     map.insert("silent".into(), Dynamic::from(false)); | ||||||
|  |     map.insert("async_exec".into(), Dynamic::from(false)); | ||||||
|  |     map.insert("log".into(), Dynamic::from(false)); | ||||||
|  |     map | ||||||
|  | } | ||||||
|  |  | ||||||
| // | // | ||||||
| // Run Function Wrappers | // Run Function Wrappers | ||||||
| // | // | ||||||
| @@ -94,6 +103,50 @@ pub fn run_silent(command: &str) -> Result<CommandResult, Box<EvalAltResult>> { | |||||||
|     run_error_to_rhai_error(process::run_silent(command)) |     run_error_to_rhai_error(process::run_silent(command)) | ||||||
| } | } | ||||||
|  |  | ||||||
|  | /// Run a command with options specified in a Map | ||||||
|  | /// | ||||||
|  | /// This provides a builder-style interface for Rhai scripts. | ||||||
|  | /// | ||||||
|  | /// # Example | ||||||
|  | /// | ||||||
|  | /// ```rhai | ||||||
|  | /// let options = new_run_options(); | ||||||
|  | /// options.die = false; | ||||||
|  | /// options.silent = true; | ||||||
|  | /// let result = run("echo Hello", options); | ||||||
|  | /// ``` | ||||||
|  | pub fn run_with_options(command: &str, options: Map) -> Result<CommandResult, Box<EvalAltResult>> { | ||||||
|  |     let mut builder = process::run(command); | ||||||
|  |      | ||||||
|  |     // Apply options from the map | ||||||
|  |     if let Some(die) = options.get("die") { | ||||||
|  |         if let Ok(die_val) = die.clone().as_bool() { | ||||||
|  |             builder = builder.die(die_val); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |      | ||||||
|  |     if let Some(silent) = options.get("silent") { | ||||||
|  |         if let Ok(silent_val) = silent.clone().as_bool() { | ||||||
|  |             builder = builder.silent(silent_val); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |      | ||||||
|  |     if let Some(async_exec) = options.get("async_exec") { | ||||||
|  |         if let Ok(async_val) = async_exec.clone().as_bool() { | ||||||
|  |             builder = builder.async_exec(async_val); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |      | ||||||
|  |     if let Some(log) = options.get("log") { | ||||||
|  |         if let Ok(log_val) = log.clone().as_bool() { | ||||||
|  |             builder = builder.log(log_val); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |      | ||||||
|  |     // Execute the command | ||||||
|  |     run_error_to_rhai_error(builder.execute()) | ||||||
|  | } | ||||||
|  |  | ||||||
| // | // | ||||||
| // Process Management Function Wrappers | // Process Management Function Wrappers | ||||||
| // | // | ||||||
|   | |||||||
| @@ -117,6 +117,36 @@ mod tests { | |||||||
|         assert_eq!(result, ()); |         assert_eq!(result, ()); | ||||||
|     } |     } | ||||||
|      |      | ||||||
|  |     #[test] | ||||||
|  |     fn test_run_with_options() { | ||||||
|  |         let mut engine = Engine::new(); | ||||||
|  |         register(&mut engine).unwrap(); | ||||||
|  |          | ||||||
|  |         // Test running a command with custom options | ||||||
|  |         #[cfg(target_os = "windows")] | ||||||
|  |         let script = r#" | ||||||
|  |             let options = new_run_options(); | ||||||
|  |             options["die"] = true; | ||||||
|  |             options["silent"] = false; | ||||||
|  |             options["log"] = true; | ||||||
|  |             let result = run("echo Hello World", options); | ||||||
|  |             result.success && result.stdout.contains("Hello World") | ||||||
|  |         "#; | ||||||
|  |          | ||||||
|  |         #[cfg(any(target_os = "macos", target_os = "linux"))] | ||||||
|  |         let script = r#" | ||||||
|  |             let options = new_run_options(); | ||||||
|  |             options["die"] = true; | ||||||
|  |             options["silent"] = false; | ||||||
|  |             options["log"] = true; | ||||||
|  |             let result = run("echo 'Hello World'", options); | ||||||
|  |             result.success && result.stdout.contains("Hello World") | ||||||
|  |         "#; | ||||||
|  |          | ||||||
|  |         let result = engine.eval::<bool>(script).unwrap(); | ||||||
|  |         assert!(result); | ||||||
|  |     } | ||||||
|  |  | ||||||
|     #[test] |     #[test] | ||||||
|     fn test_run_command() { |     fn test_run_command() { | ||||||
|         let mut engine = Engine::new(); |         let mut engine = Engine::new(); | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user