Skip to content

Commit

Permalink
fix: exclude image pulling time from startup timeout
Browse files Browse the repository at this point in the history
  • Loading branch information
DDtKey committed Jul 7, 2024
1 parent 73792f9 commit 1b16ed9
Showing 1 changed file with 137 additions and 136 deletions.
273 changes: 137 additions & 136 deletions testcontainers/src/runners/async_runner.rs
Original file line number Diff line number Diff line change
Expand Up @@ -53,156 +53,157 @@ where
{
async fn start(self) -> Result<ContainerAsync<I>> {
let container_req = self.into();
let startup_timeout = container_req
.startup_timeout()
.unwrap_or(DEFAULT_STARTUP_TIMEOUT);

tokio::time::timeout(startup_timeout, async {
let client = Client::lazy_client().await?;
let mut create_options: Option<CreateContainerOptions<String>> = None;
let client = Client::lazy_client().await?;
let mut create_options: Option<CreateContainerOptions<String>> = None;

let extra_hosts: Vec<_> = container_req
.hosts()
.map(|(key, value)| format!("{key}:{value}"))
.collect();

let mut config: Config<String> = Config {
image: Some(container_req.descriptor()),
host_config: Some(HostConfig {
privileged: Some(container_req.privileged()),
extra_hosts: Some(extra_hosts),
cgroupns_mode: container_req.cgroupns_mode().map(|mode| mode.into()),
userns_mode: container_req.userns_mode().map(|v| v.to_string()),
..Default::default()
}),
..Default::default()
};

let extra_hosts: Vec<_> = container_req
.hosts()
.map(|(key, value)| format!("{key}:{value}"))
.collect();
// shared memory
if let Some(bytes) = container_req.shm_size() {
config.host_config = config.host_config.map(|mut host_config| {
host_config.shm_size = Some(bytes as i64);
host_config
});
}

let mut config: Config<String> = Config {
image: Some(container_req.descriptor()),
host_config: Some(HostConfig {
privileged: Some(container_req.privileged()),
extra_hosts: Some(extra_hosts),
cgroupns_mode: container_req.cgroupns_mode().map(|mode| mode.into()),
userns_mode: container_req.userns_mode().map(|v| v.to_string()),
..Default::default()
}),
..Default::default()
};

// shared memory
if let Some(bytes) = container_req.shm_size() {
config.host_config = config.host_config.map(|mut host_config| {
host_config.shm_size = Some(bytes as i64);
host_config
});
}
// create network and add it to container creation
let network = if let Some(network) = container_req.network() {
config.host_config = config.host_config.map(|mut host_config| {
host_config.network_mode = Some(network.to_string());
host_config
});
Network::new(network, client.clone()).await?
} else {
None
};

// create network and add it to container creation
let network = if let Some(network) = container_req.network() {
config.host_config = config.host_config.map(|mut host_config| {
host_config.network_mode = Some(network.to_string());
host_config
});
Network::new(network, client.clone()).await?
} else {
None
};

// name of the container
if let Some(name) = container_req.container_name() {
create_options = Some(CreateContainerOptions {
name: name.to_owned(),
platform: None,
})
}
// name of the container
if let Some(name) = container_req.container_name() {
create_options = Some(CreateContainerOptions {
name: name.to_owned(),
platform: None,
})
}

// handle environment variables
let envs: Vec<String> = container_req
.env_vars()
.map(|(k, v)| format!("{k}={v}"))
.collect();
config.env = Some(envs);
// handle environment variables
let envs: Vec<String> = container_req
.env_vars()
.map(|(k, v)| format!("{k}={v}"))
.collect();
config.env = Some(envs);

// mounts and volumes
let mounts: Vec<_> = container_req.mounts().map(Into::into).collect();

if !mounts.is_empty() {
config.host_config = config.host_config.map(|mut host_config| {
host_config.mounts = Some(mounts);
host_config
});
}

// mounts and volumes
let mounts: Vec<_> = container_req.mounts().map(Into::into).collect();
// entrypoint
if let Some(entrypoint) = container_req.entrypoint() {
config.entrypoint = Some(vec![entrypoint.to_string()]);
}

if !mounts.is_empty() {
config.host_config = config.host_config.map(|mut host_config| {
host_config.mounts = Some(mounts);
host_config
});
}
let is_container_networked = container_req
.network()
.as_ref()
.map(|network| network.starts_with("container:"))
.unwrap_or(false);

// expose ports
if !is_container_networked {
let mapped_ports = container_req
.ports()
.map(|ports| ports.iter().map(|p| p.container_port).collect::<Vec<_>>())
.unwrap_or_default();

let ports_to_expose = container_req
.expose_ports()
.iter()
.copied()
.chain(mapped_ports)
.map(|p| (format!("{p}"), HashMap::new()))
.collect();

// entrypoint
if let Some(entrypoint) = container_req.entrypoint() {
config.entrypoint = Some(vec![entrypoint.to_string()]);
}
// exposed ports of the image + mapped ports
config.exposed_ports = Some(ports_to_expose);
}

let is_container_networked = container_req
.network()
.as_ref()
.map(|network| network.starts_with("container:"))
.unwrap_or(false);

// expose ports
if !is_container_networked {
let mapped_ports = container_req
.ports()
.map(|ports| ports.iter().map(|p| p.container_port).collect::<Vec<_>>())
.unwrap_or_default();

let ports_to_expose = container_req
.expose_ports()
.iter()
.copied()
.chain(mapped_ports)
.map(|p| (format!("{p}"), HashMap::new()))
.collect();

// exposed ports of the image + mapped ports
config.exposed_ports = Some(ports_to_expose);
}
// ports
if container_req.ports().is_some() {
let empty: Vec<_> = Vec::new();
let bindings = container_req.ports().unwrap_or(&empty).iter().map(|p| {
(
format!("{}", p.container_port),
Some(vec![PortBinding {
host_ip: None,
host_port: Some(p.host_port.to_string()),
}]),
)
});

config.host_config = config.host_config.map(|mut host_config| {
host_config.port_bindings = Some(bindings.collect());
host_config
});
} else if !is_container_networked {
config.host_config = config.host_config.map(|mut host_config| {
host_config.publish_all_ports = Some(true);
host_config
});
}

// ports
if container_req.ports().is_some() {
let empty: Vec<_> = Vec::new();
let bindings = container_req.ports().unwrap_or(&empty).iter().map(|p| {
(
format!("{}", p.container_port),
Some(vec![PortBinding {
host_ip: None,
host_port: Some(p.host_port.to_string()),
}]),
)
});

config.host_config = config.host_config.map(|mut host_config| {
host_config.port_bindings = Some(bindings.collect());
host_config
});
} else if !is_container_networked {
config.host_config = config.host_config.map(|mut host_config| {
host_config.publish_all_ports = Some(true);
host_config
});
}
let cmd: Vec<_> = container_req.cmd().map(|v| v.to_string()).collect();
if !cmd.is_empty() {
config.cmd = Some(cmd);
}

let cmd: Vec<_> = container_req.cmd().map(|v| v.to_string()).collect();
if !cmd.is_empty() {
config.cmd = Some(cmd);
// create the container with options
let create_result = client
.create_container(create_options.clone(), config.clone())
.await;
let container_id = match create_result {
Ok(id) => Ok(id),
Err(ClientError::CreateContainer(
bollard::errors::Error::DockerResponseServerError {
status_code: 404, ..
},
)) => {
client.pull_image(&container_req.descriptor()).await?;
client.create_container(create_options, config).await
}
res => res,
}?;

// create the container with options
let create_result = client
.create_container(create_options.clone(), config.clone())
.await;
let container_id = match create_result {
Ok(id) => Ok(id),
Err(ClientError::CreateContainer(
bollard::errors::Error::DockerResponseServerError {
status_code: 404, ..
},
)) => {
client.pull_image(&container_req.descriptor()).await?;
client.create_container(create_options, config).await
}
res => res,
}?;

#[cfg(feature = "watchdog")]
if client.config.command() == crate::core::env::Command::Remove {
crate::watchdog::register(container_id.clone());
}
#[cfg(feature = "watchdog")]
if client.config.command() == crate::core::env::Command::Remove {
crate::watchdog::register(container_id.clone());
}

let startup_timeout = container_req
.startup_timeout()
.unwrap_or(DEFAULT_STARTUP_TIMEOUT);

tokio::time::timeout(startup_timeout, async {
client.start_container(&container_id).await?;

let container =
Expand Down

0 comments on commit 1b16ed9

Please sign in to comment.