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