-
Notifications
You must be signed in to change notification settings - Fork 685
/
healthcheck_server.go
123 lines (100 loc) · 3.86 KB
/
healthcheck_server.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
120
121
122
123
package entrypoint
import (
"context"
"net"
"net/http"
"net/http/httputil"
"net/http/pprof"
"net/url"
_ "k8s.io/client-go/plugin/pkg/client/auth"
_ "k8s.io/client-go/plugin/pkg/client/auth/gcp"
"github.com/datawire/ambassador/v2/pkg/acp"
"github.com/datawire/ambassador/v2/pkg/debug"
"github.com/datawire/dlib/dhttp"
)
func handleCheckAlive(w http.ResponseWriter, r *http.Request, ambwatch *acp.AmbassadorWatcher) {
// The liveness check needs to explicitly try to talk to Envoy...
ambwatch.FetchEnvoyReady(r.Context())
// ...then check if the watcher says we're alive.
ok := ambwatch.IsAlive()
if ok {
_, _ = w.Write([]byte("Ambassador is alive and well\n"))
} else {
http.Error(w, "Ambassador is not alive\n", http.StatusServiceUnavailable)
}
}
func handleCheckReady(w http.ResponseWriter, r *http.Request, ambwatch *acp.AmbassadorWatcher) {
// The readiness check needs to explicitly try to talk to Envoy, too. Why?
// Because if you have a pod configured with only the readiness check but
// not the liveness check, and we don't try to talk to Envoy here, then we
// will never ever attempt to talk to Envoy at all, Envoy will never be
// declared alive, and we'll never consider Ambassador ready.
ambwatch.FetchEnvoyReady(r.Context())
ok := ambwatch.IsReady()
if ok {
_, _ = w.Write([]byte("Ambassador is ready and waiting\n"))
} else {
http.Error(w, "Ambassador is not ready\n", http.StatusServiceUnavailable)
}
}
func healthCheckHandler(ctx context.Context, ambwatch *acp.AmbassadorWatcher) error {
dbg := debug.FromContext(ctx)
// We need to do some HTTP stuff by hand to catch the readiness and liveness
// checks here, but forward everything else to diagd.
sm := http.NewServeMux()
// Handle the liveness check and the readiness check directly, by handing them
// off to our functions.
livenessTimer := dbg.Timer("check_alive")
sm.HandleFunc("/ambassador/v0/check_alive",
livenessTimer.TimedHandlerFunc(func(w http.ResponseWriter, r *http.Request) {
handleCheckAlive(w, r, ambwatch)
}))
readinessTimer := dbg.Timer("check_ready")
sm.HandleFunc("/ambassador/v0/check_ready",
readinessTimer.TimedHandlerFunc(func(w http.ResponseWriter, r *http.Request) {
handleCheckReady(w, r, ambwatch)
}))
// Serve any debug info from the golang codebase.
sm.Handle("/debug", dbg)
// Serve pprof endpoints to aid in live debugging.
sm.HandleFunc("/debug/pprof/", pprof.Index)
// For everything else, use a ReverseProxy to forward it to diagd.
//
// diagdOrigin is where diagd is listening.
diagdOrigin, _ := url.Parse("http://127.0.0.1:8004/")
// This reverseProxy is dirt simple: use a director function to
// swap the scheme and host of our request for the ones from the
// diagdOrigin. Leave everything else (notably including the path)
// alone.
reverseProxy := &httputil.ReverseProxy{
Director: func(req *http.Request) {
req.URL.Scheme = diagdOrigin.Scheme
req.URL.Host = diagdOrigin.Host
// If this request is coming from localhost, tell diagd about that.
if acp.HostPortIsLocal(req.RemoteAddr) {
req.Header.Set("X-Ambassador-Diag-IP", "127.0.0.1")
}
},
}
// Finally, use the reverseProxy to handle anything coming in on
// the magic catchall path.
sm.HandleFunc("/", reverseProxy.ServeHTTP)
// Create a listener by hand, so that we can listen on TCP v4. If we don't
// explicitly say "tcp4" here, we seem to listen _only_ on v6, and Bad Things
// Happen.
//
// XXX Why, exactly, is this? That's a lovely question -- we _should_ be OK
// here on a proper dualstack system, but apparently we don't have a proper
// dualstack system? It's quite bizarre, but Kubernetes won't become ready
// without this.
//
// XXX In fact, should we set up another Listener for v6??
listener, err := net.Listen("tcp4", ":8877")
if err != nil {
return err
}
s := &dhttp.ServerConfig{
Handler: sm,
}
return s.Serve(ctx, listener)
}