Commit 7b32c1a
authored
code review followups: mergeEnv correctness + lifecycle and packaging fixes (#3)
* fix(agent): mergeEnv must dedup so caller overrides reach the child
The previous append(host, caller...) layout was correct in intent but wrong
under libc semantics: execve hands envp through verbatim, and getenv returns
the FIRST matching entry. With host appended before caller, any key that
existed in the host env (PATH, HOME, USER, ...) would silently shadow the
caller's override.
Switch to a map-based merge so collisions resolve to a single entry, and add
a regression test that pre-populates COCOON_AGENT_OVERRIDE_VAR in the host env
then asserts the caller value reaches the child.
* fix(cmd): silence cobra error+usage dump so exit-code failures stay quiet
run() already handles real errors (logger.Error) and translates exitCodeError
into an exit status. With cobra's defaults the latter path also prints
"Error: exit code N\nUsage:..." to stderr, which is noise — the caller already
knows the exit code and didn't ask for a usage banner.
Set SilenceErrors and SilenceUsage on the root command so only run()'s
intentional outputs reach the user.
* fix(client): prefer ctx.Err over EOF / "closed before exit" on cancel
The runCancel goroutine closes conn on ctx-cancel, which surfaces in the
readLoop's Decode as io.EOF and falls through to "agent: connection closed
before exit frame" — masking the real cause from the caller.
Gate both the in-loop EOF branch and the post-loop !sawExit branch on
ctx.Err first so caller cancel / parent timeout propagates as itself.
* fix(agent): reap Serve's ctx watcher on permanent Accept error
The previous watcher goroutine only exited via <-ctx.Done(), so a permanent
Accept failure (e.g. EMFILE, syscall-level) returned from Serve while the
goroutine kept sitting on ctx until the caller cancelled — potentially never.
Rewrite the watcher as a select on ctx.Done() vs a defer-closed done channel
so every Serve return path reaps it. Also drain connWG on the permanent-error
exit so in-flight sessions finish before the function returns.
Add TestServerWatcherExitsOnPermanentAcceptError, which uses a synthetic
listener whose Accept returns a non-net.ErrClosed error, holds the parent
ctx alive, and asserts the goroutine count returns to baseline after Serve
exits. Without the fix the test reports a leak.
* fix(cmd): log non-host vsock peer rejections + add isHostPeer test
A rejected peer used to disappear silently in the Accept loop, leaving an
operator with no signal that a misconfiguration or a guest-local probe was
hitting the listener. Both Linux and Windows listeners now log a Warn on
rejection with the peer CID/port.
Threading the logger required threading a context too — the listener owns the
Accept loop, which has no per-call ctx, so we capture the serve ctx at
listenVsock(ctx, port) construction time and store it on the struct. This is
the same ctx that the agent.Server uses, so cancellation propagates uniformly.
Add cmd/transport_linux_test.go locking in the isHostPeer contract: vsock.Host
accepted, vsock.Local rejected, non-vsock RemoteAddr rejected.
* packaging(systemd): drop wrong modprobe target + cap restart churn
vhost_vsock is the HOST-side module (driver for /dev/vhost-vsock that the
hypervisor opens), not the guest-side transport — loading it inside the guest
either fails outright or is a no-op. The guest needs virtio_transport_common
+ vmw_vsock_virtio_transport, both of which auto-load on virtio-vsock device
probe. listenVsockWithRetry already covers the bind window if the device
shows up late, so the ExecStartPre is purely misleading; drop it.
Also add StartLimitBurst=5 + StartLimitIntervalSec=60s under [Unit] (their
modern home since systemd v229) so a wedged hypervisor or viosock state
can't peg journald with restart churn — five attempts in a minute is a
firm enough boundary to surface real failures.
* test(agent): drop dead nolint:mnd, add stdin-close + framedWriter coverage
mnd is not in our enabled linter list (see .golangci.yml), so the directive
on context.WithTimeout(..., 10*time.Second) is dead weight — drop it.
Add two regression tests that were missing from prior coverage:
- TestServerMsgStdinCloseTerminatesChildStdin: spawn `wc -c`, push a known
payload, send MsgStdinClose, assert exit=0 and the byte count came back
intact. Locks in the mid-stream stdin-close path that wasn't exercised by
the existing cat round-trip.
- TestFramedWriterAfterTerminal: directly drive framedWriter.Write after
the encoder has emitted a terminal frame. The errTerminalFrameSent branch
must NOT poison lastErr or fire cancel — otherwise the post-Wait
err()-join would mask the legitimate exit path.
* test(agent): trim errorAcceptListener to fields the watcher test exercises
The closeMu/closed/addr fields were stub state that no caller ever read —
remove them so the fake stays focused on its single responsibility (returning
a permanent Accept error). Drops three fields, a Close mutex pair, and an
Addr override branch.
* fix(agent): tear down conns on permanent Accept error before joining
connWG.Wait on the permanent-error path could pin Serve indefinitely if a
handleConn was wedged in framedWriter.Write against a slow peer — the
ctx-cancel and net.ErrClosed paths get the listener+conn teardown for
free (watcher goroutine or external Close), but the permanent-error
branch had no such trigger.
* chore: trim verbose comments across the review branch
Most of the comments added in this branch restated the code or carried
multi-paragraph rationale that belongs in commit messages, not source.
Collapse each to a single WHY line where one is warranted, drop the
rest. -43 net comment lines, no behavior change.
* test(agent): drop t.Parallel on goroutine-leak test
NumGoroutine baseline is perturbed by sibling parallel tests in the
package (dialTestServer spawns Serve goroutines), so the <= before
assertion can stay false even with zero leak.
* test(cmd): make staticAddrConn obey io.Reader/Writer contracts
(0, nil) from Read violates io.Reader and can cause tight loops if the
stub gets reused beyond RemoteAddr(). Return io.EOF + sane Write sink.
* fix(agent): mergeEnv host dedup keeps first occurrence
Comment claimed libc-getenv semantics (first match wins) but the impl
overwrote merged[k] on every host duplicate, ending up with last-wins.
Skip if already seen so behavior matches the documented contract.
* test(agent): make watcher leak regression use specific goroutine signal
Counting all goroutines is too broad — sibling tests can perturb the
baseline. Match on the Serve watcher's stack frame instead so the test
only fires on the goroutine it actually cares about. const block lives
at the top of the file per Cocoon style.
* test(agent): bound watcher stack dump helper
runtime.Stack with an unbounded grow loop could hand back a huge buffer
on a wedged process. Cap doubling at 16 MiB so the helper still produces
a useful diagnostic without unbounded allocation.
* test(agent): simplify watcher leak polling helper
Collapse the dump-and-count helper now that callers only need the count;
the dump path is reserved for the failure diagnostic.
* build: pin golangci-lint installer to the same release tag
master's install.sh recently regressed on resolving v2.9.0, breaking
CI lint. Pinning the installer URL to the version tag matches
upstream's recommended pattern and avoids future master breakage.1 parent 4e91309 commit 7b32c1a
13 files changed
Lines changed: 301 additions & 25 deletions
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
44 | 44 | | |
45 | 45 | | |
46 | 46 | | |
47 | | - | |
| 47 | + | |
48 | 48 | | |
49 | 49 | | |
50 | 50 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
41 | 41 | | |
42 | 42 | | |
43 | 43 | | |
| 44 | + | |
| 45 | + | |
| 46 | + | |
44 | 47 | | |
45 | | - | |
46 | | - | |
47 | | - | |
| 48 | + | |
| 49 | + | |
| 50 | + | |
| 51 | + | |
| 52 | + | |
| 53 | + | |
48 | 54 | | |
49 | 55 | | |
50 | 56 | | |
| |||
56 | 62 | | |
57 | 63 | | |
58 | 64 | | |
| 65 | + | |
| 66 | + | |
| 67 | + | |
| 68 | + | |
59 | 69 | | |
60 | 70 | | |
61 | 71 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
6 | 6 | | |
7 | 7 | | |
8 | 8 | | |
| 9 | + | |
9 | 10 | | |
10 | 11 | | |
11 | 12 | | |
| |||
15 | 16 | | |
16 | 17 | | |
17 | 18 | | |
| 19 | + | |
| 20 | + | |
| 21 | + | |
| 22 | + | |
| 23 | + | |
18 | 24 | | |
19 | 25 | | |
20 | 26 | | |
| |||
25 | 31 | | |
26 | 32 | | |
27 | 33 | | |
28 | | - | |
| 34 | + | |
29 | 35 | | |
30 | 36 | | |
31 | 37 | | |
| |||
94 | 100 | | |
95 | 101 | | |
96 | 102 | | |
| 103 | + | |
| 104 | + | |
| 105 | + | |
| 106 | + | |
| 107 | + | |
| 108 | + | |
| 109 | + | |
| 110 | + | |
| 111 | + | |
| 112 | + | |
| 113 | + | |
| 114 | + | |
| 115 | + | |
| 116 | + | |
| 117 | + | |
| 118 | + | |
| 119 | + | |
| 120 | + | |
| 121 | + | |
| 122 | + | |
| 123 | + | |
| 124 | + | |
| 125 | + | |
| 126 | + | |
| 127 | + | |
| 128 | + | |
97 | 129 | | |
98 | 130 | | |
99 | 131 | | |
| |||
224 | 256 | | |
225 | 257 | | |
226 | 258 | | |
| 259 | + | |
| 260 | + | |
| 261 | + | |
| 262 | + | |
| 263 | + | |
| 264 | + | |
| 265 | + | |
| 266 | + | |
| 267 | + | |
| 268 | + | |
| 269 | + | |
| 270 | + | |
| 271 | + | |
| 272 | + | |
| 273 | + | |
| 274 | + | |
| 275 | + | |
| 276 | + | |
| 277 | + | |
| 278 | + | |
| 279 | + | |
| 280 | + | |
| 281 | + | |
| 282 | + | |
| 283 | + | |
| 284 | + | |
| 285 | + | |
| 286 | + | |
| 287 | + | |
| 288 | + | |
| 289 | + | |
| 290 | + | |
| 291 | + | |
| 292 | + | |
| 293 | + | |
| 294 | + | |
| 295 | + | |
| 296 | + | |
| 297 | + | |
| 298 | + | |
| 299 | + | |
| 300 | + | |
| 301 | + | |
| 302 | + | |
| 303 | + | |
| 304 | + | |
| 305 | + | |
| 306 | + | |
| 307 | + | |
| 308 | + | |
| 309 | + | |
| 310 | + | |
| 311 | + | |
| 312 | + | |
| 313 | + | |
| 314 | + | |
| 315 | + | |
| 316 | + | |
| 317 | + | |
| 318 | + | |
| 319 | + | |
| 320 | + | |
| 321 | + | |
| 322 | + | |
| 323 | + | |
| 324 | + | |
| 325 | + | |
| 326 | + | |
| 327 | + | |
| 328 | + | |
| 329 | + | |
| 330 | + | |
| 331 | + | |
| 332 | + | |
| 333 | + | |
| 334 | + | |
| 335 | + | |
| 336 | + | |
| 337 | + | |
| 338 | + | |
| 339 | + | |
| 340 | + | |
| 341 | + | |
| 342 | + | |
| 343 | + | |
| 344 | + | |
| 345 | + | |
| 346 | + | |
| 347 | + | |
| 348 | + | |
| 349 | + | |
| 350 | + | |
| 351 | + | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
5 | 5 | | |
6 | 6 | | |
7 | 7 | | |
| 8 | + | |
8 | 9 | | |
9 | 10 | | |
| 11 | + | |
10 | 12 | | |
11 | 13 | | |
12 | 14 | | |
| |||
144 | 146 | | |
145 | 147 | | |
146 | 148 | | |
147 | | - | |
148 | | - | |
149 | | - | |
150 | | - | |
151 | | - | |
152 | | - | |
| 149 | + | |
| 150 | + | |
| 151 | + | |
| 152 | + | |
| 153 | + | |
| 154 | + | |
| 155 | + | |
| 156 | + | |
| 157 | + | |
| 158 | + | |
| 159 | + | |
| 160 | + | |
| 161 | + | |
| 162 | + | |
| 163 | + | |
| 164 | + | |
| 165 | + | |
| 166 | + | |
153 | 167 | | |
154 | 168 | | |
155 | 169 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
147 | 147 | | |
148 | 148 | | |
149 | 149 | | |
| 150 | + | |
| 151 | + | |
| 152 | + | |
| 153 | + | |
| 154 | + | |
| 155 | + | |
| 156 | + | |
| 157 | + | |
| 158 | + | |
| 159 | + | |
| 160 | + | |
| 161 | + | |
| 162 | + | |
| 163 | + | |
| 164 | + | |
| 165 | + | |
| 166 | + | |
| 167 | + | |
| 168 | + | |
| 169 | + | |
| 170 | + | |
| 171 | + | |
| 172 | + | |
| 173 | + | |
| 174 | + | |
| 175 | + | |
| 176 | + | |
| 177 | + | |
| 178 | + | |
| 179 | + | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
69 | 69 | | |
70 | 70 | | |
71 | 71 | | |
72 | | - | |
73 | | - | |
74 | | - | |
| 72 | + | |
| 73 | + | |
75 | 74 | | |
76 | 75 | | |
77 | 76 | | |
| 77 | + | |
| 78 | + | |
| 79 | + | |
78 | 80 | | |
79 | 81 | | |
80 | 82 | | |
| |||
105 | 107 | | |
106 | 108 | | |
107 | 109 | | |
| 110 | + | |
| 111 | + | |
| 112 | + | |
| 113 | + | |
108 | 114 | | |
109 | 115 | | |
110 | 116 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
21 | 21 | | |
22 | 22 | | |
23 | 23 | | |
| 24 | + | |
| 25 | + | |
| 26 | + | |
| 27 | + | |
24 | 28 | | |
25 | 29 | | |
26 | 30 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
44 | 44 | | |
45 | 45 | | |
46 | 46 | | |
47 | | - | |
| 47 | + | |
48 | 48 | | |
49 | 49 | | |
50 | 50 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
3 | 3 | | |
4 | 4 | | |
5 | 5 | | |
| 6 | + | |
6 | 7 | | |
7 | 8 | | |
8 | 9 | | |
9 | 10 | | |
10 | 11 | | |
| 12 | + | |
11 | 13 | | |
12 | 14 | | |
13 | 15 | | |
14 | 16 | | |
15 | | - | |
| 17 | + | |
16 | 18 | | |
17 | 19 | | |
18 | 20 | | |
19 | 21 | | |
20 | | - | |
| 22 | + | |
| 23 | + | |
| 24 | + | |
| 25 | + | |
| 26 | + | |
21 | 27 | | |
22 | 28 | | |
23 | 29 | | |
| |||
34 | 40 | | |
35 | 41 | | |
36 | 42 | | |
| 43 | + | |
| 44 | + | |
| 45 | + | |
37 | 46 | | |
38 | 47 | | |
39 | 48 | | |
| |||
45 | 54 | | |
46 | 55 | | |
47 | 56 | | |
| 57 | + | |
48 | 58 | | |
49 | 59 | | |
50 | 60 | | |
| |||
0 commit comments