From: Sébastien Lesaint Date: Tue, 5 May 2015 08:13:54 +0000 (+0200) Subject: SONAR-6365 add Java WS system status (no DB check) X-Git-Tag: 5.2-RC1~1997 X-Git-Url: https://source.dussan.org/?a=commitdiff_plain;h=9234f4d645764a01245ed27ee8da8a267a867c24;p=sonarqube.git SONAR-6365 add Java WS system status (no DB check) --- diff --git a/server/sonar-server/src/main/java/org/sonar/server/platform/ServerComponents.java b/server/sonar-server/src/main/java/org/sonar/server/platform/ServerComponents.java index c8594ed7438..4c76a9e1fef 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/platform/ServerComponents.java +++ b/server/sonar-server/src/main/java/org/sonar/server/platform/ServerComponents.java @@ -212,6 +212,7 @@ import org.sonar.server.platform.ws.MigrateDbSystemWsAction; import org.sonar.server.platform.ws.ServerWs; import org.sonar.server.platform.ws.SystemInfoWsAction; import org.sonar.server.platform.ws.SystemRestartWsAction; +import org.sonar.server.platform.ws.SystemStatusWsAction; import org.sonar.server.platform.ws.SystemWs; import org.sonar.server.platform.ws.UpgradesSystemWsAction; import org.sonar.server.plugins.InstalledPluginReferentialFactory; @@ -558,6 +559,7 @@ class ServerComponents { ThreadLocalDatabaseSessionFactory.class, // Server WS + SystemStatusWsAction.class, MigrateDbSystemWsAction.class, SystemWs.class, @@ -935,6 +937,7 @@ class ServerComponents { SystemInfoWsAction.class, UpgradesSystemWsAction.class, MigrateDbSystemWsAction.class, + SystemStatusWsAction.class, SystemWs.class, SystemMonitor.class, SonarQubeMonitor.class, diff --git a/server/sonar-server/src/main/java/org/sonar/server/platform/ws/SystemStatusWsAction.java b/server/sonar-server/src/main/java/org/sonar/server/platform/ws/SystemStatusWsAction.java new file mode 100644 index 00000000000..6b4eadc3334 --- /dev/null +++ b/server/sonar-server/src/main/java/org/sonar/server/platform/ws/SystemStatusWsAction.java @@ -0,0 +1,127 @@ +/* + * SonarQube, open source software quality management tool. + * Copyright (C) 2008-2014 SonarSource + * mailto:contact AT sonarsource DOT com + * + * SonarQube 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. + * + * SonarQube 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.ws; + +import org.sonar.api.platform.Server; +import org.sonar.api.server.ws.Request; +import org.sonar.api.server.ws.Response; +import org.sonar.api.server.ws.WebService; +import org.sonar.api.utils.text.JsonWriter; +import org.sonar.server.db.migrations.DatabaseMigration; +import org.sonar.server.platform.Platform; + +import com.google.common.io.Resources; + +/** + * Implementation of the {@code status} action for the System WebService. + */ +public class SystemStatusWsAction implements SystemWsAction { + + private final Server server; + private final DatabaseMigration databaseMigration; + private final Platform platform; + + public SystemStatusWsAction(Server server, DatabaseMigration databaseMigration, Platform platform) { + this.server = server; + this.databaseMigration = databaseMigration; + this.platform = platform; + } + + @Override + public void define(WebService.NewController controller) { + controller.createAction("status") + .setDescription("Get the server status:" + + "") + .setSince("5.2") + .setResponseExample(Resources.getResource(this.getClass(), "example-status.json")) + .setHandler(this); + } + + @Override + public void handle(Request request, Response response) throws Exception { + JsonWriter json = response.newJsonWriter(); + writeJson(json); + json.close(); + } + + private void writeJson(JsonWriter json) { + Status status = computeStatus(); + + json.beginObject(); + json.prop("id", server.getId()); + json.prop("version", server.getVersion()); + json.prop("status", status.toString()); + json.endObject(); + } + + private Status computeStatus() { + if (!isConnectedToDB()) { + return Status.DOWN; + } + + Platform.Status platformStatus = platform.status(); + switch (platformStatus) { + case BOOTING: + // can not happen since there can not even exist an instance of the current class + // unless the Platform's status is UP or SAFEMODE + return Status.DOWN; + case UP: + return Status.UP; + case SAFEMODE: + return computeFromDbMigrationStatus(); + default: + throw new IllegalArgumentException("Unsupported Platform.Status " + platformStatus); + } + } + + private boolean isConnectedToDB() { + // TODO check DB connection is up + return true; + } + + private Status computeFromDbMigrationStatus() { + DatabaseMigration.Status databaseMigrationStatus = databaseMigration.status(); + switch (databaseMigrationStatus) { + case NONE: + return Status.DB_MIGRATION_NEEDED; + case RUNNING: + return Status.DB_MIGRATION_RUNNING; + case FAILED: + return Status.DOWN; + case SUCCEEDED: + // status of Platform is supposed to be UP _before_ DatabaseMigration status becomes UP too + // so, in theory, this case can not happen + return Status.UP; + default: + throw new IllegalArgumentException("Unsupported DatabaseMigration.Status " + databaseMigrationStatus); + } + } + + private enum Status { + UP, DOWN, DB_MIGRATION_NEEDED, DB_MIGRATION_RUNNING + } + +} diff --git a/server/sonar-server/src/main/resources/org/sonar/server/platform/ws/example-status.json b/server/sonar-server/src/main/resources/org/sonar/server/platform/ws/example-status.json new file mode 100644 index 00000000000..c73b420dcbd --- /dev/null +++ b/server/sonar-server/src/main/resources/org/sonar/server/platform/ws/example-status.json @@ -0,0 +1,5 @@ +{ + "id": "20150504120436", + "version": "5.1", + "status": "UP" +} diff --git a/server/sonar-server/src/test/java/org/sonar/server/platform/ws/SystemStatusWsActionTest.java b/server/sonar-server/src/test/java/org/sonar/server/platform/ws/SystemStatusWsActionTest.java new file mode 100644 index 00000000000..2abcce7797e --- /dev/null +++ b/server/sonar-server/src/test/java/org/sonar/server/platform/ws/SystemStatusWsActionTest.java @@ -0,0 +1,198 @@ +/* + * SonarQube, open source software quality management tool. + * Copyright (C) 2008-2014 SonarSource + * mailto:contact AT sonarsource DOT com + * + * SonarQube 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. + * + * SonarQube 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.ws; + +import java.io.File; +import java.util.Date; +import java.util.Set; +import org.junit.Test; +import org.sonar.api.platform.Server; +import org.sonar.api.server.ws.Request; +import org.sonar.api.server.ws.WebService; +import org.sonar.server.db.migrations.DatabaseMigration; +import org.sonar.server.platform.Platform; +import org.sonar.server.ws.WsTester; + +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; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; +import static org.sonar.test.JsonAssert.assertJson; + +public class SystemStatusWsActionTest { + private static final String DUMMY_CONTROLLER_KEY = "dummy"; + + private static final String SERVER_ID = "20150504120436"; + private static final String SERVER_VERSION = "5.1"; + private static final String STATUS_UP = "UP"; + private static final String STATUS_DOWN = "DOWN"; + private static final String STATUS_MIGRATION_NEEDED = "DB_MIGRATION_NEEDED"; + private static final String STATUS_MIGRATION_RUNNING = "DB_MIGRATION_RUNNING"; + private static final Set SUPPORTED_DATABASE_MIGRATION_STATUSES = of(DatabaseMigration.Status.FAILED, DatabaseMigration.Status.NONE, + DatabaseMigration.Status.SUCCEEDED, DatabaseMigration.Status.RUNNING); + private static final Set SUPPORTED_PLATFORM_STATUSES = of(Platform.Status.BOOTING, Platform.Status.SAFEMODE, Platform.Status.UP); + + private static Server server = new Dummy51Server(); + private DatabaseMigration databaseMigration = mock(DatabaseMigration.class); + private Platform platform = mock(Platform.class); + private Request request = mock(Request.class); + private SystemStatusWsAction underTest = new SystemStatusWsAction(server, databaseMigration, platform); + + @Test + public void action_status_is_defined() throws Exception { + WsTester wsTester = new WsTester(); + WebService.NewController newController = wsTester.context().createController(DUMMY_CONTROLLER_KEY); + + underTest.define(newController); + newController.done(); + + WebService.Controller controller = wsTester.controller(DUMMY_CONTROLLER_KEY); + assertThat(controller.actions()).extracting("key").containsExactly("status"); + + WebService.Action action = controller.actions().iterator().next(); + assertThat(action.isPost()).isFalse(); + assertThat(action.description()).isNotEmpty(); + assertThat(action.responseExample()).isNotNull(); + + assertThat(action.params()).isEmpty(); + } + + @Test + public void verify_example() throws Exception { + when(platform.status()).thenReturn(Platform.Status.UP); + + WsTester.TestResponse response = new WsTester.TestResponse(); + underTest.handle(request, response); + + assertJson(response.outputAsString()).isSimilarTo(getClass().getResource("example-status.json")); + } + + @Test + public void status_is_UP_if_platform_is_UP_whatever_databaseMigration_status_is() throws Exception { + for (DatabaseMigration.Status databaseMigrationStatus : DatabaseMigration.Status.values()) { + verifyStatus(Platform.Status.UP, databaseMigrationStatus, STATUS_UP); + } + } + + @Test + public void status_is_DOWN_if_platform_is_BOOTING_whatever_databaseMigration_status_is() throws Exception { + for (DatabaseMigration.Status databaseMigrationStatus : DatabaseMigration.Status.values()) { + verifyStatus(Platform.Status.BOOTING, databaseMigrationStatus, STATUS_DOWN); + } + } + + @Test + public void status_is_DB_MIGRATION_NEEDED_if_platform_is_SAFEMODE_and_databaseMigration_is_NONE() throws Exception { + verifyStatus(Platform.Status.SAFEMODE, DatabaseMigration.Status.NONE, STATUS_MIGRATION_NEEDED); + } + + @Test + public void status_is_DB_MIGRATION_RUNNING_if_platform_is_SAFEMODE_and_databaseMigration_is_RUNNING() throws Exception { + verifyStatus(Platform.Status.SAFEMODE, DatabaseMigration.Status.RUNNING, STATUS_MIGRATION_RUNNING); + } + + @Test + public void status_is_UP_if_platform_is_SAFEMODE_and_databaseMigration_is_SUCCEEDED() throws Exception { + verifyStatus(Platform.Status.SAFEMODE, DatabaseMigration.Status.SUCCEEDED, STATUS_UP); + } + + @Test + public void status_is_DOWN_if_platform_is_SAFEMODE_and_databaseMigration_is_FAILED() throws Exception { + verifyStatus(Platform.Status.SAFEMODE, DatabaseMigration.Status.FAILED, STATUS_DOWN); + } + + @Test + public void safety_test_for_new_platform_status() throws Exception { + for (Platform.Status platformStatus : filter(asList(Platform.Status.values()), not(in(SUPPORTED_PLATFORM_STATUSES)))) { + for (DatabaseMigration.Status databaseMigrationStatus : DatabaseMigration.Status.values()) { + verifyStatus(platformStatus, databaseMigrationStatus, STATUS_DOWN); + } + } + } + + @Test + public void safety_test_for_new_databaseMigration_status_when_platform_is_SAFEMODE() throws Exception { + for (DatabaseMigration.Status databaseMigrationStatus : filter(asList(DatabaseMigration.Status.values()), not(in(SUPPORTED_DATABASE_MIGRATION_STATUSES)))) { + when(platform.status()).thenReturn(Platform.Status.SAFEMODE); + when(databaseMigration.status()).thenReturn(databaseMigrationStatus); + + WsTester.TestResponse response = new WsTester.TestResponse(); + underTest.handle(request, response); + } + } + + private void verifyStatus(Platform.Status platformStatus, DatabaseMigration.Status databaseMigrationStatus, String expectedStatus) throws Exception { + when(platform.status()).thenReturn(platformStatus); + when(databaseMigration.status()).thenReturn(databaseMigrationStatus); + + WsTester.TestResponse response = new WsTester.TestResponse(); + underTest.handle(request, response); + + assertJson(response.outputAsString()).isSimilarTo("{" + + " \"status\": \"" + expectedStatus + "\"\n" + + "}"); + } + + private static class Dummy51Server extends Server { + @Override + public String getId() { + return SERVER_ID; + } + + @Override + public String getVersion() { + return SERVER_VERSION; + } + + @Override + public Date getStartedAt() { + throw new UnsupportedOperationException(); + } + + @Override + public File getRootDir() { + throw new UnsupportedOperationException(); + } + + @Override + public File getDeployDir() { + throw new UnsupportedOperationException(); + } + + @Override + public String getContextPath() { + throw new UnsupportedOperationException(); + } + + @Override + public String getURL() { + throw new UnsupportedOperationException(); + } + + @Override + public String getPermanentServerId() { + throw new UnsupportedOperationException(); + } + } +}