diff --git a/example.conf b/example.conf new file mode 100644 index 0000000..d3cb182 --- /dev/null +++ b/example.conf @@ -0,0 +1 @@ +EXAMPLE FILE TO TEST \ No newline at end of file diff --git a/example_Dockerfile b/example_Dockerfile new file mode 100644 index 0000000..97fd0e7 --- /dev/null +++ b/example_Dockerfile @@ -0,0 +1,8 @@ +# syntax=docker/dockerfile:1 + +FROM node:lts-alpine +WORKDIR /app +COPY . . +RUN yarn install --production +CMD ["node", "src/index.js"] +EXPOSE 3000 \ No newline at end of file diff --git a/examples/buildah.rs b/examples/buildah.rs index b89b6f9..b1de7a0 100644 --- a/examples/buildah.rs +++ b/examples/buildah.rs @@ -20,51 +20,47 @@ pub fn run_buildah_example() -> Result<(), BuildahError> { // Step 2: Run a command in the container println!("\n=== Installing nginx in container ==="); // Use chroot isolation to avoid BPF issues - let install_result = buildah::run_with_isolation(container_id, "dnf install -y nginx", "chroot").unwrap(); + let install_result = buildah::run_with_isolation(container_id, "dnf install -y nginx", "chroot")?; println!("{:#?}", install_result); println!("Installation output: {}", install_result.stdout); - // // Step 3: Copy a file into the container - // println!("\n=== Copying configuration file to container ==="); - // // Note: This would require an actual file to exist - // buildah::copy(container_id, "./example.conf", "/etc/example.conf")?; - // println!("For a real example, you would copy a configuration file here"); + // Step 3: Copy a file into the container + println!("\n=== Copying configuration file to container ==="); + buildah::copy(container_id, "./example.conf", "/etc/example.conf").unwrap(); - // // Step 4: Configure container metadata - // println!("\n=== Configuring container metadata ==="); - // let mut config_options = HashMap::new(); - // config_options.insert("port".to_string(), "80".to_string()); - // config_options.insert("label".to_string(), "maintainer=example@example.com".to_string()); - // config_options.insert("entrypoint".to_string(), "/usr/sbin/nginx".to_string()); + // Step 4: Configure container metadata + println!("\n=== Configuring container metadata ==="); + let mut config_options = HashMap::new(); + config_options.insert("port".to_string(), "80".to_string()); + config_options.insert("label".to_string(), "maintainer=example@example.com".to_string()); + config_options.insert("entrypoint".to_string(), "/usr/sbin/nginx".to_string()); - // buildah::config(container_id, config_options)?; - // println!("Container configured"); + buildah::config(container_id, config_options)?; + println!("Container configured"); - // // Step 5: Commit the container to create a new image - // println!("\n=== Committing container to create image ==="); - // let image_name = "my-nginx:latest"; - // buildah::image_commit(container_id, image_name, Some("docker"), true, true)?; - // println!("Created image: {}", image_name); + // Step 5: Commit the container to create a new image + println!("\n=== Committing container to create image ==="); + let image_name = "my-nginx:latest"; + buildah::image_commit(container_id, image_name, Some("docker"), true, true)?; + println!("Created image: {}", image_name); - // // Step 6: List images to verify our new image exists - // println!("\n=== Listing images ==="); - // let images = buildah::images()?; - // println!("Found {} images:", images.len()); - // for image in images { - // println!(" ID: {}", image.id); - // println!(" Names: {}", image.names.join(", ")); - // println!(" Size: {}", image.size); - // println!(" Created: {}", image.created); - // println!(); - // } + // Step 6: List images to verify our new image exists + println!("\n=== Listing images ==="); + let images = buildah::images()?; + println!("Found {} images:", images.len()); + for image in images { + println!(" ID: {}", image.id); + println!(" Names: {}", image.names.join(", ")); + println!(" Size: {}", image.size); + println!(" Created: {}", image.created); + println!(); + } // // Step 7: Clean up (optional in a real workflow) - // println!("\n=== Cleaning up ==="); - // println!("In a real workflow, you might want to keep the image"); - // println!("To remove the image, you would run:"); - // println!("buildah::image_remove(\"{}\")", image_name); + println!("\n=== Cleaning up ==="); + buildah::image_remove(image_name).unwrap(); - // println!("\nBuildah example workflow completed successfully!"); + println!("\nBuildah example workflow completed successfully!"); Ok(()) } @@ -72,12 +68,8 @@ pub fn run_buildah_example() -> Result<(), BuildahError> { pub fn build_image_example() -> Result<(), BuildahError> { println!("Building an image from a Containerfile..."); - // This would typically use a command like: - // buildah build -t my-app:latest . - - // For our example, we'll just show the command structure - let build_args = &["build", "-t", "my-app:latest", "."]; - let result = buildah::execute_buildah_command(build_args)?; + // Use the build function with tag, context directory, and isolation to avoid BPF issues + let result = buildah::build(Some("my-app:latest"), ".", "example_Dockerfile", Some("chroot"))?; println!("Build output: {}", result.stdout); println!("Image built successfully!"); @@ -100,9 +92,9 @@ pub fn registry_operations_example() -> Result<(), BuildahError> { println!("Image tagged successfully"); // Push an image (this would typically go to a real registry) - println!("\n=== Pushing an image (example only) ==="); - println!("In a real scenario, you would push to a registry with:"); - println!("buildah::image_push(\"my-alpine:v1.0\", \"docker://registry.example.com/my-alpine:v1.0\", true)"); + // println!("\n=== Pushing an image (example only) ==="); + // println!("In a real scenario, you would push to a registry with:"); + // println!("buildah::image_push(\"my-alpine:v1.0\", \"docker://registry.example.com/my-alpine:v1.0\", true)"); Ok(()) } @@ -111,21 +103,13 @@ pub fn registry_operations_example() -> Result<(), BuildahError> { pub fn run_all_examples() -> Result<(), BuildahError> { println!("=== BUILDAH MODULE EXAMPLES ===\n"); - // Note: In a real application, you might want to run these - // examples individually or have proper error handling - - // Uncomment these to run the examples run_buildah_example()?; - // build_image_example()?; - // registry_operations_example()?; - - println!("\nTo run these examples, uncomment the function calls in run_all_examples()"); - println!("Note that these examples require buildah to be installed on your system"); - println!("and may require root/sudo privileges depending on your setup."); - + build_image_example()?; + registry_operations_example()?; + Ok(()) } fn main() { - run_all_examples(); + let _ = run_all_examples().unwrap(); } \ No newline at end of file diff --git a/examples/nerdctl.rs b/examples/nerdctl.rs new file mode 100644 index 0000000..15a8833 --- /dev/null +++ b/examples/nerdctl.rs @@ -0,0 +1,82 @@ +//! Example usage of the nerdctl module +//! +//! This file demonstrates how to use the nerdctl module to perform +//! common container operations like creating containers, running commands, +//! and managing images. + +use sal::virt::nerdctl::{self, NerdctlError}; + +/// Run a complete nerdctl workflow example +pub fn run_nerdctl_example() -> Result<(), NerdctlError> { + println!("Starting nerdctl example workflow..."); + + // Step 1: Pull an image + println!("\n=== Pulling nginx:latest image ==="); + let pull_result = nerdctl::image_pull("nginx:latest")?; + println!("Pull output: {}", pull_result.stdout); + + // Step 2: Create a container from the image + println!("\n=== Creating container from nginx:latest ==="); + let run_result = nerdctl::run("nginx:latest", Some("my-nginx"), true, Some(&["8080:80"]))?; + println!("Container created: {}", run_result.stdout.trim()); + let container_id = "my-nginx"; // Using the name we specified + + // Step 3: Execute a command in the container + println!("\n=== Installing curl in container ==="); + let update_result = nerdctl::exec(container_id, "apt-get update")?; + println!("Update output: {}", update_result.stdout); + + let install_result = nerdctl::exec(container_id, "apt-get install -y curl")?; + println!("Installation output: {}", install_result.stdout); + + // Step 4: Copy a file into the container (assuming nginx.conf exists) + println!("\n=== Copying configuration file to container ==="); + nerdctl::copy("./nginx.conf", format!("{}:/etc/nginx/nginx.conf", container_id).as_str())?; + + // Step 5: Commit the container to create a new image + println!("\n=== Committing container to create image ==="); + let image_name = "my-custom-nginx:latest"; + nerdctl::image_commit(container_id, image_name)?; + println!("Created image: {}", image_name); + + // Step 6: Stop and remove the container + println!("\n=== Stopping and removing container ==="); + nerdctl::stop(container_id)?; + nerdctl::remove(container_id)?; + println!("Container stopped and removed"); + + // Step 7: Create a new container from our custom image + println!("\n=== Creating container from custom image ==="); + nerdctl::run(image_name, Some("nginx-custom"), true, Some(&["8081:80"]))?; + println!("Custom container created"); + + // Step 8: List images + println!("\n=== Listing images ==="); + let images_result = nerdctl::images()?; + println!("Images: \n{}", images_result.stdout); + + // Step 9: Clean up (optional in a real workflow) + println!("\n=== Cleaning up ==="); + nerdctl::stop("nginx-custom")?; + nerdctl::remove("nginx-custom")?; + nerdctl::image_remove(image_name)?; + + println!("\nNerdctl example workflow completed successfully!"); + Ok(()) +} + +/// Main function to run all examples +pub fn run_all_examples() -> Result<(), NerdctlError> { + println!("=== NERDCTL MODULE EXAMPLES ===\n"); + + run_nerdctl_example()?; + + println!("\nNote that these examples require nerdctl to be installed on your system"); + println!("and may require root/sudo privileges depending on your setup."); + + Ok(()) +} + +fn main() { + run_all_examples(); +} \ No newline at end of file diff --git a/src/virt/buildah/containers.rs b/src/virt/buildah/containers.rs index ad9e4a4..ca4c6dc 100644 --- a/src/virt/buildah/containers.rs +++ b/src/virt/buildah/containers.rs @@ -52,3 +52,33 @@ pub fn remove(container: &str) -> Result { pub fn list() -> Result { execute_buildah_command(&["containers"]) } + +/// Build an image from a Containerfile/Dockerfile +/// +/// # Arguments +/// +/// * `tag` - Optional tag for the image (e.g., "my-app:latest") +/// * `context_dir` - The directory containing the Containerfile/Dockerfile (usually ".") +/// * `file` - Optional path to a specific Containerfile/Dockerfile +/// * `isolation` - Optional isolation method (e.g., "chroot", "rootless", "oci") +pub fn build(tag: Option<&str>, context_dir: &str, file: &str, isolation: Option<&str>) -> Result { + let mut args = Vec::new(); + args.push("build"); + + if let Some(tag_value) = tag { + args.push("-t"); + args.push(tag_value); + } + + if let Some(isolation_value) = isolation { + args.push("--isolation"); + args.push(isolation_value); + } + + args.push("-f"); + args.push(file); + + args.push(context_dir); + + execute_buildah_command(&args) +} diff --git a/src/virt/buildah/containers_test.rs b/src/virt/buildah/containers_test.rs index 7f2c741..b257e16 100644 --- a/src/virt/buildah/containers_test.rs +++ b/src/virt/buildah/containers_test.rs @@ -90,6 +90,25 @@ mod tests { fn test_list() -> Result { test_execute_buildah_command(&["containers"]) } + + fn test_build(tag: Option<&str>, context_dir: &str, file: Option<&str>) -> Result { + let mut args = Vec::new(); + args.push("build"); + + if let Some(tag_value) = tag { + args.push("-t"); + args.push(tag_value); + } + + if let Some(file_path) = file { + args.push("-f"); + args.push(file_path); + } + + args.push(context_dir); + + test_execute_buildah_command(&args) + } // Tests for each function #[test] @@ -188,6 +207,29 @@ mod tests { assert_eq!(cmd, vec!["containers"]); } + #[test] + fn test_build_function() { + reset_test_state(); + + // Test with tag and context directory + let result = test_build(Some("my-app:latest"), ".", None); + assert!(result.is_ok()); + let cmd = get_last_command(); + assert_eq!(cmd, vec!["build", "-t", "my-app:latest", "."]); + + // Test with tag, context directory, and file + let result = test_build(Some("my-app:latest"), ".", Some("Dockerfile.custom")); + assert!(result.is_ok()); + let cmd = get_last_command(); + assert_eq!(cmd, vec!["build", "-t", "my-app:latest", "-f", "Dockerfile.custom", "."]); + + // Test with just context directory + let result = test_build(None, ".", None); + assert!(result.is_ok()); + let cmd = get_last_command(); + assert_eq!(cmd, vec!["build", "."]); + } + #[test] fn test_error_handling() { reset_test_state(); diff --git a/src/virt/buildah/images.rs b/src/virt/buildah/images.rs index 0f1d89d..9082f7b 100644 --- a/src/virt/buildah/images.rs +++ b/src/virt/buildah/images.rs @@ -23,7 +23,7 @@ pub struct Image { /// # Returns /// * Result with array of Image objects on success or error details pub fn images() -> Result, BuildahError> { - let result = execute_buildah_command(&["images", "--format", "json"])?; + let result = execute_buildah_command(&["images", "--json"])?; // Try to parse the JSON output match serde_json::from_str::(&result.stdout) { diff --git a/src/virt/mod.rs b/src/virt/mod.rs index c61aaa0..382b57b 100644 --- a/src/virt/mod.rs +++ b/src/virt/mod.rs @@ -1 +1,2 @@ -pub mod buildah; \ No newline at end of file +pub mod buildah; +pub mod nerdctl; \ No newline at end of file