aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorLukasz Jarocki <lukasz.jarocki@sonarsource.com>2021-12-08 09:35:06 +0100
committerLukasz Jarocki <lukasz.jarocki@sonarsource.com>2021-12-13 15:22:35 +0100
commitd58a45b4b4fd196cccb8cbd1c9a479a3c783afe3 (patch)
treecfeb1df309b2eaaa2adeae63780472d0c0108172
parent601e7fbb0ca7cd323b69742e15cd016dac46cf62 (diff)
downloadsonarqube-d58a45b4b4fd196cccb8cbd1c9a479a3c783afe3.tar.gz
sonarqube-d58a45b4b4fd196cccb8cbd1c9a479a3c783afe3.zip
SONAR-15769 added metrics for integration with devops platforms
-rw-r--r--build.gradle2
-rw-r--r--server/sonar-alm-client/src/main/java/org/sonar/alm/client/azure/AzureDevOpsValidator.java47
-rw-r--r--server/sonar-alm-client/src/main/java/org/sonar/alm/client/bitbucket/bitbucketcloud/BitbucketCloudValidator.java46
-rw-r--r--server/sonar-alm-client/src/main/java/org/sonar/alm/client/bitbucketserver/BitbucketServerSettingsValidator.java (renamed from server/sonar-webserver-webapi/src/main/java/org/sonar/server/almintegration/validator/BitbucketServerSettingsValidator.java)3
-rw-r--r--server/sonar-alm-client/src/main/java/org/sonar/alm/client/github/GithubGlobalSettingsValidator.java (renamed from server/sonar-webserver-webapi/src/main/java/org/sonar/server/almintegration/validator/GithubGlobalSettingsValidator.java)4
-rw-r--r--server/sonar-alm-client/src/main/java/org/sonar/alm/client/gitlab/GitlabGlobalSettingsValidator.java (renamed from server/sonar-webserver-webapi/src/main/java/org/sonar/server/almintegration/validator/GitlabGlobalSettingsValidator.java)3
-rw-r--r--server/sonar-alm-client/src/test/java/org/sonar/alm/client/azure/AzureDevOpsValidatorTest.java64
-rw-r--r--server/sonar-alm-client/src/test/java/org/sonar/alm/client/bitbucket/bitbucketcloud/BitbucketCloudValidatorTest.java69
-rw-r--r--server/sonar-alm-client/src/test/java/org/sonar/alm/client/bitbucketserver/BitbucketServerSettingsValidatorTest.java (renamed from server/sonar-webserver-webapi/src/test/java/org/sonar/server/almintegration/validator/BitbucketServerSettingsValidatorTest.java)29
-rw-r--r--server/sonar-alm-client/src/test/java/org/sonar/alm/client/github/GithubGlobalSettingsValidatorTest.java (renamed from server/sonar-webserver-webapi/src/test/java/org/sonar/server/almintegration/validator/GithubGlobalSettingsValidatorTest.java)44
-rw-r--r--server/sonar-alm-client/src/test/java/org/sonar/alm/client/gitlab/GitlabGlobalSettingsValidatorTest.java (renamed from server/sonar-webserver-webapi/src/test/java/org/sonar/server/almintegration/validator/GitlabGlobalSettingsValidatorTest.java)3
-rw-r--r--server/sonar-alm-client/src/test/resources/mockito-extensions/org.mockito.plugins.MockMaker1
-rw-r--r--server/sonar-webserver-monitoring/build.gradle13
-rw-r--r--server/sonar-webserver-monitoring/src/main/java/org/sonar/server/monitoring/ServerMonitoringMetrics.java86
-rw-r--r--server/sonar-webserver-monitoring/src/main/java/org/sonar/server/monitoring/devops/DevOpsPlatformsMetricsCollector.java166
-rw-r--r--server/sonar-webserver-monitoring/src/test/java/org/sonar/server/monitoring/ServerMonitoringMetricsTest.java82
-rw-r--r--server/sonar-webserver-monitoring/src/test/java/org/sonar/server/monitoring/devops/DevOpsPlatformsMetricsCollectorTest.java185
-rw-r--r--server/sonar-webserver-webapi/src/main/java/org/sonar/server/almintegration/ws/AlmIntegrationsWSModule.java6
-rw-r--r--server/sonar-webserver-webapi/src/main/java/org/sonar/server/almsettings/ws/ValidateAction.java33
-rw-r--r--server/sonar-webserver-webapi/src/test/java/org/sonar/server/almsettings/ws/ValidateActionTest.java14
-rw-r--r--server/sonar-webserver/build.gradle1
-rw-r--r--server/sonar-webserver/src/main/java/org/sonar/server/platform/platformlevel/PlatformLevel4.java16
-rw-r--r--server/sonar-webserver/src/main/java/org/sonar/server/platform/platformlevel/PlatformLevelSafeMode.java6
-rw-r--r--settings.gradle1
24 files changed, 852 insertions, 72 deletions
diff --git a/build.gradle b/build.gradle
index 6e8237db234..3317c91db18 100644
--- a/build.gradle
+++ b/build.gradle
@@ -307,6 +307,8 @@ subprojects {
dependency('com.googlecode.json-simple:json-simple:1.1.1') {
exclude 'junit:junit'
}
+ dependency 'io.prometheus:simpleclient:0.12.0'
+ dependency 'io.prometheus:simpleclient_servlet:0.12.0'
dependency 'com.google.code.findbugs:jsr305:3.0.2'
dependency 'com.google.code.gson:gson:2.8.9'
dependency('com.google.guava:guava:28.2-jre') {
diff --git a/server/sonar-alm-client/src/main/java/org/sonar/alm/client/azure/AzureDevOpsValidator.java b/server/sonar-alm-client/src/main/java/org/sonar/alm/client/azure/AzureDevOpsValidator.java
new file mode 100644
index 00000000000..d021b6c4c87
--- /dev/null
+++ b/server/sonar-alm-client/src/main/java/org/sonar/alm/client/azure/AzureDevOpsValidator.java
@@ -0,0 +1,47 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2021 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.alm.client.azure;
+
+import org.sonar.api.server.ServerSide;
+import org.sonar.db.alm.setting.AlmSettingDto;
+import org.sonar.api.config.internal.Settings;
+
+import static java.util.Objects.requireNonNull;
+
+@ServerSide
+public class AzureDevOpsValidator {
+
+ private final AzureDevOpsHttpClient azureDevOpsHttpClient;
+ private final Settings settings;
+
+ public AzureDevOpsValidator(AzureDevOpsHttpClient azureDevOpsHttpClient, Settings settings) {
+ this.azureDevOpsHttpClient = azureDevOpsHttpClient;
+ this.settings = settings;
+ }
+
+ public void validate(AlmSettingDto dto) {
+ try {
+ azureDevOpsHttpClient.checkPAT(requireNonNull(dto.getUrl()),
+ requireNonNull(dto.getDecryptedPersonalAccessToken(settings.getEncryption())));
+ } catch (IllegalArgumentException e) {
+ throw new IllegalArgumentException("Invalid Azure URL or Personal Access Token", e);
+ }
+ }
+}
diff --git a/server/sonar-alm-client/src/main/java/org/sonar/alm/client/bitbucket/bitbucketcloud/BitbucketCloudValidator.java b/server/sonar-alm-client/src/main/java/org/sonar/alm/client/bitbucket/bitbucketcloud/BitbucketCloudValidator.java
new file mode 100644
index 00000000000..09fca29e801
--- /dev/null
+++ b/server/sonar-alm-client/src/main/java/org/sonar/alm/client/bitbucket/bitbucketcloud/BitbucketCloudValidator.java
@@ -0,0 +1,46 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2021 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.alm.client.bitbucket.bitbucketcloud;
+
+import org.sonar.api.config.internal.Settings;
+import org.sonar.api.server.ServerSide;
+import org.sonar.db.alm.setting.AlmSettingDto;
+
+import static java.util.Objects.requireNonNull;
+
+@ServerSide
+public class BitbucketCloudValidator {
+
+ private final BitbucketCloudRestClient bitbucketCloudRestClient;
+ private final Settings settings;
+
+ public BitbucketCloudValidator(BitbucketCloudRestClient bitbucketCloudRestClient, Settings settings) {
+ this.bitbucketCloudRestClient = bitbucketCloudRestClient;
+ this.settings = settings;
+ }
+
+ public void validate(AlmSettingDto dto) {
+ String clientId = requireNonNull(dto.getClientId());
+ String appId = requireNonNull(dto.getAppId());
+ String decryptedClientSecret = requireNonNull(dto.getDecryptedClientSecret(settings.getEncryption()));
+ bitbucketCloudRestClient.validate(clientId, decryptedClientSecret, appId);
+ }
+
+}
diff --git a/server/sonar-webserver-webapi/src/main/java/org/sonar/server/almintegration/validator/BitbucketServerSettingsValidator.java b/server/sonar-alm-client/src/main/java/org/sonar/alm/client/bitbucketserver/BitbucketServerSettingsValidator.java
index b0e6e63c558..c4d0656591f 100644
--- a/server/sonar-webserver-webapi/src/main/java/org/sonar/server/almintegration/validator/BitbucketServerSettingsValidator.java
+++ b/server/sonar-alm-client/src/main/java/org/sonar/alm/client/bitbucketserver/BitbucketServerSettingsValidator.java
@@ -17,9 +17,8 @@
* 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.almintegration.validator;
+package org.sonar.alm.client.bitbucketserver;
-import org.sonar.alm.client.bitbucketserver.BitbucketServerRestClient;
import org.sonar.api.config.internal.Encryption;
import org.sonar.api.config.internal.Settings;
import org.sonar.api.server.ServerSide;
diff --git a/server/sonar-webserver-webapi/src/main/java/org/sonar/server/almintegration/validator/GithubGlobalSettingsValidator.java b/server/sonar-alm-client/src/main/java/org/sonar/alm/client/github/GithubGlobalSettingsValidator.java
index 56298ced863..3542e7dac0d 100644
--- a/server/sonar-webserver-webapi/src/main/java/org/sonar/server/almintegration/validator/GithubGlobalSettingsValidator.java
+++ b/server/sonar-alm-client/src/main/java/org/sonar/alm/client/github/GithubGlobalSettingsValidator.java
@@ -17,11 +17,9 @@
* 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.almintegration.validator;
+package org.sonar.alm.client.github;
import java.util.Optional;
-import org.sonar.alm.client.github.GithubApplicationClient;
-import org.sonar.alm.client.github.GithubApplicationClientImpl;
import org.sonar.alm.client.github.config.GithubAppConfiguration;
import org.sonar.api.config.internal.Encryption;
import org.sonar.api.config.internal.Settings;
diff --git a/server/sonar-webserver-webapi/src/main/java/org/sonar/server/almintegration/validator/GitlabGlobalSettingsValidator.java b/server/sonar-alm-client/src/main/java/org/sonar/alm/client/gitlab/GitlabGlobalSettingsValidator.java
index d6758b48201..4d069561a4c 100644
--- a/server/sonar-webserver-webapi/src/main/java/org/sonar/server/almintegration/validator/GitlabGlobalSettingsValidator.java
+++ b/server/sonar-alm-client/src/main/java/org/sonar/alm/client/gitlab/GitlabGlobalSettingsValidator.java
@@ -17,9 +17,8 @@
* 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.almintegration.validator;
+package org.sonar.alm.client.gitlab;
-import org.sonar.alm.client.gitlab.GitlabHttpClient;
import org.sonar.api.config.internal.Encryption;
import org.sonar.api.config.internal.Settings;
import org.sonar.api.server.ServerSide;
diff --git a/server/sonar-alm-client/src/test/java/org/sonar/alm/client/azure/AzureDevOpsValidatorTest.java b/server/sonar-alm-client/src/test/java/org/sonar/alm/client/azure/AzureDevOpsValidatorTest.java
new file mode 100644
index 00000000000..842624fd8fa
--- /dev/null
+++ b/server/sonar-alm-client/src/test/java/org/sonar/alm/client/azure/AzureDevOpsValidatorTest.java
@@ -0,0 +1,64 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2021 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.alm.client.azure;
+
+import org.junit.Test;
+import org.sonar.api.config.internal.Settings;
+import org.sonar.db.alm.setting.AlmSettingDto;
+import org.sonarqube.ws.AlmSettings;
+
+import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.Mockito.doThrow;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+public class AzureDevOpsValidatorTest {
+
+ private final AzureDevOpsHttpClient azureDevOpsHttpClient = mock(AzureDevOpsHttpClient.class);
+ private final Settings settings = mock(Settings.class);
+ private final AzureDevOpsValidator underTest = new AzureDevOpsValidator(azureDevOpsHttpClient, settings);
+
+ @Test
+ public void validate_givenHttpClientThrowingException_throwException() {
+ AlmSettingDto dto = createMockDto();
+
+ doThrow(new IllegalArgumentException()).when(azureDevOpsHttpClient).checkPAT(any(), any());
+
+ assertThatExceptionOfType(IllegalArgumentException.class)
+ .isThrownBy(() -> underTest.validate(dto))
+ .withMessage("Invalid Azure URL or Personal Access Token");
+
+ }
+
+ @Test(expected = Test.None.class /* no exception expected */)
+ public void validate_givenHttpClientNotThrowingException_doesNotThrowException() {
+ AlmSettingDto dto = createMockDto();
+
+ underTest.validate(dto);
+ }
+
+ private AlmSettingDto createMockDto() {
+ AlmSettingDto dto = mock(AlmSettingDto.class);
+ when(dto.getUrl()).thenReturn("http://azure-devops-url.url");
+ when(dto.getDecryptedPersonalAccessToken(any())).thenReturn("decrypted-token");
+ return dto;
+ }
+}
diff --git a/server/sonar-alm-client/src/test/java/org/sonar/alm/client/bitbucket/bitbucketcloud/BitbucketCloudValidatorTest.java b/server/sonar-alm-client/src/test/java/org/sonar/alm/client/bitbucket/bitbucketcloud/BitbucketCloudValidatorTest.java
new file mode 100644
index 00000000000..f450b70de98
--- /dev/null
+++ b/server/sonar-alm-client/src/test/java/org/sonar/alm/client/bitbucket/bitbucketcloud/BitbucketCloudValidatorTest.java
@@ -0,0 +1,69 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2021 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.alm.client.bitbucket.bitbucketcloud;
+
+import org.junit.Test;
+import org.sonar.api.config.internal.Settings;
+import org.sonar.db.alm.setting.AlmSettingDto;
+
+import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.Mockito.doThrow;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+public class BitbucketCloudValidatorTest {
+
+ private final BitbucketCloudRestClient bitbucketCloudRestClient = mock(BitbucketCloudRestClient.class);
+ private final Settings settings = mock(Settings.class);
+
+ private final BitbucketCloudValidator underTest = new BitbucketCloudValidator(bitbucketCloudRestClient, settings);
+
+ private static final String EXAMPLE_APP_ID = "123";
+
+ @Test
+ public void validate_forwardsExceptionFromRestClient() {
+ AlmSettingDto dto = mock(AlmSettingDto.class);
+ when(dto.getAppId()).thenReturn(EXAMPLE_APP_ID);
+ when(dto.getClientId()).thenReturn("clientId");
+ when(dto.getDecryptedClientSecret(any())).thenReturn("secret");
+
+ doThrow(new IllegalArgumentException("Exception from bitbucket cloud rest client"))
+ .when(bitbucketCloudRestClient).validate(any(), any(), any());
+
+ assertThatExceptionOfType(IllegalArgumentException.class)
+ .isThrownBy(() -> underTest.validate(dto))
+ .withMessage("Exception from bitbucket cloud rest client");
+ }
+
+ @Test
+ public void validate_callsValidate() {
+ AlmSettingDto dto = mock(AlmSettingDto.class);
+ when(dto.getAppId()).thenReturn(EXAMPLE_APP_ID);
+ when(dto.getClientId()).thenReturn("clientId");
+ when(dto.getDecryptedClientSecret(any())).thenReturn("secret");
+
+ underTest.validate(dto);
+
+ verify(bitbucketCloudRestClient, times(1)).validate("clientId", "secret", EXAMPLE_APP_ID);
+ }
+}
diff --git a/server/sonar-webserver-webapi/src/test/java/org/sonar/server/almintegration/validator/BitbucketServerSettingsValidatorTest.java b/server/sonar-alm-client/src/test/java/org/sonar/alm/client/bitbucketserver/BitbucketServerSettingsValidatorTest.java
index e8644877c1c..a30709b11d9 100644
--- a/server/sonar-webserver-webapi/src/test/java/org/sonar/server/almintegration/validator/BitbucketServerSettingsValidatorTest.java
+++ b/server/sonar-alm-client/src/test/java/org/sonar/alm/client/bitbucketserver/BitbucketServerSettingsValidatorTest.java
@@ -17,13 +17,13 @@
* 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.almintegration.validator;
+package org.sonar.alm.client.bitbucketserver;
import org.junit.BeforeClass;
import org.junit.Test;
-import org.sonar.alm.client.bitbucketserver.BitbucketServerRestClient;
import org.sonar.api.config.internal.Encryption;
import org.sonar.api.config.internal.Settings;
+import org.sonar.db.alm.setting.ALM;
import org.sonar.db.alm.setting.AlmSettingDto;
import static org.assertj.core.api.AssertionsForClassTypes.assertThatThrownBy;
@@ -34,7 +34,6 @@ import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
-import static org.sonar.db.almsettings.AlmSettingsTesting.newBitbucketAlmSettingDto;
public class BitbucketServerSettingsValidatorTest {
private static final Encryption encryption = mock(Encryption.class);
@@ -50,9 +49,7 @@ public class BitbucketServerSettingsValidatorTest {
@Test
public void validate_success() {
- AlmSettingDto almSettingDto = newBitbucketAlmSettingDto()
- .setUrl("http://abc.com")
- .setPersonalAccessToken("abc");
+ AlmSettingDto almSettingDto = createNewBitbucketDto("http://abc.com", "abc");
when(encryption.isEncrypted(any())).thenReturn(false);
underTest.validate(almSettingDto);
@@ -66,9 +63,7 @@ public class BitbucketServerSettingsValidatorTest {
public void validate_success_with_encrypted_token() {
String encryptedToken = "abc";
String decryptedToken = "decrypted-token";
- AlmSettingDto almSettingDto = newBitbucketAlmSettingDto()
- .setUrl("http://abc.com")
- .setPersonalAccessToken(encryptedToken);
+ AlmSettingDto almSettingDto = createNewBitbucketDto("http://abc.com", encryptedToken);
when(encryption.isEncrypted(encryptedToken)).thenReturn(true);
when(encryption.decrypt(encryptedToken)).thenReturn(decryptedToken);
@@ -81,9 +76,7 @@ public class BitbucketServerSettingsValidatorTest {
@Test
public void validate_failure_on_incomplete_configuration() {
- AlmSettingDto almSettingDto = newBitbucketAlmSettingDto()
- .setUrl(null)
- .setPersonalAccessToken("abc");
+ AlmSettingDto almSettingDto = createNewBitbucketDto(null, "abc");
assertThatThrownBy(() -> underTest.validate(almSettingDto))
.isInstanceOf(IllegalArgumentException.class);
@@ -92,11 +85,17 @@ public class BitbucketServerSettingsValidatorTest {
@Test
public void validate_failure_on_bitbucket_server_api_error() {
doThrow(new IllegalArgumentException("error")).when(bitbucketServerRestClient).validateUrl(anyString());
- AlmSettingDto almSettingDto = newBitbucketAlmSettingDto()
- .setUrl("http://abc.com")
- .setPersonalAccessToken("abc");
+ AlmSettingDto almSettingDto = createNewBitbucketDto("http://abc.com", "abc");
assertThatThrownBy(() -> underTest.validate(almSettingDto))
.isInstanceOf(IllegalArgumentException.class);
}
+
+ private AlmSettingDto createNewBitbucketDto(String url, String pat) {
+ AlmSettingDto dto = new AlmSettingDto();
+ dto.setAlm(ALM.BITBUCKET);
+ dto.setUrl(url);
+ dto.setPersonalAccessToken(pat);
+ return dto;
+ }
}
diff --git a/server/sonar-webserver-webapi/src/test/java/org/sonar/server/almintegration/validator/GithubGlobalSettingsValidatorTest.java b/server/sonar-alm-client/src/test/java/org/sonar/alm/client/github/GithubGlobalSettingsValidatorTest.java
index 5c3d274a65b..317cfdc1a32 100644
--- a/server/sonar-webserver-webapi/src/test/java/org/sonar/server/almintegration/validator/GithubGlobalSettingsValidatorTest.java
+++ b/server/sonar-alm-client/src/test/java/org/sonar/alm/client/github/GithubGlobalSettingsValidatorTest.java
@@ -17,15 +17,16 @@
* 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.almintegration.validator;
+package org.sonar.alm.client.github;
+import javax.annotation.Nullable;
import org.junit.BeforeClass;
import org.junit.Test;
import org.mockito.ArgumentCaptor;
-import org.sonar.alm.client.github.GithubApplicationClientImpl;
import org.sonar.alm.client.github.config.GithubAppConfiguration;
import org.sonar.api.config.internal.Encryption;
import org.sonar.api.config.internal.Settings;
+import org.sonar.db.alm.setting.ALM;
import org.sonar.db.alm.setting.AlmSettingDto;
import static org.assertj.core.api.Assertions.assertThat;
@@ -34,12 +35,14 @@ import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
-import static org.sonar.db.almsettings.AlmSettingsTesting.newGithubAlmSettingDto;
public class GithubGlobalSettingsValidatorTest {
private static final Encryption encryption = mock(Encryption.class);
private static final Settings settings = mock(Settings.class);
+ private static final String EXAMPLE_APP_ID = "123";
+ private static final String EXAMPLE_PRIVATE_KEY = "private_key";
+
private final GithubApplicationClientImpl appClient = mock(GithubApplicationClientImpl.class);
private final GithubGlobalSettingsValidator underTest = new GithubGlobalSettingsValidator(appClient, settings);
@@ -50,9 +53,8 @@ public class GithubGlobalSettingsValidatorTest {
@Test
public void github_global_settings_validation() {
- AlmSettingDto almSettingDto = newGithubAlmSettingDto()
- .setClientId("clientId")
- .setClientSecret("clientSecret");
+ AlmSettingDto almSettingDto = createNewGithubDto("clientId", "clientSecret", EXAMPLE_APP_ID, EXAMPLE_PRIVATE_KEY);
+
when(encryption.isEncrypted(any())).thenReturn(false);
GithubAppConfiguration configuration = underTest.validate(almSettingDto);
@@ -68,10 +70,8 @@ public class GithubGlobalSettingsValidatorTest {
public void github_global_settings_validation_with_encrypted_key() {
String encryptedKey = "encrypted-key";
String decryptedKey = "decrypted-key";
- AlmSettingDto almSettingDto = newGithubAlmSettingDto()
- .setClientId("clientId")
- .setPrivateKey(encryptedKey)
- .setClientSecret("clientSecret");
+ AlmSettingDto almSettingDto = createNewGithubDto("clientId", "clientSecret", EXAMPLE_APP_ID, encryptedKey);
+
when(encryption.isEncrypted(encryptedKey)).thenReturn(true);
when(encryption.decrypt(encryptedKey)).thenReturn(decryptedKey);
@@ -88,10 +88,7 @@ public class GithubGlobalSettingsValidatorTest {
@Test
public void github_validation_checks_invalid_appId() {
- AlmSettingDto almSettingDto = newGithubAlmSettingDto()
- .setAppId("abc")
- .setClientId("clientId")
- .setClientSecret("clientSecret");
+ AlmSettingDto almSettingDto = createNewGithubDto("clientId", "clientSecret", "abc", null);
assertThatThrownBy(() -> underTest.validate(almSettingDto))
.isInstanceOf(IllegalArgumentException.class)
@@ -100,7 +97,8 @@ public class GithubGlobalSettingsValidatorTest {
@Test
public void github_validation_checks_missing_appId() {
- AlmSettingDto almSettingDto = newGithubAlmSettingDto().setAppId(null);
+ AlmSettingDto almSettingDto = new AlmSettingDto();
+ almSettingDto.setAppId(null);
assertThatThrownBy(() -> underTest.validate(almSettingDto))
.isInstanceOf(IllegalArgumentException.class)
@@ -109,7 +107,7 @@ public class GithubGlobalSettingsValidatorTest {
@Test
public void github_validation_checks_missing_clientId() {
- AlmSettingDto almSettingDto = newGithubAlmSettingDto().setClientId(null);
+ AlmSettingDto almSettingDto = createNewGithubDto(null, null, EXAMPLE_APP_ID, null);
assertThatThrownBy(() -> underTest.validate(almSettingDto))
.isInstanceOf(IllegalArgumentException.class)
@@ -118,11 +116,23 @@ public class GithubGlobalSettingsValidatorTest {
@Test
public void github_validation_checks_missing_clientSecret() {
- AlmSettingDto almSettingDto = newGithubAlmSettingDto().setClientSecret(null);
+ AlmSettingDto almSettingDto = createNewGithubDto("clientId", null, EXAMPLE_APP_ID, null);
assertThatThrownBy(() -> underTest.validate(almSettingDto))
.isInstanceOf(IllegalArgumentException.class)
.hasMessage("Missing Client Secret");
}
+
+ private AlmSettingDto createNewGithubDto(@Nullable String clientId, @Nullable String clientSecret,
+ @Nullable String appId, @Nullable String privateKey) {
+ AlmSettingDto dto = new AlmSettingDto();
+ dto.setAlm(ALM.GITHUB);
+ dto.setUrl("http://github-example-url.com");
+ dto.setClientId(clientId);
+ dto.setClientSecret(clientSecret);
+ dto.setAppId(appId);
+ dto.setPrivateKey(privateKey);
+ return dto;
+ }
}
diff --git a/server/sonar-webserver-webapi/src/test/java/org/sonar/server/almintegration/validator/GitlabGlobalSettingsValidatorTest.java b/server/sonar-alm-client/src/test/java/org/sonar/alm/client/gitlab/GitlabGlobalSettingsValidatorTest.java
index 186b671ed2f..3b033252c7c 100644
--- a/server/sonar-webserver-webapi/src/test/java/org/sonar/server/almintegration/validator/GitlabGlobalSettingsValidatorTest.java
+++ b/server/sonar-alm-client/src/test/java/org/sonar/alm/client/gitlab/GitlabGlobalSettingsValidatorTest.java
@@ -17,10 +17,11 @@
* 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.almintegration.validator;
+package org.sonar.alm.client.gitlab;
import org.junit.BeforeClass;
import org.junit.Test;
+import org.sonar.alm.client.gitlab.GitlabGlobalSettingsValidator;
import org.sonar.alm.client.gitlab.GitlabHttpClient;
import org.sonar.api.config.internal.Encryption;
import org.sonar.api.config.internal.Settings;
diff --git a/server/sonar-alm-client/src/test/resources/mockito-extensions/org.mockito.plugins.MockMaker b/server/sonar-alm-client/src/test/resources/mockito-extensions/org.mockito.plugins.MockMaker
new file mode 100644
index 00000000000..1f0955d450f
--- /dev/null
+++ b/server/sonar-alm-client/src/test/resources/mockito-extensions/org.mockito.plugins.MockMaker
@@ -0,0 +1 @@
+mock-maker-inline
diff --git a/server/sonar-webserver-monitoring/build.gradle b/server/sonar-webserver-monitoring/build.gradle
new file mode 100644
index 00000000000..854ae9a59e6
--- /dev/null
+++ b/server/sonar-webserver-monitoring/build.gradle
@@ -0,0 +1,13 @@
+description = 'SonarQube :: Monitoring'
+
+dependencies {
+ compile project(path: ':sonar-plugin-api', configuration: 'shadow')
+ compile project(':server:sonar-webserver-api')
+ compile project(':server:sonar-alm-client')
+ compile 'io.prometheus:simpleclient'
+
+ testCompile 'junit:junit'
+ testCompile 'org.assertj:assertj-core'
+ testCompile 'org.mockito:mockito-core'
+
+}
diff --git a/server/sonar-webserver-monitoring/src/main/java/org/sonar/server/monitoring/ServerMonitoringMetrics.java b/server/sonar-webserver-monitoring/src/main/java/org/sonar/server/monitoring/ServerMonitoringMetrics.java
new file mode 100644
index 00000000000..1fb4c46adaa
--- /dev/null
+++ b/server/sonar-webserver-monitoring/src/main/java/org/sonar/server/monitoring/ServerMonitoringMetrics.java
@@ -0,0 +1,86 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2021 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.monitoring;
+
+import io.prometheus.client.Gauge;
+import org.sonar.api.server.ServerSide;
+
+@ServerSide
+public class ServerMonitoringMetrics {
+
+ private final Gauge githubConfigOk;
+ private final Gauge gitlabConfigOk;
+ private final Gauge bitbucketConfigOk;
+ private final Gauge azureConfigOk;
+
+ public ServerMonitoringMetrics() {
+ githubConfigOk = Gauge.build()
+ .name("github_config_ok")
+ .help("Tells whether SonarQube instance has configured GitHub integration and its status is green. 0 for green, 1 otherwise .")
+ .register();
+
+ gitlabConfigOk = Gauge.build()
+ .name("gitlab_config_ok")
+ .help("Tells whether SonarQube instance has configured GitLab integration and its status is green. 0 for green, 1 otherwise .")
+ .register();
+
+ bitbucketConfigOk = Gauge.build()
+ .name("bitbucket_config_ok")
+ .help("Tells whether SonarQube instance has configured BitBucket integration and its status is green. 0 for green, 1 otherwise .")
+ .register();
+
+ azureConfigOk = Gauge.build()
+ .name("azure_config_ok")
+ .help("Tells whether SonarQube instance has configured Azure integration and its status is green. 0 for green, 1 otherwise .")
+ .register();
+ }
+
+ public void setGithubStatusToGreen() {
+ githubConfigOk.set(0);
+ }
+
+ public void setGithubStatusToRed() {
+ githubConfigOk.set(1);
+ }
+
+ public void setGitlabStatusToGreen() {
+ gitlabConfigOk.set(0);
+ }
+
+ public void setGitlabStatusToRed() {
+ gitlabConfigOk.set(1);
+ }
+
+ public void setAzureStatusToGreen() {
+ azureConfigOk.set(0);
+ }
+
+ public void setAzureStatusToRed() {
+ azureConfigOk.set(1);
+ }
+
+ public void setBitbucketStatusToGreen() {
+ bitbucketConfigOk.set(0);
+ }
+
+ public void setBitbucketStatusToRed() {
+ bitbucketConfigOk.set(1);
+ }
+}
diff --git a/server/sonar-webserver-monitoring/src/main/java/org/sonar/server/monitoring/devops/DevOpsPlatformsMetricsCollector.java b/server/sonar-webserver-monitoring/src/main/java/org/sonar/server/monitoring/devops/DevOpsPlatformsMetricsCollector.java
new file mode 100644
index 00000000000..fcee736a451
--- /dev/null
+++ b/server/sonar-webserver-monitoring/src/main/java/org/sonar/server/monitoring/devops/DevOpsPlatformsMetricsCollector.java
@@ -0,0 +1,166 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2021 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.monitoring.devops;
+
+import com.google.common.annotations.VisibleForTesting;
+import com.google.common.util.concurrent.ThreadFactoryBuilder;
+import java.util.List;
+import java.util.concurrent.Executors;
+import java.util.concurrent.ScheduledExecutorService;
+import java.util.stream.Collectors;
+import org.picocontainer.Startable;
+import org.sonar.alm.client.azure.AzureDevOpsValidator;
+import org.sonar.alm.client.bitbucket.bitbucketcloud.BitbucketCloudValidator;
+import org.sonar.alm.client.bitbucketserver.BitbucketServerSettingsValidator;
+import org.sonar.alm.client.github.GithubGlobalSettingsValidator;
+import org.sonar.alm.client.gitlab.GitlabGlobalSettingsValidator;
+import org.sonar.api.config.Configuration;
+import org.sonar.api.server.ServerSide;
+import org.sonar.db.DbClient;
+import org.sonar.db.DbSession;
+import org.sonar.db.alm.setting.ALM;
+import org.sonar.db.alm.setting.AlmSettingDto;
+import org.sonar.server.monitoring.ServerMonitoringMetrics;
+
+import static java.util.concurrent.TimeUnit.MILLISECONDS;
+
+@ServerSide
+public class DevOpsPlatformsMetricsCollector implements Startable {
+
+ private static final String DELAY_IN_MILISECONDS_PROPERTY = "sonar.server.monitoring.devops.initial.delay";
+ private static final String PERIOD_IN_MILISECONDS_PROPERTY = "sonar.server.monitoring.devops.period";
+
+ private final Configuration config;
+
+ private final BitbucketServerSettingsValidator bitbucketServerValidator;
+ private final GithubGlobalSettingsValidator githubValidator;
+ private final GitlabGlobalSettingsValidator gitlabValidator;
+ private final BitbucketCloudValidator bitbucketCloudValidator;
+ private final AzureDevOpsValidator azureDevOpsValidator;
+
+ private final DbClient dbClient;
+ private final ServerMonitoringMetrics metrics;
+
+ private ScheduledExecutorService scheduledExecutorService;
+
+ public DevOpsPlatformsMetricsCollector(ServerMonitoringMetrics metrics, DbClient dbClient,
+ BitbucketServerSettingsValidator bitbucketServerValidator, GithubGlobalSettingsValidator githubValidator,
+ GitlabGlobalSettingsValidator gitlabValidator, BitbucketCloudValidator bitbucketCloudValidator,
+ AzureDevOpsValidator azureDevOpsValidator, Configuration config) {
+ this.bitbucketCloudValidator = bitbucketCloudValidator;
+ this.bitbucketServerValidator = bitbucketServerValidator;
+ this.githubValidator = githubValidator;
+ this.azureDevOpsValidator = azureDevOpsValidator;
+ this.gitlabValidator = gitlabValidator;
+ this.metrics = metrics;
+ this.dbClient = dbClient;
+ this.config = config;
+ }
+
+ @Override
+ public void start() {
+ this.scheduledExecutorService = Executors.newSingleThreadScheduledExecutor(
+ new ThreadFactoryBuilder()
+ .setDaemon(true)
+ .setNameFormat(getClass().getCanonicalName() + "-thread-%d")
+ .build());
+ long delayInMilliseconds = config.getLong(DELAY_IN_MILISECONDS_PROPERTY).orElse(10_000L);
+ long periodInMilliseconds = config.getLong(PERIOD_IN_MILISECONDS_PROPERTY).orElse(300_000L);
+ scheduledExecutorService.scheduleWithFixedDelay(createTask(), delayInMilliseconds, periodInMilliseconds, MILLISECONDS);
+ }
+
+ @Override
+ public void stop() {
+ scheduledExecutorService.shutdown();
+ }
+
+ @VisibleForTesting
+ Runnable createTask() {
+ return () -> {
+ try (DbSession dbSession = dbClient.openSession(false)) {
+ List<AlmSettingDto> almSettingDtos = dbClient.almSettingDao().selectAll(dbSession);
+ validateBitbucket(getALMsDTOs(almSettingDtos, ALM.BITBUCKET));
+ validateBitbucketCloud(getALMsDTOs(almSettingDtos, ALM.BITBUCKET_CLOUD));
+ validateGithub(getALMsDTOs(almSettingDtos, ALM.GITHUB));
+ validateGitlab(getALMsDTOs(almSettingDtos, ALM.GITLAB));
+ validateAzure(getALMsDTOs(almSettingDtos, ALM.AZURE_DEVOPS));
+ }
+ };
+ }
+
+ private static List<AlmSettingDto> getALMsDTOs(List<AlmSettingDto> almSettingDtos, ALM alm) {
+ return almSettingDtos.stream().filter(dto -> dto.getAlm() == alm).collect(Collectors.toList());
+ }
+
+ private void validateGithub(List<AlmSettingDto> almSettingDtos) {
+ try {
+ for (AlmSettingDto dto : almSettingDtos) {
+ githubValidator.validate(dto);
+ }
+ metrics.setGithubStatusToGreen();
+ } catch (RuntimeException e) {
+ metrics.setGithubStatusToRed();
+ }
+ }
+
+ private void validateBitbucket(List<AlmSettingDto> almSettingDtos) {
+ try {
+ for (AlmSettingDto dto : almSettingDtos) {
+ bitbucketServerValidator.validate(dto);
+ }
+ metrics.setBitbucketStatusToGreen();
+ } catch (Exception e) {
+ metrics.setBitbucketStatusToRed();
+ }
+ }
+
+ private void validateBitbucketCloud(List<AlmSettingDto> almSettingDtos) {
+ try {
+ for (AlmSettingDto dto : almSettingDtos) {
+ bitbucketCloudValidator.validate(dto);
+ }
+ metrics.setBitbucketStatusToGreen();
+ } catch (Exception e) {
+ metrics.setBitbucketStatusToRed();
+ }
+ }
+
+ private void validateGitlab(List<AlmSettingDto> almSettingDtos) {
+ try {
+ for (AlmSettingDto dto : almSettingDtos) {
+ gitlabValidator.validate(dto);
+ }
+ metrics.setGitlabStatusToGreen();
+ } catch (Exception e) {
+ metrics.setGitlabStatusToRed();
+ }
+ }
+
+ private void validateAzure(List<AlmSettingDto> almSettingDtos) {
+ try {
+ for (AlmSettingDto dto : almSettingDtos) {
+ azureDevOpsValidator.validate(dto);
+ }
+ metrics.setAzureStatusToGreen();
+ } catch (Exception e) {
+ metrics.setAzureStatusToRed();
+ }
+ }
+}
diff --git a/server/sonar-webserver-monitoring/src/test/java/org/sonar/server/monitoring/ServerMonitoringMetricsTest.java b/server/sonar-webserver-monitoring/src/test/java/org/sonar/server/monitoring/ServerMonitoringMetricsTest.java
new file mode 100644
index 00000000000..0849f04a3e3
--- /dev/null
+++ b/server/sonar-webserver-monitoring/src/test/java/org/sonar/server/monitoring/ServerMonitoringMetricsTest.java
@@ -0,0 +1,82 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2021 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.monitoring;
+
+import io.prometheus.client.Collector;
+import io.prometheus.client.CollectorRegistry;
+import java.util.Collections;
+import java.util.Enumeration;
+import org.junit.Before;
+import org.junit.Test;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+//@Execution(SAME_THREAD) for JUnit5
+public class ServerMonitoringMetricsTest {
+
+ @Before
+ public void before() {
+ CollectorRegistry.defaultRegistry.clear();
+ }
+
+ @Test
+ public void creatingClassShouldAddMetricsToRegistry() {
+ assertThat(sizeOfDefaultRegistry()).isNotPositive();
+
+ new ServerMonitoringMetrics();
+
+ assertThat(sizeOfDefaultRegistry()).isPositive();
+ }
+
+ @Test
+ public void setters_setGreenStatusForMetricsInTheMetricsRegistry() {
+ ServerMonitoringMetrics metrics = new ServerMonitoringMetrics();
+
+ metrics.setGithubStatusToGreen();
+ metrics.setGitlabStatusToGreen();
+ metrics.setAzureStatusToGreen();
+ metrics.setBitbucketStatusToGreen();
+
+ assertThat(CollectorRegistry.defaultRegistry.getSampleValue("github_config_ok")).isZero();
+ assertThat(CollectorRegistry.defaultRegistry.getSampleValue("gitlab_config_ok")).isZero();
+ assertThat(CollectorRegistry.defaultRegistry.getSampleValue("bitbucket_config_ok")).isZero();
+ assertThat(CollectorRegistry.defaultRegistry.getSampleValue("azure_config_ok")).isZero();
+ }
+
+ @Test
+ public void setters_setRedStatusForMetricsInTheMetricsRegistry() {
+ ServerMonitoringMetrics metrics = new ServerMonitoringMetrics();
+
+ metrics.setGithubStatusToRed();
+ metrics.setGitlabStatusToRed();
+ metrics.setAzureStatusToRed();
+ metrics.setBitbucketStatusToRed();
+
+ assertThat(CollectorRegistry.defaultRegistry.getSampleValue("github_config_ok")).isEqualTo(1);
+ assertThat(CollectorRegistry.defaultRegistry.getSampleValue("gitlab_config_ok")).isEqualTo(1);
+ assertThat(CollectorRegistry.defaultRegistry.getSampleValue("bitbucket_config_ok")).isEqualTo(1);
+ assertThat(CollectorRegistry.defaultRegistry.getSampleValue("azure_config_ok")).isEqualTo(1);
+ }
+
+ private int sizeOfDefaultRegistry() {
+ Enumeration<Collector.MetricFamilySamples> metrics = CollectorRegistry.defaultRegistry.metricFamilySamples();
+ return Collections.list(metrics).size();
+ }
+}
diff --git a/server/sonar-webserver-monitoring/src/test/java/org/sonar/server/monitoring/devops/DevOpsPlatformsMetricsCollectorTest.java b/server/sonar-webserver-monitoring/src/test/java/org/sonar/server/monitoring/devops/DevOpsPlatformsMetricsCollectorTest.java
new file mode 100644
index 00000000000..d464825e9ee
--- /dev/null
+++ b/server/sonar-webserver-monitoring/src/test/java/org/sonar/server/monitoring/devops/DevOpsPlatformsMetricsCollectorTest.java
@@ -0,0 +1,185 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2021 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.monitoring.devops;
+
+import org.sonar.api.config.Configuration;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Optional;
+import java.util.Set;
+import org.junit.Before;
+import org.junit.Test;
+import org.sonar.alm.client.azure.AzureDevOpsValidator;
+import org.sonar.alm.client.bitbucket.bitbucketcloud.BitbucketCloudValidator;
+import org.sonar.alm.client.bitbucketserver.BitbucketServerSettingsValidator;
+import org.sonar.alm.client.github.GithubGlobalSettingsValidator;
+import org.sonar.alm.client.gitlab.GitlabGlobalSettingsValidator;
+import org.sonar.db.DbClient;
+import org.sonar.db.alm.setting.ALM;
+import org.sonar.db.alm.setting.AlmSettingDao;
+import org.sonar.db.alm.setting.AlmSettingDto;
+import org.sonar.server.monitoring.ServerMonitoringMetrics;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.Mockito.doThrow;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.verifyNoInteractions;
+import static org.mockito.Mockito.when;
+
+public class DevOpsPlatformsMetricsCollectorTest {
+
+ private final ServerMonitoringMetrics serverMonitoringMetrics = mock(ServerMonitoringMetrics.class);
+ private final DbClient dbClient = mock(DbClient.class);
+ private final BitbucketServerSettingsValidator bitbucketServerValidator = mock(BitbucketServerSettingsValidator.class);
+ private final GithubGlobalSettingsValidator githubValidator = mock(GithubGlobalSettingsValidator.class);
+ private final GitlabGlobalSettingsValidator gitlabValidator = mock(GitlabGlobalSettingsValidator.class);
+ private final BitbucketCloudValidator bitbucketCloudValidator = mock(BitbucketCloudValidator.class);
+ private final AzureDevOpsValidator azureDevOpsValidator = mock(AzureDevOpsValidator.class);
+ private final Configuration config = mock(Configuration.class);
+
+ private DevOpsPlatformsMetricsCollector collector;
+
+ @Before
+ public void before() {
+ collector = new DevOpsPlatformsMetricsCollector(serverMonitoringMetrics,
+ dbClient, bitbucketServerValidator, githubValidator, gitlabValidator, bitbucketCloudValidator,
+ azureDevOpsValidator, config);
+ }
+
+ @Test
+ public void start_startsNewDeamonThread() {
+ collector.start();
+
+ Optional<Thread> newDeamonThread = findNewDeamonThread();
+
+ assertThat(newDeamonThread).isPresent();
+ assertThat(newDeamonThread.get().isDaemon()).isTrue();
+ }
+
+ @Test
+ public void createTask_givenOneConfigForEachALM_allValidatorsAreCalled() {
+ AlmSettingDao dao = mock(AlmSettingDao.class);
+ List<AlmSettingDto> almSettingDtos = createAlmSettingDtos();
+ when(dao.selectAll(any())).thenReturn(almSettingDtos);
+ when(dbClient.almSettingDao()).thenReturn(dao);
+
+ collector.createTask().run();
+
+ verify(bitbucketCloudValidator, times(1)).validate(findDto(ALM.BITBUCKET_CLOUD, almSettingDtos));
+ verify(bitbucketServerValidator, times(1)).validate(findDto(ALM.BITBUCKET, almSettingDtos));
+ verify(azureDevOpsValidator, times(1)).validate(findDto(ALM.AZURE_DEVOPS, almSettingDtos));
+ verify(gitlabValidator, times(1)).validate(findDto(ALM.GITLAB, almSettingDtos));
+ verify(githubValidator, times(1)).validate(findDto(ALM.GITHUB, almSettingDtos));
+ }
+
+ @Test
+ public void createTask_givenOnlyGitHubConfigured_validateOnlyGithub() {
+ AlmSettingDao dao = mock(AlmSettingDao.class);
+ AlmSettingDto githubDto = new AlmSettingDto();
+ githubDto.setAlm(ALM.GITHUB);
+ when(dao.selectAll(any())).thenReturn(List.of(githubDto));
+ when(dbClient.almSettingDao()).thenReturn(dao);
+
+ collector.createTask().run();
+
+ verifyNoInteractions(bitbucketCloudValidator);
+ verifyNoInteractions(bitbucketServerValidator);
+ verifyNoInteractions(azureDevOpsValidator);
+ verifyNoInteractions(gitlabValidator);
+
+ verify(githubValidator, times(1)).validate(githubDto);
+ }
+
+ @Test
+ public void createTask_givenAllValidationsFailing_setAllMetricsStatusesToFalse() {
+ AlmSettingDao dao = mock(AlmSettingDao.class);
+ List<AlmSettingDto> almSettingDtos = createAlmSettingDtos();
+ when(dao.selectAll(any())).thenReturn(almSettingDtos);
+ when(dbClient.almSettingDao()).thenReturn(dao);
+
+ doThrow(new RuntimeException()).when(bitbucketCloudValidator).validate(any());
+ doThrow(new RuntimeException()).when(bitbucketServerValidator).validate(any());
+ doThrow(new RuntimeException()).when(azureDevOpsValidator).validate(any());
+ doThrow(new RuntimeException()).when(gitlabValidator).validate(any());
+ doThrow(new RuntimeException()).when(githubValidator).validate(any());
+
+ collector.createTask().run();
+
+ verify(serverMonitoringMetrics, times(2)).setBitbucketStatusToRed(); //2 validators for Bitbucket
+ verify(serverMonitoringMetrics, times(1)).setAzureStatusToRed();
+ verify(serverMonitoringMetrics, times(1)).setGitlabStatusToRed();
+ verify(serverMonitoringMetrics, times(1)).setGithubStatusToRed();
+ }
+
+ @Test
+ public void createTask_givenAllValidationsArePassing_setAllMetricsStatusesToTrue() {
+ AlmSettingDao dao = mock(AlmSettingDao.class);
+ List<AlmSettingDto> almSettingDtos = createAlmSettingDtos();
+ when(dao.selectAll(any())).thenReturn(almSettingDtos);
+ when(dbClient.almSettingDao()).thenReturn(dao);
+
+ collector.createTask().run();
+
+ verify(serverMonitoringMetrics, times(2)).setBitbucketStatusToGreen(); //2 validators for Bitbucket
+ verify(serverMonitoringMetrics, times(1)).setAzureStatusToGreen();
+ verify(serverMonitoringMetrics, times(1)).setGitlabStatusToGreen();
+ verify(serverMonitoringMetrics, times(1)).setGithubStatusToGreen();
+ }
+
+ @Test
+ public void createTask_givenFirstGithubValidationNotPassingAndSecondPassing_setGitHubValidationToTrue() {
+ AlmSettingDao dao = mock(AlmSettingDao.class);
+ List<AlmSettingDto> almSettingDtos = createAlmSettingDtos();
+ when(dao.selectAll(any())).thenReturn(almSettingDtos);
+ when(dbClient.almSettingDao()).thenReturn(dao);
+
+ when(githubValidator.validate(any()))
+ .thenThrow(new RuntimeException())
+ .thenReturn(null);
+
+ collector.createTask().run();
+
+ verify(serverMonitoringMetrics, times(1)).setGithubStatusToRed();
+ verify(serverMonitoringMetrics, times(0)).setGithubStatusToGreen();
+ }
+
+ private AlmSettingDto findDto(ALM alm, List<AlmSettingDto> almSettingDtos) {
+ return almSettingDtos.stream().filter(d -> d.getAlm() == alm).findFirst().get();
+ }
+
+ private List<AlmSettingDto> createAlmSettingDtos() {
+ List<AlmSettingDto> dtos = new ArrayList<>();
+ for(ALM alm : ALM.values()) {
+ AlmSettingDto almSettingDto = new AlmSettingDto();
+ almSettingDto.setAlm(alm);
+ dtos.add(almSettingDto);
+ }
+ return dtos;
+ }
+
+ private Optional<Thread> findNewDeamonThread() {
+ Set<Thread> threadSet = Thread.getAllStackTraces().keySet();
+ String threadPartialName = DevOpsPlatformsMetricsCollector.class.getName();
+ return threadSet.stream().filter(t -> t.getName().contains(threadPartialName)).findFirst();
+ }
+}
diff --git a/server/sonar-webserver-webapi/src/main/java/org/sonar/server/almintegration/ws/AlmIntegrationsWSModule.java b/server/sonar-webserver-webapi/src/main/java/org/sonar/server/almintegration/ws/AlmIntegrationsWSModule.java
index 0ab3065bc59..528b78e98bd 100644
--- a/server/sonar-webserver-webapi/src/main/java/org/sonar/server/almintegration/ws/AlmIntegrationsWSModule.java
+++ b/server/sonar-webserver-webapi/src/main/java/org/sonar/server/almintegration/ws/AlmIntegrationsWSModule.java
@@ -20,9 +20,6 @@
package org.sonar.server.almintegration.ws;
import org.sonar.core.platform.Module;
-import org.sonar.server.almintegration.validator.BitbucketServerSettingsValidator;
-import org.sonar.server.almintegration.validator.GithubGlobalSettingsValidator;
-import org.sonar.server.almintegration.validator.GitlabGlobalSettingsValidator;
import org.sonar.server.almintegration.ws.azure.ImportAzureProjectAction;
import org.sonar.server.almintegration.ws.azure.ListAzureProjectsAction;
import org.sonar.server.almintegration.ws.azure.SearchAzureReposAction;
@@ -50,14 +47,11 @@ public class AlmIntegrationsWSModule extends Module {
SearchBitbucketServerReposAction.class,
SearchBitbucketCloudReposAction.class,
GetGithubClientIdAction.class,
- GithubGlobalSettingsValidator.class,
- BitbucketServerSettingsValidator.class,
ImportGithubProjectAction.class,
ListGithubOrganizationsAction.class,
ListGithubRepositoriesAction.class,
ImportGitLabProjectAction.class,
SearchGitlabReposAction.class,
- GitlabGlobalSettingsValidator.class,
ImportAzureProjectAction.class,
ListAzureProjectsAction.class,
SearchAzureReposAction.class,
diff --git a/server/sonar-webserver-webapi/src/main/java/org/sonar/server/almsettings/ws/ValidateAction.java b/server/sonar-webserver-webapi/src/main/java/org/sonar/server/almsettings/ws/ValidateAction.java
index 8195eb94a0e..4fb5b98e39b 100644
--- a/server/sonar-webserver-webapi/src/main/java/org/sonar/server/almsettings/ws/ValidateAction.java
+++ b/server/sonar-webserver-webapi/src/main/java/org/sonar/server/almsettings/ws/ValidateAction.java
@@ -20,7 +20,9 @@
package org.sonar.server.almsettings.ws;
import org.sonar.alm.client.azure.AzureDevOpsHttpClient;
+import org.sonar.alm.client.azure.AzureDevOpsValidator;
import org.sonar.alm.client.bitbucket.bitbucketcloud.BitbucketCloudRestClient;
+import org.sonar.alm.client.bitbucket.bitbucketcloud.BitbucketCloudValidator;
import org.sonar.api.config.internal.Encryption;
import org.sonar.api.config.internal.Settings;
import org.sonar.api.server.ws.Request;
@@ -29,9 +31,9 @@ import org.sonar.api.server.ws.WebService;
import org.sonar.db.DbClient;
import org.sonar.db.DbSession;
import org.sonar.db.alm.setting.AlmSettingDto;
-import org.sonar.server.almintegration.validator.BitbucketServerSettingsValidator;
-import org.sonar.server.almintegration.validator.GithubGlobalSettingsValidator;
-import org.sonar.server.almintegration.validator.GitlabGlobalSettingsValidator;
+import org.sonar.alm.client.bitbucketserver.BitbucketServerSettingsValidator;
+import org.sonar.alm.client.github.GithubGlobalSettingsValidator;
+import org.sonar.alm.client.gitlab.GitlabGlobalSettingsValidator;
import org.sonar.server.user.UserSession;
public class ValidateAction implements AlmSettingsWsAction {
@@ -46,7 +48,8 @@ public class ValidateAction implements AlmSettingsWsAction {
private final GitlabGlobalSettingsValidator gitlabSettingsValidator;
private final GithubGlobalSettingsValidator githubGlobalSettingsValidator;
private final BitbucketServerSettingsValidator bitbucketServerSettingsValidator;
- private final BitbucketCloudRestClient bitbucketCloudRestClient;
+ private final BitbucketCloudValidator bitbucketCloudValidator;
+ private final AzureDevOpsValidator azureDevOpsValidator;
public ValidateAction(DbClient dbClient,
Settings settings,
@@ -56,7 +59,8 @@ public class ValidateAction implements AlmSettingsWsAction {
GithubGlobalSettingsValidator githubGlobalSettingsValidator,
GitlabGlobalSettingsValidator gitlabSettingsValidator,
BitbucketServerSettingsValidator bitbucketServerSettingsValidator,
- BitbucketCloudRestClient bitbucketCloudRestClient) {
+ BitbucketCloudValidator bitbucketCloudValidator,
+ AzureDevOpsValidator azureDevOpsValidator) {
this.dbClient = dbClient;
this.encryption = settings.getEncryption();
this.userSession = userSession;
@@ -65,7 +69,8 @@ public class ValidateAction implements AlmSettingsWsAction {
this.githubGlobalSettingsValidator = githubGlobalSettingsValidator;
this.gitlabSettingsValidator = gitlabSettingsValidator;
this.bitbucketServerSettingsValidator = bitbucketServerSettingsValidator;
- this.bitbucketCloudRestClient = bitbucketCloudRestClient;
+ this.bitbucketCloudValidator = bitbucketCloudValidator;
+ this.azureDevOpsValidator = azureDevOpsValidator;
}
@Override
@@ -106,24 +111,12 @@ public class ValidateAction implements AlmSettingsWsAction {
bitbucketServerSettingsValidator.validate(almSettingDto);
break;
case BITBUCKET_CLOUD:
- validateBitbucketCloud(almSettingDto);
+ bitbucketCloudValidator.validate(almSettingDto);
break;
case AZURE_DEVOPS:
- validateAzure(almSettingDto);
+ azureDevOpsValidator.validate(almSettingDto);
break;
}
}
}
-
- private void validateAzure(AlmSettingDto almSettingDto) {
- try {
- azureDevOpsHttpClient.checkPAT(almSettingDto.getUrl(), almSettingDto.getDecryptedPersonalAccessToken(encryption));
- } catch (IllegalArgumentException e) {
- throw new IllegalArgumentException("Invalid Azure URL or Personal Access Token", e);
- }
- }
-
- private void validateBitbucketCloud(AlmSettingDto almSettingDto) {
- bitbucketCloudRestClient.validate(almSettingDto.getClientId(), almSettingDto.getDecryptedClientSecret(encryption), almSettingDto.getAppId());
- }
}
diff --git a/server/sonar-webserver-webapi/src/test/java/org/sonar/server/almsettings/ws/ValidateActionTest.java b/server/sonar-webserver-webapi/src/test/java/org/sonar/server/almsettings/ws/ValidateActionTest.java
index 97da7ad21fb..985ad92c9c8 100644
--- a/server/sonar-webserver-webapi/src/test/java/org/sonar/server/almsettings/ws/ValidateActionTest.java
+++ b/server/sonar-webserver-webapi/src/test/java/org/sonar/server/almsettings/ws/ValidateActionTest.java
@@ -24,7 +24,9 @@ import org.junit.Rule;
import org.junit.Test;
import org.mockito.ArgumentCaptor;
import org.sonar.alm.client.azure.AzureDevOpsHttpClient;
+import org.sonar.alm.client.azure.AzureDevOpsValidator;
import org.sonar.alm.client.bitbucket.bitbucketcloud.BitbucketCloudRestClient;
+import org.sonar.alm.client.bitbucket.bitbucketcloud.BitbucketCloudValidator;
import org.sonar.api.config.internal.Encryption;
import org.sonar.api.config.internal.Settings;
import org.sonar.api.resources.ResourceTypes;
@@ -33,9 +35,9 @@ import org.sonar.db.DbTester;
import org.sonar.db.alm.setting.ALM;
import org.sonar.db.alm.setting.AlmSettingDto;
import org.sonar.db.user.UserDto;
-import org.sonar.server.almintegration.validator.BitbucketServerSettingsValidator;
-import org.sonar.server.almintegration.validator.GithubGlobalSettingsValidator;
-import org.sonar.server.almintegration.validator.GitlabGlobalSettingsValidator;
+import org.sonar.alm.client.bitbucketserver.BitbucketServerSettingsValidator;
+import org.sonar.alm.client.github.GithubGlobalSettingsValidator;
+import org.sonar.alm.client.gitlab.GitlabGlobalSettingsValidator;
import org.sonar.server.almsettings.MultipleAlmFeatureProvider;
import org.sonar.server.component.ComponentFinder;
import org.sonar.server.exceptions.ForbiddenException;
@@ -66,13 +68,15 @@ public class ValidateActionTest {
private final ComponentFinder componentFinder = new ComponentFinder(db.getDbClient(), mock(ResourceTypes.class));
private final AlmSettingsSupport almSettingsSupport = new AlmSettingsSupport(db.getDbClient(), userSession, componentFinder, multipleAlmFeatureProvider);
private final AzureDevOpsHttpClient azureDevOpsHttpClient = mock(AzureDevOpsHttpClient.class);
+ private final BitbucketCloudRestClient bitbucketCloudRestClient = mock(BitbucketCloudRestClient.class);
private final GitlabGlobalSettingsValidator gitlabSettingsValidator = mock(GitlabGlobalSettingsValidator.class);
private final GithubGlobalSettingsValidator githubGlobalSettingsValidator = mock(GithubGlobalSettingsValidator.class);
private final BitbucketServerSettingsValidator bitbucketServerSettingsValidator = mock(BitbucketServerSettingsValidator.class);
- private final BitbucketCloudRestClient bitbucketCloudRestClient = mock(BitbucketCloudRestClient.class);
+ private final BitbucketCloudValidator bitbucketCloudValidator = new BitbucketCloudValidator(bitbucketCloudRestClient, settings);
+ private final AzureDevOpsValidator azureDevOpsValidator = new AzureDevOpsValidator(azureDevOpsHttpClient, settings);
private final WsActionTester ws = new WsActionTester(
new ValidateAction(db.getDbClient(), settings, userSession, almSettingsSupport, azureDevOpsHttpClient, githubGlobalSettingsValidator,
- gitlabSettingsValidator, bitbucketServerSettingsValidator, bitbucketCloudRestClient));
+ gitlabSettingsValidator, bitbucketServerSettingsValidator, bitbucketCloudValidator, azureDevOpsValidator));
@BeforeClass
public static void setUp() {
diff --git a/server/sonar-webserver/build.gradle b/server/sonar-webserver/build.gradle
index 89cf5af8373..ea2fcd9cf6c 100644
--- a/server/sonar-webserver/build.gradle
+++ b/server/sonar-webserver/build.gradle
@@ -21,6 +21,7 @@ dependencies {
compile project(':server:sonar-process')
compile project(':server:sonar-webserver-core')
compile project(':server:sonar-webserver-webapi')
+ compile project(':server:sonar-webserver-monitoring')
compileOnly 'com.google.code.findbugs:jsr305'
diff --git a/server/sonar-webserver/src/main/java/org/sonar/server/platform/platformlevel/PlatformLevel4.java b/server/sonar-webserver/src/main/java/org/sonar/server/platform/platformlevel/PlatformLevel4.java
index a0b22729d1b..fe0c229346c 100644
--- a/server/sonar-webserver/src/main/java/org/sonar/server/platform/platformlevel/PlatformLevel4.java
+++ b/server/sonar-webserver/src/main/java/org/sonar/server/platform/platformlevel/PlatformLevel4.java
@@ -22,11 +22,16 @@ package org.sonar.server.platform.platformlevel;
import java.util.List;
import org.sonar.alm.client.TimeoutConfigurationImpl;
import org.sonar.alm.client.azure.AzureDevOpsHttpClient;
+import org.sonar.alm.client.azure.AzureDevOpsValidator;
import org.sonar.alm.client.bitbucket.bitbucketcloud.BitbucketCloudRestClient;
+import org.sonar.alm.client.bitbucket.bitbucketcloud.BitbucketCloudValidator;
import org.sonar.alm.client.bitbucketserver.BitbucketServerRestClient;
+import org.sonar.alm.client.bitbucketserver.BitbucketServerSettingsValidator;
import org.sonar.alm.client.github.GithubApplicationClientImpl;
import org.sonar.alm.client.github.GithubApplicationHttpClientImpl;
+import org.sonar.alm.client.github.GithubGlobalSettingsValidator;
import org.sonar.alm.client.github.security.GithubAppSecurityImpl;
+import org.sonar.alm.client.gitlab.GitlabGlobalSettingsValidator;
import org.sonar.alm.client.gitlab.GitlabHttpClient;
import org.sonar.api.profiles.XMLProfileParser;
import org.sonar.api.profiles.XMLProfileSerializer;
@@ -122,6 +127,8 @@ import org.sonar.server.metric.MetricFinder;
import org.sonar.server.metric.UnanalyzedLanguageMetrics;
import org.sonar.server.metric.ws.MetricsWsModule;
import org.sonar.server.monitoring.MonitoringWsModule;
+import org.sonar.server.monitoring.devops.DevOpsPlatformsMetricsCollector;
+import org.sonar.server.monitoring.ServerMonitoringMetrics;
import org.sonar.server.newcodeperiod.ws.NewCodePeriodsWsModule;
import org.sonar.server.notification.NotificationModule;
import org.sonar.server.notification.ws.NotificationWsModule;
@@ -508,6 +515,11 @@ public class PlatformLevel4 extends PlatformLevel {
GitlabHttpClient.class,
AzureDevOpsHttpClient.class,
AlmIntegrationsWSModule.class,
+ BitbucketCloudValidator.class,
+ BitbucketServerSettingsValidator.class,
+ GithubGlobalSettingsValidator.class,
+ GitlabGlobalSettingsValidator.class,
+ AzureDevOpsValidator.class,
// ALM settings
AlmSettingsWsModule.class,
@@ -564,6 +576,10 @@ public class PlatformLevel4 extends PlatformLevel {
TelemetryDaemon.class,
TelemetryClient.class,
+ // monitoring
+ ServerMonitoringMetrics.class,
+ DevOpsPlatformsMetricsCollector.class,
+
PluginsRiskConsentFilter.class
);
diff --git a/server/sonar-webserver/src/main/java/org/sonar/server/platform/platformlevel/PlatformLevelSafeMode.java b/server/sonar-webserver/src/main/java/org/sonar/server/platform/platformlevel/PlatformLevelSafeMode.java
index fb0bea230f4..311b34c3cff 100644
--- a/server/sonar-webserver/src/main/java/org/sonar/server/platform/platformlevel/PlatformLevelSafeMode.java
+++ b/server/sonar-webserver/src/main/java/org/sonar/server/platform/platformlevel/PlatformLevelSafeMode.java
@@ -20,6 +20,7 @@
package org.sonar.server.platform.platformlevel;
import org.sonar.server.authentication.SafeModeUserSession;
+import org.sonar.server.monitoring.ServerMonitoringMetrics;
import org.sonar.server.platform.ServerImpl;
import org.sonar.server.platform.db.migration.AutoDbMigration;
import org.sonar.server.platform.db.migration.DatabaseMigrationImpl;
@@ -57,7 +58,10 @@ public class PlatformLevelSafeMode extends PlatformLevel {
// WS engine
SafeModeUserSession.class,
WebServiceEngine.class,
- WebServiceFilter.class);
+ WebServiceFilter.class,
+
+ // Monitoring
+ ServerMonitoringMetrics.class);
addIfStartupLeader(
DatabaseMigrationImpl.class,
MigrationEngineModule.class,
diff --git a/settings.gradle b/settings.gradle
index fb4ee883d96..b4719ca3386 100644
--- a/settings.gradle
+++ b/settings.gradle
@@ -39,6 +39,7 @@ include 'server:sonar-webserver-es'
include 'server:sonar-webserver-webapi'
include 'server:sonar-webserver-ws'
include 'server:sonar-alm-client'
+include 'server:sonar-webserver-monitoring'
include 'sonar-application'
include 'sonar-check-api'