Skip to content

Commit c26e53c

Browse files
authored
wfe: Add healthz endpoint (#8484)
Deployment suggestion: If currently using the `build` endpoint for health checks, change it to `health`. Fixes #8476
1 parent 4627c1f commit c26e53c

File tree

3 files changed

+53
-0
lines changed

3 files changed

+53
-0
lines changed

test/integration/wfe_test.go

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,9 @@ import (
66
"io"
77
"net/http"
88
"testing"
9+
"time"
910

11+
"github.com/letsencrypt/boulder/core"
1012
"github.com/letsencrypt/boulder/test"
1113
)
1214

@@ -50,3 +52,22 @@ func TestWFEHTTPMetrics(t *testing.T) {
5052
test.AssertContains(t, string(body), `response_time_count{code="200",endpoint="/directory",method="GET"}`)
5153
resp.Body.Close()
5254
}
55+
56+
// TestWFEHealthz checks to make sure that the /health endpoint returns 200 OK,
57+
// retrying in case overrides take a moment to load.
58+
func TestWFEHealthz(t *testing.T) {
59+
retries := 0
60+
var status int
61+
for retries < 5 {
62+
time.Sleep(core.RetryBackoff(retries, time.Millisecond*2, time.Millisecond*50, 2))
63+
resp, err := http.Get("http://boulder.service.consul:4001/healthz")
64+
test.AssertNotError(t, err, "GET boulder-wfe2 healthz")
65+
status = resp.StatusCode
66+
resp.Body.Close()
67+
if status == http.StatusOK {
68+
break
69+
}
70+
retries++
71+
}
72+
test.AssertEquals(t, status, http.StatusOK)
73+
}

wfe2/wfe.go

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,7 @@ const (
7171
getCertPath = "/get/cert/"
7272
getCertInfoPath = "/get/certinfo/"
7373
buildIDPath = "/build"
74+
healthzPath = "/healthz"
7475
)
7576

7677
const (
@@ -425,6 +426,7 @@ func (wfe *WebFrontEndImpl) Handler(stats prometheus.Registerer, oTelHTTPOptions
425426
wfe.HandleFunc(m, getCertPath, wfe.Certificate, "GET")
426427
wfe.HandleFunc(m, getCertInfoPath, wfe.CertificateInfo, "GET")
427428
wfe.HandleFunc(m, buildIDPath, wfe.BuildID, "GET")
429+
wfe.HandleFunc(m, healthzPath, wfe.Healthz, "GET")
428430

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

1806+
type WfeHealthzResponse struct {
1807+
Details string
1808+
}
1809+
1810+
// Healthz tells the requester whether we're ready to serve requests.
1811+
func (wfe *WebFrontEndImpl) Healthz(ctx context.Context, logEvent *web.RequestEvent, response http.ResponseWriter, request *http.Request) {
1812+
status := http.StatusOK
1813+
details := "OK"
1814+
1815+
if !wfe.txnBuilder.Ready() {
1816+
status = http.StatusServiceUnavailable
1817+
details = "waiting for overrides"
1818+
}
1819+
1820+
jsonResponse, err := json.Marshal(WfeHealthzResponse{Details: details})
1821+
if err != nil {
1822+
wfe.log.Warningf("Could not marshal healthz response: %s", err)
1823+
}
1824+
1825+
err = wfe.writeJsonResponse(response, logEvent, status, jsonResponse)
1826+
if err != nil {
1827+
wfe.log.Warningf("Could not write response: %s", err)
1828+
}
1829+
}
1830+
18041831
// Options responds to an HTTP OPTIONS request.
18051832
func (wfe *WebFrontEndImpl) Options(response http.ResponseWriter, request *http.Request, methodsStr string, methodsMap map[string]bool) {
18061833
// Every OPTIONS request gets an Allow header with a list of supported methods.

wfe2/wfe_test.go

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1095,6 +1095,11 @@ func TestHTTPMethods(t *testing.T) {
10951095
Path: buildIDPath,
10961096
Allowed: getOnly,
10971097
},
1098+
{
1099+
Name: "Health path should be GET only",
1100+
Path: healthzPath,
1101+
Allowed: getOnly,
1102+
},
10981103
{
10991104
Name: "Rollover path should be POST only",
11001105
Path: rolloverPath,

0 commit comments

Comments
 (0)