testcontainers_modules/cncf_distribution/
mod.rs

1use testcontainers::{core::WaitFor, Image};
2
3const NAME: &str = "registry";
4const TAG: &str = "2";
5
6/// Module to work with a custom Docker registry inside of tests.
7///
8/// Starts an instance of [`CNCF Distribution`], an easy-to-use registry for container images.
9///
10/// # Example
11/// ```
12/// use testcontainers_modules::{cncf_distribution, testcontainers::runners::SyncRunner};
13///
14/// let registry = cncf_distribution::CncfDistribution::default()
15///     .start()
16///     .unwrap();
17///
18/// let image_name = "test";
19/// let image_tag = format!(
20///     "{}:{}/{image_name}",
21///     registry.get_host().unwrap(),
22///     registry.get_host_port_ipv4(5000).unwrap()
23/// );
24///
25/// // now you can push an image tagged with `image_tag` and pull it afterward
26/// ```
27///
28/// [`CNCF Distribution`]: https://distribution.github.io/distribution/
29#[derive(Debug, Default, Clone)]
30pub struct CncfDistribution {
31    /// (remove if there is another variable)
32    /// Field is included to prevent this struct to be a unit struct.
33    /// This allows extending functionality (and thus further variables) without breaking changes
34    _priv: (),
35}
36
37impl Image for CncfDistribution {
38    fn name(&self) -> &str {
39        NAME
40    }
41
42    fn tag(&self) -> &str {
43        TAG
44    }
45
46    fn ready_conditions(&self) -> Vec<WaitFor> {
47        vec![WaitFor::message_on_stderr("listening on [::]:5000")]
48    }
49}
50
51#[cfg(test)]
52mod tests {
53    use bollard::query_parameters::{
54        CreateImageOptionsBuilder, PushImageOptionsBuilder, RemoveImageOptions,
55    };
56    use futures::StreamExt;
57    use testcontainers::{runners::AsyncBuilder, GenericBuildableImage, Image};
58
59    use crate::{cncf_distribution, testcontainers::runners::AsyncRunner};
60
61    const DOCKERFILE: &str = "
62        FROM scratch
63        COPY hello.sh /
64    ";
65
66    #[tokio::test]
67    async fn distribution_push_pull_image() -> Result<(), Box<dyn std::error::Error + 'static>> {
68        let _ = pretty_env_logger::try_init();
69        let distribution_node = cncf_distribution::CncfDistribution::default()
70            .start()
71            .await?;
72        let docker = bollard::Docker::connect_with_local_defaults().unwrap();
73
74        let image_name = &format!(
75            "localhost:{}/test",
76            distribution_node.get_host_port_ipv4(5000).await?
77        );
78        let image_tag = "latest";
79
80        let image = GenericBuildableImage::new(image_name, image_tag)
81            .with_dockerfile_string(DOCKERFILE)
82            .with_data(b"#!/bin/sh\necho 'Hello World'", "./hello.sh")
83            .build_image()
84            .await?;
85
86        // Push image, and then remove it
87        let mut push_image = docker.push_image(
88            image.name(),
89            Some(PushImageOptionsBuilder::new().tag(image.tag()).build()),
90            None,
91        );
92        while let Some(x) = push_image.next().await {
93            println!("Push image: {:?}", x.unwrap());
94        }
95
96        docker
97            .remove_image(image.name(), None::<RemoveImageOptions>, None)
98            .await
99            .unwrap();
100
101        // Pull image
102        let mut create_image = docker.create_image(
103            Some(
104                CreateImageOptionsBuilder::new()
105                    .from_image(image.name())
106                    .tag(image.tag())
107                    .build(),
108            ),
109            None,
110            None,
111        );
112        while let Some(x) = create_image.next().await {
113            println!("Create image: {:?}", x.unwrap());
114        }
115
116        assert_eq!(
117            docker
118                .inspect_image(image.name())
119                .await
120                .unwrap()
121                .repo_tags
122                .unwrap()[0],
123            format!("{}:{}", image.name(), image.tag())
124        );
125
126        Ok(())
127    }
128}