From af37124fb9fc67d55fe76bb648dbb435227aacf6 Mon Sep 17 00:00:00 2001 From: Simon Brandhof Date: Wed, 18 Apr 2018 17:57:55 +0200 Subject: [PATCH] SONAR-10592 new WS api/ce/info --- .../org/sonar/server/ce/ws/CeWsModule.java | 2 + .../org/sonar/server/ce/ws/InfoAction.java | 76 +++++++++++ .../org/sonar/server/ce/ws/info-example.json | 4 + .../sonar/server/ce/ws/CeWsModuleTest.java | 2 +- .../sonar/server/ce/ws/InfoActionTest.java | 129 ++++++++++++++++++ .../org/sonarqube/ws/client/ce/CeService.java | 12 ++ sonar-ws/src/main/protobuf/ws-ce.proto | 6 + 7 files changed, 230 insertions(+), 1 deletion(-) create mode 100644 server/sonar-server/src/main/java/org/sonar/server/ce/ws/InfoAction.java create mode 100644 server/sonar-server/src/main/resources/org/sonar/server/ce/ws/info-example.json create mode 100644 server/sonar-server/src/test/java/org/sonar/server/ce/ws/InfoActionTest.java diff --git a/server/sonar-server/src/main/java/org/sonar/server/ce/ws/CeWsModule.java b/server/sonar-server/src/main/java/org/sonar/server/ce/ws/CeWsModule.java index 63fd305ae74..c83157dc1dd 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/ce/ws/CeWsModule.java +++ b/server/sonar-server/src/main/java/org/sonar/server/ce/ws/CeWsModule.java @@ -30,6 +30,8 @@ public class CeWsModule extends Module { ActivityStatusAction.class, CancelAction.class, CancelAllAction.class, + ComponentAction.class, + InfoAction.class, IsQueueEmptyWs.class, ComponentAction.class, SubmitAction.class, diff --git a/server/sonar-server/src/main/java/org/sonar/server/ce/ws/InfoAction.java b/server/sonar-server/src/main/java/org/sonar/server/ce/ws/InfoAction.java new file mode 100644 index 00000000000..fc30d4d05ee --- /dev/null +++ b/server/sonar-server/src/main/java/org/sonar/server/ce/ws/InfoAction.java @@ -0,0 +1,76 @@ +/* + * SonarQube + * Copyright (C) 2009-2018 SonarSource SA + * mailto:info 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.ce.ws; + +import java.util.Optional; +import org.sonar.api.server.ws.Request; +import org.sonar.api.server.ws.Response; +import org.sonar.api.server.ws.WebService; +import org.sonar.ce.queue.CeQueue; +import org.sonar.server.user.AbstractUserSession; +import org.sonar.server.user.SystemPasscode; +import org.sonar.server.user.SystemPasscodeImpl; +import org.sonar.server.user.UserSession; +import org.sonar.server.ws.WsUtils; +import org.sonarqube.ws.Ce; + +public class InfoAction implements CeWsAction { + + private final UserSession userSession; + private final SystemPasscode systemPasscode; + private final CeQueue ceQueue; + + public InfoAction(UserSession userSession, SystemPasscode systemPasscode, CeQueue ceQueue) { + this.userSession = userSession; + this.systemPasscode = systemPasscode; + this.ceQueue = ceQueue; + } + + @Override + public void define(WebService.NewController controller) { + controller.createAction("info") + .setDescription("Gets information about Compute Engine. Requires the system administration permission or " + + "system passcode (see " + SystemPasscodeImpl.PASSCODE_CONF_PROPERTY + " in sonar.properties).") + .setSince("7.2") + .setHandler(this) + .setResponseExample(getClass().getResource("info-example.json")); + } + + @Override + public void handle(Request request, Response response) throws Exception { + if (!userSession.isSystemAdministrator() && !systemPasscode.isValid(request)) { + throw AbstractUserSession.insufficientPrivilegesException(); + } + + Ce.InfoWsResponse.Builder builder = Ce.InfoWsResponse.newBuilder(); + Optional pause = ceQueue.getWorkersPause(); + builder.setWorkersPaused(isPaused(pause)); + builder.setWorkersPauseRequested(isPauseRequested(pause)); + WsUtils.writeProtobuf(builder.build(), request, response); + } + + private static boolean isPaused(Optional pause) { + return pause.isPresent() && pause.get() == CeQueue.WorkersPause.PAUSED; + } + + private static boolean isPauseRequested(Optional pause) { + return pause.isPresent() && pause.get() == CeQueue.WorkersPause.PAUSING; + } +} diff --git a/server/sonar-server/src/main/resources/org/sonar/server/ce/ws/info-example.json b/server/sonar-server/src/main/resources/org/sonar/server/ce/ws/info-example.json new file mode 100644 index 00000000000..f1c8aec60cb --- /dev/null +++ b/server/sonar-server/src/main/resources/org/sonar/server/ce/ws/info-example.json @@ -0,0 +1,4 @@ +{ + "workersPaused": false, + "workersPauseRequested": true +} diff --git a/server/sonar-server/src/test/java/org/sonar/server/ce/ws/CeWsModuleTest.java b/server/sonar-server/src/test/java/org/sonar/server/ce/ws/CeWsModuleTest.java index 76caf43dc72..e1d039f9f90 100644 --- a/server/sonar-server/src/test/java/org/sonar/server/ce/ws/CeWsModuleTest.java +++ b/server/sonar-server/src/test/java/org/sonar/server/ce/ws/CeWsModuleTest.java @@ -31,6 +31,6 @@ public class CeWsModuleTest { public void verify_count_of_added_components() { ComponentContainer container = new ComponentContainer(); new CeWsModule().configure(container); - assertThat(container.size()).isEqualTo(12 + COMPONENTS_IN_EMPTY_COMPONENT_CONTAINER); + assertThat(container.size()).isEqualTo(13 + COMPONENTS_IN_EMPTY_COMPONENT_CONTAINER); } } diff --git a/server/sonar-server/src/test/java/org/sonar/server/ce/ws/InfoActionTest.java b/server/sonar-server/src/test/java/org/sonar/server/ce/ws/InfoActionTest.java new file mode 100644 index 00000000000..1e2a4bae142 --- /dev/null +++ b/server/sonar-server/src/test/java/org/sonar/server/ce/ws/InfoActionTest.java @@ -0,0 +1,129 @@ +/* + * SonarQube + * Copyright (C) 2009-2018 SonarSource SA + * mailto:info 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.ce.ws; + +import java.util.Optional; +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.ExpectedException; +import org.sonar.api.server.ws.WebService; +import org.sonar.ce.queue.CeQueue; +import org.sonar.server.exceptions.ForbiddenException; +import org.sonar.server.tester.UserSessionRule; +import org.sonar.server.user.SystemPasscode; +import org.sonar.server.ws.WsActionTester; +import org.sonarqube.ws.Ce; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +public class InfoActionTest { + + @Rule + public ExpectedException expectedException = ExpectedException.none(); + @Rule + public UserSessionRule userSession = UserSessionRule.standalone(); + + private SystemPasscode passcode = mock(SystemPasscode.class); + private CeQueue ceQueue = mock(CeQueue.class); + private InfoAction underTest = new InfoAction(userSession, passcode, ceQueue); + private WsActionTester ws = new WsActionTester(underTest); + + @Test + public void test_definition() { + WebService.Action def = ws.getDef(); + assertThat(def.key()).isEqualTo("info"); + assertThat(def.isInternal()).isFalse(); + assertThat(def.isPost()).isFalse(); + assertThat(def.params()).isEmpty(); + } + + @Test + public void test_example_of_response() { + userSession.logIn().setSystemAdministrator(); + when(ceQueue.getWorkersPause()).thenReturn(Optional.of(CeQueue.WorkersPause.PAUSING)); + + ws.newRequest().execute().assertJson(ws.getDef().responseExampleAsString()); + } + + @Test + public void test_workers_in_pausing_state() { + userSession.logIn().setSystemAdministrator(); + when(ceQueue.getWorkersPause()).thenReturn(Optional.of(CeQueue.WorkersPause.PAUSING)); + + Ce.InfoWsResponse response = ws.newRequest().executeProtobuf(Ce.InfoWsResponse.class); + assertThat(response.getWorkersPaused()).isFalse(); + assertThat(response.getWorkersPauseRequested()).isTrue(); + } + + @Test + public void test_workers_in_paused_state() { + userSession.logIn().setSystemAdministrator(); + when(ceQueue.getWorkersPause()).thenReturn(Optional.of(CeQueue.WorkersPause.PAUSED)); + + Ce.InfoWsResponse response = ws.newRequest().executeProtobuf(Ce.InfoWsResponse.class); + assertThat(response.getWorkersPaused()).isTrue(); + assertThat(response.getWorkersPauseRequested()).isFalse(); + } + + @Test + public void test_workers_in_resumed_state() { + userSession.logIn().setSystemAdministrator(); + when(ceQueue.getWorkersPause()).thenReturn(Optional.empty()); + + Ce.InfoWsResponse response = ws.newRequest().executeProtobuf(Ce.InfoWsResponse.class); + assertThat(response.getWorkersPaused()).isFalse(); + assertThat(response.getWorkersPauseRequested()).isFalse(); + } + + @Test + public void throw_ForbiddenException_if_not_system_administrator() { + userSession.logIn().setNonSystemAdministrator(); + + expectedException.expect(ForbiddenException.class); + expectedException.expectMessage("Insufficient privileges"); + + ws.newRequest().execute(); + } + + @Test + public void throw_ForbiddenException_if_invalid_passcode() { + userSession.anonymous(); + when(passcode.isValid(any())).thenReturn(false); + + expectedException.expect(ForbiddenException.class); + expectedException.expectMessage("Insufficient privileges"); + + ws.newRequest().execute(); + } + + @Test + public void authenticate_with_passcode() { + userSession.anonymous(); + when(passcode.isValid(any())).thenReturn(true); + when(ceQueue.getWorkersPause()).thenReturn(Optional.empty()); + + Ce.InfoWsResponse response = ws.newRequest().executeProtobuf(Ce.InfoWsResponse.class); + assertThat(response.getWorkersPaused()).isFalse(); + assertThat(response.getWorkersPauseRequested()).isFalse(); + } +} diff --git a/sonar-ws/src/main/java/org/sonarqube/ws/client/ce/CeService.java b/sonar-ws/src/main/java/org/sonarqube/ws/client/ce/CeService.java index effd68204df..aa49783c8cc 100644 --- a/sonar-ws/src/main/java/org/sonarqube/ws/client/ce/CeService.java +++ b/sonar-ws/src/main/java/org/sonarqube/ws/client/ce/CeService.java @@ -29,6 +29,7 @@ import org.sonarqube.ws.client.WsConnector; import org.sonarqube.ws.Ce.ActivityResponse; import org.sonarqube.ws.Ce.ActivityStatusWsResponse; import org.sonarqube.ws.Ce.ComponentResponse; +import org.sonarqube.ws.Ce.InfoWsResponse; import org.sonarqube.ws.Ce.SubmitResponse; import org.sonarqube.ws.Ce.TaskResponse; import org.sonarqube.ws.Ce.TaskTypesWsResponse; @@ -126,6 +127,17 @@ public class CeService extends BaseService { ComponentResponse.parser()); } + /** + * This is a GET request. + * @see Further information about this action online (including a response example) + * @since 7.2 + */ + public InfoWsResponse info() { + return call( + new GetRequest(path("info")), + InfoWsResponse.parser()); + } + /** * * This is part of the internal API. diff --git a/sonar-ws/src/main/protobuf/ws-ce.proto b/sonar-ws/src/main/protobuf/ws-ce.proto index 5787a326e5e..b52038250c0 100644 --- a/sonar-ws/src/main/protobuf/ws-ce.proto +++ b/sonar-ws/src/main/protobuf/ws-ce.proto @@ -57,6 +57,12 @@ message ComponentResponse { optional Task current = 2; } +// GET api/ce/info +message InfoWsResponse { + optional bool workersPaused = 1; + optional bool workersPauseRequested = 2; +} + // GET api/ce/task_types message TaskTypesWsResponse { repeated string taskTypes = 1; -- 2.39.5