private final Boolean hasUnanalyzedCpp;
private final List<String> customSecurityConfigs;
private final long sonarlintWeeklyUsers;
+ private final long numberOfConnectedSonarLintClients;
private TelemetryData(Builder builder) {
serverId = builder.serverId;
externalAuthenticationProviders = builder.externalAuthenticationProviders;
projectCountByScm = builder.projectCountByScm;
projectCountByCi = builder.projectCountByCi;
+ numberOfConnectedSonarLintClients = builder.numberOfConnectedSonarLintClients;
}
public String getServerId() {
return sonarlintWeeklyUsers;
}
+ public long sonarLintConnectedClients() {
+ return numberOfConnectedSonarLintClients;
+ }
+
public long getUserCount() {
return userCount;
}
private List<String> externalAuthenticationProviders;
private Map<String, Long> projectCountByScm;
private Map<String, Long> projectCountByCi;
+ private long numberOfConnectedSonarLintClients;
private Builder() {
// enforce static factory method
return this;
}
+ Builder setNumberOfConnectedSonarLintClients(long numberOfConnectedSonarLintClients) {
+ this.numberOfConnectedSonarLintClients = numberOfConnectedSonarLintClients;
+ return this;
+ }
+
TelemetryData build() {
requireNonNull(serverId);
requireNonNull(version);
.setExternalAuthenticationProviders(asList("github", "gitlab"))
.setProjectCountByScm(Collections.emptyMap())
.setSonarlintWeeklyUsers(10)
+ .setNumberOfConnectedSonarLintClients(5)
.setProjectCountByCi(Collections.emptyMap())
.setDatabase(new TelemetryData.Database("H2", "11"))
.setUsingBranches(true);
compile project(':server:sonar-process')
compile project(':server:sonar-server-common')
compile project(':server:sonar-webserver-api')
+ compile project(':server:sonar-webserver-pushapi')
compile project(':server:sonar-webserver-es')
compile project(':sonar-core')
compile project(':sonar-duplications')
import org.sonar.server.platform.monitoring.cluster.EsClusterStateSection;
import org.sonar.server.platform.monitoring.cluster.GlobalInfoLoader;
import org.sonar.server.platform.monitoring.cluster.GlobalSystemSection;
+import org.sonar.server.platform.monitoring.cluster.ServerPushSection;
import org.sonar.server.platform.monitoring.cluster.NodeSystemSection;
import org.sonar.server.platform.monitoring.cluster.ProcessInfoProvider;
import org.sonar.server.platform.monitoring.cluster.SearchNodesInfoLoaderImpl;
PluginsSection.class,
SettingsSection.class,
AlmConfigurationSection.class,
+ ServerPushSection.class,
BundledSection.class
);
--- /dev/null
+/*
+ * 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.platform.monitoring.cluster;
+
+import org.sonar.api.server.ServerSide;
+import org.sonar.process.systeminfo.SystemInfoSection;
+import org.sonar.process.systeminfo.protobuf.ProtobufSystemInfo;
+import org.sonar.server.pushapi.sonarlint.SonarLintClientsRegistry;
+
+import static org.sonar.process.systeminfo.SystemInfoUtils.setAttribute;
+
+@ServerSide
+public class ServerPushSection implements SystemInfoSection {
+
+ private final SonarLintClientsRegistry sonarLintClientsRegistry;
+
+ public ServerPushSection(SonarLintClientsRegistry sonarLintClientsRegistry) {
+ this.sonarLintClientsRegistry = sonarLintClientsRegistry;
+ }
+
+ @Override
+ public ProtobufSystemInfo.Section toProtobuf() {
+ ProtobufSystemInfo.Section.Builder protobuf = ProtobufSystemInfo.Section.newBuilder();
+ protobuf.setName("Server Push Connections");
+ setAttribute(protobuf, "SonarLint Connected Clients", sonarLintClientsRegistry.countConnectedClients());
+ return protobuf.build();
+ }
+}
private final LicenseReader licenseReader;
public TelemetryDataLoaderImpl(Server server, DbClient dbClient, PluginRepository pluginRepository, UserIndex userIndex, ProjectMeasuresIndex projectMeasuresIndex,
- PlatformEditionProvider editionProvider, InternalProperties internalProperties, Configuration configuration, DockerSupport dockerSupport) {
+ PlatformEditionProvider editionProvider, InternalProperties internalProperties, Configuration configuration, DockerSupport dockerSupport) {
this(server, dbClient, pluginRepository, userIndex, projectMeasuresIndex, editionProvider, internalProperties, configuration, dockerSupport, null);
}
public TelemetryDataLoaderImpl(Server server, DbClient dbClient, PluginRepository pluginRepository, UserIndex userIndex, ProjectMeasuresIndex projectMeasuresIndex,
- PlatformEditionProvider editionProvider, InternalProperties internalProperties, Configuration configuration,
- DockerSupport dockerSupport, @Nullable LicenseReader licenseReader) {
+ PlatformEditionProvider editionProvider, InternalProperties internalProperties, Configuration configuration,
+ DockerSupport dockerSupport, @Nullable LicenseReader licenseReader) {
this.server = server;
this.dbClient = dbClient;
this.pluginRepository = pluginRepository;
Collection<ComponentAdapter<?>> adapters = container.getPicoContainer().getComponentAdapters();
assertThat(adapters)
- .hasSize(COMPONENTS_IN_EMPTY_COMPONENT_CONTAINER + 19);
+ .hasSize(COMPONENTS_IN_EMPTY_COMPONENT_CONTAINER + 20);
}
@Test
Collection<ComponentAdapter<?>> adapters = container.getPicoContainer().getComponentAdapters();
assertThat(adapters)
- .hasSize(COMPONENTS_IN_EMPTY_COMPONENT_CONTAINER + 13);
+ .hasSize(COMPONENTS_IN_EMPTY_COMPONENT_CONTAINER + 14);
}
}
--- /dev/null
+/*
+ * 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.platform.monitoring;
+
+import org.junit.Test;
+import org.sonar.process.systeminfo.protobuf.ProtobufSystemInfo;
+import org.sonar.server.platform.monitoring.cluster.ServerPushSection;
+import org.sonar.server.pushapi.sonarlint.SonarLintClientsRegistry;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.assertj.core.api.Assertions.tuple;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+public class ServerPushSectionTest {
+
+ private final SonarLintClientsRegistry sonarLintClientsRegistry = mock(SonarLintClientsRegistry.class);
+
+ private final ServerPushSection underTest = new ServerPushSection(sonarLintClientsRegistry);
+
+ @Test
+ public void toProtobuf_with5ConnectedSonarLintClients() {
+
+ when(sonarLintClientsRegistry.countConnectedClients()).thenReturn(5L);
+
+ ProtobufSystemInfo.Section section = underTest.toProtobuf();
+
+ assertThat(section.getName()).isEqualTo("Server Push Connections");
+ assertThat(section.getAttributesList())
+ .extracting(ProtobufSystemInfo.Attribute::getKey, ProtobufSystemInfo.Attribute::getLongValue)
+ .containsExactlyInAnyOrder(tuple("SonarLint Connected Clients", 5L));
+ }
+}
import org.sonar.server.platform.DockerSupport;
import org.sonar.server.property.InternalProperties;
import org.sonar.server.property.MapInternalProperties;
+import org.sonar.server.pushapi.sonarlint.SonarLintClientsRegistry;
import org.sonar.server.user.index.UserIndex;
import org.sonar.server.user.index.UserIndexer;
import org.sonar.updatecenter.common.Version;
private final LicenseReader licenseReader = mock(LicenseReader.class);
private final TelemetryDataLoader communityUnderTest = new TelemetryDataLoaderImpl(server, db.getDbClient(), pluginRepository, new UserIndex(es.client(), system2),
- new ProjectMeasuresIndex(es.client(), null, system2), editionProvider, internalProperties, configuration, dockerSupport, null);
+ new ProjectMeasuresIndex(es.client(), null, system2), editionProvider, internalProperties, configuration, dockerSupport);
private final TelemetryDataLoader commercialUnderTest = new TelemetryDataLoaderImpl(server, db.getDbClient(), pluginRepository, new UserIndex(es.client(), system2),
new ProjectMeasuresIndex(es.client(), null, system2), editionProvider, internalProperties, configuration, dockerSupport, licenseReader);
dependencies {
compile project(path: ':sonar-plugin-api', configuration: 'shadow')
compile project(':server:sonar-webserver-api')
+ compile project(':server:sonar-webserver-pushapi')
compile project(':server:sonar-alm-client')
compile 'io.prometheus:simpleclient'
private final Gauge webUptimeMinutes;
+ private final Gauge numberOfConnectedSonarLintClients;
+
public ServerMonitoringMetrics() {
githubHealthIntegrationStatus = Gauge.build()
.name("sonarqube_health_integration_github_status")
.help("Number of minutes for how long the SonarQube instance is running")
.register();
+ numberOfConnectedSonarLintClients = Gauge.build()
+ .name("sonarqube_number_of_connected_sonarlint_clients")
+ .help("Number of connected SonarLint clients")
+ .register();
+
}
public void setGithubStatusToGreen() {
public void setWebUptimeMinutes(long minutes) {
webUptimeMinutes.set(minutes);
}
+
+ public void setNumberOfConnectedSonarLintClients(long noOfClients) {
+ numberOfConnectedSonarLintClients.set(noOfClients);
+ }
}
--- /dev/null
+/*
+ * 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.sonar.api.config.Configuration;
+import org.sonar.server.pushapi.sonarlint.SonarLintClientsRegistry;
+
+public class SonarLintConnectedClientsTask implements MonitoringTask {
+
+ private static final String DELAY_IN_MILISECONDS_PROPERTY = "sonar.server.monitoring.other.initial.delay";
+ private static final String PERIOD_IN_MILISECONDS_PROPERTY = "sonar.server.monitoring.other.period";
+
+ private final ServerMonitoringMetrics serverMonitoringMetrics;
+ private final SonarLintClientsRegistry sonarLintClientsRegistry;
+ private final Configuration config;
+
+ public SonarLintConnectedClientsTask(ServerMonitoringMetrics serverMonitoringMetrics, SonarLintClientsRegistry sonarLintClientsRegistry,
+ Configuration configuration) {
+ this.serverMonitoringMetrics = serverMonitoringMetrics;
+ this.sonarLintClientsRegistry = sonarLintClientsRegistry;
+ this.config = configuration;
+ }
+
+ @Override
+ public void run() {
+ serverMonitoringMetrics.setNumberOfConnectedSonarLintClients(sonarLintClientsRegistry.countConnectedClients());
+ }
+
+ @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(10_000L);
+ }
+}
.isEqualTo(10);
}
+ @Test
+ public void setters_setNumberOfConnectedSonarLintClients() {
+ ServerMonitoringMetrics metrics = new ServerMonitoringMetrics();
+
+ metrics.setNumberOfConnectedSonarLintClients(5);
+
+ assertThat(CollectorRegistry.defaultRegistry.getSampleValue("sonarqube_number_of_connected_sonarlint_clients"))
+ .isEqualTo(5);
+ }
+
@Test
public void setters_setElasticsearchMetricsWithLabels() {
ServerMonitoringMetrics metrics = new ServerMonitoringMetrics();
--- /dev/null
+/*
+ * 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 java.util.HashMap;
+import java.util.Map;
+import java.util.Optional;
+import org.junit.Test;
+import org.sonar.api.config.Configuration;
+import org.sonar.server.pushapi.sonarlint.SonarLintClientsRegistry;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+public class SonarLintConnectedClientsTaskTest {
+
+ private final ServerMonitoringMetrics metrics = mock(ServerMonitoringMetrics.class);
+ private final SonarLintClientsRegistry sonarLintClientsRegistry = mock(SonarLintClientsRegistry.class);
+ private final SonarLintConnectedClientsTaskTest.DumpMapConfiguration config = new SonarLintConnectedClientsTaskTest.DumpMapConfiguration();
+ private final SonarLintConnectedClientsTask underTest = new SonarLintConnectedClientsTask(metrics, sonarLintClientsRegistry, config);
+
+ @Test
+ public void run_when5ConnectedClients_updateWith5() {
+ when(sonarLintClientsRegistry.countConnectedClients()).thenReturn(5L);
+
+ underTest.run();
+
+ verify(metrics).setNumberOfConnectedSonarLintClients(5L);
+ }
+
+ @Test
+ public void getDelay_returnNumberIfConfigEmpty() {
+ long delay = underTest.getDelay();
+
+ assertThat(delay).isPositive();
+ }
+
+ @Test
+ public void getDelay_returnNumberFromConfig() {
+ config.put("sonar.server.monitoring.other.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.other.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");
+ }
+ }
+
+}
*/
package org.sonar.server.pushapi.sonarlint;
-import com.google.common.annotations.VisibleForTesting;
import java.util.List;
import java.util.concurrent.CopyOnWriteArrayList;
import javax.servlet.AsyncEvent;
LOG.debug("Removing SonarLint client");
}
- @VisibleForTesting
- List<SonarLintClient> getAllClients() {
- return clients;
+ public long countConnectedClients() {
+ return clients.size();
}
class SonarLintClientEventsListener implements AsyncListener {
underTest.registerClient(sonarLintClient);
- assertThat(underTest.getAllClients()).hasSize(1);
+ assertThat(underTest.countConnectedClients()).isEqualTo(1);
underTest.unregisterClient(sonarLintClient);
- assertThat(underTest.getAllClients()).isEmpty();
+ assertThat(underTest.countConnectedClients()).isZero();
}
@Test
underTest.registerClient(sonarLintClient);
}
- assertThat(underTest.getAllClients()).hasSize(10);
+ assertThat(underTest.countConnectedClients()).isEqualTo(10);
}
}
import org.sonar.server.monitoring.MainCollector;
import org.sonar.server.monitoring.MonitoringWsModule;
import org.sonar.server.monitoring.ServerMonitoringMetrics;
+import org.sonar.server.monitoring.SonarLintConnectedClientsTask;
import org.sonar.server.monitoring.WebUptimeTask;
import org.sonar.server.monitoring.ce.NumberOfTasksInQueueTask;
import org.sonar.server.monitoring.ce.RecentTasksDurationTask;
ComputeEngineMetricStatusTask.class,
ElasticSearchMetricTask.class,
WebUptimeTask.class,
+ SonarLintConnectedClientsTask.class,
MainCollector.class,