use aurora_core::{AuroraResult, Pipeline, Value};
use std::process::Command;
fn check_docker() -> AuroraResult<()> {
let status = Command::new("docker")
.arg("--version")
.output()
.ok()
.map(|o| o.status.success())
.unwrap_or(false);
if !status {
return Err(aurora_core::AuroraError::CommandNotFound(
"docker is not installed".into()
));
}
Ok(())
}
pub fn docker_ps() -> AuroraResult<Pipeline> {
check_docker()?;
let output = Command::new("docker")
.args(["ps", "-a", "--format", "{{json .}}"])
.output()
.map_err(|e| aurora_core::AuroraError::ModuleError(
format!("docker ps failed: {}", e)
))?;
let stdout = String::from_utf8_lossy(&output.stdout);
let mut rows: Vec<Vec<Value>> = Vec::new();
for line in stdout.lines() {
if line.is_empty() { continue; }
if let Ok(parsed) = serde_json::from_str::<serde_json::Value>(&line) {
let id = parsed.get("ID").and_then(|v| v.as_str()).unwrap_or("").to_string();
let image = parsed.get("Image").and_then(|v| v.as_str()).unwrap_or("").to_string();
let status = parsed.get("Status").and_then(|v| v.as_str()).unwrap_or("").to_string();
let ports = parsed.get("Ports").and_then(|v| v.as_str()).unwrap_or("").to_string();
let names = parsed.get("Names").and_then(|v| v.as_str()).unwrap_or("").to_string();
rows.push(vec![
Value::String(id),
Value::String(image),
Value::String(status),
Value::String(ports),
Value::String(names),
]);
}
}
Ok(Pipeline::table(
vec!["id".into(), "image".into(), "status".into(), "ports".into(), "names".into()],
rows,
))
}
pub fn docker_logs(container: &str, tail: Option<usize>) -> AuroraResult<Pipeline> {
check_docker()?;
let mut cmd = Command::new("docker");
cmd.args(["logs"]);
if let Some(n) = tail {
cmd.args(["--tail", &n.to_string()]);
}
cmd.arg(container);
let output = cmd.output()
.map_err(|e| aurora_core::AuroraError::ModuleError(
format!("docker logs failed: {}", e)
))?;
let stdout = String::from_utf8_lossy(&output.stdout);
let stderr = String::from_utf8_lossy(&output.stderr);
let combined = format!("{}{}", stdout, stderr);
let rows: Vec<Vec<Value>> = combined.lines()
.map(|l| vec![Value::String(l.into())])
.collect();
Ok(Pipeline::table(vec!["line".into()], rows))
}
pub fn docker_exec(container: &str, command: &[String]) -> AuroraResult<Pipeline> {
check_docker()?;
let args: Vec<&str> = command.iter().map(|s| s.as_str()).collect();
let output = Command::new("docker")
.arg("exec")
.arg(container)
.args(&args)
.output()
.map_err(|e| aurora_core::AuroraError::ModuleError(
format!("docker exec failed: {}", e)
))?;
let stdout = String::from_utf8_lossy(&output.stdout);
let stderr = String::from_utf8_lossy(&output.stderr);
let combined = format!("{}{}", stdout, stderr);
Ok(Pipeline::table(
vec!["output".into()],
vec![vec![Value::String(combined)]],
))
}
pub fn docker_images() -> AuroraResult<Pipeline> {
check_docker()?;
let output = Command::new("docker")
.args(["images", "--format", "{{json .}}"])
.output()
.map_err(|e| aurora_core::AuroraError::ModuleError(
format!("docker images failed: {}", e)
))?;
let stdout = String::from_utf8_lossy(&output.stdout);
let mut rows: Vec<Vec<Value>> = Vec::new();
for line in stdout.lines() {
if line.is_empty() { continue; }
if let Ok(parsed) = serde_json::from_str::<serde_json::Value>(line) {
let repo = parsed.get("Repository").and_then(|v| v.as_str()).unwrap_or("").to_string();
let tag = parsed.get("Tag").and_then(|v| v.as_str()).unwrap_or("").to_string();
let id = parsed.get("ID").and_then(|v| v.as_str()).unwrap_or("").to_string();
let created = parsed.get("CreatedAt").and_then(|v| v.as_str()).unwrap_or("").to_string();
let size = parsed.get("Size").and_then(|v| v.as_str()).unwrap_or("").to_string();
rows.push(vec![
Value::String(repo),
Value::String(tag),
Value::String(id),
Value::String(created),
Value::String(size),
]);
}
}
Ok(Pipeline::table(
vec!["repository".into(), "tag".into(), "id".into(), "created".into(), "size".into()],
rows,
))
}