...
This commit is contained in:
parent
7cdd9f5559
commit
3803a54529
@ -185,7 +185,7 @@ fn handle_child_output(mut child: Child, silent: bool) -> Result<CommandResult,
|
|||||||
if let Ok(l) = line {
|
if let Ok(l) = line {
|
||||||
// Print the line if not silent and flush immediately
|
// Print the line if not silent and flush immediately
|
||||||
if !silent_clone {
|
if !silent_clone {
|
||||||
// Always print stderr, even if silent is true, for error visibility
|
// Print stderr with error prefix
|
||||||
eprintln!("\x1b[31mERROR: {}\x1b[0m", l); // Red color for errors
|
eprintln!("\x1b[31mERROR: {}\x1b[0m", l); // Red color for errors
|
||||||
std::io::stderr().flush().unwrap_or(());
|
std::io::stderr().flush().unwrap_or(());
|
||||||
}
|
}
|
||||||
@ -241,14 +241,8 @@ fn process_command_output(output: Result<Output, std::io::Error>) -> Result<Comm
|
|||||||
Ok(out) => {
|
Ok(out) => {
|
||||||
let stdout = String::from_utf8_lossy(&out.stdout).to_string();
|
let stdout = String::from_utf8_lossy(&out.stdout).to_string();
|
||||||
let stderr = String::from_utf8_lossy(&out.stderr).to_string();
|
let stderr = String::from_utf8_lossy(&out.stderr).to_string();
|
||||||
|
// We'll collect stderr but not print it here
|
||||||
// Print stderr if there's any, even for silent execution
|
// It will be included in the error message if the command fails
|
||||||
if !stderr.is_empty() {
|
|
||||||
eprintln!("\x1b[31mCommand stderr output:\x1b[0m");
|
|
||||||
for line in stderr.lines() {
|
|
||||||
eprintln!("\x1b[31m{}\x1b[0m", line);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// If the command failed, print a clear error message
|
// If the command failed, print a clear error message
|
||||||
if !out.status.success() {
|
if !out.status.success() {
|
||||||
@ -356,10 +350,13 @@ fn run_script_internal(script: &str, silent: bool) -> Result<CommandResult, RunE
|
|||||||
// Execute the script and handle the result
|
// Execute the script and handle the result
|
||||||
let result = execute_script_internal(&interpreter, &script_path, silent);
|
let result = execute_script_internal(&interpreter, &script_path, silent);
|
||||||
|
|
||||||
// If there was an error, print a clear error message
|
// If there was an error, print a clear error message only if it's not a CommandFailed error
|
||||||
|
// (which would already have printed the stderr)
|
||||||
if let Err(ref e) = result {
|
if let Err(ref e) = result {
|
||||||
|
if !matches!(e, RunError::CommandFailed(_)) {
|
||||||
eprintln!("\x1b[31mScript execution failed: {}\x1b[0m", e);
|
eprintln!("\x1b[31mScript execution failed: {}\x1b[0m", e);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
result
|
result
|
||||||
}
|
}
|
||||||
@ -485,8 +482,11 @@ impl<'a> RunBuilder<'a> {
|
|||||||
Ok(res)
|
Ok(res)
|
||||||
},
|
},
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
// Always print the error, even if die is false
|
// Print the error only if it's not a CommandFailed error
|
||||||
|
// (which would already have printed the stderr)
|
||||||
|
if !matches!(e, RunError::CommandFailed(_)) {
|
||||||
eprintln!("\x1b[31mCommand error: {}\x1b[0m", e);
|
eprintln!("\x1b[31mCommand error: {}\x1b[0m", e);
|
||||||
|
}
|
||||||
|
|
||||||
if self.die {
|
if self.die {
|
||||||
Err(e)
|
Err(e)
|
||||||
|
@ -14,7 +14,7 @@ fn nerdctl_download(){
|
|||||||
copy(`/tmp/${name}/bin/*`,"/root/hero/bin/");
|
copy(`/tmp/${name}/bin/*`,"/root/hero/bin/");
|
||||||
delete(`/tmp/${name}`);
|
delete(`/tmp/${name}`);
|
||||||
|
|
||||||
run("apt-get -y install buildah")
|
run("apt-get -y install buildah runc")
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -0,0 +1,128 @@
|
|||||||
|
// File: /root/code/git.ourworld.tf/herocode/sal/src/virt/nerdctl/container_functions.rs
|
||||||
|
|
||||||
|
use crate::process::CommandResult;
|
||||||
|
use crate::virt::nerdctl::{execute_nerdctl_command, NerdctlError};
|
||||||
|
|
||||||
|
/// Run a container from an image
|
||||||
|
///
|
||||||
|
/// # Arguments
|
||||||
|
///
|
||||||
|
/// * `image` - Image to run
|
||||||
|
/// * `name` - Optional name for the container
|
||||||
|
/// * `detach` - Whether to run in detached mode
|
||||||
|
/// * `ports` - Optional port mappings
|
||||||
|
/// * `snapshotter` - Optional snapshotter to use
|
||||||
|
///
|
||||||
|
/// # Returns
|
||||||
|
///
|
||||||
|
/// * `Result<CommandResult, NerdctlError>` - Command result or error
|
||||||
|
pub fn run(
|
||||||
|
image: &str,
|
||||||
|
name: Option<&str>,
|
||||||
|
detach: bool,
|
||||||
|
ports: Option<&[&str]>,
|
||||||
|
snapshotter: Option<&str>,
|
||||||
|
) -> Result<CommandResult, NerdctlError> {
|
||||||
|
let mut args = vec!["run"];
|
||||||
|
|
||||||
|
if detach {
|
||||||
|
args.push("-d");
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Some(name_value) = name {
|
||||||
|
args.push("--name");
|
||||||
|
args.push(name_value);
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Some(ports_value) = ports {
|
||||||
|
for port in ports_value {
|
||||||
|
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` - Container name or ID
|
||||||
|
/// * `command` - Command to execute
|
||||||
|
///
|
||||||
|
/// # Returns
|
||||||
|
///
|
||||||
|
/// * `Result<CommandResult, NerdctlError>` - Command result or error
|
||||||
|
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` - Source path (can be container:path or local path)
|
||||||
|
/// * `dest` - Destination path (can be container:path or local path)
|
||||||
|
///
|
||||||
|
/// # Returns
|
||||||
|
///
|
||||||
|
/// * `Result<CommandResult, NerdctlError>` - Command result or error
|
||||||
|
pub fn copy(source: &str, dest: &str) -> Result<CommandResult, NerdctlError> {
|
||||||
|
execute_nerdctl_command(&["cp", source, dest])
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Stop a container
|
||||||
|
///
|
||||||
|
/// # Arguments
|
||||||
|
///
|
||||||
|
/// * `container` - Container name or ID
|
||||||
|
///
|
||||||
|
/// # Returns
|
||||||
|
///
|
||||||
|
/// * `Result<CommandResult, NerdctlError>` - Command result or error
|
||||||
|
pub fn stop(container: &str) -> Result<CommandResult, NerdctlError> {
|
||||||
|
execute_nerdctl_command(&["stop", container])
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Remove a container
|
||||||
|
///
|
||||||
|
/// # Arguments
|
||||||
|
///
|
||||||
|
/// * `container` - Container name or ID
|
||||||
|
///
|
||||||
|
/// # Returns
|
||||||
|
///
|
||||||
|
/// * `Result<CommandResult, NerdctlError>` - Command result or error
|
||||||
|
pub fn remove(container: &str) -> Result<CommandResult, NerdctlError> {
|
||||||
|
execute_nerdctl_command(&["rm", container])
|
||||||
|
}
|
||||||
|
|
||||||
|
/// List containers
|
||||||
|
///
|
||||||
|
/// # Arguments
|
||||||
|
///
|
||||||
|
/// * `all` - Whether to list all containers (including stopped ones)
|
||||||
|
///
|
||||||
|
/// # Returns
|
||||||
|
///
|
||||||
|
/// * `Result<CommandResult, NerdctlError>` - Command result or error
|
||||||
|
pub fn list(all: bool) -> Result<CommandResult, NerdctlError> {
|
||||||
|
let mut args = vec!["ps"];
|
||||||
|
|
||||||
|
if all {
|
||||||
|
args.push("-a");
|
||||||
|
}
|
||||||
|
|
||||||
|
execute_nerdctl_command(&args)
|
||||||
|
}
|
@ -5,6 +5,8 @@ mod tests {
|
|||||||
use super::super::container_types::{Container, ContainerStatus, ResourceUsage};
|
use super::super::container_types::{Container, ContainerStatus, ResourceUsage};
|
||||||
use super::super::NerdctlError;
|
use super::super::NerdctlError;
|
||||||
use std::error::Error;
|
use std::error::Error;
|
||||||
|
use std::thread;
|
||||||
|
use std::time::Duration;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_container_builder_pattern() {
|
fn test_container_builder_pattern() {
|
||||||
@ -73,4 +75,181 @@ mod tests {
|
|||||||
assert_eq!(health_check.retries.unwrap(), 3);
|
assert_eq!(health_check.retries.unwrap(), 3);
|
||||||
assert_eq!(health_check.start_period.as_ref().unwrap(), "5s");
|
assert_eq!(health_check.start_period.as_ref().unwrap(), "5s");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
#[ignore] // Ignore by default as it requires nerdctl to be installed and running
|
||||||
|
fn test_container_runtime_and_resources() {
|
||||||
|
// Check if nerdctl is available and properly configured
|
||||||
|
let nerdctl_check = super::super::execute_nerdctl_command(&["info"]);
|
||||||
|
if nerdctl_check.is_err() {
|
||||||
|
println!("Skipping test: nerdctl is not available or properly configured");
|
||||||
|
println!("Error: {:?}", nerdctl_check.err());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create a unique container name for this test
|
||||||
|
let container_name = format!("test-runtime-{}", std::time::SystemTime::now()
|
||||||
|
.duration_since(std::time::UNIX_EPOCH)
|
||||||
|
.unwrap()
|
||||||
|
.as_secs());
|
||||||
|
|
||||||
|
// Create and build a container that will use resources
|
||||||
|
// Use a simple container with a basic command to avoid dependency on external images
|
||||||
|
let container_result = Container::from_image(&container_name, "busybox:latest").unwrap()
|
||||||
|
.with_detach(true)
|
||||||
|
.build();
|
||||||
|
|
||||||
|
// Check if the build was successful
|
||||||
|
if container_result.is_err() {
|
||||||
|
println!("Failed to build container: {:?}", container_result.err());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
let container = container_result.unwrap();
|
||||||
|
println!("Container created successfully: {}", container_name);
|
||||||
|
|
||||||
|
// Start the container with a simple command
|
||||||
|
let start_result = container.exec("sh -c 'for i in $(seq 1 10); do echo $i; sleep 1; done'");
|
||||||
|
if start_result.is_err() {
|
||||||
|
println!("Failed to start container: {:?}", start_result.err());
|
||||||
|
// Try to clean up
|
||||||
|
let _ = container.remove();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
println!("Container started successfully");
|
||||||
|
|
||||||
|
// Wait for the container to start and consume resources
|
||||||
|
thread::sleep(Duration::from_secs(3));
|
||||||
|
|
||||||
|
// Check container status
|
||||||
|
let status_result = container.status();
|
||||||
|
if status_result.is_err() {
|
||||||
|
println!("Failed to get container status: {:?}", status_result.err());
|
||||||
|
// Try to clean up
|
||||||
|
let _ = container.stop();
|
||||||
|
let _ = container.remove();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
let status = status_result.unwrap();
|
||||||
|
println!("Container status: {:?}", status);
|
||||||
|
|
||||||
|
// Verify the container is running
|
||||||
|
if status.status != "running" {
|
||||||
|
println!("Container is not running, status: {}", status.status);
|
||||||
|
// Try to clean up
|
||||||
|
let _ = container.remove();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check resource usage
|
||||||
|
let resources_result = container.resources();
|
||||||
|
if resources_result.is_err() {
|
||||||
|
println!("Failed to get resource usage: {:?}", resources_result.err());
|
||||||
|
// Try to clean up
|
||||||
|
let _ = container.stop();
|
||||||
|
let _ = container.remove();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
let resources = resources_result.unwrap();
|
||||||
|
println!("Container resources: {:?}", resources);
|
||||||
|
|
||||||
|
// Verify the container is using memory (if we can get the information)
|
||||||
|
if resources.memory_usage == "0B" || resources.memory_usage == "unknown" {
|
||||||
|
println!("Warning: Container memory usage is {}", resources.memory_usage);
|
||||||
|
} else {
|
||||||
|
println!("Container is using memory: {}", resources.memory_usage);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Clean up - stop and remove the container
|
||||||
|
println!("Stopping container...");
|
||||||
|
let stop_result = container.stop();
|
||||||
|
if stop_result.is_err() {
|
||||||
|
println!("Warning: Failed to stop container: {:?}", stop_result.err());
|
||||||
|
}
|
||||||
|
|
||||||
|
println!("Removing container...");
|
||||||
|
let remove_result = container.remove();
|
||||||
|
if remove_result.is_err() {
|
||||||
|
println!("Warning: Failed to remove container: {:?}", remove_result.err());
|
||||||
|
}
|
||||||
|
|
||||||
|
println!("Test completed successfully");
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_container_with_custom_command() {
|
||||||
|
// Create a container with a custom command
|
||||||
|
let container = Container::new("test-command-container").unwrap()
|
||||||
|
.with_port("8080:80")
|
||||||
|
.with_volume("/tmp:/data")
|
||||||
|
.with_env("TEST_ENV", "test_value")
|
||||||
|
.with_detach(true);
|
||||||
|
|
||||||
|
// Verify container properties
|
||||||
|
assert_eq!(container.name, "test-command-container");
|
||||||
|
assert_eq!(container.ports.len(), 1);
|
||||||
|
assert_eq!(container.ports[0], "8080:80");
|
||||||
|
assert_eq!(container.volumes.len(), 1);
|
||||||
|
assert_eq!(container.volumes[0], "/tmp:/data");
|
||||||
|
assert_eq!(container.env_vars.len(), 1);
|
||||||
|
assert_eq!(container.env_vars.get("TEST_ENV").unwrap(), "test_value");
|
||||||
|
assert_eq!(container.detach, true);
|
||||||
|
|
||||||
|
// Convert the container to a command string that would be used to run it
|
||||||
|
let command_args = container_to_command_args(&container);
|
||||||
|
|
||||||
|
// Verify the command arguments contain all the expected options
|
||||||
|
assert!(command_args.contains(&"--name".to_string()));
|
||||||
|
assert!(command_args.contains(&"test-command-container".to_string()));
|
||||||
|
assert!(command_args.contains(&"-p".to_string()));
|
||||||
|
assert!(command_args.contains(&"8080:80".to_string()));
|
||||||
|
assert!(command_args.contains(&"-v".to_string()));
|
||||||
|
assert!(command_args.contains(&"/tmp:/data".to_string()));
|
||||||
|
assert!(command_args.contains(&"-e".to_string()));
|
||||||
|
assert!(command_args.contains(&"TEST_ENV=test_value".to_string()));
|
||||||
|
assert!(command_args.contains(&"-d".to_string()));
|
||||||
|
|
||||||
|
println!("Command args: {:?}", command_args);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Helper function to convert a container to command arguments
|
||||||
|
fn container_to_command_args(container: &Container) -> Vec<String> {
|
||||||
|
let mut args = Vec::new();
|
||||||
|
args.push("run".to_string());
|
||||||
|
|
||||||
|
if container.detach {
|
||||||
|
args.push("-d".to_string());
|
||||||
|
}
|
||||||
|
|
||||||
|
args.push("--name".to_string());
|
||||||
|
args.push(container.name.clone());
|
||||||
|
|
||||||
|
// Add port mappings
|
||||||
|
for port in &container.ports {
|
||||||
|
args.push("-p".to_string());
|
||||||
|
args.push(port.clone());
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add volume mounts
|
||||||
|
for volume in &container.volumes {
|
||||||
|
args.push("-v".to_string());
|
||||||
|
args.push(volume.clone());
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add environment variables
|
||||||
|
for (key, value) in &container.env_vars {
|
||||||
|
args.push("-e".to_string());
|
||||||
|
args.push(format!("{}={}", key, value));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add image if available
|
||||||
|
if let Some(image) = &container.image {
|
||||||
|
args.push(image.clone());
|
||||||
|
}
|
||||||
|
|
||||||
|
args
|
||||||
|
}
|
||||||
}
|
}
|
@ -5,6 +5,7 @@ mod container;
|
|||||||
mod container_builder;
|
mod container_builder;
|
||||||
mod health_check;
|
mod health_check;
|
||||||
mod container_operations;
|
mod container_operations;
|
||||||
|
mod container_functions;
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod container_test;
|
mod container_test;
|
||||||
|
|
||||||
@ -51,3 +52,4 @@ impl Error for NerdctlError {
|
|||||||
pub use images::*;
|
pub use images::*;
|
||||||
pub use cmd::*;
|
pub use cmd::*;
|
||||||
pub use container_types::{Container, HealthCheck, ContainerStatus, ResourceUsage};
|
pub use container_types::{Container, HealthCheck, ContainerStatus, ResourceUsage};
|
||||||
|
pub use container_functions::*;
|
Loading…
Reference in New Issue
Block a user