feat: Add PostgreSQL and Redis client support
- Add PostgreSQL client functionality for database interactions. - Add Redis client functionality for cache and data store operations. - Extend Rhai scripting with PostgreSQL and Redis client modules. - Add documentation and test cases for both clients.
This commit is contained in:
@@ -2,21 +2,35 @@
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::super::container_types::{Container, ContainerStatus, ResourceUsage};
|
||||
use super::super::NerdctlError;
|
||||
use std::error::Error;
|
||||
use super::super::container_types::Container;
|
||||
use std::process::Command;
|
||||
use std::thread;
|
||||
use std::time::Duration;
|
||||
|
||||
|
||||
// Helper function to check if nerdctl is available
|
||||
fn is_nerdctl_available() -> bool {
|
||||
match Command::new("which").arg("nerdctl").output() {
|
||||
Ok(output) => output.status.success(),
|
||||
Err(_) => false,
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_container_builder_pattern() {
|
||||
// Skip test if nerdctl is not available
|
||||
if !is_nerdctl_available() {
|
||||
println!("Skipping test: nerdctl is not available");
|
||||
return;
|
||||
}
|
||||
|
||||
// Create a container with builder pattern
|
||||
let container = Container::new("test-container").unwrap()
|
||||
let container = Container::new("test-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-container");
|
||||
assert_eq!(container.ports.len(), 1);
|
||||
@@ -27,23 +41,36 @@ mod tests {
|
||||
assert_eq!(container.env_vars.get("TEST_ENV").unwrap(), "test_value");
|
||||
assert_eq!(container.detach, true);
|
||||
}
|
||||
|
||||
|
||||
#[test]
|
||||
fn test_container_from_image() {
|
||||
// Skip test if nerdctl is not available
|
||||
if !is_nerdctl_available() {
|
||||
println!("Skipping test: nerdctl is not available");
|
||||
return;
|
||||
}
|
||||
|
||||
// Create a container from image
|
||||
let container = Container::from_image("test-container", "alpine:latest").unwrap();
|
||||
|
||||
|
||||
// Verify container properties
|
||||
assert_eq!(container.name, "test-container");
|
||||
assert_eq!(container.image.as_ref().unwrap(), "alpine:latest");
|
||||
}
|
||||
|
||||
|
||||
#[test]
|
||||
fn test_container_health_check() {
|
||||
// Skip test if nerdctl is not available
|
||||
if !is_nerdctl_available() {
|
||||
println!("Skipping test: nerdctl is not available");
|
||||
return;
|
||||
}
|
||||
|
||||
// Create a container with health check
|
||||
let container = Container::new("test-container").unwrap()
|
||||
let container = Container::new("test-container")
|
||||
.unwrap()
|
||||
.with_health_check("curl -f http://localhost/ || exit 1");
|
||||
|
||||
|
||||
// Verify health check
|
||||
assert!(container.health_check.is_some());
|
||||
let health_check = container.health_check.unwrap();
|
||||
@@ -53,19 +80,26 @@ mod tests {
|
||||
assert!(health_check.retries.is_none());
|
||||
assert!(health_check.start_period.is_none());
|
||||
}
|
||||
|
||||
|
||||
#[test]
|
||||
fn test_container_health_check_options() {
|
||||
// Skip test if nerdctl is not available
|
||||
if !is_nerdctl_available() {
|
||||
println!("Skipping test: nerdctl is not available");
|
||||
return;
|
||||
}
|
||||
|
||||
// Create a container with health check options
|
||||
let container = Container::new("test-container").unwrap()
|
||||
let container = Container::new("test-container")
|
||||
.unwrap()
|
||||
.with_health_check_options(
|
||||
"curl -f http://localhost/ || exit 1",
|
||||
Some("30s"),
|
||||
Some("10s"),
|
||||
Some(3),
|
||||
Some("5s")
|
||||
Some("5s"),
|
||||
);
|
||||
|
||||
|
||||
// Verify health check options
|
||||
assert!(container.health_check.is_some());
|
||||
let health_check = container.health_check.unwrap();
|
||||
@@ -75,7 +109,7 @@ mod tests {
|
||||
assert_eq!(health_check.retries.unwrap(), 3);
|
||||
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() {
|
||||
@@ -86,42 +120,47 @@ mod tests {
|
||||
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());
|
||||
|
||||
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()
|
||||
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'");
|
||||
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() {
|
||||
@@ -131,10 +170,10 @@ mod tests {
|
||||
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);
|
||||
@@ -142,7 +181,7 @@ mod tests {
|
||||
let _ = container.remove();
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
// Check resource usage
|
||||
let resources_result = container.resources();
|
||||
if resources_result.is_err() {
|
||||
@@ -152,42 +191,55 @@ mod tests {
|
||||
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);
|
||||
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!(
|
||||
"Warning: Failed to remove container: {:?}",
|
||||
remove_result.err()
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
println!("Test completed successfully");
|
||||
}
|
||||
|
||||
|
||||
#[test]
|
||||
fn test_container_with_custom_command() {
|
||||
// Skip test if nerdctl is not available
|
||||
if !is_nerdctl_available() {
|
||||
println!("Skipping test: nerdctl is not available");
|
||||
return;
|
||||
}
|
||||
|
||||
// Create a container with a custom command
|
||||
let container = Container::new("test-command-container").unwrap()
|
||||
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);
|
||||
@@ -197,10 +249,10 @@ mod tests {
|
||||
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()));
|
||||
@@ -211,45 +263,45 @@ mod tests {
|
||||
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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
Reference in New Issue
Block a user