diff --git a/http/sys_health.go b/http/sys_health.go index bde39a28e2..73b85688c5 100644 --- a/http/sys_health.go +++ b/http/sys_health.go @@ -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 diff --git a/http/sys_health_test.go b/http/sys_health_test.go index 6e50e91dd4..03b5f207ac 100644 --- a/http/sys_health_test.go +++ b/http/sys_health_test.go @@ -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) + } } diff --git a/website/source/docs/http/sys-health.html.md b/website/source/docs/http/sys-health.html.md index 19016f9346..9720f12a12 100644 --- a/website/source/docs/http/sys-health.html.md +++ b/website/source/docs/http/sys-health.html.md @@ -11,8 +11,9 @@ description: |-
Description
- 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.
Method
@@ -25,7 +26,25 @@ description: |- standbyok optional 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 + +
  • + activecode + optional + A query parameter provided to indicate the status code that should + be returned for an active node instead of the default of `200` +
  • +
  • + standbycode + optional + A query parameter provided to indicate the status code that should + be returned for a standby node instead of the default of `429` +
  • +
  • + sealedcode + optional + A query parameter provided to indicate the status code that should + be returned for a sealed node instead of the default of `500`
  • @@ -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.