diff --git a/README.md b/README.md index 7862a6a..4e88dfb 100644 --- a/README.md +++ b/README.md @@ -30,7 +30,8 @@ function onShutdown () { console.log('cleanup finished, server is shutting down'); } -function healthCheck () { +function healthCheck ({ state }) { + // `state.isShuttingDown` (boolean) shows whether the server is shutting down or not return Promise.resolve( // optionally include a resolve value to be included as // info in the health check response @@ -50,7 +51,7 @@ const server = http.createServer((request, response) => { const options = { // health check options healthChecks: { - '/healthcheck': healthCheck, // a function returning a promise indicating service health, + '/healthcheck': healthCheck, // a function accepting a state and returning a promise indicating service health, verbatim: true, // [optional = false] use object returned from /healthcheck verbatim in response, __unsafeExposeStackTraces: true // [optional = false] return stack traces in error response if healthchecks throw errors }, diff --git a/lib/standalone-tests/terminus.onsignal.nofail.js b/lib/standalone-tests/terminus.onsignal.nofail.js index 39054d9..4cdc86c 100644 --- a/lib/standalone-tests/terminus.onsignal.nofail.js +++ b/lib/standalone-tests/terminus.onsignal.nofail.js @@ -7,7 +7,7 @@ const SIGNAL = 'SIGINT' createTerminus(server, { healthChecks: { - '/health': () => Promise.resolve() + '/health': ({ state }) => Promise.resolve({ state }) }, sendFailuresDuringShutdown: false, signal: SIGNAL, diff --git a/lib/terminus.js b/lib/terminus.js index 8b459b9..89df394 100644 --- a/lib/terminus.js +++ b/lib/terminus.js @@ -85,7 +85,7 @@ function decorateWithHealthCheck (server, state, options) { } let info try { - info = await healthCheck() + info = await healthCheck({ state }) } catch (error) { logger('healthcheck failed', error) return sendFailure(res, { error: error.causes, exposeStackTraces: healthChecks.__unsafeExposeStackTraces }) diff --git a/lib/terminus.spec.js b/lib/terminus.spec.js index 1782ef5..49b8ffb 100644 --- a/lib/terminus.spec.js +++ b/lib/terminus.spec.js @@ -216,6 +216,54 @@ describe('Terminus', () => { expect(loggerRan).to.eql(true) }) + it('exposes internal state (isShuttingDown: false) to health check', async () => { + let onHealthCheckRan = false + let exposedState + + createTerminus(server, { + healthChecks: { + '/health': ({ state }) => { + onHealthCheckRan = true + exposedState = state + return Promise.resolve() + } + } + }) + server.listen(8000) + + const response = await fetch('http://localhost:8000/health') + expect(response.status).to.eql(200) + expect(response.headers.has('Content-Type')).to.eql(true) + expect(response.headers.get('Content-Type')).to.eql('application/json') + expect(onHealthCheckRan).to.eql(true) + expect(exposedState).to.eql({ isShuttingDown: false }) + }) + + it('exposes internal state (isShuttingDown: true) when shutting down', (done) => { + let responseAssertionsComplete = false + let exposedState + + // We're only truly finished when the response has been analyzed and the forked http process has exited, + // freeing up port 8000 for future tests + execFile('node', ['lib/standalone-tests/terminus.onsignal.nofail.js'], (error) => { + expect(error.signal).to.eql('SIGINT') + expect(responseAssertionsComplete).to.eql(true) + expect(exposedState).to.eql({ isShuttingDown: true }) + done() + }) + + // let the process start up + setTimeout(() => { + fetch('http://localhost:8000/health') + .then(async (res) => { + expect(res.status).to.eql(200) + responseAssertionsComplete = true + const json = await res.json() + exposedState = json.info.state + }) + }, 300) + }) + describe('includes error on reject', async () => { const errors = [ new Error('test error 1'),