public void addSteps(MigrationStepRegistry registry) {
registry
.add(2800, "Truncate environment variables and system properties from existing scanner reports",
- TruncateEnvAndSystemVarsFromScannerContext.class);
+ TruncateEnvAndSystemVarsFromScannerContext.class)
+ .add(2801, "populate install version and install date internal properties", PopulateInstallDateAndVersion.class);
}
}
--- /dev/null
+/*
+ * 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.version.v79;
+
+import java.sql.SQLException;
+import org.sonar.api.SonarRuntime;
+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;
+
+public class PopulateInstallDateAndVersion extends DataChange {
+
+ private static final Logger LOG = Loggers.get(PopulateInstallDateAndVersion.class);
+ private static final String INSTALLATION_DATE = "installation.date";
+ private static final String INSTALLATION_VERSION = "installation.version";
+ private final SonarRuntime sonarRuntime;
+ private final System2 system2;
+
+ public PopulateInstallDateAndVersion(Database db, SonarRuntime sonarRuntime, System2 system2) {
+ super(db);
+ this.sonarRuntime = sonarRuntime;
+ this.system2 = system2;
+ }
+
+ @Override
+ protected void execute(Context context) throws SQLException {
+ removeProperties(context);
+ Long createdAt = context.prepareSelect("select min(created_at) from users where created_at is not null")
+ .get(row -> row.getLong(1));
+ if (createdAt != null && createdAt != 0) {
+ populateInstallationDate(context, createdAt);
+ populateInstallationVersion(context, createdAt);
+ }
+ }
+
+ private void populateInstallationDate(Context context, Long createdAt) throws SQLException {
+ insertInternalProperty(context, INSTALLATION_DATE, String.valueOf(createdAt));
+ }
+
+ private void populateInstallationVersion(Context context, Long createdAt) throws SQLException {
+ if (Math.abs(system2.now() - createdAt) / 60 / 60 / 1000 <= 24) {
+ String apiVersion = sonarRuntime.getApiVersion().toString();
+ insertInternalProperty(context, INSTALLATION_VERSION, apiVersion);
+ } else {
+ // if the difference between now and smallest account creation date is more than a day, we consider that this is a
+ // start with an existing SQ, and not a fresh start. in this case, we do not populate the internalProperty,
+ // as there is no way to know the original SQ installation version.
+ LOG.warn("skipping " + INSTALLATION_VERSION + " because we cannot determine what is the installation version.");
+ }
+ }
+
+ private void insertInternalProperty(Context context, String key, String value) throws SQLException {
+ context.prepareUpsert("insert into internal_properties (kee, is_empty, text_value, clob_value, created_at) VALUES (?, ?, ?, ?, ?)")
+ .setString(1, key)
+ .setBoolean(2, false)
+ .setString(3, value)
+ .setString(4, null)
+ .setLong(5, system2.now())
+ .execute().commit().close();
+ }
+
+ private static void removeProperties(Context context) throws SQLException {
+ context.prepareUpsert("delete from internal_properties where kee = ? or kee = ?")
+ .setString(1, INSTALLATION_DATE)
+ .setString(2, INSTALLATION_VERSION)
+ .execute().commit().close();
+
+ }
+}
--- /dev/null
+/*
+ * 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.
+ */
+@ParametersAreNonnullByDefault
+package org.sonar.server.platform.db.migration.version.v79;
+
+import javax.annotation.ParametersAreNonnullByDefault;
+
@Test
public void verify_migration_count() {
- verifyMigrationCount(underTest, 1);
+ verifyMigrationCount(underTest, 2);
}
}
--- /dev/null
+/*
+ * 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.version.v79;
+
+import java.sql.SQLException;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.function.Function;
+import org.apache.commons.lang.math.RandomUtils;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.sonar.api.SonarRuntime;
+import org.sonar.api.utils.System2;
+import org.sonar.api.utils.Version;
+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.groups.Tuple.tuple;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+public class PopulateInstallDateAndVersionTest {
+
+ @Rule
+ public CoreDbTester db = CoreDbTester.createForSchema(PopulateInstallDateAndVersionTest.class, "schema.sql");
+
+ private System2 system2 = mock(System2.class);
+ private SonarRuntime sonarRuntime = mock(SonarRuntime.class);
+
+ private DataChange underTest = new PopulateInstallDateAndVersion(db.database(), sonarRuntime, system2);
+
+ private Function<Map<String, Object>, Object> field(String name) {
+ return m -> m.get(name);
+ }
+
+ @Before
+ public void before() {
+ Version version = Version.create(7, 9, 0);
+ when(sonarRuntime.getApiVersion()).thenReturn(version);
+ when(system2.now()).thenReturn(RandomUtils.nextLong());
+ truncateUsers();
+ truncateInternalProperties();
+ }
+
+ @Test
+ public void migrateFreshInstall() throws SQLException {
+ Long createdAt = system2.now() - (23 * 60 * 60 * 1000);
+ insertAdminUser(createdAt);
+
+ underTest.execute();
+
+ assertThat(db.select("select * from internal_properties")).extracting(
+ field("CREATED_AT"), field("CLOB_VALUE"),
+ field("KEE"), field("TEXT_VALUE"), field("IS_EMPTY"))
+ .containsExactlyInAnyOrder(
+ tuple(system2.now(), null, "installation.date", String.valueOf(createdAt), false),
+ tuple(system2.now(), null, "installation.version", "7.9", false));
+ }
+
+ @Test
+ public void migrateOldInstance() throws SQLException {
+ Long createdAt = system2.now() - (25 * 60 * 60 * 1000);
+ insertAdminUser(createdAt);
+
+ underTest.execute();
+
+ assertThat(db.select("select * from internal_properties")).extracting(
+ field("CREATED_AT"), field("CLOB_VALUE"),
+ field("KEE"), field("TEXT_VALUE"), field("IS_EMPTY"))
+ .containsExactlyInAnyOrder(
+ tuple(system2.now(), null, "installation.date", String.valueOf(createdAt), false));
+ }
+
+ @Test
+ public void migrateNoUsers() throws SQLException {
+ underTest.execute();
+
+ assertThat(db.select("select * from internal_properties").stream().count()).isEqualTo(0);
+ }
+
+ private void insertAdminUser(long createdAt) {
+ Map<String, Object> values = new HashMap<>();
+ values.put("UUID", "UUID");
+ values.put("login", "admin");
+ values.put("name", "Administrator");
+ values.put("email", null);
+ values.put("EXTERNAL_ID", "admin");
+ values.put("EXTERNAL_LOGIN", "admin");
+ values.put("external_identity_provider", "sonarqube");
+ values.put("user_local", true);
+ values.put("crypted_password", "a373a0e667abb2604c1fd571eb4ad47fe8cc0878");
+ values.put("salt", "48bc4b0d93179b5103fd3885ea9119498e9d161b");
+ values.put("created_at", createdAt);
+ values.put("updated_at", createdAt);
+ values.put("IS_ROOT", true);
+ values.put("ONBOARDED", false);
+ db.executeInsert("users", values);
+ }
+
+ private void truncateUsers() {
+ db.executeUpdateSql("truncate table users");
+ }
+
+ private void truncateInternalProperties() {
+ db.executeUpdateSql("truncate table internal_properties");
+ }
+}
--- /dev/null
+CREATE TABLE "INTERNAL_PROPERTIES" (
+ "KEE" VARCHAR(20) NOT NULL,
+ "IS_EMPTY" BOOLEAN NOT NULL,
+ "TEXT_VALUE" VARCHAR(4000),
+ "CLOB_VALUE" CLOB,
+ "CREATED_AT" BIGINT,
+
+ CONSTRAINT "PK_INTERNAL_PROPERTIES" PRIMARY KEY ("KEE")
+);
+
+CREATE TABLE "USERS" (
+ "ID" INTEGER NOT NULL GENERATED BY DEFAULT AS IDENTITY (START WITH 1, INCREMENT BY 1),
+ "UUID" VARCHAR(255) NOT NULL,
+ "LOGIN" VARCHAR(255) NOT NULL,
+ "NAME" VARCHAR(200),
+ "EMAIL" VARCHAR(100),
+ "CRYPTED_PASSWORD" VARCHAR(100),
+ "SALT" VARCHAR(40),
+ "HASH_METHOD" VARCHAR(10),
+ "ACTIVE" BOOLEAN DEFAULT TRUE,
+ "SCM_ACCOUNTS" VARCHAR(4000),
+ "EXTERNAL_ID" VARCHAR(255) NOT NULL,
+ "EXTERNAL_LOGIN" VARCHAR(255) NOT NULL,
+ "EXTERNAL_IDENTITY_PROVIDER" VARCHAR(100) NOT NULL,
+ "IS_ROOT" BOOLEAN NOT NULL,
+ "USER_LOCAL" BOOLEAN,
+ "ONBOARDED" BOOLEAN NOT NULL,
+ "HOMEPAGE_TYPE" VARCHAR(40),
+ "HOMEPAGE_PARAMETER" VARCHAR(40),
+ "ORGANIZATION_UUID" VARCHAR(40),
+ "LAST_CONNECTION_DATE" BIGINT,
+ "CREATED_AT" BIGINT,
+ "UPDATED_AT" BIGINT
+);
+CREATE UNIQUE INDEX "USERS_UUID" ON "USERS" ("UUID");
+CREATE UNIQUE INDEX "USERS_LOGIN" ON "USERS" ("LOGIN");
+CREATE UNIQUE INDEX "UNIQ_EXTERNAL_ID" ON "USERS" ("EXTERNAL_IDENTITY_PROVIDER", "EXTERNAL_ID");
+CREATE UNIQUE INDEX "UNIQ_EXTERNAL_LOGIN" ON "USERS" ("EXTERNAL_IDENTITY_PROVIDER", "EXTERNAL_LOGIN");
+CREATE INDEX "USERS_UPDATED_AT" ON "USERS" ("UPDATED_AT");
String COMPUTE_ENGINE_PAUSE = "ce.pause";
String BITBUCKETCLOUD_APP_SHAREDSECRET = "bbc.app.sharedSecret";
+
+ /**
+ * First installation date
+ */
+ String INSTALLATION_DATE = "installation.date";
+
+ /**
+ * first installation SQ version
+ */
+ String INSTALLATION_VERSION = "installation.version";
+
/**
* Read the value of the specified property.
*
import java.sql.SQLException;
import org.apache.commons.dbutils.DbUtils;
import org.picocontainer.Startable;
+import org.sonar.api.SonarRuntime;
+import org.sonar.api.utils.System2;
import org.sonar.api.utils.log.Loggers;
import org.sonar.db.DbClient;
import org.sonar.db.DbSession;
import org.sonar.server.platform.db.migration.engine.MigrationEngine;
import org.sonar.server.platform.db.migration.step.MigrationSteps;
+import static org.sonar.server.property.InternalProperties.INSTALLATION_DATE;
+import static org.sonar.server.property.InternalProperties.INSTALLATION_VERSION;
+
/**
* FIXME fix this class to remove use of DdlUtils.createSchema
*/
private final DbClient dbClient;
private final MigrationEngine migrationEngine;
private final MigrationSteps migrationSteps;
+ private final SonarRuntime sonarRuntime;
+ private final System2 system2;
- public AutoDbMigration(DefaultServerUpgradeStatus serverUpgradeStatus, DbClient dbClient, MigrationEngine migrationEngine, MigrationSteps migrationSteps) {
+ public AutoDbMigration(DefaultServerUpgradeStatus serverUpgradeStatus, DbClient dbClient, MigrationEngine migrationEngine, MigrationSteps migrationSteps,
+ SonarRuntime sonarRuntime, System2 system2) {
this.serverUpgradeStatus = serverUpgradeStatus;
this.dbClient = dbClient;
this.migrationEngine = migrationEngine;
this.migrationSteps = migrationSteps;
- }
-
- @Override
- public void start() {
- if (serverUpgradeStatus.isFreshInstall()) {
- Loggers.get(getClass()).info("Automatically perform DB migration on fresh install");
- Dialect dialect = dbClient.getDatabase().getDialect();
- if (H2.ID.equals(dialect.getId())) {
- installH2();
- } else {
- migrationEngine.execute();
- }
- } else if (serverUpgradeStatus.isUpgraded() && serverUpgradeStatus.isBlueGreen()) {
- Loggers.get(getClass()).info("Automatically perform DB migration on blue/green deployment");
- migrationEngine.execute();
- }
- }
-
- @VisibleForTesting
- void installH2() {
- Connection connection = null;
- try (DbSession session = dbClient.openSession(false)) {
- connection = session.getConnection();
- createH2Schema(connection, dbClient.getDatabase().getDialect().getId());
- } finally {
- DbUtils.closeQuietly(connection);
- }
- }
-
- @VisibleForTesting
- protected void createH2Schema(Connection connection, String dialectId) {
- DdlUtils.createSchema(connection, dialectId, false);
- populateSchemaMigration(connection, migrationSteps.getMaxMigrationNumber());
- hackFixForProjectMeasureTreeQueries(connection);
+ this.sonarRuntime = sonarRuntime;
+ this.system2 = system2;
}
private static void populateSchemaMigration(Connection connection, long maxMigrationNumber) {
connection.commit();
}
- @FunctionalInterface
- private interface Preparer {
- void prepare(PreparedStatement statement, long counter) throws SQLException;
+ @Override
+ public void start() {
+ if (serverUpgradeStatus.isFreshInstall()) {
+ Loggers.get(getClass()).info("Automatically perform DB migration on fresh install");
+ Dialect dialect = dbClient.getDatabase().getDialect();
+ if (H2.ID.equals(dialect.getId())) {
+ installH2();
+ } else {
+ migrationEngine.execute();
+ }
+ } else if (serverUpgradeStatus.isUpgraded() && serverUpgradeStatus.isBlueGreen()) {
+ Loggers.get(getClass()).info("Automatically perform DB migration on blue/green deployment");
+ migrationEngine.execute();
+ }
+ }
+
+ @VisibleForTesting
+ void installH2() {
+ Connection connection = null;
+ try (DbSession session = dbClient.openSession(false)) {
+ connection = session.getConnection();
+ createH2Schema(connection, dbClient.getDatabase().getDialect().getId());
+ } finally {
+ DbUtils.closeQuietly(connection);
+ }
+ }
+
+ @VisibleForTesting
+ protected void createH2Schema(Connection connection, String dialectId) {
+ DdlUtils.createSchema(connection, dialectId, false);
+ populateInstallDateAndVersion(connection);
+ populateSchemaMigration(connection, migrationSteps.getMaxMigrationNumber());
+ hackFixForProjectMeasureTreeQueries(connection);
+ }
+
+ private void populateInstallDateAndVersion(Connection connection) {
+ insertInternalProperty(connection, INSTALLATION_DATE, String.valueOf(system2.now()));
+ insertInternalProperty(connection, INSTALLATION_VERSION, sonarRuntime.getApiVersion().toString());
+ }
+
+ private void insertInternalProperty(Connection connection, String key, String value) {
+ try (PreparedStatement preparedStatementDate = connection
+ .prepareStatement("insert into internal_properties (kee, is_empty, text_value, clob_value, created_at) values (?, ?, ?, ?, ?)")) {
+ preparedStatementDate.setString(1, key);
+ preparedStatementDate.setBoolean(2, false);
+ preparedStatementDate.setString(3, value);
+ preparedStatementDate.setString(4, null);
+ preparedStatementDate.setLong(5, system2.now());
+ preparedStatementDate.execute();
+ } catch (SQLException e) {
+ throw new RuntimeException("Failed to insert internal properties " + key, e);
+ }
}
@Override
public void stop() {
// nothing to do
}
+
+ @FunctionalInterface
+ private interface Preparer {
+ void prepare(PreparedStatement statement, long counter) throws SQLException;
+ }
}
private final Map<String, Long> nclocByLanguage;
private final Optional<EditionProvider.Edition> edition;
private final String licenseType;
+ private final Long installationDate;
+ private final String installationVersion;
private TelemetryData(Builder builder) {
serverId = builder.serverId;
nclocByLanguage = builder.projectMeasuresStatistics.getNclocByLanguage();
edition = builder.edition;
licenseType = builder.licenseType;
+ installationDate = builder.installationDate;
+ installationVersion = builder.installationVersion;
}
public String getServerId() {
return Optional.ofNullable(licenseType);
}
+ public Long getInstallationDate(){
+ return installationDate;
+ }
+
+ public String getInstallationVersion(){
+ return installationVersion;
+ }
+
static Builder builder() {
return new Builder();
}
private Boolean usingBranches;
private Optional<EditionProvider.Edition> edition;
private String licenseType;
+ private Long installationDate;
+ private String installationVersion;
private Builder() {
// enforce static factory method
return this;
}
+ public Builder setInstallationDate(@Nullable Long installationDate){
+ this.installationDate = installationDate;
+ return this;
+ }
+
+ public Builder setInstallationVersion(@Nullable String installationVersion){
+ this.installationVersion = installationVersion;
+ return this;
+ }
+
TelemetryData build() {
requireNonNull(serverId);
requireNonNull(version);
json.endObject();
});
json.endArray();
+ if (statistics.getInstallationDate() != null) {
+ json.prop("installationDate", statistics.getInstallationDate());
+ }
+ if (statistics.getInstallationVersion() != null) {
+ json.prop("installationVersion", statistics.getInstallationVersion());
+ }
json.endObject();
}
}
import java.sql.DatabaseMetaData;
import java.sql.SQLException;
import java.util.Map;
+import java.util.Optional;
import java.util.function.Function;
import javax.annotation.CheckForNull;
import javax.annotation.Nullable;
import org.sonar.server.measure.index.ProjectMeasuresIndex;
import org.sonar.server.measure.index.ProjectMeasuresStatistics;
import org.sonar.server.organization.DefaultOrganizationProvider;
+import org.sonar.server.property.InternalProperties;
import org.sonar.server.telemetry.TelemetryData.Database;
import org.sonar.server.user.index.UserIndex;
import org.sonar.server.user.index.UserQuery;
private final ProjectMeasuresIndex projectMeasuresIndex;
private final PlatformEditionProvider editionProvider;
private final DefaultOrganizationProvider defaultOrganizationProvider;
+ private final InternalProperties internalProperties;
@CheckForNull
private final LicenseReader licenseReader;
public TelemetryDataLoader(Server server, DbClient dbClient, PluginRepository pluginRepository, UserIndex userIndex, ProjectMeasuresIndex projectMeasuresIndex,
- PlatformEditionProvider editionProvider, DefaultOrganizationProvider defaultOrganizationProvider) {
- this(server, dbClient, pluginRepository, userIndex, projectMeasuresIndex, editionProvider, defaultOrganizationProvider, null);
+ PlatformEditionProvider editionProvider, DefaultOrganizationProvider defaultOrganizationProvider, InternalProperties internalProperties) {
+ this(server, dbClient, pluginRepository, userIndex, projectMeasuresIndex, editionProvider, defaultOrganizationProvider, internalProperties, null);
}
public TelemetryDataLoader(Server server, DbClient dbClient, PluginRepository pluginRepository, UserIndex userIndex, ProjectMeasuresIndex projectMeasuresIndex,
- PlatformEditionProvider editionProvider, DefaultOrganizationProvider defaultOrganizationProvider, @Nullable LicenseReader licenseReader) {
+ PlatformEditionProvider editionProvider, DefaultOrganizationProvider defaultOrganizationProvider, InternalProperties internalProperties,
+ @Nullable LicenseReader licenseReader) {
this.server = server;
this.dbClient = dbClient;
this.pluginRepository = pluginRepository;
this.editionProvider = editionProvider;
this.defaultOrganizationProvider = defaultOrganizationProvider;
this.licenseReader = licenseReader;
+ this.internalProperties = internalProperties;
+ }
+
+ private static Database loadDatabaseMetadata(DbSession dbSession) {
+ try {
+ DatabaseMetaData metadata = dbSession.getConnection().getMetaData();
+ return new Database(metadata.getDatabaseProductName(), metadata.getDatabaseProductVersion());
+ } catch (SQLException e) {
+ throw new IllegalStateException("Fail to get DB metadata", e);
+ }
}
public TelemetryData load() {
data.setNcloc(dbClient.liveMeasureDao().sumNclocOfBiggestLongLivingBranch(dbSession, query));
}
+ Optional<String> installationDateProperty = internalProperties.read(InternalProperties.INSTALLATION_DATE);
+ if (installationDateProperty.isPresent()) {
+ data.setInstallationDate(Long.valueOf(installationDateProperty.get()));
+ }
+ Optional<String> installationVersionProperty = internalProperties.read(InternalProperties.INSTALLATION_VERSION);
+ data.setInstallationVersion(installationVersionProperty.orElse(null));
+
return data.build();
}
String loadServerId() {
return server.getId();
}
-
- private static Database loadDatabaseMetadata(DbSession dbSession) {
- try {
- DatabaseMetaData metadata = dbSession.getConnection().getMetaData();
- return new Database(metadata.getDatabaseProductName(), metadata.getDatabaseProductVersion());
- } catch (SQLException e) {
- throw new IllegalStateException("Fail to get DB metadata", e);
- }
- }
}
--- /dev/null
+/*
+ * 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;
+
+import java.sql.Connection;
+import java.sql.ResultSet;
+import java.sql.SQLException;
+import org.assertj.core.api.Assertions;
+import org.junit.Test;
+import org.mockito.Mockito;
+import org.sonar.api.SonarRuntime;
+import org.sonar.api.utils.System2;
+import org.sonar.api.utils.Version;
+import org.sonar.db.DbClient;
+import org.sonar.db.DbSession;
+import org.sonar.db.H2Database;
+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.history.MigrationHistoryTableImpl;
+import org.sonar.server.platform.db.migration.step.MigrationSteps;
+import org.sonar.server.property.InternalProperties;
+
+import static org.mockito.ArgumentMatchers.anyBoolean;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+public class AutoDbMigrationH2Test {
+
+ private DbClient dbClient = mock(DbClient.class, Mockito.RETURNS_DEEP_STUBS);
+ private DefaultServerUpgradeStatus serverUpgradeStatus = mock(DefaultServerUpgradeStatus.class);
+ private MigrationEngine migrationEngine = mock(MigrationEngine.class);
+ private MigrationSteps migrationSteps = mock(MigrationSteps.class);
+ private SonarRuntime sonarRuntime = mock(SonarRuntime.class);
+ private System2 system2 = mock(System2.class);
+
+ private AutoDbMigration underTest = new AutoDbMigration(serverUpgradeStatus, dbClient, migrationEngine, migrationSteps, sonarRuntime, system2);
+
+
+ @Test
+ public void testInstallH2() throws SQLException {
+ DbSession dbSession = mock(DbSession.class);
+ when(dbClient.getDatabase().getDialect()).thenReturn(new H2());
+ when(dbClient.openSession(anyBoolean())).thenReturn(dbSession);
+ when(system2.now()).thenReturn(123456789L);
+ H2Database db = new H2Database("sonar", false);
+ db.start();
+ Connection connection = db.getDataSource().getConnection();
+ when(dbSession.getConnection()).thenReturn(connection);
+ Version version = Version.create(7, 9, 0);
+ when(sonarRuntime.getApiVersion()).thenReturn(version);
+ new MigrationHistoryTableImpl(db).start();
+
+ underTest.installH2();
+
+ String selectInstallVersion = "select text_value from internal_properties where kee = '" + InternalProperties.INSTALLATION_VERSION + "'";
+ ResultSet resultSetVersion = db.getDataSource().getConnection().prepareStatement(selectInstallVersion).executeQuery();
+ resultSetVersion.next();
+ Assertions.assertThat(resultSetVersion.getString(1)).isEqualTo("7.9");
+
+ String selectInstallDate = "select text_value from internal_properties where kee = '" + InternalProperties.INSTALLATION_DATE + "'";
+ ResultSet resultSetDate = db.getDataSource().getConnection().prepareStatement(selectInstallDate).executeQuery();
+ resultSetDate.next();
+ Assertions.assertThat(resultSetDate.getString(1)).isEqualTo("123456789");
+ }
+
+}
import org.junit.Test;
import org.junit.rules.ExpectedException;
import org.mockito.Mockito;
+import org.sonar.api.SonarRuntime;
+import org.sonar.api.utils.System2;
import org.sonar.api.utils.log.LogTester;
import org.sonar.api.utils.log.LoggerLevel;
import org.sonar.db.DbClient;
private DefaultServerUpgradeStatus serverUpgradeStatus = mock(DefaultServerUpgradeStatus.class);
private MigrationEngine migrationEngine = mock(MigrationEngine.class);
private MigrationSteps migrationSteps = mock(MigrationSteps.class);
- private AutoDbMigration underTest = new AutoDbMigration(serverUpgradeStatus, dbClient, migrationEngine, migrationSteps);
+ private SonarRuntime sonarRuntime = mock(SonarRuntime.class);
+ private System2 system2 = mock(System2.class);
+ private AutoDbMigration underTest = new AutoDbMigration(serverUpgradeStatus, dbClient, migrationEngine, migrationSteps, sonarRuntime, system2);
- private AutoDbMigration noRealH2Creation = spy(new AutoDbMigration(serverUpgradeStatus, dbClient, migrationEngine, migrationSteps) {
+ private AutoDbMigration noRealH2Creation = spy(new AutoDbMigration(serverUpgradeStatus, dbClient, migrationEngine, migrationSteps, sonarRuntime, system2) {
@Override
protected void createH2Schema(Connection connection, String dialectId) {
// do nothing
+ "\"Application Nodes\":[{\"Name\":\"appNodes\",\"\":{\"name\":\"appNodes\"}}],"
+ "\"Search Nodes\":[{\"Name\":\"searchNodes\",\"\":{\"name\":\"searchNodes\"}}],"
+ "\"Statistics\":{\"id\":\"\",\"version\":\"\",\"database\":{\"name\":\"\",\"version\":\"\"},\"plugins\":[],"
- + "\"userCount\":0,\"projectCount\":0,\"usingBranches\":false,\"ncloc\":0,\"projectCountByLanguage\":[],\"nclocByLanguage\":[]}}");
+ + "\"userCount\":0,\"projectCount\":0,\"usingBranches\":false,\"ncloc\":0,\"projectCountByLanguage\":[]," +
+ "\"nclocByLanguage\":[],\"installationDate\":0,\"installationVersion\":\"\"}}");
}
private static NodeInfo createNodeInfo(String name) {
// response does not contain empty "Section Three"
assertThat(writer.toString()).isEqualTo("{\"Health\":\"GREEN\",\"Health Causes\":[],\"Section One\":{\"foo\":\"bar\"},\"Section Two\":{\"one\":1,\"two\":2}," +
"\"Statistics\":{\"id\":\"\",\"version\":\"\",\"database\":{\"name\":\"\",\"version\":\"\"},\"plugins\":[],\"userCount\":0,\"projectCount\":0,\"usingBranches\":false," +
- "\"ncloc\":0,\"projectCountByLanguage\":[],\"nclocByLanguage\":[]}}");
+ "\"ncloc\":0,\"projectCountByLanguage\":[],\"nclocByLanguage\":[],\"installationDate\":0,\"installationVersion\":\"\"}}");
}
private void logInAsSystemAdministrator() {
private PlatformEditionProvider editionProvider = mock(PlatformEditionProvider.class);
private final TelemetryDataLoader communityDataLoader = new TelemetryDataLoader(server, db.getDbClient(), pluginRepository, new UserIndex(es.client(), system2),
- new ProjectMeasuresIndex(es.client(), null, system2), editionProvider, new DefaultOrganizationProviderImpl(db.getDbClient()), null);
+ new ProjectMeasuresIndex(es.client(), null, system2), editionProvider, new DefaultOrganizationProviderImpl(db.getDbClient()), internalProperties, null);
private TelemetryDaemon communityUnderTest = new TelemetryDaemon(communityDataLoader, client, settings.asConfig(), internalProperties, system2);
private final LicenseReader licenseReader = mock(LicenseReader.class);
private final TelemetryDataLoader commercialDataLoader = new TelemetryDataLoader(server, db.getDbClient(), pluginRepository, new UserIndex(es.client(), system2),
- new ProjectMeasuresIndex(es.client(), null, system2), editionProvider, new DefaultOrganizationProviderImpl(db.getDbClient()), licenseReader);
+ new ProjectMeasuresIndex(es.client(), null, system2), editionProvider, new DefaultOrganizationProviderImpl(db.getDbClient()), internalProperties, licenseReader);
private TelemetryDaemon commercialUnderTest = new TelemetryDaemon(commercialDataLoader, client, settings.asConfig(), internalProperties, system2);
@After
assertThat(json.getValue()).contains(id, version);
}
+ @Test
+ public void send_server_installation_date_and_installation_version() throws IOException {
+ initTelemetrySettingsToDefaultValues();
+ settings.setProperty("sonar.telemetry.frequencyInSeconds", "1");
+ String installationVersion = "7.9.BEST.LTS.EVER";
+ Long installationDate = 1546300800000L; // 2019/01/01
+ internalProperties.write(InternalProperties.INSTALLATION_DATE, String.valueOf(installationDate));
+ internalProperties.write(InternalProperties.INSTALLATION_VERSION, installationVersion);
+
+ communityUnderTest.start();
+
+ ArgumentCaptor<String> json = captureJson();
+ assertThat(json.getValue()).contains(installationVersion, installationDate.toString());
+ }
+
+ @Test
+ public void do_not_send_server_installation_details_if_missing_property() throws IOException {
+ initTelemetrySettingsToDefaultValues();
+ settings.setProperty("sonar.telemetry.frequencyInSeconds", "1");
+
+ communityUnderTest.start();
+
+ ArgumentCaptor<String> json = captureJson();
+ assertThat(json.getValue()).doesNotContain("installationVersion", "installationDate");
+ }
+
@Test
public void do_not_send_data_if_last_ping_earlier_than_one_week_ago() throws IOException {
initTelemetrySettingsToDefaultValues();