Skip to content

Commit

Permalink
SONARCLOUD-75 Automatic run of DB migrations on blue/green deployments
Browse files Browse the repository at this point in the history
  • Loading branch information
Simon Brandhof authored and SonarTech committed Jun 14, 2018
1 parent df2f2c4 commit 7db084d
Show file tree
Hide file tree
Showing 6 changed files with 78 additions and 35 deletions.
Expand Up @@ -48,20 +48,19 @@ public void start() {
throw MessageException.of("Database was upgraded to a more recent of SonarQube. Backup must probably be restored or db settings are incorrect."); throw MessageException.of("Database was upgraded to a more recent of SonarQube. Backup must probably be restored or db settings are incorrect.");
} }
if (status == DatabaseVersion.Status.REQUIRES_UPGRADE) { if (status == DatabaseVersion.Status.REQUIRES_UPGRADE) {
if (configuration.getBoolean(ProcessProperties.Property.BLUE_GREEN_ENABLED.getKey()).orElse(false)) {
throw new IllegalStateException("Blue/green deployment is not supported. Database must be upgraded.");
}

Optional<Long> currentVersion = this.version.getVersion(); Optional<Long> currentVersion = this.version.getVersion();
if (currentVersion.isPresent() && currentVersion.get() < DatabaseVersion.MIN_UPGRADE_VERSION) { if (currentVersion.isPresent() && currentVersion.get() < DatabaseVersion.MIN_UPGRADE_VERSION) {
throw MessageException.of("Current version is too old. Please upgrade to Long Term Support version firstly."); throw MessageException.of("Current version is too old. Please upgrade to Long Term Support version firstly.");
} }
String msg = "Database must be upgraded. Please backup database and browse /setup"; boolean blueGreen = configuration.getBoolean(ProcessProperties.Property.BLUE_GREEN_ENABLED.getKey()).orElse(false);
Loggers.get(DatabaseServerCompatibility.class).warn(msg); if (!blueGreen) {
Loggers.get(STARTUP_LOGGER_NAME).warn('\n' String msg = "Database must be upgraded. Please backup database and browse /setup";
+ HIGHLIGHTER + '\n' Loggers.get(DatabaseServerCompatibility.class).warn(msg);
+ " " + msg Loggers.get(STARTUP_LOGGER_NAME).warn('\n'
+ '\n' + HIGHLIGHTER); + HIGHLIGHTER + '\n'
+ " " + msg
+ '\n' + HIGHLIGHTER);
}
} }
} }


Expand Down
Expand Up @@ -23,24 +23,25 @@
import org.apache.commons.lang.builder.ReflectionToStringBuilder; import org.apache.commons.lang.builder.ReflectionToStringBuilder;
import org.apache.commons.lang.builder.ToStringStyle; import org.apache.commons.lang.builder.ToStringStyle;
import org.picocontainer.Startable; import org.picocontainer.Startable;
import org.sonar.api.config.Configuration;
import org.sonar.api.platform.ServerUpgradeStatus; import org.sonar.api.platform.ServerUpgradeStatus;
import org.sonar.process.ProcessProperties;
import org.sonar.server.platform.db.migration.step.MigrationSteps; import org.sonar.server.platform.db.migration.step.MigrationSteps;
import org.sonar.server.platform.db.migration.version.DatabaseVersion; import org.sonar.server.platform.db.migration.version.DatabaseVersion;


/** public class DefaultServerUpgradeStatus implements ServerUpgradeStatus, Startable {
* @since 2.5
*/
public final class DefaultServerUpgradeStatus implements ServerUpgradeStatus, Startable {


private final DatabaseVersion dbVersion; private final DatabaseVersion dbVersion;
private final MigrationSteps migrationSteps; private final MigrationSteps migrationSteps;
private final Configuration configuration;


// available when connected to db // available when connected to db
private long initialDbVersion; private long initialDbVersion;


public DefaultServerUpgradeStatus(DatabaseVersion dbVersion, MigrationSteps migrationSteps) { public DefaultServerUpgradeStatus(DatabaseVersion dbVersion, MigrationSteps migrationSteps, Configuration configuration) {
this.dbVersion = dbVersion; this.dbVersion = dbVersion;
this.migrationSteps = migrationSteps; this.migrationSteps = migrationSteps;
this.configuration = configuration;
} }


@Override @Override
Expand Down Expand Up @@ -69,6 +70,10 @@ public int getInitialDbVersion() {
return (int) initialDbVersion; return (int) initialDbVersion;
} }


