Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 13 additions & 5 deletions testcontainers/src/core/client.rs
Original file line number Diff line number Diff line change
Expand Up @@ -445,10 +445,18 @@ impl Client {
Ok(())
}

pub(crate) async fn pull_image(&self, descriptor: &str) -> Result<(), ClientError> {
pub(crate) async fn pull_image(
&self,
descriptor: &str,
platform: Option<String>,
) -> Result<(), ClientError> {
let pull_options = CreateImageOptionsBuilder::new()
.from_image(descriptor)
.platform(self.config.platform().unwrap_or_default())
.platform(
platform
.as_deref()
.unwrap_or_else(|| self.config.platform().unwrap_or_default()),
)
.build();

let credentials = self.credentials_for_image(descriptor).await;
Expand All @@ -462,7 +470,7 @@ impl Client {
Err(BollardError::DockerResponseServerError {
status_code: _,
message: _,
}) => {
}) if !matches!(platform.as_deref(), Some("linux/amd64")) => {
self.pull_image_linux_amd64(descriptor).await?;
}
_ => {
Expand Down Expand Up @@ -708,7 +716,7 @@ mod tests {
let config = env::Config::load::<OsEnvWithPlatformLinuxAmd64>().await?;
let mut client = Client::new().await?;
client.config = config;
client.pull_image("hello-world:latest").await?;
client.pull_image("hello-world:latest", None).await?;

let image = client.bollard.inspect_image("hello-world:latest").await?;

Expand All @@ -718,7 +726,7 @@ mod tests {
let config = env::Config::load::<OsEnvWithPlatformLinux386>().await?;
let mut client = Client::new().await?;
client.config = config;
client.pull_image("hello-world:latest").await?;
client.pull_image("hello-world:latest", None).await?;

let image = client.bollard.inspect_image("hello-world:latest").await?;

Expand Down
7 changes: 7 additions & 0 deletions testcontainers/src/core/containers/request.rs
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ pub struct ContainerRequest<I: Image> {
pub(crate) image_name: Option<String>,
pub(crate) image_tag: Option<String>,
pub(crate) container_name: Option<String>,
pub(crate) platform: Option<String>,
pub(crate) network: Option<String>,
pub(crate) labels: BTreeMap<String, String>,
pub(crate) env_vars: BTreeMap<String, String>,
Expand Down Expand Up @@ -94,6 +95,10 @@ impl<I: Image> ContainerRequest<I> {
&self.container_name
}

pub fn platform(&self) -> &Option<String> {
&self.platform
}

pub fn env_vars(&self) -> impl Iterator<Item = (Cow<'_, str>, Cow<'_, str>)> {
self.image
.env_vars()
Expand Down Expand Up @@ -236,6 +241,7 @@ impl<I: Image> From<I> for ContainerRequest<I> {
image_name: None,
image_tag: None,
container_name: None,
platform: None,
network: None,
labels: BTreeMap::default(),
env_vars: BTreeMap::default(),
Expand Down Expand Up @@ -292,6 +298,7 @@ impl<I: Image + Debug> Debug for ContainerRequest<I> {
.field("image_name", &self.image_name)
.field("image_tag", &self.image_tag)
.field("container_name", &self.container_name)
.field("platform", &self.platform)
.field("network", &self.network)
.field("labels", &self.labels)
.field("env_vars", &self.env_vars)
Expand Down
23 changes: 23 additions & 0 deletions testcontainers/src/core/image/image_ext.rs
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,20 @@ pub trait ImageExt<I: Image> {
/// Sets the container name.
fn with_container_name(self, name: impl Into<String>) -> ContainerRequest<I>;

/// Sets the platform the container will be run on.
///
/// Platform in the format `os[/arch[/variant]]` used for image lookup.
///
/// # Examples
///
/// ```rust,no_run
/// use testcontainers::{GenericImage, ImageExt};
///
/// let image = GenericImage::new("image", "tag")
/// .with_platform("linux/amd64");
/// ```
fn with_platform(self, platform: impl Into<String>) -> ContainerRequest<I>;

/// Sets the network the container will be connected to.
fn with_network(self, network: impl Into<String>) -> ContainerRequest<I>;

Expand Down Expand Up @@ -272,6 +286,15 @@ impl<RI: Into<ContainerRequest<I>>, I: Image> ImageExt<I> for RI {
}
}

fn with_platform(self, platform: impl Into<String>) -> ContainerRequest<I> {
let container_req = self.into();

ContainerRequest {
platform: Some(platform.into()),
..container_req
}
}

fn with_network(self, network: impl Into<String>) -> ContainerRequest<I> {
let container_req = self.into();
ContainerRequest {
Expand Down
38 changes: 31 additions & 7 deletions testcontainers/src/runners/async_runner.rs
Original file line number Diff line number Diff line change
Expand Up @@ -177,13 +177,27 @@ where
None
};

let mut options_builder: Option<CreateContainerOptionsBuilder> = None;

// name of the container
if let Some(name) = container_req.container_name() {
let options = CreateContainerOptionsBuilder::new()
.name(name)
.platform(client.config.platform().unwrap_or_default())
.build();
create_options = Some(options)
let options = CreateContainerOptionsBuilder::new().name(name);

options_builder = Some(options);
}

// platform of the container
if let Some(platform) = container_req.platform() {
let options = options_builder.unwrap_or(CreateContainerOptionsBuilder::new());
options_builder = Some(options.platform(platform));
} else {
// set platform from global platform setting if available
let options = options_builder.unwrap_or(CreateContainerOptionsBuilder::new());
options_builder = Some(options.platform(client.config.platform().unwrap_or_default()));
}

if let Some(options) = options_builder {
create_options = Some(options.build());
}

// handle environment variables
Expand Down Expand Up @@ -301,7 +315,12 @@ where
status_code: 404, ..
},
)) => {
client.pull_image(&container_req.descriptor()).await?;
client
.pull_image(
&container_req.descriptor(),
container_req.platform().clone(),
)
.await?;
client.create_container(create_options, config).await
}
res => res,
Expand Down Expand Up @@ -344,7 +363,12 @@ where
async fn pull_image(self) -> Result<ContainerRequest<I>> {
let container_req = self.into();
let client = Client::lazy_client().await?;
client.pull_image(&container_req.descriptor()).await?;
client
.pull_image(
&container_req.descriptor(),
container_req.platform().clone(),
)
.await?;

Ok(container_req)
}
Expand Down
Loading