From 037518088d63efbc1e54b7c7bc4e552da20449a2 Mon Sep 17 00:00:00 2001 From: Julien Lancelot Date: Mon, 18 Dec 2017 14:38:55 +0100 Subject: [PATCH] MMF-1074 Read 'sonar.quality' setting and update default organization --- .../db/migration/version/v70/DbVersion70.java | 5 +- ...alSonarQualityGateSettingToDefaultOrg.java | 78 +++++++++ .../version/v70/DbVersion70Test.java | 2 +- ...narQualityGateSettingToDefaultOrgTest.java | 163 ++++++++++++++++++ .../initial.sql | 58 +++++++ 5 files changed, 303 insertions(+), 3 deletions(-) create mode 100644 server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v70/ReadGlobalSonarQualityGateSettingToDefaultOrg.java create mode 100644 server/sonar-db-migration/src/test/java/org/sonar/server/platform/db/migration/version/v70/ReadGlobalSonarQualityGateSettingToDefaultOrgTest.java create mode 100644 server/sonar-db-migration/src/test/resources/org/sonar/server/platform/db/migration/version/v70/ReadGlobalSonarQualityGateSettingToDefaultOrgTest/initial.sql diff --git a/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v70/DbVersion70.java b/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v70/DbVersion70.java index 7d355c5753c..a389efd9709 100644 --- a/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v70/DbVersion70.java +++ b/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v70/DbVersion70.java @@ -47,8 +47,9 @@ public class DbVersion70 implements DbVersion { .add(1917, "Populate ORG_QUALITY_GATES table", PopulateOrgQualityGates.class) .add(1918, "Populate default quality gate on organization", PopulateDefaultQualityGate.class) .add(1919, "Associate existing quality gates to default organization", AssociateQualityGatesToDefaultOrganization.class) - .add(1920, "Delete 'sonar.qualitygate' setting at global level", DeleteGlobalSonarQualityGateSetting.class) - .add(1921, "Make ORGANIZATIONS.DEFAULT_QUALITY_GATE_UUID not nullable", SetDefaultQualityGateUuidAsNotNullableInOrganizations.class) + .add(1920, "Read 'sonar.qualitygate' setting and set the value to default organization", ReadGlobalSonarQualityGateSettingToDefaultOrg.class) + .add(1921, "Delete 'sonar.qualitygate' setting at global level", DeleteGlobalSonarQualityGateSetting.class) + .add(1922, "Make ORGANIZATIONS.DEFAULT_QUALITY_GATE_UUID not nullable", SetDefaultQualityGateUuidAsNotNullableInOrganizations.class) ; } } diff --git a/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v70/ReadGlobalSonarQualityGateSettingToDefaultOrg.java b/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v70/ReadGlobalSonarQualityGateSettingToDefaultOrg.java new file mode 100644 index 00000000000..849a0896069 --- /dev/null +++ b/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v70/ReadGlobalSonarQualityGateSettingToDefaultOrg.java @@ -0,0 +1,78 @@ +/* + * SonarQube + * Copyright (C) 2009-2017 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.version.v70; + +import java.sql.SQLException; +import javax.annotation.CheckForNull; +import org.sonar.api.utils.System2; +import org.sonar.api.utils.log.Logger; +import org.sonar.api.utils.log.Loggers; +import org.sonar.db.Database; +import org.sonar.server.platform.db.migration.step.DataChange; +import org.sonar.server.platform.db.migration.step.MassUpdate; + +import static com.google.common.base.Preconditions.checkState; +import static java.lang.Long.parseLong; + +public class ReadGlobalSonarQualityGateSettingToDefaultOrg extends DataChange { + + private static final Logger LOGGER = Loggers.get(ReadGlobalSonarQualityGateSettingToDefaultOrg.class); + private final System2 system2; + + public ReadGlobalSonarQualityGateSettingToDefaultOrg(Database db, System2 system2) { + super(db); + this.system2 = system2; + } + + @Override + protected void execute(Context context) throws SQLException { + String defaultOrgUuid = getDefaultOrgUuid(context); + String defaultQualityGate = getDefaultQualityGate(context); + if (defaultQualityGate == null) { + LOGGER.info("No default quality gate set"); + return; + } + + MassUpdate massUpdate = context.prepareMassUpdate(); + massUpdate.select("select uuid from quality_gates where id=?") + .setLong(1, parseLong(defaultQualityGate)); + massUpdate.update("update organizations set default_quality_gate_uuid = ?, updated_at = ? where uuid=?"); + massUpdate.execute((row, update) -> { + update.setString(1, row.getString(1)); + update.setLong(2, system2.now()); + update.setString(3, defaultOrgUuid); + return true; + }); + } + + private static String getDefaultOrgUuid(Context context) throws SQLException { + String defaultOrgUuid = context.prepareSelect("select text_value from internal_properties where kee = 'organization.default'") + .get(row -> row.getString(1)); + checkState(defaultOrgUuid != null, "Default organization uuid is missing"); + return defaultOrgUuid; + } + + @CheckForNull + private static String getDefaultQualityGate(Context context) throws SQLException { + return context.prepareSelect("select text_value from properties where prop_key=? and resource_id is null") + .setString(1, "sonar.qualitygate") + .get(row -> row.getNullableString(1)); + } +} diff --git a/server/sonar-db-migration/src/test/java/org/sonar/server/platform/db/migration/version/v70/DbVersion70Test.java b/server/sonar-db-migration/src/test/java/org/sonar/server/platform/db/migration/version/v70/DbVersion70Test.java index 5fd33f8206a..010a5befb63 100644 --- a/server/sonar-db-migration/src/test/java/org/sonar/server/platform/db/migration/version/v70/DbVersion70Test.java +++ b/server/sonar-db-migration/src/test/java/org/sonar/server/platform/db/migration/version/v70/DbVersion70Test.java @@ -35,7 +35,7 @@ public class DbVersion70Test { @Test public void verify_migration_count() { - verifyMigrationCount(underTest, 22); + verifyMigrationCount(underTest, 23); } } diff --git a/server/sonar-db-migration/src/test/java/org/sonar/server/platform/db/migration/version/v70/ReadGlobalSonarQualityGateSettingToDefaultOrgTest.java b/server/sonar-db-migration/src/test/java/org/sonar/server/platform/db/migration/version/v70/ReadGlobalSonarQualityGateSettingToDefaultOrgTest.java new file mode 100644 index 00000000000..7776c3bd79f --- /dev/null +++ b/server/sonar-db-migration/src/test/java/org/sonar/server/platform/db/migration/version/v70/ReadGlobalSonarQualityGateSettingToDefaultOrgTest.java @@ -0,0 +1,163 @@ +/* + * SonarQube + * Copyright (C) 2009-2017 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.version.v70; + +import java.sql.SQLException; +import java.util.Date; +import java.util.stream.Collectors; +import javax.annotation.Nullable; +import org.assertj.core.groups.Tuple; +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.ExpectedException; +import org.sonar.api.utils.System2; +import org.sonar.api.utils.internal.TestSystem2; +import org.sonar.core.util.Uuids; +import org.sonar.db.CoreDbTester; +import org.sonar.server.platform.db.migration.step.DataChange; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.tuple; + +public class ReadGlobalSonarQualityGateSettingToDefaultOrgTest { + + private static final long PAST = 10_000_000_000L; + private static final long NOW = 20_000_000_000L; + + private System2 system2 = new TestSystem2().setNow(NOW); + + @Rule + public ExpectedException expectedException = ExpectedException.none(); + + @Rule + public CoreDbTester db = CoreDbTester.createForSchema(ReadGlobalSonarQualityGateSettingToDefaultOrgTest.class, "initial.sql"); + + private DataChange underTest = new ReadGlobalSonarQualityGateSettingToDefaultOrg(db.database(), system2); + + @Test + public void read_sonar_quality_gate_setting_and_update_default_organization() throws SQLException { + String defaultQualityGate = insertQualityGate(); + String otherQualityGate = insertQualityGate(); + String defaultOrganization = insertOrganization(otherQualityGate); + String otherOrganization = insertOrganization(otherQualityGate); + insertDefaultOrgProperty(defaultOrganization); + insertSetting("sonar.qualitygate", selectQualityGateId(defaultQualityGate)); + + underTest.execute(); + + assertDefaultQualityGate(defaultOrganization, tuple(defaultQualityGate, NOW)); + } + + @Test + public void does_nothing_when_no_default_quality_gate_setting() throws Exception { + String defaultQualityGate = insertQualityGate(); + String defaultOrganization = insertOrganization(defaultQualityGate); + insertDefaultOrgProperty(defaultOrganization); + insertQualityGate(); + + underTest.execute(); + + assertDefaultQualityGate(defaultOrganization, tuple(defaultQualityGate, PAST)); + } + + @Test + public void migration_is_reentrant() throws Exception { + String defaultOrganization = insertOrganization(null); + insertDefaultOrgProperty(defaultOrganization); + String qualityGate = insertQualityGate(); + insertSetting("sonar.qualitygate", selectQualityGateId(qualityGate)); + + underTest.execute(); + assertDefaultQualityGate(defaultOrganization, tuple(qualityGate, NOW)); + + underTest.execute(); + assertDefaultQualityGate(defaultOrganization, tuple(qualityGate, NOW)); + } + + @Test + public void fail_when_no_default_organization() throws Exception { + String qualityGate = insertQualityGate(); + insertSetting("sonar.qualitygate", selectQualityGateId(qualityGate)); + + expectedException.expect(IllegalStateException.class); + expectedException.expectMessage("Default organization uuid is missing"); + + underTest.execute(); + } + + private void assertDefaultQualityGate(String uuid, Tuple... expectedTuples) { + assertThat(db.select(String.format("SELECT DEFAULT_QUALITY_GATE_UUID, UPDATED_AT FROM ORGANIZATIONS WHERE UUID='%s'", uuid)) + .stream() + .map(map -> new Tuple(map.get("DEFAULT_QUALITY_GATE_UUID"), map.get("UPDATED_AT"))) + .collect(Collectors.toList())) + .containsExactlyInAnyOrder(expectedTuples); + } + + private long selectQualityGateId(String uuid){ + return ((Long) db.selectFirst("select id as \"ID\" from quality_gates where uuid='" + uuid + "'").get("ID")).intValue(); + } + + private String insertOrganization(@Nullable String defaultQualityGateUuid) { + String uuid = Uuids.createFast(); + db.executeInsert( + "organizations", + "UUID", uuid, + "KEE", uuid, + "NAME", uuid, + "GUARDED", String.valueOf(false), + "NEW_PROJECT_PRIVATE", String.valueOf(true), + "DEFAULT_QUALITY_GATE_UUID", defaultQualityGateUuid, + "CREATED_AT", PAST, + "UPDATED_AT", PAST); + return uuid; + } + + private void insertDefaultOrgProperty(String uuid) { + db.executeInsert( + "internal_properties", + "KEE", "organization.default", + "IS_EMPTY", String.valueOf(false), + "TEXT_VALUE", uuid, + "CREATED_AT", PAST); + } + + private String insertQualityGate() { + String uuid = Uuids.createFast(); + db.executeInsert( + "quality_gates", + "UUID", uuid, + "NAME", uuid, + "IS_BUILT_IN", String.valueOf(false), + "CREATED_AT", new Date(PAST), + "UPDATED_AT", new Date(PAST)); + return uuid; + } + + private void insertSetting(String key, Long value) { + db.executeInsert( + "properties", + "PROP_KEY", key, + "TEXT_VALUE", value, + "IS_EMPTY", false, + "CREATED_AT", PAST); + } + +} diff --git a/server/sonar-db-migration/src/test/resources/org/sonar/server/platform/db/migration/version/v70/ReadGlobalSonarQualityGateSettingToDefaultOrgTest/initial.sql b/server/sonar-db-migration/src/test/resources/org/sonar/server/platform/db/migration/version/v70/ReadGlobalSonarQualityGateSettingToDefaultOrgTest/initial.sql new file mode 100644 index 00000000000..a9959d138c0 --- /dev/null +++ b/server/sonar-db-migration/src/test/resources/org/sonar/server/platform/db/migration/version/v70/ReadGlobalSonarQualityGateSettingToDefaultOrgTest/initial.sql @@ -0,0 +1,58 @@ +CREATE TABLE "PROPERTIES" ( + "ID" INTEGER NOT NULL GENERATED BY DEFAULT AS IDENTITY (START WITH 1, INCREMENT BY 1), + "PROP_KEY" VARCHAR(512) NOT NULL, + "RESOURCE_ID" INTEGER, + "USER_ID" INTEGER, + "IS_EMPTY" BOOLEAN NOT NULL, + "TEXT_VALUE" VARCHAR(4000), + "CLOB_VALUE" CLOB, + "CREATED_AT" BIGINT +); +CREATE INDEX "PROPERTIES_KEY" ON "PROPERTIES" ("PROP_KEY"); + +CREATE TABLE "ORGANIZATIONS" ( + "UUID" VARCHAR(40) NOT NULL PRIMARY KEY, + "KEE" VARCHAR(32) NOT NULL, + "NAME" VARCHAR(64) NOT NULL, + "DESCRIPTION" VARCHAR(256), + "URL" VARCHAR(256), + "AVATAR_URL" VARCHAR(256), + "GUARDED" BOOLEAN NOT NULL, + "USER_ID" INTEGER, + "DEFAULT_PERM_TEMPLATE_PROJECT" VARCHAR(40), + "DEFAULT_PERM_TEMPLATE_VIEW" VARCHAR(40), + "DEFAULT_GROUP_ID" INTEGER, + "DEFAULT_QUALITY_GATE_UUID" VARCHAR(40), + "NEW_PROJECT_PRIVATE" BOOLEAN NOT NULL, + "CREATED_AT" BIGINT NOT NULL, + "UPDATED_AT" BIGINT NOT NULL +); +CREATE UNIQUE INDEX "PK_ORGANIZATIONS" ON "ORGANIZATIONS" ("UUID"); +CREATE UNIQUE INDEX "ORGANIZATION_KEY" ON "ORGANIZATIONS" ("KEE"); + +CREATE TABLE "QUALITY_GATES" ( + "ID" INTEGER NOT NULL GENERATED BY DEFAULT AS IDENTITY (START WITH 1, INCREMENT BY 1), + "UUID" VARCHAR(40) NOT NULL, + "NAME" VARCHAR(100) NOT NULL, + "IS_BUILT_IN" BOOLEAN NOT NULL, + "CREATED_AT" TIMESTAMP, + "UPDATED_AT" TIMESTAMP, +); +CREATE UNIQUE INDEX "UNIQ_QUALITY_GATES_UUID" ON "QUALITY_GATES" ("UUID"); + +CREATE TABLE "ORG_QUALITY_GATES" ( + "UUID" VARCHAR(40) NOT NULL PRIMARY KEY, + "ORGANIZATION_UUID" VARCHAR(40) NOT NULL, + "QUALITY_GATE_UUID" VARCHAR(40) NOT NULL +); +CREATE UNIQUE INDEX "UNIQ_ORG_QUALITY_GATES" ON "ORG_QUALITY_GATES" ("ORGANIZATION_UUID","QUALITY_GATE_UUID"); + +CREATE TABLE "INTERNAL_PROPERTIES" ( + "KEE" VARCHAR(20) NOT NULL PRIMARY KEY, + "IS_EMPTY" BOOLEAN NOT NULL, + "TEXT_VALUE" VARCHAR(4000), + "CLOB_VALUE" CLOB, + "CREATED_AT" BIGINT +); +CREATE UNIQUE INDEX "UNIQ_INTERNAL_PROPERTIES" ON "INTERNAL_PROPERTIES" ("KEE"); + -- 2.39.5