diff options
author | Lukasz Jarocki <lukasz.jarocki@sonarsource.com> | 2021-12-10 10:26:07 +0100 |
---|---|---|
committer | Lukasz Jarocki <lukasz.jarocki@sonarsource.com> | 2021-12-13 15:22:58 +0100 |
commit | 0d0b7e4ff71957681e868404f87a526314f89181 (patch) | |
tree | 7a404c2e87183a90b3ee716c4874a21d798c6879 | |
parent | 38d139582c17354e22e54ea03a73213cd87ca87d (diff) | |
download | sonarqube-0d0b7e4ff71957681e868404f87a526314f89181.tar.gz sonarqube-0d0b7e4ff71957681e868404f87a526314f89181.zip |
SONAR-15770 added compute engine metrics
29 files changed, 1571 insertions, 353 deletions
diff --git a/server/sonar-db-dao/src/main/java/org/sonar/db/ce/CeActivityDao.java b/server/sonar-db-dao/src/main/java/org/sonar/db/ce/CeActivityDao.java index 613f000eb6d..0ebd518ff56 100644 --- a/server/sonar-db-dao/src/main/java/org/sonar/db/ce/CeActivityDao.java +++ b/server/sonar-db-dao/src/main/java/org/sonar/db/ce/CeActivityDao.java @@ -61,6 +61,10 @@ public class CeActivityDao implements Dao { return mapper(dbSession).selectOlderThan(beforeDate); } + public List<CeActivityDto> selectNewerThan(DbSession dbSession, long beforeDate) { + return mapper(dbSession).selectNewerThan(beforeDate); + } + public List<CeActivityDto> selectByTaskType(DbSession dbSession, String taskType) { return mapper(dbSession).selectByTaskType(taskType); } diff --git a/server/sonar-db-dao/src/main/java/org/sonar/db/ce/CeActivityMapper.java b/server/sonar-db-dao/src/main/java/org/sonar/db/ce/CeActivityMapper.java index b6a2a00c1cb..f4e95203874 100644 --- a/server/sonar-db-dao/src/main/java/org/sonar/db/ce/CeActivityMapper.java +++ b/server/sonar-db-dao/src/main/java/org/sonar/db/ce/CeActivityMapper.java @@ -34,6 +34,8 @@ public interface CeActivityMapper { List<CeActivityDto> selectOlderThan(@Param("beforeDate") long beforeDate); + List<CeActivityDto> selectNewerThan(@Param("afterDate") long afterDate); + int countLastByStatusAndMainComponentUuid(@Param("status") CeActivityDto.Status status, @Nullable @Param("mainComponentUuid") String mainComponentUuid); void insert(CeActivityDto dto); diff --git a/server/sonar-db-dao/src/main/resources/org/sonar/db/ce/CeActivityMapper.xml b/server/sonar-db-dao/src/main/resources/org/sonar/db/ce/CeActivityMapper.xml index 17c222152dd..f94c83b5ffc 100644 --- a/server/sonar-db-dao/src/main/resources/org/sonar/db/ce/CeActivityMapper.xml +++ b/server/sonar-db-dao/src/main/resources/org/sonar/db/ce/CeActivityMapper.xml @@ -143,6 +143,15 @@ where ca.created_at < #{beforeDate,jdbcType=BIGINT} </select> + + <select id="selectNewerThan" parameterType="long" resultType="org.sonar.db.ce.CeActivityDto"> + select + <include refid="columns"/> + from ce_activity ca + left outer join ce_scanner_context csc on csc.task_uuid = ca.uuid + where + ca.created_at > #{afterDate,jdbcType=BIGINT} + </select> <select id="countLastByStatusAndMainComponentUuid" resultType="int"> select diff --git a/server/sonar-db-dao/src/test/java/org/sonar/db/ce/CeActivityDaoTest.java b/server/sonar-db-dao/src/test/java/org/sonar/db/ce/CeActivityDaoTest.java index cca5ec95293..6916d6ba297 100644 --- a/server/sonar-db-dao/src/test/java/org/sonar/db/ce/CeActivityDaoTest.java +++ b/server/sonar-db-dao/src/test/java/org/sonar/db/ce/CeActivityDaoTest.java @@ -647,6 +647,16 @@ public class CeActivityDaoTest { } @Test + public void selectNewerThan() { + insertWithCreationDate("TASK_1", 1_450_000_000_000L); + insertWithCreationDate("TASK_2", 1_460_000_000_000L); + insertWithCreationDate("TASK_3", 1_470_000_000_000L); + + List<CeActivityDto> dtos = underTest.selectNewerThan(db.getSession(), 1_455_000_000_000L); + assertThat(dtos).extracting("uuid").containsOnly("TASK_2", "TASK_3"); + } + + @Test public void selectOlder_populates_hasScannerContext_flag() { insertWithCreationDate("TASK_1", 1_450_000_000_000L); CeActivityDto dto2 = insertWithCreationDate("TASK_2", 1_450_000_000_000L); diff --git a/server/sonar-webserver-monitoring/src/main/java/org/sonar/server/monitoring/MainCollector.java b/server/sonar-webserver-monitoring/src/main/java/org/sonar/server/monitoring/MainCollector.java new file mode 100644 index 00000000000..389a1d7bc72 --- /dev/null +++ b/server/sonar-webserver-monitoring/src/main/java/org/sonar/server/monitoring/MainCollector.java @@ -0,0 +1,60 @@ +/* + * 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 com.google.common.annotations.VisibleForTesting; +import com.google.common.util.concurrent.ThreadFactoryBuilder; +import java.util.concurrent.Executors; +import java.util.concurrent.ScheduledExecutorService; +import org.picocontainer.Startable; + +import static java.util.concurrent.TimeUnit.MILLISECONDS; + +public class MainCollector implements Startable { + + private final MonitoringTask[] monitoringTasks; + private ScheduledExecutorService scheduledExecutorService; + + public MainCollector(MonitoringTask[] monitoringTasks) { + this.monitoringTasks = monitoringTasks; + } + + @Override + public void start() { + this.scheduledExecutorService = Executors.newSingleThreadScheduledExecutor( + new ThreadFactoryBuilder() + .setDaemon(true) + .setNameFormat(getClass().getCanonicalName() + "-thread-%d") + .build()); + for (MonitoringTask task : monitoringTasks) { + scheduledExecutorService.scheduleWithFixedDelay(task, task.getDelay(), task.getPeriod(), MILLISECONDS); + } + } + + @Override + public void stop() { + scheduledExecutorService.shutdown(); + } + + @VisibleForTesting + ScheduledExecutorService getScheduledExecutorService() { + return scheduledExecutorService; + } +} diff --git a/server/sonar-webserver-monitoring/src/main/java/org/sonar/server/monitoring/MonitoringTask.java b/server/sonar-webserver-monitoring/src/main/java/org/sonar/server/monitoring/MonitoringTask.java new file mode 100644 index 00000000000..c445d8269a1 --- /dev/null +++ b/server/sonar-webserver-monitoring/src/main/java/org/sonar/server/monitoring/MonitoringTask.java @@ -0,0 +1,27 @@ +/* + * 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; + +public interface MonitoringTask extends Runnable { + + long getDelay(); + + long getPeriod(); +} 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 index 1fb4c46adaa..a58870a7905 100644 --- 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 @@ -20,6 +20,7 @@ package org.sonar.server.monitoring; import io.prometheus.client.Gauge; +import io.prometheus.client.Summary; import org.sonar.api.server.ServerSide; @ServerSide @@ -30,6 +31,9 @@ public class ServerMonitoringMetrics { private final Gauge bitbucketConfigOk; private final Gauge azureConfigOk; + private final Gauge cePendingTasksTotal; + private final Summary ceTasksRunningDuration; + public ServerMonitoringMetrics() { githubConfigOk = Gauge.build() .name("github_config_ok") @@ -50,6 +54,17 @@ public class ServerMonitoringMetrics { .name("azure_config_ok") .help("Tells whether SonarQube instance has configured Azure integration and its status is green. 0 for green, 1 otherwise .") .register(); + + cePendingTasksTotal = Gauge.build() + .name("sonarqube_compute_engine_pending_tasks_total") + .help("Number of tasks at given point of time that were pending in the Compute Engine queue [SHARED, same value for every SonarQube instance]") + .register(); + + ceTasksRunningDuration = Summary.build() + .name("sonarqube_compute_engine_tasks_running_duration_seconds") + .help("Compute engine task running time in seconds") + .labelNames("task_type", "project_key") + .register(); } public void setGithubStatusToGreen() { @@ -83,4 +98,13 @@ public class ServerMonitoringMetrics { public void setBitbucketStatusToRed() { bitbucketConfigOk.set(1); } + + public void setNumberOfPendingTasks(int numberOfPendingTasks) { + cePendingTasksTotal.set(numberOfPendingTasks); + } + + public void observeComputeEngineTaskDuration(long durationInSeconds, String taskType, String projectKey) { + ceTasksRunningDuration.labels(taskType, projectKey).observe(durationInSeconds); + } + } diff --git a/server/sonar-webserver-monitoring/src/main/java/org/sonar/server/monitoring/ce/ComputeEngineMetricsTask.java b/server/sonar-webserver-monitoring/src/main/java/org/sonar/server/monitoring/ce/ComputeEngineMetricsTask.java new file mode 100644 index 00000000000..f0394be4bfd --- /dev/null +++ b/server/sonar-webserver-monitoring/src/main/java/org/sonar/server/monitoring/ce/ComputeEngineMetricsTask.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.server.monitoring.ce; + +import org.sonar.api.config.Configuration; +import org.sonar.db.DbClient; +import org.sonar.server.monitoring.MonitoringTask; +import org.sonar.server.monitoring.ServerMonitoringMetrics; + +public abstract class ComputeEngineMetricsTask implements MonitoringTask { + + private static final String DELAY_IN_MILISECONDS_PROPERTY = "sonar.server.monitoring.ce.initial.delay"; + private static final String PERIOD_IN_MILISECONDS_PROPERTY = "sonar.server.monitoring.ce.period"; + + protected final DbClient dbClient; + protected final ServerMonitoringMetrics metrics; + + private final Configuration config; + + protected ComputeEngineMetricsTask(DbClient dbClient, ServerMonitoringMetrics metrics, Configuration config) { + this.dbClient = dbClient; + this.metrics = metrics; + this.config = config; + } + + @Override + public long getDelay() { + return config.getLong(DELAY_IN_MILISECONDS_PROPERTY).orElse(10_000L); + } + + @Override + public long getPeriod() { + return config.getLong(PERIOD_IN_MILISECONDS_PROPERTY).orElse(30_000L); + } +} diff --git a/server/sonar-webserver-monitoring/src/main/java/org/sonar/server/monitoring/ce/NumberOfTasksInQueueTask.java b/server/sonar-webserver-monitoring/src/main/java/org/sonar/server/monitoring/ce/NumberOfTasksInQueueTask.java new file mode 100644 index 00000000000..0a48db31706 --- /dev/null +++ b/server/sonar-webserver-monitoring/src/main/java/org/sonar/server/monitoring/ce/NumberOfTasksInQueueTask.java @@ -0,0 +1,42 @@ +/* + * 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.ce; + +import org.sonar.api.config.Configuration; +import org.sonar.db.DbClient; +import org.sonar.db.DbSession; +import org.sonar.db.ce.CeQueueDto; +import org.sonar.server.monitoring.ServerMonitoringMetrics; + +public class NumberOfTasksInQueueTask extends ComputeEngineMetricsTask { + + public NumberOfTasksInQueueTask(DbClient dbClient, ServerMonitoringMetrics metrics, Configuration config) { + super(dbClient, metrics, config); + } + + @Override + public void run() { + try (DbSession dbSession = dbClient.openSession(false)) { + int size = dbClient.ceQueueDao().countByStatus(dbSession, CeQueueDto.Status.PENDING); + metrics.setNumberOfPendingTasks(size); + } + } + +} diff --git a/server/sonar-webserver-monitoring/src/main/java/org/sonar/server/monitoring/ce/RecentTasksDurationTask.java b/server/sonar-webserver-monitoring/src/main/java/org/sonar/server/monitoring/ce/RecentTasksDurationTask.java new file mode 100644 index 00000000000..1aae8cc0fea --- /dev/null +++ b/server/sonar-webserver-monitoring/src/main/java/org/sonar/server/monitoring/ce/RecentTasksDurationTask.java @@ -0,0 +1,94 @@ +/* + * 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.ce; + +import java.util.Collection; +import java.util.List; +import java.util.Map; +import java.util.stream.Collectors; +import org.sonar.api.config.Configuration; +import org.sonar.api.utils.System2; +import org.sonar.api.utils.log.Logger; +import org.sonar.api.utils.log.Loggers; +import org.sonar.db.DbClient; +import org.sonar.db.DbSession; +import org.sonar.db.ce.CeActivityDto; +import org.sonar.db.component.ComponentDto; +import org.sonar.server.monitoring.ServerMonitoringMetrics; + +import static java.util.Objects.requireNonNull; + +public class RecentTasksDurationTask extends ComputeEngineMetricsTask { + + private static final Logger LOGGER = Loggers.get(RecentTasksDurationTask.class); + private final System2 system; + + private long lastUpdatedTimestamp; + + public RecentTasksDurationTask(DbClient dbClient, ServerMonitoringMetrics metrics, Configuration config, + System2 system) { + super(dbClient, metrics, config); + this.system = system; + this.lastUpdatedTimestamp = system.now(); + } + + @Override + public void run() { + try (DbSession dbSession = dbClient.openSession(false)) { + List<CeActivityDto> recentSuccessfulTasks = getRecentSuccessfulTasks(dbSession); + + Collection<String> componentUuids = recentSuccessfulTasks.stream() + .map(CeActivityDto::getMainComponentUuid) + .collect(Collectors.toList()); + List<ComponentDto> componentDtos = dbClient.componentDao().selectByUuids(dbSession, componentUuids); + Map<String, String> componentUuidAndKeys = componentDtos.stream() + .collect(Collectors.toMap(ComponentDto::uuid, ComponentDto::getKey)); + + reportObservedDurationForTasks(recentSuccessfulTasks, componentUuidAndKeys); + } + lastUpdatedTimestamp = system.now(); + } + + private List<CeActivityDto> getRecentSuccessfulTasks(DbSession dbSession) { + List<CeActivityDto> recentTasks = dbClient.ceActivityDao().selectNewerThan(dbSession, lastUpdatedTimestamp); + return recentTasks.stream() + .filter(c -> c.getStatus() == CeActivityDto.Status.SUCCESS) + .collect(Collectors.toList()); + } + + private void reportObservedDurationForTasks(List<CeActivityDto> tasks, Map<String, String> componentUuidAndKeys) { + for (CeActivityDto task : tasks) { + String mainComponentUuid = task.getMainComponentUuid(); + Long executionTimeMs = task.getExecutionTimeMs(); + try { + requireNonNull(mainComponentUuid); + requireNonNull(executionTimeMs); + + String mainComponentKey = componentUuidAndKeys.get(mainComponentUuid); + requireNonNull(mainComponentKey); + + metrics.observeComputeEngineTaskDuration(executionTimeMs, task.getTaskType(), mainComponentUuid); + } catch (RuntimeException e) { + LOGGER.warn("Can't report metric data for a CE task with component uuid " + mainComponentUuid, e); + } + } + + } +} diff --git a/server/sonar-webserver-monitoring/src/main/java/org/sonar/server/monitoring/devops/AzureMetricsTask.java b/server/sonar-webserver-monitoring/src/main/java/org/sonar/server/monitoring/devops/AzureMetricsTask.java new file mode 100644 index 00000000000..34d6a3e9ebf --- /dev/null +++ b/server/sonar-webserver-monitoring/src/main/java/org/sonar/server/monitoring/devops/AzureMetricsTask.java @@ -0,0 +1,65 @@ +/* + * 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 java.util.List; +import org.sonar.alm.client.azure.AzureDevOpsValidator; +import org.sonar.api.config.Configuration; +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; + +public class AzureMetricsTask extends DevOpsMetricsTask { + + private final AzureDevOpsValidator azureDevOpsValidator; + + public AzureMetricsTask(ServerMonitoringMetrics metrics, AzureDevOpsValidator azureDevOpsValidator, + DbClient dbClient, Configuration config) { + super(dbClient, metrics, config); + this.azureDevOpsValidator = azureDevOpsValidator; + } + + @Override + public void run() { + try (DbSession dbSession = dbClient.openSession(false)) { + List<AlmSettingDto> azureSettingsDtos = dbClient.almSettingDao().selectByAlm(dbSession, ALM.AZURE_DEVOPS); + + if (azureSettingsDtos.isEmpty()) { + metrics.setAzureStatusToRed(); + return; + } + + validate(azureSettingsDtos); + } + } + + private void validate(List<AlmSettingDto> almSettingDtos) { + try { + for (AlmSettingDto dto : almSettingDtos) { + azureDevOpsValidator.validate(dto); + } + metrics.setAzureStatusToGreen(); + } catch (Exception e) { + metrics.setAzureStatusToRed(); + } + } +} diff --git a/server/sonar-webserver-monitoring/src/main/java/org/sonar/server/monitoring/devops/BitbucketMetricsTask.java b/server/sonar-webserver-monitoring/src/main/java/org/sonar/server/monitoring/devops/BitbucketMetricsTask.java new file mode 100644 index 00000000000..2a9c7b11d4b --- /dev/null +++ b/server/sonar-webserver-monitoring/src/main/java/org/sonar/server/monitoring/devops/BitbucketMetricsTask.java @@ -0,0 +1,73 @@ +/* + * 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 java.util.List; +import org.sonar.alm.client.bitbucket.bitbucketcloud.BitbucketCloudValidator; +import org.sonar.alm.client.bitbucketserver.BitbucketServerSettingsValidator; +import org.sonar.api.config.Configuration; +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; + +public class BitbucketMetricsTask extends DevOpsMetricsTask { + + private final BitbucketCloudValidator bitbucketCloudValidator; + private final BitbucketServerSettingsValidator bitbucketServerValidator; + + public BitbucketMetricsTask(ServerMonitoringMetrics metrics, BitbucketCloudValidator bitbucketCloudValidator, + BitbucketServerSettingsValidator bitbucketServerSettingsValidator, DbClient dbClient, Configuration config) { + super(dbClient, metrics, config); + this.bitbucketCloudValidator = bitbucketCloudValidator; + this.bitbucketServerValidator = bitbucketServerSettingsValidator; + } + + @Override + public void run() { + try (DbSession dbSession = dbClient.openSession(false)) { + List<AlmSettingDto> bitbucketServerDtos = dbClient.almSettingDao().selectByAlm(dbSession, ALM.BITBUCKET); + List<AlmSettingDto> bitbucketCloudDtos = dbClient.almSettingDao().selectByAlm(dbSession, ALM.BITBUCKET_CLOUD); + + if (bitbucketServerDtos.isEmpty() && bitbucketCloudDtos.isEmpty()) { + metrics.setBitbucketStatusToRed(); + return; + } + + try { + validate(bitbucketServerDtos, bitbucketCloudDtos); + metrics.setBitbucketStatusToGreen(); + } catch (RuntimeException e) { + metrics.setBitbucketStatusToRed(); + } + + } + } + + private void validate(List<AlmSettingDto> bitbucketServerDtos, List<AlmSettingDto> bitbucketCloudDtos) { + for (AlmSettingDto dto : bitbucketServerDtos) { + bitbucketServerValidator.validate(dto); + } + for (AlmSettingDto dto : bitbucketCloudDtos) { + bitbucketCloudValidator.validate(dto); + } + } +} diff --git a/server/sonar-webserver-monitoring/src/main/java/org/sonar/server/monitoring/devops/DevOpsMetricsTask.java b/server/sonar-webserver-monitoring/src/main/java/org/sonar/server/monitoring/devops/DevOpsMetricsTask.java new file mode 100644 index 00000000000..666d86d00ac --- /dev/null +++ b/server/sonar-webserver-monitoring/src/main/java/org/sonar/server/monitoring/devops/DevOpsMetricsTask.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.server.monitoring.devops; + +import org.sonar.api.config.Configuration; +import org.sonar.db.DbClient; +import org.sonar.server.monitoring.MonitoringTask; +import org.sonar.server.monitoring.ServerMonitoringMetrics; + +public abstract class DevOpsMetricsTask implements MonitoringTask { + + 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"; + + protected final DbClient dbClient; + protected final ServerMonitoringMetrics metrics; + + private final Configuration config; + + protected DevOpsMetricsTask(DbClient dbClient, ServerMonitoringMetrics metrics, Configuration config) { + this.dbClient = dbClient; + this.metrics = metrics; + this.config = config; + } + + @Override + public long getDelay() { + return config.getLong(DELAY_IN_MILISECONDS_PROPERTY).orElse(10_000L); + } + + @Override + public long getPeriod() { + return config.getLong(PERIOD_IN_MILISECONDS_PROPERTY).orElse(300_000L); + } +} 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 deleted file mode 100644 index fcee736a451..00000000000 --- a/server/sonar-webserver-monitoring/src/main/java/org/sonar/server/monitoring/devops/DevOpsPlatformsMetricsCollector.java +++ /dev/null @@ -1,166 +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.monitoring.devops; - -import com.google.common.annotations.VisibleForTesting; -import com.google.common.util.concurrent.ThreadFactoryBuilder; -import java.util.List; -import java.util.concurrent.Executors; -import java.util.concurrent.ScheduledExecutorService; -import java.util.stream.Collectors; -import org.picocontainer.Startable; -import org.sonar.alm.client.azure.AzureDevOpsValidator; -import org.sonar.alm.client.bitbucket.bitbucketcloud.BitbucketCloudValidator; -import org.sonar.alm.client.bitbucketserver.BitbucketServerSettingsValidator; -import org.sonar.alm.client.github.GithubGlobalSettingsValidator; -import org.sonar.alm.client.gitlab.GitlabGlobalSettingsValidator; -import org.sonar.api.config.Configuration; -import org.sonar.api.server.ServerSide; -import org.sonar.db.DbClient; -import org.sonar.db.DbSession; -import org.sonar.db.alm.setting.ALM; -import org.sonar.db.alm.setting.AlmSettingDto; -import org.sonar.server.monitoring.ServerMonitoringMetrics; - -import static java.util.concurrent.TimeUnit.MILLISECONDS; - -@ServerSide -public class DevOpsPlatformsMetricsCollector implements Startable { - - private static final String DELAY_IN_MILISECONDS_PROPERTY = "sonar.server.monitoring.devops.initial.delay"; - private static final String PERIOD_IN_MILISECONDS_PROPERTY = "sonar.server.monitoring.devops.period"; - - private final Configuration config; - - private final BitbucketServerSettingsValidator bitbucketServerValidator; - private final GithubGlobalSettingsValidator githubValidator; - private final GitlabGlobalSettingsValidator gitlabValidator; - private final BitbucketCloudValidator bitbucketCloudValidator; - private final AzureDevOpsValidator azureDevOpsValidator; - - private final DbClient dbClient; - private final ServerMonitoringMetrics metrics; - - private ScheduledExecutorService scheduledExecutorService; - - public DevOpsPlatformsMetricsCollector(ServerMonitoringMetrics metrics, DbClient dbClient, - BitbucketServerSettingsValidator bitbucketServerValidator, GithubGlobalSettingsValidator githubValidator, - GitlabGlobalSettingsValidator gitlabValidator, BitbucketCloudValidator bitbucketCloudValidator, - AzureDevOpsValidator azureDevOpsValidator, Configuration config) { - this.bitbucketCloudValidator = bitbucketCloudValidator; - this.bitbucketServerValidator = bitbucketServerValidator; - this.githubValidator = githubValidator; - this.azureDevOpsValidator = azureDevOpsValidator; - this.gitlabValidator = gitlabValidator; - this.metrics = metrics; - this.dbClient = dbClient; - this.config = config; - } - - @Override - public void start() { - this.scheduledExecutorService = Executors.newSingleThreadScheduledExecutor( - new ThreadFactoryBuilder() - .setDaemon(true) - .setNameFormat(getClass().getCanonicalName() + "-thread-%d") - .build()); - long delayInMilliseconds = config.getLong(DELAY_IN_MILISECONDS_PROPERTY).orElse(10_000L); - long periodInMilliseconds = config.getLong(PERIOD_IN_MILISECONDS_PROPERTY).orElse(300_000L); - scheduledExecutorService.scheduleWithFixedDelay(createTask(), delayInMilliseconds, periodInMilliseconds, MILLISECONDS); - } - - @Override - public void stop() { - scheduledExecutorService.shutdown(); - } - - @VisibleForTesting - Runnable createTask() { - return () -> { - try (DbSession dbSession = dbClient.openSession(false)) { - List<AlmSettingDto> almSettingDtos = dbClient.almSettingDao().selectAll(dbSession); - validateBitbucket(getALMsDTOs(almSettingDtos, ALM.BITBUCKET)); - validateBitbucketCloud(getALMsDTOs(almSettingDtos, ALM.BITBUCKET_CLOUD)); - validateGithub(getALMsDTOs(almSettingDtos, ALM.GITHUB)); - validateGitlab(getALMsDTOs(almSettingDtos, ALM.GITLAB)); - validateAzure(getALMsDTOs(almSettingDtos, ALM.AZURE_DEVOPS)); - } - }; - } - - private static List<AlmSettingDto> getALMsDTOs(List<AlmSettingDto> almSettingDtos, ALM alm) { - return almSettingDtos.stream().filter(dto -> dto.getAlm() == alm).collect(Collectors.toList()); - } - - private void validateGithub(List<AlmSettingDto> almSettingDtos) { - try { - for (AlmSettingDto dto : almSettingDtos) { - githubValidator.validate(dto); - } - metrics.setGithubStatusToGreen(); - } catch (RuntimeException e) { - metrics.setGithubStatusToRed(); - } - } - - private void validateBitbucket(List<AlmSettingDto> almSettingDtos) { - try { - for (AlmSettingDto dto : almSettingDtos) { - bitbucketServerValidator.validate(dto); - } - metrics.setBitbucketStatusToGreen(); - } catch (Exception e) { - metrics.setBitbucketStatusToRed(); - } - } - - private void validateBitbucketCloud(List<AlmSettingDto> almSettingDtos) { - try { - for (AlmSettingDto dto : almSettingDtos) { - bitbucketCloudValidator.validate(dto); - } - metrics.setBitbucketStatusToGreen(); - } catch (Exception e) { - metrics.setBitbucketStatusToRed(); - } - } - - private void validateGitlab(List<AlmSettingDto> almSettingDtos) { - try { - for (AlmSettingDto dto : almSettingDtos) { - gitlabValidator.validate(dto); - } - metrics.setGitlabStatusToGreen(); - } catch (Exception e) { - metrics.setGitlabStatusToRed(); - } - } - - private void validateAzure(List<AlmSettingDto> almSettingDtos) { - try { - for (AlmSettingDto dto : almSettingDtos) { - azureDevOpsValidator.validate(dto); - } - metrics.setAzureStatusToGreen(); - } catch (Exception e) { - metrics.setAzureStatusToRed(); - } - } -} diff --git a/server/sonar-webserver-monitoring/src/main/java/org/sonar/server/monitoring/devops/GithubMetricsTask.java b/server/sonar-webserver-monitoring/src/main/java/org/sonar/server/monitoring/devops/GithubMetricsTask.java new file mode 100644 index 00000000000..7e0d76ffcc6 --- /dev/null +++ b/server/sonar-webserver-monitoring/src/main/java/org/sonar/server/monitoring/devops/GithubMetricsTask.java @@ -0,0 +1,65 @@ +/* + * 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 java.util.List; +import org.sonar.alm.client.github.GithubGlobalSettingsValidator; +import org.sonar.api.config.Configuration; +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; + +public class GithubMetricsTask extends DevOpsMetricsTask { + + private final GithubGlobalSettingsValidator githubValidator; + + public GithubMetricsTask(ServerMonitoringMetrics metrics, GithubGlobalSettingsValidator githubValidator, + DbClient dbClient, Configuration config) { + super(dbClient, metrics, config); + this.githubValidator = githubValidator; + } + + @Override + public void run() { + try (DbSession dbSession = dbClient.openSession(false)) { + List<AlmSettingDto> githubSettingsDtos = dbClient.almSettingDao().selectByAlm(dbSession, ALM.GITHUB); + + if (githubSettingsDtos.isEmpty()) { + metrics.setGithubStatusToRed(); + return; + } + + validateGithub(githubSettingsDtos); + } + } + + private void validateGithub(List<AlmSettingDto> almSettingDtos) { + try { + for (AlmSettingDto dto : almSettingDtos) { + githubValidator.validate(dto); + } + metrics.setGithubStatusToGreen(); + } catch (RuntimeException e) { + metrics.setGithubStatusToRed(); + } + } +} diff --git a/server/sonar-webserver-monitoring/src/main/java/org/sonar/server/monitoring/devops/GitlabMetricsTask.java b/server/sonar-webserver-monitoring/src/main/java/org/sonar/server/monitoring/devops/GitlabMetricsTask.java new file mode 100644 index 00000000000..c0ef583939f --- /dev/null +++ b/server/sonar-webserver-monitoring/src/main/java/org/sonar/server/monitoring/devops/GitlabMetricsTask.java @@ -0,0 +1,65 @@ +/* + * 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 java.util.List; +import org.sonar.alm.client.gitlab.GitlabGlobalSettingsValidator; +import org.sonar.api.config.Configuration; +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; + +public class GitlabMetricsTask extends DevOpsMetricsTask { + + private final GitlabGlobalSettingsValidator gitlabValidator; + + public GitlabMetricsTask(ServerMonitoringMetrics metrics, GitlabGlobalSettingsValidator gitlabValidator, + DbClient dbClient, Configuration config) { + super(dbClient, metrics, config); + this.gitlabValidator = gitlabValidator; + } + + @Override + public void run() { + try (DbSession dbSession = dbClient.openSession(false)) { + List<AlmSettingDto> gitlabSettingsDtos = dbClient.almSettingDao().selectByAlm(dbSession, ALM.GITLAB); + + if (gitlabSettingsDtos.isEmpty()) { + metrics.setGitlabStatusToRed(); + return; + } + + validateGitlab(gitlabSettingsDtos); + } + } + + private void validateGitlab(List<AlmSettingDto> almSettingDtos) { + try { + for (AlmSettingDto dto : almSettingDtos) { + gitlabValidator.validate(dto); + } + metrics.setGitlabStatusToGreen(); + } catch (RuntimeException e) { + metrics.setGitlabStatusToRed(); + } + } +} diff --git a/server/sonar-webserver-monitoring/src/test/java/org/sonar/server/monitoring/MainCollectorTest.java b/server/sonar-webserver-monitoring/src/test/java/org/sonar/server/monitoring/MainCollectorTest.java new file mode 100644 index 00000000000..eac306f0f77 --- /dev/null +++ b/server/sonar-webserver-monitoring/src/test/java/org/sonar/server/monitoring/MainCollectorTest.java @@ -0,0 +1,75 @@ +/* + * 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 org.junit.After; +import org.junit.Before; +import org.junit.Test; + +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; +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 MainCollectorTest { + + private final MonitoringTask task1 = mock(MonitoringTask.class); + private final MonitoringTask task2 = mock(MonitoringTask.class); + + private MainCollector underTest; + + @Before + public void before() { + MonitoringTask[] tasks = {task1, task2}; + for(MonitoringTask task : tasks) { + when(task.getDelay()).thenReturn(1L); + when(task.getPeriod()).thenReturn(1L); + } + underTest = new MainCollector(tasks); + } + + @After + public void stop() { + underTest.stop(); + } + + @Test + public void startAndStop_executorServiceIsShutdown() { + underTest.start(); + + assertFalse(underTest.getScheduledExecutorService().isShutdown()); + + underTest.stop(); + + assertTrue(underTest.getScheduledExecutorService().isShutdown()); + } + + @Test + public void start_givenTwoTasks_callsGetsDelayAndPeriodFromTasks() { + underTest.start(); + + verify(task1, times(1)).getDelay(); + verify(task1, times(1)).getPeriod(); + verify(task2, times(1)).getDelay(); + verify(task2, times(1)).getPeriod(); + } +} 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 index 0849f04a3e3..4dafde05eaa 100644 --- 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 @@ -23,6 +23,7 @@ import io.prometheus.client.Collector; import io.prometheus.client.CollectorRegistry; import java.util.Collections; import java.util.Enumeration; +import java.util.Iterator; import org.junit.Before; import org.junit.Test; @@ -75,6 +76,28 @@ public class ServerMonitoringMetricsTest { assertThat(CollectorRegistry.defaultRegistry.getSampleValue("azure_config_ok")).isEqualTo(1); } + @Test + public void setters_setNumberOfPendingTasks() { + ServerMonitoringMetrics metrics = new ServerMonitoringMetrics(); + + metrics.setNumberOfPendingTasks(10); + + assertThat(CollectorRegistry.defaultRegistry.getSampleValue("sonarqube_compute_engine_pending_tasks_total")) + .isEqualTo(10); + } + + @Test + public void observeComputeEngineTaskDurationTest() { + ServerMonitoringMetrics metrics = new ServerMonitoringMetrics(); + String[] labelNames = {"task_type", "project_key"}; + String[] labelValues = {"REPORT", "projectKey"}; + + metrics.observeComputeEngineTaskDuration(10, labelValues[0], labelValues[1]); + + assertThat(CollectorRegistry.defaultRegistry.getSampleValue("sonarqube_compute_engine_tasks_running_duration_seconds_sum", + labelNames, labelValues)).isEqualTo(10); + } + private int sizeOfDefaultRegistry() { Enumeration<Collector.MetricFamilySamples> metrics = CollectorRegistry.defaultRegistry.metricFamilySamples(); return Collections.list(metrics).size(); diff --git a/server/sonar-webserver-monitoring/src/test/java/org/sonar/server/monitoring/ce/ComputeEngineMetricsTaskTest.java b/server/sonar-webserver-monitoring/src/test/java/org/sonar/server/monitoring/ce/ComputeEngineMetricsTaskTest.java new file mode 100644 index 00000000000..711fb4dfa6b --- /dev/null +++ b/server/sonar-webserver-monitoring/src/test/java/org/sonar/server/monitoring/ce/ComputeEngineMetricsTaskTest.java @@ -0,0 +1,108 @@ +/* + * 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.ce; + +import java.util.HashMap; +import java.util.Map; +import java.util.Optional; +import org.junit.Before; +import org.junit.Test; +import org.sonar.api.config.Configuration; +import org.sonar.db.DbClient; +import org.sonar.server.monitoring.ServerMonitoringMetrics; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.Mockito.mock; + +public class ComputeEngineMetricsTaskTest { + + private final ServerMonitoringMetrics metrics = mock(ServerMonitoringMetrics.class); + private final DbClient dbClient = mock(DbClient.class); + private DumpMapConfiguration config; + + private ComputeEngineMetricsTask underTest; + + @Before + public void before() { + config = new DumpMapConfiguration(); + underTest = new ComputeEngineMetricsTask(dbClient, metrics, config) { + @Override + public void run() { + //intentionally empty + } + }; + } + + @Test + public void getDelay_returnNumberIfConfigEmpty() { + long delay = underTest.getDelay(); + + assertThat(delay).isPositive(); + } + + @Test + public void getDelay_returnNumberFromConfig() { + config.put("sonar.server.monitoring.ce.initial.delay", "100000"); + + long delay = underTest.getDelay(); + + assertThat(delay).isEqualTo(100_000L); + } + + @Test + public void getPeriod_returnNumberIfConfigEmpty() { + long delay = underTest.getPeriod(); + + assertThat(delay).isPositive(); + } + + @Test + public void getPeriod_returnNumberFromConfig() { + config.put("sonar.server.monitoring.ce.period", "100000"); + + long delay = underTest.getPeriod(); + + assertThat(delay).isEqualTo(100_000L); + } + + private static class DumpMapConfiguration implements Configuration { + private final Map<String, String> keyValues = new HashMap<>(); + + public Configuration put(String key, String value) { + keyValues.put(key, value.trim()); + return this; + } + + @Override + public Optional<String> get(String key) { + return Optional.ofNullable(keyValues.get(key)); + } + + @Override + public boolean hasKey(String key) { + throw new UnsupportedOperationException("hasKey not implemented"); + } + + @Override + public String[] getStringArray(String key) { + throw new UnsupportedOperationException("getStringArray not implemented"); + } + } +} diff --git a/server/sonar-webserver-monitoring/src/test/java/org/sonar/server/monitoring/ce/NumberOfTasksInQueueTaskTest.java b/server/sonar-webserver-monitoring/src/test/java/org/sonar/server/monitoring/ce/NumberOfTasksInQueueTaskTest.java new file mode 100644 index 00000000000..dc56cb4e197 --- /dev/null +++ b/server/sonar-webserver-monitoring/src/test/java/org/sonar/server/monitoring/ce/NumberOfTasksInQueueTaskTest.java @@ -0,0 +1,51 @@ +/* + * 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.ce; + +import org.junit.Test; +import org.sonar.api.config.Configuration; +import org.sonar.db.DbClient; +import org.sonar.db.ce.CeQueueDao; +import org.sonar.server.monitoring.ServerMonitoringMetrics; + +import static org.mockito.ArgumentMatchers.any; +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 NumberOfTasksInQueueTaskTest { + + private final DbClient dbClient = mock(DbClient.class); + private final CeQueueDao ceQueueDao = mock(CeQueueDao.class); + private final ServerMonitoringMetrics metrics = mock(ServerMonitoringMetrics.class); + private final Configuration config = mock(Configuration.class); + + @Test + public void run_setsValueInMetricsBasedOnValueReturnedFromDatabase() { + NumberOfTasksInQueueTask task = new NumberOfTasksInQueueTask(dbClient, metrics, config); + when(dbClient.ceQueueDao()).thenReturn(ceQueueDao); + when(ceQueueDao.countByStatus(any(), any())).thenReturn(10); + + task.run(); + + verify(metrics, times(1)).setNumberOfPendingTasks(10); + } +} diff --git a/server/sonar-webserver-monitoring/src/test/java/org/sonar/server/monitoring/ce/RecentTasksDurationTaskTest.java b/server/sonar-webserver-monitoring/src/test/java/org/sonar/server/monitoring/ce/RecentTasksDurationTaskTest.java new file mode 100644 index 00000000000..2d4eea9b745 --- /dev/null +++ b/server/sonar-webserver-monitoring/src/test/java/org/sonar/server/monitoring/ce/RecentTasksDurationTaskTest.java @@ -0,0 +1,134 @@ +/* + * 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.ce; + +import java.util.ArrayList; +import java.util.List; +import org.junit.Before; +import org.junit.Test; +import org.sonar.api.config.Configuration; +import org.sonar.api.utils.System2; +import org.sonar.db.DbClient; +import org.sonar.db.ce.CeActivityDao; +import org.sonar.db.ce.CeActivityDto; +import org.sonar.db.ce.CeQueueDto; +import org.sonar.db.component.ComponentDao; +import org.sonar.db.component.ComponentDto; +import org.sonar.server.monitoring.ServerMonitoringMetrics; + +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyLong; +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 RecentTasksDurationTaskTest { + + private final DbClient dbClient = mock(DbClient.class); + private final CeActivityDao ceActivityDao = mock(CeActivityDao.class); + private final ComponentDao componentDao = mock(ComponentDao.class); + private final ServerMonitoringMetrics metrics = mock(ServerMonitoringMetrics.class); + private final Configuration config = mock(Configuration.class); + private final System2 system = mock(System2.class); + + @Before + public void before() { + when(dbClient.ceActivityDao()).thenReturn(ceActivityDao); + when(dbClient.componentDao()).thenReturn(componentDao); + ComponentDto componentDto = new ComponentDto(); + componentDto.setDbKey("key"); + } + + @Test + public void run_given5SuccessfulTasks_observeDurationFor5Tasks() { + RecentTasksDurationTask task = new RecentTasksDurationTask(dbClient, metrics, config, system); + List<CeActivityDto> recentTasks = createTasks(5, 0); + + when(componentDao.selectByUuids(any(), any())).thenReturn(createComponentDtos(5)); + when(ceActivityDao.selectNewerThan(any(), anyLong())).thenReturn(recentTasks); + + task.run(); + + verify(metrics, times(5)).observeComputeEngineTaskDuration(anyLong(), any(), any()); + } + + @Test + public void run_given1SuccessfulTasksAnd1Failing_observeDurationFor1Tasks() { + RecentTasksDurationTask task = new RecentTasksDurationTask(dbClient, metrics, config, system); + List<CeActivityDto> recentTasks = createTasks(1, 1); + + when(componentDao.selectByUuids(any(), any())).thenReturn(createComponentDtos(1)); + when(ceActivityDao.selectNewerThan(any(), anyLong())).thenReturn(recentTasks); + + task.run(); + + verify(metrics, times(1)).observeComputeEngineTaskDuration(anyLong(), any(), any()); + } + + @Test + public void run_givenNullExecutionTime_dontReportMetricData() { + RecentTasksDurationTask task = new RecentTasksDurationTask(dbClient, metrics, config, system); + List<CeActivityDto> recentTasks = createTasks(1, 0); + recentTasks.get(0).setExecutionTimeMs(null); + + when(componentDao.selectByUuids(any(), any())).thenReturn(createComponentDtos(1)); + when(ceActivityDao.selectNewerThan(any(), anyLong())).thenReturn(recentTasks); + + task.run(); + + verify(metrics, times(0)).observeComputeEngineTaskDuration(anyLong(), any(), any()); + } + + private List<CeActivityDto> createTasks(int numberOfSuccededTasks, int numberOfFailedTasks) { + List<CeActivityDto> dtos = new ArrayList<>(); + + for (int i=0; i<numberOfSuccededTasks; i++) { + dtos.add(newCeActivityTask(CeActivityDto.Status.SUCCESS)); + } + + for (int i=0; i<numberOfFailedTasks; i++) { + dtos.add(newCeActivityTask(CeActivityDto.Status.FAILED)); + } + + return dtos; + } + + private List<ComponentDto> createComponentDtos(int number) { + List<ComponentDto> componentDtos = new ArrayList<>(); + for(int i=0; i<5; i++) { + ComponentDto component = new ComponentDto(); + component.setUuid(i + ""); + component.setDbKey(i + ""); + componentDtos.add(component); + } + return componentDtos; + } + + private CeActivityDto newCeActivityTask(CeActivityDto.Status status) { + CeActivityDto dto = new CeActivityDto(new CeQueueDto()); + dto.setStatus(status); + dto.setMainComponentUuid("0"); + dto.setExecutionTimeMs(1000L); + return dto; + } +} + + diff --git a/server/sonar-webserver-monitoring/src/test/java/org/sonar/server/monitoring/devops/AbstractDevOpsMetricsTaskTest.java b/server/sonar-webserver-monitoring/src/test/java/org/sonar/server/monitoring/devops/AbstractDevOpsMetricsTaskTest.java new file mode 100644 index 00000000000..876b008504e --- /dev/null +++ b/server/sonar-webserver-monitoring/src/test/java/org/sonar/server/monitoring/devops/AbstractDevOpsMetricsTaskTest.java @@ -0,0 +1,38 @@ +/* + * 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 java.util.ArrayList; +import java.util.List; +import org.sonar.db.alm.setting.ALM; +import org.sonar.db.alm.setting.AlmSettingDto; + +public abstract class AbstractDevOpsMetricsTaskTest { + + protected List<AlmSettingDto> generateDtos(int numberOfDtos, ALM alm) { + List<AlmSettingDto> settingDtos = new ArrayList<>(); + for(int i=0; i<numberOfDtos; i++) { + AlmSettingDto dto = new AlmSettingDto(); + dto.setAlm(alm); + settingDtos.add(dto); + } + return settingDtos; + } +} diff --git a/server/sonar-webserver-monitoring/src/test/java/org/sonar/server/monitoring/devops/AzureMetricsTaskTest.java b/server/sonar-webserver-monitoring/src/test/java/org/sonar/server/monitoring/devops/AzureMetricsTaskTest.java new file mode 100644 index 00000000000..09081b3b8d9 --- /dev/null +++ b/server/sonar-webserver-monitoring/src/test/java/org/sonar/server/monitoring/devops/AzureMetricsTaskTest.java @@ -0,0 +1,92 @@ +/* + * 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 java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.stream.IntStream; +import org.junit.Before; +import org.junit.Test; +import org.sonar.alm.client.azure.AzureDevOpsValidator; +import org.sonar.api.config.Configuration; +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.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 AzureMetricsTaskTest extends AbstractDevOpsMetricsTaskTest { + + private final ServerMonitoringMetrics metrics = mock(ServerMonitoringMetrics.class); + private final AzureDevOpsValidator azureDevOpsValidator = mock(AzureDevOpsValidator.class); + private final DbClient dbClient = mock(DbClient.class); + private final Configuration config = mock(Configuration.class); + + private final AlmSettingDao almSettingsDao = mock(AlmSettingDao.class); + + private final AzureMetricsTask underTest = new AzureMetricsTask(metrics, azureDevOpsValidator, dbClient, config); + + @Before + public void before() { + when(dbClient.almSettingDao()).thenReturn(almSettingsDao); + } + + @Test + public void run_azureDevOpsValidatorDoesntThrowException_setGreenStatusInMetricsOnce() { + List<AlmSettingDto> dtos = generateDtos(5, ALM.AZURE_DEVOPS); + when(almSettingsDao.selectByAlm(any(), any())).thenReturn(dtos); + + underTest.run(); + + verify(metrics, times(1)).setAzureStatusToGreen(); + verify(metrics, times(0)).setAzureStatusToRed(); + } + + @Test + public void run_azureDevOpsValidatorDoesntThrowException_setRedStatusInMetricsOnce() { + List<AlmSettingDto> dtos = generateDtos(5, ALM.AZURE_DEVOPS); + when(almSettingsDao.selectByAlm(any(), any())).thenReturn(dtos); + + doThrow(new RuntimeException()).when(azureDevOpsValidator).validate(any()); + + underTest.run(); + + verify(metrics, times(0)).setAzureStatusToGreen(); + verify(metrics, times(1)).setAzureStatusToRed(); + } + + @Test + public void run_azureIntegrationNotConfigured_setRedStatusInMetricsOnce() { + when(almSettingsDao.selectByAlm(any(), any())).thenReturn(Collections.emptyList()); + + underTest.run(); + + verify(metrics, times(0)).setAzureStatusToGreen(); + verify(metrics, times(1)).setAzureStatusToRed(); + } +} diff --git a/server/sonar-webserver-monitoring/src/test/java/org/sonar/server/monitoring/devops/BitbucketMetricsTaskTest.java b/server/sonar-webserver-monitoring/src/test/java/org/sonar/server/monitoring/devops/BitbucketMetricsTaskTest.java new file mode 100644 index 00000000000..0ed22ed1388 --- /dev/null +++ b/server/sonar-webserver-monitoring/src/test/java/org/sonar/server/monitoring/devops/BitbucketMetricsTaskTest.java @@ -0,0 +1,122 @@ +/* + * 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 java.util.Collections; +import org.junit.Before; +import org.junit.Test; +import org.sonar.alm.client.bitbucket.bitbucketcloud.BitbucketCloudValidator; +import org.sonar.alm.client.bitbucketserver.BitbucketServerSettingsValidator; +import org.sonar.api.config.Configuration; +import org.sonar.db.DbClient; +import org.sonar.db.DbSession; +import org.sonar.db.alm.setting.ALM; +import org.sonar.db.alm.setting.AlmSettingDao; +import org.sonar.server.monitoring.ServerMonitoringMetrics; + +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyBoolean; +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 BitbucketMetricsTaskTest extends AbstractDevOpsMetricsTaskTest { + + private final ServerMonitoringMetrics metrics = mock(ServerMonitoringMetrics.class); + private final BitbucketCloudValidator bitbucketCloudValidator = mock(BitbucketCloudValidator.class); + private final BitbucketServerSettingsValidator bitbucketServerValidator = mock(BitbucketServerSettingsValidator.class); + private final DbClient dbClient = mock(DbClient.class); + private final Configuration config = mock(Configuration.class); + + private final AlmSettingDao almSettingsDao = mock(AlmSettingDao.class); + private final DbSession dbSession = mock(DbSession.class); + + private final BitbucketMetricsTask underTest = new BitbucketMetricsTask(metrics, bitbucketCloudValidator, + bitbucketServerValidator, dbClient, config); + + @Before + public void before() { + when(dbClient.almSettingDao()).thenReturn(almSettingsDao); + when(dbClient.openSession(anyBoolean())).thenReturn(dbSession); + } + + @Test + public void run_bitbucketValidatorsDontThrowException_setGreenStatusInMetricsOnce() { + when(almSettingsDao.selectByAlm(dbSession, ALM.BITBUCKET)).thenReturn(generateDtos(5, ALM.BITBUCKET)); + when(almSettingsDao.selectByAlm(dbSession, ALM.BITBUCKET_CLOUD)).thenReturn(generateDtos(5, ALM.BITBUCKET_CLOUD)); + + underTest.run(); + + verify(metrics, times(1)).setBitbucketStatusToGreen(); + verify(metrics, times(0)).setBitbucketStatusToRed(); + } + + @Test + public void run_bitbucketValidatorsDoThrowException_setRedStatusInMetricsOnce() { + when(almSettingsDao.selectByAlm(dbSession, ALM.BITBUCKET)).thenReturn(generateDtos(5, ALM.BITBUCKET)); + when(almSettingsDao.selectByAlm(dbSession, ALM.BITBUCKET_CLOUD)).thenReturn(generateDtos(5, ALM.BITBUCKET_CLOUD)); + + doThrow(new RuntimeException()).when(bitbucketCloudValidator).validate(any()); + doThrow(new RuntimeException()).when(bitbucketServerValidator).validate(any()); + + underTest.run(); + + verify(metrics, times(0)).setBitbucketStatusToGreen(); + verify(metrics, times(1)).setBitbucketStatusToRed(); + } + + + @Test + public void run_bitbucketServerValidatorThrowExceptionCloudDoesNot_setRedStatusInMetricsOnce() { + when(almSettingsDao.selectByAlm(dbSession, ALM.BITBUCKET)).thenReturn(generateDtos(5, ALM.BITBUCKET)); + when(almSettingsDao.selectByAlm(dbSession, ALM.BITBUCKET_CLOUD)).thenReturn(generateDtos(5, ALM.BITBUCKET_CLOUD)); + + doThrow(new RuntimeException()).when(bitbucketServerValidator).validate(any()); + + underTest.run(); + + verify(metrics, times(0)).setBitbucketStatusToGreen(); + verify(metrics, times(1)).setBitbucketStatusToRed(); + } + + @Test + public void run_bitbucketServerConfiguredBitbucketCloudNot_setGreenStatusInMetricsOnce() { + when(almSettingsDao.selectByAlm(dbSession, ALM.BITBUCKET)).thenReturn(generateDtos(1, ALM.BITBUCKET)); + + underTest.run(); + + verify(metrics, times(1)).setBitbucketStatusToGreen(); + verify(metrics, times(0)).setBitbucketStatusToRed(); + } + + @Test + public void run_bitbucketIntegrationNotConfigured_setRedStatusInMetricsOnce() { + when(almSettingsDao.selectByAlm(dbSession, ALM.BITBUCKET)).thenReturn(Collections.emptyList()); + when(almSettingsDao.selectByAlm(dbSession, ALM.BITBUCKET_CLOUD)).thenReturn(Collections.emptyList()); + + underTest.run(); + + verify(metrics, times(0)).setBitbucketStatusToGreen(); + verify(metrics, times(1)).setBitbucketStatusToRed(); + } + +} diff --git a/server/sonar-webserver-monitoring/src/test/java/org/sonar/server/monitoring/devops/DevOpsMetricsTaskTest.java b/server/sonar-webserver-monitoring/src/test/java/org/sonar/server/monitoring/devops/DevOpsMetricsTaskTest.java new file mode 100644 index 00000000000..904c250d6dc --- /dev/null +++ b/server/sonar-webserver-monitoring/src/test/java/org/sonar/server/monitoring/devops/DevOpsMetricsTaskTest.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.devops; + +import java.util.Optional; +import org.junit.Before; +import org.junit.Test; +import org.sonar.api.config.Configuration; +import org.sonar.db.DbClient; +import org.sonar.server.monitoring.ServerMonitoringMetrics; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +public class DevOpsMetricsTaskTest { + + private final ServerMonitoringMetrics metrics = mock(ServerMonitoringMetrics.class); + private final DbClient dbClient = mock(DbClient.class); + private final Configuration config = mock(Configuration.class); + + private DevOpsMetricsTask underTest; + + @Before + public void before() { + underTest = new DevOpsMetricsTask(dbClient, metrics, config) { + @Override + public void run() { + //intentionally empty + } + }; + } + + @Test + public void getDelay_returnNumberIfConfigEmpty() { + when(config.get("sonar.server.monitoring.devops.initial.delay")).thenReturn(Optional.empty()); + + long delay = underTest.getDelay(); + + assertThat(delay).isPositive(); + } + + @Test + public void getDelay_returnNumberFromConfig() { + when(config.getLong("sonar.server.monitoring.devops.initial.delay")).thenReturn(Optional.of(100_000L)); + + long delay = underTest.getDelay(); + + assertThat(delay).isEqualTo(100_000L); + } + + @Test + public void getPeriod_returnNumberIfConfigEmpty() { + when(config.get("sonar.server.monitoring.devops.period")).thenReturn(Optional.empty()); + + long delay = underTest.getPeriod(); + + assertThat(delay).isPositive(); + } + + @Test + public void getPeriod_returnNumberFromConfig() { + when(config.getLong("sonar.server.monitoring.devops.period")).thenReturn(Optional.of(100_000L)); + + long delay = underTest.getPeriod(); + + assertThat(delay).isEqualTo(100_000L); + } +} 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 deleted file mode 100644 index d464825e9ee..00000000000 --- a/server/sonar-webserver-monitoring/src/test/java/org/sonar/server/monitoring/devops/DevOpsPlatformsMetricsCollectorTest.java +++ /dev/null @@ -1,185 +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.monitoring.devops; - -import org.sonar.api.config.Configuration; -import java.util.ArrayList; -import java.util.List; -import java.util.Optional; -import java.util.Set; -import org.junit.Before; -import org.junit.Test; -import org.sonar.alm.client.azure.AzureDevOpsValidator; -import org.sonar.alm.client.bitbucket.bitbucketcloud.BitbucketCloudValidator; -import org.sonar.alm.client.bitbucketserver.BitbucketServerSettingsValidator; -import org.sonar.alm.client.github.GithubGlobalSettingsValidator; -import org.sonar.alm.client.gitlab.GitlabGlobalSettingsValidator; -import org.sonar.db.DbClient; -import org.sonar.db.alm.setting.ALM; -import org.sonar.db.alm.setting.AlmSettingDao; -import org.sonar.db.alm.setting.AlmSettingDto; -import org.sonar.server.monitoring.ServerMonitoringMetrics; - -import static org.assertj.core.api.Assertions.assertThat; -import static org.mockito.ArgumentMatchers.any; -import static org.mockito.Mockito.doThrow; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.times; -import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.verifyNoInteractions; -import static org.mockito.Mockito.when; - -public class DevOpsPlatformsMetricsCollectorTest { - - private final ServerMonitoringMetrics serverMonitoringMetrics = mock(ServerMonitoringMetrics.class); - private final DbClient dbClient = mock(DbClient.class); - private final BitbucketServerSettingsValidator bitbucketServerValidator = mock(BitbucketServerSettingsValidator.class); - private final GithubGlobalSettingsValidator githubValidator = mock(GithubGlobalSettingsValidator.class); - private final GitlabGlobalSettingsValidator gitlabValidator = mock(GitlabGlobalSettingsValidator.class); - private final BitbucketCloudValidator bitbucketCloudValidator = mock(BitbucketCloudValidator.class); - private final AzureDevOpsValidator azureDevOpsValidator = mock(AzureDevOpsValidator.class); - private final Configuration config = mock(Configuration.class); - - private DevOpsPlatformsMetricsCollector collector; - - @Before - public void before() { - collector = new DevOpsPlatformsMetricsCollector(serverMonitoringMetrics, - dbClient, bitbucketServerValidator, githubValidator, gitlabValidator, bitbucketCloudValidator, - azureDevOpsValidator, config); - } - - @Test - public void start_startsNewDeamonThread() { - collector.start(); - - Optional<Thread> newDeamonThread = findNewDeamonThread(); - - assertThat(newDeamonThread).isPresent(); - assertThat(newDeamonThread.get().isDaemon()).isTrue(); - } - - @Test - public void createTask_givenOneConfigForEachALM_allValidatorsAreCalled() { - AlmSettingDao dao = mock(AlmSettingDao.class); - List<AlmSettingDto> almSettingDtos = createAlmSettingDtos(); - when(dao.selectAll(any())).thenReturn(almSettingDtos); - when(dbClient.almSettingDao()).thenReturn(dao); - - collector.createTask().run(); - - verify(bitbucketCloudValidator, times(1)).validate(findDto(ALM.BITBUCKET_CLOUD, almSettingDtos)); - verify(bitbucketServerValidator, times(1)).validate(findDto(ALM.BITBUCKET, almSettingDtos)); - verify(azureDevOpsValidator, times(1)).validate(findDto(ALM.AZURE_DEVOPS, almSettingDtos)); - verify(gitlabValidator, times(1)).validate(findDto(ALM.GITLAB, almSettingDtos)); - verify(githubValidator, times(1)).validate(findDto(ALM.GITHUB, almSettingDtos)); - } - - @Test - public void createTask_givenOnlyGitHubConfigured_validateOnlyGithub() { - AlmSettingDao dao = mock(AlmSettingDao.class); - AlmSettingDto githubDto = new AlmSettingDto(); - githubDto.setAlm(ALM.GITHUB); - when(dao.selectAll(any())).thenReturn(List.of(githubDto)); - when(dbClient.almSettingDao()).thenReturn(dao); - - collector.createTask().run(); - - verifyNoInteractions(bitbucketCloudValidator); - verifyNoInteractions(bitbucketServerValidator); - verifyNoInteractions(azureDevOpsValidator); - verifyNoInteractions(gitlabValidator); - - verify(githubValidator, times(1)).validate(githubDto); - } - - @Test - public void createTask_givenAllValidationsFailing_setAllMetricsStatusesToFalse() { - AlmSettingDao dao = mock(AlmSettingDao.class); - List<AlmSettingDto> almSettingDtos = createAlmSettingDtos(); - when(dao.selectAll(any())).thenReturn(almSettingDtos); - when(dbClient.almSettingDao()).thenReturn(dao); - - doThrow(new RuntimeException()).when(bitbucketCloudValidator).validate(any()); - doThrow(new RuntimeException()).when(bitbucketServerValidator).validate(any()); - doThrow(new RuntimeException()).when(azureDevOpsValidator).validate(any()); - doThrow(new RuntimeException()).when(gitlabValidator).validate(any()); - doThrow(new RuntimeException()).when(githubValidator).validate(any()); - - collector.createTask().run(); - - verify(serverMonitoringMetrics, times(2)).setBitbucketStatusToRed(); //2 validators for Bitbucket - verify(serverMonitoringMetrics, times(1)).setAzureStatusToRed(); - verify(serverMonitoringMetrics, times(1)).setGitlabStatusToRed(); - verify(serverMonitoringMetrics, times(1)).setGithubStatusToRed(); - } - - @Test - public void createTask_givenAllValidationsArePassing_setAllMetricsStatusesToTrue() { - AlmSettingDao dao = mock(AlmSettingDao.class); - List<AlmSettingDto> almSettingDtos = createAlmSettingDtos(); - when(dao.selectAll(any())).thenReturn(almSettingDtos); - when(dbClient.almSettingDao()).thenReturn(dao); - - collector.createTask().run(); - - verify(serverMonitoringMetrics, times(2)).setBitbucketStatusToGreen(); //2 validators for Bitbucket - verify(serverMonitoringMetrics, times(1)).setAzureStatusToGreen(); - verify(serverMonitoringMetrics, times(1)).setGitlabStatusToGreen(); - verify(serverMonitoringMetrics, times(1)).setGithubStatusToGreen(); - } - - @Test - public void createTask_givenFirstGithubValidationNotPassingAndSecondPassing_setGitHubValidationToTrue() { - AlmSettingDao dao = mock(AlmSettingDao.class); - List<AlmSettingDto> almSettingDtos = createAlmSettingDtos(); - when(dao.selectAll(any())).thenReturn(almSettingDtos); - when(dbClient.almSettingDao()).thenReturn(dao); - - when(githubValidator.validate(any())) - .thenThrow(new RuntimeException()) - .thenReturn(null); - - collector.createTask().run(); - - verify(serverMonitoringMetrics, times(1)).setGithubStatusToRed(); - verify(serverMonitoringMetrics, times(0)).setGithubStatusToGreen(); - } - - private AlmSettingDto findDto(ALM alm, List<AlmSettingDto> almSettingDtos) { - return almSettingDtos.stream().filter(d -> d.getAlm() == alm).findFirst().get(); - } - - private List<AlmSettingDto> createAlmSettingDtos() { - List<AlmSettingDto> dtos = new ArrayList<>(); - for(ALM alm : ALM.values()) { - AlmSettingDto almSettingDto = new AlmSettingDto(); - almSettingDto.setAlm(alm); - dtos.add(almSettingDto); - } - return dtos; - } - - private Optional<Thread> findNewDeamonThread() { - Set<Thread> threadSet = Thread.getAllStackTraces().keySet(); - String threadPartialName = DevOpsPlatformsMetricsCollector.class.getName(); - return threadSet.stream().filter(t -> t.getName().contains(threadPartialName)).findFirst(); - } -} diff --git a/server/sonar-webserver-monitoring/src/test/java/org/sonar/server/monitoring/devops/GithubMetricsTaskTest.java b/server/sonar-webserver-monitoring/src/test/java/org/sonar/server/monitoring/devops/GithubMetricsTaskTest.java new file mode 100644 index 00000000000..fff40ad0a0f --- /dev/null +++ b/server/sonar-webserver-monitoring/src/test/java/org/sonar/server/monitoring/devops/GithubMetricsTaskTest.java @@ -0,0 +1,91 @@ +/* + * 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 java.util.Collections; +import java.util.List; +import org.junit.Before; +import org.junit.Test; +import org.sonar.alm.client.github.GithubGlobalSettingsValidator; +import org.sonar.alm.client.gitlab.GitlabGlobalSettingsValidator; +import org.sonar.api.config.Configuration; +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.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 GithubMetricsTaskTest extends AbstractDevOpsMetricsTaskTest { + + private final ServerMonitoringMetrics metrics = mock(ServerMonitoringMetrics.class); + private final GithubGlobalSettingsValidator githubValidator = mock(GithubGlobalSettingsValidator.class); + private final DbClient dbClient = mock(DbClient.class); + private final Configuration config = mock(Configuration.class); + + private final AlmSettingDao almSettingsDao = mock(AlmSettingDao.class); + + private final GithubMetricsTask underTest = new GithubMetricsTask(metrics, githubValidator, dbClient, config); + + @Before + public void before() { + when(dbClient.almSettingDao()).thenReturn(almSettingsDao); + } + + @Test + public void run_githubValidatorDoesntThrowException_setGreenStatusInMetricsOnce() { + List<AlmSettingDto> dtos = generateDtos(5, ALM.GITHUB); + when(almSettingsDao.selectByAlm(any(), any())).thenReturn(dtos); + + underTest.run(); + + verify(metrics, times(1)).setGithubStatusToGreen(); + verify(metrics, times(0)).setGithubStatusToRed(); + } + + @Test + public void run_githubValidatorDoesntThrowException_setRedStatusInMetricsOnce() { + List<AlmSettingDto> dtos = generateDtos(5, ALM.GITHUB); + when(almSettingsDao.selectByAlm(any(), any())).thenReturn(dtos); + + doThrow(new RuntimeException()).when(githubValidator).validate(any()); + + underTest.run(); + + verify(metrics, times(0)).setGithubStatusToGreen(); + verify(metrics, times(1)).setGithubStatusToRed(); + } + + @Test + public void run_githubIntegrationNotConfigured_setRedStatusInMetricsOnce() { + when(almSettingsDao.selectByAlm(any(), any())).thenReturn(Collections.emptyList()); + + underTest.run(); + + verify(metrics, times(0)).setGithubStatusToGreen(); + verify(metrics, times(1)).setGithubStatusToRed(); + } +} diff --git a/server/sonar-webserver-monitoring/src/test/java/org/sonar/server/monitoring/devops/GitlabMetricsTaskTest.java b/server/sonar-webserver-monitoring/src/test/java/org/sonar/server/monitoring/devops/GitlabMetricsTaskTest.java new file mode 100644 index 00000000000..18f7e98f05a --- /dev/null +++ b/server/sonar-webserver-monitoring/src/test/java/org/sonar/server/monitoring/devops/GitlabMetricsTaskTest.java @@ -0,0 +1,90 @@ +/* + * 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 java.util.Collections; +import java.util.List; +import org.junit.Before; +import org.junit.Test; +import org.sonar.alm.client.gitlab.GitlabGlobalSettingsValidator; +import org.sonar.api.config.Configuration; +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.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 GitlabMetricsTaskTest extends AbstractDevOpsMetricsTaskTest { + + private final ServerMonitoringMetrics metrics = mock(ServerMonitoringMetrics.class); + private final GitlabGlobalSettingsValidator gitlabValidator = mock(GitlabGlobalSettingsValidator.class); + private final DbClient dbClient = mock(DbClient.class); + private final Configuration config = mock(Configuration.class); + + private final AlmSettingDao almSettingsDao = mock(AlmSettingDao.class); + + private final GitlabMetricsTask underTest = new GitlabMetricsTask(metrics, gitlabValidator, dbClient, config); + + @Before + public void before() { + when(dbClient.almSettingDao()).thenReturn(almSettingsDao); + } + + @Test + public void run_gitlabValidatorDoesntThrowException_setGreenStatusInMetricsOnce() { + List<AlmSettingDto> dtos = generateDtos(5, ALM.GITLAB); + when(almSettingsDao.selectByAlm(any(), any())).thenReturn(dtos); + + underTest.run(); + + verify(metrics, times(1)).setGitlabStatusToGreen(); + verify(metrics, times(0)).setGitlabStatusToRed(); + } + + @Test + public void run_gitlabValidatorDoesntThrowException_setRedStatusInMetricsOnce() { + List<AlmSettingDto> dtos = generateDtos(5, ALM.GITLAB); + when(almSettingsDao.selectByAlm(any(), any())).thenReturn(dtos); + + doThrow(new RuntimeException()).when(gitlabValidator).validate(any()); + + underTest.run(); + + verify(metrics, times(0)).setGitlabStatusToGreen(); + verify(metrics, times(1)).setGitlabStatusToRed(); + } + + @Test + public void run_gitlabIntegrationNotConfigured_setRedStatusInMetricsOnce() { + when(almSettingsDao.selectByAlm(any(), any())).thenReturn(Collections.emptyList()); + + underTest.run(); + + verify(metrics, times(0)).setGitlabStatusToGreen(); + verify(metrics, times(1)).setGitlabStatusToRed(); + } +} 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 fe0c229346c..af5d604e2bc 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 @@ -126,9 +126,15 @@ import org.sonar.server.measure.ws.MeasuresWsModule; import org.sonar.server.metric.MetricFinder; import org.sonar.server.metric.UnanalyzedLanguageMetrics; import org.sonar.server.metric.ws.MetricsWsModule; +import org.sonar.server.monitoring.MainCollector; import org.sonar.server.monitoring.MonitoringWsModule; -import org.sonar.server.monitoring.devops.DevOpsPlatformsMetricsCollector; +import org.sonar.server.monitoring.ce.NumberOfTasksInQueueTask; +import org.sonar.server.monitoring.ce.RecentTasksDurationTask; +import org.sonar.server.monitoring.devops.AzureMetricsTask; +import org.sonar.server.monitoring.devops.BitbucketMetricsTask; import org.sonar.server.monitoring.ServerMonitoringMetrics; +import org.sonar.server.monitoring.devops.GithubMetricsTask; +import org.sonar.server.monitoring.devops.GitlabMetricsTask; import org.sonar.server.newcodeperiod.ws.NewCodePeriodsWsModule; import org.sonar.server.notification.NotificationModule; import org.sonar.server.notification.ws.NotificationWsModule; @@ -578,7 +584,16 @@ public class PlatformLevel4 extends PlatformLevel { // monitoring ServerMonitoringMetrics.class, - DevOpsPlatformsMetricsCollector.class, + + AzureMetricsTask.class, + BitbucketMetricsTask.class, + GithubMetricsTask.class, + GitlabMetricsTask.class, + + NumberOfTasksInQueueTask.class, + RecentTasksDurationTask.class, + + MainCollector.class, PluginsRiskConsentFilter.class |