Compare commits
8 Commits
a32cfb788b
...
dc49e78d00
Author | SHA1 | Date | |
---|---|---|---|
dc49e78d00 | |||
119d7ee50e | |||
d6b53a72bb | |||
5f6420a421 | |||
d5bc0bbfc4 | |||
|
3f461334ec | ||
|
288f17453b | ||
|
d3e5df44bb |
16
.roo/mcp.json
Normal file
16
.roo/mcp.json
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
{
|
||||||
|
"mcpServers": {
|
||||||
|
"gitea": {
|
||||||
|
"command": "/Users/despiegk/hero/bin/mcpgitea",
|
||||||
|
"args": [
|
||||||
|
"-t", "stdio",
|
||||||
|
"--host", "https://gitea.com",
|
||||||
|
"--token", "5bd13c898368a2edbfcef43f898a34857b51b37a"
|
||||||
|
],
|
||||||
|
"env": {
|
||||||
|
"GITEA_HOST": "https://git.ourworld.tf/",
|
||||||
|
"GITEA_ACCESS_TOKEN": "5bd13c898368a2edbfcef43f898a34857b51b37a"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
1
example.conf
Normal file
1
example.conf
Normal file
@ -0,0 +1 @@
|
|||||||
|
EXAMPLE FILE TO TEST
|
8
example_Dockerfile
Normal file
8
example_Dockerfile
Normal file
@ -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
|
115
examples/buildah.rs
Normal file
115
examples/buildah.rs
Normal file
@ -0,0 +1,115 @@
|
|||||||
|
//! Example usage of the buildah module
|
||||||
|
//!
|
||||||
|
//! This file demonstrates how to use the buildah module to perform
|
||||||
|
//! common container operations like creating containers, running commands,
|
||||||
|
//! and managing images.
|
||||||
|
|
||||||
|
use sal::virt::buildah::{self, BuildahError};
|
||||||
|
use std::collections::HashMap;
|
||||||
|
|
||||||
|
/// Run a complete buildah workflow example
|
||||||
|
pub fn run_buildah_example() -> Result<(), BuildahError> {
|
||||||
|
println!("Starting buildah example workflow...");
|
||||||
|
|
||||||
|
// Step 1: Create a container from an image
|
||||||
|
println!("\n=== Creating container from fedora:latest ===");
|
||||||
|
let result = buildah::from("fedora:latest")?;
|
||||||
|
let container_id = result.stdout.trim();
|
||||||
|
println!("Created container: {}", container_id);
|
||||||
|
|
||||||
|
// 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")?;
|
||||||
|
println!("{:#?}", install_result);
|
||||||
|
println!("Installation output: {}", install_result.stdout);
|
||||||
|
|
||||||
|
// 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());
|
||||||
|
|
||||||
|
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 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 ===");
|
||||||
|
buildah::image_remove(image_name).unwrap();
|
||||||
|
|
||||||
|
println!("\nBuildah example workflow completed successfully!");
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Demonstrate how to build an image from a Containerfile/Dockerfile
|
||||||
|
pub fn build_image_example() -> Result<(), BuildahError> {
|
||||||
|
println!("Building an image from a Containerfile...");
|
||||||
|
|
||||||
|
// 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!");
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Example of pulling and pushing images
|
||||||
|
pub fn registry_operations_example() -> Result<(), BuildahError> {
|
||||||
|
println!("Demonstrating registry operations...");
|
||||||
|
|
||||||
|
// Pull an image
|
||||||
|
println!("\n=== Pulling an image ===");
|
||||||
|
buildah::image_pull("docker.io/library/alpine:latest", true)?;
|
||||||
|
println!("Image pulled successfully");
|
||||||
|
|
||||||
|
// Tag the image
|
||||||
|
println!("\n=== Tagging the image ===");
|
||||||
|
buildah::image_tag("alpine:latest", "my-alpine:v1.0")?;
|
||||||
|
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)");
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Main function to run all examples
|
||||||
|
pub fn run_all_examples() -> Result<(), BuildahError> {
|
||||||
|
println!("=== BUILDAH MODULE EXAMPLES ===\n");
|
||||||
|
|
||||||
|
run_buildah_example()?;
|
||||||
|
build_image_example()?;
|
||||||
|
registry_operations_example()?;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
let _ = run_all_examples().unwrap();
|
||||||
|
}
|
84
examples/nerdctl.rs
Normal file
84
examples/nerdctl.rs
Normal file
@ -0,0 +1,84 @@
|
|||||||
|
//! 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 ===");
|
||||||
|
// Use "native" snapshotter to avoid overlay mount issues
|
||||||
|
let run_result = nerdctl::run("nginx:latest", Some("my-nginx"), true, Some(&["8080:80"]), Some("native"))?;
|
||||||
|
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 ===");
|
||||||
|
// Use "native" snapshotter to avoid overlay mount issues
|
||||||
|
nerdctl::run(image_name, Some("nginx-custom"), true, Some(&["8081:80"]), Some("native"))?;
|
||||||
|
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() {
|
||||||
|
let _ = run_all_examples().unwrap();
|
||||||
|
}
|
@ -80,7 +80,7 @@ impl Error for GitError {
|
|||||||
*/
|
*/
|
||||||
pub fn git_clone(url: &str) -> Result<String, GitError> {
|
pub fn git_clone(url: &str) -> Result<String, GitError> {
|
||||||
// Check if git is installed
|
// Check if git is installed
|
||||||
let git_check = Command::new("git")
|
let _git_check = Command::new("git")
|
||||||
.arg("--version")
|
.arg("--version")
|
||||||
.output()
|
.output()
|
||||||
.map_err(GitError::GitNotInstalled)?;
|
.map_err(GitError::GitNotInstalled)?;
|
||||||
|
@ -293,17 +293,6 @@ impl GitExecutor {
|
|||||||
|
|
||||||
// Execute git command with username/password
|
// Execute git command with username/password
|
||||||
fn execute_with_credentials(&self, args: &[&str], username: &str, password: &str) -> Result<Output, GitExecutorError> {
|
fn execute_with_credentials(&self, args: &[&str], username: &str, password: &str) -> Result<Output, GitExecutorError> {
|
||||||
// Helper method to execute a command and handle the result
|
|
||||||
fn execute_command(command: &mut Command) -> Result<Output, GitExecutorError> {
|
|
||||||
let output = command.output()?;
|
|
||||||
|
|
||||||
if output.status.success() {
|
|
||||||
Ok(output)
|
|
||||||
} else {
|
|
||||||
let error = String::from_utf8_lossy(&output.stderr);
|
|
||||||
Err(GitExecutorError::GitCommandFailed(error.to_string()))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// For HTTPS authentication, we need to modify the URL to include credentials
|
// For HTTPS authentication, we need to modify the URL to include credentials
|
||||||
// Create a new vector to hold our modified arguments
|
// Create a new vector to hold our modified arguments
|
||||||
let modified_args: Vec<String> = args.iter().map(|&arg| {
|
let modified_args: Vec<String> = args.iter().map(|&arg| {
|
||||||
|
@ -1,4 +1,3 @@
|
|||||||
|
|
||||||
mod git;
|
mod git;
|
||||||
mod git_executor;
|
mod git_executor;
|
||||||
|
|
||||||
|
@ -41,6 +41,7 @@ pub mod git;
|
|||||||
pub mod os;
|
pub mod os;
|
||||||
pub mod redisclient;
|
pub mod redisclient;
|
||||||
pub mod text;
|
pub mod text;
|
||||||
|
pub mod virt;
|
||||||
|
|
||||||
// Version information
|
// Version information
|
||||||
/// Returns the version of the SAL library
|
/// Returns the version of the SAL library
|
||||||
|
@ -1,4 +0,0 @@
|
|||||||
pub mod os;
|
|
||||||
pub mod env;
|
|
||||||
pub mod text;
|
|
||||||
pub mod git;
|
|
@ -76,7 +76,7 @@ pub struct CommandResult {
|
|||||||
|
|
||||||
impl CommandResult {
|
impl CommandResult {
|
||||||
/// Create a default failed result with an error message
|
/// Create a default failed result with an error message
|
||||||
fn error(message: &str) -> Self {
|
fn _error(message: &str) -> Self {
|
||||||
Self {
|
Self {
|
||||||
stdout: String::new(),
|
stdout: String::new(),
|
||||||
stderr: message.to_string(),
|
stderr: message.to_string(),
|
||||||
@ -132,10 +132,6 @@ fn handle_child_output(mut child: Child, silent: bool) -> Result<CommandResult,
|
|||||||
let stdout = child.stdout.take();
|
let stdout = child.stdout.take();
|
||||||
let stderr = child.stderr.take();
|
let stderr = child.stderr.take();
|
||||||
|
|
||||||
// Buffers for captured output
|
|
||||||
let mut captured_stdout = String::new();
|
|
||||||
let mut captured_stderr = String::new();
|
|
||||||
|
|
||||||
// Process stdout
|
// Process stdout
|
||||||
let stdout_handle = if let Some(out) = stdout {
|
let stdout_handle = if let Some(out) = stdout {
|
||||||
let reader = BufReader::new(out);
|
let reader = BufReader::new(out);
|
||||||
@ -191,18 +187,18 @@ fn handle_child_output(mut child: Child, silent: bool) -> Result<CommandResult,
|
|||||||
.map_err(|e| RunError::ChildProcessError(format!("Failed to wait on child process: {}", e)))?;
|
.map_err(|e| RunError::ChildProcessError(format!("Failed to wait on child process: {}", e)))?;
|
||||||
|
|
||||||
// Join our stdout thread if it exists
|
// Join our stdout thread if it exists
|
||||||
if let Some(handle) = stdout_handle {
|
let captured_stdout = if let Some(handle) = stdout_handle {
|
||||||
captured_stdout = handle.join().unwrap_or_default();
|
handle.join().unwrap_or_default()
|
||||||
} else {
|
} else {
|
||||||
captured_stdout = "Failed to capture stdout".to_string();
|
"Failed to capture stdout".to_string()
|
||||||
}
|
};
|
||||||
|
|
||||||
// Join our stderr thread if it exists
|
// Join our stderr thread if it exists
|
||||||
if let Some(handle) = stderr_handle {
|
let captured_stderr = if let Some(handle) = stderr_handle {
|
||||||
captured_stderr = handle.join().unwrap_or_default();
|
handle.join().unwrap_or_default()
|
||||||
} else {
|
} else {
|
||||||
captured_stderr = "Failed to capture stderr".to_string();
|
"Failed to capture stderr".to_string()
|
||||||
}
|
};
|
||||||
|
|
||||||
// Return the command result
|
// Return the command result
|
||||||
Ok(CommandResult {
|
Ok(CommandResult {
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
use redis::{Client, Connection, Commands, RedisError, RedisResult, Cmd};
|
use redis::{Client, Connection, RedisError, RedisResult, Cmd};
|
||||||
use std::env;
|
use std::env;
|
||||||
use std::path::Path;
|
use std::path::Path;
|
||||||
use std::sync::{Arc, Mutex, Once};
|
use std::sync::{Arc, Mutex, Once};
|
||||||
|
@ -8,10 +8,26 @@ pub fn from(image: &str) -> Result<CommandResult, BuildahError> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Run a command in a container
|
/// Run a command in a container
|
||||||
|
///
|
||||||
|
/// # Arguments
|
||||||
|
///
|
||||||
|
/// * `container` - The container ID or name
|
||||||
|
/// * `command` - The command to run
|
||||||
pub fn run(container: &str, command: &str) -> Result<CommandResult, BuildahError> {
|
pub fn run(container: &str, command: &str) -> Result<CommandResult, BuildahError> {
|
||||||
execute_buildah_command(&["run", container, "sh", "-c", command])
|
execute_buildah_command(&["run", container, "sh", "-c", command])
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Run a command in a container with specified isolation
|
||||||
|
///
|
||||||
|
/// # Arguments
|
||||||
|
///
|
||||||
|
/// * `container` - The container ID or name
|
||||||
|
/// * `command` - The command to run
|
||||||
|
/// * `isolation` - Isolation method (e.g., "chroot", "rootless", "oci")
|
||||||
|
pub fn run_with_isolation(container: &str, command: &str, isolation: &str) -> Result<CommandResult, BuildahError> {
|
||||||
|
execute_buildah_command(&["run", "--isolation", isolation, container, "sh", "-c", command])
|
||||||
|
}
|
||||||
|
|
||||||
/// Copy files into a container
|
/// Copy files into a container
|
||||||
pub fn copy(container: &str, source: &str, dest: &str) -> Result<CommandResult, BuildahError> {
|
pub fn copy(container: &str, source: &str, dest: &str) -> Result<CommandResult, BuildahError> {
|
||||||
execute_buildah_command(&["copy", container, source, dest])
|
execute_buildah_command(&["copy", container, source, dest])
|
||||||
@ -36,3 +52,33 @@ pub fn remove(container: &str) -> Result<CommandResult, BuildahError> {
|
|||||||
pub fn list() -> Result<CommandResult, BuildahError> {
|
pub fn list() -> Result<CommandResult, BuildahError> {
|
||||||
execute_buildah_command(&["containers"])
|
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<CommandResult, BuildahError> {
|
||||||
|
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)
|
||||||
|
}
|
||||||
|
249
src/virt/buildah/containers_test.rs
Normal file
249
src/virt/buildah/containers_test.rs
Normal file
@ -0,0 +1,249 @@
|
|||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use crate::process::CommandResult;
|
||||||
|
use crate::virt::buildah::BuildahError;
|
||||||
|
use std::sync::Mutex;
|
||||||
|
use lazy_static::lazy_static;
|
||||||
|
|
||||||
|
// Create a test-specific implementation of the containers module functions
|
||||||
|
// that we can use to verify the correct arguments are passed
|
||||||
|
|
||||||
|
lazy_static! {
|
||||||
|
static ref LAST_COMMAND: Mutex<Vec<String>> = Mutex::new(Vec::new());
|
||||||
|
static ref SHOULD_FAIL: Mutex<bool> = Mutex::new(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn reset_test_state() {
|
||||||
|
let mut cmd = LAST_COMMAND.lock().unwrap();
|
||||||
|
cmd.clear();
|
||||||
|
let mut fail = SHOULD_FAIL.lock().unwrap();
|
||||||
|
*fail = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn set_should_fail(fail: bool) {
|
||||||
|
let mut should_fail = SHOULD_FAIL.lock().unwrap();
|
||||||
|
*should_fail = fail;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_last_command() -> Vec<String> {
|
||||||
|
let cmd = LAST_COMMAND.lock().unwrap();
|
||||||
|
cmd.clone()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Test-specific implementation of execute_buildah_command
|
||||||
|
fn test_execute_buildah_command(args: &[&str]) -> Result<CommandResult, BuildahError> {
|
||||||
|
// Record the command
|
||||||
|
{
|
||||||
|
let mut cmd = LAST_COMMAND.lock().unwrap();
|
||||||
|
cmd.clear();
|
||||||
|
for arg in args {
|
||||||
|
cmd.push(arg.to_string());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if we should fail
|
||||||
|
let should_fail = {
|
||||||
|
let fail = SHOULD_FAIL.lock().unwrap();
|
||||||
|
*fail
|
||||||
|
};
|
||||||
|
|
||||||
|
if should_fail {
|
||||||
|
Err(BuildahError::CommandFailed("Command failed".to_string()))
|
||||||
|
} else {
|
||||||
|
Ok(CommandResult {
|
||||||
|
stdout: "mock stdout".to_string(),
|
||||||
|
stderr: "".to_string(),
|
||||||
|
success: true,
|
||||||
|
code: 0,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Test implementations of the container functions
|
||||||
|
fn test_from(image: &str) -> Result<CommandResult, BuildahError> {
|
||||||
|
test_execute_buildah_command(&["from", image])
|
||||||
|
}
|
||||||
|
|
||||||
|
fn test_run(container: &str, command: &str, isolation: Option<&str>) -> Result<CommandResult, BuildahError> {
|
||||||
|
match isolation {
|
||||||
|
Some(iso) => test_execute_buildah_command(&["run", "--isolation", iso, container, "sh", "-c", command]),
|
||||||
|
None => test_execute_buildah_command(&["run", container, "sh", "-c", command])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn test_copy(container: &str, source: &str, dest: &str) -> Result<CommandResult, BuildahError> {
|
||||||
|
test_execute_buildah_command(&["copy", container, source, dest])
|
||||||
|
}
|
||||||
|
|
||||||
|
fn test_add(container: &str, source: &str, dest: &str) -> Result<CommandResult, BuildahError> {
|
||||||
|
test_execute_buildah_command(&["add", container, source, dest])
|
||||||
|
}
|
||||||
|
|
||||||
|
fn test_commit(container: &str, image_name: &str) -> Result<CommandResult, BuildahError> {
|
||||||
|
test_execute_buildah_command(&["commit", container, image_name])
|
||||||
|
}
|
||||||
|
|
||||||
|
fn test_remove(container: &str) -> Result<CommandResult, BuildahError> {
|
||||||
|
test_execute_buildah_command(&["rm", container])
|
||||||
|
}
|
||||||
|
|
||||||
|
fn test_list() -> Result<CommandResult, BuildahError> {
|
||||||
|
test_execute_buildah_command(&["containers"])
|
||||||
|
}
|
||||||
|
|
||||||
|
fn test_build(tag: Option<&str>, context_dir: &str, file: Option<&str>) -> Result<CommandResult, BuildahError> {
|
||||||
|
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]
|
||||||
|
fn test_from_function() {
|
||||||
|
reset_test_state();
|
||||||
|
|
||||||
|
let image = "alpine:latest";
|
||||||
|
let result = test_from(image);
|
||||||
|
|
||||||
|
assert!(result.is_ok());
|
||||||
|
let cmd = get_last_command();
|
||||||
|
assert_eq!(cmd, vec!["from", "alpine:latest"]);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_run_function() {
|
||||||
|
reset_test_state();
|
||||||
|
|
||||||
|
let container = "my-container";
|
||||||
|
let command = "echo hello";
|
||||||
|
|
||||||
|
// Test without isolation
|
||||||
|
let result = test_run(container, command, None);
|
||||||
|
assert!(result.is_ok());
|
||||||
|
let cmd = get_last_command();
|
||||||
|
assert_eq!(cmd, vec!["run", "my-container", "sh", "-c", "echo hello"]);
|
||||||
|
|
||||||
|
// Test with isolation
|
||||||
|
let result = test_run(container, command, Some("chroot"));
|
||||||
|
assert!(result.is_ok());
|
||||||
|
let cmd = get_last_command();
|
||||||
|
assert_eq!(cmd, vec!["run", "--isolation", "chroot", "my-container", "sh", "-c", "echo hello"]);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_copy_function() {
|
||||||
|
reset_test_state();
|
||||||
|
|
||||||
|
let container = "my-container";
|
||||||
|
let source = "/local/path";
|
||||||
|
let dest = "/container/path";
|
||||||
|
let result = test_copy(container, source, dest);
|
||||||
|
|
||||||
|
assert!(result.is_ok());
|
||||||
|
let cmd = get_last_command();
|
||||||
|
assert_eq!(cmd, vec!["copy", "my-container", "/local/path", "/container/path"]);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_add_function() {
|
||||||
|
reset_test_state();
|
||||||
|
|
||||||
|
let container = "my-container";
|
||||||
|
let source = "/local/path";
|
||||||
|
let dest = "/container/path";
|
||||||
|
let result = test_add(container, source, dest);
|
||||||
|
|
||||||
|
assert!(result.is_ok());
|
||||||
|
let cmd = get_last_command();
|
||||||
|
assert_eq!(cmd, vec!["add", "my-container", "/local/path", "/container/path"]);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_commit_function() {
|
||||||
|
reset_test_state();
|
||||||
|
|
||||||
|
let container = "my-container";
|
||||||
|
let image_name = "my-image:latest";
|
||||||
|
let result = test_commit(container, image_name);
|
||||||
|
|
||||||
|
assert!(result.is_ok());
|
||||||
|
let cmd = get_last_command();
|
||||||
|
assert_eq!(cmd, vec!["commit", "my-container", "my-image:latest"]);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_remove_function() {
|
||||||
|
reset_test_state();
|
||||||
|
|
||||||
|
let container = "my-container";
|
||||||
|
let result = test_remove(container);
|
||||||
|
|
||||||
|
assert!(result.is_ok());
|
||||||
|
let cmd = get_last_command();
|
||||||
|
assert_eq!(cmd, vec!["rm", "my-container"]);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_list_function() {
|
||||||
|
reset_test_state();
|
||||||
|
|
||||||
|
let result = test_list();
|
||||||
|
|
||||||
|
assert!(result.is_ok());
|
||||||
|
let cmd = get_last_command();
|
||||||
|
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();
|
||||||
|
set_should_fail(true);
|
||||||
|
|
||||||
|
let image = "alpine:latest";
|
||||||
|
let result = test_from(image);
|
||||||
|
|
||||||
|
assert!(result.is_err());
|
||||||
|
match result {
|
||||||
|
Err(BuildahError::CommandFailed(msg)) => {
|
||||||
|
assert_eq!(msg, "Command failed");
|
||||||
|
},
|
||||||
|
_ => panic!("Expected CommandFailed error"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -1,4 +1,3 @@
|
|||||||
use std::process::Command;
|
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
use crate::virt::buildah::execute_buildah_command;
|
use crate::virt::buildah::execute_buildah_command;
|
||||||
use crate::process::CommandResult;
|
use crate::process::CommandResult;
|
||||||
@ -24,7 +23,7 @@ pub struct Image {
|
|||||||
/// # Returns
|
/// # Returns
|
||||||
/// * Result with array of Image objects on success or error details
|
/// * Result with array of Image objects on success or error details
|
||||||
pub fn images() -> Result<Vec<Image>, BuildahError> {
|
pub fn images() -> Result<Vec<Image>, BuildahError> {
|
||||||
let result = execute_buildah_command(&["images", "--format", "json"])?;
|
let result = execute_buildah_command(&["images", "--json"])?;
|
||||||
|
|
||||||
// Try to parse the JSON output
|
// Try to parse the JSON output
|
||||||
match serde_json::from_str::<serde_json::Value>(&result.stdout) {
|
match serde_json::from_str::<serde_json::Value>(&result.stdout) {
|
||||||
|
@ -1,6 +1,8 @@
|
|||||||
mod containers;
|
mod containers;
|
||||||
mod images;
|
mod images;
|
||||||
mod cmd;
|
mod cmd;
|
||||||
|
#[cfg(test)]
|
||||||
|
mod containers_test;
|
||||||
|
|
||||||
use std::fmt;
|
use std::fmt;
|
||||||
use std::error::Error;
|
use std::error::Error;
|
||||||
@ -41,8 +43,6 @@ impl Error for BuildahError {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub use containers::*;
|
pub use containers::*;
|
||||||
pub use images::*;
|
pub use images::*;
|
||||||
pub use cmd::*;
|
pub use cmd::*;
|
||||||
|
|
||||||
|
2
src/virt/mod.rs
Normal file
2
src/virt/mod.rs
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
pub mod buildah;
|
||||||
|
pub mod nerdctl;
|
37
src/virt/nerdctl/cmd.rs
Normal file
37
src/virt/nerdctl/cmd.rs
Normal file
@ -0,0 +1,37 @@
|
|||||||
|
// File: /root/code/git.ourworld.tf/herocode/sal/src/virt/nerdctl/cmd.rs
|
||||||
|
|
||||||
|
// Basic nerdctl operations for container management
|
||||||
|
use std::process::Command;
|
||||||
|
use crate::process::CommandResult;
|
||||||
|
use super::NerdctlError;
|
||||||
|
|
||||||
|
/// Execute a nerdctl command and return the result
|
||||||
|
pub fn execute_nerdctl_command(args: &[&str]) -> Result<CommandResult, NerdctlError> {
|
||||||
|
let output = Command::new("nerdctl")
|
||||||
|
.args(args)
|
||||||
|
.output();
|
||||||
|
|
||||||
|
match output {
|
||||||
|
Ok(output) => {
|
||||||
|
let stdout = String::from_utf8_lossy(&output.stdout).to_string();
|
||||||
|
let stderr = String::from_utf8_lossy(&output.stderr).to_string();
|
||||||
|
|
||||||
|
let result = CommandResult {
|
||||||
|
stdout,
|
||||||
|
stderr,
|
||||||
|
success: output.status.success(),
|
||||||
|
code: output.status.code().unwrap_or(-1),
|
||||||
|
};
|
||||||
|
|
||||||
|
if result.success {
|
||||||
|
Ok(result)
|
||||||
|
} else {
|
||||||
|
Err(NerdctlError::CommandFailed(format!("Command failed with code {}: {}",
|
||||||
|
result.code, result.stderr.trim())))
|
||||||
|
}
|
||||||
|
},
|
||||||
|
Err(e) => {
|
||||||
|
Err(NerdctlError::CommandExecutionFailed(e))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
105
src/virt/nerdctl/containers.rs
Normal file
105
src/virt/nerdctl/containers.rs
Normal file
@ -0,0 +1,105 @@
|
|||||||
|
// File: /root/code/git.ourworld.tf/herocode/sal/src/virt/nerdctl/containers.rs
|
||||||
|
|
||||||
|
use crate::virt::nerdctl::execute_nerdctl_command;
|
||||||
|
use crate::process::CommandResult;
|
||||||
|
use super::NerdctlError;
|
||||||
|
|
||||||
|
/// Run a container from an image
|
||||||
|
///
|
||||||
|
/// # Arguments
|
||||||
|
///
|
||||||
|
/// * `image` - The image to run
|
||||||
|
/// * `name` - Optional container name
|
||||||
|
/// * `detach` - Whether to run in detached mode
|
||||||
|
/// * `ports` - Optional port mappings (e.g., ["8080:80"])
|
||||||
|
/// * `snapshotter` - Optional snapshotter to use (e.g., "native", "fuse-overlayfs")
|
||||||
|
pub fn run(image: &str, name: Option<&str>, detach: bool, ports: Option<&[&str]>, snapshotter: Option<&str>) -> Result<CommandResult, NerdctlError> {
|
||||||
|
// First, try to remove any existing container with the same name to avoid conflicts
|
||||||
|
if let Some(name_str) = name {
|
||||||
|
// Ignore errors since the container might not exist
|
||||||
|
let _ = execute_nerdctl_command(&["rm", "-f", name_str]);
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut args = vec!["run"];
|
||||||
|
|
||||||
|
if detach {
|
||||||
|
args.push("-d");
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Some(name_str) = name {
|
||||||
|
args.push("--name");
|
||||||
|
args.push(name_str);
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Some(port_mappings) = ports {
|
||||||
|
for port in port_mappings {
|
||||||
|
args.push("-p");
|
||||||
|
args.push(port);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Some(snapshotter_value) = snapshotter {
|
||||||
|
args.push("--snapshotter");
|
||||||
|
args.push(snapshotter_value);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add flags to avoid BPF issues
|
||||||
|
args.push("--cgroup-manager=cgroupfs");
|
||||||
|
|
||||||
|
args.push(image);
|
||||||
|
|
||||||
|
execute_nerdctl_command(&args)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Execute a command in a container
|
||||||
|
///
|
||||||
|
/// # Arguments
|
||||||
|
///
|
||||||
|
/// * `container` - The container ID or name
|
||||||
|
/// * [command](cci:1://file:///root/code/git.ourworld.tf/herocode/sal/src/process/run.rs:303:0-324:1) - The command to run
|
||||||
|
pub fn exec(container: &str, command: &str) -> Result<CommandResult, NerdctlError> {
|
||||||
|
execute_nerdctl_command(&["exec", container, "sh", "-c", command])
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Copy files between container and local filesystem
|
||||||
|
///
|
||||||
|
/// # Arguments
|
||||||
|
///
|
||||||
|
/// * [source](cci:1://file:///root/code/git.ourworld.tf/herocode/sal/src/process/run.rs:55:4-64:5) - Source path (can be container:path or local path)
|
||||||
|
/// * `dest` - Destination path (can be container:path or local path)
|
||||||
|
pub fn copy(source: &str, dest: &str) -> Result<CommandResult, NerdctlError> {
|
||||||
|
execute_nerdctl_command(&["cp", source, dest])
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Stop a container
|
||||||
|
///
|
||||||
|
/// # Arguments
|
||||||
|
///
|
||||||
|
/// * `container` - The container ID or name
|
||||||
|
pub fn stop(container: &str) -> Result<CommandResult, NerdctlError> {
|
||||||
|
execute_nerdctl_command(&["stop", container])
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Remove a container
|
||||||
|
///
|
||||||
|
/// # Arguments
|
||||||
|
///
|
||||||
|
/// * `container` - The container ID or name
|
||||||
|
pub fn remove(container: &str) -> Result<CommandResult, NerdctlError> {
|
||||||
|
execute_nerdctl_command(&["rm", container])
|
||||||
|
}
|
||||||
|
|
||||||
|
/// List containers
|
||||||
|
///
|
||||||
|
/// # Arguments
|
||||||
|
///
|
||||||
|
/// * `all` - Whether to show all containers (including stopped ones)
|
||||||
|
pub fn list(all: bool) -> Result<CommandResult, NerdctlError> {
|
||||||
|
let mut args = vec!["ps"];
|
||||||
|
|
||||||
|
if all {
|
||||||
|
args.push("-a");
|
||||||
|
}
|
||||||
|
|
||||||
|
execute_nerdctl_command(&args)
|
||||||
|
}
|
86
src/virt/nerdctl/images.rs
Normal file
86
src/virt/nerdctl/images.rs
Normal file
@ -0,0 +1,86 @@
|
|||||||
|
// File: /root/code/git.ourworld.tf/herocode/sal/src/virt/nerdctl/images.rs
|
||||||
|
|
||||||
|
use std::collections::HashMap;
|
||||||
|
use crate::virt::nerdctl::execute_nerdctl_command;
|
||||||
|
use crate::process::CommandResult;
|
||||||
|
use super::NerdctlError;
|
||||||
|
use serde_json::{self, Value};
|
||||||
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
|
/// Represents a container image
|
||||||
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||||
|
pub struct Image {
|
||||||
|
/// Image ID
|
||||||
|
pub id: String,
|
||||||
|
/// Image repository
|
||||||
|
pub repository: String,
|
||||||
|
/// Image tag
|
||||||
|
pub tag: String,
|
||||||
|
/// Image size
|
||||||
|
pub size: String,
|
||||||
|
/// Creation timestamp
|
||||||
|
pub created: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// List images in local storage
|
||||||
|
pub fn images() -> Result<CommandResult, NerdctlError> {
|
||||||
|
execute_nerdctl_command(&["images"])
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Remove one or more images
|
||||||
|
///
|
||||||
|
/// # Arguments
|
||||||
|
///
|
||||||
|
/// * `image` - Image ID or name
|
||||||
|
pub fn image_remove(image: &str) -> Result<CommandResult, NerdctlError> {
|
||||||
|
execute_nerdctl_command(&["rmi", image])
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Push an image to a registry
|
||||||
|
///
|
||||||
|
/// # Arguments
|
||||||
|
///
|
||||||
|
/// * `image` - Image name
|
||||||
|
/// * `destination` - Destination registry URL
|
||||||
|
pub fn image_push(image: &str, destination: &str) -> Result<CommandResult, NerdctlError> {
|
||||||
|
execute_nerdctl_command(&["push", image, destination])
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Add an additional name to a local image
|
||||||
|
///
|
||||||
|
/// # Arguments
|
||||||
|
///
|
||||||
|
/// * `image` - Image ID or name
|
||||||
|
/// * `new_name` - New name for the image
|
||||||
|
pub fn image_tag(image: &str, new_name: &str) -> Result<CommandResult, NerdctlError> {
|
||||||
|
execute_nerdctl_command(&["tag", image, new_name])
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Pull an image from a registry
|
||||||
|
///
|
||||||
|
/// # Arguments
|
||||||
|
///
|
||||||
|
/// * `image` - Image name
|
||||||
|
pub fn image_pull(image: &str) -> Result<CommandResult, NerdctlError> {
|
||||||
|
execute_nerdctl_command(&["pull", image])
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Commit a container to an image
|
||||||
|
///
|
||||||
|
/// # Arguments
|
||||||
|
///
|
||||||
|
/// * `container` - Container ID or name
|
||||||
|
/// * `image_name` - New name for the image
|
||||||
|
pub fn image_commit(container: &str, image_name: &str) -> Result<CommandResult, NerdctlError> {
|
||||||
|
execute_nerdctl_command(&["commit", container, image_name])
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Build an image using a Dockerfile
|
||||||
|
///
|
||||||
|
/// # Arguments
|
||||||
|
///
|
||||||
|
/// * `tag` - Tag for the new image
|
||||||
|
/// * `context_path` - Path to the build context
|
||||||
|
pub fn image_build(tag: &str, context_path: &str) -> Result<CommandResult, NerdctlError> {
|
||||||
|
execute_nerdctl_command(&["build", "-t", tag, context_path])
|
||||||
|
}
|
47
src/virt/nerdctl/mod.rs
Normal file
47
src/virt/nerdctl/mod.rs
Normal file
@ -0,0 +1,47 @@
|
|||||||
|
mod containers;
|
||||||
|
mod images;
|
||||||
|
mod cmd;
|
||||||
|
|
||||||
|
use std::fmt;
|
||||||
|
use std::error::Error;
|
||||||
|
use std::io;
|
||||||
|
|
||||||
|
/// Error type for nerdctl operations
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub enum NerdctlError {
|
||||||
|
/// The nerdctl command failed to execute
|
||||||
|
CommandExecutionFailed(io::Error),
|
||||||
|
/// The nerdctl command executed but returned an error
|
||||||
|
CommandFailed(String),
|
||||||
|
/// Failed to parse JSON output
|
||||||
|
JsonParseError(String),
|
||||||
|
/// Failed to convert data
|
||||||
|
ConversionError(String),
|
||||||
|
/// Generic error
|
||||||
|
Other(String),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl fmt::Display for NerdctlError {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
|
match self {
|
||||||
|
NerdctlError::CommandExecutionFailed(e) => write!(f, "Failed to execute nerdctl command: {}", e),
|
||||||
|
NerdctlError::CommandFailed(e) => write!(f, "Nerdctl command failed: {}", e),
|
||||||
|
NerdctlError::JsonParseError(e) => write!(f, "Failed to parse JSON: {}", e),
|
||||||
|
NerdctlError::ConversionError(e) => write!(f, "Conversion error: {}", e),
|
||||||
|
NerdctlError::Other(e) => write!(f, "{}", e),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Error for NerdctlError {
|
||||||
|
fn source(&self) -> Option<&(dyn Error + 'static)> {
|
||||||
|
match self {
|
||||||
|
NerdctlError::CommandExecutionFailed(e) => Some(e),
|
||||||
|
_ => None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub use containers::*;
|
||||||
|
pub use images::*;
|
||||||
|
pub use cmd::*;
|
@ -465,3 +465,39 @@ nerdctl supports various security features:
|
|||||||
- `--security-opt apparmor=profile`: Apply an AppArmor profile
|
- `--security-opt apparmor=profile`: Apply an AppArmor profile
|
||||||
- `--cap-add`/`--cap-drop`: Add or drop Linux capabilities
|
- `--cap-add`/`--cap-drop`: Add or drop Linux capabilities
|
||||||
- `--privileged`: Give extended privileges to the container
|
- `--privileged`: Give extended privileges to the container
|
||||||
|
|
||||||
|
## Typical Workflow Example
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Create a container from an existing image
|
||||||
|
container=$(nerdctl run -d --name my-nginx nginx:latest)
|
||||||
|
|
||||||
|
# Execute a command in the container
|
||||||
|
nerdctl exec $container apt-get update
|
||||||
|
nerdctl exec $container apt-get install -y curl
|
||||||
|
|
||||||
|
# Copy local configuration files to the container
|
||||||
|
nerdctl cp ./nginx.conf $container:/etc/nginx/nginx.conf
|
||||||
|
|
||||||
|
# Commit the container to create a new image
|
||||||
|
nerdctl commit $container my-custom-nginx:latest
|
||||||
|
|
||||||
|
# Stop and remove the container
|
||||||
|
nerdctl stop $container
|
||||||
|
nerdctl rm $container
|
||||||
|
|
||||||
|
# Create a new container from our custom image
|
||||||
|
nerdctl run -d --name nginx-custom -p 8080:80 my-custom-nginx:latest
|
||||||
|
|
||||||
|
# Build an image using a Dockerfile
|
||||||
|
nerdctl build -t my-app:latest .
|
||||||
|
|
||||||
|
# Push the image to a registry
|
||||||
|
nerdctl push my-custom-nginx:latest docker.io/username/my-custom-nginx:latest
|
||||||
|
|
||||||
|
# List images
|
||||||
|
nerdctl images
|
||||||
|
|
||||||
|
# List containers
|
||||||
|
nerdctl ps -a
|
||||||
|
```
|
Loading…
Reference in New Issue
Block a user