diff --git a/src/docs/docs/sal/buildah.md b/src/docs/docs/sal/buildah.md index dcb124b..23919a3 100644 --- a/src/docs/docs/sal/buildah.md +++ b/src/docs/docs/sal/buildah.md @@ -4,7 +4,7 @@ sidebar_position: 20 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. diff --git a/src/docs/docs/sal/nerdctl.md b/src/docs/docs/sal/nerdctl.md new file mode 100644 index 0000000..ba112a6 --- /dev/null +++ b/src/docs/docs/sal/nerdctl.md @@ -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 = ` + + + + Rhai Nerdctl Demo + + + +

Hello from Rhai Nerdctl!

+

This page is served by an Nginx container created using the Rhai nerdctl wrapper.

+ + +`; + +// 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", "./")` | diff --git a/src/rhai/nerdctl.rs b/src/rhai/nerdctl.rs index 10861a5..50e81bb 100644 --- a/src/rhai/nerdctl.rs +++ b/src/rhai/nerdctl.rs @@ -50,6 +50,11 @@ pub fn container_from_image(name: &str, image: &str) -> Result Container { + container.reset() +} + /// Add a port mapping to a Container pub fn container_with_port(mut container: Container, port: &str) -> Container { container.with_port(port) @@ -476,6 +481,7 @@ pub fn register_nerdctl_module(engine: &mut Engine) -> Result<(), Box 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 /// /// # Arguments diff --git a/src/virt/nerdctl/container_operations.rs b/src/virt/nerdctl/container_operations.rs index 8de3ccf..084fc57 100644 --- a/src/virt/nerdctl/container_operations.rs +++ b/src/virt/nerdctl/container_operations.rs @@ -7,12 +7,54 @@ use serde_json; impl Container { /// Start the container and verify it's running + /// If the container hasn't been created yet, it will be created automatically. /// /// # Returns /// /// * `Result` - Command result or error with detailed information pub fn start(&self) -> Result { - 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 let start_result = execute_nerdctl_command(&["start", container_id]); @@ -24,14 +66,14 @@ impl Container { } // Verify the container is actually running - match self.verify_running() { + match container.verify_running() { Ok(true) => start_result, Ok(false) => { // Container started but isn't running - get detailed information let mut error_message = format!("Container {} started but is not running.", container_id); // Get container status - if let Ok(status) = self.status() { + if let Ok(status) = container.status() { error_message.push_str(&format!("\nStatus: {}, State: {}, Health: {}", status.status, status.state, @@ -69,7 +111,7 @@ impl Container { } } } else { - Err(NerdctlError::Other("No container ID available".to_string())) + Err(NerdctlError::Other("Failed to create container. No container ID available.".to_string())) } }