-
Notifications
You must be signed in to change notification settings - Fork 162
feat: add host port mapping support for container-to-host communication #830
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Conversation
✅ Deploy Preview for testcontainers-rust ready!
To edit notification comments on pull requests, go to your Netlify project configuration. |
Currently one of the tests fail on CI: Tried it locally but can't really reproduce, and the changes I made shouldn't effect those tests. The error is |
Yes, it seems to be flakiness. Thanks for the contribution! 🚀 I will try to take a look soon! |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I already started reviewing piece by piece and then realized conceptual issue: #830 (comment) - the implementation relies only on target_os
which isn't enough (It won't work for remote docker, testcontainers cloud and etc)
Tbh I think that's the blocker for the whole implementation we need to address 🤔
See details in #821 and references in comments - that's generally why sshd
is used in other languages as a fallback mechanism
❓We could consider adding initial support for non-remote hosts only (with explicit documentation) to split this into several steps, but we have to ensure it can be extended without breaking things (#830 (comment))
async fn supports_host_gateway(client: &Client) -> bool { | ||
if !cfg!(target_os = "linux") { | ||
log::trace!("Platform: not Linux, host-gateway unsupported"); | ||
return false; | ||
} | ||
|
||
log::trace!("Platform: Linux, checking Docker version for host-gateway"); | ||
|
||
// Check Docker version to see if it supports host-gateway | ||
match client.docker_version().await { | ||
Ok(Some(version_str)) => { | ||
let supported = is_docker_version_at_least(&version_str, 20, 10); | ||
if !supported { | ||
log::warn!("Docker version {} detected on Linux - host-gateway may not be supported (requires 20.10+)", version_str); | ||
} | ||
supported | ||
} | ||
Ok(None) => { | ||
// No version info available, assume it's supported but warn | ||
log::warn!( | ||
"Docker version not available - assuming host-gateway is supported on Linux" | ||
); | ||
true | ||
} | ||
Err(err) => { | ||
// If we can't get version info, assume it's supported but warn | ||
log::warn!( | ||
"Failed to detect Docker version ({}), assuming host-gateway is supported on Linux", | ||
err | ||
); | ||
true | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This looks like good candidate to be a method of client as main argument is &Client
But in order to split the logic and decouple from the Client
I'd suggest to define some trait and impl it for the Client
(e.g impl HostMapping for Client { ...}
)
let parts: Vec<&str> = version_str.split('.').collect(); | ||
if parts.len() < 2 { | ||
return false; | ||
} | ||
|
||
let major: u32 = parts[0].parse().unwrap_or(0); | ||
let minor: u32 = parts[1].parse().unwrap_or(0); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Let's rely on https://crates.io/crates/semver for that?
/// Checks if we're running on Docker Desktop (macOS or Windows). | ||
/// Docker Desktop provides `host.docker.internal` by default. | ||
fn is_docker_desktop() -> bool { | ||
let is_desktop = cfg!(any(target_os = "macos", target_os = "windows")); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This won’t actually work as is because we can’t rely solely on the operating system of the user for that.
Testcontainers support remote Docker hosts.
So, I'm generally ok to split work into 2-3 steps:
But let's discuss what can be achieved in the first iteration |
Summary
with_exposed_host_port()
API to enable containers to connect back to host serviceshost-gateway
on Linux (Docker 20.10+)host.docker.internal
on Docker Desktop (macOS/Windows)Background
This feature, requested on #821, enables containers to communicate with services running on the host machine by exposing host ports through a standardized hostname
host.testcontainers.internal
. This is particularly useful for integration tests where containers need to call back to test services or mock servers running on the host.The implementation leverages native Docker features that have been stable for years:
host-gateway
support was added in Docker 20.10 (released October 2020, ~5 years ago)host.docker.internal
has been available on Docker Desktop since early versionsAny Docker installation older than 20.10 would be significantly outdated, so the current implementation focuses on
these native approaches rather than complex fallback mechanisms.
As for the DinD case, I will look into it in the future if I can find the time.