fix: create artifact plugin socket dir before starting the plugin server. Fixes #16784 - #16785
fix: create artifact plugin socket dir before starting the plugin server. Fixes #16784#16785myzk-a wants to merge 1 commit into
Conversation
Signed-off-by: myzk-a <rorosocksxion@gmail.com>
|
Marking this ready for review. All checks are green except one, which is unrelated to this change. The failing check is
This PR touches only Happy to rebase once #16000 lands if you would rather see a fully green run first. |
|
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: Path: .coderabbit.yaml Review profile: CHILL Plan: Pro Plus Run ID: 📒 Files selected for processing (1)
Included review availability: Your plan provides up to 10 included reviews per hour; 9 remain after this review. 📝 WalkthroughWalkthroughThe artifact plugin initialization command now creates the socket directory before starting the plugin server. It passes the existing plugin name to ChangesArtifact plugin initialization
Estimated code review effort: 2 (Simple) | ~10 minutes Merge Risk: ⚪ Minimal · up to The change creates the artifact-plugin socket directory before starting the server, preventing the reported startup race without changing the plugin contract; no actionable merge-blocking risk remains beyond normal checks and review. Suggested reviewers: 🚥 Pre-merge checks | ✅ 3 | ❌ 2❌ Failed checks (2 warnings)
✅ Passed checks (3 passed)
✨ Finishing Touches 💡 1🛠️ Fix failing CI checks 💡
🧪 Generate unit tests (beta)
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. Comment |
See the pull request guide for details on each item.
make pre-commit -Bmake feature-new)Fixes #16784
Motivation
In the artifact-plugin init container (
init-artifact-<name>), argoexec forks the plugin server before creating the directory the server binds its unix socket in. When the forked server wins that race,bind()fails withENOENT, the server exits, and nothing notices — argoexec then waits out its full 120-second socket timeout while the pod sits inInit:1/2, and the step fails.This affects any workflow that loads an artifact through a plugin. It is also the cause of the intermittent
test-examplesCI failures onexamples/artifact-passing-explicit-plugin.yaml, which reproduce at 2/54 = 3.7% of suite runs (see #16784 for the measurement).Only the load path is affected. The sidecar used to save artifacts gets an emptyDir mounted at exactly
SocketDir(), so kubelet creates the directory and the ordering cannot matter there. The init container has no such mount and relies solely onos.MkdirAll— which is why onlyinit-artifact-*ever hangs.Modifications
Move
os.MkdirAll(pluginName.SocketDir(), 0755)out ofloadArtifactPluginand up above thego func()that starts the plugin server, so the directory is guaranteed to exist before the server can callbind().This does not introduce a new invariant: it makes the init path match the ordering the sidecar path has always had (directory exists → server starts → bind succeeds).
Behaviour is otherwise unchanged. The only observable difference outside the success path is that a
MkdirAllfailure is now reported before the server is forked rather than after, so no orphaned child process is created, and the error carries context.I have deliberately left the second half of the problem for a follow-up, and would rather agree the shape of it on #16784 first: when the plugin server dies for any reason, argoexec still waits the full 120 s for a socket that can never appear, because
startCommandreturns successfully and nothing then watches the child.NewDriver's retry loop already selects onctx.Done(), so cancelling the context when the child exits would be enough — but the cleanest way to observe that exit interacts withcloser()(which is itselfcmd.Wait()), so it deserves its own discussion.Verification
Reproduced deterministically by delaying only the
MkdirAll— a singletime.Sleep(500 * time.Millisecond)at the top ofloadArtifactPlugin, not committed — which makes the forked server reachbind()first every time:bind: no such file or directoryThe only difference between the two runs is the position of
MkdirAll; the delay is identical in both, at the same place in the source. Without the delay the failure occurs naturally in 2/54 suite runs.Every failure in the unfixed run was on
artifact-passing-explicit-plugin.yaml, with the same error and the same ~130 s duration as the failures that occur naturally, so the delay does not create a new failure mode — it makes the existing one certain.The captured container log of a failure (with the controller at
--loglevel=debug, which the executor inherits):and the same container after the fix:
Runs, on my fork:
On tests: the race is not deterministically testable in a unit test, and I did not want to add one that passes whether or not the bug is present. The existing e2e coverage (
test-examples/examples/artifact-passing-explicit-plugin.yaml) exercises this path on every CI run — it is what surfaced the bug — and the two runs above are the evidence that the ordering is the cause. Happy to add something else if reviewers would prefer it.One thing worth flagging separately: none of this was visible in CI, because
.github/actions/e2e-failure-debug/action.ymlcollects pod logs with a singlekubectl logs --all-containers, which aborts at the first not-yet-started container and therefore drops every line from the container holding the answer. I will send that as a separate PR; it is useful regardless of this fix.Documentation
No documentation change. This is an internal bug fix with no user-visible behaviour change — the documented plugin contract (the server listens on the socket path it is given) is unchanged; this makes argo hold up its own end of it reliably.
AI
Claude Code was used throughout this investigation: analysing the CI logs, identifying the root cause, designing the forced-race experiment above, and drafting #16784 and this description.
The code change is mine, and I reviewed and understand it. I ran every CI experiment on my fork myself and read the resulting logs. Before proposing the change I had the history of #14915 checked to confirm the current ordering was not a deliberate design decision that I would be undoing — it was never discussed there, which is why I am confident this is a placement oversight rather than a fence worth leaving standing.
Summary by CodeRabbit