diff --git a/src/main/antora/modules/ROOT/pages/redis/connection-modes.adoc b/src/main/antora/modules/ROOT/pages/redis/connection-modes.adoc index cc010e7cf8..e509a06fca 100644 --- a/src/main/antora/modules/ROOT/pages/redis/connection-modes.adoc +++ b/src/main/antora/modules/ROOT/pages/redis/connection-modes.adoc @@ -103,6 +103,9 @@ public RedisConnectionFactory jedisConnectionFactory() { * `spring.redis.sentinel.nodes`: Comma delimited list of host:port pairs. * `spring.redis.sentinel.username`: The username to apply when authenticating with Redis Sentinel (requires Redis 6) * `spring.redis.sentinel.password`: The password to apply when authenticating with Redis Sentinel +* `spring.redis.sentinel.dataNode.username`: The username to apply when authenticating with Redis Data Node +* `spring.redis.sentinel.dataNode.password`: The password to apply when authenticating with Redis Data Node +* `spring.redis.sentinel.dataNode.database`: The database index to apply when authenticating with Redis Data Node ==== Sometimes, direct interaction with one of the Sentinels is required. Using `RedisConnectionFactory.getSentinelConnection()` or `RedisConnection.getSentinelCommands()` gives you access to the first active Sentinel configured. diff --git a/src/main/java/org/springframework/data/redis/connection/RedisSentinelConfiguration.java b/src/main/java/org/springframework/data/redis/connection/RedisSentinelConfiguration.java index 3f8414c0d2..583d539bb3 100644 --- a/src/main/java/org/springframework/data/redis/connection/RedisSentinelConfiguration.java +++ b/src/main/java/org/springframework/data/redis/connection/RedisSentinelConfiguration.java @@ -41,6 +41,8 @@ * @author Mark Paluch * @author Vikas Garg * @author John Blum + * @author Samuel Klose + * @author Mustapha Zorgati * @since 1.4 */ public class RedisSentinelConfiguration implements RedisConfiguration, SentinelConfiguration { @@ -49,6 +51,9 @@ public class RedisSentinelConfiguration implements RedisConfiguration, SentinelC private static final String REDIS_SENTINEL_NODES_CONFIG_PROPERTY = "spring.redis.sentinel.nodes"; private static final String REDIS_SENTINEL_USERNAME_CONFIG_PROPERTY = "spring.redis.sentinel.username"; private static final String REDIS_SENTINEL_PASSWORD_CONFIG_PROPERTY = "spring.redis.sentinel.password"; + private static final String REDIS_SENTINEL_DATA_NODE_USERNAME_CONFIG_PROPERTY = "spring.redis.sentinel.dataNode.username"; + private static final String REDIS_SENTINEL_DATA_NODE_PASSWORD_CONFIG_PROPERTY = "spring.redis.sentinel.dataNode.password"; + private static final String REDIS_SENTINEL_DATA_NODE_DATABASE_CONFIG_PROPERTY = "spring.redis.sentinel.dataNode.database"; private int database; @@ -122,6 +127,30 @@ public RedisSentinelConfiguration(PropertySource propertySource) { String sentinelUsername = String.valueOf(propertySource.getProperty(REDIS_SENTINEL_USERNAME_CONFIG_PROPERTY)); this.setSentinelUsername(sentinelUsername); } + + if (propertySource.containsProperty(REDIS_SENTINEL_DATA_NODE_USERNAME_CONFIG_PROPERTY)) { + String dataNodeUsername = String + .valueOf(propertySource.getProperty(REDIS_SENTINEL_DATA_NODE_USERNAME_CONFIG_PROPERTY)); + this.setUsername(dataNodeUsername); + } + + if (propertySource.containsProperty(REDIS_SENTINEL_DATA_NODE_PASSWORD_CONFIG_PROPERTY)) { + String dataNodePassword = String + .valueOf(propertySource.getProperty(REDIS_SENTINEL_DATA_NODE_PASSWORD_CONFIG_PROPERTY)); + this.setPassword(dataNodePassword); + } + + if (propertySource.containsProperty(REDIS_SENTINEL_DATA_NODE_DATABASE_CONFIG_PROPERTY)) { + String databaseSource = String + .valueOf(propertySource.getProperty(REDIS_SENTINEL_DATA_NODE_DATABASE_CONFIG_PROPERTY)); + int database; + try { + database = Integer.parseInt(databaseSource); + } catch (NumberFormatException ex) { + throw new IllegalArgumentException(String.format("Invalid DB index '%s'; integer required", databaseSource)); + } + this.setDatabase(database); + } } /** diff --git a/src/test/java/org/springframework/data/redis/connection/RedisSentinelConfigurationUnitTests.java b/src/test/java/org/springframework/data/redis/connection/RedisSentinelConfigurationUnitTests.java index 74f433fe65..055139d5a0 100644 --- a/src/test/java/org/springframework/data/redis/connection/RedisSentinelConfigurationUnitTests.java +++ b/src/test/java/org/springframework/data/redis/connection/RedisSentinelConfigurationUnitTests.java @@ -21,6 +21,7 @@ import java.util.Collections; import java.util.HashSet; +import org.assertj.core.api.ThrowableAssert; import org.junit.jupiter.api.Test; import org.springframework.mock.env.MockPropertySource; @@ -32,6 +33,8 @@ * @author Christoph Strobl * @author Mark Paluch * @author Vikas Garg + * @author Samuel Klose + * @author Mustapha Zorgati */ class RedisSentinelConfigurationUnitTests { @@ -186,4 +189,56 @@ void readSentinelUsernameFromConfigProperty() { assertThat(config.getSentinelPassword()).isEqualTo(RedisPassword.of("foo")); assertThat(config.getSentinels()).hasSize(1).contains(new RedisNode("127.0.0.1", 123)); } + + @Test // GH-2860 + void readSentinelDataNodeUsernameFromConfigProperty() { + MockPropertySource propertySource = new MockPropertySource(); + propertySource.setProperty("spring.redis.sentinel.dataNode.username", "datanode-user"); + + RedisSentinelConfiguration config = new RedisSentinelConfiguration(propertySource); + + assertThat(config.getDataNodeUsername()).isEqualTo("datanode-user"); + } + + @Test // GH-2860 + void readSentinelDataNodePasswordFromConfigProperty() { + MockPropertySource propertySource = new MockPropertySource(); + propertySource.setProperty("spring.redis.sentinel.dataNode.password", "datanode-password"); + + RedisSentinelConfiguration config = new RedisSentinelConfiguration(propertySource); + + assertThat(config.getDataNodePassword()).isEqualTo(RedisPassword.of("datanode-password")); + } + + @Test // GH-2860 + void readSentinelDataNodeDatabaseFromConfigProperty() { + MockPropertySource propertySource = new MockPropertySource(); + propertySource.setProperty("spring.redis.sentinel.dataNode.database", "5"); + + RedisSentinelConfiguration config = new RedisSentinelConfiguration(propertySource); + + assertThat(config.getDatabase()).isEqualTo(5); + } + + @Test // GH-2860 + void shouldThrowErrorWhen() { + MockPropertySource propertySource = new MockPropertySource(); + propertySource.setProperty("spring.redis.sentinel.dataNode.database", "thisIsNotAnInteger"); + + ThrowableAssert.ThrowingCallable call = () -> new RedisSentinelConfiguration(propertySource); + + assertThatThrownBy(call).isInstanceOf(IllegalArgumentException.class) + .hasMessage("Invalid DB index '%s'; integer required", "thisIsNotAnInteger"); + } + + @Test // GH-2860 + void shouldThrowErrorWhen2() { + MockPropertySource propertySource = new MockPropertySource(); + propertySource.setProperty("spring.redis.sentinel.dataNode.database", "null"); + + ThrowableAssert.ThrowingCallable call = () -> new RedisSentinelConfiguration(propertySource); + + assertThatThrownBy(call).isInstanceOf(IllegalArgumentException.class) + .hasMessage("Invalid DB index '%s'; integer required", "null"); + } }