From: Lukasz Jarocki Date: Wed, 8 Dec 2021 08:35:06 +0000 (+0100) Subject: SONAR-15769 added metrics for integration with devops platforms X-Git-Tag: 9.3.0.51899~127 X-Git-Url: https://source.dussan.org/?a=commitdiff_plain;h=d58a45b4b4fd196cccb8cbd1c9a479a3c783afe3;p=sonarqube.git SONAR-15769 added metrics for integration with devops platforms --- 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-alm-client/src/main/java/org/sonar/alm/client/bitbucketserver/BitbucketServerSettingsValidator.java b/server/sonar-alm-client/src/main/java/org/sonar/alm/client/bitbucketserver/BitbucketServerSettingsValidator.java new file mode 100644 index 00000000000..c4d0656591f --- /dev/null +++ b/server/sonar-alm-client/src/main/java/org/sonar/alm/client/bitbucketserver/BitbucketServerSettingsValidator.java @@ -0,0 +1,48 @@ +/* + * 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.bitbucketserver; + +import org.sonar.api.config.internal.Encryption; +import org.sonar.api.config.internal.Settings; +import org.sonar.api.server.ServerSide; +import org.sonar.db.alm.setting.AlmSettingDto; + +@ServerSide +public class BitbucketServerSettingsValidator { + private final BitbucketServerRestClient bitbucketServerRestClient; + private final Encryption encryption; + + public BitbucketServerSettingsValidator(BitbucketServerRestClient bitbucketServerRestClient, Settings settings) { + this.bitbucketServerRestClient = bitbucketServerRestClient; + this.encryption = settings.getEncryption(); + } + + public void validate(AlmSettingDto almSettingDto) { + String bitbucketUrl = almSettingDto.getUrl(); + String bitbucketToken = almSettingDto.getDecryptedPersonalAccessToken(encryption); + if (bitbucketUrl == null || bitbucketToken == null) { + throw new IllegalArgumentException("Your global Bitbucket Server configuration is incomplete."); + } + + bitbucketServerRestClient.validateUrl(bitbucketUrl); + bitbucketServerRestClient.validateToken(bitbucketUrl, bitbucketToken); + bitbucketServerRestClient.validateReadPermission(bitbucketUrl, bitbucketToken); + } +} diff --git a/server/sonar-alm-client/src/main/java/org/sonar/alm/client/github/GithubGlobalSettingsValidator.java b/server/sonar-alm-client/src/main/java/org/sonar/alm/client/github/GithubGlobalSettingsValidator.java new file mode 100644 index 00000000000..3542e7dac0d --- /dev/null +++ b/server/sonar-alm-client/src/main/java/org/sonar/alm/client/github/GithubGlobalSettingsValidator.java @@ -0,0 +1,63 @@ +/* + * 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.github; + +import java.util.Optional; +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.api.server.ServerSide; +import org.sonar.db.alm.setting.AlmSettingDto; + +import static org.apache.commons.lang.StringUtils.isBlank; + +@ServerSide +public class GithubGlobalSettingsValidator { + + private final Encryption encryption; + private final GithubApplicationClient githubApplicationClient; + + public GithubGlobalSettingsValidator(GithubApplicationClientImpl githubApplicationClient, Settings settings) { + this.encryption = settings.getEncryption(); + this.githubApplicationClient = githubApplicationClient; + } + + public GithubAppConfiguration validate(AlmSettingDto settings) { + long appId; + try { + appId = Long.parseLong(Optional.ofNullable(settings.getAppId()).orElseThrow(() -> new IllegalArgumentException("Missing appId"))); + } catch (NumberFormatException e) { + throw new IllegalArgumentException("Invalid appId; " + e.getMessage()); + } + if (isBlank(settings.getClientId())) { + throw new IllegalArgumentException("Missing Client Id"); + } + if (isBlank(settings.getDecryptedClientSecret(encryption))) { + throw new IllegalArgumentException("Missing Client Secret"); + } + GithubAppConfiguration configuration = new GithubAppConfiguration(appId, settings.getDecryptedPrivateKey(encryption), + settings.getUrl()); + + githubApplicationClient.checkApiEndpoint(configuration); + githubApplicationClient.checkAppPermissions(configuration); + + return configuration; + } +} diff --git a/server/sonar-alm-client/src/main/java/org/sonar/alm/client/gitlab/GitlabGlobalSettingsValidator.java b/server/sonar-alm-client/src/main/java/org/sonar/alm/client/gitlab/GitlabGlobalSettingsValidator.java new file mode 100644 index 00000000000..4d069561a4c --- /dev/null +++ b/server/sonar-alm-client/src/main/java/org/sonar/alm/client/gitlab/GitlabGlobalSettingsValidator.java @@ -0,0 +1,52 @@ +/* + * 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.gitlab; + +import org.sonar.api.config.internal.Encryption; +import org.sonar.api.config.internal.Settings; +import org.sonar.api.server.ServerSide; +import org.sonar.db.alm.setting.AlmSettingDto; + +@ServerSide +public class GitlabGlobalSettingsValidator { + + private final Encryption encryption; + private final GitlabHttpClient gitlabHttpClient; + + public GitlabGlobalSettingsValidator(GitlabHttpClient gitlabHttpClient, Settings settings) { + this.encryption = settings.getEncryption(); + this.gitlabHttpClient = gitlabHttpClient; + } + + public void validate(AlmSettingDto almSettingDto) { + String gitlabUrl = almSettingDto.getUrl(); + String accessToken = almSettingDto.getDecryptedPersonalAccessToken(encryption); + + if (gitlabUrl == null || accessToken == null) { + throw new IllegalArgumentException("Your Gitlab global configuration is incomplete."); + } + + gitlabHttpClient.checkUrl(gitlabUrl); + gitlabHttpClient.checkToken(gitlabUrl, accessToken); + gitlabHttpClient.checkReadPermission(gitlabUrl, accessToken); + gitlabHttpClient.checkWritePermission(gitlabUrl, accessToken); + } + +} 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-alm-client/src/test/java/org/sonar/alm/client/bitbucketserver/BitbucketServerSettingsValidatorTest.java b/server/sonar-alm-client/src/test/java/org/sonar/alm/client/bitbucketserver/BitbucketServerSettingsValidatorTest.java new file mode 100644 index 00000000000..a30709b11d9 --- /dev/null +++ b/server/sonar-alm-client/src/test/java/org/sonar/alm/client/bitbucketserver/BitbucketServerSettingsValidatorTest.java @@ -0,0 +1,101 @@ +/* + * 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.bitbucketserver; + +import org.junit.BeforeClass; +import org.junit.Test; +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; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyString; +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 BitbucketServerSettingsValidatorTest { + private static final Encryption encryption = mock(Encryption.class); + private static final Settings settings = mock(Settings.class); + + private final BitbucketServerRestClient bitbucketServerRestClient = mock(BitbucketServerRestClient.class); + private final BitbucketServerSettingsValidator underTest = new BitbucketServerSettingsValidator(bitbucketServerRestClient, settings); + + @BeforeClass + public static void setUp() { + when(settings.getEncryption()).thenReturn(encryption); + } + + @Test + public void validate_success() { + AlmSettingDto almSettingDto = createNewBitbucketDto("http://abc.com", "abc"); + when(encryption.isEncrypted(any())).thenReturn(false); + + underTest.validate(almSettingDto); + + verify(bitbucketServerRestClient, times(1)).validateUrl("http://abc.com"); + verify(bitbucketServerRestClient, times(1)).validateToken("http://abc.com", "abc"); + verify(bitbucketServerRestClient, times(1)).validateReadPermission("http://abc.com", "abc"); + } + + @Test + public void validate_success_with_encrypted_token() { + String encryptedToken = "abc"; + String decryptedToken = "decrypted-token"; + AlmSettingDto almSettingDto = createNewBitbucketDto("http://abc.com", encryptedToken); + when(encryption.isEncrypted(encryptedToken)).thenReturn(true); + when(encryption.decrypt(encryptedToken)).thenReturn(decryptedToken); + + underTest.validate(almSettingDto); + + verify(bitbucketServerRestClient, times(1)).validateUrl("http://abc.com"); + verify(bitbucketServerRestClient, times(1)).validateToken("http://abc.com", decryptedToken); + verify(bitbucketServerRestClient, times(1)).validateReadPermission("http://abc.com", decryptedToken); + } + + @Test + public void validate_failure_on_incomplete_configuration() { + AlmSettingDto almSettingDto = createNewBitbucketDto(null, "abc"); + + assertThatThrownBy(() -> underTest.validate(almSettingDto)) + .isInstanceOf(IllegalArgumentException.class); + } + + @Test + public void validate_failure_on_bitbucket_server_api_error() { + doThrow(new IllegalArgumentException("error")).when(bitbucketServerRestClient).validateUrl(anyString()); + 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-alm-client/src/test/java/org/sonar/alm/client/github/GithubGlobalSettingsValidatorTest.java b/server/sonar-alm-client/src/test/java/org/sonar/alm/client/github/GithubGlobalSettingsValidatorTest.java new file mode 100644 index 00000000000..317cfdc1a32 --- /dev/null +++ b/server/sonar-alm-client/src/test/java/org/sonar/alm/client/github/GithubGlobalSettingsValidatorTest.java @@ -0,0 +1,138 @@ +/* + * 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.github; + +import javax.annotation.Nullable; +import org.junit.BeforeClass; +import org.junit.Test; +import org.mockito.ArgumentCaptor; +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; +import static org.assertj.core.api.Assertions.assertThatThrownBy; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +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); + + @BeforeClass + public static void setUp() { + when(settings.getEncryption()).thenReturn(encryption); + } + + @Test + public void github_global_settings_validation() { + AlmSettingDto almSettingDto = createNewGithubDto("clientId", "clientSecret", EXAMPLE_APP_ID, EXAMPLE_PRIVATE_KEY); + + when(encryption.isEncrypted(any())).thenReturn(false); + + GithubAppConfiguration configuration = underTest.validate(almSettingDto); + + ArgumentCaptor configurationArgumentCaptor = ArgumentCaptor.forClass(GithubAppConfiguration.class); + verify(appClient).checkApiEndpoint(configurationArgumentCaptor.capture()); + verify(appClient).checkAppPermissions(configurationArgumentCaptor.capture()); + assertThat(configuration.getId()).isEqualTo(configurationArgumentCaptor.getAllValues().get(0).getId()); + assertThat(configuration.getId()).isEqualTo(configurationArgumentCaptor.getAllValues().get(1).getId()); + } + + @Test + public void github_global_settings_validation_with_encrypted_key() { + String encryptedKey = "encrypted-key"; + String decryptedKey = "decrypted-key"; + AlmSettingDto almSettingDto = createNewGithubDto("clientId", "clientSecret", EXAMPLE_APP_ID, encryptedKey); + + when(encryption.isEncrypted(encryptedKey)).thenReturn(true); + when(encryption.decrypt(encryptedKey)).thenReturn(decryptedKey); + + GithubAppConfiguration configuration = underTest.validate(almSettingDto); + + ArgumentCaptor configurationArgumentCaptor = ArgumentCaptor.forClass(GithubAppConfiguration.class); + verify(appClient).checkApiEndpoint(configurationArgumentCaptor.capture()); + verify(appClient).checkAppPermissions(configurationArgumentCaptor.capture()); + assertThat(configuration.getId()).isEqualTo(configurationArgumentCaptor.getAllValues().get(0).getId()); + assertThat(decryptedKey).isEqualTo(configurationArgumentCaptor.getAllValues().get(0).getPrivateKey()); + assertThat(configuration.getId()).isEqualTo(configurationArgumentCaptor.getAllValues().get(1).getId()); + assertThat(decryptedKey).isEqualTo(configurationArgumentCaptor.getAllValues().get(1).getPrivateKey()); + } + + @Test + public void github_validation_checks_invalid_appId() { + AlmSettingDto almSettingDto = createNewGithubDto("clientId", "clientSecret", "abc", null); + + assertThatThrownBy(() -> underTest.validate(almSettingDto)) + .isInstanceOf(IllegalArgumentException.class) + .hasMessage("Invalid appId; For input string: \"abc\""); + } + + @Test + public void github_validation_checks_missing_appId() { + AlmSettingDto almSettingDto = new AlmSettingDto(); + almSettingDto.setAppId(null); + + assertThatThrownBy(() -> underTest.validate(almSettingDto)) + .isInstanceOf(IllegalArgumentException.class) + .hasMessage("Missing appId"); + } + + @Test + public void github_validation_checks_missing_clientId() { + AlmSettingDto almSettingDto = createNewGithubDto(null, null, EXAMPLE_APP_ID, null); + + assertThatThrownBy(() -> underTest.validate(almSettingDto)) + .isInstanceOf(IllegalArgumentException.class) + .hasMessage("Missing Client Id"); + } + + @Test + public void github_validation_checks_missing_clientSecret() { + 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-alm-client/src/test/java/org/sonar/alm/client/gitlab/GitlabGlobalSettingsValidatorTest.java b/server/sonar-alm-client/src/test/java/org/sonar/alm/client/gitlab/GitlabGlobalSettingsValidatorTest.java new file mode 100644 index 00000000000..3b033252c7c --- /dev/null +++ b/server/sonar-alm-client/src/test/java/org/sonar/alm/client/gitlab/GitlabGlobalSettingsValidatorTest.java @@ -0,0 +1,104 @@ +/* + * 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.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; +import org.sonar.db.alm.setting.AlmSettingDto; + +import static org.assertj.core.api.Assertions.assertThatThrownBy; +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 GitlabGlobalSettingsValidatorTest { + private static final Encryption encryption = mock(Encryption.class); + private static final Settings settings = mock(Settings.class); + + private final GitlabHttpClient gitlabHttpClient = mock(GitlabHttpClient.class); + + private final GitlabGlobalSettingsValidator underTest = new GitlabGlobalSettingsValidator(gitlabHttpClient, settings); + + @BeforeClass + public static void setUp() { + when(settings.getEncryption()).thenReturn(encryption); + } + + @Test + public void validate_success() { + String token = "personal-access-token"; + AlmSettingDto almSettingDto = new AlmSettingDto() + .setUrl("https://gitlab.com/api") + .setPersonalAccessToken("personal-access-token"); + when(encryption.isEncrypted(token)).thenReturn(false); + + underTest.validate(almSettingDto); + verify(gitlabHttpClient, times(1)).checkUrl(almSettingDto.getUrl()); + verify(gitlabHttpClient, times(1)).checkToken(almSettingDto.getUrl(), almSettingDto.getDecryptedPersonalAccessToken(encryption)); + verify(gitlabHttpClient, times(1)).checkReadPermission(almSettingDto.getUrl(), almSettingDto.getDecryptedPersonalAccessToken(encryption)); + verify(gitlabHttpClient, times(1)).checkWritePermission(almSettingDto.getUrl(), almSettingDto.getDecryptedPersonalAccessToken(encryption)); + } + + @Test + public void validate_success_with_encrypted_token() { + String encryptedToken = "personal-access-token"; + String decryptedToken = "decrypted-token"; + AlmSettingDto almSettingDto = new AlmSettingDto() + .setUrl("https://gitlab.com/api") + .setPersonalAccessToken(encryptedToken); + when(encryption.isEncrypted(encryptedToken)).thenReturn(true); + when(encryption.decrypt(encryptedToken)).thenReturn(decryptedToken); + + underTest.validate(almSettingDto); + + verify(gitlabHttpClient, times(1)).checkUrl(almSettingDto.getUrl()); + verify(gitlabHttpClient, times(1)).checkToken(almSettingDto.getUrl(), decryptedToken); + verify(gitlabHttpClient, times(1)).checkReadPermission(almSettingDto.getUrl(), decryptedToken); + verify(gitlabHttpClient, times(1)).checkWritePermission(almSettingDto.getUrl(), decryptedToken); + } + + @Test + public void validate_fail_url_not_set() { + AlmSettingDto almSettingDto = new AlmSettingDto() + .setUrl(null) + .setPersonalAccessToken("personal-access-token"); + + assertThatThrownBy(() -> underTest.validate(almSettingDto)) + .isInstanceOf(IllegalArgumentException.class) + .hasMessage("Your Gitlab global configuration is incomplete."); + } + + @Test + public void validate_fail_pat_not_set() { + AlmSettingDto almSettingDto = new AlmSettingDto() + .setUrl("https://gitlab.com/api") + .setPersonalAccessToken(null); + + assertThatThrownBy(() -> underTest.validate(almSettingDto)) + .isInstanceOf(IllegalArgumentException.class) + .hasMessage("Your Gitlab global configuration is incomplete."); + } + +} 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 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 getALMsDTOs(List almSettingDtos, ALM alm) { + return almSettingDtos.stream().filter(dto -> dto.getAlm() == alm).collect(Collectors.toList()); + } + + private void validateGithub(List almSettingDtos) { + try { + for (AlmSettingDto dto : almSettingDtos) { + githubValidator.validate(dto); + } + metrics.setGithubStatusToGreen(); + } catch (RuntimeException e) { + metrics.setGithubStatusToRed(); + } + } + + private void validateBitbucket(List almSettingDtos) { + try { + for (AlmSettingDto dto : almSettingDtos) { + bitbucketServerValidator.validate(dto); + } + metrics.setBitbucketStatusToGreen(); + } catch (Exception e) { + metrics.setBitbucketStatusToRed(); + } + } + + private void validateBitbucketCloud(List almSettingDtos) { + try { + for (AlmSettingDto dto : almSettingDtos) { + bitbucketCloudValidator.validate(dto); + } + metrics.setBitbucketStatusToGreen(); + } catch (Exception e) { + metrics.setBitbucketStatusToRed(); + } + } + + private void validateGitlab(List almSettingDtos) { + try { + for (AlmSettingDto dto : almSettingDtos) { + gitlabValidator.validate(dto); + } + metrics.setGitlabStatusToGreen(); + } catch (Exception e) { + metrics.setGitlabStatusToRed(); + } + } + + private void validateAzure(List 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 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 newDeamonThread = findNewDeamonThread(); + + assertThat(newDeamonThread).isPresent(); + assertThat(newDeamonThread.get().isDaemon()).isTrue(); + } + + @Test + public void createTask_givenOneConfigForEachALM_allValidatorsAreCalled() { + AlmSettingDao dao = mock(AlmSettingDao.class); + List 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 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 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 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 almSettingDtos) { + return almSettingDtos.stream().filter(d -> d.getAlm() == alm).findFirst().get(); + } + + private List createAlmSettingDtos() { + List dtos = new ArrayList<>(); + for(ALM alm : ALM.values()) { + AlmSettingDto almSettingDto = new AlmSettingDto(); + almSettingDto.setAlm(alm); + dtos.add(almSettingDto); + } + return dtos; + } + + private Optional findNewDeamonThread() { + Set 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/validator/BitbucketServerSettingsValidator.java b/server/sonar-webserver-webapi/src/main/java/org/sonar/server/almintegration/validator/BitbucketServerSettingsValidator.java deleted file mode 100644 index b0e6e63c558..00000000000 --- a/server/sonar-webserver-webapi/src/main/java/org/sonar/server/almintegration/validator/BitbucketServerSettingsValidator.java +++ /dev/null @@ -1,49 +0,0 @@ -/* - * 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.almintegration.validator; - -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; -import org.sonar.db.alm.setting.AlmSettingDto; - -@ServerSide -public class BitbucketServerSettingsValidator { - private final BitbucketServerRestClient bitbucketServerRestClient; - private final Encryption encryption; - - public BitbucketServerSettingsValidator(BitbucketServerRestClient bitbucketServerRestClient, Settings settings) { - this.bitbucketServerRestClient = bitbucketServerRestClient; - this.encryption = settings.getEncryption(); - } - - public void validate(AlmSettingDto almSettingDto) { - String bitbucketUrl = almSettingDto.getUrl(); - String bitbucketToken = almSettingDto.getDecryptedPersonalAccessToken(encryption); - if (bitbucketUrl == null || bitbucketToken == null) { - throw new IllegalArgumentException("Your global Bitbucket Server configuration is incomplete."); - } - - bitbucketServerRestClient.validateUrl(bitbucketUrl); - bitbucketServerRestClient.validateToken(bitbucketUrl, bitbucketToken); - bitbucketServerRestClient.validateReadPermission(bitbucketUrl, bitbucketToken); - } -} diff --git a/server/sonar-webserver-webapi/src/main/java/org/sonar/server/almintegration/validator/GithubGlobalSettingsValidator.java b/server/sonar-webserver-webapi/src/main/java/org/sonar/server/almintegration/validator/GithubGlobalSettingsValidator.java deleted file mode 100644 index 56298ced863..00000000000 --- a/server/sonar-webserver-webapi/src/main/java/org/sonar/server/almintegration/validator/GithubGlobalSettingsValidator.java +++ /dev/null @@ -1,65 +0,0 @@ -/* - * 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.almintegration.validator; - -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; -import org.sonar.api.server.ServerSide; -import org.sonar.db.alm.setting.AlmSettingDto; - -import static org.apache.commons.lang.StringUtils.isBlank; - -@ServerSide -public class GithubGlobalSettingsValidator { - - private final Encryption encryption; - private final GithubApplicationClient githubApplicationClient; - - public GithubGlobalSettingsValidator(GithubApplicationClientImpl githubApplicationClient, Settings settings) { - this.encryption = settings.getEncryption(); - this.githubApplicationClient = githubApplicationClient; - } - - public GithubAppConfiguration validate(AlmSettingDto settings) { - long appId; - try { - appId = Long.parseLong(Optional.ofNullable(settings.getAppId()).orElseThrow(() -> new IllegalArgumentException("Missing appId"))); - } catch (NumberFormatException e) { - throw new IllegalArgumentException("Invalid appId; " + e.getMessage()); - } - if (isBlank(settings.getClientId())) { - throw new IllegalArgumentException("Missing Client Id"); - } - if (isBlank(settings.getDecryptedClientSecret(encryption))) { - throw new IllegalArgumentException("Missing Client Secret"); - } - GithubAppConfiguration configuration = new GithubAppConfiguration(appId, settings.getDecryptedPrivateKey(encryption), - settings.getUrl()); - - githubApplicationClient.checkApiEndpoint(configuration); - githubApplicationClient.checkAppPermissions(configuration); - - return configuration; - } -} diff --git a/server/sonar-webserver-webapi/src/main/java/org/sonar/server/almintegration/validator/GitlabGlobalSettingsValidator.java b/server/sonar-webserver-webapi/src/main/java/org/sonar/server/almintegration/validator/GitlabGlobalSettingsValidator.java deleted file mode 100644 index d6758b48201..00000000000 --- a/server/sonar-webserver-webapi/src/main/java/org/sonar/server/almintegration/validator/GitlabGlobalSettingsValidator.java +++ /dev/null @@ -1,53 +0,0 @@ -/* - * 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.almintegration.validator; - -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; -import org.sonar.db.alm.setting.AlmSettingDto; - -@ServerSide -public class GitlabGlobalSettingsValidator { - - private final Encryption encryption; - private final GitlabHttpClient gitlabHttpClient; - - public GitlabGlobalSettingsValidator(GitlabHttpClient gitlabHttpClient, Settings settings) { - this.encryption = settings.getEncryption(); - this.gitlabHttpClient = gitlabHttpClient; - } - - public void validate(AlmSettingDto almSettingDto) { - String gitlabUrl = almSettingDto.getUrl(); - String accessToken = almSettingDto.getDecryptedPersonalAccessToken(encryption); - - if (gitlabUrl == null || accessToken == null) { - throw new IllegalArgumentException("Your Gitlab global configuration is incomplete."); - } - - gitlabHttpClient.checkUrl(gitlabUrl); - gitlabHttpClient.checkToken(gitlabUrl, accessToken); - gitlabHttpClient.checkReadPermission(gitlabUrl, accessToken); - gitlabHttpClient.checkWritePermission(gitlabUrl, accessToken); - } - -} 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/almintegration/validator/BitbucketServerSettingsValidatorTest.java b/server/sonar-webserver-webapi/src/test/java/org/sonar/server/almintegration/validator/BitbucketServerSettingsValidatorTest.java deleted file mode 100644 index e8644877c1c..00000000000 --- a/server/sonar-webserver-webapi/src/test/java/org/sonar/server/almintegration/validator/BitbucketServerSettingsValidatorTest.java +++ /dev/null @@ -1,102 +0,0 @@ -/* - * 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.almintegration.validator; - -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.AlmSettingDto; - -import static org.assertj.core.api.AssertionsForClassTypes.assertThatThrownBy; -import static org.mockito.ArgumentMatchers.any; -import static org.mockito.ArgumentMatchers.anyString; -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; -import static org.sonar.db.almsettings.AlmSettingsTesting.newBitbucketAlmSettingDto; - -public class BitbucketServerSettingsValidatorTest { - private static final Encryption encryption = mock(Encryption.class); - private static final Settings settings = mock(Settings.class); - - private final BitbucketServerRestClient bitbucketServerRestClient = mock(BitbucketServerRestClient.class); - private final BitbucketServerSettingsValidator underTest = new BitbucketServerSettingsValidator(bitbucketServerRestClient, settings); - - @BeforeClass - public static void setUp() { - when(settings.getEncryption()).thenReturn(encryption); - } - - @Test - public void validate_success() { - AlmSettingDto almSettingDto = newBitbucketAlmSettingDto() - .setUrl("http://abc.com") - .setPersonalAccessToken("abc"); - when(encryption.isEncrypted(any())).thenReturn(false); - - underTest.validate(almSettingDto); - - verify(bitbucketServerRestClient, times(1)).validateUrl("http://abc.com"); - verify(bitbucketServerRestClient, times(1)).validateToken("http://abc.com", "abc"); - verify(bitbucketServerRestClient, times(1)).validateReadPermission("http://abc.com", "abc"); - } - - @Test - public void validate_success_with_encrypted_token() { - String encryptedToken = "abc"; - String decryptedToken = "decrypted-token"; - AlmSettingDto almSettingDto = newBitbucketAlmSettingDto() - .setUrl("http://abc.com") - .setPersonalAccessToken(encryptedToken); - when(encryption.isEncrypted(encryptedToken)).thenReturn(true); - when(encryption.decrypt(encryptedToken)).thenReturn(decryptedToken); - - underTest.validate(almSettingDto); - - verify(bitbucketServerRestClient, times(1)).validateUrl("http://abc.com"); - verify(bitbucketServerRestClient, times(1)).validateToken("http://abc.com", decryptedToken); - verify(bitbucketServerRestClient, times(1)).validateReadPermission("http://abc.com", decryptedToken); - } - - @Test - public void validate_failure_on_incomplete_configuration() { - AlmSettingDto almSettingDto = newBitbucketAlmSettingDto() - .setUrl(null) - .setPersonalAccessToken("abc"); - - assertThatThrownBy(() -> underTest.validate(almSettingDto)) - .isInstanceOf(IllegalArgumentException.class); - } - - @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"); - - assertThatThrownBy(() -> underTest.validate(almSettingDto)) - .isInstanceOf(IllegalArgumentException.class); - } -} diff --git a/server/sonar-webserver-webapi/src/test/java/org/sonar/server/almintegration/validator/GithubGlobalSettingsValidatorTest.java b/server/sonar-webserver-webapi/src/test/java/org/sonar/server/almintegration/validator/GithubGlobalSettingsValidatorTest.java deleted file mode 100644 index 5c3d274a65b..00000000000 --- a/server/sonar-webserver-webapi/src/test/java/org/sonar/server/almintegration/validator/GithubGlobalSettingsValidatorTest.java +++ /dev/null @@ -1,128 +0,0 @@ -/* - * 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.almintegration.validator; - -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.AlmSettingDto; - -import static org.assertj.core.api.Assertions.assertThat; -import static org.assertj.core.api.Assertions.assertThatThrownBy; -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 final GithubApplicationClientImpl appClient = mock(GithubApplicationClientImpl.class); - private final GithubGlobalSettingsValidator underTest = new GithubGlobalSettingsValidator(appClient, settings); - - @BeforeClass - public static void setUp() { - when(settings.getEncryption()).thenReturn(encryption); - } - - @Test - public void github_global_settings_validation() { - AlmSettingDto almSettingDto = newGithubAlmSettingDto() - .setClientId("clientId") - .setClientSecret("clientSecret"); - when(encryption.isEncrypted(any())).thenReturn(false); - - GithubAppConfiguration configuration = underTest.validate(almSettingDto); - - ArgumentCaptor configurationArgumentCaptor = ArgumentCaptor.forClass(GithubAppConfiguration.class); - verify(appClient).checkApiEndpoint(configurationArgumentCaptor.capture()); - verify(appClient).checkAppPermissions(configurationArgumentCaptor.capture()); - assertThat(configuration.getId()).isEqualTo(configurationArgumentCaptor.getAllValues().get(0).getId()); - assertThat(configuration.getId()).isEqualTo(configurationArgumentCaptor.getAllValues().get(1).getId()); - } - - @Test - 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"); - when(encryption.isEncrypted(encryptedKey)).thenReturn(true); - when(encryption.decrypt(encryptedKey)).thenReturn(decryptedKey); - - GithubAppConfiguration configuration = underTest.validate(almSettingDto); - - ArgumentCaptor configurationArgumentCaptor = ArgumentCaptor.forClass(GithubAppConfiguration.class); - verify(appClient).checkApiEndpoint(configurationArgumentCaptor.capture()); - verify(appClient).checkAppPermissions(configurationArgumentCaptor.capture()); - assertThat(configuration.getId()).isEqualTo(configurationArgumentCaptor.getAllValues().get(0).getId()); - assertThat(decryptedKey).isEqualTo(configurationArgumentCaptor.getAllValues().get(0).getPrivateKey()); - assertThat(configuration.getId()).isEqualTo(configurationArgumentCaptor.getAllValues().get(1).getId()); - assertThat(decryptedKey).isEqualTo(configurationArgumentCaptor.getAllValues().get(1).getPrivateKey()); - } - - @Test - public void github_validation_checks_invalid_appId() { - AlmSettingDto almSettingDto = newGithubAlmSettingDto() - .setAppId("abc") - .setClientId("clientId") - .setClientSecret("clientSecret"); - - assertThatThrownBy(() -> underTest.validate(almSettingDto)) - .isInstanceOf(IllegalArgumentException.class) - .hasMessage("Invalid appId; For input string: \"abc\""); - } - - @Test - public void github_validation_checks_missing_appId() { - AlmSettingDto almSettingDto = newGithubAlmSettingDto().setAppId(null); - - assertThatThrownBy(() -> underTest.validate(almSettingDto)) - .isInstanceOf(IllegalArgumentException.class) - .hasMessage("Missing appId"); - } - - @Test - public void github_validation_checks_missing_clientId() { - AlmSettingDto almSettingDto = newGithubAlmSettingDto().setClientId(null); - - assertThatThrownBy(() -> underTest.validate(almSettingDto)) - .isInstanceOf(IllegalArgumentException.class) - .hasMessage("Missing Client Id"); - } - - @Test - public void github_validation_checks_missing_clientSecret() { - AlmSettingDto almSettingDto = newGithubAlmSettingDto().setClientSecret(null); - - assertThatThrownBy(() -> underTest.validate(almSettingDto)) - .isInstanceOf(IllegalArgumentException.class) - .hasMessage("Missing Client Secret"); - - } -} diff --git a/server/sonar-webserver-webapi/src/test/java/org/sonar/server/almintegration/validator/GitlabGlobalSettingsValidatorTest.java b/server/sonar-webserver-webapi/src/test/java/org/sonar/server/almintegration/validator/GitlabGlobalSettingsValidatorTest.java deleted file mode 100644 index 186b671ed2f..00000000000 --- a/server/sonar-webserver-webapi/src/test/java/org/sonar/server/almintegration/validator/GitlabGlobalSettingsValidatorTest.java +++ /dev/null @@ -1,103 +0,0 @@ -/* - * 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.almintegration.validator; - -import org.junit.BeforeClass; -import org.junit.Test; -import org.sonar.alm.client.gitlab.GitlabHttpClient; -import org.sonar.api.config.internal.Encryption; -import org.sonar.api.config.internal.Settings; -import org.sonar.db.alm.setting.AlmSettingDto; - -import static org.assertj.core.api.Assertions.assertThatThrownBy; -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 GitlabGlobalSettingsValidatorTest { - private static final Encryption encryption = mock(Encryption.class); - private static final Settings settings = mock(Settings.class); - - private final GitlabHttpClient gitlabHttpClient = mock(GitlabHttpClient.class); - - private final GitlabGlobalSettingsValidator underTest = new GitlabGlobalSettingsValidator(gitlabHttpClient, settings); - - @BeforeClass - public static void setUp() { - when(settings.getEncryption()).thenReturn(encryption); - } - - @Test - public void validate_success() { - String token = "personal-access-token"; - AlmSettingDto almSettingDto = new AlmSettingDto() - .setUrl("https://gitlab.com/api") - .setPersonalAccessToken("personal-access-token"); - when(encryption.isEncrypted(token)).thenReturn(false); - - underTest.validate(almSettingDto); - verify(gitlabHttpClient, times(1)).checkUrl(almSettingDto.getUrl()); - verify(gitlabHttpClient, times(1)).checkToken(almSettingDto.getUrl(), almSettingDto.getDecryptedPersonalAccessToken(encryption)); - verify(gitlabHttpClient, times(1)).checkReadPermission(almSettingDto.getUrl(), almSettingDto.getDecryptedPersonalAccessToken(encryption)); - verify(gitlabHttpClient, times(1)).checkWritePermission(almSettingDto.getUrl(), almSettingDto.getDecryptedPersonalAccessToken(encryption)); - } - - @Test - public void validate_success_with_encrypted_token() { - String encryptedToken = "personal-access-token"; - String decryptedToken = "decrypted-token"; - AlmSettingDto almSettingDto = new AlmSettingDto() - .setUrl("https://gitlab.com/api") - .setPersonalAccessToken(encryptedToken); - when(encryption.isEncrypted(encryptedToken)).thenReturn(true); - when(encryption.decrypt(encryptedToken)).thenReturn(decryptedToken); - - underTest.validate(almSettingDto); - - verify(gitlabHttpClient, times(1)).checkUrl(almSettingDto.getUrl()); - verify(gitlabHttpClient, times(1)).checkToken(almSettingDto.getUrl(), decryptedToken); - verify(gitlabHttpClient, times(1)).checkReadPermission(almSettingDto.getUrl(), decryptedToken); - verify(gitlabHttpClient, times(1)).checkWritePermission(almSettingDto.getUrl(), decryptedToken); - } - - @Test - public void validate_fail_url_not_set() { - AlmSettingDto almSettingDto = new AlmSettingDto() - .setUrl(null) - .setPersonalAccessToken("personal-access-token"); - - assertThatThrownBy(() -> underTest.validate(almSettingDto)) - .isInstanceOf(IllegalArgumentException.class) - .hasMessage("Your Gitlab global configuration is incomplete."); - } - - @Test - public void validate_fail_pat_not_set() { - AlmSettingDto almSettingDto = new AlmSettingDto() - .setUrl("https://gitlab.com/api") - .setPersonalAccessToken(null); - - assertThatThrownBy(() -> underTest.validate(almSettingDto)) - .isInstanceOf(IllegalArgumentException.class) - .hasMessage("Your Gitlab global configuration is incomplete."); - } - -} 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'