From d3f1775ff556addcd4b666cf971aa9da2a176795 Mon Sep 17 00:00:00 2001 From: =?utf8?q?S=C3=A9bastien=20Lesaint?= Date: Wed, 26 Jun 2019 15:57:04 +0200 Subject: [PATCH] SONAR-12127 don't run new migrations when upgrading from 7.0 --- .../ComputeEngineContainerImplTest.java | 2 +- .../MigrationConfigurationModule.java | 4 +- .../history/MigrationHistoryImpl.java | 5 +- .../history/MigrationHistoryMeddler.java | 54 ++++++++++ .../history/MigrationHistoryImplTest.java | 9 +- .../history/MigrationHistoryMeddlerTest.java | 100 ++++++++++++++++++ 6 files changed, 169 insertions(+), 5 deletions(-) create mode 100644 server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/history/MigrationHistoryMeddler.java create mode 100644 server/sonar-db-migration/src/test/java/org/sonar/server/platform/db/migration/history/MigrationHistoryMeddlerTest.java diff --git a/server/sonar-ce/src/test/java/org/sonar/ce/container/ComputeEngineContainerImplTest.java b/server/sonar-ce/src/test/java/org/sonar/ce/container/ComputeEngineContainerImplTest.java index e35c0192c82..3f923deac85 100644 --- a/server/sonar-ce/src/test/java/org/sonar/ce/container/ComputeEngineContainerImplTest.java +++ b/server/sonar-ce/src/test/java/org/sonar/ce/container/ComputeEngineContainerImplTest.java @@ -122,7 +122,7 @@ public class ComputeEngineContainerImplTest { ); assertThat(picoContainer.getParent().getParent().getComponentAdapters()).hasSize( CONTAINER_ITSELF - + 23 // MigrationConfigurationModule + + 24 // MigrationConfigurationModule + 17 // level 2 ); assertThat(picoContainer.getParent().getParent().getParent().getComponentAdapters()).hasSize( diff --git a/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/MigrationConfigurationModule.java b/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/MigrationConfigurationModule.java index b11239533fe..081b4db7f84 100644 --- a/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/MigrationConfigurationModule.java +++ b/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/MigrationConfigurationModule.java @@ -21,6 +21,7 @@ package org.sonar.server.platform.db.migration; import org.sonar.core.platform.Module; import org.sonar.server.platform.db.migration.history.MigrationHistoryImpl; +import org.sonar.server.platform.db.migration.history.MigrationHistoryMeddler; import org.sonar.server.platform.db.migration.step.MigrationStepRegistryImpl; import org.sonar.server.platform.db.migration.step.MigrationStepsProvider; import org.sonar.server.platform.db.migration.version.v56.DbVersion56; @@ -75,6 +76,7 @@ public class MigrationConfigurationModule extends Module { new MigrationStepsProvider(), // history - MigrationHistoryImpl.class); + MigrationHistoryImpl.class, + MigrationHistoryMeddler.class); } } diff --git a/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/history/MigrationHistoryImpl.java b/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/history/MigrationHistoryImpl.java index a14ca6cb850..200794806ad 100644 --- a/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/history/MigrationHistoryImpl.java +++ b/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/history/MigrationHistoryImpl.java @@ -40,15 +40,18 @@ public class MigrationHistoryImpl implements MigrationHistory { private static final String SCHEMA_MIGRATIONS_TABLE = "schema_migrations"; private final Database database; + private final MigrationHistoryMeddler migrationHistoryMeddler; - public MigrationHistoryImpl(Database database) { + public MigrationHistoryImpl(Database database, MigrationHistoryMeddler migrationHistoryMeddler) { this.database = database; + this.migrationHistoryMeddler = migrationHistoryMeddler; } @Override public void start() { try (Connection connection = database.getDataSource().getConnection()) { checkState(DatabaseUtils.tableExists(MigrationHistoryTable.NAME, connection), "Migration history table is missing"); + migrationHistoryMeddler.meddle(this); } catch (SQLException e) { Throwables.propagate(e); } diff --git a/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/history/MigrationHistoryMeddler.java b/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/history/MigrationHistoryMeddler.java new file mode 100644 index 00000000000..4fc9937e171 --- /dev/null +++ b/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/history/MigrationHistoryMeddler.java @@ -0,0 +1,54 @@ +/* + * SonarQube + * Copyright (C) 2009-2019 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonar.server.platform.db.migration.history; + +import com.google.common.collect.ImmutableMap; +import java.util.Map; +import org.sonar.server.platform.db.migration.step.MigrationSteps; +import org.sonar.server.platform.db.migration.step.RegisteredMigrationStep; + +/** + * Under some conditions, the migration history must be manipulated. This is the role of this class + * which is called by {@link MigrationHistory#start()}. + */ +public class MigrationHistoryMeddler { + private final Map meddledSteps = ImmutableMap.of( + // SONAR-12127 several DB migration were added in 7.9 to migrate to from 6.7 to 7.0 + // If already on 7.0, we don't want any of these DB migrations ran + // => we change last migration number of those 7.0 instance to the new max migration number for 7.0 + 1_923L, 1_959L); + private final MigrationSteps migrationSteps; + + public MigrationHistoryMeddler(MigrationSteps migrationSteps) { + this.migrationSteps = migrationSteps; + } + + public void meddle(MigrationHistory migrationHistory) { + // change last migration number on specific cases + migrationHistory.getLastMigrationNumber() + .ifPresent(migrationNumber -> { + Long newMigrationNumber = meddledSteps.get(migrationNumber); + if (newMigrationNumber != null) { + RegisteredMigrationStep registeredMigrationStep = migrationSteps.readFrom(newMigrationNumber).get(0); + migrationHistory.done(registeredMigrationStep); + } + }); + } +} diff --git a/server/sonar-db-migration/src/test/java/org/sonar/server/platform/db/migration/history/MigrationHistoryImplTest.java b/server/sonar-db-migration/src/test/java/org/sonar/server/platform/db/migration/history/MigrationHistoryImplTest.java index 9aee9d6a1cb..3ac7734cc18 100644 --- a/server/sonar-db-migration/src/test/java/org/sonar/server/platform/db/migration/history/MigrationHistoryImplTest.java +++ b/server/sonar-db-migration/src/test/java/org/sonar/server/platform/db/migration/history/MigrationHistoryImplTest.java @@ -31,6 +31,8 @@ import org.sonar.server.platform.db.migration.step.MigrationStep; import org.sonar.server.platform.db.migration.step.RegisteredMigrationStep; import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; public class MigrationHistoryImplTest { @Rule @@ -38,11 +40,14 @@ public class MigrationHistoryImplTest { @Rule public ExpectedException expectedException = ExpectedException.none(); - private MigrationHistoryImpl underTest = new MigrationHistoryImpl(dbTester.database()); + private MigrationHistoryMeddler migrationHistoryMeddler = mock(MigrationHistoryMeddler.class); + private MigrationHistoryImpl underTest = new MigrationHistoryImpl(dbTester.database(), migrationHistoryMeddler); @Test - public void start_does_not_fail_if_table_history_exists() { + public void start_does_not_fail_if_table_history_exists_and_calls_meddler() { underTest.start(); + + verify(migrationHistoryMeddler).meddle(underTest); } @Test diff --git a/server/sonar-db-migration/src/test/java/org/sonar/server/platform/db/migration/history/MigrationHistoryMeddlerTest.java b/server/sonar-db-migration/src/test/java/org/sonar/server/platform/db/migration/history/MigrationHistoryMeddlerTest.java new file mode 100644 index 00000000000..09f80f8694a --- /dev/null +++ b/server/sonar-db-migration/src/test/java/org/sonar/server/platform/db/migration/history/MigrationHistoryMeddlerTest.java @@ -0,0 +1,100 @@ +/* + * SonarQube + * Copyright (C) 2009-2019 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonar.server.platform.db.migration.history; + +import com.tngtech.java.junit.dataprovider.DataProvider; +import com.tngtech.java.junit.dataprovider.DataProviderRunner; +import com.tngtech.java.junit.dataprovider.UseDataProvider; +import java.util.List; +import java.util.Optional; +import java.util.Random; +import java.util.stream.Collectors; +import java.util.stream.IntStream; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.sonar.server.platform.db.migration.step.MigrationStep; +import org.sonar.server.platform.db.migration.step.MigrationSteps; +import org.sonar.server.platform.db.migration.step.RegisteredMigrationStep; + +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.verifyNoMoreInteractions; +import static org.mockito.Mockito.when; + +@RunWith(DataProviderRunner.class) +public class MigrationHistoryMeddlerTest { + private static final long OLD_VERSION_70_LAST_MIGRATION_NUMBER = 1_923L; + private static final long NEW_VERSION_70_LAST_MIGRATION_NUMBER = 1_959L; + + private MigrationSteps migrationSteps = mock(MigrationSteps.class); + private MigrationHistory migrationHistory = mock(MigrationHistory.class); + private MigrationHistoryMeddler underTest = new MigrationHistoryMeddler(migrationSteps); + + @Test + public void no_effect_if_no_last_migration_number() { + when(migrationHistory.getLastMigrationNumber()).thenReturn(Optional.empty()); + + underTest.meddle(migrationHistory); + + verify(migrationHistory).getLastMigrationNumber(); + verifyNoMoreInteractions(migrationHistory, migrationSteps); + } + + @Test + @UseDataProvider("non_old_70_last_migration_number") + public void no_history_meddling_if_last_migration_number_is_not_old_70_last_migration_number(long lastMigrationNumber) { + when(migrationHistory.getLastMigrationNumber()).thenReturn(Optional.of(lastMigrationNumber)); + + underTest.meddle(migrationHistory); + + verify(migrationHistory).getLastMigrationNumber(); + verifyNoMoreInteractions(migrationHistory, migrationSteps); + } + + @Test + public void update_last_migration_number_if_last_migration_number_is_old_70_last_migration_number() { + verifyUpdateLastMigrationNumber(OLD_VERSION_70_LAST_MIGRATION_NUMBER, NEW_VERSION_70_LAST_MIGRATION_NUMBER); + } + + public void verifyUpdateLastMigrationNumber(long oldVersion, long expectedNewVersion) { + when(migrationHistory.getLastMigrationNumber()).thenReturn(Optional.of(oldVersion)); + List stepsFromNewLastMigrationNumber = IntStream.range(0, 1 + new Random().nextInt(30)) + .mapToObj(i -> new RegisteredMigrationStep(i, "desc_" + i, MigrationStep.class)) + .collect(Collectors.toList()); + when(migrationSteps.readFrom(expectedNewVersion)).thenReturn(stepsFromNewLastMigrationNumber); + + underTest.meddle(migrationHistory); + + verify(migrationHistory).getLastMigrationNumber(); + verify(migrationSteps).readFrom(expectedNewVersion); + verify(migrationHistory).done(stepsFromNewLastMigrationNumber.get(0)); + verifyNoMoreInteractions(migrationHistory, migrationSteps); + } + + @DataProvider + public static Object[][] non_old_70_last_migration_number() { + return new Object[][] { + {1L}, + {OLD_VERSION_70_LAST_MIGRATION_NUMBER - 1 - new Random().nextInt(12)}, + {OLD_VERSION_70_LAST_MIGRATION_NUMBER + 1 + new Random().nextInt(12)} + }; + } + +} -- 2.39.5