@hmcts/nodejs-healthcheck
Advanced tools
Comparing version 1.6.0 to 1.7.0
'use strict' | ||
const request = require('superagent'), | ||
outputs = require('./outputs'); | ||
const request = require('superagent') | ||
const outputs = require('./outputs') | ||
class WebCheck { | ||
constructor(url, options={}) { | ||
this.url = url; | ||
this.callback = options.callback || this.defaultCallback; | ||
this.timeout = options.timeout || 2000; | ||
this.deadline = options.deadline || 5000; | ||
constructor (url, options = {}) { | ||
this.url = url | ||
this.callback = options.callback || this.defaultCallback | ||
this.timeout = options.timeout || 2000 | ||
this.deadline = options.deadline || 5000 | ||
this.ca = options.ca | ||
} | ||
static create(url, options={}) { | ||
return new WebCheck(url, options); | ||
static create (url, options = {}) { | ||
return new WebCheck(url, options) | ||
} | ||
defaultCallback(err, res) { | ||
return !err && res.status === 200 ? outputs.up() : outputs.down(err); | ||
defaultCallback (err, res) { | ||
return !err && res.status === 200 ? outputs.up() : outputs.down(err) | ||
} | ||
call() { | ||
call () { | ||
return new Promise((resolve) => { | ||
@@ -29,41 +28,39 @@ request | ||
.ca(this.ca) | ||
.timeout({response: this.timeout, deadline: this.deadline}) | ||
.timeout({ response: this.timeout, deadline: this.deadline }) | ||
.end((err, res) => { | ||
resolve(this.callback(err, res)); | ||
}); | ||
}); | ||
resolve(this.callback(err, res)) | ||
}) | ||
}) | ||
} | ||
} | ||
class RawCheck { | ||
constructor(callback) { | ||
this.callback = callback; | ||
constructor (callback) { | ||
this.callback = callback | ||
} | ||
static create(callback) { | ||
return new RawCheck(callback); | ||
static create (callback) { | ||
return new RawCheck(callback) | ||
} | ||
call(req, res) { | ||
call (req, res) { | ||
return new Promise((resolve) => { | ||
resolve(this.callback(req, res)); | ||
}); | ||
resolve(this.callback(req, res)) | ||
}) | ||
} | ||
} | ||
class CompositeCheck { | ||
constructor(checks) { | ||
this.checks = checks; | ||
constructor (checks) { | ||
this.checks = checks | ||
} | ||
static create(checks) { | ||
return new CompositeCheck(checks); | ||
static create (checks) { | ||
return new CompositeCheck(checks) | ||
} | ||
call(req, res) { | ||
let checks = Object.entries(this.checks), | ||
promises = checks.map(check => check[1].call(req, res)), | ||
all = Promise.all(promises); | ||
call (req, res) { | ||
const checks = Object.entries(this.checks) | ||
const promises = checks.map(check => check[1].call(req, res)) | ||
const all = Promise.all(promises) | ||
@@ -75,13 +72,12 @@ return new Promise(resolve => { | ||
.map((result, i) => [checks[i][0], result]) | ||
.reduce((prev, curr) => Object.assign(prev, {[curr[0]]: curr[1]}), {})) | ||
}); | ||
}); | ||
.reduce((prev, curr) => Object.assign(prev, { [curr[0]]: curr[1] }), {})) | ||
}) | ||
}) | ||
} | ||
} | ||
module.exports = { | ||
"WebCheck": WebCheck, | ||
"RawCheck": RawCheck, | ||
"CompositeCheck": CompositeCheck, | ||
}; | ||
WebCheck: WebCheck, | ||
RawCheck: RawCheck, | ||
CompositeCheck: CompositeCheck | ||
} |
@@ -6,9 +6,10 @@ 'use strict' | ||
function addTo(app, config) { | ||
function addTo (app, config) { | ||
app.get('/health', routes.configure(config)) | ||
app.get('/health/liveness', (req, res) => res.json(outputs.status(outputs.UP))) | ||
app.get('/health/readiness', routes.checkReadiness(config.readinessChecks)) | ||
} | ||
module.exports = { | ||
'addTo': addTo | ||
addTo: addTo | ||
} |
'use strict' | ||
const UP = "UP", | ||
DOWN = "DOWN"; | ||
const UP = 'UP' | ||
const DOWN = 'DOWN' | ||
function up(extra={}) { | ||
return status(true, extra); | ||
function up (extra = {}) { | ||
return status(true, extra) | ||
} | ||
function down(extra={}) { | ||
return status(false, extra); | ||
function down (extra = {}) { | ||
return status(false, extra) | ||
} | ||
function status(s, extra={}) { | ||
function status (s, extra = {}) { | ||
return Object.assign({}, extra, { | ||
"status": s ? UP : DOWN, | ||
}); | ||
status: s ? UP : DOWN | ||
}) | ||
} | ||
module.exports = { | ||
"up": up, | ||
"down": down, | ||
"status": status, | ||
"UP": UP, | ||
"DOWN": DOWN, | ||
}; | ||
up: up, | ||
down: down, | ||
status: status, | ||
UP: UP, | ||
DOWN: DOWN | ||
} |
@@ -1,10 +0,10 @@ | ||
'use strict'; | ||
const checks = require('./checks'), | ||
outputs = require('./outputs'), | ||
versionFile = require('./versionFile'); | ||
'use strict' | ||
const checks = require('./checks') | ||
const outputs = require('./outputs') | ||
const versionFile = require('./versionFile') | ||
const { Logger } = require('@hmcts/nodejs-logging'); | ||
const logger = Logger.getLogger('@hmcts/nodejs-logging/routes'); | ||
const { Logger } = require('@hmcts/nodejs-logging') | ||
const logger = Logger.getLogger('@hmcts/nodejs-logging/routes') | ||
function getBuildInfo(extra) { | ||
function getBuildInfo (extra) { | ||
return Promise.all([ | ||
@@ -15,20 +15,20 @@ versionFile.version(), | ||
]).then(([version, commit, date]) => { | ||
let buildInfo = { | ||
environment: process.env.PACKAGES_ENVIRONMENT || process.env.REFORM_ENVIRONMENT || "unknown", | ||
project: process.env.PACKAGES_PROJECT || process.env.REFORM_TEAM || "unknown", | ||
name: process.env.PACKAGES_NAME || process.env.REFORM_SERVICE_NAME || "unknown", | ||
const buildInfo = { | ||
environment: process.env.PACKAGES_ENVIRONMENT || process.env.REFORM_ENVIRONMENT || 'unknown', | ||
project: process.env.PACKAGES_PROJECT || process.env.REFORM_TEAM || 'unknown', | ||
name: process.env.PACKAGES_NAME || process.env.REFORM_SERVICE_NAME || 'unknown', | ||
version, | ||
commit, | ||
date | ||
}; | ||
} | ||
if (extra) { | ||
return Object.assign(buildInfo, { extra }); | ||
return Object.assign(buildInfo, { extra }) | ||
} else { | ||
return buildInfo; | ||
return buildInfo | ||
} | ||
}); | ||
}) | ||
} | ||
function configure(config) { | ||
const check = new checks.CompositeCheck(config.checks); | ||
function configure (config) { | ||
const check = new checks.CompositeCheck(config.checks) | ||
@@ -40,3 +40,3 @@ return (req, res) => { | ||
const allOk = Object.values(results) | ||
.every(result => result.status === outputs.UP); | ||
.every(result => result.status === outputs.UP) | ||
const output = Object.assign( | ||
@@ -46,19 +46,44 @@ outputs.status(allOk), | ||
{ buildInfo } | ||
); | ||
const status = allOk ? 200 : 500; | ||
) | ||
const status = allOk ? 200 : 500 | ||
if (!allOk) { | ||
const downHealthChecks = Object.values(results) | ||
.filter(result => result.status === outputs.DOWN); | ||
.filter(result => result.status === outputs.DOWN) | ||
logger.error('Health check failed, result for down endpoints: ', JSON.stringify(downHealthChecks)); | ||
logger.error('Health check failed, result for down endpoints: ', JSON.stringify(downHealthChecks)) | ||
} | ||
res.status(status).json(output); | ||
}); | ||
res.status(status).json(output) | ||
}) | ||
} | ||
} | ||
function checkReadiness (readinessChecks = {}) { | ||
const check = new checks.CompositeCheck(readinessChecks) | ||
return (req, res) => { | ||
return Promise | ||
.resolve(check.call(req, res)) | ||
.then((results) => { | ||
const allOk = Object.values(results) | ||
.every((result) => result.status === outputs.UP) | ||
const output = Object.assign( | ||
outputs.status(allOk), | ||
results | ||
) | ||
const status = allOk ? 200 : 500 | ||
if (!allOk) { | ||
const downHealthChecks = Object.values(results) | ||
.filter((result) => result.status === outputs.DOWN) | ||
logger.error('Health check failed, result for down endpoints: ', JSON.stringify(downHealthChecks)) | ||
} | ||
res.status(status).json(output) | ||
}) | ||
} | ||
} | ||
module.exports = { | ||
"getBuildInfo": getBuildInfo, | ||
"configure": configure, | ||
}; | ||
getBuildInfo: getBuildInfo, | ||
configure: configure, | ||
checkReadiness: checkReadiness | ||
} |
@@ -1,34 +0,34 @@ | ||
const fs = require('fs').promises; | ||
const yaml = require('js-yaml'); | ||
const fs = require('fs').promises | ||
const yaml = require('js-yaml') | ||
let defaultObj; | ||
let defaultObj | ||
const versionFile = () => { | ||
const versionFilePath = `${process.env.NODE_PATH || '.'}/version`; | ||
const versionFilePath = `${process.env.NODE_PATH || '.'}/version` | ||
defaultObj = { | ||
defaultObj = { | ||
version: process.env.PACKAGES_VERSION || 'unknown', | ||
commit: 'unknown', | ||
date: 'unknown' | ||
}; | ||
} | ||
return fs.readFile(versionFilePath) | ||
.then(yaml.safeLoad) | ||
.catch((err) => defaultObj); | ||
}; | ||
.catch((err) => defaultObj) | ||
} | ||
const version = () => { | ||
return versionFile().then(props => { | ||
return (props.version) ? (props.number) ? props.version + "-" + props.number : props.version : defaultObj.version | ||
}); | ||
}; | ||
return (props.version) ? (props.number) ? props.version + '-' + props.number : props.version : defaultObj.version | ||
}) | ||
} | ||
const commit = () => { | ||
return versionFile().then(props => props.commit || defaultObj.commit); | ||
}; | ||
return versionFile().then(props => props.commit || defaultObj.commit) | ||
} | ||
const date = () => { | ||
return versionFile().then(props => props.date || defaultObj.date); | ||
}; | ||
return versionFile().then(props => props.date || defaultObj.date) | ||
} | ||
module.exports = { version, commit, date }; | ||
module.exports = { version, commit, date } |
24
index.js
'use strict' | ||
const checks = require('./healthcheck/checks'), | ||
outputs = require('./healthcheck/outputs'), | ||
routes = require('./healthcheck/routes'), | ||
install = require('./healthcheck/install'); | ||
const checks = require('./healthcheck/checks') | ||
const outputs = require('./healthcheck/outputs') | ||
const routes = require('./healthcheck/routes') | ||
const install = require('./healthcheck/install') | ||
module.exports = { | ||
"addTo": install.addTo, | ||
"configure": routes.configure, | ||
addTo: install.addTo, | ||
configure: routes.configure, | ||
// outputs | ||
"up": outputs.up, | ||
"down": outputs.down, | ||
"status": outputs.status, | ||
up: outputs.up, | ||
down: outputs.down, | ||
status: outputs.status, | ||
// checks | ||
"web": checks.WebCheck.create, | ||
"raw": checks.RawCheck.create, | ||
}; | ||
web: checks.WebCheck.create, | ||
raw: checks.RawCheck.create | ||
} |
{ | ||
"name": "@hmcts/nodejs-healthcheck", | ||
"version": "1.6.0", | ||
"version": "1.7.0", | ||
"description": "Healthcheck endpoint for Reform nodejs applications", | ||
@@ -10,3 +10,4 @@ "main": "index.js", | ||
"scripts": { | ||
"test": "mocha test/unit/* --reporter spec" | ||
"test": "mocha test/unit/* --reporter spec", | ||
"lint": "eslint ." | ||
}, | ||
@@ -27,2 +28,8 @@ "repository": { | ||
"chai-as-promised": "^7.1.1", | ||
"eslint": "^7.1.0", | ||
"eslint-config-standard": "^14.1.1", | ||
"eslint-plugin-import": "^2.20.2", | ||
"eslint-plugin-node": "^11.1.0", | ||
"eslint-plugin-promise": "^4.2.1", | ||
"eslint-plugin-standard": "^4.0.1", | ||
"express": "^4.16.4", | ||
@@ -29,0 +36,0 @@ "mocha": "7", |
@@ -7,2 +7,8 @@ # Nodejs Healthcheck | ||
It exposes 3 endpoints: | ||
1. `/health` - Returns 200 by default along with `buildInfo`, can optionally include result evaluating all `checks` passed in config. | ||
2. `/health/livness` - Returns 200 always. | ||
3. `/health/readiness` - Returns 200 by default , can optionally include result evaluating all `readinessChecks` passed in config. | ||
## Usage | ||
@@ -13,5 +19,27 @@ | ||
```javascript | ||
const healthcheck = require('@hmcts/nodejs-healthcheck'); | ||
const config = { | ||
checks: { | ||
mySimpleWebCheck: healthcheck.web("https://example.com/status"), | ||
myComplexWebCheck: healthcheck.web("https://example.com/other", { | ||
callback: (err, res) => { | ||
return res.body.status == "good" ? healthcheck.up() : healthcheck.down() | ||
}, | ||
timeout: 5000, | ||
deadline: 10000, | ||
}), | ||
myRawCheck: healthcheck.raw(() => { | ||
return myInternalCheck() ? healthcheck.up() : healthcheck.down() | ||
}) | ||
}, | ||
buildInfo: { | ||
myCustomBuildInfo: "yay" | ||
} | ||
}; | ||
healthcheck.addTo(app, config); | ||
``` | ||
app.get("/status", healthcheck.configure({ | ||
You can optionally include [readiness checks](#what-to-include-in-readiness-checks). | ||
```javascript | ||
const config = { | ||
checks: { | ||
@@ -30,8 +58,30 @@ mySimpleWebCheck: healthcheck.web("https://example.com/status"), | ||
}, | ||
readinessChecks: { | ||
mySimpleWebCheck: healthcheck.web("https://example.com/status") | ||
}, | ||
buildInfo: { | ||
myCustomBuildInfo: "yay" | ||
} | ||
})); | ||
}; | ||
healthcheck.addTo(app, config); | ||
``` | ||
## what to include in readiness checks | ||
- On Kubernetes, readiness probes will be called periodically throughout the lifetime of the container. Container will be made temporarily unavailable from serving traffic when the readiness check fails. | ||
- The requests won't even reach your application to handle errors. So, it is very important to consider what checks should be included into readiness probe. | ||
- While adding all dependant services to readiness check can help in identifying any misconfiguration during startup, it could cause unwanted downtime for the application. | ||
- K8s introduced startUp Probes (Alpha in 1.16 ) to handle startup cases separately. | ||
Based on above, you should include a dependency into readiness checks only if they are exclusive/hard dependencies for your service. Unavailability of soft dependencies needs to be handled in code to give appropriate customer experience. | ||
Good example for check to be included in readiness: | ||
- A private cache / database like `Redis` or `Elastic Search` which are exclusive to the application (not shared). | ||
Bad example for check to be included in readiness: | ||
- Any shared components like IDAM, S2S or CCD. | ||
## Publishing | ||
@@ -38,0 +88,0 @@ |
@@ -1,13 +0,13 @@ | ||
const chai = require('chai'); | ||
const chaiAsPromised = require("chai-as-promised"); | ||
const expect = chai.expect; | ||
const sinon = require('sinon'); | ||
const sinonChai = require('sinon-chai'); | ||
chai.should(); | ||
chai.use(sinonChai); | ||
chai.use(chaiAsPromised); | ||
const chai = require('chai') | ||
const chaiAsPromised = require('chai-as-promised') | ||
const expect = chai.expect | ||
const sinon = require('sinon') | ||
const sinonChai = require('sinon-chai') | ||
chai.should() | ||
chai.use(sinonChai) | ||
chai.use(chaiAsPromised) | ||
module.exports = { | ||
expect, | ||
sinon | ||
}; | ||
expect, | ||
sinon | ||
} |
'use strict' | ||
const {expect, sinon} = require('../chai-sinon'), | ||
checks = require('../../healthcheck/checks'), | ||
nock = require('nock'); | ||
const { expect, sinon } = require('../chai-sinon') | ||
const checks = require('../../healthcheck/checks') | ||
const nock = require('nock') | ||
describe('Checks', () => { | ||
let check; | ||
let check | ||
describe('web', () => { | ||
let request; | ||
let request | ||
describe('basic web check', () => { | ||
beforeEach(() => { | ||
check = checks.WebCheck.create("https://example.com/status"); | ||
request = nock('https://example.com').get('/status'); | ||
}); | ||
check = checks.WebCheck.create('https://example.com/status') | ||
request = nock('https://example.com').get('/status') | ||
}) | ||
it("should return status UP on HTTP 200 OK", (done) => { | ||
request.reply(200, "OK"); | ||
it('should return status UP on HTTP 200 OK', (done) => { | ||
request.reply(200, 'OK') | ||
check.call().then((result) => { | ||
expect(result).to.eql({"status": "UP"}); | ||
done(); | ||
}); | ||
expect(result).to.eql({ status: 'UP' }) | ||
done() | ||
}) | ||
}); | ||
[201, 400, 500, 503].forEach((code) => { | ||
it("should return status DOWN on HTTP " + code, (done) => { | ||
request.reply(code, "SOMETHING"); | ||
it('should return status DOWN on HTTP ' + code, (done) => { | ||
request.reply(code, 'SOMETHING') | ||
check.call().then((result) => { | ||
expect(result).to.contain({"status": "DOWN"}); | ||
done(); | ||
}); | ||
}); | ||
}); | ||
expect(result).to.contain({ status: 'DOWN' }) | ||
done() | ||
}) | ||
}) | ||
}) | ||
}) | ||
}); | ||
describe("custom web check", () => { | ||
describe('custom web check', () => { | ||
beforeEach(() => { | ||
check = new checks.WebCheck("https://example.com/status", { | ||
check = new checks.WebCheck('https://example.com/status', { | ||
callback: (err, res) => { | ||
if (res.body.status === "good") { | ||
return {"status": "UP"}; | ||
if (res.body.status === 'good') { | ||
return { status: 'UP' } | ||
} else { | ||
return {"status": "DOWN"}; | ||
return { status: 'DOWN' } | ||
} | ||
} | ||
}); | ||
request = nock('https://example.com').get('/status'); | ||
}); | ||
}) | ||
request = nock('https://example.com').get('/status') | ||
}) | ||
it("should return down on down condition", (done) => { | ||
request.reply(200, {"status": "bad"}); | ||
it('should return down on down condition', (done) => { | ||
request.reply(200, { status: 'bad' }) | ||
check.call().then((result) => { | ||
expect(result).to.eql({"status": "DOWN"}); | ||
done(); | ||
}); | ||
}); | ||
expect(result).to.eql({ status: 'DOWN' }) | ||
done() | ||
}) | ||
}) | ||
it("should return up on up condition", (done) => { | ||
request.reply(500, {"status": "good"}); | ||
it('should return up on up condition', (done) => { | ||
request.reply(500, { status: 'good' }) | ||
check.call().then((result) => { | ||
expect(result).to.eql({"status": "UP"}); | ||
done(); | ||
}); | ||
}); | ||
expect(result).to.eql({ status: 'UP' }) | ||
done() | ||
}) | ||
}) | ||
}) | ||
}) | ||
}); | ||
}); | ||
describe('raw', () => { | ||
describe('basic raw check', () => { | ||
it('should output exactly what is provided', (done) => { | ||
let f = () => { return true; } | ||
check = new checks.RawCheck(f); | ||
const f = () => { return true } | ||
check = new checks.RawCheck(f) | ||
check.call().then((result) => { | ||
expect(result).to.eql(true); | ||
done(); | ||
}); | ||
}); | ||
expect(result).to.eql(true) | ||
done() | ||
}) | ||
}) | ||
it('passes the express req and res to the callback', () => { | ||
const expressRequest = sinon.spy(); | ||
const expressResponse = sinon.spy(); | ||
const expressRequest = sinon.spy() | ||
const expressResponse = sinon.spy() | ||
const check = new checks.RawCheck((req, res) => { | ||
expect(req).to.eql(expressRequest); | ||
expect(res).to.eql(expressResponse); | ||
}); | ||
return check.call(expressRequest, expressResponse); | ||
}); | ||
expect(req).to.eql(expressRequest) | ||
expect(res).to.eql(expressResponse) | ||
}) | ||
return check.call(expressRequest, expressResponse) | ||
}) | ||
}) | ||
}) | ||
}); | ||
}); | ||
describe('composite', () => { | ||
describe('basic composite check', () => { | ||
it('should output a map of composed check results', (done) => { | ||
let f1 = () => { return "f1"; } | ||
let f2 = () => { return "f2"; } | ||
const f1 = () => { return 'f1' } | ||
const f2 = () => { return 'f2' } | ||
check = new checks.CompositeCheck({ | ||
"c1": checks.RawCheck.create(f1), | ||
"c2": checks.RawCheck.create(f2), | ||
}); | ||
c1: checks.RawCheck.create(f1), | ||
c2: checks.RawCheck.create(f2) | ||
}) | ||
check.call().then((result) => { | ||
expect(result).to.eql({ | ||
"c1": "f1", | ||
"c2": "f2", | ||
}); | ||
done(); | ||
}); | ||
}); | ||
c1: 'f1', | ||
c2: 'f2' | ||
}) | ||
done() | ||
}) | ||
}) | ||
it('passes the express req and res to the child checks', (done) => { | ||
const expressRequest = sinon.spy(); | ||
const expressResponse = sinon.spy(); | ||
const expressRequest = sinon.spy() | ||
const expressResponse = sinon.spy() | ||
const childCheck = new checks.RawCheck((req, res) => { | ||
expect(req).to.eql(expressRequest); | ||
expect(res).to.eql(expressResponse); | ||
done(); | ||
}); | ||
expect(req).to.eql(expressRequest) | ||
expect(res).to.eql(expressResponse) | ||
done() | ||
}) | ||
const check = new checks.CompositeCheck({ | ||
'child': childCheck | ||
}); | ||
child: childCheck | ||
}) | ||
check.call(expressRequest, expressResponse); | ||
}); | ||
}); | ||
}); | ||
}); | ||
check.call(expressRequest, expressResponse) | ||
}) | ||
}) | ||
}) | ||
}) |
'use strict' | ||
const request = require('supertest'); | ||
const express = require('express'); | ||
const app = express(); | ||
const install = require('../../healthcheck/install'); | ||
const {expect, sinon} = require('../chai-sinon'); | ||
const request = require('supertest') | ||
const express = require('express') | ||
const app = express() | ||
const install = require('../../healthcheck/install') | ||
const { expect } = require('../chai-sinon') | ||
let validConfig = { | ||
const validConfig = { | ||
checks: {}, | ||
@@ -14,25 +14,25 @@ buildInfo: { | ||
} | ||
}; | ||
} | ||
before(() => { | ||
install.addTo(app, validConfig); | ||
}); | ||
install.addTo(app, validConfig) | ||
}) | ||
describe('Testing liveness', function() { | ||
it('should return 200 OK', function(done) { | ||
request(app) | ||
.get('/health/liveness') | ||
.expect(200) | ||
.end((err, res) => { | ||
if (err) { | ||
return done(err); | ||
} | ||
expect(res.body.status).to.be.equal('UP'); | ||
done(); | ||
}); | ||
}); | ||
}); | ||
describe('Testing liveness', function () { | ||
it('should return 200 OK', function (done) { | ||
request(app) | ||
.get('/health/liveness') | ||
.expect(200) | ||
.end((err, res) => { | ||
if (err) { | ||
return done(err) | ||
} | ||
expect(res.body.status).to.be.equal('UP') | ||
done() | ||
}) | ||
}) | ||
}) | ||
describe('Testing Readiness for 200 OK', function() { | ||
it('should return 200 OK', function(done) { | ||
describe('Testing health for 200 OK', function () { | ||
it('should return 200 OK', function (done) { | ||
request(app) | ||
@@ -43,9 +43,23 @@ .get('/health') | ||
if (err) { | ||
return done(err); | ||
return done(err) | ||
} | ||
expect(res.body.status).to.be.equal('UP'); | ||
done(); | ||
}); | ||
}); | ||
}); | ||
expect(res.body.status).to.be.equal('UP') | ||
done() | ||
}) | ||
}) | ||
}) | ||
describe('Testing readiness for 200 OK', function () { | ||
it('should return 200 OK', function (done) { | ||
request(app) | ||
.get('/health/readiness') | ||
.expect(200) | ||
.end((err, res) => { | ||
if (err) { | ||
return done(err) | ||
} | ||
expect(res.body.status).to.be.equal('UP') | ||
done() | ||
}) | ||
}) | ||
}) |
'use strict' | ||
const {expect, sinon} = require('../chai-sinon'), | ||
outputs = require('../../healthcheck/outputs'); | ||
const { expect } = require('../chai-sinon') | ||
const outputs = require('../../healthcheck/outputs') | ||
describe('Status output', () => { | ||
it('should output up', () => { | ||
expect(outputs.up()).to.eql({"status": "UP"}); | ||
}); | ||
expect(outputs.up()).to.eql({ status: 'UP' }) | ||
}) | ||
it('should output down', () => { | ||
expect(outputs.down()).to.eql({"status": "DOWN"}); | ||
}); | ||
expect(outputs.down()).to.eql({ status: 'DOWN' }) | ||
}) | ||
it('should output up with extra properties', () => { | ||
expect(outputs.up({"extra": "value"})).to.eql({"status": "UP", "extra": "value"}); | ||
}); | ||
expect(outputs.up({ extra: 'value' })).to.eql({ status: 'UP', extra: 'value' }) | ||
}) | ||
it('should output down with extra properties', () => { | ||
expect(outputs.down({"extra": "value"})).to.eql({"status": "DOWN", "extra": "value"}); | ||
}); | ||
expect(outputs.down({ extra: 'value' })).to.eql({ status: 'DOWN', extra: 'value' }) | ||
}) | ||
it('should not allow status to be overwritten', () => { | ||
expect(outputs.up({"status": "DOWN"})).to.eql({"status": "UP"}); | ||
}); | ||
}); | ||
expect(outputs.up({ status: 'DOWN' })).to.eql({ status: 'UP' }) | ||
}) | ||
}) |
'use strict' | ||
/* global describe, beforeEach, afterEach, it */ | ||
const {expect, sinon} = require('../chai-sinon'), | ||
routes = require('../../healthcheck/routes'), | ||
checks = require('../../healthcheck/checks'), | ||
outputs = require('../../healthcheck/outputs'), | ||
versionFile = require('../../healthcheck/versionFile'), | ||
nock = require('nock'); | ||
const { expect, sinon } = require('../chai-sinon') | ||
const routes = require('../../healthcheck/routes') | ||
const checks = require('../../healthcheck/checks') | ||
const outputs = require('../../healthcheck/outputs') | ||
const versionFile = require('../../healthcheck/versionFile') | ||
describe('Routes', () => { | ||
let originalEnv = {}; | ||
const originalEnv = {} | ||
const envKeys = [ | ||
'PACKAGES_ENVIRONMENT', 'PACKAGES_PROJECT', 'PACKAGES_NAME' | ||
]; | ||
] | ||
beforeEach(() => { | ||
sinon.stub(versionFile, 'commit'); | ||
sinon.stub(versionFile, 'date'); | ||
sinon.stub(versionFile, 'version'); | ||
versionFile.commit.resolves('abc1234'); | ||
versionFile.date.resolves('Jan 1 1970'); | ||
versionFile.version.resolves('1.4.3-42'); | ||
sinon.stub(versionFile, 'commit') | ||
sinon.stub(versionFile, 'date') | ||
sinon.stub(versionFile, 'version') | ||
versionFile.commit.resolves('abc1234') | ||
versionFile.date.resolves('Jan 1 1970') | ||
versionFile.version.resolves('1.4.3-42') | ||
envKeys.forEach(key => { | ||
originalEnv[key] = process.env[key]; | ||
process.env[key] = "test " + key; | ||
}); | ||
}); | ||
originalEnv[key] = process.env[key] | ||
process.env[key] = 'test ' + key | ||
}) | ||
}) | ||
afterEach(() => { | ||
versionFile.commit.restore(); | ||
versionFile.date.restore(); | ||
versionFile.version.restore(); | ||
versionFile.commit.restore() | ||
versionFile.date.restore() | ||
versionFile.version.restore() | ||
envKeys.forEach(key => { | ||
if (typeof originalEnv[key] === "undefined") { | ||
delete process.env[key]; | ||
if (typeof originalEnv[key] === 'undefined') { | ||
delete process.env[key] | ||
} else { | ||
process.env[key] = originalEnv[key]; | ||
process.env[key] = originalEnv[key] | ||
} | ||
}); | ||
}); | ||
}) | ||
}) | ||
describe('getBuildInfo', () => { | ||
it('should add build info from environment', () => { | ||
return expect(routes.getBuildInfo()).to.eventually.eql({ | ||
environment: "test PACKAGES_ENVIRONMENT", | ||
project: "test PACKAGES_PROJECT", | ||
name: "test PACKAGES_NAME", | ||
version: "1.4.3-42", | ||
environment: 'test PACKAGES_ENVIRONMENT', | ||
project: 'test PACKAGES_PROJECT', | ||
name: 'test PACKAGES_NAME', | ||
version: '1.4.3-42', | ||
commit: 'abc1234', | ||
date: 'Jan 1 1970' | ||
}); | ||
}); | ||
}) | ||
}) | ||
it('should include extra build info', () => { | ||
const extra = routes.getBuildInfo({ foo: "bar" }).then(_ => _.extra); | ||
return expect(extra).to.eventually.eql({ foo: "bar" }); | ||
}); | ||
const extra = routes.getBuildInfo({ foo: 'bar' }).then(_ => _.extra) | ||
return expect(extra).to.eventually.eql({ foo: 'bar' }) | ||
}) | ||
}) | ||
}); | ||
describe('configure', () => { | ||
let makeCheck = isOk => checks.RawCheck.create(() => isOk ? outputs.up() : outputs.down()); | ||
let makeReqRes = (expectedStatus, expectedJson) => { | ||
let req = sinon.spy(), | ||
res = {}; | ||
const makeCheck = isOk => checks.RawCheck.create(() => isOk ? outputs.up() : outputs.down()) | ||
const makeReqRes = (expectedStatus, expectedJson) => { | ||
const req = sinon.spy() | ||
const res = {} | ||
res.status = status => { | ||
expect(status).to.eql(expectedStatus); | ||
return res; | ||
}; | ||
expect(status).to.eql(expectedStatus) | ||
return res | ||
} | ||
res.json = json => { | ||
expect(json).to.eql(expectedJson); | ||
return res; | ||
}; | ||
return [req, res]; | ||
}; | ||
expect(json).to.eql(expectedJson) | ||
return res | ||
} | ||
return [req, res] | ||
} | ||
it('should return 200 OK if all checks pass', () => { | ||
let route = routes.configure({ | ||
const route = routes.configure({ | ||
checks: { | ||
check1: makeCheck(true), | ||
check2: makeCheck(true), | ||
check2: makeCheck(true) | ||
} | ||
}); | ||
let [req, res] = makeReqRes(200, { | ||
}) | ||
const [req, res] = makeReqRes(200, { | ||
status: outputs.UP, | ||
check1: {status: "UP"}, | ||
check2: {status: "UP"}, | ||
check1: { status: 'UP' }, | ||
check2: { status: 'UP' }, | ||
buildInfo: { | ||
environment: "test PACKAGES_ENVIRONMENT", | ||
project: "test PACKAGES_PROJECT", | ||
name: "test PACKAGES_NAME", | ||
version: "1.4.3-42", | ||
environment: 'test PACKAGES_ENVIRONMENT', | ||
project: 'test PACKAGES_PROJECT', | ||
name: 'test PACKAGES_NAME', | ||
version: '1.4.3-42', | ||
commit: 'abc1234', | ||
date: 'Jan 1 1970' | ||
} | ||
}); | ||
}) | ||
return route(req, res); | ||
}); | ||
return route(req, res) | ||
}) | ||
it('should return 500 DOWN if any checks fail', () => { | ||
let route = routes.configure({ | ||
const route = routes.configure({ | ||
checks: { | ||
check1: makeCheck(false), | ||
check2: makeCheck(true), | ||
check2: makeCheck(true) | ||
} | ||
}); | ||
let [req, res] = makeReqRes(500, { | ||
status: "DOWN", | ||
check1: {status: "DOWN"}, | ||
check2: {status: "UP"}, | ||
}) | ||
const [req, res] = makeReqRes(500, { | ||
status: 'DOWN', | ||
check1: { status: 'DOWN' }, | ||
check2: { status: 'UP' }, | ||
buildInfo: { | ||
environment: "test PACKAGES_ENVIRONMENT", | ||
project: "test PACKAGES_PROJECT", | ||
name: "test PACKAGES_NAME", | ||
version: "1.4.3-42", | ||
environment: 'test PACKAGES_ENVIRONMENT', | ||
project: 'test PACKAGES_PROJECT', | ||
name: 'test PACKAGES_NAME', | ||
version: '1.4.3-42', | ||
commit: 'abc1234', | ||
date: 'Jan 1 1970' | ||
} | ||
}); | ||
}) | ||
return route(req, res); | ||
return route(req, res) | ||
}) | ||
}); | ||
it('should return 200 and UP if readiness check is undefined', () => { | ||
const route = routes.checkReadiness() | ||
const [req, res] = makeReqRes(200, { | ||
status: 'UP' | ||
}) | ||
return route(req, res) | ||
}) | ||
it('should return 200 OK if all checks pass', () => { | ||
const route = routes.checkReadiness({ | ||
check1: makeCheck(true), | ||
check2: makeCheck(true) | ||
}) | ||
const [req, res] = makeReqRes(200, { | ||
status: outputs.UP, | ||
check1: { status: 'UP' }, | ||
check2: { status: 'UP' } | ||
}) | ||
return route(req, res) | ||
}) | ||
it('should return 500 DOWN if any readiness checks fail', () => { | ||
const route = routes.checkReadiness({ | ||
check1: makeCheck(false), | ||
check2: makeCheck(true) | ||
}) | ||
const [req, res] = makeReqRes(500, { | ||
status: 'DOWN', | ||
check1: { status: 'DOWN' }, | ||
check2: { status: 'UP' } | ||
}) | ||
return route(req, res) | ||
}) | ||
it('should return the extra build info', () => { | ||
let route = routes.configure({ | ||
const route = routes.configure({ | ||
checks: { | ||
@@ -136,26 +167,24 @@ check1: makeCheck(true) | ||
buildInfo: { | ||
foo: "bar" | ||
foo: 'bar' | ||
} | ||
}); | ||
let [req, res] = makeReqRes(200, { | ||
status: "UP", | ||
check1: {status: "UP"}, | ||
}) | ||
const [req, res] = makeReqRes(200, { | ||
status: 'UP', | ||
check1: { status: 'UP' }, | ||
buildInfo: { | ||
environment: "test PACKAGES_ENVIRONMENT", | ||
project: "test PACKAGES_PROJECT", | ||
name: "test PACKAGES_NAME", | ||
version: "1.4.3-42", | ||
environment: 'test PACKAGES_ENVIRONMENT', | ||
project: 'test PACKAGES_PROJECT', | ||
name: 'test PACKAGES_NAME', | ||
version: '1.4.3-42', | ||
commit: 'abc1234', | ||
date: 'Jan 1 1970', | ||
extra: { | ||
foo: "bar" | ||
foo: 'bar' | ||
} | ||
} | ||
}); | ||
}) | ||
return route(req, res); | ||
}); | ||
}); | ||
}); | ||
return route(req, res) | ||
}) | ||
}) | ||
}) |
'use strict' | ||
/* global describe, beforeEach, afterEach, it */ | ||
const {expect, sinon} = require('../chai-sinon'); | ||
const fs = require('fs').promises; | ||
const versionFile = require('../../healthcheck/versionFile'); | ||
const { expect, sinon } = require('../chai-sinon') | ||
const fs = require('fs').promises | ||
const versionFile = require('../../healthcheck/versionFile') | ||
describe('versionFile', () => { | ||
beforeEach(() => { | ||
sinon.stub(fs, 'readFile'); | ||
}); | ||
sinon.stub(fs, 'readFile') | ||
}) | ||
afterEach(() => { | ||
fs.readFile.restore(); | ||
}); | ||
fs.readFile.restore() | ||
}) | ||
describe('version', () => { | ||
it('should resolve the version', () => { | ||
fs.readFile.resolves('version: 1.4.3\nnumber: 42'); | ||
return expect(versionFile.version()).to.eventually.eql('1.4.3-42'); | ||
}); | ||
fs.readFile.resolves('version: 1.4.3\nnumber: 42') | ||
return expect(versionFile.version()).to.eventually.eql('1.4.3-42') | ||
}) | ||
it('should resolve the version even if build is missing', () => { | ||
fs.readFile.resolves('version: 1.4.3'); | ||
return expect(versionFile.version()).to.eventually.eql('1.4.3'); | ||
}); | ||
fs.readFile.resolves('version: 1.4.3') | ||
return expect(versionFile.version()).to.eventually.eql('1.4.3') | ||
}) | ||
it('should resolve "unknown" if no version present in version file but build is there', () => { | ||
fs.readFile.resolves('build: 42'); | ||
return expect(versionFile.version()).to.eventually.eql('unknown'); | ||
}); | ||
fs.readFile.resolves('build: 42') | ||
return expect(versionFile.version()).to.eventually.eql('unknown') | ||
}) | ||
it('should resolve "unknown" if no versionFile present', () => { | ||
fs.readFile.rejects('no file found'); | ||
return expect(versionFile.version()).to.eventually.eql('unknown'); | ||
}); | ||
fs.readFile.rejects('no file found') | ||
return expect(versionFile.version()).to.eventually.eql('unknown') | ||
}) | ||
it('should resolve "unknown" if no version present in version file', () => { | ||
fs.readFile.resolves('foo: bar'); | ||
return expect(versionFile.version()).to.eventually.eql('unknown'); | ||
}); | ||
fs.readFile.resolves('foo: bar') | ||
return expect(versionFile.version()).to.eventually.eql('unknown') | ||
}) | ||
describe('version with Environment Variables', () => { | ||
let versionFileWithEnv | ||
let versionFileWithEnv | ||
beforeEach(() => { | ||
process.env["PACKAGES_VERSION"] = 'v1.42' | ||
process.env.PACKAGES_VERSION = 'v1.42' | ||
versionFileWithEnv = require('../../healthcheck/versionFile') | ||
}); | ||
process.env.PACKAGES_VERSION = 'v1.42' | ||
process.env.PACKAGES_VERSION = 'v1.42' | ||
versionFileWithEnv = require('../../healthcheck/versionFile') | ||
}) | ||
afterEach(() => { | ||
delete process.env.PACKAGES_VERSION | ||
delete process.env["PACKAGES_VERSION"] | ||
}); | ||
delete process.env.PACKAGES_VERSION | ||
}) | ||
it('should resolve the version to Version even with Environment Variables', () => { | ||
fs.readFile.resolves('version: 1.4.3\nnumber: 42'); | ||
return expect(versionFileWithEnv.version()).to.eventually.eql('1.4.3-42'); | ||
}); | ||
it('should resolve the version to Version even with Environment Variables', () => { | ||
fs.readFile.resolves('version: 1.4.3\nnumber: 42') | ||
return expect(versionFileWithEnv.version()).to.eventually.eql('1.4.3-42') | ||
}) | ||
it('should resolve the version even if build is missing even with Environment Variables', () => { | ||
fs.readFile.resolves('version: 1.4.3'); | ||
return expect(versionFile.version()).to.eventually.eql('1.4.3'); | ||
}); | ||
it('should resolve the version even if build is missing even with Environment Variables', () => { | ||
fs.readFile.resolves('version: 1.4.3') | ||
return expect(versionFile.version()).to.eventually.eql('1.4.3') | ||
}) | ||
it('should resolve the version to Environment Variables if no version present in version file but build is there', () => { | ||
fs.readFile.resolves('number: 42'); | ||
return expect(versionFile.version()).to.eventually.eql('v1.42'); | ||
}); | ||
it('should resolve the version to Environment Variables if no version present in version file but build is there', () => { | ||
fs.readFile.resolves('number: 42') | ||
return expect(versionFile.version()).to.eventually.eql('v1.42') | ||
}) | ||
it('should resolve the version to Environment Variables "v1.42" when versionFile has no version', () => { | ||
fs.readFile.resolves('foo: bar'); | ||
return expect(versionFileWithEnv.version()).to.eventually.eql('v1.42'); | ||
}); | ||
it('should resolve the version to Environment Variables "v1.42" when versionFile has no version', () => { | ||
fs.readFile.resolves('foo: bar') | ||
return expect(versionFileWithEnv.version()).to.eventually.eql('v1.42') | ||
}) | ||
it('should resolve the version to Environment Variables "v1.42" when no versionFile exists', () => { | ||
fs.readFile.rejects('no file found'); | ||
return expect(versionFileWithEnv.version()).to.eventually.eql('v1.42'); | ||
}); | ||
it('should resolve the version to Environment Variables "v1.42" when no versionFile exists', () => { | ||
fs.readFile.rejects('no file found') | ||
return expect(versionFileWithEnv.version()).to.eventually.eql('v1.42') | ||
}) | ||
}) | ||
}) | ||
}); | ||
}); | ||
describe('commit', () => { | ||
it('should resolve the commit sha', () => { | ||
fs.readFile.resolves('commit: abc1234'); | ||
return expect(versionFile.commit()).to.eventually.eql('abc1234'); | ||
}); | ||
fs.readFile.resolves('commit: abc1234') | ||
return expect(versionFile.commit()).to.eventually.eql('abc1234') | ||
}) | ||
it('should resolve "unknown" if no versionFile present', () => { | ||
fs.readFile.rejects('no file found'); | ||
return expect(versionFile.commit()).to.eventually.eql('unknown'); | ||
}); | ||
fs.readFile.rejects('no file found') | ||
return expect(versionFile.commit()).to.eventually.eql('unknown') | ||
}) | ||
it('should resolve "unknown" if no commit present in version file', () => { | ||
fs.readFile.resolves('foo: bar'); | ||
return expect(versionFile.commit()).to.eventually.eql('unknown'); | ||
}); | ||
}); | ||
fs.readFile.resolves('foo: bar') | ||
return expect(versionFile.commit()).to.eventually.eql('unknown') | ||
}) | ||
}) | ||
describe('date', () => { | ||
it('should resolve the built date', () => { | ||
fs.readFile.resolves('date: Jan 1 1970'); | ||
return expect(versionFile.date()).to.eventually.eql('Jan 1 1970'); | ||
}); | ||
fs.readFile.resolves('date: Jan 1 1970') | ||
return expect(versionFile.date()).to.eventually.eql('Jan 1 1970') | ||
}) | ||
it('should resolve "unknown" if no versionFile present', () => { | ||
fs.readFile.rejects('no file found'); | ||
return expect(versionFile.date()).to.eventually.eql('unknown'); | ||
}); | ||
fs.readFile.rejects('no file found') | ||
return expect(versionFile.date()).to.eventually.eql('unknown') | ||
}) | ||
it('should resolve "unknown" if no commit present in version file', () => { | ||
fs.readFile.resolves('foo: bar'); | ||
return expect(versionFile.date()).to.eventually.eql('unknown'); | ||
}); | ||
}); | ||
}); | ||
fs.readFile.resolves('foo: bar') | ||
return expect(versionFile.date()).to.eventually.eql('unknown') | ||
}) | ||
}) | ||
}) |
Sorry, the diff of this file is not supported yet
Major refactor
Supply chain riskPackage has recently undergone a major refactor. It may be unstable or indicate significant internal changes. Use caution when updating to versions that include significant changes.
Found 1 instance in 1 package
Environment variable access
Supply chain riskPackage accesses environment variables, which may be a sign of credential stuffing or data theft.
Found 1 instance in 1 package
32277
21
707
91
15
1