This is an automated email from the ASF dual-hosted git repository.
apupier pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/camel.git
The following commit(s) were added to refs/heads/main by this push:
new 9ba6a0ac42e4 fix(camel-infinispan): fix test failures on Linux with
Podman due to IPv6 loopback resolution
9ba6a0ac42e4 is described below
commit 9ba6a0ac42e4d4b51c848147d367f0c0001a050e
Author: Aurélien Pupier <[email protected]>
AuthorDate: Wed Oct 7 11:03:36 2026 +0200
fix(camel-infinispan): fix test failures on Linux with Podman due to
IPv6 loopback resolution
On Linux with Podman, containers are started in host-network mode so
that port mappings work without a separate network namespace. In that
mode, Testcontainers' container.getHost() returns "localhost". The
JVM on recent Linux systems resolves "localhost" to the IPv6 loopback
address (::1) before the IPv4 one (127.0.0.1), but the Infinispan
server only binds on IPv4. Every fresh RemoteCacheManager therefore
failed its initial TCP connection with:
TransportException: finishConnect(..) failed with error(-111):
Connection refused: localhost/[0:0:0:0:0:0:0:1]:11222
Three distinct problems were causing test failures:
1. Initial connection uses the wrong address family
All test helpers that built a ConfigurationBuilder passed
service.host() (or service.getServiceAddress()) directly to
.host(...) / setHosts(...). This caused the IPv6 resolution.
Fix: add a InfinispanRemoteTestSupport.resolvedHost(String) helper
that maps "localhost" -> "127.0.0.1" and apply it at every call
site:
- InfinispanRemoteTestSupport.getConfiguration()
- InfinispanRemoteKeyValueRepositoryIT.getConfiguration()
- InfinispanRemoteClusteredTestSupport.createConfiguration()
- InfinispanRemoteConfigurationIT.getBaseConfiguration()
-
SpringInfinispanRemoteIdempotentRepositoryTestSupport.setupResources()
-
InfinispanRemoteEmbeddingStoreIT.createInfinispanRemoteConfiguration()
2. Topology-aware client intelligence re-resolves "localhost" to ::1
When BASIC client intelligence was not set, the Hotrod client asked
the server for a topology update after the initial connection and
re-connected to the advertised address, which was again "localhost"
-> ::1. The conditional guard
if (!Boolean.getBoolean(INFINISPAN_CONTAINER_NETWORK_MODE_HOST))
wrongly skipped BASIC mode in host-network mode, meaning every new
RemoteCacheManager created inside a @BeforeEach would fail on the
topology-driven reconnect.
Fix: always set BASIC client intelligence unconditionally in all
test ConfigurationBuilders; remove the now-unused
InfinispanProperties import.
3. TransportException not retried during schema registration
InfinispanRemoteManager.registerSchemaWithRetry() caught
HotRodClientException but immediately re-threw any exception that
was not an IllegalLifecycleStateException (line: "if
(!isIllegalLifecycleStateException(e)) throw e").
TransportException is a HotRodClientException subclass representing
a transient connectivity failure; on the very first retry attempt
it escaped the loop, task.run() returned false, and the method
threw IllegalStateException("Failed to register schema after PT1M")
without actually waiting.
Fix: add isTransportException() and treat TransportException as a
retriable condition alongside IllegalLifecycleStateException. This
is also correct in production: if the server is briefly unreachable
at startup (e.g. Kubernetes rolling deploy), schema registration
should retry within the configured timeout rather than fail
immediately.
Additionally, waitForCacheReady() in InfinispanRemoteTestSupport
only ignored RemoteIllegalLifecycleStateException; it now also
ignores TransportException so that transient startup races do not
immediately fail the Awaitility condition.
Co-authored-by: IBM Bob 2.2.1
Signed-off-by: Aurélien Pupier <[email protected]>
---
.../infinispan/remote/InfinispanRemoteManager.java | 6 ++++-
.../remote/InfinispanRemoteConfigurationIT.java | 5 +++-
.../remote/InfinispanRemoteEmbeddingStoreIT.java | 4 ++-
.../InfinispanRemoteKeyValueRepositoryIT.java | 16 +++++++-----
.../remote/InfinispanRemoteTestSupport.java | 30 +++++++++++++++-------
.../InfinispanRemoteClusteredTestSupport.java | 4 ++-
...ispanRemoteIdempotentRepositoryTestSupport.java | 13 ++++++++--
7 files changed, 56 insertions(+), 22 deletions(-)
diff --git
a/components/camel-infinispan/camel-infinispan/src/main/java/org/apache/camel/component/infinispan/remote/InfinispanRemoteManager.java
b/components/camel-infinispan/camel-infinispan/src/main/java/org/apache/camel/component/infinispan/remote/InfinispanRemoteManager.java
index 092ce4e7f6f3..67c7d55440f8 100644
---
a/components/camel-infinispan/camel-infinispan/src/main/java/org/apache/camel/component/infinispan/remote/InfinispanRemoteManager.java
+++
b/components/camel-infinispan/camel-infinispan/src/main/java/org/apache/camel/component/infinispan/remote/InfinispanRemoteManager.java
@@ -167,7 +167,7 @@ public class InfinispanRemoteManager extends ServiceSupport
implements Infinispa
EmbeddingStoreUtil.registerSchema(configuration,
cacheContainer);
return true;
} catch (HotRodClientException e) {
- if (!isIllegalLifecycleStateException(e)) {
+ if (!isIllegalLifecycleStateException(e) &&
!isTransportException(e)) {
throw e;
}
if (firstAttempt[0]) {
@@ -191,6 +191,10 @@ public class InfinispanRemoteManager extends
ServiceSupport implements Infinispa
}
}
+ private static boolean isTransportException(HotRodClientException e) {
+ return e.getClass().getSimpleName().contains("TransportException");
+ }
+
private static boolean
isIllegalLifecycleStateException(HotRodClientException e) {
if
(e.getClass().getSimpleName().contains("IllegalLifecycleStateException")) {
return true;
diff --git
a/components/camel-infinispan/camel-infinispan/src/test/java/org/apache/camel/component/infinispan/remote/InfinispanRemoteConfigurationIT.java
b/components/camel-infinispan/camel-infinispan/src/test/java/org/apache/camel/component/infinispan/remote/InfinispanRemoteConfigurationIT.java
index a446e5ce95d6..39fb9e4477fb 100644
---
a/components/camel-infinispan/camel-infinispan/src/test/java/org/apache/camel/component/infinispan/remote/InfinispanRemoteConfigurationIT.java
+++
b/components/camel-infinispan/camel-infinispan/src/test/java/org/apache/camel/component/infinispan/remote/InfinispanRemoteConfigurationIT.java
@@ -66,7 +66,10 @@ public class InfinispanRemoteConfigurationIT {
cacheContConf.put("connection_timeout", "15000");
configuration.setConfigurationProperties(cacheContConf);
- configuration.setHosts(service.host() + ":" + service.port());
+ // Normalise "localhost" to "127.0.0.1" so the JVM does not resolve it
to ::1 (IPv6),
+ // which the server does not bind to in host-network mode on
Podman/Linux.
+ String host = InfinispanRemoteTestSupport.resolvedHost(service.host());
+ configuration.setHosts(host + ":" + service.port());
configuration.setSecure(true);
configuration.setUsername(service.username());
configuration.setPassword(service.password());
diff --git
a/components/camel-infinispan/camel-infinispan/src/test/java/org/apache/camel/component/infinispan/remote/InfinispanRemoteEmbeddingStoreIT.java
b/components/camel-infinispan/camel-infinispan/src/test/java/org/apache/camel/component/infinispan/remote/InfinispanRemoteEmbeddingStoreIT.java
index 913f16234a31..6bea22160325 100644
---
a/components/camel-infinispan/camel-infinispan/src/test/java/org/apache/camel/component/infinispan/remote/InfinispanRemoteEmbeddingStoreIT.java
+++
b/components/camel-infinispan/camel-infinispan/src/test/java/org/apache/camel/component/infinispan/remote/InfinispanRemoteEmbeddingStoreIT.java
@@ -188,7 +188,9 @@ public class InfinispanRemoteEmbeddingStoreIT extends
InfinispanRemoteTestSuppor
private InfinispanRemoteConfiguration
createInfinispanRemoteConfiguration() {
InfinispanRemoteConfiguration configuration = new
InfinispanRemoteConfiguration();
- configuration.setHosts(service.getServiceAddress());
+ // Normalise "localhost" to "127.0.0.1" so the JVM does not resolve it
to ::1 (IPv6),
+ // which the server does not bind to in host-network mode on
Podman/Linux.
+
configuration.setHosts(InfinispanRemoteTestSupport.resolvedHost(service.host())
+ ":" + service.port());
configuration.setUsername(service.username());
configuration.setPassword(service.password());
configuration.setSaslMechanism("SCRAM-SHA-512");
diff --git
a/components/camel-infinispan/camel-infinispan/src/test/java/org/apache/camel/component/infinispan/remote/InfinispanRemoteKeyValueRepositoryIT.java
b/components/camel-infinispan/camel-infinispan/src/test/java/org/apache/camel/component/infinispan/remote/InfinispanRemoteKeyValueRepositoryIT.java
index 64cafe5a80f3..3b0660a89a82 100644
---
a/components/camel-infinispan/camel-infinispan/src/test/java/org/apache/camel/component/infinispan/remote/InfinispanRemoteKeyValueRepositoryIT.java
+++
b/components/camel-infinispan/camel-infinispan/src/test/java/org/apache/camel/component/infinispan/remote/InfinispanRemoteKeyValueRepositoryIT.java
@@ -23,7 +23,6 @@ import java.util.concurrent.TimeUnit;
import org.apache.camel.CamelContext;
import org.apache.camel.impl.DefaultCamelContext;
-import org.apache.camel.test.infra.infinispan.common.InfinispanProperties;
import org.apache.camel.test.infra.infinispan.services.InfinispanService;
import
org.apache.camel.test.infra.infinispan.services.InfinispanServiceFactory;
import org.infinispan.client.hotrod.RemoteCacheManager;
@@ -282,9 +281,11 @@ class InfinispanRemoteKeyValueRepositoryIT {
clientBuilder.forceReturnValues(true);
+ // Normalise "localhost" to "127.0.0.1" so the JVM does not resolve it
to ::1 (IPv6),
+ // which the server does not bind to in host-network mode on
Podman/Linux.
clientBuilder
.addServer()
- .host(service.host())
+ .host("localhost".equalsIgnoreCase(service.host()) ?
"127.0.0.1" : service.host())
.port(service.port());
clientBuilder
@@ -298,11 +299,12 @@ class InfinispanRemoteKeyValueRepositoryIT {
.saslMechanism("SCRAM-SHA-512")
.realm("default");
- if
(!Boolean.getBoolean(InfinispanProperties.INFINISPAN_CONTAINER_NETWORK_MODE_HOST))
{
- Properties properties = new Properties();
- properties.put("infinispan.client.hotrod.client_intelligence",
"BASIC");
- clientBuilder.withProperties(properties);
- }
+ // Always use BASIC intelligence to prevent the client from following
server topology
+ // and reconnecting to an address that may resolve to IPv6 (::1) on
Podman/Linux,
+ // where the Infinispan server only listens on IPv4.
+ Properties properties = new Properties();
+ properties.put("infinispan.client.hotrod.client_intelligence",
"BASIC");
+ clientBuilder.withProperties(properties);
return clientBuilder;
}
}
diff --git
a/components/camel-infinispan/camel-infinispan/src/test/java/org/apache/camel/component/infinispan/remote/InfinispanRemoteTestSupport.java
b/components/camel-infinispan/camel-infinispan/src/test/java/org/apache/camel/component/infinispan/remote/InfinispanRemoteTestSupport.java
index 347fce543e68..5e4aa8f61e9e 100644
---
a/components/camel-infinispan/camel-infinispan/src/test/java/org/apache/camel/component/infinispan/remote/InfinispanRemoteTestSupport.java
+++
b/components/camel-infinispan/camel-infinispan/src/test/java/org/apache/camel/component/infinispan/remote/InfinispanRemoteTestSupport.java
@@ -26,13 +26,13 @@ import org.apache.camel.support.task.ForegroundTask;
import org.apache.camel.support.task.Tasks;
import org.apache.camel.support.task.budget.Budgets;
import org.apache.camel.support.task.budget.IterationBoundedBudget;
-import org.apache.camel.test.infra.infinispan.common.InfinispanProperties;
import org.apache.camel.test.infra.infinispan.services.InfinispanService;
import
org.apache.camel.test.infra.infinispan.services.InfinispanServiceFactory;
import org.awaitility.Awaitility;
import org.infinispan.client.hotrod.RemoteCacheManager;
import org.infinispan.client.hotrod.configuration.ConfigurationBuilder;
import
org.infinispan.client.hotrod.exceptions.RemoteIllegalLifecycleStateException;
+import org.infinispan.client.hotrod.exceptions.TransportException;
import org.infinispan.commons.api.BasicCache;
import org.infinispan.configuration.cache.CacheMode;
import org.junit.jupiter.api.Assumptions;
@@ -113,10 +113,11 @@ public class InfinispanRemoteTestSupport extends
InfinispanTestSupport {
clientBuilder
.forceReturnValues(true);
- // add server from the test infra service
+ // add server from the test infra service; normalise "localhost" to
"127.0.0.1" so the
+ // JVM does not resolve it to the IPv6 loopback (::1), which the
server does not bind to.
clientBuilder
.addServer()
- .host(service.host())
+ .host(resolvedHost(service.host()))
.port(service.port());
// add security info
@@ -131,14 +132,24 @@ public class InfinispanRemoteTestSupport extends
InfinispanTestSupport {
.saslMechanism("SCRAM-SHA-512")
.realm("default");
- if
(!Boolean.getBoolean(InfinispanProperties.INFINISPAN_CONTAINER_NETWORK_MODE_HOST))
{
- Properties properties = new Properties();
- properties.put("infinispan.client.hotrod.client_intelligence",
"BASIC");
- clientBuilder.withProperties(properties);
- }
+ // Always use BASIC intelligence to prevent the client from following
server topology
+ // and reconnecting to an address that may resolve to IPv6 (::1) on
Podman/Linux,
+ // where the Infinispan server only listens on IPv4.
+ Properties properties = new Properties();
+ properties.put("infinispan.client.hotrod.client_intelligence",
"BASIC");
+ clientBuilder.withProperties(properties);
return clientBuilder;
}
+ /**
+ * Normalises a hostname so that {@code "localhost"} is always returned as
{@code "127.0.0.1"}. On Linux with Podman
+ * the JVM resolves {@code "localhost"} to the IPv6 loopback ({@code ::1})
before the IPv4 one, but the Infinispan
+ * server only listens on IPv4 in host-network mode.
+ */
+ public static String resolvedHost(String host) {
+ return "localhost".equalsIgnoreCase(host) ? "127.0.0.1" : host;
+ }
+
@BindToRegistry
public ComponentCustomizer infinispanComponentCustomizer() {
return ComponentCustomizer.forType(
@@ -150,7 +161,8 @@ public class InfinispanRemoteTestSupport extends
InfinispanTestSupport {
Awaitility.await()
.atMost(Duration.ofMillis(timeoutMs))
.pollInterval(Duration.ofMillis(250))
- .ignoreExceptionsMatching(e -> e instanceof
RemoteIllegalLifecycleStateException)
+ .ignoreExceptionsMatching(
+ e -> e instanceof RemoteIllegalLifecycleStateException
|| e instanceof TransportException)
.until(() -> {
// Attempt to create/get the cache
manager.administration()
diff --git
a/components/camel-infinispan/camel-infinispan/src/test/java/org/apache/camel/component/infinispan/remote/cluster/InfinispanRemoteClusteredTestSupport.java
b/components/camel-infinispan/camel-infinispan/src/test/java/org/apache/camel/component/infinispan/remote/cluster/InfinispanRemoteClusteredTestSupport.java
index 99bd7026d9a7..fa77ee376532 100644
---
a/components/camel-infinispan/camel-infinispan/src/test/java/org/apache/camel/component/infinispan/remote/cluster/InfinispanRemoteClusteredTestSupport.java
+++
b/components/camel-infinispan/camel-infinispan/src/test/java/org/apache/camel/component/infinispan/remote/cluster/InfinispanRemoteClusteredTestSupport.java
@@ -35,10 +35,12 @@ public final class InfinispanRemoteClusteredTestSupport {
properties.put("infinispan.client.hotrod.client_intelligence",
"BASIC");
+ // Normalise "localhost" to "127.0.0.1" so the JVM does not resolve it
to ::1 (IPv6),
+ // which the server does not bind to in host-network mode on
Podman/Linux.
return new ConfigurationBuilder()
.withProperties(properties)
.addServer()
- .host(service.host())
+ .host("localhost".equalsIgnoreCase(service.host()) ?
"127.0.0.1" : service.host())
.port(service.port())
.security()
.authentication()
diff --git
a/components/camel-infinispan/camel-infinispan/src/test/java/org/apache/camel/component/infinispan/remote/spring/SpringInfinispanRemoteIdempotentRepositoryTestSupport.java
b/components/camel-infinispan/camel-infinispan/src/test/java/org/apache/camel/component/infinispan/remote/spring/SpringInfinispanRemoteIdempotentRepositoryTestSupport.java
index 16b64f0efd67..edb5cf4320e2 100644
---
a/components/camel-infinispan/camel-infinispan/src/test/java/org/apache/camel/component/infinispan/remote/spring/SpringInfinispanRemoteIdempotentRepositoryTestSupport.java
+++
b/components/camel-infinispan/camel-infinispan/src/test/java/org/apache/camel/component/infinispan/remote/spring/SpringInfinispanRemoteIdempotentRepositoryTestSupport.java
@@ -16,6 +16,7 @@
*/
package org.apache.camel.component.infinispan.remote.spring;
+import java.util.Properties;
import java.util.UUID;
import
org.apache.camel.component.infinispan.remote.InfinispanRemoteTestSupport;
@@ -46,10 +47,11 @@ public abstract class
SpringInfinispanRemoteIdempotentRepositoryTestSupport exte
clientBuilder
.forceReturnValues(true);
- // add server from the test infra service
+ // add server from the test infra service; normalise "localhost" to
"127.0.0.1" so the
+ // JVM does not resolve it to the IPv6 loopback (::1), which the
server does not bind to.
clientBuilder
.addServer()
- .host(service.host())
+ .host(InfinispanRemoteTestSupport.resolvedHost(service.host()))
.port(service.port());
// add security info
@@ -64,6 +66,13 @@ public abstract class
SpringInfinispanRemoteIdempotentRepositoryTestSupport exte
.saslMechanism("SCRAM-SHA-512")
.realm("default");
+ // Always use BASIC intelligence to prevent the client from following
server topology
+ // and reconnecting to an address that may resolve to IPv6 (::1) on
Podman/Linux,
+ // where the Infinispan server only listens on IPv4.
+ Properties intelligenceProps = new Properties();
+ intelligenceProps.put("infinispan.client.hotrod.client_intelligence",
"BASIC");
+ clientBuilder.withProperties(intelligenceProps);
+
RemoteCacheManager manager = new
RemoteCacheManager(clientBuilder.create());
MarshallerRegistration.init(MarshallerUtil.getSerializationContext(manager));
InfinispanRemoteTestSupport.waitForCacheReady(manager, "idempotent",
30000);