Skip to main content

aurora_modules/
docker.rs

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}