Pin ports in tests#1162
Conversation
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## main #1162 +/- ##
=======================================
Coverage 82.78% 82.78%
=======================================
Files 123 123
Lines 10127 10127
=======================================
Hits 8384 8384
Misses 1743 1743 ☔ View full report in Codecov by Harness. 🚀 New features to boost your workflow:
|
There was a problem hiding this comment.
Pull request overview
This PR reduces CI flakes caused by Dapr sidecar startup failures where multiple daprd listeners can end up attempting to bind the same randomly-chosen port, leading to bind: address already in use and a fatal sidecar exit. It does this primarily by pinning additional daprd listener ports in the integration test harness and by making the example-test retry detection cover both internal and API gRPC startup failures.
Changes:
- Pin daprd internal gRPC and metrics ports (in addition to existing gRPC/HTTP pins) for integration test sidecars.
- Broaden the example-test “port bind failure” detection marker to match both internal and API gRPC fatal-start messages.
- Update
DaprRunnerunit tests to validate retry behavior for both internal and API gRPC server startup failures.
Reviewed changes
Copilot reviewed 4 out of 4 changed files in this pull request and generated no comments.
| File | Description |
|---|---|
tests/integration/conftest.py |
Adds pinned internal_grpc_port and metrics_port to the integration sidecar launcher and passes the corresponding dapr run flags. |
tests/integration/AGENTS.md |
Documents the expanded set of pinned ports and the motivation (avoiding daprd listener port-collision flakes). |
tests/examples/test_dapr_runner.py |
Updates unit tests to cover both internal and API gRPC fatal-start variants for retry behavior. |
tests/examples/conftest.py |
Broadens the port-bind failure marker to match the fatal line regardless of whether it references internal or API gRPC. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
19d371d to
21854e9
Compare
Signed-off-by: Albert Callarisa <albert@diagrid.io>
Signed-off-by: Albert Callarisa <albert@diagrid.io>
Description
Fixes the "bind: address already in use" flakes that kill
validateCI jobs at sidecar startup. The fix is one principle applied everywhere: tests never let the Dapr CLI pick random ports, and never launch a sidecar until its ports are actually bindable.Root cause 1: the CLI random-port picker assigns duplicate ports
When ports are unset, the CLI picks random free ports independently per listener and can hand the same port to two listeners of one daprd. Observed across four listener pairs: metrics vs internal gRPC and metrics vs API gRPC (runs 28439709999, 28198520326, 27741369607, 29419165370), HTTP vs gRPC (29482935119), and metrics vs HTTP (29485069994). The last variant is why retry-on-output was abandoned entirely: the metrics listener satisfies the CLI's HTTP readiness probe, the sidecar reports "up and running", the app receives Prometheus text as API responses, and the run exits "successfully" with zero failure markers — structurally invisible to output matching. Worth filing against dapr/cli; this PR stops depending on the picker instead.
Root cause 2: pinned ports inside the OS ephemeral range get stolen
Dapr's conventional 500xx ports sit inside Linux's ephemeral source-port range (32768–60999). Any process's outbound localhost connection can be assigned one as its source port, and an established socket blocks daprd's listener bind. Proven twice on this PR (validate 3.14, validate 3.10): daprd retries the internal bind 10×1s and dies; in the second run the CLI's own preflight reported
Port 50002 is not availableover a minute after every managed process was dead. dapr/dapr#10149 doesn't cover this — it only stops daprd's own dials from stealing its ports. Hence all test-pinned ports live below 32768.Root cause 3: teardown races
dapr runspawns daprd (and the app, in its own process group) which outlive the CLI, so "CLI exited" never meant "ports free". Rather than tracking process lifetimes, the startup gate verifies the actual invariant — the ports are bindable — which covers every straggler regardless of process-group membership.