-
Notifications
You must be signed in to change notification settings - Fork 1
/
signals.go
84 lines (72 loc) · 2.38 KB
/
signals.go
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
79
80
81
82
83
84
package lu
import (
"context"
"os"
"os/signal"
"syscall"
"github.com/luno/jettison/j"
"github.com/luno/jettison/log"
)
// AppContext manages two contexts for running an app. It responds to different signals by
// cancelling one or both of these contexts. This behaviour allows us to do graceful shutdown
// in kubernetes using a stop script. If the application terminates before the stop script finishes
// then we get an error event from Kubernetes, so we need to be able to shut the application down
// using one signal, then exit the stop script and let Kubernetes send another signal to do the final
// termination. See this for more details on the hook behaviour
// https://kubernetes.io/docs/concepts/containers/container-lifecycle-hooks/
//
// For SIGINT and SIGTERM, we will cancel both contexts, the application should
// finish all processes and call os.Exit
//
// For SIGQUIT, we cancel just the AppContext, the application should shut down all
// processes and wait for termination.
type AppContext struct {
signals chan os.Signal
// AppContext should be used for running the application.
// When it's cancelled, the application should stop running all processes.
AppContext context.Context
appCancel context.CancelFunc
// TerminationContext should be used for the execution of application.
// When it's cancelled the application binary should terminate.
// AppContext will be cancelled with this context as well.
TerminationContext context.Context
termCancel context.CancelFunc
}
func NewAppContext(ctx context.Context) AppContext {
c := AppContext{
signals: make(chan os.Signal, 1),
}
c.TerminationContext, c.termCancel = context.WithCancel(ctx)
c.AppContext, c.appCancel = context.WithCancel(c.TerminationContext)
signal.Notify(c.signals, syscall.SIGQUIT, syscall.SIGINT, syscall.SIGTERM)
go c.monitor(ctx)
return c
}
func (c AppContext) Stop() {
signal.Stop(c.signals)
close(c.signals)
}
func (c AppContext) monitor(ctx context.Context) {
for {
select {
case <-ctx.Done():
return
case s, ok := <-c.signals:
if !ok {
return
}
call, ok := s.(syscall.Signal)
if !ok {
log.Info(ctx, "received unknown OS signal", j.KV("signal", s))
continue
}
log.Info(ctx, "received OS signal", j.KV("signal", call))
switch call {
case syscall.SIGQUIT:
c.appCancel()
case syscall.SIGINT, syscall.SIGTERM:
c.termCancel()
}
}
}
}