]> source.dussan.org Git - sonarqube.git/commitdiff
SONAR-7168 /api/system/restart now restarts SQ in production mode
authorSébastien Lesaint <sebastien.lesaint@sonarsource.com>
Thu, 7 Jan 2016 11:18:10 +0000 (12:18 +0100)
committerSébastien Lesaint <sebastien.lesaint@sonarsource.com>
Wed, 13 Jan 2016 12:42:41 +0000 (13:42 +0100)
server/sonar-server/src/main/java/org/sonar/server/platform/ws/RestartAction.java
server/sonar-server/src/test/java/org/sonar/server/platform/ws/RestartActionTest.java
server/sonar-server/src/test/java/org/sonar/server/platform/ws/SystemWsTest.java

index 5fa9bc0521da69c98badc7e8cdf5f3fcc463f7d1..d2caac6a8ebaa1369eae4878fe2342229a147dd4 100644 (file)
  */
 package org.sonar.server.platform.ws;
 
+import java.io.File;
 import org.sonar.api.config.Settings;
 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.log.Logger;
 import org.sonar.api.utils.log.Loggers;
-import org.sonar.server.exceptions.ForbiddenException;
+import org.sonar.api.web.UserRole;
+import org.sonar.process.DefaultProcessCommands;
+import org.sonar.process.ProcessCommands;
 import org.sonar.server.platform.Platform;
+import org.sonar.server.user.UserSession;
+
+import static com.google.common.base.Preconditions.checkArgument;
 
 /**
  * Implementation of the {@code restart} action for the System WebService.
@@ -34,11 +40,15 @@ import org.sonar.server.platform.Platform;
 public class RestartAction implements SystemWsAction {
 
   private static final Logger LOGGER = Loggers.get(RestartAction.class);
+  private static final String PROPERTY_SHARED_PATH = "process.sharedDir";
+  private static final String PROPERTY_PROCESS_INDEX = "process.index";
 
+  private final UserSession userSession;
   private final Settings settings;
   private final Platform platform;
 
-  public RestartAction(Settings settings, Platform platform) {
+  public RestartAction(UserSession userSession, Settings settings, Platform platform) {
+    this.userSession = userSession;
     this.settings = settings;
     this.platform = platform;
   }
@@ -46,8 +56,10 @@ public class RestartAction implements SystemWsAction {
   @Override
   public void define(WebService.NewController controller) {
     controller.createAction("restart")
-      .setDescription("Restart server. Available only on development mode (sonar.web.dev=true). " +
-        "Ruby on Rails extensions are not reloaded.")
+      .setDescription("Restart server. " +
+          "In development mode (sonar.web.dev=true), performs a partial and quick restart of only the web server where " +
+          "Ruby on Rails extensions are not reloaded. " +
+          "In Production mode, require system administration permission and fully restart web server and Elastic Search processes.")
       .setSince("4.3")
       .setPost(true)
       .setHandler(this);
@@ -55,14 +67,30 @@ public class RestartAction implements SystemWsAction {
 
   @Override
   public void handle(Request request, Response response) {
-    if (!settings.getBoolean("sonar.web.dev")) {
-      throw new ForbiddenException("Webservice available only in dev mode");
+    if (settings.getBoolean("sonar.web.dev")) {
+      LOGGER.info("Restart server");
+      platform.restart();
+      LOGGER.info("Server restarted");
+    } else {
+      LOGGER.info("Requesting SonarQube restart");
+      userSession.checkPermission(UserRole.ADMIN);
+      ProcessCommands commands = new DefaultProcessCommands(
+        nonNullValueAsFile(PROPERTY_SHARED_PATH), nonNullAsInt(PROPERTY_PROCESS_INDEX));
+      commands.askForRestart();
     }
-
-    LOGGER.info("Restart server");
-    platform.restart();
-    LOGGER.info("Server restarted");
     response.noContent();
   }
 
+  private int nonNullAsInt(String key) {
+    String s = settings.getString(key);
+    checkArgument(s != null, "Property %s is not set", key);
+    return Integer.parseInt(s);
+  }
+
+  public File nonNullValueAsFile(String key) {
+    String s = settings.getString(key);
+    checkArgument(s != null, "Property %s is not set", key);
+    return new File(s);
+  }
+
 }
index 40ee840fe85b6193b84e5974d89a74b995687034..968f82aa2d64d60aeba4dbe47933783b78e322fd 100644 (file)
  */
 package org.sonar.server.platform.ws;
 
