use crate::Error; use bollard::models::ContainerSummary; use bollard::Docker; use std::collections::HashMap; /// Namespace to manage containers together (like compose projects) type ServiceGroup = String; const DEFAULT_GROUP: &str = "NONE"; #[derive(Debug)] struct Services(HashMap>); impl From<&Vec> for Services { fn from(value: &Vec) -> Self { let mut services = HashMap::new(); for summary in value.iter().cloned() { let container = crate::docker::Container::from(summary); if container.mounts.is_empty() { continue; } 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 = services.entry(group).or_default(); list.push(container); } Self(services) } } pub async fn manage(containers: &Vec) -> crate::Result<()> { let services = Services::from(containers); // TODO: reuse main instance let docker = Docker::connect_with_local_defaults()?; // TODO: iterate over groups in parallel for (group, containers) in services.0.iter() { // stop containers of service group let stop_tasks = containers.into_iter().map(async |container| { eprintln!("Stoping container: {:#?}", container.id); let stop_opts = bollard::query_parameters::StopContainerOptionsBuilder::new().build(); docker.stop_container(&container.id, Some(stop_opts)).await?; Ok::<(), crate::Error>(()) }); futures::future::try_join_all(stop_tasks).await?; // create container with the same mounts as each container in the group // run the new container // restart the containers let start_tasks = containers.into_iter().map(async |container| { eprintln!("Starting container: {:#?}", container.id); let start_opts = bollard::query_parameters::StartContainerOptionsBuilder::new().build(); docker.start_container(&container.id, Some(start_opts)).await?; Ok::<(), crate::Error>(()) }); futures::future::try_join_all(start_tasks).await?; } Ok(()) }