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",
|
"libc",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "anyhow"
|
||||||
|
version = "1.0.98"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "e16d2d3311acee920a9eb8d33b8cbc1787ce4a264e85f964c2404b969bdcd487"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "autocfg"
|
name = "autocfg"
|
||||||
version = "1.5.0"
|
version = "1.5.0"
|
||||||
|
|
@ -183,6 +189,8 @@ version = "0.1.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bollard",
|
"bollard",
|
||||||
"futures",
|
"futures",
|
||||||
|
"serde",
|
||||||
|
"serde_yml",
|
||||||
"thiserror",
|
"thiserror",
|
||||||
"tokio",
|
"tokio",
|
||||||
]
|
]
|
||||||
|
|
@ -613,6 +621,16 @@ version = "0.2.174"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "1171693293099992e19cddea4e8b849964e9846f4acee11b3948bcc337be8776"
|
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]]
|
[[package]]
|
||||||
name = "litemap"
|
name = "litemap"
|
||||||
version = "0.8.0"
|
version = "0.8.0"
|
||||||
|
|
@ -855,6 +873,21 @@ dependencies = [
|
||||||
"time",
|
"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]]
|
[[package]]
|
||||||
name = "shlex"
|
name = "shlex"
|
||||||
version = "1.3.0"
|
version = "1.3.0"
|
||||||
|
|
@ -1066,6 +1099,12 @@ version = "1.0.4"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "b6c140620e7ffbb22c2dee59cafe6084a59b5ffc27a8859a5f0d494b5d52b6be"
|
checksum = "b6c140620e7ffbb22c2dee59cafe6084a59b5ffc27a8859a5f0d494b5d52b6be"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "version_check"
|
||||||
|
version = "0.9.5"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "want"
|
name = "want"
|
||||||
version = "0.3.1"
|
version = "0.3.1"
|
||||||
|
|
|
||||||
|
|
@ -6,5 +6,7 @@ edition = "2024"
|
||||||
[dependencies]
|
[dependencies]
|
||||||
bollard = "0.19.1"
|
bollard = "0.19.1"
|
||||||
futures = "0.3.31"
|
futures = "0.3.31"
|
||||||
|
serde = "1.0.219"
|
||||||
|
serde_yml = "0.0.12"
|
||||||
thiserror = "2.0.12"
|
thiserror = "2.0.12"
|
||||||
tokio = { version = "1.45.1", features = ["macros", "rt", "rt-multi-thread"] }
|
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 error;
|
||||||
pub mod manager;
|
pub mod manager;
|
||||||
|
mod docker;
|
||||||
|
|
||||||
pub use error::{Error, Result};
|
pub use error::{Error, Result};
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
use crate::Error;
|
use crate::Error;
|
||||||
use bollard::Docker;
|
|
||||||
use bollard::models::ContainerSummary;
|
use bollard::models::ContainerSummary;
|
||||||
|
use bollard::Docker;
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
|
|
||||||
/// Namespace to manage containers together (like compose projects)
|
/// Namespace to manage containers together (like compose projects)
|
||||||
|
|
@ -9,42 +9,33 @@ type ServiceGroup = String;
|
||||||
const DEFAULT_GROUP: &str = "NONE";
|
const DEFAULT_GROUP: &str = "NONE";
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
struct Services(HashMap<ServiceGroup, Vec<ContainerSummary>>);
|
struct Services(HashMap<ServiceGroup, Vec<crate::docker::Container>>);
|
||||||
|
|
||||||
impl From<&Vec<ContainerSummary>> for Services {
|
impl From<&Vec<ContainerSummary>> for Services {
|
||||||
fn from(value: &Vec<ContainerSummary>) -> Self {
|
fn from(value: &Vec<ContainerSummary>) -> Self {
|
||||||
let mut services = HashMap::new();
|
let mut services = HashMap::new();
|
||||||
|
|
||||||
for container in value.iter().cloned() {
|
for summary in value.iter().cloned() {
|
||||||
if container
|
let container = crate::docker::Container::from(summary);
|
||||||
.mounts
|
|
||||||
.as_deref()
|
if container.mounts.is_empty() {
|
||||||
.is_none_or(|mounts| mounts.is_empty())
|
|
||||||
{
|
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
let project = match &container.labels {
|
let group = match &container.labels.service.group {
|
||||||
None => DEFAULT_GROUP.to_owned(),
|
// Doesn't have a group defined by the user,
|
||||||
Some(labels) => {
|
// try to use the compose hash or throw it with the default group
|
||||||
// Returns user provided group if exits
|
None => container
|
||||||
if labels.contains_key("epoch.service.group") {
|
.labels
|
||||||
labels
|
.service
|
||||||
.get("epoch.service.group")
|
.compose_hash
|
||||||
.and_then(|value| Some(value.to_owned()))
|
.as_ref()
|
||||||
.unwrap()
|
.map(|s| s.to_string())
|
||||||
} else {
|
.unwrap_or_else(|| DEFAULT_GROUP.to_owned()),
|
||||||
labels
|
Some(group) => group.to_owned(),
|
||||||
// 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 list: &mut Vec<ContainerSummary> = services.entry(project).or_default();
|
let list: &mut Vec<crate::docker::Container> = services.entry(group).or_default();
|
||||||
list.push(container);
|
list.push(container);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -62,16 +53,11 @@ pub async fn manage(containers: &Vec<ContainerSummary>) -> crate::Result<()> {
|
||||||
for (group, containers) in services.0.iter() {
|
for (group, containers) in services.0.iter() {
|
||||||
// stop containers of service group
|
// stop containers of service group
|
||||||
let stop_tasks = containers.into_iter().map(async |container| {
|
let stop_tasks = containers.into_iter().map(async |container| {
|
||||||
let id = container
|
eprintln!("Stoping container: {:#?}", container.id);
|
||||||
.id
|
|
||||||
.as_ref()
|
|
||||||
.ok_or(Error::Static("Error getting containers id"))?;
|
|
||||||
|
|
||||||
eprintln!("Stoping container: {:#?}", id);
|
|
||||||
|
|
||||||
let stop_opts = bollard::query_parameters::StopContainerOptionsBuilder::new().build();
|
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>(())
|
Ok::<(), crate::Error>(())
|
||||||
});
|
});
|
||||||
|
|
@ -84,16 +70,11 @@ pub async fn manage(containers: &Vec<ContainerSummary>) -> crate::Result<()> {
|
||||||
|
|
||||||
// restart the containers
|
// restart the containers
|
||||||
let start_tasks = containers.into_iter().map(async |container| {
|
let start_tasks = containers.into_iter().map(async |container| {
|
||||||
let id = container
|
eprintln!("Starting container: {:#?}", container.id);
|
||||||
.id
|
|
||||||
.as_ref()
|
|
||||||
.ok_or(Error::Static("Error getting containers id"))?;
|
|
||||||
|
|
||||||
eprintln!("Starting container: {:#?}", id);
|
|
||||||
|
|
||||||
let start_opts = bollard::query_parameters::StartContainerOptionsBuilder::new().build();
|
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>(())
|
Ok::<(), crate::Error>(())
|
||||||
});
|
});
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue