diff --git a/pkg/signal/testfiles/main.go b/pkg/signal/testfiles/main.go new file mode 100644 index 0000000000..e56854c7c3 --- /dev/null +++ b/pkg/signal/testfiles/main.go @@ -0,0 +1,43 @@ +package main + +import ( + "os" + "syscall" + "time" + + "github.com/docker/docker/pkg/signal" + "github.com/sirupsen/logrus" +) + +func main() { + sigmap := map[string]os.Signal{ + "TERM": syscall.SIGTERM, + "QUIT": syscall.SIGQUIT, + "INT": os.Interrupt, + } + signal.Trap(func() { + time.Sleep(time.Second) + os.Exit(99) + }, logrus.StandardLogger()) + go func() { + p, err := os.FindProcess(os.Getpid()) + if err != nil { + panic(err) + } + s := os.Getenv("SIGNAL_TYPE") + multiple := os.Getenv("IF_MULTIPLE") + switch s { + case "TERM", "INT": + if multiple == "1" { + for { + p.Signal(sigmap[s]) + } + } else { + p.Signal(sigmap[s]) + } + case "QUIT": + p.Signal(sigmap[s]) + } + }() + time.Sleep(2 * time.Second) +} diff --git a/pkg/signal/trap_linux_test.go b/pkg/signal/trap_linux_test.go new file mode 100644 index 0000000000..2622766b50 --- /dev/null +++ b/pkg/signal/trap_linux_test.go @@ -0,0 +1,82 @@ +// +build linux + +package signal + +import ( + "fmt" + "io/ioutil" + "os" + "os/exec" + "syscall" + "testing" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +func buildTestBinary(t *testing.T, tmpdir string, prefix string) (string, string) { + tmpDir, err := ioutil.TempDir(tmpdir, prefix) + require.NoError(t, err) + exePath := tmpDir + "/" + prefix + wd, _ := os.Getwd() + testHelperCode := wd + "/testfiles/main.go" + cmd := exec.Command("go", "build", "-o", exePath, testHelperCode) + err = cmd.Run() + require.NoError(t, err) + return exePath, tmpDir +} + +func TestTrap(t *testing.T) { + var sigmap = []struct { + name string + signal os.Signal + multiple bool + }{ + {"TERM", syscall.SIGTERM, false}, + {"QUIT", syscall.SIGQUIT, true}, + {"INT", os.Interrupt, false}, + {"TERM", syscall.SIGTERM, true}, + {"INT", os.Interrupt, true}, + } + exePath, tmpDir := buildTestBinary(t, "", "main") + defer os.RemoveAll(tmpDir) + + for _, v := range sigmap { + cmd := exec.Command(exePath) + cmd.Env = append(os.Environ(), fmt.Sprintf("SIGNAL_TYPE=%s", v.name)) + if v.multiple { + cmd.Env = append(cmd.Env, "IF_MULTIPLE=1") + } + err := cmd.Start() + require.NoError(t, err) + err = cmd.Wait() + if e, ok := err.(*exec.ExitError); ok { + code := e.Sys().(syscall.WaitStatus).ExitStatus() + if v.multiple { + assert.Equal(t, 128+int(v.signal.(syscall.Signal)), code) + } else { + assert.Equal(t, 99, code) + } + continue + } + t.Fatal("process didn't end with any error") + } + +} + +func TestDumpStacks(t *testing.T) { + directory, err := ioutil.TempDir("", "test-dump-tasks") + assert.NoError(t, err) + defer os.RemoveAll(directory) + dumpPath, err := DumpStacks(directory) + assert.NoError(t, err) + readFile, _ := ioutil.ReadFile(dumpPath) + fileData := string(readFile) + assert.Contains(t, fileData, "goroutine") +} + +func TestDumpStacksWithEmptyInput(t *testing.T) { + path, err := DumpStacks("") + assert.NoError(t, err) + assert.Equal(t, os.Stderr.Name(), path) +}