### Basic Docker Compose Usage Example Source: https://github.com/testcontainers/testcontainers-rs/blob/main/docs/features/docker_compose.md Demonstrates starting services defined in a compose file and accessing a 'web' service by name. ```rust use testcontainers::compose::DockerCompose; #[tokio::test] async fn test_with_compose() -> Result<(), Box> { let mut compose = DockerCompose::with_local_client(&["tests/docker-compose.yml"]); compose.up().await?; // Access service by name let web = compose.service("web").expect("web service"); let port = web.get_host_port_ipv4(8080).await?; let response = reqwest::get(format!("http://localhost:{}", port)).await?; assert!(response.status().is_success()); Ok(()) // Automatic cleanup on drop } ``` -------------------------------- ### Start PostgreSQL Container with SyncRunner Source: https://context7.com/testcontainers/testcontainers-rs/llms.txt Demonstrates starting a PostgreSQL container using the synchronous API with `SyncRunner`. Configure user, password, database, and wait for a specific message on stderr before proceeding. Requires the `blocking` feature. ```rust use testcontainers:: core::{IntoContainerPort, WaitFor}, runners::SyncRunner, GenericImage, ImageExt, }; #[test] fn test_postgres_sync() { let container = GenericImage::new("postgres", "16-alpine") .with_exposed_port(5432.tcp()) .with_env_var("POSTGRES_USER", "test") .with_env_var("POSTGRES_PASSWORD", "test") .with_env_var("POSTGRES_DB", "testdb") .with_wait_for(WaitFor::message_on_stderr("database system is ready to accept connections")) .start() .expect("Failed to start Postgres"); let port = container.get_host_port_ipv4(5432.tcp()).unwrap(); let conn_str = format!("postgres://test:test@localhost:{port}/testdb"); println!("Postgres at {conn_str}"); } ``` -------------------------------- ### Complete Docker Compose Integration Example Source: https://github.com/testcontainers/testcontainers-rs/blob/main/docs/features/docker_compose.md A comprehensive example demonstrating how to set up, run, and interact with services defined in a Docker Compose file within a Rust integration test. It includes setting environment variables, accessing service ports, and performing basic operations on database and Redis services. ```rust use testcontainers::{ compose::DockerCompose, core::IntoContainerPort, }; #[tokio::test] async fn integration_test_with_compose() -> Result<(), Box> { let mut compose = DockerCompose::with_local_client(&[ "tests/docker-compose.yml", ]) .with_env("POSTGRES_PASSWORD", "test-password") .with_env("REDIS_MAXMEMORY", "256mb"); compose.up().await?; // List all running services println!("Running services: {:?}", compose.services()); // Access database let db_port = compose.get_host_port_ipv4("db", 5432).await?; let db_url = format!("postgres://postgres:test-password@localhost:{}/test", db_port); let db_pool = sqlx::PgPool::connect(&db_url).await?; // Run migrations or setup sqlx::query("CREATE TABLE IF NOT EXISTS users (id INT PRIMARY KEY)") .execute(&db_pool) .await?; // Access Redis let redis_port = compose.get_host_port_ipv4("redis", 6379).await?; let redis_client = redis::Client::open(format!("redis://localhost:{}", redis_port))?; let mut con = redis_client.get_connection()?; redis::cmd("SET").arg("test-key").arg("test-value").query::<()>(&mut con)?; // Access web service let web_port = compose.get_host_port_ipv4("web", 8080).await?; let response = reqwest::get(format!("http://localhost:{}/health", web_port)) .await?; let response_text = response.text().await?; assert_eq!(response_text, "OK"); Ok(()) // Automatic cleanup: containers, networks, and volumes are removed } ``` -------------------------------- ### Enable and Start Podman Socket Source: https://github.com/testcontainers/testcontainers-rs/blob/main/docs/features/configuration.md Use these commands to enable and start the Podman user socket, which is necessary for Podman compatibility with Testcontainers. ```sh systemctl --user enable podman.socket systemctl --user start podman.socket systemctl --user status podman.socket ``` -------------------------------- ### Start a Generic Redis Container Source: https://github.com/testcontainers/testcontainers-rs/blob/main/docs/features/creating_container.md Use this snippet to start a generic Redis container with exposed ports, environment variables, and a specific readiness check. Ensure you have the necessary imports. ```rust use testcontainers:: core::{IntoContainerPort, WaitFor}, runners::AsyncRunner, GenericImage, ImageExt; let _container = GenericImage::new("redis", "7.2.4") .with_exposed_port(6379.tcp()) .with_env_var("DEBUG", "1") .with_wait_for(WaitFor::message_on_stdout("Ready to accept connections")) .start() .await .unwrap(); ``` -------------------------------- ### Listing All Services in Docker Compose Source: https://github.com/testcontainers/testcontainers-rs/blob/main/docs/features/docker_compose.md Demonstrates how to iterate over and print the names of all services defined in the Docker Compose setup. ```rust compose.up().await?; for service_name in compose.services() { println!("Service: {}", service_name); } ``` -------------------------------- ### Run Redis Container (Async API) Source: https://github.com/testcontainers/testcontainers-rs/blob/main/README.md Demonstrates how to start a Redis container using the asynchronous API with Tokio. ```rust use testcontainers::{core::{IntoContainerPort, WaitFor}, runners::AsyncRunner, GenericImage, ImageExt}; #[tokio::test] async fn test_redis() { let container = GenericImage::new("redis", "7.2.4") .with_exposed_port(6379.tcp()) .with_wait_for(WaitFor::message_on_stdout("Ready to accept connections")) .with_network("bridge") .with_env_var("DEBUG", "1") .start() .await .expect("Failed to start Redis"); } ``` -------------------------------- ### Start Docker Compose Stack with Testcontainers Source: https://context7.com/testcontainers/testcontainers-rs/llms.txt Use `DockerCompose` to start a full `docker compose` stack. Requires the `docker-compose` Cargo feature. Access each service's container API after starting. ```toml [dev-dependencies] testcontainers = { version = "0.27", features = ["docker-compose"] } ``` ```rust use testcontainers::compose::DockerCompose; // docker-compose.yml: // services: // db: // image: postgres:16 // environment: // POSTGRES_PASSWORD: ${POSTGRES_PASSWORD} // ports: ["5432"] // cache: // image: redis:7-alpine // ports: ["6379"] #[tokio::test] async fn test_with_compose() -> anyhow::Result<()> { let mut compose = DockerCompose::with_local_client(&["tests/docker-compose.yml"]) .with_env("POSTGRES_PASSWORD", "secret"); compose.up().await?; // Resolve host ports for each service let db_port = compose.get_host_port_ipv4("db", 5432).await?; let cache_port = compose.get_host_port_ipv4("cache", 6379).await?; println!("Postgres: localhost:{db_port}, Redis: localhost:{cache_port}"); // Full ContainerAsync API via service() let db = compose.service("db").expect("db must exist"); let logs = String::from_utf8(db.stderr_to_vec().await?)?; assert!(logs.contains("ready to accept")); // Enumerate running services for name in compose.services() { println!("Running service: {name}"); } // Explicit teardown (also happens automatically on drop) compose.down().await?; Ok(()) } ``` -------------------------------- ### Use PostgreSQL Module with SyncRunner Source: https://github.com/testcontainers/testcontainers-rs/blob/main/docs/quickstart/community_modules.md Example of starting a PostgreSQL container using `SyncRunner`. Ensure the `postgres` and `blocking` features are enabled for `testcontainers-modules`. ```rust use testcontainers_modules::{postgres, testcontainers::runners::SyncRunner}; #[test] fn test_with_postgres() { let container = postgres::Postgres::default().start().unwrap(); let host_port = container.get_host_port_ipv4(5432).unwrap(); let connection_string = &format!( "postgres://postgres:postgres@127.0.0.1:{host_port}/postgres", ); } ``` -------------------------------- ### Run Redis Container (Blocking API) Source: https://github.com/testcontainers/testcontainers-rs/blob/main/README.md Demonstrates how to start a Redis container using the blocking API. Requires the 'blocking' feature to be enabled. ```rust use testcontainers::{core::{IntoContainerPort, WaitFor}, runners::SyncRunner, GenericImage, ImageExt}; #[test] fn test_redis() { let container = GenericImage::new("redis", "7.2.4") .with_exposed_port(6379.tcp()) .with_wait_for(WaitFor::message_on_stdout("Ready to accept connections")) .with_network("bridge") .with_env_var("DEBUG", "1") .start() .expect("Failed to start Redis"); } ``` -------------------------------- ### Sample Docker Compose File Source: https://github.com/testcontainers/testcontainers-rs/blob/main/docs/features/docker_compose.md An example `docker-compose.yml` file demonstrating service definitions for a PostgreSQL database, Redis cache, and a web application, including environment variable usage and port mappings. ```yaml services: db: image: postgres:16 environment: POSTGRES_PASSWORD: ${POSTGRES_PASSWORD} POSTGRES_DB: test ports: - "5432" redis: image: redis:7-alpine command: redis-server --maxmemory ${REDIS_MAXMEMORY:-128mb} ports: - "6379" web: image: my-web-app:latest environment: DATABASE_URL: postgres://postgres:${POSTGRES_PASSWORD}@db:5432/test REDIS_URL: redis://redis:6379 ports: - "8080" depends_on: - db - redis ``` -------------------------------- ### Minimal Redis Test with Docker Compose Source: https://github.com/testcontainers/testcontainers-rs/blob/main/docs/features/docker_compose.md A basic example demonstrating how to spin up a Redis service defined in a Docker Compose file and interact with it. ```rust use testcontainers::compose::DockerCompose; #[tokio::test] async fn test_redis() -> Result<(), Box> { let mut compose = DockerCompose::with_local_client(&["tests/docker-compose.yml"]); compose.up().await?; let redis = compose.service("redis").expect("redis service"); let port = redis.get_host_port_ipv4(6379).await?; // Use redis at localhost:{port} let client = redis::Client::open(format!("redis://localhost:{}", port))?; let mut con = client.get_connection()?; redis::cmd("PING").query::(&mut con)?; Ok(()) } ``` ```yaml services: redis: image: redis:7-alpine ports: - "6379" ``` -------------------------------- ### Copying Files Into Containers (Before Startup) Source: https://github.com/testcontainers/testcontainers-rs/blob/main/docs/features/files.md Stage files or directories into the container's filesystem before it starts using `ImageExt::with_copy_to`. This method accepts raw bytes or host paths and preserves nested directory structures. You can also override file metadata like permissions using `CopyTargetOptions`. ```APIDOC ## Copying Files Into Containers (Before Startup) Use `ImageExt::with_copy_to` to stage files or directories before the container starts. Content can come from raw bytes or host paths: ```rust // Example: copying inline bytes and directories into a container use testcontainers::{GenericImage, WaitFor}; let project_assets = std::path::Path::new("tests/fixtures/assets"); let image = GenericImage::new("alpine", "latest") .with_wait_for(WaitFor::seconds(1)) .with_copy_to("/opt/app/config.yaml", br"mode = \"test\"".to_vec()) .with_copy_to("/opt/app/assets", project_assets); ``` Everything is packed into a TAR archive, preserving nested directories. The helper accepts either `Vec` or any path-like value implementing `CopyDataSource`. By default, the destination path inherits the mode of the source file on Unix hosts (or falls back to `0o644` elsewhere). Use `CopyTargetOptions` when you need to override per-copy metadata such as permissions: ```rust use testcontainers::{CopyTargetOptions, GenericImage, ImageExt}; let image = GenericImage::new("alpine", "latest") .with_copy_to( CopyTargetOptions::new("/opt/app/secret.yaml").with_mode(0o600), "./fixtures/secret.yaml", ) .with_copy_to( CopyTargetOptions::new("/opt/app/blob.bin").with_mode(0o640), br"raw bytes".to_vec(), ); ``` `CopyTargetOptions::new` wraps any path-like target and keeps backward compatibility with string literals—existing code continues to compile. Symbolic links still follow Docker’s TAR semantics; the `mode` override only applies to the final file entry recorded in the archive. ``` -------------------------------- ### Using Local Docker Compose Client Source: https://github.com/testcontainers/testcontainers-rs/blob/main/docs/features/docker_compose.md Initializes Docker Compose using the locally installed Docker CLI and Compose plugin. Requires Compose files to be present on the filesystem. ```rust let mut compose = DockerCompose::with_local_client(&["docker-compose.yml"]); compose.up().await?; ``` -------------------------------- ### Start Redis Container with AsyncRunner Source: https://context7.com/testcontainers/testcontainers-rs/llms.txt Use GenericImage to start a Redis container with specific configurations like exposed ports, environment variables, and readiness checks. The container is automatically stopped when it goes out of scope. Requires `tokio` runtime. ```rust use testcontainers:: core::{IntoContainerPort, WaitFor}, runners::AsyncRunner, GenericImage, ImageExt, }; #[tokio::test] async fn test_redis() -> Result<(), Box> { let container = GenericImage::new("redis", "7.2.4") .with_exposed_port(6379.tcp()) .with_wait_for(WaitFor::message_on_stdout("Ready to accept connections")) .with_env_var("REDIS_LOGLEVEL", "verbose") .with_startup_timeout(std::time::Duration::from_secs(30)) .start() .await?; let host = container.get_host().await?; let port = container.get_host_port_ipv4(6379.tcp()).await?; let url = format!("redis://{host}:{port}"); println!("Redis available at {url}"); // container is stopped and removed when it drops out of scope Ok(()) } ``` -------------------------------- ### Accessing Service Container API Source: https://github.com/testcontainers/testcontainers-rs/blob/main/docs/features/docker_compose.md Shows how to get a service container reference after `up()` and use its full API, including getting ports and executing commands. ```rust compose.up().await?; let redis = compose.service("redis").expect("redis service exists"); // Full container API available let port = redis.get_host_port_ipv4(6379).await?; let logs = redis.stdout(true); redis.exec(ExecCommand::new(["redis-cli", "PING"])).await?; ``` -------------------------------- ### Spin up a Redis Container (Async) Source: https://github.com/testcontainers/testcontainers-rs/blob/main/docs/quickstart/testcontainers.md Use `GenericImage` to create and start a Redis container. It exposes port 6379 and waits for a specific log message indicating readiness. The container is automatically removed when it goes out of scope due to RAII. ```rust use testcontainers::{ core::{IntoContainerPort, WaitFor}, runners::AsyncRunner, GenericImage, }; #[tokio::test] async fn test_redis() { let _container = GenericImage::new("redis", "7.2.4") .with_exposed_port(6379.tcp()) .with_wait_for(WaitFor::message_on_stdout("Ready to accept connections")) .start() .await .unwrap(); } ``` -------------------------------- ### Define and Start a Custom Redis Image Source: https://github.com/testcontainers/testcontainers-rs/blob/main/docs/features/creating_container.md Define a custom image type implementing the `Image` trait for reusable defaults, then start it. This approach is useful for popular services and allows for custom configurations. ```rust use testcontainers:: core::{ContainerPort, WaitFor}, runners::AsyncRunner, Image; #[derive(Debug, Clone)] struct RedisImage { ports: [ContainerPort; 1], } impl Default for RedisImage { fn default() -> Self { Self { ports: [ContainerPort::Tcp(6379)], } } } impl Image for RedisImage { fn name(&self) -> &str { "redis" } fn tag(&self) -> &str { "7.2.4" } fn ready_conditions(&self) -> Vec { vec![WaitFor::message_on_stdout("Ready to accept connections")] } fn expose_ports(&self) -> &[ContainerPort] { &self.ports } } let _container = RedisImage::default().start().await.unwrap(); ``` -------------------------------- ### Mount Host Directory for Read/Write Access Source: https://github.com/testcontainers/testcontainers-rs/blob/main/docs/features/files.md Use `Mount::bind` to create bind or tmpfs mounts for writable workspaces. This example demonstrates mounting a host directory for read/write access to a container path, suitable for large data or efficient I/O. ```rust use std::path::Path; use testcontainers::core::{mounts::Mount, AccessMode, MountType}; let host_data = Path::new("/var/tmp/integration-data"); let mount = Mount::bind(host_data, "/workspace") .with_mode(AccessMode::ReadWrite) .with_type(MountType::Bind); let image = GenericImage::new("python", "3.13") .with_mount(mount) .with_cmd(["python", "/workspace/run.py"]); ``` -------------------------------- ### Using Containerised Docker Compose Client Source: https://github.com/testcontainers/testcontainers-rs/blob/main/docs/features/docker_compose.md Initializes Docker Compose by running the `docker compose` command inside a container. This eliminates the need for a local Docker CLI installation and ensures consistent Compose version usage. ```rust let mut compose = DockerCompose::with_containerised_client(&["docker-compose.yml"]).await; compose.up().await?; ``` -------------------------------- ### Execute Command and Get Result Source: https://github.com/testcontainers/testcontainers-rs/blob/main/docs/features/exec_commands.md Execute a prepared `ExecCommand` on a container and retrieve the results. The `ExecResult` provides methods to access the exit code, stdout, and stderr. ```rust let result = container.exec(command).await?; let exit_code = result.exit_code().await?; let stdout = result.stdout_to_vec().await?; let stderr = result.stderr_to_vec().await?; ``` -------------------------------- ### Configure SyncRunner for Blocking API Source: https://context7.com/testcontainers/testcontainers-rs/llms.txt Enable the `blocking` feature in Cargo.toml to use `SyncRunner` for starting containers in non-async tests. The API is identical to `AsyncRunner` but uses blocking calls. ```toml # Cargo.toml [dev-dependencies] testcontainers = { version = "0.27", features = ["blocking"] } ``` -------------------------------- ### Copy Files and Directories into Container Source: https://github.com/testcontainers/testcontainers-rs/blob/main/docs/features/files.md Use `with_copy_to` to stage files or directories from host paths or raw bytes into the container before it starts. Preserves nested directories and allows overriding file permissions with `CopyTargetOptions`. ```rust use testcontainers::{GenericImage, WaitFor}; let project_assets = std::path::Path::new("tests/fixtures/assets"); let image = GenericImage::new("alpine", "latest") .with_wait_for(WaitFor::seconds(1)) .with_copy_to("/opt/app/config.yaml", br#"mode = \"test\""#.to_vec()) .with_copy_to("/opt/app/assets", project_assets); ``` ```rust use testcontainers::{CopyTargetOptions, GenericImage, ImageExt}; let image = GenericImage::new("alpine", "latest") .with_copy_to( CopyTargetOptions::new("/opt/app/secret.yaml").with_mode(0o600), "./fixtures/secret.yaml", ) .with_copy_to( CopyTargetOptions::new("/opt/app/blob.bin").with_mode(0o640), br"raw bytes".to_vec(), ); ``` -------------------------------- ### Configure Build and Pull Options for Docker Compose Source: https://github.com/testcontainers/testcontainers-rs/blob/main/docs/features/docker_compose.md Specify whether to build images defined in the compose file or pull the latest images before starting the stack. Both `with_build(true)` and `with_pull(true)` can be used together. ```rust let mut compose = DockerCompose::with_local_client(&["docker-compose.yml"]) .with_build(true) // Build images defined in compose file .with_pull(true); // Pull latest images before starting compose.up().await?; ``` -------------------------------- ### Copy Files To and From Containers in Rust Source: https://context7.com/testcontainers/testcontainers-rs/llms.txt Stage inline bytes and local files into a container before it starts, or pull files from a running container to a local path or memory. ```rust use tempfile::tempdir; use testcontainers:: core::{CmdWaitFor, ExecCommand, WaitFor}, runners::AsyncRunner, CopyTargetOptions, GenericImage, ImageExt, ; #[tokio::test] async fn copy_files_example() -> anyhow::Result<()> { // Stage inline bytes and a local file into the container let container = GenericImage::new("alpine", "3.19") .with_copy_to("/app/config.toml", br#"mode = "test""#.to_vec()) .with_copy_to( CopyTargetOptions::new("/app/secret.yaml").with_mode(0o600), br"api_key: test-key-abc".to_vec(), ) .with_cmd(["sh", "-c", "cat /app/config.toml > /tmp/out.txt && sleep 5"]) .with_wait_for(WaitFor::seconds(2)) .start() .await?; // Copy a file back from the running container let dir = tempdir()?; let dest = dir.path().join("out.txt"); container.copy_file_from("/tmp/out.txt", dest.as_path()).await?; let content = tokio::fs::read_to_string(&dest).await?; assert!(content.contains("test")); // Copy to memory instead of a file let mut bytes = Vec::new(); container.copy_file_from("/app/config.toml", &mut bytes).await?; assert_eq!(bytes, br#"mode = "test""#); Ok(()) } ``` -------------------------------- ### Implement Lifecycle Hooks for Custom Docker Image Source: https://context7.com/testcontainers/testcontainers-rs/llms.txt Override `exec_before_ready` to run commands before readiness checks and `exec_after_start` to run commands after readiness. Ensure commands have appropriate readiness conditions. ```rust use testcontainers:: core::{CmdWaitFor, ContainerPort, ContainerState, ExecCommand, WaitFor}, runners::AsyncRunner, Image; struct MyDbImage; impl Image for MyDbImage { fn name(&self) -> &str { "mydb" } fn tag(&self) -> &str { "latest" } fn ready_conditions(&self) -> Vec { vec![WaitFor::message_on_stdout("Server started")] } fn expose_ports(&self) -> &[ContainerPort] { &[ContainerPort::Tcp(5000)] } // Runs commands BEFORE ready_conditions are evaluated fn exec_before_ready( &self, cs: ContainerState, ) -> testcontainers::core::error::Result> { Ok(vec![ ExecCommand::new(["mydb-cli", "init", "--force"]) .with_cmd_ready_condition(CmdWaitFor::exit_code(0)), ]) } // Runs commands AFTER ready_conditions pass fn exec_after_start( &self, cs: ContainerState, ) -> testcontainers::core::error::Result> { Ok(vec![ ExecCommand::new(["mydb-cli", "create-schema", "test"]) .with_cmd_ready_condition(CmdWaitFor::message_on_stdout("Schema created")), ]) } } #[tokio::test] async fn test_lifecycle_hooks() -> anyhow::Result<()> { let _container = MyDbImage.start().await?; Ok(()) } ``` -------------------------------- ### Configure Custom Health Check for MySQL Container Source: https://context7.com/testcontainers/testcontainers-rs/llms.txt Configures a custom health check for a MySQL container using `Healthcheck::cmd`. It specifies the command to run, interval, timeout, retries, and start period. The container is then started and waits for the health check to pass. ```rust use std::time::Duration; use testcontainers::{ core::{Healthcheck, WaitFor}, runners::AsyncRunner, GenericImage, ImageExt, }; #[tokio::test] async fn test_mysql_healthcheck() -> anyhow::Result<()> { let container = GenericImage::new("mysql", "8.0") .with_env_var("MYSQL_ROOT_PASSWORD", "rootpass") .with_env_var("MYSQL_DATABASE", "testdb") .with_health_check( Healthcheck::cmd(["mysqladmin", "ping", "-h", "localhost", "-uroot", "-prootpass"]) .with_interval(Duration::from_secs(2)) .with_timeout(Duration::from_secs(1)) .with_retries(10) .with_start_period(Duration::from_secs(5)), ) .with_wait_for(WaitFor::healthcheck()) .start() .await?; println!("MySQL container id: {}", container.id()); Ok(()) } ``` -------------------------------- ### Create an ExecCommand Source: https://github.com/testcontainers/testcontainers-rs/blob/main/docs/features/exec_commands.md Construct an `ExecCommand` to specify the command to run and its readiness conditions. Use `CmdWaitFor::message_on_stdout` to wait for a specific output. ```rust let command = ExecCommand::new(vec!["echo", "Hello, World!"]) .with_container_ready_conditions(vec![/* conditions */]) .with_cmd_ready_condition(CmdWaitFor::message_on_stdout("Hello, World!")); ``` -------------------------------- ### Using Mounts for Writable Workspaces Source: https://github.com/testcontainers/testcontainers-rs/blob/main/docs/features/files.md Employ `Mount` helpers for bind or tmpfs mounts when direct file copying is not suitable. Bind mounts allow read/write access to host directories, while tmpfs mounts provide ephemeral in-memory storage for scratch data or caches. Configure access modes and mount types as needed. ```APIDOC ## Using Mounts for Writable Workspaces When a bind or tmpfs mount fits better than copy semantics, use the `Mount` helpers: ```rust // Example: mounting a host directory for read/write access use std::path::Path; use testcontainers::core::{mounts::Mount, AccessMode, MountType}; let host_data = Path::new("/var/tmp/integration-data"); let mount = Mount::bind(host_data, "/workspace") .with_mode(AccessMode::ReadWrite) .with_type(MountType::Bind); let image = GenericImage::new("python", "3.13") .with_mount(mount) .with_cmd(["python", "/workspace/run.py"]); ``` Bind mounts share host state directly. Tmpfs mounts create ephemeral in-memory storage useful for scratch data or caches. ``` -------------------------------- ### Dockerfile ARG Instruction Placement Source: https://github.com/testcontainers/testcontainers-rs/blob/main/docs/features/building_images.md Illustrates the correct placement of `ARG` instructions in a Dockerfile, emphasizing that they must appear before their first usage. ```dockerfile # Correct ARG VERSION ENV APP_VERSION=$VERSION # Incorrect - ARG comes after usage ENV APP_VERSION=$VERSION ARG VERSION ``` -------------------------------- ### Building Multiple Image Variants Source: https://github.com/testcontainers/testcontainers-rs/blob/main/docs/features/building_images.md Demonstrates building multiple variants of an application image, each configured with different build arguments and ports. ```rust async fn build_variant(variant: &str, port: u16) -> Result> { GenericBuildableImage::new("my-app", variant) .with_dockerfile_string(format!(r#" FROM alpine:latest ARG PORT ENV APP_PORT=$PORT CMD ["sh", "-c", "echo 'Running on port $APP_PORT' && sleep infinity"] "#)) .build_image_with( BuildImageOptions::new() .with_build_arg("PORT", port.to_string()) .with_skip_if_exists(true) ) .await } #[tokio::test] async fn test_multiple_variants() -> Result<(), Box> { let image1 = build_variant("dev", 8080).await?; let image2 = build_variant("staging", 8081).await?; // Use images in tests... Ok(()) } ``` -------------------------------- ### Using Auto Docker Compose Client Source: https://github.com/testcontainers/testcontainers-rs/blob/main/docs/features/docker_compose.md Initializes Docker Compose by first attempting to use the local Docker CLI and falling back to the containerised client if the local CLI is unavailable. ```rust use testcontainers::compose::{AutoComposeOptions, DockerCompose}; let options = AutoComposeOptions::new(&["docker-compose.yml"]); let mut compose = DockerCompose::with_auto_client(options).await?; compose.up().await?; ``` -------------------------------- ### Configure Healthchecks for Docker Compose Source: https://github.com/testcontainers/testcontainers-rs/blob/main/docs/features/docker_compose.md Define healthcheck configurations in your Docker Compose file to ensure services are healthy before Testcontainers proceeds. This example uses Nginx with a curl command to verify the web server is responding. ```yaml services: web: image: nginx healthcheck: test: ["CMD", "curl", "-f", "http://localhost"] interval: 5s timeout: 3s retries: 3 ports: - "80" ``` -------------------------------- ### Descriptive Image Tagging Source: https://github.com/testcontainers/testcontainers-rs/blob/main/docs/features/building_images.md Illustrates good practice for naming and tagging Docker images to prevent conflicts. ```rust // Good: specific and descriptive GenericBuildableImage::new("test-user-service", "integration-v1") // Avoid: generic names that might conflict GenericBuildableImage::new("test", "latest") ``` -------------------------------- ### Build Image Options: Combining Options Source: https://github.com/testcontainers/testcontainers-rs/blob/main/docs/features/building_images.md Chain multiple build options together to customize the Docker image build process. This allows for flexible configuration of build behavior. ```rust let image = GenericBuildableImage::new("my-app", "latest") .with_dockerfile_string("FROM alpine:latest\nARG VERSION") .build_image_with( BuildImageOptions::new() .with_skip_if_exists(true) .with_no_cache(false) .with_build_arg("VERSION", "1.0.0") ) .await?; ``` -------------------------------- ### Build Image Options: Build Arguments Source: https://github.com/testcontainers/testcontainers-rs/blob/main/docs/features/building_images.md Pass build-time variables to your Dockerfile using `ARG` instructions. This allows for dynamic configuration of the image during the build process. ```rust let image = GenericBuildableImage::new("my-app", "latest") .with_dockerfile_string( r#" FROM alpine:latest ARG VERSION ARG BUILD_DATE RUN echo \"Building version ${VERSION} on ${BUILD_DATE}\" "#, ) .build_image_with( BuildImageOptions::new() .with_build_arg("VERSION", "1.0.0") .with_build_arg("BUILD_DATE", "2024-10-25") ) .await?; ``` ```rust use std::collections::HashMap; let mut args = HashMap::new(); args.insert("VERSION".to_string(), "1.0.0".to_string()); args.insert("ENVIRONMENT".to_string(), "test".to_string()); let image = GenericBuildableImage::new("my-app", "latest") .with_dockerfile_string("FROM alpine:latest\nARG VERSION\nARG ENVIRONMENT") .build_image_with( BuildImageOptions::new() .with_build_args(args) ) .await?; ``` -------------------------------- ### Accessing Ports, Logs, and Executing Commands on Services Source: https://github.com/testcontainers/testcontainers-rs/blob/main/docs/features/docker_compose.md Illustrates accessing mapped ports (IPv4 and IPv6), streaming logs (stdout/stderr), executing commands, and retrieving container information. ```rust compose.up().await?; let redis = compose.service("redis").expect("redis service"); // Get mapped ports let port = redis.get_host_port_ipv4(6379).await?; let ipv6_port = redis.get_host_port_ipv6(6379).await?; // Stream logs let stdout = redis.stdout(true); let stderr = redis.stderr(false); // Execute commands let result = redis.exec(ExecCommand::new(["redis-cli", "PING"])).await?; // Get container info let container_id = redis.id(); let host = redis.get_host().await?; ``` -------------------------------- ### Build a Simple Custom Docker Image Source: https://github.com/testcontainers/testcontainers-rs/blob/main/docs/features/building_images.md Define and build a Docker image from a Dockerfile string and inline data. This is useful for testing against custom application images. ```rust use testcontainers::{ core::WaitFor, runners::AsyncRunner, GenericBuildableImage, }; #[tokio::test] async fn test_custom_image() -> Result<(), Box> { let image = GenericBuildableImage::new("my-test-app", "latest") .with_dockerfile_string( r#" FROM alpine:latest COPY --chmod=0755 app.sh /usr/local/bin/app ENTRYPOINT ["/usr/local/bin/app"] "#, ) .with_data( "#!/bin/sh echo \"Hello from custom image!\" ", "./app.sh", ) .build_image() .await?; let container = image .with_wait_for(WaitFor::message_on_stdout("Hello from custom image!")) .start() .await?; Ok(()) } ``` -------------------------------- ### Configure Docker Host via properties-config Feature Source: https://context7.com/testcontainers/testcontainers-rs/llms.txt Use the `properties-config` feature to enable the `~/.testcontainers.properties` file for configuring Docker host and TLS settings. ```toml # ~/.testcontainers.properties (requires properties-config feature) tc.host=tcp://remote-docker:2376 docker.tls.verify=1 docker.cert.path=/home/user/.docker/certs ``` -------------------------------- ### Using Build Arguments for Flexibility Source: https://github.com/testcontainers/testcontainers-rs/blob/main/docs/features/building_images.md Configures a test image using build arguments, allowing for dynamic versioning or configuration during the build process. ```rust fn build_test_image(version: &str) -> GenericBuildableImage { GenericBuildableImage::new("my-app", version) .with_dockerfile_string("FROM alpine:latest\nARG APP_VERSION\nENV VERSION=$APP_VERSION") .build_image_with( BuildImageOptions::new() .with_build_arg("APP_VERSION", version) ) } ``` -------------------------------- ### Build and Run Custom Docker Images in Rust Source: https://context7.com/testcontainers/testcontainers-rs/llms.txt Build a custom Docker image on the fly using BuildKit from a Dockerfile string and associated data. The resulting `GenericImage` can then be run. ```rust use testcontainers:: core::{BuildImageOptions, WaitFor}, runners::{AsyncBuilder, AsyncRunner}, GenericBuildableImage, ImageExt, ; #[tokio::test] async fn build_and_run_custom_image() -> anyhow::Result<()> { let image = GenericBuildableImage::new("my-test-app", "v1") .with_dockerfile_string( r###"FROM alpine:3.19 COPY app.sh /usr/local/bin/app RUN chmod +x /usr/local/bin/app CMD ["/usr/local/bin/app"]"###, ) .with_data( "#!/bin/sh\necho 'App is running!'\nsleep 30\n", "./app.sh", ) .build_image_with( BuildImageOptions::new() .with_skip_if_exists(true) // skip rebuild if already built .with_no_cache(false) .with_build_arg("VERSION", "1.0.0"), ) .await?; let container = image .with_wait_for(WaitFor::message_on_stdout("App is running!")) .start() .await?; println!("Container {} is running", container.id()); Ok(()) } ``` -------------------------------- ### Mount Host Directories and Tmpfs in Rust Source: https://context7.com/testcontainers/testcontainers-rs/llms.txt Map host paths into containers using bind mounts or create ephemeral in-memory storage with tmpfs. Attach these mounts using `ImageExt::with_mount`. ```rust use std::path::PathBuf; use testcontainers:: core:: mounts::{AccessMode, Mount, MountType}, WaitFor, , runners::AsyncRunner, GenericImage, ImageExt, ; #[tokio::test] async fn mount_example() -> anyhow::Result<()> { let host_dir = PathBuf::from("/tmp/test-workspace"); tokio::fs::create_dir_all(&host_dir).await?; tokio::fs::write(host_dir.join("input.txt"), "hello from host").await?; let container = GenericImage::new("alpine", "3.19") .with_mount(Mount::bind_mount( host_dir.to_str().unwrap(), "/workspace", )) .with_cmd(["sh", "-c", "cat /workspace/input.txt > /workspace/output.txt"]) .with_wait_for(WaitFor::seconds(1)) .start() .await?; let output = tokio::fs::read_to_string(host_dir.join("output.txt")).await?; assert_eq!(output, "hello from host"); Ok(()) } ``` -------------------------------- ### Using Multiple Docker Compose Files Source: https://github.com/testcontainers/testcontainers-rs/blob/main/docs/features/docker_compose.md Integrate multiple compose files, such as a base file and an override file, by providing a slice of file paths to `DockerCompose::with_local_client`. ```rust let mut compose = DockerCompose::with_local_client(&[ "docker-compose.yml", "docker-compose.test.yml", ]); compose.up().await?; ``` -------------------------------- ### Synchronous Image Build with Options Source: https://github.com/testcontainers/testcontainers-rs/blob/main/docs/features/building_images.md Builds a Docker image synchronously, applying specific build options such as skip_if_exists and build arguments. ```rust use testcontainers:: core::BuildImageOptions, runners::{SyncBuilder, SyncRunner}, GenericBuildableImage; #[test] fn test_sync_build_with_options() -> Result<(), Box> { let image = GenericBuildableImage::new("my-app", "latest") .with_dockerfile_string("FROM alpine:latest\nARG VERSION") .build_image_with( BuildImageOptions::new() .with_skip_if_exists(true) .with_build_arg("VERSION", "1.0.0") )?; let container = image.start()?; Ok(()) } ``` -------------------------------- ### Inspect and Control a Running Container Source: https://context7.com/testcontainers/testcontainers-rs/llms.txt Demonstrates how to inspect and control a running container. This includes resolving host and port, checking liveness, streaming logs, pausing/unpausing, stopping the container, and querying its exit code. ```rust use testcontainers::{ core::{ExecCommand, IntoContainerPort, WaitFor}, runners::AsyncRunner, GenericImage, ImageExt, }; use tokio::io::AsyncBufReadExt; #[tokio::test] async fn inspect_and_control_container() -> anyhow::Result<()> { let container = GenericImage::new("testcontainers/helloworld", "1.3.0") .with_exposed_port(8080.tcp()) .start() .await?; // Resolve connection details let host = container.get_host().await?; let port = container.get_host_port_ipv4(8080.tcp()).await?; println!("Service at http://{host}:{port}"); // Check liveness assert!(container.is_running().await?); // Stream logs line-by-line let stderr = container.stderr(true); let mut lines = stderr.lines(); if let Some(line) = lines.next_line().await? { println!("First stderr line: {line}"); } // Collect all stdout as bytes let stdout_bytes = container.stdout_to_vec().await?; println!("stdout: {}", String::from_utf8_lossy(&stdout_bytes)); // Pause and resume container.pause().await?; container.unpause().await?; // Stop gracefully (SIGINT, then SIGKILL after 5s) container.stop_with_timeout(Some(5)).await?; // Query exit code after stop let code = container.exit_code().await?; println!("Exit code: {code:?}"); Ok(()) } ``` -------------------------------- ### Building from a Complex Dockerfile Source: https://github.com/testcontainers/testcontainers-rs/blob/main/docs/features/building_images.md Constructs a Docker image from a multi-stage Dockerfile, copying necessary project files into the build context. ```rust let dockerfile = r#" FROM rust:1.75 as builder WORKDIR /app COPY Cargo.toml Cargo.lock ./ COPY src ./src RUN cargo build --release FROM debian:bookworm-slim COPY --from=builder /app/target/release/myapp /usr/local/bin/ CMD ["/usr/local/bin/myapp"] "#; let image = GenericBuildableImage::new("my-rust-app", "latest") .with_dockerfile_string(dockerfile) .with_file("./Cargo.toml", "./Cargo.toml") .with_file("./Cargo.lock", "./Cargo.lock") .with_file("./src", "./src") .build_image() .await?; ``` -------------------------------- ### Configure Container with `ImageExt` Fluent Builder Source: https://context7.com/testcontainers/testcontainers-rs/llms.txt Use the `ImageExt` trait for a chainable builder pattern to configure various aspects of a container, including name, tag, ports, environment variables, and readiness checks. This provides a flexible way to customize container requests. ```rust use std::time::Duration; use testcontainers:: core::{CgroupnsMode, IntoContainerPort, WaitFor}, runners::AsyncRunner, GenericImage, ImageExt, ; #[tokio::test] async fn configured_container() -> anyhow::Result<()> { let container = GenericImage::new("nginx", "1.25-alpine") .with_name("my-registry.io/nginx") // override registry/owner .with_tag("1.25.3-alpine") // pin exact digest/tag .with_container_name("nginx-test") // assign container name .with_platform("linux/amd64") // target architecture .with_exposed_port(80.tcp()) .with_mapped_port(18080, 80.tcp()) // pin specific host port .with_network("bridge") .with_env_var("NGINX_HOST", "localhost") .with_hostname("nginx-host") .with_label("test-suite", "integration") .with_working_dir("/etc/nginx") .with_user("nginx") .with_shm_size(64 * 1024 * 1024) // 64 MiB shared memory .with_privileged(false) .with_cap_add("NET_BIND_SERVICE") .with_startup_timeout(Duration::from_secs(60)) .with_wait_for(WaitFor::message_on_stderr("start worker process")) .start() .await?; let port = container.get_host_port_ipv4(80.tcp()).await?; println!("nginx at http://localhost:{port}"); Ok(()) } ``` -------------------------------- ### Import testcontainers Core API Source: https://github.com/testcontainers/testcontainers-rs/blob/main/docs/quickstart/community_modules.md Demonstrates how to import `ImageExt` from the core `testcontainers` crate, which is re-exported by `testcontainers-modules`. ```rust use testcontainers_modules::testcontainers::ImageExt; ``` -------------------------------- ### Set Multiple Environment Variables using HashMap Source: https://github.com/testcontainers/testcontainers-rs/blob/main/docs/features/docker_compose.md Configure multiple environment variables for your Docker Compose stack efficiently by providing a HashMap to the `with_env_vars` method. ```rust let mut env_vars = HashMap::new(); env_vars.insert("API_KEY".to_string(), "test-key-123".to_string()); env_vars.insert("DEBUG".to_string(), "true".to_string()); let mut compose = DockerCompose::with_local_client(&["docker-compose.yml"]) .with_env_vars(env_vars); compose.up().await?; ``` -------------------------------- ### Configuring Containerised Client with Project Directory Source: https://github.com/testcontainers/testcontainers-rs/blob/main/docs/features/docker_compose.md Sets an explicit project directory for the containerised Docker Compose client, which is necessary for correctly resolving relative paths in bind mounts. ```rust use testcontainers::compose::{ContainerisedComposeOptions, DockerCompose}; let options = ContainerisedComposeOptions::new(&["/home/me/app/docker-compose.yml"]) .with_project_directory("/home/me/app"); let mut compose = DockerCompose::with_containerised_client(options).await?; compose.up().await?; ``` -------------------------------- ### Add Files to Docker Build Context Source: https://github.com/testcontainers/testcontainers-rs/blob/main/docs/features/building_images.md Include files from your filesystem or provide inline data as part of the Docker image build context. This is useful for supplying configuration files or application binaries. ```rust let image = GenericBuildableImage::new("my-app", "latest") .with_dockerfile("./path/to/Dockerfile") .with_file("./target/release/myapp", "./myapp") .build_image() .await?; ``` ```rust let image = GenericBuildableImage::new("my-app", "latest") .with_dockerfile_string("FROM alpine:latest\nCOPY config.json /config.json") .with_data(r#"{"port": 8080}"#, "./config.json") .build_image() .await?; ``` -------------------------------- ### Check Code Formatting with rustfmt Source: https://github.com/testcontainers/testcontainers-rs/blob/main/CONTRIBUTING.md Ensures code adheres to project formatting standards using rustfmt. Run this command to check for any formatting issues. ```shell cargo +nightly fmt --all -- --check ``` -------------------------------- ### Choose TLS Backend for Testcontainers Source: https://context7.com/testcontainers/testcontainers-rs/llms.txt Select the TLS backend for Testcontainers by configuring Cargo.toml. The default uses `rustls` with the `ring` crate, while an alternative uses `aws-lc-rs` for FIPS compatibility. ```toml # Cargo.toml — choose TLS backend [dev-dependencies] # default: ring (rustls + ring) testcontainers = { version = "0.27" } # alternative: AWS-LC (FIPS-compatible) testcontainers = { version = "0.27", default-features = false, features = ["aws-lc-rs"] } ``` -------------------------------- ### Synchronous Image Build Source: https://github.com/testcontainers/testcontainers-rs/blob/main/docs/features/building_images.md Builds a Docker image using the synchronous API. Requires the `blocking` feature to be enabled. ```rust use testcontainers:: core::BuildImageOptions, runners::{SyncBuilder, SyncRunner}, GenericBuildableImage; #[test] fn test_sync_build() -> Result<(), Box> { let image = GenericBuildableImage::new("my-app", "latest") .with_dockerfile_string("FROM alpine:latest") .build_image()?; let container = image.start()?; Ok(()) } ```