Skip to content

fix(k8s): data race + premature output read in runCommandStreaming#238

Merged
rafeegnash merged 1 commit into
masterfrom
fix/k8s-exec-streaming-race
Jun 25, 2026
Merged

fix(k8s): data race + premature output read in runCommandStreaming#238
rafeegnash merged 1 commit into
masterfrom
fix/k8s-exec-streaming-race

Conversation

@rafeegnash

Copy link
Copy Markdown
Collaborator

Problem

runCommandStreaming spawns separate stdout and stderr scanner goroutines that both call output.WriteString on a single strings.Builder with no synchronization. strings.Builder is not safe for concurrent use → data race. There's also no WaitGroup, and output.String() is read right after cmd.Wait(), which does not join the scanners — so the read can be torn/incomplete.

Where

  • internal/k8s/plan/exec.go:300-337 (runCommandStreaming)

Fix

  • Guard the shared strings.Builder with a sync.Mutex.
  • Join both scanner goroutines with a sync.WaitGroup and drain the pipes (wg.Wait()) before cmd.Wait(), per the os/exec docs ("it is incorrect to call Wait before all reads from the pipe have completed").

Testing

go build ./internal/k8s/..., go vet, and go test -race ./internal/k8s/plan/ all pass.

Closes #208

The stdout/stderr scanner goroutines both wrote to one strings.Builder
with no synchronization (Builder isn't concurrency-safe), and output was
read after cmd.Wait() without joining the goroutines — a data race and a
torn/incomplete read. Guard the builder with a mutex and join both
scanners via a WaitGroup, draining the pipes before cmd.Wait() per the
os/exec contract.

Closes #208
@rafeegnash
rafeegnash merged commit b988507 into master Jun 25, 2026
5 checks passed
@rafeegnash
rafeegnash deleted the fix/k8s-exec-streaming-race branch June 25, 2026 08:12
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

k8s plan exec: data race on shared strings.Builder + output read before stream goroutines finish

1 participant