aboutsummaryrefslogtreecommitdiffstats
path: root/tests
diff options
context:
space:
mode:
authorEric Hartmann <hartmann.eric@gmail.com>2017-05-17 17:36:19 +0200
committerSimon Brandhof <simon.brandhof@sonarsource.com>2017-09-05 14:24:12 +0200
commitc7ae1857b15ac53d7442cdf33685036455b8deb4 (patch)
treebc781869e388cb9203e636e2b375f2d98fa015db /tests
parent958fa89f09a58082139b440269704c4e4955cdd7 (diff)
downloadsonarqube-c7ae1857b15ac53d7442cdf33685036455b8deb4.tar.gz
sonarqube-c7ae1857b15ac53d7442cdf33685036455b8deb4.zip
Add ITs on cluster
Diffstat (limited to 'tests')
-rw-r--r--tests/src/test/java/org/sonarqube/tests/Category5Suite.java4
-rw-r--r--tests/src/test/java/org/sonarqube/tests/cluster/Cluster.java312
-rw-r--r--tests/src/test/java/org/sonarqube/tests/cluster/DataCenterEdition.java51
-rw-r--r--tests/src/test/java/org/sonarqube/tests/cluster/DataCenterEditionTest.java33
-rw-r--r--tests/src/test/java/org/sonarqube/tests/cluster/StartupLogWatcherImpl.java33
5 files changed, 432 insertions, 1 deletions
diff --git a/tests/src/test/java/org/sonarqube/tests/Category5Suite.java b/tests/src/test/java/org/sonarqube/tests/Category5Suite.java
index 19ed5d87d37..fd12d951546 100644
--- a/tests/src/test/java/org/sonarqube/tests/Category5Suite.java
+++ b/tests/src/test/java/org/sonarqube/tests/Category5Suite.java
@@ -22,6 +22,7 @@ package org.sonarqube.tests;
import org.junit.runner.RunWith;
import org.junit.runners.Suite;
import org.sonarqube.tests.ce.CeWorkersTest;
+import org.sonarqube.tests.cluster.DataCenterEditionTest;
import org.sonarqube.tests.qualityProfile.ActiveRuleEsResilienceTest;
import org.sonarqube.tests.qualityProfile.BuiltInQualityProfilesNotificationTest;
import org.sonarqube.tests.rule.RuleEsResilienceTest;
@@ -62,7 +63,8 @@ import org.sonarqube.tests.user.UserEsResilienceTest;
TelemetryUploadTest.class,
TelemetryOptOutTest.class,
// ce
- CeWorkersTest.class
+ CeWorkersTest.class,
+ DataCenterEditionTest.class
})
public class Category5Suite {
diff --git a/tests/src/test/java/org/sonarqube/tests/cluster/Cluster.java b/tests/src/test/java/org/sonarqube/tests/cluster/Cluster.java
new file mode 100644
index 00000000000..d6f0e10c813
--- /dev/null
+++ b/tests/src/test/java/org/sonarqube/tests/cluster/Cluster.java
@@ -0,0 +1,312 @@
+/*
+ * 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.sonarqube.tests.cluster;
+
+import com.google.common.net.HostAndPort;
+import com.sonar.orchestrator.Orchestrator;
+import com.sonar.orchestrator.OrchestratorBuilder;
+import com.sonar.orchestrator.util.NetworkUtils;
+import java.net.Inet4Address;
+import java.net.InetAddress;
+import java.net.NetworkInterface;
+import java.net.SocketException;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.EnumSet;
+import java.util.Enumeration;
+import java.util.List;
+import java.util.Properties;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.ForkJoinPool;
+import java.util.function.Predicate;
+import java.util.stream.Collectors;
+
+import static org.sonarqube.tests.cluster.Cluster.NodeType.CE;
+import static org.sonarqube.tests.cluster.Cluster.NodeType.ES;
+import static org.sonarqube.tests.cluster.Cluster.NodeType.WEB;
+
+public class Cluster {
+
+ protected static final String CLUSTER_ENABLED = "sonar.cluster.enabled";
+ protected static final String CLUSTER_CE_DISABLED = "sonar.cluster.ce.disabled";
+ protected static final String CLUSTER_SEARCH_DISABLED = "sonar.cluster.search.disabled";
+ protected static final String CLUSTER_SEARCH_HOSTS = "sonar.cluster.search.hosts";
+ protected static final String CLUSTER_WEB_DISABLED = "sonar.cluster.web.disabled";
+ protected static final String CLUSTER_HOSTS = "sonar.cluster.hosts";
+ protected static final String CLUSTER_PORT = "sonar.cluster.port";
+ protected static final String CLUSTER_NETWORK_INTERFACES = "sonar.cluster.networkInterfaces";
+ protected static final String CLUSTER_NAME = "sonar.cluster.name";
+
+ protected static final String SEARCH_HOST = "sonar.search.host";
+ protected static final String SEARCH_PORT = "sonar.search.port";
+ protected static final String SEARCH_JAVA_OPTS = "sonar.search.javaOpts";
+
+ protected static final String WEB_JAVA_OPTS = "sonar.web.javaOpts";
+ protected static final String WEB_PORT = "sonar.web.port";
+
+ protected static final String CE_JAVA_OPTS = "sonar.ce.javaOpts";
+
+ public enum NodeType {
+ ES, CE, WEB;
+
+ public static final EnumSet<NodeType> ALL = EnumSet.allOf(NodeType.class);
+ }
+
+ private final List<Node> nodes;
+ private final ForkJoinPool forkJoinPool = new ForkJoinPool(5);
+
+ private Cluster(List<Node> nodes) {
+ this.nodes = nodes;
+ assignPorts();
+ completeNodesConfiguration();
+ buildOrchestrators();
+ }
+
+ public void start() throws ExecutionException, InterruptedException {
+ forkJoinPool.submit(
+ () -> nodes.parallelStream().forEach(
+ node -> node.getOrchestrator().start()
+ )
+ ).get();
+ }
+
+ public void stop() throws ExecutionException, InterruptedException {
+ forkJoinPool.submit(
+ () -> nodes.parallelStream().forEach(
+ node -> node.getOrchestrator().stop()
+ )
+ ).get();
+ }
+
+ public void stopAll(Predicate<Node> predicate) throws ExecutionException, InterruptedException {
+ forkJoinPool.submit(
+ () -> nodes.parallelStream()
+ .filter(predicate)
+ .forEach(n -> n.getOrchestrator().stop())
+ ).get();
+ }
+
+ public void startAll(Predicate<Node> predicate) throws ExecutionException, InterruptedException {
+ forkJoinPool.submit(
+ () -> nodes.parallelStream()
+ .filter(predicate)
+ .forEach(n -> n.getOrchestrator().start())
+ ).get();
+ }
+
+ public List<Node> getNodes() {
+ return Collections.unmodifiableList(nodes);
+ }
+
+ private void assignPorts() {
+ nodes.stream().forEach(
+ node -> {
+ node.setHzPort(NetworkUtils.getNextAvailablePort(getNonloopbackIPv4Address()));
+ if (node.getTypes().contains(ES)) {
+ node.setEsPort(NetworkUtils.getNextAvailablePort(getNonloopbackIPv4Address()));
+ }
+ if (node.getTypes().contains(WEB)) {
+ node.setWebPort(NetworkUtils.getNextAvailablePort(getNonloopbackIPv4Address()));
+ }
+ }
+ );
+ }
+
+ public static InetAddress getNonloopbackIPv4Address() {
+ try {
+ Enumeration<NetworkInterface> nets = NetworkInterface.getNetworkInterfaces();
+ for (NetworkInterface networkInterface : Collections.list(nets)) {
+ if (!networkInterface.isLoopback() && networkInterface.isUp() && !isBlackListed(networkInterface)) {
+ Enumeration<InetAddress> inetAddresses = networkInterface.getInetAddresses();
+ while (inetAddresses.hasMoreElements()) {
+ InetAddress inetAddress = inetAddresses.nextElement();
+ if (inetAddress instanceof Inet4Address) {
+ return inetAddress;
+ }
+ }
+ }
+ }
+ } catch (SocketException se) {
+ throw new RuntimeException("Cannot find a non loopback card required for tests", se);
+ }
+ throw new RuntimeException("Cannot find a non loopback card required for tests");
+ }
+
+ private static boolean isBlackListed(NetworkInterface networkInterface) {
+ return networkInterface.getName().startsWith("docker") ||
+ networkInterface.getName().startsWith("vboxnet");
+ }
+
+ private void completeNodesConfiguration() {
+ String inet = getNonloopbackIPv4Address().getHostAddress();
+ String clusterHosts = nodes.stream()
+ .map(node -> HostAndPort.fromParts(inet, node.getHzPort()).toString())
+ .collect(Collectors.joining(","));
+ String elasticsearchHosts = nodes.stream()
+ .filter(node -> node.getTypes().contains(ES))
+ .map(node -> HostAndPort.fromParts(inet, node.getEsPort()).toString())
+ .collect(Collectors.joining(","));
+
+ nodes.stream().forEach(
+ node -> {
+ node.addProperty(CLUSTER_NETWORK_INTERFACES, inet);
+ node.addProperty(CLUSTER_HOSTS, clusterHosts);
+ node.addProperty(CLUSTER_PORT, Integer.toString(node.getHzPort()));
+ node.addProperty(CLUSTER_SEARCH_HOSTS, elasticsearchHosts);
+ node.addProperty(SEARCH_PORT, Integer.toString(node.getEsPort()));
+ node.addProperty(SEARCH_HOST, inet);
+ node.addProperty(WEB_PORT, Integer.toString(node.getWebPort()));
+
+ if (!node.getTypes().contains(CE)) {
+ node.addProperty(CLUSTER_CE_DISABLED, "true");
+ }
+ if (!node.getTypes().contains(WEB)) {
+ node.addProperty(CLUSTER_WEB_DISABLED, "true");
+ }
+ if (!node.getTypes().contains(ES)) {
+ node.addProperty(CLUSTER_SEARCH_DISABLED, "true");
+ }
+ }
+ );
+ }
+
+ private void buildOrchestrators() {
+ nodes.stream().limit(1).forEach(
+ node -> buildOrchestrator(node, false)
+ );
+ nodes.stream().skip(1).forEach(
+ node -> buildOrchestrator(node, true)
+ );
+ }
+
+ private void buildOrchestrator(Node node, boolean keepDatabase) {
+ OrchestratorBuilder builder = Orchestrator.builderEnv()
+ .setOrchestratorProperty("orchestrator.keepDatabase", Boolean.toString(keepDatabase))
+ .setStartupLogWatcher(new StartupLogWatcherImpl());
+
+ node.getProperties().entrySet().stream().forEach(
+ e -> builder.setServerProperty((String) e.getKey(), (String) e.getValue())
+ );
+
+ node.setOrchestrator(builder.build());
+ }
+
+ public static Builder builder() {
+ return new Builder();
+ }
+
+ public static class Builder {
+ private final List<Node> nodes = new ArrayList<>();
+
+ public Cluster build() {
+ return new Cluster(nodes);
+ }
+
+ public Builder addNode(EnumSet<NodeType> types) {
+ nodes.add(new Node(types));
+ return this;
+ }
+ }
+
+ /**
+ * A cluster node
+ */
+ public static class Node {
+ private final EnumSet<NodeType> types;
+ private int webPort;
+ private int esPort;
+ private int hzPort;
+ private Orchestrator orchestrator = null;
+ private Properties properties = new Properties();
+
+ public Node(EnumSet<NodeType> types) {
+ this.types = types;
+
+ // Default properties
+ properties.setProperty(CLUSTER_ENABLED, "true");
+ properties.setProperty(CLUSTER_NAME, "sonarqube");
+ properties.setProperty(CE_JAVA_OPTS, "-Xmx256m");
+ properties.setProperty(WEB_JAVA_OPTS, "-Xmx256m");
+ properties.setProperty(SEARCH_JAVA_OPTS, "-Xmx256m -Xms256m " +
+ "-XX:+UseConcMarkSweepGC " +
+ "-XX:CMSInitiatingOccupancyFraction=75 " +
+ "-XX:+UseCMSInitiatingOccupancyOnly " +
+ "-XX:+AlwaysPreTouch " +
+ "-server " +
+ "-Xss1m " +
+ "-Djava.awt.headless=true " +
+ "-Dfile.encoding=UTF-8 " +
+ "-Djna.nosys=true " +
+ "-Djdk.io.permissionsUseCanonicalPath=true " +
+ "-Dio.netty.noUnsafe=true " +
+ "-Dio.netty.noKeySetOptimization=true " +
+ "-Dio.netty.recycler.maxCapacityPerThread=0 " +
+ "-Dlog4j.shutdownHookEnabled=false " +
+ "-Dlog4j2.disable.jmx=true " +
+ "-Dlog4j.skipJansi=true " +
+ "-XX:+HeapDumpOnOutOfMemoryError");
+ }
+
+ public Properties getProperties() {
+ return properties;
+ }
+
+ public Orchestrator getOrchestrator() {
+ return orchestrator;
+ }
+
+ private void setOrchestrator(Orchestrator orchestrator) {
+ this.orchestrator = orchestrator;
+ }
+
+ public EnumSet<NodeType> getTypes() {
+ return types;
+ }
+
+ public int getWebPort() {
+ return webPort;
+ }
+
+ public int getEsPort() {
+ return esPort;
+ }
+
+ public int getHzPort() {
+ return hzPort;
+ }
+
+ private void setWebPort(int webPort) {
+ this.webPort = webPort;
+ }
+
+ private void setEsPort(int esPort) {
+ this.esPort = esPort;
+ }
+
+ private void setHzPort(int hzPort) {
+ this.hzPort = hzPort;
+ }
+
+ private void addProperty(String key, String value) {
+ properties.setProperty(key, value);
+ }
+ }
+}
diff --git a/tests/src/test/java/org/sonarqube/tests/cluster/DataCenterEdition.java b/tests/src/test/java/org/sonarqube/tests/cluster/DataCenterEdition.java
new file mode 100644
index 00000000000..07007e97c67
--- /dev/null
+++ b/tests/src/test/java/org/sonarqube/tests/cluster/DataCenterEdition.java
@@ -0,0 +1,51 @@
+/*
+ * 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.sonarqube.tests.cluster;
+
+import java.util.concurrent.ExecutionException;
+
+import static java.util.EnumSet.of;
+import static org.sonarqube.tests.cluster.Cluster.NodeType.CE;
+import static org.sonarqube.tests.cluster.Cluster.NodeType.ES;
+import static org.sonarqube.tests.cluster.Cluster.NodeType.WEB;
+
+public class DataCenterEdition {
+
+ private final Cluster cluster;
+
+ public DataCenterEdition() {
+ cluster = Cluster.builder()
+ .addNode(of(ES))
+ .addNode(of(ES))
+ .addNode(of(ES))
+ .addNode(of(WEB, CE))
+ .addNode(of(WEB, CE))
+ .build();
+ }
+
+ public void stop() throws ExecutionException, InterruptedException {
+ cluster.stop();
+ }
+
+ public void start() throws ExecutionException, InterruptedException {
+ cluster.start();
+ }
+}
diff --git a/tests/src/test/java/org/sonarqube/tests/cluster/DataCenterEditionTest.java b/tests/src/test/java/org/sonarqube/tests/cluster/DataCenterEditionTest.java
new file mode 100644
index 00000000000..e7b7634fcf3
--- /dev/null
+++ b/tests/src/test/java/org/sonarqube/tests/cluster/DataCenterEditionTest.java
@@ -0,0 +1,33 @@
+/*
+ * 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.sonarqube.tests.cluster;
+
+import java.util.concurrent.ExecutionException;
+import org.junit.Test;
+
+public class DataCenterEditionTest {
+ @Test
+ public void launch() throws ExecutionException, InterruptedException {
+ DataCenterEdition dce = new DataCenterEdition();
+ dce.start();
+ dce.stop();
+ }
+}
diff --git a/tests/src/test/java/org/sonarqube/tests/cluster/StartupLogWatcherImpl.java b/tests/src/test/java/org/sonarqube/tests/cluster/StartupLogWatcherImpl.java
new file mode 100644
index 00000000000..170ddc16872
--- /dev/null
+++ b/tests/src/test/java/org/sonarqube/tests/cluster/StartupLogWatcherImpl.java
@@ -0,0 +1,33 @@
+/*
+ * 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.sonarqube.tests.cluster;
+
+
+import com.sonar.orchestrator.server.StartupLogWatcher;
+
+public class StartupLogWatcherImpl implements StartupLogWatcher {
+ private static final String STARTUP_EXPECTED_MESSAGE = "SonarQube is up";
+
+ @Override
+ public boolean isStarted(String logLine) {
+ return logLine.contains(STARTUP_EXPECTED_MESSAGE);
+ }
+}