Skip to content

Commit 48c05c8

Browse files
committed
feat: add Container*::is_running
1 parent 547e24e commit 48c05c8

File tree

5 files changed

+66
-3
lines changed

5 files changed

+66
-3
lines changed

testcontainers/src/core/client.rs

Lines changed: 18 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,8 @@ use std::{
77
use bollard::{
88
auth::DockerCredentials,
99
container::{
10-
Config, CreateContainerOptions, ListContainersOptions, LogOutput, LogsOptions,
11-
RemoveContainerOptions, UploadToContainerOptions,
10+
Config, CreateContainerOptions, InspectContainerOptions, ListContainersOptions, LogOutput,
11+
LogsOptions, RemoveContainerOptions, UploadToContainerOptions,
1212
},
1313
errors::Error as BollardError,
1414
exec::{CreateExecOptions, StartExecOptions, StartExecResults},
@@ -340,6 +340,22 @@ impl Client {
340340
.map_err(ClientError::UploadToContainerError)
341341
}
342342

343+
pub(crate) async fn container_is_running(
344+
&self,
345+
container_id: &str,
346+
) -> Result<bool, ClientError> {
347+
let container_info = self
348+
.bollard
349+
.inspect_container(container_id, Some(InspectContainerOptions { size: false }))
350+
.await
351+
.map_err(ClientError::InspectContainer)?;
352+
if let Some(state) = container_info.state {
353+
Ok(state.running.unwrap_or_default())
354+
} else {
355+
Ok(false)
356+
}
357+
}
358+
343359
pub(crate) async fn pull_image(&self, descriptor: &str) -> Result<(), ClientError> {
344360
let pull_options = Some(CreateImageOptions {
345361
from_image: descriptor,

testcontainers/src/core/containers/async_container.rs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -369,6 +369,12 @@ where
369369
Ok(stderr)
370370
}
371371

372+
/// Returns whether the container is still running.
373+
pub async fn is_running(&self) -> Result<bool> {
374+
let status = self.docker_client.container_is_running(&self.id).await?;
375+
Ok(status)
376+
}
377+
372378
pub(crate) async fn block_until_ready(&self, ready_conditions: Vec<WaitFor>) -> Result<()> {
373379
log::debug!("Waiting for container {} to be ready", self.id);
374380
let id = self.id();

testcontainers/src/core/containers/sync_container.rs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -209,6 +209,11 @@ where
209209
Ok(stderr)
210210
}
211211

212+
/// Returns whether the container is still running.
213+
pub async fn is_running(&self) -> Result<bool> {
214+
self.rt().block_on(self.async_impl().is_running())
215+
}
216+
212217
/// Returns reference to inner `Runtime`. It's safe to unwrap because it's `Some` until `Container` is dropped.
213218
fn rt(&self) -> &Arc<tokio::runtime::Runtime> {
214219
&self.inner.as_ref().unwrap().runtime

testcontainers/tests/async_runner.rs

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -267,3 +267,22 @@ async fn async_copy_files_to_container() -> anyhow::Result<()> {
267267

268268
Ok(())
269269
}
270+
271+
#[tokio::test]
272+
async fn async_container_is_running() -> anyhow::Result<()> {
273+
let _ = pretty_env_logger::try_init();
274+
275+
// Container that should run for 1 second
276+
let container = GenericImage::new("alpine", "latest")
277+
.with_cmd(vec!["sleep", "1"])
278+
.start()
279+
.await?;
280+
281+
assert!(container.is_running().await?);
282+
283+
// After waiting for two seconds it shouldn't be running anymore
284+
tokio::time::sleep(Duration::from_secs(2)).await;
285+
assert!(!container.is_running().await?);
286+
287+
Ok(())
288+
}

testcontainers/tests/sync_runner.rs

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
#![cfg(feature = "blocking")]
22

3-
use std::time::Instant;
3+
use std::time::{Duration, Instant};
44

55
use testcontainers::{
66
core::{
@@ -287,3 +287,20 @@ fn sync_copy_files_to_container() -> anyhow::Result<()> {
287287

288288
Ok(())
289289
}
290+
291+
fn sync_container_is_running() -> anyhow::Result<()> {
292+
let _ = pretty_env_logger::try_init();
293+
294+
// Container that should run for 1 second
295+
let container = GenericImage::new("alpine", "latest")
296+
.with_cmd(vec!["sleep", "1"])
297+
.start()?;
298+
299+
assert!(container.is_running()?);
300+
301+
// After waiting for two seconds it shouldn't be running anymore
302+
std::thread::sleep(Duration::from_secs(2));
303+
assert!(!container.is_running()?);
304+
305+
Ok(())
306+
}

0 commit comments

Comments
 (0)