Skip to main content

omnyssh_core/ssh/services/
docker.rs

1//! Docker service provider.
2//!
3//! Detects Docker and Docker Compose and extracts basic container metrics.
4
5use async_trait::async_trait;
6
7use super::{metric_int, ServiceProvider};
8use crate::event::ServiceKind;
9use crate::ssh::probe::ProbeOutput;
10
11/// Docker service provider.
12pub struct DockerProvider;
13
14#[async_trait]
15impl ServiceProvider for DockerProvider {
16    fn kind(&self) -> ServiceKind {
17        ServiceKind::Docker
18    }
19
20    fn detect(&self, probe_output: &ProbeOutput) -> bool {
21        // Check if Docker section has content
22        probe_output.has_section("DOCKER")
23    }
24
25    /// Extract basic metrics from Quick Scan docker ps output.
26    /// This allows us to show container count immediately.
27    fn quick_metrics(&self, probe_output: &ProbeOutput) -> Vec<super::ServiceMetric> {
28        let mut metrics = Vec::new();
29
30        if let Some(docker_output) = probe_output.get_section("DOCKER") {
31            // Parse docker ps output: ID\tNames\tStatus\tImage
32            let lines: Vec<&str> = docker_output.lines().collect();
33            let total = lines.len() as i64;
34
35            // Count running containers (Status contains "Up")
36            let running = lines
37                .iter()
38                .filter(|line| {
39                    let parts: Vec<&str> = line.split('\t').collect();
40                    if parts.len() >= 3 {
41                        parts[2].contains("Up")
42                    } else {
43                        false
44                    }
45                })
46                .count() as i64;
47
48            metrics.push(metric_int("containers_total", total));
49            metrics.push(metric_int("containers_running", running));
50        }
51
52        metrics
53    }
54}
55
56// ---------------------------------------------------------------------------
57// Tests
58// ---------------------------------------------------------------------------
59
60#[cfg(test)]
61mod tests {
62    use super::*;
63
64    #[test]
65    fn test_docker_detect_from_probe() {
66        let probe_output = "===OMNYSSH:DOCKER===\nabc123\tnginx\tUp 2 hours\tnginx:latest\n";
67        let parsed = ProbeOutput::parse(probe_output).expect("should parse");
68        let provider = DockerProvider;
69        assert!(provider.detect(&parsed));
70    }
71
72    #[test]
73    fn test_docker_not_detected_when_absent() {
74        let probe_output = "===OMNYSSH:OS===\nUbuntu\n";
75        let parsed = ProbeOutput::parse(probe_output).expect("should parse");
76        let provider = DockerProvider;
77        assert!(!provider.detect(&parsed));
78    }
79}