Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

*: add /health endpoint to grpc-proxy #8322

Merged
merged 3 commits into from
Jul 31, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 0 additions & 2 deletions e2e/metrics_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,6 @@
// See the License for the specific language governing permissions and
// limitations under the License.

// +build !cluster_proxy

package e2e

import (
Expand Down
6 changes: 4 additions & 2 deletions etcdmain/grpc_proxy.go
Original file line number Diff line number Diff line change
Expand Up @@ -154,7 +154,7 @@ func startGRPCProxy(cmd *cobra.Command, args []string) {

client := mustNewClient()

srvhttp, httpl := mustHTTPListener(m, tlsinfo)
srvhttp, httpl := mustHTTPListener(m, tlsinfo, client)
errc := make(chan error)
go func() { errc <- newGRPCProxyServer(client).Serve(grpcl) }()
go func() { errc <- srvhttp.Serve(httpl) }()
Expand All @@ -164,6 +164,7 @@ func startGRPCProxy(cmd *cobra.Command, args []string) {
go func() {
mux := http.NewServeMux()
etcdhttp.HandlePrometheus(mux)
grpcproxy.HandleHealth(mux, client)
plog.Fatal(http.Serve(mhttpl, mux))
}()
}
Expand Down Expand Up @@ -310,10 +311,11 @@ func newGRPCProxyServer(client *clientv3.Client) *grpc.Server {
return server
}

func mustHTTPListener(m cmux.CMux, tlsinfo *transport.TLSInfo) (*http.Server, net.Listener) {
func mustHTTPListener(m cmux.CMux, tlsinfo *transport.TLSInfo, c *clientv3.Client) (*http.Server, net.Listener) {
httpmux := http.NewServeMux()
httpmux.HandleFunc("/", http.NotFound)
etcdhttp.HandlePrometheus(httpmux)
grpcproxy.HandleHealth(httpmux, c)
if grpcProxyEnablePprof {
for p, h := range debugutil.PProfHandlers() {
httpmux.Handle(p, h)
Expand Down
19 changes: 10 additions & 9 deletions etcdserver/api/etcdhttp/metrics.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,13 +29,13 @@ import (

const (
pathMetrics = "/metrics"
pathHealth = "/health"
PathHealth = "/health"
)

// HandleMetricsHealth registers metrics and health handlers.
func HandleMetricsHealth(mux *http.ServeMux, srv *etcdserver.EtcdServer) {
mux.Handle(pathMetrics, prometheus.Handler())
mux.Handle(pathHealth, newHealthHandler(srv))
mux.Handle(PathHealth, NewHealthHandler(func() Health { return checkHealth(srv) }))
}

// HandlePrometheus registers prometheus handler on '/metrics'.
Expand All @@ -45,18 +45,18 @@ func HandlePrometheus(mux *http.ServeMux) {

// HandleHealth registers health handler on '/health'.
func HandleHealth(mux *http.ServeMux, srv *etcdserver.EtcdServer) {
mux.Handle(pathHealth, newHealthHandler(srv))
mux.Handle(PathHealth, NewHealthHandler(func() Health { return checkHealth(srv) }))
}

// newHealthHandler handles '/health' requests.
func newHealthHandler(srv *etcdserver.EtcdServer) http.HandlerFunc {
// NewHealthHandler handles '/health' requests.
func NewHealthHandler(hfunc func() Health) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
if r.Method != http.MethodGet {
w.Header().Set("Allow", http.MethodGet)
http.Error(w, "Method Not Allowed", http.StatusMethodNotAllowed)
return
}
h := checkHealth(srv)
h := hfunc()
d, _ := json.Marshal(h)
if !h.Health {
http.Error(w, string(d), http.StatusServiceUnavailable)
Expand All @@ -67,14 +67,15 @@ func newHealthHandler(srv *etcdserver.EtcdServer) http.HandlerFunc {
}
}

// Health defines etcd server health status.
// TODO: remove manual parsing in etcdctl cluster-health
type health struct {
type Health struct {
Health bool `json:"health"`
Errors []string `json:"errors,omitempty"`
}

func checkHealth(srv *etcdserver.EtcdServer) health {
h := health{Health: false}
func checkHealth(srv *etcdserver.EtcdServer) Health {
h := Health{Health: false}

as := srv.Alarms()
if len(as) > 0 {
Expand Down
43 changes: 43 additions & 0 deletions proxy/grpcproxy/health.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
// Copyright 2017 The etcd Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package grpcproxy

import (
"net/http"
"time"

"github.com/coreos/etcd/clientv3"
"github.com/coreos/etcd/etcdserver/api/etcdhttp"
"github.com/coreos/etcd/etcdserver/api/v3rpc/rpctypes"

"golang.org/x/net/context"
)

// HandleHealth registers health handler on '/health'.
func HandleHealth(mux *http.ServeMux, c *clientv3.Client) {
mux.Handle(etcdhttp.PathHealth, etcdhttp.NewHealthHandler(func() etcdhttp.Health { return checkHealth(c) }))
}

func checkHealth(c *clientv3.Client) etcdhttp.Health {
h := etcdhttp.Health{Health: false}
ctx, cancel := context.WithTimeout(c.Ctx(), time.Second)
_, err := c.Get(ctx, "a")
cancel()
h.Health = err == nil || err == rpctypes.ErrPermissionDenied
if !h.Health {
h.Errors = append(h.Errors, err.Error())
}
return h
}