Fix gh 629 new (#683)

* Swaps deprecated ConditionalOnEnabledEndpoint for updated annotation.

Swaps with ConditionalOnAvailableEndpoint

* Migrates to new OutputCaptureRule

* Migrates to new OutputCaptureRule

* Add health check loadBalancing implementation.

* Make isAlive() method protected.

Co-authored-by: Spencer Gibb <spencer@gibb.tech>
This commit is contained in:
Olga Maciaszek-Sharma
2020-02-04 10:14:08 +01:00
committed by GitHub
parent 744ad89177
commit 47a810606d
10 changed files with 442 additions and 31 deletions

View File

@@ -28,10 +28,13 @@
|spring.cloud.inetutils.timeout-seconds | 1 | Timeout, in seconds, for calculating hostname.
|spring.cloud.inetutils.use-only-site-local-interfaces | false | Whether to use only interfaces with site local addresses. See {@link InetAddress#isSiteLocalAddress()} for more details.
|spring.cloud.loadbalancer.cache.caffeine.spec | | The spec to use to create caches. See CaffeineSpec for more details on the spec format.
|spring.cloud.loadbalancer.cache.capacity | 256 | Initial cache capacity expressed as int.
|spring.cloud.loadbalancer.cache.ttl | 30s | Time To Live - time counted from writing of the record, after which cache entries are expired, expressed as a {@link Duration}. The property {@link String} has to be in keeping with the appropriate syntax as specified in Spring Boot <code>StringToDurationConverter</code>. @see <a href= "https://github.com/spring-projects/spring-boot/blob/master/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/convert/StringToDurationConverter.java">StringToDurationConverter.java</a>
|spring.cloud.loadbalancer.health-check.initial-delay | 0 | Initial delay value for the HealthCheck scheduler.
|spring.cloud.loadbalancer.health-check.interval | 30s | Interval for rerunning the HealthCheck scheduler.
|spring.cloud.loadbalancer.health-check.path | |
|spring.cloud.loadbalancer.retry.enabled | true |
|spring.cloud.loadbalancer.ribbon.enabled | true | Causes `RibbonLoadBalancerClient` to be used by default.
|spring.cloud.loadbalancer.zone | | A {@link String} representation of the <code>zone</code> used for filtering instances by zoned load-balancing implementations.
|spring.cloud.refresh.enabled | true | Enables autoconfiguration for the refresh scope and associated features.
|spring.cloud.refresh.extra-refreshable | true | Additional class names for beans to post process into refresh scope.
|spring.cloud.service-registry.auto-registration.enabled | true | Whether service auto-registration is enabled. Defaults to true.

View File

@@ -906,12 +906,71 @@ public class CustomLoadBalancerConfiguration {
@Bean
public ServiceInstanceListSupplier discoveryClientServiceInstanceListSupplier(
ReactiveDiscoveryClient discoveryClient, Environment environment,
LoadBalancerProperties loadBalancerProperties,
LoadBalancerZoneConfig zoneConfig,
ApplicationContext context) {
DiscoveryClientServiceInstanceListSupplier firstDelegate = new DiscoveryClientServiceInstanceListSupplier(
discoveryClient, environment);
ZonePreferenceServiceInstanceListSupplier delegate = new ZonePreferenceServiceInstanceListSupplier(firstDelegate,
loadBalancerProperties);
zoneConfig);
ObjectProvider<LoadBalancerCacheManager> cacheManagerProvider = context
.getBeanProvider(LoadBalancerCacheManager.class);
if (cacheManagerProvider.getIfAvailable() != null) {
return new CachingServiceInstanceListSupplier(delegate,
cacheManagerProvider.getIfAvailable());
}
return delegate;
}
}
----
=== Instance Health-Check for LoadBalancer
It is possible to enable a scheduled HealthCheck for the LoadBalancer. The `HealthCheckServiceInstanceListSupplier`
is provided for that. It regularly verifies if the instances provided by a delegate
`ServiceInstanceListSupplier` are still alive and only returns the healthy instances,
unless there are none - then it returns all the retrieved instances.
TIP: This mechanism is particularly helpful while using the `SimpleDiscoveryClient`. For the
clients backed by an actual Service Registry, it's not necessary to use, as we already get
healthy instances after querying the external ServiceDiscovery.
The `HealthCheckServiceInstanceListSupplier` uses `InstanceHealthChecker` to verify if the instances are
alive. We provide a default `PingHealthChecker` instance. It uses `WebClient` to execute
requests against the `health` endpoint of the instance. You can also provide your own implementation
of `InstanceHealthChecker` instead.
`HealthCheckServiceInstanceListSupplier` uses properties prefixed with
`spring.cloud.loadbalancer.healthcheck`. You can set the `initialDelay` and `interval`
for the scheduler.
For the `PingHealthChecker`, you can set the default path for the healthcheck URL by setting
the value of the `spring.cloud.loadbalancer.healthcheck.path.default`. You can also set a specific value
for any given service by setting the value of the `spring.cloud.loadbalancer.healthcheck.path.[SERVICE_ID]`,
substituting the `[SERVICE_ID]` with the correct ID of your service. If the path is not set,
`/actuator/health` is used by default.
In order to use the health-check scheduler approach, you will have to instantiate a `HealthCheckServiceInstanceListSupplier` bean in a <<custom-loadbalancer-configuration,custom configuration>>.
We use delegates to work with `ServiceInstanceListSupplier` beans.
We suggest passing a `DiscoveryClientServiceInstanceListSupplier` delegate in the constructor of `HealthCheckServiceInstanceListSupplier` and, in turn, wrapping the latter with a `CachingServiceInstanceListSupplier` to leverage <<loadbalancer-caching, LoadBalancer caching mechanism>>.
You could use this sample configuration to set it up:
[[zoned-based-custom-loadbalancer-configuration]]
[source,java,indent=0]
----
public class CustomLoadBalancerConfiguration {
@Bean
public ServiceInstanceListSupplier discoveryClientServiceInstanceListSupplier(
ReactiveDiscoveryClient discoveryClient, Environment environment,
LoadBalancerProperties loadBalancerProperties,
ApplicationContext context,
InstanceHealthChecker healthChecker) {
DiscoveryClientServiceInstanceListSupplier firstDelegate = new DiscoveryClientServiceInstanceListSupplier(
discoveryClient, environment);
HealthCheckServiceInstanceListSupplier delegate = new HealthCheckServiceInstanceListSupplier(firstDelegate,
loadBalancerProperties, healthChecker);
ObjectProvider<LoadBalancerCacheManager> cacheManagerProvider = context
.getBeanProvider(LoadBalancerCacheManager.class);
if (cacheManagerProvider.getIfAvailable() != null) {

View File

@@ -16,7 +16,11 @@
package org.springframework.cloud.client.loadbalancer.reactive;
import java.time.Duration;
import java.util.Map;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.util.LinkedCaseInsensitiveMap;
/**
* A {@link ConfigurationProperties} bean for Spring Cloud LoadBalancer.
@@ -28,17 +32,56 @@ import org.springframework.boot.context.properties.ConfigurationProperties;
public class LoadBalancerProperties {
/**
* A {@link String} representation of the <code>zone</code> used for filtering
* instances by zoned load-balancing implementations.
* Properties for <code>HealthCheckServiceInstanceListSupplier</code>.
*/
private String zone;
private HealthCheck healthCheck = new HealthCheck();
public String getZone() {
return zone;
public HealthCheck getHealthCheck() {
return healthCheck;
}
public void setZone(String zone) {
this.zone = zone;
public void setHealthCheck(HealthCheck healthCheck) {
this.healthCheck = healthCheck;
}
public static class HealthCheck {
/**
* Initial delay value for the HealthCheck scheduler.
*/
private int initialDelay = 0;
/**
* Interval for rerunning the HealthCheck scheduler.
*/
private Duration interval = Duration.ofSeconds(30);
private Map<String, String> path = new LinkedCaseInsensitiveMap<>();
public int getInitialDelay() {
return initialDelay;
}
public void setInitialDelay(int initialDelay) {
this.initialDelay = initialDelay;
}
public Map<String, String> getPath() {
return path;
}
public void setPath(Map<String, String> path) {
this.path = path;
}
public Duration getInterval() {
return interval;
}
public void setInterval(Duration interval) {
this.interval = interval;
}
}
}

View File

@@ -19,14 +19,12 @@ package org.springframework.cloud.loadbalancer.annotation;
import org.springframework.beans.factory.ObjectProvider;
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.cloud.client.ConditionalOnBlockingDiscoveryEnabled;
import org.springframework.cloud.client.ConditionalOnDiscoveryEnabled;
import org.springframework.cloud.client.ConditionalOnReactiveDiscoveryEnabled;
import org.springframework.cloud.client.ServiceInstance;
import org.springframework.cloud.client.discovery.DiscoveryClient;
import org.springframework.cloud.client.discovery.ReactiveDiscoveryClient;
import org.springframework.cloud.client.loadbalancer.reactive.LoadBalancerProperties;
import org.springframework.cloud.loadbalancer.cache.LoadBalancerCacheManager;
import org.springframework.cloud.loadbalancer.core.CachingServiceInstanceListSupplier;
import org.springframework.cloud.loadbalancer.core.CachingServiceInstanceSupplier;
@@ -49,18 +47,11 @@ import org.springframework.core.env.Environment;
* @author Tim Ysewyn
*/
@Configuration(proxyBeanMethods = false)
@EnableConfigurationProperties(LoadBalancerProperties.class)
@ConditionalOnDiscoveryEnabled
public class LoadBalancerClientConfiguration {
private static final int REACTIVE_SERVICE_INSTANCE_SUPPLIER_ORDER = 193827465;
@Bean
@ConditionalOnMissingBean
LoadBalancerProperties loadBalancerProperties() {
return new LoadBalancerProperties();
}
@Bean
@ConditionalOnMissingBean
public ReactorLoadBalancer<ServiceInstance> reactorServiceInstanceLoadBalancer(

View File

@@ -22,7 +22,9 @@ import java.util.List;
import org.springframework.beans.factory.ObjectProvider;
import org.springframework.boot.autoconfigure.AutoConfigureBefore;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.cloud.client.loadbalancer.reactive.LoadBalancerBeanPostProcessorAutoConfiguration;
import org.springframework.cloud.client.loadbalancer.reactive.LoadBalancerProperties;
import org.springframework.cloud.client.loadbalancer.reactive.ReactiveLoadBalancerAutoConfiguration;
import org.springframework.cloud.client.loadbalancer.reactive.ReactorLoadBalancerClientAutoConfiguration;
import org.springframework.cloud.loadbalancer.annotation.LoadBalancerClientSpecification;
@@ -30,6 +32,7 @@ import org.springframework.cloud.loadbalancer.annotation.LoadBalancerClients;
import org.springframework.cloud.loadbalancer.support.LoadBalancerClientFactory;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.env.Environment;
/**
* @author Spencer Gibb
@@ -37,6 +40,7 @@ import org.springframework.context.annotation.Configuration;
*/
@Configuration(proxyBeanMethods = false)
@LoadBalancerClients
@EnableConfigurationProperties(LoadBalancerProperties.class)
@AutoConfigureBefore({ ReactorLoadBalancerClientAutoConfiguration.class,
LoadBalancerBeanPostProcessorAutoConfiguration.class,
ReactiveLoadBalancerAutoConfiguration.class })
@@ -49,6 +53,13 @@ public class LoadBalancerAutoConfiguration {
this.configurations = configurations;
}
@Bean
@ConditionalOnMissingBean
public LoadBalancerZoneConfig zoneConfig(Environment environment) {
return new LoadBalancerZoneConfig(
environment.getProperty("spring.cloud.loadbalancer.zone"));
}
@ConditionalOnMissingBean
@Bean
public LoadBalancerClientFactory loadBalancerClientFactory() {

View File

@@ -0,0 +1,42 @@
/*
* Copyright 2012-2019 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.loadbalancer.config;
/**
* @author Olga Maciaszek-Sharma
*/
public class LoadBalancerZoneConfig {
/**
* A {@link String} representation of the <code>zone</code> used for filtering
* instances by zoned load-balancing implementations.
*/
private String zone;
public LoadBalancerZoneConfig(String zone) {
this.zone = zone;
}
public String getZone() {
return zone;
}
public void setZone(String zone) {
this.zone = zone;
}
}

View File

@@ -0,0 +1,136 @@
/*
* Copyright 2012-2019 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.loadbalancer.core;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.concurrent.TimeUnit;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import reactor.core.publisher.Flux;
import reactor.core.publisher.FluxSink;
import reactor.core.publisher.Mono;
import reactor.core.scheduler.Schedulers;
import org.springframework.cloud.client.ServiceInstance;
import org.springframework.cloud.client.loadbalancer.reactive.LoadBalancerProperties;
import org.springframework.http.HttpStatus;
import org.springframework.web.reactive.function.client.WebClient;
import org.springframework.web.util.UriComponentsBuilder;
/**
* A {@link ServiceInstanceListSupplier} implementation that verifies whether the
* instances are alive and only returns the healthy one, unless there are none. Uses
* {@link WebClient} to ping the <code>health</code> endpoint of the instances.
*
* @author Olga Maciaszek-Sharma
* @since 2.2.0
*/
public class HealthCheckServiceInstanceListSupplier
implements ServiceInstanceListSupplier {
private static final Log LOG = LogFactory
.getLog(HealthCheckServiceInstanceListSupplier.class);
private final ServiceInstanceListSupplier delegate;
private final LoadBalancerProperties.HealthCheck healthCheck;
private final WebClient webClient;
private final String defaultHealthCheckPath;
private List<ServiceInstance> instances = Collections
.synchronizedList(new ArrayList<>());
private List<ServiceInstance> healthyInstances = Collections
.synchronizedList(new ArrayList<>());
public HealthCheckServiceInstanceListSupplier(ServiceInstanceListSupplier delegate,
LoadBalancerProperties.HealthCheck healthCheck, WebClient webClient) {
this.delegate = delegate;
this.healthCheck = healthCheck;
defaultHealthCheckPath = healthCheck.getPath().getOrDefault("default",
"/actuator/health");
this.webClient = webClient;
initInstances();
}
private void initInstances() {
delegate.get().subscribe(delegateInstances -> {
instances.clear();
instances.addAll(delegateInstances);
});
Flux<List<ServiceInstance>> healthCheckFlux = healthCheckFlux();
healthCheckFlux.subscribe(verifiedInstances -> {
healthyInstances.clear();
healthyInstances.addAll(verifiedInstances);
});
}
protected Flux<List<ServiceInstance>> healthCheckFlux() {
return Flux.create(emitter -> Schedulers
.newSingle("Health Check Verifier: " + getServiceId(), true)
.schedulePeriodically(() -> {
List<ServiceInstance> verifiedInstances = new ArrayList<>();
Flux.fromIterable(instances).filterWhen(this::isAlive)
.subscribe(serviceInstance -> {
verifiedInstances.add(serviceInstance);
emitter.next(verifiedInstances);
});
}, healthCheck.getInitialDelay(), healthCheck.getInterval().toMillis(),
TimeUnit.MILLISECONDS),
FluxSink.OverflowStrategy.LATEST);
}
@Override
public String getServiceId() {
return delegate.getServiceId();
}
@Override
public Flux<List<ServiceInstance>> get() {
if (!healthyInstances.isEmpty()) {
return Flux.defer(() -> Flux.fromIterable(healthyInstances).collectList());
}
// If there are no healthy instances, it might be better to still retry on all of
// them
if (LOG.isWarnEnabled()) {
LOG.warn(
"No verified healthy instances were found, returning all listed instances.");
}
return Flux.defer(() -> Flux.fromIterable(instances).collectList());
}
protected Mono<Boolean> isAlive(ServiceInstance serviceInstance) {
String healthCheckPropertyValue = healthCheck.getPath()
.get(serviceInstance.getServiceId());
String healthCheckPath = healthCheckPropertyValue != null
? healthCheckPropertyValue : defaultHealthCheckPath;
return webClient.get()
.uri(UriComponentsBuilder.fromUri(serviceInstance.getUri())
.path(healthCheckPath).build().toUri())
.exchange()
.map(clientResponse -> HttpStatus.OK.equals(clientResponse.statusCode()));
}
}

View File

@@ -23,7 +23,7 @@ import java.util.Map;
import reactor.core.publisher.Flux;
import org.springframework.cloud.client.ServiceInstance;
import org.springframework.cloud.client.loadbalancer.reactive.LoadBalancerProperties;
import org.springframework.cloud.loadbalancer.config.LoadBalancerZoneConfig;
/**
* An implementation of {@link ServiceInstanceListSupplier} that filters instances
@@ -42,14 +42,14 @@ public class ZonePreferenceServiceInstanceListSupplier
private final ServiceInstanceListSupplier delegate;
private final LoadBalancerProperties loadBalancerProperties;
private final LoadBalancerZoneConfig zoneConfig;
private String zone;
public ZonePreferenceServiceInstanceListSupplier(ServiceInstanceListSupplier delegate,
LoadBalancerProperties loadBalancerProperties) {
LoadBalancerZoneConfig zoneConfig) {
this.delegate = delegate;
this.loadBalancerProperties = loadBalancerProperties;
this.zoneConfig = zoneConfig;
}
@Override
@@ -64,7 +64,7 @@ public class ZonePreferenceServiceInstanceListSupplier
private List<ServiceInstance> filteredByZone(List<ServiceInstance> serviceInstances) {
if (zone == null) {
zone = loadBalancerProperties.getZone();
zone = zoneConfig.getZone();
}
if (zone != null) {
List<ServiceInstance> filteredInstances = new ArrayList<>();

View File

@@ -0,0 +1,126 @@
/*
* Copyright 2012-2019 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.loadbalancer.core;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.web.server.LocalServerPort;
import org.springframework.cloud.client.DefaultServiceInstance;
import org.springframework.cloud.client.ServiceInstance;
import org.springframework.cloud.client.loadbalancer.reactive.LoadBalancerProperties;
import org.springframework.context.annotation.Configuration;
import org.springframework.mock.env.MockEnvironment;
import org.springframework.test.context.junit.jupiter.SpringExtension;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.reactive.function.client.WebClient;
import static org.assertj.core.api.Assertions.assertThat;
/**
* Tests for {@link HealthCheckServiceInstanceListSupplier}.
*
* @author Olga Maciaszek-Sharma
*/
@ExtendWith(SpringExtension.class)
@SpringBootTest(
classes = HealthCheckServiceInstanceListSupplierTests.TestApplication.class,
webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
class HealthCheckServiceInstanceListSupplierTests {
@LocalServerPort
private int port;
private final WebClient webClient = WebClient.create();
private LoadBalancerProperties.HealthCheck healthCheck = new LoadBalancerProperties.HealthCheck();
@SuppressWarnings("ConstantConditions")
@Test
void shouldCheckInstanceWithProvidedHealthCheckPath() {
healthCheck.getPath().put("ignored-service", "/health");
HealthCheckServiceInstanceListSupplier listSupplier = new HealthCheckServiceInstanceListSupplier(
ServiceInstanceListSupplier.FixedServiceInstanceListSupplier
.with(new MockEnvironment()).build(),
healthCheck, webClient);
ServiceInstance serviceInstance = new DefaultServiceInstance("ignored-service-1",
"ignored-service", "127.0.0.1", port, false);
boolean alive = listSupplier.isAlive(serviceInstance).block();
assertThat(alive).isTrue();
}
@SuppressWarnings("ConstantConditions")
@Test
void shouldCheckInstanceWithDefaultHealthCheckPath() {
HealthCheckServiceInstanceListSupplier listSupplier = new HealthCheckServiceInstanceListSupplier(
ServiceInstanceListSupplier.FixedServiceInstanceListSupplier
.with(new MockEnvironment()).build(),
healthCheck, webClient);
ServiceInstance serviceInstance = new DefaultServiceInstance("ignored-service-1",
"ignored-service", "127.0.0.1", port, false);
boolean alive = listSupplier.isAlive(serviceInstance).block();
assertThat(alive).isTrue();
}
@SuppressWarnings("ConstantConditions")
@Test
void shouldReturnFalseIfEndpointNotFound() {
healthCheck.getPath().put("ignored-service", "/test");
HealthCheckServiceInstanceListSupplier listSupplier = new HealthCheckServiceInstanceListSupplier(
ServiceInstanceListSupplier.FixedServiceInstanceListSupplier
.with(new MockEnvironment()).build(),
healthCheck, webClient);
ServiceInstance serviceInstance = new DefaultServiceInstance("ignored-service-1",
"ignored-service", "127.0.0.1", port, false);
boolean alive = listSupplier.isAlive(serviceInstance).block();
assertThat(alive).isFalse();
}
@Configuration(proxyBeanMethods = false)
@EnableAutoConfiguration
@RestController
static class TestApplication {
public static void main(String[] args) {
SpringApplication.run(
HealthCheckServiceInstanceListSupplierTests.TestApplication.class,
args);
}
@GetMapping("/health")
void healthCheck() {
}
@GetMapping("/actuator/health")
void defaultHealthCheck() {
}
}
}

View File

@@ -27,7 +27,7 @@ import reactor.core.publisher.Flux;
import org.springframework.cloud.client.DefaultServiceInstance;
import org.springframework.cloud.client.ServiceInstance;
import org.springframework.cloud.client.loadbalancer.reactive.LoadBalancerProperties;
import org.springframework.cloud.loadbalancer.config.LoadBalancerZoneConfig;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatCode;
@@ -44,10 +44,10 @@ class ZonePreferenceServiceInstanceListSupplierTests {
private DiscoveryClientServiceInstanceListSupplier delegate = mock(
DiscoveryClientServiceInstanceListSupplier.class);
private LoadBalancerProperties loadBalancerProperties = new LoadBalancerProperties();
private LoadBalancerZoneConfig zoneConfig = new LoadBalancerZoneConfig(null);
private ZonePreferenceServiceInstanceListSupplier supplier = new ZonePreferenceServiceInstanceListSupplier(
delegate, loadBalancerProperties);
delegate, zoneConfig);
private ServiceInstance first = serviceInstance("test-1", buildZoneMetadata("zone1"));
@@ -63,7 +63,7 @@ class ZonePreferenceServiceInstanceListSupplierTests {
@Test
void shouldFilterInstancesByZone() {
loadBalancerProperties.setZone("zone1");
zoneConfig.setZone("zone1");
when(delegate.get()).thenReturn(
Flux.just(Arrays.asList(first, second, third, fourth, fifth)));
@@ -78,7 +78,7 @@ class ZonePreferenceServiceInstanceListSupplierTests {
@Test
void shouldReturnAllInstancesIfNoZoneInstances() {
loadBalancerProperties.setZone("zone1");
zoneConfig.setZone("zone1");
when(delegate.get()).thenReturn(Flux.just(Arrays.asList(third, fourth)));
List<ServiceInstance> filtered = supplier.get().blockFirst();
@@ -89,7 +89,7 @@ class ZonePreferenceServiceInstanceListSupplierTests {
@Test
void shouldNotThrowNPEIfNullInstanceMetadata() {
loadBalancerProperties.setZone("zone1");
zoneConfig.setZone("zone1");
when(delegate.get()).thenReturn(
Flux.just(Collections.singletonList(serviceInstance("test-6", null))));
assertThatCode(() -> supplier.get().blockFirst()).doesNotThrowAnyException();
@@ -97,7 +97,7 @@ class ZonePreferenceServiceInstanceListSupplierTests {
@Test
void shouldReturnAllInstancesIfNoZone() {
loadBalancerProperties.setZone(null);
zoneConfig.setZone(null);
when(delegate.get())
.thenReturn(Flux.just(Arrays.asList(first, second, third, fourth)));