Repository navigation
Expand file tree
/
Copy pathservices_test.go
More file actions
78 lines (72 loc) · 2.45 KB
/
Copy pathservices_test.go
File metadata and controls
78 lines (72 loc) · 2.45 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
//go:build !windows
package exec
import (
"os"
"path/filepath"
"strings"
"syscall"
"testing"
"time"
)
// TestStopServiceProcessEscalatesFromTermToKill uses a child that deliberately
// ignores SIGTERM: the grace must be honored, and the group must still die.
func TestStopServiceProcessEscalatesFromTermToKill(t *testing.T) {
logPath := filepath.Join(t.TempDir(), "service.log")
pid, startedAt, err := StartDetachedService(
`trap '' TERM; echo ready; while true; do sleep 0.05; done`, t.TempDir(), logPath)
if err != nil {
t.Fatalf("start detached service: %v", err)
}
defer testProcessSignal(-pid, syscall.SIGKILL)
if startedAt.IsZero() {
t.Fatal("detached service recorded no start time")
}
// The trap has to be installed before the stop, or the escalation this
// test exists for never happens.
ready := time.Now().Add(10 * time.Second)
for {
body, _ := os.ReadFile(logPath)
if strings.Contains(string(body), "ready") {
break
}
if time.Now().After(ready) {
t.Fatal("detached service never signalled ready")
}
time.Sleep(10 * time.Millisecond)
}
begun := time.Now()
if err := StopServiceProcess(pid, startedAt); err != nil {
t.Fatalf("stop service process: %v", err)
}
if elapsed := time.Since(begun); elapsed < serviceTerminateGrace {
t.Fatalf("kill escalated after %s, before the %s grace", elapsed, serviceTerminateGrace)
}
deadline := time.Now().Add(2 * time.Second)
for serviceLeaderAlive(pid) && time.Now().Before(deadline) {
time.Sleep(serviceStopPoll)
}
if serviceLeaderAlive(pid) {
t.Fatal("process group survived TERM and KILL")
}
if err := StopServiceProcess(pid, startedAt); err != nil {
t.Fatalf("stopping an already dead group is not an error: %v", err)
}
}
// TestProcessIdentityRefusesPIDReuse is the guard that keeps re-adoption from
// claiming whatever process happens to hold a recycled pid.
func TestProcessIdentityRefusesPIDReuse(t *testing.T) {
logPath := filepath.Join(t.TempDir(), "service.log")
pid, startedAt, err := StartDetachedService(`sleep 30`, t.TempDir(), logPath)
if err != nil {
t.Fatalf("start detached service: %v", err)
}
defer testProcessSignal(-pid, syscall.SIGKILL)
matched, err := ProcessIdentityMatches(pid, startedAt)
if err != nil || !matched {
t.Fatalf("live identity matched=%v err=%v", matched, err)
}
matched, err = ProcessIdentityMatches(pid, startedAt.Add(-time.Hour))
if err != nil || matched {
t.Fatalf("recycled identity matched=%v err=%v", matched, err)
}
}