forked from infobloxopen/atlas-app-toolkit
-
Notifications
You must be signed in to change notification settings - Fork 0
/
handler.go
119 lines (98 loc) · 2.8 KB
/
handler.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
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
package health
import (
"net/http"
"sync"
)
type checksHandler struct {
lock sync.RWMutex
livenessPath string
livenessChecks map[string]Check
readinessPath string
readinessChecks map[string]Check
}
// Checker ...
type Checker interface {
AddLiveness(name string, check Check)
AddReadiness(name string, check Check)
Handler() http.Handler
RegisterHandler(mux *http.ServeMux)
}
// NewChecksHandler accepts two strings: health and ready paths.
// These paths will be used for liveness and readiness checks.
func NewChecksHandler(healthPath, readyPath string) Checker {
if healthPath[0] != '/' {
healthPath = "/" + healthPath
}
if readyPath[0] != '/' {
readyPath = "/" + readyPath
}
ch := &checksHandler{
livenessPath: healthPath,
livenessChecks: map[string]Check{},
readinessPath: readyPath,
readinessChecks: map[string]Check{},
}
return ch
}
func (ch *checksHandler) AddLiveness(name string, check Check) {
ch.lock.Lock()
defer ch.lock.Unlock()
ch.livenessChecks[name] = check
}
func (ch *checksHandler) AddReadiness(name string, check Check) {
ch.lock.Lock()
defer ch.lock.Unlock()
ch.readinessChecks[name] = check
}
// Handler returns a new http.Handler for the given health checker
func (ch *checksHandler) Handler() http.Handler {
mux := http.NewServeMux()
ch.registerMux(mux)
return mux
}
// RegisterHandler registers the given health and readiness patterns onto the given http.ServeMux
func (ch *checksHandler) RegisterHandler(mux *http.ServeMux) {
ch.registerMux(mux)
}
func (ch *checksHandler) registerMux(mux *http.ServeMux) {
mux.HandleFunc(ch.readinessPath, ch.readyEndpoint)
mux.HandleFunc(ch.livenessPath, ch.healthEndpoint)
}
func (ch *checksHandler) healthEndpoint(rw http.ResponseWriter, r *http.Request) {
ch.handle(rw, r, ch.livenessChecks)
}
func (ch *checksHandler) readyEndpoint(rw http.ResponseWriter, r *http.Request) {
ch.handle(rw, r, ch.readinessChecks)
}
func (ch *checksHandler) handle(rw http.ResponseWriter, r *http.Request, checksSets ...map[string]Check) {
if r.Method != http.MethodGet {
http.Error(rw, http.StatusText(http.StatusMethodNotAllowed), http.StatusMethodNotAllowed)
return
}
errors := map[string]error{}
status := http.StatusOK
ch.lock.RLock()
defer ch.lock.RUnlock()
for _, checks := range checksSets {
for name, check := range checks {
if check == nil {
continue
}
if err := check(); err != nil {
status = http.StatusServiceUnavailable
errors[name] = err
}
}
}
rw.WriteHeader(status)
return
// Uncomment to write errors and get non-empty response
// rw.Header().Set("Content-Type", "application/json; charset=utf-8")
// if status == http.StatusOK {
// rw.Write([]byte("{}\n"))
// } else {
// encoder := json.NewEncoder(rw)
// encoder.SetIndent("", " ")
// encoder.Encode(errors)
// }
}