feat(portal-watch): initial implementation
This commit is contained in:
parent
8a4a40f7f4
commit
5e7bec0aa7
3
packages/portal-watch/.gitignore
vendored
Normal file
3
packages/portal-watch/.gitignore
vendored
Normal file
@ -0,0 +1,3 @@
|
||||
node_modules
|
||||
.DS_Store
|
||||
npm-debug.log
|
173
packages/portal-watch/lib/index.js
Normal file
173
packages/portal-watch/lib/index.js
Normal 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();
|
||||
}
|
||||
};
|
22
packages/portal-watch/package.json
Normal file
22
packages/portal-watch/package.json
Normal 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"
|
||||
}
|
||||
}
|
49
packages/portal-watch/test/index.js
Normal file
49
packages/portal-watch/test/index.js
Normal 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);
|
||||
});
|
74
yarn.lock
74
yarn.lock
@ -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"
|
||||
|
Loading…
Reference in New Issue
Block a user