This commit is contained in:
despiegk 2025-04-05 15:10:02 +02:00
parent 768542afd3
commit 7bf2ffe47d
8 changed files with 554 additions and 139 deletions

View File

@ -25,7 +25,6 @@ let image = builder.image;
```
### Builder Methods
The Builder object provides the following methods:
- `run(command)`: Run a command in the container
@ -36,12 +35,18 @@ The Builder object provides the following methods:
- `remove()`: Remove the container
- `reset()`: Remove the container and clear the container_id
- `config(options)`: Configure container metadata
- `set_entrypoint(entrypoint)`: Set the entrypoint for the container
- `set_cmd(cmd)`: Set the default command for the container
- `debug_mode`: Get or set the debug flag (true/false)
- `write_content(content, dest_path)`: Write content to a file in the container
- `read_content(source_path)`: Read content from a file in the container
- `images()`: List images in local storage
- `image_remove(image)`: Remove an image
- `image_pull(image, tls_verify)`: Pull an image from a registry
- `image_push(image, destination, tls_verify)`: Push an image to a registry
- `image_tag(image, new_name)`: Add a tag to an image
- `build(tag, context_dir, file, isolation)`: Build an image from a Dockerfile
- `build(tag, context_dir, file, isolation)`: Build an image from a Dockerfile
### Example
@ -49,6 +54,9 @@ The Builder object provides the following methods:
// Create a builder
let builder = bah_new("my-container", "alpine:latest");
// Enable debug mode to see command output
builder.debug_mode = true;
// Reset the builder to remove any existing container
builder.reset();
@ -59,6 +67,16 @@ builder = bah_new("my-container", "alpine:latest");
let result = builder.run("echo 'Hello from container'");
println(`Command output: ${result.stdout}`);
// Write content directly to a file in the container
let script_content = `#!/bin/sh
echo "Hello from startup script"
`;
builder.write_content(script_content, "/start.sh");
builder.run("chmod +x /start.sh");
// Set the entrypoint for the container
builder.set_entrypoint("/start.sh");
// Add a file
file_write("test_file.txt", "Test content");
builder.add("test_file.txt", "/");
@ -122,3 +140,100 @@ builder.reset();
// Create a new container with the same name
builder = bah_new("my-container", "alpine:latest");
```
### `debug_mode`
Get or set the debug flag for the Builder. When debug mode is enabled, all buildah commands will output their stdout/stderr, making it easier to debug issues.
**Example:**
```js
// Create a Builder
let builder = bah_new("my-container", "alpine:latest");
// Enable debug mode
builder.debug_mode = true;
// Run a command with debug output
builder.run("echo 'Hello with debug'");
// Disable debug mode
builder.debug_mode = false;
```
### `set_entrypoint(entrypoint)`
Sets the entrypoint for the container. The entrypoint is the command that will be executed when the container starts.
**Parameters:**
- `entrypoint` (string): The entrypoint command
**Returns:** Command result if successful.
**Example:**
```js
// Create a Builder
let builder = bah_new("my-container", "alpine:latest");
// Set the entrypoint
builder.set_entrypoint("/start.sh");
```
### `set_cmd(cmd)`
Sets the default command for the container. This is used as arguments to the entrypoint.
**Parameters:**
- `cmd` (string): The default command
**Returns:** Command result if successful.
**Example:**
```js
// Create a Builder
let builder = bah_new("my-container", "alpine:latest");
// Set the default command
builder.set_cmd("--verbose");
```
### `write_content(content, dest_path)`
Writes content to a file in the container.
**Parameters:**
- `content` (string): The content to write
- `dest_path` (string): The destination path in the container
**Returns:** Command result if successful.
**Example:**
```js
// Create a Builder
let builder = bah_new("my-container", "alpine:latest");
// Write content to a file
let content = "Hello, world!";
builder.write_content(content, "/hello.txt");
```
### `read_content(source_path)`
Reads content from a file in the container.
**Parameters:**
- `source_path` (string): The source path in the container
**Returns:** The file content as a string if successful.
**Example:**
```js
// Create a Builder
let builder = bah_new("my-container", "alpine:latest");
// Write content to a file
builder.write_content("Hello, world!", "/hello.txt");
// Read content from the file
let content = builder.read_content("/hello.txt");
println(content); // Outputs: Hello, world!
```

