This is an automated email from the ASF dual-hosted git repository. ilyak pushed a commit to branch master in repository https://gitbox.apache.org/repos/asf/ignite.git
The following commit(s) were added to refs/heads/master by this push: new 31ac65e IGNITE-14499 Support dynamic DNS in TcpDiscoveryVmIpFinder - Fixes #8981. 31ac65e is described below commit 31ac65eb853d45b305981252e742448bcf352629 Author: aalexand...@gridgain.com <aalexand...@gridgain.com> AuthorDate: Thu Apr 8 16:26:30 2021 +0300 IGNITE-14499 Support dynamic DNS in TcpDiscoveryVmIpFinder - Fixes #8981. Signed-off-by: Ilya Kasnacheev <ilya.kasnach...@gmail.com> --- .../tcp/ipfinder/vm/TcpDiscoveryVmIpFinder.java | 104 ++-- .../vm/TcpDiscoveryVmIpFinderDnsResolveTest.java | 630 +++++++++++++++++++++ .../IgniteSpiDiscoverySelfTestSuite.java | 2 + 3 files changed, 696 insertions(+), 40 deletions(-) diff --git a/modules/core/src/main/java/org/apache/ignite/spi/discovery/tcp/ipfinder/vm/TcpDiscoveryVmIpFinder.java b/modules/core/src/main/java/org/apache/ignite/spi/discovery/tcp/ipfinder/vm/TcpDiscoveryVmIpFinder.java index 9b60dd9..a6170b4 100644 --- a/modules/core/src/main/java/org/apache/ignite/spi/discovery/tcp/ipfinder/vm/TcpDiscoveryVmIpFinder.java +++ b/modules/core/src/main/java/org/apache/ignite/spi/discovery/tcp/ipfinder/vm/TcpDiscoveryVmIpFinder.java @@ -17,7 +17,9 @@ package org.apache.ignite.spi.discovery.tcp.ipfinder.vm; +import java.net.InetAddress; import java.net.InetSocketAddress; +import java.net.UnknownHostException; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; @@ -54,9 +56,13 @@ public class TcpDiscoveryVmIpFinder extends TcpDiscoveryIpFinderAdapter { @LoggerResource private IgniteLogger log; - /** Addresses. */ + /** Addresses from the configuration or IGNITE_TCP_DISCOVERY_ADDRESSES. */ @GridToStringInclude - private Collection<InetSocketAddress> addrs; + private Collection<String> addrs = new ArrayList<>(); + + /** Registered InetSocketAddresses. */ + @GridToStringInclude + private Collection<InetSocketAddress> registeredAddrs = new LinkedHashSet<>(); /** * Initialize from system property. @@ -65,15 +71,13 @@ public class TcpDiscoveryVmIpFinder extends TcpDiscoveryIpFinderAdapter { String ips = IgniteSystemProperties.getString(IGNITE_TCP_DISCOVERY_ADDRESSES); if (!F.isEmpty(ips)) { - Collection<InetSocketAddress> addrsList = new LinkedHashSet<>(); - for (String s : ips.split(",")) { if (!F.isEmpty(s)) { s = s.trim(); if (!F.isEmpty(s)) { try { - addrsList.addAll(address(s)); + addrs.add(s); } catch (IgniteSpiException e) { throw new IgniteException(e); @@ -81,11 +85,7 @@ public class TcpDiscoveryVmIpFinder extends TcpDiscoveryIpFinderAdapter { } } } - - addrs = addrsList; } - else - addrs = new LinkedHashSet<>(); } /** @@ -136,12 +136,11 @@ public class TcpDiscoveryVmIpFinder extends TcpDiscoveryIpFinderAdapter { if (F.isEmpty(addrs)) return this; - Collection<InetSocketAddress> newAddrs = new LinkedHashSet<>(); - + // Validate the spot that addresses can be resolved, to preserve the existing behavior. for (String ipStr : addrs) - newAddrs.addAll(address(ipStr)); + address(ipStr); - this.addrs = newAddrs; + this.addrs = new ArrayList<>(addrs); return this; } @@ -183,8 +182,20 @@ public class TcpDiscoveryVmIpFinder extends TcpDiscoveryIpFinderAdapter { return addresses(ipStr, "\\:", errMsg); } - // Provided address does not contain port (will use default one). - return Collections.singleton(new InetSocketAddress(ipStr, 0)); + Collection<InetSocketAddress> col = new LinkedHashSet<>(); + + try { + InetAddress[] inetAddresses = InetAddress.getAllByName(ipStr); + + for (InetAddress addrs : inetAddresses) + col.add(new InetSocketAddress(addrs, 0)); + } + catch (UnknownHostException ignored) { + // Preserve existing behavior on UnknownHostException + col = Collections.singleton(new InetSocketAddress(ipStr, 0)); + } + + return col; } /** @@ -204,36 +215,44 @@ public class TcpDiscoveryVmIpFinder extends TcpDiscoveryIpFinderAdapter { if (tokens.length == 2) { String addrStr = tokens[0]; String portStr = tokens[1]; + int port1, port2; if (portStr.contains("..")) { - try { - int port1 = Integer.parseInt(portStr.substring(0, portStr.indexOf(".."))); - int port2 = Integer.parseInt(portStr.substring(portStr.indexOf("..") + 2, portStr.length())); + port1 = Integer.parseInt(portStr.substring(0, portStr.indexOf(".."))); + port2 = Integer.parseInt(portStr.substring(portStr.indexOf("..") + 2, portStr.length())); + } + else + port1 = port2 = Integer.parseInt(portStr); - if (port2 < port1 || port1 == port2 || port1 <= 0 || port2 <= 0) - throw new IgniteSpiException(errMsg); + if ((port1 != port2 && port2 < port1) || port1 <= 0 || port2 <= 0) + throw new IgniteSpiException(errMsg); - Collection<InetSocketAddress> res = new ArrayList<>(port2 - port1); + try { + Collection<InetSocketAddress> res = new ArrayList<>(); - // Upper bound included. + InetAddress[] inetAddresses; + + try { + inetAddresses = InetAddress.getAllByName(addrStr); + } + catch (UnknownHostException e) { + // Ignore for (int i = port1; i <= port2; i++) res.add(new InetSocketAddress(addrStr, i)); return res; } - catch (IllegalArgumentException e) { - throw new IgniteSpiException(errMsg, e); - } - } - else { - try { - int port = Integer.parseInt(portStr); - return Collections.singleton(new InetSocketAddress(addrStr, port)); - } - catch (IllegalArgumentException e) { - throw new IgniteSpiException(errMsg, e); + for (InetAddress curAddr : inetAddresses) { + // Upper bound included. + for (int i = port1; i <= port2; i++) + res.add(new InetSocketAddress(curAddr, i)); } + + return res; + } + catch (IllegalArgumentException e) { + throw new IgniteSpiException(errMsg, e); } } else @@ -242,25 +261,30 @@ public class TcpDiscoveryVmIpFinder extends TcpDiscoveryIpFinderAdapter { /** {@inheritDoc} */ @Override public synchronized Collection<InetSocketAddress> getRegisteredAddresses() { - return Collections.unmodifiableCollection(addrs); + Collection<InetSocketAddress> resolvedAddrs = new LinkedHashSet<>(); + + for (String ipStr : addrs) + resolvedAddrs.addAll(address(ipStr)); + + resolvedAddrs.addAll(registeredAddrs); + + return resolvedAddrs; } /** {@inheritDoc} */ @Override public synchronized void registerAddresses(Collection<InetSocketAddress> addrs) { assert !F.isEmpty(addrs); - this.addrs = new LinkedHashSet<>(this.addrs); - - this.addrs.addAll(addrs); + if (!F.isEmpty(addrs)) + this.registeredAddrs.addAll(addrs); } /** {@inheritDoc} */ @Override public synchronized void unregisterAddresses(Collection<InetSocketAddress> addrs) { assert !F.isEmpty(addrs); - this.addrs = new LinkedHashSet<>(this.addrs); - - this.addrs.removeAll(addrs); + if (!F.isEmpty(addrs)) + this.registeredAddrs.removeAll(addrs); } /** {@inheritDoc} */ diff --git a/modules/core/src/test/java/org/apache/ignite/spi/discovery/tcp/ipfinder/vm/TcpDiscoveryVmIpFinderDnsResolveTest.java b/modules/core/src/test/java/org/apache/ignite/spi/discovery/tcp/ipfinder/vm/TcpDiscoveryVmIpFinderDnsResolveTest.java new file mode 100644 index 0000000..371cdc7 --- /dev/null +++ b/modules/core/src/test/java/org/apache/ignite/spi/discovery/tcp/ipfinder/vm/TcpDiscoveryVmIpFinderDnsResolveTest.java @@ -0,0 +1,630 @@ +/* + * 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.spi.discovery.tcp.ipfinder.vm; + +import java.lang.reflect.Field; +import java.lang.reflect.InvocationHandler; +import java.lang.reflect.Method; +import java.lang.reflect.Proxy; +import java.net.InetAddress; +import java.net.InetSocketAddress; +import java.net.UnknownHostException; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.HashSet; +import java.util.Iterator; +import java.util.Objects; +import java.util.Set; +import org.apache.ignite.testframework.junits.common.GridCommonAbstractTest; +import org.junit.AfterClass; +import org.junit.BeforeClass; +import org.junit.Test; + +/** */ +public class TcpDiscoveryVmIpFinderDnsResolveTest extends GridCommonAbstractTest { + + /** Fqnd */ + private static String FQDN = "test.domain"; + + /** Multiple Fqnd */ + private static String MULTI_FQDN = "multi.test.domain"; + + /** Incorrect fqnd */ + private static String BAD_FQDN = "bad.domain"; + + /** Incorrect ip */ + private static String BAD_IP = "555.0.0.0"; + + /** Incorrect ipv6 */ + private static String BAD_IP_6 = "[oo00::0000:ooo:ooo:ooo]"; + + /** local host address */ + private static String LOCAL_HOST = "localhost"; + + /** Ip1 */ + private static String IP1 = "10.0.0.1"; + + /** Ip2 */ + private static String IP2 = "10.0.0.2"; + + /** DNS service */ + private static TwoIpRoundRobinDnsService hostNameSvc; + + /** original DNS */ + private static Object nameSvc; + + /** + */ + @BeforeClass + public static void before() throws Exception { + hostNameSvc = new TwoIpRoundRobinDnsService(FQDN, MULTI_FQDN, IP1, IP2); + + INameService.install(hostNameSvc); + } + + /** + */ + @AfterClass + public static void cleanup() throws Exception { + INameService.uninstall(); + } + + /** + * Current test checks that in case if DNS will not be able to resolve hostname, + * then no exceptions were thrown from getRegisteredAddresses method. + */ + @Test + public void testFqdnResolveWhenDnsCantResolveHostName() { + TcpDiscoveryVmIpFinder ipFinder = new TcpDiscoveryVmIpFinder(); + + Set<String> addrs = new HashSet<>(); + + addrs.add(BAD_FQDN); + addrs.add(BAD_FQDN + ":47500"); + addrs.add(BAD_FQDN + ":47501..47502"); + + addrs.add(BAD_IP); + addrs.add(BAD_IP + ":47500"); + addrs.add(BAD_IP + ":47501..47502"); + + addrs.add(BAD_IP_6); + addrs.add(BAD_IP_6 + ":47500"); + addrs.add(BAD_IP_6 + ":47501..47502"); + + ipFinder.setAddresses(addrs); + + Collection<InetSocketAddress> resolved = ipFinder.getRegisteredAddresses(); + + assertNotNull(resolved); + + //for every host 4 entries (for 0, 47500, 47501, 47502) + assertEquals(resolved.size(), 12); + + Iterator<InetSocketAddress> it = resolved.iterator(); + + System.out.println("Resolved addresses " + resolved); + + while (it.hasNext()) { + InetSocketAddress addr = it.next(); + + assertNull(addr.getAddress()); + } + } + + /** + * Current test checks that TcpDiscoveryVmIpFinder will return new IP if DNS service will change the FQDN resolution. + * + * @param fqdn - A fully qualified domain name. + * @param expectedCount - how many addresses should be resolved. + * @throws Exception on error. + */ + public void fqdnResolveAfterDnsHostChange(String fqdn, int expectedCount) throws Exception { + TcpDiscoveryVmIpFinder ipFinder = new TcpDiscoveryVmIpFinder(); + + Set<String> addrs = new HashSet<>(); + + addrs.add(fqdn); + + ipFinder.setAddresses(addrs); + + Collection<InetSocketAddress> resolved1 = ipFinder.getRegisteredAddresses(); + + assertEquals(expectedCount, resolved1.size()); + + InetSocketAddress addr1 = resolved1.iterator().next(); + + //because of JAVA networkaddress cache ttl can be turn on. + //will be great to change current test and run it in separate JVM. + //and set there -Dsun.net.inetaddr.ttl=0 -Dsun.net.inetaddr.negative.ttl=0 + Thread.sleep(50_000); + + Collection<InetSocketAddress> resolved2 = ipFinder.getRegisteredAddresses(); + + assertEquals(expectedCount, resolved2.size()); + + InetSocketAddress addr2 = resolved2.iterator().next(); + + log.info("Adrrs1 - " + addr1.getAddress() + " Adrrs2 - " + addr2.getAddress()); + + assertFalse("Addresses weren't resolved second time. Probably DNS cache has TTL more then 1 min, if yes " + + "then please mute this test. Adrrs1 - " + addr1.getAddress() + " Adrrs2 - " + addr2.getAddress(), + addr1.equals(addr2)); + } + + /** + * Current test checks that TcpDiscoveryVmIpFinder will return new IP if DNS service will change the FQDN resolution. + * There are no ports set, only hostname. + * + * @throws Exception if failed. + */ + @Test + public void testFqdnResolveAfterDnsHostChange() throws Exception { + fqdnResolveAfterDnsHostChange(FQDN, 1); + } + + /** + * Current test checks that TcpDiscoveryVmIpFinder will return new IP if DNS service will change the FQDN resolution. + * Port will be used. + * + * @throws Exception if failed. + */ + @Test + public void testFqdnWithPortResolveAfterDnsHostChange() throws Exception { + fqdnResolveAfterDnsHostChange(FQDN + ":47500", 1); + } + + /** + * Current test checks that TcpDiscoveryVmIpFinder will return new IP if DNS service will change the FQDN resolution. + * Port range will be used, + * + * @throws Exception if failed. + */ + @Test + public void testFqdnWithPortRangeResolveAfterDnsHostChange() throws Exception { + fqdnResolveAfterDnsHostChange(FQDN + ":47500..47501", 2); + } + + /** + * Current test checks that in case of roundrobin DNS server that returns two IP if we set the FQDN name with port range + * then every pair of host/port addresses will have the same IP (not different). + */ + @Test + public void testFqdnWithPortRangeResolveWithTwoIpRoundRobinDns() { + TcpDiscoveryVmIpFinder ipFinder = new TcpDiscoveryVmIpFinder(); + + Set<String> addrs = new HashSet<>(); + + addrs.add(FQDN + ":47500..47509"); + + ipFinder.setAddresses(addrs); + + Collection<InetSocketAddress> resolved = ipFinder.getRegisteredAddresses(); + + log.info("Resolved addresses are " + resolved); + + assertTrue(resolved.size() == 10); + + Iterator<InetSocketAddress> it = resolved.iterator(); + + InetSocketAddress first = it.next(); + + String ip = first.getAddress().getHostAddress(); + + String host = first.getHostName(); + + while (it.hasNext()) { + InetSocketAddress curr = it.next(); + + assertTrue("IP address isn't the same. ip - " + curr.getAddress().getHostAddress() + " expected " + ip, + ip.equals(curr.getAddress().getHostAddress())); + + assertTrue("FQDN isn't the same. cur - " + curr.getHostName() + " expected " + host, + host.equals(curr.getHostName())); + } + } + + /** + * Current test checks that in case if TcpDiscoveryVmIpFinder has FQDN name and additional one "registeredAddress" + * then both of them can be resolved. + * + * @param fgdn - A fully qualified domain name. + * @param expectedSize - how many addresses should be resolved. + * @param expectedDomainName - expected FQDN name. + */ + public void fqdnResolveWithRegisteredAddrs(String fgdn, int expectedSize, String expectedDomainName) { + TcpDiscoveryVmIpFinder ipFinder = new TcpDiscoveryVmIpFinder(); + + Set<String> addrs = new HashSet<>(); + + addrs.add(fgdn); + + ipFinder.setAddresses(addrs); + + Collection<InetSocketAddress> registerAddrs = new HashSet<>(); + + registerAddrs.add(new InetSocketAddress(LOCAL_HOST, 0)); + + ipFinder.registerAddresses(registerAddrs); + + Collection<InetSocketAddress> resolved = ipFinder.getRegisteredAddresses(); + + assertTrue(resolved.size() == expectedSize); + + log.info("Resolved addresses are " + resolved); + + Iterator<InetSocketAddress> it = resolved.iterator(); + + while (it.hasNext()) { + InetSocketAddress addr = it.next(); + + assertNotNull(addr); + + assertTrue(expectedDomainName.equals(addr.getHostName()) || LOCAL_HOST.equals(addr.getHostName())); + } + } + + /** + * Current test checks that in case if TcpDiscoveryVmIpFinder has FQDN name and additional one "registeredAddress" + * then both of them can be resolved. There are no ports will be used, only hostname. + * + * @throws Exception if failed. + */ + @Test + public void testFqdnResolveWithRegisteredAddrs() throws Exception { + fqdnResolveWithRegisteredAddrs(FQDN, 2, FQDN); + } + + /** + * Current test checks that in case if TcpDiscoveryVmIpFinder has MULTI_FQDN (resolved to two ip) name and additional one "registeredAddress" + * then both of them can be resolved. There are no ports will be used, only hostname. + * + * @throws Exception if failed. + */ + @Test + public void testMultiFqdnResolveWithRegisteredAddrs() throws Exception { + fqdnResolveWithRegisteredAddrs(MULTI_FQDN, 3, MULTI_FQDN); + } + + /** + * Current test checks that in case if TcpDiscoveryVmIpFinder has FQDN name and additional one "registeredAddress" + * then both of them can be resolved. Port will be used. + * + * @throws Exception if failed. + */ + @Test + public void testFqdnWithPortResolveWithRegisteredAddrs() throws Exception { + fqdnResolveWithRegisteredAddrs(FQDN + ":47500", 2, FQDN); + } + + /** + * Current test checks that in case if TcpDiscoveryVmIpFinder has MULTI_FQDN (resolved to two ip) name and additional one "registeredAddress" + * then both of them can be resolved. Port will be used. + * + * @throws Exception if failed. + */ + @Test + public void testMultiFqdnWithPortResolveWithRegisteredAddrs() throws Exception { + fqdnResolveWithRegisteredAddrs(MULTI_FQDN + ":47500", 3, MULTI_FQDN); + } + + /** + * Current test checks that in case if TcpDiscoveryVmIpFinder has FQDN name and additional one "registeredAddress" + * then both of them can be resolved. Port range will be used. + * + * @throws Exception if failed. + */ + @Test + public void testFqdnWithPortRangeResolveWithRegisteredAddrs() throws Exception { + fqdnResolveWithRegisteredAddrs(FQDN + ":47500..47501", 3, FQDN); + } + + /** + * Current test checks that in case if TcpDiscoveryVmIpFinder has MULTI_FQDN (resolved to two ip) name and additional one "registeredAddress" + * then both of them can be resolved. Port range will be used. + * + * @throws Exception if failed. + */ + @Test + public void testMultiFqdnWithPortRangeResolveWithRegisteredAddrs() throws Exception { + fqdnResolveWithRegisteredAddrs(MULTI_FQDN + ":47500..47501", 5, MULTI_FQDN); + } + + /** + * Current test checks that in case if FQDN name with port range can be resolved in several IP addresses at the same time + * then should be returned set with full ip/port sets for all ip addresses that can be resolved. + */ + @Test + public void testMultiFqdnResolveWithPortRange() { + TcpDiscoveryVmIpFinder ipFinder = new TcpDiscoveryVmIpFinder(); + + Collection<String> addrs = new ArrayList<>(); + + addrs.add(MULTI_FQDN + ":47500..47509"); + + ipFinder.setAddresses(addrs); + + Collection<InetSocketAddress> resolved = ipFinder.getRegisteredAddresses(); + + assertTrue(resolved.size() == 20); + + log.info("Resolved addresses are " + resolved); + + Iterator<InetSocketAddress> it = resolved.iterator(); + + int cntIp1 = 0; + + int cntIp2 = 0; + + while (it.hasNext()) { + InetSocketAddress curr = it.next(); + + assertTrue(MULTI_FQDN.equals(curr.getHostName())); + + if (IP1.equals(curr.getAddress().getHostAddress())) + cntIp1++; + if (IP2.equals(curr.getAddress().getHostAddress())) + cntIp2++; + } + + assertTrue(cntIp1 == 10); + + assertTrue(cntIp2 == 10); + } + + /** + * Current test checks that if FQDN name can be resolved in several IP addresses at the same time + * then all of them should be returned. + * + * @param fqdn - A fully qualified domain name. + */ + public void multiFqdnResolve(String fqdn) { + TcpDiscoveryVmIpFinder ipFinder = new TcpDiscoveryVmIpFinder(); + + Collection<String> addrs = new ArrayList<>(); + + addrs.add(fqdn); + + ipFinder.setAddresses(addrs); + + Collection<InetSocketAddress> resolved = ipFinder.getRegisteredAddresses(); + + assertTrue(resolved.size() == 2); + + log.info("Resolved addresses are " + resolved); + + Iterator<InetSocketAddress> it = resolved.iterator(); + + InetSocketAddress addr1 = it.next(); + + InetSocketAddress addr2 = it.next(); + + assertNotNull(addr1); + + assertNotNull(addr2); + + assertTrue(MULTI_FQDN.equals(addr1.getHostName()) && MULTI_FQDN.equals(addr2.getHostName())); + + assertFalse("Addresses are the same. Adrrs1 - " + addr1.getAddress() + + " Adrrs2 - " + addr2.getAddress(), addr1.equals(addr2)); + } + + /** + * Current test checks that if FQDN name can be resolved in several IP addresses at the same time + * then all of them should be returned. There are no ports, only host name + */ + @Test + public void testMultiFqdnResolve() { + multiFqdnResolve(MULTI_FQDN); + } + + /** + * Current test checks that if FQDN name can be resolved in several IP addresses at the same time + * then all of them should be returned. Only one port will be set. + */ + @Test + public void testMultiFqdnWithPortResolve() { + multiFqdnResolve(MULTI_FQDN + ":47500"); + } + + /** + * Custom hostname service. + */ + @SuppressWarnings("restriction") + public static class TwoIpRoundRobinDnsService implements INameService { + /** ip1 */ + private final String ip1; + + /** ip2 */ + private final String ip2; + + /** fqdn */ + private final String fqdn; + + /** multiple fqdn */ + private final String multipleFqdn; + + /** change flag */ + private boolean needReturnIp1 = false; + + /** */ + public TwoIpRoundRobinDnsService(String fqdn, String multipleFqdn, String ip1, String ip2) { + this.multipleFqdn = multipleFqdn; + this.ip1 = ip1; + this.ip2 = ip2; + this.fqdn = fqdn; + } + + /** {@inheritDoc} */ + @Override public InetAddress[] lookupAllHostAddr(String paramStr) throws UnknownHostException { + if (fqdn.equals(paramStr)) { + String ip = needReturnIp1 ? ip1 : ip2; + + needReturnIp1 = !needReturnIp1; + + final byte[] arrOfByte = sun.net.util.IPAddressUtil.textToNumericFormatV4(ip); + + final InetAddress addr = InetAddress.getByAddress(paramStr, arrOfByte); + + return new InetAddress[] {addr}; + } + else if (multipleFqdn.equals(paramStr)) { + final byte[] arrOfByte1 = sun.net.util.IPAddressUtil.textToNumericFormatV4(ip1); + + final byte[] arrOfByte2 = sun.net.util.IPAddressUtil.textToNumericFormatV4(ip2); + + final InetAddress addr1 = InetAddress.getByAddress(paramStr, arrOfByte1); + + final InetAddress addr2 = InetAddress.getByAddress(paramStr, arrOfByte2); + + return new InetAddress[] {addr1, addr2}; + } + else + throw new UnknownHostException(); + } + + /** {@inheritDoc} */ + @Override public String getHostByAddr(byte[] paramArrOfByte) throws UnknownHostException { + throw new UnknownHostException(); + } + + /** {@inheritDoc} */ + @Override public boolean equals(Object o) { + if (this == o) + return true; + if (o == null || getClass() != o.getClass()) + return false; + TwoIpRoundRobinDnsService svc = (TwoIpRoundRobinDnsService)o; + return needReturnIp1 == svc.needReturnIp1 && + Objects.equals(ip1, svc.ip1) && + Objects.equals(ip2, svc.ip2) && + Objects.equals(fqdn, svc.fqdn) && + Objects.equals(multipleFqdn, svc.multipleFqdn); + } + + /** {@inheritDoc} */ + @Override public int hashCode() { + + return Objects.hash(ip1, ip2, fqdn, multipleFqdn, needReturnIp1); + } + } + + /** */ + public interface INameService extends InvocationHandler { + /** */ + static void install( + INameService dns) throws IllegalArgumentException, IllegalAccessException, NoSuchFieldException, SecurityException, ClassNotFoundException { + final Class<?> inetAddrCls = InetAddress.class; + + Object neu; + + Field nameSvcField; + + try { + //JAVA 9+ class + final Class<?> iface = Class.forName("java.net.InetAddress$NameService"); + + nameSvcField = inetAddrCls.getDeclaredField("nameService"); + + neu = Proxy.newProxyInstance(iface.getClassLoader(), new Class<?>[] {iface}, dns); + } + catch (final ClassNotFoundException | NoSuchFieldException e) { + //JAVA <8 class + nameSvcField = inetAddrCls.getDeclaredField("nameServices"); + + final Class<?> iface = Class.forName("sun.net.spi.nameservice.NameService"); + + neu = Collections.singletonList(Proxy.newProxyInstance(iface.getClassLoader(), new Class<?>[] {iface}, dns)); + } + + nameSvcField.setAccessible(true); + + nameSvc = nameSvcField.get(inetAddrCls); + + nameSvcField.set(inetAddrCls, neu); + } + + /** */ + static void uninstall() throws IllegalArgumentException, IllegalAccessException, NoSuchFieldException, SecurityException { + final Class<?> inetAddrCls = InetAddress.class; + + Field nameSvcField; + + try { + //JAVA 9+ class + Class.forName("java.net.InetAddress$NameService"); + + nameSvcField = inetAddrCls.getDeclaredField("nameService"); + } + catch (final ClassNotFoundException | NoSuchFieldException e) { + //JAVA <8 class + nameSvcField = inetAddrCls.getDeclaredField("nameServices"); + } + + nameSvcField.setAccessible(true); + + nameSvcField.set(inetAddrCls, nameSvc); + } + + /** + * Lookup a host mapping by name. Retrieve the IP addresses associated with a host + * + * @param host the specified hostname + * @return array of IP addresses for the requested host + * @throws UnknownHostException if no IP address for the {@code host} could be found + */ + InetAddress[] lookupAllHostAddr(final String host) throws UnknownHostException; + + /** + * Lookup the host corresponding to the IP address provided + * + * @param addr byte array representing an IP address + * @return {@code String} representing the host name mapping + * @throws UnknownHostException if no host found for the specified IP address + */ + String getHostByAddr(final byte[] addr) throws UnknownHostException; + + /** */ + @Override default Object invoke(final Object proxy, final Method method, + final Object[] args) throws Throwable { + switch (method.getName()) { + case "lookupAllHostAddr": + return lookupAllHostAddr((String)args[0]); + case "getHostByAddr": + return getHostByAddr((byte[])args[0]); + default: + final StringBuilder o = new StringBuilder(); + + o.append(method.getReturnType().getCanonicalName() + " " + method.getName() + "("); + + final Class<?>[] ps = method.getParameterTypes(); + + for (int i = 0; i < ps.length; ++i) { + if (i > 0) + o.append(", "); + + o.append(ps[i].getCanonicalName()).append(" p").append(i); + } + + o.append(")"); + + throw new UnsupportedOperationException(o.toString()); + } + } + } +} diff --git a/modules/core/src/test/java/org/apache/ignite/testsuites/IgniteSpiDiscoverySelfTestSuite.java b/modules/core/src/test/java/org/apache/ignite/testsuites/IgniteSpiDiscoverySelfTestSuite.java index f29d54a..8b0bcbc 100644 --- a/modules/core/src/test/java/org/apache/ignite/testsuites/IgniteSpiDiscoverySelfTestSuite.java +++ b/modules/core/src/test/java/org/apache/ignite/testsuites/IgniteSpiDiscoverySelfTestSuite.java @@ -75,6 +75,7 @@ import org.apache.ignite.spi.discovery.tcp.TcpDiscoveryWithWrongServerTest; import org.apache.ignite.spi.discovery.tcp.ipfinder.jdbc.TcpDiscoveryJdbcIpFinderSelfTest; import org.apache.ignite.spi.discovery.tcp.ipfinder.multicast.TcpDiscoveryMulticastIpFinderSelfTest; import org.apache.ignite.spi.discovery.tcp.ipfinder.sharedfs.TcpDiscoverySharedFsIpFinderSelfTest; +import org.apache.ignite.spi.discovery.tcp.ipfinder.vm.TcpDiscoveryVmIpFinderDnsResolveTest; import org.apache.ignite.spi.discovery.tcp.ipfinder.vm.TcpDiscoveryVmIpFinderSelfTest; import org.apache.ignite.testframework.GridTestUtils; import org.junit.BeforeClass; @@ -88,6 +89,7 @@ import static org.apache.ignite.IgniteSystemProperties.IGNITE_OVERRIDE_MCAST_GRP */ @RunWith(Suite.class) @Suite.SuiteClasses({ + TcpDiscoveryVmIpFinderDnsResolveTest.class, TcpDiscoveryVmIpFinderSelfTest.class, TcpDiscoverySharedFsIpFinderSelfTest.class, TcpDiscoveryJdbcIpFinderSelfTest.class,