Skip to content

Commit e337458

Browse files
committed
feat: Adds option to stop containers with a timeout
Previously containers were stopped with the system default timeout before issuing SIGKILL which was interfering with clean shutdown of processes being tested. This commit adds a new API of stop_with_timeout(Option<timeout_seconds>) in addition to the original stop() which retains its original functionality. A value of Some(-1) will issue a SIGTERM then wait indefinitely, a value of Some(t) will issue a SIGTERM then wait t seconds before issuing a SIGKILL. A value of None will follow the system default - often waiting 10 seconds, but this is configurable within the docker engine.
1 parent 9121760 commit e337458

File tree

5 files changed

+41
-9
lines changed

5 files changed

+41
-9
lines changed

testcontainers/src/core/client.rs

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -162,9 +162,16 @@ impl Client {
162162
.map_err(ClientError::RemoveContainer)
163163
}
164164

165-
pub(crate) async fn stop(&self, id: &str) -> Result<(), ClientError> {
165+
pub(crate) async fn stop(
166+
&self,
167+
id: &str,
168+
timeout_seconds: Option<i64>,
169+
) -> Result<(), ClientError> {
166170
self.bollard
167-
.stop_container(id, None)
171+
.stop_container(
172+
id,
173+
timeout_seconds.map(|t| bollard::container::StopContainerOptions { t }),
174+
)
168175
.await
169176
.map_err(ClientError::StopContainer)
170177
}

testcontainers/src/core/containers/async_container.rs

Lines changed: 19 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -280,11 +280,27 @@ where
280280
Ok(())
281281
}
282282

283-
/// Stops the container (not the same with `pause`).
283+
/// Stops the container (not the same with `pause`) using the default 10 second timeout
284284
pub async fn stop(&self) -> Result<()> {
285-
log::debug!("Stopping docker container {}", self.id);
285+
self.stop_with_timeout(None).await?;
286+
Ok(())
287+
}
288+
289+
/// Stops the container with timeout before issuing SIGKILL (not the same with `pause`).
290+
///
291+
/// Set Some(-1) to wait indefinitely, None to use system configured default and Some(0)
292+
/// to forcibly stop the container immediately - otherwise the runtime will issue SIGINT
293+
/// and then wait timeout_seconds seconds for the process to stop before issuing SIGKILL.
294+
pub async fn stop_with_timeout(&self, timeout_seconds: Option<i64>) -> Result<()> {
295+
log::debug!(
296+
"Stopping docker container {} with {} second timeout",
297+
self.id,
298+
timeout_seconds
299+
.map(|t| t.to_string())
300+
.unwrap_or("'system default'".into())
301+
);
286302

287-
self.docker_client.stop(&self.id).await?;
303+
self.docker_client.stop(&self.id, timeout_seconds).await?;
288304
Ok(())
289305
}
290306

testcontainers/src/core/containers/sync_container.rs

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -127,11 +127,21 @@ where
127127
})
128128
}
129129

130-
/// Stops the container (not the same with `pause`).
130+
/// Stops the container (not the same with `pause`) using the default 10 second timeout.
131131
pub fn stop(&self) -> Result<()> {
132132
self.rt().block_on(self.async_impl().stop())
133133
}
134134

135+
/// Stops the container with timeout before issuing SIGKILL (not the same with `pause`).
136+
///
137+
/// Set Some(-1) to wait indefinitely, None to use system configured default and Some(0)
138+
/// to forcibly stop the container immediately - otherwise the runtime will issue SIGINT
139+
/// and then wait timeout_seconds seconds for the process to stop before issuing SIGKILL.
140+
pub fn stop_with_timeout(&self, timeout_seconds: Option<i64>) -> Result<()> {
141+
self.rt()
142+
.block_on(self.async_impl().stop_with_timeout(timeout_seconds))
143+
}
144+
135145
/// Starts the container.
136146
pub fn start(&self) -> Result<()> {
137147
self.rt().block_on(self.async_impl().start())

testcontainers/src/runners/async_runner.rs

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -289,8 +289,7 @@ where
289289
res => res,
290290
}?;
291291

292-
let copy_to_sources: Vec<&CopyToContainer> =
293-
container_req.copy_to_sources().map(Into::into).collect();
292+
let copy_to_sources: Vec<&CopyToContainer> = container_req.copy_to_sources().collect();
294293

295294
for copy_to_source in copy_to_sources {
296295
client

testcontainers/src/watchdog.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ static WATCHDOG: Lazy<Mutex<Watchdog>> = Lazy::new(|| {
3434
.unwrap_or_default()
3535
{
3636
signal_docker
37-
.stop(&container_id)
37+
.stop(&container_id, None)
3838
.await
3939
.expect("failed to stop container");
4040
signal_docker

0 commit comments

Comments
 (0)