Retry the macOS integration tests only when the VM's resolver times out - #1970
Retry the macOS integration tests only when the VM's resolver times out#1970JamBalaya56562 wants to merge 2 commits into
Conversation
Docker runs inside a QEMU-backed Lima VM on the macOS runners, and name
resolution in that VM times out often enough to take down a whole reactor run
that has nothing wrong with it:
fabric8io#2 [internal] load metadata for registry.access.redhat.com/ubi8/ubi-minimal:8.6
fabric8io#2 ERROR: failed to do request: ... lookup registry.access.redhat.com: i/o timeout
It is not tied to one registry - the same signature has hit Docker Hub, naming
the resolver it gave up on:
lookup registry-1.docker.io on 127.0.0.53:53:
read udp 127.0.0.1:33196->127.0.0.53:53: i/o timeout
Retrying the whole step would be the obvious answer and the wrong one: a full
reactor run takes 29 to 34 minutes, so every flake would cost half an hour, and
a genuine failure would pay it too before failing again. So the retry is
narrowed twice over. It only happens for that resolver signature - anything
else fails immediately, with a message saying so - and it resumes from the
module Maven names in its own `-rf` hint rather than starting over. The run
that prompted this failed on module 14 of 36 after 17 minutes, so resuming
covers 23 modules instead of 36.
The retry is a single attempt, and it always leaves a warning annotation
behind, so a resolver that has stopped working entirely still turns the job red
and how often this fires stays visible in the logs.
2a1569f to
0bee366
Compare
| fi | ||
|
|
||
| # Maven prints the module to resume from, so only that module and the ones after it run | ||
| # again; everything already built stays built. |
There was a problem hiding this comment.
This may not be accurate because the initial invocation includes clean, which removes the reactor's build outputs. Could we clarify what Maven actually avoids rebuilding when -rf is used after a clean install failure
There was a problem hiding this comment.
You are right, and the comment was wrong. clean does run for every module in
the resumed reactor, so those are rebuilt from scratch.
What resuming actually skips is the modules before the resume point: they are
left out of the reactor altogether, so no phase runs for them and their images
and containers are not built a second time. I checked it on a throwaway
three-module reactor — after mvn clean install -rf :b, a marker file left in
a/target survived while the ones in b/target and c/target were removed.
That is still where the saving is: the run that prompted this failed on module
14 of 36 after 17 minutes, so resuming re-runs 23 modules instead of 36, against
29 to 34 minutes for a full reactor. Their artifacts are already in ~/.m2 from
the first attempt, so the resumed build resolves against them.
Reworded in the follow-up commit.
The comment claimed that "everything already built stays built", which is not what happens: the retry runs `mvn clean install -rf :<module>`, and `clean` does run for every module in the resumed reactor. What it actually saves is the modules *before* the resume point, which are left out of the reactor altogether - no phase runs for them at all, so their images and containers are not built a second time. Measured on a three-module reactor: after `mvn clean install -rf :b`, a marker file left in `a/target` survived while the ones in `b/target` and `c/target` were removed. That is still where the time goes - the run that prompted this failed on module 14 of 36 after 17 minutes - but the comment should describe it correctly.
Why
Docker runs inside a QEMU-backed Lima VM on the macOS runners, and name resolution in that VM times out often enough to take down reactor runs that have nothing wrong with them. Most recently on this job:
It is not about one registry. The same signature has hit Docker Hub, and there the error names the resolver it gave up on:
127.0.0.53is the VM's ownsystemd-resolved, so what fails is the client side, inside the VM — not the registries, which resolve and serve manifests fine from a healthy network.Why not just retry the step
A full reactor run on these runners takes 29 to 34 minutes (measured on two green macOS jobs). Retrying the step outright means every flake costs another half hour, and a genuine failure pays that too before failing a second time.
So the retry is narrowed twice over:
1. Only for that signature. Anything else fails immediately, with an annotation saying why. The two real failures found on macOS this month — a Postgres healthcheck timeout and a 409 during container removal — would not be retried.
2. Only from where it broke. Maven prints the module to resume from, and the step reuses it. The run above failed on module 14 of 36 after 17 minutes; resuming re-runs 23 modules instead of 36.
It is a single attempt, and it always leaves a
::warning::behind, so a resolver that has genuinely stopped working still turns the job red, and how often this fires stays visible in the logs.Verification
The step's script was extracted from the YAML and run against a stubbed
mvn, so what was tested is what the workflow contains.mvninvocations-rf :dmp-it-buildx-push::error::Integration tests failed for a reason other than name resolution-rfhintThe log is written to
${RUNNER_TEMP}rather than intoit/, so nothing new appears in the workspace during the build — confirmed in the same runs.Not covered by this
This does not make the resolver reliable, it only stops one timeout from discarding 17 minutes of work. Two plugin-side levers were measured first and neither addresses it:
<buildx><configFile>with a[dns]section configures the containers BuildKit runs forRUNsteps, not buildkitd's own registry lookups. Pointed at an unroutable resolver (203.0.113.1),load metadatastill completed in 2.7s while theRUNstep reportednameserver 203.0.113.1.<buildx><driverOpts><network>host</network>puts buildkitd on the VM's resolver at127.0.0.53— the one that timed out above.Independent of #1968, which covers the Windows daemon readiness and pre-pulls base images for the classic build path. If that lands first this needs a trivial rebase, since it moves
DOCKER_HOSTto the job'senv.