diff --git a/.gitignore b/.gitignore index 787a19d..7d442f5 100644 --- a/.gitignore +++ b/.gitignore @@ -31,3 +31,4 @@ node_modules # Created by .ignore support plugin (hsz.mobi) .idea/ +/lib/ diff --git a/.npmignore b/.npmignore new file mode 100644 index 0000000..2fb8114 --- /dev/null +++ b/.npmignore @@ -0,0 +1 @@ +/src/ \ No newline at end of file diff --git a/index.js b/index.js deleted file mode 100644 index 76e1189..0000000 --- a/index.js +++ /dev/null @@ -1,3 +0,0 @@ -import ConsulProvider from './lib/consul-provider' - -export default ConsulProvider; \ No newline at end of file diff --git a/lib/consul-provider.js b/lib/consul-provider.js index 6dd2393..3e8f14d 100644 --- a/lib/consul-provider.js +++ b/lib/consul-provider.js @@ -1,97 +1,204 @@ -import {ServiceInformation, ClusterProvider} from 'microphone-core'; -import {CronJob} from 'cron'; +'use strict'; -export default class ConsulProvider extends ClusterProvider { +var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }(); - constructor(client, logger) { - super(); +Object.defineProperty(exports, "__esModule", { + value: true +}); - this._client = client; - this._logger = logger; +var _microphoneCore = require('microphone-core'); - this._serviceName = ""; - this._serviceId = ""; - this._version = ""; - this._uri = ""; - this._useEbayFabio = false; - } +var _cron = require('cron'); - async findServiceInstancesAsync(name) { - if (this._useEbayFabio === true) { - return [ - new ServiceInformation("http://localhost", 9999) - ]; - } +function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } - return this._client.findServiceAsync(name); - } +function _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return call && (typeof call === "object" || typeof call === "function") ? call : self; } - async registerServiceAsync(serviceName, serviceId, version, uri) { - this._serviceName = serviceName; - this._serviceId = serviceId; - this._version = version; - this._uri = uri; - await this._client.registerServiceAsync(serviceName, serviceId, uri); +function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; } - this.__startReaper(); - } +var ConsulProvider = function (_ClusterProvider) { + _inherits(ConsulProvider, _ClusterProvider); + + function ConsulProvider(client, logger) { + _classCallCheck(this, ConsulProvider); - async bootstrapClientAsync() { - this.__startReaper(); + var _this = _possibleConstructorReturn(this, Object.getPrototypeOf(ConsulProvider).call(this)); - //Task.FromResult(0); - return new Promise((resolve, reject)=> { - return resolve({}); - }); + _this._client = client; + _this._logger = logger; + + _this._serviceName = ""; + _this._serviceId = ""; + _this._version = ""; + _this._uri = ""; + _this._useEbayFabio = false; + return _this; } - __startReaper() { - function actionCompleteHandler(err, httpResponse, body) { - //noop + _createClass(ConsulProvider, [{ + key: 'findServiceInstancesAsync', + value: function findServiceInstancesAsync(name) { + return regeneratorRuntime.async(function findServiceInstancesAsync$(_context) { + while (1) { + switch (_context.prev = _context.next) { + case 0: + if (!(this._useEbayFabio === true)) { + _context.next = 2; + break; + } + + return _context.abrupt('return', [new _microphoneCore.ServiceInformation("http://localhost", 9999)]); + + case 2: + return _context.abrupt('return', this._client.findServiceAsync(name)); + + case 3: + case 'end': + return _context.stop(); + } + } + }, null, this); + } + }, { + key: 'registerServiceAsync', + value: function registerServiceAsync(serviceName, serviceId, version, uri) { + return regeneratorRuntime.async(function registerServiceAsync$(_context2) { + while (1) { + switch (_context2.prev = _context2.next) { + case 0: + this._serviceName = serviceName; + this._serviceId = serviceId; + this._version = version; + this._uri = uri; + _context2.next = 6; + return regeneratorRuntime.awrap(this._client.registerServiceAsync(serviceName, serviceId, uri)); + + case 6: + + this.__startReaper(); + + case 7: + case 'end': + return _context2.stop(); + } + } + }, null, this); } + }, { + key: 'bootstrapClientAsync', + value: function bootstrapClientAsync() { + return regeneratorRuntime.async(function bootstrapClientAsync$(_context3) { + while (1) { + switch (_context3.prev = _context3.next) { + case 0: + this.__startReaper(); + + //Task.FromResult(0); + return _context3.abrupt('return', new Promise(function (resolve, reject) { + return resolve({}); + })); + + case 2: + case 'end': + return _context3.stop(); + } + } + }, null, this); + } + }, { + key: '__startReaper', + value: function __startReaper() { + function actionCompleteHandler(err, httpResponse, body) { + //noop + } - var lookup = []; + var lookup = []; - async function onTick() { - if (this._port > 0) this._client.setPort(this._port); + function onTick() { + var res, criticalServiceId; + return regeneratorRuntime.async(function onTick$(_context4) { + while (1) { + switch (_context4.prev = _context4.next) { + case 0: + if (this._port > 0) this._client.setPort(this._port); + _context4.prev = 1; + _context4.next = 4; + return regeneratorRuntime.awrap(this._client.getCriticalServicesAsync()); - try { - var res = await this._client.getCriticalServicesAsync(); + case 4: + res = _context4.sent; + _context4.t0 = regeneratorRuntime.keys(res); - for (let criticalServiceId in res) { - if (lookup.indexOf(criticalServiceId) !== -1) { - await c.unRegisterServiceAsync(criticalServiceId); - this._logger.info(`Reaper: Removing ${criticalServiceId}`); - } - else { - lookup.push(criticalServiceId); - this._logger.info(`Reaper: Marking ${criticalServiceId}`); - } - } + case 6: + if ((_context4.t1 = _context4.t0()).done) { + _context4.next = 18; + break; + } - //remove entries that are no longer critical - lookup.filter(serviceId => !(lookup.indexOf(serviceId) !== -1)); + criticalServiceId = _context4.t1.value; + if (!(lookup.indexOf(criticalServiceId) !== -1)) { + _context4.next = 14; + break; + } + + _context4.next = 11; + return regeneratorRuntime.awrap(c.unRegisterServiceAsync(criticalServiceId)); + + case 11: + this._logger.info('Reaper: Removing ' + criticalServiceId); + _context4.next = 16; + break; + + case 14: + lookup.push(criticalServiceId); + this._logger.info('Reaper: Marking ' + criticalServiceId); + + case 16: + _context4.next = 6; + break; + + case 18: + + //remove entries that are no longer critical + lookup.filter(function (serviceId) { + return !(lookup.indexOf(serviceId) !== -1); + }); + + _context4.next = 24; + break; + + case 21: + _context4.prev = 21; + _context4.t2 = _context4['catch'](1); + + this._logger.error(_context4.t2, "Crashed"); + + case 24: + case 'end': + return _context4.stop(); + } + } + }, null, this, [[1, 21]]); } - catch (ex) { - this._logger.error(ex, "Crashed"); - } - } - try { - let job = new CronJob({ - cronTime: "*/5 * * * * *" /*every 5 seconds*/, - onTick: onTick.bind(this), + try { + var job = new _cron.CronJob({ + cronTime: "*/5 * * * * *" /*every 5 seconds*/ + , onTick: onTick.bind(this), start: false - } - ); + }); - job.start(); - this._logger.info("Reaper: started.."); - } - catch (ex) { - this._logger.error(ex); + job.start(); + this._logger.info("Reaper: started.."); + } catch (ex) { + this._logger.error(ex); + } } - } -} \ No newline at end of file + }]); + + return ConsulProvider; +}(_microphoneCore.ClusterProvider); + +exports.default = ConsulProvider; \ No newline at end of file diff --git a/lib/consul-rest-client.js b/lib/consul-rest-client.js index 432dd38..5cfd9ea 100644 --- a/lib/consul-rest-client.js +++ b/lib/consul-rest-client.js @@ -1,80 +1,171 @@ -import request from 'request'; -import os from 'os'; -import {ServiceInformation} from 'microphone-core'; +'use strict'; -const DEFAULT_PORT = 8500; -const DEFAULT_HOST = 'http://localhost'; +var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }(); -export default class ConsulRestClient { - constructor(address, port) { - this._address = address || DEFAULT_HOST; - this._port = port || DEFAULT_PORT; - } +Object.defineProperty(exports, "__esModule", { + value: true +}); - async registerServiceAsync(serviceName, serviceId, address) { - let payload = { - ID: serviceId, - Name: serviceName, - Tags: [`urlprefix-/${serviceName}`], - Address: os.hostname(), - Port: address.Port, - Check: { - HTTP: `${address}status`, - Interval: "1s" - } - }; - - let options = { - uri: `${this._address}:${this._port}/v1/agent/service/register`, - type: 'POST', - json: payload - }; - - await this.__request(options, "Could not register service"); - } +var _request = require('request'); - async findServiceAsync(serviceName) { - let options = { - uri: `${this._address}:${this._port}/v1/health/service/${serviceName}`, - type: 'GET' - }; +var _request2 = _interopRequireDefault(_request); - let serviceArray = JSON.parse(await this.__request(options, "Could not find services")); +var _os = require('os'); - return serviceArray.map(svcItem => new ServiceInformation(svcItem["Service"]["Address"], svcItem["Service"]["Port"])); - } +var _os2 = _interopRequireDefault(_os); - async getCriticalServicesAsync() { - var options = { - uri: `${this._address}:${this._port}/v1/health/state/critical`, - type: 'GET' - }; +var _microphoneCore = require('microphone-core'); - let serviceArray = JSON.parse(await this.__request(options, "Could not get service health")); +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } - return serviceArray.map(svcItem => svcItem["ServiceID"]); - } +function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } - async unRegisterServiceAsync(serviceId) { - var options = { - uri: `${this._address}:${this._port}/v1/agent/service/deregister/${serviceId}`, - type: 'GET' - }; +var DEFAULT_PORT = 8500; +var DEFAULT_HOST = 'http://localhost'; - await this.__request(options, "Could not de register service"); - } +var ConsulRestClient = function () { + function ConsulRestClient(address, port) { + _classCallCheck(this, ConsulRestClient); - setPort(port) { + this._address = address || DEFAULT_HOST; this._port = port || DEFAULT_PORT; } - __request(options, message) { - return new Promise((resolve, reject)=> { - request(options, (error, response, body)=> { - if (error) return reject(new Error(message)); - - resolve(body); + _createClass(ConsulRestClient, [{ + key: 'registerServiceAsync', + value: function registerServiceAsync(serviceName, serviceId, address) { + var payload, options; + return regeneratorRuntime.async(function registerServiceAsync$(_context) { + while (1) { + switch (_context.prev = _context.next) { + case 0: + payload = { + ID: serviceId, + Name: serviceName, + Tags: ['urlprefix-/' + serviceName], + Address: _os2.default.hostname(), + Port: address.Port, + Check: { + HTTP: address + 'status', + Interval: "1s" + } + }; + options = { + uri: this._address + ':' + this._port + '/v1/agent/service/register', + type: 'POST', + json: payload + }; + _context.next = 4; + return regeneratorRuntime.awrap(this.__request(options, "Could not register service")); + + case 4: + case 'end': + return _context.stop(); + } + } + }, null, this); + } + }, { + key: 'findServiceAsync', + value: function findServiceAsync(serviceName) { + var options, serviceArray; + return regeneratorRuntime.async(function findServiceAsync$(_context2) { + while (1) { + switch (_context2.prev = _context2.next) { + case 0: + options = { + uri: this._address + ':' + this._port + '/v1/health/service/' + serviceName, + type: 'GET' + }; + _context2.t0 = JSON; + _context2.next = 4; + return regeneratorRuntime.awrap(this.__request(options, "Could not find services")); + + case 4: + _context2.t1 = _context2.sent; + serviceArray = _context2.t0.parse.call(_context2.t0, _context2.t1); + return _context2.abrupt('return', serviceArray.map(function (svcItem) { + return new _microphoneCore.ServiceInformation(svcItem["Service"]["Address"], svcItem["Service"]["Port"]); + })); + + case 7: + case 'end': + return _context2.stop(); + } + } + }, null, this); + } + }, { + key: 'getCriticalServicesAsync', + value: function getCriticalServicesAsync() { + var options, serviceArray; + return regeneratorRuntime.async(function getCriticalServicesAsync$(_context3) { + while (1) { + switch (_context3.prev = _context3.next) { + case 0: + options = { + uri: this._address + ':' + this._port + '/v1/health/state/critical', + type: 'GET' + }; + _context3.t0 = JSON; + _context3.next = 4; + return regeneratorRuntime.awrap(this.__request(options, "Could not get service health")); + + case 4: + _context3.t1 = _context3.sent; + serviceArray = _context3.t0.parse.call(_context3.t0, _context3.t1); + return _context3.abrupt('return', serviceArray.map(function (svcItem) { + return svcItem["ServiceID"]; + })); + + case 7: + case 'end': + return _context3.stop(); + } + } + }, null, this); + } + }, { + key: 'unRegisterServiceAsync', + value: function unRegisterServiceAsync(serviceId) { + var options; + return regeneratorRuntime.async(function unRegisterServiceAsync$(_context4) { + while (1) { + switch (_context4.prev = _context4.next) { + case 0: + options = { + uri: this._address + ':' + this._port + '/v1/agent/service/deregister/' + serviceId, + type: 'GET' + }; + _context4.next = 3; + return regeneratorRuntime.awrap(this.__request(options, "Could not de register service")); + + case 3: + case 'end': + return _context4.stop(); + } + } + }, null, this); + } + }, { + key: 'setPort', + value: function setPort(port) { + this._port = port || DEFAULT_PORT; + } + }, { + key: '__request', + value: function __request(options, message) { + return new Promise(function (resolve, reject) { + (0, _request2.default)(options, function (error, response, body) { + if (error) return reject(new Error(message)); + + resolve(body); + }); }); - }); - } -} \ No newline at end of file + } + }]); + + return ConsulRestClient; +}(); + +exports.default = ConsulRestClient; \ No newline at end of file diff --git a/package.json b/package.json index aa08b74..f5c373e 100644 --- a/package.json +++ b/package.json @@ -2,19 +2,30 @@ "name": "microphone-consul", "version": "1.0.0", "description": "Consul provider for microphone.js Microservice framework", - "main": "index.js", + "main": "lib/index.js", "scripts": { + "compile": "babel src --out-dir lib", + "prepublish": "npm run compile", "test": "node ./node_modules/babel-cli/bin/babel-node.js ./node_modules/mocha/bin/_mocha tests/**/*.spec.js", "test-cov": "node ./node_modules/babel-cli/bin/babel-node.js ./node_modules/isparta/bin/isparta cover --report lcov --report text --report html ./node_modules/mocha/bin/_mocha -- --reporter dot tests/**/*.spec.js", "test-travis": "node ./node_modules/babel-cli/bin/babel-node.js ./node_modules/isparta/bin/isparta cover --report lcov ./node_modules/mocha/bin/_mocha -- --reporter dot tests/**/*.spec.js && cat ./coverage/lcov.info | ./node_modules/coveralls/bin/coveralls.js && rm -rf ./coverage" }, + "babel": { + "presets": [ + "es2015" + ], + "plugins": [ + "syntax-async-functions", + "transform-regenerator" + ] + }, "author": [ "ziyasal " ], "license": "MIT", "dependencies": { "cron": "^1.1.0", - "microphone-core": "^1.0.0", + "microphone-core": "^1.0.2", "request": "^2.67.0" }, "devDependencies": { diff --git a/sample/main.js b/sample/main.js index 96b85dc..7d2b95c 100644 --- a/sample/main.js +++ b/sample/main.js @@ -1,4 +1,4 @@ -import ConsulRestClient from '../lib/consul-rest-client'; +import ConsulRestClient from '../src/consul-rest-client'; async function main() { diff --git a/src/consul-provider.js b/src/consul-provider.js new file mode 100644 index 0000000..6dd2393 --- /dev/null +++ b/src/consul-provider.js @@ -0,0 +1,97 @@ +import {ServiceInformation, ClusterProvider} from 'microphone-core'; +import {CronJob} from 'cron'; + +export default class ConsulProvider extends ClusterProvider { + + constructor(client, logger) { + super(); + + this._client = client; + this._logger = logger; + + this._serviceName = ""; + this._serviceId = ""; + this._version = ""; + this._uri = ""; + this._useEbayFabio = false; + } + + async findServiceInstancesAsync(name) { + if (this._useEbayFabio === true) { + return [ + new ServiceInformation("http://localhost", 9999) + ]; + } + + return this._client.findServiceAsync(name); + } + + async registerServiceAsync(serviceName, serviceId, version, uri) { + this._serviceName = serviceName; + this._serviceId = serviceId; + this._version = version; + this._uri = uri; + await this._client.registerServiceAsync(serviceName, serviceId, uri); + + this.__startReaper(); + } + + async bootstrapClientAsync() { + this.__startReaper(); + + //Task.FromResult(0); + return new Promise((resolve, reject)=> { + return resolve({}); + }); + } + + __startReaper() { + function actionCompleteHandler(err, httpResponse, body) { + //noop + } + + var lookup = []; + + async function onTick() { + if (this._port > 0) this._client.setPort(this._port); + + + try { + var res = await this._client.getCriticalServicesAsync(); + + for (let criticalServiceId in res) { + if (lookup.indexOf(criticalServiceId) !== -1) { + await c.unRegisterServiceAsync(criticalServiceId); + this._logger.info(`Reaper: Removing ${criticalServiceId}`); + } + else { + lookup.push(criticalServiceId); + this._logger.info(`Reaper: Marking ${criticalServiceId}`); + } + } + + //remove entries that are no longer critical + lookup.filter(serviceId => !(lookup.indexOf(serviceId) !== -1)); + + } + catch (ex) { + this._logger.error(ex, "Crashed"); + } + } + + try { + let job = new CronJob({ + cronTime: "*/5 * * * * *" /*every 5 seconds*/, + onTick: onTick.bind(this), + start: false + } + ); + + job.start(); + this._logger.info("Reaper: started.."); + } + catch (ex) { + this._logger.error(ex); + } + } +} \ No newline at end of file diff --git a/src/consul-rest-client.js b/src/consul-rest-client.js new file mode 100644 index 0000000..432dd38 --- /dev/null +++ b/src/consul-rest-client.js @@ -0,0 +1,80 @@ +import request from 'request'; +import os from 'os'; +import {ServiceInformation} from 'microphone-core'; + +const DEFAULT_PORT = 8500; +const DEFAULT_HOST = 'http://localhost'; + +export default class ConsulRestClient { + constructor(address, port) { + this._address = address || DEFAULT_HOST; + this._port = port || DEFAULT_PORT; + } + + async registerServiceAsync(serviceName, serviceId, address) { + let payload = { + ID: serviceId, + Name: serviceName, + Tags: [`urlprefix-/${serviceName}`], + Address: os.hostname(), + Port: address.Port, + Check: { + HTTP: `${address}status`, + Interval: "1s" + } + }; + + let options = { + uri: `${this._address}:${this._port}/v1/agent/service/register`, + type: 'POST', + json: payload + }; + + await this.__request(options, "Could not register service"); + } + + async findServiceAsync(serviceName) { + let options = { + uri: `${this._address}:${this._port}/v1/health/service/${serviceName}`, + type: 'GET' + }; + + let serviceArray = JSON.parse(await this.__request(options, "Could not find services")); + + return serviceArray.map(svcItem => new ServiceInformation(svcItem["Service"]["Address"], svcItem["Service"]["Port"])); + } + + async getCriticalServicesAsync() { + var options = { + uri: `${this._address}:${this._port}/v1/health/state/critical`, + type: 'GET' + }; + + let serviceArray = JSON.parse(await this.__request(options, "Could not get service health")); + + return serviceArray.map(svcItem => svcItem["ServiceID"]); + } + + async unRegisterServiceAsync(serviceId) { + var options = { + uri: `${this._address}:${this._port}/v1/agent/service/deregister/${serviceId}`, + type: 'GET' + }; + + await this.__request(options, "Could not de register service"); + } + + setPort(port) { + this._port = port || DEFAULT_PORT; + } + + __request(options, message) { + return new Promise((resolve, reject)=> { + request(options, (error, response, body)=> { + if (error) return reject(new Error(message)); + + resolve(body); + }); + }); + } +} \ No newline at end of file diff --git a/src/index.js b/src/index.js new file mode 100644 index 0000000..f965a95 --- /dev/null +++ b/src/index.js @@ -0,0 +1,3 @@ +import ConsulProvider from './consul-provider' + +export default ConsulProvider; \ No newline at end of file diff --git a/tests/consul-provider.spec.js b/tests/consul-provider.spec.js index 400585e..b99402b 100644 --- a/tests/consul-provider.spec.js +++ b/tests/consul-provider.spec.js @@ -2,7 +2,7 @@ import chai from 'chai'; import sinon from "sinon"; -import ConsulProvider from '../index'; +import ConsulProvider from '../src/index'; chai.should(); diff --git a/tests/consul-rest-client.spec.js b/tests/consul-rest-client.spec.js index 7f11e49..38d2d8a 100644 --- a/tests/consul-rest-client.spec.js +++ b/tests/consul-rest-client.spec.js @@ -1,6 +1,6 @@ import chai from 'chai'; -import ConsulRestClient from '../lib/consul-rest-client'; +import ConsulRestClient from '../src/consul-rest-client'; chai.should();