add depends_on start order resolution

This commit is contained in:
lukas-heiligenbrunner 2024-08-27 23:25:15 +02:00
parent d22037031a
commit 228e8ff9b9
3 changed files with 65 additions and 10 deletions

View File

@ -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<Vec<String>> {
let mut container_ids = Vec::new();
) -> anyhow::Result<HashMap<String, String>> {
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<String>) -> anyhow::Result<()> {
for id in ids {
docker.start_container::<String>(&id, None).await?;
fn resolve_start_order(compose: &DockerCompose) -> Vec<String> {
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<String, bool>,
start_order: &mut Vec<String>,
) {
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<String, String>,
) -> anyhow::Result<()> {
// resolve dependency resolution
let start_order = resolve_start_order(compose);
for container_name in start_order {
docker
.start_container::<String>(
ids.get(&container_name).ok_or(anyhow!(
"no created container found with name {}",
container_name
))?,
None,
)
.await?;
}
Ok(())
}

View File

@ -33,7 +33,7 @@ pub async fn up(compose_file: Option<String>, 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?;

View File

@ -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<String>) -> anyhow::Result<()> {
pub(crate) async fn monitor_build_outputs(
docker: &Docker,
ids: HashMap<String, String>,
) -> 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<String>) ->
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");