diff --git a/control-tower/.gitignore b/control-tower/.gitignore new file mode 100644 index 00000000..aa82f4b4 --- /dev/null +++ b/control-tower/.gitignore @@ -0,0 +1,4 @@ +node_modules +.DS_Store +npm-debug.log +.eslintcache diff --git a/control-tower/README.md b/control-tower/README.md new file mode 100644 index 00000000..02e6170a --- /dev/null +++ b/control-tower/README.md @@ -0,0 +1 @@ +# command-tower diff --git a/control-tower/bin/control-tower b/control-tower/bin/control-tower new file mode 100644 index 00000000..770a6e66 --- /dev/null +++ b/control-tower/bin/control-tower @@ -0,0 +1,15 @@ +#!/usr/bin/env node + +'use strict'; + +const ControlTower = require('../'); + +const tower = new ControlTower(); +tower.start((err) => { + if (err) { + console.error(err); + process.exit(1); + } + + console.log('Control Tower Started'); +}); diff --git a/control-tower/lib/index.js b/control-tower/lib/index.js new file mode 100644 index 00000000..5ba778d7 --- /dev/null +++ b/control-tower/lib/index.js @@ -0,0 +1,79 @@ +'use strict'; + +const CMonClient = require('cmon-client'); +const PortalData = require('portal-data'); +const VAsync = require('vasync'); + + +const internals = {}; + + +module.exports = class { + constructor (options) { + options = options || {}; + this._data = new PortalData(options.data); + this._cmon = new CMonClient(options.cmon); + this._deployments = {}; + } + + start (cb) { + this._data.connect((err) => { + if (err) { + return cb(err); + } + + this._poll(); + + this._data.deploymentChanges((err, changes) => { + if (changes) { + this._refreshContainers(changes.id); + } + }); + }); + } + + _refreshContainers (deploymentId) { + this._data.getServices(deploymentId).then((services) => { + this._deployments[deploymentId] = services.containers; + }); + } + + _listContainers () { + let containers = []; + const deploymentIds = Object.keys(this._deployments); + + deploymentIds.forEach((deploymentId) => { + containers = containers.concat(this._deployments[deploymentId]); + }); + + return containers; + } + + _poll () { + if (this._isPolling) { + return; + } + + const finish = () => { + this._isPolling = false; + setTimeout(() => this._poll(), 1000); + }; + + this._isPolling = true; + VAsync.forEachParallel({ + func: this._cmon.metrics, + inputs: this._listContainers() + }, (err, results) => { + if (err) { + console.error(err); + return finish(); + } + + this._saveMetrics(results, finish); + }); + } + + _saveMetrics (metrics, cb) { + cb(); + } +} diff --git a/control-tower/package.json b/control-tower/package.json new file mode 100644 index 00000000..c4c9f00a --- /dev/null +++ b/control-tower/package.json @@ -0,0 +1,28 @@ +{ + "name": "control-tower", + "version": "0.0.0", + "description": "portal service metrics watcher", + "main": "lib", + "bin": { + "control-tower": "./bin/control-tower" + }, + "scripts": { + "lint": "belly-button", + "rethinkdb-up": "docker run -d -p 8080:8080 -p 28015:28015 -p 29015:29015 --name rethinkdb rethinkdb", + "rethinkdb-down": "docker rm -f rethinkdb", + "test": "npm run lint && lab -t 40" + }, + "keywords": [], + "author": "wyatt", + "license": "MPL-2.0", + "devDependencies": { + "belly-button": "^3.1.0", + "code": "^4.0.0", + "lab": "^13.0.4" + }, + "dependencies": { + "cmon-client": "^1.1.0", + "portal-data": "^1.0.0", + "vasync": "^1.6.4" + } +} diff --git a/control-tower/test/index.js b/control-tower/test/index.js new file mode 100644 index 00000000..b405a8ed --- /dev/null +++ b/control-tower/test/index.js @@ -0,0 +1,26 @@ +'use strict'; + +const Code = require('code'); +const Lab = require('lab'); +const ControlTower = require('../'); + + +// Test shortcuts + +const lab = exports.lab = Lab.script(); +const describe = lab.describe; +const it = lab.it; +const expect = Code.expect; + + +const internals = { + options: { data: { test: true, name: 'test' } } +}; + + +describe('start()', () => { + it.skip('starts to listen for service changes and monitors metrics', (done) => { + const controlTower = new ControlTower(internals.options); + done(); + }); +});