Skip to content

Commit

Permalink
feat(redis): provide Redis hosts programmatically
Browse files Browse the repository at this point in the history
This allows for configuration of properties like redis connection password coming from other
sources.

Closes #16284
  • Loading branch information
machi1990 committed Apr 8, 2021
1 parent 9e917d4 commit f544fc9
Show file tree
Hide file tree
Showing 9 changed files with 222 additions and 7 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@
import io.quarkus.deployment.builditem.nativeimage.RuntimeInitializedClassBuildItem;
import io.quarkus.redis.client.RedisClient;
import io.quarkus.redis.client.RedisClientName;
import io.quarkus.redis.client.RedisHostsProvider;
import io.quarkus.redis.client.reactive.ReactiveRedisClient;
import io.quarkus.redis.client.runtime.MutinyRedis;
import io.quarkus.redis.client.runtime.MutinyRedisAPI;
Expand All @@ -53,6 +54,14 @@ ExtensionSslNativeSupportBuildItem activateSslNativeSupport() {
return new ExtensionSslNativeSupportBuildItem(Feature.REDIS_CLIENT.getName());
}

@BuildStep
AdditionalBeanBuildItem registerAdditionalBeans() {
return new AdditionalBeanBuildItem.Builder()
.setUnremovable()
.addBeanClass(RedisHostsProvider.class)
.build();
}

