]> source.dussan.org Git - sonarqube.git/commitdiff
SONAR-9802 request all nodes from api/system/cluster_info
authorSimon Brandhof <simon.brandhof@sonarsource.com>
Tue, 19 Sep 2017 15:39:54 +0000 (17:39 +0200)
committerSimon Brandhof <simon.brandhof@sonarsource.com>
Tue, 26 Sep 2017 21:49:38 +0000 (23:49 +0200)
32 files changed:
server/sonar-ce/src/main/java/org/sonar/ce/container/ComputeEngineContainerImpl.java
server/sonar-ce/src/main/java/org/sonar/ce/monitoring/CeSystemInfoModule.java [new file with mode: 0644]
server/sonar-process/src/main/java/org/sonar/process/systeminfo/Global.java [new file with mode: 0644]
server/sonar-process/src/main/java/org/sonar/process/systeminfo/JvmPropertiesSection.java
server/sonar-process/src/main/java/org/sonar/process/systeminfo/JvmStateSection.java
server/sonar-process/src/main/java/org/sonar/process/systeminfo/SystemInfoUtils.java
server/sonar-process/src/test/java/org/sonar/process/systeminfo/JvmPropertiesSectionTest.java
server/sonar-process/src/test/java/org/sonar/process/systeminfo/JvmStateSectionTest.java
server/sonar-process/src/test/java/org/sonar/process/systeminfo/SystemInfoUtilsTest.java [new file with mode: 0644]
server/sonar-server/src/main/java/org/sonar/server/platform/monitoring/EsStatisticsSection.java
server/sonar-server/src/main/java/org/sonar/server/platform/monitoring/OfficialDistribution.java [new file with mode: 0644]
server/sonar-server/src/main/java/org/sonar/server/platform/monitoring/PluginsSection.java
server/sonar-server/src/main/java/org/sonar/server/platform/monitoring/SettingsSection.java
server/sonar-server/src/main/java/org/sonar/server/platform/monitoring/StandaloneSystemSection.java [new file with mode: 0644]
server/sonar-server/src/main/java/org/sonar/server/platform/monitoring/SystemSection.java [deleted file]
server/sonar-server/src/main/java/org/sonar/server/platform/monitoring/WebSystemInfoModule.java
server/sonar-server/src/main/java/org/sonar/server/platform/monitoring/cluster/AppNodesInfoLoader.java [new file with mode: 0644]
server/sonar-server/src/main/java/org/sonar/server/platform/monitoring/cluster/AppNodesInfoLoaderImpl.java [new file with mode: 0644]
server/sonar-server/src/main/java/org/sonar/server/platform/monitoring/cluster/GlobalInfoLoader.java [new file with mode: 0644]
server/sonar-server/src/main/java/org/sonar/server/platform/monitoring/cluster/GlobalSystemSection.java [new file with mode: 0644]
server/sonar-server/src/main/java/org/sonar/server/platform/monitoring/cluster/NodeInfo.java [new file with mode: 0644]
server/sonar-server/src/main/java/org/sonar/server/platform/monitoring/cluster/NodeSystemSection.java [new file with mode: 0644]
server/sonar-server/src/main/java/org/sonar/server/platform/monitoring/cluster/ProcessInfoProvider.java [new file with mode: 0644]
server/sonar-server/src/main/java/org/sonar/server/platform/monitoring/cluster/package-info.java [new file with mode: 0644]
server/sonar-server/src/main/java/org/sonar/server/platform/ws/ClusterInfoAction.java
server/sonar-server/src/main/java/org/sonar/server/platform/ws/InfoAction.java
server/sonar-server/src/main/java/org/sonar/server/platform/ws/InfoActionModule.java [deleted file]
server/sonar-server/src/main/java/org/sonar/server/telemetry/TelemetryModule.java [deleted file]
server/sonar-server/src/test/java/org/sonar/server/platform/monitoring/OfficialDistributionTest.java [new file with mode: 0644]
server/sonar-server/src/test/java/org/sonar/server/platform/monitoring/StandaloneSystemSectionTest.java [new file with mode: 0644]
server/sonar-server/src/test/java/org/sonar/server/platform/monitoring/SystemSectionTest.java [deleted file]
server/sonar-server/src/test/java/org/sonar/server/platform/monitoring/cluster/NodeInfoTest.java [new file with mode: 0644]

index b443c7f689482817f3816b30942e600fa0dc880f..79f042caf3d5a40b0cb9bc20f21bd9125890f812 100644 (file)
@@ -49,6 +49,7 @@ import org.sonar.ce.StandaloneCeDistributedInformation;
 import org.sonar.ce.cleaning.CeCleaningModule;
 import org.sonar.ce.db.ReadOnlyPropertiesDao;
 import org.sonar.ce.log.CeProcessLogging;
+import org.sonar.ce.monitoring.CeSystemInfoModule;
 import org.sonar.ce.platform.ComputeEngineExtensionInstaller;
 import org.sonar.ce.queue.CeQueueCleaner;
 import org.sonar.ce.queue.PurgeCeActivities;
@@ -423,6 +424,7 @@ public class ComputeEngineContainerImpl implements ComputeEngineContainer {
       container.add(
         StartableHazelcastMember.class,
         CeDistributedInformationImpl.class);
+      container.add(CeSystemInfoModule.forClusterMode());
     } else {
       container.add(StandaloneCeDistributedInformation.class);
     }
diff --git a/server/sonar-ce/src/main/java/org/sonar/ce/monitoring/CeSystemInfoModule.java b/server/sonar-ce/src/main/java/org/sonar/ce/monitoring/CeSystemInfoModule.java
new file mode 100644 (file)
index 0000000..be53383
--- /dev/null
@@ -0,0 +1,41 @@
+/*
+ * 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.ce.monitoring;
+
+import org.sonar.process.systeminfo.JvmPropertiesSection;
+import org.sonar.process.systeminfo.JvmStateSection;
+import org.sonar.server.platform.monitoring.DatabaseSection;
+import org.sonar.server.platform.monitoring.cluster.ProcessInfoProvider;
+
+public class CeSystemInfoModule {
+
+  private CeSystemInfoModule() {
+    // do not instantiate
+  }
+
+  public static Object[] forClusterMode() {
+    return new Object[] {
+      new JvmPropertiesSection("Compute Engine JVM Properties"),
+      new JvmStateSection("Compute Engine JVM State"),
+      DatabaseSection.class,
+      ProcessInfoProvider.class
+    };
+  }
+}
diff --git a/server/sonar-process/src/main/java/org/sonar/process/systeminfo/Global.java b/server/sonar-process/src/main/java/org/sonar/process/systeminfo/Global.java
new file mode 100644 (file)
index 0000000..83e9568
--- /dev/null
@@ -0,0 +1,29 @@
+/*
+ * 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.process.systeminfo;
+
+/**
+ * Interface to mark {@link SystemInfoSection} of web server as global.
+ * In case of cluster mode, all the processes and nodes would return
+ * the same values, so it's loaded once on the web server that receives
+ * the user request.
+ */
+public interface Global {
+}
index 8b0a31b0065b20da451a154808199674398de38c..1cb014bde4f711b2575354549941272ee9d93e7a 100644 (file)
@@ -21,6 +21,7 @@ package org.sonar.process.systeminfo;
 
 import java.util.Map;
 import java.util.Objects;
+import java.util.TreeMap;
 import org.sonar.process.systeminfo.protobuf.ProtobufSystemInfo;
 
 import static org.sonar.process.systeminfo.SystemInfoUtils.setAttribute;
