diff --git a/pom.xml b/pom.xml
index ef291c5d5..6c0843fc9 100644
--- a/pom.xml
+++ b/pom.xml
@@ -189,12 +189,6 @@
0.1.1-beta1
test
-
- io.github.cdimascio
- dotenv-java
- 2.2.0
- test
-
@@ -1044,6 +1038,38 @@
ci
+
+ entraid-it
+
+
+
+ maven-surefire-plugin
+
+ true
+
+
+
+ maven-failsafe-plugin
+
+ entraid
+ false
+
+ **/*IntegrationTests
+
+
+
+
+ integration-test
+
+ integration-test
+ verify
+
+
+
+
+
+
+
diff --git a/src/test/java/io/lettuce/TestTags.java b/src/test/java/io/lettuce/TestTags.java
index 68a3434e0..06820f300 100644
--- a/src/test/java/io/lettuce/TestTags.java
+++ b/src/test/java/io/lettuce/TestTags.java
@@ -29,4 +29,9 @@ public class TestTags {
*/
public static final String API_GENERATOR = "api_generator";
+ /**
+ * Tag for EntraId integration tests (require a running environment with configured microsoft EntraId authentication)
+ */
+ public static final String ENTRA_ID = "entraid";
+
}
diff --git a/src/test/java/io/lettuce/authx/EntraIdClusterIntegrationTests.java b/src/test/java/io/lettuce/authx/EntraIdClusterIntegrationTests.java
new file mode 100644
index 000000000..e8b3873de
--- /dev/null
+++ b/src/test/java/io/lettuce/authx/EntraIdClusterIntegrationTests.java
@@ -0,0 +1,140 @@
+package io.lettuce.authx;
+
+import io.lettuce.core.ClientOptions;
+import io.lettuce.core.RedisURI;
+import io.lettuce.core.SocketOptions;
+import io.lettuce.core.TimeoutOptions;
+import io.lettuce.core.api.StatefulRedisConnection;
+import io.lettuce.core.cluster.ClusterClientOptions;
+import io.lettuce.core.cluster.ClusterTopologyRefreshOptions;
+import io.lettuce.core.cluster.RedisClusterClient;
+import io.lettuce.core.cluster.api.StatefulRedisClusterConnection;
+import io.lettuce.core.cluster.api.sync.RedisAdvancedClusterCommands;
+import io.lettuce.core.resource.ClientResources;
+import io.lettuce.core.resource.DnsResolver;
+import io.lettuce.test.env.Endpoints;
+import io.lettuce.test.env.Endpoints.Endpoint;
+import org.junit.jupiter.api.AfterAll;
+import org.junit.jupiter.api.Assumptions;
+import org.junit.jupiter.api.BeforeAll;
+import org.junit.jupiter.api.Tag;
+import org.junit.jupiter.api.Test;
+import redis.clients.authentication.core.TokenAuthConfig;
+import redis.clients.authentication.entraid.EntraIDTokenAuthConfigBuilder;
+
+import java.time.Duration;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.UUID;
+import java.util.concurrent.ExecutionException;
+
+import static io.lettuce.TestTags.ENTRA_ID;
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.assertj.core.api.Assertions.fail;
+import static org.junit.jupiter.api.Assumptions.assumeTrue;
+
+@Tag(ENTRA_ID)
+public class EntraIdClusterIntegrationTests {
+
+ private static final EntraIdTestContext testCtx = EntraIdTestContext.DEFAULT;
+
+ private static TokenBasedRedisCredentialsProvider credentialsProvider;
+
+ private static RedisClusterClient clusterClient;
+
+ private static ClientResources resources;
+
+ private static Endpoint cluster;
+
+ @BeforeAll
+ public static void setup() {
+ cluster = Endpoints.DEFAULT.getEndpoint("cluster-entraid-acl");
+ if (cluster != null) {
+ Assumptions.assumeTrue(testCtx.getClientId() != null && testCtx.getClientSecret() != null,
+ "Skipping EntraID tests. Azure AD credentials not provided!");
+
+ // Configure timeout options to assure fast test failover
+ ClusterClientOptions clientOptions = ClusterClientOptions.builder()
+ .socketOptions(SocketOptions.builder().connectTimeout(Duration.ofSeconds(1)).build())
+ .timeoutOptions(TimeoutOptions.enabled(Duration.ofSeconds(1)))
+ // enable re-authentication
+ .reauthenticateBehavior(ClientOptions.ReauthenticateBehavior.ON_NEW_CREDENTIALS).build();
+
+ TokenAuthConfig tokenAuthConfig = EntraIDTokenAuthConfigBuilder.builder().clientId(testCtx.getClientId())
+ .secret(testCtx.getClientSecret()).authority(testCtx.getAuthority()).scopes(testCtx.getRedisScopes())
+ .expirationRefreshRatio(0.0000001F).build();
+
+ credentialsProvider = TokenBasedRedisCredentialsProvider.create(tokenAuthConfig);
+
+ resources = ClientResources.builder()
+ // .dnsResolver(DnsResolver.jvmDefault())
+ .build();
+
+ List seedURI = new ArrayList<>();
+ for (String addr : cluster.getRawEndpoints().get(0).getAddr()) {
+ seedURI.add(RedisURI.builder().withAuthentication(credentialsProvider).withHost(addr)
+ .withPort(cluster.getRawEndpoints().get(0).getPort()).build());
+ }
+
+ clusterClient = RedisClusterClient.create(resources, seedURI);
+ clusterClient.setOptions(clientOptions);
+ }
+ }
+
+ @AfterAll
+ public static void cleanup() {
+ if (credentialsProvider != null) {
+ credentialsProvider.close();
+ }
+ if (resources != null) {
+ resources.shutdown();
+ }
+ }
+
+ // T.1.1
+ // Verify authentication using Azure AD with service principals using Redis Cluster Client
+ @Test
+ public void clusterWithSecret_azureServicePrincipalIntegrationTest() throws ExecutionException, InterruptedException {
+ assumeTrue(cluster != null, "Skipping EntraID tests. Redis host with enabled EntraId not provided!");
+
+ try (StatefulRedisClusterConnection defaultConnection = clusterClient.connect()) {
+ RedisAdvancedClusterCommands sync = defaultConnection.sync();
+ String keyPrefix = UUID.randomUUID().toString();
+ Map mset = prepareMset(keyPrefix);
+
+ assertThat(sync.mset(mset)).isEqualTo("OK");
+
+ for (String mykey : mset.keySet()) {
+ assertThat(defaultConnection.sync().get(mykey)).isEqualTo("value-" + mykey);
+ assertThat(defaultConnection.async().get(mykey).get()).isEqualTo("value-" + mykey);
+ assertThat(defaultConnection.reactive().get(mykey).block()).isEqualTo("value-" + mykey);
+ }
+ assertThat(sync.del(mset.keySet().toArray(new String[0]))).isEqualTo(mset.keySet().size());
+
+ // Test connections to each node
+ defaultConnection.getPartitions().forEach((partition) -> {
+ StatefulRedisConnection, ?> nodeConnection = defaultConnection.getConnection(partition.getNodeId());
+ assertThat(nodeConnection.sync().ping()).isEqualTo("PONG");
+ });
+
+ defaultConnection.getPartitions().forEach((partition) -> {
+ StatefulRedisConnection, ?> nodeConnection = defaultConnection.getConnection(partition.getUri().getHost(),
+ partition.getUri().getPort());
+ assertThat(nodeConnection.sync().ping()).isEqualTo("PONG");
+ });
+ }
+ }
+
+ Map prepareMset(String keyPrefix) {
+ Map mset = new HashMap<>();
+ for (char c = 'a'; c <= 'z'; c++) {
+ String keySuffix = new String(new char[] { c, c, c }); // Generates "aaa", "bbb", etc.
+ String key = String.format("%s-{%s}", keyPrefix, keySuffix);
+ mset.put(key, "value-" + key);
+ }
+ return mset;
+ }
+
+}
diff --git a/src/test/java/io/lettuce/authx/EntraIdIntegrationTests.java b/src/test/java/io/lettuce/authx/EntraIdIntegrationTests.java
index a4eba6704..ba2f08d76 100644
--- a/src/test/java/io/lettuce/authx/EntraIdIntegrationTests.java
+++ b/src/test/java/io/lettuce/authx/EntraIdIntegrationTests.java
@@ -9,67 +9,69 @@
import io.lettuce.core.TransactionResult;
import io.lettuce.core.api.StatefulRedisConnection;
import io.lettuce.core.api.async.RedisAsyncCommands;
+import io.lettuce.core.api.sync.RedisCommands;
import io.lettuce.core.cluster.ClusterClientOptions;
-import io.lettuce.core.cluster.RedisClusterClient;
-import io.lettuce.core.cluster.api.StatefulRedisClusterConnection;
import io.lettuce.core.pubsub.StatefulRedisPubSubConnection;
import io.lettuce.core.support.PubSubTestListener;
import io.lettuce.test.Wait;
+import io.lettuce.test.env.Endpoints;
+import io.lettuce.test.env.Endpoints.Endpoint;
import org.junit.jupiter.api.AfterAll;
import org.junit.jupiter.api.Assumptions;
import org.junit.jupiter.api.BeforeAll;
+import org.junit.jupiter.api.Tag;
import org.junit.jupiter.api.Test;
import redis.clients.authentication.core.TokenAuthConfig;
import redis.clients.authentication.entraid.EntraIDTokenAuthConfigBuilder;
import java.time.Duration;
+import java.util.UUID;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;
+import static io.lettuce.TestTags.ENTRA_ID;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.fail;
+import static org.junit.jupiter.api.Assumptions.assumeTrue;
+@Tag(ENTRA_ID)
public class EntraIdIntegrationTests {
- private static final EntraIdTestContext testCtx = EntraIdTestContext.DEFAULT;;
-
- private static ClusterClientOptions clientOptions;
+ private static final EntraIdTestContext testCtx = EntraIdTestContext.DEFAULT;
private static TokenBasedRedisCredentialsProvider credentialsProvider;
private static RedisClient client;
- private static RedisClusterClient clusterClient;
+ private static Endpoint standalone;
@BeforeAll
public static void setup() {
- Assumptions.assumeTrue(testCtx.host() != null && !testCtx.host().isEmpty(),
- "Skipping EntraID tests. Redis host with enabled EntraId not provided!");
-
- // Configure timeout options to assure fast test failover
- clientOptions = ClusterClientOptions.builder()
- .socketOptions(SocketOptions.builder().connectTimeout(Duration.ofSeconds(1)).build())
- .timeoutOptions(TimeoutOptions.enabled(Duration.ofSeconds(1)))
- .reauthenticateBehavior(ClientOptions.ReauthenticateBehavior.ON_NEW_CREDENTIALS).build();
-
- TokenAuthConfig tokenAuthConfig = EntraIDTokenAuthConfigBuilder.builder().clientId(testCtx.getClientId())
- .secret(testCtx.getClientSecret()).authority(testCtx.getAuthority()).scopes(testCtx.getRedisScopes())
- .expirationRefreshRatio(0.0000001F).build();
-
- credentialsProvider = TokenBasedRedisCredentialsProvider.create(tokenAuthConfig);
-
- RedisURI uri = RedisURI.builder().withHost(testCtx.host()).withPort(testCtx.port())
- .withAuthentication(credentialsProvider).build();
+ standalone = Endpoints.DEFAULT.getEndpoint("standalone-entraid-acl");
+ if (standalone != null) {
+ Assumptions.assumeTrue(testCtx.getClientId() != null && testCtx.getClientSecret() != null,
+ "Skipping EntraID tests. Azure AD credentials not provided!");
+ // Configure timeout options to assure fast test failover
+ ClusterClientOptions clientOptions = ClusterClientOptions.builder()
+ .socketOptions(SocketOptions.builder().connectTimeout(Duration.ofSeconds(1)).build())
+ .timeoutOptions(TimeoutOptions.enabled(Duration.ofSeconds(1)))
+ // enable re-authentication
+ .reauthenticateBehavior(ClientOptions.ReauthenticateBehavior.ON_NEW_CREDENTIALS).build();
+
+ TokenAuthConfig tokenAuthConfig = EntraIDTokenAuthConfigBuilder.builder().clientId(testCtx.getClientId())
+ .secret(testCtx.getClientSecret()).authority(testCtx.getAuthority()).scopes(testCtx.getRedisScopes())
+ .expirationRefreshRatio(0.0000001F).build();
+
+ credentialsProvider = TokenBasedRedisCredentialsProvider.create(tokenAuthConfig);
+
+ RedisURI uri = RedisURI.create((standalone.getEndpoints().get(0)));
+ uri.setCredentialsProvider(credentialsProvider);
+ client = RedisClient.create(uri);
+ client.setOptions(clientOptions);
- client = RedisClient.create(uri);
- client.setOptions(clientOptions);
-
- RedisURI clusterUri = RedisURI.builder().withHost(testCtx.clusterHost().get(0)).withPort(testCtx.clusterPort())
- .withAuthentication(credentialsProvider).build();
- clusterClient = RedisClusterClient.create(clusterUri);
- clusterClient.setOptions(clientOptions);
+ }
}
@AfterAll
@@ -83,28 +85,16 @@ public static void cleanup() {
// Verify authentication using Azure AD with service principals using Redis Standalone client
@Test
public void standaloneWithSecret_azureServicePrincipalIntegrationTest() throws ExecutionException, InterruptedException {
- try (StatefulRedisConnection connection = client.connect()) {
- assertThat(connection.sync().aclWhoami()).isEqualTo(testCtx.getSpOID());
- assertThat(connection.async().aclWhoami().get()).isEqualTo(testCtx.getSpOID());
- assertThat(connection.reactive().aclWhoami().block()).isEqualTo(testCtx.getSpOID());
- }
- }
-
- // T.1.1
- // Verify authentication using Azure AD with service principals using Redis Cluster Client
- @Test
- public void clusterWithSecret_azureServicePrincipalIntegrationTest() throws ExecutionException, InterruptedException {
-
- try (StatefulRedisClusterConnection connection = clusterClient.connect()) {
- assertThat(connection.sync().aclWhoami()).isEqualTo(testCtx.getSpOID());
- assertThat(connection.async().aclWhoami().get()).isEqualTo(testCtx.getSpOID());
- assertThat(connection.reactive().aclWhoami().block()).isEqualTo(testCtx.getSpOID());
+ assumeTrue(standalone != null, "Skipping EntraID tests. Redis host with enabled EntraId not provided!");
- connection.getPartitions().forEach((partition) -> {
- try (StatefulRedisConnection, ?> nodeConnection = connection.getConnection(partition.getNodeId())) {
- assertThat(nodeConnection.sync().aclWhoami()).isEqualTo(testCtx.getSpOID());
- }
- });
+ try (StatefulRedisConnection connection = client.connect()) {
+ RedisCommands sync = connection.sync();
+ String key = UUID.randomUUID().toString();
+ sync.set(key, "value");
+ assertThat(connection.sync().get(key)).isEqualTo("value");
+ assertThat(connection.async().get(key).get()).isEqualTo("value");
+ assertThat(connection.reactive().get(key).block()).isEqualTo("value");
+ sync.del(key);
}
}
@@ -112,6 +102,7 @@ public void clusterWithSecret_azureServicePrincipalIntegrationTest() throws Exec
// Test that the Redis client is not blocked/interrupted during token renewal.
@Test
public void renewalDuringOperationsTest() throws InterruptedException {
+ assumeTrue(standalone != null, "Skipping EntraID tests. Redis host with enabled EntraId not provided!");
// Counter to track the number of command cycles
AtomicInteger commandCycleCount = new AtomicInteger(0);
@@ -162,6 +153,8 @@ public void renewalDuringOperationsTest() throws InterruptedException {
// Test basic Pub/Sub functionality is not blocked/interrupted during token renewal.
@Test
public void renewalDuringPubSubOperationsTest() throws InterruptedException {
+ assumeTrue(standalone != null, "Skipping EntraID tests. Redis host with enabled EntraId not provided!");
+
try (StatefulRedisPubSubConnection connectionPubSub = client.connectPubSub();
StatefulRedisPubSubConnection connectionPubSub1 = client.connectPubSub()) {
@@ -183,7 +176,7 @@ public void renewalDuringPubSubOperationsTest() throws InterruptedException {
latch.countDown();
});
- assertThat(latch.await(1, TimeUnit.SECONDS)).isTrue(); // Wait for at least 10 token renewals
+ assertThat(latch.await(2, TimeUnit.SECONDS)).isTrue(); // Wait for at least 10 token renewals
pubsubThread.join(); // Wait for the pub/sub thread to finish
// Verify that all messages were received
diff --git a/src/test/java/io/lettuce/authx/EntraIdManagedIdentityIntegrationTests.java b/src/test/java/io/lettuce/authx/EntraIdManagedIdentityIntegrationTests.java
new file mode 100644
index 000000000..16cfea4a6
--- /dev/null
+++ b/src/test/java/io/lettuce/authx/EntraIdManagedIdentityIntegrationTests.java
@@ -0,0 +1,105 @@
+package io.lettuce.authx;
+
+import io.lettuce.core.ClientOptions;
+import io.lettuce.core.RedisClient;
+import io.lettuce.core.RedisURI;
+import io.lettuce.core.api.StatefulRedisConnection;
+import io.lettuce.core.api.sync.RedisCommands;
+import io.lettuce.core.cluster.ClusterClientOptions;
+import io.lettuce.test.env.Endpoints;
+import io.lettuce.test.env.Endpoints.Endpoint;
+import org.junit.jupiter.api.BeforeAll;
+import org.junit.jupiter.api.Tag;
+import org.junit.jupiter.api.Test;
+import redis.clients.authentication.core.TokenAuthConfig;
+import redis.clients.authentication.entraid.EntraIDTokenAuthConfigBuilder;
+import redis.clients.authentication.entraid.ManagedIdentityInfo;
+
+import java.util.Collections;
+import java.util.Set;
+import java.util.UUID;
+import java.util.concurrent.ExecutionException;
+
+import static io.lettuce.TestTags.ENTRA_ID;
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.junit.jupiter.api.Assumptions.assumeTrue;
+
+@Tag(ENTRA_ID)
+public class EntraIdManagedIdentityIntegrationTests {
+
+ private static final EntraIdTestContext testCtx = EntraIdTestContext.DEFAULT;
+
+ private static RedisClient client;
+
+ private static Endpoint standalone;
+
+ private static Set managedIdentityAudience = Collections.singleton("https://redis.azure.com");
+
+ @BeforeAll
+ public static void setup() {
+ standalone = Endpoints.DEFAULT.getEndpoint("standalone-entraid-acl");
+ assumeTrue(standalone != null, "Skipping test because no Redis endpoint is configured!");
+ }
+
+ @Test
+ public void withUserAssignedId_azureManagedIdentityIntegrationTest() throws ExecutionException, InterruptedException {
+
+ // enable re-authentication
+ ClusterClientOptions clientOptions = ClusterClientOptions.builder()
+ .reauthenticateBehavior(ClientOptions.ReauthenticateBehavior.ON_NEW_CREDENTIALS).build();
+
+ TokenAuthConfig tokenAuthConfig = EntraIDTokenAuthConfigBuilder.builder()
+ .userAssignedManagedIdentity(ManagedIdentityInfo.UserManagedIdentityType.OBJECT_ID,
+ testCtx.getUserAssignedManagedIdentity())
+ .scopes(managedIdentityAudience).build();
+
+ try (TokenBasedRedisCredentialsProvider credentialsProvider = TokenBasedRedisCredentialsProvider
+ .create(tokenAuthConfig)) {
+
+ RedisURI uri = RedisURI.create((standalone.getEndpoints().get(0)));
+ uri.setCredentialsProvider(credentialsProvider);
+ client = RedisClient.create(uri);
+ client.setOptions(clientOptions);
+
+ try (StatefulRedisConnection connection = client.connect()) {
+ RedisCommands sync = connection.sync();
+ String key = UUID.randomUUID().toString();
+ sync.set(key, "value");
+ assertThat(connection.sync().get(key)).isEqualTo("value");
+ assertThat(connection.async().get(key).get()).isEqualTo("value");
+ assertThat(connection.reactive().get(key).block()).isEqualTo("value");
+ sync.del(key);
+ }
+ }
+ }
+
+ @Test
+ public void withSystemAssignedId_azureManagedIdentityIntegrationTest() throws ExecutionException, InterruptedException {
+ // enable re-authentication
+ ClusterClientOptions clientOptions = ClusterClientOptions.builder()
+ .reauthenticateBehavior(ClientOptions.ReauthenticateBehavior.ON_NEW_CREDENTIALS).build();
+
+ TokenAuthConfig tokenAuthConfig = EntraIDTokenAuthConfigBuilder.builder().systemAssignedManagedIdentity()
+ .scopes(managedIdentityAudience).build();
+
+ try (TokenBasedRedisCredentialsProvider credentialsProvider = TokenBasedRedisCredentialsProvider
+ .create(tokenAuthConfig)) {
+
+ RedisURI uri = RedisURI.create((standalone.getEndpoints().get(0)));
+ uri.setCredentialsProvider(credentialsProvider);
+ client = RedisClient.create(uri);
+ client.setOptions(clientOptions);
+
+ try (StatefulRedisConnection connection = client.connect()) {
+ RedisCommands sync = connection.sync();
+ String key = UUID.randomUUID().toString();
+ sync.set(key, "value");
+ assertThat(connection.sync().get(key)).isEqualTo("value");
+ assertThat(connection.async().get(key).get()).isEqualTo("value");
+ assertThat(connection.reactive().get(key).block()).isEqualTo("value");
+ sync.del(key);
+ }
+ }
+ }
+
+}
diff --git a/src/test/java/io/lettuce/authx/EntraIdTestContext.java b/src/test/java/io/lettuce/authx/EntraIdTestContext.java
index 7abfac0fe..551dc0358 100644
--- a/src/test/java/io/lettuce/authx/EntraIdTestContext.java
+++ b/src/test/java/io/lettuce/authx/EntraIdTestContext.java
@@ -1,10 +1,7 @@
package io.lettuce.authx;
-import io.github.cdimascio.dotenv.Dotenv;
-
import java.util.Arrays;
import java.util.HashSet;
-import java.util.List;
import java.util.Set;
public class EntraIdTestContext {
@@ -13,21 +10,11 @@ public class EntraIdTestContext {
private static final String AZURE_CLIENT_SECRET = "AZURE_CLIENT_SECRET";
- private static final String AZURE_SP_OID = "AZURE_SP_OID";
-
private static final String AZURE_AUTHORITY = "AZURE_AUTHORITY";
private static final String AZURE_REDIS_SCOPES = "AZURE_REDIS_SCOPES";
- private static final String REDIS_AZURE_HOST = "REDIS_AZURE_HOST";
-
- private static final String REDIS_AZURE_PORT = "REDIS_AZURE_PORT";
-
- private static final String REDIS_AZURE_CLUSTER_HOST = "REDIS_AZURE_CLUSTER_HOST";
-
- private static final String REDIS_AZURE_CLUSTER_PORT = "REDIS_AZURE_CLUSTER_PORT";
-
- private static final String REDIS_AZURE_DB = "REDIS_AZURE_DB";
+ private static final String AZURE_USER_ASSIGNED_MANAGED_ID = "AZURE_USER_ASSIGNED_MANAGED_ID";
private final String clientId;
@@ -35,67 +22,32 @@ public class EntraIdTestContext {
private final String clientSecret;
- private final String spOID;
-
- private final Set redisScopes;
+ private Set redisScopes;
- private final String redisHost;
-
- private final int redisPort;
-
- private final List redisClusterHost;
-
- private final int redisClusterPort;
-
- private static Dotenv dotenv;
- static {
- dotenv = Dotenv.configure().directory("src/test/resources").filename(".env.entraid").load();
- }
+ private String userAssignedManagedIdentity;
public static final EntraIdTestContext DEFAULT = new EntraIdTestContext();
private EntraIdTestContext() {
- // Using Dotenv directly here
- clientId = dotenv.get(AZURE_CLIENT_ID, "");
- clientSecret = dotenv.get(AZURE_CLIENT_SECRET, "");
- spOID = dotenv.get(AZURE_SP_OID, "");
- authority = dotenv.get(AZURE_AUTHORITY, "https://login.microsoftonline.com/your-tenant-id");
- redisHost = dotenv.get(REDIS_AZURE_HOST);
- redisPort = Integer.parseInt(dotenv.get(REDIS_AZURE_PORT, "6379"));
- redisClusterHost = Arrays.asList(dotenv.get(REDIS_AZURE_CLUSTER_HOST, "").split(","));
- redisClusterPort = Integer.parseInt(dotenv.get(REDIS_AZURE_CLUSTER_PORT, "6379"));
- String redisScopesEnv = dotenv.get(AZURE_REDIS_SCOPES, "https://redis.azure.com/.default");
- if (redisScopesEnv != null && !redisScopesEnv.isEmpty()) {
- this.redisScopes = new HashSet<>(Arrays.asList(redisScopesEnv.split(";")));
- } else {
- this.redisScopes = new HashSet<>();
- }
- }
-
- public String host() {
- return redisHost;
- }
-
- public int port() {
- return redisPort;
- }
-
- public List clusterHost() {
- return redisClusterHost;
+ clientId = System.getenv(AZURE_CLIENT_ID);
+ authority = System.getenv(AZURE_AUTHORITY);
+ clientSecret = System.getenv(AZURE_CLIENT_SECRET);
+ this.userAssignedManagedIdentity = System.getenv(AZURE_USER_ASSIGNED_MANAGED_ID);
}
- public int clusterPort() {
- return redisClusterPort;
+ public EntraIdTestContext(String clientId, String authority, String clientSecret, Set redisScopes,
+ String userAssignedManagedIdentity) {
+ this.clientId = clientId;
+ this.authority = authority;
+ this.clientSecret = clientSecret;
+ this.redisScopes = redisScopes;
+ this.userAssignedManagedIdentity = userAssignedManagedIdentity;
}
public String getClientId() {
return clientId;
}
- public String getSpOID() {
- return spOID;
- }
-
public String getAuthority() {
return authority;
}
@@ -105,7 +57,15 @@ public String getClientSecret() {
}
public Set getRedisScopes() {
+ if (redisScopes == null) {
+ String redisScopesEnv = System.getenv(AZURE_REDIS_SCOPES);
+ this.redisScopes = new HashSet<>(Arrays.asList(redisScopesEnv.split(";")));
+ }
return redisScopes;
}
+ public String getUserAssignedManagedIdentity() {
+ return userAssignedManagedIdentity;
+ }
+
}
diff --git a/src/test/java/io/lettuce/test/env/Endpoints.java b/src/test/java/io/lettuce/test/env/Endpoints.java
new file mode 100644
index 000000000..0631713cd
--- /dev/null
+++ b/src/test/java/io/lettuce/test/env/Endpoints.java
@@ -0,0 +1,235 @@
+package io.lettuce.test.env;
+
+import com.fasterxml.jackson.annotation.JsonProperty;
+
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import com.fasterxml.jackson.databind.ObjectMapper;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.io.File;
+import java.io.IOException;
+
+public class Endpoints {
+
+ private static final Logger log = LoggerFactory.getLogger(Endpoints.class);
+
+ private Map endpoints;
+
+ public static final Endpoints DEFAULT;
+
+ static {
+ String filePath = System.getenv("REDIS_ENDPOINTS_CONFIG_PATH");
+ if (filePath == null || filePath.isEmpty()) {
+ log.info("REDIS_ENDPOINTS_CONFIG_PATH environment variable is not set. No Endpoints configuration will be loaded.");
+ DEFAULT = new Endpoints(Collections.emptyMap());
+ } else {
+ DEFAULT = fromFile(filePath);
+ }
+ }
+
+ private Endpoints(Map endpoints) {
+ this.endpoints = endpoints;
+ }
+
+ /**
+ * Factory method to create an Endpoints instance from a file.
+ *
+ * @param filePath Path to the JSON file.
+ * @return Populated Endpoints instance.
+ */
+ public static Endpoints fromFile(String filePath) {
+ try {
+ ObjectMapper objectMapper = new ObjectMapper();
+ File file = new File(filePath);
+
+ HashMap endpoints = objectMapper.readValue(file,
+ objectMapper.getTypeFactory().constructMapType(HashMap.class, String.class, Endpoint.class));
+ return new Endpoints(endpoints);
+
+ } catch (IOException e) {
+ throw new RuntimeException("Failed to load Endpoints from file: " + filePath, e);
+ }
+ }
+
+ /**
+ * Get an endpoint by name.
+ *
+ * @param name the name of the endpoint.
+ * @return the corresponding Endpoint or {@code null} if not found.
+ */
+ public Endpoint getEndpoint(String name) {
+ return endpoints != null ? endpoints.get(name) : null;
+ }
+
+ public Map getEndpoints() {
+ return endpoints;
+ }
+
+ public void setEndpoints(Map endpoints) {
+ this.endpoints = endpoints;
+ }
+
+ // Inner classes for Endpoint and RawEndpoint
+ public static class Endpoint {
+
+ @JsonProperty("bdb_id")
+ private int bdbId;
+
+ private String username;
+
+ private String password;
+
+ private boolean tls;
+
+ @JsonProperty("raw_endpoints")
+ private List rawEndpoints;
+
+ private List endpoints;
+
+ // Getters and Setters
+ public int getBdbId() {
+ return bdbId;
+ }
+
+ public void setBdbId(int bdbId) {
+ this.bdbId = bdbId;
+ }
+
+ public String getUsername() {
+ return username;
+ }
+
+ public void setUsername(String username) {
+ this.username = username;
+ }
+
+ public String getPassword() {
+ return password;
+ }
+
+ public void setPassword(String password) {
+ this.password = password;
+ }
+
+ public boolean isTls() {
+ return tls;
+ }
+
+ public void setTls(boolean tls) {
+ this.tls = tls;
+ }
+
+ public List getRawEndpoints() {
+ return rawEndpoints;
+ }
+
+ public void setRawEndpoints(List rawEndpoints) {
+ this.rawEndpoints = rawEndpoints;
+ }
+
+ public List getEndpoints() {
+ return endpoints;
+ }
+
+ public void setEndpoints(List endpoints) {
+ this.endpoints = endpoints;
+ }
+
+ }
+
+ public static class RawEndpoint {
+
+ private List addr;
+
+ @JsonProperty("addr_type")
+ private String addrType;
+
+ @JsonProperty("dns_name")
+ private String dnsName;
+
+ @JsonProperty("oss_cluster_api_preferred_endpoint_type")
+ private String preferredEndpointType;
+
+ @JsonProperty("oss_cluster_api_preferred_ip_type")
+ private String preferredIpType;
+
+ private int port;
+
+ @JsonProperty("proxy_policy")
+ private String proxyPolicy;
+
+ private String uid;
+
+ // Getters and Setters
+ public List getAddr() {
+ return addr;
+ }
+
+ public void setAddr(List addr) {
+ this.addr = addr;
+ }
+
+ public String getAddrType() {
+ return addrType;
+ }
+
+ public void setAddrType(String addrType) {
+ this.addrType = addrType;
+ }
+
+ public String getDnsName() {
+ return dnsName;
+ }
+
+ public void setDnsName(String dnsName) {
+ this.dnsName = dnsName;
+ }
+
+ public String getPreferredEndpointType() {
+ return preferredEndpointType;
+ }
+
+ public void setPreferredEndpointType(String preferredEndpointType) {
+ this.preferredEndpointType = preferredEndpointType;
+ }
+
+ public String getPreferredIpType() {
+ return preferredIpType;
+ }
+
+ public void setPreferredIpType(String preferredIpType) {
+ this.preferredIpType = preferredIpType;
+ }
+
+ public int getPort() {
+ return port;
+ }
+
+ public void setPort(int port) {
+ this.port = port;
+ }
+
+ public String getProxyPolicy() {
+ return proxyPolicy;
+ }
+
+ public void setProxyPolicy(String proxyPolicy) {
+ this.proxyPolicy = proxyPolicy;
+ }
+
+ public String getUid() {
+ return uid;
+ }
+
+ public void setUid(String uid) {
+ this.uid = uid;
+ }
+
+ }
+
+}