cmd/containerboot: return context error when canceled during tailscale up/set

TestContainerBoot/kube_shutdown_during_state_write flaked with exit
code 1 instead of 0 when SIGTERM arrived while "tailscale up" was
still running. Two problems combined:

tailscaleUp and tailscaleSet wrapped errors with %v, flattening the
error chain, so main's errors.Is(err, context.Canceled) check could
not recognize a graceful shutdown.

Even with %w, cmd.Run under a canceled context usually reports the
death of the killed subprocess ("signal: killed") rather than the
context error that caused it, since Wait prefers the process error.

Check ctx.Err() explicitly and return it (wrapped with %w) so that
a shutdown-driven cancellation is recognized wherever it lands
relative to the subprocess lifetime.

Before: the exit-code failure reproduced 4 times in 808 stress runs
under CPU starvation. After: 0 in 1195 runs.

Fixes #19380

Change-Id: Ie15ca722d2d5ac2a3f79b2d0ab01fb71d4b9220d
Signed-off-by: Brad Fitzpatrick <bradfitz@tailscale.com>
This commit is contained in:
Brad Fitzpatrick
2026-07-27 05:42:29 -07:00
committed by Brad Fitzpatrick
parent 97a75c837d
commit b93d9ba1ff
+14 -2
View File
@@ -150,7 +150,15 @@ func tailscaleUp(ctx context.Context, cfg *settings) error {
cmd.Stdout = os.Stdout cmd.Stdout = os.Stdout
cmd.Stderr = os.Stderr cmd.Stderr = os.Stderr
if err := cmd.Run(); err != nil { if err := cmd.Run(); err != nil {
return fmt.Errorf("tailscale up failed: %v", err) if ctxErr := ctx.Err(); ctxErr != nil {
// A canceled context kills the command, and cmd.Run can
// report the subprocess's death ("signal: killed") rather
// than the context error that caused it. Return the
// context error so that callers (and ultimately main) can
// recognize a graceful shutdown with errors.Is.
return fmt.Errorf("tailscale up failed: %w", ctxErr)
}
return fmt.Errorf("tailscale up failed: %w", err)
} }
return nil return nil
} }
@@ -180,7 +188,11 @@ func tailscaleSet(ctx context.Context, cfg *settings) error {
cmd.Stdout = os.Stdout cmd.Stdout = os.Stdout
cmd.Stderr = os.Stderr cmd.Stderr = os.Stderr
if err := cmd.Run(); err != nil { if err := cmd.Run(); err != nil {
return fmt.Errorf("tailscale set failed: %v", err) if ctxErr := ctx.Err(); ctxErr != nil {
// See the equivalent check in tailscaleUp.
return fmt.Errorf("tailscale set failed: %w", ctxErr)
}
return fmt.Errorf("tailscale set failed: %w", err)
} }
return nil return nil
} }