...
This commit is contained in:
		
							
								
								
									
										104
									
								
								src/os/fs.rs
									
									
									
									
									
								
							
							
						
						
									
										104
									
								
								src/os/fs.rs
									
									
									
									
									
								
							| @@ -14,6 +14,7 @@ pub enum FsError { | ||||
|     CopyFailed(io::Error), | ||||
|     DeleteFailed(io::Error), | ||||
|     CommandFailed(String), | ||||
|     CommandNotFound(String), | ||||
|     CommandExecutionError(io::Error), | ||||
|     InvalidGlobPattern(glob::PatternError), | ||||
|     NotADirectory(String), | ||||
| @@ -36,6 +37,7 @@ impl fmt::Display for FsError { | ||||
|             FsError::CopyFailed(e) => write!(f, "Failed to copy file: {}", e), | ||||
|             FsError::DeleteFailed(e) => write!(f, "Failed to delete: {}", e), | ||||
|             FsError::CommandFailed(e) => write!(f, "{}", e), | ||||
|             FsError::CommandNotFound(e) => write!(f, "Command not found: {}", e), | ||||
|             FsError::CommandExecutionError(e) => write!(f, "Failed to execute command: {}", e), | ||||
|             FsError::InvalidGlobPattern(e) => write!(f, "Invalid glob pattern: {}", e), | ||||
|             FsError::NotADirectory(path) => write!(f, "Path '{}' exists but is not a directory", path), | ||||
| @@ -740,3 +742,105 @@ pub fn file_write_append(path: &str, content: &str) -> Result<String, FsError> { | ||||
|      | ||||
|     Ok(format!("Successfully appended to file '{}'", path)) | ||||
| } | ||||
|  | ||||
| /** | ||||
|  * Check if a command exists in the system PATH. | ||||
|  * | ||||
|  * # Arguments | ||||
|  * | ||||
|  * * `command` - The command to check | ||||
|  * | ||||
|  * # Returns | ||||
|  * | ||||
|  * * `String` - Empty string if the command doesn't exist, path to the command if it does | ||||
|  * | ||||
|  * # Examples | ||||
|  * | ||||
|  * ``` | ||||
|  * let cmd_path = which("ls"); | ||||
|  * if cmd_path != "" { | ||||
|  *     println!("ls is available at: {}", cmd_path); | ||||
|  * } | ||||
|  * ``` | ||||
|  */ | ||||
| pub fn which(command: &str) -> String { | ||||
|     // Use the appropriate command based on the platform | ||||
|     #[cfg(target_os = "windows")] | ||||
|     let output = Command::new("where") | ||||
|         .arg(command) | ||||
|         .output(); | ||||
|          | ||||
|     #[cfg(not(target_os = "windows"))] | ||||
|     let output = Command::new("which") | ||||
|         .arg(command) | ||||
|         .output(); | ||||
|          | ||||
|     match output { | ||||
|         Ok(out) => { | ||||
|             if out.status.success() { | ||||
|                 let path = String::from_utf8_lossy(&out.stdout).trim().to_string(); | ||||
|                 path | ||||
|             } else { | ||||
|                 String::new() | ||||
|             } | ||||
|         }, | ||||
|         Err(_) => String::new(), | ||||
|     } | ||||
| } | ||||
|  | ||||
| /** | ||||
|  * Ensure that one or more commands exist in the system PATH. | ||||
|  * If any command doesn't exist, an error is thrown. | ||||
|  * | ||||
|  * # Arguments | ||||
|  * | ||||
|  * * `commands` - The command(s) to check, comma-separated for multiple commands | ||||
|  * | ||||
|  * # Returns | ||||
|  * | ||||
|  * * `Ok(String)` - A success message indicating all commands exist | ||||
|  * * `Err(FsError)` - An error if any command doesn't exist | ||||
|  * | ||||
|  * # Examples | ||||
|  * | ||||
|  * ``` | ||||
|  * // Check if a single command exists | ||||
|  * let result = cmd_ensure_exists("nerdctl")?; | ||||
|  * | ||||
|  * // Check if multiple commands exist | ||||
|  * let result = cmd_ensure_exists("nerdctl,docker,containerd")?; | ||||
|  * ``` | ||||
|  */ | ||||
| pub fn cmd_ensure_exists(commands: &str) -> Result<String, FsError> { | ||||
|     // Split the input by commas to handle multiple commands | ||||
|     let command_list: Vec<&str> = commands.split(',') | ||||
|         .map(|s| s.trim()) | ||||
|         .filter(|s| !s.is_empty()) | ||||
|         .collect(); | ||||
|      | ||||
|     if command_list.is_empty() { | ||||
|         return Err(FsError::CommandFailed("No commands specified to check".to_string())); | ||||
|     } | ||||
|      | ||||
|     let mut missing_commands = Vec::new(); | ||||
|      | ||||
|     // Check each command | ||||
|     for cmd in &command_list { | ||||
|         let cmd_path = which(cmd); | ||||
|         if cmd_path.is_empty() { | ||||
|             missing_commands.push(cmd.to_string()); | ||||
|         } | ||||
|     } | ||||
|      | ||||
|     // If any commands are missing, return an error | ||||
|     if !missing_commands.is_empty() { | ||||
|         return Err(FsError::CommandNotFound(missing_commands.join(", "))); | ||||
|     } | ||||
|      | ||||
|     // All commands exist | ||||
|     if command_list.len() == 1 { | ||||
|         Ok(format!("Command '{}' exists", command_list[0])) | ||||
|     } else { | ||||
|         Ok(format!("All commands exist: {}", command_list.join(", "))) | ||||
|     } | ||||
| } | ||||
|   | ||||
| @@ -95,6 +95,79 @@ pub fn container_with_health_check(mut container: Container, cmd: &str) -> Conta | ||||
|     container.with_health_check(cmd) | ||||
| } | ||||
|  | ||||
| /// Add multiple port mappings to a Container | ||||
| pub fn container_with_ports(mut container: Container, ports: Array) -> Container { | ||||
|     for port in ports.iter() { | ||||
|         if port.is_string() { | ||||
|             let port_str = port.clone().cast::<String>(); | ||||
|             container = container.with_port(&port_str); | ||||
|         } | ||||
|     } | ||||
|     container | ||||
| } | ||||
|  | ||||
| /// Add multiple volume mounts to a Container | ||||
| pub fn container_with_volumes(mut container: Container, volumes: Array) -> Container { | ||||
|     for volume in volumes.iter() { | ||||
|         if volume.is_string() { | ||||
|             let volume_str = volume.clone().cast::<String>(); | ||||
|             container = container.with_volume(&volume_str); | ||||
|         } | ||||
|     } | ||||
|     container | ||||
| } | ||||
|  | ||||
| /// Add multiple environment variables to a Container | ||||
| pub fn container_with_envs(mut container: Container, env_map: Map) -> Container { | ||||
|     for (key, value) in env_map.iter() { | ||||
|         if value.is_string() { | ||||
|             let value_str = value.clone().cast::<String>(); | ||||
|             container = container.with_env(&key, &value_str); | ||||
|         } | ||||
|     } | ||||
|     container | ||||
| } | ||||
|  | ||||
| /// Add multiple network aliases to a Container | ||||
| pub fn container_with_network_aliases(mut container: Container, aliases: Array) -> Container { | ||||
|     for alias in aliases.iter() { | ||||
|         if alias.is_string() { | ||||
|             let alias_str = alias.clone().cast::<String>(); | ||||
|             container = container.with_network_alias(&alias_str); | ||||
|         } | ||||
|     } | ||||
|     container | ||||
| } | ||||
|  | ||||
| /// Set memory swap limit for a Container | ||||
| pub fn container_with_memory_swap_limit(mut container: Container, memory_swap: &str) -> Container { | ||||
|     container.with_memory_swap_limit(memory_swap) | ||||
| } | ||||
|  | ||||
| /// Set CPU shares for a Container | ||||
| pub fn container_with_cpu_shares(mut container: Container, shares: &str) -> Container { | ||||
|     container.with_cpu_shares(shares) | ||||
| } | ||||
|  | ||||
| /// Set health check with options for a Container | ||||
| pub fn container_with_health_check_options( | ||||
|     mut container: Container, | ||||
|     cmd: &str, | ||||
|     interval: Option<&str>, | ||||
|     timeout: Option<&str>, | ||||
|     retries: Option<i64>, | ||||
|     start_period: Option<&str> | ||||
| ) -> Container { | ||||
|     // Convert i64 to u32 for retries | ||||
|     let retries_u32 = retries.map(|r| r as u32); | ||||
|     container.with_health_check_options(cmd, interval, timeout, retries_u32, start_period) | ||||
| } | ||||
|  | ||||
| /// Set snapshotter for a Container | ||||
| pub fn container_with_snapshotter(mut container: Container, snapshotter: &str) -> Container { | ||||
|     container.with_snapshotter(snapshotter) | ||||
| } | ||||
|  | ||||
| /// Set detach mode for a Container | ||||
| pub fn container_with_detach(mut container: Container, detach: bool) -> Container { | ||||
|     container.with_detach(detach) | ||||
| @@ -412,6 +485,14 @@ pub fn register_nerdctl_module(engine: &mut Engine) -> Result<(), Box<EvalAltRes | ||||
|     engine.register_fn("with_memory_limit", container_with_memory_limit); | ||||
|     engine.register_fn("with_restart_policy", container_with_restart_policy); | ||||
|     engine.register_fn("with_health_check", container_with_health_check); | ||||
|     engine.register_fn("with_ports", container_with_ports); | ||||
|     engine.register_fn("with_volumes", container_with_volumes); | ||||
|     engine.register_fn("with_envs", container_with_envs); | ||||
|     engine.register_fn("with_network_aliases", container_with_network_aliases); | ||||
|     engine.register_fn("with_memory_swap_limit", container_with_memory_swap_limit); | ||||
|     engine.register_fn("with_cpu_shares", container_with_cpu_shares); | ||||
|     engine.register_fn("with_health_check_options", container_with_health_check_options); | ||||
|     engine.register_fn("with_snapshotter", container_with_snapshotter); | ||||
|     engine.register_fn("with_detach", container_with_detach); | ||||
|     engine.register_fn("build", container_build); | ||||
|     engine.register_fn("start", container_start); | ||||
|   | ||||
| @@ -35,6 +35,10 @@ pub fn register_os_module(engine: &mut Engine) -> Result<(), Box<EvalAltResult>> | ||||
|     engine.register_fn("file_write", file_write); | ||||
|     engine.register_fn("file_write_append", file_write_append); | ||||
|      | ||||
|     // Register command check functions | ||||
|     engine.register_fn("which", which); | ||||
|     engine.register_fn("cmd_ensure_exists", cmd_ensure_exists); | ||||
|      | ||||
|     // Register download functions | ||||
|     engine.register_fn("download", download); | ||||
|     engine.register_fn("download_install", download_install); | ||||
| @@ -176,4 +180,19 @@ pub fn download(url: &str, dest: &str, min_size_kb: i64) -> Result<String, Box<E | ||||
| /// Download a file and install it if it's a supported package format. | ||||
| pub fn download_install(url: &str, min_size_kb: i64) -> Result<String, Box<EvalAltResult>> { | ||||
|     os::download_install(url, min_size_kb).to_rhai_error() | ||||
| } | ||||
|  | ||||
| /// Wrapper for os::which | ||||
| /// | ||||
| /// Check if a command exists in the system PATH. | ||||
| pub fn which(command: &str) -> String { | ||||
|     os::which(command) | ||||
| } | ||||
|  | ||||
| /// Wrapper for os::cmd_ensure_exists | ||||
| /// | ||||
| /// Ensure that one or more commands exist in the system PATH. | ||||
| /// If any command doesn't exist, an error is thrown. | ||||
| pub fn cmd_ensure_exists(commands: &str) -> Result<String, Box<EvalAltResult>> { | ||||
|     os::cmd_ensure_exists(commands).to_rhai_error() | ||||
| } | ||||
| @@ -1,100 +0,0 @@ | ||||
| // 07_nerdctl_operations.rhai | ||||
| // Demonstrates container operations using SAL's nerdctl integration | ||||
| // Note: This script requires nerdctl to be installed and may need root privileges | ||||
|  | ||||
| // Check if nerdctl is installed | ||||
| let nerdctl_exists = which("nerdctl"); | ||||
| println(`Nerdctl exists: ${nerdctl_exists}`); | ||||
|  | ||||
| // List available images (only if nerdctl is installed) | ||||
| println("Listing available container images:"); | ||||
| if nerdctl_exists == "" { | ||||
|     println("Nerdctl is not installed. Please install it first."); | ||||
|     // You can use the install_nerdctl.rhai script to install nerdctl | ||||
|     // EXIT | ||||
| } | ||||
|  | ||||
| // List images | ||||
| let images_result = nerdctl_images(); | ||||
| println(`Images result: success=${images_result.success}, code=${images_result.code}`); | ||||
| println(`Images output:\n${images_result.stdout}`); | ||||
|  | ||||
| // Pull an image if needed | ||||
| println("\nPulling alpine:latest image:"); | ||||
| let pull_result = nerdctl_image_pull("alpine:latest"); | ||||
| println(`Pull result: success=${pull_result.success}, code=${pull_result.code}`); | ||||
| println(`Pull output: ${pull_result.stdout}`); | ||||
|  | ||||
| // Create a container using the simple run function | ||||
| println("\nCreating a container from alpine image:"); | ||||
| let container_name = "rhai-nerdctl-test"; | ||||
| let container = nerdctl_run("alpine:latest", container_name); | ||||
| println(`Container result: success=${container.success}, code=${container.code}`); | ||||
| println(`Container stdout: "${container.stdout}"`); | ||||
| println(`Container stderr: "${container.stderr}"`); | ||||
|  | ||||
| // Run a command in the container | ||||
| println("\nRunning a command in the container:"); | ||||
| let run_result = nerdctl_exec(container_name, "echo 'Hello from container'"); | ||||
| println(`Command output: ${run_result.stdout}`); | ||||
|  | ||||
| // Create a test file and copy it to the container | ||||
| println("\nAdding a file to the container:"); | ||||
| let test_file = "test_file.txt"; | ||||
| run(`echo "Test content" > ${test_file}`); | ||||
| let copy_result = nerdctl_copy(test_file, `${container_name}:/`); | ||||
| println(`Copy result: ${copy_result.success}`); | ||||
|  | ||||
| // Commit the container to create a new image | ||||
| println("\nCommitting the container to create a new image:"); | ||||
| let image_name = "my-custom-alpine:latest"; | ||||
| let commit_result = nerdctl_image_commit(container_name, image_name); | ||||
| println(`Commit result: ${commit_result.success}`); | ||||
|  | ||||
| // Stop and remove the container | ||||
| println("\nStopping the container:"); | ||||
| let stop_result = nerdctl_stop(container_name); | ||||
| println(`Stop result: ${stop_result.success}`); | ||||
|  | ||||
| println("\nRemoving the container:"); | ||||
| let remove_result = nerdctl_remove(container_name); | ||||
| println(`Remove result: ${remove_result.success}`); | ||||
|  | ||||
| // Clean up the test file | ||||
| delete(test_file); | ||||
|  | ||||
| // Demonstrate run options | ||||
| println("\nDemonstrating run options:"); | ||||
| let run_options = nerdctl_new_run_options(); | ||||
| run_options.name = "rhai-nerdctl-options-test"; | ||||
| run_options.detach = true; | ||||
| run_options.ports = ["8080:80"]; | ||||
| run_options.snapshotter = "native"; | ||||
|  | ||||
| println("Run options configured:"); | ||||
| println(`  - Name: ${run_options.name}`); | ||||
| println(`  - Detach: ${run_options.detach}`); | ||||
| println(`  - Ports: ${run_options.ports}`); | ||||
| println(`  - Snapshotter: ${run_options.snapshotter}`); | ||||
|  | ||||
| // Create a container with options | ||||
| println("\nCreating a container with options:"); | ||||
| let container_with_options = nerdctl_run_with_options("alpine:latest", run_options); | ||||
| println(`Container with options result: ${container_with_options.success}`); | ||||
|  | ||||
| // Clean up the container with options | ||||
| println("\nCleaning up the container with options:"); | ||||
| nerdctl_stop("rhai-nerdctl-options-test"); | ||||
| nerdctl_remove("rhai-nerdctl-options-test"); | ||||
|  | ||||
| // List all containers (including stopped ones) | ||||
| println("\nListing all containers:"); | ||||
| let list_result = nerdctl_list(true); | ||||
| println(`List result: ${list_result.stdout}`); | ||||
|  | ||||
| // Remove the custom image | ||||
| println("\nRemoving the custom image:"); | ||||
| let image_remove_result = nerdctl_image_remove(image_name); | ||||
| println(`Image remove result: ${image_remove_result.success}`); | ||||
|  | ||||
| "Nerdctl operations script completed successfully!" | ||||
| @@ -1,150 +0,0 @@ | ||||
| // 08_nerdctl_web_server.rhai | ||||
| // Demonstrates a complete workflow to set up a web server using nerdctl | ||||
| // Note: This script requires nerdctl to be installed and may need root privileges | ||||
|  | ||||
| // Check if nerdctl is installed | ||||
| let nerdctl_exists = which("nerdctl"); | ||||
| if nerdctl_exists == "" { | ||||
|     println("Nerdctl is not installed. Please install it first."); | ||||
|     // You can use the install_nerdctl.rhai script to install nerdctl | ||||
|     // EXIT | ||||
| } | ||||
|  | ||||
| println("Starting nerdctl web server workflow..."); | ||||
|  | ||||
| // Step 1: Pull the nginx image | ||||
| println("\n=== Pulling nginx:latest image ==="); | ||||
| let pull_result = nerdctl_image_pull("nginx:latest"); | ||||
| if !pull_result.success { | ||||
|     println(`Failed to pull nginx image: ${pull_result.stderr}`); | ||||
|     // EXIT | ||||
| } | ||||
| println("Successfully pulled nginx:latest image"); | ||||
|  | ||||
| // Step 2: Create a custom nginx configuration file | ||||
| println("\n=== Creating custom nginx configuration ==="); | ||||
| let config_content = ` | ||||
| server { | ||||
|     listen 80; | ||||
|     server_name localhost; | ||||
|  | ||||
|     location / { | ||||
|         root /usr/share/nginx/html; | ||||
|         index index.html; | ||||
|     } | ||||
| } | ||||
| `; | ||||
|  | ||||
| let config_file = "custom-nginx.conf"; | ||||
| run(`echo "${config_content}" > ${config_file}`); | ||||
| println("Created custom nginx configuration file"); | ||||
|  | ||||
| // Step 3: Create a custom index.html file | ||||
| println("\n=== Creating custom index.html ==="); | ||||
| let html_content = ` | ||||
| <!DOCTYPE html> | ||||
| <html> | ||||
| <head> | ||||
|     <title>Rhai Nerdctl Demo</title> | ||||
|     <style> | ||||
|         body { | ||||
|             font-family: Arial, sans-serif; | ||||
|             margin: 40px; | ||||
|             line-height: 1.6; | ||||
|             color: #333; | ||||
|         } | ||||
|         h1 { | ||||
|             color: #0066cc; | ||||
|         } | ||||
|     </style> | ||||
| </head> | ||||
| <body> | ||||
|     <h1>Hello from Rhai Nerdctl!</h1> | ||||
|     <p>This page is served by an Nginx container created using the Rhai nerdctl wrapper.</p> | ||||
|     <p>Current time: ${now()}</p> | ||||
| </body> | ||||
| </html> | ||||
| `; | ||||
|  | ||||
| let html_file = "index.html"; | ||||
| run(`echo "${html_content}" > ${html_file}`); | ||||
| println("Created custom index.html file"); | ||||
|  | ||||
| // Step 4: Create and run the nginx container with options | ||||
| println("\n=== Creating nginx container ==="); | ||||
| let container_name = "rhai-nginx-demo"; | ||||
|  | ||||
| // First, try to remove any existing container with the same name | ||||
| let _ = nerdctl_remove(container_name); | ||||
|  | ||||
| let run_options = nerdctl_new_run_options(); | ||||
| run_options.name = container_name; | ||||
| run_options.detach = true; | ||||
| run_options.ports = ["8080:80"]; | ||||
| run_options.snapshotter = "native"; | ||||
|  | ||||
| let container = nerdctl_run_with_options("nginx:latest", run_options); | ||||
| if !container.success { | ||||
|     println(`Failed to create container: ${container.stderr}`); | ||||
|     // EXIT | ||||
| } | ||||
| println(`Successfully created container: ${container_name}`); | ||||
|  | ||||
| // Step 5: Copy the custom files to the container | ||||
| println("\n=== Copying custom files to container ==="); | ||||
| let copy_config = nerdctl_copy(config_file, `${container_name}:/etc/nginx/conf.d/default.conf`); | ||||
| if !copy_config.success { | ||||
|     println(`Failed to copy config file: ${copy_config.stderr}`); | ||||
| } | ||||
|  | ||||
| let copy_html = nerdctl_copy(html_file, `${container_name}:/usr/share/nginx/html/index.html`); | ||||
| if !copy_html.success { | ||||
|     println(`Failed to copy HTML file: ${copy_html.stderr}`); | ||||
| } | ||||
| println("Successfully copied custom files to container"); | ||||
|  | ||||
| // Step 6: Restart the container to apply changes | ||||
| println("\n=== Restarting container to apply changes ==="); | ||||
| let stop_result = nerdctl_stop(container_name); | ||||
| if !stop_result.success { | ||||
|     println(`Failed to stop container: ${stop_result.stderr}`); | ||||
| } | ||||
|  | ||||
| let start_result = nerdctl_exec(container_name, "nginx -s reload"); | ||||
| if !start_result.success { | ||||
|     println(`Failed to reload nginx: ${start_result.stderr}`); | ||||
| } | ||||
| println("Successfully restarted container"); | ||||
|  | ||||
| // Step 7: Commit the container to create a custom image | ||||
| println("\n=== Committing container to create custom image ==="); | ||||
| let image_name = "rhai-nginx-custom:latest"; | ||||
| let commit_result = nerdctl_image_commit(container_name, image_name); | ||||
| if !commit_result.success { | ||||
|     println(`Failed to commit container: ${commit_result.stderr}`); | ||||
| } | ||||
| println(`Successfully created custom image: ${image_name}`); | ||||
|  | ||||
| // Step 8: Display information about the running container | ||||
| println("\n=== Container Information ==="); | ||||
| println("The nginx web server is now running."); | ||||
| println("You can access it at: http://localhost:8080"); | ||||
| println("Container name: " + container_name); | ||||
| println("Custom image: " + image_name); | ||||
|  | ||||
| // Step 9: Clean up (commented out for demonstration purposes) | ||||
| // println("\n=== Cleaning up ==="); | ||||
| // nerdctl_stop(container_name); | ||||
| // nerdctl_remove(container_name); | ||||
| // nerdctl_image_remove(image_name); | ||||
| // delete(config_file); | ||||
| // delete(html_file); | ||||
|  | ||||
| println("\nNerdctl web server workflow completed successfully!"); | ||||
| println("The web server is running at http://localhost:8080"); | ||||
| println("To clean up, run the following commands:"); | ||||
| println(`  nerdctl stop ${container_name}`); | ||||
| println(`  nerdctl rm ${container_name}`); | ||||
| println(`  nerdctl rmi ${image_name}`); | ||||
|  | ||||
| "Nerdctl web server script completed successfully!" | ||||
| @@ -2,7 +2,7 @@ | ||||
| 
 | ||||
| 
 | ||||
| fn nerdctl_download(){ | ||||
|     let name="nerctl"; | ||||
|     let name="nerdctl"; | ||||
|     let url="https://github.com/containerd/nerdctl/releases/download/v2.0.4/nerdctl-2.0.4-linux-amd64.tar.gz"; | ||||
|     download(url,`/tmp/${name}`,20); | ||||
|     copy(`/tmp/${name}/*`,"/root/hero/bin/"); | ||||
| @@ -1,189 +0,0 @@ | ||||
| // nerdctl_test.rhai | ||||
| // Tests the nerdctl wrapper functionality without requiring a running containerd daemon | ||||
|  | ||||
| // Check if nerdctl is installed | ||||
| let nerdctl_exists = which("nerdctl"); | ||||
| println(`Nerdctl exists: ${nerdctl_exists}`); | ||||
|  | ||||
| // Test creating run options | ||||
| println("\nTesting run options creation:"); | ||||
| let run_options = new_run_options(); | ||||
| println(`Default run options created: ${run_options}`); | ||||
| println(`- name: ${run_options.name}`); | ||||
| println(`- detach: ${run_options.detach}`); | ||||
| println(`- ports: ${run_options.ports}`); | ||||
| println(`- snapshotter: ${run_options.snapshotter}`); | ||||
|  | ||||
| // Modify run options | ||||
| println("\nModifying run options:"); | ||||
| run_options.name = "test-container"; | ||||
| run_options.detach = false; | ||||
| run_options.ports = ["8080:80", "8443:443"]; | ||||
| run_options.snapshotter = "overlayfs"; | ||||
| println(`Modified run options: ${run_options}`); | ||||
| println(`- name: ${run_options.name}`); | ||||
| println(`- detach: ${run_options.detach}`); | ||||
| println(`- ports: ${run_options.ports}`); | ||||
| println(`- snapshotter: ${run_options.snapshotter}`); | ||||
|  | ||||
| // Test function availability | ||||
| println("\nTesting function availability:"); | ||||
| let functions = [ | ||||
|     // Legacy functions | ||||
|     "nerdctl_run", | ||||
|     "nerdctl_run_with_name", | ||||
|     "nerdctl_run_with_port", | ||||
|     "nerdctl_exec", | ||||
|     "nerdctl_copy", | ||||
|     "nerdctl_stop", | ||||
|     "nerdctl_remove", | ||||
|     "nerdctl_list", | ||||
|     "nerdctl_images", | ||||
|     "nerdctl_image_remove", | ||||
|     "nerdctl_image_push", | ||||
|     "nerdctl_image_tag", | ||||
|     "nerdctl_image_pull", | ||||
|     "nerdctl_image_commit", | ||||
|     "nerdctl_image_build", | ||||
|      | ||||
|     // Builder pattern functions | ||||
|     "nerdctl_container_new", | ||||
|     "nerdctl_container_from_image", | ||||
|     "with_port", | ||||
|     "with_volume", | ||||
|     "with_env", | ||||
|     "with_network", | ||||
|     "with_network_alias", | ||||
|     "with_cpu_limit", | ||||
|     "with_memory_limit", | ||||
|     "with_restart_policy", | ||||
|     "with_health_check", | ||||
|     "with_detach", | ||||
|     "build", | ||||
|     "start", | ||||
|     "stop", | ||||
|     "remove", | ||||
|     "exec", | ||||
|     "copy" | ||||
| ]; | ||||
|  | ||||
| // Try to access each function (this will throw an error if the function doesn't exist) | ||||
| for func in functions { | ||||
|     let exists = is_function_registered(func); | ||||
|     println(`Function ${func} registered: ${exists}`); | ||||
| } | ||||
|  | ||||
| // Helper function to get current timestamp in seconds | ||||
| fn timestamp() { | ||||
|     // Use the current time in seconds since epoch as a unique identifier | ||||
|     return now(); | ||||
| } | ||||
|  | ||||
| // Test the builder pattern with actual container creation and execution | ||||
| println("\nTesting container builder pattern with actual container:"); | ||||
| try { | ||||
|     // Generate a unique container name based on timestamp | ||||
|     let container_name = "test-alpine-container"; | ||||
|      | ||||
|     // First, try to remove any existing container with this name | ||||
|     println(`Cleaning up any existing container named '${container_name}'...`); | ||||
|     try { | ||||
|         // Try to stop the container first (in case it's running) | ||||
|         nerdctl_stop(container_name); | ||||
|         println("Stopped existing container"); | ||||
|     } catch(e) { | ||||
|         println("No running container to stop"); | ||||
|     } | ||||
|      | ||||
|     try { | ||||
|         // Try to remove the container | ||||
|         nerdctl_remove(container_name); | ||||
|         println("Removed existing container"); | ||||
|     } catch(e) { | ||||
|         println("No container to remove"); | ||||
|     } | ||||
|      | ||||
|     // Create a container with builder pattern using Alpine image with a command that keeps it running | ||||
|     println("\nCreating new container from Alpine image..."); | ||||
|     let container = nerdctl_container_from_image(container_name, "alpine:latest"); | ||||
|     println(`Created container from image: ${container.name} (${container.image})`); | ||||
|      | ||||
|     // Configure the container | ||||
|     container = container | ||||
|         .with_port("8080:80") | ||||
|         .with_volume("/tmp:/data") | ||||
|         .with_env("TEST_ENV", "test_value") | ||||
|         .with_detach(true); | ||||
|      | ||||
|     // Print container properties before building | ||||
|     println("Container properties before building:"); | ||||
|     println(`- name: ${container.name}`); | ||||
|     println(`- image: ${container.image}`); | ||||
|     println(`- ports: ${container.ports}`); | ||||
|     println(`- volumes: ${container.volumes}`); | ||||
|     println(`- detach: ${container.detach}`); | ||||
|      | ||||
|     // Build the container | ||||
|     println("\nBuilding the container..."); | ||||
|     container = container.build(); | ||||
|     println(`Container built successfully with ID: ${container.container_id}`); | ||||
|      | ||||
|     // Start the container | ||||
|     println("\nStarting the container..."); | ||||
|     let start_result = container.start(); | ||||
|     println(`Start result: ${start_result.success}`); | ||||
|      | ||||
|     // Execute a command in the running container | ||||
|     println("\nExecuting command in the container..."); | ||||
|     let exec_result = container.exec("echo 'Hello from Alpine container!'"); | ||||
|     println(`Command output: ${exec_result.stdout}`); | ||||
|     println("\nExecuting command in the container..."); | ||||
|     let run_cmd = container.exec("echo 'Hello from Alpine container'"); | ||||
|     println(`Command output: ${run_cmd.stdout}`); | ||||
|      | ||||
|     // List all containers to verify it's running | ||||
|     println("\nListing all containers:"); | ||||
|     let list_result = nerdctl_list(true); | ||||
|     println(`Container list: ${list_result.stdout}`); | ||||
|      | ||||
|     // Stop and remove the container | ||||
|     println("\nStopping and removing the container..."); | ||||
|     let stop_result = container.stop(); | ||||
|     println(`Stop result: ${stop_result.success}`); | ||||
|      | ||||
|     let remove_result = container.remove(); | ||||
|     println(`Remove result: ${remove_result.success}`); | ||||
|     println("Container stopped and removed successfully"); | ||||
|      | ||||
|     // Return success message only if everything worked | ||||
|     return "Container builder pattern test completed successfully!"; | ||||
| } catch(e) { | ||||
|     // Print the error and return a failure message | ||||
|     println(`ERROR: ${e}`); | ||||
|      | ||||
|     // Try to clean up if possible | ||||
|     try { | ||||
|         println("Attempting to clean up after error..."); | ||||
|         nerdctl_stop("test-alpine-container"); | ||||
|         nerdctl_remove("test-alpine-container"); | ||||
|         println("Cleanup completed"); | ||||
|     } catch(cleanup_error) { | ||||
|         println(`Cleanup failed: ${cleanup_error}`); | ||||
|     } | ||||
|      | ||||
|     // Return failure message | ||||
|     return "Container builder pattern test FAILED!"; | ||||
| } | ||||
|  | ||||
| // Helper function to check if a function is registered | ||||
| fn is_function_registered(name) { | ||||
|     try { | ||||
|         // This will throw an error if the function doesn't exist | ||||
|         eval(`${name}`); | ||||
|         return true; | ||||
|     } catch { | ||||
|         return false; | ||||
|     } | ||||
| } | ||||
|  | ||||
| // The final result depends on the outcome of the container test | ||||
							
								
								
									
										130
									
								
								src/rhaiexamples/nerdctl_webserver copy.rhai
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										130
									
								
								src/rhaiexamples/nerdctl_webserver copy.rhai
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,130 @@ | ||||
| // 08_nerdctl_web_server.rhai | ||||
| // Demonstrates a complete workflow to set up a web server using nerdctl | ||||
| // Note: This script requires nerdctl to be installed and may need root privileges | ||||
|  | ||||
| // Ensure nerdctl is installed | ||||
| println("Checking if nerdctl is installed..."); | ||||
| // Fix the typo in nerdctl and check all required commands | ||||
| let result = cmd_ensure_exists("nerdctl,runc,buildah"); | ||||
| println("All required commands are installed and available."); | ||||
|  | ||||
| println("Starting nerdctl web server workflow..."); | ||||
|  | ||||
| // Create and use a temporary directory for all files | ||||
| let work_dir = "/tmp/nerdctl"; | ||||
| mkdir(work_dir); | ||||
| chdir(work_dir); | ||||
| println(`Working in directory: ${work_dir}`); | ||||
|  | ||||
|  | ||||
| println("\n=== Creating custom nginx configuration ==="); | ||||
| let config_content = ` | ||||
| server { | ||||
|     listen 80; | ||||
|     server_name localhost; | ||||
|  | ||||
|     location / { | ||||
|         root /usr/share/nginx/html; | ||||
|         index index.html; | ||||
|     } | ||||
| } | ||||
| `; | ||||
|  | ||||
| let config_file = `${work_dir}/custom-nginx.conf`; | ||||
| // Use file_write instead of run command | ||||
| file_write(config_file, config_content); | ||||
| println(`Created custom nginx configuration file at ${config_file}`); | ||||
|  | ||||
| // Step 3: Create a custom index.html file | ||||
| println("\n=== Creating custom index.html ==="); | ||||
| let html_content = ` | ||||
| <!DOCTYPE html> | ||||
| <html> | ||||
| <head> | ||||
|     <title>Rhai Nerdctl Demo</title> | ||||
|     <style> | ||||
|         body { | ||||
|             font-family: Arial, sans-serif; | ||||
|             margin: 40px; | ||||
|             line-height: 1.6; | ||||
|             color: #333; | ||||
|         } | ||||
|         h1 { | ||||
|             color: #0066cc; | ||||
|         } | ||||
|     </style> | ||||
| </head> | ||||
| <body> | ||||
|     <h1>Hello from Rhai Nerdctl!</h1> | ||||
|     <p>This page is served by an Nginx container created using the Rhai nerdctl wrapper.</p> | ||||
|     <p>Current time: ${now()}</p> | ||||
| </body> | ||||
| </html> | ||||
| `; | ||||
|  | ||||
| let html_file = `${work_dir}/index.html`; | ||||
| // Use file_write instead of run command | ||||
| file_write(html_file, html_content); | ||||
| println(`Created custom index.html file at ${html_file}`); | ||||
|  | ||||
| println("\n=== Creating nginx container ==="); | ||||
| let container_name = "rhai-nginx-demo"; | ||||
|  | ||||
| // First, try to remove any existing container with the same name | ||||
| nerdctl_remove(container_name); | ||||
|  | ||||
| let env_map = #{}; // Create an empty map | ||||
| env_map["NGINX_HOST"] = "localhost"; | ||||
| env_map["NGINX_PORT"] = "80"; | ||||
| env_map["NGINX_WORKER_PROCESSES"] = "auto"; | ||||
| let network_aliases = ["web-server", "nginx-demo", "http-service"]; | ||||
|  | ||||
| // Create a container with a rich set of options using batch methods | ||||
| let container = nerdctl_container_from_image(container_name, "nginx:latest") | ||||
|     .with_detach(true) | ||||
|     .with_ports(["8080:80"])                // Add multiple ports at once | ||||
|     .with_volumes([`${work_dir}:/usr/share/nginx/html`, "/var/log:/var/log/nginx"])  // Mount our work dir | ||||
|     .with_envs(env_map)               // Add multiple environment variables at once | ||||
|     .with_network("bridge") | ||||
|     .with_network_aliases(network_aliases)  // Add multiple network aliases at once | ||||
|     .with_cpu_limit("1.0") | ||||
|     .with_memory_limit("512m") | ||||
|     .with_memory_swap_limit("1g")     // New method | ||||
|     .with_cpu_shares("1024")          // New method | ||||
|     // .with_restart_policy("unless-stopped") | ||||
|     // .with_snapshotter("native") | ||||
|     // Add health check with a multiline script | ||||
| //     .with_health_check_options( | ||||
| //         `#!/bin/bash | ||||
| // # Health check script for nginx container | ||||
| // # This script will check if the nginx server is responding correctly | ||||
|  | ||||
| // # Try to connect to the server | ||||
| // response=$(curl -s -o /dev/null -w "%{http_code}" http://localhost/ || echo "failed") | ||||
|  | ||||
| // # Check the response | ||||
| // if [ "$response" = "200" ]; then | ||||
| //     echo "Nginx is healthy" | ||||
| //     exit 0 | ||||
| // else | ||||
| //     echo "Nginx is not healthy, got response: $response" | ||||
| //     exit 1 | ||||
| // fi`, | ||||
| //         "5s",                         // Interval | ||||
| //         "3s",                         // Timeout | ||||
| //         3,                            // Retries | ||||
| //         "10s"                         // Start period | ||||
| //     ); | ||||
|      | ||||
| // Build and start the container | ||||
| println("Building and starting the container..."); | ||||
| let built_container = container.build(); | ||||
| let start_result = built_container.start(); | ||||
| println(`Container started: ${start_result.success}`); | ||||
|  | ||||
| println(`Successfully created and started container: ${container_name}`); | ||||
|  | ||||
| println("\nNerdctl web server workflow completed successfully!"); | ||||
| println("The web server is running at http://localhost:8080"); | ||||
|  | ||||
| "Nerdctl web server script completed successfully!" | ||||
							
								
								
									
										85
									
								
								src/rhaiexamples/nerdctl_webserver.rhai
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										85
									
								
								src/rhaiexamples/nerdctl_webserver.rhai
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,85 @@ | ||||
| // 08__web_server.rhai | ||||
| // Demonstrates a complete workflow to set up a web server using  | ||||
| // Note: This script requires  to be installed and may need root privileges | ||||
|  | ||||
| println("Starting  web server workflow..."); | ||||
|  | ||||
| // Create and use a temporary directory for all files | ||||
| let work_dir = "/tmp/"; | ||||
| mkdir(work_dir); | ||||
| chdir(work_dir); | ||||
| println(`Working in directory: ${work_dir}`); | ||||
|  | ||||
|  | ||||
| println("\n=== Creating custom nginx configuration ==="); | ||||
| let config_content = ` | ||||
| server { | ||||
|     listen 80; | ||||
|     server_name localhost; | ||||
|  | ||||
|     location / { | ||||
|         root /usr/share/nginx/html; | ||||
|         index index.html; | ||||
|     } | ||||
| } | ||||
| `; | ||||
|  | ||||
| let config_file = `${work_dir}/custom-nginx.conf`; | ||||
| // Use file_write instead of run command | ||||
| file_write(config_file, config_content); | ||||
| println(`Created custom nginx configuration file at ${config_file}`); | ||||
|  | ||||
| // Step 3: Create a custom index.html file | ||||
| println("\n=== Creating custom index.html ==="); | ||||
| let html_content = ` | ||||
| <!DOCTYPE html> | ||||
| <html> | ||||
| <head> | ||||
|     <title>Demo</title> | ||||
|     <style> | ||||
|         body { | ||||
|             font-family: Arial, sans-serif; | ||||
|             margin: 40px; | ||||
|             line-height: 1.6; | ||||
|             color: #333; | ||||
|         } | ||||
|         h1 { | ||||
|             color: #0066cc; | ||||
|         } | ||||
|     </style> | ||||
| </head> | ||||
| <body> | ||||
|     <h1>Hello from HeroScript !</h1> | ||||
|     <p>This page is served by an Nginx container.</p> | ||||
| </body> | ||||
| </html> | ||||
| `; | ||||
|  | ||||
| let html_file = `${work_dir}/index.html`; | ||||
| // Use file_write instead of run command | ||||
| file_write(html_file, html_content); | ||||
| println(`Created custom index.html file at ${html_file}`); | ||||
|  | ||||
| println("\n=== Creating nginx container ==="); | ||||
| let container_name = "nginx-demo"; | ||||
|  | ||||
| let env_map = #{}; // Create an empty map | ||||
| env_map["NGINX_HOST"] = "localhost"; | ||||
| env_map["NGINX_PORT"] = "80"; | ||||
| env_map["NGINX_WORKER_PROCESSES"] = "auto"; | ||||
|  | ||||
| // Create a container with a rich set of options using batch methods | ||||
| let container = _container_from_image(container_name, "nginx:latest") | ||||
|     .reset() | ||||
|     .with_detach(true) | ||||
|     .with_ports(["8080:80"])                // Add multiple ports at once | ||||
|     .with_volumes([`${work_dir}:/usr/share/nginx/html`, "/var/log:/var/log/nginx"])  // Mount our work dir | ||||
|     .with_envs(env_map)               // Add multiple environment variables at once | ||||
|     .with_cpu_limit("1.0") | ||||
|     .with_memory_limit("512m") | ||||
|  | ||||
|  | ||||
| println("\n web server workflow completed successfully!"); | ||||
| println("The web server is running at http://localhost:8080"); | ||||
|  | ||||
| "Web server script completed successfully!" | ||||
| @@ -3,6 +3,7 @@ | ||||
| use std::collections::HashMap; | ||||
| use crate::process::CommandResult; | ||||
| use crate::virt::nerdctl::{execute_nerdctl_command, NerdctlError}; | ||||
| use crate::os; | ||||
| use super::container_types::{Container, HealthCheck, ContainerStatus, ResourceUsage}; | ||||
|  | ||||
| impl Container { | ||||
| @@ -16,6 +17,15 @@ impl Container { | ||||
|     /// | ||||
|     /// * `Result<Self, NerdctlError>` - Container instance or error | ||||
|     pub fn new(name: &str) -> Result<Self, NerdctlError> { | ||||
|         // Check if required commands exist | ||||
|         match os::cmd_ensure_exists("nerdctl,runc,buildah") { | ||||
|             Err(e) => return Err(NerdctlError::CommandExecutionFailed( | ||||
|                 std::io::Error::new(std::io::ErrorKind::NotFound, | ||||
|                 format!("Required commands not found: {}", e)) | ||||
|             )), | ||||
|             _ => {} | ||||
|         } | ||||
|          | ||||
|         // Check if container exists | ||||
|         let result = execute_nerdctl_command(&["ps", "-a", "--format", "{{.Names}} {{.ID}}"])?; | ||||
|          | ||||
|   | ||||
| @@ -3,6 +3,7 @@ | ||||
| use std::collections::HashMap; | ||||
| use crate::virt::nerdctl::{execute_nerdctl_command, NerdctlError}; | ||||
| use super::container_types::{Container, HealthCheck}; | ||||
| use super::health_check_script::prepare_health_check_command; | ||||
|  | ||||
| impl Container { | ||||
|     /// Add a port mapping | ||||
| @@ -220,8 +221,11 @@ impl Container { | ||||
|     /// | ||||
|     /// * `Self` - The container instance for method chaining | ||||
|     pub fn with_health_check(mut self, cmd: &str) -> Self { | ||||
|         // Use the health check script module to prepare the command | ||||
|         let prepared_cmd = prepare_health_check_command(cmd, &self.name); | ||||
|          | ||||
|         self.health_check = Some(HealthCheck { | ||||
|             cmd: cmd.to_string(), | ||||
|             cmd: prepared_cmd, | ||||
|             interval: None, | ||||
|             timeout: None, | ||||
|             retries: None, | ||||
| @@ -251,8 +255,11 @@ impl Container { | ||||
|         retries: Option<u32>, | ||||
|         start_period: Option<&str>, | ||||
|     ) -> Self { | ||||
|         // Use the health check script module to prepare the command | ||||
|         let prepared_cmd = prepare_health_check_command(cmd, &self.name); | ||||
|          | ||||
|         let mut health_check = HealthCheck { | ||||
|             cmd: cmd.to_string(), | ||||
|             cmd: prepared_cmd, | ||||
|             interval: None, | ||||
|             timeout: None, | ||||
|             retries: None, | ||||
|   | ||||
| @@ -288,18 +288,18 @@ impl Container { | ||||
|         } else { | ||||
|             Err(NerdctlError::Other("No container ID available".to_string())) | ||||
|         } | ||||
|          | ||||
|         /// Get container logs | ||||
|         /// | ||||
|         /// # Returns | ||||
|         /// | ||||
|         /// * `Result<CommandResult, NerdctlError>` - Command result or error | ||||
|         pub fn logs(&self) -> Result<CommandResult, NerdctlError> { | ||||
|             if let Some(container_id) = &self.container_id { | ||||
|                 execute_nerdctl_command(&["logs", container_id]) | ||||
|             } else { | ||||
|                 Err(NerdctlError::Other("No container ID available".to_string())) | ||||
|             } | ||||
|     } | ||||
|      | ||||
|     /// Get container logs | ||||
|     /// | ||||
|     /// # Returns | ||||
|     /// | ||||
|     /// * `Result<CommandResult, NerdctlError>` - Command result or error | ||||
|     pub fn logs(&self) -> Result<CommandResult, NerdctlError> { | ||||
|         if let Some(container_id) = &self.container_id { | ||||
|             execute_nerdctl_command(&["logs", container_id]) | ||||
|         } else { | ||||
|             Err(NerdctlError::Other("No container ID available".to_string())) | ||||
|         } | ||||
|     } | ||||
|      | ||||
|   | ||||
							
								
								
									
										79
									
								
								src/virt/nerdctl/health_check_script.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										79
									
								
								src/virt/nerdctl/health_check_script.rs
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,79 @@ | ||||
| // File: /root/code/git.ourworld.tf/herocode/sal/src/virt/nerdctl/health_check_script.rs | ||||
|  | ||||
| use std::fs; | ||||
| use std::path::Path; | ||||
| use std::os::unix::fs::PermissionsExt; | ||||
|  | ||||
| /// Handles health check scripts for containers | ||||
| ///  | ||||
| /// This module provides functionality to create and manage health check scripts | ||||
| /// for containers, allowing for more complex health checks than simple commands. | ||||
|  | ||||
| /// Converts a health check command or script to a usable command | ||||
| ///  | ||||
| /// If the input is a single-line command, it is returned as is. | ||||
| /// If the input is a multi-line script, it is written to a file in the | ||||
| /// /root/hero/var/containers directory and the path to that file is returned. | ||||
| ///  | ||||
| /// # Arguments | ||||
| ///  | ||||
| /// * `cmd` - The command or script to convert | ||||
| /// * `container_name` - The name of the container, used to create a unique script name | ||||
| ///  | ||||
| /// # Returns | ||||
| ///  | ||||
| /// * `String` - The command to use for the health check | ||||
| pub fn prepare_health_check_command(cmd: &str, container_name: &str) -> String { | ||||
|     // If the command is a multiline script, write it to a file | ||||
|     if cmd.contains("\n") { | ||||
|         // Create the directory if it doesn't exist | ||||
|         let dir_path = "/root/hero/var/containers"; | ||||
|         if let Err(_) = fs::create_dir_all(dir_path) { | ||||
|             // If we can't create the directory, just use the command as is | ||||
|             return cmd.to_string(); | ||||
|         } | ||||
|          | ||||
|         // Create a unique filename based on container name | ||||
|         let script_path = format!("{}/healthcheck_{}.sh", dir_path, container_name); | ||||
|          | ||||
|         // Write the script to the file | ||||
|         if let Err(_) = fs::write(&script_path, cmd) { | ||||
|             // If we can't write the file, just use the command as is | ||||
|             return cmd.to_string(); | ||||
|         } | ||||
|          | ||||
|         // Make the script executable | ||||
|         if let Ok(metadata) = fs::metadata(&script_path) { | ||||
|             let mut perms = metadata.permissions(); | ||||
|             perms.set_mode(0o755); | ||||
|             if let Err(_) = fs::set_permissions(&script_path, perms) { | ||||
|                 // If we can't set permissions, just use the script path with sh | ||||
|                 return format!("sh {}", script_path); | ||||
|             } | ||||
|         } else { | ||||
|             // If we can't get metadata, just use the script path with sh | ||||
|             return format!("sh {}", script_path); | ||||
|         } | ||||
|          | ||||
|         // Use the script path as the command | ||||
|         script_path | ||||
|     } else { | ||||
|         // If it's a single line command, use it as is | ||||
|         cmd.to_string() | ||||
|     } | ||||
| } | ||||
|  | ||||
| /// Cleans up health check scripts for a container | ||||
| ///  | ||||
| /// # Arguments | ||||
| ///  | ||||
| /// * `container_name` - The name of the container whose health check scripts should be cleaned up | ||||
| pub fn cleanup_health_check_scripts(container_name: &str) { | ||||
|     let dir_path = "/root/hero/var/containers"; | ||||
|     let script_path = format!("{}/healthcheck_{}.sh", dir_path, container_name); | ||||
|      | ||||
|     // Try to remove the script file if it exists | ||||
|     if Path::new(&script_path).exists() { | ||||
|         let _ = fs::remove_file(script_path); | ||||
|     } | ||||
| } | ||||
| @@ -4,6 +4,7 @@ mod container_types; | ||||
| mod container; | ||||
| mod container_builder; | ||||
| mod health_check; | ||||
| mod health_check_script; | ||||
| mod container_operations; | ||||
| mod container_functions; | ||||
| #[cfg(test)] | ||||
| @@ -52,4 +53,5 @@ impl Error for NerdctlError { | ||||
| pub use images::*; | ||||
| pub use cmd::*; | ||||
| pub use container_types::{Container, HealthCheck, ContainerStatus, ResourceUsage}; | ||||
| pub use container_functions::*; | ||||
| pub use container_functions::*; | ||||
| pub use health_check_script::*; | ||||
		Reference in New Issue
	
	Block a user