Skip to content

Commit 3e537f9

Browse files
authored
Fix TBA cluster integration tests (#4068)
* - fix cluster int tests - add entraid integ tests - fix ConcurrentModification * fix ConcurrentModification with RedisEntraIDClusterIntegrationTests.testClusterWithReAuth
1 parent e6a94d4 commit 3e537f9

File tree

2 files changed

+155
-12
lines changed

2 files changed

+155
-12
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,134 @@
1+
package redis.clients.jedis.authentication;
2+
3+
import static org.hamcrest.Matchers.greaterThanOrEqualTo;
4+
import static org.junit.Assert.assertEquals;
5+
import static org.mockito.ArgumentMatchers.any;
6+
import static org.mockito.Mockito.atLeast;
7+
import static org.mockito.Mockito.doAnswer;
8+
import static org.mockito.Mockito.spy;
9+
import static org.mockito.Mockito.verify;
10+
import static org.awaitility.Awaitility.await;
11+
import static org.awaitility.Durations.*;
12+
13+
import java.util.Collections;
14+
import java.util.List;
15+
import java.util.concurrent.CopyOnWriteArrayList;
16+
import java.util.concurrent.CountDownLatch;
17+
import java.util.concurrent.ExecutionException;
18+
import java.util.concurrent.ExecutorService;
19+
import java.util.concurrent.Executors;
20+
import java.util.concurrent.Future;
21+
22+
import org.junit.BeforeClass;
23+
import org.junit.Test;
24+
import org.slf4j.Logger;
25+
import org.slf4j.LoggerFactory;
26+
27+
import redis.clients.authentication.core.TokenAuthConfig;
28+
import redis.clients.authentication.entraid.EntraIDTokenAuthConfigBuilder;
29+
import redis.clients.jedis.Connection;
30+
import redis.clients.jedis.ConnectionPoolConfig;
31+
import redis.clients.jedis.DefaultJedisClientConfig;
32+
import redis.clients.jedis.EndpointConfig;
33+
import redis.clients.jedis.HostAndPort;
34+
import redis.clients.jedis.HostAndPorts;
35+
import redis.clients.jedis.JedisClientConfig;
36+
import redis.clients.jedis.JedisCluster;
37+
38+
public class RedisEntraIDClusterIntegrationTests {
39+
private static final Logger log = LoggerFactory
40+
.getLogger(RedisEntraIDClusterIntegrationTests.class);
41+
42+
private static EntraIDTestContext testCtx;
43+
private static EndpointConfig endpointConfig;
44+
private static HostAndPort hnp;
45+
46+
@BeforeClass
47+
public static void before() {
48+
try {
49+
testCtx = EntraIDTestContext.DEFAULT;
50+
endpointConfig = HostAndPorts.getRedisEndpoint("cluster-entraid-acl");
51+
hnp = endpointConfig.getHostAndPort();
52+
} catch (IllegalArgumentException e) {
53+
log.warn("Skipping test because no Redis endpoint is configured");
54+
org.junit.Assume.assumeTrue(false);
55+
}
56+
}
57+
58+
@Test
59+
public void testClusterInitWithAuthXManager() {
60+
TokenAuthConfig tokenAuthConfig = EntraIDTokenAuthConfigBuilder.builder()
61+
.lowerRefreshBoundMillis(1000).clientId(testCtx.getClientId())
62+
.secret(testCtx.getClientSecret()).authority(testCtx.getAuthority())
63+
.scopes(testCtx.getRedisScopes()).build();
64+
65+
int defaultDirections = 5;
66+
JedisClientConfig config = DefaultJedisClientConfig.builder()
67+
.authXManager(new AuthXManager(tokenAuthConfig)).build();
68+
69+
ConnectionPoolConfig DEFAULT_POOL_CONFIG = new ConnectionPoolConfig();
70+
71+
try (JedisCluster jc = new JedisCluster(hnp, config, defaultDirections,
72+
DEFAULT_POOL_CONFIG)) {
73+
74+
assertEquals("OK", jc.set("foo", "bar"));
75+
assertEquals("bar", jc.get("foo"));
76+
assertEquals(1, jc.del("foo"));
77+
}
78+
}
79+
80+
@Test
81+
public void testClusterWithReAuth() throws InterruptedException, ExecutionException {
82+
TokenAuthConfig tokenAuthConfig = EntraIDTokenAuthConfigBuilder.builder()
83+
// 0.00002F is to make it fit into 2 seconds, we need at least 2 attempt in 2 seconds
84+
// to trigger re-authentication.
85+
// For expiration time between 30 minutes to 12 hours
86+
// token renew will happen in from 36ms up to 864ms
87+
// If the received token has more than 12 hours to expire, this test will probably fail, and need to be adjusted.
88+
.expirationRefreshRatio(0.00002F).clientId(testCtx.getClientId())
89+
.secret(testCtx.getClientSecret()).authority(testCtx.getAuthority())
90+
.scopes(testCtx.getRedisScopes()).build();
91+
92+
AuthXManager authXManager = new AuthXManager(tokenAuthConfig);
93+
94+
authXManager = spy(authXManager);
95+
96+
List<Connection> connections = new CopyOnWriteArrayList<>();
97+
doAnswer(invocation -> {
98+
Connection connection = spy((Connection) invocation.getArgument(0));
99+
invocation.getArguments()[0] = connection;
100+
connections.add(connection);
101+
Object result = invocation.callRealMethod();
102+
return result;
103+
}).when(authXManager).addConnection(any(Connection.class));
104+
105+
JedisClientConfig config = DefaultJedisClientConfig.builder().authXManager(authXManager)
106+
.build();
107+
108+
ExecutorService executorService = Executors.newFixedThreadPool(2);
109+
CountDownLatch latch = new CountDownLatch(1);
110+
try (JedisCluster jc = new JedisCluster(Collections.singleton(hnp), config)) {
111+
Runnable task = () -> {
112+
while (latch.getCount() > 0) {
113+
assertEquals("OK", jc.set("foo", "bar"));
114+
}
115+
};
116+
Future task1 = executorService.submit(task);
117+
Future task2 = executorService.submit(task);
118+
119+
await().pollInterval(ONE_HUNDRED_MILLISECONDS).atMost(TWO_SECONDS)
120+
.until(connections::size, greaterThanOrEqualTo(2));
121+
122+
connections.forEach(conn -> {
123+
await().pollInterval(ONE_HUNDRED_MILLISECONDS).atMost(TWO_SECONDS)
124+
.untilAsserted(() -> verify(conn, atLeast(2)).reAuthenticate());
125+
});
126+
latch.countDown();
127+
task1.get();
128+
task2.get();
129+
} finally {
130+
latch.countDown();
131+
executorService.shutdown();
132+
}
133+
}
134+
}

src/test/java/redis/clients/jedis/authentication/TokenBasedAuthenticationClusterIntegrationTests.java

+21-12
Original file line numberDiff line numberDiff line change
@@ -10,9 +10,9 @@
1010
import static org.awaitility.Awaitility.await;
1111
import static org.awaitility.Durations.*;
1212

13-
import java.util.ArrayList;
1413
import java.util.Collections;
1514
import java.util.List;
15+
import java.util.concurrent.CopyOnWriteArrayList;
1616
import java.util.concurrent.CountDownLatch;
1717
import java.util.concurrent.ExecutionException;
1818
import java.util.concurrent.ExecutorService;
@@ -28,7 +28,7 @@
2828
import redis.clients.authentication.core.IdentityProviderConfig;
2929
import redis.clients.authentication.core.SimpleToken;
3030
import redis.clients.authentication.core.Token;
31-
import redis.clients.authentication.entraid.EntraIDTokenAuthConfigBuilder;
31+
import redis.clients.authentication.core.TokenAuthConfig;
3232
import redis.clients.jedis.Connection;
3333
import redis.clients.jedis.ConnectionPoolConfig;
3434
import redis.clients.jedis.DefaultJedisClientConfig;
@@ -48,7 +48,7 @@ public class TokenBasedAuthenticationClusterIntegrationTests {
4848
@BeforeClass
4949
public static void before() {
5050
try {
51-
endpointConfig = HostAndPorts.getRedisEndpoint("cluster-entraid-acl");
51+
endpointConfig = HostAndPorts.getRedisEndpoint("cluster");
5252
hnp = endpointConfig.getHostAndPort();
5353
} catch (IllegalArgumentException e) {
5454
log.warn("Skipping test because no Redis endpoint is configured");
@@ -64,20 +64,25 @@ public IdentityProvider getProvider() {
6464
return new IdentityProvider() {
6565
@Override
6666
public Token requestToken() {
67-
return new SimpleToken("default", "cluster",
67+
return new SimpleToken(endpointConfig.getUsername(),
68+
endpointConfig.getPassword() == null ? ""
69+
: endpointConfig.getPassword(),
6870
System.currentTimeMillis() + 5 * 1000, System.currentTimeMillis(),
6971
null);
7072
}
7173
};
7274
}
7375
};
74-
AuthXManager manager = new AuthXManager(EntraIDTokenAuthConfigBuilder.builder()
75-
.lowerRefreshBoundMillis(1000).identityProviderConfig(idpConfig).build());
76+
77+
AuthXManager manager = new AuthXManager(TokenAuthConfig.builder()
78+
.lowerRefreshBoundMillis(1000).tokenRequestExecTimeoutInMs(3000)
79+
.identityProviderConfig(idpConfig).build());
7680

7781
int defaultDirections = 5;
7882
JedisClientConfig config = DefaultJedisClientConfig.builder().authXManager(manager).build();
7983

8084
ConnectionPoolConfig DEFAULT_POOL_CONFIG = new ConnectionPoolConfig();
85+
8186
try (JedisCluster jc = new JedisCluster(hnp, config, defaultDirections,
8287
DEFAULT_POOL_CONFIG)) {
8388

@@ -95,19 +100,23 @@ public IdentityProvider getProvider() {
95100
return new IdentityProvider() {
96101
@Override
97102
public Token requestToken() {
98-
return new SimpleToken("default", "cluster",
103+
return new SimpleToken(endpointConfig.getUsername(),
104+
endpointConfig.getPassword() == null ? ""
105+
: endpointConfig.getPassword(),
99106
System.currentTimeMillis() + 5 * 1000, System.currentTimeMillis(),
100107
null);
101108
}
102109
};
103110
}
104111
};
105-
AuthXManager authXManager = new AuthXManager(EntraIDTokenAuthConfigBuilder.builder()
106-
.lowerRefreshBoundMillis(4600).identityProviderConfig(idpConfig).build());
112+
113+
AuthXManager authXManager = new AuthXManager(TokenAuthConfig.builder()
114+
.lowerRefreshBoundMillis(4600).tokenRequestExecTimeoutInMs(3000)
115+
.identityProviderConfig(idpConfig).build());
107116

108117
authXManager = spy(authXManager);
109118

110-
List<Connection> connections = new ArrayList<>();
119+
List<Connection> connections = new CopyOnWriteArrayList<>();
111120
doAnswer(invocation -> {
112121
Connection connection = spy((Connection) invocation.getArgument(0));
113122
invocation.getArguments()[0] = connection;
@@ -130,11 +139,11 @@ public Token requestToken() {
130139
Future task1 = executorService.submit(task);
131140
Future task2 = executorService.submit(task);
132141

133-
await().pollInterval(ONE_HUNDRED_MILLISECONDS).atMost(ONE_SECOND)
142+
await().pollInterval(ONE_HUNDRED_MILLISECONDS).atMost(TWO_SECONDS)
134143
.until(connections::size, greaterThanOrEqualTo(2));
135144

136145
connections.forEach(conn -> {
137-
await().pollInterval(ONE_HUNDRED_MILLISECONDS).atMost(ONE_SECOND)
146+
await().pollInterval(ONE_HUNDRED_MILLISECONDS).atMost(TWO_SECONDS)
138147
.untilAsserted(() -> verify(conn, atLeast(2)).reAuthenticate());
139148
});
140149
latch.countDown();

0 commit comments

Comments
 (0)