diff --git a/Documentation/v2/admin_guide.md b/Documentation/v2/admin_guide.md index f3f4431e26e..66bec34a186 100644 --- a/Documentation/v2/admin_guide.md +++ b/Documentation/v2/admin_guide.md @@ -45,12 +45,12 @@ It is important to monitor your production etcd cluster for healthy information #### Health Monitoring -At lowest level, etcd exposes health information via HTTP at `/health` in JSON format. If it returns `{"health": "true"}`, then the cluster is healthy. Please note the `/health` endpoint is still an experimental one as in etcd 2.2. +At lowest level, etcd exposes health information via HTTP at `/health` in JSON format. If it returns `{"health":true}`, then the cluster is healthy. ``` $ curl -L http://127.0.0.1:2379/health -{"health": "true"} +{"health":true} ``` You can also use etcdctl to check the cluster-wide health information. It will contact all the members of the cluster and collect the health information for you. diff --git a/Documentation/v2/other_apis.md b/Documentation/v2/other_apis.md index 339d9f8e55e..dfea231c2ff 100644 --- a/Documentation/v2/other_apis.md +++ b/Documentation/v2/other_apis.md @@ -29,5 +29,5 @@ curl http://10.0.0.10:2379/health ``` ```json -{"health": "true"} +{"health":true} ``` diff --git a/e2e/ctl_v3_alarm_test.go b/e2e/ctl_v3_alarm_test.go index 50baae5e9e7..912620e810d 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/e2e/metrics_test.go b/e2e/metrics_test.go index bbb9ee79983..48d5edec2fc 100644 --- a/e2e/metrics_test.go +++ b/e2e/metrics_test.go @@ -41,7 +41,7 @@ func metricsTest(cx ctlCtx) { if err := cURLGet(cx.epc, cURLReq{endpoint: "/metrics", expected: `etcd_debugging_mvcc_keys_total 1`, metricsURLScheme: cx.cfg.metricsURLScheme}); err != nil { cx.t.Fatalf("failed get with curl (%v)", err) } - if err := cURLGet(cx.epc, cURLReq{endpoint: "/health", expected: `{"health": "true"}`, metricsURLScheme: cx.cfg.metricsURLScheme}); err != nil { + if err := cURLGet(cx.epc, cURLReq{endpoint: "/health", expected: `{"health":true}`, metricsURLScheme: cx.cfg.metricsURLScheme}); 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 7925924c30b..03ae616422d 100644 --- a/etcdserver/api/etcdhttp/metrics.go +++ b/etcdserver/api/etcdhttp/metrics.go @@ -16,7 +16,7 @@ package etcdhttp import ( "context" - "fmt" + "encoding/json" "net/http" "time" @@ -57,7 +57,7 @@ func newHealthHandler(srv *etcdserver.EtcdServer) http.HandlerFunc { return } h := checkHealth(srv) - d := []byte(fmt.Sprintf(`{"health": "%v"}`, h.Health)) + d, _ := json.Marshal(h) if !h.Health { http.Error(w, string(d), http.StatusServiceUnavailable) return @@ -67,24 +67,34 @@ func newHealthHandler(srv *etcdserver.EtcdServer) http.HandlerFunc { } } +// TODO: remove manual parsing in etcdctl cluster-health type health struct { - Health bool `json:"health"` + Health bool `json:"health"` + Errors []string `json:"errors,omitempty"` } 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, etcdserver.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