addAttributeInMb(protobuf, "Non Heap Init (MB)", nonHeap.getInit());
addAttributeInMb(protobuf, "Non Heap Max (MB)", nonHeap.getMax());
addAttributeInMb(protobuf, "Non Heap Used (MB)", nonHeap.getUsed());
+
ThreadMXBean thread = ManagementFactory.getThreadMXBean();
- setAttribute(protobuf, "Thread Count", thread.getThreadCount());
+ setAttribute(protobuf, "Threads", thread.getThreadCount());
+
+ setAttribute(protobuf,"Processors", Runtime.getRuntime().availableProcessors());
return protobuf.build();
}
public class JvmStateSectionTest {
- public static final String PROCESS_NAME = "the process name";
+ private static final String PROCESS_NAME = "the process name";
@Test
public void toSystemInfoSection() {
assertThat(section.getName()).isEqualTo(PROCESS_NAME);
assertThat(section.getAttributesCount()).isGreaterThan(0);
- assertThat(section.getAttributesList()).extracting("key").contains("Thread Count");
+ assertThat(section.getAttributesList()).extracting("key").contains("Threads", "Processors");
}
@Test
+++ /dev/null
-/*
- * SonarQube
- * Copyright (C) 2009-2017 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 java.util.Map;
-import org.elasticsearch.ElasticsearchException;
-import org.elasticsearch.action.admin.cluster.node.stats.NodeStats;
-import org.elasticsearch.action.admin.cluster.node.stats.NodesStatsResponse;
-import org.elasticsearch.action.admin.cluster.stats.ClusterStatsResponse;
-import org.elasticsearch.action.admin.indices.stats.IndexStats;
-import org.elasticsearch.action.admin.indices.stats.IndicesStatsResponse;
-import org.elasticsearch.cluster.health.ClusterHealthStatus;
-import org.elasticsearch.common.breaker.CircuitBreaker;
-import org.sonar.api.utils.log.Loggers;
-import org.sonar.process.systeminfo.protobuf.ProtobufSystemInfo;
-import org.sonar.server.es.EsClient;
-
-import static org.apache.commons.io.FileUtils.byteCountToDisplaySize;
-import static org.sonar.process.systeminfo.SystemInfoUtils.setAttribute;
-
-public class EsSection extends BaseSectionMBean implements EsSectionMBean {
-
- private final EsClient esClient;
-
- public EsSection(EsClient esClient) {
- this.esClient = esClient;
- }
-
- @Override
- public String name() {
- return "Elasticsearch";
- }
-
- /**
- * MXBean does not allow to return enum {@link ClusterHealthStatus}, so
- * returning String.
- */
- @Override
- public String getState() {
- return getStateAsEnum().name();
- }
-
- private ClusterHealthStatus getStateAsEnum() {
- return clusterStats().getStatus();
- }
-
- @Override
- public ProtobufSystemInfo.Section toProtobuf() {
- ProtobufSystemInfo.Section.Builder protobuf = ProtobufSystemInfo.Section.newBuilder();
- protobuf.setName(name());
- try {
- setAttribute(protobuf, "State", getStateAsEnum().name());
- completeNodeAttributes(protobuf);
- completeIndexAttributes(protobuf);
-
- } catch (Exception es) {
- Loggers.get(EsSection.class).warn("Failed to retrieve ES attributes. There will be only a single \"state\" attribute.", es);
- setAttribute(protobuf, "State", es.getCause() instanceof ElasticsearchException ? es.getCause().getMessage() : es.getMessage());
- }
- return protobuf.build();
- }
-
- private void completeIndexAttributes(ProtobufSystemInfo.Section.Builder protobuf) {
- IndicesStatsResponse indicesStats = esClient.prepareStats().all().get();
- for (Map.Entry<String, IndexStats> indexStats : indicesStats.getIndices().entrySet()) {
- String prefix = "Index " + indexStats.getKey() + " - ";
- setAttribute(protobuf, prefix + "Docs", indexStats.getValue().getPrimaries().getDocs().getCount());
- setAttribute(protobuf, prefix + "Shards", indexStats.getValue().getShards().length);
- setAttribute(protobuf, prefix + "Store Size", byteCountToDisplaySize(indexStats.getValue().getPrimaries().getStore().getSizeInBytes()));
- }
- }
-
- private void completeNodeAttributes(ProtobufSystemInfo.Section.Builder protobuf) {
- NodesStatsResponse nodesStats = esClient.prepareNodesStats().all().get();
- if (!nodesStats.getNodes().isEmpty()) {
- NodeStats stats = nodesStats.getNodes().get(0);
- setAttribute(protobuf, "Disk Available", byteCountToDisplaySize(stats.getFs().getTotal().getAvailable().getBytes()));
- setAttribute(protobuf, "Store Size", byteCountToDisplaySize(stats.getIndices().getStore().getSizeInBytes()));
- setAttribute(protobuf, "Open Files", stats.getProcess().getOpenFileDescriptors());
- setAttribute(protobuf, "JVM Heap Usage", formatPercent(stats.getJvm().getMem().getHeapUsedPercent()));
- setAttribute(protobuf, "JVM Heap Used", byteCountToDisplaySize(stats.getJvm().getMem().getHeapUsed().getBytes()));
- setAttribute(protobuf, "JVM Heap Max", byteCountToDisplaySize(stats.getJvm().getMem().getHeapMax().getBytes()));
- setAttribute(protobuf, "JVM Non Heap Used", byteCountToDisplaySize(stats.getJvm().getMem().getNonHeapUsed().getBytes()));
- setAttribute(protobuf, "JVM Threads", stats.getJvm().getThreads().getCount());
- setAttribute(protobuf, "Field Data Memory", byteCountToDisplaySize(stats.getIndices().getFieldData().getMemorySizeInBytes()));
- setAttribute(protobuf, "Field Data Circuit Breaker Limit", byteCountToDisplaySize(stats.getBreaker().getStats(CircuitBreaker.FIELDDATA).getLimit()));
- setAttribute(protobuf, "Field Data Circuit Breaker Estimation", byteCountToDisplaySize(stats.getBreaker().getStats(CircuitBreaker.FIELDDATA).getEstimated()));
- setAttribute(protobuf, "Request Circuit Breaker Limit", byteCountToDisplaySize(stats.getBreaker().getStats(CircuitBreaker.REQUEST).getLimit()));
- setAttribute(protobuf, "Request Circuit Breaker Estimation", byteCountToDisplaySize(stats.getBreaker().getStats(CircuitBreaker.REQUEST).getEstimated()));
- setAttribute(protobuf, "Query Cache Memory", byteCountToDisplaySize(stats.getIndices().getQueryCache().getMemorySizeInBytes()));
- setAttribute(protobuf, "Request Cache Memory", byteCountToDisplaySize(stats.getIndices().getRequestCache().getMemorySizeInBytes()));
- }
- }
-
- private ClusterStatsResponse clusterStats() {
- return esClient.prepareClusterStats().get();
- }
-
- private static String formatPercent(long amount) {
- return String.format("%.1f%%", 100 * amount * 1.0D / 100L);
- }
-}
+++ /dev/null
-/*
- * SonarQube
- * Copyright (C) 2009-2017 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;
-
-/**
- * The public attributes of {@link EsSection}
- * to be exported in JMX bean.
- */
-public interface EsSectionMBean {
- String getState();
-}
--- /dev/null
+/*
+ * SonarQube
+ * Copyright (C) 2009-2017 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.elasticsearch.ElasticsearchException;
+import org.elasticsearch.action.admin.cluster.node.stats.NodeStats;
+import org.elasticsearch.action.admin.cluster.node.stats.NodesStatsResponse;
+import org.elasticsearch.action.admin.cluster.stats.ClusterStatsResponse;
+import org.elasticsearch.cluster.health.ClusterHealthStatus;
+import org.elasticsearch.common.breaker.CircuitBreaker;
+import org.sonar.api.utils.log.Loggers;
+import org.sonar.process.systeminfo.SystemInfoSection;
+import org.sonar.process.systeminfo.protobuf.ProtobufSystemInfo;
+import org.sonar.server.es.EsClient;
+
+import static org.apache.commons.io.FileUtils.byteCountToDisplaySize;
+import static org.sonar.process.systeminfo.SystemInfoUtils.setAttribute;
+
+public class EsStateSection implements SystemInfoSection {
+
+ private final EsClient esClient;
+
+ public EsStateSection(EsClient esClient) {
+ this.esClient = esClient;
+ }
+
+ private ClusterHealthStatus getStateAsEnum() {
+ return clusterStats().getStatus();
+ }
+
+ @Override
+ public ProtobufSystemInfo.Section toProtobuf() {
+ ProtobufSystemInfo.Section.Builder protobuf = ProtobufSystemInfo.Section.newBuilder();
+ protobuf.setName("Search State");
+ try {
+ setAttribute(protobuf, "State", getStateAsEnum().name());
+ completeNodeAttributes(protobuf);
+ } catch (Exception es) {
+ Loggers.get(EsStateSection.class).warn("Failed to retrieve ES attributes. There will be only a single \"state\" attribute.", es);
+ setAttribute(protobuf, "State", es.getCause() instanceof ElasticsearchException ? es.getCause().getMessage() : es.getMessage());
+ }
+ return protobuf.build();
+ }
+
+ private void completeNodeAttributes(ProtobufSystemInfo.Section.Builder protobuf) {
+ NodesStatsResponse nodesStats = esClient.prepareNodesStats().all().get();
+ if (!nodesStats.getNodes().isEmpty()) {
+ NodeStats stats = nodesStats.getNodes().get(0);
+ setAttribute(protobuf, "Disk Available", byteCountToDisplaySize(stats.getFs().getTotal().getAvailable().getBytes()));
+ setAttribute(protobuf, "Store Size", byteCountToDisplaySize(stats.getIndices().getStore().getSizeInBytes()));
+ setAttribute(protobuf, "Open Files", stats.getProcess().getOpenFileDescriptors());
+ setAttribute(protobuf, "JVM Heap Usage", formatPercent(stats.getJvm().getMem().getHeapUsedPercent()));
+ setAttribute(protobuf, "JVM Heap Used", byteCountToDisplaySize(stats.getJvm().getMem().getHeapUsed().getBytes()));
+ setAttribute(protobuf, "JVM Heap Max", byteCountToDisplaySize(stats.getJvm().getMem().getHeapMax().getBytes()));
+ setAttribute(protobuf, "JVM Non Heap Used", byteCountToDisplaySize(stats.getJvm().getMem().getNonHeapUsed().getBytes()));
+ setAttribute(protobuf, "JVM Threads", stats.getJvm().getThreads().getCount());
+ setAttribute(protobuf, "Field Data Memory", byteCountToDisplaySize(stats.getIndices().getFieldData().getMemorySizeInBytes()));
+ setAttribute(protobuf, "Field Data Circuit Breaker Limit", byteCountToDisplaySize(stats.getBreaker().getStats(CircuitBreaker.FIELDDATA).getLimit()));
+ setAttribute(protobuf, "Field Data Circuit Breaker Estimation", byteCountToDisplaySize(stats.getBreaker().getStats(CircuitBreaker.FIELDDATA).getEstimated()));
+ setAttribute(protobuf, "Request Circuit Breaker Limit", byteCountToDisplaySize(stats.getBreaker().getStats(CircuitBreaker.REQUEST).getLimit()));
+ setAttribute(protobuf, "Request Circuit Breaker Estimation", byteCountToDisplaySize(stats.getBreaker().getStats(CircuitBreaker.REQUEST).getEstimated()));
+ setAttribute(protobuf, "Query Cache Memory", byteCountToDisplaySize(stats.getIndices().getQueryCache().getMemorySizeInBytes()));
+ setAttribute(protobuf, "Request Cache Memory", byteCountToDisplaySize(stats.getIndices().getRequestCache().getMemorySizeInBytes()));
+ }
+ }
+
+ private ClusterStatsResponse clusterStats() {
+ return esClient.prepareClusterStats().get();
+ }
+
+ private static String formatPercent(long amount) {
+ return String.format("%.1f%%", 100 * amount * 1.0D / 100L);
+ }
+}
--- /dev/null
+/*
+ * SonarQube
+ * Copyright (C) 2009-2017 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 java.util.Map;
+import org.elasticsearch.ElasticsearchException;
+import org.elasticsearch.action.admin.indices.stats.IndexStats;
+import org.elasticsearch.action.admin.indices.stats.IndicesStatsResponse;
+import org.sonar.api.utils.log.Loggers;
+import org.sonar.process.systeminfo.SystemInfoSection;
+import org.sonar.process.systeminfo.protobuf.ProtobufSystemInfo;
+import org.sonar.server.es.EsClient;
+
+import static org.apache.commons.io.FileUtils.byteCountToDisplaySize;
+import static org.sonar.process.systeminfo.SystemInfoUtils.setAttribute;
+
+public class EsStatisticsSection implements SystemInfoSection {
+
+ private final EsClient esClient;
+
+ public EsStatisticsSection(EsClient esClient) {
+ this.esClient = esClient;
+ }
+
+ @Override
+ public ProtobufSystemInfo.Section toProtobuf() {
+ ProtobufSystemInfo.Section.Builder protobuf = ProtobufSystemInfo.Section.newBuilder();
+ protobuf.setName("Search Statistics");
+ try {
+ completeIndexAttributes(protobuf);
+ } catch (Exception es) {
+ Loggers.get(EsStatisticsSection.class).warn("Failed to retrieve ES attributes. There will be only a single \"Error\" attribute.", es);
+ setAttribute(protobuf, "Error", es.getCause() instanceof ElasticsearchException ? es.getCause().getMessage() : es.getMessage());
+ }
+ return protobuf.build();
+ }
+
+ private void completeIndexAttributes(ProtobufSystemInfo.Section.Builder protobuf) {
+ IndicesStatsResponse indicesStats = esClient.prepareStats().all().get();
+ for (Map.Entry<String, IndexStats> indexStats : indicesStats.getIndices().entrySet()) {
+ String prefix = "Index " + indexStats.getKey() + " - ";
+ setAttribute(protobuf, prefix + "Docs", indexStats.getValue().getPrimaries().getDocs().getCount());
+ setAttribute(protobuf, prefix + "Shards", indexStats.getValue().getShards().length);
+ setAttribute(protobuf, prefix + "Store Size", byteCountToDisplaySize(indexStats.getValue().getPrimaries().getStore().getSizeInBytes()));
+ }
+ }
+}
+++ /dev/null
-/*
- * SonarQube
- * Copyright (C) 2009-2017 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 com.google.common.base.Joiner;
-import java.io.File;
-import java.util.List;
-import javax.annotation.CheckForNull;
-import javax.annotation.Nullable;
-import org.sonar.api.CoreProperties;
-import org.sonar.api.config.Configuration;
-import org.sonar.api.platform.Server;
-import org.sonar.api.security.SecurityRealm;
-import org.sonar.api.server.authentication.IdentityProvider;
-import org.sonar.core.util.stream.MoreCollectors;
-import org.sonar.process.ProcessProperties;
-import org.sonar.process.systeminfo.protobuf.ProtobufSystemInfo;
-import org.sonar.server.authentication.IdentityProviderRepository;
-import org.sonar.server.health.Health;
-import org.sonar.server.health.HealthChecker;
-import org.sonar.server.platform.ServerIdLoader;
-import org.sonar.server.platform.ServerLogging;
-import org.sonar.server.user.SecurityRealmFactory;
-
-import static org.sonar.process.systeminfo.SystemInfoUtils.setAttribute;
-
-public class SonarQubeSection extends BaseSectionMBean implements SonarQubeSectionMBean {
-
- private static final Joiner COMMA_JOINER = Joiner.on(", ");
-
- static final String BRANDING_FILE_PATH = "web/WEB-INF/classes/com/sonarsource/branding";
-
- private final Configuration config;
- private final SecurityRealmFactory securityRealmFactory;
- private final IdentityProviderRepository identityProviderRepository;
- private final Server server;
- private final ServerLogging serverLogging;
- private final ServerIdLoader serverIdLoader;
- private final HealthChecker healthChecker;
-
- public SonarQubeSection(Configuration config, SecurityRealmFactory securityRealmFactory,
- IdentityProviderRepository identityProviderRepository, Server server, ServerLogging serverLogging,
- ServerIdLoader serverIdLoader, HealthChecker healthChecker) {
- this.config = config;
- this.securityRealmFactory = securityRealmFactory;
- this.identityProviderRepository = identityProviderRepository;
- this.server = server;
- this.serverLogging = serverLogging;
- this.serverIdLoader = serverIdLoader;
- this.healthChecker = healthChecker;
- }
-
- @Override
- public String getServerId() {
- return serverIdLoader.getRaw().orElse(null);
- }
-
- @Override
- public String getVersion() {
- return server.getVersion();
- }
-
- @Override
- public String getLogLevel() {
- return serverLogging.getRootLoggerLevel().name();
- }
-
- @CheckForNull
- private String getExternalUserAuthentication() {
- SecurityRealm realm = securityRealmFactory.getRealm();
- return realm == null ? null : realm.getName();
- }
-
- private List<String> getEnabledIdentityProviders() {
- return identityProviderRepository.getAllEnabledAndSorted()
- .stream()
- .filter(IdentityProvider::isEnabled)
- .map(IdentityProvider::getName)
- .collect(MoreCollectors.toList());
- }
-
- private List<String> getAllowsToSignUpEnabledIdentityProviders() {
- return identityProviderRepository.getAllEnabledAndSorted()
- .stream()
- .filter(IdentityProvider::isEnabled)
- .filter(IdentityProvider::allowsUsersToSignUp)
- .map(IdentityProvider::getName)
- .collect(MoreCollectors.toList());
- }
-
- private boolean getForceAuthentication() {
- return config.getBoolean(CoreProperties.CORE_FORCE_AUTHENTICATION_PROPERTY).orElse(false);
- }
-
- private boolean isOfficialDistribution() {
- // the dependency com.sonarsource:sonarsource-branding is shaded to webapp
- // during release (see sonar-web pom)
- File brandingFile = new File(server.getRootDir(), BRANDING_FILE_PATH);
- // no need to check that the file exists. java.io.File#length() returns zero in this case.
- return brandingFile.length() > 0L;
- }
-
- @Override
- public String name() {
- return "SonarQube";
- }
-
- @Override
- public ProtobufSystemInfo.Section toProtobuf() {
- ProtobufSystemInfo.Section.Builder protobuf = ProtobufSystemInfo.Section.newBuilder();
- protobuf.setName(name());
-
- serverIdLoader.get().ifPresent(serverId -> {
- setAttribute(protobuf, "Server ID", serverId.getId());
- setAttribute(protobuf, "Server ID validated", serverId.isValid());
- });
- Health health = healthChecker.checkNode();
- setAttribute(protobuf, "Health", health.getStatus().name());
- setAttribute(protobuf, "Health Causes", health.getCauses());
- setAttribute(protobuf, "Version", getVersion());
- setAttribute(protobuf, "External User Authentication", getExternalUserAuthentication());
- addIfNotEmpty(protobuf, "Accepted external identity providers", getEnabledIdentityProviders());
- addIfNotEmpty(protobuf, "External identity providers whose users are allowed to sign themselves up", getAllowsToSignUpEnabledIdentityProviders());
- setAttribute(protobuf, "Force authentication", getForceAuthentication());
- setAttribute(protobuf, "Official Distribution", isOfficialDistribution());
- setAttribute(protobuf, "Home Dir", config.get(ProcessProperties.PATH_HOME).orElse(null));
- setAttribute(protobuf, "Data Dir", config.get(ProcessProperties.PATH_DATA).orElse(null));
- setAttribute(protobuf, "Temp Dir", config.get(ProcessProperties.PATH_TEMP).orElse(null));
- setAttribute(protobuf, "Logs Dir", config.get(ProcessProperties.PATH_LOGS).orElse(null));
- setAttribute(protobuf, "Logs Level", getLogLevel());
- return protobuf.build();
- }
-
- private static void addIfNotEmpty(ProtobufSystemInfo.Section.Builder protobuf, String key, @Nullable List<String> values) {
- if (values != null && !values.isEmpty()) {
- setAttribute(protobuf, key, COMMA_JOINER.join(values));
- }
- }
-}
+++ /dev/null
-/*
- * SonarQube
- * Copyright (C) 2009-2017 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 javax.annotation.CheckForNull;
-
-public interface SonarQubeSectionMBean {
- @CheckForNull
- String getServerId();
-
- String getVersion();
-
- String getLogLevel();
-}
*/
package org.sonar.server.platform.monitoring;
-import java.lang.management.ClassLoadingMXBean;
-import java.lang.management.ManagementFactory;
-import java.lang.management.MemoryMXBean;
-import java.lang.management.RuntimeMXBean;
-import java.lang.management.ThreadMXBean;
-import java.util.Date;
-import org.sonar.api.utils.System2;
-import org.sonar.process.systeminfo.SystemInfoSection;
+import com.google.common.base.Joiner;
+import java.io.File;
+import java.util.List;
+import javax.annotation.CheckForNull;
+import javax.annotation.Nullable;
+import org.sonar.api.CoreProperties;
+import org.sonar.api.config.Configuration;
+import org.sonar.api.platform.Server;
+import org.sonar.api.security.SecurityRealm;
+import org.sonar.api.server.authentication.IdentityProvider;
+import org.sonar.core.util.stream.MoreCollectors;
+import org.sonar.process.ProcessProperties;
import org.sonar.process.systeminfo.protobuf.ProtobufSystemInfo;
+import org.sonar.server.authentication.IdentityProviderRepository;
+import org.sonar.server.health.Health;
+import org.sonar.server.health.HealthChecker;
+import org.sonar.server.platform.ServerIdLoader;
+import org.sonar.server.platform.ServerLogging;
+import org.sonar.server.user.SecurityRealmFactory;
-import static java.lang.String.format;
-import static org.sonar.api.utils.DateUtils.formatDateTime;
import static org.sonar.process.systeminfo.SystemInfoUtils.setAttribute;
-/**
- * JVM runtime information. Not exported as a MXBean because these information
- * are natively provided.
- */
-public class SystemSection implements SystemInfoSection {
- private final System2 system;
+public class SystemSection extends BaseSectionMBean implements SystemSectionMBean {
+
+ private static final Joiner COMMA_JOINER = Joiner.on(", ");
+
+ static final String BRANDING_FILE_PATH = "web/WEB-INF/classes/com/sonarsource/branding";
- public SystemSection() {
- this(System2.INSTANCE);
+ private final Configuration config;
+ private final SecurityRealmFactory securityRealmFactory;
+ private final IdentityProviderRepository identityProviderRepository;
+ private final Server server;
+ private final ServerLogging serverLogging;
+ private final ServerIdLoader serverIdLoader;
+ private final HealthChecker healthChecker;
+
+ public SystemSection(Configuration config, SecurityRealmFactory securityRealmFactory,
+ IdentityProviderRepository identityProviderRepository, Server server, ServerLogging serverLogging,
+ ServerIdLoader serverIdLoader, HealthChecker healthChecker) {
+ this.config = config;
+ this.securityRealmFactory = securityRealmFactory;
+ this.identityProviderRepository = identityProviderRepository;
+ this.server = server;
+ this.serverLogging = serverLogging;
+ this.serverIdLoader = serverIdLoader;
+ this.healthChecker = healthChecker;
}
- SystemSection(System2 system) {
- this.system = system;
+ @Override
+ public String getServerId() {
+ return serverIdLoader.getRaw().orElse(null);
}
@Override
- public ProtobufSystemInfo.Section toProtobuf() {
- ProtobufSystemInfo.Section.Builder protobuf = ProtobufSystemInfo.Section.newBuilder();
- protobuf.setName("System");
+ public String getVersion() {
+ return server.getVersion();
+ }
- setAttribute(protobuf, "System Date", formatDateTime(new Date(system.now())));
- setAttribute(protobuf, "Start Time", formatDateTime(new Date(runtimeMXBean().getStartTime())));
- setAttribute(protobuf, "JVM Vendor", runtimeMXBean().getVmVendor());
- setAttribute(protobuf, "JVM Name", runtimeMXBean().getVmName());
- setAttribute(protobuf, "JVM Version", runtimeMXBean().getVmVersion());
- setAttribute(protobuf, "Processors", runtime().availableProcessors());
- setAttribute(protobuf, "System Classpath", runtimeMXBean().getClassPath());
- setAttribute(protobuf, "BootClassPath", runtimeMXBean().getBootClassPath());
- setAttribute(protobuf, "Library Path", runtimeMXBean().getLibraryPath());
- setAttribute(protobuf, "Total Memory", formatMemory(runtime().totalMemory()));
- setAttribute(protobuf, "Free Memory", formatMemory(runtime().freeMemory()));
- setAttribute(protobuf, "Max Memory", formatMemory(runtime().maxMemory()));
- setAttribute(protobuf, "Heap", memoryMXBean().getHeapMemoryUsage().toString());
- setAttribute(protobuf, "Non Heap", memoryMXBean().getNonHeapMemoryUsage().toString());
- setAttribute(protobuf, "System Load Average", format("%.1f%% (last minute)", ManagementFactory.getOperatingSystemMXBean().getSystemLoadAverage() * 100.0));
- setAttribute(protobuf, "Loaded Classes", classLoadingMXBean().getLoadedClassCount());
- setAttribute(protobuf, "Total Loaded Classes", classLoadingMXBean().getTotalLoadedClassCount());
- setAttribute(protobuf, "Unloaded Classes", classLoadingMXBean().getUnloadedClassCount());
- setAttribute(protobuf, "Threads", threadMXBean().getThreadCount());
- setAttribute(protobuf, "Threads Peak", threadMXBean().getPeakThreadCount());
- setAttribute(protobuf, "Daemon Thread", threadMXBean().getDaemonThreadCount());
- return protobuf.build();
+ @Override
+ public String getLogLevel() {
+ return serverLogging.getRootLoggerLevel().name();
}
- private static RuntimeMXBean runtimeMXBean() {
- return ManagementFactory.getRuntimeMXBean();
+ @CheckForNull
+ private String getExternalUserAuthentication() {
+ SecurityRealm realm = securityRealmFactory.getRealm();
+ return realm == null ? null : realm.getName();
}
- private static Runtime runtime() {
- return Runtime.getRuntime();
+ private List<String> getEnabledIdentityProviders() {
+ return identityProviderRepository.getAllEnabledAndSorted()
+ .stream()
+ .filter(IdentityProvider::isEnabled)
+ .map(IdentityProvider::getName)
+ .collect(MoreCollectors.toList());
}
- private static MemoryMXBean memoryMXBean() {
- return ManagementFactory.getMemoryMXBean();
+ private List<String> getAllowsToSignUpEnabledIdentityProviders() {
+ return identityProviderRepository.getAllEnabledAndSorted()
+ .stream()
+ .filter(IdentityProvider::isEnabled)
+ .filter(IdentityProvider::allowsUsersToSignUp)
+ .map(IdentityProvider::getName)
+ .collect(MoreCollectors.toList());
}
- private static ClassLoadingMXBean classLoadingMXBean() {
- return ManagementFactory.getClassLoadingMXBean();
+ private boolean getForceAuthentication() {
+ return config.getBoolean(CoreProperties.CORE_FORCE_AUTHENTICATION_PROPERTY).orElse(false);
}
- private static ThreadMXBean threadMXBean() {
- return ManagementFactory.getThreadMXBean();
+ private boolean isOfficialDistribution() {
+ // the dependency com.sonarsource:sonarsource-branding is shaded to webapp
+ // during release (see sonar-web pom)
+ File brandingFile = new File(server.getRootDir(), BRANDING_FILE_PATH);
+ // no need to check that the file exists. java.io.File#length() returns zero in this case.
+ return brandingFile.length() > 0L;
+ }
+
+ @Override
+ public String name() {
+ // JMX name
+ return "SonarQube";
+ }
+
+ @Override
+ public ProtobufSystemInfo.Section toProtobuf() {
+ ProtobufSystemInfo.Section.Builder protobuf = ProtobufSystemInfo.Section.newBuilder();
+ protobuf.setName("System");
+
+ serverIdLoader.get().ifPresent(serverId -> {
+ setAttribute(protobuf, "Server ID", serverId.getId());
+ setAttribute(protobuf, "Server ID validated", serverId.isValid());
+ });
+ Health health = healthChecker.checkNode();
+ setAttribute(protobuf, "Health", health.getStatus().name());
+ setAttribute(protobuf, "Health Causes", health.getCauses());
+ setAttribute(protobuf, "Version", getVersion());
+ setAttribute(protobuf, "External User Authentication", getExternalUserAuthentication());
+ addIfNotEmpty(protobuf, "Accepted external identity providers", getEnabledIdentityProviders());
+ addIfNotEmpty(protobuf, "External identity providers whose users are allowed to sign themselves up", getAllowsToSignUpEnabledIdentityProviders());
+ setAttribute(protobuf, "High Availability", false);
+ setAttribute(protobuf, "Official Distribution", isOfficialDistribution());
+ setAttribute(protobuf, "Force authentication", getForceAuthentication());
+ setAttribute(protobuf, "Home Dir", config.get(ProcessProperties.PATH_HOME).orElse(null));
+ setAttribute(protobuf, "Data Dir", config.get(ProcessProperties.PATH_DATA).orElse(null));
+ setAttribute(protobuf, "Temp Dir", config.get(ProcessProperties.PATH_TEMP).orElse(null));
+ setAttribute(protobuf, "Logs Dir", config.get(ProcessProperties.PATH_LOGS).orElse(null));
+ setAttribute(protobuf, "Logs Level", getLogLevel());
+ return protobuf.build();
}
- private static String formatMemory(long memoryInBytes) {
- return format("%d MB", memoryInBytes / 1_000_000);
+ private static void addIfNotEmpty(ProtobufSystemInfo.Section.Builder protobuf, String key, @Nullable List<String> values) {
+ if (values != null && !values.isEmpty()) {
+ setAttribute(protobuf, key, COMMA_JOINER.join(values));
+ }
}
}
--- /dev/null
+/*
+ * SonarQube
+ * Copyright (C) 2009-2017 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 javax.annotation.CheckForNull;
+
+public interface SystemSectionMBean {
+ @CheckForNull
+ String getServerId();
+
+ String getVersion();
+
+ String getLogLevel();
+}
new JvmPropertiesSection("Web JVM Properties"),
new JvmStateSection("Web JVM State"),
DatabaseSection.class,
- EsSection.class,
+ EsStateSection.class,
+ EsStatisticsSection.class,
PluginsSection.class,
SettingsSection.class,
- SonarQubeSection.class,
SystemSection.class,
StandaloneInfoAction.class
new JvmPropertiesSection("Web JVM Properties"),
new JvmStateSection("Web JVM State"),
DatabaseSection.class,
- EsSection.class,
+ EsStateSection.class,
PluginsSection.class,
ClusterInfoAction.class
*/
package org.sonar.server.platform.ws;
+import java.util.Arrays;
import org.sonar.api.server.ws.Request;
import org.sonar.api.server.ws.Response;
import org.sonar.api.server.ws.WebService;
try (JsonWriter json = response.newJsonWriter()) {
json.beginObject();
- // global section
- json.prop("Cluster", true);
- json.prop("Cluster Name", "foo");
+ json.name("System");
+ json.beginObject();
+ json.prop("High Availability", true);
+ json.prop("Cluster Name", "fooo");
json.prop("Server Id", "ABC123");
json.prop("Health", "RED");
json
.name("Health Causes")
- .beginArray().beginObject().prop("message", "Requires at least two search nodes").endObject().endArray();
+ .beginArray().values(Arrays.asList("foo", "bar")).endArray();
+ json.endObject();
json.name("Settings");
json.beginObject();
json.prop("sonar.externalIdentityProviders", "GitHub, BitBucket");
json.endObject();
-
-
json.name("Database");
json
.beginObject()
.prop("workersPerNode", 4)
.endObject();
- json.name("Elasticsearch");
+ json.name("Search");
json
.beginObject()
.prop("Health", "GREEN")
+++ /dev/null
-/*
- * SonarQube
- * Copyright (C) 2009-2017 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.elasticsearch.ElasticsearchException;
-import org.elasticsearch.cluster.health.ClusterHealthStatus;
-import org.junit.Rule;
-import org.junit.Test;
-import org.sonar.api.config.internal.MapSettings;
-import org.sonar.process.systeminfo.protobuf.ProtobufSystemInfo;
-import org.sonar.server.es.EsClient;
-import org.sonar.server.es.EsTester;
-import org.sonar.server.issue.index.IssueIndexDefinition;
-
-import static org.assertj.core.api.Assertions.assertThat;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.when;
-import static org.sonar.process.systeminfo.SystemInfoUtils.attribute;
-import static org.sonar.server.platform.monitoring.SystemInfoTesting.assertThatAttributeIs;
-
-public class EsSectionTest {
-
- @Rule
- public EsTester esTester = new EsTester(new IssueIndexDefinition(new MapSettings().asConfig()));
-
- private EsSection underTest = new EsSection(esTester.client());
-
- @Test
- public void name() {
- assertThat(underTest.name()).isEqualTo("Elasticsearch");
- }
-
- @Test
- public void es_state() {
- assertThat(underTest.getState()).isEqualTo(ClusterHealthStatus.GREEN.name());
- assertThatAttributeIs(underTest.toProtobuf(), "State", ClusterHealthStatus.GREEN.name());
- }
-
- @Test
- public void node_attributes() {
- ProtobufSystemInfo.Section section = underTest.toProtobuf();
- assertThat(attribute(section, "Store Size")).isNotNull();
- }
-
- @Test
- public void index_attributes() {
- ProtobufSystemInfo.Section section = underTest.toProtobuf();
-
- // one index "issues"
- assertThat(attribute(section, "Index issues - Docs").getLongValue()).isEqualTo(0L);
- assertThat(attribute(section, "Index issues - Shards").getLongValue()).isGreaterThan(0);
- assertThat(attribute(section, "Index issues - Store Size").getStringValue()).isNotNull();
- }
-
- @Test
- public void attributes_displays_exception_message_when_cause_null_when_client_fails() {
- EsClient esClientMock = mock(EsClient.class);
- EsSection underTest = new EsSection(esClientMock);
- when(esClientMock.prepareClusterStats()).thenThrow(new RuntimeException("RuntimeException with no cause"));
-
- ProtobufSystemInfo.Section section = underTest.toProtobuf();
- assertThatAttributeIs(section, "State", "RuntimeException with no cause");
- }
-
- @Test
- public void attributes_displays_exception_message_when_cause_is_not_ElasticSearchException_when_client_fails() {
- EsClient esClientMock = mock(EsClient.class);
- EsSection underTest = new EsSection(esClientMock);
- when(esClientMock.prepareClusterStats()).thenThrow(new RuntimeException("RuntimeException with cause not ES", new IllegalArgumentException("some cause message")));
-
- ProtobufSystemInfo.Section section = underTest.toProtobuf();
- assertThatAttributeIs(section, "State", "RuntimeException with cause not ES");
- }
-
- @Test
- public void attributes_displays_cause_message_when_cause_is_ElasticSearchException_when_client_fails() {
- EsClient esClientMock = mock(EsClient.class);
- EsSection underTest = new EsSection(esClientMock);
- when(esClientMock.prepareClusterStats()).thenThrow(new RuntimeException("RuntimeException with ES cause", new ElasticsearchException("some cause message")));
-
- ProtobufSystemInfo.Section section = underTest.toProtobuf();
- assertThatAttributeIs(section, "State", "some cause message");
- }
-}
--- /dev/null
+/*
+ * SonarQube
+ * Copyright (C) 2009-2017 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.elasticsearch.ElasticsearchException;
+import org.elasticsearch.cluster.health.ClusterHealthStatus;
+import org.junit.Rule;
+import org.junit.Test;
+import org.sonar.api.config.internal.MapSettings;
+import org.sonar.process.systeminfo.protobuf.ProtobufSystemInfo;
+import org.sonar.server.es.EsClient;
+import org.sonar.server.es.EsTester;
+import org.sonar.server.issue.index.IssueIndexDefinition;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+import static org.sonar.process.systeminfo.SystemInfoUtils.attribute;
+import static org.sonar.server.platform.monitoring.SystemInfoTesting.assertThatAttributeIs;
+
+public class EsStateSectionTest {
+
+ @Rule
+ public EsTester esTester = new EsTester(new IssueIndexDefinition(new MapSettings().asConfig()));
+
+ private EsStateSection underTest = new EsStateSection(esTester.client());
+
+ @Test
+ public void name() {
+ assertThat(underTest.toProtobuf().getName()).isEqualTo("Search State");
+ }
+
+ @Test
+ public void es_state() {
+ assertThatAttributeIs(underTest.toProtobuf(), "State", ClusterHealthStatus.GREEN.name());
+ }
+
+ @Test
+ public void node_attributes() {
+ ProtobufSystemInfo.Section section = underTest.toProtobuf();
+ assertThat(attribute(section, "Store Size")).isNotNull();
+ }
+
+ @Test
+ public void attributes_displays_exception_message_when_cause_null_when_client_fails() {
+ EsClient esClientMock = mock(EsClient.class);
+ EsStateSection underTest = new EsStateSection(esClientMock);
+ when(esClientMock.prepareClusterStats()).thenThrow(new RuntimeException("RuntimeException with no cause"));
+
+ ProtobufSystemInfo.Section section = underTest.toProtobuf();
+ assertThatAttributeIs(section, "State", "RuntimeException with no cause");
+ }
+
+ @Test
+ public void attributes_displays_exception_message_when_cause_is_not_ElasticSearchException_when_client_fails() {
+ EsClient esClientMock = mock(EsClient.class);
+ EsStateSection underTest = new EsStateSection(esClientMock);
+ when(esClientMock.prepareClusterStats()).thenThrow(new RuntimeException("RuntimeException with cause not ES", new IllegalArgumentException("some cause message")));
+
+ ProtobufSystemInfo.Section section = underTest.toProtobuf();
+ assertThatAttributeIs(section, "State", "RuntimeException with cause not ES");
+ }
+
+ @Test
+ public void attributes_displays_cause_message_when_cause_is_ElasticSearchException_when_client_fails() {
+ EsClient esClientMock = mock(EsClient.class);
+ EsStateSection underTest = new EsStateSection(esClientMock);
+ when(esClientMock.prepareClusterStats()).thenThrow(new RuntimeException("RuntimeException with ES cause", new ElasticsearchException("some cause message")));
+
+ ProtobufSystemInfo.Section section = underTest.toProtobuf();
+ assertThatAttributeIs(section, "State", "some cause message");
+ }
+}
--- /dev/null
+/*
+ * SonarQube
+ * Copyright (C) 2009-2017 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.elasticsearch.ElasticsearchException;
+import org.junit.Rule;
+import org.junit.Test;
+import org.sonar.api.config.internal.MapSettings;
+import org.sonar.process.systeminfo.protobuf.ProtobufSystemInfo;
+import org.sonar.server.es.EsClient;
+import org.sonar.server.es.EsTester;
+import org.sonar.server.issue.index.IssueIndexDefinition;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+import static org.sonar.process.systeminfo.SystemInfoUtils.attribute;
+import static org.sonar.server.platform.monitoring.SystemInfoTesting.assertThatAttributeIs;
+
+public class EsStatisticsSectionTest {
+
+ @Rule
+ public EsTester esTester = new EsTester(new IssueIndexDefinition(new MapSettings().asConfig()));
+
+ private EsStatisticsSection underTest = new EsStatisticsSection(esTester.client());
+
+ @Test
+ public void name() {
+ assertThat(underTest.toProtobuf().getName()).isEqualTo("Search Statistics");
+ }
+
+ @Test
+ public void index_attributes() {
+ ProtobufSystemInfo.Section section = underTest.toProtobuf();
+
+ // one index "issues"
+ assertThat(attribute(section, "Index issues - Docs").getLongValue()).isEqualTo(0L);
+ assertThat(attribute(section, "Index issues - Shards").getLongValue()).isGreaterThan(0);
+ assertThat(attribute(section, "Index issues - Store Size").getStringValue()).isNotNull();
+ }
+
+ @Test
+ public void attributes_displays_exception_message_when_cause_null_when_client_fails() {
+ EsClient esClientMock = mock(EsClient.class);
+ EsStatisticsSection underTest = new EsStatisticsSection(esClientMock);
+ when(esClientMock.prepareStats()).thenThrow(new RuntimeException("RuntimeException with no cause"));
+
+ ProtobufSystemInfo.Section section = underTest.toProtobuf();
+ assertThatAttributeIs(section, "Error", "RuntimeException with no cause");
+ }
+
+ @Test
+ public void attributes_displays_exception_message_when_cause_is_not_ElasticSearchException_when_client_fails() {
+ EsClient esClientMock = mock(EsClient.class);
+ EsStatisticsSection underTest = new EsStatisticsSection(esClientMock);
+ when(esClientMock.prepareStats()).thenThrow(new RuntimeException("RuntimeException with cause not ES", new IllegalArgumentException("some cause message")));
+
+ ProtobufSystemInfo.Section section = underTest.toProtobuf();
+ assertThatAttributeIs(section, "Error", "RuntimeException with cause not ES");
+ }
+
+ @Test
+ public void attributes_displays_cause_message_when_cause_is_ElasticSearchException_when_client_fails() {
+ EsClient esClientMock = mock(EsClient.class);
+ EsStatisticsSection underTest = new EsStatisticsSection(esClientMock);
+ when(esClientMock.prepareStats()).thenThrow(new RuntimeException("RuntimeException with ES cause", new ElasticsearchException("some cause message")));
+
+ ProtobufSystemInfo.Section section = underTest.toProtobuf();
+ assertThatAttributeIs(section, "Error", "some cause message");
+ }
+}
+++ /dev/null
-/*
- * SonarQube
- * Copyright (C) 2009-2017 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 java.io.File;
-import java.util.Optional;
-import org.apache.commons.io.FileUtils;
-import org.junit.Before;
-import org.junit.Rule;
-import org.junit.Test;
-import org.junit.rules.TemporaryFolder;
-import org.sonar.api.config.internal.MapSettings;
-import org.sonar.api.platform.Server;
-import org.sonar.api.security.SecurityRealm;
-import org.sonar.api.utils.log.LoggerLevel;
-import org.sonar.process.systeminfo.protobuf.ProtobufSystemInfo;
-import org.sonar.server.authentication.IdentityProviderRepositoryRule;
-import org.sonar.server.authentication.TestIdentityProvider;
-import org.sonar.server.health.Health;
-import org.sonar.server.health.TestStandaloneHealthChecker;
-import org.sonar.server.platform.ServerId;
-import org.sonar.server.platform.ServerIdLoader;
-import org.sonar.server.platform.ServerLogging;
-import org.sonar.server.user.SecurityRealmFactory;
-
-import static java.util.Arrays.asList;
-import static org.assertj.core.api.Assertions.assertThat;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.when;
-import static org.sonar.process.systeminfo.SystemInfoUtils.attribute;
-import static org.sonar.server.platform.monitoring.SystemInfoTesting.assertThatAttributeIs;
-
-public class SonarQubeSectionTest {
-
- private static final String SERVER_ID_PROPERTY = "Server ID";
- private static final String SERVER_ID_VALIDATED_PROPERTY = "Server ID validated";
-
- @Rule
- public TemporaryFolder temp = new TemporaryFolder();
-
- @Rule
- public IdentityProviderRepositoryRule identityProviderRepository = new IdentityProviderRepositoryRule();
-
- private MapSettings settings = new MapSettings();
- private Server server = mock(Server.class);
- private ServerIdLoader serverIdLoader = mock(ServerIdLoader.class);
- private ServerLogging serverLogging = mock(ServerLogging.class);
- private SecurityRealmFactory securityRealmFactory = mock(SecurityRealmFactory.class);
- private TestStandaloneHealthChecker healthChecker = new TestStandaloneHealthChecker();
-
- private SonarQubeSection underTest = new SonarQubeSection(settings.asConfig(), securityRealmFactory, identityProviderRepository, server,
- serverLogging, serverIdLoader, healthChecker);
-
- @Before
- public void setUp() throws Exception {
- when(serverLogging.getRootLoggerLevel()).thenReturn(LoggerLevel.DEBUG);
- when(serverIdLoader.getRaw()).thenReturn(Optional.empty());
- when(serverIdLoader.get()).thenReturn(Optional.empty());
- }
-
- @Test
- public void name_is_not_empty() {
- assertThat(underTest.name()).isNotEmpty();
- }
-
- @Test
- public void test_getServerId() {
- when(serverIdLoader.getRaw()).thenReturn(Optional.of("ABC"));
- assertThat(underTest.getServerId()).isEqualTo("ABC");
-
- when(serverIdLoader.getRaw()).thenReturn(Optional.empty());
- assertThat(underTest.getServerId()).isNull();
- }
-
- @Test
- public void attributes_contain_information_about_valid_server_id() {
- when(serverIdLoader.get()).thenReturn(Optional.of(new ServerId("ABC", true)));
-
- ProtobufSystemInfo.Section protobuf = underTest.toProtobuf();
- assertThatAttributeIs(protobuf, SERVER_ID_PROPERTY, "ABC");
- assertThatAttributeIs(protobuf, SERVER_ID_VALIDATED_PROPERTY, true);
- }
-
- @Test
- public void attributes_contain_information_about_non_valid_server_id() {
- when(serverIdLoader.get()).thenReturn(Optional.of(new ServerId("ABC", false)));
-
- ProtobufSystemInfo.Section protobuf = underTest.toProtobuf();
- assertThatAttributeIs(protobuf, SERVER_ID_PROPERTY, "ABC");
- assertThatAttributeIs(protobuf, SERVER_ID_VALIDATED_PROPERTY, false);
- }
-
- @Test
- public void attributes_do_not_contain_information_about_server_id_if_absent() {
- when(serverIdLoader.get()).thenReturn(Optional.empty());
-
- ProtobufSystemInfo.Section protobuf = underTest.toProtobuf();
- assertThat(attribute(protobuf, SERVER_ID_PROPERTY)).isNull();
- assertThat(attribute(protobuf, SERVER_ID_VALIDATED_PROPERTY)).isNull();
- }
-
- @Test
- public void official_distribution() throws Exception {
- File rootDir = temp.newFolder();
- FileUtils.write(new File(rootDir, SonarQubeSection.BRANDING_FILE_PATH), "1.2");
-
- when(server.getRootDir()).thenReturn(rootDir);
-
- ProtobufSystemInfo.Section protobuf = underTest.toProtobuf();
- assertThatAttributeIs(protobuf, "Official Distribution", true);
- }
-
- @Test
- public void not_an_official_distribution() throws Exception {
- File rootDir = temp.newFolder();
- // branding file is missing
- when(server.getRootDir()).thenReturn(rootDir);
-
- ProtobufSystemInfo.Section protobuf = underTest.toProtobuf();
- assertThatAttributeIs(protobuf, "Official Distribution", false);
- }
-
- @Test
- public void get_log_level() throws Exception {
- ProtobufSystemInfo.Section protobuf = underTest.toProtobuf();
- assertThatAttributeIs(protobuf, "Logs Level", "DEBUG");
- }
-
- @Test
- public void get_realm() throws Exception {
- SecurityRealm realm = mock(SecurityRealm.class);
- when(realm.getName()).thenReturn("LDAP");
- when(securityRealmFactory.getRealm()).thenReturn(realm);
-
- ProtobufSystemInfo.Section protobuf = underTest.toProtobuf();
- assertThatAttributeIs(protobuf, "External User Authentication", "LDAP");
- }
-
- @Test
- public void no_realm() throws Exception {
- when(securityRealmFactory.getRealm()).thenReturn(null);
-
- ProtobufSystemInfo.Section protobuf = underTest.toProtobuf();
- assertThat(attribute(protobuf, "External User Authentication")).isNull();
- }
-
- @Test
- public void get_enabled_identity_providers() throws Exception {
- identityProviderRepository.addIdentityProvider(new TestIdentityProvider()
- .setKey("github")
- .setName("GitHub")
- .setEnabled(true));
- identityProviderRepository.addIdentityProvider(new TestIdentityProvider()
- .setKey("bitbucket")
- .setName("Bitbucket")
- .setEnabled(true));
- identityProviderRepository.addIdentityProvider(new TestIdentityProvider()
- .setKey("disabled")
- .setName("Disabled")
- .setEnabled(false));
-
- ProtobufSystemInfo.Section protobuf = underTest.toProtobuf();
- assertThatAttributeIs(protobuf, "Accepted external identity providers", "Bitbucket, GitHub");
- }
-
- @Test
- public void get_enabled_identity_providers_allowing_users_to_signup() throws Exception {
- identityProviderRepository.addIdentityProvider(new TestIdentityProvider()
- .setKey("github")
- .setName("GitHub")
- .setEnabled(true)
- .setAllowsUsersToSignUp(true));
- identityProviderRepository.addIdentityProvider(new TestIdentityProvider()
- .setKey("bitbucket")
- .setName("Bitbucket")
- .setEnabled(true)
- .setAllowsUsersToSignUp(false));
- identityProviderRepository.addIdentityProvider(new TestIdentityProvider()
- .setKey("disabled")
- .setName("Disabled")
- .setEnabled(false)
- .setAllowsUsersToSignUp(true));
-
- ProtobufSystemInfo.Section protobuf = underTest.toProtobuf();
- assertThatAttributeIs(protobuf, "External identity providers whose users are allowed to sign themselves up", "GitHub");
- }
-
- @Test
- public void return_health() {
- healthChecker.setHealth(Health.newHealthCheckBuilder()
- .setStatus(Health.Status.YELLOW)
- .addCause("foo")
- .addCause("bar")
- .build());
-
- ProtobufSystemInfo.Section protobuf = underTest.toProtobuf();
- assertThatAttributeIs(protobuf, "Health", "YELLOW");
- SystemInfoTesting.assertThatAttributeHasOnlyValues(protobuf, "Health Causes", asList("foo", "bar"));
- }
-}
*/
package org.sonar.server.platform.monitoring;
+import java.io.File;
+import java.util.Optional;
+import org.apache.commons.io.FileUtils;
+import org.junit.Before;
+import org.junit.Rule;
import org.junit.Test;
+import org.junit.rules.TemporaryFolder;
+import org.sonar.api.config.internal.MapSettings;
+import org.sonar.api.platform.Server;
+import org.sonar.api.security.SecurityRealm;
+import org.sonar.api.utils.log.LoggerLevel;
import org.sonar.process.systeminfo.protobuf.ProtobufSystemInfo;
+import org.sonar.server.authentication.IdentityProviderRepositoryRule;
+import org.sonar.server.authentication.TestIdentityProvider;
+import org.sonar.server.health.Health;
+import org.sonar.server.health.TestStandaloneHealthChecker;
+import org.sonar.server.platform.ServerId;
+import org.sonar.server.platform.ServerIdLoader;
+import org.sonar.server.platform.ServerLogging;
+import org.sonar.server.user.SecurityRealmFactory;
+import static java.util.Arrays.asList;
import static org.assertj.core.api.Assertions.assertThat;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
import static org.sonar.process.systeminfo.SystemInfoUtils.attribute;
+import static org.sonar.server.platform.monitoring.SystemInfoTesting.assertThatAttributeIs;
public class SystemSectionTest {
- private SystemSection underTest = new SystemSection();
+ private static final String SERVER_ID_PROPERTY = "Server ID";
+ private static final String SERVER_ID_VALIDATED_PROPERTY = "Server ID validated";
+
+ @Rule
+ public TemporaryFolder temp = new TemporaryFolder();
+
+ @Rule
+ public IdentityProviderRepositoryRule identityProviderRepository = new IdentityProviderRepositoryRule();
+
+ private MapSettings settings = new MapSettings();
+ private Server server = mock(Server.class);
+ private ServerIdLoader serverIdLoader = mock(ServerIdLoader.class);
+ private ServerLogging serverLogging = mock(ServerLogging.class);
+ private SecurityRealmFactory securityRealmFactory = mock(SecurityRealmFactory.class);
+ private TestStandaloneHealthChecker healthChecker = new TestStandaloneHealthChecker();
+
+ private SystemSection underTest = new SystemSection(settings.asConfig(), securityRealmFactory, identityProviderRepository, server,
+ serverLogging, serverIdLoader, healthChecker);
+
+ @Before
+ public void setUp() throws Exception {
+ when(serverLogging.getRootLoggerLevel()).thenReturn(LoggerLevel.DEBUG);
+ when(serverIdLoader.getRaw()).thenReturn(Optional.empty());
+ when(serverIdLoader.get()).thenReturn(Optional.empty());
+ }
+
+ @Test
+ public void name_is_not_empty() {
+ assertThat(underTest.name()).isNotEmpty();
+ }
+
+ @Test
+ public void test_getServerId() {
+ when(serverIdLoader.getRaw()).thenReturn(Optional.of("ABC"));
+ assertThat(underTest.getServerId()).isEqualTo("ABC");
+
+ when(serverIdLoader.getRaw()).thenReturn(Optional.empty());
+ assertThat(underTest.getServerId()).isNull();
+ }
+
+ @Test
+ public void attributes_contain_information_about_valid_server_id() {
+ when(serverIdLoader.get()).thenReturn(Optional.of(new ServerId("ABC", true)));
+
+ ProtobufSystemInfo.Section protobuf = underTest.toProtobuf();
+ assertThatAttributeIs(protobuf, SERVER_ID_PROPERTY, "ABC");
+ assertThatAttributeIs(protobuf, SERVER_ID_VALIDATED_PROPERTY, true);
+ }
+
+ @Test
+ public void attributes_contain_information_about_non_valid_server_id() {
+ when(serverIdLoader.get()).thenReturn(Optional.of(new ServerId("ABC", false)));
+
+ ProtobufSystemInfo.Section protobuf = underTest.toProtobuf();
+ assertThatAttributeIs(protobuf, SERVER_ID_PROPERTY, "ABC");
+ assertThatAttributeIs(protobuf, SERVER_ID_VALIDATED_PROPERTY, false);
+ }
+
+ @Test
+ public void attributes_do_not_contain_information_about_server_id_if_absent() {
+ when(serverIdLoader.get()).thenReturn(Optional.empty());
+
+ ProtobufSystemInfo.Section protobuf = underTest.toProtobuf();
+ assertThat(attribute(protobuf, SERVER_ID_PROPERTY)).isNull();
+ assertThat(attribute(protobuf, SERVER_ID_VALIDATED_PROPERTY)).isNull();
+ }
@Test
- public void name() {
- assertThat(underTest.toProtobuf().getName()).isEqualTo("System");
+ public void official_distribution() throws Exception {
+ File rootDir = temp.newFolder();
+ FileUtils.write(new File(rootDir, SystemSection.BRANDING_FILE_PATH), "1.2");
+
+ when(server.getRootDir()).thenReturn(rootDir);
+
+ ProtobufSystemInfo.Section protobuf = underTest.toProtobuf();
+ assertThatAttributeIs(protobuf, "Official Distribution", true);
+ }
+
+ @Test
+ public void not_an_official_distribution() throws Exception {
+ File rootDir = temp.newFolder();
+ // branding file is missing
+ when(server.getRootDir()).thenReturn(rootDir);
+
+ ProtobufSystemInfo.Section protobuf = underTest.toProtobuf();
+ assertThatAttributeIs(protobuf, "Official Distribution", false);
+ }
+
+ @Test
+ public void get_log_level() throws Exception {
+ ProtobufSystemInfo.Section protobuf = underTest.toProtobuf();
+ assertThatAttributeIs(protobuf, "Logs Level", "DEBUG");
+ }
+
+ @Test
+ public void get_realm() throws Exception {
+ SecurityRealm realm = mock(SecurityRealm.class);
+ when(realm.getName()).thenReturn("LDAP");
+ when(securityRealmFactory.getRealm()).thenReturn(realm);
+
+ ProtobufSystemInfo.Section protobuf = underTest.toProtobuf();
+ assertThatAttributeIs(protobuf, "External User Authentication", "LDAP");
+ }
+
+ @Test
+ public void no_realm() throws Exception {
+ when(securityRealmFactory.getRealm()).thenReturn(null);
+
+ ProtobufSystemInfo.Section protobuf = underTest.toProtobuf();
+ assertThat(attribute(protobuf, "External User Authentication")).isNull();
+ }
+
+ @Test
+ public void get_enabled_identity_providers() throws Exception {
+ identityProviderRepository.addIdentityProvider(new TestIdentityProvider()
+ .setKey("github")
+ .setName("GitHub")
+ .setEnabled(true));
+ identityProviderRepository.addIdentityProvider(new TestIdentityProvider()
+ .setKey("bitbucket")
+ .setName("Bitbucket")
+ .setEnabled(true));
+ identityProviderRepository.addIdentityProvider(new TestIdentityProvider()
+ .setKey("disabled")
+ .setName("Disabled")
+ .setEnabled(false));
+
+ ProtobufSystemInfo.Section protobuf = underTest.toProtobuf();
+ assertThatAttributeIs(protobuf, "Accepted external identity providers", "Bitbucket, GitHub");
+ }
+
+ @Test
+ public void get_enabled_identity_providers_allowing_users_to_signup() throws Exception {
+ identityProviderRepository.addIdentityProvider(new TestIdentityProvider()
+ .setKey("github")
+ .setName("GitHub")
+ .setEnabled(true)
+ .setAllowsUsersToSignUp(true));
+ identityProviderRepository.addIdentityProvider(new TestIdentityProvider()
+ .setKey("bitbucket")
+ .setName("Bitbucket")
+ .setEnabled(true)
+ .setAllowsUsersToSignUp(false));
+ identityProviderRepository.addIdentityProvider(new TestIdentityProvider()
+ .setKey("disabled")
+ .setName("Disabled")
+ .setEnabled(false)
+ .setAllowsUsersToSignUp(true));
+
+ ProtobufSystemInfo.Section protobuf = underTest.toProtobuf();
+ assertThatAttributeIs(protobuf, "External identity providers whose users are allowed to sign themselves up", "GitHub");
}
@Test
- public void system_properties() {
- ProtobufSystemInfo.Section section = underTest.toProtobuf();
+ public void return_health() {
+ healthChecker.setHealth(Health.newHealthCheckBuilder()
+ .setStatus(Health.Status.YELLOW)
+ .addCause("foo")
+ .addCause("bar")
+ .build());
- assertThat(attribute(section, "System Date").getStringValue()).isNotEmpty();
- assertThat(attribute(section, "Processors").getLongValue()).isGreaterThan(0);
+ ProtobufSystemInfo.Section protobuf = underTest.toProtobuf();
+ assertThatAttributeIs(protobuf, "Health", "YELLOW");
+ SystemInfoTesting.assertThatAttributeHasOnlyValues(protobuf, "Health Causes", asList("foo", "bar"));
}
}
Name: string;
Health: HealthType;
'Health Causes': HealthCause[];
+ 'Logs Level': string;
}
export interface SysInfo extends SysValueObject {
Cluster: boolean;
Health: HealthType;
'Health Causes': HealthCause[];
- 'Application Nodes': NodeInfo[];
- 'Search Nodes': NodeInfo[];
}
export function setLogLevel(level: string): Promise<void | Response> {
+++ /dev/null
-/*
- * SonarQube
- * Copyright (C) 2009-2017 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.
- */
-import React from 'react';
-import { shallow } from 'enzyme';
-import ItemValue from '../item-value';
-import ItemBoolean from '../item-boolean';
-import ItemObject from '../item-object';
-import ItemLogLevel from '../item-log-level';
-
-describe('Item Value', () => {
- it('should render string', () => {
- const result = shallow(<ItemValue value="/some/path/as/an/example" />);
- expect(result.find('code').text()).toBe('/some/path/as/an/example');
- });
-});
-
-describe('ItemBoolean', () => {
- it('should render `true`', () => {
- const result = shallow(<ItemBoolean value={true} />);
- expect(result.find('.icon-check').length).toBe(1);
- });
-
- it('should render `false`', () => {
- const result = shallow(<ItemBoolean value={false} />);
- expect(result.find('.icon-delete').length).toBe(1);
- });
-});
-
-describe('ItemObject', () => {
- it('should render object', () => {
- const result = shallow(<ItemObject value={{ name: 'Java', version: '3.2' }} />);
- expect(result.find('table').length).toBe(1);
- expect(result.find('tr').length).toBe(2);
- });
-
- it('should render `true` inside object', () => {
- const result = shallow(<ItemObject value={{ isCool: true }} />);
- const itemValue = result.find(ItemValue);
- expect(itemValue.length).toBe(1);
- expect(itemValue.prop('value')).toBe(true);
- });
-
- it('should render object inside object', () => {
- const result = shallow(
- <ItemObject value={{ users: { docs: 1, shards: 5 }, tests: { docs: 68, shards: 5 } }} />
- );
- expect(result.find(ItemValue).length).toBe(2);
- expect(
- result
- .find(ItemValue)
- .at(0)
- .prop('value')
- ).toEqual({ docs: 1, shards: 5 });
- expect(
- result
- .find(ItemValue)
- .at(1)
- .prop('value')
- ).toEqual({ docs: 68, shards: 5 });
- });
-});
-
-describe('Log Level', () => {
- it('should render select box', () => {
- const result = shallow(<ItemLogLevel value="INFO" />);
- expect(result.find('select').length).toBe(1);
- expect(result.find('option').length).toBe(3);
- });
-
- it('should set initial value', () => {
- const result = shallow(<ItemLogLevel value="DEBUG" />);
- expect(result.find('select').prop('value')).toBe('DEBUG');
- });
-
- it('should render warning', () => {
- const result = shallow(<ItemLogLevel value="DEBUG" />);
- expect(result.find('.alert').length).toBe(1);
- });
-
- it('should not render warning', () => {
- const result = shallow(<ItemLogLevel value="INFO" />);
- expect(result.find('.alert').length).toBe(0);
- });
-});
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
-import * as utils from '../utils';
+import * as u from '../utils';
describe('parseQuery', () => {
it('should correctly parse the expand array', () => {
- expect(utils.parseQuery({})).toEqual({ expandedCards: [] });
- expect(utils.parseQuery({ expand: 'foo,bar' })).toEqual({ expandedCards: ['foo', 'bar'] });
+ expect(u.parseQuery({})).toEqual({ expandedCards: [] });
+ expect(u.parseQuery({ expand: 'foo,bar' })).toEqual({ expandedCards: ['foo', 'bar'] });
});
});
describe('serializeQuery', () => {
it('should correctly serialize the expand array', () => {
- expect(utils.serializeQuery({ expandedCards: [] })).toEqual({});
- expect(utils.serializeQuery({ expandedCards: ['foo', 'bar'] })).toEqual({ expand: 'foo,bar' });
+ expect(u.serializeQuery({ expandedCards: [] })).toEqual({});
+ expect(u.serializeQuery({ expandedCards: ['foo', 'bar'] })).toEqual({ expand: 'foo,bar' });
});
});
describe('groupSections', () => {
it('should correctly group the root field into a main section', () => {
- expect(utils.groupSections({ foo: 'Foo', bar: 3, baz: { a: 'a' } })).toEqual({
+ expect(u.groupSections({ foo: 'Foo', bar: 3, baz: { a: 'a' } })).toEqual({
mainSection: { foo: 'Foo', bar: 3 },
sections: { baz: { a: 'a' } }
});
});
});
+
+describe('getSystemLogsLevel', () => {
+ it('should correctly return log level for standalone mode', () => {
+ expect(u.getSystemLogsLevel({ 'Logs Level': 'FOO' } as u.StandaloneSysInfo)).toBe('FOO');
+ expect(u.getSystemLogsLevel({} as u.StandaloneSysInfo)).toBe('INFO');
+ expect(u.getSystemLogsLevel()).toBe('INFO');
+ });
+ it('should return the worst log level for cluster mode', () => {
+ expect(
+ u.getSystemLogsLevel({
+ Cluster: true,
+ 'Application Nodes': [{ 'Logs Level': 'INFO' }, { 'Logs Level': 'DEBUG' }]
+ } as u.ClusterSysInfo)
+ ).toBe('DEBUG');
+ });
+});
import Helmet from 'react-helmet';
import ClusterSysInfos from './ClusterSysInfos';
import PageHeader from './PageHeader';
-import StandAloneSysInfos from './StandAloneSysInfos';
+import StandaloneSysInfos from './StandaloneSysInfos';
import { translate } from '../../../helpers/l10n';
import { getSystemInfo, SysInfo } from '../../../api/system';
-import { isCluster, parseQuery, Query, serializeQuery } from '../utils';
+import {
+ ClusterSysInfo,
+ getSystemLogsLevel,
+ isCluster,
+ parseQuery,
+ Query,
+ serializeQuery,
+ StandaloneSysInfo
+} from '../utils';
import { RawQuery } from '../../../helpers/query';
import '../styles.css';
updateQuery = (newQuery: Query) => {
const query = serializeQuery({ ...parseQuery(this.props.location.query), ...newQuery });
- this.context.router.push({ pathname: this.props.location.pathname, query });
+ this.context.router.replace({ pathname: this.props.location.pathname, query });
};
renderSysInfo() {
if (isCluster(sysInfoData)) {
return (
<ClusterSysInfos
- sysInfoData={sysInfoData}
expandedCards={query.expandedCards}
+ sysInfoData={sysInfoData as ClusterSysInfo}
toggleCard={this.toggleSysInfoCards}
/>
);
}
- return <StandAloneSysInfos sysInfoData={sysInfoData} />;
+ return (
+ <StandaloneSysInfos
+ expandedCards={query.expandedCards}
+ sysInfoData={sysInfoData as StandaloneSysInfo}
+ toggleCard={this.toggleSysInfoCards}
+ />
+ );
}
render() {
const { loading, sysInfoData } = this.state;
- // TODO Correctly get logLevel, we are not sure yet how we want to do it for cluster mode
return (
<div className="page page-limited">
<Helmet title={translate('system_info.page')} />
<PageHeader
loading={loading}
isCluster={isCluster(sysInfoData)}
- logLevel="INFO"
+ logLevel={getSystemLogsLevel(sysInfoData)}
showActions={sysInfoData != undefined}
+ onLogLevelChange={this.fetchSysInfo}
/>
{this.renderSysInfo()}
</div>
import Modal from 'react-modal';
import { setLogLevel } from '../../../api/system';
import { translate } from '../../../helpers/l10n';
+import { LOGS_LEVELS } from '../utils';
interface Props {
infoMsg: string;
updating: boolean;
}
-const LOG_LEVELS = ['INFO', 'DEBUG', 'TRACE'];
-
export default class ChangeLogLevelForm extends React.PureComponent<Props, State> {
constructor(props: Props) {
super(props);
<h2>{header}</h2>
</div>
<div className="modal-body">
- {LOG_LEVELS.map(level => (
+ {LOGS_LEVELS.map(level => (
<p key={level} className="spacer-bottom">
<input
+ id={`loglevel-${level}`}
type="radio"
className="spacer-right text-middle"
name="system.log_levels"
checked={level === newLevel}
onChange={this.handleLevelChange}
/>
- {level}
+ <label htmlFor={`loglevel-${level}`}>{level}</label>
</p>
))}
<div className="alert alert-info spacer-top">{this.props.infoMsg}</div>
import HealthCard from './info-items/HealthCard';
import { translate } from '../../../helpers/l10n';
import {
+ ClusterSysInfo,
getAppNodes,
getHealth,
getHealthCauses,
- getMainCardSection,
+ getClusterMainCardSection,
getNodeName,
getSearchNodes,
ignoreInfoFields
} from '../utils';
-import { SysInfo } from '../../../api/system';
interface Props {
expandedCards: string[];
- sysInfoData: SysInfo;
+ sysInfoData: ClusterSysInfo;
toggleCard: (toggledCard: string) => void;
}
name={mainCardName}
onClick={toggleCard}
open={expandedCards.includes(mainCardName)}
- sysInfoData={ignoreInfoFields(getMainCardSection(sysInfoData))}
+ sysInfoData={ignoreInfoFields(getClusterMainCardSection(sysInfoData))}
/>
<li className="note system-info-health-title">
{translate('system.application_nodes_title')}
canRestart: boolean;
cluster: boolean;
logLevel: string;
+ onLogLevelChange: () => void;
}
interface State {
constructor(props: Props) {
super(props);
this.state = {
+ logLevel: props.logLevel,
openLogsLevelForm: false,
- openRestartForm: false,
- logLevel: props.logLevel
+ openRestartForm: false
};
}
handleLogsLevelChange = (logLevel: string) => {
this.setState({ logLevel });
+ this.props.onLogLevelChange();
this.handleLogsLevelClose();
};
loading: boolean;
logLevel: string;
showActions: boolean;
+ onLogLevelChange: () => void;
}
-export default function PageHeader({ isCluster, loading, logLevel, showActions }: Props) {
+export default function PageHeader(props: Props) {
return (
<header className="page-header">
<h1 className="page-title">{translate('system_info.page')}</h1>
- {showActions && (
+ {props.showActions && (
<PageActions
- canDownloadLogs={!isCluster}
- canRestart={!isCluster}
- cluster={isCluster}
- logLevel={logLevel}
+ canDownloadLogs={!props.isCluster}
+ canRestart={!props.isCluster}
+ cluster={props.isCluster}
+ logLevel={props.logLevel}
+ onLogLevelChange={props.onLogLevelChange}
/>
)}
- {loading && (
+ {props.loading && (
<div className="page-actions">
<i className="spinner" />
</div>
+++ /dev/null
-/*
- * SonarQube
- * Copyright (C) 2009-2017 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.
- */
-import * as React from 'react';
-
-interface Props {
- sysInfoData: object;
-}
-
-export default class StandAloneSysInfos extends React.PureComponent<Props> {
- render() {
- return <div>StandAloneSysInfos</div>;
- }
-}
--- /dev/null
+/*
+ * SonarQube
+ * Copyright (C) 2009-2017 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.
+ */
+import * as React from 'react';
+import { map } from 'lodash';
+import HealthCard from './info-items/HealthCard';
+import {
+ getHealth,
+ getHealthCauses,
+ getStandaloneMainSections,
+ getStandaloneSecondarySections,
+ ignoreInfoFields,
+ StandaloneSysInfo
+} from '../utils';
+
+interface Props {
+ expandedCards: string[];
+ sysInfoData: StandaloneSysInfo;
+ toggleCard: (toggledCard: string) => void;
+}
+
+export default function StandAloneSysInfos({ expandedCards, sysInfoData, toggleCard }: Props) {
+ const mainCardName = 'System';
+ return (
+ <ul>
+ <HealthCard
+ biggerHealth={true}
+ health={getHealth(sysInfoData)}
+ healthCauses={getHealthCauses(sysInfoData)}
+ name={mainCardName}
+ onClick={toggleCard}
+ open={expandedCards.includes(mainCardName)}
+ sysInfoData={ignoreInfoFields(getStandaloneMainSections(sysInfoData))}
+ />
+ {map(getStandaloneSecondarySections(sysInfoData), (section, name) => (
+ <HealthCard
+ key={name}
+ name={name}
+ onClick={toggleCard}
+ open={expandedCards.includes(name)}
+ sysInfoData={ignoreInfoFields(section)}
+ />
+ ))}
+ </ul>
+ );
+}
import * as React from 'react';
import { shallow } from 'enzyme';
import ClusterSysInfos from '../ClusterSysInfos';
-import { HealthType, SysInfo } from '../../../../api/system';
+import { HealthType } from '../../../../api/system';
+import { ClusterSysInfo } from '../../utils';
-const sysInfoData: SysInfo = {
+const sysInfoData: ClusterSysInfo = {
Cluster: true,
Health: HealthType.RED,
Name: 'Foo',
'Health Causes': [{ message: 'Database down' }],
- 'Application Nodes': [{ Name: 'Bar', Health: HealthType.GREEN, 'Health Causes': [] }],
- 'Search Nodes': [{ Name: 'Baz', Health: HealthType.YELLOW, 'Health Causes': [] }]
+ 'Application Nodes': [
+ { Name: 'Bar', Health: HealthType.GREEN, 'Health Causes': [], 'Logs Level': 'INFO' }
+ ],
+ 'Search Nodes': [
+ { Name: 'Baz', Health: HealthType.YELLOW, 'Health Causes': [], 'Logs Level': 'INFO' }
+ ]
};
it('should render correctly', () => {
+ expect(
+ getWrapper({
+ sysInfoData: {
+ ...sysInfoData,
+ 'Application Nodes': [
+ { Name: 'Foo', Health: HealthType.GREEN, 'Health Causes': [], 'Logs Level': 'INFO' },
+ { Name: 'Bar', Health: HealthType.RED, 'Health Causes': [], 'Logs Level': 'DEBUG' },
+ { Name: 'Baz', Health: HealthType.YELLOW, 'Health Causes': [], 'Logs Level': 'TRACE' }
+ ]
+ }
+ }).find('HealthCard')
+ ).toHaveLength(5);
+});
+
+it('should support more than two nodes', () => {
expect(getWrapper()).toMatchSnapshot();
});
it('should render correctly', () => {
expect(
shallow(
- <PageActions canDownloadLogs={true} canRestart={true} cluster={false} logLevel="INFO" />
+ <PageActions
+ canDownloadLogs={true}
+ canRestart={true}
+ cluster={false}
+ logLevel="INFO"
+ onLogLevelChange={() => {}}
+ />
)
).toMatchSnapshot();
});
it('should render without restart and log download', () => {
expect(
shallow(
- <PageActions canDownloadLogs={false} canRestart={false} cluster={true} logLevel="INFO" />
+ <PageActions
+ canDownloadLogs={false}
+ canRestart={false}
+ cluster={true}
+ logLevel="INFO"
+ onLogLevelChange={() => {}}
+ />
)
).toMatchSnapshot();
});
it('should open restart modal', () => {
const wrapper = shallow(
- <PageActions canDownloadLogs={true} canRestart={true} cluster={false} logLevel="INFO" />
+ <PageActions
+ canDownloadLogs={true}
+ canRestart={true}
+ cluster={false}
+ logLevel="INFO"
+ onLogLevelChange={() => {}}
+ />
);
click(wrapper.find('#restart-server-button'));
expect(wrapper.find('RestartForm')).toHaveLength(1);
it('should open change log level modal', () => {
const wrapper = shallow(
- <PageActions canDownloadLogs={true} canRestart={true} cluster={false} logLevel="INFO" />
+ <PageActions
+ canDownloadLogs={true}
+ canRestart={true}
+ cluster={false}
+ logLevel="INFO"
+ onLogLevelChange={() => {}}
+ />
);
click(wrapper.find('#edit-logs-level-button'));
expect(wrapper.find('ChangeLogLevelForm')).toHaveLength(1);
it('should render correctly', () => {
expect(
- shallow(<PageHeader isCluster={true} loading={false} logLevel="INFO" showActions={true} />)
+ shallow(
+ <PageHeader
+ isCluster={true}
+ loading={false}
+ logLevel="INFO"
+ showActions={true}
+ onLogLevelChange={() => {}}
+ />
+ )
).toMatchSnapshot();
});
it('should show a loading spinner and no actions', () => {
expect(
- shallow(<PageHeader isCluster={true} loading={true} logLevel="INFO" showActions={false} />)
+ shallow(
+ <PageHeader
+ isCluster={true}
+ loading={true}
+ logLevel="INFO"
+ showActions={false}
+ onLogLevelChange={() => {}}
+ />
+ )
).toMatchSnapshot();
});
--- /dev/null
+/*
+ * SonarQube
+ * Copyright (C) 2009-2017 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.
+ */
+import * as React from 'react';
+import { shallow } from 'enzyme';
+import StandaloneSysInfos from '../StandaloneSysInfos';
+import { HealthType } from '../../../../api/system';
+import { StandaloneSysInfo } from '../../utils';
+
+const sysInfoData: StandaloneSysInfo = {
+ Cluster: true,
+ Health: HealthType.RED,
+ 'Logs Level': 'DEBUG',
+ Name: 'Foo',
+ 'Health Causes': [{ message: 'Database down' }],
+ 'Web JVM': { 'Max Memory': '2Gb' },
+ 'Compute Engine': { Pending: 4 },
+ Elasticsearch: { 'Number of Nodes': 1 }
+};
+
+it('should render correctly', () => {
+ expect(getWrapper()).toMatchSnapshot();
+});
+
+function getWrapper(props = {}) {
+ return shallow(
+ <StandaloneSysInfos
+ expandedCards={['Compute Engine', 'Foo']}
+ sysInfoData={sysInfoData}
+ toggleCard={() => {}}
+ {...props}
+ />
+ );
+}
<input
checked={false}
className="spacer-right text-middle"
+ id="loglevel-INFO"
name="system.log_levels"
onChange={[Function]}
type="radio"
value="INFO"
/>
- INFO
+ <label
+ htmlFor="loglevel-INFO"
+ >
+ INFO
+ </label>
</p>
<p
className="spacer-bottom"
<input
checked={true}
className="spacer-right text-middle"
+ id="loglevel-DEBUG"
name="system.log_levels"
onChange={[Function]}
type="radio"
value="DEBUG"
/>
- DEBUG
+ <label
+ htmlFor="loglevel-DEBUG"
+ >
+ DEBUG
+ </label>
</p>
<p
className="spacer-bottom"
<input
checked={false}
className="spacer-right text-middle"
+ id="loglevel-TRACE"
name="system.log_levels"
onChange={[Function]}
type="radio"
value="TRACE"
/>
- TRACE
+ <label
+ htmlFor="loglevel-TRACE"
+ >
+ TRACE
+ </label>
</p>
<div
className="alert alert-info spacer-top"
<input
checked={true}
className="spacer-right text-middle"
+ id="loglevel-INFO"
name="system.log_levels"
onChange={[Function]}
type="radio"
value="INFO"
/>
- INFO
+ <label
+ htmlFor="loglevel-INFO"
+ >
+ INFO
+ </label>
</p>
<p
className="spacer-bottom"
<input
checked={false}
className="spacer-right text-middle"
+ id="loglevel-DEBUG"
name="system.log_levels"
onChange={[Function]}
type="radio"
value="DEBUG"
/>
- DEBUG
+ <label
+ htmlFor="loglevel-DEBUG"
+ >
+ DEBUG
+ </label>
</p>
<p
className="spacer-bottom"
<input
checked={false}
className="spacer-right text-middle"
+ id="loglevel-TRACE"
name="system.log_levels"
onChange={[Function]}
type="radio"
value="TRACE"
/>
- TRACE
+ <label
+ htmlFor="loglevel-TRACE"
+ >
+ TRACE
+ </label>
</p>
<div
className="alert alert-info spacer-top"
// Jest Snapshot v1, https://goo.gl/fbAQLP
-exports[`should render correctly 1`] = `
+exports[`should support more than two nodes 1`] = `
<ul>
<HealthCard
biggerHealth={true}
open={false}
sysInfoData={
Object {
+ "Logs Level": "INFO",
"Name": "Bar",
}
}
open={false}
sysInfoData={
Object {
+ "Logs Level": "INFO",
"Name": "Baz",
}
}
canRestart={false}
cluster={true}
logLevel="INFO"
+ onLogLevelChange={[Function]}
/>
</header>
`;
--- /dev/null
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[`should render correctly 1`] = `
+<ul>
+ <HealthCard
+ biggerHealth={true}
+ health="RED"
+ healthCauses={
+ Array [
+ Object {
+ "message": "Database down",
+ },
+ ]
+ }
+ name="System"
+ onClick={[Function]}
+ open={false}
+ sysInfoData={
+ Object {
+ "Logs Level": "DEBUG",
+ "Name": "Foo",
+ }
+ }
+ />
+ <HealthCard
+ name="Web"
+ onClick={[Function]}
+ open={false}
+ sysInfoData={
+ Object {
+ "Web Database Connectivity": undefined,
+ "Web JVM": Object {
+ "Max Memory": "2Gb",
+ },
+ "Web JVM Properties": undefined,
+ }
+ }
+ />
+ <HealthCard
+ name="Compute Engine"
+ onClick={[Function]}
+ open={true}
+ sysInfoData={
+ Object {
+ "Compute Engine JVM": undefined,
+ "Compute Engine JVM Properties": undefined,
+ "Pending": 4,
+ }
+ }
+ />
+ <HealthCard
+ name="Search"
+ onClick={[Function]}
+ open={false}
+ sysInfoData={
+ Object {
+ "Elasticsearch": Object {
+ "Number of Nodes": 1,
+ },
+ "Search JVM": undefined,
+ "Search JVM Properties": undefined,
+ }
+ }
+ />
+</ul>
+`;
import OpenCloseIcon from '../../../../components/icons-components/OpenCloseIcon';
import Section from './Section';
import { HealthType, HealthCause, SysValueObject } from '../../../../api/system';
-import { groupSections } from '../../utils';
+import { LOGS_LEVELS, groupSections, getLogsLevel } from '../../utils';
+import { translate } from '../../../../helpers/l10n';
interface Props {
biggerHealth?: boolean;
- health: HealthType;
- healthCauses: HealthCause[];
+ health?: HealthType;
+ healthCauses?: HealthCause[];
onClick: (toggledCard: string) => void;
open: boolean;
name: string;
onDetailLeave = () => this.setState({ hoveringDetail: false });
render() {
- const { open, sysInfoData } = this.props;
+ const { health, open, sysInfoData } = this.props;
const { mainSection, sections } = groupSections(sysInfoData);
const showFields = open && mainSection && Object.keys(mainSection).length > 0;
const showSections = open && sections;
+ const logLevel = getLogsLevel(sysInfoData);
+ const showLogLevelWarning = logLevel && logLevel !== LOGS_LEVELS[0];
return (
<li
className={classNames('boxed-group system-info-health-card', {
<OpenCloseIcon className="little-spacer-right" open={open} />
{this.props.name}
</span>
- <HealthItem
- className={classNames('pull-right', { 'big-dot': this.props.biggerHealth })}
- health={this.props.health}
- healthCauses={this.props.healthCauses}
- />
+ {health && (
+ <HealthItem
+ biggerHealth={this.props.biggerHealth}
+ className="pull-right spacer-left"
+ health={health}
+ healthCauses={this.props.healthCauses}
+ />
+ )}
+ {showLogLevelWarning && (
+ <span className="pull-right alert alert-danger">
+ {translate('system.log_level.warning.short')}
+ </span>
+ )}
</div>
{open && (
<div
import * as React from 'react';
import * as classNames from 'classnames';
import HealthCauseItem from './HealthCauseItem';
+import StatusIndicator from '../../../../components/common/StatusIndicator';
import { HealthType, HealthCause } from '../../../../api/system';
interface Props {
+ biggerHealth?: boolean;
className?: string;
health: HealthType;
healthCauses?: HealthCause[];
}
-export default function HealthItem({ className, health, healthCauses }: Props) {
+export default function HealthItem({ biggerHealth, className, health, healthCauses }: Props) {
const hasHealthCauses = healthCauses && healthCauses.length > 0 && health !== HealthType.GREEN;
return (
<div className={classNames('system-info-health-info', className)}>
healthCauses!.map((cause, idx) => (
<HealthCauseItem key={idx} className="spacer-right" health={health} healthCause={cause} />
))}
- <span className={classNames('system-info-health-dot', health)} />
+ <StatusIndicator color={health.toLowerCase()} size={biggerHealth ? 'big' : undefined} />
</div>
);
}
expect(getShallowWrapper({ open: true, sysInfoData })).toMatchSnapshot();
});
+it('should display the log level alert', () => {
+ expect(
+ getShallowWrapper({ sysInfoData: { 'Logs Level': 'DEBUG' } }).find('.alert')
+ ).toMatchSnapshot();
+});
+
function getShallowWrapper(props = {}) {
return shallow(
<HealthCard
it('should render correctly', () => {
expect(
- shallow(<HealthItem health={HealthType.RED} healthCauses={[{ message: 'foo' }]} />)
+ shallow(
+ <HealthItem biggerHealth={true} health={HealthType.RED} healthCauses={[{ message: 'foo' }]} />
+ )
).toMatchSnapshot();
});
// Jest Snapshot v1, https://goo.gl/fbAQLP
+exports[`should display the log level alert 1`] = `
+<span
+ className="pull-right alert alert-danger"
+>
+ system.log_level.warning.short
+</span>
+`;
+
exports[`should display the sysinfo detail 1`] = `
<li
className="boxed-group system-info-health-card"
Foobar
</span>
<HealthItem
- className="pull-right big-dot"
+ biggerHealth={true}
+ className="pull-right spacer-left"
health="RED"
healthCauses={
Array [
Foobar
</span>
<HealthItem
- className="pull-right"
+ biggerHealth={false}
+ className="pull-right spacer-left"
health="RED"
healthCauses={
Array [
Foobar
</span>
<HealthItem
- className="pull-right"
+ biggerHealth={false}
+ className="pull-right spacer-left"
health="RED"
healthCauses={
Array [
<div
className="system-info-health-info"
>
- <span
- className="system-info-health-dot GREEN"
+ <StatusIndicator
+ color="green"
/>
</div>
`;
<div
className="system-info-health-info"
>
- <span
- className="system-info-health-dot YELLOW"
+ <StatusIndicator
+ color="yellow"
/>
</div>
`;
}
}
/>
- <span
- className="system-info-health-dot RED"
+ <StatusIndicator
+ color="red"
+ size="big"
/>
</div>
`;
}
}
/>
- <span
- className="system-info-health-dot YELLOW"
+ <StatusIndicator
+ color="yellow"
/>
</div>
`;
+++ /dev/null
-/*
- * SonarQube
- * Copyright (C) 2009-2017 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.
- */
-import React from 'react';
-
-export default class ItemBoolean extends React.PureComponent {
- render() {
- if (this.props.value) {
- return <i className="icon-check" />;
- } else {
- return <i className="icon-delete" />;
- }
- }
-}
+++ /dev/null
-/*
- * SonarQube
- * Copyright (C) 2009-2017 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.
- */
-import React from 'react';
-import { setLogLevel } from '../../api/system';
-import { translate } from '../../helpers/l10n';
-
-const LOG_LEVELS = ['INFO', 'DEBUG', 'TRACE'];
-
-export default class ItemLogLevel extends React.PureComponent {
- constructor(props) {
- super(props);
- this.state = { level: props.value };
- }
-
- onChange = () => {
- const newValue = this.refs.select.value;
- setLogLevel(newValue).then(() => {
- this.setState({ level: newValue });
- });
- };
-
- render() {
- const options = LOG_LEVELS.map(level => (
- <option key={level} value={level}>
- {level}
- </option>
- ));
- const warning =
- this.state.level !== 'INFO' ? (
- <div className="alert alert-danger spacer-top" style={{ wordBreak: 'normal' }}>
- {translate('system.log_level.warning')}
- </div>
- ) : null;
- return (
- <div>
- <select ref="select" onChange={this.onChange} value={this.state.level}>
- {options}
- </select>
- {warning}
- </div>
- );
- }
-}
+++ /dev/null
-/*
- * SonarQube
- * Copyright (C) 2009-2017 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.
- */
-import React from 'react';
-import ItemValue from './item-value';
-
-export default class ItemObject extends React.PureComponent {
- render() {
- const rows = Object.keys(this.props.value).map(key => {
- return (
- <tr key={key}>
- <td className="thin nowrap">{key}</td>
- <td>
- <ItemValue value={this.props.value[key]} />
- </td>
- </tr>
- );
- });
- return (
- <table className="data">
- <tbody>{rows}</tbody>
- </table>
- );
- }
-}
+++ /dev/null
-/*
- * SonarQube
- * Copyright (C) 2009-2017 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.
- */
-import React from 'react';
-import ItemBoolean from './item-boolean';
-import ItemObject from './item-object';
-import ItemLogLevel from './item-log-level';
-
-export default class ItemValue extends React.PureComponent {
- render() {
- if (this.props.name === 'Logs Level') {
- return <ItemLogLevel value={this.props.value} />;
- }
-
- const rawValue = this.props.value;
- let formattedValue;
- switch (typeof this.props.value) {
- case 'boolean':
- formattedValue = <ItemBoolean value={rawValue} />;
- break;
- case 'object':
- formattedValue = <ItemObject value={rawValue} />;
- break;
- default:
- formattedValue = <code>{rawValue}</code>;
- }
- return formattedValue;
- }
-}
+++ /dev/null
-/*
- * SonarQube
- * Copyright (C) 2009-2017 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.
- */
-import React from 'react';
-import Helmet from 'react-helmet';
-import { sortBy } from 'lodash';
-import { getSystemInfo } from '../../api/system';
-import Section from './section';
-import { translate } from '../../helpers/l10n';
-import RestartForm from '../../components/common/RestartForm';
-
-const SECTIONS_ORDER = [
- 'SonarQube',
- 'Database',
- 'System',
- 'Elasticsearch State',
- 'Elasticsearch',
- 'Compute Engine Tasks',
- 'Compute Engine State',
- 'Compute Engine Database Connection',
- 'JvmProperties'
-];
-
-export default class Main extends React.PureComponent {
- state = { openRestartForm: false };
-
- componentDidMount() {
- getSystemInfo().then(info => this.setState({ sections: this.parseSections(info) }));
- }
-
- parseSections = data => {
- const sections = Object.keys(data).map(section => {
- return { name: section, items: this.parseItems(data[section]) };
- });
- return this.orderSections(sections);
- };
-
- orderSections = sections => sortBy(sections, section => SECTIONS_ORDER.indexOf(section.name));
-
- parseItems = data => {
- const items = Object.keys(data).map(item => {
- return { name: item, value: data[item] };
- });
- return this.orderItems(items);
- };
-
- orderItems = items => sortBy(items, 'name');
-
- handleServerRestartOpen = () => this.setState({ openRestartForm: true });
- handleServerRestartClose = () => this.setState({ openRestartForm: false });
-
- render() {
- let sections = null;
- if (this.state && this.state.sections) {
- sections = this.state.sections
- .filter(section => SECTIONS_ORDER.indexOf(section.name) >= 0)
- .map(section => (
- <Section key={section.name} section={section.name} items={section.items} />
- ));
- }
-
- return (
- <div className="page page-limited">
- <Helmet title={translate('system_info.page')} />
- <header className="page-header">
- <h1 className="page-title">{translate('system_info.page')}</h1>
- <div className="page-actions">
- <a href={window.baseUrl + '/api/system/info'} id="download-link">
- Download
- </a>
- <div className="display-inline-block dropdown big-spacer-left">
- <button data-toggle="dropdown">
- Logs <i className="icon-dropdown" />
- </button>
- <ul className="dropdown-menu">
- <li>
- <a href={window.baseUrl + '/api/system/logs?process=app'} id="logs-link">
- Main Process
- </a>
- </li>
- <li>
- <a href={window.baseUrl + '/api/system/logs?process=ce'} id="ce-logs-link">
- Compute Engine
- </a>
- </li>
- <li>
- <a href={window.baseUrl + '/api/system/logs?process=es'} id="es-logs-link">
- Elasticsearch
- </a>
- </li>
- <li>
- <a href={window.baseUrl + '/api/system/logs?process=web'} id="web-logs-link">
- Web Server
- </a>
- </li>
- </ul>
- </div>
- <button
- id="restart-server-button"
- className="big-spacer-left"
- onClick={this.handleServerRestartOpen}>
- Restart Server
- </button>
- {this.state.openRestartForm && <RestartForm onClose={this.handleServerRestartClose} />}
- </div>
- </header>
- {sections}
- </div>
- );
- }
-}
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
-import { RouterState, RouteComponent, IndexRouteProps } from 'react-router';
+import { RouterState, IndexRouteProps } from 'react-router';
const routes = [
{
getIndexRoute(_: RouterState, callback: (err: any, route: IndexRouteProps) => any) {
import('./components/App').then(i => callback(null, { component: i.default }));
}
- },
- {
- path: 'old',
- getComponent(_: RouterState, callback: (err: any, component: RouteComponent) => any) {
- import('./main').then(i => callback(null, (i as any).default));
- }
}
];
+++ /dev/null
-/*
- * SonarQube
- * Copyright (C) 2009-2017 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.
- */
-import React from 'react';
-import ItemValue from './item-value';
-
-export default class Section extends React.PureComponent {
- render() {
- const items = this.props.items.map(item => {
- return (
- <tr key={item.name}>
- <td className="thin">
- <div style={{ width: '25vw', overflow: 'hidden', textOverflow: 'ellipsis' }}>
- {item.name}
- </div>
- </td>
- <td style={{ wordBreak: 'break-all' }}>
- <ItemValue name={item.name} value={item.value} />
- </td>
- </tr>
- );
- });
-
- return (
- <div className="big-spacer-bottom">
- <h3 className="spacer-bottom">{this.props.section}</h3>
- <table className="data zebra" id={this.props.section}>
- <tbody>{items}</tbody>
- </table>
- </div>
- );
- }
-}
padding-bottom: 15px;
}
+.system-info-health-card .boxed-group-header > .alert {
+ margin-top: -6px;
+}
+
.system-info-health-card .boxed-group-inner {
padding-top: 0;
}
}
.system-info-health-info {
- margin-top: -4px;
-}
-
-.system-info-health-dot {
- display: inline-block;
- width: 16px;
- height: 16px;
- margin: 4px;
- border-radius: 16px;
- box-sizing: border-box;
-}
-
-.system-info-health-dot.GREEN {
- background-color: #00aa00;
+ margin-top: -12px;
}
-.system-info-health-dot.YELLOW {
- background-color: #eabe06;
-}
-.system-info-health-dot.RED {
- background-color: #d4333f;
+.system-info-health-info .status-indicator {
+ position: relative;
+ top: 8px;
}
.system-info-health-info .alert {
display: inline-block;
- position: relative;
- top: -8px;
-}
-
-.system-info-health-info.big-dot .system-info-health-dot {
- width: 24px;
- height: 24px;
- margin: 0;
- border-radius: 24px;
}
-.system-info-health-info.no-margin .system-info-health-dot {
+.system-info-health-info.no-margin .status-indicator {
margin: 0;
}
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
-import { each, omit, memoize } from 'lodash';
+import { each, omit, memoize, sortBy } from 'lodash';
import {
cleanQuery,
parseAsArray,
expandedCards: string[];
}
+export interface ClusterSysInfo extends SysInfo {
+ 'Application Nodes': NodeInfo[];
+ 'Search Nodes': NodeInfo[];
+}
+
+export interface StandaloneSysInfo extends SysInfo {
+ 'Logs Level': string;
+}
+
+export const LOGS_LEVELS = ['INFO', 'DEBUG', 'TRACE'];
export const HEALTH_FIELD = 'Health';
export const HEALTHCAUSES_FIELD = 'Health Causes';
return omit(sysInfoObject, ['Cluster', HEALTH_FIELD, HEALTHCAUSES_FIELD]);
}
-export function getAppNodes(sysInfoData: SysInfo): NodeInfo[] {
- return sysInfoData['Application Nodes'];
-}
-
export function getHealth(sysInfoObject: SysValueObject): HealthType {
return sysInfoObject[HEALTH_FIELD] as HealthType;
}
return sysInfoObject[HEALTHCAUSES_FIELD] as HealthCause[];
}
-export function getMainCardSection(sysInfoData: SysInfo): SysValueObject {
- return omit(sysInfoData, ['Application Nodes', 'Search Nodes', 'Settings', 'Statistics']);
+export function getLogsLevel(sysInfoObject: SysValueObject): string {
+ return (sysInfoObject['Logs Level'] || LOGS_LEVELS[0]) as string;
+}
+
+export function getSystemLogsLevel(sysInfoData?: SysInfo): string {
+ const defaultLevel = LOGS_LEVELS[0];
+ if (!sysInfoData) {
+ return defaultLevel;
+ }
+ if (isCluster(sysInfoData)) {
+ const nodes = sortBy(getAppNodes(sysInfoData as ClusterSysInfo), node =>
+ LOGS_LEVELS.indexOf(getLogsLevel(node))
+ );
+ return nodes.length > 0 ? getLogsLevel(nodes[nodes.length - 1]) : defaultLevel;
+ } else {
+ return getLogsLevel(sysInfoData);
+ }
}
export function getNodeName(nodeInfo: NodeInfo): string {
return nodeInfo['Name'];
}
-export function getSearchNodes(sysInfoData: SysInfo): NodeInfo[] {
+export function getClusterMainCardSection(sysInfoData: ClusterSysInfo): SysValueObject {
+ return omit(sysInfoData, ['Application Nodes', 'Search Nodes', 'Settings', 'Statistics']);
+}
+
+export function getStandaloneMainSections(sysInfoData: StandaloneSysInfo): SysValueObject {
+ return omit(sysInfoData, [
+ 'Settings',
+ 'Statistics',
+ 'Compute Engine',
+ 'Compute Engine JVM',
+ 'Compute Engine JVM Properties',
+ 'Elasticsearch',
+ 'Search JVM',
+ 'Search JVM Properties',
+ 'Web Database Connectivity',
+ 'Web JVM',
+ 'Web JVM Properties'
+ ]);
+}
+
+export function getStandaloneSecondarySections(sysInfoData: StandaloneSysInfo): SysInfoSection {
+ return {
+ Web: {
+ 'Web Database Connectivity': sysInfoData['Web Database Connectivity'],
+ 'Web JVM': sysInfoData['Web JVM'],
+ 'Web JVM Properties': sysInfoData['Web JVM Properties']
+ },
+ 'Compute Engine': {
+ ...sysInfoData['Compute Engine'] as SysValueObject,
+ 'Compute Engine JVM': sysInfoData['Compute Engine JVM'],
+ 'Compute Engine JVM Properties': sysInfoData['Compute Engine JVM Properties']
+ },
+ Search: {
+ Elasticsearch: sysInfoData['Elasticsearch'] as SysValueObject,
+ 'Search JVM': sysInfoData['Search JVM'],
+ 'Search JVM Properties': sysInfoData['Search JVM Properties']
+ }
+ };
+}
+
+export function getAppNodes(sysInfoData: ClusterSysInfo): NodeInfo[] {
+ return sysInfoData['Application Nodes'];
+}
+
+export function getSearchNodes(sysInfoData: ClusterSysInfo): NodeInfo[] {
return sysInfoData['Search Nodes'];
}
}
export function isCluster(sysInfoData?: SysInfo): boolean {
- return sysInfoData != undefined && sysInfoData['Cluster'];
+ return sysInfoData != undefined && sysInfoData['Cluster'] === true;
}
export const parseQuery = memoize((urlQuery: RawQuery): Query => {
text-align: right;
}
-.branch-status-indicator {
- display: block;
- width: 8px;
- height: 8px;
- border-radius: 8px;
- margin: 4px 0;
-}
-
-.branch-status-indicator.is-failed {
- background-color: #d4333f;
-}
-
-.branch-status-indicator.is-passed {
- background-color: #00aa00;
+.branch-status .status-indicator {
+ margin: 0;
}
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
import * as React from 'react';
-import * as classNames from 'classnames';
import { Branch } from '../../app/types';
import Level from '../ui/Level';
import BugIcon from '../icons-components/BugIcon';
import CodeSmellIcon from '../icons-components/CodeSmellIcon';
+import StatusIndicator from './StatusIndicator';
import VulnerabilityIcon from '../icons-components/VulnerabilityIcon';
import { isShortLivingBranch } from '../../helpers/branches';
import './BranchStatus.css';
const totalIssues =
branch.status.bugs + branch.status.vulnerabilities + branch.status.codeSmells;
- const indicatorClassName = classNames('branch-status-indicator', {
- 'is-failed': totalIssues > 0,
- 'is-passed': totalIssues === 0
- });
+ const indicatorColor = totalIssues > 0 ? 'red' : 'green';
return concise ? (
<ul className="list-inline branch-status">
<li>{totalIssues}</li>
<li className="spacer-left">
- <i className={indicatorClassName} />
+ <StatusIndicator color={indicatorColor} size="small" />
</li>
</ul>
) : (
<ul className="list-inline branch-status">
<li className="spacer-right">
- <i className={indicatorClassName} />
+ <StatusIndicator color={indicatorColor} size="small" />
</li>
<li>
{branch.status.bugs}
--- /dev/null
+.status-indicator {
+ display: inline-block;
+ box-sizing: border-box;
+ width: 16px;
+ height: 16px;
+ border-radius: 16px;
+ margin: 4px;
+}
+
+.status-indicator.small-status-indicator {
+ width: 8px;
+ height: 8px;
+ border-radius: 8px;
+ margin: 8px;
+}
+
+.status-indicator.big-status-indicator {
+ width: 24px;
+ height: 24px;
+ border-radius: 24px;
+ margin: 0;
+}
+
+.status-indicator.red {
+ background-color: #d4333f;
+}
+
+.sstatus-indicator.yellow {
+ background-color: #eabe06;
+}
+
+.status-indicator.green {
+ background-color: #00aa00;
+}
--- /dev/null
+/*
+ * SonarQube
+ * Copyright (C) 2009-2016 SonarSource SA
+ * mailto:contact 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.
+ */
+import * as React from 'react';
+import * as classNames from 'classnames';
+import './StatusIndicator.css';
+
+interface Props {
+ className?: string;
+ color?: string;
+ size?: string;
+}
+
+export default function StatusIndicator({ className, color, size }: Props) {
+ return (
+ <i
+ className={classNames(
+ 'status-indicator',
+ color,
+ {
+ 'small-status-indicator': size === 'small',
+ 'big-status-indicator': size === 'big'
+ },
+ className
+ )}
+ />
+ );
+}
<li
className="spacer-right"
>
- <i
- className="branch-status-indicator is-passed"
+ <StatusIndicator
+ color="green"
+ size="small"
/>
</li>
<li>
<li
className="spacer-right"
>
- <i
- className="branch-status-indicator is-failed"
+ <StatusIndicator
+ color="red"
+ size="small"
/>
</li>
<li>
<li
className="spacer-right"
>
- <i
- className="branch-status-indicator is-failed"
+ <StatusIndicator
+ color="red"
+ size="small"
/>
</li>
<li>
height={size}
style={{ fill: 'currentColor' }}>
{open ? (
- <path d="M13.506 8.539l-5.191 5.184q-0.133 0.133-0.315 0.133t-0.315-0.133l-5.191-5.184q-0.133-0.133-0.133-0.318t0.133-0.318l1.161-1.154q0.133-0.133 0.315-0.133t0.315 0.133l3.715 3.715 3.715-3.715q0.133-0.133 0.315-0.133t0.315 0.133l1.161 1.154q0.133 0.133 0.133 0.318t-0.133 0.318z" />
+ <path d="M13.506 9.289l-5.191 5.184q-0.133 0.133-0.315 0.133t-0.315-0.133l-5.191-5.184q-0.133-0.133-0.133-0.318t0.133-0.318l1.161-1.154q0.133-0.133 0.315-0.133t0.315 0.133l3.715 3.715 3.715-3.715q0.133-0.133 0.315-0.133t0.315 0.133l1.161 1.154q0.133 0.133 0.133 0.318t-0.133 0.318z" />
) : (
- <path d="M13.527 8.318l-5.244 5.244q-0.134 0.134-0.318 0.134t-0.318-0.134l-1.173-1.173q-0.134-0.134-0.134-0.318t0.134-0.318l3.753-3.753-3.753-3.753q-0.134-0.134-0.134-0.318t0.134-0.318l1.173-1.173q0.134-0.134 0.318-0.134t0.318 0.134l5.244 5.244q0.134 0.134 0.134 0.318t-0.134 0.318z" />
+ <path d="M13.527 9.318l-5.244 5.244q-0.134 0.134-0.318 0.134t-0.318-0.134l-1.173-1.173q-0.134-0.134-0.134-0.318t0.134-0.318l3.753-3.753-3.753-3.753q-0.134-0.134-0.134-0.318t0.134-0.318l1.173-1.173q0.134-0.134 0.318-0.134t0.318 0.134l5.244 5.244q0.134 0.134 0.134 0.318t-0.134 0.318z" />
)}
</svg>
);
system.download_logs=Download Logs
system.download_system_info=Download System Info
system.is_restarting=Server is restarting. This page will be automatically refreshed.
-system.log_level.warning=Current level has performance impacts, please make sure to get back to INFO level once your investigation is done. Please note that when the server is restarted, logging will revert to the level configured in sonar.properties.
+system.log_level.warning=This level has performance impacts, please make sure to get back to INFO level once your investigation is done. Please note that when the server is restarted, logging will revert to the level configured in sonar.properties.
+system.log_level.warning.short=Current logs level has performance impacts, get back to INFO level.
system.log_level.info=Changes don't apply to Search.
system.logs_level=Logs level
system.restart_server=Restart Server