Add support for the configuration watcher to shut down the application to refresh the application (#1799)
See #1772
This commit is contained in:
@@ -21,8 +21,12 @@ import reactor.core.publisher.Mono;
|
||||
|
||||
import org.springframework.cloud.bus.event.PathDestinationFactory;
|
||||
import org.springframework.cloud.bus.event.RefreshRemoteApplicationEvent;
|
||||
import org.springframework.cloud.bus.event.RemoteApplicationEvent;
|
||||
import org.springframework.cloud.bus.event.ShutdownRemoteApplicationEvent;
|
||||
import org.springframework.context.ApplicationEventPublisher;
|
||||
|
||||
import static org.springframework.cloud.kubernetes.configuration.watcher.ConfigurationWatcherConfigurationProperties.RefreshStrategy.SHUTDOWN;
|
||||
|
||||
/**
|
||||
* An event publisher for an 'event bus' type of application.
|
||||
*
|
||||
@@ -34,16 +38,28 @@ final class BusRefreshTrigger implements RefreshTrigger {
|
||||
|
||||
private final String busId;
|
||||
|
||||
BusRefreshTrigger(ApplicationEventPublisher applicationEventPublisher, String busId) {
|
||||
private final ConfigurationWatcherConfigurationProperties watcherConfigurationProperties;
|
||||
|
||||
BusRefreshTrigger(ApplicationEventPublisher applicationEventPublisher, String busId,
|
||||
ConfigurationWatcherConfigurationProperties watcherConfigurationProperties) {
|
||||
this.applicationEventPublisher = applicationEventPublisher;
|
||||
this.busId = busId;
|
||||
this.watcherConfigurationProperties = watcherConfigurationProperties;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Mono<Void> triggerRefresh(KubernetesObject configMap, String appName) {
|
||||
applicationEventPublisher.publishEvent(new RefreshRemoteApplicationEvent(configMap, busId,
|
||||
new PathDestinationFactory().getDestination(appName)));
|
||||
applicationEventPublisher.publishEvent(createRefreshApplicationEvent(configMap, appName));
|
||||
return Mono.empty();
|
||||
}
|
||||
|
||||
private RemoteApplicationEvent createRefreshApplicationEvent(KubernetesObject configMap, String appName) {
|
||||
if (watcherConfigurationProperties.getRefreshStrategy() == SHUTDOWN) {
|
||||
return new ShutdownRemoteApplicationEvent(configMap, busId,
|
||||
new PathDestinationFactory().getDestination(appName));
|
||||
}
|
||||
return new RefreshRemoteApplicationEvent(configMap, busId,
|
||||
new PathDestinationFactory().getDestination(appName));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -70,6 +70,8 @@ public class ConfigurationWatcherConfigurationProperties {
|
||||
@DurationUnit(ChronoUnit.MILLIS)
|
||||
private Duration refreshDelay = Duration.ofMillis(120000);
|
||||
|
||||
private RefreshStrategy refreshStrategy = RefreshStrategy.REFRESH;
|
||||
|
||||
private int threadPoolSize = 1;
|
||||
|
||||
private String actuatorPath = "/actuator";
|
||||
@@ -115,4 +117,28 @@ public class ConfigurationWatcherConfigurationProperties {
|
||||
this.threadPoolSize = threadPoolSize;
|
||||
}
|
||||
|
||||
public RefreshStrategy getRefreshStrategy() {
|
||||
return refreshStrategy;
|
||||
}
|
||||
|
||||
public void setRefreshStrategy(RefreshStrategy refreshStrategy) {
|
||||
this.refreshStrategy = refreshStrategy;
|
||||
}
|
||||
|
||||
public enum RefreshStrategy {
|
||||
|
||||
/**
|
||||
* Call the Actuator refresh endpoint or send a refresh event over Spring Cloud
|
||||
* Bus.
|
||||
*/
|
||||
REFRESH,
|
||||
|
||||
/**
|
||||
* Call the Actuator shutdown endpoint or send a shutdown event over Spring Cloud
|
||||
* Bus.
|
||||
*/
|
||||
SHUTDOWN
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -31,6 +31,8 @@ import org.springframework.util.StringUtils;
|
||||
import org.springframework.web.reactive.function.client.WebClient;
|
||||
import org.springframework.web.util.UriComponentsBuilder;
|
||||
|
||||
import static org.springframework.cloud.kubernetes.configuration.watcher.ConfigurationWatcherConfigurationProperties.RefreshStrategy.SHUTDOWN;
|
||||
|
||||
/**
|
||||
* @author wind57
|
||||
*/
|
||||
@@ -91,7 +93,7 @@ final class HttpRefreshTrigger implements RefreshTrigger {
|
||||
}
|
||||
else {
|
||||
int port = actuatorPort < 0 ? si.getPort() : actuatorPort;
|
||||
actuatorUriBuilder = actuatorUriBuilder.path(actuatorPath + "/refresh").port(port);
|
||||
actuatorUriBuilder = actuatorUriBuilder.path(actuatorPath + getRefreshStrategyEndpoint()).port(port);
|
||||
}
|
||||
|
||||
return actuatorUriBuilder.build().toUri();
|
||||
@@ -99,7 +101,7 @@ final class HttpRefreshTrigger implements RefreshTrigger {
|
||||
|
||||
private void setActuatorUriFromAnnotation(UriComponentsBuilder actuatorUriBuilder, String metadataUri) {
|
||||
URI annotationUri = URI.create(metadataUri);
|
||||
actuatorUriBuilder.path(annotationUri.getPath() + "/refresh");
|
||||
actuatorUriBuilder.path(annotationUri.getPath() + getRefreshStrategyEndpoint());
|
||||
|
||||
// The URI may not contain a host so if that is the case the port in the URI will
|
||||
// be -1. The authority of the URI will be :<port> for example :9090, we just need
|
||||
@@ -114,4 +116,11 @@ final class HttpRefreshTrigger implements RefreshTrigger {
|
||||
}
|
||||
}
|
||||
|
||||
private String getRefreshStrategyEndpoint() {
|
||||
if (k8SConfigurationProperties.getRefreshStrategy() == SHUTDOWN) {
|
||||
return "/shutdown";
|
||||
}
|
||||
return "/refresh";
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -41,8 +41,8 @@ class RefreshTriggerAutoConfiguration {
|
||||
@ConditionalOnMissingBean
|
||||
@Profile({ AMQP, KAFKA })
|
||||
BusRefreshTrigger busRefreshTrigger(ApplicationEventPublisher applicationEventPublisher,
|
||||
BusProperties busProperties) {
|
||||
return new BusRefreshTrigger(applicationEventPublisher, busProperties.getId());
|
||||
BusProperties busProperties, ConfigurationWatcherConfigurationProperties properties) {
|
||||
return new BusRefreshTrigger(applicationEventPublisher, busProperties.getId(), properties);
|
||||
}
|
||||
|
||||
@Bean
|
||||
|
||||
@@ -28,6 +28,8 @@ import org.mockito.junit.jupiter.MockitoExtension;
|
||||
|
||||
import org.springframework.cloud.bus.BusProperties;
|
||||
import org.springframework.cloud.bus.event.RefreshRemoteApplicationEvent;
|
||||
import org.springframework.cloud.bus.event.RemoteApplicationEvent;
|
||||
import org.springframework.cloud.bus.event.ShutdownRemoteApplicationEvent;
|
||||
import org.springframework.cloud.kubernetes.client.config.KubernetesClientConfigMapPropertySourceLocator;
|
||||
import org.springframework.cloud.kubernetes.commons.KubernetesNamespaceProvider;
|
||||
import org.springframework.cloud.kubernetes.commons.config.reload.ConfigReloadProperties;
|
||||
@@ -39,6 +41,7 @@ import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.mockito.Mockito.verify;
|
||||
import static org.springframework.cloud.kubernetes.commons.KubernetesNamespaceProvider.NAMESPACE_PROPERTY;
|
||||
import static org.springframework.cloud.kubernetes.configuration.watcher.ConfigurationWatcherConfigurationProperties.RefreshStrategy;
|
||||
|
||||
/**
|
||||
* @author Ryan Baxter
|
||||
@@ -68,31 +71,52 @@ class BusEventBasedConfigMapWatcherChangeDetectorTests {
|
||||
|
||||
private BusProperties busProperties;
|
||||
|
||||
private MockEnvironment mockEnvironment;
|
||||
|
||||
@BeforeEach
|
||||
void setup() {
|
||||
MockEnvironment mockEnvironment = new MockEnvironment();
|
||||
mockEnvironment = new MockEnvironment();
|
||||
mockEnvironment.setProperty(NAMESPACE_PROPERTY, "default");
|
||||
ConfigurationWatcherConfigurationProperties configurationWatcherConfigurationProperties = new ConfigurationWatcherConfigurationProperties();
|
||||
busProperties = new BusProperties();
|
||||
changeDetector = new BusEventBasedConfigMapWatcherChangeDetector(coreV1Api, mockEnvironment,
|
||||
ConfigReloadProperties.DEFAULT, UPDATE_STRATEGY, configMapPropertySourceLocator,
|
||||
new KubernetesNamespaceProvider(mockEnvironment), configurationWatcherConfigurationProperties,
|
||||
threadPoolTaskExecutor, new BusRefreshTrigger(applicationEventPublisher, busProperties.getId()));
|
||||
}
|
||||
|
||||
@Test
|
||||
void triggerRefreshWithConfigMap() {
|
||||
V1ObjectMeta objectMeta = new V1ObjectMeta();
|
||||
objectMeta.setName("foo");
|
||||
V1ConfigMap configMap = new V1ConfigMap();
|
||||
configMap.setMetadata(objectMeta);
|
||||
changeDetector.triggerRefresh(configMap, configMap.getMetadata().getName());
|
||||
ArgumentCaptor<RefreshRemoteApplicationEvent> argumentCaptor = ArgumentCaptor
|
||||
.forClass(RefreshRemoteApplicationEvent.class);
|
||||
triggerRefreshWithConfigMap(RefreshStrategy.REFRESH, argumentCaptor);
|
||||
}
|
||||
|
||||
@Test
|
||||
void triggerRefreshWithConfigMapUsingShutdown() {
|
||||
ArgumentCaptor<ShutdownRemoteApplicationEvent> argumentCaptor = ArgumentCaptor
|
||||
.forClass(ShutdownRemoteApplicationEvent.class);
|
||||
triggerRefreshWithConfigMap(RefreshStrategy.SHUTDOWN, argumentCaptor);
|
||||
}
|
||||
|
||||
void triggerRefreshWithConfigMap(RefreshStrategy strategy,
|
||||
ArgumentCaptor<? extends RemoteApplicationEvent> argumentCaptor) {
|
||||
V1ObjectMeta objectMeta = new V1ObjectMeta();
|
||||
objectMeta.setName("foo");
|
||||
V1ConfigMap configMap = getV1ConfigMap(objectMeta, strategy);
|
||||
verify(applicationEventPublisher).publishEvent(argumentCaptor.capture());
|
||||
assertThat(argumentCaptor.getValue().getSource()).isEqualTo(configMap);
|
||||
assertThat(argumentCaptor.getValue().getOriginService()).isEqualTo(busProperties.getId());
|
||||
assertThat(argumentCaptor.getValue().getDestinationService()).isEqualTo("foo:**");
|
||||
}
|
||||
|
||||
private V1ConfigMap getV1ConfigMap(V1ObjectMeta objectMeta, RefreshStrategy refreshStrategy) {
|
||||
V1ConfigMap configMap = new V1ConfigMap();
|
||||
configMap.setMetadata(objectMeta);
|
||||
ConfigurationWatcherConfigurationProperties configurationWatcherConfigurationProperties = new ConfigurationWatcherConfigurationProperties();
|
||||
configurationWatcherConfigurationProperties.setRefreshStrategy(refreshStrategy);
|
||||
BusEventBasedConfigMapWatcherChangeDetector changeDetector = new BusEventBasedConfigMapWatcherChangeDetector(
|
||||
coreV1Api, mockEnvironment, ConfigReloadProperties.DEFAULT, UPDATE_STRATEGY,
|
||||
configMapPropertySourceLocator, new KubernetesNamespaceProvider(mockEnvironment),
|
||||
configurationWatcherConfigurationProperties, threadPoolTaskExecutor, new BusRefreshTrigger(
|
||||
applicationEventPublisher, busProperties.getId(), configurationWatcherConfigurationProperties));
|
||||
changeDetector.triggerRefresh(configMap, configMap.getMetadata().getName());
|
||||
return configMap;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -28,6 +28,7 @@ import org.mockito.junit.jupiter.MockitoExtension;
|
||||
|
||||
import org.springframework.cloud.bus.BusProperties;
|
||||
import org.springframework.cloud.bus.event.RefreshRemoteApplicationEvent;
|
||||
import org.springframework.cloud.bus.event.RemoteApplicationEvent;
|
||||
import org.springframework.cloud.kubernetes.client.config.KubernetesClientSecretsPropertySourceLocator;
|
||||
import org.springframework.cloud.kubernetes.commons.KubernetesNamespaceProvider;
|
||||
import org.springframework.cloud.kubernetes.commons.config.reload.ConfigReloadProperties;
|
||||
@@ -39,6 +40,7 @@ import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.mockito.Mockito.verify;
|
||||
import static org.springframework.cloud.kubernetes.commons.KubernetesNamespaceProvider.NAMESPACE_PROPERTY;
|
||||
import static org.springframework.cloud.kubernetes.configuration.watcher.ConfigurationWatcherConfigurationProperties.RefreshStrategy;
|
||||
|
||||
/**
|
||||
* @author Ryan Baxter
|
||||
@@ -64,35 +66,55 @@ class BusEventBasedSecretsWatcherChangeDetectorTests {
|
||||
@Mock
|
||||
private ApplicationEventPublisher applicationEventPublisher;
|
||||
|
||||
private BusEventBasedSecretsWatcherChangeDetector changeDetector;
|
||||
|
||||
private BusProperties busProperties;
|
||||
|
||||
private MockEnvironment mockEnvironment;
|
||||
|
||||
@BeforeEach
|
||||
void setup() {
|
||||
MockEnvironment mockEnvironment = new MockEnvironment();
|
||||
mockEnvironment = new MockEnvironment();
|
||||
mockEnvironment.setProperty(NAMESPACE_PROPERTY, "default");
|
||||
ConfigurationWatcherConfigurationProperties configurationWatcherConfigurationProperties = new ConfigurationWatcherConfigurationProperties();
|
||||
busProperties = new BusProperties();
|
||||
changeDetector = new BusEventBasedSecretsWatcherChangeDetector(coreV1Api, mockEnvironment,
|
||||
ConfigReloadProperties.DEFAULT, UPDATE_STRATEGY, secretsPropertySourceLocator,
|
||||
new KubernetesNamespaceProvider(mockEnvironment), configurationWatcherConfigurationProperties,
|
||||
threadPoolTaskExecutor, new BusRefreshTrigger(applicationEventPublisher, busProperties.getId()));
|
||||
}
|
||||
|
||||
@Test
|
||||
void triggerRefreshWithSecret() {
|
||||
V1ObjectMeta objectMeta = new V1ObjectMeta();
|
||||
objectMeta.setName("foo");
|
||||
V1Secret secret = new V1Secret();
|
||||
secret.setMetadata(objectMeta);
|
||||
changeDetector.triggerRefresh(secret, secret.getMetadata().getName());
|
||||
ArgumentCaptor<RefreshRemoteApplicationEvent> argumentCaptor = ArgumentCaptor
|
||||
.forClass(RefreshRemoteApplicationEvent.class);
|
||||
triggerRefreshWithSecret(ConfigurationWatcherConfigurationProperties.RefreshStrategy.REFRESH, argumentCaptor);
|
||||
}
|
||||
|
||||
@Test
|
||||
void triggerRefreshWithSecretWithShutdown() {
|
||||
ArgumentCaptor<RefreshRemoteApplicationEvent> argumentCaptor = ArgumentCaptor
|
||||
.forClass(RefreshRemoteApplicationEvent.class);
|
||||
triggerRefreshWithSecret(ConfigurationWatcherConfigurationProperties.RefreshStrategy.REFRESH, argumentCaptor);
|
||||
}
|
||||
|
||||
void triggerRefreshWithSecret(RefreshStrategy strategy,
|
||||
ArgumentCaptor<? extends RemoteApplicationEvent> argumentCaptor) {
|
||||
V1ObjectMeta objectMeta = new V1ObjectMeta();
|
||||
objectMeta.setName("foo");
|
||||
V1Secret secret = getV1Secret(objectMeta, strategy);
|
||||
verify(applicationEventPublisher).publishEvent(argumentCaptor.capture());
|
||||
assertThat(argumentCaptor.getValue().getSource()).isEqualTo(secret);
|
||||
assertThat(argumentCaptor.getValue().getOriginService()).isEqualTo(busProperties.getId());
|
||||
assertThat(argumentCaptor.getValue().getDestinationService()).isEqualTo("foo:**");
|
||||
}
|
||||
|
||||
private V1Secret getV1Secret(V1ObjectMeta objectMeta,
|
||||
ConfigurationWatcherConfigurationProperties.RefreshStrategy refreshStrategy) {
|
||||
V1Secret secret = new V1Secret();
|
||||
secret.setMetadata(objectMeta);
|
||||
ConfigurationWatcherConfigurationProperties configurationWatcherConfigurationProperties = new ConfigurationWatcherConfigurationProperties();
|
||||
configurationWatcherConfigurationProperties.setRefreshStrategy(refreshStrategy);
|
||||
BusEventBasedSecretsWatcherChangeDetector changeDetector = new BusEventBasedSecretsWatcherChangeDetector(
|
||||
coreV1Api, mockEnvironment, ConfigReloadProperties.DEFAULT, UPDATE_STRATEGY,
|
||||
secretsPropertySourceLocator, new KubernetesNamespaceProvider(mockEnvironment),
|
||||
configurationWatcherConfigurationProperties, threadPoolTaskExecutor, new BusRefreshTrigger(
|
||||
applicationEventPublisher, busProperties.getId(), configurationWatcherConfigurationProperties));
|
||||
changeDetector.triggerRefresh(secret, secret.getMetadata().getName());
|
||||
return secret;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -30,6 +30,7 @@ import io.kubernetes.client.openapi.models.V1ConfigMap;
|
||||
import io.kubernetes.client.openapi.models.V1EndpointAddress;
|
||||
import io.kubernetes.client.openapi.models.V1ObjectMeta;
|
||||
import io.kubernetes.client.util.ClientBuilder;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.junit.jupiter.api.AfterAll;
|
||||
import org.junit.jupiter.api.BeforeAll;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
@@ -58,6 +59,7 @@ import static org.mockito.ArgumentMatchers.eq;
|
||||
import static org.mockito.Mockito.mockStatic;
|
||||
import static org.mockito.Mockito.when;
|
||||
import static org.springframework.cloud.kubernetes.commons.KubernetesNamespaceProvider.NAMESPACE_PROPERTY;
|
||||
import static org.springframework.cloud.kubernetes.configuration.watcher.ConfigurationWatcherConfigurationProperties.RefreshStrategy;
|
||||
|
||||
/**
|
||||
* @author Ryan Baxter
|
||||
@@ -83,9 +85,11 @@ class HttpBasedConfigMapWatchChangeDetectorTests {
|
||||
@Mock
|
||||
private KubernetesInformerReactiveDiscoveryClient reactiveDiscoveryClient;
|
||||
|
||||
private HttpBasedConfigMapWatchChangeDetector changeDetector;
|
||||
private MockEnvironment mockEnvironment;
|
||||
|
||||
private ConfigurationWatcherConfigurationProperties configurationWatcherConfigurationProperties;
|
||||
private WebClient webClient;
|
||||
|
||||
private ConfigurationUpdateStrategy strategy;
|
||||
|
||||
@BeforeAll
|
||||
static void beforeAll() {
|
||||
@@ -105,58 +109,112 @@ class HttpBasedConfigMapWatchChangeDetectorTests {
|
||||
|
||||
@BeforeEach
|
||||
void setup() {
|
||||
|
||||
MockEnvironment mockEnvironment = new MockEnvironment();
|
||||
mockEnvironment = new MockEnvironment();
|
||||
mockEnvironment.setProperty(NAMESPACE_PROPERTY, "default");
|
||||
configurationWatcherConfigurationProperties = new ConfigurationWatcherConfigurationProperties();
|
||||
WebClient webClient = WebClient.builder().build();
|
||||
|
||||
ConfigurationUpdateStrategy strategy = new ConfigurationUpdateStrategy("refresh", () -> {
|
||||
webClient = WebClient.builder().build();
|
||||
strategy = new ConfigurationUpdateStrategy("refresh", () -> {
|
||||
|
||||
});
|
||||
|
||||
changeDetector = new HttpBasedConfigMapWatchChangeDetector(coreV1Api, mockEnvironment,
|
||||
ConfigReloadProperties.DEFAULT, strategy, configMapPropertySourceLocator,
|
||||
new KubernetesNamespaceProvider(mockEnvironment), configurationWatcherConfigurationProperties,
|
||||
threadPoolTaskExecutor, new HttpRefreshTrigger(reactiveDiscoveryClient,
|
||||
configurationWatcherConfigurationProperties, webClient));
|
||||
}
|
||||
|
||||
@Test
|
||||
void triggerConfigMapRefresh() {
|
||||
void triggerConfigMapRefreshUsingRefresh() {
|
||||
triggerConfigMapRefresh("/actuator/refresh", RefreshStrategy.REFRESH);
|
||||
}
|
||||
|
||||
@Test
|
||||
void triggerConfigMapRefreshUsingShutdown() {
|
||||
triggerConfigMapRefresh("/actuator/shutdown", RefreshStrategy.SHUTDOWN);
|
||||
}
|
||||
|
||||
void triggerConfigMapRefresh(String actuatorPath, RefreshStrategy refreshStrategy) {
|
||||
stubReactiveCall();
|
||||
V1ConfigMap configMap = new V1ConfigMap();
|
||||
V1ObjectMeta objectMeta = new V1ObjectMeta();
|
||||
objectMeta.setName("foo");
|
||||
configMap.setMetadata(objectMeta);
|
||||
WireMock.configureFor("localhost", WIRE_MOCK_SERVER.port());
|
||||
WireMock.stubFor(WireMock.post(WireMock.urlEqualTo("/actuator/refresh"))
|
||||
.willReturn(WireMock.aResponse().withStatus(200)));
|
||||
WireMock
|
||||
.stubFor(WireMock.post(WireMock.urlEqualTo(actuatorPath)).willReturn(WireMock.aResponse().withStatus(200)));
|
||||
|
||||
ConfigurationWatcherConfigurationProperties configurationWatcherConfigurationProperties = new ConfigurationWatcherConfigurationProperties();
|
||||
configurationWatcherConfigurationProperties.setRefreshStrategy(refreshStrategy);
|
||||
HttpBasedConfigMapWatchChangeDetector changeDetector = new HttpBasedConfigMapWatchChangeDetector(coreV1Api,
|
||||
mockEnvironment, ConfigReloadProperties.DEFAULT, strategy, configMapPropertySourceLocator,
|
||||
new KubernetesNamespaceProvider(mockEnvironment), configurationWatcherConfigurationProperties,
|
||||
threadPoolTaskExecutor, new HttpRefreshTrigger(reactiveDiscoveryClient,
|
||||
configurationWatcherConfigurationProperties, webClient));
|
||||
StepVerifier.create(changeDetector.triggerRefresh(configMap, configMap.getMetadata().getName()))
|
||||
.verifyComplete();
|
||||
WireMock.verify(WireMock.postRequestedFor(WireMock.urlEqualTo("/actuator/refresh")));
|
||||
WireMock.verify(WireMock.postRequestedFor(WireMock.urlEqualTo(actuatorPath)));
|
||||
}
|
||||
|
||||
@Test
|
||||
void triggerConfigMapRefreshWithPropertiesBasedActuatorPath() {
|
||||
void triggerConfigMapRefreshWithPropertiesBasedActuatorPathUsingRefresh() {
|
||||
triggerConfigMapRefreshWithPropertiesBasedActuatorPath("/refresh", RefreshStrategy.REFRESH);
|
||||
}
|
||||
|
||||
@Test
|
||||
void triggerConfigMapRefreshWithPropertiesBasedActuatorPathUsingShutdown() {
|
||||
triggerConfigMapRefreshWithPropertiesBasedActuatorPath("/shutdown", RefreshStrategy.SHUTDOWN);
|
||||
}
|
||||
|
||||
void triggerConfigMapRefreshWithPropertiesBasedActuatorPath(String endpoint, RefreshStrategy refreshStrategy) {
|
||||
stubReactiveCall();
|
||||
ConfigurationWatcherConfigurationProperties configurationWatcherConfigurationProperties = new ConfigurationWatcherConfigurationProperties();
|
||||
configurationWatcherConfigurationProperties.setRefreshStrategy(refreshStrategy);
|
||||
configurationWatcherConfigurationProperties.setActuatorPath("/my/custom/actuator");
|
||||
V1ConfigMap configMap = new V1ConfigMap();
|
||||
V1ObjectMeta objectMeta = new V1ObjectMeta();
|
||||
objectMeta.setName("foo");
|
||||
configMap.setMetadata(objectMeta);
|
||||
WireMock.configureFor("localhost", WIRE_MOCK_SERVER.port());
|
||||
WireMock.stubFor(WireMock.post(WireMock.urlEqualTo("/my/custom/actuator/refresh"))
|
||||
WireMock.stubFor(WireMock.post(WireMock.urlEqualTo("/my/custom/actuator" + endpoint))
|
||||
.willReturn(WireMock.aResponse().withStatus(200)));
|
||||
HttpBasedConfigMapWatchChangeDetector changeDetector = new HttpBasedConfigMapWatchChangeDetector(coreV1Api,
|
||||
mockEnvironment, ConfigReloadProperties.DEFAULT, strategy, configMapPropertySourceLocator,
|
||||
new KubernetesNamespaceProvider(mockEnvironment), configurationWatcherConfigurationProperties,
|
||||
threadPoolTaskExecutor, new HttpRefreshTrigger(reactiveDiscoveryClient,
|
||||
configurationWatcherConfigurationProperties, webClient));
|
||||
StepVerifier.create(changeDetector.triggerRefresh(configMap, configMap.getMetadata().getName()))
|
||||
.verifyComplete();
|
||||
WireMock.verify(WireMock.postRequestedFor(WireMock.urlEqualTo("/my/custom/actuator/refresh")));
|
||||
WireMock.verify(WireMock.postRequestedFor(WireMock.urlEqualTo("/my/custom/actuator" + endpoint)));
|
||||
}
|
||||
|
||||
@Test
|
||||
void triggerConfigMapRefreshWithAnnotationActuatorPath() {
|
||||
void triggerConfigMapRefreshWithAnnotationActuatorPathUsingRefresh() {
|
||||
triggerConfigMapRefreshWithAnnotationActuatorPath("/refresh", RefreshStrategy.REFRESH);
|
||||
}
|
||||
|
||||
@Test
|
||||
void triggerConfigMapRefreshWithAnnotationActuatorPathUsingShutdown() {
|
||||
triggerConfigMapRefreshWithAnnotationActuatorPath("/shutdown", RefreshStrategy.SHUTDOWN);
|
||||
}
|
||||
|
||||
void triggerConfigMapRefreshWithAnnotationActuatorPath(String endpoint, RefreshStrategy refreshStrategy) {
|
||||
int port = WIRE_MOCK_SERVER.port();
|
||||
WireMock.configureFor("localhost", port);
|
||||
List<ServiceInstance> instances = getServiceInstances(port);
|
||||
when(reactiveDiscoveryClient.getInstances(eq("foo"))).thenReturn(Flux.fromIterable(instances));
|
||||
V1ConfigMap configMap = new V1ConfigMap();
|
||||
V1ObjectMeta objectMeta = new V1ObjectMeta();
|
||||
objectMeta.setName("foo");
|
||||
configMap.setMetadata(objectMeta);
|
||||
WireMock.stubFor(WireMock.post(WireMock.urlEqualTo("/my/custom/actuator" + endpoint))
|
||||
.willReturn(WireMock.aResponse().withStatus(200)));
|
||||
ConfigurationWatcherConfigurationProperties configurationWatcherConfigurationProperties = new ConfigurationWatcherConfigurationProperties();
|
||||
configurationWatcherConfigurationProperties.setRefreshStrategy(refreshStrategy);
|
||||
HttpBasedConfigMapWatchChangeDetector changeDetector = new HttpBasedConfigMapWatchChangeDetector(coreV1Api,
|
||||
mockEnvironment, ConfigReloadProperties.DEFAULT, strategy, configMapPropertySourceLocator,
|
||||
new KubernetesNamespaceProvider(mockEnvironment), configurationWatcherConfigurationProperties,
|
||||
threadPoolTaskExecutor, new HttpRefreshTrigger(reactiveDiscoveryClient,
|
||||
configurationWatcherConfigurationProperties, webClient));
|
||||
StepVerifier.create(changeDetector.triggerRefresh(configMap, configMap.getMetadata().getName()))
|
||||
.verifyComplete();
|
||||
WireMock.verify(WireMock.postRequestedFor(WireMock.urlEqualTo("/my/custom/actuator" + endpoint)));
|
||||
}
|
||||
|
||||
private static @NotNull List<ServiceInstance> getServiceInstances(int port) {
|
||||
Map<String, String> metadata = new HashMap<>();
|
||||
metadata.put(ConfigurationWatcherConfigurationProperties.ANNOTATION_KEY,
|
||||
"http://:" + port + "/my/custom/actuator");
|
||||
@@ -169,20 +227,10 @@ class HttpBasedConfigMapWatchChangeDetectorTests {
|
||||
DefaultKubernetesServiceInstance fooServiceInstance = new DefaultKubernetesServiceInstance("foo", "foo",
|
||||
fooEndpointAddress.getIp(), fooEndpointPort.getPort(), metadata, false);
|
||||
instances.add(fooServiceInstance);
|
||||
when(reactiveDiscoveryClient.getInstances(eq("foo"))).thenReturn(Flux.fromIterable(instances));
|
||||
V1ConfigMap configMap = new V1ConfigMap();
|
||||
V1ObjectMeta objectMeta = new V1ObjectMeta();
|
||||
objectMeta.setName("foo");
|
||||
configMap.setMetadata(objectMeta);
|
||||
WireMock.stubFor(WireMock.post(WireMock.urlEqualTo("/my/custom/actuator/refresh"))
|
||||
.willReturn(WireMock.aResponse().withStatus(200)));
|
||||
StepVerifier.create(changeDetector.triggerRefresh(configMap, configMap.getMetadata().getName()))
|
||||
.verifyComplete();
|
||||
WireMock.verify(WireMock.postRequestedFor(WireMock.urlEqualTo("/my/custom/actuator/refresh")));
|
||||
return instances;
|
||||
}
|
||||
|
||||
private void stubReactiveCall() {
|
||||
|
||||
V1EndpointAddress fooEndpointAddress = new V1EndpointAddress();
|
||||
fooEndpointAddress.setIp("127.0.0.1");
|
||||
fooEndpointAddress.setHostname("localhost");
|
||||
|
||||
@@ -52,12 +52,14 @@ import org.springframework.cloud.kubernetes.commons.config.reload.ConfigurationU
|
||||
import org.springframework.cloud.kubernetes.commons.discovery.DefaultKubernetesServiceInstance;
|
||||
import org.springframework.mock.env.MockEnvironment;
|
||||
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
|
||||
import org.springframework.util.StringUtils;
|
||||
import org.springframework.web.reactive.function.client.WebClient;
|
||||
|
||||
import static org.mockito.ArgumentMatchers.eq;
|
||||
import static org.mockito.Mockito.mockStatic;
|
||||
import static org.mockito.Mockito.when;
|
||||
import static org.springframework.cloud.kubernetes.commons.KubernetesNamespaceProvider.NAMESPACE_PROPERTY;
|
||||
import static org.springframework.cloud.kubernetes.configuration.watcher.ConfigurationWatcherConfigurationProperties.RefreshStrategy;
|
||||
|
||||
/**
|
||||
* @author Ryan Baxter
|
||||
@@ -86,21 +88,15 @@ class HttpBasedSecretsWatchChangeDetectorTests {
|
||||
@Mock
|
||||
private KubernetesInformerReactiveDiscoveryClient reactiveDiscoveryClient;
|
||||
|
||||
private HttpBasedSecretsWatchChangeDetector changeDetector;
|
||||
private MockEnvironment mockEnvironment;
|
||||
|
||||
private ConfigurationWatcherConfigurationProperties configurationWatcherConfigurationProperties;
|
||||
private WebClient webClient;
|
||||
|
||||
@BeforeEach
|
||||
void setup() {
|
||||
MockEnvironment mockEnvironment = new MockEnvironment();
|
||||
mockEnvironment = new MockEnvironment();
|
||||
mockEnvironment.setProperty(NAMESPACE_PROPERTY, "default");
|
||||
configurationWatcherConfigurationProperties = new ConfigurationWatcherConfigurationProperties();
|
||||
WebClient webClient = WebClient.builder().build();
|
||||
changeDetector = new HttpBasedSecretsWatchChangeDetector(coreV1Api, mockEnvironment,
|
||||
ConfigReloadProperties.DEFAULT, updateStrategy, secretsPropertySourceLocator,
|
||||
new KubernetesNamespaceProvider(mockEnvironment), configurationWatcherConfigurationProperties,
|
||||
threadPoolTaskExecutor, new HttpRefreshTrigger(reactiveDiscoveryClient,
|
||||
configurationWatcherConfigurationProperties, webClient));
|
||||
webClient = WebClient.builder().build();
|
||||
}
|
||||
|
||||
@BeforeAll
|
||||
@@ -120,36 +116,83 @@ class HttpBasedSecretsWatchChangeDetectorTests {
|
||||
}
|
||||
|
||||
@Test
|
||||
void triggerSecretRefresh() {
|
||||
void triggerSecretRefreshUsingRefresh() {
|
||||
triggerSecretRefresh("/refresh", RefreshStrategy.REFRESH);
|
||||
}
|
||||
|
||||
@Test
|
||||
void triggerSecretRefreshUsingShutdown() {
|
||||
triggerSecretRefresh("/shutdown", RefreshStrategy.SHUTDOWN);
|
||||
}
|
||||
|
||||
void triggerSecretRefresh(String endpoint, RefreshStrategy refreshStrategy) {
|
||||
stubReactiveCall();
|
||||
V1Secret secret = new V1Secret();
|
||||
V1ObjectMeta objectMeta = new V1ObjectMeta();
|
||||
objectMeta.setName("foo");
|
||||
secret.setMetadata(objectMeta);
|
||||
WireMock.configureFor("localhost", WIRE_MOCK_SERVER.port());
|
||||
WireMock.stubFor(WireMock.post(WireMock.urlEqualTo("/actuator/refresh"))
|
||||
WireMock.stubFor(WireMock.post(WireMock.urlEqualTo("/actuator" + endpoint))
|
||||
.willReturn(WireMock.aResponse().withStatus(200)));
|
||||
HttpBasedSecretsWatchChangeDetector changeDetector = getHttpBasedSecretsWatchChangeDetector(refreshStrategy);
|
||||
StepVerifier.create(changeDetector.triggerRefresh(secret, secret.getMetadata().getName())).verifyComplete();
|
||||
WireMock.verify(WireMock.postRequestedFor(WireMock.urlEqualTo("/actuator/refresh")));
|
||||
WireMock.verify(WireMock.postRequestedFor(WireMock.urlEqualTo("/actuator" + endpoint)));
|
||||
}
|
||||
|
||||
private HttpBasedSecretsWatchChangeDetector getHttpBasedSecretsWatchChangeDetector(
|
||||
RefreshStrategy refreshStrategy) {
|
||||
return getHttpBasedSecretsWatchChangeDetector(null, refreshStrategy);
|
||||
}
|
||||
|
||||
private HttpBasedSecretsWatchChangeDetector getHttpBasedSecretsWatchChangeDetector(String actuatorPath,
|
||||
RefreshStrategy refreshStrategy) {
|
||||
ConfigurationWatcherConfigurationProperties configurationWatcherConfigurationProperties = new ConfigurationWatcherConfigurationProperties();
|
||||
if (StringUtils.hasText(actuatorPath)) {
|
||||
configurationWatcherConfigurationProperties.setActuatorPath(actuatorPath);
|
||||
}
|
||||
configurationWatcherConfigurationProperties.setRefreshStrategy(refreshStrategy);
|
||||
return new HttpBasedSecretsWatchChangeDetector(coreV1Api, mockEnvironment, ConfigReloadProperties.DEFAULT,
|
||||
updateStrategy, secretsPropertySourceLocator, new KubernetesNamespaceProvider(mockEnvironment),
|
||||
configurationWatcherConfigurationProperties, threadPoolTaskExecutor, new HttpRefreshTrigger(
|
||||
reactiveDiscoveryClient, configurationWatcherConfigurationProperties, webClient));
|
||||
}
|
||||
|
||||
@Test
|
||||
void triggerSecretRefreshWithPropertiesBasedActuatorPath() {
|
||||
void triggerSecretRefreshWithPropertiesBasedActuatorPathUsingRefresh() {
|
||||
triggerSecretRefreshWithPropertiesBasedActuatorPath("/refresh", RefreshStrategy.REFRESH);
|
||||
}
|
||||
|
||||
@Test
|
||||
void triggerSecretRefreshWithPropertiesBasedActuatorPathUsingShutdown() {
|
||||
triggerSecretRefreshWithPropertiesBasedActuatorPath("/shutdown", RefreshStrategy.SHUTDOWN);
|
||||
}
|
||||
|
||||
void triggerSecretRefreshWithPropertiesBasedActuatorPath(String endpoint, RefreshStrategy refreshStrategy) {
|
||||
stubReactiveCall();
|
||||
configurationWatcherConfigurationProperties.setActuatorPath("/my/custom/actuator");
|
||||
V1Secret secret = new V1Secret();
|
||||
V1ObjectMeta objectMeta = new V1ObjectMeta();
|
||||
objectMeta.setName("foo");
|
||||
secret.setMetadata(objectMeta);
|
||||
WireMock.configureFor("localhost", WIRE_MOCK_SERVER.port());
|
||||
WireMock.stubFor(WireMock.post(WireMock.urlEqualTo("/my/custom/actuator/refresh"))
|
||||
WireMock.stubFor(WireMock.post(WireMock.urlEqualTo("/my/custom/actuator" + endpoint))
|
||||
.willReturn(WireMock.aResponse().withStatus(200)));
|
||||
HttpBasedSecretsWatchChangeDetector changeDetector = getHttpBasedSecretsWatchChangeDetector(
|
||||
"/my/custom/actuator", refreshStrategy);
|
||||
StepVerifier.create(changeDetector.triggerRefresh(secret, secret.getMetadata().getName())).verifyComplete();
|
||||
WireMock.verify(WireMock.postRequestedFor(WireMock.urlEqualTo("/my/custom/actuator/refresh")));
|
||||
WireMock.verify(WireMock.postRequestedFor(WireMock.urlEqualTo("/my/custom/actuator" + endpoint)));
|
||||
}
|
||||
|
||||
@Test
|
||||
void triggerSecretRefreshWithAnnotationActuatorPath() {
|
||||
void triggerSecretRefreshWithAnnotationActuatorPathUsingRefresh() {
|
||||
triggerSecretRefreshWithAnnotationActuatorPath("/refresh", RefreshStrategy.REFRESH);
|
||||
}
|
||||
|
||||
@Test
|
||||
void triggerSecretRefreshWithAnnotationActuatorPathUsingShutdown() {
|
||||
triggerSecretRefreshWithAnnotationActuatorPath("/shutdown", RefreshStrategy.SHUTDOWN);
|
||||
}
|
||||
|
||||
void triggerSecretRefreshWithAnnotationActuatorPath(String endpoint, RefreshStrategy refreshStrategy) {
|
||||
WireMock.configureFor("localhost", WIRE_MOCK_SERVER.port());
|
||||
Map<String, String> metadata = new HashMap<>();
|
||||
metadata.put(ConfigurationWatcherConfigurationProperties.ANNOTATION_KEY,
|
||||
@@ -168,10 +211,12 @@ class HttpBasedSecretsWatchChangeDetectorTests {
|
||||
V1ObjectMeta objectMeta = new V1ObjectMeta();
|
||||
objectMeta.setName("foo");
|
||||
secret.setMetadata(objectMeta);
|
||||
WireMock.stubFor(WireMock.post(WireMock.urlEqualTo("/my/custom/actuator/refresh"))
|
||||
WireMock.stubFor(WireMock.post(WireMock.urlEqualTo("/my/custom/actuator" + endpoint))
|
||||
.willReturn(WireMock.aResponse().withStatus(200)));
|
||||
HttpBasedSecretsWatchChangeDetector changeDetector = getHttpBasedSecretsWatchChangeDetector(
|
||||
"/my/custom/actuator", refreshStrategy);
|
||||
StepVerifier.create(changeDetector.triggerRefresh(secret, secret.getMetadata().getName())).verifyComplete();
|
||||
WireMock.verify(WireMock.postRequestedFor(WireMock.urlEqualTo("/my/custom/actuator/refresh")));
|
||||
WireMock.verify(WireMock.postRequestedFor(WireMock.urlEqualTo("/my/custom/actuator" + endpoint)));
|
||||
}
|
||||
|
||||
private void stubReactiveCall() {
|
||||
|
||||
Reference in New Issue
Block a user