Skip to content

Commit

Permalink
Improve hydra initialization process (#128)
Browse files Browse the repository at this point in the history
* Improve hydra initialization process

* Update tests

* Support for JSON via HYDRA_SERVICE env var

* Improved metrics and handling of messages with missing header.
  • Loading branch information
cjus authored May 24, 2017
1 parent 980d7ff commit b651028
Show file tree
Hide file tree
Showing 4 changed files with 119 additions and 29 deletions.
116 changes: 94 additions & 22 deletions index.js
Original file line number Diff line number Diff line change
Expand Up @@ -93,30 +93,16 @@ class Hydra extends EventEmitter {
*/
init(config, testMode) {
this.testMode = testMode;

if (typeof config === 'string') {
const configHelper = require('./lib/config');
return configHelper.init(config)
.then(() => {
return this.init(configHelper.getObject(), testMode);
});
}

const initPromise = new Promise((resolve, reject) => {
if (!config || !config.hydra) {
reject(new Error('Config missing hydra branch'));
return;
}
if (!config.hydra.redis) {
reject(new Error('Config missing hydra.redis branch'));
return;
}
if (!config.hydra.serviceName || (!config.hydra.servicePort && !config.hydra.servicePort === 0)) {
reject(new Error('Config missing serviceName or servicePort'));
return;
}
if (config.hydra.serviceName.includes(':')) {
reject(new Error('Config can not have a colon character in its name'));
return;
}
let loader = (newConfig) => {
return Promise.series(this.registeredPlugins, (plugin) => plugin.setConfig(newConfig.hydra))
.then((..._results) => {
Expand All @@ -132,7 +118,73 @@ class Hydra extends EventEmitter {
});
};

if (process.env.HYDRA_REDIS_URL && process.env.HYDRA_SERVICE) {
if (!config || !config.hydra) {
config = Object.assign({
'hydra': {
'serviceIP': '',
'servicePort': 0,
'serviceType': '',
'serviceDescription': '',
'redis': {
'url': 'redis://127.0.0.1:6379/15'
}
}
});
}

if (!config.hydra.redis) {
config.hydra = Object.assign(config.hydra, {
'redis': {
'url': 'redis://127.0.0.1:6379/15'
}
});
}

if (process.env.HYDRA_REDIS_URL) {
Object.assign(config.hydra, {
redis: {
url: process.env.HYDRA_REDIS_URL
}
});
}

let partialConfig = true;
if (process.env.HYDRA_SERVICE) {
let hydraService = process.env.HYDRA_SERVICE.trim();
if (hydraService[0] === '{') {
let newHydraBranch = Utils.safeJSONParse(hydraService);
Object.assign(config.hydra, newHydraBranch);
partialConfig = false;
} if (hydraService.includes('|')) {
hydraService = hydraService.replace(/(\r\n|\r|\n)/g, '');
let newHydraBranch = {};
let key = '';
let val = '';
let segs = hydraService.split('|');
segs.forEach((segment) => {
segment = segment.trim();
[key, val] = segment.split('=');
newHydraBranch[key] = val;
});
Object.assign(config.hydra, newHydraBranch);
partialConfig = false;
}
}

if (!config.hydra.serviceName || (!config.hydra.servicePort && !config.hydra.servicePort === 0)) {
reject(new Error('Config missing serviceName or servicePort'));
return;
}
if (config.hydra.serviceName.includes(':')) {
reject(new Error('serviceName can not have a colon character in its name'));
return;
}
if (config.hydra.serviceName.includes(' ')) {
reject(new Error('serviceName can not have a space character in its name'));
return;
}

if (partialConfig && process.env.HYDRA_REDIS_URL) {
this._connectToRedis({redis: {url: process.env.HYDRA_REDIS_URL}})
.then(() => {
if (!this.redisdb) {
Expand Down Expand Up @@ -1071,11 +1123,12 @@ class Hydra extends EventEmitter {

this.redisdb.get(`${redisPreKey}:${instance.serviceName}:${instance.instanceID}:presence`, (err, _result) => {
if (err) {
this.emit('metric', `service:unavailable|${instance.serviceName}|${instance.instanceID}`);
this.emit('metric', `service:unavailable|${instance.serviceName}|${instance.instanceID}|presence:not:found`);
reject(err);
} else {
this.redisdb.hget(`${redisPreKey}:nodes`, instance.instanceID, (err, result) => {
if (err) {
this.emit('metric', `service:unavailable|${instance.serviceName}|${instance.instanceID}|instance:not:found`);
reject(err);
} else {
instance = Utils.safeJSONParse(result);
Expand All @@ -1099,18 +1152,19 @@ class Hydra extends EventEmitter {
options.body = Utils.safeJSONStringify(umfmsg.body);
serverRequest.send(Object.assign(options, sendOpts))
.then((res) => {
if (res.payLoad && res.headers['content-type'].indexOf('json') > -1) {
if (res.payLoad && res.headers['content-type'] && res.headers['content-type'].indexOf('json') > -1) {
res = Object.assign(res, Utils.safeJSONParse(res.payLoad.toString('utf8')));
delete res.payLoad;
}
resolve(serverResponse.createResponseObject(res.statusCode, res));
})
.catch((_err) => {
this.emit('metric', `service:unavailable|${instance.serviceName}|${instance.instanceID}`);
.catch((err) => {
instanceList.shift();
if (instanceList.length === 0) {
this.emit('metric', `service:unavailable|${instance.serviceName}|${instance.instanceID}|attempts:exhausted`);
resolve(this._createServerResponseWithReason(ServerResponse.HTTP_SERVICE_UNAVAILABLE, `An instance of ${instance.serviceName} is unavailable`));
} else {
this.emit('metric', `service:unavailable|${instance.serviceName}|${instance.instanceID}|${err.message}`);
this._tryAPIRequest(instanceList, parsedRoute, umfmsg, resolve, reject, sendOpts);
}
});
Expand All @@ -1131,7 +1185,7 @@ class Hydra extends EventEmitter {
* @return {promise} promise - response from API in resolved promise or
* error in rejected promise.
*/
_makeAPIRequest(message, sendOpts={}) {
_makeAPIRequest(message, sendOpts = { }) {
return new Promise((resolve, reject) => {
let umfmsg = UMFMessage.createMessage(message);
if (!umfmsg.validate()) {
Expand Down Expand Up @@ -1493,6 +1547,15 @@ class Hydra extends EventEmitter {
return require('./lib/umfmessage');
}

/**
* @name _getServerRequestHelper
* @summary returns ServerRequest helper
* @return {object} helper - service request helper
*/
_getServerRequestHelper() {
return require('./lib/server-request');
}

/**
* @name _getServerResponseHelper
* @summary returns ServerResponse helper
Expand Down Expand Up @@ -2032,6 +2095,15 @@ class IHydra extends Hydra {
return super._getUMFMessageHelper();
}

/**
* @name getServerRequestHelper
* @summary returns ServerRequest helper
* @return {object} helper - service request helper
*/
getServerRequestHelper() {
return super._getServerRequestHelper();
}

/**
* @name getServerResponseHelper
* @summary returns ServerResponse helper
Expand Down
1 change: 1 addition & 0 deletions lib/server-request.js
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ class ServerRequest {
send(options) {
return new Promise((resolve, reject) => {
if (options.method === 'POST' || options.method === 'PUT') {
options.headers = options.headers || {};
options.headers['content-length'] = Buffer.byteLength(options.body, 'utf8');
} else {
delete options.body;
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "hydra",
"version": "1.3.12",
"version": "1.4.6",
"license": "MIT",
"author": "Carlos Justiniano",
"contributors": [
Expand Down
29 changes: 23 additions & 6 deletions specs/test.js
Original file line number Diff line number Diff line change
Expand Up @@ -81,25 +81,42 @@ describe('Hydra', function() {
})
.catch((err) => {
expect(err).to.not.be.null;
expect(err.message).to.equal('Config missing hydra branch');
expect(err.message).to.equal('Config missing serviceName or servicePort');
done();
});
});

/**
* @description Hydra should load if serviceName and servicePort is provided
*/
it('should load if serviceName and servicePort is provided', (done) => {
hydra.init({
hydra: {
serviceName: 'test-service',
servicePort: 3000
}
}, true)
.then(() => {
done();
})
.catch((err) => {
expect(err).to.be.null;
done();
});
});

/**
* @description Hydra should fail to load without a hydra.redis branch in configuration
* @description Hydra should load without a hydra.redis branch in configuration
*/
it('should fail without config hydra.redis branch', (done) => {
it('should load without config hydra.redis branch', (done) => {
let config = getConfig();
delete config.hydra.redis;
hydra.init(config, true)
.then(() => {
expect(true).to.be.false;
done();
})
.catch((err) => {
expect(err).to.not.be.null;
expect(err.message).to.equal('Config missing hydra.redis branch');
expect(err).to.be.null;
done();
});
});
Expand Down

0 comments on commit b651028

Please sign in to comment.