@@ -41,7 +42,8 @@ public class JvmPropertiesSection implements SystemInfoSection {
     ProtobufSystemInfo.Section.Builder protobuf = ProtobufSystemInfo.Section.newBuilder();
     protobuf.setName(name);
 
-    for (Map.Entry<Object, Object> systemProp : System.getProperties().entrySet()) {
+    Map<Object, Object> sortedProperties = new TreeMap<>(System.getProperties());
+    for (Map.Entry<Object, Object> systemProp : sortedProperties.entrySet()) {
       if (systemProp.getValue() != null) {
         setAttribute(protobuf, Objects.toString(systemProp.getKey()), Objects.toString(systemProp.getValue()));
       }
index 656fba7e75b0e30d99d12027893cb2f56fc4cb8e..804e2f0de5e90f9d574dad5d54ab216b0c70449c 100644 (file)
@@ -61,8 +61,6 @@ public class JvmStateSection implements SystemInfoSection {
     ThreadMXBean thread = ManagementFactory.getThreadMXBean();
     setAttribute(protobuf, "Threads", thread.getThreadCount());
 
-    setAttribute(protobuf,"Processors", Runtime.getRuntime().availableProcessors());
-
     return protobuf.build();
   }
 
index 63dbdec055d93c767a0bc92c9f248e3432927f79..0126bbe2ae7d170d1418ad79cd9ef3c629b866ee 100644 (file)
  */
 package org.sonar.process.systeminfo;
 
+import java.util.ArrayList;
 import java.util.Collection;
+import java.util.List;
+import java.util.Map;
+import java.util.TreeMap;
 import javax.annotation.CheckForNull;
 import javax.annotation.Nullable;
 import org.sonar.process.systeminfo.protobuf.ProtobufSystemInfo;
+import org.sonar.process.systeminfo.protobuf.ProtobufSystemInfo.Section;
+
+import static java.util.Arrays.stream;
 
 public class SystemInfoUtils {
 
@@ -30,7 +37,7 @@ public class SystemInfoUtils {
     // prevent instantiation
   }
 
-  public static void setAttribute(ProtobufSystemInfo.Section.Builder section, String key, @Nullable String value) {
+  public static void setAttribute(Section.Builder section, String key, @Nullable String value) {
     if (value != null) {
       section.addAttributesBuilder()
         .setKey(key)
@@ -39,7 +46,7 @@ public class SystemInfoUtils {
     }
   }
 
-  public static void setAttribute(ProtobufSystemInfo.Section.Builder section, String key, @Nullable Collection<String> values) {
+  public static void setAttribute(Section.Builder section, String key, @Nullable Collection<String> values) {
     if (values != null) {
       section.addAttributesBuilder()
         .setKey(key)
@@ -48,14 +55,14 @@ public class SystemInfoUtils {
     }
   }
 
-  public static void setAttribute(ProtobufSystemInfo.Section.Builder section, String key, boolean value) {
+  public static void setAttribute(Section.Builder section, String key, boolean value) {
     section.addAttributesBuilder()
       .setKey(key)
       .setBooleanValue(value)
       .build();
   }
 
-  public static void setAttribute(ProtobufSystemInfo.Section.Builder section, String key, long value) {
+  public static void setAttribute(Section.Builder section, String key, long value) {
     section.addAttributesBuilder()
       .setKey(key)
       .setLongValue(value)
@@ -63,7 +70,7 @@ public class SystemInfoUtils {
   }
 
   @CheckForNull
-  public static ProtobufSystemInfo.Attribute attribute(ProtobufSystemInfo.Section section, String key) {
+  public static ProtobufSystemInfo.Attribute attribute(Section section, String key) {
     for (ProtobufSystemInfo.Attribute attribute : section.getAttributesList()) {
       if (attribute.getKey().equals(key)) {
         return attribute;
@@ -71,4 +78,19 @@ public class SystemInfoUtils {
     }
     return null;
   }
+
+  public static List<Section> order(Collection<Section> sections, String... orderedNames) {
+    Map<String, Section> alphabeticalOrderedMap = new TreeMap<>();
+    sections.forEach(section -> alphabeticalOrderedMap.put(section.getName(), section));
+
+    List<Section> result = new ArrayList<>(sections.size());
+    stream(orderedNames).forEach(name -> {
+      Section section = alphabeticalOrderedMap.remove(name);
+      if (section != null) {
+        result.add(section);
+      }
+    });
+    result.addAll(alphabeticalOrderedMap.values());
+    return result;
+  }
 }
index dcf4cb9893c2c59e972c94b6538cee5ba8531a03..2050e0a3f73c006a2619842fec9114bc70f4084f 100644 (file)
  */
 package org.sonar.process.systeminfo;
 
-import org.assertj.core.api.Assertions;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import java.util.stream.Collectors;
 import org.junit.Test;
 import org.sonar.process.systeminfo.protobuf.ProtobufSystemInfo;
 
 import static org.assertj.core.api.Assertions.assertThat;
-import static org.sonar.process.systeminfo.SystemInfoUtils.attribute;
 
 public class JvmPropertiesSectionTest {
 
@@ -36,10 +38,17 @@ public class JvmPropertiesSectionTest {
   }
 
   @Test
-  public void test_toProtobuf() {
+  public void system_properties_are_returned_in_alphabetical_order() {
     ProtobufSystemInfo.Section section = underTest.toProtobuf();
 
-    Assertions.assertThat(attribute(section, "java.vm.vendor").getStringValue()).isNotEmpty();
-    Assertions.assertThat(attribute(section, "os.name").getStringValue()).isNotEmpty();
+    List<String> keys = section.getAttributesList()
+      .stream()
+      .map(ProtobufSystemInfo.Attribute::getKey)
+      .collect(Collectors.toList());
+    assertThat(keys).contains("java.vm.vendor", "os.name");
+
+    List<String> sortedKeys = new ArrayList<>(keys);
+    Collections.sort(sortedKeys);
+    assertThat(sortedKeys).isEqualTo(keys);
   }
 }
index 3057e9933e9b50b9dfa45c42ee94e07bd7ec386b..77da5df5719eea46f6312dce7e062768eb66babe 100644 (file)
@@ -39,7 +39,7 @@ public class JvmStateSectionTest {
 
     assertThat(section.getName()).isEqualTo(PROCESS_NAME);
     assertThat(section.getAttributesCount()).isGreaterThan(0);
-    assertThat(section.getAttributesList()).extracting("key").contains("Threads", "Processors");
+    assertThat(section.getAttributesList()).extracting("key").contains("Threads", "Heap Max (MB)");
   }
 
   @Test
diff --git a/server/sonar-process/src/test/java/org/sonar/process/systeminfo/SystemInfoUtilsTest.java b/server/sonar-process/src/test/java/org/sonar/process/systeminfo/SystemInfoUtilsTest.java
new file mode 100644 (file)
index 0000000..03410f3
--- /dev/null
@@ -0,0 +1,50 @@
+/*
+ * 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.process.systeminfo;
+
+import java.util.Collection;
+import java.util.List;
+import java.util.stream.Collectors;
+import org.junit.Test;
+import org.sonar.process.systeminfo.protobuf.ProtobufSystemInfo.Section;
+
+import static java.util.Arrays.asList;
+import static org.assertj.core.api.Assertions.assertThat;
+
+public class SystemInfoUtilsTest {
+
+  @Test
+  public void test_order() {
+    Collection<Section> sections = asList(
+      newSection("end2"),
+      newSection("bar"),
+      newSection("end1"),
+      newSection("foo"));
+
+    List<String> ordered = SystemInfoUtils.order(sections, "foo", "bar").stream()
+      .map(Section::getName)
+      .collect(Collectors.toList());
+    assertThat(ordered).isEqualTo(asList("foo", "bar", "end1", "end2"));
+  }
+
+  private static Section newSection(String name) {
+    return Section.newBuilder().setName(name).build();
+  }
+}
index 12656ae4f3e49dc82fcd3a9a3eb07f4035231252..c317854fc6fc4abc96555d788c4fd2fee0225fc0 100644 (file)
@@ -23,7 +23,9 @@ 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.server.ServerSide;
 import org.sonar.api.utils.log.Loggers;
+import org.sonar.process.systeminfo.Global;
 import org.sonar.process.systeminfo.SystemInfoSection;
 import org.sonar.process.systeminfo.protobuf.ProtobufSystemInfo;
 import org.sonar.server.es.EsClient;
@@ -31,7 +33,8 @@ 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 {
+@ServerSide
+public class EsStatisticsSection implements SystemInfoSection, Global {
 
   private final EsClient esClient;
 
diff --git a/server/sonar-server/src/main/java/org/sonar/server/platform/monitoring/OfficialDistribution.java b/server/sonar-server/src/main/java/org/sonar/server/platform/monitoring/OfficialDistribution.java
new file mode 100644 (file)
index 0000000..4120fcd
--- /dev/null
@@ -0,0 +1,43 @@
+/*
+ * 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 org.sonar.api.platform.Server;
+import org.sonar.api.server.ServerSide;
+
+@ServerSide
+public class OfficialDistribution {
+  static final String BRANDING_FILE_PATH = "web/WEB-INF/classes/com/sonarsource/branding";
+
+  private final Server server;
+
+  public OfficialDistribution(Server server) {
+    this.server = server;
+  }
+
+  public boolean check() {
+    // 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;
+  }
+}
index 491a8bf1265ae299eb7c7e03397f5b48fa6feec4..15508a91fd916ef6f8f17bf107f869b2c660b374 100644 (file)
@@ -19,6 +19,7 @@
  */
 package org.sonar.server.platform.monitoring;
 
+import org.sonar.api.server.ServerSide;
 import org.sonar.core.platform.PluginInfo;
 import org.sonar.core.platform.PluginRepository;
 import org.sonar.process.systeminfo.SystemInfoSection;
@@ -27,6 +28,7 @@ import org.sonar.updatecenter.common.Version;
 
 import static org.sonar.process.systeminfo.SystemInfoUtils.setAttribute;
 
+@ServerSide
 public class PluginsSection implements SystemInfoSection {
   private final PluginRepository repository;
 
index 12f1d688623d04f338eade47429381e3f98b156d..e6e3a473635c2e3c08d996ed384c52e65a5e521d 100644 (file)
@@ -24,13 +24,16 @@ import org.sonar.api.PropertyType;
 import org.sonar.api.config.PropertyDefinition;
 import org.sonar.api.config.PropertyDefinitions;
 import org.sonar.api.config.Settings;
+import org.sonar.api.server.ServerSide;
+import org.sonar.process.systeminfo.Global;
 import org.sonar.process.systeminfo.SystemInfoSection;
 import org.sonar.process.systeminfo.protobuf.ProtobufSystemInfo;
 
 import static org.apache.commons.lang.StringUtils.abbreviate;
 import static org.sonar.process.systeminfo.SystemInfoUtils.setAttribute;
 
-public class SettingsSection implements SystemInfoSection {
+@ServerSide
+public class SettingsSection implements SystemInfoSection, Global {
 
   static final int MAX_VALUE_LENGTH = 500;
   private final Settings settings;
diff --git a/server/sonar-server/src/main/java/org/sonar/server/platform/monitoring/StandaloneSystemSection.java b/server/sonar-server/src/main/java/org/sonar/server/platform/monitoring/StandaloneSystemSection.java
new file mode 100644 (file)
index 0000000..acdffa2
--- /dev/null
@@ -0,0 +1,150 @@
+/*
+ * 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.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 StandaloneSystemSection extends BaseSectionMBean implements SystemSectionMBean {
+
+  private static final Joiner COMMA_JOINER = Joiner.on(", ");
+
+  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 OfficialDistribution officialDistribution;
+  private final HealthChecker healthChecker;
+
+  public StandaloneSystemSection(Configuration config, SecurityRealmFactory securityRealmFactory,
+    IdentityProviderRepository identityProviderRepository, Server server, ServerLogging serverLogging,
+    ServerIdLoader serverIdLoader, OfficialDistribution officialDistribution, HealthChecker healthChecker) {
+    this.config = config;
+    this.securityRealmFactory = securityRealmFactory;
+    this.identityProviderRepository = identityProviderRepository;
+    this.server = server;
+    this.serverLogging = serverLogging;
+    this.serverIdLoader = serverIdLoader;
+    this.officialDistribution = officialDistribution;
+    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);
+  }
+
+  @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", officialDistribution.check());
+    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());
+    setAttribute(protobuf, "Processors", Runtime.getRuntime().availableProcessors());
+    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));
+    }
+  }
+}
diff --git a/server/sonar-server/src/main/java/org/sonar/server/platform/monitoring/SystemSection.java b/server/sonar-server/src/main/java/org/sonar/server/platform/monitoring/SystemSection.java
deleted file mode 100644 (file)
index d19f716..0000000
+++ /dev/null
@@ -1,158 +0,0 @@
-/*
- * 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 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";
-
-  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;
-  }
-
-  @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() {
-    // 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 void addIfNotEmpty(ProtobufSystemInfo.Section.Builder protobuf, String key, @Nullable List<String> values) {
-    if (values != null && !values.isEmpty()) {
-      setAttribute(protobuf, key, COMMA_JOINER.join(values));
-    }
-  }
-}
index 09c40749fc84a1ba86443365cac54daf9e3cb707..b5f495f2c64ca94e7733a0e1a9b93d06d7d99288 100644 (file)
@@ -21,6 +21,11 @@ package org.sonar.server.platform.monitoring;
 
 import org.sonar.process.systeminfo.JvmPropertiesSection;
 import org.sonar.process.systeminfo.JvmStateSection;
+import org.sonar.server.platform.monitoring.cluster.AppNodesInfoLoaderImpl;
+import org.sonar.server.platform.monitoring.cluster.GlobalInfoLoader;
+import org.sonar.server.platform.monitoring.cluster.GlobalSystemSection;
+import org.sonar.server.platform.monitoring.cluster.ProcessInfoProvider;
+import org.sonar.server.platform.monitoring.cluster.NodeSystemSection;
 import org.sonar.server.platform.ws.ClusterInfoAction;
 import org.sonar.server.platform.ws.StandaloneInfoAction;
 
@@ -39,7 +44,9 @@ public class WebSystemInfoModule {
       EsStatisticsSection.class,
       PluginsSection.class,
       SettingsSection.class,
-      SystemSection.class,
+      StandaloneSystemSection.class,
+
+      OfficialDistribution.class,
 
       StandaloneInfoAction.class
     };
@@ -50,9 +57,17 @@ public class WebSystemInfoModule {
       new JvmPropertiesSection("Web JVM Properties"),
       new JvmStateSection("Web JVM State"),
       DatabaseSection.class,
-      EsStateSection.class,
+      EsStatisticsSection.class,
+      GlobalSystemSection.class,
+      NodeSystemSection.class,
       PluginsSection.class,
+      SettingsSection.class,
+
+      OfficialDistribution.class,
 
+      ProcessInfoProvider.class,
+      GlobalInfoLoader.class,
+      AppNodesInfoLoaderImpl.class,
       ClusterInfoAction.class
     };
   }
diff --git a/server/sonar-server/src/main/java/org/sonar/server/platform/monitoring/cluster/AppNodesInfoLoader.java b/server/sonar-server/src/main/java/org/sonar/server/platform/monitoring/cluster/AppNodesInfoLoader.java
new file mode 100644 (file)
index 0000000..f3ffd95
--- /dev/null
@@ -0,0 +1,27 @@
+/*
+ * 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.cluster;
+
+import java.util.Collection;
+
+public interface AppNodesInfoLoader {
+
+  Collection<NodeInfo> load();
+}
diff --git a/server/sonar-server/src/main/java/org/sonar/server/platform/monitoring/cluster/AppNodesInfoLoaderImpl.java b/server/sonar-server/src/main/java/org/sonar/server/platform/monitoring/cluster/AppNodesInfoLoaderImpl.java
new file mode 100644 (file)
index 0000000..03eaaa0
--- /dev/null
@@ -0,0 +1,86 @@
+/*
+ * 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.cluster;
+
+import com.hazelcast.core.Member;
+import com.hazelcast.core.MemberSelector;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Optional;
+import org.sonar.api.server.ServerSide;
+import org.sonar.process.ProcessId;
+import org.sonar.process.cluster.hz.DistributedAnswer;
+import org.sonar.process.cluster.hz.HazelcastMember;
+import org.sonar.process.systeminfo.protobuf.ProtobufSystemInfo;
+
+@ServerSide
+public class AppNodesInfoLoaderImpl implements AppNodesInfoLoader {
+
+  private final HazelcastMember hzMember;
+
+  public AppNodesInfoLoaderImpl(HazelcastMember hzMember) {
+    this.hzMember = hzMember;
+  }
+
+  public Collection<NodeInfo> load() {
+    try {
+      Map<String, NodeInfo> nodesByName = new HashMap<>();
+      DistributedAnswer<ProtobufSystemInfo.SystemInfo> distributedAnswer = hzMember.call(ProcessInfoProvider::provide, new CeWebMemberSelector(), 15_000L);
+      for (Member member : distributedAnswer.getMembers()) {
+        String nodeName = member.getStringAttribute(HazelcastMember.Attribute.NODE_NAME);
+        NodeInfo nodeInfo = nodesByName.get(nodeName);
+        if (nodeInfo == null) {
+          nodeInfo = new NodeInfo(nodeName);
+          nodesByName.put(nodeName, nodeInfo);
+
+          String hostname = member.getStringAttribute(HazelcastMember.Attribute.HOSTNAME);
+          nodeInfo.setAttribute("Hostname", hostname);
+        }
+        completeNodeInfo(distributedAnswer, member, nodeInfo);
+      }
+      return nodesByName.values();
+
+    } catch (InterruptedException e) {
+      Thread.currentThread().interrupt();
+      throw new IllegalStateException(e);
+    }
+  }
+
+  private void completeNodeInfo(DistributedAnswer<ProtobufSystemInfo.SystemInfo> distributedAnswer, Member member, NodeInfo nodeInfo) {
+    Optional<ProtobufSystemInfo.SystemInfo> nodeAnswer = distributedAnswer.getAnswer(member);
+    Optional<Exception> failure = distributedAnswer.getFailed(member);
+    if (distributedAnswer.hasTimedOut(member)) {
+      nodeInfo.setAttribute("Error", "Failed to retrieve information on time");
+    } else if (failure.isPresent()) {
+      nodeInfo.setAttribute("Error", "Failed to retrieve information: " + failure.get().getMessage());
+    } else if (nodeAnswer.isPresent()) {
+      nodeAnswer.get().getSectionsList().forEach(nodeInfo::addSection);
+    }
+  }
+
+  private static class CeWebMemberSelector implements MemberSelector {
+    @Override
+    public boolean select(Member member) {
+      String processKey = member.getStringAttribute(HazelcastMember.Attribute.PROCESS_KEY);
+      return processKey.equals(ProcessId.WEB_SERVER.getKey()) || processKey.equals(ProcessId.COMPUTE_ENGINE.getKey());
+    }
+  }
+}
diff --git a/server/sonar-server/src/main/java/org/sonar/server/platform/monitoring/cluster/GlobalInfoLoader.java b/server/sonar-server/src/main/java/org/sonar/server/platform/monitoring/cluster/GlobalInfoLoader.java
new file mode 100644 (file)
index 0000000..f9c01d3
--- /dev/null
@@ -0,0 +1,45 @@
+/*
+ * 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.cluster;
+
+import java.util.Arrays;
+import java.util.List;
+import java.util.stream.Collectors;
+import org.sonar.api.server.ServerSide;
+import org.sonar.process.systeminfo.Global;
+import org.sonar.process.systeminfo.SystemInfoSection;
+import org.sonar.process.systeminfo.protobuf.ProtobufSystemInfo;
+
+@ServerSide
+public class GlobalInfoLoader {
+  private final List<SystemInfoSection> globalSections;
+
+  public GlobalInfoLoader(SystemInfoSection[] sections) {
+    this.globalSections = Arrays.stream(sections)
+      .filter(section -> section instanceof Global)
+      .collect(Collectors.toList());
+  }
+
+  public List<ProtobufSystemInfo.Section> load() {
+    return globalSections.stream()
+      .map(SystemInfoSection::toProtobuf)
+      .collect(Collectors.toList());
+  }
+}
diff --git a/server/sonar-server/src/main/java/org/sonar/server/platform/monitoring/cluster/GlobalSystemSection.java b/server/sonar-server/src/main/java/org/sonar/server/platform/monitoring/cluster/GlobalSystemSection.java
new file mode 100644 (file)
index 0000000..90875c5
--- /dev/null
@@ -0,0 +1,116 @@
+/*
+ * 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.cluster;
+
+import com.google.common.base.Joiner;
+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.security.SecurityRealm;
+import org.sonar.api.server.ServerSide;
+import org.sonar.api.server.authentication.IdentityProvider;
+import org.sonar.core.util.stream.MoreCollectors;
+import org.sonar.process.systeminfo.Global;
+import org.sonar.process.systeminfo.SystemInfoSection;
+import org.sonar.process.systeminfo.protobuf.ProtobufSystemInfo;
+import org.sonar.server.authentication.IdentityProviderRepository;
+import org.sonar.server.health.ClusterHealth;
+import org.sonar.server.health.HealthChecker;
+import org.sonar.server.platform.ServerIdLoader;
+import org.sonar.server.user.SecurityRealmFactory;
+
+import static org.sonar.process.systeminfo.SystemInfoUtils.setAttribute;
+
+@ServerSide
+public class GlobalSystemSection implements SystemInfoSection, Global {
+  private static final Joiner COMMA_JOINER = Joiner.on(", ");
+
+  private final Configuration config;
+  private final ServerIdLoader serverIdLoader;
+  private final SecurityRealmFactory securityRealmFactory;
+  private final IdentityProviderRepository identityProviderRepository;
+  private final HealthChecker healthChecker;
+
+  public GlobalSystemSection(Configuration config, ServerIdLoader serverIdLoader, SecurityRealmFactory securityRealmFactory,
+    IdentityProviderRepository identityProviderRepository, HealthChecker healthChecker) {
+    this.config = config;
+    this.serverIdLoader = serverIdLoader;
+    this.securityRealmFactory = securityRealmFactory;
+    this.identityProviderRepository = identityProviderRepository;
+    this.healthChecker = healthChecker;
+  }
+
+  @Override
+  public ProtobufSystemInfo.Section toProtobuf() {
+    ProtobufSystemInfo.Section.Builder protobuf = ProtobufSystemInfo.Section.newBuilder();
+    protobuf.setName("System");
+
+    ClusterHealth health = healthChecker.checkCluster();
+
+    setAttribute(protobuf, "Health", health.getHealth().getStatus().name());
+    setAttribute(protobuf, "Health Causes", health.getHealth().getCauses());
+    serverIdLoader.get().ifPresent(serverId -> {
+      setAttribute(protobuf, "Server ID", serverId.getId());
+      setAttribute(protobuf, "Server ID validated", serverId.isValid());
+    });
+    setAttribute(protobuf, "High Availability", true);
+    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());
+    return protobuf.build();
+  }
+
+  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 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));
+    }
+  }
+
+  @CheckForNull
+  private String getExternalUserAuthentication() {
+    SecurityRealm realm = securityRealmFactory.getRealm();
+    return realm == null ? null : realm.getName();
+  }
+
+}
diff --git a/server/sonar-server/src/main/java/org/sonar/server/platform/monitoring/cluster/NodeInfo.java b/server/sonar-server/src/main/java/org/sonar/server/platform/monitoring/cluster/NodeInfo.java
new file mode 100644 (file)
index 0000000..3a53e1b
--- /dev/null
@@ -0,0 +1,76 @@
+/*
+ * 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.cluster;
+
+import java.util.ArrayList;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
+import org.sonar.process.systeminfo.protobuf.ProtobufSystemInfo;
+
+public class NodeInfo {
+
+  private final String name;
+  private final Map<String, String> attributes = new LinkedHashMap<>();
+  private final List<ProtobufSystemInfo.Section> sections = new ArrayList<>();
+
+  public NodeInfo(String name) {
+    this.name = name;
+  }
+
+  public String getName() {
+    return name;
+  }
+
+  public NodeInfo setAttribute(String key, String value) {
+    this.attributes.put(key, value);
+    return this;
+  }
+
+  public Map<String, String> getAttributes() {
+    return attributes;
+  }
+
+  public NodeInfo addSection(ProtobufSystemInfo.Section section) {
+    this.sections.add(section);
+    return this;
+  }
+
+  public List<ProtobufSystemInfo.Section> getSections() {
+    return sections;
+  }
+
+  @Override
+  public boolean equals(Object o) {
+    if (this == o) {
+      return true;
+    }
+    if (o == null || getClass() != o.getClass()) {
+      return false;
+    }
+    NodeInfo nodeInfo = (NodeInfo) o;
+    return name.equals(nodeInfo.name);
+  }
+
+  @Override
+  public int hashCode() {
+    return name.hashCode();
+  }
+}
diff --git a/server/sonar-server/src/main/java/org/sonar/server/platform/monitoring/cluster/NodeSystemSection.java b/server/sonar-server/src/main/java/org/sonar/server/platform/monitoring/cluster/NodeSystemSection.java
new file mode 100644 (file)
index 0000000..8ab8a07
--- /dev/null
@@ -0,0 +1,71 @@
+/*
+ * 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.cluster;
+
+import org.sonar.api.config.Configuration;
+import org.sonar.api.platform.Server;
+import org.sonar.api.server.ServerSide;
+import org.sonar.process.ProcessProperties;
+import org.sonar.process.systeminfo.SystemInfoSection;
+import org.sonar.process.systeminfo.protobuf.ProtobufSystemInfo;
+import org.sonar.server.health.Health;
+import org.sonar.server.health.HealthChecker;
+import org.sonar.server.platform.ServerLogging;
+import org.sonar.server.platform.monitoring.OfficialDistribution;
+
+import static org.sonar.process.systeminfo.SystemInfoUtils.setAttribute;
+
+@ServerSide
+public class NodeSystemSection implements SystemInfoSection {
+
+  private final Configuration config;
+  private final Server server;
+  private final ServerLogging serverLogging;
+  private final OfficialDistribution officialDistribution;
+  private final HealthChecker healthChecker;
+
+  public NodeSystemSection(Configuration config, Server server, ServerLogging serverLogging,
+    OfficialDistribution officialDistribution, HealthChecker healthChecker) {
+    this.config = config;
+    this.server = server;
+    this.serverLogging = serverLogging;
+    this.officialDistribution = officialDistribution;
+    this.healthChecker = healthChecker;
+  }
+
+  @Override
+  public ProtobufSystemInfo.Section toProtobuf() {
+    ProtobufSystemInfo.Section.Builder protobuf = ProtobufSystemInfo.Section.newBuilder();
+    protobuf.setName("System");
+
+    Health health = healthChecker.checkNode();
+    setAttribute(protobuf, "Health", health.getStatus().name());
+    setAttribute(protobuf, "Health Causes", health.getCauses());
+    setAttribute(protobuf, "Version", server.getVersion());
+    setAttribute(protobuf, "Official Distribution", officialDistribution.check());
+    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", serverLogging.getRootLoggerLevel().name());
+    return protobuf.build();
+  }
+
+}
diff --git a/server/sonar-server/src/main/java/org/sonar/server/platform/monitoring/cluster/ProcessInfoProvider.java b/server/sonar-server/src/main/java/org/sonar/server/platform/monitoring/cluster/ProcessInfoProvider.java
new file mode 100644 (file)
index 0000000..3f4350c
--- /dev/null
@@ -0,0 +1,61 @@
+/*
+ * 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.cluster;
+
+import java.util.Arrays;
+import java.util.List;
+import java.util.stream.Collectors;
+import org.sonar.api.Startable;
+import org.sonar.api.ce.ComputeEngineSide;
+import org.sonar.api.server.ServerSide;
+import org.sonar.process.systeminfo.Global;
+import org.sonar.process.systeminfo.SystemInfoSection;
+import org.sonar.process.systeminfo.protobuf.ProtobufSystemInfo;
+
+@ServerSide
+@ComputeEngineSide
+public class ProcessInfoProvider implements Startable {
+
+  /** Used for Hazelcast's distributed queries in cluster mode */
+  private static ProcessInfoProvider INSTANCE;
+  private final List<SystemInfoSection> sections;
+
+  public ProcessInfoProvider(SystemInfoSection[] sections) {
+    this.sections = Arrays.stream(sections)
+      .filter(section -> !(section instanceof Global))
+      .collect(Collectors.toList());
+  }
+
+  @Override
+  public void start() {
+    INSTANCE = this;
+  }
+
+  @Override
+  public void stop() {
+    INSTANCE = null;
+  }
+
+  public static ProtobufSystemInfo.SystemInfo provide() {
+    ProtobufSystemInfo.SystemInfo.Builder protobuf = ProtobufSystemInfo.SystemInfo.newBuilder();
+    INSTANCE.sections.forEach(section -> protobuf.addSections(section.toProtobuf()));
+    return protobuf.build();
+  }
+}
diff --git a/server/sonar-server/src/main/java/org/sonar/server/platform/monitoring/cluster/package-info.java b/server/sonar-server/src/main/java/org/sonar/server/platform/monitoring/cluster/package-info.java
new file mode 100644 (file)
index 0000000..ef39a62
--- /dev/null
@@ -0,0 +1,23 @@
+/*
+ * 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.
+ */
+@ParametersAreNonnullByDefault
+package org.sonar.server.platform.monitoring.cluster;
+
+import javax.annotation.ParametersAreNonnullByDefault;
index d298d99c03ab5bc32e7e0107f47e3dcb0a219aa7..87264373367605e4d5ecf9b442d58c6e706f317f 100644 (file)
  */
 package org.sonar.server.platform.ws;
 
-import java.util.Arrays;
+import java.util.Collection;
 import org.sonar.api.server.ws.Request;
 import org.sonar.api.server.ws.Response;
 import org.sonar.api.server.ws.WebService;
 import org.sonar.api.utils.text.JsonWriter;
+import org.sonar.process.systeminfo.protobuf.ProtobufSystemInfo;
+import org.sonar.server.platform.monitoring.cluster.AppNodesInfoLoader;
+import org.sonar.server.platform.monitoring.cluster.GlobalInfoLoader;
+import org.sonar.server.platform.monitoring.cluster.NodeInfo;
 import org.sonar.server.user.UserSession;
 
 public class ClusterInfoAction implements SystemWsAction {
 
   private final UserSession userSession;
+  private final GlobalInfoLoader globalInfoLoader;
+  private final AppNodesInfoLoader appNodesInfoLoader;
 
-  public ClusterInfoAction(UserSession userSession) {
+  public ClusterInfoAction(UserSession userSession, GlobalInfoLoader globalInfoLoader, AppNodesInfoLoader appNodesInfoLoader) {
     this.userSession = userSession;
+    this.globalInfoLoader = globalInfoLoader;
+    this.appNodesInfoLoader = appNodesInfoLoader;
   }
 
   @Override
@@ -50,143 +58,200 @@ public class ClusterInfoAction implements SystemWsAction {
 
     try (JsonWriter json = response.newJsonWriter()) {
       json.beginObject();
-
-      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().values(Arrays.asList("foo", "bar")).endArray();
-      json.endObject();
-
-      json.name("Settings");
-      json.beginObject();
-      json.prop("sonar.forceAuthentication", true);
-      json.prop("sonar.externalIdentityProviders", "GitHub, BitBucket");
+      writeGlobal(json);
+      writeApplicationNodes(json);
       json.endObject();
+    }
 
-      json.name("Database");
-      json
-        .beginObject()
-        .prop("Name", "PostgreSQL")
-        .prop("Version", "9.6.3")
-        .endObject();
-
-      json.name("Compute Engine");
-      json
-        .beginObject()
-        .prop("Pending", 5)
-        .prop("In Progress", 4)
-        .prop("workers", 8)
-        .prop("workersPerNode", 4)
-        .endObject();
-
-      json.name("Search");
-      json
-        .beginObject()
-        .prop("Health", "GREEN")
-        .prop("Number of Nodes", 4)
-        .prop("Index Components - Docs", 152_515_155)
-        .prop("Index Components - Shards", 20)
-        .prop("Index Components - Size", "25GB")
-        .prop("Index Issues - Docs", 5)
-        .prop("Index Issues - Shards", 5)
-        .prop("Index Issues - Size", "52MB")
-        .prop("Index Tests - Docs", 56605)
-        .prop("Index Tests - Shards", 2)
-        .prop("Index Tests - Size", "520MB")
-        .endObject();
-
-      json.name("Application Nodes");
-      json
-        .beginArray()
-        .beginObject()
-        .prop("Name", "Mont Blanc")
-        .prop("Host", "10.158.92.16")
-        .prop("Health", "YELLOW")
-        .name("healthCauses").beginArray().beginObject().prop("message", "Db connectivity error").endObject().endArray()
-        .prop("Start Time", "2017-05-30T10:23:45")
-        .prop("Official Distribution", true)
-        .prop("Processors", 4);
-      json
-        .name("Web JVM").beginObject()
-        .prop("JVM Name", "Java HotSpot(TM) 64-Bit Server VM")
-        .prop("JVM Vendor", "Oracle Corporation")
-        .prop("Max Memory", "948MB")
-        .prop("Free Memory", "38MB")
-        .endObject()
-
-        .name("Web JVM Properties").beginObject()
-        .prop("catalina.home", "/sonarsource/var/tmp/sonarsource/sssonarqube/tc")
-        .prop("glowroot.tmp.dir", "/var/tmp/sonarsource/ssglowroot-agent")
-        .prop("glowroot.adad.dir", "/var/tmp/sonarsource/ssglowroot-agent")
-        .prop("java.specification.version", "1.8")
-        .endObject()
-
-        .name("Web Database Connectivity").beginObject()
-        .prop("Driver", "PostgreSQL JDBC Driver")
-        .prop("Driver Version", "PostgreSQL JDBC Driver")
-        .prop("Pool Idle Connections", 2)
-        .prop("Pool Max Connections", 50)
-        .prop("URL", "jdbc:postgresql://next-rds.cn6pfc2xc6oq.us-east-1.rds.amazonaws.com/dory")
-        .endObject();
-
-      json
-        .name("Compute Engine JVM").beginObject()
-        .prop("JVM Name", "Java HotSpot(TM) 64-Bit Server VM")
-        .prop("JVM Vendor", "Oracle Corporation")
-        .prop("Max Memory", "25MB")
-        .prop("Free Memory", "8MB")
-        .endObject();
-
-      json
-        .name("Compute Engine JVM Properties").beginObject()
-        .prop("java.ext.dirs", "/opt/sonarsource/jvm/java-1.8.0-sun-x64/jre/lib/ext:/usr/java/packages/lib/ext")
-        .prop("java.io.tmpdir", "/opt/sonarsource/jvm/java-1.8.0-sun-x64/jre/lib/ext:/usr/java/packages/lib/ext")
-        .prop("java.library.path", "/opt/sonarsource/jvm/java-1.8.0-sun-x64/jre/lib/ext:/usr/java/packages/lib/ext")
-        .prop("java.net.preferIPv4Stack", true)
-        .prop("java.rmi.server.randomIDs", true)
-        .prop("java.specification.version", "1.8")
-        .endObject();
+//    try (JsonWriter json = response.newJsonWriter()) {
+//      json.beginObject();
+//
+//      // global section
+//      json.prop("Cluster", true);
+//      json.prop("Cluster Name", "foo");
+//      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();
+//
+//      json.name("Settings");
+//      json.beginObject();
+//      json.prop("sonar.forceAuthentication", true);
+//      json.prop("sonar.externalIdentityProviders", "GitHub, BitBucket");
+//      json.endObject();
+//
+//      json.name("Database");
+//      json
+//        .beginObject()
+//        .prop("Name", "PostgreSQL")
+//        .prop("Version", "9.6.3")
+//        .endObject();
+//
+//      json.name("Compute Engine");
+//      json
+//        .beginObject()
+//        .prop("Pending", 5)
+//        .prop("In Progress", 4)
+//        .prop("workers", 8)
+//        .prop("workersPerNode", 4)
+//        .endObject();
+//
+//      json.name("Elasticsearch");
+//      json
+//        .beginObject()
+//        .prop("Health", "GREEN")
+//        .prop("Number of Nodes", 4)
+//        .prop("Index Components - Docs", 152_515_155)
+//        .prop("Index Components - Shards", 20)
+//        .prop("Index Components - Size", "25GB")
+//        .prop("Index Issues - Docs", 5)
+//        .prop("Index Issues - Shards", 5)
+//        .prop("Index Issues - Size", "52MB")
+//        .prop("Index Tests - Docs", 56605)
+//        .prop("Index Tests - Shards", 2)
+//        .prop("Index Tests - Size", "520MB")
+//        .endObject();
+//
+//      json.name("Application Nodes");
+//      json
+//        .beginArray()
+//        .beginObject()
+//        .prop("Name", "Mont Blanc")
+//        .prop("Host", "10.158.92.16")
+//        .prop("Health", "YELLOW")
+//        .name("healthCauses").beginArray().beginObject().prop("message", "Db connectivity error").endObject().endArray()
+//        .prop("Start Time", "2017-05-30T10:23:45")
+//        .prop("Official Distribution", true)
+//        .prop("Processors", 4);
+//      json
+//        .name("Web JVM").beginObject()
+//        .prop("JVM Name", "Java HotSpot(TM) 64-Bit Server VM")
+//        .prop("JVM Vendor", "Oracle Corporation")
+//        .prop("Max Memory", "948MB")
+//        .prop("Free Memory", "38MB")
+//        .endObject()
+//
+//        .name("Web JVM Properties").beginObject()
+//        .prop("catalina.home", "/sonarsource/var/tmp/sonarsource/sssonarqube/tc")
+//        .prop("glowroot.tmp.dir", "/var/tmp/sonarsource/ssglowroot-agent")
+//        .prop("glowroot.adad.dir", "/var/tmp/sonarsource/ssglowroot-agent")
+//        .prop("java.specification.version", "1.8")
+//        .endObject()
+//
+//        .name("Web Database Connectivity").beginObject()
+//        .prop("Driver", "PostgreSQL JDBC Driver")
+//        .prop("Driver Version", "PostgreSQL JDBC Driver")
+//        .prop("Pool Idle Connections", 2)
+//        .prop("Pool Max Connections", 50)
+//        .prop("URL", "jdbc:postgresql://next-rds.cn6pfc2xc6oq.us-east-1.rds.amazonaws.com/dory")
+//        .endObject();
+//
+//      json
+//        .name("Compute Engine JVM").beginObject()
+//        .prop("JVM Name", "Java HotSpot(TM) 64-Bit Server VM")
+//        .prop("JVM Vendor", "Oracle Corporation")
+//        .prop("Max Memory", "25MB")
+//        .prop("Free Memory", "8MB")
+//        .endObject();
+//
+//      json
+//        .name("Compute Engine JVM Properties").beginObject()
+//        .prop("java.ext.dirs", "/opt/sonarsource/jvm/java-1.8.0-sun-x64/jre/lib/ext:/usr/java/packages/lib/ext")
+//        .prop("java.io.tmpdir", "/opt/sonarsource/jvm/java-1.8.0-sun-x64/jre/lib/ext:/usr/java/packages/lib/ext")
+//        .prop("java.library.path", "/opt/sonarsource/jvm/java-1.8.0-sun-x64/jre/lib/ext:/usr/java/packages/lib/ext")
+//        .prop("java.net.preferIPv4Stack", true)
+//        .prop("java.rmi.server.randomIDs", true)
+//        .prop("java.specification.version", "1.8")
+//        .endObject();
+//
+//      json.endObject().endArray();
+//
+//      json.name("Search Nodes");
+//      json
+//        .beginArray()
+//        .beginObject()
+//        .prop("Name", "Parmelan")
+//        .prop("Host", "10.158.92.19")
+//        .prop("Health", "GREEN")
+//        .name("Health Causes").beginArray().endArray()
+//        .prop("Start Time", "2017-05-30T10:23:45")
+//        .prop("Processors", 2)
+//        .prop("Disk Available", "25GB")
+//        .prop("JVM Threads", 52)
+//
+//        .name("JVM Properties").beginObject()
+//        .prop("java.ext.dirs", "/opt/sonarsource/jvm/java-1.8.0-sun-x64/jre/lib/ext:/usr/java/packages/lib/ext")
+//        .prop("java.io.tmpdir", "/opt/sonarsource/jvm/java-1.8.0-sun-x64/jre/lib/ext:/usr/java/packages/lib/ext")
+//        .prop("java.library.path", "/opt/sonarsource/jvm/java-1.8.0-sun-x64/jre/lib/ext:/usr/java/packages/lib/ext")
+//        .prop("java.net.preferIPv4Stack", true)
+//        .prop("java.rmi.server.randomIDs", true)
+//        .endObject()
+//
+//        .name("JVM").beginObject()
+//        .prop("java.ext.dirs", "/opt/sonarsource/jvm/java-1.8.0-sun-x64/jre/lib/ext:/usr/java/packages/lib/ext")
+//        .prop("java.io.tmpdir", "/opt/sonarsource/jvm/java-1.8.0-sun-x64/jre/lib/ext:/usr/java/packages/lib/ext")
+//        .prop("java.library.path", "/opt/sonarsource/jvm/java-1.8.0-sun-x64/jre/lib/ext:/usr/java/packages/lib/ext")
+//        .prop("java.net.preferIPv4Stack", true)
+//        .prop("java.rmi.server.randomIDs", true)
+//        .endObject()
+//
+//        .endObject()
+//        .endArray();
+//
+//      json.endObject();
+//    }
+  }
 
-      json.endObject().endArray();
+  private void writeGlobal(JsonWriter json) {
+    globalInfoLoader.load().forEach(section -> sectionToJson(section, json));
+  }
 
-      json.name("Search Nodes");
-      json
-        .beginArray()
-        .beginObject()
-        .prop("Name", "Parmelan")
-        .prop("Host", "10.158.92.19")
-        .prop("Health", "GREEN")
-        .name("Health Causes").beginArray().endArray()
-        .prop("Start Time", "2017-05-30T10:23:45")
-        .prop("Processors", 2)
-        .prop("Disk Available", "25GB")
-        .prop("JVM Threads", 52)
+  private void writeApplicationNodes(JsonWriter json) {
+    json.name("Application Nodes").beginArray();
 
-        .name("JVM Properties").beginObject()
-        .prop("java.ext.dirs", "/opt/sonarsource/jvm/java-1.8.0-sun-x64/jre/lib/ext:/usr/java/packages/lib/ext")
-        .prop("java.io.tmpdir", "/opt/sonarsource/jvm/java-1.8.0-sun-x64/jre/lib/ext:/usr/java/packages/lib/ext")
-        .prop("java.library.path", "/opt/sonarsource/jvm/java-1.8.0-sun-x64/jre/lib/ext:/usr/java/packages/lib/ext")
-        .prop("java.net.preferIPv4Stack", true)
-        .prop("java.rmi.server.randomIDs", true)
-        .endObject()
+    Collection<NodeInfo> appNodes = appNodesInfoLoader.load();
+    for (NodeInfo applicationNode : appNodes) {
+      writeApplicationNode(json, applicationNode);
+    }
+    json.endArray();
+  }
 
-        .name("JVM").beginObject()
-        .prop("java.ext.dirs", "/opt/sonarsource/jvm/java-1.8.0-sun-x64/jre/lib/ext:/usr/java/packages/lib/ext")
-        .prop("java.io.tmpdir", "/opt/sonarsource/jvm/java-1.8.0-sun-x64/jre/lib/ext:/usr/java/packages/lib/ext")
-        .prop("java.library.path", "/opt/sonarsource/jvm/java-1.8.0-sun-x64/jre/lib/ext:/usr/java/packages/lib/ext")
-        .prop("java.net.preferIPv4Stack", true)
-        .prop("java.rmi.server.randomIDs", true)
-        .endObject()
+  private void writeApplicationNode(JsonWriter json, NodeInfo applicationNode) {
+    json.beginObject();
+    json.prop("Name", applicationNode.getName());
+    applicationNode.getSections().forEach(section -> sectionToJson(section, json));
+    json.endObject();
+  }
 
-        .endObject()
-        .endArray();
+  private static void sectionToJson(ProtobufSystemInfo.Section section, JsonWriter json) {
+    json.name(section.getName());
+    json.beginObject();
+    for (ProtobufSystemInfo.Attribute attribute : section.getAttributesList()) {
+      attributeToJson(json, attribute);
+    }
+    json.endObject();
+  }
 
-      json.endObject();
+  private static void attributeToJson(JsonWriter json, ProtobufSystemInfo.Attribute attribute) {
+    switch (attribute.getValueCase()) {
+      case BOOLEAN_VALUE:
+        json.prop(attribute.getKey(), attribute.getBooleanValue());
+        break;
+      case LONG_VALUE:
+        json.prop(attribute.getKey(), attribute.getLongValue());
+        break;
+      case DOUBLE_VALUE:
+        json.prop(attribute.getKey(), attribute.getDoubleValue());
+        break;
+      case STRING_VALUE:
+        json.prop(attribute.getKey(), attribute.getStringValue());
+        break;
+      case VALUE_NOT_SET:
+        json.name(attribute.getKey()).beginArray().values(attribute.getStringValuesList()).endArray();
+        break;
+      default:
+        throw new IllegalArgumentException("Unsupported type: " + attribute.getValueCase());
     }
   }
 }
index 39491bc60d91e17d69cf09bb9e7b3cb138894a91..e19cea3974f066105812a94a8d7c8f85365e821f 100644 (file)
  */
 package org.sonar.server.platform.ws;
 
-import java.util.Arrays;
-import java.util.Optional;
+import java.util.List;
 import org.sonar.api.server.ws.Request;
 import org.sonar.api.server.ws.Response;
 import org.sonar.api.server.ws.WebService;
 import org.sonar.api.utils.text.JsonWriter;
 import org.sonar.ce.http.CeHttpClient;
+import org.sonar.core.util.stream.MoreCollectors;
 import org.sonar.process.systeminfo.SystemInfoSection;
+import org.sonar.process.systeminfo.SystemInfoUtils;
 import org.sonar.process.systeminfo.protobuf.ProtobufSystemInfo;
 import org.sonar.server.telemetry.TelemetryDataLoader;
 import org.sonar.server.user.UserSession;
 
+import static java.util.Arrays.stream;
 import static org.sonar.server.telemetry.TelemetryDataJsonWriter.writeTelemetryData;
 
 /**
@@ -38,6 +40,9 @@ import static org.sonar.server.telemetry.TelemetryDataJsonWriter.writeTelemetryD
  */
 public class InfoAction implements SystemWsAction {
 
+  private static final String[] ORDERED_SECTION_NAMES = {
+    "System", "Database", "Web JVM Properties", "Web JVM State", "Search State", "Search Statistics",
+    "Compute Engine Database Connection", "Compute Engine JVM State", "Compute Engine Tasks"};
   private final UserSession userSession;
   private final CeHttpClient ceHttpClient;
   private final SystemInfoSection[] systemInfoSections;
@@ -73,14 +78,18 @@ public class InfoAction implements SystemWsAction {
 
   private void writeJson(JsonWriter json) {
     json.beginObject();
-    Arrays.stream(systemInfoSections)
+
+    List<ProtobufSystemInfo.Section> sections = stream(systemInfoSections)
       .map(SystemInfoSection::toProtobuf)
+      .collect(MoreCollectors.toArrayList());
+    ceHttpClient.retrieveSystemInfo()
+      .ifPresent(ce -> sections.addAll(ce.getSectionsList()));
+    SystemInfoUtils
+      .order(sections, ORDERED_SECTION_NAMES)
       .forEach(section -> sectionToJson(section, json));
-    Optional<ProtobufSystemInfo.SystemInfo> ceSysInfo = ceHttpClient.retrieveSystemInfo();
-    if (ceSysInfo.isPresent()) {
-      ceSysInfo.get().getSectionsList().forEach(section -> sectionToJson(section, json));
-    }
+
     writeStatistics(json);
+
     json.endObject();
   }
 
diff --git a/server/sonar-server/src/main/java/org/sonar/server/platform/ws/InfoActionModule.java b/server/sonar-server/src/main/java/org/sonar/server/platform/ws/InfoActionModule.java
deleted file mode 100644 (file)
index fb3f385..0000000
+++ /dev/null
@@ -1,33 +0,0 @@
-/*
- * 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.ws;
-
-import org.sonar.core.platform.Module;
-import org.sonar.server.telemetry.TelemetryDataLoader;
-
-public class InfoActionModule extends Module {
-  @Override
-  protected void configureModule() {
-    add(TelemetryDataLoader.class,
-      InfoAction.class,
-      ClusterInfoAction.class,
-      StandaloneInfoAction.class);
-  }
-}
diff --git a/server/sonar-server/src/main/java/org/sonar/server/telemetry/TelemetryModule.java b/server/sonar-server/src/main/java/org/sonar/server/telemetry/TelemetryModule.java
deleted file mode 100644 (file)
index 447f11e..0000000
+++ /dev/null
@@ -1,30 +0,0 @@
-/*
- * 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.telemetry;
-
-import org.sonar.core.platform.Module;
-
-public class TelemetryModule extends Module {
-  @Override
-  protected void configureModule() {
-    add(TelemetryDaemon.class,
-      TelemetryClient.class);
-  }
-}
diff --git a/server/sonar-server/src/test/java/org/sonar/server/platform/monitoring/OfficialDistributionTest.java b/server/sonar-server/src/test/java/org/sonar/server/platform/monitoring/OfficialDistributionTest.java
new file mode 100644 (file)
index 0000000..3f6a1cd
--- /dev/null
@@ -0,0 +1,59 @@
+/*
+ * 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 org.apache.commons.io.FileUtils;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.TemporaryFolder;
+import org.sonar.api.platform.Server;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+
+public class OfficialDistributionTest {
+
+  @Rule
+  public TemporaryFolder temp = new TemporaryFolder();
+
+  private Server server = mock(Server.class);
+  private OfficialDistribution underTest = new OfficialDistribution(server);
+
+  @Test
+  public void official_distribution() throws Exception {
+    File rootDir = temp.newFolder();
+    FileUtils.write(new File(rootDir, OfficialDistribution.BRANDING_FILE_PATH), "1.2");
+    when(server.getRootDir()).thenReturn(rootDir);
+
+    assertThat(underTest.check()).isTrue();
+  }
+
+  @Test
+  public void not_an_official_distribution() throws Exception {
+    File rootDir = temp.newFolder();
+    // branding file is missing
+    when(server.getRootDir()).thenReturn(rootDir);
+
+    assertThat(underTest.check()).isFalse();
+  }
+}
diff --git a/server/sonar-server/src/test/java/org/sonar/server/platform/monitoring/StandaloneSystemSectionTest.java b/server/sonar-server/src/test/java/org/sonar/server/platform/monitoring/StandaloneSystemSectionTest.java
new file mode 100644 (file)
index 0000000..ea5f52d
--- /dev/null
@@ -0,0 +1,213 @@
+/*
+ * 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.Optional;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+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 StandaloneSystemSectionTest {
+
+  private static final String SERVER_ID_PROPERTY = "Server ID";
+  private static final String SERVER_ID_VALIDATED_PROPERTY = "Server ID validated";
+
+  @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 OfficialDistribution officialDistribution = mock(OfficialDistribution.class);
+
+  private StandaloneSystemSection underTest = new StandaloneSystemSection(settings.asConfig(), securityRealmFactory, identityProviderRepository, server,
+    serverLogging, serverIdLoader, officialDistribution, 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 {
+    when(officialDistribution.check()).thenReturn(true);
+
+    ProtobufSystemInfo.Section protobuf = underTest.toProtobuf();
+    assertThatAttributeIs(protobuf, "Official Distribution", true);
+  }
+
+  @Test
+  public void not_an_official_distribution() throws Exception {
+    when(officialDistribution.check()).thenReturn(false);
+
+    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"));
+  }
+
+  @Test
+  public void return_nb_of_processors() {
+    ProtobufSystemInfo.Section protobuf = underTest.toProtobuf();
+    assertThat(attribute(protobuf, "Processors").getLongValue()).isGreaterThan(0);
+  }
+}
diff --git a/server/sonar-server/src/test/java/org/sonar/server/platform/monitoring/SystemSectionTest.java b/server/sonar-server/src/test/java/org/sonar/server/platform/monitoring/SystemSectionTest.java
deleted file mode 100644 (file)
index c1ae9b3..0000000
+++ /dev/null
@@ -1,217 +0,0 @@
-/*
- * 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 SystemSectionTest {
-
-  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 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 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"));
-  }
-}
diff --git a/server/sonar-server/src/test/java/org/sonar/server/platform/monitoring/cluster/NodeInfoTest.java b/server/sonar-server/src/test/java/org/sonar/server/platform/monitoring/cluster/NodeInfoTest.java
new file mode 100644 (file)
index 0000000..04e67d6
--- /dev/null
@@ -0,0 +1,42 @@
+/*
+ * 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.cluster;
+
+import org.junit.Test;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+public class NodeInfoTest {
+
+  @Test
+  public void test_equals_and_hashCode() {
+    NodeInfo foo = new NodeInfo("foo");
+    NodeInfo bar = new NodeInfo("bar");
+    NodeInfo bar2 = new NodeInfo("bar");
+
+    assertThat(foo.equals(foo)).isTrue();
+    assertThat(foo.equals(bar)).isFalse();
+    assertThat(bar.equals(bar2)).isTrue();
+
+    assertThat(bar.hashCode()).isEqualTo(bar.hashCode());
+    assertThat(bar.hashCode()).isEqualTo(bar2.hashCode());
+  }
+
+}