Skip to content

Commit 0487692

Browse files
waveywavestekton-robot
authored andcommitted
Add Signal Forwarding to Entrypoint Runner
The Entrypoint process should be notified and signals received should be propogated as necessary. This signal forwarding mimics the one in https://github.com/pablo-ruth/go-init which is an golang implementation of https://github.com/Yelp/dumb-init . The cmd.Run() has also been replaced with a cmd.Start() and cmd.Wait() to systematically start the command and Wait for it's completion without prematurely exiting.
1 parent ed4d021 commit 0487692

File tree

2 files changed

+57
-3
lines changed

2 files changed

+57
-3
lines changed

cmd/entrypoint/runner.go

Lines changed: 35 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@ package main
33
import (
44
"os"
55
"os/exec"
6+
"os/signal"
7+
"syscall"
68

79
"github.com/tektoncd/pipeline/pkg/entrypoint"
810
)
@@ -11,22 +13,52 @@ import (
1113
// stdout/stderr are collected -- needs e2e tests.
1214

1315
// realRunner actually runs commands.
14-
type realRunner struct{}
16+
type realRunner struct {
17+
signals chan os.Signal
18+
}
1519

1620
var _ entrypoint.Runner = (*realRunner)(nil)
1721

18-
func (*realRunner) Run(args ...string) error {
22+
func (rr *realRunner) Run(args ...string) error {
1923
if len(args) == 0 {
2024
return nil
2125
}
2226
name, args := args[0], args[1:]
2327

28+
// Receive system signals on "rr.signals"
29+
if rr.signals == nil {
30+
rr.signals = make(chan os.Signal, 1)
31+
}
32+
defer close(rr.signals)
33+
signal.Notify(rr.signals)
34+
defer signal.Reset()
35+
2436
cmd := exec.Command(name, args...)
2537
cmd.Stdout = os.Stdout
2638
cmd.Stderr = os.Stderr
39+
// dedicated PID group used to forward signals to
40+
// main process and all children
41+
cmd.SysProcAttr = &syscall.SysProcAttr{Setpgid: true}
42+
43+
// Start defined command
44+
if err := cmd.Start(); err != nil {
45+
return err
46+
}
2747

28-
if err := cmd.Run(); err != nil {
48+
// Goroutine for signals forwarding
49+
go func() {
50+
for s := range rr.signals {
51+
// Forward signal to main process and all children
52+
if s != syscall.SIGCHLD {
53+
_ = syscall.Kill(-cmd.Process.Pid, s.(syscall.Signal))
54+
}
55+
}
56+
}()
57+
58+
// Wait for command to exit
59+
if err := cmd.Wait(); err != nil {
2960
return err
3061
}
62+
3163
return nil
3264
}

cmd/entrypoint/runner_test.go

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
package main
2+
3+
import (
4+
"os"
5+
"syscall"
6+
"testing"
7+
)
8+
9+
// TestRealRunnerSignalForwarding will artificially put an interrupt signal (SIGINT) in the rr.signals chan.
10+
// The chan will not be reinitialized in the runner considering we have already initialized it here.
11+
// Once the sleep process starts, if the signal is successfully received by the parent process, it
12+
// will interrupt and stop the sleep command.
13+
func TestRealRunnerSignalForwarding(t *testing.T) {
14+
rr := realRunner{}
15+
rr.signals = make(chan os.Signal, 1)
16+
rr.signals <- syscall.SIGINT
17+
if err := rr.Run("sleep", "3600"); err.Error() == "signal: interrupt" {
18+
t.Logf("SIGINT forwarded to Entrypoint")
19+
} else {
20+
t.Fatalf("Unexpected error received: %v", err)
21+
}
22+
}

0 commit comments

Comments
 (0)