Compare commits
2 Commits
5fec3b967b
...
c177ac5efb
Author | SHA1 | Date | |
---|---|---|---|
c177ac5efb | |||
6d4c1742e7 |
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::*;
|
Loading…
Reference in New Issue
Block a user