From 228e8ff9b98ee83b8349304a7977c2b4b4f72653 Mon Sep 17 00:00:00 2001 From: lukas-heiligenbrunner Date: Tue, 27 Aug 2024 23:25:15 +0200 Subject: [PATCH] add depends_on start order resolution --- lib/src/container.rs | 61 +++++++++++++++++++++++++++++++++++++++----- lib/src/lib.rs | 2 +- lib/src/monitor.rs | 12 ++++++--- 3 files changed, 65 insertions(+), 10 deletions(-) diff --git a/lib/src/container.rs b/lib/src/container.rs index 0329de3..a79304d 100644 --- a/lib/src/container.rs +++ b/lib/src/container.rs @@ -5,13 +5,14 @@ use bollard::container::{Config, CreateContainerOptions, RemoveContainerOptions} use bollard::models::{HostConfig, PortBinding, PortMap, RestartPolicy, RestartPolicyNameEnum}; use bollard::Docker; use std::collections::HashMap; +use std::iter::Map; pub(crate) async fn create_containers( compose: &DockerCompose, docker: &Docker, detach: bool, -) -> anyhow::Result> { - let mut container_ids = Vec::new(); +) -> anyhow::Result> { + let mut container_ids = HashMap::new(); let parent_dir = parent_dir_name()?; for (name, service) in &compose.services { let env = create_env_vec(&service.environment); @@ -43,14 +44,62 @@ pub(crate) async fn create_containers( conf, ) .await?; - container_ids.push(create_info.id); + container_ids.insert(name.clone(), create_info.id); } Ok(container_ids) } -pub(crate) async fn start_containers(docker: &Docker, ids: Vec) -> anyhow::Result<()> { - for id in ids { - docker.start_container::(&id, None).await?; +fn resolve_start_order(compose: &DockerCompose) -> Vec { + let mut start_order = Vec::new(); + let mut visited = HashMap::new(); + + for service_name in compose.services.keys() { + resolve_service(service_name, compose, &mut visited, &mut start_order); + } + + start_order +} + +fn resolve_service( + service_name: &str, + compose: &DockerCompose, + visited: &mut HashMap, + start_order: &mut Vec, +) { + if let Some(&true) = visited.get(service_name) { + return; + } + visited.insert(service_name.to_string(), true); + + if let Some(service) = compose.services.get(service_name) { + if let Some(dependencies) = &service.depends_on { + for dependency in dependencies { + resolve_service(dependency, compose, visited, start_order); + } + } + } + + start_order.push(service_name.to_string()); +} + +pub(crate) async fn start_containers( + compose: &DockerCompose, + docker: &Docker, + ids: HashMap, +) -> anyhow::Result<()> { + // resolve dependency resolution + let start_order = resolve_start_order(compose); + + for container_name in start_order { + docker + .start_container::( + ids.get(&container_name).ok_or(anyhow!( + "no created container found with name {}", + container_name + ))?, + None, + ) + .await?; } Ok(()) } diff --git a/lib/src/lib.rs b/lib/src/lib.rs index 93c7a1e..15be8b5 100644 --- a/lib/src/lib.rs +++ b/lib/src/lib.rs @@ -33,7 +33,7 @@ pub async fn up(compose_file: Option, detach: bool) -> anyhow::Result<() pull_images(&compose, &docker).await; create_volumes(&compose, &docker).await?; let ids = create_containers(&compose, &docker, detach).await?; - start_containers(&docker, ids.clone()).await?; + start_containers(&compose, &docker, ids.clone()).await?; if !detach { monitor_build_outputs(&docker, ids).await?; diff --git a/lib/src/monitor.rs b/lib/src/monitor.rs index fc3373e..7e18136 100644 --- a/lib/src/monitor.rs +++ b/lib/src/monitor.rs @@ -3,11 +3,17 @@ use bollard::container::{AttachContainerOptions, LogOutput}; use bollard::Docker; use futures_util::future::join_all; use futures_util::StreamExt; +use std::collections::HashMap; +use std::iter::Map; -pub(crate) async fn monitor_build_outputs(docker: &Docker, ids: Vec) -> anyhow::Result<()> { +pub(crate) async fn monitor_build_outputs( + docker: &Docker, + ids: HashMap, +) -> anyhow::Result<()> { let mut handles = vec![]; - for id in ids.clone() { + for id in ids.values() { let docker = docker.clone(); + let id = id.clone(); handles.push(tokio::spawn(async move { let r = monitor_logs(&docker, id.clone()).await; println!("Container {} exited with {:?}", id, r) @@ -17,7 +23,7 @@ pub(crate) async fn monitor_build_outputs(docker: &Docker, ids: Vec) -> tokio::select! { _ = tokio::signal::ctrl_c() => { println!("Ctrl-c received, stopping containers"); - stop_containers_by_ids(docker, ids.clone()).await?; + stop_containers_by_ids(docker, ids.values().map(|v| v.to_string()).collect()).await?; } _ = join_all(handles) => { println!("All containers exited");