Browse Source

Merge pull request #7068 from heyitsanthony/fix-v2-health

v2http: submit QGET in health endpoint if no progress
Anthony Romano 9 years ago
parent
commit
12d930b40f
2 changed files with 35 additions and 16 deletions
  1. 28 0
      e2e/ctl_v2_test.go
  2. 7 16
      etcdserver/api/v2http/client.go

+ 28 - 0
e2e/ctl_v2_test.go

@@ -312,6 +312,29 @@ func TestCtlV2AuthWithCommonName(t *testing.T) {
 	}
 }
 
+func TestCtlV2ClusterHealth(t *testing.T) {
+	defer testutil.AfterTest(t)
+	epc := setupEtcdctlTest(t, &configNoTLS, true)
+	defer func() {
+		if err := epc.Close(); err != nil {
+			t.Fatalf("error closing etcd processes (%v)", err)
+		}
+	}()
+
+	// has quorum
+	if err := etcdctlClusterHealth(epc, "cluster is healthy"); err != nil {
+		t.Fatalf("cluster-health expected to be healthy (%v)", err)
+	}
+
+	// cut quorum
+	epc.procs[0].Stop()
+	epc.procs[1].Stop()
+	if err := etcdctlClusterHealth(epc, "cluster is unhealthy"); err != nil {
+		t.Fatalf("cluster-health expected to be unhealthy (%v)", err)
+	}
+	epc.procs[0], epc.procs[1] = nil, nil
+}
+
 func etcdctlPrefixArgs(clus *etcdProcessCluster) []string {
 	endpoints := ""
 	if proxies := clus.proxies(); len(proxies) != 0 {
@@ -330,6 +353,11 @@ func etcdctlPrefixArgs(clus *etcdProcessCluster) []string {
 	return cmdArgs
 }
 
+func etcdctlClusterHealth(clus *etcdProcessCluster, val string) error {
+	cmdArgs := append(etcdctlPrefixArgs(clus), "cluster-health")
+	return spawnWithExpect(cmdArgs, val)
+}
+
 func etcdctlSet(clus *etcdProcessCluster, key, value string) error {
 	cmdArgs := append(etcdctlPrefixArgs(clus), "set", key, value)
 	return spawnWithExpect(cmdArgs, value)

+ 7 - 16
etcdserver/api/v2http/client.go

@@ -346,32 +346,23 @@ func serveVars(w http.ResponseWriter, r *http.Request) {
 	fmt.Fprintf(w, "\n}\n")
 }
 
-// TODO: change etcdserver to raft interface when we have it.
-//       add test for healthHandler when we have the interface ready.
 func healthHandler(server *etcdserver.EtcdServer) http.HandlerFunc {
 	return func(w http.ResponseWriter, r *http.Request) {
 		if !allowMethod(w, r.Method, "GET") {
 			return
 		}
-
 		if uint64(server.Leader()) == raft.None {
 			http.Error(w, `{"health": "false"}`, http.StatusServiceUnavailable)
 			return
 		}
-
-		// wait for raft's progress
-		index := server.Index()
-		for i := 0; i < 3; i++ {
-			time.Sleep(250 * time.Millisecond)
-			if server.Index() > index {
-				w.WriteHeader(http.StatusOK)
-				w.Write([]byte(`{"health": "true"}`))
-				return
-			}
+		ctx, cancel := context.WithTimeout(context.Background(), time.Second)
+		defer cancel()
+		if _, err := server.Do(ctx, etcdserverpb.Request{Method: "QGET"}); err != nil {
+			http.Error(w, `{"health": "false"}`, http.StatusServiceUnavailable)
+			return
 		}
-
-		http.Error(w, `{"health": "false"}`, http.StatusServiceUnavailable)
-		return
+		w.WriteHeader(http.StatusOK)
+		w.Write([]byte(`{"health": "true"}`))
 	}
 }