/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF 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.apache.ignite.internal.client.integration;
import java.net.InetAddress;
import java.util.Collection;
import java.util.Collections;
import java.util.LinkedList;
import java.util.List;
import org.apache.ignite.Ignite;
import org.apache.ignite.internal.client.GridClient;
import org.apache.ignite.internal.client.GridClientConfiguration;
import org.apache.ignite.internal.client.GridClientException;
import org.apache.ignite.internal.client.GridClientFactory;
import org.apache.ignite.internal.client.GridClientNode;
import org.apache.ignite.internal.client.GridClientProtocol;
import org.apache.ignite.internal.util.IgniteUtils;
import org.apache.ignite.internal.util.typedef.F;
import org.apache.ignite.internal.util.typedef.G;
import org.apache.ignite.internal.util.typedef.P1;
import org.apache.ignite.internal.util.typedef.internal.U;
import org.apache.ignite.lang.IgniteBiTuple;
import org.apache.ignite.testframework.GridTestUtils;
import org.apache.ignite.testframework.junits.common.GridCommonAbstractTest;
import org.jetbrains.annotations.Nullable;
/**
* Tests the REST client-server connectivity with various configurations.
*/
public abstract class ClientAbstractConnectivitySelfTest extends GridCommonAbstractTest {
/** */
private static final String WILDCARD_IP = "0.0.0.0";
/** */
private static final String LOOPBACK_IP = "127.0.0.1";
/**
* @return IP addresses.
* @throws Exception If failed.
*/
private static IgniteBiTuple<Collection<String>, Collection<String>> getAllIps() throws Exception {
return U.resolveLocalAddresses(InetAddress.getByName("0.0.0.0"));
}
/** {@inheritDoc} */
@Override protected void afterTest() throws Exception {
GridClientFactory.stopAll();
G.stopAll(true);
}
/**
* Starts a REST-enabled node.
*
* @param name Node name.
* @param addr REST address (default if null).
* @param port REST port (default if null).
* @return Started node.
* @throws Exception If case of configuration or startup error.
*/
protected abstract Ignite startRestNode(String name, @Nullable String addr, @Nullable Integer port) throws Exception;
/**
* @return Default REST port.
*/
protected abstract int defaultRestPort();
/**
* @return REST address attribute name.
*/
protected abstract String restAddressAttributeName();
/**
* @return REST host name attribute name.
*/
protected abstract String restHostNameAttributeName();
/**
* @return REST port attribute name.
*/
protected abstract String restPortAttributeName();
/**
* @return REST protocol.
*/
protected abstract GridClientProtocol protocol();
/**
* Starts a REST client.
*
* @param addr REST server address.
* @param port REST server port.
* @return A successfully started REST client.
* @throws GridClientException If failed to start REST client.
*/
protected GridClient startClient(String addr, int port) throws GridClientException {
GridClientConfiguration cliCfg = new GridClientConfiguration();
cliCfg.setServers(Collections.singleton(addr + ":" + port));
cliCfg.setProtocol(protocol());
return GridClientFactory.start(cliCfg);
}
/**
* Tests correct behavior in case of 1 REST-enabled node
* with default settings.
*
* @throws Exception If failed.
*/
public void testOneNodeDefaultHostAndPort() throws Exception {
startRestNode("grid1", null, null);
checkConnectivityByIp(LOOPBACK_IP, getAllIps());
String extIp = F.find(U.allLocalIps(), null, new IpV4AddressPredicate());
checkConnectivityByIp(extIp, getAllIps());
}
/**
* Simple test of address list filtering.
* @throws Exception If failed.
*/
public void testResolveReachableOneAddress() throws Exception {
InetAddress addr = InetAddress.getByAddress(new byte[] {127, 0, 0, 1} );
List <InetAddress> filtered = IgniteUtils.filterReachable(Collections.singletonList(addr));
assertEquals(1, filtered.size());
assertEquals(addr, filtered.get(0));
}
/**
* Tests correct behavior in case of 1 REST-enabled node
* with explicitly specified loopback address setting.
*
* @throws Exception If error occurs.
*/
public void testOneNodeLoopbackHost() throws Exception {
startRestNode("grid1", LOOPBACK_IP, defaultRestPort());
checkConnectivityByIp(LOOPBACK_IP, F.t((Collection<String>)Collections.singleton(LOOPBACK_IP),
(Collection<String>)Collections.<String>emptySet()));
}
/**
* Tests correct behavior in case of 1 REST-enabled node
* with explicitly specified 0.0.0.0 address.
*
* @throws Exception If error occurs.
*/
public void testOneNodeZeroIpv4Address() throws Exception {
startRestNode("grid1", WILDCARD_IP, defaultRestPort());
Collection<String> addrs = new LinkedList<>();
addrs.add(LOOPBACK_IP);
Collection<String> nonLoopbackAddrs = U.allLocalIps();
assertNotNull(nonLoopbackAddrs);
addrs.addAll(F.view(nonLoopbackAddrs, new IpV4AddressPredicate()));
// The client should be able to connect through all IPv4 addresses.
for (String addr : addrs) {
log.info("Trying address: " + addr);
GridClient cli = startClient(addr, defaultRestPort());
List<GridClientNode> nodes = cli.compute().refreshTopology(true, false);
assertEquals(1, nodes.size());
GridClientNode node = F.first(nodes);
assertNotNull(node);
assertEquals(getAllIps().get1(), node.attribute(restAddressAttributeName()));
assertEquals(getAllIps().get2(), node.attribute(restHostNameAttributeName()));
List<String> nodeAddrs = node.tcpAddresses();
assertTrue(nodeAddrs.contains(LOOPBACK_IP));
assertTrue(F.containsAll(nodeAddrs, addrs));
}
}
/**
* Tests correct behavior in case of 2 REST-enabled nodes with default
* settings.
*
* @throws Exception If error occurs.
*/
public void testTwoNodesDefaultHostAndPort() throws Exception {
startRestNode("grid1", null, null);
startRestNode("grid2", null, null);
GridClient cli = startClient(LOOPBACK_IP, defaultRestPort());
List<GridClientNode> nodes = cli.compute().refreshTopology(true, false);
assertEquals(2, nodes.size());
assertTrue(F.forAll(nodes, new P1<GridClientNode>() {
@Override public boolean apply(GridClientNode node) {
return node.tcpAddresses().contains(LOOPBACK_IP);
}
}));
GridTestUtils.assertOneToOne(
nodes,
new P1<GridClientNode>() {
@Override public boolean apply(GridClientNode node) {
try {
return eqAddresses(getAllIps(), node) &&
Integer.valueOf(defaultRestPort()).equals(node.attribute(restPortAttributeName()));
}
catch (Exception ignored) {
return false;
}
}
},
new P1<GridClientNode>() {
@Override public boolean apply(GridClientNode node) {
try {
return eqAddresses(getAllIps(), node) &&
Integer.valueOf(defaultRestPort() + 1).equals(node.attribute(restPortAttributeName()));
}
catch (Exception ignored) {
return false;
}
}
}
);
}
/**
* Tests correct behavior in case of shutdown node used to refresh topology state.
*
* @throws Exception If error occurs.
*/
public void testRefreshTopologyOnNodeLeft() throws Exception {
startRestNode("grid1", null, null);
startRestNode("grid2", null, null);
GridClient cli = startClient(LOOPBACK_IP, defaultRestPort());
List<GridClientNode> nodes = cli.compute().refreshTopology(true, false);
assertEquals(2, nodes.size());
stopGrid("grid1");
nodes = cli.compute().refreshTopology(true, false);
assertEquals(1, nodes.size());
startRestNode("grid3", null, null);
nodes = cli.compute().refreshTopology(true, false);
assertEquals(2, nodes.size());
stopGrid("grid2");
nodes = cli.compute().refreshTopology(true, false);
assertEquals(1, nodes.size());
}
/**
* @param connectIp IP to test.
* @param nodeIp Expected IP reported to client.
* @throws GridClientException If failed.
*/
private void checkConnectivityByIp(String connectIp, IgniteBiTuple<Collection<String>, Collection<String>> nodeIp)
throws GridClientException {
GridClient cli = startClient(connectIp, defaultRestPort());
List<GridClientNode> nodes = cli.compute().refreshTopology(true, false);
assertEquals(1, nodes.size());
GridClientNode node = F.first(nodes);
assertNotNull(node);
assertTrue(eqAddresses(nodeIp, node));
}
/**
* @param nodeIp Node ip.
* @param node Node.
* @return {@code True} if addresses are equal, {@code false} otherwise.
*/
private boolean eqAddresses(IgniteBiTuple<Collection<String>, Collection<String>> nodeIp, GridClientNode node) {
return F.eqOrdered(nodeIp.get1(), (Collection<String>)(node.attribute(restAddressAttributeName()))) &&
F.eqOrdered(nodeIp.get2(), (Collection<String>)(node.attribute(restHostNameAttributeName())));
}
/**
* Predicate that returns IPv4 address strings.
*/
private static class IpV4AddressPredicate implements P1<String> {
/** {@inheritDoc} */
@Override public boolean apply(String s) {
return s.matches("\\d+\\.\\d+\\.\\d+\\.\\d+");
}
}
}