@BuildStep
List<AdditionalBeanBuildItem> registerRedisBeans() {
return Arrays.asList(
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
package io.quarkus.redis.client;

import java.net.URI;
import java.util.Set;

/**
* Programmatically provides redis hosts
*/
public interface RedisHostsProvider {
/**
* Returns the hosts for this provider.
* <p>
* The host provided uses the following schema `redis://[username:password@][host][:port][/database]`
*
* @return the hosts
*/
Set<URI> getHosts();
}
Original file line number Diff line number Diff line change
@@ -1,8 +1,12 @@
package io.quarkus.redis.client.runtime;

import java.net.URI;
import java.util.Collections;
import java.util.Set;

import io.quarkus.arc.Arc;
import io.quarkus.arc.ArcContainer;
import io.quarkus.redis.client.RedisHostsProvider;
import io.quarkus.redis.client.runtime.RedisConfig.RedisConfiguration;
import io.quarkus.runtime.configuration.ConfigurationException;
import io.vertx.redis.client.RedisClientType;
Expand All @@ -14,20 +18,25 @@ public class RedisClientUtil {
public static RedisOptions buildOptions(RedisConfiguration redisConfig) {
RedisOptions options = new RedisOptions();
options.setType(redisConfig.clientType);
Set<URI> hosts = Collections.emptySet();

if (redisConfig.hosts.isPresent()) {
hosts = redisConfig.hosts.get();
} else if (redisConfig.hostsProviderName.isPresent()) {
RedisHostsProvider hostsProvider = findProvider(redisConfig.hostsProviderName.get());
hosts = hostsProvider.getHosts();
}

if (RedisClientType.STANDALONE == redisConfig.clientType) {
if (redisConfig.hosts.isPresent() && redisConfig.hosts.get().size() > 1) {
if (hosts.size() > 1) {
throw new ConfigurationException("Multiple hosts supplied for non clustered configuration");
}
}

if (redisConfig.hosts.isPresent()) {
Set<URI> hosts = redisConfig.hosts.get();
for (URI host : hosts) {
options.addConnectionString(host.toString());
}

for (URI host : hosts) {
options.addConnectionString(host.toString());
}

options.setMaxNestedArrays(redisConfig.maxNestedArrays);
options.setMaxWaitingHandlers(redisConfig.maxWaitingHandlers);
options.setMaxPoolSize(redisConfig.maxPoolSize);
Expand Down Expand Up @@ -60,4 +69,17 @@ public static boolean isDefault(String clientName) {
public static RedisConfiguration getConfiguration(RedisConfig config, String name) {
return isDefault(name) ? config.defaultClient : config.additionalRedisClients.get(name);
}

public static RedisHostsProvider findProvider(String name) {
ArcContainer container = Arc.container();
RedisHostsProvider hostsProvider = name != null
? (RedisHostsProvider) container.instance(name).get()
: container.instance(RedisHostsProvider.class).get();

if (hostsProvider == null) {
throw new RuntimeException("unable to find redis host provider named: " + (name == null ? "default" : name));
}

return hostsProvider;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -59,12 +59,26 @@ public static class RedisConfiguration {
* 1 element.
* <p>
* The URI provided uses the following schema `redis://[username:password@][host][:port][/database]`
* Use `quarkus.redis.hosts-provider-name` to provide the hosts programmatically.
* <p>
*
* @see <a href="https://www.iana.org/assignments/uri-schemes/prov/redis">Redis scheme on www.iana.org</a>
*/
@ConfigItem(defaultValueDocumentation = "redis://localhost:6379")
public Optional<Set<URI>> hosts;

/**
* The hosts provider bean name.
* <p>
* It is the {@code &#64;Named} value of the hosts provider bean. It is used to discriminate if multiple
* `io.quarkus.redis.client.RedisHostsProvider` beans are available.
*
* <p>
* Used when `quarkus.redis.hosts` is not set.
*/
@ConfigItem
public Optional<String> hostsProviderName = Optional.empty();

/**
* The maximum delay to wait before a blocking command to redis server times out
*/
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
package io.quarkus.redis.it;

import java.net.URI;
import java.util.Collections;
import java.util.Set;

import javax.enterprise.context.ApplicationScoped;
import javax.inject.Named;

import io.quarkus.redis.client.RedisHostsProvider;

@ApplicationScoped
@Named("test-hosts-provider")
public class RedisLocalHostProvider implements RedisHostsProvider {
@Override
public Set<URI> getHosts() {
return Collections.singleton(URI.create("redis://localhost:6379/3"));
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
package io.quarkus.redis.it;

import java.util.Arrays;

import javax.enterprise.context.ApplicationScoped;
import javax.inject.Inject;
import javax.ws.rs.GET;
import javax.ws.rs.POST;
import javax.ws.rs.Path;
import javax.ws.rs.PathParam;

import io.quarkus.redis.client.RedisClient;
import io.quarkus.redis.client.RedisClientName;
import io.quarkus.redis.client.reactive.ReactiveRedisClient;
import io.smallrye.mutiny.Uni;
import io.vertx.redis.client.Response;

@Path("/quarkus-redis-provided-hosts")
@ApplicationScoped
public class RedisWithProvidedHostsResource {
private RedisClient redisClient;
private ReactiveRedisClient reactiveRedisClient;

@Inject
public RedisWithProvidedHostsResource(@RedisClientName("provided-hosts") RedisClient redisClient,
@RedisClientName("provided-hosts") ReactiveRedisClient reactiveRedisClient) {
this.redisClient = redisClient;
this.reactiveRedisClient = reactiveRedisClient;
}

// synchronous
@GET
@Path("/sync/{key}")
public String getSync(@PathParam("key") String key) {
Response response = redisClient.get(key);
return response == null ? null : response.toString();
}

@POST
@Path("/sync/{key}")
public void setSync(@PathParam("key") String key, String value) {
this.redisClient.set(Arrays.asList(key, value));
}

// reactive
@GET
@Path("/reactive/{key}")
public Uni<String> getReactive(@PathParam("key") String key) {
return reactiveRedisClient
.get(key)
.map(response -> response == null ? null : response.toString());
}

@POST
@Path("/reactive/{key}")
public Uni<Void> setReactive(@PathParam("key") String key, String value) {
return this.reactiveRedisClient
.set(Arrays.asList(key, value))
.map(response -> null);
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -2,3 +2,4 @@ quarkus.redis.hosts=redis://localhost:6379/0
quarkus.redis.named-client.hosts=redis://localhost:6379/1
quarkus.redis.parameter-injection.hosts=redis://localhost:6379/2
quarkus.redis.named-reactive-client.hosts=redis://localhost:6379/1
quarkus.redis.provided-hosts.hosts-provider-name=test-hosts-provider
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
package io.quarkus.redis.it;

import io.quarkus.test.junit.NativeImageTest;

@NativeImageTest
class QuarkusRedisWithProvidedHostsIT extends QuarkusRedisWithProvidedHostsTest {

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
package io.quarkus.redis.it;

import org.hamcrest.CoreMatchers;
import org.junit.jupiter.api.Test;

import io.quarkus.test.junit.QuarkusTest;
import io.restassured.RestAssured;

@QuarkusTest
class QuarkusRedisWithProvidedHostsTest {
static final String SYNC_KEY = "named-sync-key";
static final String SYNC_VALUE = "named-sync-value";

static final String REACTIVE_KEY = "named-reactive-key";
static final String REACTIVE_VALUE = "named-reactive-value";

@Test
public void sync() {
RestAssured.given()
.when()
.get("/quarkus-redis-provided-hosts/sync/" + SYNC_KEY)
.then()
.statusCode(204); // the key is not set yet

RestAssured.given()
.body(SYNC_VALUE)
.when()
.post("/quarkus-redis-provided-hosts/sync/" + SYNC_KEY)
.then()
.statusCode(204);

RestAssured.given()
.when()
.get("/quarkus-redis-provided-hosts/sync/" + SYNC_KEY)
.then()
.statusCode(200)
.body(CoreMatchers.is(SYNC_VALUE));
}

@Test
public void reactive() {
RestAssured.given()
.when()
.get("/quarkus-redis-provided-hosts/reactive/" + REACTIVE_KEY)
.then()
.statusCode(204); // the reactive key is not set yet

RestAssured.given()
.body(REACTIVE_VALUE)
.when()
.post("/quarkus-redis-provided-hosts/reactive/" + REACTIVE_KEY)
.then()
.statusCode(204);

RestAssured.given()
.when()
.get("/quarkus-redis-provided-hosts/reactive/" + REACTIVE_KEY)
.then()
.statusCode(200)
.body(CoreMatchers.is(REACTIVE_VALUE));
}
}

0 comments on commit f544fc9

Please sign in to comment.