@@ -9,14 +9,14 @@ use crate::e2e::environment::TestEnvironment;
99use crate :: e2e:: tasks:: preflight_cleanup:: {
1010 cleanup_build_directory, cleanup_templates_directory, PreflightCleanupError ,
1111} ;
12- use tracing:: info;
12+ use crate :: shared:: executor:: CommandExecutor ;
13+ use tracing:: { info, warn} ;
1314
1415/// Performs pre-flight cleanup for Docker-based E2E tests
1516///
1617/// This function is specifically designed for Docker-based E2E tests that use
17- /// testcontainers for container lifecycle management. It only cleans directories
18- /// since Docker containers are automatically cleaned up when testcontainer objects
19- /// are dropped.
18+ /// testcontainers for container lifecycle management. It cleans up directories
19+ /// and any hanging Docker containers from previous interrupted test runs.
2020///
2121/// # Arguments
2222///
@@ -41,8 +41,8 @@ pub fn cleanup_lingering_resources(env: &TestEnvironment) -> Result<(), Prefligh
4141 // Clean the templates directory to ensure fresh embedded template extraction for E2E tests
4242 cleanup_templates_directory ( env) ?;
4343
44- // Note: Docker containers are automatically cleaned up by testcontainers when objects are dropped
45- // No need for explicit container cleanup like with LXD/OpenTofu
44+ // Clean up any hanging Docker containers from interrupted test runs
45+ cleanup_hanging_docker_containers ( env ) ;
4646
4747 info ! (
4848 operation = "preflight_cleanup_docker" ,
@@ -51,3 +51,109 @@ pub fn cleanup_lingering_resources(env: &TestEnvironment) -> Result<(), Prefligh
5151 ) ;
5252 Ok ( ( ) )
5353}
54+
55+ /// Clean up hanging Docker containers from interrupted test runs
56+ ///
57+ /// This function handles the case where testcontainers didn't clean up properly
58+ /// due to abrupt test termination. It removes containers with the instance name
59+ /// to prevent container name conflicts in subsequent test runs.
60+ ///
61+ /// # Safety
62+ ///
63+ /// This function is only intended for E2E test environments and should never
64+ /// be called in production code paths. It specifically targets test containers.
65+ ///
66+ /// # Arguments
67+ ///
68+ /// * `env` - The test environment containing the instance name
69+ fn cleanup_hanging_docker_containers ( env : & TestEnvironment ) {
70+ let instance_name = env. config . instance_name . as_str ( ) ;
71+ let command_executor = CommandExecutor :: new ( ) ;
72+
73+ info ! (
74+ operation = "hanging_container_cleanup" ,
75+ container_name = instance_name,
76+ "Checking for hanging Docker containers from previous test runs"
77+ ) ;
78+
79+ // First, check if the container exists
80+ let check_result = command_executor. run_command (
81+ "docker" ,
82+ & [ "ps" , "-aq" , "--filter" , & format ! ( "name={}" , instance_name) ] ,
83+ None ,
84+ ) ;
85+
86+ match check_result {
87+ Ok ( output) => {
88+ if output. trim ( ) . is_empty ( ) {
89+ info ! (
90+ operation = "hanging_container_cleanup" ,
91+ container_name = instance_name,
92+ status = "clean" ,
93+ "No hanging containers found"
94+ ) ;
95+ return ;
96+ }
97+
98+ info ! (
99+ operation = "hanging_container_cleanup" ,
100+ container_name = instance_name,
101+ "Found hanging container, attempting cleanup"
102+ ) ;
103+
104+ // Try to stop the container (in case it's running)
105+ match command_executor. run_command ( "docker" , & [ "stop" , instance_name] , None ) {
106+ Ok ( _) => {
107+ info ! (
108+ operation = "hanging_container_cleanup" ,
109+ container_name = instance_name,
110+ action = "stop" ,
111+ status = "success" ,
112+ "Container stopped successfully"
113+ ) ;
114+ }
115+ Err ( e) => {
116+ // Container might not be running, which is okay
117+ warn ! (
118+ operation = "hanging_container_cleanup" ,
119+ container_name = instance_name,
120+ action = "stop" ,
121+ status = "skipped" ,
122+ error = %e,
123+ "Could not stop container (probably not running)"
124+ ) ;
125+ }
126+ }
127+
128+ // Remove the container
129+ match command_executor. run_command ( "docker" , & [ "rm" , instance_name] , None ) {
130+ Ok ( _) => {
131+ info ! (
132+ operation = "hanging_container_cleanup" ,
133+ container_name = instance_name,
134+ status = "success" ,
135+ "Hanging container cleaned up successfully"
136+ ) ;
137+ }
138+ Err ( e) => {
139+ warn ! (
140+ operation = "hanging_container_cleanup" ,
141+ container_name = instance_name,
142+ status = "failed" ,
143+ error = %e,
144+ "Failed to remove hanging container (this may cause test failures)"
145+ ) ;
146+ }
147+ }
148+ }
149+ Err ( e) => {
150+ warn ! (
151+ operation = "hanging_container_cleanup" ,
152+ container_name = instance_name,
153+ status = "check_failed" ,
154+ error = %e,
155+ "Could not check for hanging containers"
156+ ) ;
157+ }
158+ }
159+ }
0 commit comments