feat(portal-watch): initial implementation

This commit is contained in:
geek 2017-06-15 16:22:15 +01:00 committed by Sérgio Ramos
parent 8a4a40f7f4
commit 5e7bec0aa7
5 changed files with 287 additions and 34 deletions

3
packages/portal-watch/.gitignore vendored Normal file
View File

@ -0,0 +1,3 @@
node_modules
.DS_Store
npm-debug.log

View File

@ -0,0 +1,173 @@
'use strict';
// const Assert = require('assert');
const TritonWatch = require('triton-watch');
const DEPLOYMENT_GROUP = 'docker:label:com.docker.compose.project';
const SERVICE = 'docker:label:com.docker.compose.service';
const HASH = 'docker:label:com.docker.compose.config-hash';
module.exports = class Watcher {
constructor (options) {
options = options || {};
// todo assert options
this._data = options.data;
this._tritonWatch = new TritonWatch({
frequency: 500,
triton: {
profile: {
url: options.url || process.env.SDC_URL,
account: options.account || process.env.SDC_ACCOUNT,
keyId: options.keyId || process.env.SDC_KEY_ID
}
}
});
this._tritonWatch.on('change', (container) => { return this.onChange(container); });
}
getDeploymentGroupId (name, cb) {
this._data.getDeploymentGroup({ name }, (err, deploymentGroup) => {
if (err) {
return cb(err);
}
return cb(null, deploymentGroup && deploymentGroup.id);
});
}
getServiceId ({ serviceName, deploymentGroupId }, cb) {
this._data.getServices({ name: serviceName, deploymentGroupId }, (err, services) => {
if (err) {
return cb(err);
}
if (!services || !services.length) {
return cb();
}
return cb(null, services.pop().id);
});
}
getInstance (machineId, cb) {
this._data.getInstances({ machineId }, (err, instances) => {
if (err) {
return cb(err);
}
if (!instances || !instances.length) {
return cb();
}
return cb(null, instances.pop());
});
}
resolveChanges ({ machine, deploymentGroupId, serviceId, instance }) {
const handleError = (err) => {
if (err) {
console.error(err);
}
};
const create = () => {
return this._data.updateInstance({
name: machine.name,
status: machine.state.toUpperCase(),
machineId: machine.id
}, handleError);
};
const update = () => {
return this._data.updateInstance({
id: instance.id,
status: machine.state.toUpperCase()
}, handleError);
};
return (!instance || !instance.id) ?
create() :
update();
}
onChange (machine) {
if (!machine) {
console.error('`change` event received without machine data');
return;
}
const { id, tags = [] } = machine;
// assert id existence
if (!id) {
console.error('`change` event received for a machine without `id`');
return;
}
// assert that it's a docker-compose project
const isCompose = [DEPLOYMENT_GROUP, SERVICE, HASH].every(
(name) => { return tags[name]; }
);
if (!isCompose) {
console.error(`Changed machine ${id} was not provisioned by docker-compose`);
return;
}
const deploymentGroupName = tags[DEPLOYMENT_GROUP];
const serviceName = tags[SERVICE];
const handleError = (next) => {
return (err, item) => {
if (err) {
console.error(err);
return;
}
next(item);
};
};
const getInstance = (deploymentGroupId, serviceId) => {
this.getInstance(id, handleError((instance) => {
return this.resolveChanges({
machine,
deploymentGroupId,
serviceId,
instance
});
}));
};
// assert that service exists
const assertService = (deploymentGroupId) => {
this.getServiceId({ serviceName, deploymentGroupId }, handleError((serviceId) => {
if (!serviceId) {
console.error(`Service "${serviceName}" form DeploymentGroup "${deploymentGroupName}" for machine ${id} not found`);
return;
}
getInstance(deploymentGroupId, serviceId);
}));
};
// assert that project managed by this portal
const assertDeploymentGroup = () => {
this.getDeploymentGroupId(deploymentGroupName, handleError((deploymentGroupId) => {
if (!deploymentGroupId) {
console.error(`DeploymentGroup "${deploymentGroupName}" for machine ${id} not found`);
return;
}
assertService(deploymentGroupId);
}));
};
assertDeploymentGroup();
}
};

