103 lines
3.3 KiB
Rust
103 lines
3.3 KiB
Rust
use anyhow::Result;
|
|
use bollard::{container::ListContainersOptions, service::ContainerSummary, Docker};
|
|
use std::{collections::HashMap, path::Path};
|
|
use tokio::fs;
|
|
|
|
use crate::node::{container::ContainerInfo, nix::parse_arion_compose};
|
|
|
|
use super::{compose, utils, NodeInfo, ServiceInfo, StackInfo};
|
|
|
|
impl StackInfo {
|
|
pub fn stats(&self) -> (usize, usize) {
|
|
let running = self.services.iter().filter(|s| s.running).count();
|
|
(running, self.services.len() - running)
|
|
}
|
|
}
|
|
|
|
fn get_service(containers: &[ContainerInfo], stack_name: &str, service: &str) -> ServiceInfo {
|
|
let container = containers.iter().find(|cont| {
|
|
let labels = cont.labels.clone().unwrap_or_default();
|
|
labels.get("com.docker.compose.project") == Some(&stack_name.to_string())
|
|
&& labels.get("com.docker.compose.service") == Some(&service.to_string())
|
|
});
|
|
|
|
match container {
|
|
Some(info) => ServiceInfo {
|
|
name: service.to_string(),
|
|
container: Some(info.name.clone()),
|
|
running: info.running(),
|
|
},
|
|
_ => ServiceInfo {
|
|
name: service.to_string(),
|
|
container: None,
|
|
running: false,
|
|
},
|
|
}
|
|
}
|
|
|
|
pub async fn all(base_dir: &Path, docker: &Docker) -> Result<NodeInfo> {
|
|
let containers: Vec<ContainerInfo> = docker
|
|
.list_containers(Some(ListContainersOptions::<String> {
|
|
all: true,
|
|
limit: None,
|
|
..Default::default()
|
|
}))
|
|
.await?
|
|
.iter()
|
|
.map(|c| c.clone().into())
|
|
.collect();
|
|
|
|
let mut dirs = fs::read_dir(base_dir).await?;
|
|
let mut stacks = vec![];
|
|
while let Some(dir) = dirs.next_entry().await? {
|
|
let meta = dir.metadata().await?;
|
|
if !meta.is_dir() {
|
|
continue;
|
|
}
|
|
if utils::is_stack(&dir.path()).await? {
|
|
let folder = dir.file_name().to_string_lossy().to_string();
|
|
let compose_file = compose::get(base_dir, &folder).await?;
|
|
let info = parse_arion_compose(&compose_file)?;
|
|
let name = info.project;
|
|
// Check status by analyzing containers
|
|
let active = containers
|
|
.iter()
|
|
.any(|cont| cont.state == "running" && cont.stack() == Some(name.clone()));
|
|
let services = info
|
|
.services
|
|
.iter()
|
|
.map(|service| get_service(&containers, &name, service))
|
|
.collect();
|
|
stacks.push(StackInfo {
|
|
folder,
|
|
name,
|
|
active,
|
|
services,
|
|
})
|
|
}
|
|
}
|
|
|
|
Ok(NodeInfo { stacks, containers })
|
|
}
|
|
|
|
pub async fn containers(docker: &Docker, stack_name: &str) -> Result<Vec<ContainerSummary>> {
|
|
Ok(docker
|
|
.list_containers(Some(ListContainersOptions {
|
|
all: true,
|
|
limit: None,
|
|
size: true,
|
|
filters: HashMap::from([(
|
|
"label".to_string(),
|
|
vec![format!("com.docker.compose.project={}", stack_name)],
|
|
)]),
|
|
}))
|
|
.await?)
|
|
}
|
|
|
|
// Shortcut function to get a stack name from directory
|
|
pub async fn stack_name(base_dir: &Path, folder: &str) -> Result<String> {
|
|
let compose_file = compose::get(base_dir, folder).await?;
|
|
let info = parse_arion_compose(&compose_file)?;
|
|
Ok(info.project)
|
|
}
|