sal/src/virt/buildah/README.md
timurgordon 65e404e517
Some checks are pending
Rhai Tests / Run Rhai Tests (push) Waiting to run
merge branches and document
2025-05-23 21:12:17 +03:00

233 lines
15 KiB
Markdown

# SAL Buildah Module (`sal::virt::buildah`)
## Overview
The Buildah module in SAL provides a comprehensive Rust interface for interacting with the `buildah` command-line tool. It allows users to build OCI (Open Container Initiative) and Docker-compatible container images programmatically. The module offers both a high-level `Builder` API for step-by-step image construction and static functions for managing images in local storage.
A Rhai script interface for this module is also available via `sal::rhai::buildah`, making these functionalities accessible from `herodo` scripts.
## Core Components
### 1. `Builder` Struct (`sal::virt::buildah::Builder`)
The `Builder` struct is the primary entry point for constructing container images. It encapsulates a Buildah working container, created from a base image, and provides methods to modify this container and eventually commit it as a new image.
- **Creation**: `Builder::new(name: &str, image: &str) -> Result<Builder, BuildahError>`
- Creates a new working container (or re-attaches to an existing one with the same name) from the specified base `image`.
- **Debug Mode**: `builder.set_debug(true)` / `builder.debug()`
- Enables/disables verbose logging for Buildah commands executed by this builder instance.
#### Working Container Operations:
- `builder.run(command: &str) -> Result<CommandResult, BuildahError>`: Executes a shell command inside the working container (e.g., `buildah run <container> -- <command>`).
- `builder.run_with_isolation(command: &str, isolation: &str) -> Result<CommandResult, BuildahError>`: Runs a command with specified isolation (e.g., "chroot").
- `builder.copy(source_on_host: &str, dest_in_container: &str) -> Result<CommandResult, BuildahError>`: Copies files/directories from the host to the container (`buildah copy`).
- `builder.add(source_on_host: &str, dest_in_container: &str) -> Result<CommandResult, BuildahError>`: Adds files/directories to the container (`buildah add`), potentially handling URLs and archive extraction.
- `builder.config(options: HashMap<String, String>) -> Result<CommandResult, BuildahError>`: Modifies image metadata (e.g., environment variables, labels, entrypoint, cmd). Example options: `{"env": "MYVAR=value", "label": "mylabel=myvalue"}`.
- `builder.set_entrypoint(entrypoint: &str) -> Result<CommandResult, BuildahError>`: Sets the image entrypoint.
- `builder.set_cmd(cmd: &str) -> Result<CommandResult, BuildahError>`: Sets the default command for the image.
- `builder.commit(image_name: &str) -> Result<CommandResult, BuildahError>`: Commits the current state of the working container to a new image named `image_name`.
- `builder.remove() -> Result<CommandResult, BuildahError>`: Removes the working container (`buildah rm`).
- `builder.reset() -> Result<(), BuildahError>`: Removes the working container and resets the builder state.
### 2. Static Image Management Functions (on `Builder`)
These functions operate on images in the local Buildah storage and are not tied to a specific `Builder` instance.
- `Builder::images() -> Result<Vec<Image>, BuildahError>`: Lists all images available locally (`buildah images --json`). Returns a vector of `Image` structs.
- `Builder::image_remove(image_ref: &str) -> Result<CommandResult, BuildahError>`: Removes an image (`buildah rmi <image_ref>`).
- `Builder::image_pull(image_name: &str, tls_verify: bool) -> Result<CommandResult, BuildahError>`: Pulls an image from a registry (`buildah pull`).
- `Builder::image_push(image_ref: &str, destination: &str, tls_verify: bool) -> Result<CommandResult, BuildahError>`: Pushes an image to a registry (`buildah push`).
- `Builder::image_tag(image_ref: &str, new_name: &str) -> Result<CommandResult, BuildahError>`: Tags an image (`buildah tag`).
- `Builder::image_commit(container_ref: &str, image_name: &str, format: Option<&str>, squash: bool, rm: bool) -> Result<CommandResult, BuildahError>`: A static version to commit any existing container to an image, with options for format (e.g., "oci", "docker"), squashing layers, and removing the container post-commit.
- `Builder::build(tag: Option<&str>, context_dir: &str, file: &str, isolation: Option<&str>) -> Result<CommandResult, BuildahError>`: Builds an image from a Dockerfile/Containerfile (`buildah bud`).
*Note: Many static image functions also have a `_with_debug(..., debug: bool)` variant for explicit debug control.*
### 3. `Image` Struct (`sal::virt::buildah::Image`)
Represents a container image as listed by `buildah images`.
```rust
pub struct Image {
pub id: String, // Image ID
pub names: Vec<String>, // Image names/tags
pub size: String, // Image size
pub created: String, // Creation timestamp (as string)
}
```
### 4. `ContentOperations` (`sal::virt::buildah::ContentOperations`)
Provides static methods for reading and writing file content directly within a container, useful for dynamic configuration or inspection.
- `ContentOperations::write_content(container_id: &str, content: &str, dest_path_in_container: &str) -> Result<CommandResult, BuildahError>`: Writes string content to a file inside the specified container.
- `ContentOperations::read_content(container_id: &str, source_path_in_container: &str) -> Result<String, BuildahError>`: Reads the content of a file from within the specified container into a string.
### 5. `BuildahError` Enum (`sal::virt::buildah::BuildahError`)
Defines the error types that can occur during Buildah operations:
- `CommandExecutionFailed(io::Error)`: The `buildah` command itself failed to start.
- `CommandFailed(String)`: The `buildah` command ran but returned a non-zero exit code or error.
- `JsonParseError(String)`: Failed to parse JSON output from Buildah.
- `ConversionError(String)`: Error during data conversion.
- `Other(String)`: Generic error.
## Key Design Points
The SAL Buildah module is designed with the following principles:
- **Builder Pattern**: The `Builder` struct (`sal::virt::buildah::Builder`) employs a builder pattern, enabling a fluent, step-by-step, and stateful approach to constructing container images. Each `Builder` instance manages a specific working container.
- **Separation of Concerns**:
- **Instance Methods**: Operations specific to a working container (e.g., `run`, `copy`, `config`, `commit`) are methods on the `Builder` instance.
- **Static Methods**: General image management tasks (e.g., listing images with `Builder::images()`, removing images with `Builder::image_remove()`, pulling, pushing, tagging, and building from a Dockerfile with `Builder::build()`) are provided as static functions on the `Builder` struct.
- **Direct Content Manipulation**: The `ContentOperations` struct provides static methods (`write_content`, `read_content`) to directly interact with files within a Buildah container. This is typically achieved by temporarily mounting the container or using `buildah add` with temporary files, abstracting the complexity from the user.
- **Debuggability**: Fine-grained control over `buildah` command logging is provided. The `builder.set_debug(true)` method enables verbose output for a specific `Builder` instance. Many static functions also offer `_with_debug(..., debug: bool)` variants. This is managed internally via a thread-local flag passed to the core `execute_buildah_command` function.
- **Comprehensive Rhai Integration**: Most functionalities of the Buildah module are exposed to Rhai scripts executed via `herodo`, allowing for powerful automation of image building workflows. This is facilitated by the `sal::rhai::buildah` module.
## Low-Level Command Execution
- `execute_buildah_command(args: &[&str]) -> Result<CommandResult, BuildahError>` (in `sal::virt::buildah::cmd`):
The core function that executes `buildah` commands. It handles debug logging based on a thread-local flag, which is managed by the higher-level `Builder` methods and `_with_debug` static function variants.
## Usage Example (Rust)
```rust
use sal::virt::buildah::{Builder, BuildahError, ContentOperations};
use std::collections::HashMap;
fn build_custom_image() -> Result<String, BuildahError> {
// Create a new builder from a base image (e.g., alpine)
let mut builder = Builder::new("my-custom-container", "docker.io/library/alpine:latest")?;
builder.set_debug(true);
// Run some commands
builder.run("apk add --no-cache curl")?;
builder.run("mkdir /app")?;
// Add a file
ContentOperations::write_content(builder.container_id().unwrap(), "Hello from SAL!", "/app/hello.txt")?;
// Set image configuration
let mut config_opts = HashMap::new();
config_opts.insert("workingdir".to_string(), "/app".to_string());
config_opts.insert("label".to_string(), "maintainer=sal-user".to_string());
builder.config(config_opts)?;
builder.set_entrypoint("["/usr/bin/curl"]")?;
builder.set_cmd("["http://ifconfig.me"]")?;
// Commit the image
let image_tag = "localhost/my-custom-image:latest";
builder.commit(image_tag)?;
println!("Successfully built image: {}", image_tag);
// Clean up the working container
builder.remove()?;
Ok(image_tag.to_string())
}
fn main() {
match build_custom_image() {
Ok(tag) => println!("Image {} created.", tag),
Err(e) => eprintln!("Error building image: {}", e),
}
}
```
## Rhai Scripting with `herodo`
The Buildah module's capabilities are extensively exposed to Rhai scripts, enabling automation of image building and management tasks via the `herodo` CLI tool. The `sal::rhai::buildah` module registers the necessary functions and types.
Below is a summary of commonly used Rhai functions for Buildah. (Note: `builder` refers to an instance of `BuildahBuilder` obtained typically via `bah_new`).
### Builder Object Management
- `bah_new(name: String, image: String) -> BuildahBuilder`: Creates a new Buildah builder instance (working container) from a base `image` with a given `name`.
- `builder.remove()`: Removes the working container associated with the `builder`.
- `builder.reset()`: Removes the working container and resets the `builder` state.
### Builder Configuration & Operations
- `builder.set_debug(is_debug: bool)`: Enables or disables verbose debug logging for commands executed by this `builder`.
- `builder.debug_mode` (property): Get or set the debug mode (e.g., `let mode = builder.debug_mode; builder.debug_mode = true;`).
- `builder.container_id` (property): Returns the ID of the working container (e.g., `let id = builder.container_id;`).
- `builder.name` (property): Returns the name of the builder/working container.
- `builder.image` (property): Returns the base image name used by the builder.
- `builder.run(command: String)`: Executes a shell command inside the `builder`'s working container.
- `builder.run_with_isolation(command: String, isolation: String)`: Runs a command with specified isolation (e.g., "chroot").
- `builder.copy(source_on_host: String, dest_in_container: String)`: Copies files/directories from the host to the `builder`'s container.
- `builder.add(source_on_host: String, dest_in_container: String)`: Adds files/directories to the `builder`'s container (can handle URLs and auto-extract archives).
- `builder.config(options: Map)`: Modifies image metadata. `options` is a Rhai map, e.g., `#{ "env": "MYVAR=value", "label": "foo=bar" }`.
- `builder.set_entrypoint(entrypoint: String)`: Sets the image entrypoint (e.g., `builder.set_entrypoint("[/app/run.sh]")`).
- `builder.set_cmd(cmd: String)`: Sets the default command for the image (e.g., `builder.set_cmd("[--help]")`).
- `builder.commit(image_tag: String)`: Commits the current state of the `builder`'s working container to a new image with `image_tag`.
### Content Operations (with a Builder instance)
- `bah_write_content(builder: BuildahBuilder, content: String, dest_path_in_container: String)`: Writes string `content` to a file at `dest_path_in_container` inside the `builder`'s container.
- `bah_read_content(builder: BuildahBuilder, source_path_in_container: String) -> String`: Reads the content of a file from `source_path_in_container` within the `builder`'s container.
### Global Image Operations
These functions generally correspond to static methods in Rust and operate on the local Buildah image storage.
- `bah_images() -> Array`: Lists all images available locally. Returns an array of `BuildahImage` objects.
- `bah_image_remove(image_ref: String)`: Removes an image (e.g., by ID or tag) from local storage.
- `bah_image_pull(image_name: String, tls_verify: bool)`: Pulls an image from a registry.
- `bah_image_push(image_ref: String, destination: String, tls_verify: bool)`: Pushes a local image to a registry.
- `bah_image_tag(image_ref: String, new_name: String)`: Adds a new tag (`new_name`) to an existing image (`image_ref`).
- `bah_build(tag: String, context_dir: String, file: String, isolation: String)`: Builds an image from a Dockerfile/Containerfile (equivalent to `buildah bud`). `file` is the path to the Dockerfile relative to `context_dir`. `isolation` can be e.g., "chroot".
### Example `herodo` Rhai Script (Revisited)
```rhai
// Create a new builder
let builder = bah_new("my-rhai-app", "docker.io/library/alpine:latest");
builder.debug_mode = true; // Enable debug logging for this builder
// Run commands in the container
builder.run("apk add --no-cache figlet curl");
builder.run("mkdir /data");
// Write content to a file in the container
bah_write_content(builder, "Hello from SAL Buildah via Rhai!", "/data/message.txt");
// Configure image metadata
builder.config(#{
"env": "APP_VERSION=1.0",
"label": "author=HerodoUser"
});
builder.set_entrypoint('["figlet"]');
builder.set_cmd('["Rhai Build"]');
// Commit the image
let image_name = "localhost/my-rhai-app:v1";
builder.commit(image_name);
print(`Image committed: ${image_name}`);
// Clean up the working container
builder.remove();
print("Builder container removed.");
// List local images
print("Current local images:");
let images = bah_images();
for img in images {
print(` ID: ${img.id}, Name(s): ${img.names}, Size: ${img.size}`);
}
// Example: Build from a Dockerfile (assuming Dockerfile exists at /tmp/build_context/Dockerfile)
// Ensure /tmp/build_context/Dockerfile exists with simple content like:
// FROM alpine
// RUN echo "Built with bah_build" > /built.txt
// CMD cat /built.txt
//
// if exist("/tmp/build_context/Dockerfile") {
// print("Building from Dockerfile...");
// bah_build("localhost/from-dockerfile:latest", "/tmp/build_context", "Dockerfile", "chroot");
// print("Dockerfile build complete.");
// bah_image_remove("localhost/from-dockerfile:latest"); // Clean up
// } else {
// print("Skipping Dockerfile build example: /tmp/build_context/Dockerfile not found.");
// }
```
This README provides a guide to using the SAL Buildah module. For more detailed information on specific functions and their parameters, consult the Rust doc comments within the source code.