diff --git a/testcontainers/src/core/client.rs b/testcontainers/src/core/client.rs index 9e7f96a7..4efc2ce4 100644 --- a/testcontainers/src/core/client.rs +++ b/testcontainers/src/core/client.rs @@ -1,4 +1,4 @@ -use std::io; +use std::{io, str::FromStr}; use bollard::{ auth::DockerCredentials, @@ -12,6 +12,7 @@ use bollard::{ use bollard_stubs::models::{ContainerInspectResponse, ExecInspectResponse, Network}; use futures::{StreamExt, TryStreamExt}; use tokio::sync::OnceCell; +use url::Url; use crate::core::{ client::exec::ExecResult, @@ -312,15 +313,16 @@ impl Client { pub(crate) async fn docker_hostname(&self) -> Result { let docker_host = self.config.docker_host(); - match docker_host.scheme() { - "tcp" | "http" | "https" => { - docker_host - .host() - .map(|host| host.to_owned()) - .ok_or_else(|| { - ConfigurationError::InvalidDockerHost(docker_host.to_string()).into() - }) - } + let docker_host_url = Url::from_str(docker_host) + .map_err(|e| ConfigurationError::InvalidDockerHost(e.to_string()))?; + + match docker_host_url.scheme() { + "tcp" | "http" | "https" => docker_host_url + .host() + .map(|host| host.to_owned()) + .ok_or_else(|| { + ConfigurationError::InvalidDockerHost(docker_host.to_string()).into() + }), "unix" | "npipe" => { if is_in_container().await { let host = self diff --git a/testcontainers/src/core/client/bollard_client.rs b/testcontainers/src/core/client/bollard_client.rs index 11139803..e185c4cc 100644 --- a/testcontainers/src/core/client/bollard_client.rs +++ b/testcontainers/src/core/client/bollard_client.rs @@ -1,6 +1,7 @@ -use std::time::Duration; +use std::{str::FromStr, time::Duration}; use bollard::{Docker, API_DEFAULT_VERSION}; +use url::Url; use crate::core::env; @@ -8,32 +9,23 @@ const DEFAULT_TIMEOUT: Duration = Duration::from_secs(2 * 60); pub(super) fn init(config: &env::Config) -> Result { let host = config.docker_host(); + let host_url = Url::from_str(host)?; - match host.scheme() { + match host_url.scheme() { "https" => connect_with_ssl(config), "http" | "tcp" => { if config.tls_verify() { connect_with_ssl(config) } else { - Docker::connect_with_http( - host.as_str(), - DEFAULT_TIMEOUT.as_secs(), - API_DEFAULT_VERSION, - ) + Docker::connect_with_http(host, DEFAULT_TIMEOUT.as_secs(), API_DEFAULT_VERSION) } } #[cfg(unix)] - "unix" => Docker::connect_with_unix( - host.as_str(), - DEFAULT_TIMEOUT.as_secs(), - API_DEFAULT_VERSION, - ), + "unix" => Docker::connect_with_unix(host, DEFAULT_TIMEOUT.as_secs(), API_DEFAULT_VERSION), #[cfg(windows)] - "npipe" => Docker::connect_with_named_pipe( - host.as_str(), - DEFAULT_TIMEOUT.as_secs(), - API_DEFAULT_VERSION, - ), + "npipe" => { + Docker::connect_with_named_pipe(host, DEFAULT_TIMEOUT.as_secs(), API_DEFAULT_VERSION) + } _ => Err(bollard::errors::Error::UnsupportedURISchemeError { uri: host.to_string(), }), @@ -44,7 +36,7 @@ fn connect_with_ssl(config: &env::Config) -> Result, - host: Option, + tc_host: Option, + host: Option, tls_verify: Option, cert_path: Option, command: Option, @@ -48,9 +46,9 @@ pub(crate) struct Config { #[derive(Debug, Default, serde::Deserialize)] struct TestcontainersProperties { #[serde(rename = "tc.host")] - tc_host: Option, + tc_host: Option, #[serde(rename = "docker.host")] - host: Option, + host: Option, #[serde_as(as = "Option")] #[serde(rename = "docker.tls.verify")] tls_verify: Option, @@ -103,11 +101,7 @@ impl Config { where E: GetEnvValue, { - let host = E::get_env_value("DOCKER_HOST") - .as_deref() - .map(FromStr::from_str) - .transpose() - .map_err(|e: url::ParseError| ConfigurationError::InvalidDockerHost(e.to_string()))?; + let host = E::get_env_value("DOCKER_HOST"); let tls_verify = E::get_env_value("DOCKER_TLS_VERIFY").map(|v| v == "1"); let cert_path = E::get_env_value("DOCKER_CERT_PATH").map(PathBuf::from); let command = E::get_env_value("TESTCONTAINERS_COMMAND") @@ -132,14 +126,11 @@ impl Config { /// 2. `DOCKER_HOST` environment variable. /// 3. Docker host from the `docker.host` property in the `~/.testcontainers.properties` file. /// 4. Else, the default Docker socket will be returned. - pub(crate) fn docker_host(&self) -> Url { + pub(crate) fn docker_host(&self) -> &str { self.tc_host - .as_ref() - .or(self.host.as_ref()) - .cloned() - .unwrap_or_else(|| { - Url::from_str(DEFAULT_DOCKER_HOST).expect("default host is valid URL") - }) + .as_deref() + .or(self.host.as_deref()) + .unwrap_or(DEFAULT_DOCKER_HOST) } pub(crate) fn tls_verify(&self) -> bool { @@ -211,8 +202,8 @@ mod tests { #[test] fn deserialize_java_properties() { - let tc_host = Url::parse("http://tc-host").unwrap(); - let docker_host = Url::parse("http://docker-host").unwrap(); + let tc_host = "http://tc-host".into(); + let docker_host = "http://docker-host".into(); let tls_verify = 1; let cert_path = "/path/to/cert"; diff --git a/testcontainers/src/runners/async_runner.rs b/testcontainers/src/runners/async_runner.rs index 460706f1..50d6c3ba 100644 --- a/testcontainers/src/runners/async_runner.rs +++ b/testcontainers/src/runners/async_runner.rs @@ -297,6 +297,7 @@ mod tests { Ok(()) } + #[cfg(unix)] #[tokio::test] async fn async_run_command_should_map_exposed_port_udp_sctp() -> anyhow::Result<()> { let client = Client::lazy_client().await?; @@ -370,6 +371,7 @@ mod tests { Ok(()) } + #[cfg(unix)] #[tokio::test] async fn async_run_command_should_map_ports_udp_sctp() -> anyhow::Result<()> { let client = Client::lazy_client().await?; diff --git a/testcontainers/tests/async_runner.rs b/testcontainers/tests/async_runner.rs index c3bc8f86..b8636a7d 100644 --- a/testcontainers/tests/async_runner.rs +++ b/testcontainers/tests/async_runner.rs @@ -41,7 +41,8 @@ async fn bollard_can_run_hello_world_with_multi_thread() -> anyhow::Result<()> { } async fn cleanup_hello_world_image() -> anyhow::Result<()> { - let docker = Docker::connect_with_unix_defaults()?; + let docker = Docker::connect_with_local_defaults()?; + futures::future::join_all( docker .list_images::(None)