diff options
author | Simon Brandhof <simon.brandhof@sonarsource.com> | 2017-10-17 16:45:10 +0200 |
---|---|---|
committer | Simon Brandhof <simon.brandhof@sonarsource.com> | 2017-10-18 10:43:22 +0200 |
commit | 9e4fe4aca049a680ba0476312f89a5fe133412cf (patch) | |
tree | de8500e3b8c26cfe094f38dd782709a815a273de /tests | |
parent | d3195eafe8e0253fa8a7044013476de94fcb5edb (diff) | |
download | sonarqube-9e4fe4aca049a680ba0476312f89a5fe133412cf.tar.gz sonarqube-9e4fe4aca049a680ba0476312f89a5fe133412cf.zip |
Remove cluster tests
Diffstat (limited to 'tests')
5 files changed, 0 insertions, 1019 deletions
diff --git a/tests/src/test/java/org/sonarqube/tests/Category5Suite.java b/tests/src/test/java/org/sonarqube/tests/Category5Suite.java index bd4ee5b26d0..13e6c3d9921 100644 --- a/tests/src/test/java/org/sonarqube/tests/Category5Suite.java +++ b/tests/src/test/java/org/sonarqube/tests/Category5Suite.java @@ -25,7 +25,6 @@ import org.sonarqube.tests.analysis.AnalysisEsResilienceTest; import org.sonarqube.tests.authorisation.SystemPasscodeTest; import org.sonarqube.tests.ce.CeShutdownTest; import org.sonarqube.tests.ce.CeWorkersTest; -import org.sonarqube.tests.cluster.ClusterTest; import org.sonarqube.tests.issue.IssueCreationDatePluginChangedTest; import org.sonarqube.tests.qualityProfile.ActiveRuleEsResilienceTest; import org.sonarqube.tests.qualityProfile.BuiltInQualityProfilesNotificationTest; @@ -51,7 +50,6 @@ import org.sonarqube.tests.user.UserEsResilienceTest; */ @RunWith(Suite.class) @Suite.SuiteClasses({ - ClusterTest.class, ServerSystemRestartingOrchestrator.class, RestartTest.class, SettingsTestRestartingOrchestrator.class, diff --git a/tests/src/test/java/org/sonarqube/tests/cluster/Cluster.java b/tests/src/test/java/org/sonarqube/tests/cluster/Cluster.java deleted file mode 100644 index 58fb754f4b2..00000000000 --- a/tests/src/test/java/org/sonarqube/tests/cluster/Cluster.java +++ /dev/null @@ -1,131 +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.sonarqube.tests.cluster; - -import com.sonar.orchestrator.Orchestrator; -import com.sonar.orchestrator.OrchestratorBuilder; -import java.util.ArrayList; -import java.util.List; -import java.util.function.Consumer; -import java.util.stream.Stream; -import javax.annotation.Nullable; -import org.slf4j.LoggerFactory; -import util.ItUtils; - -import static java.util.stream.Collectors.joining; - -class Cluster implements AutoCloseable { - - @Nullable - private final String clusterName; - - private final List<Node> nodes = new ArrayList<>(); - private final String systemPassCode = "fooBar2000"; - - Cluster(@Nullable String name) { - this.clusterName = name; - } - - Node startNode(NodeConfig config, Consumer<OrchestratorBuilder> consumer) { - Node node = addNode(config, consumer); - node.start(); - return node; - } - - Node addNode(NodeConfig config, Consumer<OrchestratorBuilder> consumer) { - OrchestratorBuilder builder = newOrchestratorBuilder(config); - builder.setServerProperty("sonar.web.systemPasscode", systemPassCode); - - switch (config.getType()) { - case SEARCH: - builder - .setServerProperty("sonar.cluster.node.type", "search") - .setServerProperty("sonar.search.host", config.getAddress().getHostAddress()) - .setServerProperty("sonar.search.port", "" + config.getSearchPort().get()) - .setServerProperty("sonar.search.javaOpts", "-Xmx64m -Xms64m -XX:+HeapDumpOnOutOfMemoryError"); - break; - case APPLICATION: - builder - .setServerProperty("sonar.cluster.node.type", "application") - .setServerProperty("sonar.web.host", config.getAddress().getHostAddress()) - .setServerProperty("sonar.web.port", "" + config.getWebPort().get()) - .setServerProperty("sonar.web.javaOpts", "-Xmx128m -Xms16m -XX:+HeapDumpOnOutOfMemoryError") - .setServerProperty("sonar.auth.jwtBase64Hs256Secret", "HrPSavOYLNNrwTY+SOqpChr7OwvbR/zbDLdVXRN0+Eg=") - .setServerProperty("sonar.ce.javaOpts", "-Xmx32m -Xms16m -XX:+HeapDumpOnOutOfMemoryError"); - break; - } - consumer.accept(builder); - Orchestrator orchestrator = builder.build(); - Node node = new Node(config, orchestrator, systemPassCode); - nodes.add(node); - return node; - } - - Stream<Node> getNodes() { - return nodes.stream(); - } - - Stream<Node> getAppNodes() { - return nodes.stream().filter(n -> n.getConfig().getType() == NodeConfig.NodeType.APPLICATION); - } - - Node getAppNode(int index) { - return getAppNodes().skip(index).findFirst().orElseThrow(IllegalArgumentException::new); - } - - Stream<Node> getSearchNodes() { - return nodes.stream().filter(n -> n.getConfig().getType() == NodeConfig.NodeType.SEARCH); - } - - Node getSearchNode(int index) { - return getSearchNodes().skip(index).findFirst().orElseThrow(IllegalArgumentException::new); - } - - @Override - public void close() throws Exception { - // nodes are stopped in order of creation - for (Node node : nodes) { - try { - node.stop(); - } catch (Exception e) { - LoggerFactory.getLogger(getClass()).error("Fail to stop node", e); - } - } - } - - private OrchestratorBuilder newOrchestratorBuilder(NodeConfig node) { - OrchestratorBuilder builder = Orchestrator.builderEnv(); - builder.setOrchestratorProperty("orchestrator.keepDatabase", "true"); - builder.setServerProperty("sonar.cluster.enabled", "true"); - builder.setServerProperty("sonar.cluster.node.host", node.getAddress().getHostAddress()); - builder.setServerProperty("sonar.cluster.node.port", "" + node.getHzPort()); - builder.setServerProperty("sonar.cluster.hosts", node.getConnectedNodes().stream().map(NodeConfig::getHzHost).collect(joining(","))); - builder.setServerProperty("sonar.cluster.search.hosts", node.getSearchNodes().stream().map(NodeConfig::getSearchHost).collect(joining(","))); - if (clusterName != null) { - builder.setServerProperty("sonar.cluster.name", clusterName); - } - if (node.getName().isPresent()) { - builder.setServerProperty("sonar.cluster.node.name", node.getName().get()); - } - builder.addPlugin(ItUtils.pluginArtifact("server-plugin")); - builder.setStartupLogWatcher(logLine -> true); - return builder; - } -} diff --git a/tests/src/test/java/org/sonarqube/tests/cluster/ClusterTest.java b/tests/src/test/java/org/sonarqube/tests/cluster/ClusterTest.java deleted file mode 100644 index 74d69832b42..00000000000 --- a/tests/src/test/java/org/sonarqube/tests/cluster/ClusterTest.java +++ /dev/null @@ -1,451 +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.sonarqube.tests.cluster; - -import com.google.gson.internal.LinkedTreeMap; -import com.sonar.orchestrator.Orchestrator; -import com.sonar.orchestrator.OrchestratorBuilder; -import com.sonar.orchestrator.db.DefaultDatabase; -import java.io.File; -import java.util.ArrayList; -import java.util.List; -import java.util.Map; -import java.util.function.BinaryOperator; -import java.util.function.Consumer; -import java.util.stream.IntStream; -import org.apache.commons.io.FileUtils; -import org.junit.BeforeClass; -import org.junit.Ignore; -import org.junit.Rule; -import org.junit.Test; -import org.junit.rules.DisableOnDebug; -import org.junit.rules.ExpectedException; -import org.junit.rules.TemporaryFolder; -import org.junit.rules.TestRule; -import org.junit.rules.Timeout; -import org.sonarqube.pageobjects.Navigation; -import org.sonarqube.pageobjects.SystemInfoPage; -import org.sonarqube.ws.WsSystem; -import org.sonarqube.ws.client.HttpException; -import org.sonarqube.ws.client.issue.SearchWsRequest; -import util.ItUtils; - -import static com.google.common.base.Preconditions.checkState; -import static org.assertj.core.api.Assertions.assertThat; -import static org.junit.Assert.fail; -import static org.sonarqube.tests.cluster.NodeConfig.newApplicationConfig; -import static org.sonarqube.tests.cluster.NodeConfig.newSearchConfig; - -@Ignore -public class ClusterTest { - - @Rule - public TestRule safeguard = new DisableOnDebug(Timeout.seconds(300)); - - @Rule - public ExpectedException expectedException = ExpectedException.none(); - - @Rule - public TemporaryFolder temp = new TemporaryFolder(); - - @BeforeClass - public static void initDbSchema() throws Exception { - Orchestrator orchestrator = Orchestrator.builderEnv() - // enforce (re-)creation of database schema - .setOrchestratorProperty("orchestrator.keepDatabase", "false") - .build(); - DefaultDatabase db = new DefaultDatabase(orchestrator.getConfiguration()); - checkState(!db.getClient().getDialect().equals("h2"), "H2 is not supported in cluster mode"); - db.start(); - db.stop(); - } - - @Test - public void test_high_availability_topology() throws Exception { - try (Cluster cluster = newCluster(3, 2)) { - cluster.getNodes().forEach(Node::start); - - cluster.getAppNode(0).waitForHealthGreen(); - cluster.getAppNodes().forEach(node -> assertThat(node.getStatus()).hasValue(WsSystem.Status.UP)); - - cluster.getNodes().forEach(node -> { - node.assertThatProcessesAreUp(); - assertThat(node.anyLogsContain(" ERROR ")).isFalse(); - assertThat(node.anyLogsContain("MessageException")).isFalse(); - }); - - verifyGreenHealthOfNodes(cluster); - - // verify that there's a single web startup leader - Node startupLeader = cluster.getAppNodes() - .filter(Node::isStartupLeader) - .reduce(singleElement()) - .get(); - assertThat(startupLeader.hasStartupLeaderOperations()).isTrue(); - assertThat(startupLeader.hasCreatedSearchIndices()).isTrue(); - - // verify that the second app node is a startup follower - Node startupFollower = cluster.getAppNodes() - .filter(Node::isStartupFollower) - .reduce(singleElement()) - .get(); - assertThat(startupFollower.hasStartupLeaderOperations()).isFalse(); - assertThat(startupFollower.hasCreatedSearchIndices()).isFalse(); - - cluster.getAppNodes().forEach(app -> { - // compute engine is being started when app node is already in status UP - app.waitForCeLogsContain("Compute Engine is operational"); - assertThat(app.anyLogsContain("Process[ce] is up")).isTrue(); - }); - - testSystemInfoPage(cluster, cluster.getAppNode(0)); - testSystemInfoPage(cluster, cluster.getAppNode(1)); - } - } - - private void verifyGreenHealthOfNodes(Cluster cluster) { - WsSystem.HealthResponse health = cluster.getAppNode(0).getHealth().get(); - cluster.getNodes().forEach(node -> { - WsSystem.Node healthNode = health.getNodes().getNodesList().stream() - .filter(n -> n.getPort() == node.getConfig().getHzPort()) - .findFirst() - .orElseThrow(() -> new IllegalStateException("Node with port " + node.getConfig().getHzPort() + " not found in api/system/health")); - assertThat(healthNode.getStartedAt()).isNotEmpty(); - assertThat(healthNode.getHost()).isNotEmpty(); - assertThat(healthNode.getCausesCount()).isEqualTo(0); - assertThat(healthNode.getHealth()).isEqualTo(WsSystem.Health.GREEN); - }); - } - - private void testSystemInfoPage(Cluster cluster, Node node) { - Navigation nav = node.openBrowser().logIn().submitCredentials("admin"); - node.wsClient().users().skipOnboardingTutorial(); - SystemInfoPage page = nav.openSystemInfo(); - - page.getCardItem("System") - .shouldHaveHealth() - .shouldHaveMainSection() - .shouldHaveSection("Database") - .shouldHaveField("Database Version") - .shouldHaveSection("Search State") - .shouldHaveSection("Search Indexes") - .shouldNotHaveSection("Settings") - .shouldNotHaveSection("Plugins") - .shouldHaveField("High Availability") - .shouldNotHaveField("Official Distribution") - .shouldNotHaveField("Version") - .shouldNotHaveField("Logs Level") - .shouldNotHaveField("Health") - .shouldNotHaveField("Health Causes"); - - cluster.getNodes().forEach(clusterNodes -> { - page.shouldHaveCard(clusterNodes.getConfig().getName().get()); - }); - - page.getCardItem(String.valueOf(node.getConfig().getName().get())) - .shouldHaveHealth() - .shouldHaveSection("System") - .shouldHaveSection("Database") - .shouldHaveSection("Web Logging") - .shouldHaveSection("Compute Engine Logging") - .shouldNotHaveSection("Settings") - .shouldHaveField("Version") - .shouldHaveField("Official Distribution") - .shouldHaveField("Processors") - .shouldNotHaveField("Health") - .shouldNotHaveField("Health Causes"); - - page.getCardItem(cluster.getSearchNode(0).getConfig().getName().get()) - .shouldHaveHealth() - .shouldHaveSection("Search State") - .shouldHaveField("Disk Available") - .shouldHaveField("Max File Descriptors") - .shouldNotHaveField("Health") - .shouldNotHaveField("Health Causes"); - } - - @Test - public void minimal_cluster_is_2_search_and_1_application_nodes() throws Exception { - try (Cluster cluster = newCluster(2, 1)) { - cluster.getNodes().forEach(Node::start); - - Node app = cluster.getAppNode(0); - app.waitForStatusUp(); - app.waitForCeLogsContain("Compute Engine is operational"); - - app.waitForHealth(WsSystem.Health.YELLOW); - WsSystem.HealthResponse health = app.getHealth().orElseThrow(() -> new IllegalStateException("Health is not available")); - assertThat(health.getCausesList()).extracting(WsSystem.Cause::getMessage) - .contains("There should be at least three search nodes") - .contains("There should be at least two application nodes"); - - assertThat(app.isStartupLeader()).isTrue(); - assertThat(app.hasStartupLeaderOperations()).isTrue(); - - cluster.getNodes().forEach(node -> { - assertThat(node.anyLogsContain(" ERROR ")).isFalse(); - node.assertThatProcessesAreUp(); - }); - } - } - - @Test - public void configuration_of_connection_to_other_nodes_can_be_non_exhaustive() throws Exception { - try (Cluster cluster = new Cluster(null)) { - NodeConfig searchConfig1 = newSearchConfig("Search 1"); - NodeConfig searchConfig2 = newSearchConfig("Search 2"); - NodeConfig appConfig = newApplicationConfig("App 1"); - - // HZ bus : app -> search 2 -> search1, which is not recommended at all !!! - searchConfig2.addConnectionToBus(searchConfig1); - appConfig.addConnectionToBus(searchConfig2); - - // search1 is not configured to connect search2 - // app is not configured to connect to search 1 - // --> not recommended at all !!! - searchConfig2.addConnectionToSearch(searchConfig1); - appConfig.addConnectionToSearch(searchConfig2); - - cluster.startNode(searchConfig1, nothing()); - cluster.startNode(searchConfig2, nothing()); - Node app = cluster.startNode(appConfig, nothing()); - - app.waitForStatusUp(); - assertThat(app.isStartupLeader()).isTrue(); - assertThat(app.hasStartupLeaderOperations()).isTrue(); - - // no errors - cluster.getNodes().forEach(node -> assertThat(node.anyLogsContain(" ERROR ")).isFalse()); - } - } - - @Test - public void cluster_name_can_be_overridden() throws Exception { - try (Cluster cluster = new Cluster("foo")) { - NodeConfig searchConfig1 = newSearchConfig("Search 1"); - NodeConfig searchConfig2 = newSearchConfig("Search 2"); - NodeConfig appConfig = newApplicationConfig("App 1"); - NodeConfig.interconnectBus(searchConfig1, searchConfig2, appConfig); - NodeConfig.interconnectSearch(searchConfig1, searchConfig2, appConfig); - - cluster.startNode(searchConfig1, nothing()); - cluster.startNode(searchConfig2, nothing()); - cluster.startNode(appConfig, nothing()); - - Node appNode = cluster.getAppNode(0); - appNode.waitForStatusUp(); - } - } - - @Test - public void node_fails_to_join_cluster_if_different_cluster_name() throws Exception { - try (Cluster cluster = new Cluster("foo")) { - NodeConfig searchConfig1 = newSearchConfig("Search 1"); - NodeConfig searchConfig2 = newSearchConfig("Search 2"); - NodeConfig.interconnectBus(searchConfig1, searchConfig2); - NodeConfig.interconnectSearch(searchConfig1, searchConfig2); - cluster.startNode(searchConfig1, nothing()); - cluster.startNode(searchConfig2, nothing()); - - NodeConfig searchConfig3 = newSearchConfig("Search 3") - .addConnectionToSearch(searchConfig1) - .addConnectionToBus(searchConfig1, searchConfig2); - Node search3 = cluster.addNode(searchConfig3, b -> b - .setServerProperty("sonar.cluster.name", "bar") - .setStartupLogWatcher(logLine -> logLine.contains("SonarQube is up"))); - try { - search3.start(); - fail(); - } catch (IllegalStateException e) { - assertThat(e).hasMessage("Server startup failure"); - // TODO how to force process to write into sonar.log, even if sonar.log.console=true ? - // assertThat(search3.anyLogsContain("This node has a cluster name [bar], which does not match [foo] from the cluster")).isTrue(); - } - } - } - - @Test - public void restarting_all_application_nodes_elects_a_new_startup_leader() throws Exception { - // no need for 3 search nodes, 2 is enough for the test - try (Cluster cluster = newCluster(2, 2)) { - cluster.getNodes().forEach(Node::start); - cluster.getAppNodes().forEach(Node::waitForStatusUp); - - // stop application nodes only - cluster.getAppNodes().forEach(app -> { - app.stop(); - app.cleanUpLogs(); - // logs are empty, no more possible to know if node was startup leader/follower - assertThat(app.isStartupLeader()).isFalse(); - assertThat(app.isStartupFollower()).isFalse(); - }); - - // restart application nodes - cluster.getAppNodes().forEach(Node::start); - cluster.getAppNodes().forEach(Node::waitForStatusUp); - - // one app node is elected as startup leader. It does some initialization stuff, - // like registration of rules. Search indices already exist and are up-to-date. - Node startupLeader = cluster.getAppNodes() - .filter(Node::isStartupLeader) - .reduce(singleElement()) - .get(); - assertThat(startupLeader.hasStartupLeaderOperations()).isTrue(); - assertThat(startupLeader.hasCreatedSearchIndices()).isFalse(); - - Node startupFollower = cluster.getAppNodes() - .filter(Node::isStartupFollower) - .reduce(singleElement()) - .get(); - assertThat(startupFollower.hasStartupLeaderOperations()).isFalse(); - assertThat(startupFollower.hasCreatedSearchIndices()).isFalse(); - assertThat(startupFollower).isNotSameAs(startupLeader); - } - } - - @Test - public void set_log_level_affects_all_nodes() throws Exception { - try (Cluster cluster = newCluster(2, 2)) { - cluster.getNodes().forEach(Node::start); - cluster.getAppNodes().forEach(Node::waitForStatusUp); - - cluster.getAppNodes().forEach(node -> { - assertThat(node.webLogsContain(" TRACE web[")).isFalse(); - }); - - cluster.getAppNode(0).wsClient().system().changeLogLevel("TRACE"); - - cluster.getAppNodes().forEach(node -> { - - // do something, that will produce logging - node.wsClient().issues().search(new SearchWsRequest()); - - // check logs - assertThat(node.webLogsContain(" TRACE web[")).isTrue(); - }); - - Map<String, Object> data = ItUtils.jsonToMap(cluster.getAppNode(0).wsClient().system().info().content()); - ArrayList<Object> applicationNodes = (ArrayList<Object>) data.get("Application Nodes"); - applicationNodes.forEach(node -> { - LinkedTreeMap<Object, Object> nodeData = (LinkedTreeMap<Object, Object>) node; - LinkedTreeMap<Object, Object> ceLoggingData = (LinkedTreeMap<Object, Object>) nodeData.get("Compute Engine Logging"); - assertThat(ceLoggingData.get("Logs Level")).as("Compute engine logs level of a node").isEqualTo("TRACE"); - }); - } - } - - @Test - public void restart_action_is_not_allowed_for_cluster_nodes() throws Exception { - try (Cluster cluster = newCluster(2, 1)) { - cluster.getNodes().forEach(Node::start); - cluster.getAppNodes().forEach(Node::waitForStatusUp); - - cluster.getAppNodes().forEach(node -> { - try { - node.wsClient().system().restart(); - fail("The restart webservice must not succeed on cluster nodes"); - } catch (HttpException e) { - // all good, we expected this! - assertThat(e.code()).isEqualTo(400); - assertThat(e.content()).contains("Restart not allowed for cluster nodes"); - } - }); - } - } - - @Test - public void health_becomes_RED_when_all_search_nodes_go_down() throws Exception { - try (Cluster cluster = newCluster(2, 1)) { - cluster.getNodes().forEach(Node::start); - - Node app = cluster.getAppNode(0); - app.waitForHealth(WsSystem.Health.YELLOW); - - cluster.getSearchNodes().forEach(Node::stop); - - app.waitForHealth(WsSystem.Health.RED); - assertThat(app.getHealth().get().getCausesList()).extracting(WsSystem.Cause::getMessage) - .contains("Elasticsearch status is RED (unavailable)"); - } - } - - @Test - public void health_ws_is_available_when_server_is_starting() throws Exception { - File startupLock = temp.newFile(); - FileUtils.touch(startupLock); - - try (Cluster cluster = newCluster(2, 0)) { - // add an application node that pauses during startup - NodeConfig appConfig = NodeConfig.newApplicationConfig("App 1") - .addConnectionToBus(cluster.getSearchNode(0).getConfig()) - .addConnectionToSearch(cluster.getSearchNode(0).getConfig()); - Node appNode = cluster.addNode(appConfig, b -> b.setServerProperty("sonar.web.startupLock.path", startupLock.getAbsolutePath())); - - cluster.getNodes().forEach(Node::start); - - appNode.waitFor(node -> WsSystem.Status.STARTING == node.getStatus().orElse(null)); - - // WS answers whereas server is still not started - assertThat(appNode.getHealth().get().getHealth()).isEqualTo(WsSystem.Health.RED); - - // just to be sure, verify that server is still being started - assertThat(appNode.getStatus()).hasValue(WsSystem.Status.STARTING); - - startupLock.delete(); - } - } - - /** - * Used to have non-blocking {@link Node#start()}. Orchestrator considers - * node to be up as soon as the first log is generated. - */ - private static Consumer<OrchestratorBuilder> nothing() { - return b -> { - }; - } - - /** - * Configure a cluster with recommended configuration (each node has references - * to other nodes) - */ - private static Cluster newCluster(int nbOfSearchNodes, int nbOfAppNodes) { - Cluster cluster = new Cluster(null); - - List<NodeConfig> configs = new ArrayList<>(); - IntStream.range(0, nbOfSearchNodes).forEach(i -> configs.add(newSearchConfig("Search " + i))); - IntStream.range(0, nbOfAppNodes).forEach(i -> configs.add(newApplicationConfig("App " + i))); - NodeConfig[] configsArray = configs.toArray(new NodeConfig[configs.size()]); - - // a node is connected to all nodes, including itself (see sonar.cluster.hosts) - NodeConfig.interconnectBus(configsArray); - - // search nodes are interconnected, and app nodes connect to all search nodes - NodeConfig.interconnectSearch(configsArray); - - configs.forEach(c -> cluster.addNode(c, nothing())); - return cluster; - } - - private static BinaryOperator<Node> singleElement() { - return (a, b) -> { - throw new IllegalStateException("More than one element"); - }; - } -} diff --git a/tests/src/test/java/org/sonarqube/tests/cluster/Node.java b/tests/src/test/java/org/sonarqube/tests/cluster/Node.java deleted file mode 100644 index 8b200f496b8..00000000000 --- a/tests/src/test/java/org/sonarqube/tests/cluster/Node.java +++ /dev/null @@ -1,248 +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.sonarqube.tests.cluster; - -import com.google.common.util.concurrent.Uninterruptibles; -import com.sonar.orchestrator.Orchestrator; -import java.io.File; -import java.io.IOException; -import java.net.ServerSocket; -import java.util.List; -import java.util.Optional; -import java.util.concurrent.TimeUnit; -import java.util.function.Predicate; -import javax.annotation.Nullable; -import org.apache.commons.io.FileUtils; -import org.sonarqube.pageobjects.Navigation; -import org.sonarqube.tests.LogsTailer; -import org.sonarqube.ws.WsSystem; -import org.sonarqube.ws.client.WsClient; -import util.ItUtils; - -import static com.google.common.base.Preconditions.checkState; -import static org.assertj.core.api.Assertions.assertThat; - -class Node { - - private final NodeConfig config; - private final Orchestrator orchestrator; - private final String systemPassCode; - private LogsTailer logsTailer; - private final LogsTailer.Content content = new LogsTailer.Content(); - - Node(NodeConfig config, Orchestrator orchestrator, String systemPassCode) { - this.config = config; - this.orchestrator = orchestrator; - this.systemPassCode = systemPassCode; - } - - NodeConfig getConfig() { - return config; - } - - /** - * Non-blocking startup of node. The method does not wait for - * node to be started because Orchestrator uses a StartupLogWatcher - * that returns as soon as a log is generated. - */ - void start() { - orchestrator.start(); - logsTailer = LogsTailer.builder() - .addFile(orchestrator.getServer().getWebLogs()) - .addFile(orchestrator.getServer().getCeLogs()) - .addFile(orchestrator.getServer().getEsLogs()) - .addFile(orchestrator.getServer().getAppLogs()) - .addConsumer(content) - .build(); - } - - void stop() { - orchestrator.stop(); - if (logsTailer != null) { - logsTailer.close(); - } - } - - void cleanUpLogs() { - if (orchestrator.getServer() != null) { - FileUtils.deleteQuietly(orchestrator.getServer().getWebLogs()); - FileUtils.deleteQuietly(orchestrator.getServer().getCeLogs()); - FileUtils.deleteQuietly(orchestrator.getServer().getEsLogs()); - FileUtils.deleteQuietly(orchestrator.getServer().getAppLogs()); - } - } - - boolean isStartupLeader() { - return webLogsContain("Cluster enabled (startup leader)"); - } - - boolean isStartupFollower() { - return webLogsContain("Cluster enabled (startup follower)"); - } - - void waitForStatusUp() { - waitFor(node -> WsSystem.Status.UP == node.getStatus().orElse(null)); - } - - /** - * Waiting for health to be green... or yellow on the boxes that - * have less than 15% of free disk space. In that case Elasticsearch - * can't build shard replicas so it is yellow. - */ - void waitForHealthGreen() { - waitFor(node -> { - Optional<WsSystem.HealthResponse> health = node.getHealth(); - if (!health.isPresent()) { - return false; - } - if (health.get().getHealth() == WsSystem.Health.GREEN) { - return true; - } - if (health.get().getHealth() == WsSystem.Health.YELLOW) { - List<WsSystem.Cause> causes = health.get().getCausesList(); - return causes.size() == 1 && "Elasticsearch status is YELLOW".equals(causes.get(0).getMessage()); - } - return false; - }); - } - - void waitForHealth(WsSystem.Health expectedHealth) { - waitFor(node -> expectedHealth.equals(node.getHealth().map(WsSystem.HealthResponse::getHealth).orElse(null))); - } - - Optional<WsSystem.Status> getStatus() { - checkState(config.getType() == NodeConfig.NodeType.APPLICATION); - if (orchestrator.getServer() == null) { - return Optional.empty(); - } - try { - return Optional.ofNullable(ItUtils.newAdminWsClient(orchestrator).system().status().getStatus()); - } catch (Exception e) { - return Optional.empty(); - } - } - - Optional<WsSystem.HealthResponse> getHealth() { - checkState(config.getType() == NodeConfig.NodeType.APPLICATION); - if (orchestrator.getServer() == null) { - return Optional.empty(); - } - try { - return Optional.ofNullable(ItUtils.newSystemUserWsClient(orchestrator, systemPassCode).system().health()); - } catch (Exception e) { - return Optional.empty(); - } - } - - void waitFor(Predicate<Node> predicate) { - try { - while (!predicate.test(this)) { - Thread.sleep(500); - } - } catch (InterruptedException e) { - Thread.currentThread().interrupt(); - } - } - - void assertThatProcessesAreUp() { - assertThat(arePortsBound()).as(getConfig().getType().toString()).isTrue(); - switch (config.getType()) { - case SEARCH: - assertThat(anyLogsContain("Process[es] is up")).isTrue(); - assertThat(anyLogsContain("Process[web] is up")).isFalse(); - assertThat(anyLogsContain("Elasticsearch cluster enabled")).isTrue(); - break; - case APPLICATION: - assertThat(anyLogsContain("Process[es] is up")).isFalse(); - assertThat(anyLogsContain("Process[web] is up")).isTrue(); - assertThat(anyLogsContain("Elasticsearch cluster enabled")).isFalse(); - break; - } - } - - void waitForCeLogsContain(String expectedMessage) { - boolean found = false; - while (!found) { - found = orchestrator.getServer() != null && fileContains(orchestrator.getServer().getCeLogs(), expectedMessage); - if (!found) { - Uninterruptibles.sleepUninterruptibly(1_000, TimeUnit.MILLISECONDS); - } - } - } - - boolean hasStartupLeaderOperations() throws IOException { - if (orchestrator.getServer() == null) { - return false; - } - String logs = FileUtils.readFileToString(orchestrator.getServer().getWebLogs()); - return logs.contains("Register metrics") && - logs.contains("Register rules"); - } - - boolean hasCreatedSearchIndices() throws IOException { - if (orchestrator.getServer() == null) { - return false; - } - String logs = FileUtils.readFileToString(orchestrator.getServer().getWebLogs()); - return logs.contains("[o.s.s.e.IndexCreator] Create index"); - } - - boolean anyLogsContain(String message) { - return content.hasText(message); - } - - boolean webLogsContain(String message) { - if (orchestrator.getServer() == null) { - return false; - } - return fileContains(orchestrator.getServer().getWebLogs(), message); - } - - Navigation openBrowser() { - return Navigation.create(orchestrator); - } - - private static boolean fileContains(@Nullable File logFile, String message) { - try { - return logFile != null && logFile.exists() && FileUtils.readFileToString(logFile).contains(message); - } catch (Exception e) { - throw new IllegalStateException(e); - } - } - - private boolean arePortsBound() { - return isPortBound(config.getHzPort()) && - config.getSearchPort().map(this::isPortBound).orElse(true) && - config.getWebPort().map(this::isPortBound).orElse(true); - } - - private boolean isPortBound(int port) { - try (ServerSocket socket = new ServerSocket(port, 50, config.getAddress())) { - return false; - } catch (IOException e) { - return true; - } - } - - public WsClient wsClient() { - checkState(config.getType() == NodeConfig.NodeType.APPLICATION); - return ItUtils.newAdminWsClient(orchestrator); - } -} diff --git a/tests/src/test/java/org/sonarqube/tests/cluster/NodeConfig.java b/tests/src/test/java/org/sonarqube/tests/cluster/NodeConfig.java deleted file mode 100644 index cd7951a0bb7..00000000000 --- a/tests/src/test/java/org/sonarqube/tests/cluster/NodeConfig.java +++ /dev/null @@ -1,187 +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.sonarqube.tests.cluster; - -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.Arrays; -import java.util.Collections; -import java.util.Enumeration; -import java.util.List; -import java.util.Optional; -import javax.annotation.Nullable; - -import static com.google.common.base.Preconditions.checkArgument; - -class NodeConfig { - - enum NodeType { - SEARCH("search"), APPLICATION("application"); - - final String value; - - NodeType(String value) { - this.value = value; - } - - String getValue() { - return value; - } - } - - private final NodeType type; - @Nullable - private final String name; - private final InetAddress address; - private final int hzPort; - @Nullable - private final Integer searchPort; - @Nullable - private final Integer webPort; - private final List<NodeConfig> connectedNodes = new ArrayList<>(); - private final List<NodeConfig> searchNodes = new ArrayList<>(); - - private NodeConfig(NodeType type, @Nullable String name) { - this.type = type; - this.name = name; - this.address = getNonLoopbackIpv4Address(); - this.hzPort = NetworkUtils.getNextAvailablePort(this.address); - this.connectedNodes.add(this); - switch (type) { - case SEARCH: - this.searchPort = NetworkUtils.getNextAvailablePort(this.address); - this.webPort = null; - this.searchNodes.add(this); - break; - case APPLICATION: - this.searchPort = null; - this.webPort = NetworkUtils.getNextAvailablePort(this.address); - break; - default: - throw new IllegalArgumentException(); - } - } - - NodeType getType() { - return type; - } - - Optional<String> getName() { - return Optional.ofNullable(name); - } - - InetAddress getAddress() { - return address; - } - - int getHzPort() { - return hzPort; - } - - Optional<Integer> getSearchPort() { - return Optional.ofNullable(searchPort); - } - - Optional<Integer> getWebPort() { - return Optional.ofNullable(webPort); - } - - String getHzHost() { - return address.getHostAddress() + ":" + hzPort; - } - - String getSearchHost() { - return address.getHostAddress() + ":" + searchPort; - } - - NodeConfig addConnectionToBus(NodeConfig... configs) { - connectedNodes.addAll(Arrays.asList(configs)); - return this; - } - - NodeConfig addConnectionToSearch(NodeConfig... configs) { - Arrays.stream(configs).forEach(config -> { - checkArgument(config.getType() == NodeType.SEARCH); - searchNodes.add(config); - }); - return this; - } - - List<NodeConfig> getConnectedNodes() { - return connectedNodes; - } - - List<NodeConfig> getSearchNodes() { - return searchNodes; - } - - static NodeConfig newApplicationConfig(String name) { - return new NodeConfig(NodeType.APPLICATION, name); - } - - static NodeConfig newSearchConfig(String name) { - return new NodeConfig(NodeType.SEARCH, name); - } - - /** - * See property sonar.cluster.hosts - */ - static void interconnectBus(NodeConfig... configs) { - Arrays.stream(configs).forEach(config -> Arrays.stream(configs).filter(c -> c != config).forEach(config::addConnectionToBus)); - } - - /** - * See property sonar.cluster.search.hosts - */ - static void interconnectSearch(NodeConfig... configs) { - Arrays.stream(configs).forEach(config -> Arrays.stream(configs) - .filter(c -> c.getType() == NodeType.SEARCH) - .forEach(config::addConnectionToSearch)); - } - - private 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"); - } -} |