healthchecks
Advanced tools
Comparing version 1.3.1 to 1.4.0
@@ -0,1 +1,5 @@ | ||
## Version 1.4.0 2015-03-24 | ||
CHANGED run checks against localAddress/localPort | ||
## Version 1.3.1 2015-03-23 | ||
@@ -2,0 +6,0 @@ |
@@ -26,2 +26,3 @@ // Exports an Express middleware factory. | ||
const ms = require('ms'); | ||
const Path = require('path'); | ||
const request = require('request'); | ||
@@ -35,50 +36,2 @@ const URL = require('url'); | ||
// Read the checks file and returns a check function (see checkFunction). | ||
function readChecks(filename, timeout) { | ||
const checks = File.readFileSync(filename, 'utf-8') | ||
.split(/[\n\r]+/) // Split into lines | ||
.map(function(line) { // Ignore leading/trailing spaces | ||
return line.trim(); | ||
}) | ||
.filter(function(line) { // Ignore empty lines | ||
return line.length; | ||
}) | ||
.filter(function(line) { // Ignore comments | ||
return line[0] !== '#'; | ||
}) | ||
.filter(function(line) { // Ignore name = value pairs | ||
return !/^\w+=/.test(line); | ||
}) | ||
.map(function(line) { // Split line to URL + expected value | ||
const match = line.match(/^(\S+)\s*(.*)/); | ||
debug('Added check', match[1], match[2]); | ||
return { | ||
url: match[1], | ||
expected: match[2] | ||
}; | ||
}) | ||
.map(function(check) { // Valid URLs only | ||
// URLs may be relative to the server, so contain an absolute path | ||
const url = URL.parse(check.url); | ||
assert(url.pathname && url.pathname[0] === '/', 'Check URL must have absolute pathname'); | ||
assert(!url.protocol || /^https?:$/.test(url.protocol), 'Check URL may only use HTTP/S protocol'); | ||
return check; | ||
}) | ||
.reduce(function(checks, check) { | ||
const url = check.url; | ||
checks[url] = checks[url] || []; | ||
if (check.expected) | ||
checks[url].push(check.expected); | ||
return checks; | ||
}, {}); | ||
// Returns a check function that will use these checks / settings | ||
const context = { | ||
checks: checks, | ||
timeout: timeout | ||
}; | ||
return checkFunction.bind(context); | ||
} | ||
// Represents a check outcome with the properties url, reason, etc. | ||
@@ -88,12 +41,8 @@ function Outcome(url, expected, error, response, body, elapsed) { | ||
this.elapsed = elapsed; | ||
if (error) { | ||
if (error.code === 'ETIMEDOUT') { | ||
this.reason = 'timeout'; | ||
this.timeout = true; | ||
} else { | ||
this.reason = 'error'; | ||
this.error = error; | ||
} | ||
if (error && error.code === 'ETIMEDOUT') { | ||
this.reason = 'timeout'; | ||
this.timeout = true; | ||
} else if (error) { | ||
this.reason = 'error'; | ||
this.error = error; | ||
} else { | ||
@@ -103,5 +52,5 @@ | ||
this.body = body; | ||
if (response.statusCode < 200 || response.statusCode >= 400) { | ||
if (response.statusCode < 200 || response.statusCode >= 400) | ||
this.reason = 'statusCode'; | ||
} else { | ||
else { | ||
@@ -141,3 +90,3 @@ const allMatching = _.all(expected, function(text) { | ||
// failed - A list of all check URLs that failed | ||
function checkFunction(baseURL, requestID) { | ||
function checkFunction(protocol, hostname, port, requestID) { | ||
const checks = this.checks; | ||
@@ -149,5 +98,11 @@ const timeout = this.timeout; | ||
return new Promise(function(resolve) { | ||
// We need to make an HTTP/S request to the current server, based on the hostname/port passed to us, | ||
// so the HTTP check would go to http://localhost:80/ or some such URL. | ||
// Checks have relative URLs, resolve them to absolute URLs | ||
const absoluteURL = URL.resolve(baseURL, checkURL); | ||
checkURL = URL.resolve(protocol + '://localhost/', checkURL); | ||
const requestURI = _.assign(URL.parse(checkURL), { host: null, port: port, hostname: hostname }); | ||
const headers = { | ||
'host': URL.parse(checkURL).hostname, | ||
'user-agent': 'Mozilla/5.0 (compatible) Healthchecks http://broadly.com' | ||
@@ -158,3 +113,3 @@ }; | ||
const params = { | ||
uri: absoluteURL, | ||
uri: URL.format(requestURI), | ||
headers: headers, | ||
@@ -167,3 +122,3 @@ timeout: timeout | ||
const elapsed = Date.now() - start; | ||
const outcome = new Outcome(absoluteURL, expected, error, response, body, elapsed); | ||
const outcome = new Outcome(checkURL, expected, error, response, body, elapsed); | ||
resolve(outcome); | ||
@@ -173,15 +128,15 @@ | ||
case 'error': { | ||
debug('Server responded with error ' + error.message, absoluteURL); | ||
debug('Server responded with error ' + error.message, checkURL); | ||
break; | ||
} | ||
case 'timeout': { | ||
debug('Server response timeout', absoluteURL); | ||
debug('Server response timeout', checkURL); | ||
break; | ||
} | ||
case 'statusCode': { | ||
debug('Server responded with status code ' + response.statusCode, absoluteURL); | ||
debug('Server responded with status code ' + response.statusCode, checkURL); | ||
break; | ||
} | ||
case 'body': { | ||
debug('Server response did not contain expected text', absoluteURL); | ||
debug('Server response did not contain expected text', checkURL); | ||
break; | ||
@@ -211,2 +166,51 @@ } | ||
// Read the checks file and returns a check function (see checkFunction). | ||
function readChecks(filename, timeout) { | ||
const checks = File.readFileSync(filename, 'utf-8') | ||
.split(/[\n\r]+/) // Split into lines | ||
.map(function(line) { // Ignore leading/trailing spaces | ||
return line.trim(); | ||
}) | ||
.filter(function(line) { // Ignore empty lines | ||
return line.length; | ||
}) | ||
.filter(function(line) { // Ignore comments | ||
return line[0] !== '#'; | ||
}) | ||
.filter(function(line) { // Ignore name = value pairs | ||
return !/^\w+=/.test(line); | ||
}) | ||
.map(function(line) { // Split line to URL + expected value | ||
const match = line.match(/^(\S+)\s*(.*)/); | ||
debug('Added check', match[1], match[2]); | ||
return { | ||
url: match[1], | ||
expected: match[2] | ||
}; | ||
}) | ||
.map(function(check) { // Valid URLs only | ||
// URLs may be relative to the server, so contain an absolute path | ||
const url = URL.parse(check.url); | ||
assert(url.pathname && url.pathname[0] === '/', 'Check URL must have absolute pathname'); | ||
assert(!url.protocol || /^https?:$/.test(url.protocol), 'Check URL may only use HTTP/S protocol'); | ||
return check; | ||
}) | ||
.reduce(function(memo, check) { | ||
const url = check.url; | ||
memo[url] = memo[url] || []; | ||
if (check.expected) | ||
memo[url].push(check.expected); | ||
return memo; | ||
}, {}); | ||
// Returns a check function that will use these checks / settings | ||
const context = { | ||
checks: checks, | ||
timeout: timeout | ||
}; | ||
return checkFunction.bind(context); | ||
} | ||
// Call this function to configure and return the middleware. | ||
@@ -217,9 +221,9 @@ module.exports = function healthchecks(options) { | ||
// Pass filename as first argument or named option | ||
const filename = (typeof(options) === 'string') ? options : options.filename; | ||
const filename = (typeof options === 'string') ? options : options.filename; | ||
assert(filename, 'Missing checks filename'); | ||
// Pass timeout as named option, or use default | ||
const timeoutArg = (typeof(options) !== 'string' && options.timeout) || DEFAULT_TIMEOUT; | ||
const timeoutArg = (typeof options !== 'string' && options.timeout) || DEFAULT_TIMEOUT; | ||
// If timeout argument is a string (e.g. "3d"), convert to milliseconds | ||
const timeout = (typeof(timeoutArg) === 'string') ? ms(timeoutArg) : +timeoutArg; | ||
const timeout = (typeof timeoutArg === 'string') ? ms(timeoutArg) : +timeoutArg; | ||
@@ -233,3 +237,3 @@ const onFailed = options.onFailed || function() {}; | ||
// Load Handlebars template for rendering results | ||
const template = File.readFileSync(__dirname + '/index.hbs', 'utf-8'); | ||
const template = File.readFileSync(Path.join(__dirname, '/index.hbs'), 'utf-8'); | ||
Handlebars.registerHelper('ms', ms); | ||
@@ -239,18 +243,16 @@ const render = Handlebars.compile(template); | ||
// Return the Express middleware | ||
return function(request, response) { | ||
return function(req, res) { | ||
debug('Health checks: running'); | ||
// The base URL where this middleware is mounted on. We do this | ||
// dynamically, so sending healthchecks request to HTTP vs HTTPS, or | ||
// different domain, may result in running different checks. | ||
const mounted = URL.format({ | ||
protocol: request.protocol, | ||
host: request.headers.host, | ||
port: request.port | ||
}); | ||
const requestID = req.headers['x-request-id']; | ||
const requestID = request.headers['x-request-id']; | ||
// We use local address/port to health check this server, e.g. the checks | ||
// may say //www.example.com/ but in development we connect to | ||
// 127.0.0.1:5000 | ||
const protocol = req.protocol; | ||
const hostname = req.socket.localAddress; | ||
const port = req.socket.localPort; | ||
// Run all checks | ||
runChecks(mounted, requestID) | ||
runChecks(protocol, hostname, port, requestID) | ||
.then(function(outcomes) { | ||
@@ -270,5 +272,5 @@ debug('Health checks: ' + outcomes.passed.length + ' passed and ' + outcomes.failed.length + ' failed'); | ||
}); | ||
response.writeHeader(statusCode); | ||
response.write(html); | ||
response.end(); | ||
res.writeHeader(statusCode); | ||
res.write(html); | ||
res.end(); | ||
@@ -275,0 +277,0 @@ if (outcomes.failed.length) |
{ | ||
"name": "healthchecks", | ||
"version": "1.3.1", | ||
"version": "1.4.0", | ||
"scripts": { | ||
@@ -5,0 +5,0 @@ "test": "mocha" |
@@ -46,3 +46,3 @@ const assert = require('assert'); | ||
assert.equal(failed.length, 1); | ||
assert.equal(failed[0].url, 'http://localhost:3000/error'); | ||
assert.equal(failed[0].url, 'http://localhost/error'); | ||
assert.equal(failed[0].reason, 'error'); | ||
@@ -55,3 +55,3 @@ | ||
assert.equal(failed[0].toString(), 'http://localhost:3000/error => socket hang up'); | ||
assert.equal(failed[0].toString(), 'http://localhost/error => socket hang up'); | ||
done(); | ||
@@ -78,3 +78,3 @@ }); | ||
assert.equal(failed.length, 1); | ||
assert.equal(failed[0].url, 'http://localhost:3000/timeout'); | ||
assert.equal(failed[0].url, 'http://localhost/timeout'); | ||
assert.equal(failed[0].reason, 'timeout'); | ||
@@ -87,3 +87,3 @@ | ||
assert.equal(failed[0].toString(), 'http://localhost:3000/timeout => timeout'); | ||
assert.equal(failed[0].toString(), 'http://localhost/timeout => timeout'); | ||
done(); | ||
@@ -109,3 +109,3 @@ }); | ||
assert.equal(failed.length, 1); | ||
assert.equal(failed[0].url, 'http://localhost:3000/status'); | ||
assert.equal(failed[0].url, 'http://localhost/status'); | ||
assert.equal(failed[0].reason, 'statusCode'); | ||
@@ -118,3 +118,3 @@ | ||
assert.equal(failed[0].toString(), 'http://localhost:3000/status => 400'); | ||
assert.equal(failed[0].toString(), 'http://localhost/status => 400'); | ||
done(); | ||
@@ -139,3 +139,3 @@ }); | ||
assert.equal(failed.length, 1); | ||
assert.equal(failed[0].url, 'http://localhost:3000/expected'); | ||
assert.equal(failed[0].url, 'http://localhost/expected'); | ||
assert.equal(failed[0].reason, 'body'); | ||
@@ -148,3 +148,3 @@ | ||
assert.equal(failed[0].toString(), 'http://localhost:3000/expected => body'); | ||
assert.equal(failed[0].toString(), 'http://localhost/expected => body'); | ||
done(); | ||
@@ -151,0 +151,0 @@ }); |
@@ -22,6 +22,6 @@ const Browser = require('zombie'); | ||
browser.assert.text('h1', 'Passed'); | ||
browser.assert.text('.passed li:nth-of-type(1)', /http:\/\/localhost:3000\/error \d+ms/); | ||
browser.assert.text('.passed li:nth-of-type(2)', /http:\/\/localhost:3000\/expected \d+ms/); | ||
browser.assert.text('.passed li:nth-of-type(3)', /http:\/\/localhost:3000\/status \d+ms/); | ||
browser.assert.text('.passed li:nth-of-type(4)', /http:\/\/localhost:3000\/timeout \d+ms/); | ||
browser.assert.text('.passed li:nth-of-type(1)', /http:\/\/localhost\/error \d+ms/); | ||
browser.assert.text('.passed li:nth-of-type(2)', /http:\/\/localhost\/expected \d+ms/); | ||
browser.assert.text('.passed li:nth-of-type(3)', /http:\/\/localhost\/status \d+ms/); | ||
browser.assert.text('.passed li:nth-of-type(4)', /http:\/\/localhost\/timeout \d+ms/); | ||
done(); | ||
@@ -42,3 +42,3 @@ }); | ||
browser.assert.elements('.failed li', 1); | ||
browser.assert.text('.failed li:nth-of-type(1)', /^http:\/\/localhost:3000\/error => socket hang up \d+ms$/); | ||
browser.assert.text('.failed li:nth-of-type(1)', /^http:\/\/localhost\/error => socket hang up \d+ms$/); | ||
browser.assert.elements('.passed li', 3); | ||
@@ -66,3 +66,3 @@ done(); | ||
browser.assert.elements('.failed li', 1); | ||
browser.assert.text('.failed li:nth-of-type(1)', /^http:\/\/localhost:3000\/timeout => timeout \d+s$/); | ||
browser.assert.text('.failed li:nth-of-type(1)', /^http:\/\/localhost\/timeout => timeout \d+s$/); | ||
browser.assert.elements('.passed li', 3); | ||
@@ -88,3 +88,3 @@ done(); | ||
browser.assert.elements('.failed li', 1); | ||
browser.assert.text('.failed li:nth-of-type(1)', /^http:\/\/localhost:3000\/status => 400 \d+ms$/); | ||
browser.assert.text('.failed li:nth-of-type(1)', /^http:\/\/localhost\/status => 400 \d+ms$/); | ||
browser.assert.elements('.passed li', 3); | ||
@@ -110,3 +110,3 @@ done(); | ||
browser.assert.elements('.failed li', 1); | ||
browser.assert.text('.failed li:nth-of-type(1)', /^http:\/\/localhost:3000\/expected => body \d+ms$/); | ||
browser.assert.text('.failed li:nth-of-type(1)', /^http:\/\/localhost\/expected => body \d+ms$/); | ||
browser.assert.elements('.passed li', 3); | ||
@@ -113,0 +113,0 @@ done(); |
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
32565
600