*/
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.
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;
}
@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);
@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);
+ }
+
}
*/
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 {
}
@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();
+ }
+
}
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;
@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();