From 625463f6abaf40ea50198b2d6949814ae74d15a1 Mon Sep 17 00:00:00 2001 From: Girish Ramakrishnan Date: Thu, 30 Jul 2020 10:21:46 -0700 Subject: [PATCH] export the database before upgrade it's possible that a) backups are completely disabled b) skip backup option is selected when upgrading in the above cases, the dump file is not generated and thus any addon upgrade will fail. to fix, we dump the db fresh for database upgrades. --- CHANGES | 1 - src/addons.js | 43 ++++++++++++++++++++++++++++++++++++++++--- 2 files changed, 40 insertions(+), 4 deletions(-) diff --git a/CHANGES b/CHANGES index fa92c1680..0891de2ff 100644 --- a/CHANGES +++ b/CHANGES @@ -2046,7 +2046,6 @@ * Add note that password reset and invite links expire in 24 hours [5.5.0] -* **IMPORTANT:** DO NOT UPGRADE WITHOUT A BACKUP - PostgreSQL UPGRADE REQUIRES A VALID BACKUP. * postgresql: update to PostgreSQL 11 * postgresql: add citext extension to whitelist for loomio * postgresql: add btree_gist,postgres_fdw extensions for gitlab diff --git a/src/addons.js b/src/addons.js index b4d34c812..d8538d809 100644 --- a/src/addons.js +++ b/src/addons.js @@ -749,6 +749,43 @@ function importDatabase(addon, callback) { // not clear, if repair workflow should be part of addon or per-app appdb.update(app.id, { installationState: apps.ISTATE_ERROR, error: { message: error.message } }, iteratorCallback); }); + }, function (error) { + safe.fs.unlinkSync(path.join(paths.ADDON_CONFIG_DIR, `exported-${addon}`)); // clean up for future migrations + + callback(error); + }); + }); +} + +function exportDatabase(addon, callback) { + assert.strictEqual(typeof addon, 'string'); + assert.strictEqual(typeof callback, 'function'); + + debug(`exportDatabase: Exporting ${addon}`); + + if (fs.existsSync(path.join(paths.ADDON_CONFIG_DIR, `exported-${addon}`))) { + debug(`exportDatabase: Already exported addon ${addon} in previous run`); + return callback(null); + } + + appdb.getAll(function (error, apps) { + if (error) return callback(error); + + async.eachSeries(apps, function iterator (app, iteratorCallback) { + if (!app.manifest.adddons || !(addon in app.manifest.addons)) return iteratorCallback(); // app doesn't use the addon + + debug(`exportDatabase: Exporting addon ${addon} of app ${app.id}`); + + ADDONS[addon].backup(app, app.manifest.addons[addon], function (error) { + if (error) { + debug(`exportDatabase: Error exporting ${addon} of app ${app.id}.`, error); + return iteratorCallback(error); + } + + safe.fs.writeFileSync(path.join(paths.ADDON_CONFIG_DIR, `exported-${addon}`), '', 'utf8'); + + shell.sudo('exportDatabase', [ RMADDONDIR_CMD, addon ], {}, iteratorCallback); + }); }, callback); }); } @@ -1131,7 +1168,7 @@ function startMysql(existingInfra, callback) { const upgrading = existingInfra.version !== 'none' && requiresUpgrade(existingInfra.images.mysql.tag, tag); if (upgrading) debug('startMysql: mysql will be upgraded'); - const upgradeFunc = upgrading ? shell.sudo.bind(null, 'startMysql', [ RMADDONDIR_CMD, 'mysql' ], {}) : (next) => next(); + const upgradeFunc = upgrading ? exportDatabase.bind(null, 'mysql') : (next) => next(); upgradeFunc(function (error) { if (error) return callback(error); @@ -1348,7 +1385,7 @@ function startPostgresql(existingInfra, callback) { const upgrading = existingInfra.version !== 'none' && requiresUpgrade(existingInfra.images.postgresql.tag, tag); if (upgrading) debug('startPostgresql: postgresql will be upgraded'); - const upgradeFunc = upgrading ? shell.sudo.bind(null, 'startPostgresql', [ RMADDONDIR_CMD, 'postgresql' ], {}) : (next) => next(); + const upgradeFunc = upgrading ? exportDatabase.bind(null, 'postgresql') : (next) => next(); upgradeFunc(function (error) { if (error) return callback(error); @@ -1562,7 +1599,7 @@ function startMongodb(existingInfra, callback) { const upgrading = existingInfra.version !== 'none' && requiresUpgrade(existingInfra.images.mongodb.tag, tag); if (upgrading) debug('startMongodb: mongodb will be upgraded'); - const upgradeFunc = upgrading ? shell.sudo.bind(null, 'startMongodb', [ RMADDONDIR_CMD, 'mongodb' ], {}) : (next) => next(); + const upgradeFunc = upgrading ? exportDatabase.bind(null, 'mongodb') : (next) => next(); upgradeFunc(function (error) { if (error) return callback(error);