staxman-old/src/stack/list.rs

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)
}