improve cleanup if container setup fail
ci/woodpecker/push/tests Pipeline was successful

This commit is contained in:
qpismont committed 2026-09-29 16:30:34 +00:00
1 parent e7b8048992
commit 4999091f32
1 file changed
+73 -53
+73 -53
View File
@@ -84,18 +84,20 @@ impl DevContainer {
workspace_dir: &Path,
) -> Result<Container, ContainerError> {
let id = unique_id();
let image = self
.image
.clone()
.unwrap_or_else(|| self.local_image_tag(&id));
let owns_image = self.image.is_none();
let container_name = self.container_name(&id);
let network = format!("{container_name}-net");
let workspace_folder = self.workspace_folder();
info!(image = %image, "Starting sandbox container");
match owns_image {
true => {
let setup = async {
if owns_image {
let container_file_path = self.container_file_path.as_ref().ok_or_else(|| {
ContainerError::Unexpected(String::from(
"the devcontainer has neither an image nor a container file",
@@ -103,74 +105,78 @@ impl DevContainer {
})?;
self.build(runtime, container_file_path, &image).await?;
}
false => {
} else {
runtime.pull_image(&image).await?;
}
}
let name = self.container_name(&id);
let network = format!("{name}-net");
runtime.create_network(&network).await?;
runtime.create_network(&network).await?;
let workspace_folder = self.workspace_folder();
let body = ContainerCreateBody {
image: Some(image.clone()),
cmd: Some(vec![String::from("sleep"), String::from("infinity")]),
user: self.remote_user.clone(),
env: Some(
self.container_env
.iter()
.map(|(key, value)| format!("{key}={value}"))
.collect(),
),
working_dir: Some(workspace_folder.clone()),
host_config: Some(HostConfig {
network_mode: Some(network.clone()),
let body = ContainerCreateBody {
image: Some(image.clone()),
cmd: Some(vec![String::from("sleep"), String::from("infinity")]),
user: self.remote_user.clone(),
env: Some(
self.container_env
.iter()
.map(|(key, value)| format!("{key}={value}"))
.collect(),
),
working_dir: Some(workspace_folder.clone()),
host_config: Some(HostConfig {
network_mode: Some(network.clone()),
..Default::default()
}),
..Default::default()
}),
..Default::default()
};
};
if let Err(err) = runtime.create_container(&name, body).await {
let _ = runtime.remove_network(&network).await;
return Err(err);
runtime.create_container(&container_name, body).await?;
runtime.start_container(&container_name).await?;
runtime
.upload_directory(&container_name, &workspace_folder, workspace_dir)
.await?;
Ok::<(), ContainerError>(())
}
.await;
if let Err(err) = runtime.start_container(&name).await {
let _ = runtime.remove_container(&name).await;
let _ = runtime.remove_network(&network).await;
return Err(err);
}
if let Err(err) = runtime
.upload_directory(&name, &workspace_folder, workspace_dir)
.await
{
let _ = runtime.remove_container(&name).await;
let _ = runtime.remove_network(&network).await;
if let Err(err) = setup {
cleanup_failed_startup(
runtime,
&container_name,
&network,
owns_image.then_some(image.as_str()),
)
.await;
return Err(err);
}
let container = Container::new(
runtime.clone(),
name,
self.workspace_folder(),
container_name.clone(),
workspace_folder,
self.remote_user.clone(),
Some(network.clone()),
owns_image.then_some(image),
owns_image.then_some(image.clone()),
);
// Les hooks s'exécutent avec accès au réseau (installation de dépendances, etc.).
if let Err(err) = self.run_hooks(&container, runtime.timeout()).await {
let _ = container.remove().await;
return Err(err);
}
let post_setup = async {
self.run_hooks(&container, runtime.timeout()).await?;
runtime
.disconnect_network(&network, &container_name)
.await?;
// Coupe l'accès réseau pour le reste de la durée de vie de la sandbox.
if let Err(err) = runtime.disconnect_network(&network, container.name()).await {
let _ = container.remove().await;
Ok::<(), ContainerError>(())
}
.await;
if let Err(err) = post_setup {
cleanup_failed_startup(
runtime,
&container_name,
&network,
owns_image.then_some(image.as_str()),
)
.await;
return Err(err);
}
@@ -203,6 +209,20 @@ impl DevContainer {
}
}
async fn cleanup_failed_startup(
runtime: &ContainerRuntime,
container: &str,
network: &str,
owned_image: Option<&str>,
) {
let _ = runtime.remove_container(container).await;
let _ = runtime.remove_network(network).await;
if let Some(image) = owned_image {
let _ = runtime.remove_image(image).await;
}
}
impl Default for DevContainer {
fn default() -> Self {
Self {