diff --git a/src/platform.js b/src/platform.js index 0b62a1d10..33f94d0d6 100644 --- a/src/platform.js +++ b/src/platform.js @@ -45,18 +45,15 @@ function start(callback) { if (!existingInfra) existingInfra = { version: 'corrupt' }; } + settings.events.on(settings.PLATFORM_CONFIG_KEY, updateAddons); + // short-circuit for the restart case if (_.isEqual(infra, existingInfra)) { debug('platform is uptodate at version %s', infra.version); - updateAddons(function (error) { - if (error) return callback(error); + emitPlatformReady(); - emitPlatformReady(); - - callback(); - }); - return; + return callback(); } debug('Updating infrastructure from %s to %s', existingInfra.version, infra.version); @@ -69,8 +66,7 @@ function start(callback) { startAddons.bind(null, existingInfra), removeOldImages, startApps.bind(null, existingInfra), - fs.writeFile.bind(fs, paths.INFRA_VERSION_FILE, JSON.stringify(infra, null, 4)), - updateAddons + fs.writeFile.bind(fs, paths.INFRA_VERSION_FILE, JSON.stringify(infra, null, 4)) ], function (error) { if (error) return callback(error); @@ -89,22 +85,19 @@ function stop(callback) { taskmanager.pauseTasks(callback); } -function updateAddons(callback) { - settings.getPlatformConfig(function (error, platformConfig) { - if (error) return callback(error); +function updateAddons(platformConfig, callback) { + callback = callback || NOOP_CALLBACK; - for (var containerName of [ 'mysql', 'postgresql', 'mail', 'mongodb' ]) { - const containerConfig = platformConfig[containerName]; - if (!containerConfig) continue; + // TODO: this should possibly also rollback memory to default + async.eachSeries([ 'mysql', 'postgresql', 'mail', 'mongodb' ], function iterator(containerName, iteratorCallback) { + const containerConfig = platformConfig[containerName]; + if (!containerConfig) return iteratorCallback(); - if (!containerConfig.memory || !containerConfig.memorySwap) continue; + if (!containerConfig.memory || !containerConfig.memorySwap) return iteratorCallback(); - const cmd = `docker update --memory ${containerConfig.memory} --memory-swap ${containerConfig.memorySwap} ${containerName}`; - shell.execSync(`update${containerName}`, cmd); - } - - callback(); - }); + const args = `update --memory ${containerConfig.memory} --memory-swap ${containerConfig.memorySwap} ${containerName}`.split(' '); + shell.exec(`update${containerName}`, '/usr/bin/docker', args, { }, iteratorCallback); + }, callback); } function emitPlatformReady() { @@ -291,7 +284,15 @@ function startAddons(existingInfra, callback) { debug('startAddons: existing infra. incremental addon create %j', startFuncs.map(function (f) { return f.name; })); } - async.series(startFuncs, callback); + async.series(startFuncs, function (error) { + if (error) return callback(error); + + settings.getPlatformConfig(function (error, platformConfig) { + if (error) return callback(error); + + updateAddons(platformConfig, callback); + }); + }); } function startApps(existingInfra, callback) { diff --git a/src/routes/settings.js b/src/routes/settings.js index 03cea6df4..5d59df473 100644 --- a/src/routes/settings.js +++ b/src/routes/settings.js @@ -20,7 +20,10 @@ exports = module.exports = { setTimeZone: setTimeZone, getAppstoreConfig: getAppstoreConfig, - setAppstoreConfig: setAppstoreConfig + setAppstoreConfig: setAppstoreConfig, + + getPlatformConfig: getPlatformConfig, + setPlatformConfig: setPlatformConfig }; var assert = require('assert'), @@ -169,6 +172,34 @@ function setBackupConfig(req, res, next) { }); } +function getPlatformConfig(req, res, next) { + settings.getPlatformConfig(function (error, config) { + if (error) return next(new HttpError(500, error)); + + next(new HttpSuccess(200, config)); + }); +} + +function setPlatformConfig(req, res, next) { + assert.strictEqual(typeof req.body, 'object'); + + for (let addon of [ 'mysql', 'postgresql', 'mail', 'mongodb' ]) { + if (!(addon in req.body)) continue; + if (typeof req.body[addon] !== 'object') return next(new HttpError(400, 'addon config must be an object')); + + if (typeof req.body[addon].memory !== 'number') return next(new HttpError(400, 'memory must be a number')); + if (typeof req.body[addon].memorySwap !== 'number') return next(new HttpError(400, 'memorySwap must be a number')); + } + + settings.setPlatformConfig(req.body, function (error) { + if (error && error.reason === SettingsError.BAD_FIELD) return next(new HttpError(400, error.message)); + if (error && error.reason === SettingsError.EXTERNAL_ERROR) return next(new HttpError(402, error.message)); + if (error) return next(new HttpError(500, error)); + + next(new HttpSuccess(200, {})); + }); +} + function getAppstoreConfig(req, res, next) { settings.getAppstoreConfig(function (error, result) { if (error) return next(new HttpError(500, error)); diff --git a/src/server.js b/src/server.js index ef840c5b1..93ddc64ca 100644 --- a/src/server.js +++ b/src/server.js @@ -224,6 +224,8 @@ function initializeExpressSync() { router.post('/api/v1/settings/cloudron_avatar', settingsScope, multipart, routes.settings.setCloudronAvatar); router.get ('/api/v1/settings/backup_config', settingsScope, routes.settings.getBackupConfig); router.post('/api/v1/settings/backup_config', settingsScope, routes.settings.setBackupConfig); + router.get ('/api/v1/settings/platform_config', settingsScope, routes.settings.getPlatformConfig); + router.post('/api/v1/settings/platform_config', settingsScope, routes.settings.setPlatformConfig); router.get ('/api/v1/settings/time_zone', settingsScope, routes.settings.getTimeZone); router.post('/api/v1/settings/time_zone', settingsScope, routes.settings.setTimeZone); diff --git a/src/settings.js b/src/settings.js index ec399fb5b..59163c831 100644 --- a/src/settings.js +++ b/src/settings.js @@ -390,6 +390,11 @@ function getPlatformConfig(callback) { function setPlatformConfig(platformConfig, callback) { assert.strictEqual(typeof callback, 'function'); + for (let addon of [ 'mysql', 'postgresql', 'mail', 'mongodb' ]) { + if (!platformConfig[addon]) continue; + if (platformConfig[addon].memorySwap < platformConfig[addon].memory) return callback(new SettingsError(SettingsError.BAD_FIELD, 'memorySwap must be larger than memory')); + } + settingsdb.set(exports.PLATFORM_CONFIG_KEY, JSON.stringify(platformConfig), function (error) { if (error) return callback(new SettingsError(SettingsError.INTERNAL_ERROR, error));