diff --git a/CHANGES b/CHANGES index 059aadd16..e3784b2d3 100644 --- a/CHANGES +++ b/CHANGES @@ -1932,4 +1932,5 @@ * graphs: add app graphs * older encrypted backups cannot be used in this version * Add backup listing UI +* stopping an app will stop dependent services diff --git a/src/addons.js b/src/addons.js index bd0af47ef..da1672a32 100644 --- a/src/addons.js +++ b/src/addons.js @@ -7,6 +7,9 @@ exports = module.exports = { getServiceLogs: getServiceLogs, restartService: restartService, + startAppServices, + stopAppServices, + startServices: startServices, updateServiceConfig: updateServiceConfig, @@ -213,6 +216,8 @@ const SERVICES = { const APP_SERVICES = { redis: { status: (instance, done) => containerStatus(`redis-${instance}`, 'CLOUDRON_REDIS_TOKEN', done), + start: (instance, done) => docker.startContainer(`redis-${instance}`, done), + stop: (instance, done) => docker.stopContainer(`redis-${instance}`, done), restart: (instance, done) => restartContainer(`redis-${instance}`, done), defaultMemoryLimit: 150 * 1024 * 1024 } @@ -554,6 +559,42 @@ function restartService(id, callback) { } } +// in the future, we can refcount and lazy start global services +function startAppServices(app, callback) { + assert.strictEqual(typeof app, 'object'); + assert.strictEqual(typeof callback, 'function'); + + const instance = app.id; + async.eachSeries(Object.keys(app.manifest.addons || {}), function (addon, iteratorDone) { + if (!(addon in APP_SERVICES)) return iteratorDone(); + + APP_SERVICES[addon].start(instance, function (error) { // assume addons name is service name + // error ignored because we don't want "start app" to error. use can fix it from Services + if (error) debug(`startAppServices: ${addon}:${instance}`, error); + + iteratorDone(); + }); + }, callback); +} + +// in the future, we can refcount and stop global services as well +function stopAppServices(app, callback) { + assert.strictEqual(typeof app, 'object'); + assert.strictEqual(typeof callback, 'function'); + + const instance = app.id; + async.eachSeries(Object.keys(app.manifest.addons || {}), function (addon, iteratorDone) { + if (!(addon in APP_SERVICES)) return iteratorDone(); + + APP_SERVICES[addon].stop(instance, function (error) { // assume addons name is service name + // error ignored because we don't want "start app" to error. use can fix it from Services + if (error) debug(`stopAppServices: ${addon}:${instance}`, error); + + iteratorDone(); + }); + }, callback); +} + function waitForContainer(containerName, tokenEnvName, callback) { assert.strictEqual(typeof containerName, 'string'); assert.strictEqual(typeof tokenEnvName, 'string'); diff --git a/src/apptask.js b/src/apptask.js index b623765d8..29b290265 100644 --- a/src/apptask.js +++ b/src/apptask.js @@ -899,7 +899,10 @@ function start(app, args, progressCallback, callback) { assert.strictEqual(typeof callback, 'function'); async.series([ - progressCallback.bind(null, { percent: 20, message: 'Starting container' }), + progressCallback.bind(null, { percent: 10, message: 'Starting app services' }), + addons.startAppServices.bind(null, app), + + progressCallback.bind(null, { percent: 35, message: 'Starting container' }), docker.startContainer.bind(null, app.id), // stopped apps do not renew certs. currently, we don't do DNS to not overwrite existing user settings @@ -927,6 +930,9 @@ function stop(app, args, progressCallback, callback) { progressCallback.bind(null, { percent: 20, message: 'Stopping container' }), docker.stopContainers.bind(null, app.id), + progressCallback.bind(null, { percent: 50, message: 'Stopping app services' }), + addons.stopAppServices.bind(null, app), + progressCallback.bind(null, { percent: 100, message: 'Done' }), updateApp.bind(null, app, { installationState: apps.ISTATE_INSTALLED, error: null, health: null }) ], function seriesDone(error) {