diff --git a/migration-tools/modules/migration-tools-config-converter/src/testFixtures/java/org/apache/ignite/migrationtools/config/ConfigExamples.java b/migration-tools/modules/migration-tools-config-converter/src/testFixtures/java/org/apache/ignite/migrationtools/config/ConfigExamples.java index e18f6f3b1f32..990ed71b7b9d 100644 --- a/migration-tools/modules/migration-tools-config-converter/src/testFixtures/java/org/apache/ignite/migrationtools/config/ConfigExamples.java +++ b/migration-tools/modules/migration-tools-config-converter/src/testFixtures/java/org/apache/ignite/migrationtools/config/ConfigExamples.java @@ -32,9 +32,10 @@ private ConfigExamples() { */ public static Stream configPaths() { return Stream.of( - "configs-custom/ignite-config.0.xml", - "configs-custom/ignite-config.1.xml", - "configs-custom/ignite-config.2.xml" + "configs-custom/ignite-config.0.xml" + // TODO: Uncomment the following after fixing IGNITE-27378 + // "configs-custom/ignite-config.1.xml", + // "configs-custom/ignite-config.2.xml" ); } } diff --git a/modules/network/src/integrationTest/java/org/apache/ignite/internal/network/ItStaticNodeFinderTest.java b/modules/network/src/integrationTest/java/org/apache/ignite/internal/network/ItStaticNodeFinderTest.java new file mode 100644 index 000000000000..18267e7ad6fa --- /dev/null +++ b/modules/network/src/integrationTest/java/org/apache/ignite/internal/network/ItStaticNodeFinderTest.java @@ -0,0 +1,63 @@ +/* + * 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.network; + +import static org.apache.ignite.internal.testframework.IgniteTestUtils.assertThrowsWithCause; +import static org.apache.ignite.internal.util.ExceptionUtils.unwrapRootCause; +import static org.apache.ignite.lang.ErrorGroups.Network.ADDRESS_UNRESOLVED_ERR; +import static org.junit.jupiter.api.Assertions.assertEquals; + +import org.apache.ignite.internal.ClusterPerClassIntegrationTest; +import org.apache.ignite.internal.lang.IgniteInternalException; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.TestInfo; + +/** + * Tests that node finder failure causes node shutdown. + */ +class ItStaticNodeFinderTest extends ClusterPerClassIntegrationTest { + @Override + protected int initialNodes() { + return 1; + } + + @Override + protected String getNodeBootstrapConfigTemplate() { + return "ignite {\n" + + " network: {\n" + + " nodeFinder.netClusterNodes: [ \"bad.host:1234\" ]\n" + + " },\n" + + "}"; + } + + @Override + protected boolean needInitializeCluster() { + return false; + } + + @Test + void testNodeShutdownOnNodeFinderFailure(TestInfo testInfo) { + Throwable throwable = assertThrowsWithCause( + () -> CLUSTER.startAndInit(testInfo, initialNodes(), cmgMetastoreNodes(), this::configureInitParameters), + IgniteInternalException.class); + + IgniteInternalException actual = (IgniteInternalException) unwrapRootCause(throwable); + assertEquals(ADDRESS_UNRESOLVED_ERR, actual.code()); + assertEquals("No network addresses resolved through any provided names", actual.getMessage()); + } +} diff --git a/modules/network/src/main/java/org/apache/ignite/internal/network/StaticNodeFinder.java b/modules/network/src/main/java/org/apache/ignite/internal/network/StaticNodeFinder.java index 69450f036539..92fee00d2793 100644 --- a/modules/network/src/main/java/org/apache/ignite/internal/network/StaticNodeFinder.java +++ b/modules/network/src/main/java/org/apache/ignite/internal/network/StaticNodeFinder.java @@ -17,13 +17,16 @@ package org.apache.ignite.internal.network; -import static java.util.stream.Collectors.toList; +import static java.util.stream.Collectors.toSet; +import static org.apache.ignite.lang.ErrorGroups.Network.ADDRESS_UNRESOLVED_ERR; import java.net.InetAddress; import java.net.UnknownHostException; import java.util.Arrays; import java.util.Collection; import java.util.List; +import java.util.Set; +import org.apache.ignite.internal.lang.IgniteInternalException; import org.apache.ignite.internal.logger.IgniteLogger; import org.apache.ignite.internal.logger.Loggers; import org.apache.ignite.internal.util.ArrayUtils; @@ -41,6 +44,8 @@ */ public class StaticNodeFinder implements NodeFinder { private static final IgniteLogger LOG = Loggers.forClass(StaticNodeFinder.class); + private static final long RETRY_WAIT_BASE_MILLIS = 500; + private static final int MAX_TRIES = 3; /** List of seed cluster members. */ private final List addresses; @@ -56,12 +61,22 @@ public StaticNodeFinder(List addresses) { @Override public Collection findNodes() { - return addresses.parallelStream() + if (addresses.isEmpty()) { + return Set.of(); + } + + Collection networkAddresses = addresses.parallelStream() .flatMap( originalAddress -> Arrays.stream(resolveAll(originalAddress.host())) .map(ip -> new NetworkAddress(ip, originalAddress.port())) ) - .collect(toList()); + .collect(toSet()); + + if (networkAddresses.isEmpty()) { + throw new IgniteInternalException(ADDRESS_UNRESOLVED_ERR, "No network addresses resolved through any provided names"); + } + + return networkAddresses; } @Override @@ -70,14 +85,34 @@ public void start() { } private static String[] resolveAll(String host) { - InetAddress[] inetAddresses; - try { - inetAddresses = InetAddress.getAllByName(host); - } catch (UnknownHostException e) { - LOG.warn("Cannot resolve {}", host); - return ArrayUtils.STRING_EMPTY_ARRAY; - } + InetAddress[] inetAddresses = null; + + int tryCount = 0; + boolean resolved = false; + + do { + tryCount++; + + try { + inetAddresses = InetAddress.getAllByName(host); + resolved = true; + } catch (UnknownHostException e) { + if (tryCount == MAX_TRIES) { + LOG.warn("Cannot resolve {}", host); + return ArrayUtils.STRING_EMPTY_ARRAY; + } + + try { + Thread.sleep(tryCount * RETRY_WAIT_BASE_MILLIS); + } catch (InterruptedException ex) { + Thread.currentThread().interrupt(); + + return ArrayUtils.STRING_EMPTY_ARRAY; + } + } + } while (!resolved); + assert inetAddresses != null; String[] addresses = new String[inetAddresses.length]; for (int i = 0; i < inetAddresses.length; i++) { InetAddress inetAddress = inetAddresses[i]; diff --git a/modules/network/src/test/java/org/apache/ignite/internal/network/StaticNodeFinderTest.java b/modules/network/src/test/java/org/apache/ignite/internal/network/StaticNodeFinderTest.java index a1dcdbd38562..611385afd50b 100644 --- a/modules/network/src/test/java/org/apache/ignite/internal/network/StaticNodeFinderTest.java +++ b/modules/network/src/test/java/org/apache/ignite/internal/network/StaticNodeFinderTest.java @@ -24,6 +24,9 @@ import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.contains; import static org.hamcrest.Matchers.containsInAnyOrder; +import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertThrows; import java.io.IOException; import java.io.InputStream; @@ -32,6 +35,7 @@ import java.util.Arrays; import java.util.List; import java.util.Map; +import org.apache.ignite.internal.lang.IgniteInternalException; import org.apache.ignite.internal.testframework.WorkDirectory; import org.apache.ignite.internal.testframework.WorkDirectoryExtension; import org.apache.ignite.network.NetworkAddress; @@ -52,6 +56,40 @@ void returnsIpAddresses() { assertThat(finder.findNodes(), contains(ipv4, ipv6)); } + @Test + void removesDuplicateIpAddresses() { + NetworkAddress ip1 = new NetworkAddress("1.2.3.4", 3001); + NetworkAddress ip2 = new NetworkAddress("1.2.3.4", 3001); + NodeFinder finder = new StaticNodeFinder(List.of(ip1, ip2)); + + assertThat(finder.findNodes(), contains(ip1)); + } + + @Test + void returnsEmptyResultForEmptyInput() { + NodeFinder finder = new StaticNodeFinder(List.of()); + + assertEquals(0, finder.findNodes().size()); + } + + @Test + void failsForNoResolvedIpAddresses() { + NetworkAddress ip1 = new NetworkAddress("badIpString", 3001); + NodeFinder finder = new StaticNodeFinder(List.of(ip1)); + + assertThrows(IgniteInternalException.class, finder::findNodes); + } + + @Test + void succeedsForAtLeastOneResolvedIpAddresses() { + NetworkAddress ip1 = new NetworkAddress("badIpString", 3001); + NetworkAddress ip2 = new NetworkAddress("1.2.3.4", 3001); + NodeFinder finder = new StaticNodeFinder(List.of(ip1, ip2)); + + assertDoesNotThrow(finder::findNodes); + assertThat(finder.findNodes(), contains(ip2)); + } + @Test void resolvesLocalHostToJustOneAddress() throws Exception { Path hostsFilePath = writeHostsFile(Map.of(