rustainers
rustainers
is a simple, opinionated way to run containers for tests.
TLDR
More information about this crate can be found in the crate documentation.
Or just see example directory.
Differences with testcontainers
This crate is an alternative to the testcontainers-rs crate.
The key differences are
- this crate supports docker, podman, or nerdctl
- this crate supports docker compose, podman-compose or nerdctl compose
- the start command is
async
, and the crate is using tokio
For now, the implementation is based on the CLI command. We may add more runner based on Rust api later.
Run a simple container
You need a Runner
to launch an image. You can use the Runner::auto
function to detect an available runner, or use the Runner::docker
, Runner::podman
, Runner::nerdctl
functions to choose a specific runner.
Then you need to create a runnable image, see module images
to use an existing image, or create your own image.
And you just need to start
your image. The running container can provide some methods to help you to use this container, e.g. a connection URL to access your container.
use rustainers::runner::{Runner, RunOption};
use rustainers::images::Postgres;
async fn pg() -> anyhow::Result<()> {
let runner = Runner::auto()?;
let image = Postgres::default().with_tag("15.2");
let container = runner.start(image).await?;
let url = container.url().await?;
do_something_with_postgres(url).await?;
Ok(())
}
async fn do_something_with_postgres(url: String) -> anyhow::Result<()> {
Ok(())
}
Livecycle of a container
When you start a runnable image, the runner first check the state of the container. It may already exists, or we may need to create it.
Then, the runner wait until all wait strategies to be reached, for example it can check the health of the container.
And it's returning a container. This container have a reference on the runnable image, this image can provide helper functions to retrieve for example the database URL.
When the container is dropped, by default, it stop the container.
You can opt-in to detach the container to avoid stopping in during the drop.
Using compose
To run containers defined with a docker-compose file, you also need a runner.
Then you can start your containers with compose_start
.
use rdkafka::producer::{FutureProducer, FutureRecord};
use rdkafka::ClientConfig;
use tracing::{info, Level};
use rustainers::compose::images::KafkaSchemaRegistry;
use rustainers::runner::Runner;
#[tokio::main]
async fn main() -> anyhow::Result<()> {
let runner = Runner::auto()?;
let image = KafkaSchemaRegistry::build_single_kraft().await?;
let containers = runner.compose_start(image).await?;
info!("Now I can use {containers}");
do_something_with_kafka(&containers).await?;
Ok(())
}
async fn do_something_with_kafka(image: &KafkaSchemaRegistry) -> anyhow::Result<()> {
let topic = "plop";
let broker_address = image.broker_address().await?;
info!("Using kafka broker: {broker_address}");
let schema_registry_url = image.schema_registry_endpoint().await?;
info!("Using schema registry: {schema_registry_url}");
// ...
Ok(())
}
Create a custom image
See images
module documentation.
Create a custom compose images
See compose::images
module documentation.