This commit is contained in:
despiegk 2025-04-05 07:40:32 +02:00
parent c177ac5efb
commit 4c50d4b62c
6 changed files with 285 additions and 6 deletions

View File

@ -4,7 +4,7 @@ sidebar_position: 20
hide_title: true hide_title: true
--- ---
# Buildah = container builder # Container Builder
The Buildah module provides functions for working with containers and images using the Buildah tool. Buildah helps you create and manage container images. The Buildah module provides functions for working with containers and images using the Buildah tool. Buildah helps you create and manage container images.

View File

@ -0,0 +1,198 @@
---
title: "run containers"
sidebar_position: 21
hide_title: true
---
# Container Manager
The Container Manager module provides a comprehensive API for working with containers using nerdctl. It offers a modern builder pattern approach for container management.
## Container Builder Pattern
The Container Builder Pattern allows for fluent, chainable configuration of containers. This pattern makes container creation more readable and maintainable by allowing you to build complex container configurations step by step.
### Creating a Container
Start by creating a new container instance:
```rhai
// Create an empty container with just a name
let container = nerdctl_container_new("my-container");
// Or create a container from an image
let container = nerdctl_container_from_image("my-container", "nginx:latest");
```
### Configuring the Container
Once you have a container instance, you can configure it using the various builder methods:
```rhai
// Configure the container with various options
let container = nerdctl_container_from_image("web-server", "nginx:latest")
.with_port("8080:80") // Map port 8080 to container port 80
.with_volume("/host/path:/container/path") // Mount a volume
.with_env("NGINX_HOST", "localhost") // Set an environment variable
.with_network("bridge") // Set the network
.with_detach(true); // Run in detached mode
```
### Resetting Container Configuration
If you need to reset the container configuration to its default state while keeping the name and image:
```rhai
// Reset the container configuration
let container = nerdctl_container_from_image("web-server", "nginx:latest")
.reset() // Reset all configuration to defaults
.with_port("8080:80") // Start configuring again
.with_detach(true);
```
### Building and Starting the Container
After configuring the container, you can build and start it:
```rhai
// Build the container (creates it but doesn't start it)
let built_container = container.build();
// Start the container
let start_result = built_container.start();
// Check if the container started successfully
if (start_result.success) {
println("Container started successfully!");
} else {
println(`Failed to start container: ${start_result.stderr}`);
}
```
### Container Lifecycle Operations
Once your container is running, you can perform various operations:
```rhai
// Execute a command in the container
let exec_result = container.exec("ls -la");
// Get container logs
let logs = container.logs();
// Stop the container
let stop_result = container.stop();
// Remove the container
let remove_result = container.remove();
```
## Available Builder Methods
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)` |
## Complete Example: Web Server
Here's a complete example that demonstrates setting up an Nginx web server using the Container Builder Pattern:
```rhai
// Create a temporary directory for our files
let work_dir = "/tmp/nerdctl";
mkdir(work_dir);
chdir(work_dir);
// Create a custom index.html file
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>
</body>
</html>
`;
// Write the HTML file
let html_file = `${work_dir}/index.html`;
file_write(html_file, html_content);
// Set up environment variables
let env_map = #{};
env_map["NGINX_HOST"] = "localhost";
env_map["NGINX_PORT"] = "80";
env_map["NGINX_WORKER_PROCESSES"] = "auto";
// Create and configure the container
let container_name = "rhai-nginx-demo";
// First, try to remove any existing container with the same name
nerdctl_remove(container_name);
// Create a container with a rich set of options using the builder pattern
let container = nerdctl_container_from_image(container_name, "nginx:latest")
.reset() // Reset to default configuration
.with_detach(true)
.with_ports(["8080:80"]) // Add multiple ports at once
.with_volumes([`${work_dir}:/usr/share/nginx/html`]) // Mount our work dir
.with_envs(env_map) // Add multiple environment variables at once
.with_network("bridge")
.with_network_aliases(["web-server", "nginx-demo"]) // Add multiple network aliases
.with_cpu_limit("1.0")
.with_memory_limit("512m");
// Build and start the container
let built_container = container.build();
let start_result = built_container.start();
println("The web server is running at http://localhost:8080");
```
## 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", "./")` |

View File

