From e1af4751c2d9b79d790810b5c9929557add0adeb Mon Sep 17 00:00:00 2001 From: =?utf8?q?S=C3=A9bastien=20Lesaint?= Date: Mon, 25 Apr 2016 16:08:36 +0200 Subject: [PATCH] =?utf8?q?SONAR=E2=88=927565=20add=20RESTARTING=20status?= =?utf8?q?=20in=20/api/system/status?= MIME-Version: 1.0 Content-Type: text/plain; charset=utf8 Content-Transfer-Encoding: 8bit --- .../sonar/server/app/RestartFlagHolder.java | 42 +++++++++++++++++++ .../server/app/RestartFlagHolderImpl.java | 41 ++++++++++++++++++ .../org/sonar/server/platform/Platform.java | 6 ++- .../platformlevel/PlatformLevel1.java | 2 + .../server/platform/ws/RestartAction.java | 15 +++++-- .../server/platform/ws/StatusAction.java | 11 +++-- .../server/platform/ws/RestartActionTest.java | 38 +++++++++++++++-- .../server/platform/ws/StatusActionTest.java | 18 +++++++- .../server/platform/ws/SystemWsTest.java | 3 +- 9 files changed, 161 insertions(+), 15 deletions(-) create mode 100644 server/sonar-server/src/main/java/org/sonar/server/app/RestartFlagHolder.java create mode 100644 server/sonar-server/src/main/java/org/sonar/server/app/RestartFlagHolderImpl.java diff --git a/server/sonar-server/src/main/java/org/sonar/server/app/RestartFlagHolder.java b/server/sonar-server/src/main/java/org/sonar/server/app/RestartFlagHolder.java new file mode 100644 index 00000000000..cd868258633 --- /dev/null +++ b/server/sonar-server/src/main/java/org/sonar/server/app/RestartFlagHolder.java @@ -0,0 +1,42 @@ +/* + * SonarQube + * Copyright (C) 2009-2016 SonarSource SA + * mailto:contact 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.app; + +/** + * Holds a boolean flag representing the restarting status of the WebServer. + * This boolean is {@code false} by default and can safely be changed concurrently using methods {@link #set()} and + * {@link #unset()}. + */ +public interface RestartFlagHolder { + /** + * @return whether restarting flag has been set or not. + */ + boolean isRestarting(); + + /** + * Sets the restarting flag to {@code true}, no matter it already is or not. + */ + void set(); + + /** + * Sets the restarting flag to {@code false}, no matter it already is or not. + */ + void unset(); +} diff --git a/server/sonar-server/src/main/java/org/sonar/server/app/RestartFlagHolderImpl.java b/server/sonar-server/src/main/java/org/sonar/server/app/RestartFlagHolderImpl.java new file mode 100644 index 00000000000..8b9af93053e --- /dev/null +++ b/server/sonar-server/src/main/java/org/sonar/server/app/RestartFlagHolderImpl.java @@ -0,0 +1,41 @@ +/* + * SonarQube + * Copyright (C) 2009-2016 SonarSource SA + * mailto:contact 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.app; + +import java.util.concurrent.atomic.AtomicBoolean; + +public class RestartFlagHolderImpl implements RestartFlagHolder { + private final AtomicBoolean restarting = new AtomicBoolean(false); + + @Override + public boolean isRestarting() { + return restarting.get(); + } + + @Override + public void set() { + restarting.set(true); + } + + @Override + public void unset() { + restarting.set(false); + } +} diff --git a/server/sonar-server/src/main/java/org/sonar/server/platform/Platform.java b/server/sonar-server/src/main/java/org/sonar/server/platform/Platform.java index a801164fd20..68f4dadbd91 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/platform/Platform.java +++ b/server/sonar-server/src/main/java/org/sonar/server/platform/Platform.java @@ -157,10 +157,12 @@ public class Platform { if (!started) { return Status.BOOTING; } - if (levelSafeMode != null && currentLevel == levelSafeMode) { + PlatformLevel current = this.currentLevel; + PlatformLevel levelSafe = this.levelSafeMode; + if (levelSafe != null && current == levelSafe) { return Status.SAFEMODE; } - if (currentLevel == level4) { + if (current == level4) { return Status.UP; } return Status.BOOTING; diff --git a/server/sonar-server/src/main/java/org/sonar/server/platform/platformlevel/PlatformLevel1.java b/server/sonar-server/src/main/java/org/sonar/server/platform/platformlevel/PlatformLevel1.java index a64ef830bf2..6f2a5b1b667 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/platform/platformlevel/PlatformLevel1.java +++ b/server/sonar-server/src/main/java/org/sonar/server/platform/platformlevel/PlatformLevel1.java @@ -36,6 +36,7 @@ import org.sonar.db.semaphore.SemaphoresImpl; import org.sonar.db.version.DatabaseVersion; import org.sonar.db.version.MigrationStepModule; import org.sonar.server.app.ProcessCommandWrapperImpl; +import org.sonar.server.app.RestartFlagHolderImpl; import org.sonar.server.db.EmbeddedDatabaseFactory; import org.sonar.server.issue.index.IssueIndex; import org.sonar.server.platform.DatabaseServerCompatibility; @@ -70,6 +71,7 @@ public class PlatformLevel1 extends PlatformLevel { add( SonarQubeVersionFactory.create(System2.INSTANCE), ProcessCommandWrapperImpl.class, + RestartFlagHolderImpl.class, WebServerSettings.class, ServerImpl.class, UuidFactoryImpl.INSTANCE, diff --git a/server/sonar-server/src/main/java/org/sonar/server/platform/ws/RestartAction.java b/server/sonar-server/src/main/java/org/sonar/server/platform/ws/RestartAction.java index 69fba19b975..58c87ac0750 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/platform/ws/RestartAction.java +++ b/server/sonar-server/src/main/java/org/sonar/server/platform/ws/RestartAction.java @@ -27,6 +27,7 @@ import org.sonar.api.utils.log.Logger; import org.sonar.api.utils.log.Loggers; import org.sonar.api.web.UserRole; import org.sonar.server.app.ProcessCommandWrapper; +import org.sonar.server.app.RestartFlagHolder; import org.sonar.server.platform.Platform; import org.sonar.server.user.UserSession; @@ -41,12 +42,14 @@ public class RestartAction implements SystemWsAction { private final Settings settings; private final Platform platform; private final ProcessCommandWrapper processCommandWrapper; + private final RestartFlagHolder restartFlagHolder; - public RestartAction(UserSession userSession, Settings settings, Platform platform, ProcessCommandWrapper processCommandWrapper) { + public RestartAction(UserSession userSession, Settings settings, Platform platform, ProcessCommandWrapper processCommandWrapper, RestartFlagHolder restartFlagHolder) { this.userSession = userSession; this.settings = settings; this.platform = platform; this.processCommandWrapper = processCommandWrapper; + this.restartFlagHolder = restartFlagHolder; } @Override @@ -65,11 +68,17 @@ public class RestartAction implements SystemWsAction { public void handle(Request request, Response response) { if (settings.getBoolean("sonar.web.dev")) { LOGGER.info("Fast restarting WebServer..."); - platform.restart(); - LOGGER.info("WebServer restarted"); + restartFlagHolder.set(); + try { + platform.restart(); + LOGGER.info("WebServer restarted"); + } finally { + restartFlagHolder.unset(); + } } else { userSession.checkPermission(UserRole.ADMIN); LOGGER.info("SonarQube restart requested by {}", userSession.getLogin()); + restartFlagHolder.set(); processCommandWrapper.requestSQRestart(); } response.noContent(); diff --git a/server/sonar-server/src/main/java/org/sonar/server/platform/ws/StatusAction.java b/server/sonar-server/src/main/java/org/sonar/server/platform/ws/StatusAction.java index b7b6a99f2cc..57b6c420553 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/platform/ws/StatusAction.java +++ b/server/sonar-server/src/main/java/org/sonar/server/platform/ws/StatusAction.java @@ -31,6 +31,7 @@ import org.sonar.db.DbClient; import org.sonar.db.DbSession; import org.sonar.db.IsAliveMapper; import org.sonar.db.version.DatabaseMigration; +import org.sonar.server.app.RestartFlagHolder; import org.sonar.server.platform.Platform; /** @@ -44,12 +45,14 @@ public class StatusAction implements SystemWsAction { private final DatabaseMigration databaseMigration; private final Platform platform; private final DbClient dbClient; + private final RestartFlagHolder restartFlagHolder; - public StatusAction(Server server, DatabaseMigration databaseMigration, Platform platform, DbClient dbClient) { + public StatusAction(Server server, DatabaseMigration databaseMigration, Platform platform, DbClient dbClient, RestartFlagHolder restartFlagHolder) { this.server = server; this.databaseMigration = databaseMigration; this.platform = platform; this.dbClient = dbClient; + this.restartFlagHolder = restartFlagHolder; } @Override @@ -60,6 +63,8 @@ public class StatusAction implements SystemWsAction { "
  • UP: SonarQube instance is up and running
  • " + "
  • 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).
  • " + + "
  • RESTARTING: SonarQube instance is still up but a restart has been requested " + + "(refer to WS /api/system/restart for details).
  • " + "
  • DB_MIGRATION_NEEDED: database migration is required. DB migration can be started using WS /api/system/migrate_db.
  • " + "
  • DB_MIGRATION_RUNNING: DB migration is running (refer to WS /api/system/migrate_db for details)
  • " + "") @@ -97,7 +102,7 @@ public class StatusAction implements SystemWsAction { // unless the Platform's status is UP or SAFEMODE return Status.DOWN; case UP: - return Status.UP; + return restartFlagHolder.isRestarting() ? Status.RESTARTING : Status.UP; case SAFEMODE: return computeFromDbMigrationStatus(); default: @@ -133,7 +138,7 @@ public class StatusAction implements SystemWsAction { } private enum Status { - UP, DOWN, DB_MIGRATION_NEEDED, DB_MIGRATION_RUNNING + UP, DOWN, DB_MIGRATION_NEEDED, DB_MIGRATION_RUNNING, RESTARTING } } diff --git a/server/sonar-server/src/test/java/org/sonar/server/platform/ws/RestartActionTest.java b/server/sonar-server/src/test/java/org/sonar/server/platform/ws/RestartActionTest.java index 8d08facfdf5..c45b101dd55 100644 --- a/server/sonar-server/src/test/java/org/sonar/server/platform/ws/RestartActionTest.java +++ b/server/sonar-server/src/test/java/org/sonar/server/platform/ws/RestartActionTest.java @@ -22,11 +22,14 @@ package org.sonar.server.platform.ws; import org.junit.Rule; import org.junit.Test; import org.junit.rules.ExpectedException; +import org.mockito.InOrder; +import org.mockito.Mockito; import org.sonar.api.config.Settings; import org.sonar.api.utils.log.LogTester; import org.sonar.api.utils.log.LoggerLevel; import org.sonar.api.web.UserRole; import org.sonar.server.app.ProcessCommandWrapper; +import org.sonar.server.app.RestartFlagHolder; import org.sonar.server.exceptions.ForbiddenException; import org.sonar.server.platform.Platform; import org.sonar.server.tester.UserSessionRule; @@ -34,8 +37,8 @@ import org.sonar.server.ws.WsActionTester; import org.sonar.server.ws.WsTester; import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.Mockito.doThrow; import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.verify; public class RestartActionTest { @Rule @@ -48,7 +51,10 @@ public class RestartActionTest { private Settings settings = new Settings(); private Platform platform = mock(Platform.class); private ProcessCommandWrapper processCommandWrapper = mock(ProcessCommandWrapper.class); - private RestartAction sut = new RestartAction(userSessionRule, settings, platform, processCommandWrapper); + private RestartFlagHolder restartFlagHolder = mock(RestartFlagHolder.class); + private RestartAction sut = new RestartAction(userSessionRule, settings, platform, processCommandWrapper, restartFlagHolder); + private InOrder inOrder = Mockito.inOrder(platform, restartFlagHolder, processCommandWrapper); + private WsActionTester actionTester = new WsActionTester(sut); @Test @@ -59,7 +65,30 @@ public class RestartActionTest { WsTester tester = new WsTester(ws); tester.newPostRequest("api/system", "restart").execute(); - verify(platform).restart(); + InOrder inOrder = Mockito.inOrder(platform, restartFlagHolder); + inOrder.verify(restartFlagHolder).set(); + inOrder.verify(platform).restart(); + inOrder.verify(restartFlagHolder).unset(); + } + + @Test + public void restart_flag_is_unset_in_dev_mode_even_if_restart_fails() throws Exception { + settings.setProperty("sonar.web.dev", true); + RuntimeException toBeThrown = new RuntimeException("simulating platform.restart() failed"); + doThrow(toBeThrown).when(platform).restart(); + + SystemWs ws = new SystemWs(sut); + + WsTester tester = new WsTester(ws); + try { + tester.newPostRequest("api/system", "restart").execute(); + } catch (RuntimeException e) { + assertThat(e).isSameAs(toBeThrown); + } finally { + inOrder.verify(restartFlagHolder).set(); + inOrder.verify(platform).restart(); + inOrder.verify(restartFlagHolder).unset(); + } } @Test @@ -75,7 +104,8 @@ public class RestartActionTest { actionTester.newRequest().execute(); - verify(processCommandWrapper).requestSQRestart(); + inOrder.verify(restartFlagHolder).set(); + inOrder.verify(processCommandWrapper).requestSQRestart(); } @Test diff --git a/server/sonar-server/src/test/java/org/sonar/server/platform/ws/StatusActionTest.java b/server/sonar-server/src/test/java/org/sonar/server/platform/ws/StatusActionTest.java index 74ef58d2ad4..2e0960a2d81 100644 --- a/server/sonar-server/src/test/java/org/sonar/server/platform/ws/StatusActionTest.java +++ b/server/sonar-server/src/test/java/org/sonar/server/platform/ws/StatusActionTest.java @@ -31,6 +31,8 @@ import org.sonar.db.DbClient; import org.sonar.db.DbSession; import org.sonar.db.IsAliveMapper; import org.sonar.db.version.DatabaseMigration; +import org.sonar.server.app.RestartFlagHolder; +import org.sonar.server.app.RestartFlagHolderImpl; import org.sonar.server.platform.Platform; import org.sonar.server.ws.WsTester; @@ -54,6 +56,7 @@ public class StatusActionTest { 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 String STATUS_RESTARTING = "RESTARTING"; 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); @@ -64,7 +67,8 @@ public class StatusActionTest { private DbClient dbClient = mock(DbClient.class); private DbSession dbSession = mock(DbSession.class); private IsAliveMapper isAliveMapper = mock(IsAliveMapper.class); - private StatusAction underTest = new StatusAction(server, databaseMigration, platform, dbClient); + private RestartFlagHolder restartFlagHolder = new RestartFlagHolderImpl(); + private StatusAction underTest = new StatusAction(server, databaseMigration, platform, dbClient, restartFlagHolder); private Request request = mock(Request.class); @@ -97,6 +101,7 @@ public class StatusActionTest { public void verify_example() throws Exception { when(isAliveMapper.isAlive()).thenReturn(IsAliveMapper.IS_ALIVE_RETURNED_VALUE); when(platform.status()).thenReturn(Platform.Status.UP); + restartFlagHolder.unset(); WsTester.TestResponse response = new WsTester.TestResponse(); underTest.handle(request, response); @@ -105,12 +110,21 @@ public class StatusActionTest { } @Test - public void status_is_UP_if_platform_is_UP_whatever_databaseMigration_status_is() throws Exception { + public void status_is_UP_if_platform_is_UP_and_restartFlag_is_false_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_RESTARTING_if_platform_is_UP_and_restartFlag_is_true_whatever_databaseMigration_status_is() throws Exception { + restartFlagHolder.set(); + + for (DatabaseMigration.Status databaseMigrationStatus : DatabaseMigration.Status.values()) { + verifyStatus(Platform.Status.UP, databaseMigrationStatus, STATUS_RESTARTING); + } + } + @Test public void status_is_DOWN_if_platform_is_BOOTING_whatever_databaseMigration_status_is() throws Exception { for (DatabaseMigration.Status databaseMigrationStatus : DatabaseMigration.Status.values()) { diff --git a/server/sonar-server/src/test/java/org/sonar/server/platform/ws/SystemWsTest.java b/server/sonar-server/src/test/java/org/sonar/server/platform/ws/SystemWsTest.java index adda7539e6b..112df0694a3 100644 --- a/server/sonar-server/src/test/java/org/sonar/server/platform/ws/SystemWsTest.java +++ b/server/sonar-server/src/test/java/org/sonar/server/platform/ws/SystemWsTest.java @@ -23,6 +23,7 @@ import org.junit.Test; import org.sonar.api.config.Settings; import org.sonar.api.server.ws.WebService; import org.sonar.server.app.ProcessCommandWrapper; +import org.sonar.server.app.RestartFlagHolder; import org.sonar.server.platform.Platform; import org.sonar.server.platform.monitoring.ProcessSystemInfoClient; import org.sonar.server.tester.AnonymousMockUserSession; @@ -37,7 +38,7 @@ public class SystemWsTest { @Test public void define() { - RestartAction action1 = new RestartAction(mock(UserSession.class), mock(Settings.class), mock(Platform.class), mock(ProcessCommandWrapper.class)); + RestartAction action1 = new RestartAction(mock(UserSession.class), mock(Settings.class), mock(Platform.class), mock(ProcessCommandWrapper.class), mock(RestartFlagHolder.class)); InfoAction action2 = new InfoAction(new AnonymousMockUserSession(), processSystemInfoClient); SystemWs ws = new SystemWs(action1, action2); WebService.Context context = new WebService.Context(); -- 2.39.5