1
0
mirror of https://github.com/yldio/copilot.git synced 2024-11-14 23:30:05 +02:00

feat(portal-data): can stop services

This commit is contained in:
geek 2017-06-08 13:43:24 -05:00 committed by Sérgio Ramos
parent 7e01748e8a
commit 1e157641b2
7 changed files with 302 additions and 15 deletions

View File

@ -120,6 +120,7 @@ enum InstanceStatus {
PAUSED
EXITED
DELETED
STOPPED
}
type Instance {

View File

@ -39,4 +39,16 @@ module.exports = class DockerComposeClient extends EventEmitter {
return this._invoke('scale', options, manifest, cb);
}
config({ projectName, services, manifest }, cb) {
const options = {
// eslint-disable-next-line camelcase
project_name: projectName,
services: services.map(service => ({
name: service
}))
};
return this._invoke('config', options, manifest, cb);
}
};

View File

@ -69,6 +69,30 @@ const server = new Server({
projectName: options.project_name,
services: options.services
});
},
// eslint-disable-next-line object-shorthand
config: function(options, manifest, fn) {
if (typeof options !== 'object') {
return fn(new Error('Expected options'));
}
if (typeof options.project_name !== 'string') {
return fn(new Error('Expected project name'));
}
if (typeof manifest !== 'string') {
return fn(new Error('Expected manifest'));
}
try {
safeLoad(manifest);
} catch (err) {
return fn(err);
}
fn(null, {
projectName: options.project_name
});
}
});
@ -123,6 +147,30 @@ it('scale()', done => {
);
});
it('config()', done => {
const manifest = `
hello:
image: hello-world:latest
world:
image: consul:latest
node:
image: node:latest
`;
client.config(
{
projectName,
services: ['hello'],
manifest
},
(err, res) => {
expect(err).to.not.exist();
expect(res).to.exist();
done();
}
);
});
it('handles errors', done => {
client.once('error', err => {
expect(err).to.exist();

View File

@ -2,6 +2,7 @@
const EventEmitter = require('events');
const DockerClient = require('docker-compose-client');
const Dockerode = require('dockerode');
const Hoek = require('hoek');
const Penseur = require('penseur');
const VAsync = require('vasync');
@ -14,7 +15,7 @@ const internals = {
db: {
test: false
},
dockerHost: 'tcp://0.0.0.0:4242'
dockerComposeHost: 'tcp://0.0.0.0:4242'
},
tables: {
'portals': { id: { type: 'uuid' }, primary: 'id', secondary: false, purge: false },
@ -36,9 +37,10 @@ module.exports = class Data extends EventEmitter {
// Penseur will assert that the options are correct
this._db = new Penseur.Db(settings.name, settings.db);
this._docker = new DockerClient(settings.dockerHost);
this._dockerCompose = new DockerClient(settings.dockerComposeHost);
this._docker = new Dockerode(settings.docker);
this._docker.on('error', (err) => {
this._dockerCompose.on('error', (err) => {
this.emit('error', err);
});
}
@ -489,7 +491,7 @@ module.exports = class Data extends EventEmitter {
manifest: manifest.raw
};
options.services[service.name] = replicas;
this._docker.scale(options, (err) => {
this._dockerCompose.scale(options, (err) => {
if (err) {
return cb(err);
}
@ -523,7 +525,7 @@ module.exports = class Data extends EventEmitter {
setImmediate(() => {
let isHandled = false;
this._docker.provision({ projectName: deploymentGroup.name, manifest: clientManifest.raw }, (err, res) => {
this._dockerCompose.provision({ projectName: deploymentGroup.name, manifest: clientManifest.raw }, (err, res) => {
if (err) {
this.emit('error', err);
return;
@ -538,7 +540,8 @@ module.exports = class Data extends EventEmitter {
const options = {
manifestServices: manifest.json.services || manifest.json,
deploymentGroup,
manifestId: key
manifestId: key,
provisionRes: res
};
this.provisionServices(options);
});
@ -575,7 +578,7 @@ module.exports = class Data extends EventEmitter {
// services
provisionServices ({ manifestServices, deploymentGroup, manifestId }, cb) {
provisionServices ({ manifestServices, deploymentGroup, manifestId, provisionRes }, cb) {
// insert instance information
// insert service information
// insert version information -- will update deploymentGroups
@ -591,7 +594,7 @@ module.exports = class Data extends EventEmitter {
const manifestService = manifestServices[serviceName];
const clientInstance = {
name: serviceName,
machineId: `${deploymentGroup.name}_${serviceName}_1`,
machineId: provisionRes[serviceName].plan.containers[0].id,
status: 'CREATED'
};
this.createInstance(clientInstance, (err, createdInstance) => {
@ -604,7 +607,8 @@ module.exports = class Data extends EventEmitter {
name: serviceName,
slug: serviceName,
deploymentGroupId: deploymentGroup.id,
instances: [createdInstance]
instances: [createdInstance],
info: provisionRes[serviceName]
};
this.createService(clientService, (err, createdService) => {
@ -750,6 +754,62 @@ module.exports = class Data extends EventEmitter {
});
}
stopServices ({ ids }, cb) {
this._db.services.get(ids, (err, services) => {
if (err) {
return cb(err);
}
if (!services || !services.length) {
return cb();
}
let instanceIds = [];
services.forEach((service) => {
instanceIds = instanceIds.concat(service.instance_ids);
});
VAsync.forEachParallel({
func: (instanceId, next) => {
this._db.instances.get(instanceId, (err, instance) => {
if (err) {
return next(err);
}
const container = this._docker.getContainer(instance.machine_id);
container.stop((err) => {
if (err) {
return next(err);
}
this.updateInstance({ id: instance.id, status: 'STOPPED' }, next);
});
});
},
inputs: instanceIds
}, (err, results) => {
if (err) {
return cb(err);
}
this.getServices({ ids }, cb);
});
});
}
startServices ({ ids }, cb) {
}
restartServices ({ ids }, cb) {
}
deleteServices ({ ids }, cb) {
}
// instances
@ -774,6 +834,16 @@ module.exports = class Data extends EventEmitter {
});
}
updateInstance ({ id, status }, cb) {
this._db.instances.update(id, { status }, (err, instance) => {
if (err) {
return cb(err);
}
cb(null, instance ? Transform.fromInstance(instance) : {});
});
}
// packages

View File

@ -17,6 +17,7 @@
"license": "MPL-2.0",
"dependencies": {
"docker-compose-client": "^1.0.7",
"dockerode": "^2.4.3",
"hoek": "^4.1.1",
"penseur": "^7.8.1",
"vasync": "^1.6.4",

View File

@ -6,16 +6,32 @@ const Code = require('code');
const Lab = require('lab');
const PortalData = require('../');
const lab = exports.lab = Lab.script();
const afterEach = lab.afterEach;
const it = lab.it;
const describe = lab.describe;
const expect = Code.expect;
const internals = {
options: { name: 'test', db: { test: true } },
options: {
name: 'test',
db: { test: true }
},
composeFile: Fs.readFileSync(Path.join(__dirname, 'docker-compose.yml')).toString()
};
afterEach((done) => {
const data = new PortalData({ name: 'test', db: { test: true } });
data.connect(() => {
data._db.r.dbDrop('test').run(data._db._connection, () => {
done();
});
});
});
describe('connect()', () => {
it('connects to the database', (done) => {
const data = new PortalData(internals.options);
@ -688,3 +704,38 @@ describe.skip('scale()', () => {
});
});
});
// skipping by default since it takes so long
describe.skip('stopServices()', () => {
it('stops all instances of a service', { timeout: 180000 }, (done) => {
const data = new PortalData(internals.options);
data.connect((err) => {
expect(err).to.not.exist();
data.createDeploymentGroup({ name: 'something' }, (err, deploymentGroup) => {
expect(err).to.not.exist();
const clientManifest = {
deploymentGroupId: deploymentGroup.id,
type: 'compose',
format: 'yml',
raw: internals.composeFile
};
data.provisionManifest(clientManifest, (err, manifest) => {
expect(err).to.not.exist();
setTimeout(() => {
data.getDeploymentGroup({ id: deploymentGroup.id }, (err, deploymentGroup) => {
expect(err).to.not.exist();
deploymentGroup.services().then((deploymentGroupServices) => {
data.stopServices({ ids: [deploymentGroupServices[0].id] }, (err, services) => {
expect(err).to.not.exist();
expect(services).to.exist();
done();
});
});
});
}, 80000);
});
});
});
});
});

View File

@ -2,6 +2,13 @@
# yarn lockfile v1
JSONStream@0.10.0:
version "0.10.0"
resolved "https://registry.yarnpkg.com/JSONStream/-/JSONStream-0.10.0.tgz#74349d0d89522b71f30f0a03ff9bd20ca6f12ac0"
dependencies:
jsonparse "0.0.5"
through ">=2.2.7 <3"
acorn-jsx@^3.0.0:
version "3.0.1"
resolved "https://registry.yarnpkg.com/acorn-jsx/-/acorn-jsx-3.0.1.tgz#afdf9488fb1ecefc8348f6fb22f464e32a58b36b"
@ -105,6 +112,12 @@ bindings@~1.2.1:
version "1.2.1"
resolved "https://registry.yarnpkg.com/bindings/-/bindings-1.2.1.tgz#14ad6113812d2d37d72e67b4cacb4bb726505f11"
bl@^1.0.0:
version "1.2.1"
resolved "https://registry.yarnpkg.com/bl/-/bl-1.2.1.tgz#cac328f7bee45730d404b692203fcb590e172d5e"
dependencies:
readable-stream "^2.0.5"
"bluebird@>= 2.3.2 < 3":
version "2.11.0"
resolved "https://registry.yarnpkg.com/bluebird/-/bluebird-2.11.0.tgz#534b9033c022c9579c56ba3b3e5a5caafbb650e1"
@ -212,6 +225,14 @@ concat-stream@^1.5.2:
readable-stream "^2.2.2"
typedarray "^0.0.6"
concat-stream@~1.5.1:
version "1.5.2"
resolved "https://registry.yarnpkg.com/concat-stream/-/concat-stream-1.5.2.tgz#708978624d856af41a5a741defdd261da752c266"
dependencies:
inherits "~2.0.1"
readable-stream "~2.0.0"
typedarray "~0.0.5"
core-util-is@~1.0.0:
version "1.0.2"
resolved "https://registry.yarnpkg.com/core-util-is/-/core-util-is-1.0.2.tgz#b5fd54220aa2bc5ab57aab7140c940754503c1a7"
@ -222,7 +243,7 @@ d@1:
dependencies:
es5-ext "^0.10.9"
debug@^2.1.1:
debug@^2.1.1, debug@^2.6.0:
version "2.6.8"
resolved "https://registry.yarnpkg.com/debug/-/debug-2.6.8.tgz#e731531ca2ede27d188222427da17821d68ff4fc"
dependencies:
@ -259,6 +280,23 @@ docker-compose-client@^1.0.7:
apr-awaitify "^1.0.4"
zerorpc "^0.9.7"
docker-modem@0.3.x:
version "0.3.7"
resolved "https://registry.yarnpkg.com/docker-modem/-/docker-modem-0.3.7.tgz#3f510d09f5d334dc2134228f92bd344671227df4"
dependencies:
JSONStream "0.10.0"
debug "^2.6.0"
readable-stream "~1.0.26-4"
split-ca "^1.0.0"
dockerode@^2.4.3:
version "2.4.3"
resolved "https://registry.yarnpkg.com/dockerode/-/dockerode-2.4.3.tgz#7ec55b492fc9f289e77325ff07c6a3a96021b4f2"
dependencies:
concat-stream "~1.5.1"
docker-modem "0.3.x"
tar-fs "~1.12.0"
doctrine@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/doctrine/-/doctrine-2.0.0.tgz#c73d8d2909d22291e1a007a395804da8b665fe63"
@ -266,6 +304,12 @@ doctrine@^2.0.0:
esutils "^2.0.2"
isarray "^1.0.0"
end-of-stream@^1.0.0, end-of-stream@^1.1.0:
version "1.4.0"
resolved "https://registry.yarnpkg.com/end-of-stream/-/end-of-stream-1.4.0.tgz#7a90d833efda6cfa6eac0f4949dbb0fad3a63206"
dependencies:
once "^1.4.0"
es5-ext@^0.10.14, es5-ext@^0.10.9, es5-ext@~0.10.14:
version "0.10.21"
resolved "https://registry.yarnpkg.com/es5-ext/-/es5-ext-0.10.21.tgz#19a725f9e51d0300bbc1e8e821109fd9daf55925"
@ -636,6 +680,10 @@ is-resolvable@^1.0.0:
dependencies:
tryit "^1.0.1"
isarray@0.0.1:
version "0.0.1"
resolved "https://registry.yarnpkg.com/isarray/-/isarray-0.0.1.tgz#8a18acfca9a8f4177e09abfc6038939b05d1eedf"
isarray@^1.0.0, isarray@~1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/isarray/-/isarray-1.0.0.tgz#bb935d48582cba168c06834957a54a3e07124f11"
@ -682,6 +730,10 @@ jsonify@~0.0.0:
version "0.0.0"
resolved "https://registry.yarnpkg.com/jsonify/-/jsonify-0.0.0.tgz#2c74b6ee41d93ca51b7b5aaee8f503631d252a73"
jsonparse@0.0.5:
version "0.0.5"
resolved "https://registry.yarnpkg.com/jsonparse/-/jsonparse-0.0.5.tgz#330542ad3f0a654665b778f3eb2d9a9fa507ac64"
jsonpointer@^4.0.0:
version "4.0.1"
resolved "https://registry.yarnpkg.com/jsonpointer/-/jsonpointer-4.0.1.tgz#4fd92cb34e0e9db3c89c8622ecf51f9b978c6cb9"
@ -786,7 +838,7 @@ object-assign@^4.0.1, object-assign@^4.1.0:
version "4.1.1"
resolved "https://registry.yarnpkg.com/object-assign/-/object-assign-4.1.1.tgz#2109adc7965887cfc05cbbd442cac8bfbb360863"
once@^1.3.0:
once@^1.3.0, once@^1.3.1, once@^1.4.0:
version "1.4.0"
resolved "https://registry.yarnpkg.com/once/-/once-1.4.0.tgz#583b1aa775961d4b113ac17d9c50baef9dd76bd1"
dependencies:
@ -871,11 +923,18 @@ progress@^1.1.8:
version "1.1.8"
resolved "https://registry.yarnpkg.com/progress/-/progress-1.1.8.tgz#e260c78f6161cdd9b0e56cc3e0a85de17c7a57be"
pump@^1.0.0:
version "1.0.2"
resolved "https://registry.yarnpkg.com/pump/-/pump-1.0.2.tgz#3b3ee6512f94f0e575538c17995f9f16990a5d51"
dependencies:
end-of-stream "^1.1.0"
once "^1.3.1"
radix62@1.x.x:
version "1.0.1"
resolved "https://registry.yarnpkg.com/radix62/-/radix62-1.0.1.tgz#cc2f27a49543b44ddaac712380409354bbe009a5"
readable-stream@^2.2.2:
readable-stream@^2.0.0, readable-stream@^2.0.5, readable-stream@^2.2.2:
version "2.2.9"
resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-2.2.9.tgz#cf78ec6f4a6d1eb43d26488cac97f042e74b7fc8"
dependencies:
@ -887,6 +946,26 @@ readable-stream@^2.2.2:
string_decoder "~1.0.0"
util-deprecate "~1.0.1"
readable-stream@~1.0.26-4:
version "1.0.34"
resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-1.0.34.tgz#125820e34bc842d2f2aaafafe4c2916ee32c157c"
dependencies:
core-util-is "~1.0.0"
inherits "~2.0.1"
isarray "0.0.1"
string_decoder "~0.10.x"
readable-stream@~2.0.0:
version "2.0.6"
resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-2.0.6.tgz#8f90341e68a53ccc928788dacfcd11b36eb9b78e"
dependencies:
core-util-is "~1.0.0"
inherits "~2.0.1"
isarray "~1.0.0"
process-nextick-args "~1.0.6"
string_decoder "~0.10.x"
util-deprecate "~1.0.1"
readline2@^1.0.1:
version "1.0.1"
resolved "https://registry.yarnpkg.com/readline2/-/readline2-1.0.1.tgz#41059608ffc154757b715d9989d199ffbf372e35"
@ -993,6 +1072,10 @@ source-map@^0.4.4:
dependencies:
amdefine ">=0.0.4"
split-ca@^1.0.0:
version "1.0.1"
resolved "https://registry.yarnpkg.com/split-ca/-/split-ca-1.0.1.tgz#6c83aff3692fa61256e0cd197e05e9de157691a6"
sprintf-js@~1.0.2:
version "1.0.3"
resolved "https://registry.yarnpkg.com/sprintf-js/-/sprintf-js-1.0.3.tgz#04e6926f662895354f3dd015203633b857297e2c"
@ -1012,6 +1095,10 @@ string-width@^2.0.0:
is-fullwidth-code-point "^2.0.0"
strip-ansi "^3.0.0"
string_decoder@~0.10.x:
version "0.10.31"
resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-0.10.31.tgz#62e203bc41766c6c28c9fc84301dab1c5310fa94"
string_decoder@~1.0.0:
version "1.0.1"
resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-1.0.1.tgz#62e200f039955a6810d8df0a33ffc0f013662d98"
@ -1047,11 +1134,28 @@ table@^3.7.8:
slice-ansi "0.0.4"
string-width "^2.0.0"
tar-fs@~1.12.0:
version "1.12.0"
resolved "https://registry.yarnpkg.com/tar-fs/-/tar-fs-1.12.0.tgz#a6a80553d8a54c73de1d0ae0e79de77035605e1d"
dependencies:
mkdirp "^0.5.0"
pump "^1.0.0"
tar-stream "^1.1.2"
tar-stream@^1.1.2:
version "1.5.4"
resolved "https://registry.yarnpkg.com/tar-stream/-/tar-stream-1.5.4.tgz#36549cf04ed1aee9b2a30c0143252238daf94016"
dependencies:
bl "^1.0.0"
end-of-stream "^1.0.0"
readable-stream "^2.0.0"
xtend "^4.0.0"
text-table@~0.2.0:
version "0.2.0"
resolved "https://registry.yarnpkg.com/text-table/-/text-table-0.2.0.tgz#7f5ee823ae805207c00af2df4a84ec3fcfa570b4"
through@^2.3.6:
"through@>=2.2.7 <3", through@^2.3.6:
version "2.3.8"
resolved "https://registry.yarnpkg.com/through/-/through-2.3.8.tgz#0dd4c9ffaabc357960b1b724115d7e0e86a2e1f5"
@ -1071,7 +1175,7 @@ type-check@~0.3.2:
dependencies:
prelude-ls "~1.1.2"
typedarray@^0.0.6:
typedarray@^0.0.6, typedarray@~0.0.5:
version "0.0.6"
resolved "https://registry.yarnpkg.com/typedarray/-/typedarray-0.0.6.tgz#867ac74e3864187b1d3d47d996a78ec5c8830777"