@ -50,6 +50,11 @@ pub fn container_from_image(name: &str, image: &str) -> Result<Container, Box<Ev
nerdctl_error_to_rhai_error(Container::from_image(name, image)) nerdctl_error_to_rhai_error(Container::from_image(name, image))
} }
/// Reset the container configuration to defaults while keeping the name and image
pub fn container_reset(mut container: Container) -> Container {
container.reset()
}
/// Add a port mapping to a Container /// Add a port mapping to a Container
pub fn container_with_port(mut container: Container, port: &str) -> Container { pub fn container_with_port(mut container: Container, port: &str) -> Container {
container.with_port(port) container.with_port(port)
@ -476,6 +481,7 @@ pub fn register_nerdctl_module(engine: &mut Engine) -> Result<(), Box<EvalAltRes
engine.register_fn("nerdctl_container_from_image", container_from_image); engine.register_fn("nerdctl_container_from_image", container_from_image);
// Register Container instance methods // Register Container instance methods
engine.register_fn("reset", container_reset);
engine.register_fn("with_port", container_with_port); engine.register_fn("with_port", container_with_port);
engine.register_fn("with_volume", container_with_volume); engine.register_fn("with_volume", container_with_volume);
engine.register_fn("with_env", container_with_env); engine.register_fn("with_env", container_with_env);

View File

@ -69,7 +69,7 @@ env_map["NGINX_PORT"] = "80";
env_map["NGINX_WORKER_PROCESSES"] = "auto"; env_map["NGINX_WORKER_PROCESSES"] = "auto";
// Create a container with a rich set of options using batch methods // Create a container with a rich set of options using batch methods
let container = _container_from_image(container_name, "nginx:latest") let container = nerdctl_container_from_image(container_name, "nginx:latest")
.reset() .reset()
.with_detach(true) .with_detach(true)
.with_ports(["8080:80"]) // Add multiple ports at once .with_ports(["8080:80"]) // Add multiple ports at once
@ -77,6 +77,7 @@ let container = _container_from_image(container_name, "nginx:latest")
.with_envs(env_map) // Add multiple environment variables at once .with_envs(env_map) // Add multiple environment variables at once
.with_cpu_limit("1.0") .with_cpu_limit("1.0")
.with_memory_limit("512m") .with_memory_limit("512m")
.start();
println("\n web server workflow completed successfully!"); println("\n web server workflow completed successfully!");

View File

@ -6,6 +6,38 @@ use super::container_types::{Container, HealthCheck};
use super::health_check_script::prepare_health_check_command; use super::health_check_script::prepare_health_check_command;
impl Container { impl Container {
/// Reset the container configuration to defaults while keeping the name and image
///
/// # Returns
///
/// * `Self` - The container instance for method chaining
pub fn reset(mut self) -> Self {
let name = self.name;
let image = self.image.clone();
let container_id = self.container_id.clone();
// Create a new container with just the name, image, and container_id
Self {
name,
container_id,
image,
config: std::collections::HashMap::new(),
ports: Vec::new(),
volumes: Vec::new(),
env_vars: std::collections::HashMap::new(),
network: None,
network_aliases: Vec::new(),
cpu_limit: None,
memory_limit: None,
memory_swap_limit: None,
cpu_shares: None,
restart_policy: None,
health_check: None,
detach: false,
snapshotter: None,
}
}
/// Add a port mapping /// Add a port mapping
/// ///
/// # Arguments /// # Arguments

View File

@ -7,12 +7,54 @@ use serde_json;
impl Container { impl Container {
/// Start the container and verify it's running /// Start the container and verify it's running
/// If the container hasn't been created yet, it will be created automatically.
/// ///
/// # Returns /// # Returns
/// ///
/// * `Result<CommandResult, NerdctlError>` - Command result or error with detailed information /// * `Result<CommandResult, NerdctlError>` - Command result or error with detailed information
pub fn start(&self) -> Result<CommandResult, NerdctlError> { pub fn start(&self) -> Result<CommandResult, NerdctlError> {
if let Some(container_id) = &self.container_id { // If container_id is None, we need to create the container first
let container = if self.container_id.is_none() {
// Check if we have an image specified
if self.image.is_none() {
return Err(NerdctlError::Other("No image specified for container creation".to_string()));
}
// Clone self and create the container
println!("Container not created yet. Creating container from image...");
// First, try to pull the image if it doesn't exist locally
let image = self.image.as_ref().unwrap();
match execute_nerdctl_command(&["image", "inspect", image]) {
Err(_) => {
println!("Image '{}' not found locally. Pulling image...", image);
if let Err(e) = execute_nerdctl_command(&["pull", image]) {
return Err(NerdctlError::CommandFailed(
format!("Failed to pull image '{}': {}", image, e)
));
}
println!("Image '{}' pulled successfully.", image);
},
Ok(_) => {
println!("Image '{}' found locally.", image);
}
}
// Now create the container
match self.clone().build() {
Ok(built) => built,
Err(e) => {
return Err(NerdctlError::CommandFailed(
format!("Failed to create container from image '{}': {}", image, e)
));
}
}
} else {
// Container already has an ID, use it as is
self.clone()
};
if let Some(container_id) = &container.container_id {
// First, try to start the container // First, try to start the container
let start_result = execute_nerdctl_command(&["start", container_id]); let start_result = execute_nerdctl_command(&["start", container_id]);
@ -24,14 +66,14 @@ impl Container {
} }
// Verify the container is actually running // Verify the container is actually running
match self.verify_running() { match container.verify_running() {
Ok(true) => start_result, Ok(true) => start_result,
Ok(false) => { Ok(false) => {
// Container started but isn't running - get detailed information // Container started but isn't running - get detailed information
let mut error_message = format!("Container {} started but is not running.", container_id); let mut error_message = format!("Container {} started but is not running.", container_id);
// Get container status // Get container status
if let Ok(status) = self.status() { if let Ok(status) = container.status() {
error_message.push_str(&format!("\nStatus: {}, State: {}, Health: {}", error_message.push_str(&format!("\nStatus: {}, State: {}, Health: {}",
status.status, status.status,
status.state, status.state,
@ -69,7 +111,7 @@ impl Container {
} }
} }
} else { } else {
Err(NerdctlError::Other("No container ID available".to_string())) Err(NerdctlError::Other("Failed to create container. No container ID available.".to_string()))
} }
} }