refactor: introduce local library strucs for containers
This commit is contained in:
parent
8e89aeba75
commit
f613efc882
6 changed files with 136 additions and 41 deletions
39
Cargo.lock
generated
39
Cargo.lock
generated
|
|
@ -32,6 +32,12 @@ dependencies = [
|
|||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "anyhow"
|
||||
version = "1.0.98"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e16d2d3311acee920a9eb8d33b8cbc1787ce4a264e85f964c2404b969bdcd487"
|
||||
|
||||
[[package]]
|
||||
name = "autocfg"
|
||||
version = "1.5.0"
|
||||
|
|
@ -183,6 +189,8 @@ version = "0.1.0"
|
|||
dependencies = [
|
||||
"bollard",
|
||||
"futures",
|
||||
"serde",
|
||||
"serde_yml",
|
||||
"thiserror",
|
||||
"tokio",
|
||||
]
|
||||
|
|
@ -613,6 +621,16 @@ version = "0.2.174"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1171693293099992e19cddea4e8b849964e9846f4acee11b3948bcc337be8776"
|
||||
|
||||
[[package]]
|
||||
name = "libyml"
|
||||
version = "0.0.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3302702afa434ffa30847a83305f0a69d6abd74293b6554c18ec85c7ef30c980"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"version_check",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "litemap"
|
||||
version = "0.8.0"
|
||||
|
|
@ -855,6 +873,21 @@ dependencies = [
|
|||
"time",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde_yml"
|
||||
version = "0.0.12"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "59e2dd588bf1597a252c3b920e0143eb99b0f76e4e082f4c92ce34fbc9e71ddd"
|
||||
dependencies = [
|
||||
"indexmap 2.9.0",
|
||||
"itoa",
|
||||
"libyml",
|
||||
"memchr",
|
||||
"ryu",
|
||||
"serde",
|
||||
"version_check",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "shlex"
|
||||
version = "1.3.0"
|
||||
|
|
@ -1066,6 +1099,12 @@ version = "1.0.4"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b6c140620e7ffbb22c2dee59cafe6084a59b5ffc27a8859a5f0d494b5d52b6be"
|
||||
|
||||
[[package]]
|
||||
name = "version_check"
|
||||
version = "0.9.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a"
|
||||
|
||||
[[package]]
|
||||
name = "want"
|
||||
version = "0.3.1"
|
||||
|
|
|
|||
|
|
@ -6,5 +6,7 @@ edition = "2024"
|
|||
[dependencies]
|
||||
bollard = "0.19.1"
|
||||
futures = "0.3.31"
|
||||
serde = "1.0.219"
|
||||
serde_yml = "0.0.12"
|
||||
thiserror = "2.0.12"
|
||||
tokio = { version = "1.45.1", features = ["macros", "rt", "rt-multi-thread"] }
|
||||
|
|
|
|||
72
src/docker.rs
Normal file
72
src/docker.rs
Normal file
|
|
@ -0,0 +1,72 @@
|
|||
use bollard::models::{ContainerSummary, ContainerSummaryStateEnum, MountPoint};
|
||||
use serde::de::DeserializeOwned;
|
||||
use serde_yml::Value;
|
||||
use std::collections::HashMap;
|
||||
|
||||
mod labels;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct Container {
|
||||
pub id: String,
|
||||
pub name: String,
|
||||
pub image: String,
|
||||
pub state: ContainerSummaryStateEnum,
|
||||
pub mounts: Vec<MountPoint>,
|
||||
pub labels: Labels,
|
||||
}
|
||||
|
||||
impl From<ContainerSummary> for Container {
|
||||
fn from(value: ContainerSummary) -> Self {
|
||||
let container_name: String = value
|
||||
.names
|
||||
.unwrap_or_default()
|
||||
.iter()
|
||||
.map(|name| name.strip_prefix('/').unwrap_or(name).to_string())
|
||||
.collect();
|
||||
|
||||
Self {
|
||||
id: value.id.unwrap_or_default(),
|
||||
name: container_name,
|
||||
image: value.image.unwrap_or_default(),
|
||||
state: value.state.unwrap_or(ContainerSummaryStateEnum::EMPTY),
|
||||
mounts: value.mounts.unwrap_or_default(),
|
||||
labels: value.labels.unwrap_or_default().into(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct Labels {
|
||||
pub enable: bool,
|
||||
pub service: ServiceConfig,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct ServiceConfig {
|
||||
pub compose_hash: Option<String>,
|
||||
pub group: Option<String>,
|
||||
}
|
||||
|
||||
impl Labels {
|
||||
fn parse_label<T: Default + DeserializeOwned>(value: Option<&String>) -> T {
|
||||
if value.is_none() {
|
||||
return T::default();
|
||||
}
|
||||
|
||||
let v: Value = serde_yml::from_str(value.unwrap()).unwrap_or_default();
|
||||
|
||||
serde_yml::from_value(v).unwrap_or_default()
|
||||
}
|
||||
}
|
||||
|
||||
impl From<HashMap<String, String>> for Labels {
|
||||
fn from(value: HashMap<String, String>) -> Self {
|
||||
Self {
|
||||
enable: Self::parse_label(value.get("epoch.enable")),
|
||||
service: ServiceConfig {
|
||||
compose_hash: Self::parse_label(value.get("com.docker.compose.config-hash")),
|
||||
group: Self::parse_label(value.get("epoch.service.group")),
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
0
src/docker/labels.rs
Normal file
0
src/docker/labels.rs
Normal file
|
|
@ -1,4 +1,5 @@
|
|||
pub mod error;
|
||||
pub mod manager;
|
||||
mod docker;
|
||||
|
||||
pub use error::{Error, Result};
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
use crate::Error;
|
||||
use bollard::Docker;
|
||||
use bollard::models::ContainerSummary;
|
||||
use bollard::Docker;
|
||||
use std::collections::HashMap;
|
||||
|
||||
/// Namespace to manage containers together (like compose projects)
|
||||
|
|
@ -9,42 +9,33 @@ type ServiceGroup = String;
|
|||
const DEFAULT_GROUP: &str = "NONE";
|
||||
|
||||
#[derive(Debug)]
|
||||
struct Services(HashMap<ServiceGroup, Vec<ContainerSummary>>);
|
||||
struct Services(HashMap<ServiceGroup, Vec<crate::docker::Container>>);
|
||||
|
||||
impl From<&Vec<ContainerSummary>> for Services {
|
||||
fn from(value: &Vec<ContainerSummary>) -> Self {
|
||||
let mut services = HashMap::new();
|
||||
|
||||
for container in value.iter().cloned() {
|
||||
if container
|
||||
.mounts
|
||||
.as_deref()
|
||||
.is_none_or(|mounts| mounts.is_empty())
|
||||
{
|
||||
for summary in value.iter().cloned() {
|
||||
let container = crate::docker::Container::from(summary);
|
||||
|
||||
if container.mounts.is_empty() {
|
||||
continue;
|
||||
}
|
||||
|
||||
let project = match &container.labels {
|
||||
None => DEFAULT_GROUP.to_owned(),
|
||||
Some(labels) => {
|
||||
// Returns user provided group if exits
|
||||
if labels.contains_key("epoch.service.group") {
|
||||
labels
|
||||
.get("epoch.service.group")
|
||||
.and_then(|value| Some(value.to_owned()))
|
||||
.unwrap()
|
||||
} else {
|
||||
labels
|
||||
// Group by compose hash
|
||||
.get("com.docker.compose.config-hash")
|
||||
.and_then(|value| Some(value.to_owned()))
|
||||
// No group found
|
||||
.unwrap_or(DEFAULT_GROUP.to_owned())
|
||||
}
|
||||
}
|
||||
let group = match &container.labels.service.group {
|
||||
// Doesn't have a group defined by the user,
|
||||
// try to use the compose hash or throw it with the default group
|
||||
None => container
|
||||
.labels
|
||||
.service
|
||||
.compose_hash
|
||||
.as_ref()
|
||||
.map(|s| s.to_string())
|
||||
.unwrap_or_else(|| DEFAULT_GROUP.to_owned()),
|
||||
Some(group) => group.to_owned(),
|
||||
};
|
||||
|
||||
let list: &mut Vec<ContainerSummary> = services.entry(project).or_default();
|
||||
let list: &mut Vec<crate::docker::Container> = services.entry(group).or_default();
|
||||
list.push(container);
|
||||
}
|
||||
|
||||
|
|
@ -62,16 +53,11 @@ pub async fn manage(containers: &Vec<ContainerSummary>) -> crate::Result<()> {
|
|||
for (group, containers) in services.0.iter() {
|
||||
// stop containers of service group
|
||||
let stop_tasks = containers.into_iter().map(async |container| {
|
||||
let id = container
|
||||
.id
|
||||
.as_ref()
|
||||
.ok_or(Error::Static("Error getting containers id"))?;
|
||||
|
||||
eprintln!("Stoping container: {:#?}", id);
|
||||
eprintln!("Stoping container: {:#?}", container.id);
|
||||
|
||||
let stop_opts = bollard::query_parameters::StopContainerOptionsBuilder::new().build();
|
||||
|
||||
docker.stop_container(id, Some(stop_opts)).await?;
|
||||
docker.stop_container(&container.id, Some(stop_opts)).await?;
|
||||
|
||||
Ok::<(), crate::Error>(())
|
||||
});
|
||||
|
|
@ -84,16 +70,11 @@ pub async fn manage(containers: &Vec<ContainerSummary>) -> crate::Result<()> {
|
|||
|
||||
// restart the containers
|
||||
let start_tasks = containers.into_iter().map(async |container| {
|
||||
let id = container
|
||||
.id
|
||||
.as_ref()
|
||||
.ok_or(Error::Static("Error getting containers id"))?;
|
||||
|
||||
eprintln!("Starting container: {:#?}", id);
|
||||
eprintln!("Starting container: {:#?}", container.id);
|
||||
|
||||
let start_opts = bollard::query_parameters::StartContainerOptionsBuilder::new().build();
|
||||
|
||||
docker.start_container(id, Some(start_opts)).await?;
|
||||
docker.start_container(&container.id, Some(start_opts)).await?;
|
||||
|
||||
Ok::<(), crate::Error>(())
|
||||
});
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue