1use aurora_core::{AuroraResult, Pipeline, Value};
2use std::process::Command;
3
4fn check_docker() -> AuroraResult<()> {
5 let status = Command::new("docker")
6 .arg("--version")
7 .output()
8 .ok()
9 .map(|o| o.status.success())
10 .unwrap_or(false);
11 if !status {
12 return Err(aurora_core::AuroraError::CommandNotFound(
13 "docker is not installed".into()
14 ));
15 }
16 Ok(())
17}
18
19pub fn docker_ps() -> AuroraResult<Pipeline> {
20 check_docker()?;
21 let output = Command::new("docker")
22 .args(["ps", "-a", "--format", "{{json .}}"])
23 .output()
24 .map_err(|e| aurora_core::AuroraError::ModuleError(
25 format!("docker ps failed: {}", e)
26 ))?;
27
28 let stdout = String::from_utf8_lossy(&output.stdout);
29 let mut rows: Vec<Vec<Value>> = Vec::new();
30
31 for line in stdout.lines() {
32 if line.is_empty() { continue; }
33 if let Ok(parsed) = serde_json::from_str::<serde_json::Value>(&line) {
34 let id = parsed.get("ID").and_then(|v| v.as_str()).unwrap_or("").to_string();
35 let image = parsed.get("Image").and_then(|v| v.as_str()).unwrap_or("").to_string();
36 let status = parsed.get("Status").and_then(|v| v.as_str()).unwrap_or("").to_string();
37 let ports = parsed.get("Ports").and_then(|v| v.as_str()).unwrap_or("").to_string();
38 let names = parsed.get("Names").and_then(|v| v.as_str()).unwrap_or("").to_string();
39
40 rows.push(vec![
41 Value::String(id),
42 Value::String(image),
43 Value::String(status),
44 Value::String(ports),
45 Value::String(names),
46 ]);
47 }
48 }
49
50 Ok(Pipeline::table(
51 vec!["id".into(), "image".into(), "status".into(), "ports".into(), "names".into()],
52 rows,
53 ))
54}
55
56pub fn docker_logs(container: &str, tail: Option<usize>) -> AuroraResult<Pipeline> {
57 check_docker()?;
58 let mut cmd = Command::new("docker");
59 cmd.args(["logs"]);
60 if let Some(n) = tail {
61 cmd.args(["--tail", &n.to_string()]);
62 }
63 cmd.arg(container);
64
65 let output = cmd.output()
66 .map_err(|e| aurora_core::AuroraError::ModuleError(
67 format!("docker logs failed: {}", e)
68 ))?;
69
70 let stdout = String::from_utf8_lossy(&output.stdout);
71 let stderr = String::from_utf8_lossy(&output.stderr);
72 let combined = format!("{}{}", stdout, stderr);
73
74 let rows: Vec<Vec<Value>> = combined.lines()
75 .map(|l| vec![Value::String(l.into())])
76 .collect();
77
78 Ok(Pipeline::table(vec!["line".into()], rows))
79}
80
81pub fn docker_exec(container: &str, command: &[String]) -> AuroraResult<Pipeline> {
82 check_docker()?;
83 let args: Vec<&str> = command.iter().map(|s| s.as_str()).collect();
84
85 let output = Command::new("docker")
86 .arg("exec")
87 .arg(container)
88 .args(&args)
89 .output()
90 .map_err(|e| aurora_core::AuroraError::ModuleError(
91 format!("docker exec failed: {}", e)
92 ))?;
93
94 let stdout = String::from_utf8_lossy(&output.stdout);
95 let stderr = String::from_utf8_lossy(&output.stderr);
96 let combined = format!("{}{}", stdout, stderr);
97
98 Ok(Pipeline::table(
99 vec!["output".into()],
100 vec![vec![Value::String(combined)]],
101 ))
102}
103
104pub fn docker_images() -> AuroraResult<Pipeline> {
105 check_docker()?;
106 let output = Command::new("docker")
107 .args(["images", "--format", "{{json .}}"])
108 .output()
109 .map_err(|e| aurora_core::AuroraError::ModuleError(
110 format!("docker images failed: {}", e)
111 ))?;
112
113 let stdout = String::from_utf8_lossy(&output.stdout);
114 let mut rows: Vec<Vec<Value>> = Vec::new();
115
116 for line in stdout.lines() {
117 if line.is_empty() { continue; }
118 if let Ok(parsed) = serde_json::from_str::<serde_json::Value>(line) {
119 let repo = parsed.get("Repository").and_then(|v| v.as_str()).unwrap_or("").to_string();
120 let tag = parsed.get("Tag").and_then(|v| v.as_str()).unwrap_or("").to_string();
121 let id = parsed.get("ID").and_then(|v| v.as_str()).unwrap_or("").to_string();
122 let created = parsed.get("CreatedAt").and_then(|v| v.as_str()).unwrap_or("").to_string();
123 let size = parsed.get("Size").and_then(|v| v.as_str()).unwrap_or("").to_string();
124
125 rows.push(vec![
126 Value::String(repo),
127 Value::String(tag),
128 Value::String(id),
129 Value::String(created),
130 Value::String(size),
131 ]);
132 }
133 }
134
135 Ok(Pipeline::table(
136 vec!["repository".into(), "tag".into(), "id".into(), "created".into(), "size".into()],
137 rows,
138 ))
139}