Skip to content

Commit

Permalink
fix(zero): fix zero's health endpoint to return json response (#8858)
Browse files Browse the repository at this point in the history
Fixes #5517 Zero's /health endpoint just returns OK unlike Alpha's JSON
output.

## Problem
curl http://alpha-hostname:alpha-http-port/health returns a JSON output
whereas, curl http://zero-hostname:zero-http-port/health returns a plain
text "OK".

## Solution
Added code to return a plain text output ("OK") for the "text/plain" to
maintain the backward compatibility and return JSON output for "Accept:
application/json" header.

Example Output:
$ curl http://localhost:58696/health
OK

$ curl -H "Accept: application/json" http://localhost:58696/health

{"instance":"zero","address":"zero1:5080","status":"healthy","version":"v23.0.0-16-g90139243f","uptime":9223372036,"lastEcho":1686137664}
  • Loading branch information
jbhamra1 authored Jun 28, 2023
1 parent 5deb42c commit 973efa3
Show file tree
Hide file tree
Showing 2 changed files with 102 additions and 2 deletions.
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",
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") {
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")
}

0 comments on commit 973efa3

Please sign in to comment.