Skip to content
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
21 changes: 21 additions & 0 deletions test/integration/wfe_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,9 @@ import (
"io"
"net/http"
"testing"
"time"

"github.com/letsencrypt/boulder/core"
"github.com/letsencrypt/boulder/test"
)

Expand Down Expand Up @@ -50,3 +52,22 @@ func TestWFEHTTPMetrics(t *testing.T) {
test.AssertContains(t, string(body), `response_time_count{code="200",endpoint="/directory",method="GET"}`)
resp.Body.Close()
}

// TestWFEHealthz checks to make sure that the /health endpoint returns 200 OK,
// retrying in case overrides take a moment to load.
func TestWFEHealthz(t *testing.T) {
retries := 0
var status int
for retries < 5 {
time.Sleep(core.RetryBackoff(retries, time.Millisecond*2, time.Millisecond*50, 2))
resp, err := http.Get("http://boulder.service.consul:4001/healthz")
test.AssertNotError(t, err, "GET boulder-wfe2 healthz")
status = resp.StatusCode
resp.Body.Close()
if status == http.StatusOK {
break
}
retries++
}
test.AssertEquals(t, status, http.StatusOK)
}
27 changes: 27 additions & 0 deletions wfe2/wfe.go
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,7 @@ const (
getCertPath = "/get/cert/"
getCertInfoPath = "/get/certinfo/"
buildIDPath = "/build"
healthzPath = "/healthz"
)

const (
Expand Down Expand Up @@ -425,6 +426,7 @@ func (wfe *WebFrontEndImpl) Handler(stats prometheus.Registerer, oTelHTTPOptions
wfe.HandleFunc(m, getCertPath, wfe.Certificate, "GET")
wfe.HandleFunc(m, getCertInfoPath, wfe.CertificateInfo, "GET")
wfe.HandleFunc(m, buildIDPath, wfe.BuildID, "GET")
wfe.HandleFunc(m, healthzPath, wfe.Healthz, "GET")

// Endpoint for draft-ietf-acme-ari
if features.Get().ServeRenewalInfo {
Expand Down Expand Up @@ -1801,6 +1803,31 @@ func (wfe *WebFrontEndImpl) BuildID(ctx context.Context, logEvent *web.RequestEv
}
}

type WfeHealthzResponse struct {
Details string
}

// Healthz tells the requester whether we're ready to serve requests.
func (wfe *WebFrontEndImpl) Healthz(ctx context.Context, logEvent *web.RequestEvent, response http.ResponseWriter, request *http.Request) {
status := http.StatusOK
details := "OK"

if !wfe.txnBuilder.Ready() {
status = http.StatusServiceUnavailable
details = "waiting for overrides"
}

jsonResponse, err := json.Marshal(WfeHealthzResponse{Details: details})
if err != nil {
wfe.log.Warningf("Could not marshal healthz response: %s", err)
}

err = wfe.writeJsonResponse(response, logEvent, status, jsonResponse)
if err != nil {
wfe.log.Warningf("Could not write response: %s", err)
}
}

// Options responds to an HTTP OPTIONS request.
func (wfe *WebFrontEndImpl) Options(response http.ResponseWriter, request *http.Request, methodsStr string, methodsMap map[string]bool) {
// Every OPTIONS request gets an Allow header with a list of supported methods.
Expand Down
5 changes: 5 additions & 0 deletions wfe2/wfe_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -1095,6 +1095,11 @@ func TestHTTPMethods(t *testing.T) {
Path: buildIDPath,
Allowed: getOnly,
},
{
Name: "Health path should be GET only",
Path: healthzPath,
Allowed: getOnly,
},
{
Name: "Rollover path should be POST only",
Path: rolloverPath,
Expand Down