diff --git a/e2e/ctl_v2_test.go b/e2e/ctl_v2_test.go index 2442f40b5..294a478f0 100644 --- a/e2e/ctl_v2_test.go +++ b/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) diff --git a/etcdserver/api/v2http/client.go b/etcdserver/api/v2http/client.go index 39631dc5c..038f5417e 100644 --- a/etcdserver/api/v2http/client.go +++ b/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"}`)) } }