Skip to content

Commit

Permalink
Return info msg for /health endpoint
Browse files Browse the repository at this point in the history
Return 200 OK for status `ready` and `upTimeStats`

Resolves jaegertracing#1450
  • Loading branch information
stefanvassilev committed Apr 8, 2019
1 parent 64f8bce commit 1640594
Show file tree
Hide file tree
Showing 2 changed files with 85 additions and 11 deletions.
66 changes: 56 additions & 10 deletions pkg/healthcheck/handler.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,10 @@
package healthcheck

import (
"encoding/json"
"net/http"
"sync/atomic"
"time"

"go.uber.org/zap"
)
Expand Down Expand Up @@ -46,23 +48,46 @@ func (s Status) String() string {
}
}

type upTimeStats struct {
StartedAt time.Time `json:"startedAt"`
UpTime string `json:"upTime"`
}

type healthCheckResponse struct {
statusCode int
StatusMsg string `json:"status"`
upTimeStats
}

// HealthCheck provides an HTTP endpoint that returns the health status of the service
type HealthCheck struct {
state int32 // atomic, keep at the top to be word-aligned
logger *zap.Logger
mapping map[Status]int
server *http.Server
state int32 // atomic, keep at the top to be word-aligned
status string
upTimeStats upTimeStats
logger *zap.Logger
mapping map[Status]healthCheckResponse
server *http.Server
}

// New creates a HealthCheck with the specified initial state.
func New() *HealthCheck {
hc := &HealthCheck{
state: int32(Unavailable),
mapping: map[Status]int{
Unavailable: http.StatusServiceUnavailable,
Ready: http.StatusNoContent,
upTimeStats: upTimeStats{
StartedAt: time.Now(),
},
logger: zap.NewNop(),
mapping: map[Status]healthCheckResponse{
Unavailable: {
statusCode: http.StatusServiceUnavailable,
StatusMsg: "Server not available",
},
Ready: {
statusCode: http.StatusOK,
StatusMsg: "up",
},
},
server: nil,
}
return hc
}
Expand All @@ -75,12 +100,33 @@ func (hc *HealthCheck) SetLogger(logger *zap.Logger) {
// Handler creates a new HTTP handler.
func (hc *HealthCheck) Handler() http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) {
w.WriteHeader(hc.mapping[hc.Get()])
// this is written only for response with an entity, so, it won't be used for a 204 - No content
w.Write([]byte("Server not available"))

resp := hc.mapping[hc.Get()]
w.WriteHeader(resp.statusCode)

w.Header().Set("Content-Type", "application/json")
w.Write(hc.createRespBody(resp))
})
}

func (hc *HealthCheck) createRespBody(resp healthCheckResponse) []byte {

timeStats := hc.upTimeStats
if resp.statusCode == http.StatusOK {
timeStats.UpTime = upTime(timeStats.StartedAt)
}

hc.upTimeStats = timeStats
resp.upTimeStats = timeStats

healthCheckStatus, _ := json.Marshal(resp)
return healthCheckStatus
}

func upTime(startTime time.Time) string {
return time.Since(startTime).String()
}

// Set a new health check status
func (hc *HealthCheck) Set(state Status) {
atomic.StoreInt32(&hc.state, int32(state))
Expand Down
30 changes: 29 additions & 1 deletion pkg/healthcheck/internal_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,9 @@ import (
"net/http"
"net/http/httptest"
"testing"
"encoding/json"
"io/ioutil"
"time"

"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
Expand All @@ -34,5 +37,30 @@ func TestHttpCall(t *testing.T) {

resp, err := http.Get(server.URL + "/")
require.NoError(t, err)
assert.Equal(t, http.StatusNoContent, resp.StatusCode)
assert.Equal(t, http.StatusOK, resp.StatusCode)

hr := getHealthCheckResponse(t, resp, )

assert.Equal(t, "up", hr.StatusMsg)
assert.Equal(t, hc.upTimeStats.UpTime, hr.upTimeStats.UpTime)
assert.True(t, hc.upTimeStats.StartedAt.Equal(hr.upTimeStats.StartedAt))

time.Sleep(300)
hc.Set(Unavailable)

resp, err = http.Get(server.URL + "/")
require.NoError(t, err)
assert.Equal(t, http.StatusServiceUnavailable, resp.StatusCode)

hrNew := getHealthCheckResponse(t, resp)
assert.Equal(t, hc.upTimeStats.UpTime, hrNew.upTimeStats.UpTime)

}
func getHealthCheckResponse(t *testing.T, resp *http.Response) healthCheckResponse {
body, err := ioutil.ReadAll(resp.Body)
require.NoError(t, err)
var hr healthCheckResponse
err = json.Unmarshal(body, &hr)
require.NoError(t, err)
return hr
}

0 comments on commit 1640594

Please sign in to comment.