View File

@ -0,0 +1,22 @@
{
"name": "portal-watch",
"version": "1.0.0",
"private": true,
"license": "MPL-2.0",
"description": "resolver of triton change events",
"repository": "github:yldio/joyent-portal",
"main": "lib",
"scripts": {
"lint": "belly-button --fix",
"lint-ci": "belly-button",
"test": "lab -c --ignore debug",
"test-ci": "echo 0 `# lab -c -r console -o stdout -r tap -o $CIRCLE_TEST_REPORTS/test/portal-watch.xml`"
},
"dependencies": {
"triton-watch": "^1.0.1"
},
"devDependencies": {
"belly-button": "^3.1.0",
"lab": "^13.1.0"
}
}

View File

@ -0,0 +1,49 @@
'use strict';
const Lab = require('lab');
const PortalWatch = require('../');
const lab = exports.lab = Lab.script();
const it = lab.it;
const expect = Lab.expect;
it('updates instances with the current status', (done) => {
const data = {
getDeploymentGroup: (options, next) => {
expect(options.name).to.equal('test-project');
next(null, { id: 'deployment-group-id' });
},
getServices: (options, next) => {
expect(options.deploymentGroupId).to.equal('deployment-group-id');
expect(options.name).to.equal('test-service');
next(null, [{ id: 'service-id' }]);
},
getInstances: (options, next) => {
expect(options.machineId).to.equal('test-id');
next(null, [{ id: 'instance-id' }]);
},
updateInstance: (options, next) => {
expect(options.id).to.equal('instance-id');
expect(options.status).to.equal('DELETED');
done();
}
};
const machine = {
id: 'test-id',
tags: {
'docker:label:com.docker.compose.project': 'test-project',
'docker:label:com.docker.compose.service': 'test-service',
'docker:label:com.docker.compose.config-hash': 'test-hash'
},
state: 'deleted'
};
const portalOptions = { data, url: 'url', account: 'account', keyId: 'de:e7:73:9a:aa:91:bb:3e:72:8d:cc:62:ca:58:a2:ec' };
const portalWatch = new PortalWatch(portalOptions);
portalWatch._tritonWatch.removeAllListeners('change');
portalWatch.onChange(machine);
});

View File