View File

@ -91,27 +91,27 @@ let remove_result = container.remove();
The Container Builder Pattern provides the following methods for configuring containers:
| Method | Description | Example |
|--------|-------------|---------|
| `reset()` | Reset configuration to defaults | `.reset()` |
| `with_port(port)` | Add a port mapping | `.with_port("8080:80")` |
| `with_ports(ports_array)` | Add multiple port mappings | `.with_ports(["8080:80", "443:443"])` |
| `with_volume(volume)` | Add a volume mount | `.with_volume("/host/path:/container/path")` |
| `with_volumes(volumes_array)` | Add multiple volume mounts | `.with_volumes(["/host/path1:/container/path1", "/host/path2:/container/path2"])` |
| `with_env(key, value)` | Add an environment variable | `.with_env("NGINX_HOST", "localhost")` |
| `with_envs(env_map)` | Add multiple environment variables | `.with_envs(#{"KEY1": "value1", "KEY2": "value2"})` |
| `with_network(network)` | Set the network | `.with_network("bridge")` |
| `with_network_alias(alias)` | Add a network alias | `.with_network_alias("web-server")` |
| `with_network_aliases(aliases_array)` | Add multiple network aliases | `.with_network_aliases(["web-server", "http-service"])` |
| `with_cpu_limit(cpus)` | Set CPU limit | `.with_cpu_limit("1.0")` |
| `with_cpu_shares(shares)` | Set CPU shares | `.with_cpu_shares("1024")` |
| `with_memory_limit(memory)` | Set memory limit | `.with_memory_limit("512m")` |
| `with_memory_swap_limit(memory_swap)` | Set memory swap limit | `.with_memory_swap_limit("1g")` |
| `with_restart_policy(policy)` | Set restart policy | `.with_restart_policy("unless-stopped")` |
| `with_health_check(cmd)` | Set health check command | `.with_health_check("curl -f http://localhost/ || exit 1")` |
| `with_health_check_options(cmd, interval, timeout, retries, start_period)` | Set health check with options | `.with_health_check_options("curl -f http://localhost/ || exit 1", "5s", "3s", 3, "10s")` |
| `with_snapshotter(snapshotter)` | Set snapshotter | `.with_snapshotter("native")` |
| `with_detach(detach)` | Set detach mode | `.with_detach(true)` |
| Method | Description | Example |
| -------------------------------------------------------------------------- | ---------------------------------- | --------------------------------------------------------------------------------- |
| `reset()` | Reset configuration to defaults | `.reset()` |
| `with_port(port)` | Add a port mapping | `.with_port("8080:80")` |
| `with_ports(ports_array)` | Add multiple port mappings | `.with_ports(["8080:80", "443:443"])` |
| `with_volume(volume)` | Add a volume mount | `.with_volume("/host/path:/container/path")` |
| `with_volumes(volumes_array)` | Add multiple volume mounts | `.with_volumes(["/host/path1:/container/path1", "/host/path2:/container/path2"])` |
| `with_env(key, value)` | Add an environment variable | `.with_env("NGINX_HOST", "localhost")` |
| `with_envs(env_map)` | Add multiple environment variables | `.with_envs(#{"KEY1": "value1", "KEY2": "value2"})` |
| `with_network(network)` | Set the network | `.with_network("bridge")` |
| `with_network_alias(alias)` | Add a network alias | `.with_network_alias("web-server")` |
| `with_network_aliases(aliases_array)` | Add multiple network aliases | `.with_network_aliases(["web-server", "http-service"])` |
| `with_cpu_limit(cpus)` | Set CPU limit | `.with_cpu_limit("1.0")` |
| `with_cpu_shares(shares)` | Set CPU shares | `.with_cpu_shares("1024")` |
| `with_memory_limit(memory)` | Set memory limit | `.with_memory_limit("512m")` |
| `with_memory_swap_limit(memory_swap)` | Set memory swap limit | `.with_memory_swap_limit("1g")` |
| `with_restart_policy(policy)` | Set restart policy | `.with_restart_policy("unless-stopped")` |
| `with_health_check(cmd)` | Set health check command | `.with_health_check("curl -f http://localhost/ | | exit 1")` |
| `with_health_check_options(cmd, interval, timeout, retries, start_period)` | Set health check with options | `.with_health_check_options("curl -f http://localhost/ | | exit 1", "5s", "3s", 3, "10s")` |
| `with_snapshotter(snapshotter)` | Set snapshotter | `.with_snapshotter("native")` |
| `with_detach(detach)` | Set detach mode | `.with_detach(true)` |
## Complete Example: Web Server
@ -183,16 +183,57 @@ let start_result = built_container.start();
println("The web server is running at http://localhost:8080");
```
## Using Local Images Created with Buildah
When working with images created by Buildah, you may need to take additional steps to ensure nerdctl can find and use these images. This is because Buildah and nerdctl may use different storage backends by default.
### Tagging with localhost Prefix
One approach is to tag the Buildah-created image with a `localhost/` prefix:
```rhai
// Create and commit a container with Buildah
let builder = bah_new("my-container", "alpine:latest");
builder.run("echo 'Hello' > /hello.txt");
builder.commit("my-custom-image:latest");
// Tag the image with localhost prefix for nerdctl compatibility
let local_image_name = "localhost/my-custom-image:latest";
bah_image_tag("my-custom-image:latest", local_image_name);
// Now use the image with nerdctl
let container = nerdctl_container_from_image("my-app", local_image_name)
.with_detach(true)
.build();
```
### Using a Local Registry
For more reliable interoperability, you can push the image to a local registry:
```rhai
// Push the Buildah-created image to a local registry
bah_image_push("my-custom-image:latest", "localhost:5000/my-custom-image:latest", false);
// Pull the image with nerdctl
nerdctl_image_pull("localhost:5000/my-custom-image:latest");
// Use the image
let container = nerdctl_container_from_image("my-app", "localhost:5000/my-custom-image:latest")
.with_detach(true)
.build();
```
## Image Management Functions
The module also provides functions for managing container images:
| Function | Description | Example |
|----------|-------------|---------|
| `nerdctl_images()` | List images in local storage | `nerdctl_images()` |
| `nerdctl_image_remove(image)` | Remove an image | `nerdctl_image_remove("nginx:latest")` |
| `nerdctl_image_push(image, destination)` | Push an image to a registry | `nerdctl_image_push("my-image:latest", "registry.example.com/my-image:latest")` |
| `nerdctl_image_tag(image, new_name)` | Add an additional name to a local image | `nerdctl_image_tag("nginx:latest", "my-nginx:latest")` |
| `nerdctl_image_pull(image)` | Pull an image from a registry | `nerdctl_image_pull("nginx:latest")` |
| `nerdctl_image_commit(container, image_name)` | Commit a container to an image | `nerdctl_image_commit("web-server", "my-nginx:latest")` |
| `nerdctl_image_build(tag, context_path)` | Build an image using a Dockerfile | `nerdctl_image_build("my-image:latest", "./")` |
| Function | Description | Example |
| --------------------------------------------- | --------------------------------------- | ------------------------------------------------------------------------------- |
| `nerdctl_images()` | List images in local storage | `nerdctl_images()` |
| `nerdctl_image_remove(image)` | Remove an image | `nerdctl_image_remove("nginx:latest")` |
| `nerdctl_image_push(image, destination)` | Push an image to a registry | `nerdctl_image_push("my-image:latest", "registry.example.com/my-image:latest")` |
| `nerdctl_image_tag(image, new_name)` | Add an additional name to a local image | `nerdctl_image_tag("nginx:latest", "my-nginx:latest")` |
| `nerdctl_image_pull(image)` | Pull an image from a registry | `nerdctl_image_pull("nginx:latest")` |
| `nerdctl_image_commit(container, image_name)` | Commit a container to an image | `nerdctl_image_commit("web-server", "my-nginx:latest")` |
| `nerdctl_image_build(tag, context_path)` | Build an image using a Dockerfile | `nerdctl_image_build("my-image:latest", "./")` |

View File

@ -264,7 +264,7 @@ chdir("project/src");
### `download(url, dest, min_size_kb)`
Downloads a file from a URL to a destination using the curl command. If the URL ends with a supported archive format, the file will be automatically extracted to the destination directory.
Downloads a file from a URL to a destination directory using the curl command. If the URL ends with a supported archive format, the file will be automatically extracted to the destination directory.
**Supported archive formats for automatic extraction:**
- `.tar.gz`
@ -274,15 +274,32 @@ Downloads a file from a URL to a destination using the curl command. If the URL
**Parameters:**
- `url` (string): The URL to download from
- `dest` (string): The destination path to save the file
- `dest` (string): The destination directory where the file will be saved or extracted
- `min_size_kb` (integer): The minimum expected file size in kilobytes (for validation)
**Returns:** The path where the file was saved or extracted.
**Example:**
```js
// Download a file
download("https://example.com/file.zip", "downloads/file.zip", 10);
// Download a file to a directory
download("https://example.com/file.zip", "downloads/", 10);
```
### `download_file(url, dest, min_size_kb)`
Downloads a file from a URL to a specific file destination using the curl command. This function is designed for downloading files to a specific path, not for extracting archives.
**Parameters:**
- `url` (string): The URL to download from
- `dest` (string): The destination file path where the file will be saved
- `min_size_kb` (integer): The minimum expected file size in kilobytes (for validation)
**Returns:** The path where the file was saved.
**Example:**
```js
// Download a file to a specific path
download_file("https://example.com/file.txt", "downloads/myfile.txt", 10);
```
### `download_install(url, min_size_kb)`
@ -301,4 +318,20 @@ Downloads a file and installs it if it's a supported package format.
**Example:**
```js
// Download and install a package
download_install("https://example.com/package.deb", 1000);
download_install("https://example.com/package.deb", 1000);
```
### `chmod_exec(path)`
Makes a file executable (equivalent to `chmod +x` in Unix).
**Parameters:**
- `path` (string): The path to the file to make executable
**Returns:** A message confirming the file was made executable.
**Example:**
```js
// Make a file executable
chmod_exec("downloads/script.sh");
```

View File

@ -58,34 +58,167 @@ impl Error for DownloadError {
/**
* Download a file from URL to destination using the curl command.
*
* This function is primarily intended for downloading archives that will be extracted
* to a directory.
*
* # Arguments
*
*
* * `url` - The URL to download from
* * `dest` - The destination path where the file will be saved
* * `dest` - The destination directory where the file will be saved or extracted
* * `min_size_kb` - Minimum required file size in KB (0 for no minimum)
*
*
* # Returns
*
*
* * `Ok(String)` - The path where the file was saved or extracted
* * `Err(DownloadError)` - An error if the download failed
*
*
* # Examples
*
*
* ```
* // Download a file with no minimum size requirement
* let path = download("https://example.com/file.txt", "/tmp/file.txt", 0)?;
*
* let path = download("https://example.com/file.txt", "/tmp/", 0)?;
*
* // Download a file with minimum size requirement of 100KB
* let path = download("https://example.com/file.zip", "/tmp/file.zip", 100)?;
* let path = download("https://example.com/file.zip", "/tmp/", 100)?;
* ```
*
*
* # Notes
*
*
* If the URL ends with .tar.gz, .tgz, .tar, or .zip, the file will be automatically
* extracted to the destination directory.
*/
pub fn download(url: &str, dest: &str, min_size_kb: i64) -> Result<String, DownloadError> {
// Create parent directories if they don't exist
let dest_path = Path::new(dest);
fs::create_dir_all(dest_path).map_err(DownloadError::CreateDirectoryFailed)?;
// Extract filename from URL
let filename = match url.split('/').last() {
Some(name) => name,
None => return Err(DownloadError::InvalidUrl("cannot extract filename".to_string()))
};
// Create a full path for the downloaded file
let file_path = format!("{}/{}", dest.trim_end_matches('/'), filename);
// Create a temporary path for downloading
let temp_path = format!("{}.download", file_path);
// Use curl to download the file with progress bar
println!("Downloading {} to {}", url, file_path);
let output = Command::new("curl")
.args(&["--progress-bar", "--location", "--fail", "--output", &temp_path, url])
.status()
.map_err(DownloadError::CurlExecutionFailed)?;
if !output.success() {
return Err(DownloadError::DownloadFailed(url.to_string()));
}
// Show file size after download
match fs::metadata(&temp_path) {
Ok(metadata) => {
let size_bytes = metadata.len();
let size_kb = size_bytes / 1024;
let size_mb = size_kb / 1024;
if size_mb > 1 {
println!("Download complete! File size: {:.2} MB", size_bytes as f64 / (1024.0 * 1024.0));
} else {
println!("Download complete! File size: {:.2} KB", size_bytes as f64 / 1024.0);
}
},
Err(_) => println!("Download complete!"),
}
// Check file size if minimum size is specified
if min_size_kb > 0 {
let metadata = fs::metadata(&temp_path).map_err(DownloadError::FileMetadataError)?;
let size_kb = metadata.len() as i64 / 1024;
if size_kb < min_size_kb {
fs::remove_file(&temp_path).map_err(DownloadError::RemoveFileFailed)?;
return Err(DownloadError::FileTooSmall(size_kb, min_size_kb));
}
}
// Check if it's a compressed file that needs extraction
let lower_url = url.to_lowercase();
let is_archive = lower_url.ends_with(".tar.gz") ||
lower_url.ends_with(".tgz") ||
lower_url.ends_with(".tar") ||
lower_url.ends_with(".zip");
if is_archive {
// Extract the file using the appropriate command with progress indication
println!("Extracting {} to {}", temp_path, dest);
let output = if lower_url.ends_with(".zip") {
Command::new("unzip")
.args(&["-o", &temp_path, "-d", dest]) // Removed -q for verbosity
.status()
} else if lower_url.ends_with(".tar.gz") || lower_url.ends_with(".tgz") {
Command::new("tar")
.args(&["-xzvf", &temp_path, "-C", dest]) // Added v for verbosity
.status()
} else {
Command::new("tar")
.args(&["-xvf", &temp_path, "-C", dest]) // Added v for verbosity
.status()
};
match output {
Ok(status) => {
if !status.success() {
return Err(DownloadError::ExtractionFailed("Error extracting archive".to_string()));
}
},
Err(e) => return Err(DownloadError::CommandExecutionFailed(e)),
}
// Show number of extracted files
match fs::read_dir(dest) {
Ok(entries) => {
let count = entries.count();
println!("Extraction complete! Extracted {} files/directories", count);
},
Err(_) => println!("Extraction complete!"),
}
// Remove the temporary file
fs::remove_file(&temp_path).map_err(DownloadError::RemoveFileFailed)?;
Ok(dest.to_string())
} else {
// Just rename the temporary file to the final destination
fs::rename(&temp_path, &file_path).map_err(|e| DownloadError::CreateDirectoryFailed(e))?;
Ok(file_path)
}
}
/**
* Download a file from URL to a specific file destination using the curl command.
*
* # Arguments
*
* * `url` - The URL to download from
* * `dest` - The destination file path where the file will be saved
* * `min_size_kb` - Minimum required file size in KB (0 for no minimum)
*
* # Returns
*
* * `Ok(String)` - The path where the file was saved
* * `Err(DownloadError)` - An error if the download failed
*
* # Examples
*
* ```
* // Download a file with no minimum size requirement
* let path = download_file("https://example.com/file.txt", "/tmp/file.txt", 0)?;
*
* // Download a file with minimum size requirement of 100KB
* let path = download_file("https://example.com/file.zip", "/tmp/file.zip", 100)?;
* ```
*/
pub fn download_file(url: &str, dest: &str, min_size_kb: i64) -> Result<String, DownloadError> {
// Create parent directories if they don't exist
let dest_path = Path::new(dest);
if let Some(parent) = dest_path.parent() {
@ -131,61 +264,73 @@ pub fn download(url: &str, dest: &str, min_size_kb: i64) -> Result<String, Downl
}
}
// Check if it's a compressed file that needs extraction
let lower_url = url.to_lowercase();
let is_archive = lower_url.ends_with(".tar.gz") ||
lower_url.ends_with(".tgz") ||
lower_url.ends_with(".tar") ||
lower_url.ends_with(".zip");
// Rename the temporary file to the final destination
fs::rename(&temp_path, dest).map_err(|e| DownloadError::CreateDirectoryFailed(e))?;
if is_archive {
// Create the destination directory
fs::create_dir_all(dest).map_err(DownloadError::CreateDirectoryFailed)?;
// Extract the file using the appropriate command with progress indication
println!("Extracting {} to {}", temp_path, dest);
let output = if lower_url.ends_with(".zip") {
Command::new("unzip")
.args(&["-o", &temp_path, "-d", dest]) // Removed -q for verbosity
.status()
} else if lower_url.ends_with(".tar.gz") || lower_url.ends_with(".tgz") {
Command::new("tar")
.args(&["-xzvf", &temp_path, "-C", dest]) // Added v for verbosity
.status()
} else {
Command::new("tar")
.args(&["-xvf", &temp_path, "-C", dest]) // Added v for verbosity
.status()
};
match output {
Ok(status) => {
if !status.success() {
return Err(DownloadError::ExtractionFailed("Error extracting archive".to_string()));
}
},
Err(e) => return Err(DownloadError::CommandExecutionFailed(e)),
}
// Show number of extracted files
match fs::read_dir(dest) {
Ok(entries) => {
let count = entries.count();
println!("Extraction complete! Extracted {} files/directories", count);
},
Err(_) => println!("Extraction complete!"),
}
// Remove the temporary file
fs::remove_file(&temp_path).map_err(DownloadError::RemoveFileFailed)?;
Ok(dest.to_string())
} else {
// Just rename the temporary file to the final destination
fs::rename(&temp_path, dest).map_err(|e| DownloadError::CreateDirectoryFailed(e))?;
Ok(dest.to_string())
Ok(dest.to_string())
}
/**
* Make a file executable (equivalent to chmod +x).
*
* # Arguments
*
* * `path` - The path to the file to make executable
*
* # Returns
*
* * `Ok(String)` - A success message including the path
* * `Err(DownloadError)` - An error if the operation failed
*
* # Examples
*
* ```
* // Make a file executable
* chmod_exec("/path/to/file")?;
* ```
*/
pub fn chmod_exec(path: &str) -> Result<String, DownloadError> {
let path_obj = Path::new(path);
// Check if the path exists and is a file
if !path_obj.exists() {
return Err(DownloadError::NotAFile(format!("Path does not exist: {}", path)));
}
if !path_obj.is_file() {
return Err(DownloadError::NotAFile(format!("Path is not a file: {}", path)));
}
// Get current permissions
let metadata = fs::metadata(path).map_err(DownloadError::FileMetadataError)?;
let mut permissions = metadata.permissions();
// Set executable bit for user, group, and others
#[cfg(unix)]
{
use std::os::unix::fs::PermissionsExt;
let mode = permissions.mode();
// Add executable bit for user, group, and others (equivalent to +x)
let new_mode = mode | 0o111;
permissions.set_mode(new_mode);
}
#[cfg(not(unix))]
{
// On non-Unix platforms, we can't set executable bit directly
// Just return success with a warning
return Ok(format!("Made {} executable (note: non-Unix platform, may not be fully supported)", path));
}
// Apply the new permissions
fs::set_permissions(path, permissions).map_err(|e|
DownloadError::CommandExecutionFailed(io::Error::new(
io::ErrorKind::Other,
format!("Failed to set executable permissions: {}", e)
))
)?;
Ok(format!("Made {} executable", path))
}
/**
@ -223,11 +368,24 @@ pub fn download_install(url: &str, min_size_kb: i64) -> Result<String, DownloadE
// Create a proper destination path
let dest_path = format!("/tmp/{}", filename);
let download_result = download(url, &dest_path, min_size_kb)?;
// Check if it's a compressed file that needs extraction
let lower_url = url.to_lowercase();
let is_archive = lower_url.ends_with(".tar.gz") ||
lower_url.ends_with(".tgz") ||
lower_url.ends_with(".tar") ||
lower_url.ends_with(".zip");
let download_result = if is_archive {
// For archives, use the directory-based download function
download(url, "/tmp", min_size_kb)?
} else {
// For regular files, use the file-specific download function
download_file(url, &dest_path, min_size_kb)?
};
// Check if the downloaded result is a file
let path = Path::new(&dest_path);
if !path.is_file() {
if !path.is_file() {
return Ok(download_result); // Not a file, might be an extracted directory
}

View File

@ -41,7 +41,9 @@ pub fn register_os_module(engine: &mut Engine) -> Result<(), Box<EvalAltResult>>
// Register download functions
engine.register_fn("download", download);
engine.register_fn("download_file", download_file);
engine.register_fn("download_install", download_install);
engine.register_fn("chmod_exec", chmod_exec);
Ok(())
}
@ -175,6 +177,13 @@ pub fn download(url: &str, dest: &str, min_size_kb: i64) -> Result<String, Box<E
os::download(url, dest, min_size_kb).to_rhai_error()
}
/// Wrapper for os::download_file
///
/// Download a file from URL to a specific file destination using the curl command.
pub fn download_file(url: &str, dest: &str, min_size_kb: i64) -> Result<String, Box<EvalAltResult>> {
os::download_file(url, dest, min_size_kb).to_rhai_error()
}
/// Wrapper for os::download_install
///
/// Download a file and install it if it's a supported package format.
@ -182,6 +191,13 @@ pub fn download_install(url: &str, min_size_kb: i64) -> Result<String, Box<EvalA
os::download_install(url, min_size_kb).to_rhai_error()
}
/// Wrapper for os::chmod_exec
///
/// Make a file executable (equivalent to chmod +x).
pub fn chmod_exec(path: &str) -> Result<String, Box<EvalAltResult>> {
os::chmod_exec(path).to_rhai_error()
}
/// Wrapper for os::which
///
/// Check if a command exists in the system PATH.

View File

@ -107,35 +107,44 @@ println(read_script);
println(`Committing container to image '${final_image_name}'...`);
let commit_result = builder.commit(final_image_name);
// // Clean up the buildah container
// println("Cleaning up buildah container...");
// builder.remove();
// Clean up the buildah container
println("Cleaning up buildah container...");
builder.remove();
// // Now use nerdctl to run a container from the new image
// println("\nStarting container from the new image using nerdctl...");
// Now use nerdctl to run a container from the new image
println("\nStarting container from the new image using nerdctl...");
// // Create a container using the builder pattern
// // Use localhost/ prefix to ensure nerdctl uses the local image
// let local_image_name = "localhost/" + final_image_name;
// println(`Using local image: ${local_image_name}`);
// Create a container using the builder pattern
// Use localhost/ prefix to ensure nerdctl uses the local image
let local_image_name = "localhost/" + final_image_name;
println(`Using local image: ${local_image_name}`);
// // Tag the image with the localhost prefix
// let tag_result = bah_image_tag(final_image_name, local_image_name);
// println(`Tagged image as ${local_image_name}`);
// Tag the image with the localhost prefix for nerdctl compatibility
println(`Tagging image as ${local_image_name}...`);
let tag_result = bah_image_tag(final_image_name, local_image_name);
// let container = nerdctl_container_from_image("golang-nginx-demo", local_image_name)
// .with_detach(true)
// .with_port("8080:80") // Map port 80 in the container to 8080 on the host
// .with_restart_policy("unless-stopped")
// .build();
// Print a command to check if the image exists in buildah
println("\nTo verify the image was created with buildah, run:");
println("buildah images");
// // Start the container
// let start_result = container.start();
// Note: If nerdctl cannot find the image, you may need to push it to a registry
println("\nNote: If nerdctl cannot find the image, you may need to push it to a registry:");
println("buildah push localhost/custom-golang-nginx:latest docker://localhost:5000/custom-golang-nginx:latest");
println("nerdctl pull localhost:5000/custom-golang-nginx:latest");
// println("\nWorkflow completed successfully!");
// println("The web server should be running at http://localhost:8080");
// println("You can check container logs with: nerdctl logs golang-nginx-demo");
// println("To stop the container: nerdctl stop golang-nginx-demo");
// println("To remove the container: nerdctl rm golang-nginx-demo");
let container = nerdctl_container_from_image("golang-nginx-demo", local_image_name)
.with_detach(true)
.with_port("8080:80") // Map port 80 in the container to 8080 on the host
.with_restart_policy("unless-stopped")
.build();
// Start the container
let start_result = container.start();
println("\nWorkflow completed successfully!");
println("The web server should be running at http://localhost:8080");
println("You can check container logs with: nerdctl logs golang-nginx-demo");
println("To stop the container: nerdctl stop golang-nginx-demo");
println("To remove the container: nerdctl rm golang-nginx-demo");
"Buildah and nerdctl workflow completed successfully!"

View File

@ -63,6 +63,43 @@ print(` Minimum size check passed:${success_msg}`);
// Clean up test files
delete(download_dir);
print("Cleaned up test directory");
//PART 4
print("\nDownload Tests completed successfully!");
// Test the new download_file function
print("\nTesting download_file function:");
let text_url = "https://raw.githubusercontent.com/freeflowuniverse/herolib/main/README.md";
let text_file_dest = `${download_dir}/README.md`;
// Create the directory again for this test
mkdir(download_dir);
// Download a text file using the new download_file function
let file_result = download_file(text_url, text_file_dest, 0);
print(` File downloaded to: ${file_result}`);
// Check if the file exists and has content
let file_exists = exist(text_file_dest);
print(` File exists: ${file_exists}`);
let file_content = file_read(text_file_dest);
let content_check = if file_content.len() > 100 { "yes" } else { "no" };
print(` File has content: ${content_check}`);
//PART 5
// Test the new chmod_exec function
print("\nTesting chmod_exec function:");
// Create a simple shell script
let script_path = `${download_dir}/test_script.sh`;
file_write(script_path, "#!/bin/sh\necho 'Hello from test script'");
// Make it executable
let chmod_result = chmod_exec(script_path);
print(` ${chmod_result}`);
// Clean up test files again
delete(download_dir);
print("Cleaned up test directory");
print("\nAll Download Tests completed successfully!");
"Download Tests Success"
"Download Tests Success"

View File

@ -2,19 +2,25 @@
fn nerdctl_download(){
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/");
delete(`/tmp/${name}`);
// 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}`,20000);
// copy(`/tmp/${name}/*`,"/root/hero/bin/");
// delete(`/tmp/${name}`);
let name="containerd";
let url="https://github.com/containerd/containerd/releases/download/v2.0.4/containerd-2.0.4-linux-amd64.tar.gz";
download(url,`/tmp/${name}`,20);
copy(`/tmp/${name}/bin/*`,"/root/hero/bin/");
delete(`/tmp/${name}`);
// let name="containerd";
// let url="https://github.com/containerd/containerd/releases/download/v2.0.4/containerd-2.0.4-linux-amd64.tar.gz";
// download(url,`/tmp/${name}`,20000);
// copy(`/tmp/${name}/bin/*`,"/root/hero/bin/");
// delete(`/tmp/${name}`);
run("apt-get -y install buildah runc")
// run("apt-get -y install buildah runc")
let url="https://github.com/threefoldtech/rfs/releases/download/v2.0.6/rfs";
download_file(url,`/tmp/rfs`,10000);
chmod_exec(url);
copy(`/tmp/rfs`,"/root/hero/bin/");
//delete(`/tmp/rfs`);
}