233 lines
15 KiB
Markdown
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.
|