public boolean isBlueGreen() {
return configuration.getBoolean(ProcessProperties.Property.BLUE_GREEN_ENABLED.getKey()).orElse(false);
}

@Override @Override
public String toString() { public String toString() {
return new ReflectionToStringBuilder(this, ToStringStyle.SHORT_PREFIX_STYLE).toString(); return new ReflectionToStringBuilder(this, ToStringStyle.SHORT_PREFIX_STYLE).toString();
Expand Down
Expand Up @@ -25,26 +25,26 @@
import java.sql.SQLException; import java.sql.SQLException;
import org.apache.commons.dbutils.DbUtils; import org.apache.commons.dbutils.DbUtils;
import org.picocontainer.Startable; import org.picocontainer.Startable;
import org.sonar.api.platform.ServerUpgradeStatus;
import org.sonar.api.utils.log.Loggers; import org.sonar.api.utils.log.Loggers;
import org.sonar.db.DbClient; import org.sonar.db.DbClient;
import org.sonar.db.DbSession; import org.sonar.db.DbSession;
import org.sonar.db.DdlUtils; import org.sonar.db.DdlUtils;
import org.sonar.db.dialect.Dialect; import org.sonar.db.dialect.Dialect;
import org.sonar.db.dialect.H2; import org.sonar.db.dialect.H2;
import org.sonar.server.platform.DefaultServerUpgradeStatus;
import org.sonar.server.platform.db.migration.engine.MigrationEngine; import org.sonar.server.platform.db.migration.engine.MigrationEngine;
import org.sonar.server.platform.db.migration.step.MigrationSteps; import org.sonar.server.platform.db.migration.step.MigrationSteps;


/** /**
* FIXME fix this class to remove use of DdlUtils.createSchema * FIXME fix this class to remove use of DdlUtils.createSchema
*/ */
public class AutoDbMigration implements Startable { public class AutoDbMigration implements Startable {
private final ServerUpgradeStatus serverUpgradeStatus; private final DefaultServerUpgradeStatus serverUpgradeStatus;
private final DbClient dbClient; private final DbClient dbClient;
private final MigrationEngine migrationEngine; private final MigrationEngine migrationEngine;
private final MigrationSteps migrationSteps; private final MigrationSteps migrationSteps;


public AutoDbMigration(ServerUpgradeStatus serverUpgradeStatus, DbClient dbClient, MigrationEngine migrationEngine, MigrationSteps migrationSteps) { public AutoDbMigration(DefaultServerUpgradeStatus serverUpgradeStatus, DbClient dbClient, MigrationEngine migrationEngine, MigrationSteps migrationSteps) {
this.serverUpgradeStatus = serverUpgradeStatus; this.serverUpgradeStatus = serverUpgradeStatus;
this.dbClient = dbClient; this.dbClient = dbClient;
this.migrationEngine = migrationEngine; this.migrationEngine = migrationEngine;
Expand All @@ -53,15 +53,16 @@ public AutoDbMigration(ServerUpgradeStatus serverUpgradeStatus, DbClient dbClien


@Override @Override
public void start() { public void start() {
if (!serverUpgradeStatus.isFreshInstall()) { if (serverUpgradeStatus.isFreshInstall()) {
return; Loggers.get(getClass()).info("Automatically perform DB migration on fresh install");
} Dialect dialect = dbClient.getDatabase().getDialect();

if (H2.ID.equals(dialect.getId())) {
Loggers.get(getClass()).info("Automatically perform DB migration on fresh install"); installH2();
Dialect dialect = dbClient.getDatabase().getDialect(); } else {
if (H2.ID.equals(dialect.getId())) { migrationEngine.execute();
installH2(); }
} else { } else if (serverUpgradeStatus.isUpgraded() && serverUpgradeStatus.isBlueGreen()) {
Loggers.get(getClass()).info("Automatically perform DB migration on blue/green deployment");
migrationEngine.execute(); migrationEngine.execute();
} }
} }
Expand Down
Expand Up @@ -88,15 +88,14 @@ public void do_nothing_if_up_to_date() {
} }


@Test @Test
public void fail_if_upgrade_required_on_blue_green_deployment() { public void upgrade_automatically_if_blue_green_deployment() {
settings.setProperty("sonar.blueGreenEnabled", "true"); settings.setProperty("sonar.blueGreenEnabled", "true");
DatabaseVersion version = mock(DatabaseVersion.class); DatabaseVersion version = mock(DatabaseVersion.class);
when(version.getStatus()).thenReturn(DatabaseVersion.Status.REQUIRES_UPGRADE); when(version.getStatus()).thenReturn(DatabaseVersion.Status.REQUIRES_UPGRADE);
when(version.getVersion()).thenReturn(Optional.of(DatabaseVersion.MIN_UPGRADE_VERSION)); when(version.getVersion()).thenReturn(Optional.of(DatabaseVersion.MIN_UPGRADE_VERSION));


expectedException.expect(IllegalStateException.class);
expectedException.expectMessage("Blue/green deployment is not supported. Database must be upgraded.");

new DatabaseServerCompatibility(version, settings.asConfig()).start(); new DatabaseServerCompatibility(version, settings.asConfig()).start();

assertThat(logTester.logs(LoggerLevel.WARN)).isEmpty();
} }
} }
Expand Up @@ -22,6 +22,8 @@
import java.util.Optional; import java.util.Optional;
import org.junit.Before; import org.junit.Before;
import org.junit.Test; import org.junit.Test;
import org.sonar.api.config.internal.ConfigurationBridge;
import org.sonar.api.config.internal.MapSettings;
import org.sonar.server.platform.db.migration.step.MigrationSteps; import org.sonar.server.platform.db.migration.step.MigrationSteps;
import org.sonar.server.platform.db.migration.version.DatabaseVersion; import org.sonar.server.platform.db.migration.version.DatabaseVersion;


Expand All @@ -33,7 +35,8 @@ public class DefaultServerUpgradeStatusTest {
private static final long LAST_VERSION = 150; private static final long LAST_VERSION = 150;
private MigrationSteps migrationSteps = mock(MigrationSteps.class); private MigrationSteps migrationSteps = mock(MigrationSteps.class);
private DatabaseVersion dbVersion = mock(DatabaseVersion.class); private DatabaseVersion dbVersion = mock(DatabaseVersion.class);
private DefaultServerUpgradeStatus underTest = new DefaultServerUpgradeStatus(dbVersion, migrationSteps); private MapSettings settings = new MapSettings();
private DefaultServerUpgradeStatus underTest = new DefaultServerUpgradeStatus(dbVersion, migrationSteps, new ConfigurationBridge(settings));


@Before @Before
public void setUp() throws Exception { public void setUp() throws Exception {
Expand Down Expand Up @@ -73,4 +76,16 @@ public void shouldNotBeUpgraded() {
assertThat(underTest.isUpgraded()).isFalse(); assertThat(underTest.isUpgraded()).isFalse();
assertThat(underTest.getInitialDbVersion()).isEqualTo((int) LAST_VERSION); assertThat(underTest.getInitialDbVersion()).isEqualTo((int) LAST_VERSION);
} }

@Test
public void isBlueGreen() {
settings.clear();
assertThat(underTest.isBlueGreen()).isFalse();

settings.setProperty("sonar.blueGreenEnabled", true);
assertThat(underTest.isBlueGreen()).isTrue();

settings.setProperty("sonar.blueGreenEnabled", false);
assertThat(underTest.isBlueGreen()).isFalse();
}
} }
Expand Up @@ -24,7 +24,6 @@
import org.junit.Test; import org.junit.Test;
import org.junit.rules.ExpectedException; import org.junit.rules.ExpectedException;
import org.mockito.Mockito; import org.mockito.Mockito;
import org.sonar.api.platform.ServerUpgradeStatus;
import org.sonar.api.utils.log.LogTester; import org.sonar.api.utils.log.LogTester;
import org.sonar.api.utils.log.LoggerLevel; import org.sonar.api.utils.log.LoggerLevel;
import org.sonar.db.DbClient; import org.sonar.db.DbClient;
Expand All @@ -35,6 +34,7 @@
import org.sonar.db.dialect.MySql; import org.sonar.db.dialect.MySql;
import org.sonar.db.dialect.Oracle; import org.sonar.db.dialect.Oracle;
import org.sonar.db.dialect.PostgreSql; import org.sonar.db.dialect.PostgreSql;
import org.sonar.server.platform.DefaultServerUpgradeStatus;
import org.sonar.server.platform.db.migration.engine.MigrationEngine; import org.sonar.server.platform.db.migration.engine.MigrationEngine;
import org.sonar.server.platform.db.migration.step.MigrationSteps; import org.sonar.server.platform.db.migration.step.MigrationSteps;


Expand All @@ -53,7 +53,7 @@ public class AutoDbMigrationTest {
public LogTester logTester = new LogTester(); public LogTester logTester = new LogTester();


private DbClient dbClient = mock(DbClient.class, Mockito.RETURNS_DEEP_STUBS); private DbClient dbClient = mock(DbClient.class, Mockito.RETURNS_DEEP_STUBS);
private ServerUpgradeStatus serverUpgradeStatus = mock(ServerUpgradeStatus.class); private DefaultServerUpgradeStatus serverUpgradeStatus = mock(DefaultServerUpgradeStatus.class);
private MigrationEngine migrationEngine = mock(MigrationEngine.class); private MigrationEngine migrationEngine = mock(MigrationEngine.class);
private MigrationSteps migrationSteps = mock(MigrationSteps.class); private MigrationSteps migrationSteps = mock(MigrationSteps.class);
private AutoDbMigration underTest = new AutoDbMigration(serverUpgradeStatus, dbClient, migrationEngine, migrationSteps); private AutoDbMigration underTest = new AutoDbMigration(serverUpgradeStatus, dbClient, migrationEngine, migrationSteps);
Expand Down Expand Up @@ -116,7 +116,31 @@ public void start_does_nothing_if_not_fresh_install() {
verify(noRealH2Creation).start(); verify(noRealH2Creation).start();
verifyNoMoreInteractions(noRealH2Creation); verifyNoMoreInteractions(noRealH2Creation);
verifyZeroInteractions(migrationEngine); verifyZeroInteractions(migrationEngine);
assertThat(logTester.logs()).isEmpty(); assertThat(logTester.logs(LoggerLevel.INFO)).isEmpty();
}

@Test
public void start_runs_MigrationEngine_if_blue_green_upgrade() {
mockFreshInstall(false);
when(serverUpgradeStatus.isUpgraded()).thenReturn(true);
when(serverUpgradeStatus.isBlueGreen()).thenReturn(true);

underTest.start();

verify(migrationEngine).execute();
assertThat(logTester.logs(LoggerLevel.INFO)).contains("Automatically perform DB migration on blue/green deployment");
}

@Test
public void start_does_nothing_if_blue_green_but_no_upgrade() {
mockFreshInstall(false);
when(serverUpgradeStatus.isUpgraded()).thenReturn(false);
when(serverUpgradeStatus.isBlueGreen()).thenReturn(true);

underTest.start();

verifyZeroInteractions(migrationEngine);
assertThat(logTester.logs(LoggerLevel.INFO)).isEmpty();
} }


@Test @Test
Expand Down

0 comments on commit 7db084d

Please sign in to comment.