- Add PostgreSQL client functionality for database interactions. - Add Redis client functionality for cache and data store operations. - Extend Rhai scripting with PostgreSQL and Redis client modules. - Add documentation and test cases for both clients. |
||
---|---|---|
.. | ||
nerdctldocs | ||
cmd.rs | ||
container_builder.rs | ||
container_functions.rs | ||
container_operations.rs | ||
container_test.rs | ||
container_types.rs | ||
container.rs | ||
health_check_script.rs | ||
health_check.rs | ||
images.rs | ||
mod.rs | ||
nerdctl-essentials.md | ||
README.md |
Container API for nerdctl
This module provides a Rust API for managing containers using nerdctl, a Docker-compatible CLI for containerd.
Overview
The Container API is designed with a builder pattern to make it easy to create and manage containers. It provides a fluent interface for configuring container options and performing operations on containers.
Key Components
Container
: The main struct representing a containerHealthCheck
: Configuration for container health checksContainerStatus
: Information about a container's statusResourceUsage
: Information about a container's resource usage
Getting Started
Prerequisites
- nerdctl must be installed on your system
- containerd must be running
Basic Usage
Add the following to your Cargo.toml
:
[dependencies]
sal = { path = "/path/to/sal" }
Then import the Container API in your Rust code:
use sal::virt::nerdctl::Container;
Usage Examples
Getting a Container by Name
You can get a reference to an existing container by name:
use sal::virt::nerdctl::Container;
// Get a container by name (if it exists)
match Container::new("existing-container") {
Ok(container) => {
if container.container_id.is_some() {
println!("Found container with ID: {}", container.container_id.unwrap());
// Perform operations on the existing container
let status = container.status()?;
println!("Container status: {}", status.status);
} else {
println!("Container exists but has no ID");
}
},
Err(e) => {
println!("Error getting container: {}", e);
}
}
Creating a Container
You can create a new container from an image using the builder pattern:
use sal::virt::nerdctl::Container;
// Create a container from an image
let container = Container::from_image("my-nginx", "nginx:latest")?
.with_port("8080:80")
.with_env("NGINX_HOST", "example.com")
.with_volume("/tmp/nginx:/usr/share/nginx/html")
.with_health_check("curl -f http://localhost/ || exit 1")
.with_detach(true)
.build()?;
Container Operations
Once you have a container, you can perform various operations on it:
// Execute a command in the container
let result = container.exec("echo 'Hello from container'")?;
println!("Command output: {}", result.stdout);
// Get container status
let status = container.status()?;
println!("Container status: {}", status.status);
// Get resource usage
let resources = container.resources()?;
println!("CPU usage: {}", resources.cpu_usage);
println!("Memory usage: {}", resources.memory_usage);
// Stop and remove the container
container.stop()?;
container.remove()?;
Container Configuration Options
The Container API supports a wide range of configuration options through its builder pattern:
Ports
Map container ports to host ports:
// Map a single port
.with_port("8080:80")
// Map multiple ports
.with_ports(&["8080:80", "8443:443"])
Volumes
Mount host directories or volumes in the container:
// Mount a single volume
.with_volume("/host/path:/container/path")
// Mount multiple volumes
.with_volumes(&["/host/path1:/container/path1", "/host/path2:/container/path2"])
Environment Variables
Set environment variables in the container:
// Set a single environment variable
.with_env("KEY", "value")
// Set multiple environment variables
let mut env_map = HashMap::new();
env_map.insert("KEY1", "value1");
env_map.insert("KEY2", "value2");
.with_envs(&env_map)
Network Configuration
Configure container networking:
// Set the network
.with_network("bridge")
// Add a network alias
.with_network_alias("my-container")
// Add multiple network aliases
.with_network_aliases(&["alias1", "alias2"])
Resource Limits
Set CPU and memory limits:
// Set CPU limit (e.g., 0.5 for half a CPU, 2 for 2 CPUs)
.with_cpu_limit("0.5")
// Set memory limit (e.g., 512m for 512MB, 1g for 1GB)
.with_memory_limit("512m")
// Set memory swap limit
.with_memory_swap_limit("1g")
// Set CPU shares (relative weight)
.with_cpu_shares("1024")
Health Checks
Configure container health checks:
// Simple health check
.with_health_check("curl -f http://localhost/ || exit 1")
// Health check with custom options
.with_health_check_options(
"curl -f http://localhost/ || exit 1", // Command
Some("30s"), // Interval
Some("10s"), // Timeout
Some(3), // Retries
Some("5s") // Start period
)
Other Options
Other container configuration options:
// Set restart policy
.with_restart_policy("always") // Options: no, always, on-failure, unless-stopped
// Set snapshotter
.with_snapshotter("native") // Options: native, fuse-overlayfs, etc.
// Set detach mode
.with_detach(true) // Run in detached mode
Container Operations
Once a container is created, you can perform various operations on it:
Basic Operations
// Start the container
container.start()?;
// Stop the container
container.stop()?;
// Remove the container
container.remove()?;
Command Execution
// Execute a command in the container
let result = container.exec("echo 'Hello from container'")?;
println!("Command output: {}", result.stdout);
File Operations
// Copy files between the container and host
container.copy("container_name:/path/in/container", "/path/on/host")?;
container.copy("/path/on/host", "container_name:/path/in/container")?;
// Export the container to a tarball
container.export("/path/to/export.tar")?;
Image Operations
// Commit the container to an image
container.commit("my-custom-image:latest")?;
Status and Monitoring
// Get container status
let status = container.status()?;
println!("Container state: {}", status.state);
println!("Container status: {}", status.status);
println!("Created: {}", status.created);
println!("Started: {}", status.started);
// Get health status
let health_status = container.health_status()?;
println!("Health status: {}", health_status);
// Get resource usage
let resources = container.resources()?;
println!("CPU usage: {}", resources.cpu_usage);
println!("Memory usage: {}", resources.memory_usage);
println!("Memory limit: {}", resources.memory_limit);
println!("Memory percentage: {}", resources.memory_percentage);
println!("Network I/O: {} / {}", resources.network_input, resources.network_output);
println!("Block I/O: {} / {}", resources.block_input, resources.block_output);
println!("PIDs: {}", resources.pids);
Error Handling
The Container API uses a custom error type NerdctlError
that can be one of the following:
CommandExecutionFailed
: The nerdctl command failed to executeCommandFailed
: The nerdctl command executed but returned an errorJsonParseError
: Failed to parse JSON outputConversionError
: Failed to convert dataOther
: Generic error
Example error handling:
match Container::new("non-existent-container") {
Ok(container) => {
// Container exists
println!("Container found");
},
Err(e) => {
match e {
NerdctlError::CommandExecutionFailed(io_error) => {
println!("Failed to execute nerdctl command: {}", io_error);
},
NerdctlError::CommandFailed(error_msg) => {
println!("nerdctl command failed: {}", error_msg);
},
_ => {
println!("Other error: {}", e);
}
}
}
}
Implementation Details
The Container API is implemented in several modules:
container_types.rs
: Contains the struct definitionscontainer.rs
: Contains the main Container implementationcontainer_builder.rs
: Contains the builder pattern methodscontainer_operations.rs
: Contains the container operationshealth_check.rs
: Contains the HealthCheck implementation
This modular approach makes the code more maintainable and easier to understand.
Complete Example
Here's a complete example that demonstrates the Container API:
use std::error::Error;
use sal::virt::nerdctl::Container;
fn main() -> Result<(), Box<dyn Error>> {
// Create a container from an image
println!("Creating container from image...");
let container = Container::from_image("my-nginx", "nginx:latest")?
.with_port("8080:80")
.with_env("NGINX_HOST", "example.com")
.with_volume("/tmp/nginx:/usr/share/nginx/html")
.with_health_check("curl -f http://localhost/ || exit 1")
.with_detach(true)
.build()?;
println!("Container created successfully");
// Execute a command in the container
println!("Executing command in container...");
let result = container.exec("echo 'Hello from container'")?;
println!("Command output: {}", result.stdout);
// Get container status
println!("Getting container status...");
let status = container.status()?;
println!("Container status: {}", status.status);
// Get resource usage
println!("Getting resource usage...");
let resources = container.resources()?;
println!("CPU usage: {}", resources.cpu_usage);
println!("Memory usage: {}", resources.memory_usage);
// Stop and remove the container
println!("Stopping and removing container...");
container.stop()?;
container.remove()?;
println!("Container stopped and removed");
Ok(())
}