From d59fe03ce587117ed8040af13cb206a53d67656e Mon Sep 17 00:00:00 2001 From: Gyu-Ho Lee Date: Wed, 26 Jul 2017 12:02:23 -0700 Subject: [PATCH] api/etcdhttp: serve error information in '/health' Signed-off-by: Gyu-Ho Lee --- e2e/ctl_v3_alarm_test.go | 2 +- etcdserver/api/etcdhttp/metrics.go | 35 +++++++++++++++++++++++++----- 2 files changed, 30 insertions(+), 7 deletions(-) diff --git a/e2e/ctl_v3_alarm_test.go b/e2e/ctl_v3_alarm_test.go index 50baae5e9e72..3733ad207b81 100644 --- a/e2e/ctl_v3_alarm_test.go +++ b/e2e/ctl_v3_alarm_test.go @@ -53,7 +53,7 @@ func alarmTest(cx ctlCtx) { } // '/health' handler should return 'false' - if err := cURLGet(cx.epc, cURLReq{endpoint: "/health", expected: `{"health": "false"}`}); err != nil { + if err := cURLGet(cx.epc, cURLReq{endpoint: "/health", expected: `{"health": "false", "errors": ["NOSPACE"]}`}); err != nil { cx.t.Fatalf("failed get with curl (%v)", err) } diff --git a/etcdserver/api/etcdhttp/metrics.go b/etcdserver/api/etcdhttp/metrics.go index 7925924c30bd..446c5a489440 100644 --- a/etcdserver/api/etcdhttp/metrics.go +++ b/etcdserver/api/etcdhttp/metrics.go @@ -21,6 +21,7 @@ import ( "time" "github.com/coreos/etcd/etcdserver" + "github.com/coreos/etcd/etcdserver/api/v3rpc/rpctypes" "github.com/coreos/etcd/etcdserver/etcdserverpb" "github.com/coreos/etcd/raft" @@ -57,34 +58,56 @@ func newHealthHandler(srv *etcdserver.EtcdServer) http.HandlerFunc { return } h := checkHealth(srv) - d := []byte(fmt.Sprintf(`{"health": "%v"}`, h.Health)) + d := h.marshal() if !h.Health { - http.Error(w, string(d), http.StatusServiceUnavailable) + http.Error(w, d, http.StatusServiceUnavailable) return } w.WriteHeader(http.StatusOK) - w.Write(d) + w.Write([]byte(d)) } } type health struct { - Health bool `json:"health"` + Health bool `json:"health"` + Errors []string `json:"errors"` +} + +func (h health) marshal() string { + s := fmt.Sprintf(`{"health": "%v"`, h.Health) + if len(h.Errors) > 0 { + ss := []string{} + for _, v := range h.Errors { + ss = append(ss, fmt.Sprintf("%q", v)) + } + s += fmt.Sprintf(`, "errors": %v`, ss) + } + s += "}" + return s } func checkHealth(srv *etcdserver.EtcdServer) health { h := health{Health: false} - if len(srv.Alarms()) > 0 { - // TODO: provide alarm lists + + as := srv.Alarms() + if len(as) > 0 { + for _, v := range as { + h.Errors = append(h.Errors, v.Alarm.String()) + } return h } if uint64(srv.Leader()) == raft.None { + h.Errors = append(h.Errors, rpctypes.ErrNoLeader.Error()) return h } ctx, cancel := context.WithTimeout(context.Background(), time.Second) _, err := srv.Do(ctx, etcdserverpb.Request{Method: "QGET"}) cancel() + if err != nil { + h.Errors = append(h.Errors, err.Error()) + } h.Health = err == nil return h