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