Show individual Docker containers as sub-services similar to nginx sites
This commit is contained in:
parent
6b22d23a2e
commit
f5acf44e3b
@ -435,16 +435,17 @@ impl ServiceCollector {
|
|||||||
"sshd" | "ssh" => self.get_ssh_active_users().await.map(|s| vec![s]),
|
"sshd" | "ssh" => self.get_ssh_active_users().await.map(|s| vec![s]),
|
||||||
"nginx" => self.get_nginx_description().await.map(|s| vec![s]),
|
"nginx" => self.get_nginx_description().await.map(|s| vec![s]),
|
||||||
"apache2" | "httpd" => self.get_web_server_connections().await.map(|s| vec![s]),
|
"apache2" | "httpd" => self.get_web_server_connections().await.map(|s| vec![s]),
|
||||||
"docker" | "docker-registry" => self.get_docker_containers().await.map(|s| vec![s]),
|
"docker-registry" => self.get_docker_registry_info().await.map(|s| vec![s]),
|
||||||
|
"docker" => self.get_docker_containers().await.map(|s| vec![s]),
|
||||||
"postgresql" | "postgres" => self.get_postgres_connections().await.map(|s| vec![s]),
|
"postgresql" | "postgres" => self.get_postgres_connections().await.map(|s| vec![s]),
|
||||||
"mysql" | "mariadb" => self.get_mysql_connections().await.map(|s| vec![s]),
|
"mysql" | "mariadb" => self.get_mysql_connections().await.map(|s| vec![s]),
|
||||||
"redis" | "redis-immich" => self.get_redis_info().await.map(|s| vec![s]),
|
"redis" | "redis-immich" => self.get_redis_info().await.map(|s| vec![s]),
|
||||||
"mongodb" | "mongod" => self.get_mongodb_info().await.map(|s| vec![s]),
|
|
||||||
"gitea" => self.get_gitea_info().await.map(|s| vec![s]),
|
"gitea" => self.get_gitea_info().await.map(|s| vec![s]),
|
||||||
"immich-server" | "immich" => self.get_immich_info().await.map(|s| vec![s]),
|
"immich-server" => self.get_immich_info().await.map(|s| vec![s]),
|
||||||
"vaultwarden" => self.get_vaultwarden_info().await.map(|s| vec![s]),
|
"vaultwarden" => self.get_vaultwarden_info().await.map(|s| vec![s]),
|
||||||
"unifi" => self.get_unifi_info().await.map(|s| vec![s]),
|
"unifi" => self.get_unifi_info().await.map(|s| vec![s]),
|
||||||
"mosquitto" => self.get_mosquitto_info().await.map(|s| vec![s]),
|
"mosquitto" => self.get_mosquitto_info().await.map(|s| vec![s]),
|
||||||
|
"haasp-webgrid" => self.get_haasp_webgrid_info().await.map(|s| vec![s]),
|
||||||
_ => None,
|
_ => None,
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -506,9 +507,9 @@ impl ServiceCollector {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn get_docker_containers(&self) -> Option<String> {
|
async fn get_docker_containers(&self) -> Option<Vec<String>> {
|
||||||
let output = Command::new("docker")
|
let output = Command::new("/run/current-system/sw/bin/docker")
|
||||||
.args(["ps", "--format", "table {{.Names}}"])
|
.args(["ps", "--format", "{{.Names}}"])
|
||||||
.stdout(Stdio::piped())
|
.stdout(Stdio::piped())
|
||||||
.stderr(Stdio::piped())
|
.stderr(Stdio::piped())
|
||||||
.output()
|
.output()
|
||||||
@ -520,12 +521,16 @@ impl ServiceCollector {
|
|||||||
}
|
}
|
||||||
|
|
||||||
let stdout = String::from_utf8_lossy(&output.stdout);
|
let stdout = String::from_utf8_lossy(&output.stdout);
|
||||||
let container_count = stdout.lines().count().saturating_sub(1); // Subtract header line
|
let containers: Vec<String> = stdout
|
||||||
|
.lines()
|
||||||
|
.filter(|line| !line.trim().is_empty())
|
||||||
|
.map(|line| line.trim().to_string())
|
||||||
|
.collect();
|
||||||
|
|
||||||
if container_count > 0 {
|
if containers.is_empty() {
|
||||||
Some(format!("{} running containers", container_count))
|
None
|
||||||
} else {
|
} else {
|
||||||
Some("no containers running".to_string())
|
Some(containers)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -573,7 +578,24 @@ impl ServiceCollector {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Fallback: check MySQL connections on port 3306
|
// Fallback: check MySQL unix socket connections (more common than TCP)
|
||||||
|
let output = Command::new("/run/current-system/sw/bin/ss")
|
||||||
|
.args(["-x", "state", "connected", "src", "*mysql*"])
|
||||||
|
.stdout(Stdio::piped())
|
||||||
|
.stderr(Stdio::piped())
|
||||||
|
.output()
|
||||||
|
.await
|
||||||
|
.ok()?;
|
||||||
|
|
||||||
|
if output.status.success() {
|
||||||
|
let stdout = String::from_utf8_lossy(&output.stdout);
|
||||||
|
let connection_count = stdout.lines().count().saturating_sub(1);
|
||||||
|
if connection_count > 0 {
|
||||||
|
return Some(format!("{} connections", connection_count));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Also try TCP port 3306 as final fallback
|
||||||
let output = Command::new("/run/current-system/sw/bin/ss")
|
let output = Command::new("/run/current-system/sw/bin/ss")
|
||||||
.args(["-tn", "state", "established", "dport", "= :3306"])
|
.args(["-tn", "state", "established", "dport", "= :3306"])
|
||||||
.stdout(Stdio::piped())
|
.stdout(Stdio::piped())
|
||||||
@ -911,9 +933,9 @@ impl ServiceCollector {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async fn get_immich_info(&self) -> Option<String> {
|
async fn get_immich_info(&self) -> Option<String> {
|
||||||
// Check HTTP connections - Immich typically runs on port 2283 or behind nginx
|
// Check HTTP connections - Immich runs on port 8084 (from nginx proxy config)
|
||||||
let output = Command::new("/run/current-system/sw/bin/ss")
|
let output = Command::new("/run/current-system/sw/bin/ss")
|
||||||
.args(["-tn", "state", "established", "dport", "= :2283"])
|
.args(["-tn", "state", "established", "dport", "= :8084"])
|
||||||
.stdout(Stdio::piped())
|
.stdout(Stdio::piped())
|
||||||
.stderr(Stdio::piped())
|
.stderr(Stdio::piped())
|
||||||
.output()
|
.output()
|
||||||
@ -932,34 +954,9 @@ impl ServiceCollector {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async fn get_vaultwarden_info(&self) -> Option<String> {
|
async fn get_vaultwarden_info(&self) -> Option<String> {
|
||||||
// Try common vaultwarden ports: 8080, 8084, 8000
|
// Check vaultwarden connections on port 8222 (from nginx proxy config)
|
||||||
let ports = ["8080", "8084", "8000"];
|
let output = Command::new("/run/current-system/sw/bin/ss")
|
||||||
|
.args(["-tn", "state", "established", "dport", "= :8222"])
|
||||||
for port in &ports {
|
|
||||||
let output = Command::new("/run/current-system/sw/bin/ss")
|
|
||||||
.args(["-tn", "state", "established", "dport", &format!("= :{}", port)])
|
|
||||||
.stdout(Stdio::piped())
|
|
||||||
.stderr(Stdio::piped())
|
|
||||||
.output()
|
|
||||||
.await
|
|
||||||
.ok()?;
|
|
||||||
|
|
||||||
if output.status.success() {
|
|
||||||
let stdout = String::from_utf8_lossy(&output.stdout);
|
|
||||||
let connection_count = stdout.lines().count().saturating_sub(1);
|
|
||||||
if connection_count > 0 {
|
|
||||||
return Some(format!("{} connections", connection_count));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
None
|
|
||||||
}
|
|
||||||
|
|
||||||
async fn get_unifi_info(&self) -> Option<String> {
|
|
||||||
// Check for device count via UniFi API (if accessible)
|
|
||||||
let output = Command::new("curl")
|
|
||||||
.args(["-s", "-f", "--insecure", "https://localhost:8443/api/self/sites"])
|
|
||||||
.stdout(Stdio::piped())
|
.stdout(Stdio::piped())
|
||||||
.stderr(Stdio::piped())
|
.stderr(Stdio::piped())
|
||||||
.output()
|
.output()
|
||||||
@ -967,12 +964,32 @@ impl ServiceCollector {
|
|||||||
.ok()?;
|
.ok()?;
|
||||||
|
|
||||||
if output.status.success() {
|
if output.status.success() {
|
||||||
return Some("Controller active".to_string());
|
let stdout = String::from_utf8_lossy(&output.stdout);
|
||||||
|
let connection_count = stdout.lines().count().saturating_sub(1);
|
||||||
|
if connection_count > 0 {
|
||||||
|
return Some(format!("{} connections", connection_count));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Fallback: check data directory
|
None
|
||||||
if tokio::fs::metadata("/var/lib/unifi/data").await.is_ok() {
|
}
|
||||||
return Some("Data directory exists".to_string());
|
|
||||||
|
async fn get_unifi_info(&self) -> Option<String> {
|
||||||
|
// Check UniFi connections on port 8080 (TCP)
|
||||||
|
let output = Command::new("/run/current-system/sw/bin/ss")
|
||||||
|
.args(["-tn", "state", "established", "dport", "= :8080"])
|
||||||
|
.stdout(Stdio::piped())
|
||||||
|
.stderr(Stdio::piped())
|
||||||
|
.output()
|
||||||
|
.await
|
||||||
|
.ok()?;
|
||||||
|
|
||||||
|
if output.status.success() {
|
||||||
|
let stdout = String::from_utf8_lossy(&output.stdout);
|
||||||
|
let connection_count = stdout.lines().count().saturating_sub(1);
|
||||||
|
if connection_count > 0 {
|
||||||
|
return Some(format!("{} connections", connection_count));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
None
|
None
|
||||||
@ -999,10 +1016,10 @@ impl ServiceCollector {
|
|||||||
None
|
None
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn get_mongodb_info(&self) -> Option<String> {
|
async fn get_docker_registry_info(&self) -> Option<String> {
|
||||||
// Check MongoDB connections on port 27017
|
// Check Docker registry connections on port 5000 (from nginx proxy config)
|
||||||
let output = Command::new("/run/current-system/sw/bin/ss")
|
let output = Command::new("/run/current-system/sw/bin/ss")
|
||||||
.args(["-tn", "state", "established", "dport", "= :27017"])
|
.args(["-tn", "state", "established", "dport", "= :5000"])
|
||||||
.stdout(Stdio::piped())
|
.stdout(Stdio::piped())
|
||||||
.stderr(Stdio::piped())
|
.stderr(Stdio::piped())
|
||||||
.output()
|
.output()
|
||||||
@ -1017,9 +1034,13 @@ impl ServiceCollector {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Fallback: check data directory size
|
None
|
||||||
let output = Command::new("sudo")
|
}
|
||||||
.args(["/run/current-system/sw/bin/du", "-sh", "/var/lib/mongodb"])
|
|
||||||
|
async fn get_haasp_webgrid_info(&self) -> Option<String> {
|
||||||
|
// Check HAASP webgrid connections on port 8081
|
||||||
|
let output = Command::new("/run/current-system/sw/bin/ss")
|
||||||
|
.args(["-tn", "state", "established", "dport", "= :8081"])
|
||||||
.stdout(Stdio::piped())
|
.stdout(Stdio::piped())
|
||||||
.stderr(Stdio::piped())
|
.stderr(Stdio::piped())
|
||||||
.output()
|
.output()
|
||||||
@ -1028,8 +1049,9 @@ impl ServiceCollector {
|
|||||||
|
|
||||||
if output.status.success() {
|
if output.status.success() {
|
||||||
let stdout = String::from_utf8_lossy(&output.stdout);
|
let stdout = String::from_utf8_lossy(&output.stdout);
|
||||||
if let Some(size) = stdout.split_whitespace().next() {
|
let connection_count = stdout.lines().count().saturating_sub(1);
|
||||||
return Some(format!("Data: {}", size));
|
if connection_count > 0 {
|
||||||
|
return Some(format!("{} connections", connection_count));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1101,6 +1123,31 @@ impl Collector for ServiceCollector {
|
|||||||
healthy += 1;
|
healthy += 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
// Handle docker specially - create sub-services for containers
|
||||||
|
else if service == "docker" && matches!(service_data.status, ServiceStatus::Running) {
|
||||||
|
// Clear docker description - containers will become individual sub-services
|
||||||
|
let mut docker_service = service_data;
|
||||||
|
docker_service.description = None;
|
||||||
|
services.push(docker_service);
|
||||||
|
|
||||||
|
// Add docker containers as individual sub-services
|
||||||
|
if let Some(containers) = self.get_docker_containers().await {
|
||||||
|
for container in containers.iter() {
|
||||||
|
services.push(ServiceData {
|
||||||
|
name: container.clone(),
|
||||||
|
status: ServiceStatus::Running, // Assume containers are running if docker is running
|
||||||
|
memory_used_mb: 0.0,
|
||||||
|
memory_quota_mb: 0.0,
|
||||||
|
cpu_percent: 0.0,
|
||||||
|
sandbox_limit: None,
|
||||||
|
disk_used_gb: 0.0,
|
||||||
|
description: None,
|
||||||
|
sub_service: Some("docker".to_string()),
|
||||||
|
});
|
||||||
|
healthy += 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
services.push(service_data);
|
services.push(service_data);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -238,6 +238,9 @@ impl AutoDiscovery {
|
|||||||
"rclone",
|
"rclone",
|
||||||
// Container runtimes
|
// Container runtimes
|
||||||
"docker",
|
"docker",
|
||||||
|
// CI/CD services
|
||||||
|
"gitea-actions",
|
||||||
|
"actions-runner",
|
||||||
// Network services
|
// Network services
|
||||||
"sshd",
|
"sshd",
|
||||||
"dnsmasq",
|
"dnsmasq",
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user