]> source.dussan.org Git - sonarqube.git/commitdiff
SONAR-6365 add Java WS system status (no DB check)
authorSébastien Lesaint <sebastien.lesaint@sonarsource.com>
Tue, 5 May 2015 08:13:54 +0000 (10:13 +0200)
committerSébastien Lesaint <sebastien.lesaint@sonarsource.com>
Mon, 11 May 2015 10:28:54 +0000 (12:28 +0200)
server/sonar-server/src/main/java/org/sonar/server/platform/ServerComponents.java
server/sonar-server/src/main/java/org/sonar/server/platform/ws/SystemStatusWsAction.java [new file with mode: 0644]
server/sonar-server/src/main/resources/org/sonar/server/platform/ws/example-status.json [new file with mode: 0644]
server/sonar-server/src/test/java/org/sonar/server/platform/ws/SystemStatusWsActionTest.java [new file with mode: 0644]

index c8594ed7438171f417591e71c7d51ae0f454ab76..4c76a9e1fef217391173ebcd1c54ba67bde31cf1 100644 (file)
@@ -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 (file)
index 0000000..6b4eadc
--- /dev/null
@@ -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:" +
+        "<ul>" +
+        "<li>UP: SonarQube instance is up and running</li>" +
+        "<li>DOWN: SonarQube instance is up but not running because SQ can not connect to database or " +
+        "migration has failed (refer to WS /api/system/migrate_db for details) or some other reason (check logs).</li>" +
+        "<li>DB_MIGRATION_NEEDED: database migration is required. DB migration can be started using WS /api/system/migrate_db.</li>" +
+        "<li>DB_MIGRATION_RUNNING: DB migration is running (refer to WS /api/system/migrate_db for details)</li>" +
+        "</ul>")
+      .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 (file)
index 0000000..c73b420
--- /dev/null
@@ -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 (file)
index 0000000..2abcce7
--- /dev/null
@@ -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<DatabaseMigration.Status> SUPPORTED_DATABASE_MIGRATION_STATUSES = of(DatabaseMigration.Status.FAILED, DatabaseMigration.Status.NONE,
+    DatabaseMigration.Status.SUCCEEDED, DatabaseMigration.Status.RUNNING);
+  private static final Set<Platform.Status> 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();
+    }
+  }
+}