@ -439,10 +439,8 @@ array-includes@^3.0.3:
es-abstract "^1.7.0"
array-iterate@^1.0.0:
version "1.1.0"
resolved "https://registry.yarnpkg.com/array-iterate/-/array-iterate-1.1.0.tgz#4f13148ffffa5f2756b50460e5eac8eed31a14e6"
dependencies:
has "^1.0.1"
version "1.1.1"
resolved "https://registry.yarnpkg.com/array-iterate/-/array-iterate-1.1.1.tgz#865bf7f8af39d6b0982c60902914ac76bc0108f6"
array-union@^1.0.1:
version "1.0.2"
@ -1612,6 +1610,10 @@ browserslist@^2.1.2:
caniuse-lite "^1.0.30000684"
electron-to-chromium "^1.3.14"
brule@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/brule/-/brule-2.0.0.tgz#7020ecb04301f4e1f03f26fc00569edf81258699"
buf-compare@^1.0.0:
version "1.0.1"
resolved "https://registry.yarnpkg.com/buf-compare/-/buf-compare-1.0.1.tgz#fef28da8b8113a0a0db4430b0b6467b69730b34a"
@ -3517,20 +3519,16 @@ esquery@^1.0.0:
estraverse "^4.0.0"
esrecurse@^4.1.0:
version "4.1.0"
resolved "https://registry.yarnpkg.com/esrecurse/-/esrecurse-4.1.0.tgz#4713b6536adf7f2ac4f327d559e7756bff648220"
version "4.2.0"
resolved "https://registry.yarnpkg.com/esrecurse/-/esrecurse-4.2.0.tgz#fa9568d98d3823f9a41d91e902dcab9ea6e5b163"
dependencies:
estraverse "~4.1.0"
estraverse "^4.1.0"
object-assign "^4.0.1"
estraverse@^4.0.0, estraverse@^4.1.1, estraverse@^4.2.0:
estraverse@^4.0.0, estraverse@^4.1.0, estraverse@^4.1.1, estraverse@^4.2.0:
version "4.2.0"
resolved "https://registry.yarnpkg.com/estraverse/-/estraverse-4.2.0.tgz#0dee3fed31fcd469618ce7342099fc1afa0bdb13"
estraverse@~4.1.0:
version "4.1.1"
resolved "https://registry.yarnpkg.com/estraverse/-/estraverse-4.1.1.tgz#f6caca728933a850ef90661d0e17982ba47111a2"
estree-walker@^0.2.1:
version "0.2.1"
resolved "https://registry.yarnpkg.com/estree-walker/-/estree-walker-0.2.1.tgz#bdafe8095383d8414d5dc2ecf4c9173b6db9412e"
@ -4393,10 +4391,11 @@ hash-base@^2.0.0:
inherits "^2.0.1"
hash.js@^1.0.0, hash.js@^1.0.3:
version "1.0.3"
resolved "https://registry.yarnpkg.com/hash.js/-/hash.js-1.0.3.tgz#1332ff00156c0a0ffdd8236013d07b77a0451573"
version "1.1.1"
resolved "https://registry.yarnpkg.com/hash.js/-/hash.js-1.1.1.tgz#5cb2e796499224e69fd0b00ed01d2d4a16e7a323"
dependencies:
inherits "^2.0.1"
inherits "^2.0.3"
minimalistic-assert "^1.0.0"
hawk@~3.1.3:
version "3.1.3"
@ -4420,8 +4419,8 @@ hedges@^1.0.0:
resolved "https://registry.yarnpkg.com/hedges/-/hedges-1.2.0.tgz#45183bd0f884e62261f3a2cfbed40fad409b340c"
history@^4.5.1, history@^4.6.0:
version "4.6.2"
resolved "https://registry.yarnpkg.com/history/-/history-4.6.2.tgz#716e863e1da0e97a028eed6da644061dd1e1ed1d"
version "4.6.3"
resolved "https://registry.yarnpkg.com/history/-/history-4.6.3.tgz#6d723a8712c581d6bef37e8c26f4aedc6eb86967"
dependencies:
invariant "^2.2.1"
loose-envify "^1.2.0"
@ -5209,8 +5208,8 @@ jest-snapshot@^20.0.3:
pretty-format "^20.0.3"
jest-styled-components@^3.0.2:
version "3.0.2"
resolved "https://registry.yarnpkg.com/jest-styled-components/-/jest-styled-components-3.0.2.tgz#90047608075219dd0ee60e7c80d7c0ba90c6534d"
version "3.1.0"
resolved "https://registry.yarnpkg.com/jest-styled-components/-/jest-styled-components-3.1.0.tgz#3687b1a564b16241d19bb8ecea1e6b477d4e0c0c"
dependencies:
css "^2.2.1"
@ -6414,13 +6413,11 @@ npmlog@^4.0.2, npmlog@^4.1.0:
set-blocking "~2.0.0"
nspell@^1.0.0:
version "1.0.2"
resolved "https://registry.yarnpkg.com/nspell/-/nspell-1.0.2.tgz#834c1e46c39ca99b9d7f6d4c485ef76a8a711b8c"
version "1.0.3"
resolved "https://registry.yarnpkg.com/nspell/-/nspell-1.0.3.tgz#2ade9be62ae3769240d37b9d92fe946a817bca90"
dependencies:
has "^1.0.1"
is-buffer "^1.1.4"
trim "0.0.1"
x-is-string "^0.1.0"
num2fraction@^1.2.2:
version "1.2.2"
@ -7063,8 +7060,8 @@ progress@^2.0.0:
resolved "https://registry.yarnpkg.com/progress/-/progress-2.0.0.tgz#8a1be366bf8fc23db2bd23f10c6fe920b4389d1f"
promise@^7.1.1:
version "7.3.0"
resolved "https://registry.yarnpkg.com/promise/-/promise-7.3.0.tgz#e7feec5aa87a2cbb81acf47d9a3adbd9d4642d7b"
version "7.3.1"
resolved "https://registry.yarnpkg.com/promise/-/promise-7.3.1.tgz#064b72602b18f90f29192b8b1bc418ffd1ebd3bf"
dependencies:
asap "~2.0.3"
@ -7342,8 +7339,8 @@ react-router@^4.1.1:
warning "^3.0.0"
react-styled-flexboxgrid@^2.0.1:
version "2.0.2"
resolved "https://registry.yarnpkg.com/react-styled-flexboxgrid/-/react-styled-flexboxgrid-2.0.2.tgz#c1991bd6b249ee4b7cf0abbc35869a0d28e65620"
version "2.0.3"
resolved "https://registry.yarnpkg.com/react-styled-flexboxgrid/-/react-styled-flexboxgrid-2.0.3.tgz#308a8bbc80b1737a65f4ccf35d02afe20932a2f2"
dependencies:
lodash.isinteger "^4.0.4"
@ -7966,10 +7963,9 @@ retext-simplify@^4.0.0:
unist-util-position "^3.0.0"
retext-spell@^2.0.0:
version "2.3.0"
resolved "https://registry.yarnpkg.com/retext-spell/-/retext-spell-2.3.0.tgz#09302b3697244d429a882bb87251e0626fef6e69"
version "2.3.1"
resolved "https://registry.yarnpkg.com/retext-spell/-/retext-spell-2.3.1.tgz#f52ee13797fc4eb52de1e8b9dc3f6fe85ca97bd7"
dependencies:
has "^1.0.1"
lodash.includes "^4.2.0"
nlcst-is-literal "^1.0.0"
nlcst-to-string "^2.0.0"
@ -8726,8 +8722,8 @@ stylis@2.0.0:
resolved "https://registry.yarnpkg.com/stylis/-/stylis-2.0.0.tgz#6785a6546bd73478799a67d49d67086953b50ad5"
stylis@^3.0.19:
version "3.1.8"
resolved "https://registry.yarnpkg.com/stylis/-/stylis-3.1.8.tgz#59e643861f7e67f0c9d157d7dfa8cf25a752e836"
version "3.1.9"
resolved "https://registry.yarnpkg.com/stylis/-/stylis-3.1.9.tgz#638370451f980437f57c59e58d2e296be29fafb7"
subtext@4.x.x:
version "4.4.1"
@ -9128,7 +9124,13 @@ trim@0.0.1:
version "0.0.1"
resolved "https://registry.yarnpkg.com/trim/-/trim-0.0.1.tgz#5858547f6b290757ee95cccc666fb50084c460dd"
triton@^5.2.0:
triton-watch@^1.0.1:
version "1.0.1"
resolved "https://registry.yarnpkg.com/triton-watch/-/triton-watch-1.0.1.tgz#b1087f6a57383f1e83d0a308e65110ca9a2a38d0"
dependencies:
triton "5.2.x"
triton@5.2.x, triton@^5.2.0:
version "5.2.0"
resolved "https://registry.yarnpkg.com/triton/-/triton-5.2.0.tgz#16ce8cb155c37785b6818e403f5cf83934921d41"
dependencies:
@ -9627,7 +9629,7 @@ window-size@0.1.0:
version "0.1.0"
resolved "https://registry.yarnpkg.com/window-size/-/window-size-0.1.0.tgz#5438cd2ea93b202efa3a19fe8887aee7c94f9c9d"
wordwrap@0.0.2, wordwrap@~0.0.2:
wordwrap@0.0.2:
version "0.0.2"
resolved "https://registry.yarnpkg.com/wordwrap/-/wordwrap-0.0.2.tgz#b79669bb42ecb409f83d583cad52ca17eaa1643f"
@ -9635,6 +9637,10 @@ wordwrap@1.0.0, wordwrap@~1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/wordwrap/-/wordwrap-1.0.0.tgz#27584810891456a4171c8d0226441ade90cbcaeb"
wordwrap@~0.0.2:
version "0.0.3"
resolved "https://registry.yarnpkg.com/wordwrap/-/wordwrap-0.0.3.tgz#a3d5da6cd5c0bc0008d37234bbaf1bed63059107"
wrap-ansi@^2.0.0:
version "2.1.0"
resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-2.1.0.tgz#d8fc3d284dd05794fe84973caecdd1cf824fdd85"