Add support for ReactiveDiscoveryClient
ReactiveDiscoveryClient beans are now used to configure ReactiveVaultEndpointProvider. If no ReactiveDiscoveryClient is available, then VaultEndpointProvider gets adapted by offloading the call to a worker Scheduler to avoid blocking calls on the calling thread. Resolves gh-486.
This commit is contained in:
@@ -8,3 +8,4 @@ This section briefly covers items that are new and noteworthy in the latest rele
|
||||
* Migration of `PropertySource` initialization from Spring Cloud's Bootstrap Context to Spring Boot's <<vault.configdata,ConfigData API>>.
|
||||
* Support for the <<vault.config.backends.couchbase>> backend.
|
||||
* Configuration of keystore/truststore types through `spring.cloud.vault.ssl.key-store-type=…`/`spring.cloud.vault.ssl.trust-store-type=…` including PEM support.
|
||||
* Support for `ReactiveDiscoveryClient` by configuring a `ReactiveVaultEndpointProvider`.
|
||||
|
||||
@@ -16,8 +16,6 @@
|
||||
|
||||
package org.springframework.cloud.vault.config;
|
||||
|
||||
import java.net.URI;
|
||||
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
|
||||
import org.springframework.boot.context.properties.EnableConfigurationProperties;
|
||||
@@ -30,8 +28,6 @@ import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.context.annotation.Import;
|
||||
import org.springframework.core.Ordered;
|
||||
import org.springframework.core.annotation.Order;
|
||||
import org.springframework.util.StringUtils;
|
||||
import org.springframework.vault.client.VaultEndpoint;
|
||||
import org.springframework.vault.client.VaultEndpointProvider;
|
||||
|
||||
/**
|
||||
@@ -51,8 +47,11 @@ public class DiscoveryClientVaultBootstrapConfiguration {
|
||||
|
||||
private final VaultProperties vaultProperties;
|
||||
|
||||
private final VaultConfiguration configuration;
|
||||
|
||||
public DiscoveryClientVaultBootstrapConfiguration(VaultProperties vaultProperties) {
|
||||
this.vaultProperties = vaultProperties;
|
||||
this.configuration = new VaultConfiguration(vaultProperties);
|
||||
}
|
||||
|
||||
@Bean
|
||||
@@ -67,28 +66,13 @@ public class DiscoveryClientVaultBootstrapConfiguration {
|
||||
@ConditionalOnProperty(name = "spring.cloud.vault.enabled", matchIfMissing = true)
|
||||
public VaultEndpointProvider vaultEndpointProvider(VaultServiceInstanceProvider instanceProvider) {
|
||||
|
||||
String serviceId = this.vaultProperties.getDiscovery().getServiceId();
|
||||
String fallbackScheme;
|
||||
return () -> {
|
||||
|
||||
if (StringUtils.hasText(this.vaultProperties.getUri())) {
|
||||
fallbackScheme = URI.create(this.vaultProperties.getUri()).getScheme();
|
||||
}
|
||||
else {
|
||||
fallbackScheme = this.vaultProperties.getScheme();
|
||||
}
|
||||
String serviceId = this.vaultProperties.getDiscovery().getServiceId();
|
||||
ServiceInstance server = instanceProvider.getVaultServerInstance(serviceId);
|
||||
|
||||
ServiceInstance server = instanceProvider.getVaultServerInstance(serviceId);
|
||||
|
||||
VaultEndpoint vaultEndpoint = VaultEndpoint.create(server.getHost(), server.getPort());
|
||||
|
||||
if (server.getMetadata().containsKey("scheme")) {
|
||||
vaultEndpoint.setScheme(server.getMetadata().get("scheme"));
|
||||
}
|
||||
else {
|
||||
vaultEndpoint.setScheme(server.isSecure() ? "https" : fallbackScheme);
|
||||
}
|
||||
|
||||
return () -> vaultEndpoint;
|
||||
return this.configuration.createVaultEndpoint(server);
|
||||
};
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,93 @@
|
||||
/*
|
||||
* Copyright 2017-2020 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.cloud.vault.config;
|
||||
|
||||
import reactor.core.publisher.Flux;
|
||||
import reactor.core.publisher.Mono;
|
||||
import reactor.core.scheduler.Schedulers;
|
||||
|
||||
import org.springframework.beans.factory.ObjectProvider;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnExpression;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
|
||||
import org.springframework.boot.context.properties.EnableConfigurationProperties;
|
||||
import org.springframework.cloud.client.discovery.DiscoveryClient;
|
||||
import org.springframework.cloud.client.discovery.ReactiveDiscoveryClient;
|
||||
import org.springframework.cloud.commons.util.UtilAutoConfiguration;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.context.annotation.Import;
|
||||
import org.springframework.core.Ordered;
|
||||
import org.springframework.core.annotation.Order;
|
||||
import org.springframework.vault.client.ReactiveVaultEndpointProvider;
|
||||
import org.springframework.vault.client.VaultEndpointProvider;
|
||||
import org.springframework.vault.core.ReactiveVaultOperations;
|
||||
import org.springframework.web.reactive.function.client.WebClient;
|
||||
|
||||
/**
|
||||
* {@link org.springframework.cloud.bootstrap.BootstrapConfiguration} providing a
|
||||
* {@link VaultEndpointProvider} using {@link DiscoveryClient}.
|
||||
*
|
||||
* @author Mark Paluch
|
||||
* @since 1.1
|
||||
*/
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
@ConditionalOnProperty("spring.cloud.vault.discovery.enabled")
|
||||
@ConditionalOnExpression("${spring.cloud.vault.reactive.enabled:true}")
|
||||
@ConditionalOnClass({ Flux.class, WebClient.class, ReactiveVaultOperations.class, ReactiveDiscoveryClient.class })
|
||||
@EnableConfigurationProperties(VaultProperties.class)
|
||||
@Order(Ordered.LOWEST_PRECEDENCE - 5)
|
||||
@Import(UtilAutoConfiguration.class)
|
||||
public class ReactiveDiscoveryClientVaultBootstrapConfiguration {
|
||||
|
||||
private final VaultProperties vaultProperties;
|
||||
|
||||
private final VaultConfiguration configuration;
|
||||
|
||||
public ReactiveDiscoveryClientVaultBootstrapConfiguration(VaultProperties vaultProperties) {
|
||||
this.vaultProperties = vaultProperties;
|
||||
this.configuration = new VaultConfiguration(vaultProperties);
|
||||
}
|
||||
|
||||
@Bean
|
||||
@ConditionalOnMissingBean
|
||||
@ConditionalOnProperty(name = "spring.cloud.vault.enabled", matchIfMissing = true)
|
||||
public ReactiveVaultEndpointProvider reactiveVaultEndpointProvider(
|
||||
ObjectProvider<ReactiveDiscoveryClient> reactiveDiscoveryClients,
|
||||
ObjectProvider<VaultEndpointProvider> endpointProviders) {
|
||||
|
||||
ReactiveDiscoveryClient reactiveDiscoveryClient = reactiveDiscoveryClients.getIfAvailable();
|
||||
|
||||
if (reactiveDiscoveryClient != null) {
|
||||
ReacvtiveDiscoveryClientVaultServiceInstanceProvider instanceProvider = new ReacvtiveDiscoveryClientVaultServiceInstanceProvider(
|
||||
reactiveDiscoveryClient);
|
||||
|
||||
return () -> Mono.defer(() -> {
|
||||
|
||||
String serviceId = this.vaultProperties.getDiscovery().getServiceId();
|
||||
|
||||
return instanceProvider.getVaultServerInstance(serviceId).map(this.configuration::createVaultEndpoint);
|
||||
});
|
||||
}
|
||||
|
||||
VaultEndpointProvider endpointProvider = endpointProviders.getObject();
|
||||
|
||||
return () -> Mono.fromSupplier(endpointProvider::getVaultEndpoint).subscribeOn(Schedulers.boundedElastic());
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,61 @@
|
||||
/*
|
||||
* Copyright 2018-2020 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.cloud.vault.config;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
import reactor.core.publisher.Mono;
|
||||
|
||||
import org.springframework.cloud.client.ServiceInstance;
|
||||
import org.springframework.cloud.client.discovery.ReactiveDiscoveryClient;
|
||||
|
||||
/**
|
||||
* Provider for {@link ServiceInstance} to look up the Vault service.
|
||||
*
|
||||
* @author Mark Paluch
|
||||
* @since 3.0
|
||||
*/
|
||||
class ReacvtiveDiscoveryClientVaultServiceInstanceProvider {
|
||||
|
||||
private static final Log log = LogFactory.getLog(ReacvtiveDiscoveryClientVaultServiceInstanceProvider.class);
|
||||
|
||||
private final ReactiveDiscoveryClient client;
|
||||
|
||||
ReacvtiveDiscoveryClientVaultServiceInstanceProvider(ReactiveDiscoveryClient client) {
|
||||
this.client = client;
|
||||
}
|
||||
|
||||
Mono<ServiceInstance> getVaultServerInstance(String serviceId) {
|
||||
|
||||
log.debug("Locating Vault server (" + serviceId + ") via discovery");
|
||||
|
||||
return this.client.getInstances(serviceId).collectList().handle((instances, sink) -> {
|
||||
|
||||
if (instances.isEmpty()) {
|
||||
sink.error(new IllegalStateException("No instances found of Vault server (" + serviceId + ")"));
|
||||
return;
|
||||
}
|
||||
|
||||
ServiceInstance instance = instances.get(0);
|
||||
|
||||
log.debug("Located Vault server (" + serviceId + ") via discovery: " + instance);
|
||||
|
||||
sink.next(instance);
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
@@ -21,6 +21,7 @@ import java.time.Duration;
|
||||
import java.util.List;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
import org.springframework.cloud.client.ServiceInstance;
|
||||
import org.springframework.cloud.vault.config.VaultProperties.Ssl;
|
||||
import org.springframework.http.client.ClientHttpRequestFactory;
|
||||
import org.springframework.scheduling.TaskScheduler;
|
||||
@@ -128,6 +129,27 @@ final class VaultConfiguration {
|
||||
return vaultEndpoint;
|
||||
}
|
||||
|
||||
VaultEndpoint createVaultEndpoint(ServiceInstance server) {
|
||||
String fallbackScheme;
|
||||
|
||||
if (StringUtils.hasText(this.vaultProperties.getUri())) {
|
||||
fallbackScheme = URI.create(this.vaultProperties.getUri()).getScheme();
|
||||
}
|
||||
else {
|
||||
fallbackScheme = this.vaultProperties.getScheme();
|
||||
}
|
||||
|
||||
VaultEndpoint vaultEndpoint = VaultEndpoint.create(server.getHost(), server.getPort());
|
||||
|
||||
if (server.getMetadata().containsKey("scheme")) {
|
||||
vaultEndpoint.setScheme(server.getMetadata().get("scheme"));
|
||||
}
|
||||
else {
|
||||
vaultEndpoint.setScheme(server.isSecure() ? "https" : fallbackScheme);
|
||||
}
|
||||
return vaultEndpoint;
|
||||
}
|
||||
|
||||
RestTemplateBuilder createRestTemplateBuilder(ClientHttpRequestFactory requestFactory,
|
||||
VaultEndpointProvider endpointProvider, List<RestTemplateCustomizer> customizers,
|
||||
List<RestTemplateRequestCustomizer<?>> requestCustomizers) {
|
||||
|
||||
@@ -47,6 +47,7 @@ import org.springframework.vault.authentication.ReactiveLifecycleAwareSessionMan
|
||||
import org.springframework.vault.authentication.ReactiveSessionManager;
|
||||
import org.springframework.vault.authentication.SessionManager;
|
||||
import org.springframework.vault.authentication.VaultTokenSupplier;
|
||||
import org.springframework.vault.client.ReactiveVaultEndpointProvider;
|
||||
import org.springframework.vault.client.SimpleVaultEndpointProvider;
|
||||
import org.springframework.vault.client.VaultEndpointProvider;
|
||||
import org.springframework.vault.client.WebClientBuilder;
|
||||
@@ -82,6 +83,10 @@ public class VaultReactiveAutoConfiguration implements InitializingBean {
|
||||
|
||||
private final VaultReactiveConfiguration configuration;
|
||||
|
||||
@Nullable
|
||||
private final ReactiveVaultEndpointProvider reactiveEndpointProvider;
|
||||
|
||||
@Nullable
|
||||
private final VaultEndpointProvider endpointProvider;
|
||||
|
||||
private final List<WebClientCustomizer> customizers;
|
||||
@@ -96,14 +101,23 @@ public class VaultReactiveAutoConfiguration implements InitializingBean {
|
||||
private WebClientBuilder webClientBuilder;
|
||||
|
||||
public VaultReactiveAutoConfiguration(VaultProperties vaultProperties,
|
||||
ObjectProvider<ReactiveVaultEndpointProvider> reactiveEndpointProvider,
|
||||
ObjectProvider<VaultEndpointProvider> endpointProvider,
|
||||
ObjectProvider<List<WebClientCustomizer>> webClientCustomizers) {
|
||||
|
||||
this.vaultProperties = vaultProperties;
|
||||
this.configuration = new VaultReactiveConfiguration(vaultProperties);
|
||||
|
||||
this.endpointProvider = endpointProvider.getIfAvailable(
|
||||
() -> SimpleVaultEndpointProvider.of(new VaultConfiguration(vaultProperties).createVaultEndpoint()));
|
||||
this.reactiveEndpointProvider = reactiveEndpointProvider.getIfAvailable();
|
||||
|
||||
if (this.reactiveEndpointProvider == null) {
|
||||
this.endpointProvider = endpointProvider.getIfAvailable(() -> SimpleVaultEndpointProvider
|
||||
.of(new VaultConfiguration(vaultProperties).createVaultEndpoint()));
|
||||
}
|
||||
else {
|
||||
this.endpointProvider = null;
|
||||
}
|
||||
|
||||
this.customizers = new ArrayList<>(webClientCustomizers.getIfAvailable(Collections::emptyList));
|
||||
AnnotationAwareOrderComparator.sort(this.customizers);
|
||||
}
|
||||
@@ -117,7 +131,18 @@ public class VaultReactiveAutoConfiguration implements InitializingBean {
|
||||
}
|
||||
|
||||
protected WebClientBuilder webClientBuilder(ClientHttpConnector connector) {
|
||||
return this.configuration.createWebClientBuilder(connector, this.endpointProvider, this.customizers);
|
||||
|
||||
if (this.reactiveEndpointProvider != null) {
|
||||
return this.configuration.createWebClientBuilder(connector, this.reactiveEndpointProvider,
|
||||
this.customizers);
|
||||
}
|
||||
|
||||
if (this.endpointProvider != null) {
|
||||
return this.configuration.createWebClientBuilder(connector, this.endpointProvider, this.customizers);
|
||||
}
|
||||
|
||||
throw new IllegalStateException(
|
||||
"Cannot create WebClientBuilder as neither ReactiveEndpointProvider nor EndpointProvider configured");
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -27,6 +27,7 @@ import org.springframework.boot.autoconfigure.condition.ConditionalOnExpression;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
|
||||
import org.springframework.boot.context.properties.EnableConfigurationProperties;
|
||||
import org.springframework.cloud.bootstrap.BootstrapConfiguration;
|
||||
import org.springframework.vault.client.ReactiveVaultEndpointProvider;
|
||||
import org.springframework.vault.client.VaultEndpointProvider;
|
||||
import org.springframework.vault.client.WebClientCustomizer;
|
||||
import org.springframework.vault.core.ReactiveVaultOperations;
|
||||
@@ -53,9 +54,10 @@ import org.springframework.web.reactive.function.client.WebClient;
|
||||
public class VaultReactiveBootstrapConfiguration extends VaultReactiveAutoConfiguration {
|
||||
|
||||
public VaultReactiveBootstrapConfiguration(VaultProperties vaultProperties,
|
||||
ObjectProvider<ReactiveVaultEndpointProvider> reactiveEndpointProvider,
|
||||
ObjectProvider<VaultEndpointProvider> endpointProvider,
|
||||
ObjectProvider<List<WebClientCustomizer>> webClientCustomizers) {
|
||||
super(vaultProperties, endpointProvider, webClientCustomizers);
|
||||
super(vaultProperties, reactiveEndpointProvider, endpointProvider, webClientCustomizers);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -36,6 +36,7 @@ import org.springframework.vault.authentication.SessionManager;
|
||||
import org.springframework.vault.authentication.TokenAuthentication;
|
||||
import org.springframework.vault.authentication.VaultTokenSupplier;
|
||||
import org.springframework.vault.client.ClientHttpConnectorFactory;
|
||||
import org.springframework.vault.client.ReactiveVaultEndpointProvider;
|
||||
import org.springframework.vault.client.VaultEndpointProvider;
|
||||
import org.springframework.vault.client.VaultHttpHeaders;
|
||||
import org.springframework.vault.client.WebClientBuilder;
|
||||
@@ -70,12 +71,25 @@ final class VaultReactiveConfiguration {
|
||||
return ClientHttpConnectorFactory.create(clientOptions, sslConfiguration);
|
||||
}
|
||||
|
||||
WebClientBuilder createWebClientBuilder(ClientHttpConnector connector,
|
||||
ReactiveVaultEndpointProvider endpointProvider, List<WebClientCustomizer> customizers) {
|
||||
|
||||
WebClientBuilder builder = WebClientBuilder.builder().httpConnector(connector)
|
||||
.endpointProvider(endpointProvider);
|
||||
|
||||
return applyCustomizer(customizers, builder);
|
||||
}
|
||||
|
||||
WebClientBuilder createWebClientBuilder(ClientHttpConnector connector, VaultEndpointProvider endpointProvider,
|
||||
List<WebClientCustomizer> customizers) {
|
||||
|
||||
WebClientBuilder builder = WebClientBuilder.builder().httpConnector(connector)
|
||||
.endpointProvider(endpointProvider);
|
||||
|
||||
return applyCustomizer(customizers, builder);
|
||||
}
|
||||
|
||||
private WebClientBuilder applyCustomizer(List<WebClientCustomizer> customizers, WebClientBuilder builder) {
|
||||
customizers.forEach(builder::customizers);
|
||||
|
||||
if (StringUtils.hasText(this.vaultProperties.getNamespace())) {
|
||||
|
||||
@@ -6,6 +6,7 @@ org.springframework.cloud.vault.config.VaultHealthIndicatorAutoConfiguration
|
||||
# Bootstrap Configuration
|
||||
org.springframework.cloud.bootstrap.BootstrapConfiguration=\
|
||||
org.springframework.cloud.vault.config.DiscoveryClientVaultBootstrapConfiguration,\
|
||||
org.springframework.cloud.vault.config.ReactiveDiscoveryClientVaultBootstrapConfiguration,\
|
||||
org.springframework.cloud.vault.config.VaultBootstrapConfiguration,\
|
||||
org.springframework.cloud.vault.config.VaultReactiveBootstrapConfiguration,\
|
||||
org.springframework.cloud.vault.config.VaultBootstrapPropertySourceConfiguration
|
||||
|
||||
@@ -0,0 +1,221 @@
|
||||
/*
|
||||
* Copyright 2018-2020 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.cloud.vault.config;
|
||||
|
||||
import java.net.URI;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.mockito.Mockito;
|
||||
import reactor.core.publisher.Flux;
|
||||
import reactor.test.StepVerifier;
|
||||
|
||||
import org.springframework.boot.autoconfigure.AutoConfigurations;
|
||||
import org.springframework.boot.test.context.runner.ApplicationContextRunner;
|
||||
import org.springframework.cloud.client.ServiceInstance;
|
||||
import org.springframework.cloud.client.discovery.ReactiveDiscoveryClient;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.vault.client.ReactiveVaultEndpointProvider;
|
||||
import org.springframework.vault.client.VaultEndpoint;
|
||||
import org.springframework.vault.client.VaultEndpointProvider;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.mockito.ArgumentMatchers.anyString;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
/**
|
||||
* Tests for {@link ReactiveDiscoveryClientVaultBootstrapConfiguration}.
|
||||
*
|
||||
* @author Mark Paluch
|
||||
*/
|
||||
public class ReactiveDiscoveryClientVaultBootstrapConfigurationTests {
|
||||
|
||||
private ApplicationContextRunner contextRunner = new ApplicationContextRunner().withConfiguration(AutoConfigurations
|
||||
.of(ReactiveDiscoveryClientVaultBootstrapConfiguration.class, VaultBootstrapConfiguration.class));
|
||||
|
||||
@Test
|
||||
public void shouldRegisterDefaultBeans() {
|
||||
|
||||
this.contextRunner.withUserConfiguration(ReactiveDiscoveryConfiguration.class)
|
||||
.withPropertyValues("spring.cloud.vault.token=foo", "spring.cloud.vault.discovery.enabled=true",
|
||||
"spring.cloud.bootstrap.enabled=true")
|
||||
.run(context -> {
|
||||
|
||||
assertThat(context).hasSingleBean(ReactiveVaultEndpointProvider.class);
|
||||
|
||||
ReactiveVaultEndpointProvider endpointProvider = context
|
||||
.getBean(ReactiveVaultEndpointProvider.class);
|
||||
|
||||
endpointProvider.getVaultEndpoint().as(StepVerifier::create).assertNext(actual -> {
|
||||
assertThat(actual.getPort()).isEqualTo(1234);
|
||||
}).verifyComplete();
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldRegisterVaultEndpointAdapterBean() {
|
||||
|
||||
this.contextRunner.withUserConfiguration(BridgedDiscoveryConfiguration.class)
|
||||
.withPropertyValues("spring.cloud.vault.token=foo", "spring.cloud.vault.discovery.enabled=true",
|
||||
"spring.cloud.bootstrap.enabled=true")
|
||||
.run(context -> {
|
||||
|
||||
assertThat(context).hasSingleBean(ReactiveVaultEndpointProvider.class);
|
||||
|
||||
ReactiveVaultEndpointProvider endpointProvider = context
|
||||
.getBean(ReactiveVaultEndpointProvider.class);
|
||||
|
||||
endpointProvider.getVaultEndpoint().as(StepVerifier::create).assertNext(actual -> {
|
||||
assertThat(actual.getPort()).isEqualTo(1234);
|
||||
}).verifyComplete();
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldNotRegisterBeansIfDiscoveryDisabled() {
|
||||
|
||||
this.contextRunner.withUserConfiguration(ReactiveDiscoveryConfiguration.class)
|
||||
.withPropertyValues("spring.cloud.vault.token=foo", "spring.cloud.vault.discovery.enabled=false",
|
||||
"spring.cloud.bootstrap.enabled=true")
|
||||
.run(context -> {
|
||||
|
||||
assertThat(context.getBeanNamesForType(ReactiveVaultEndpointProvider.class)).isEmpty();
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldNotRegisterBeansIfVaultDisabled() {
|
||||
|
||||
this.contextRunner.withUserConfiguration(ReactiveDiscoveryConfiguration.class)
|
||||
.withPropertyValues("spring.cloud.vault.token=foo", "spring.cloud.vault.enabled=false",
|
||||
"spring.cloud.bootstrap.enabled=true")
|
||||
.run(context -> {
|
||||
|
||||
assertThat(context.getBeanNamesForType(ReactiveVaultEndpointProvider.class)).isEmpty();
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
static class ReactiveDiscoveryConfiguration {
|
||||
|
||||
@Bean
|
||||
ReactiveDiscoveryClient reactiveDiscoveryClient() {
|
||||
|
||||
ReactiveDiscoveryClient mock = Mockito.mock(ReactiveDiscoveryClient.class);
|
||||
when(mock.getInstances(anyString()))
|
||||
.thenReturn(Flux.just(new SimpleServiceInstance(URI.create("https://foo:1234"))));
|
||||
|
||||
return mock;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
static class BridgedDiscoveryConfiguration {
|
||||
|
||||
@Bean
|
||||
VaultEndpointProvider vaultEndpointProvider() {
|
||||
|
||||
VaultEndpointProvider mock = Mockito.mock(VaultEndpointProvider.class);
|
||||
VaultEndpoint vaultEndpoint = VaultEndpoint.create("foo", 1234);
|
||||
|
||||
when(mock.getVaultEndpoint()).thenReturn(vaultEndpoint);
|
||||
|
||||
return mock;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
static class SimpleServiceInstance implements ServiceInstance {
|
||||
|
||||
private URI uri;
|
||||
|
||||
private String host;
|
||||
|
||||
private int port;
|
||||
|
||||
private boolean secure;
|
||||
|
||||
private Map<String, String> metadata = new LinkedHashMap<>();
|
||||
|
||||
private String serviceId;
|
||||
|
||||
SimpleServiceInstance(URI uri) {
|
||||
this.setUri(uri);
|
||||
}
|
||||
|
||||
void setUri(URI uri) {
|
||||
this.uri = uri;
|
||||
this.host = this.uri.getHost();
|
||||
this.port = this.uri.getPort();
|
||||
String scheme = this.uri.getScheme();
|
||||
if ("https".equals(scheme)) {
|
||||
this.secure = true;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public URI getUri() {
|
||||
return this.uri;
|
||||
}
|
||||
|
||||
public String getHost() {
|
||||
return this.host;
|
||||
}
|
||||
|
||||
public int getPort() {
|
||||
return this.port;
|
||||
}
|
||||
|
||||
public boolean isSecure() {
|
||||
return this.secure;
|
||||
}
|
||||
|
||||
public Map<String, String> getMetadata() {
|
||||
return this.metadata;
|
||||
}
|
||||
|
||||
public String getServiceId() {
|
||||
return this.serviceId;
|
||||
}
|
||||
|
||||
public void setHost(String host) {
|
||||
this.host = host;
|
||||
}
|
||||
|
||||
public void setPort(int port) {
|
||||
this.port = port;
|
||||
}
|
||||
|
||||
public void setSecure(boolean secure) {
|
||||
this.secure = secure;
|
||||
}
|
||||
|
||||
public void setMetadata(Map<String, String> metadata) {
|
||||
this.metadata = metadata;
|
||||
}
|
||||
|
||||
public void setServiceId(String serviceId) {
|
||||
this.serviceId = serviceId;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -21,6 +21,7 @@ import java.util.concurrent.atomic.AtomicLong;
|
||||
|
||||
import org.junit.Test;
|
||||
import reactor.core.publisher.Mono;
|
||||
import reactor.test.StepVerifier;
|
||||
|
||||
import org.springframework.boot.autoconfigure.AutoConfigurations;
|
||||
import org.springframework.boot.test.context.FilteredClassLoader;
|
||||
@@ -37,6 +38,8 @@ import org.springframework.vault.authentication.ReactiveSessionManager;
|
||||
import org.springframework.vault.authentication.SessionManager;
|
||||
import org.springframework.vault.authentication.SimpleSessionManager;
|
||||
import org.springframework.vault.authentication.VaultTokenSupplier;
|
||||
import org.springframework.vault.client.ReactiveVaultEndpointProvider;
|
||||
import org.springframework.vault.client.VaultEndpoint;
|
||||
import org.springframework.vault.client.WebClientFactory;
|
||||
import org.springframework.vault.core.ReactiveVaultOperations;
|
||||
import org.springframework.vault.core.ReactiveVaultTemplate;
|
||||
@@ -158,6 +161,29 @@ public class VaultReactiveAutoConfigurationTests {
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldConfigureEndpointProvider() {
|
||||
|
||||
this.contextRunner
|
||||
.withPropertyValues("spring.cloud.vault.kv.enabled=false", "spring.cloud.vault.token=foo",
|
||||
"spring.cloud.vault.session.lifecycle.enabled=false")
|
||||
.withUserConfiguration(ReactiveEndpointProviderConfiguration.class)
|
||||
.withBean("vaultTokenSupplier", VaultTokenSupplier.class, () -> Mono::empty)
|
||||
.withBean("taskSchedulerWrapper", VaultAutoConfiguration.TaskSchedulerWrapper.class,
|
||||
() -> new VaultAutoConfiguration.TaskSchedulerWrapper(new ThreadPoolTaskScheduler()))
|
||||
.run(context -> {
|
||||
|
||||
WebClientFactory factory = context.getBean(WebClientFactory.class);
|
||||
WebClient webClient = factory.create();
|
||||
|
||||
webClient.get().uri("foo").retrieve().bodyToMono(String.class).as(StepVerifier::create)
|
||||
.verifyErrorMatches(throwable -> throwable.getMessage().contains("foobar-1"));
|
||||
|
||||
webClient.get().uri("foo").retrieve().bodyToMono(String.class).as(StepVerifier::create)
|
||||
.verifyErrorMatches(throwable -> throwable.getMessage().contains("foobar-2"));
|
||||
});
|
||||
}
|
||||
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
static class AuthenticationFactoryConfiguration {
|
||||
|
||||
@@ -189,4 +215,23 @@ public class VaultReactiveAutoConfigurationTests {
|
||||
|
||||
}
|
||||
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
static class ReactiveEndpointProviderConfiguration {
|
||||
|
||||
@Bean
|
||||
ReactiveVaultEndpointProvider reactiveVaultEndpointProvider() {
|
||||
|
||||
AtomicLong counter = new AtomicLong();
|
||||
|
||||
return () -> Mono.fromSupplier(() -> {
|
||||
|
||||
VaultEndpoint vaultEndpoint = new VaultEndpoint();
|
||||
vaultEndpoint.setHost("foobar-" + counter.incrementAndGet());
|
||||
|
||||
return vaultEndpoint;
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user