Skip to content

fix(udp): map udp ports correctly#3484

Closed
blueprismo wants to merge 1 commit intotestcontainers:mainfrom
blueprismo:main
Closed

fix(udp): map udp ports correctly#3484
blueprismo wants to merge 1 commit intotestcontainers:mainfrom
blueprismo:main

Conversation

@blueprismo
Copy link
Copy Markdown
Contributor

What does this PR do?

Fixes UDP port exposure.

Problem:
When using ExposedPorts: []string{"8080/udp"} in testcontainers-go, the MappedPort() function always returned "0/udp" instead of the actual mapped host port (e.g., "55051/udp"). This made it impossible to connect to UDP services running in containers.

Root Cause:
The issue was in the mergePortBindings() function in lifecycle.go. When nat.ParsePortSpecs() processes exposed port specifications like 8080/udp, it creates PortBinding structs with empty HostPort fields ({HostIP: "", HostPort: ""})
Docker interprets these differently:

  • HostPort: "" (empty) → "Don't bind this port to any host port" → results in port 0
  • HostPort: "0" → "Bind this port to a random available host port" → proper allocation

Solution:
Modified mergePortBindings() to convert empty HostPort values to "0" for automatic port allocation:

Why is it important?

To support UDP port bindings in testcontainers.

Related issues

n/a

How to test this PR

I added a test scenario for udp testing

@blueprismo blueprismo requested a review from a team as a code owner November 4, 2025 16:16
@netlify
Copy link
Copy Markdown

netlify Bot commented Nov 4, 2025

Deploy Preview for testcontainers-go ready!

Name Link
🔨 Latest commit 12e0c11
🔍 Latest deploy log https://app.netlify.com/projects/testcontainers-go/deploys/690a26c3826e8b00082f9a22
😎 Deploy Preview https://deploy-preview-3484--testcontainers-go.netlify.app
📱 Preview on mobile
Toggle QR Code...

QR Code

Use your smartphone camera to open QR code link.

To edit notification comments on pull requests, go to your Netlify project configuration.

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Nov 4, 2025

Summary by CodeRabbit

Bug Fixes

  • Fixed UDP port binding to properly allocate random host ports when not explicitly specified, ensuring consistent behavior across all port mapping scenarios.

Tests

  • Added comprehensive test coverage for UDP port binding functionality, including validation of port allocation, container connectivity, port binding logic, and verification that existing TCP port behavior remains unaffected.

Walkthrough

The pull request adds UDP port binding support by normalizing empty HostPort values to "0" in the mergePortBindings function, triggering Docker to allocate random ports. A new test file validates UDP port allocation, connectivity, and the internal port binding logic.

Changes

Cohort / File(s) Summary
Port binding normalization
lifecycle.go
Added post-merge normalization step in mergePortBindings to set empty HostPort fields to "0", enabling Docker auto-allocation for UDP/HostPort scenarios.
UDP port binding tests
udp_port_binding_test.go
New test file with TestUDPPortBinding (validates UDP port allocation, MappedPort exposure, and connectivity) and TestPortBindingInternalLogic (exercises mergePortBindings normalization logic and HostPort/HostIP preservation).

Estimated code review effort

🎯 2 (Simple) | ⏱️ ~12 minutes

  • lifecycle.go: Isolated normalization logic is straightforward; verify the "0" assignment correctly triggers Docker port allocation and confirm no unintended side effects on existing port binding paths.
  • udp_port_binding_test.go: Verify test assertions accurately reflect UDP behavior, validate mergePortBindings logic covers edge cases (empty HostPort, existing HostIP), and ensure connectivity test reliability.

Poem

🐰 A rabbit hops with joy today,
UDP ports now have their way!
Empty ports dance to "0" so fine,
Docker picks, the stars align.
Tests ensure it all works right,
Binding magic, pure delight! 🎉

Pre-merge checks and finishing touches

✅ Passed checks (2 passed)
Check name Status Explanation
Description check ✅ Passed The description clearly explains the UDP port exposure issue, root cause in mergePortBindings, and the solution implemented, directly relating to the changeset.
Title check ✅ Passed The title 'fix(udp): map udp ports correctly' clearly and specifically addresses the main change: fixing UDP port mapping behavior in the codebase.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@blueprismo blueprismo changed the title testcontianers: udp fix fix: testcontianers udp Nov 4, 2025
Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 7527203 and 12e0c11.

📒 Files selected for processing (2)
  • lifecycle.go (1 hunks)
  • udp_port_binding_test.go (1 hunks)
🧰 Additional context used
🧠 Learnings (1)
📚 Learning: 2025-09-29T15:08:18.694Z
Learnt from: mdelapenya
Repo: testcontainers/testcontainers-go PR: 3320
File: modules/artemis/artemis.go:98-103
Timestamp: 2025-09-29T15:08:18.694Z
Learning: In testcontainers-go, nat.Port is a type alias for string, so untyped string constants can be passed directly to functions expecting nat.Port (like wait.ForListeningPort) without explicit type conversion - the Go compiler handles the implicit conversion automatically.

Applied to files:

  • udp_port_binding_test.go
  • lifecycle.go
🧬 Code graph analysis (1)
udp_port_binding_test.go (2)
container.go (1)
  • ContainerRequest (131-171)
generic.go (2)
  • GenericContainer (52-98)
  • GenericContainerRequest (21-27)
🔇 Additional comments (1)
lifecycle.go (1)

617-625: HostPort normalization aligns with Docker expectations.

Converting empty bindings to "0" ensures Docker actually allocates a host port (especially for UDP) and keeps existing explicit mappings untouched. Nice catch.

Comment thread udp_port_binding_test.go
Comment on lines +65 to +66
address := fmt.Sprintf("%s:%s", hostIP, mappedPort.Port())
conn, err := net.DialTimeout("udp", address, 2*time.Second)
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Use net.JoinHostPort so IPv6 hosts don’t break this test.

fmt.Sprintf("%s:%s", …) produces an invalid endpoint like ::1:55051 when Docker reports an IPv6 host, causing net.DialTimeout to fail. Please switch to net.JoinHostPort to generate a valid host:port string across IPv4 and IPv6.

Apply this diff:

-		address := fmt.Sprintf("%s:%s", hostIP, mappedPort.Port())
+		address := net.JoinHostPort(hostIP, mappedPort.Port())
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
address := fmt.Sprintf("%s:%s", hostIP, mappedPort.Port())
conn, err := net.DialTimeout("udp", address, 2*time.Second)
address := net.JoinHostPort(hostIP, mappedPort.Port())
conn, err := net.DialTimeout("udp", address, 2*time.Second)
🤖 Prompt for AI Agents
In udp_port_binding_test.go around lines 65 to 66, the test builds the address
with fmt.Sprintf("%s:%s", hostIP, mappedPort.Port()) which breaks for IPv6 hosts
(e.g., ::1:55051); replace that construction with net.JoinHostPort(hostIP,
mappedPort.Port()) so the host:port string is valid for both IPv4 and IPv6
before calling net.DialTimeout.

@mdelapenya mdelapenya changed the title fix: testcontianers udp fix(udp): map udp ports correctly Nov 4, 2025
@mdelapenya mdelapenya self-assigned this Nov 4, 2025
@blueprismo
Copy link
Copy Markdown
Contributor Author

closing in favour of a new PR from a new branch != main

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants