Module rustainers::compose::images

source ·
Expand description

Compose Images

§Provide a compose runnable images

To run compose with the runner we need to implements the ToRunnableComposeContainers.

It’s a little bit different of the trait for the runnable container because we need:

  • something that provides a reference on a std::path::Path
  • wait strategies are associated with a service name
  • port mapping are associated with a service name

The provided path should contains the docker-compose file.

We can use a TemporaryDirectory that provide the path. This effective directory is created at runtime in the temporary folder, and when the compose containers is dropped this folder is removed. For debugging purpose, if you need to keep this directory, you can call TemporaryDirectory::detach.

§Custom compose containers

A compose that only have the nginx service:

use std::process::Command;

use tracing::Level;

use rustainers::compose::{
    RunnableComposeContainers, RunnableComposeContainersBuilder, TemporaryDirectory, TemporaryFile,
    ToRunnableComposeContainers,
};
use rustainers::runner::Runner;
use rustainers::{ExposedPort, PortError, WaitStrategy};

#[tokio::main]
async fn main() -> anyhow::Result<()> {
    let runner = Runner::auto()?;
    let image = ComposeNginx::new().await;
    let containers = runner.compose_start(image).await?;

    // Making a dummy HTTP request
    let url = containers.url().await?;
    Command::new("curl").args(["-v", &url]).status()?;

    Ok(())
}

#[derive(Debug)]
struct ComposeNginx {
    temp_dir: TemporaryDirectory,
    nginx_port: ExposedPort,
}

impl ComposeNginx {

    async fn new() -> Self {
        // Create a temporary directory what will be removed when the containers stop
        let temp_dir = TemporaryDirectory::with_files(
            "componse-nginx", // a name prefix
            [
            // One docker-compose.yaml file
            TemporaryFile::builder()
                .with_path("docker-compose.yaml")
                .with_content( // you can use the include_str! macro
                    r#"
version: "3.7"

services:
    nginx:
        image: nginx
        ports:
            - 80
        healthcheck:
            test: ["CMD", "curl", "-sf", "http://localhost"]
            interval: 1s
            retries: 5
            start_period: 1s
"#,
                )
                .build()],
        )
        .await
        .expect("Should create temp_dir");

        let nginx_port = ExposedPort::new(80);
        Self { temp_dir, nginx_port }
    }

    /// Build the URL of the running nginx
    async fn url(&self) -> Result<String, PortError> {
        let port = self.nginx_port.host_port().await?;
        let url = format!("http://localhost:{port}");

        Ok(url)
    }
}

impl ToRunnableComposeContainers for ComposeNginx {
    type AsPath = TemporaryDirectory;

    fn to_runnable(
        &self,
        builder: RunnableComposeContainersBuilder<Self::AsPath>,
    ) -> RunnableComposeContainers<Self::AsPath> {
        builder
            .with_compose_path(self.temp_dir.clone())
            // Use the service healthcheck
            .with_wait_strategies([("nginx", WaitStrategy::HealthCheck)])
            .with_port_mappings([("nginx", self.nginx_port.clone())])
            .build()
    }
}
DO NOT use the top-level name of the docker-compose file. The default folder name is used instead of this name.

Structs§

  • A docker compose with a single node Kafka with kraft (aka. without zookeeper) and a schema registry
  • A docker compose with a single node Redpanda