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::image::{BuildImageOptions, CreateImageOptions};
54    use futures::StreamExt;
55
56    use crate::{cncf_distribution, testcontainers::runners::AsyncRunner};
57
58    const DOCKERFILE: &[u8] = b"
59        FROM scratch
60        COPY Dockerfile /
61    ";
62
63    #[tokio::test]
64    async fn distribution_push_pull_image() -> Result<(), Box<dyn std::error::Error + 'static>> {
65        let _ = pretty_env_logger::try_init();
66        let distribution_node = cncf_distribution::CncfDistribution::default()
67            .start()
68            .await?;
69        let docker = bollard::Docker::connect_with_local_defaults().unwrap();
70        let image_tag = format!(
71            "localhost:{}/test:latest",
72            distribution_node.get_host_port_ipv4(5000).await?
73        );
74
75        let mut archive = tar::Builder::new(Vec::new());
76        let mut header = tar::Header::new_gnu();
77        header.set_path("Dockerfile").unwrap();
78        header.set_size(DOCKERFILE.len() as u64);
79        header.set_cksum();
80        archive.append(&header, DOCKERFILE).unwrap();
81
82        // Build test image
83        let mut build_image = docker.build_image(
84            BuildImageOptions {
85                dockerfile: "Dockerfile",
86                t: &image_tag,
87                ..Default::default()
88            },
89            None,
90            Some(archive.into_inner().unwrap().into()),
91        );
92        while let Some(x) = build_image.next().await {
93            println!("Build status: {:?}", x.unwrap());
94        }
95
96        // Push image, and then remove it
97        let mut push_image = docker.push_image::<String>(&image_tag, None, None);
98        while let Some(x) = push_image.next().await {
99            println!("Push image: {:?}", x.unwrap());
100        }
101        docker.remove_image(&image_tag, None, None).await.unwrap();
102
103        // Pull image
104        let mut create_image = docker.create_image(
105            Some(CreateImageOptions {
106                from_image: image_tag.as_str(),
107                ..Default::default()
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_tag)
119                .await
120                .unwrap()
121                .repo_tags
122                .unwrap()[0],
123            image_tag,
124        );
125
126        Ok(())
127    }
128}