@@ -24,8 +24,34 @@ import javax.annotation.CheckForNull; | |||
public interface DatabaseMigrationState { | |||
String UNSUPPORTED_DATABASE_MIGRATION_STATUS = "Unsupported DatabaseMigration status"; | |||
String NO_CONNECTION_TO_DB = "Cannot connect to Database."; | |||
enum Status { | |||
NONE, RUNNING, FAILED, SUCCEEDED | |||
NONE("NO_MIGRATION", "Database is up-to-date, no migration needed."), | |||
RUNNING("MIGRATION_RUNNING", "Database migration is running."), | |||
FAILED("MIGRATION_FAILED", "Migration failed: %s.<br/> Please check logs."), | |||
SUCCEEDED("MIGRATION_SUCCEEDED", "Migration succeeded."), | |||
STATUS_NOT_SUPPORTED("NOT_SUPPORTED", "Upgrade is not supported on embedded database."), | |||
MIGRATION_REQUIRED("MIGRATION_REQUIRED", "Database migration is required. DB migration can be started using WS /api/system/migrate_db."); | |||
private final String stringRepresentation; | |||
private final String message; | |||
Status(String stringRepresentation, String message) { | |||
this.stringRepresentation = stringRepresentation; | |||
this.message = message; | |||
} | |||
public String getMessage() { | |||
return message; | |||
} | |||
@Override | |||
public String toString() { | |||
return stringRepresentation; | |||
} | |||
} | |||
/** |
@@ -23,6 +23,7 @@ public class WebApiEndpoints { | |||
private static final String SYSTEM_ENDPOINTS = "/system"; | |||
public static final String LIVENESS_ENDPOINT = SYSTEM_ENDPOINTS + "/liveness"; | |||
public static final String HEALTH_ENDPOINT = SYSTEM_ENDPOINTS + "/health"; | |||
public static final String DATABASE_MIGRATIONS_ENDPOINT = SYSTEM_ENDPOINTS + "/migrations-status"; | |||
public static final String USERS_MANAGEMENT_DOMAIN = "/users-management"; | |||
public static final String USER_ENDPOINT = USERS_MANAGEMENT_DOMAIN + "/users"; |
@@ -0,0 +1,86 @@ | |||
/* | |||
* SonarQube | |||
* Copyright (C) 2009-2024 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.v2.api.system.controller; | |||
import io.swagger.v3.oas.annotations.Operation; | |||
import java.util.Optional; | |||
import javax.annotation.Nullable; | |||
import org.sonar.db.Database; | |||
import org.sonar.server.platform.db.migration.DatabaseMigrationState; | |||
import org.sonar.server.platform.db.migration.version.DatabaseVersion; | |||
import org.sonar.server.v2.common.DateString; | |||
import org.springframework.web.bind.annotation.GetMapping; | |||
import org.springframework.web.bind.annotation.RequestMapping; | |||
import org.springframework.web.bind.annotation.RestController; | |||
import static com.google.common.base.Preconditions.checkState; | |||
import static org.sonar.server.platform.db.migration.DatabaseMigrationState.NO_CONNECTION_TO_DB; | |||
import static org.sonar.server.platform.db.migration.DatabaseMigrationState.UNSUPPORTED_DATABASE_MIGRATION_STATUS; | |||
import static org.sonar.server.v2.WebApiEndpoints.DATABASE_MIGRATIONS_ENDPOINT; | |||
@RestController | |||
@RequestMapping(DATABASE_MIGRATIONS_ENDPOINT) | |||
public class DatabaseMigrationsController { | |||
private final DatabaseVersion databaseVersion; | |||
private final DatabaseMigrationState databaseMigrationState; | |||
private final Database database; | |||
public DatabaseMigrationsController(DatabaseVersion databaseVersion, DatabaseMigrationState databaseMigrationState, Database database) { | |||
this.databaseVersion = databaseVersion; | |||
this.databaseMigrationState = databaseMigrationState; | |||
this.database = database; | |||
} | |||
@Operation(summary = "Gets the status of ongoing database migrations, if any", description = "Return the detailed status of ongoing database migrations" + | |||
" including starting date. If no migration is ongoing or needed it is still possible to call this endpoint and receive appropriate information.") | |||
@GetMapping | |||
public DatabaseMigrationsResponse getStatus() { | |||
Optional<Long> currentVersion = databaseVersion.getVersion(); | |||
checkState(currentVersion.isPresent(), NO_CONNECTION_TO_DB); | |||
DatabaseVersion.Status status = databaseVersion.getStatus(); | |||
if (status == DatabaseVersion.Status.UP_TO_DATE || status == DatabaseVersion.Status.REQUIRES_DOWNGRADE) { | |||
return new DatabaseMigrationsResponse(databaseMigrationState); | |||
} else if (!database.getDialect().supportsMigration()) { | |||
return new DatabaseMigrationsResponse(DatabaseMigrationState.Status.STATUS_NOT_SUPPORTED); | |||
} else { | |||
return switch (databaseMigrationState.getStatus()) { | |||
case RUNNING, FAILED, SUCCEEDED -> new DatabaseMigrationsResponse(databaseMigrationState); | |||
case NONE -> new DatabaseMigrationsResponse(DatabaseMigrationState.Status.MIGRATION_REQUIRED); | |||
default -> throw new IllegalArgumentException(UNSUPPORTED_DATABASE_MIGRATION_STATUS); | |||
}; | |||
} | |||
} | |||
public record DatabaseMigrationsResponse(String status, @Nullable String startedAt, @Nullable String message) { | |||
public DatabaseMigrationsResponse(DatabaseMigrationState state) { | |||
this(state.getStatus().toString(), | |||
DateString.from(state.getStartedAt()), | |||
state.getError() != null ? state.getError().getMessage() : state.getStatus().getMessage()); | |||
} | |||
public DatabaseMigrationsResponse(DatabaseMigrationState.Status status) { | |||
this(status.toString(), null, status.getMessage()); | |||
} | |||
} | |||
} |
@@ -0,0 +1,37 @@ | |||
/* | |||
* SonarQube | |||
* Copyright (C) 2009-2024 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.v2.common; | |||
import java.util.Date; | |||
import javax.annotation.Nullable; | |||
public class DateString { | |||
private DateString() { | |||
// intentionally empty | |||
} | |||
public static String from(@Nullable Date date) { | |||
if (date == null) { | |||
return null; | |||
} | |||
return date.toString(); | |||
} | |||
} |
@@ -22,6 +22,7 @@ package org.sonar.server.v2.config; | |||
import javax.annotation.Nullable; | |||
import org.sonar.api.platform.Server; | |||
import org.sonar.api.resources.Languages; | |||
import org.sonar.db.Database; | |||
import org.sonar.db.DbClient; | |||
import org.sonar.server.common.gitlab.config.GitlabConfigurationService; | |||
import org.sonar.server.common.group.service.GroupMembershipService; | |||
@@ -41,6 +42,8 @@ import org.sonar.server.common.user.service.UserService; | |||
import org.sonar.server.health.HealthChecker; | |||
import org.sonar.server.platform.NodeInformation; | |||
import org.sonar.server.platform.ServerFileSystem; | |||
import org.sonar.server.platform.db.migration.DatabaseMigrationState; | |||
import org.sonar.server.platform.db.migration.version.DatabaseVersion; | |||
import org.sonar.server.rule.RuleDescriptionFormatter; | |||
import org.sonar.server.user.SystemPasscode; | |||
import org.sonar.server.user.UserSession; | |||
@@ -69,6 +72,7 @@ import org.sonar.server.v2.api.projects.controller.DefaultBoundProjectsControlle | |||
import org.sonar.server.v2.api.rule.controller.DefaultRuleController; | |||
import org.sonar.server.v2.api.rule.controller.RuleController; | |||
import org.sonar.server.v2.api.rule.converter.RuleRestResponseGenerator; | |||
import org.sonar.server.v2.api.system.controller.DatabaseMigrationsController; | |||
import org.sonar.server.v2.api.system.controller.DefaultLivenessController; | |||
import org.sonar.server.v2.api.system.controller.HealthController; | |||
import org.sonar.server.v2.api.system.controller.LivenessController; | |||
@@ -102,6 +106,12 @@ public class PlatformLevel4WebConfig { | |||
return new HealthController(healthChecker, systemPasscode, nodeInformation, userSession); | |||
} | |||
@Bean | |||
public DatabaseMigrationsController databaseMigrationsController(DatabaseVersion databaseVersion, DatabaseMigrationState databaseMigrationState, | |||
Database database) { | |||
return new DatabaseMigrationsController(databaseVersion, databaseMigrationState, database); | |||
} | |||
@Bean | |||
public UsersSearchRestResponseGenerator usersSearchResponseGenerator(UserSession userSession) { | |||
return new UsersSearchRestResponseGenerator(userSession); |
@@ -19,11 +19,15 @@ | |||
*/ | |||
package org.sonar.server.v2.config; | |||
import org.sonar.db.Database; | |||
import org.sonar.server.common.health.DbConnectionNodeCheck; | |||
import org.sonar.server.common.platform.LivenessChecker; | |||
import org.sonar.server.common.platform.SafeModeLivenessCheckerImpl; | |||
import org.sonar.server.health.HealthChecker; | |||
import org.sonar.server.platform.db.migration.DatabaseMigrationState; | |||
import org.sonar.server.platform.db.migration.version.DatabaseVersion; | |||
import org.sonar.server.user.SystemPasscode; | |||
import org.sonar.server.v2.api.system.controller.DatabaseMigrationsController; | |||
import org.sonar.server.v2.api.system.controller.DefaultLivenessController; | |||
import org.sonar.server.v2.api.system.controller.HealthController; | |||
import org.sonar.server.v2.api.system.controller.LivenessController; | |||
@@ -51,4 +55,10 @@ public class SafeModeWebConfig { | |||
public HealthController healthController(HealthChecker healthChecker, SystemPasscode systemPasscode) { | |||
return new HealthController(healthChecker, systemPasscode); | |||
} | |||
@Bean | |||
public DatabaseMigrationsController databaseMigrationsController(DatabaseVersion databaseVersion, DatabaseMigrationState databaseMigrationState, | |||
Database database) { | |||
return new DatabaseMigrationsController(databaseVersion, databaseMigrationState, database); | |||
} | |||
} |
@@ -0,0 +1,152 @@ | |||
/* | |||
* SonarQube | |||
* Copyright (C) 2009-2024 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.v2.api.system.controller; | |||
import java.util.Date; | |||
import java.util.Optional; | |||
import org.junit.jupiter.api.BeforeEach; | |||
import org.junit.jupiter.api.Test; | |||
import org.mockito.Mockito; | |||
import org.sonar.db.Database; | |||
import org.sonar.db.dialect.Dialect; | |||
import org.sonar.server.platform.db.migration.DatabaseMigrationState; | |||
import org.sonar.server.platform.db.migration.version.DatabaseVersion; | |||
import org.sonar.server.v2.api.ControllerTester; | |||
import org.springframework.test.web.servlet.MockMvc; | |||
import static org.mockito.Mockito.mock; | |||
import static org.mockito.Mockito.when; | |||
import static org.sonar.server.platform.db.migration.DatabaseMigrationState.Status.FAILED; | |||
import static org.sonar.server.platform.db.migration.DatabaseMigrationState.Status.NONE; | |||
import static org.sonar.server.platform.db.migration.DatabaseMigrationState.Status.RUNNING; | |||
import static org.sonar.server.v2.WebApiEndpoints.DATABASE_MIGRATIONS_ENDPOINT; | |||
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; | |||
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content; | |||
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; | |||
class DatabaseMigrationsControllerTest { | |||
private static final Date SOME_DATE = new Date(); | |||
private final DatabaseVersion databaseVersion = mock(); | |||
private final DatabaseMigrationState migrationState = mock(); | |||
private final Dialect dialect = mock(Dialect.class); | |||
private final Database database = mock(); | |||
private final MockMvc mockMvc = ControllerTester.getMockMvc(new DatabaseMigrationsController(databaseVersion, migrationState, database)); | |||
@BeforeEach | |||
public void before() { | |||
when(database.getDialect()).thenReturn(dialect); | |||
when(databaseVersion.getVersion()).thenReturn(Optional.of(1L)); | |||
} | |||
@Test | |||
void getStatus_whenDatabaseHasNoVersion_return500() throws Exception { | |||
Mockito.reset(databaseVersion); | |||
when(databaseVersion.getVersion()).thenReturn(Optional.empty()); | |||
mockMvc.perform(get(DATABASE_MIGRATIONS_ENDPOINT)).andExpectAll(status().is5xxServerError(), | |||
content().json("{\"message\":\"Cannot connect to Database.\"}")); | |||
} | |||
@Test | |||
void getStatus_migrationNotNeeded_returnUpToDateStatus() throws Exception { | |||
when(databaseVersion.getStatus()).thenReturn(DatabaseVersion.Status.UP_TO_DATE); | |||
when(migrationState.getStatus()).thenReturn(NONE); | |||
mockMvc.perform(get(DATABASE_MIGRATIONS_ENDPOINT)).andExpectAll(status().isOk(), | |||
content().json("{\"status\":\"NO_MIGRATION\",\"message\":\"Database is up-to-date, no migration needed.\"}")); | |||
} | |||
@Test | |||
void getStatus_whenDowngradeRequired_returnNone() throws Exception { | |||
when(databaseVersion.getStatus()).thenReturn(DatabaseVersion.Status.REQUIRES_DOWNGRADE); | |||
when(migrationState.getStatus()).thenReturn(NONE); | |||
mockMvc.perform(get(DATABASE_MIGRATIONS_ENDPOINT)).andExpectAll(status().isOk(), | |||
content().json("{\"status\":\"NO_MIGRATION\",\"message\":\"Database is up-to-date, no migration needed.\"}")); | |||
} | |||
@Test | |||
void getStatus_whenDbRequiresUpgradeButDialectIsNotSupported_returnNotSupported() throws Exception { | |||
when(databaseVersion.getStatus()).thenReturn(DatabaseVersion.Status.FRESH_INSTALL); | |||
when(dialect.supportsMigration()).thenReturn(false); | |||
mockMvc.perform(get(DATABASE_MIGRATIONS_ENDPOINT)).andExpectAll(status().isOk(), | |||
content().json("{\"status\":\"NOT_SUPPORTED\",\"message\":\"Upgrade is not supported on embedded database.\"}")); | |||
} | |||
@Test | |||
void getStatus_whenDbMigrationsRunning_returnRunning() throws Exception { | |||
when(databaseVersion.getStatus()).thenReturn(DatabaseVersion.Status.REQUIRES_UPGRADE); | |||
when(dialect.supportsMigration()).thenReturn(true); | |||
when(migrationState.getStatus()).thenReturn(RUNNING); | |||
when(migrationState.getStartedAt()).thenReturn(SOME_DATE); | |||
mockMvc.perform(get(DATABASE_MIGRATIONS_ENDPOINT)).andExpectAll(status().isOk(), | |||
content().json("{\"status\":\"MIGRATION_RUNNING\",\"message\":\"Database migration is running.\"}")); | |||
} | |||
@Test | |||
void getStatus_whenDbMigrationsFailed_returnFailed() throws Exception { | |||
when(databaseVersion.getStatus()).thenReturn(DatabaseVersion.Status.REQUIRES_UPGRADE); | |||
when(dialect.supportsMigration()).thenReturn(true); | |||
when(migrationState.getStatus()).thenReturn(DatabaseMigrationState.Status.FAILED); | |||
when(migrationState.getStartedAt()).thenReturn(SOME_DATE); | |||
mockMvc.perform(get(DATABASE_MIGRATIONS_ENDPOINT)).andExpectAll(status().isOk(), | |||
content().json("{\"status\":\"MIGRATION_FAILED\",\"message\":\"Migration failed: %s.<br/> Please check logs.\"}")); | |||
} | |||
@Test | |||
void getStatus_whenDbMigrationsSucceeded_returnSucceeded() throws Exception { | |||
when(databaseVersion.getStatus()).thenReturn(DatabaseVersion.Status.REQUIRES_UPGRADE); | |||
when(dialect.supportsMigration()).thenReturn(true); | |||
when(migrationState.getStatus()).thenReturn(DatabaseMigrationState.Status.SUCCEEDED); | |||
when(migrationState.getStartedAt()).thenReturn(SOME_DATE); | |||
mockMvc.perform(get(DATABASE_MIGRATIONS_ENDPOINT)).andExpectAll(status().isOk(), | |||
content().json("{\"status\":\"MIGRATION_SUCCEEDED\",\"message\":\"Migration succeeded.\"}")); | |||
} | |||
@Test | |||
void getStatus_whenMigrationRequired_returnMigrationRequired() throws Exception { | |||
when(databaseVersion.getStatus()).thenReturn(DatabaseVersion.Status.REQUIRES_UPGRADE); | |||
when(dialect.supportsMigration()).thenReturn(true); | |||
when(migrationState.getStatus()).thenReturn(NONE); | |||
when(migrationState.getStartedAt()).thenReturn(SOME_DATE); | |||
mockMvc.perform(get(DATABASE_MIGRATIONS_ENDPOINT)).andExpectAll(status().isOk(), | |||
content().json("{\"status\":\"MIGRATION_REQUIRED\",\"message\":\"Database migration is required. DB migration " + | |||
"can be started using WS /api/system/migrate_db.\"}")); | |||
} | |||
@Test | |||
void getStatus_whenMigrationFailedWithError_IncludeErrorInResponse() throws Exception { | |||
when(databaseVersion.getStatus()).thenReturn(DatabaseVersion.Status.FRESH_INSTALL); | |||
when(dialect.supportsMigration()).thenReturn(true); | |||
when(migrationState.getStatus()).thenReturn(FAILED); | |||
when(migrationState.getStartedAt()).thenReturn(SOME_DATE); | |||
when(migrationState.getError()).thenReturn(new UnsupportedOperationException("error message")); | |||
mockMvc.perform(get(DATABASE_MIGRATIONS_ENDPOINT)).andExpectAll(status().isOk(), | |||
content().json("{\"status\":\"MIGRATION_FAILED\",\"message\":\"error message\"}")); | |||
} | |||
} |
@@ -22,6 +22,8 @@ package org.sonar.server.platform.ws; | |||
import org.sonar.api.utils.text.JsonWriter; | |||
import org.sonar.server.platform.db.migration.DatabaseMigrationState; | |||
import static org.sonar.server.platform.db.migration.DatabaseMigrationState.Status.FAILED; | |||
import static org.sonar.server.platform.db.migration.DatabaseMigrationState.Status.MIGRATION_REQUIRED; | |||
import static org.sonar.server.platform.db.migration.DatabaseMigrationState.Status.RUNNING; | |||
public class DbMigrationJsonWriter { | |||
@@ -29,21 +31,6 @@ public class DbMigrationJsonWriter { | |||
static final String FIELD_MESSAGE = "message"; | |||
static final String FIELD_STARTED_AT = "startedAt"; | |||
static final String STATUS_NO_MIGRATION = "NO_MIGRATION"; | |||
static final String STATUS_NOT_SUPPORTED = "NOT_SUPPORTED"; | |||
static final String STATUS_MIGRATION_RUNNING = "MIGRATION_RUNNING"; | |||
static final String STATUS_MIGRATION_FAILED = "MIGRATION_FAILED"; | |||
static final String STATUS_MIGRATION_SUCCEEDED = "MIGRATION_SUCCEEDED"; | |||
static final String STATUS_MIGRATION_REQUIRED = "MIGRATION_REQUIRED"; | |||
static final String NO_CONNECTION_TO_DB = "Cannot connect to Database."; | |||
static final String UNSUPPORTED_DATABASE_MIGRATION_STATUS = "Unsupported DatabaseMigration status"; | |||
static final String MESSAGE_NO_MIGRATION_ON_EMBEDDED_DATABASE = "Upgrade is not supported on embedded database."; | |||
static final String MESSAGE_MIGRATION_REQUIRED = "Database migration is required. DB migration can be started using WS /api/system/migrate_db."; | |||
static final String MESSAGE_STATUS_NONE = "Database is up-to-date, no migration needed."; | |||
static final String MESSAGE_STATUS_RUNNING = "Database migration is running."; | |||
static final String MESSAGE_STATUS_SUCCEEDED = "Migration succeeded."; | |||
static final String MESSAGE_STATUS_FAILED = "Migration failed: %s.<br/> Please check logs."; | |||
private DbMigrationJsonWriter() { | |||
// static methods only | |||
@@ -51,73 +38,43 @@ public class DbMigrationJsonWriter { | |||
static void write(JsonWriter json, DatabaseMigrationState databaseMigrationState) { | |||
json.beginObject() | |||
.prop(FIELD_STATE, statusToJson(databaseMigrationState.getStatus())) | |||
.prop(FIELD_MESSAGE, buildMessage(databaseMigrationState)) | |||
.prop(FIELD_STATE, databaseMigrationState.getStatus().toString()) | |||
.prop(FIELD_MESSAGE, writeMessageIncludingError(databaseMigrationState)) | |||
.propDateTime(FIELD_STARTED_AT, databaseMigrationState.getStartedAt()) | |||
.endObject(); | |||
} | |||
private static String writeMessageIncludingError(DatabaseMigrationState state) { | |||
if (state.getStatus() == FAILED) { | |||
Throwable error = state.getError(); | |||
return String.format(state.getStatus().getMessage(), error != null ? error.getMessage() : "No failure error"); | |||
} else { | |||
return state.getStatus().getMessage(); | |||
} | |||
} | |||
static void writeNotSupportedResponse(JsonWriter json) { | |||
json.beginObject() | |||
.prop(FIELD_STATE, STATUS_NOT_SUPPORTED) | |||
.prop(FIELD_MESSAGE, MESSAGE_NO_MIGRATION_ON_EMBEDDED_DATABASE) | |||
.prop(FIELD_STATE, DatabaseMigrationState.Status.STATUS_NOT_SUPPORTED.toString()) | |||
.prop(FIELD_MESSAGE, DatabaseMigrationState.Status.STATUS_NOT_SUPPORTED.getMessage()) | |||
.endObject(); | |||
} | |||
static void writeJustStartedResponse(JsonWriter json, DatabaseMigrationState databaseMigrationState) { | |||
json.beginObject() | |||
.prop(FIELD_STATE, statusToJson(RUNNING)) | |||
.prop(FIELD_MESSAGE, MESSAGE_STATUS_RUNNING) | |||
.prop(FIELD_STATE, RUNNING.toString()) | |||
.prop(FIELD_MESSAGE, RUNNING.getMessage()) | |||
.propDateTime(FIELD_STARTED_AT, databaseMigrationState.getStartedAt()) | |||
.endObject(); | |||
} | |||
static void writeMigrationRequiredResponse(JsonWriter json) { | |||
json.beginObject() | |||
.prop(FIELD_STATE, STATUS_MIGRATION_REQUIRED) | |||
.prop(FIELD_MESSAGE, MESSAGE_MIGRATION_REQUIRED) | |||
.prop(FIELD_STATE, MIGRATION_REQUIRED.toString()) | |||
.prop(FIELD_MESSAGE, MIGRATION_REQUIRED.getMessage()) | |||
.endObject(); | |||
} | |||
private static String statusToJson(DatabaseMigrationState.Status status) { | |||
switch (status) { | |||
case NONE: | |||
return STATUS_NO_MIGRATION; | |||
case RUNNING: | |||
return STATUS_MIGRATION_RUNNING; | |||
case FAILED: | |||
return STATUS_MIGRATION_FAILED; | |||
case SUCCEEDED: | |||
return STATUS_MIGRATION_SUCCEEDED; | |||
default: | |||
throw new IllegalArgumentException( | |||
"Unsupported DatabaseMigration.Status " + status + " can not be converted to JSON value"); | |||
} | |||
} | |||
private static String buildMessage(DatabaseMigrationState databaseMigrationState) { | |||
switch (databaseMigrationState.getStatus()) { | |||
case NONE: | |||
return MESSAGE_STATUS_NONE; | |||
case RUNNING: | |||
return MESSAGE_STATUS_RUNNING; | |||
case SUCCEEDED: | |||
return MESSAGE_STATUS_SUCCEEDED; | |||
case FAILED: | |||
return String.format(MESSAGE_STATUS_FAILED, failureMessage(databaseMigrationState)); | |||
default: | |||
return UNSUPPORTED_DATABASE_MIGRATION_STATUS; | |||
} | |||
} | |||
private static String failureMessage(DatabaseMigrationState databaseMigrationState) { | |||
Throwable failureError = databaseMigrationState.getError(); | |||
if (failureError == null) { | |||
return "No failure error"; | |||
} | |||
return failureError.getMessage(); | |||
} | |||
static String statusDescription() { | |||
return "State values are:" + | |||
"<ul>" + |
@@ -30,8 +30,8 @@ import org.sonar.server.platform.db.migration.DatabaseMigrationState; | |||
import org.sonar.server.platform.db.migration.version.DatabaseVersion; | |||
import static com.google.common.base.Preconditions.checkState; | |||
import static org.sonar.server.platform.ws.DbMigrationJsonWriter.NO_CONNECTION_TO_DB; | |||
import static org.sonar.server.platform.ws.DbMigrationJsonWriter.UNSUPPORTED_DATABASE_MIGRATION_STATUS; | |||
import static org.sonar.server.platform.db.migration.DatabaseMigrationState.NO_CONNECTION_TO_DB; | |||
import static org.sonar.server.platform.db.migration.DatabaseMigrationState.UNSUPPORTED_DATABASE_MIGRATION_STATUS; | |||
import static org.sonar.server.platform.ws.DbMigrationJsonWriter.statusDescription; | |||
import static org.sonar.server.platform.ws.DbMigrationJsonWriter.write; | |||
import static org.sonar.server.platform.ws.DbMigrationJsonWriter.writeMigrationRequiredResponse; |
@@ -31,8 +31,8 @@ import org.sonar.server.platform.db.migration.DatabaseMigration; | |||
import org.sonar.server.platform.db.migration.DatabaseMigrationState; | |||
import static com.google.common.base.Preconditions.checkState; | |||
import static org.sonar.server.platform.ws.DbMigrationJsonWriter.NO_CONNECTION_TO_DB; | |||
import static org.sonar.server.platform.ws.DbMigrationJsonWriter.UNSUPPORTED_DATABASE_MIGRATION_STATUS; | |||
import static org.sonar.server.platform.db.migration.DatabaseMigrationState.NO_CONNECTION_TO_DB; | |||
import static org.sonar.server.platform.db.migration.DatabaseMigrationState.UNSUPPORTED_DATABASE_MIGRATION_STATUS; | |||
import static org.sonar.server.platform.ws.DbMigrationJsonWriter.statusDescription; | |||
import static org.sonar.server.platform.ws.DbMigrationJsonWriter.write; | |||
import static org.sonar.server.platform.ws.DbMigrationJsonWriter.writeJustStartedResponse; |
@@ -47,20 +47,10 @@ import static org.mockito.Mockito.mock; | |||
import static org.mockito.Mockito.reset; | |||
import static org.mockito.Mockito.when; | |||
import static org.sonar.server.platform.db.migration.DatabaseMigrationState.Status.FAILED; | |||
import static org.sonar.server.platform.db.migration.DatabaseMigrationState.Status.MIGRATION_REQUIRED; | |||
import static org.sonar.server.platform.db.migration.DatabaseMigrationState.Status.NONE; | |||
import static org.sonar.server.platform.db.migration.DatabaseMigrationState.Status.RUNNING; | |||
import static org.sonar.server.platform.db.migration.DatabaseMigrationState.Status.SUCCEEDED; | |||
import static org.sonar.server.platform.ws.DbMigrationJsonWriter.MESSAGE_MIGRATION_REQUIRED; | |||
import static org.sonar.server.platform.ws.DbMigrationJsonWriter.MESSAGE_NO_MIGRATION_ON_EMBEDDED_DATABASE; | |||
import static org.sonar.server.platform.ws.DbMigrationJsonWriter.MESSAGE_STATUS_NONE; | |||
import static org.sonar.server.platform.ws.DbMigrationJsonWriter.MESSAGE_STATUS_RUNNING; | |||
import static org.sonar.server.platform.ws.DbMigrationJsonWriter.MESSAGE_STATUS_SUCCEEDED; | |||
import static org.sonar.server.platform.ws.DbMigrationJsonWriter.STATUS_MIGRATION_FAILED; | |||
import static org.sonar.server.platform.ws.DbMigrationJsonWriter.STATUS_MIGRATION_REQUIRED; | |||
import static org.sonar.server.platform.ws.DbMigrationJsonWriter.STATUS_MIGRATION_RUNNING; | |||
import static org.sonar.server.platform.ws.DbMigrationJsonWriter.STATUS_MIGRATION_SUCCEEDED; | |||
import static org.sonar.server.platform.ws.DbMigrationJsonWriter.STATUS_NOT_SUPPORTED; | |||
import static org.sonar.server.platform.ws.DbMigrationJsonWriter.STATUS_NO_MIGRATION; | |||
import static org.sonar.test.JsonAssert.assertJson; | |||
@RunWith(DataProviderRunner.class) | |||
@@ -111,7 +101,7 @@ public class DbMigrationStatusActionTest { | |||
TestResponse response = tester.newRequest().execute(); | |||
assertJson(response.getInput()).isSimilarTo(expectedResponse(STATUS_NO_MIGRATION, MESSAGE_STATUS_NONE)); | |||
assertJson(response.getInput()).isSimilarTo(expectedResponse(NONE.toString(), NONE.getMessage())); | |||
} | |||
// this test will raise a IllegalArgumentException when an unsupported value is added to the Status enum | |||
@@ -133,7 +123,7 @@ public class DbMigrationStatusActionTest { | |||
TestResponse response = tester.newRequest().execute(); | |||
assertJson(response.getInput()).isSimilarTo(expectedResponse(STATUS_NO_MIGRATION, MESSAGE_STATUS_NONE)); | |||
assertJson(response.getInput()).isSimilarTo(expectedResponse(NONE.toString(), NONE.getMessage())); | |||
} | |||
@Test | |||
@@ -144,7 +134,7 @@ public class DbMigrationStatusActionTest { | |||
TestResponse response = tester.newRequest().execute(); | |||
assertJson(response.getInput()).isSimilarTo(expectedResponse(STATUS_NOT_SUPPORTED, MESSAGE_NO_MIGRATION_ON_EMBEDDED_DATABASE)); | |||
assertJson(response.getInput()).isSimilarTo(expectedResponse(Status.STATUS_NOT_SUPPORTED.toString(), Status.STATUS_NOT_SUPPORTED.getMessage())); | |||
} | |||
@Test | |||
@@ -157,7 +147,7 @@ public class DbMigrationStatusActionTest { | |||
TestResponse response = tester.newRequest().execute(); | |||
assertJson(response.getInput()).isSimilarTo(expectedResponse(STATUS_MIGRATION_RUNNING, MESSAGE_STATUS_RUNNING, SOME_DATE)); | |||
assertJson(response.getInput()).isSimilarTo(expectedResponse(RUNNING.toString(), RUNNING.getMessage(), SOME_DATE)); | |||
} | |||
@Test | |||
@@ -171,7 +161,7 @@ public class DbMigrationStatusActionTest { | |||
TestResponse response = tester.newRequest().execute(); | |||
assertJson(response.getInput()).isSimilarTo(expectedResponse(STATUS_MIGRATION_FAILED, failedMsg(SOME_THROWABLE_MSG), SOME_DATE)); | |||
assertJson(response.getInput()).isSimilarTo(expectedResponse(FAILED.toString(), failedMsg(SOME_THROWABLE_MSG), SOME_DATE)); | |||
} | |||
@Test | |||
@@ -185,7 +175,7 @@ public class DbMigrationStatusActionTest { | |||
TestResponse response = tester.newRequest().execute(); | |||
assertJson(response.getInput()).isSimilarTo(expectedResponse(STATUS_MIGRATION_FAILED, failedMsg(DEFAULT_ERROR_MSG), SOME_DATE)); | |||
assertJson(response.getInput()).isSimilarTo(expectedResponse(FAILED.toString(), failedMsg(DEFAULT_ERROR_MSG), SOME_DATE)); | |||
} | |||
@Test | |||
@@ -198,7 +188,7 @@ public class DbMigrationStatusActionTest { | |||
TestResponse response = tester.newRequest().execute(); | |||
assertJson(response.getInput()).isSimilarTo(expectedResponse(STATUS_MIGRATION_SUCCEEDED, MESSAGE_STATUS_SUCCEEDED, SOME_DATE)); | |||
assertJson(response.getInput()).isSimilarTo(expectedResponse(SUCCEEDED.toString(), SUCCEEDED.getMessage(), SOME_DATE)); | |||
} | |||
@Test | |||
@@ -211,7 +201,7 @@ public class DbMigrationStatusActionTest { | |||
TestResponse response = tester.newRequest().execute(); | |||
assertJson(response.getInput()).isSimilarTo(expectedResponse(STATUS_MIGRATION_REQUIRED, MESSAGE_MIGRATION_REQUIRED)); | |||
assertJson(response.getInput()).isSimilarTo(expectedResponse(MIGRATION_REQUIRED.toString(), MIGRATION_REQUIRED.getMessage())); | |||
} | |||
@DataProvider |
@@ -32,7 +32,6 @@ import org.sonar.server.ws.WsActionTester; | |||
import static com.google.common.base.Predicates.in; | |||
import static com.google.common.base.Predicates.not; | |||
import static com.google.common.collect.ImmutableSet.of; | |||
import static com.google.common.collect.Iterables.filter; | |||
import static java.util.Arrays.asList; | |||
import static org.assertj.core.api.Assertions.assertThat; | |||
@@ -49,9 +48,9 @@ public class StatusActionTest { | |||
private static final String STATUS_MIGRATION_NEEDED = "DB_MIGRATION_NEEDED"; | |||
private static final String STATUS_MIGRATION_RUNNING = "DB_MIGRATION_RUNNING"; | |||
private static final String STATUS_RESTARTING = "RESTARTING"; | |||
private static final Set<DatabaseMigrationState.Status> SUPPORTED_DATABASE_MIGRATION_STATUSES = of(DatabaseMigrationState.Status.FAILED, DatabaseMigrationState.Status.NONE, | |||
DatabaseMigrationState.Status.SUCCEEDED, DatabaseMigrationState.Status.RUNNING); | |||
private static final Set<Platform.Status> SUPPORTED_PLATFORM_STATUSES = of(Platform.Status.BOOTING, Platform.Status.SAFEMODE, Platform.Status.STARTING, Platform.Status.UP); | |||
private static final Set<DatabaseMigrationState.Status> SUPPORTED_DATABASE_MIGRATION_STATUSES = Set.of(DatabaseMigrationState.Status.FAILED, DatabaseMigrationState.Status.NONE, | |||
DatabaseMigrationState.Status.SUCCEEDED, DatabaseMigrationState.Status.RUNNING, DatabaseMigrationState.Status.STATUS_NOT_SUPPORTED, DatabaseMigrationState.Status.MIGRATION_REQUIRED); | |||
private static final Set<Platform.Status> SUPPORTED_PLATFORM_STATUSES = Set.of(Platform.Status.BOOTING, Platform.Status.SAFEMODE, Platform.Status.STARTING, Platform.Status.UP); | |||
private static Server server = new Dummy51Server(); | |||
private DatabaseMigrationState migrationState = mock(DatabaseMigrationState.class); |