Skip to content

Commit b4dcc43

Browse files
committed
Add inter-command signal test for exec_service
1 parent 1b98d1e commit b4dcc43

File tree

1 file changed

+69
-0
lines changed

1 file changed

+69
-0
lines changed

exec_service/server/server_test.go

Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ package server_test
1616

1717
import (
1818
"context"
19+
"fmt"
1920
"io"
2021
"net"
2122
"strings"
@@ -275,3 +276,71 @@ func TestCancellation(t *testing.T) {
275276
t.Fatal("stream did not terminate within 5s after cancellation")
276277
}
277278
}
279+
280+
// TestInterCommandSignal starts two concurrent RPCs on the same server:
281+
// command A waits for SIGUSR1, command B delivers it. This verifies that
282+
// commands share a PID namespace — the property that makes the exec service
283+
// useful inside a single sandbox.
284+
func TestInterCommandSignal(t *testing.T) {
285+
c := setup(t)
286+
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
287+
defer cancel()
288+
289+
// Command A: set the trap before printing the PID so the handler is
290+
// active by the time we read the PID and send the signal.
291+
streamA, err := c.RunCommand(ctx, &pb.StartCommandRequest{
292+
CommandLine: "trap 'echo got_signal; exit 0' USR1; echo $$; while true; do sleep 0.1; done",
293+
})
294+
if err != nil {
295+
t.Fatal(err)
296+
}
297+
298+
// Read until we have the PID line.
299+
var buf string
300+
var pid string
301+
for pid == "" {
302+
ev, err := streamA.Recv()
303+
if err != nil {
304+
t.Fatalf("reading PID from command A: %v", err)
305+
}
306+
if ev.GetExited() != nil {
307+
t.Fatalf("command A exited before printing PID: %+v", ev.GetExited())
308+
}
309+
buf += string(ev.GetOutput())
310+
if i := strings.Index(buf, "\n"); i >= 0 {
311+
pid = strings.TrimSpace(buf[:i])
312+
buf = buf[i+1:]
313+
}
314+
}
315+
316+
// Command B: deliver SIGUSR1 to command A.
317+
r := runCtx(t, ctx, c, fmt.Sprintf("kill -USR1 %s", pid), "")
318+
if r.exitCode != 0 {
319+
t.Fatalf("kill exit code = %d, err = %q", r.exitCode, r.errMsg)
320+
}
321+
322+
// Collect the rest of command A's output.
323+
var exitCode int32
324+
for {
325+
ev, err := streamA.Recv()
326+
if err == io.EOF {
327+
break
328+
}
329+
if err != nil {
330+
t.Fatalf("Recv: %v", err)
331+
}
332+
if out := ev.GetOutput(); len(out) > 0 {
333+
buf += string(out)
334+
}
335+
if info := ev.GetExited(); info != nil {
336+
exitCode = info.GetExitCode()
337+
}
338+
}
339+
340+
if exitCode != 0 {
341+
t.Errorf("command A exit code = %d, want 0", exitCode)
342+
}
343+
if !strings.Contains(buf, "got_signal") {
344+
t.Errorf("command A output = %q, want to contain %q", buf, "got_signal")
345+
}
346+
}

0 commit comments

Comments
 (0)