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

Fix zero's health endpoint to return json response #8858

Merged
merged 10 commits into from
Jun 28, 2023
54 changes: 52 additions & 2 deletions dgraph/cmd/zero/http.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ package zero

import (
"context"
"encoding/json"
"fmt"
"net/http"
"strconv"
Expand All @@ -26,7 +27,9 @@ import (

"github.com/gogo/protobuf/jsonpb"
"github.com/golang/glog"
"github.com/pkg/errors"

"github.com/dgraph-io/dgo/v230/protos/api"
"github.com/dgraph-io/dgraph/protos/pb"
"github.com/dgraph-io/dgraph/x"
)
Expand Down Expand Up @@ -231,9 +234,56 @@ func (st *state) getState(w http.ResponseWriter, r *http.Request) {
}
}

func (s *Server) zeroHealth(ctx context.Context) (*api.Response, error) {
if ctx.Err() != nil {
return nil, errors.Wrap(ctx.Err(), "http request context error")
}
health := pb.HealthInfo{
Instance: "zero",
mangalaman93 marked this conversation as resolved.
Show resolved Hide resolved
Address: x.WorkerConfig.MyAddr,
Status: "healthy",
Version: x.Version(),
Uptime: int64(time.Since(x.WorkerConfig.StartTime) / time.Second),
LastEcho: time.Now().Unix(),
}
jsonOut, err := json.Marshal(health)
if err != nil {
return nil, errors.Wrapf(err, "unable to marshal zero health, error")
}
return &api.Response{Json: jsonOut}, nil
}

func (st *state) pingResponse(w http.ResponseWriter, r *http.Request) {
x.AddCorsHeaders(w)

w.WriteHeader(http.StatusOK)
_, _ = w.Write([]byte("OK"))
/*
* zero is changed to also output the health in JSON format for client
* request header "Accept: application/json".
*
* Backward compatibility- Before this change the '/health' endpoint
* used to output the string OK. After the fix it returns OK when the
* client sends the request without "Accept: application/json" in its
* http header.
*/
switch r.Header.Get("Accept") {
mangalaman93 marked this conversation as resolved.
Show resolved Hide resolved
case "application/json":
resp, err := (st.zero).zeroHealth(r.Context())
if err != nil {
x.SetStatus(w, x.Error, err.Error())
return
}
w.Header().Set("Content-Type", "application/json; charset=utf-8")
w.WriteHeader(http.StatusOK)
if _, err := w.Write(resp.Json); err != nil {
glog.Warningf("http error send failed, error msg=[%v]", err)
return
}
default:
w.Header().Set("Content-Type", "text/plain; charset=utf-8")
w.WriteHeader(http.StatusOK)
if _, err := w.Write([]byte("OK")); err != nil {
glog.Warningf("http error send failed, error msg=[%v]", err)
return
}
}
}
50 changes: 50 additions & 0 deletions dgraph/cmd/zero/zero_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,13 @@ package zero

import (
"context"
"encoding/json"
"io/ioutil"
"math"
"net/http"
"net/url"
"testing"
"time"

"github.com/stretchr/testify/require"
"google.golang.org/grpc"
Expand Down Expand Up @@ -112,3 +117,48 @@ func TestProposalKey(t *testing.T) {
}
require.Equal(t, len(uniqueKeys), 10, "each iteration should create unique key")
}

func TestZeroHealth(t *testing.T) {
client := http.Client{Timeout: 3 * time.Second}
u := &url.URL{
Scheme: "http",
Host: testutil.ContainerAddr("zero1", 6080),
Path: "health",
}

// JSON format
req, err := http.NewRequest("GET", u.String(), nil)
require.NoError(t, err)

req.Header.Add("Accept", `application/json`)
start := time.Now().Unix()
resp, err := client.Do(req)
require.NoError(t, err)
defer resp.Body.Close()

body, err := ioutil.ReadAll(resp.Body)
require.NoError(t, err)

var r map[string]interface{}
err = json.Unmarshal(body, &r)
require.NoError(t, err)

require.Equal(t, "zero", r["instance"].(string))
require.Equal(t, "zero1:5080", r["address"].(string))
require.Equal(t, "healthy", r["status"].(string))
require.NotEqual(t, 0, len(r["version"].(string)))
require.Greater(t, r["uptime"].(float64), 0.0)
require.GreaterOrEqual(t, int64(r["lastEcho"].(float64)), start)

// String format
req, err = http.NewRequest("GET", u.String(), nil)
require.NoError(t, err)

resp, err = client.Do(req)
require.NoError(t, err)
defer resp.Body.Close()

body, err = ioutil.ReadAll(resp.Body)
require.NoError(t, err)
require.Equal(t, string(body), "OK")
}