+import java.io.File;
+import org.junit.Rule;
 import org.junit.Test;
+import org.junit.rules.ExpectedException;
+import org.junit.rules.TemporaryFolder;
 import org.sonar.api.config.Settings;
+import org.sonar.api.web.UserRole;
+import org.sonar.process.DefaultProcessCommands;
 import org.sonar.server.exceptions.ForbiddenException;
 import org.sonar.server.platform.Platform;
+import org.sonar.server.tester.UserSessionRule;
+import org.sonar.server.ws.WsActionTester;
 import org.sonar.server.ws.WsTester;
 
-import static org.junit.Assert.fail;
-import static org.mockito.Mockito.*;
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
 
 public class RestartActionTest {
+  private static final int PROCESS_NUMBER = 1;
+
+  @Rule
+  public UserSessionRule userSessionRule = UserSessionRule.standalone();
+  @Rule
+  public ExpectedException expectedException = ExpectedException.none();
+  @Rule
+  public TemporaryFolder temp = new TemporaryFolder();
+
 
   Settings settings = new Settings();
   Platform platform = mock(Platform.class);
-  RestartAction sut = new RestartAction(settings, platform);
+  RestartAction sut = new RestartAction(userSessionRule, settings, platform);
+  WsActionTester actionTester = new WsActionTester(sut);
 
   @Test
   public void restart_if_dev_mode() throws Exception {
@@ -46,15 +65,47 @@ public class RestartActionTest {
   }
 
   @Test
-  public void fail_if_production_mode() throws Exception {
-    SystemWs ws = new SystemWs(sut);
+  public void requires_admin_permission_if_production_mode() {
+    expectedException.expect(ForbiddenException.class);
 
-    WsTester tester = new WsTester(ws);
-    try {
-      tester.newPostRequest("api/system", "restart").execute();
-      fail();
-    } catch (ForbiddenException e) {
-      verifyZeroInteractions(platform);
-    }
+    actionTester.newRequest().execute();
   }
+
+  @Test
+  public void fail_process_sharedDir_property_not_set_in_production_mode() throws Exception {
+    userSessionRule.login().setGlobalPermissions(UserRole.ADMIN);
+
+    expectedException.expect(IllegalArgumentException.class);
+    expectedException.expectMessage("Property process.sharedDir is not set");
+
+    actionTester.newRequest().execute();
+  }
+
+  @Test
+  public void fail_process_index_property_not_set_in_production_mode() throws Exception {
+    userSessionRule.login().setGlobalPermissions(UserRole.ADMIN);
+    settings.setProperty("process.sharedDir", temp.newFolder().getAbsolutePath());
+
+    expectedException.expect(IllegalArgumentException.class);
+    expectedException.expectMessage("Property process.index is not set");
+
+    actionTester.newRequest().execute();
+  }
+
+  @Test
+  public void askForRestart_in_shared_memory_in_production_mode() throws Exception {
+    int processNumber = 2;
+    File tempFolder = temp.newFolder().getAbsoluteFile();
+
+    userSessionRule.login().setGlobalPermissions(UserRole.ADMIN);
+    settings.setProperty("process.sharedDir", tempFolder.getAbsolutePath());
+    settings.setProperty("process.index", processNumber);
+
+    DefaultProcessCommands processCommands = new DefaultProcessCommands(tempFolder, processNumber);
+    
+    actionTester.newRequest().execute();
+
+    assertThat(processCommands.askedForRestart()).isTrue();
+  }
+
 }
index db57cce361ebc49715cff203efe70f5b21149a89..34aa22df9a70bb4e342c7f3482c4fb79e0191edb 100644 (file)
@@ -24,6 +24,7 @@ import org.sonar.api.config.Settings;
 import org.sonar.api.server.ws.WebService;
 import org.sonar.server.platform.Platform;
 import org.sonar.server.tester.AnonymousMockUserSession;
+import org.sonar.server.user.UserSession;
 
 import static org.assertj.core.api.Assertions.assertThat;
 import static org.mockito.Mockito.mock;
@@ -32,7 +33,7 @@ public class SystemWsTest {
 
   @Test
   public void define() {
-    RestartAction action1 = new RestartAction(mock(Settings.class), mock(Platform.class));
+    RestartAction action1 = new RestartAction(mock(UserSession.class), mock(Settings.class), mock(Platform.class));
     InfoAction action2 = new InfoAction(new AnonymousMockUserSession());
     SystemWs ws = new SystemWs(action1, action2);
     WebService.Context context = new WebService.Context();