Compare commits
1 Commits
developmen
...
feature/S3
Author | SHA1 | Date |
---|---|---|
alexandre merle | 38df40a520 |
|
@ -0,0 +1,99 @@
|
|||
const httpServer = require('../http/server');
|
||||
const werelogs = require('werelogs');
|
||||
const errors = require('../../errors');
|
||||
const ZenkoMetrics = require('../../metrics/ZenkoMetrics');
|
||||
|
||||
function sendError(res, log, error, optMessage) {
|
||||
res.writeHead(error.code);
|
||||
let message;
|
||||
if (optMessage) {
|
||||
message = optMessage;
|
||||
} else {
|
||||
message = error.description || '';
|
||||
}
|
||||
log.debug('sending back error response', { httpCode: error.code,
|
||||
errorType: error.message,
|
||||
error: message });
|
||||
res.end(`${JSON.stringify({ errorType: error.message,
|
||||
errorMessage: message })}\n`);
|
||||
}
|
||||
|
||||
function sendSuccess(res, log, msg) {
|
||||
res.writeHead(200);
|
||||
log.debug('replying with success');
|
||||
const message = msg || 'OK';
|
||||
res.end(message);
|
||||
}
|
||||
|
||||
function checkStub(log) { // eslint-disable-line
|
||||
return true;
|
||||
}
|
||||
|
||||
class HealthProbeServer extends httpServer {
|
||||
constructor(params) {
|
||||
const logging = new werelogs.Logger('HealthProbeServer');
|
||||
super(params.port, logging);
|
||||
this.logging = logging;
|
||||
this._metrics = params.metrics || ZenkoMetrics;
|
||||
this.setBindAddress(params.bindAddress || 'localhost');
|
||||
// hooking our request processing function by calling the
|
||||
// parent's method for that
|
||||
this.onRequest(this._onRequest);
|
||||
this._reqHandlers = {
|
||||
'/_/health/liveness': this._onLiveness.bind(this),
|
||||
'/_/health/readiness': this._onReadiness.bind(this),
|
||||
'/_/monitoring/metrics': this._onMetrics.bind(this),
|
||||
'/_/metrics': this._onMetrics.bind(this),
|
||||
};
|
||||
this._livenessCheck = params.livenessCheck || checkStub;
|
||||
this._readinessCheck = params.readinessCheck || checkStub;
|
||||
}
|
||||
|
||||
onLiveCheck(f) {
|
||||
this._livenessCheck = f;
|
||||
}
|
||||
|
||||
onReadyCheck(f) {
|
||||
this._readinessCheck = f;
|
||||
}
|
||||
|
||||
_onRequest(req, res) {
|
||||
const log = this.logging.newRequestLogger();
|
||||
log.debug('request received', { method: req.method,
|
||||
url: req.url });
|
||||
if (req.method !== 'GET') {
|
||||
sendError(res, log, errors.MethodNotAllowed);
|
||||
}
|
||||
if (req.url in this._reqHandlers) {
|
||||
this._reqHandlers[req.url](req, res, log);
|
||||
} else {
|
||||
sendError(res, log, errors.InvalidURI);
|
||||
}
|
||||
}
|
||||
|
||||
_onLiveness(req, res, log) {
|
||||
if (this._livenessCheck(log)) {
|
||||
sendSuccess(res, log);
|
||||
} else {
|
||||
sendError(res, log, errors.ServiceUnavailable);
|
||||
}
|
||||
}
|
||||
|
||||
_onReadiness(req, res, log) {
|
||||
if (this._readinessCheck(log)) {
|
||||
sendSuccess(res, log);
|
||||
} else {
|
||||
sendError(res, log, errors.ServiceUnavailable);
|
||||
}
|
||||
}
|
||||
|
||||
// expose metrics to Prometheus
|
||||
_onMetrics(req, res) {
|
||||
res.writeHead(200, {
|
||||
'Content-Type': this._metrics.asPrometheusContentType(),
|
||||
});
|
||||
res.end(this._metrics.asPrometheus());
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = HealthProbeServer;
|
|
@ -0,0 +1,235 @@
|
|||
const assert = require('assert');
|
||||
const HealthProbeServer =
|
||||
require('../../../../lib/network/probe/HealthProbeServer');
|
||||
const http = require('http');
|
||||
|
||||
function makeRequest(meth, uri) {
|
||||
const params = {
|
||||
hostname: 'localhost',
|
||||
port: 4042,
|
||||
method: meth,
|
||||
path: uri,
|
||||
};
|
||||
const req = http.request(params);
|
||||
req.setNoDelay(true);
|
||||
return req;
|
||||
}
|
||||
|
||||
const healthcheckEndpoints = [
|
||||
'/_/health/liveness',
|
||||
'/_/health/readiness',
|
||||
];
|
||||
|
||||
const badHealthcheckEndpoints = [
|
||||
'/_/health/liveness_thisiswrong',
|
||||
'/_/health/readiness_thisiswrong',
|
||||
];
|
||||
|
||||
describe('network.probe.HealthProbeServer', () => {
|
||||
describe('service is "up"', () => {
|
||||
let server;
|
||||
function setup(done) {
|
||||
server = new HealthProbeServer({ port: 4042 });
|
||||
server._cbOnListening = done;
|
||||
server.start();
|
||||
}
|
||||
|
||||
before(done => {
|
||||
setup(done);
|
||||
});
|
||||
|
||||
after(done => {
|
||||
server.stop();
|
||||
done();
|
||||
});
|
||||
healthcheckEndpoints.forEach(ep => {
|
||||
it('should perform a GET and ' +
|
||||
'return 200 OK', done => {
|
||||
makeRequest('GET', ep)
|
||||
.on('response', res => {
|
||||
assert(res.statusCode === 200);
|
||||
done();
|
||||
})
|
||||
.on('error', err => {
|
||||
assert.ifError(err);
|
||||
done();
|
||||
}).end();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('service is "down"', () => {
|
||||
let server;
|
||||
function setup(done) {
|
||||
function falseStub() {
|
||||
return false;
|
||||
}
|
||||
server = new HealthProbeServer({
|
||||
port: 4042,
|
||||
livenessCheck: falseStub,
|
||||
readinessCheck: falseStub,
|
||||
});
|
||||
server.start();
|
||||
done();
|
||||
}
|
||||
|
||||
before(done => {
|
||||
setup(done);
|
||||
});
|
||||
|
||||
after(done => {
|
||||
server.stop();
|
||||
done();
|
||||
});
|
||||
|
||||
healthcheckEndpoints.forEach(ep => {
|
||||
it('should perform a GET and ' +
|
||||
'return 503 ServiceUnavailable', done => {
|
||||
makeRequest('GET', ep)
|
||||
.on('response', res => {
|
||||
assert(res.statusCode === 503);
|
||||
done();
|
||||
})
|
||||
.on('error', err => {
|
||||
assert.ifError(err);
|
||||
done();
|
||||
}).end();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('Invalid Methods', () => {
|
||||
let server;
|
||||
function setup(done) {
|
||||
server = new HealthProbeServer({
|
||||
port: 4042,
|
||||
});
|
||||
server.start();
|
||||
done();
|
||||
}
|
||||
|
||||
before(done => {
|
||||
setup(done);
|
||||
});
|
||||
|
||||
after(done => {
|
||||
server.stop();
|
||||
done();
|
||||
});
|
||||
|
||||
healthcheckEndpoints.forEach(ep => {
|
||||
it('should perform a POST and ' +
|
||||
'return 405 MethodNotAllowed', done => {
|
||||
makeRequest('POST', ep)
|
||||
.on('response', res => {
|
||||
assert(res.statusCode === 405);
|
||||
done();
|
||||
})
|
||||
.on('error', err => {
|
||||
assert.ifError(err);
|
||||
done();
|
||||
}).end();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('Invalid URI', () => {
|
||||
let server;
|
||||
function setup(done) {
|
||||
server = new HealthProbeServer({
|
||||
port: 4042,
|
||||
});
|
||||
server.start();
|
||||
done();
|
||||
}
|
||||
|
||||
before(done => {
|
||||
setup(done);
|
||||
});
|
||||
|
||||
after(done => {
|
||||
server.stop();
|
||||
done();
|
||||
});
|
||||
|
||||
badHealthcheckEndpoints.forEach(ep => {
|
||||
it('should perform a GET and ' +
|
||||
'return 400 InvalidURI', done => {
|
||||
makeRequest('GET', ep)
|
||||
.on('response', res => {
|
||||
assert(res.statusCode === 400);
|
||||
done();
|
||||
})
|
||||
.on('error', err => {
|
||||
assert.ifError(err);
|
||||
done();
|
||||
}).end();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('metrics route', () => {
|
||||
let server;
|
||||
function setup(done) {
|
||||
server = new HealthProbeServer({ port: 4042 });
|
||||
server._cbOnListening = done;
|
||||
server.start();
|
||||
}
|
||||
|
||||
before(done => {
|
||||
setup(done);
|
||||
});
|
||||
|
||||
after(done => {
|
||||
server.stop();
|
||||
done();
|
||||
});
|
||||
it('should expose metrics 1/2', done => {
|
||||
makeRequest('GET', '/_/monitoring/metrics')
|
||||
.on('response', res => {
|
||||
assert(res.statusCode === 200);
|
||||
const respBufs = [];
|
||||
res.on('data', data => {
|
||||
respBufs.push(data);
|
||||
});
|
||||
res.on('end', () => {
|
||||
const respContents = respBufs.join('');
|
||||
assert(respContents.length > 0);
|
||||
done();
|
||||
});
|
||||
res.on('error', err => {
|
||||
assert.ifError(err);
|
||||
done();
|
||||
});
|
||||
})
|
||||
.on('error', err => {
|
||||
assert.ifError(err);
|
||||
done();
|
||||
}).end();
|
||||
});
|
||||
|
||||
it('should expose metrics 2/2', done => {
|
||||
makeRequest('GET', '/_/metrics')
|
||||
.on('response', res => {
|
||||
assert(res.statusCode === 200);
|
||||
const respBufs = [];
|
||||
res.on('data', data => {
|
||||
respBufs.push(data);
|
||||
});
|
||||
res.on('end', () => {
|
||||
const respContents = respBufs.join('');
|
||||
assert(respContents.length > 0);
|
||||
done();
|
||||
});
|
||||
res.on('error', err => {
|
||||
assert.ifError(err);
|
||||
done();
|
||||
});
|
||||
})
|
||||
.on('error', err => {
|
||||
assert.ifError(err);
|
||||
done();
|
||||
}).end();
|
||||
});
|
||||
});
|
||||
});
|
Loading…
Reference in New Issue