0
Fork 0
mirror of https://github.com/TryGhost/Ghost.git synced 2025-03-11 02:12:21 -05:00

fix: enable maintenance mode only if there is an upgrade to perform (#7129)

refs #7125
- create isDatabaseOutOfDate fn for initial migration update checks
- only set maintenance.enabled to true if migration needs to happen
This commit is contained in:
Katharina Irrgang 2016-07-26 10:56:07 +02:00 committed by Hannah Wolfe
parent d08926c347
commit 417b9b6b7c
4 changed files with 131 additions and 82 deletions

View file

@ -11,7 +11,7 @@ var Promise = require('bluebird'),
updateDatabaseSchema,
migrateToDatabaseVersion,
update, logger;
execute, logger, isDatabaseOutOfDate;
// @TODO: remove me asap!
logger = {
@ -77,7 +77,7 @@ migrateToDatabaseVersion = function migrateToDatabaseVersion(version, logger, mo
* ## Update
* Does a backup, then updates the database and fixtures
*/
update = function update(options) {
execute = function execute(options) {
options = options || {};
var fromVersion = options.fromVersion,
@ -90,40 +90,51 @@ update = function update(options) {
}
};
fromVersion = forceMigration ? versioning.canMigrateFromVersion : fromVersion;
// Figure out which versions we're updating through.
// This shouldn't include the from/current version (which we're already on)
versionsToUpdate = versioning.getMigrationVersions(fromVersion, toVersion).slice(1);
return backup(logger)
.then(function () {
return Promise.mapSeries(versionsToUpdate, function (versionToUpdate) {
return migrateToDatabaseVersion(versionToUpdate, logger, modelOptions);
});
})
.then(function () {
logger.info('Finished!');
});
};
isDatabaseOutOfDate = function isDatabaseOutOfDate(options) {
options = options || {};
var fromVersion = options.fromVersion,
toVersion = options.toVersion,
forceMigration = options.forceMigration;
// CASE: current database version is lower then we support
if (fromVersion < versioning.canMigrateFromVersion) {
return Promise.reject(new errors.DatabaseVersion(
return {error: new errors.DatabaseVersion(
i18n.t('errors.data.versioning.index.cannotMigrate.error'),
i18n.t('errors.data.versioning.index.cannotMigrate.context'),
i18n.t('common.seeLinkForInstructions', {link: 'http://support.ghost.org/how-to-upgrade/'})
));
)};
}
// CASE: the database exists but is out of date
else if (fromVersion < toVersion || forceMigration) {
fromVersion = forceMigration ? versioning.canMigrateFromVersion : fromVersion;
// Figure out which versions we're updating through.
// This shouldn't include the from/current version (which we're already on)
versionsToUpdate = versioning.getMigrationVersions(fromVersion, toVersion).slice(1);
return backup(logger)
.then(function () {
return Promise.mapSeries(versionsToUpdate, function (versionToUpdate) {
return migrateToDatabaseVersion(versionToUpdate, logger, modelOptions);
});
})
.then(function () {
logger.info('Finished!');
});
return {migrate: true};
}
// CASE: database is up-to-date
else if (fromVersion === toVersion) {
return Promise.resolve();
return {migrate: false};
}
// CASE: we don't understand the version
else {
return Promise.reject(new errors.DatabaseVersion(i18n.t('errors.data.versioning.index.dbVersionNotRecognized')));
return {error: new errors.DatabaseVersion(i18n.t('errors.data.versioning.index.dbVersionNotRecognized'))};
}
};
module.exports = update;
exports.execute = execute;
exports.isDatabaseOutOfDate = isDatabaseOutOfDate;

View file

@ -69,18 +69,28 @@ function init(options) {
}).then(function () {
return versioning.getDatabaseVersion()
.then(function (currentVersion) {
var maintenanceState = config.maintenance.enabled || false;
config.maintenance.enabled = true;
migrations.update({
var response = migrations.update.isDatabaseOutOfDate({
fromVersion: currentVersion,
toVersion: versioning.getNewestDatabaseVersion(),
forceMigration: process.env.FORCE_MIGRATION
}).then(function () {
config.maintenance.enabled = maintenanceState;
}).catch(function (err) {
errors.logErrorAndExit(err, err.context, err.help);
});
}), maintenanceState;
if (response.migrate === true) {
maintenanceState = config.maintenance.enabled || false;
config.maintenance.enabled = true;
migrations.update.execute({
fromVersion: currentVersion,
toVersion: versioning.getNewestDatabaseVersion(),
forceMigration: process.env.FORCE_MIGRATION
}).then(function () {
config.maintenance.enabled = maintenanceState;
}).catch(function (err) {
errors.logErrorAndExit(err, err.context, err.help);
});
} else if (response.error) {
return Promise.reject(response.error);
}
})
.catch(function (err) {
if (err instanceof errors.DatabaseNotPopulated) {

View file

@ -6,7 +6,7 @@ var should = require('should'),
crypto = require('crypto'),
fs = require('fs'),
// Stuff we are testing
// Stuff we are testing
db = require('../../server/data/db'),
errors = require('../../server/errors'),
models = require('../../server/models'),
@ -204,6 +204,45 @@ describe('Migrations', function () {
});
});
describe('isDatabaseOutOfDate', function () {
var updateDatabaseSchemaStub, updateDatabaseSchemaReset, versionsSpy;
beforeEach(function () {
versionsSpy = sandbox.spy(schema.versioning, 'getMigrationVersions');
// For these tests, stub out the actual update task
updateDatabaseSchemaStub = sandbox.stub().returns(new Promise.resolve());
updateDatabaseSchemaReset = update.__set__('updateDatabaseSchema', updateDatabaseSchemaStub);
});
afterEach(function () {
updateDatabaseSchemaReset();
});
it('should throw error if versions are too old', function () {
var response = update.isDatabaseOutOfDate({fromVersion: '000', toVersion: '002'});
updateDatabaseSchemaStub.calledOnce.should.be.false();
(response.error instanceof errors.DatabaseVersion).should.eql(true);
});
it('should just return if versions are the same', function () {
var migrateToDatabaseVersionStub = sandbox.stub().returns(new Promise.resolve()),
migrateToDatabaseVersionReset = update.__set__('migrateToDatabaseVersion', migrateToDatabaseVersionStub),
response = update.isDatabaseOutOfDate({fromVersion: '004', toVersion: '004'});
response.migrate.should.eql(false);
versionsSpy.calledOnce.should.be.false();
migrateToDatabaseVersionStub.callCount.should.eql(0);
migrateToDatabaseVersionReset();
});
it('should throw an error if the database version is higher than the default', function () {
var response = update.isDatabaseOutOfDate({fromVersion: '010', toVersion: '004'});
updateDatabaseSchemaStub.calledOnce.should.be.false();
(response.error instanceof errors.DatabaseVersion).should.eql(true);
});
});
describe('Update', function () {
describe('Update function', function () {
var resetBackup, backupStub, fixturesStub, setDbStub, versionsSpy, tasksSpy, transactionStub, transaction;
@ -252,7 +291,7 @@ describe('Migrations', function () {
it('should attempt to run the pre & post update tasks correctly', function (done) {
// Execute
update({fromVersion: '100', toVersion: '102'}).then(function () {
update.execute({fromVersion: '100', toVersion: '102'}).then(function () {
// getMigrationVersions should be called with the correct versions
versionsSpy.calledOnce.should.be.true();
versionsSpy.calledWith('100', '102').should.be.true();
@ -288,21 +327,11 @@ describe('Migrations', function () {
}).catch(done);
});
it('should throw error if versions are too old', function (done) {
update({fromVersion: '000', toVersion: '002'}).then(function () {
done(new Error('expected database version too old error'));
}).catch(function (err) {
updateDatabaseSchemaStub.calledOnce.should.be.false();
(err instanceof errors.DatabaseVersion).should.eql(true);
done();
});
});
it('should upgrade from minimum version, if force migration is set', function (done) {
var migrateToDatabaseVersionStub = sandbox.stub().returns(new Promise.resolve()),
migrateToDatabaseVersionReset = update.__set__('migrateToDatabaseVersion', migrateToDatabaseVersionStub);
update({fromVersion: '005', toVersion: '006', forceMigration: true}).then(function () {
update.execute({fromVersion: '005', toVersion: '006', forceMigration: true}).then(function () {
// getMigrationVersions should be called with the correct versions
versionsSpy.calledOnce.should.be.true();
versionsSpy.calledWith('003', '006').should.be.true();
@ -323,7 +352,7 @@ describe('Migrations', function () {
var migrateToDatabaseVersionStub = sandbox.stub().returns(new Promise.resolve()),
migrateToDatabaseVersionReset = update.__set__('migrateToDatabaseVersion', migrateToDatabaseVersionStub);
update({fromVersion: '004', toVersion: '005'}).then(function () {
update.execute({fromVersion: '004', toVersion: '005'}).then(function () {
versionsSpy.calledOnce.should.be.true();
versionsSpy.calledWith('004', '005').should.be.true();
versionsSpy.returned(['004', '005']).should.be.true();
@ -341,7 +370,7 @@ describe('Migrations', function () {
var migrateToDatabaseVersionStub = sandbox.stub().returns(new Promise.resolve()),
migrateToDatabaseVersionReset = update.__set__('migrateToDatabaseVersion', migrateToDatabaseVersionStub);
update({fromVersion: '004', toVersion: '010'}).then(function () {
update.execute({fromVersion: '004', toVersion: '010'}).then(function () {
versionsSpy.calledOnce.should.be.true();
versionsSpy.calledWith('004', '010').should.be.true();
versionsSpy.returned(['004', '005', '006', '007', '008', '009', '010']).should.be.true();
@ -356,24 +385,11 @@ describe('Migrations', function () {
}).catch(done);
});
it('should just return if versions are the same', function (done) {
var migrateToDatabaseVersionStub = sandbox.stub().returns(new Promise.resolve()),
migrateToDatabaseVersionReset = update.__set__('migrateToDatabaseVersion', migrateToDatabaseVersionStub);
update({fromVersion: '004', toVersion: '004'}).then(function () {
versionsSpy.calledOnce.should.be.false();
migrateToDatabaseVersionStub.callCount.should.eql(0);
migrateToDatabaseVersionReset();
done();
}).catch(done);
});
it('should do an UPDATE even if versions are the same, when FORCE_MIGRATION set', function (done) {
var migrateToDatabaseVersionStub = sandbox.stub().returns(new Promise.resolve()),
migrateToDatabaseVersionReset = update.__set__('migrateToDatabaseVersion', migrateToDatabaseVersionStub);
update({fromVersion: '004', toVersion: '004', forceMigration: true}).then(function () {
update.execute({fromVersion: '004', toVersion: '004', forceMigration: true}).then(function () {
versionsSpy.calledOnce.should.be.true();
versionsSpy.calledWith('003', '004').should.be.true();
versionsSpy.returned(['003', '004']).should.be.true();
@ -385,16 +401,6 @@ describe('Migrations', function () {
done();
}).catch(done);
});
it('should throw an error if the database version is higher than the default', function (done) {
update({fromVersion: '010', toVersion: '004'}).then(function () {
done(new Error('expected database version too old error'));
}).catch(function (err) {
updateDatabaseSchemaStub.calledOnce.should.be.false();
(err instanceof errors.DatabaseVersion).should.eql(true);
done();
});
});
});
describe('Update to 004', function () {
@ -407,7 +413,7 @@ describe('Migrations', function () {
sequenceStub.returns(Promise.resolve([]));
// Execute
update({fromVersion: '003', toVersion: '004'}).then(function () {
update.execute({fromVersion: '003', toVersion: '004'}).then(function () {
versionsSpy.calledOnce.should.be.true();
versionsSpy.calledWith('003', '004').should.be.true();
@ -888,7 +894,7 @@ describe('Migrations', function () {
sequenceStub.returns(Promise.resolve([]));
// Execute
update({fromVersion: '004', toVersion: '005'}).then(function () {
update.execute({fromVersion: '004', toVersion: '005'}).then(function () {
versionsSpy.calledOnce.should.be.true();
versionsSpy.calledWith('004', '005').should.be.true();
versionsSpy.returned(['004', '005']).should.be.true();

View file

@ -51,8 +51,8 @@ describe('server bootstrap', function () {
});
describe('migrations', function () {
it('database does not exist', function (done) {
sandbox.stub(migration, 'update').returns(Promise.resolve());
it('database does not exist: expect database population', function (done) {
sandbox.stub(migration.update, 'isDatabaseOutOfDate').returns({migrate:false});
sandbox.stub(versioning, 'getDatabaseVersion', function () {
return Promise.reject();
@ -61,7 +61,7 @@ describe('server bootstrap', function () {
bootstrap()
.then(function () {
migration.populate.calledOnce.should.eql(true);
migration.update.calledOnce.should.eql(false);
migration.update.execute.calledOnce.should.eql(false);
models.Settings.populateDefaults.callCount.should.eql(1);
config.maintenance.enabled.should.eql(false);
done();
@ -71,11 +71,9 @@ describe('server bootstrap', function () {
});
});
it('database does exist', function (done) {
sandbox.stub(migration, 'update', function () {
config.maintenance.enabled.should.eql(true);
return Promise.resolve();
});
it('database does exist: expect no update', function (done) {
sandbox.stub(migration.update, 'isDatabaseOutOfDate').returns({migrate:false});
sandbox.spy(migration.update, 'execute');
sandbox.stub(versioning, 'getDatabaseVersion', function () {
return Promise.resolve('006');
@ -83,8 +81,32 @@ describe('server bootstrap', function () {
bootstrap()
.then(function () {
migration.update.calledOnce.should.eql(true);
migration.update.calledWith({
migration.update.isDatabaseOutOfDate.calledOnce.should.eql(true);
migration.update.execute.called.should.eql(false);
models.Settings.populateDefaults.callCount.should.eql(1);
migration.populate.calledOnce.should.eql(false);
done();
})
.catch(function (err) {
done(err);
});
});
it('database does exist: expect update', function (done) {
sandbox.stub(migration.update, 'isDatabaseOutOfDate').returns({migrate:true});
sandbox.stub(migration.update, 'execute').returns(Promise.resolve());
sandbox.stub(versioning, 'getDatabaseVersion', function () {
return Promise.resolve('006');
});
bootstrap()
.then(function () {
migration.update.isDatabaseOutOfDate.calledOnce.should.eql(true);
migration.update.execute.calledOnce.should.eql(true);
migration.update.execute.calledWith({
fromVersion: '006',
toVersion: '006',
forceMigration: undefined