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

Added /health endpoint which returns the version + status of the proxy and its netmaster #75

Merged
merged 1 commit into from
Jan 21, 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
83 changes: 83 additions & 0 deletions proxy/handlers.go
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,89 @@ func loginHandler(w http.ResponseWriter, req *http.Request) {
writeJSONResponse(w, LoginResponse{Token: tokenStr})
}

const (
// StatusHealthy is used to indicate a healthy response
StatusHealthy = "healthy"

// StatusUnhealthy is used to indicate an unhealthy response
StatusUnhealthy = "unhealthy"
)

// NetmasterHealthCheckResponse represents our netmaster's health and version info.
type NetmasterHealthCheckResponse struct {
Status string `json:"status"`

// if netmaster is up and working, there's no "reason" for it to be unhealthy
Reason string `json:"reason,omitempty"`

// if we can't reach netmaster, we won't have a version
Version string `json:"version,omitempty"`
}

// MarkHealthy marks netmaster as being healthy and running the specified version
func (nhcr *NetmasterHealthCheckResponse) MarkHealthy(version string) {
nhcr.Status = StatusHealthy
nhcr.Version = version
}

// MarkUnhealthy marks netmaster as being unhealthy
func (nhcr *NetmasterHealthCheckResponse) MarkUnhealthy(reason string) {
nhcr.Status = StatusUnhealthy
nhcr.Reason = reason
}

// HealthCheckResponse represents a response from the /health endpoint.
// It contains our health status + the health status of our netmaster
type HealthCheckResponse struct {
NetmasterHealth *NetmasterHealthCheckResponse `json:"netmaster"`
Status string `json:"status"`
Version string `json:"version"`
}

// MarkUnhealthy marks the proxy as being unhealthy
func (hcr *HealthCheckResponse) MarkUnhealthy() {
hcr.Status = StatusUnhealthy
}

// healthCheckHandler handles /health requests
func healthCheckHandler(config *Config) func(http.ResponseWriter, *http.Request) {
return func(w http.ResponseWriter, req *http.Request) {
common.SetJSONContentType(w)

hcr := &HealthCheckResponse{
Status: StatusHealthy, // default to being healthy
Version: config.Version,
}

nhcr := &NetmasterHealthCheckResponse{}

//
// check our netmaster's /version endpoint
//
if version, err := common.GetNetmasterVersion(config.NetmasterAddress); err != nil {
nhcr.MarkUnhealthy(err.Error())

// if netmaster is unhealthy, so are we
hcr.MarkUnhealthy()
} else {
nhcr.MarkHealthy(version)
}

hcr.NetmasterHealth = nhcr

//
// prepare the response
//
data, err := json.Marshal(hcr)
if err != nil {
serverError(w, errors.New("failed to marshal health check response: "+err.Error()))
return
}

w.Write(data)
}
}

// VersionResponse represents a response from the /version endpoint
type VersionResponse struct {
Version string `json:"version"`
Expand Down
8 changes: 8 additions & 0 deletions proxy/proxy.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,9 @@ const (
// LoginPath is the authentication endpoint on the proxy
LoginPath = "/api/v1/ccn_proxy/login"

// HealthCheckPath is the health check endpoint on the proxy
HealthCheckPath = "/health"

// VersionPath is the version endpoint on the proxy
VersionPath = "/version"

Expand Down Expand Up @@ -184,6 +187,11 @@ func addRoutes(s *Server, router *mux.Router) {
//
router.Path(VersionPath).Methods("GET").HandlerFunc(versionHandler(s.config.Version))

//
// Health check endpoint
//
router.Path(HealthCheckPath).Methods("GET").HandlerFunc(healthCheckHandler(s.config))

//
// Authentication endpoint
//
Expand Down
41 changes: 40 additions & 1 deletion systemtests/basic_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,7 @@ func (s *systemtestSuite) TestLogin(c *C) {
// TestVersion tests that /version endpoint responds with something sane.
func (s *systemtestSuite) TestVersion(c *C) {
runTest(func(ms *MockServer) {
resp, data := proxyGet(c, noToken, "/version")
resp, data := proxyGet(c, noToken, proxy.VersionPath)
c.Assert(resp.StatusCode, Equals, 200)

vr := &proxy.VersionResponse{}
Expand All @@ -93,6 +93,45 @@ func (s *systemtestSuite) TestVersion(c *C) {
})
}

// TestHealthCheck tests that /health endpoint responds properly.
func (s *systemtestSuite) TestHealthCheck(c *C) {
runTest(func(ms *MockServer) {

testFunc := func() *proxy.HealthCheckResponse {
resp, data := proxyGet(c, noToken, proxy.HealthCheckPath)
c.Assert(resp.StatusCode, Equals, 200)

hcr := &proxy.HealthCheckResponse{}
err := json.Unmarshal(data, hcr)
c.Assert(err, IsNil)

return hcr
}

//
// first check: with no configured /version endpoint on the mockserver,
// the netmaster should be marked unhealthy.
//
hcr := testFunc()

c.Assert(hcr.Status, Equals, proxy.StatusUnhealthy)
c.Assert(hcr.NetmasterHealth.Status, Equals, proxy.StatusUnhealthy)

//
// second check: we add a /version to mockserver and should get back
// a healthy response.
//
versionResponse := `{"GitCommit":"x","Version":"y","BuildTime":"z"}`
ms.AddHardcodedResponse("/version", []byte(versionResponse))

hcr = testFunc()

c.Assert(hcr.Status, Equals, proxy.StatusHealthy)
c.Assert(hcr.NetmasterHealth.Status, Equals, proxy.StatusHealthy)
c.Assert(hcr.NetmasterHealth.Version, Equals, "y")
})
}

// TestRequestProxying tests that authenticated requests are proxied to the mock
// server and the response is returned properly.
func (s *systemtestSuite) TestRequestProxying(c *C) {
Expand Down