aboutsummaryrefslogtreecommitdiffstats
path: root/kube2msb/src/kube2msb/vendor/github.com/coreos/pkg/health/health.go
blob: a1c3610fa5c5c0071bd406c14ef83d81fa6ad577 (plain)
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
124
125
126
127
package health

import (
	"expvar"
	"fmt"
	"log"
	"net/http"

	"github.com/coreos/pkg/httputil"
)

// Checkables should return nil when the thing they are checking is healthy, and an error otherwise.
type Checkable interface {
	Healthy() error
}

// Checker provides a way to make an endpoint which can be probed for system health.
type Checker struct {
	// Checks are the Checkables to be checked when probing.
	Checks []Checkable

	// Unhealthyhandler is called when one or more of the checks are unhealthy.
	// If not provided DefaultUnhealthyHandler is called.
	UnhealthyHandler UnhealthyHandler

	// HealthyHandler is called when all checks are healthy.
	// If not provided, DefaultHealthyHandler is called.
	HealthyHandler http.HandlerFunc
}

func (c Checker) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	unhealthyHandler := c.UnhealthyHandler
	if unhealthyHandler == nil {
		unhealthyHandler = DefaultUnhealthyHandler
	}

	successHandler := c.HealthyHandler
	if successHandler == nil {
		successHandler = DefaultHealthyHandler
	}

	if r.Method != "GET" {
		w.Header().Set("Allow", "GET")
		w.WriteHeader(http.StatusMethodNotAllowed)
		return
	}

	if err := Check(c.Checks); err != nil {
		unhealthyHandler(w, r, err)
		return
	}

	successHandler(w, r)
}

type UnhealthyHandler func(w http.ResponseWriter, r *http.Request, err error)

type StatusResponse struct {
	Status  string                 `json:"status"`
	Details *StatusResponseDetails `json:"details,omitempty"`
}

type StatusResponseDetails struct {
	Code    int    `json:"code,omitempty"`
	Message string `json:"message,omitempty"`
}

func Check(checks []Checkable) (err error) {
	errs := []error{}
	for _, c := range checks {
		if e := c.Healthy(); e != nil {
			errs = append(errs, e)
		}
	}

	switch len(errs) {
	case 0:
		err = nil
	case 1:
		err = errs[0]
	default:
		err = fmt.Errorf("multiple health check failure: %v", errs)
	}

	return
}

func DefaultHealthyHandler(w http.ResponseWriter, r *http.Request) {
	err := httputil.WriteJSONResponse(w, http.StatusOK, StatusResponse{
		Status: "ok",
	})
	if err != nil {
		// TODO(bobbyrullo): replace with logging from new logging pkg,
		// once it lands.
		log.Printf("Failed to write JSON response: %v", err)
	}
}

func DefaultUnhealthyHandler(w http.ResponseWriter, r *http.Request, err error) {
	writeErr := httputil.WriteJSONResponse(w, http.StatusInternalServerError, StatusResponse{
		Status: "error",
		Details: &StatusResponseDetails{
			Code:    http.StatusInternalServerError,
			Message: err.Error(),
		},
	})
	if writeErr != nil {
		// TODO(bobbyrullo): replace with logging from new logging pkg,
		// once it lands.
		log.Printf("Failed to write JSON response: %v", err)
	}
}

// ExpvarHandler is copied from https://golang.org/src/expvar/expvar.go, where it's sadly unexported.
func ExpvarHandler(w http.ResponseWriter, r *http.Request) {
	w.Header().Set("Content-Type", "application/json; charset=utf-8")
	fmt.Fprintf(w, "{\n")
	first := true
	expvar.Do(func(kv expvar.KeyValue) {
		if !first {
			fmt.Fprintf(w, ",\n")
		}
		first = false
		fmt.Fprintf(w, "%q: %s", kv.Key, kv.Value)
	})
	fmt.Fprintf(w, "\n}\n")
}