/* * Licensed to Elasticsearch under one or more contributor * license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright * ownership. Elasticsearch licenses this file to you under * the Apache License, Version 2.0 (the "License"); you may * not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.elasticsearch.action.support.nodes; import org.elasticsearch.Version; import org.elasticsearch.action.FailedNodeException; import org.elasticsearch.action.support.ActionFilter; import org.elasticsearch.action.support.ActionFilters; import org.elasticsearch.action.support.PlainActionFuture; import org.elasticsearch.action.support.broadcast.node.TransportBroadcastByNodeActionTests; import org.elasticsearch.cluster.ClusterName; import org.elasticsearch.cluster.ClusterService; import org.elasticsearch.cluster.ClusterState; import org.elasticsearch.cluster.node.DiscoveryNode; import org.elasticsearch.cluster.node.DiscoveryNodes; import org.elasticsearch.common.settings.Settings; import org.elasticsearch.common.transport.DummyTransportAddress; import org.elasticsearch.tasks.Task; import org.elasticsearch.test.ESTestCase; import org.elasticsearch.test.cluster.TestClusterService; import org.elasticsearch.test.transport.CapturingTransport; import org.elasticsearch.threadpool.ThreadPool; import org.elasticsearch.transport.TransportService; import org.junit.After; import org.junit.AfterClass; import org.junit.Before; import org.junit.BeforeClass; import java.util.ArrayList; import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicReferenceArray; public class TransportNodesActionTests extends ESTestCase { private static ThreadPool THREAD_POOL; private static ClusterName CLUSTER_NAME = new ClusterName("test-cluster"); private TestClusterService clusterService; private CapturingTransport transport; private TransportService transportService; public void testRequestIsSentToEachNode() throws Exception { TransportNodesAction action = getTestTransportNodesAction(); TestNodesRequest request = new TestNodesRequest(); PlainActionFuture<TestNodesResponse> listener = new PlainActionFuture<>(); action.doExecute(new Task(0, "some type", "some action", "test task"), request, listener); Map<String, List<CapturingTransport.CapturedRequest>> capturedRequests = transport.capturedRequestsByTargetNode(); transport.clear(); int numNodes = clusterService.state().getNodes().getSize(); // check a request was sent to the right number of nodes assertEquals(numNodes, capturedRequests.size()); } public void testFiltering() throws Exception { TransportNodesAction action = getFilteringTestTransportNodesAction(); TestNodesRequest request = new TestNodesRequest(); PlainActionFuture<TestNodesResponse> listener = new PlainActionFuture<>(); action.doExecute(new Task(0, "some type", "some action", "test task"), request, listener); Map<String, List<CapturingTransport.CapturedRequest>> capturedRequests = transport.capturedRequestsByTargetNode(); transport.clear(); // check requests were only sent to data nodes for (String nodeTarget : capturedRequests.keySet()) { assertTrue(clusterService.state().nodes().get(nodeTarget).isDataNode()); } assertEquals(clusterService.state().nodes().getDataNodes().size(), capturedRequests.size()); } @BeforeClass public static void startThreadPool() { THREAD_POOL = new ThreadPool(TransportBroadcastByNodeActionTests.class.getSimpleName()); } @AfterClass public static void destroyThreadPool() { ThreadPool.terminate(THREAD_POOL, 30, TimeUnit.SECONDS); // since static must set to null to be eligible for collection THREAD_POOL = null; } @Before public void setUp() throws Exception { super.setUp(); transport = new CapturingTransport(); clusterService = new TestClusterService(THREAD_POOL); transportService = new TransportService(transport, THREAD_POOL); transportService.start(); transportService.acceptIncomingRequests(); int numNodes = randomIntBetween(3, 10); DiscoveryNodes.Builder discoBuilder = DiscoveryNodes.builder(); List<DiscoveryNode> discoveryNodes = new ArrayList<>(); for (int i = 0; i < numNodes; i++) { Map<String, String> attributes = getRandomAttributes(); if (frequently()) { attributes.put("custom", randomBoolean() ? "match" : randomAsciiOfLengthBetween(3, 5)); } final DiscoveryNode node = newNode(i, attributes); discoBuilder = discoBuilder.put(node); discoveryNodes.add(node); } discoBuilder.localNodeId(randomFrom(discoveryNodes).getId()); discoBuilder.masterNodeId(randomFrom(discoveryNodes).getId()); ClusterState.Builder stateBuilder = ClusterState.builder(CLUSTER_NAME); stateBuilder.nodes(discoBuilder); ClusterState clusterState = stateBuilder.build(); clusterService.setState(clusterState); } private HashMap<String, String> getRandomAttributes() { String[] roles = new String[]{"master", "data", "ingest"}; HashMap<String, String> attributes = new HashMap<>(); for (String role : roles) { attributes.put(role, "true"); } List<String> unsetRoles= randomSubsetOf(randomIntBetween(1,2), roles); for (String role : unsetRoles) { attributes.put(role, "false"); } return attributes; } @After public void tearDown() throws Exception { super.tearDown(); transport.close(); } public TestTransportNodesAction getTestTransportNodesAction() { return new TestTransportNodesAction( Settings.EMPTY, THREAD_POOL, clusterService, transportService, new ActionFilters(new HashSet<ActionFilter>()), ThreadPool.Names.SAME ); } public FilteringTestTransportNodesAction getFilteringTestTransportNodesAction() { return new FilteringTestTransportNodesAction( Settings.EMPTY, THREAD_POOL, clusterService, transportService, new ActionFilters(new HashSet<ActionFilter>()), ThreadPool.Names.SAME ); } private static DiscoveryNode newNode(int nodeId, Map<String, String> attributes) { String node = "node_" + nodeId; return new DiscoveryNode(node, node, DummyTransportAddress.INSTANCE, attributes, Version.CURRENT); } private static class TestTransportNodesAction extends TransportNodesAction<TestNodesRequest, TestNodesResponse, TestNodeRequest, TestNodeResponse> { TestTransportNodesAction(Settings settings, ThreadPool threadPool, ClusterService clusterService, TransportService transportService, ActionFilters actionFilters, String nodeExecutor) { super(settings, "indices:admin/test", CLUSTER_NAME, threadPool, clusterService, transportService, actionFilters, null, TestNodesRequest.class, TestNodeRequest.class, nodeExecutor); } @Override protected TestNodesResponse newResponse(TestNodesRequest request, AtomicReferenceArray nodesResponses) { return new TestNodesResponse(); } @Override protected TestNodeRequest newNodeRequest(String nodeId, TestNodesRequest request) { return new TestNodeRequest(); } @Override protected TestNodeResponse newNodeResponse() { return new TestNodeResponse(); } @Override protected TestNodeResponse nodeOperation(TestNodeRequest request) { return new TestNodeResponse(); } @Override protected boolean accumulateExceptions() { return false; } } public static class FilteringTestTransportNodesAction extends TestTransportNodesAction { FilteringTestTransportNodesAction(Settings settings, ThreadPool threadPool, ClusterService clusterService, TransportService transportService, ActionFilters actionFilters, String nodeExecutor) { super(settings, threadPool, clusterService, transportService, actionFilters, nodeExecutor); } @Override protected String[] filterNodeIds(DiscoveryNodes nodes, String[] nodesIds) { return nodes.getDataNodes().keys().toArray(String.class); } } public static class TestNodesRequest extends BaseNodesRequest<TestNodesRequest> { } private static class TestNodesResponse extends BaseNodesResponse<TestNodeResponse> { } public static class TestNodeRequest extends BaseNodeRequest { } private static class TestNodeResponse extends BaseNodeResponse { } }