Browse Source

api/etcdhttp: serve error information in '/health', marshal health in JSON

Signed-off-by: Gyu-Ho Lee <gyuhox@gmail.com>
Gyu-Ho Lee 8 years ago
parent
commit
4267d368df
3 changed files with 17 additions and 7 deletions
  1. 1 1
      e2e/ctl_v3_alarm_test.go
  2. 1 1
      e2e/metrics_test.go
  3. 15 5
      etcdserver/api/etcdhttp/metrics.go

+ 1 - 1
e2e/ctl_v3_alarm_test.go

@@ -53,7 +53,7 @@ func alarmTest(cx ctlCtx) {
 	}
 	}
 
 
 	// '/health' handler should return 'false'
 	// '/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)
 		cx.t.Fatalf("failed get with curl (%v)", err)
 	}
 	}
 
 

+ 1 - 1
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 {
 	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)
 		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)
 		cx.t.Fatalf("failed get with curl (%v)", err)
 	}
 	}
 }
 }

+ 15 - 5
etcdserver/api/etcdhttp/metrics.go

@@ -16,7 +16,7 @@ package etcdhttp
 
 
 import (
 import (
 	"context"
 	"context"
-	"fmt"
+	"encoding/json"
 	"net/http"
 	"net/http"
 	"time"
 	"time"
 
 
@@ -57,7 +57,7 @@ func newHealthHandler(srv *etcdserver.EtcdServer) http.HandlerFunc {
 			return
 			return
 		}
 		}
 		h := checkHealth(srv)
 		h := checkHealth(srv)
-		d := []byte(fmt.Sprintf(`{"health": "%v"}`, h.Health))
+		d, _ := json.Marshal(h)
 		if !h.Health {
 		if !h.Health {
 			http.Error(w, string(d), http.StatusServiceUnavailable)
 			http.Error(w, string(d), http.StatusServiceUnavailable)
 			return
 			return
@@ -67,24 +67,34 @@ func newHealthHandler(srv *etcdserver.EtcdServer) http.HandlerFunc {
 	}
 	}
 }
 }
 
 
+// TODO: remove manual parsing in etcdctl cluster-health
 type health struct {
 type health struct {
-	Health bool `json:"health"`
+	Health bool     `json:"health"`
+	Errors []string `json:"errors,omitempty"`
 }
 }
 
 
 func checkHealth(srv *etcdserver.EtcdServer) health {
 func checkHealth(srv *etcdserver.EtcdServer) health {
 	h := health{Health: false}
 	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
 		return h
 	}
 	}
 
 
 	if uint64(srv.Leader()) == raft.None {
 	if uint64(srv.Leader()) == raft.None {
+		h.Errors = append(h.Errors, etcdserver.ErrNoLeader.Error())
 		return h
 		return h
 	}
 	}
 
 
 	ctx, cancel := context.WithTimeout(context.Background(), time.Second)
 	ctx, cancel := context.WithTimeout(context.Background(), time.Second)
 	_, err := srv.Do(ctx, etcdserverpb.Request{Method: "QGET"})
 	_, err := srv.Do(ctx, etcdserverpb.Request{Method: "QGET"})
 	cancel()
 	cancel()
+	if err != nil {
+		h.Errors = append(h.Errors, err.Error())
+	}
 
 
 	h.Health = err == nil
 	h.Health = err == nil
 	return h
 	return h