Add query parameters to /sys/health to specify return codes.

Fixes #1199
This commit is contained in:
Jeff Mitchell
2016-03-11 00:41:25 -05:00
parent 1b89fedc0b
commit ace5dd495b
3 changed files with 151 additions and 10 deletions

View File

@@ -3,6 +3,7 @@ package http
import (
"encoding/json"
"net/http"
"strconv"
"time"
"github.com/hashicorp/vault/vault"
@@ -20,9 +21,56 @@ func handleSysHealth(core *vault.Core) http.Handler {
}
func handleSysHealthGet(core *vault.Core, w http.ResponseWriter, r *http.Request) {
// Check if being a standby is allowed for the purpose of a 200 OK
_, standbyOK := r.URL.Query()["standbyok"]
// FIXME: Change the sealed code to http.StatusServiceUnavailable at some
// point
sealedCode := http.StatusInternalServerError
standbyCode := http.StatusTooManyRequests // Consul warning code
activeCode := http.StatusOK
var err error
sealedCodeStr, sealedCodeOk := r.URL.Query()["sealedcode"]
if sealedCodeOk {
if len(sealedCodeStr) < 1 {
respondError(w, http.StatusBadRequest, nil)
return
}
sealedCode, err = strconv.Atoi(sealedCodeStr[0])
if err != nil {
respondError(w, http.StatusBadRequest, nil)
return
}
}
standbyCodeStr, standbyCodeOk := r.URL.Query()["standbycode"]
if standbyCodeOk {
if len(standbyCodeStr) < 1 {
respondError(w, http.StatusBadRequest, nil)
return
}
standbyCode, err = strconv.Atoi(standbyCodeStr[0])
if err != nil {
respondError(w, http.StatusBadRequest, nil)
return
}
}
activeCodeStr, activeCodeOk := r.URL.Query()["activecode"]
if activeCodeOk {
if len(activeCodeStr) < 1 {
respondError(w, http.StatusBadRequest, nil)
return
}
activeCode, err = strconv.Atoi(activeCodeStr[0])
if err != nil {
respondError(w, http.StatusBadRequest, nil)
return
}
}
// Check system status
sealed, _ := core.Sealed()
standby, _ := core.Standby()
@@ -33,14 +81,14 @@ func handleSysHealthGet(core *vault.Core, w http.ResponseWriter, r *http.Request
}
// Determine the status code
code := http.StatusOK
code := activeCode
switch {
case !init:
code = http.StatusInternalServerError
case sealed:
code = http.StatusInternalServerError
code = sealedCode
case !standbyOK && standby:
code = 429 // Consul warning code
code = standbyCode
}
// Format the body

View File

@@ -2,6 +2,7 @@ package http
import (
"net/http"
"net/url"
"reflect"
"testing"
@@ -9,7 +10,7 @@ import (
)
func TestSysHealth_get(t *testing.T) {
core, _, _ := vault.TestCoreUnsealed(t)
core, _, root := vault.TestCoreUnsealed(t)
ln, addr := TestServer(t, core)
defer ln.Close()
@@ -30,4 +31,77 @@ func TestSysHealth_get(t *testing.T) {
if !reflect.DeepEqual(actual, expected) {
t.Fatalf("bad: %#v", actual)
}
core.Seal(root)
resp, err = http.Get(addr + "/v1/sys/health")
if err != nil {
t.Fatalf("err: %s", err)
}
actual = map[string]interface{}{}
expected = map[string]interface{}{
"initialized": true,
"sealed": true,
"standby": false,
}
testResponseStatus(t, resp, 500)
testResponseBody(t, resp, &actual)
expected["server_time_utc"] = actual["server_time_utc"]
if !reflect.DeepEqual(actual, expected) {
t.Fatalf("bad: %#v", actual)
}
}
func TestSysHealth_customcodes(t *testing.T) {
core, _, root := vault.TestCoreUnsealed(t)
ln, addr := TestServer(t, core)
defer ln.Close()
queryurl, err := url.Parse(addr + "/v1/sys/health?sealedcode=503&activecode=202")
if err != nil {
t.Fatalf("err: %s", err)
}
resp, err := http.Get(queryurl.String())
if err != nil {
t.Fatalf("err: %s", err)
}
var actual map[string]interface{}
expected := map[string]interface{}{
"initialized": true,
"sealed": false,
"standby": false,
}
testResponseStatus(t, resp, 202)
testResponseBody(t, resp, &actual)
expected["server_time_utc"] = actual["server_time_utc"]
if !reflect.DeepEqual(actual, expected) {
t.Fatalf("bad: %#v", actual)
}
core.Seal(root)
queryurl, err = url.Parse(addr + "/v1/sys/health?sealedcode=503&activecode=202")
if err != nil {
t.Fatalf("err: %s", err)
}
resp, err = http.Get(queryurl.String())
if err != nil {
t.Fatalf("err: %s", err)
}
actual = map[string]interface{}{}
expected = map[string]interface{}{
"initialized": true,
"sealed": true,
"standby": false,
}
testResponseStatus(t, resp, 503)
testResponseBody(t, resp, &actual)
expected["server_time_utc"] = actual["server_time_utc"]
if !reflect.DeepEqual(actual, expected) {
t.Fatalf("bad: %#v", actual)
}
}

View File

@@ -11,8 +11,9 @@ description: |-
<dl>
<dt>Description</dt>
<dd>
Returns the health status of Vault. This matches the semantics of a Consul HTTP health
check and provides a simple way to monitor the health of a Vault instance.
Returns the health status of Vault. This matches the semantics of a
Consul HTTP health check and provides a simple way to monitor the
health of a Vault instance.
</dd>
<dt>Method</dt>
@@ -25,7 +26,25 @@ description: |-
<span class="param">standbyok</span>
<span class="param-flags">optional</span>
A query parameter provided to indicate that being a standby should
still return a 200 status code instead of the standard 429 status code.
still return the active status code instead of the standby code
</li>
<li>
<span class="param">activecode</span>
<span class="param-flags">optional</span>
A query parameter provided to indicate the status code that should
be returned for an active node instead of the default of `200`
</li>
<li>
<span class="param">standbycode</span>
<span class="param-flags">optional</span>
A query parameter provided to indicate the status code that should
be returned for a standby node instead of the default of `429`
</li>
<li>
<span class="param">sealedcode</span>
<span class="param-flags">optional</span>
A query parameter provided to indicate the status code that should
be returned for a sealed node instead of the default of `500`
</li>
</ul>
</dd>
@@ -41,10 +60,10 @@ description: |-
}
```
Status Codes:
Default Status Codes:
* `200` if initialized, unsealed and active.
* `200` if initialized, unsealed, and active.
* `429` if unsealed and standby.
* `500` if not initialized or sealed.
* `500` if sealed, or if not initialized.
</dd>
</dl>