Skip to content

Fix silent no-op retry when exposing host ports - #458

Merged
AhmedSoliman merged 1 commit into
mainfrom
fix-host-port-forwarding-retry
Oct 26, 2026
Merged

AhmedSoliman merged 1 commit into
mainfrom
fix-host-port-forwarding-retry

Conversation

@AhmedSoliman

Copy link
Copy Markdown
Member

Problem

ServiceToServiceScopeConcurrency intermittently fails in beforeAll with:

META0003 … h2 pool connection error: failed to lookup address information: Name or service not known
http://host.testcontainers.internal:<port>

(e.g. restatedev/restate run 40347565939, also seen on restatedev/restate#5639 and other unrelated branches.)

exposeHostPort retries Testcontainers.exposeHostPorts(port), assuming a failed sshd start isn't cached. But in Testcontainers 2.0.4, PortForwardingContainer.exposeHostPort adds the port to exposedPorts before starting sshd and requesting the tunnel, and never removes it on failure. The retry then finds the port already recorded, returns immediately, and the wrapper reports success. Containers started afterwards get no host.testcontainers.internal extra host, so discovery fails after its full 20s timeout.

Fix

In infra/.../hostPortForwarding.kt:

  • Forget the port before retrying: on failure, reflectively remove (port, port) from PortForwardingContainer.INSTANCE.exposedPorts, so the next attempt actually boots sshd and requests the forward. Only the failed port is removed; tunnels other test classes rely on are untouched (unlike reset()).
  • Fail fast on a dead tunnel: after exposeHostPorts returns, check getNetwork() is present (i.e. the sshd container is running); otherwise throw so the retry kicks in.

Validation

  • Reproduced in a standalone Gradle project pinned to Testcontainers 2.0.4, using the same wrapper logic and fresh-Network setup as RestateDeployer, by forcing the first sshd start to fail: baseline wrapper reports success with no /etc/hosts entry 3/3; with this fix the retry succeeds 3/3. An already-running tunnel and other ports exposed in parallel were unaffected.
  • :infra:compileKotlin and :infra:spotlessCheck pass. Verified exposedPorts, getNetwork() and INSTANCE exist with these names in Testcontainers 2.0.4.
  • Not run against the real ServiceToServiceScopeConcurrency class locally.

Generated by Claude Code

… tunnel

Testcontainers records the port in PortForwardingContainer.exposedPorts
before booting sshd, so retrying after a failed start was a silent no-op
and containers started without the host.testcontainers.internal entry.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01178nhmVuJrMJmVwwAoiQ5f
@github-actions

Copy link
Copy Markdown

Test Results

 6 files  ±0   6 suites  ±0   2m 29s ⏱️ +15s
46 tests ±0  46 ✅ ±0  0 💤 ±0  0 ❌ ±0 
97 runs  ±0  97 ✅ ±0  0 💤 ±0  0 ❌ ±0 

Results for commit 78d7407. ± Comparison against base commit a201d78.

@AhmedSoliman
AhmedSoliman merged commit d8619ae into main Oct 26, 2026
4 checks passed
@AhmedSoliman
AhmedSoliman deleted the fix-host-port-forwarding-retry branch October 26, 2026 18:41
@github-actions github-actions Bot locked and limited conversation to collaborators Oct 26, 2026
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants