e2e/shutdown_test.go

4effb29e572777a68b56b21da1b1733fb6219e89
gitbay/e2e/shutdown_test.go history · blame · raw

83 lines · 3062 bytes

 1package e2e
 2
 3import (
 4	"fmt"
 5	"net/http"
 6	"os/exec"
 7	"path/filepath"
 8	"strings"
 9	"syscall"
10	"testing"
11	"time"
12)
13
14// SIGTERM is how a deploy restarts the daemon. It used to be a plain
15// kill: no listener closed, no request or push allowed to finish. The
16// daemon now stops its listeners, drains, and exits 0 (#105).
17func TestServeStopsOnSIGTERM(t *testing.T) {
18	inst := startInstance(t)
19	if resp, err := http.Get(inst.base() + "/healthz"); err != nil || resp.StatusCode != 200 {
20		t.Fatalf("healthz before shutdown: %v", err)
21	}
22	if err := inst.proc.Process.Signal(syscall.SIGTERM); err != nil {
23		t.Fatal(err)
24	}
25	done := make(chan error, 1)
26	go func() { done <- inst.proc.Wait() }()
27	select {
28	case err := <-done:
29		if err != nil {
30			t.Fatalf("daemon did not exit cleanly on SIGTERM: %v", err)
31		}
32	case <-time.After(15 * time.Second):
33		t.Fatal("daemon still running 15s after SIGTERM")
34	}
35	if _, err := http.Get(inst.base() + "/healthz"); err == nil {
36		t.Fatal("http listener still answering after shutdown")
37	}
38}
39
40// A CLI keeps a shared connection open between commands. On shutdown that
41// idle connection is closed at once rather than holding the drain for its
42// full 30 s; only a session mid-command is waited for (#141).
43func TestShutdownClosesIdleConnections(t *testing.T) {
44	inst := startInstance(t)
45	aliceKey := inst.newKey(t, "alice")
46	inst.admin(t, "admin", "user", "create", "alice", "--key", aliceKey+".pub")
47
48	// A control master with no session: -N, backgrounded, persistent.
49	// A unix socket path has a short limit; t.TempDir is far past it here.
50	sock := fmt.Sprintf("/tmp/gitbay-e2e-cm-%d", inst.port)
51	base := []string{"-p", fmt.Sprint(inst.port), "-i", aliceKey, "-o", "IdentitiesOnly=yes",
52		"-o", "StrictHostKeyChecking=no", "-o", "UserKnownHostsFile=" + filepath.Join(inst.sshDir, "known_hosts"),
53		"-o", "BatchMode=yes", "-o", "ControlPath=" + sock}
54	master := exec.Command("ssh", append(append([]string{}, base...), "-o", "ControlMaster=yes", "-o", "ControlPersist=yes", "-N", "-f", "git@127.0.0.1")...)
55	if out, err := master.CombinedOutput(); err != nil {
56		t.Fatalf("control master: %v\n%s", err, out)
57	}
58	t.Cleanup(func() {
59		exec.Command("ssh", append(append([]string{}, base...), "-O", "exit", "git@127.0.0.1")...).Run()
60	})
61	// The shared connection works.
62	if out, err := exec.Command("ssh", append(append([]string{}, base...), "git@127.0.0.1", "whoami")...).Output(); err != nil || strings.TrimSpace(string(out)) != "alice" {
63		t.Fatalf("multiplexed whoami: %v %q", err, out)
64	}
65
66	start := time.Now()
67	if err := inst.proc.Process.Signal(syscall.SIGTERM); err != nil {
68		t.Fatal(err)
69	}
70	done := make(chan error, 1)
71	go func() { done <- inst.proc.Wait() }()
72	select {
73	case err := <-done:
74		if err != nil {
75			t.Fatalf("daemon did not exit cleanly: %v", err)
76		}
77	case <-time.After(20 * time.Second):
78		t.Fatal("daemon still running 20s after SIGTERM with only an idle connection open")
79	}
80	if took := time.Since(start); took > 5*time.Second {
81		t.Fatalf("shutdown took %s with only an idle connection open", took)
82	}
83}