Merge pull request #240 from CartoDB/health-check
Add healthcheck endpoint
This commit is contained in:
commit
597f8a7bab
5
NEWS.md
5
NEWS.md
@ -1,6 +1,8 @@
|
|||||||
1.21.3 -- 2014-mm-dd
|
1.22.0 -- 2014-mm-dd
|
||||||
--------------------
|
--------------------
|
||||||
|
|
||||||
|
New features:
|
||||||
|
- Health check endpoint
|
||||||
|
|
||||||
1.21.2 -- 2014-12-15
|
1.21.2 -- 2014-12-15
|
||||||
--------------------
|
--------------------
|
||||||
@ -19,6 +21,7 @@ Bugfixes:
|
|||||||
- Closes fd for log files on `kill -HUP` (#230)
|
- Closes fd for log files on `kill -HUP` (#230)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
1.21.0 -- 2014-10-24
|
1.21.0 -- 2014-10-24
|
||||||
--------------------
|
--------------------
|
||||||
|
|
||||||
|
@ -151,6 +151,14 @@ var config = {
|
|||||||
// X-Tiler-Profile header containing elapsed timing for various
|
// X-Tiler-Profile header containing elapsed timing for various
|
||||||
// steps taken for producing the response.
|
// steps taken for producing the response.
|
||||||
,useProfiler:true
|
,useProfiler:true
|
||||||
|
// Settings for the health check available at /health
|
||||||
|
,health: {
|
||||||
|
enabled: false,
|
||||||
|
username: 'localhost',
|
||||||
|
z: 0,
|
||||||
|
x: 0,
|
||||||
|
y: 0
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
module.exports = config;
|
module.exports = config;
|
||||||
|
@ -160,6 +160,14 @@ var config = {
|
|||||||
handler: 'inline'
|
handler: 'inline'
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
// Settings for the health check available at /health
|
||||||
|
,health: {
|
||||||
|
enabled: true,
|
||||||
|
username: 'localhost',
|
||||||
|
z: 0,
|
||||||
|
x: 0,
|
||||||
|
y: 0
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
module.exports = config;
|
module.exports = config;
|
||||||
|
@ -160,6 +160,14 @@ var config = {
|
|||||||
handler: 'inline'
|
handler: 'inline'
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
// Settings for the health check available at /health
|
||||||
|
,health: {
|
||||||
|
enabled: false,
|
||||||
|
username: 'localhost',
|
||||||
|
z: 0,
|
||||||
|
x: 0,
|
||||||
|
y: 0
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
module.exports = config;
|
module.exports = config;
|
||||||
|
@ -147,6 +147,14 @@ var config = {
|
|||||||
// X-Tiler-Profile header containing elapsed timing for various
|
// X-Tiler-Profile header containing elapsed timing for various
|
||||||
// steps taken for producing the response.
|
// steps taken for producing the response.
|
||||||
,useProfiler:true
|
,useProfiler:true
|
||||||
|
// Settings for the health check available at /health
|
||||||
|
,health: {
|
||||||
|
enabled: false,
|
||||||
|
username: 'localhost',
|
||||||
|
z: 0,
|
||||||
|
x: 0,
|
||||||
|
y: 0
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
module.exports = config;
|
module.exports = config;
|
||||||
|
@ -5,6 +5,7 @@ var _ = require('underscore')
|
|||||||
, TemplateMaps = require('./template_maps.js')
|
, TemplateMaps = require('./template_maps.js')
|
||||||
, Cache = require('./cache_validator')
|
, Cache = require('./cache_validator')
|
||||||
, os = require('os')
|
, os = require('os')
|
||||||
|
, HealthCheck = require('./monitoring/health_check')
|
||||||
;
|
;
|
||||||
|
|
||||||
if ( ! process.env['PGAPPNAME'] )
|
if ( ! process.env['PGAPPNAME'] )
|
||||||
@ -665,6 +666,31 @@ var CartodbWindshaft = function(serverOptions) {
|
|||||||
|
|
||||||
// ---- Template maps interface ends @}
|
// ---- Template maps interface ends @}
|
||||||
|
|
||||||
|
var healthCheck = new HealthCheck(cartoData, Windshaft.tilelive);
|
||||||
|
ws.get('/health', function(req, res) {
|
||||||
|
var healthConfig = global.environment.health || {};
|
||||||
|
|
||||||
|
if (!!healthConfig.enabled) {
|
||||||
|
var startTime = Date.now();
|
||||||
|
healthCheck.check(healthConfig, function(err, result) {
|
||||||
|
var ok = !err;
|
||||||
|
var response = {
|
||||||
|
enabled: true,
|
||||||
|
ok: ok,
|
||||||
|
elapsed: Date.now() - startTime,
|
||||||
|
result: result
|
||||||
|
};
|
||||||
|
if (err) {
|
||||||
|
response.err = err.message;
|
||||||
|
}
|
||||||
|
res.send(response, ok ? 200 : 503);
|
||||||
|
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
res.send({enabled: false, ok: true}, 200);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
return ws;
|
return ws;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
90
lib/cartodb/monitoring/health_check.js
Normal file
90
lib/cartodb/monitoring/health_check.js
Normal file
@ -0,0 +1,90 @@
|
|||||||
|
var _ = require('underscore'),
|
||||||
|
dot = require('dot'),
|
||||||
|
fs = require('fs'),
|
||||||
|
path = require('path'),
|
||||||
|
Step = require('step');
|
||||||
|
|
||||||
|
function HealthCheck(metadataBackend, tilelive) {
|
||||||
|
this.metadataBackend = metadataBackend;
|
||||||
|
this.tilelive = tilelive;
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = HealthCheck;
|
||||||
|
|
||||||
|
|
||||||
|
var mapnikOptions = {
|
||||||
|
query: {
|
||||||
|
metatile: 1,
|
||||||
|
poolSize: 4,
|
||||||
|
bufferSize: 64
|
||||||
|
},
|
||||||
|
protocol: 'mapnik:',
|
||||||
|
slashes: true,
|
||||||
|
xml: null
|
||||||
|
};
|
||||||
|
|
||||||
|
var xmlTemplate = dot.template(fs.readFileSync(path.resolve(__dirname, 'map-config.xml'), 'utf-8'));
|
||||||
|
|
||||||
|
HealthCheck.prototype.check = function(config, callback) {
|
||||||
|
|
||||||
|
var self = this,
|
||||||
|
startTime,
|
||||||
|
result = {
|
||||||
|
redis: {
|
||||||
|
ok: false
|
||||||
|
},
|
||||||
|
mapnik: {
|
||||||
|
ok: false
|
||||||
|
},
|
||||||
|
tile: {
|
||||||
|
ok: false
|
||||||
|
}
|
||||||
|
};
|
||||||
|
mapnikXmlParams = config;
|
||||||
|
|
||||||
|
Step(
|
||||||
|
function getDBParams() {
|
||||||
|
startTime = Date.now();
|
||||||
|
self.metadataBackend.getAllUserDBParams(config.username, this);
|
||||||
|
},
|
||||||
|
function loadMapnik(err, dbParams) {
|
||||||
|
if (err) {
|
||||||
|
throw err;
|
||||||
|
}
|
||||||
|
result.redis = {
|
||||||
|
ok: !err,
|
||||||
|
elapsed: Date.now() - startTime,
|
||||||
|
size: Object.keys(dbParams).length
|
||||||
|
};
|
||||||
|
mapnikOptions.xml = xmlTemplate(mapnikXmlParams);
|
||||||
|
|
||||||
|
startTime = Date.now();
|
||||||
|
self.tilelive.load(mapnikOptions, this);
|
||||||
|
},
|
||||||
|
function getTile(err, source) {
|
||||||
|
if (err) {
|
||||||
|
throw err;
|
||||||
|
}
|
||||||
|
|
||||||
|
result.mapnik = {
|
||||||
|
ok: !err,
|
||||||
|
elapsed: Date.now() - startTime
|
||||||
|
};
|
||||||
|
|
||||||
|
startTime = Date.now();
|
||||||
|
source.getTile(config.z, config.x, config.y, this);
|
||||||
|
},
|
||||||
|
function handleTile(err, tile) {
|
||||||
|
result.tile = {
|
||||||
|
ok: !err
|
||||||
|
};
|
||||||
|
|
||||||
|
if (tile) {
|
||||||
|
result.tile.elapsed = Date.now() - startTime;
|
||||||
|
result.tile.size = tile.length;
|
||||||
|
}
|
||||||
|
|
||||||
|
callback(err, result);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
};
|
4
lib/cartodb/monitoring/map-config.xml
Normal file
4
lib/cartodb/monitoring/map-config.xml
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
<Map
|
||||||
|
background-color="#c33"
|
||||||
|
srs="+proj=longlat +ellps=WGS84 +datum=WGS84 +no_defs">
|
||||||
|
</Map>
|
23
npm-shrinkwrap.json
generated
23
npm-shrinkwrap.json
generated
@ -96,7 +96,7 @@
|
|||||||
},
|
},
|
||||||
"inherits": {
|
"inherits": {
|
||||||
"version": "2.0.1",
|
"version": "2.0.1",
|
||||||
"from": "inherits@2"
|
"from": "inherits@~2.0.1"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -149,6 +149,7 @@
|
|||||||
"rollbar": {
|
"rollbar": {
|
||||||
"version": "0.3.13",
|
"version": "0.3.13",
|
||||||
"from": "rollbar@~0.3.13",
|
"from": "rollbar@~0.3.13",
|
||||||
|
"resolved": "https://registry.npmjs.org/rollbar/-/rollbar-0.3.13.tgz",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"node-uuid": {
|
"node-uuid": {
|
||||||
"version": "1.4.2",
|
"version": "1.4.2",
|
||||||
@ -302,21 +303,23 @@
|
|||||||
"from": "tunnel-agent@~0.3.0"
|
"from": "tunnel-agent@~0.3.0"
|
||||||
},
|
},
|
||||||
"http-signature": {
|
"http-signature": {
|
||||||
"version": "0.10.0",
|
"version": "0.10.1",
|
||||||
"from": "http-signature@~0.10.0",
|
"from": "http-signature@~0.10.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/http-signature/-/http-signature-0.10.1.tgz",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"assert-plus": {
|
"assert-plus": {
|
||||||
"version": "0.1.2",
|
"version": "0.1.5",
|
||||||
"from": "assert-plus@0.1.2"
|
"from": "assert-plus@^0.1.5",
|
||||||
|
"resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-0.1.5.tgz"
|
||||||
},
|
},
|
||||||
"asn1": {
|
"asn1": {
|
||||||
"version": "0.1.11",
|
"version": "0.1.11",
|
||||||
"from": "asn1@0.1.11"
|
"from": "asn1@0.1.11"
|
||||||
},
|
},
|
||||||
"ctype": {
|
"ctype": {
|
||||||
"version": "0.5.2",
|
"version": "0.5.3",
|
||||||
"from": "ctype@0.5.2",
|
"from": "ctype@0.5.3",
|
||||||
"resolved": "https://registry.npmjs.org/ctype/-/ctype-0.5.2.tgz"
|
"resolved": "https://registry.npmjs.org/ctype/-/ctype-0.5.3.tgz"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@ -1615,9 +1618,9 @@
|
|||||||
"resolved": "https://registry.npmjs.org/connect/-/connect-1.9.2.tgz",
|
"resolved": "https://registry.npmjs.org/connect/-/connect-1.9.2.tgz",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"formidable": {
|
"formidable": {
|
||||||
"version": "1.0.15",
|
"version": "1.0.16",
|
||||||
"from": "formidable@1.0.x",
|
"from": "formidable@1.0.x",
|
||||||
"resolved": "https://registry.npmjs.org/formidable/-/formidable-1.0.15.tgz"
|
"resolved": "https://registry.npmjs.org/formidable/-/formidable-1.0.16.tgz"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@ -1681,7 +1684,7 @@
|
|||||||
},
|
},
|
||||||
"sphericalmercator": {
|
"sphericalmercator": {
|
||||||
"version": "1.0.3",
|
"version": "1.0.3",
|
||||||
"from": "sphericalmercator@~1.0.1",
|
"from": "sphericalmercator@~1.0.2",
|
||||||
"resolved": "https://registry.npmjs.org/sphericalmercator/-/sphericalmercator-1.0.3.tgz"
|
"resolved": "https://registry.npmjs.org/sphericalmercator/-/sphericalmercator-1.0.3.tgz"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
{
|
{
|
||||||
"private": true,
|
"private": true,
|
||||||
"name": "windshaft-cartodb",
|
"name": "windshaft-cartodb",
|
||||||
|
"version": "1.22.0",
|
||||||
"version": "1.21.3",
|
"version": "1.21.3",
|
||||||
"description": "A map tile server for CartoDB",
|
"description": "A map tile server for CartoDB",
|
||||||
"keywords": [
|
"keywords": [
|
||||||
|
72
test/acceptance/health_check.js
Normal file
72
test/acceptance/health_check.js
Normal file
@ -0,0 +1,72 @@
|
|||||||
|
var helper = require(__dirname + '/../support/test_helper');
|
||||||
|
|
||||||
|
var assert = require('../support/assert');
|
||||||
|
var CartodbWindshaft = require(__dirname + '/../../lib/cartodb/cartodb_windshaft');
|
||||||
|
var serverOptions = require(__dirname + '/../../lib/cartodb/server_options')();
|
||||||
|
var server = new CartodbWindshaft(serverOptions);
|
||||||
|
|
||||||
|
suite('health checks', function () {
|
||||||
|
|
||||||
|
beforeEach(function (done) {
|
||||||
|
global.environment.health = {
|
||||||
|
enabled: true,
|
||||||
|
username: 'localhost',
|
||||||
|
z: 0,
|
||||||
|
x: 0,
|
||||||
|
y: 0
|
||||||
|
};
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
|
||||||
|
var healthCheckRequest = {
|
||||||
|
url: '/health',
|
||||||
|
method: 'GET',
|
||||||
|
headers: {
|
||||||
|
host: 'localhost'
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
test('returns 200 and ok=true with enabled configuration', function (done) {
|
||||||
|
assert.response(server,
|
||||||
|
healthCheckRequest,
|
||||||
|
{
|
||||||
|
status: 200
|
||||||
|
},
|
||||||
|
function (res, err) {
|
||||||
|
console.log(res.body);
|
||||||
|
assert.ok(!err);
|
||||||
|
|
||||||
|
var parsed = JSON.parse(res.body);
|
||||||
|
|
||||||
|
assert.ok(parsed.enabled);
|
||||||
|
assert.ok(parsed.ok);
|
||||||
|
|
||||||
|
done();
|
||||||
|
}
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('fails for invalid user because it is not in redis', function (done) {
|
||||||
|
global.environment.health.username = 'invalid';
|
||||||
|
|
||||||
|
assert.response(server,
|
||||||
|
healthCheckRequest,
|
||||||
|
{
|
||||||
|
status: 503
|
||||||
|
},
|
||||||
|
function (res, err) {
|
||||||
|
assert.ok(!err);
|
||||||
|
|
||||||
|
var parsed = JSON.parse(res.body);
|
||||||
|
|
||||||
|
assert.equal(parsed.enabled, true);
|
||||||
|
assert.equal(parsed.ok, false);
|
||||||
|
|
||||||
|
assert.equal(parsed.result.redis.ok, false);
|
||||||
|
|
||||||
|
done();
|
||||||
|
}
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
});
|
Loading…
Reference in New Issue
Block a user