Merge branch '2.2.x'

This commit is contained in:
Spencer Gibb
2020-05-01 10:54:34 -04:00
14 changed files with 871 additions and 91 deletions
+1 -1
View File
@@ -14,7 +14,7 @@
<name>spring-cloud-netflix-dependencies</name>
<description>Spring Cloud Netflix Dependencies</description>
<properties>
<eureka.version>1.9.19</eureka.version>
<eureka.version>1.9.21</eureka.version>
</properties>
<dependencyManagement>
<dependencies>
@@ -1086,7 +1086,7 @@ public class EurekaClientConfigBean implements EurekaClientConfig, Ordered {
.append(decoderName).append("', ").append("clientDataAccept='")
.append(clientDataAccept).append("', ")
.append("shouldUnregisterOnShutdown='").append(shouldUnregisterOnShutdown)
.append("', ").append("shouldEnforceRegistrationAtInit='")
.append("shouldEnforceRegistrationAtInit='")
.append(shouldEnforceRegistrationAtInit).append("', ").append("order='")
.append(order).append("'}").toString();
}
@@ -17,13 +17,17 @@
package org.springframework.cloud.netflix.eureka.config;
import com.netflix.discovery.AbstractDiscoveryClientOptionalArgs;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.autoconfigure.condition.SearchStrategy;
import org.springframework.cloud.netflix.eureka.MutableDiscoveryClientOptionalArgs;
import org.springframework.cloud.netflix.eureka.http.RestTemplateDiscoveryClientOptionalArgs;
import org.springframework.cloud.netflix.eureka.http.WebClientDiscoveryClientOptionalArgs;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@@ -33,14 +37,32 @@ import org.springframework.context.annotation.Configuration;
@Configuration(proxyBeanMethods = false)
public class DiscoveryClientOptionalArgsConfiguration {
protected final Log logger = LogFactory.getLog(getClass());
@Bean
@ConditionalOnMissingClass("com.sun.jersey.api.client.filter.ClientFilter")
@ConditionalOnMissingBean(value = AbstractDiscoveryClientOptionalArgs.class,
@ConditionalOnMissingBean(value = { AbstractDiscoveryClientOptionalArgs.class },
search = SearchStrategy.CURRENT)
@ConditionalOnProperty(prefix = "eureka.client", name = { "webclient.enabled" },
matchIfMissing = true, havingValue = "false")
public RestTemplateDiscoveryClientOptionalArgs restTemplateDiscoveryClientOptionalArgs() {
logger.info("Eureka HTTP Client uses RestTemplate.");
return new RestTemplateDiscoveryClientOptionalArgs();
}
@Bean
@ConditionalOnMissingClass("com.sun.jersey.api.client.filter.ClientFilter")
@ConditionalOnMissingBean(
value = { AbstractDiscoveryClientOptionalArgs.class,
RestTemplateDiscoveryClientOptionalArgs.class },
search = SearchStrategy.CURRENT)
@ConditionalOnProperty(prefix = "eureka.client", name = { "webclient.enabled" },
havingValue = "true")
public WebClientDiscoveryClientOptionalArgs webClientDiscoveryClientOptionalArgs() {
logger.info("Eureka HTTP Client uses WebClient.");
return new WebClientDiscoveryClientOptionalArgs();
}
@Bean
@ConditionalOnClass(name = "com.sun.jersey.api.client.filter.ClientFilter")
@ConditionalOnMissingBean(value = AbstractDiscoveryClientOptionalArgs.class,
@@ -0,0 +1,32 @@
/*
* 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.netflix.eureka.http;
import com.netflix.discovery.AbstractDiscoveryClientOptionalArgs;
/**
* @author Daniel Lavoie
* @author Haytham Mohamed
*/
public class WebClientDiscoveryClientOptionalArgs
extends AbstractDiscoveryClientOptionalArgs<Void> {
public WebClientDiscoveryClientOptionalArgs() {
setTransportClientFactories(new WebClientTransportClientFactories());
}
}
@@ -0,0 +1,236 @@
/*
* 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.netflix.eureka.http;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import com.netflix.appinfo.InstanceInfo;
import com.netflix.appinfo.InstanceInfo.InstanceStatus;
import com.netflix.discovery.shared.Application;
import com.netflix.discovery.shared.Applications;
import com.netflix.discovery.shared.transport.EurekaHttpClient;
import com.netflix.discovery.shared.transport.EurekaHttpResponse;
import com.netflix.discovery.shared.transport.EurekaHttpResponse.EurekaHttpResponseBuilder;
import com.netflix.discovery.util.StringUtil;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.web.reactive.function.BodyInserters;
import org.springframework.web.reactive.function.client.ClientResponse;
import org.springframework.web.reactive.function.client.WebClient;
import static com.netflix.discovery.shared.transport.EurekaHttpResponse.anEurekaHttpResponse;
/**
* @author Daniel Lavoie
* @author Haytham Mohamed
*/
public class WebClientEurekaHttpClient implements EurekaHttpClient {
protected final Log logger = LogFactory.getLog(getClass());
private WebClient webClient;
public WebClientEurekaHttpClient(WebClient webClient) {
this.webClient = webClient;
}
@Override
public EurekaHttpResponse<Void> register(InstanceInfo info) {
return webClient.post().uri("apps/" + info.getAppName(), Void.class)
.body(BodyInserters.fromValue(info))
.header(HttpHeaders.ACCEPT_ENCODING, "gzip")
.header(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE)
.exchange().map(response -> eurekaHttpResponse(response)).block();
}
@Override
public EurekaHttpResponse<Void> cancel(String appName, String id) {
return webClient.delete().uri("apps/" + appName + '/' + id, Void.class).exchange()
.map(response -> eurekaHttpResponse(response)).block();
}
@Override
public EurekaHttpResponse<InstanceInfo> sendHeartBeat(String appName, String id,
InstanceInfo info, InstanceStatus overriddenStatus) {
String urlPath = "apps/" + appName + '/' + id + "?status="
+ info.getStatus().toString() + "&lastDirtyTimestamp="
+ info.getLastDirtyTimestamp().toString() + (overriddenStatus != null
? "&overriddenstatus=" + overriddenStatus.name() : "");
ClientResponse response = webClient.put().uri(urlPath, InstanceInfo.class)
.header(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE)
.header(HttpHeaders.ACCEPT, MediaType.APPLICATION_JSON_VALUE).exchange()
.block();
EurekaHttpResponseBuilder<InstanceInfo> builder = anEurekaHttpResponse(
statusCodeValueOf(response), InstanceInfo.class)
.headers(headersOf(response));
InstanceInfo entity = response.toEntity(InstanceInfo.class).block().getBody();
if (entity != null) {
builder.entity(entity);
}
return builder.build();
}
@Override
public EurekaHttpResponse<Void> statusUpdate(String appName, String id,
InstanceStatus newStatus, InstanceInfo info) {
String urlPath = "apps/" + appName + '/' + id + "/status?value="
+ newStatus.name() + "&lastDirtyTimestamp="
+ info.getLastDirtyTimestamp().toString();
return webClient.put().uri(urlPath, Void.class)
.header(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE)
.exchange().map(response -> eurekaHttpResponse(response)).block();
}
@Override
public EurekaHttpResponse<Void> deleteStatusOverride(String appName, String id,
InstanceInfo info) {
String urlPath = "apps/" + appName + '/' + id + "/status?lastDirtyTimestamp="
+ info.getLastDirtyTimestamp().toString();
return webClient.delete().uri(urlPath, Void.class)
.header(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE)
.exchange().map(response -> eurekaHttpResponse(response)).block();
}
@Override
public EurekaHttpResponse<Applications> getApplications(String... regions) {
return getApplicationsInternal("apps/", regions);
}
private EurekaHttpResponse<Applications> getApplicationsInternal(String urlPath,
String[] regions) {
String url = urlPath;
if (regions != null && regions.length > 0) {
url = url + (urlPath.contains("?") ? "&" : "?") + "regions="
+ StringUtil.join(regions);
}
ClientResponse response = webClient.get().uri(url, Applications.class)
.header(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE)
.header(HttpHeaders.ACCEPT, MediaType.APPLICATION_JSON_VALUE).exchange()
.block();
int statusCode = statusCodeValueOf(response);
Applications body = response.toEntity(Applications.class).block().getBody();
return anEurekaHttpResponse(statusCode,
statusCode == HttpStatus.OK.value() && body != null ? body : null)
.headers(headersOf(response)).build();
}
@Override
public EurekaHttpResponse<Applications> getDelta(String... regions) {
return getApplicationsInternal("apps/delta", regions);
}
@Override
public EurekaHttpResponse<Applications> getVip(String vipAddress, String... regions) {
return getApplicationsInternal("vips/" + vipAddress, regions);
}
@Override
public EurekaHttpResponse<Applications> getSecureVip(String secureVipAddress,
String... regions) {
return getApplicationsInternal("svips/" + secureVipAddress, regions);
}
@Override
public EurekaHttpResponse<Application> getApplication(String appName) {
ClientResponse response = webClient.get()
.uri("apps/" + appName, Application.class)
.header(HttpHeaders.ACCEPT, MediaType.APPLICATION_JSON_VALUE).exchange()
.block();
int statusCode = statusCodeValueOf(response);
Application body = response.toEntity(Application.class).block().getBody();
Application application = statusCode == HttpStatus.OK.value() && body != null
? body : null;
return anEurekaHttpResponse(statusCode, application).headers(headersOf(response))
.build();
}
@Override
public EurekaHttpResponse<InstanceInfo> getInstance(String appName, String id) {
return getInstanceInternal("apps/" + appName + '/' + id);
}
@Override
public EurekaHttpResponse<InstanceInfo> getInstance(String id) {
return getInstanceInternal("instances/" + id);
}
private EurekaHttpResponse<InstanceInfo> getInstanceInternal(String urlPath) {
ClientResponse response = webClient.get().uri(urlPath, InstanceInfo.class)
.header(HttpHeaders.ACCEPT, MediaType.APPLICATION_JSON_VALUE).exchange()
.block();
int statusCode = statusCodeValueOf(response);
InstanceInfo body = response.toEntity(InstanceInfo.class).block().getBody();
return anEurekaHttpResponse(statusCode,
statusCode == HttpStatus.OK.value() && body != null ? body : null)
.headers(headersOf(response)).build();
}
@Override
public void shutdown() {
// Nothing to do
}
private static Map<String, String> headersOf(ClientResponse response) {
ClientResponse.Headers httpHeaders = response.headers();
if (httpHeaders == null) {
return Collections.emptyMap();
}
HttpHeaders asHeaders = httpHeaders.asHttpHeaders();
if (asHeaders == null) {
return Collections.emptyMap();
}
Map<String, String> headers = new HashMap<>();
asHeaders.entrySet().stream().forEach(entry -> entry.getValue().stream()
.forEach(v -> headers.put(entry.getKey(), v)));
return headers;
}
private int statusCodeValueOf(ClientResponse response) {
return response.statusCode().value();
}
private EurekaHttpResponse<Void> eurekaHttpResponse(ClientResponse response) {
return anEurekaHttpResponse(statusCodeValueOf(response))
.headers(headersOf(response)).build();
}
}
@@ -0,0 +1,59 @@
/*
* 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.netflix.eureka.http;
import java.util.Collection;
import java.util.Optional;
import javax.net.ssl.HostnameVerifier;
import javax.net.ssl.SSLContext;
import com.netflix.appinfo.InstanceInfo;
import com.netflix.discovery.EurekaClientConfig;
import com.netflix.discovery.shared.transport.TransportClientFactory;
import com.netflix.discovery.shared.transport.jersey.EurekaJerseyClient;
import com.netflix.discovery.shared.transport.jersey.TransportClientFactories;
/**
* @author Daniel Lavoie
* @author Haytham Mohamed
*/
public class WebClientTransportClientFactories implements TransportClientFactories<Void> {
@Override
public TransportClientFactory newTransportClientFactory(
Collection<Void> additionalFilters, EurekaJerseyClient providedJerseyClient) {
throw new UnsupportedOperationException();
}
@Override
public TransportClientFactory newTransportClientFactory(
EurekaClientConfig clientConfig, Collection<Void> additionalFilters,
InstanceInfo myInstanceInfo) {
return new WebClientTransportClientFactory();
}
@Override
public TransportClientFactory newTransportClientFactory(
final EurekaClientConfig clientConfig,
final Collection<Void> additionalFilters, final InstanceInfo myInstanceInfo,
final Optional<SSLContext> sslContext,
final Optional<HostnameVerifier> hostnameVerifier) {
return new WebClientTransportClientFactory();
}
}
@@ -0,0 +1,196 @@
/*
* 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.netflix.eureka.http;
import java.net.URI;
import java.net.URISyntaxException;
import com.fasterxml.jackson.databind.BeanDescription;
import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.JsonSerializer;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.PropertyNamingStrategy;
import com.fasterxml.jackson.databind.SerializationConfig;
import com.fasterxml.jackson.databind.SerializationFeature;
import com.fasterxml.jackson.databind.module.SimpleModule;
import com.fasterxml.jackson.databind.ser.BeanSerializerModifier;
import com.fasterxml.jackson.databind.ser.std.BeanSerializerBase;
import com.netflix.appinfo.InstanceInfo;
import com.netflix.discovery.converters.jackson.mixin.ApplicationsJsonMixIn;
import com.netflix.discovery.converters.jackson.mixin.InstanceInfoJsonMixIn;
import com.netflix.discovery.converters.jackson.serializer.InstanceInfoJsonBeanSerializer;
import com.netflix.discovery.shared.Applications;
import com.netflix.discovery.shared.resolver.EurekaEndpoint;
import com.netflix.discovery.shared.transport.EurekaHttpClient;
import com.netflix.discovery.shared.transport.TransportClientFactory;
import reactor.core.publisher.Mono;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.codec.json.Jackson2JsonDecoder;
import org.springframework.http.codec.json.Jackson2JsonEncoder;
import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter;
import org.springframework.web.reactive.function.client.ClientResponse;
import org.springframework.web.reactive.function.client.ExchangeFilterFunction;
import org.springframework.web.reactive.function.client.ExchangeFilterFunctions;
import org.springframework.web.reactive.function.client.ExchangeStrategies;
import org.springframework.web.reactive.function.client.WebClient;
/**
* Provides the custom {@link WebClient.Builder} required by the
* {@link WebClientEurekaHttpClient}. Relies on Jackson for serialization and
* deserialization.
*
* @author Daniel Lavoie
* @author Haytham Mohamed
*/
public class WebClientTransportClientFactory implements TransportClientFactory {
@Override
public EurekaHttpClient newClient(EurekaEndpoint serviceUrl) {
WebClient.Builder builder = of(serviceUrl.getServiceUrl());
this.setExchangeStrategies(builder);
this.skipHttp400Error(builder);
return new WebClientEurekaHttpClient(builder.build());
}
private WebClient.Builder of(String serviceUrl) {
String url = serviceUrl;
WebClient.Builder builder = WebClient.builder();
try {
URI serviceURI = new URI(serviceUrl);
if (serviceURI.getUserInfo() != null) {
String[] credentials = serviceURI.getUserInfo().split(":");
if (credentials.length == 2) {
builder.filter(ExchangeFilterFunctions
.basicAuthentication(credentials[0], credentials[1]));
url = serviceUrl.replace(credentials[0] + ":" + credentials[1] + "@",
"");
}
}
}
catch (URISyntaxException ignore) {
}
return builder.baseUrl(url);
}
private void setExchangeStrategies(WebClient.Builder builder) {
ObjectMapper objectMapper = mappingJacksonHttpMessageConverter()
.getObjectMapper();
ExchangeStrategies strategies = ExchangeStrategies.builder()
.codecs(clientDefaultCodecsConfigurer -> {
clientDefaultCodecsConfigurer.defaultCodecs()
.jackson2JsonEncoder(new Jackson2JsonEncoder(objectMapper,
MediaType.APPLICATION_JSON));
clientDefaultCodecsConfigurer.defaultCodecs()
.jackson2JsonDecoder(new Jackson2JsonDecoder(objectMapper,
MediaType.APPLICATION_JSON));
}).build();
builder.exchangeStrategies(strategies);
}
private void skipHttp400Error(WebClient.Builder builder) {
builder.filter(Http4xxErrorExchangeFilterFunction());
}
// Skip over 4xx http errors
private ExchangeFilterFunction Http4xxErrorExchangeFilterFunction() {
return ExchangeFilterFunction.ofResponseProcessor(clientResponse -> {
// literally 400 pass the tests, not 4xxClientError
if (clientResponse.statusCode().value() == 400) {
ClientResponse newResponse = ClientResponse.from(clientResponse)
.statusCode(HttpStatus.OK).build();
newResponse.body((clientHttpResponse, context) -> {
return clientHttpResponse.getBody();
});
return Mono.just(newResponse);
}
return Mono.just(clientResponse);
});
}
/**
* Provides the serialization configurations required by the Eureka Server. JSON
* content exchanged with eureka requires a root node matching the entity being
* serialized or deserialized. Achieved with
* {@link SerializationFeature#WRAP_ROOT_VALUE} and
* {@link DeserializationFeature#UNWRAP_ROOT_VALUE}.
* {@link PropertyNamingStrategy.SnakeCaseStrategy} is applied to the underlying
* {@link ObjectMapper}.
* @return a {@link MappingJackson2HttpMessageConverter} object
*/
public MappingJackson2HttpMessageConverter mappingJacksonHttpMessageConverter() {
MappingJackson2HttpMessageConverter converter = new MappingJackson2HttpMessageConverter();
converter.setObjectMapper(new ObjectMapper()
.setPropertyNamingStrategy(PropertyNamingStrategy.SNAKE_CASE));
SimpleModule jsonModule = new SimpleModule();
jsonModule.setSerializerModifier(createJsonSerializerModifier()); // keyFormatter,
// compact));
converter.getObjectMapper().registerModule(jsonModule);
converter.getObjectMapper().configure(SerializationFeature.WRAP_ROOT_VALUE, true);
converter.getObjectMapper().configure(DeserializationFeature.UNWRAP_ROOT_VALUE,
true);
converter.getObjectMapper().addMixIn(Applications.class,
ApplicationsJsonMixIn.class);
converter.getObjectMapper().addMixIn(InstanceInfo.class,
InstanceInfoJsonMixIn.class);
// converter.getObjectMapper().addMixIn(DataCenterInfo.class,
// DataCenterInfoXmlMixIn.class);
// converter.getObjectMapper().addMixIn(InstanceInfo.PortWrapper.class,
// PortWrapperXmlMixIn.class);
// converter.getObjectMapper().addMixIn(Application.class,
// ApplicationXmlMixIn.class);
// converter.getObjectMapper().addMixIn(Applications.class,
// ApplicationsXmlMixIn.class);
return converter;
}
public static BeanSerializerModifier createJsonSerializerModifier() { // final
// KeyFormatter
// keyFormatter,
// final
// boolean
// compactMode)
// {
return new BeanSerializerModifier() {
@Override
public JsonSerializer<?> modifySerializer(SerializationConfig config,
BeanDescription beanDesc, JsonSerializer<?> serializer) {
/*
* if (beanDesc.getBeanClass().isAssignableFrom(Applications.class)) {
* return new ApplicationsJsonBeanSerializer((BeanSerializerBase)
* serializer, keyFormatter); }
*/
if (beanDesc.getBeanClass().isAssignableFrom(InstanceInfo.class)) {
return new InstanceInfoJsonBeanSerializer(
(BeanSerializerBase) serializer, false);
}
return serializer;
}
};
}
@Override
public void shutdown() {
}
}
@@ -17,6 +17,12 @@
"name": "ribbon.eureka.enabled",
"description": "Enables the use of Eureka with Ribbon.",
"type": "java.lang.Boolean"
},
{
"defaultValue": false,
"name": "eureka.client.webclient.enabled",
"description": "Enables the use of WebClient for Eureka HTTP Client.",
"type": "java.lang.Boolean"
}
]
}
@@ -24,12 +24,13 @@ import org.springframework.boot.builder.SpringApplicationBuilder;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.test.context.SpringBootTest.WebEnvironment;
import org.springframework.cloud.netflix.eureka.http.RestTemplateDiscoveryClientOptionalArgs;
import org.springframework.cloud.netflix.eureka.http.WebClientDiscoveryClientOptionalArgs;
import org.springframework.cloud.netflix.eureka.sample.EurekaSampleApplication;
import org.springframework.cloud.test.ClassPathExclusions;
import org.springframework.cloud.test.ModifiedClassPathRunner;
import org.springframework.context.ConfigurableApplicationContext;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.AssertionsForInterfaceTypes.assertThat;
/**
* @author Daniel Lavoie
@@ -38,15 +39,56 @@ import static org.assertj.core.api.Assertions.assertThat;
@ClassPathExclusions({ "jersey-client-*", "jersey-core-*", "jersey-apache-client4-*" })
@SpringBootTest(classes = EurekaSampleApplication.class,
webEnvironment = WebEnvironment.RANDOM_PORT)
public class RestTemplateOptionalArgsConfigurationTest {
public class EurekaHttpClientsOptionalArgsConfigurationTest {
@Test
public void contextLoads() {
public void contextLoadsWithRestTemplate() {
try (ConfigurableApplicationContext context = new SpringApplicationBuilder()
.web(WebApplicationType.NONE).sources(EurekaSampleApplication.class)
.properties(new String[] { "eureka.client.webclient.enabled=false" })
.run()) {
assertThat(context.getBean(RestTemplateDiscoveryClientOptionalArgs.class))
.isNotNull();
try {
Object bean = context.getBean(WebClientDiscoveryClientOptionalArgs.class);
assertThat(bean).isNull();
}
catch (Exception ex) {
}
}
}
@Test
public void contextLoadsWithWebClient() {
try (ConfigurableApplicationContext context = new SpringApplicationBuilder()
.web(WebApplicationType.NONE).sources(EurekaSampleApplication.class)
.properties(new String[] { "eureka.client.webclient.enabled=true" })
.run()) {
assertThat(context.getBean(WebClientDiscoveryClientOptionalArgs.class))
.isNotNull();
try {
Object bean = context
.getBean(RestTemplateDiscoveryClientOptionalArgs.class);
assertThat(bean).isNull();
}
catch (Exception ex) {
}
}
}
@Test
public void contextLoadsWithRestTemplateAsDefault() {
try (ConfigurableApplicationContext context = new SpringApplicationBuilder()
.web(WebApplicationType.NONE).sources(EurekaSampleApplication.class)
.run()) {
assertThat(context.getBean(RestTemplateDiscoveryClientOptionalArgs.class))
.isNotNull();
try {
Object bean = context.getBean(WebClientDiscoveryClientOptionalArgs.class);
assertThat(bean).isNull();
}
catch (Exception ex) {
}
}
}
@@ -0,0 +1,112 @@
/*
* Copyright 2012-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.netflix.eureka.http;
import com.netflix.appinfo.InstanceInfo;
import com.netflix.discovery.shared.Applications;
import com.netflix.discovery.shared.transport.EurekaHttpClient;
import org.junit.Test;
import org.springframework.http.HttpStatus;
import static org.assertj.core.api.Assertions.assertThat;
/**
* @author Haytham Mohamed
**/
public abstract class AbstractEurekaHttpClientTest {
protected EurekaHttpClient eurekaHttpClient;
protected InstanceInfo info;
abstract public void setup();
@Test
public void testRegister() {
assertThat(eurekaHttpClient.register(info).getStatusCode())
.isEqualTo(HttpStatus.OK.value());
}
@Test
public void testCancel() {
assertThat(eurekaHttpClient.cancel("test", "test").getStatusCode())
.isEqualTo(HttpStatus.OK.value());
}
@Test
public void testSendHeartBeat() {
assertThat(eurekaHttpClient.sendHeartBeat("test", "test", info, null)
.getStatusCode()).isEqualTo(HttpStatus.OK.value());
}
@Test
public void testSendHeartBeatFourOFour() {
assertThat(eurekaHttpClient.sendHeartBeat("fourOFour", "test", info, null)
.getStatusCode()).isEqualTo(HttpStatus.NOT_FOUND.value());
}
@Test
public void testStatusUpdate() {
assertThat(eurekaHttpClient
.statusUpdate("test", "test", InstanceInfo.InstanceStatus.UP, info)
.getStatusCode()).isEqualTo(HttpStatus.OK.value());
}
@Test
public void testDeleteStatusOverride() {
assertThat(eurekaHttpClient.deleteStatusOverride("test", "test", info)
.getStatusCode()).isEqualTo(HttpStatus.OK.value());
}
@Test
public void testGetApplications() {
Applications entity = eurekaHttpClient.getApplications().getEntity();
assertThat(entity).isNotNull();
assertThat(eurekaHttpClient.getApplications("us", "eu").getEntity()).isNotNull();
}
@Test
public void testGetDelta() {
eurekaHttpClient.getDelta().getEntity();
eurekaHttpClient.getDelta("us", "eu").getEntity();
}
@Test
public void testGetVips() {
eurekaHttpClient.getVip("test");
eurekaHttpClient.getVip("test", "us", "eu");
}
@Test
public void testGetSecureVip() {
eurekaHttpClient.getSecureVip("test");
eurekaHttpClient.getSecureVip("test", "us", "eu");
}
@Test
public void testGetApplication() {
eurekaHttpClient.getApplication("test");
}
@Test
public void testGetInstance() {
eurekaHttpClient.getInstance("test");
eurekaHttpClient.getInstance("test", "test");
}
}
@@ -16,14 +16,9 @@
package org.springframework.cloud.netflix.eureka.http;
import com.netflix.appinfo.InstanceInfo;
import com.netflix.appinfo.InstanceInfo.InstanceStatus;
import com.netflix.appinfo.providers.EurekaConfigBasedInstanceInfoProvider;
import com.netflix.discovery.shared.Applications;
import com.netflix.discovery.shared.resolver.DefaultEndpoint;
import com.netflix.discovery.shared.transport.EurekaHttpClient;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
@@ -32,12 +27,9 @@ import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.test.context.SpringBootTest.WebEnvironment;
import org.springframework.cloud.commons.util.InetUtils;
import org.springframework.cloud.netflix.eureka.EurekaInstanceConfigBean;
import org.springframework.http.HttpStatus;
import org.springframework.test.annotation.DirtiesContext;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import static org.assertj.core.api.Assertions.assertThat;
/**
* @author Daniel Lavoie
*/
@@ -46,7 +38,7 @@ import static org.assertj.core.api.Assertions.assertThat;
properties = { "debug=true", "security.basic.enabled=true" },
webEnvironment = WebEnvironment.RANDOM_PORT)
@DirtiesContext
public class RestTemplateEurekaHttpClientTest {
public class RestTemplateEurekaHttpClientTest extends AbstractEurekaHttpClientTest {
@Autowired
private InetUtils inetUtils;
@@ -54,10 +46,6 @@ public class RestTemplateEurekaHttpClientTest {
@Value("http://${security.user.name}:${security.user.password}@localhost:${local.server.port}")
private String serviceUrl;
private EurekaHttpClient eurekaHttpClient;
private InstanceInfo info;
@Before
public void setup() {
eurekaHttpClient = new RestTemplateTransportClientFactory()
@@ -78,76 +66,4 @@ public class RestTemplateEurekaHttpClientTest {
info = new EurekaConfigBasedInstanceInfoProvider(config).get();
}
@Test
public void testRegister() {
assertThat(eurekaHttpClient.register(info).getStatusCode())
.isEqualTo(HttpStatus.OK.value());
}
@Test
public void testCancel() {
assertThat(eurekaHttpClient.cancel("test", "test").getStatusCode())
.isEqualTo(HttpStatus.OK.value());
}
@Test
public void testSendHeartBeat() {
assertThat(eurekaHttpClient.sendHeartBeat("test", "test", info, null)
.getStatusCode()).isEqualTo(HttpStatus.OK.value());
}
@Test
public void testSendHeartBeatFourOFour() {
assertThat(eurekaHttpClient.sendHeartBeat("fourOFour", "test", info, null)
.getStatusCode()).isEqualTo(HttpStatus.NOT_FOUND.value());
}
@Test
public void testStatusUpdate() {
assertThat(eurekaHttpClient.statusUpdate("test", "test", InstanceStatus.UP, info)
.getStatusCode()).isEqualTo(HttpStatus.OK.value());
}
@Test
public void testDeleteStatusOverride() {
assertThat(eurekaHttpClient.deleteStatusOverride("test", "test", info)
.getStatusCode()).isEqualTo(HttpStatus.OK.value());
}
@Test
public void testGetApplications() {
Applications entity = eurekaHttpClient.getApplications().getEntity();
assertThat(entity).isNotNull();
assertThat(eurekaHttpClient.getApplications("us", "eu").getEntity()).isNotNull();
}
@Test
public void testGetDelta() {
eurekaHttpClient.getDelta().getEntity();
eurekaHttpClient.getDelta("us", "eu").getEntity();
}
@Test
public void testGetVips() {
eurekaHttpClient.getVip("test");
eurekaHttpClient.getVip("test", "us", "eu");
}
@Test
public void testGetSecureVip() {
eurekaHttpClient.getSecureVip("test");
eurekaHttpClient.getSecureVip("test", "us", "eu");
}
@Test
public void testGetApplication() {
eurekaHttpClient.getApplication("test");
}
@Test
public void testGetInstance() {
eurekaHttpClient.getInstance("test");
eurekaHttpClient.getInstance("test", "test");
}
}
@@ -0,0 +1,70 @@
/*
* 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.netflix.eureka.http;
import com.netflix.appinfo.providers.EurekaConfigBasedInstanceInfoProvider;
import com.netflix.discovery.shared.resolver.DefaultEndpoint;
import org.junit.Before;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.test.context.SpringBootTest.WebEnvironment;
import org.springframework.cloud.commons.util.InetUtils;
import org.springframework.cloud.netflix.eureka.EurekaInstanceConfigBean;
import org.springframework.test.annotation.DirtiesContext;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
/**
* @author Daniel Lavoie
*/
@RunWith(SpringJUnit4ClassRunner.class)
@SpringBootTest(classes = EurekaServerMockApplication.class,
properties = { "debug=true", "security.basic.enabled=true",
"eureka.client.webclient.enabled=true" },
webEnvironment = WebEnvironment.RANDOM_PORT)
@DirtiesContext
public class WebClientEurekaHttpClientTest extends AbstractEurekaHttpClientTest {
@Autowired
private InetUtils inetUtils;
@Value("http://${security.user.name}:${security.user.password}@localhost:${local.server.port}")
private String serviceUrl;
@Before
public void setup() {
eurekaHttpClient = new WebClientTransportClientFactory()
.newClient(new DefaultEndpoint(serviceUrl));
EurekaInstanceConfigBean config = new EurekaInstanceConfigBean(inetUtils);
String appname = "customapp";
config.setIpAddress("127.0.0.1");
config.setHostname("localhost");
config.setAppname(appname);
config.setVirtualHostName(appname);
config.setSecureVirtualHostName(appname);
config.setNonSecurePort(4444);
config.setSecurePort(8443);
config.setInstanceId("127.0.0.1:customapp:4444");
info = new EurekaConfigBasedInstanceInfoProvider(config).get();
}
}
@@ -0,0 +1,31 @@
/*
* 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.netflix.eureka.http;
import org.junit.Test;
/**
* @author Daniel Lavoie
*/
public class WebClientTransportClientFactoriesTest {
@Test(expected = UnsupportedOperationException.class)
public void testJerseyIsUnsuported() {
new WebClientTransportClientFactories().newTransportClientFactory(null, null);
}
}
@@ -0,0 +1,58 @@
/*
* 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.netflix.eureka.http;
import com.netflix.discovery.shared.resolver.DefaultEndpoint;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
/**
* @author Daniel Lavoie
*/
public class WebClientTransportClientFactoryTest {
private WebClientTransportClientFactory transportClientFatory;
@Before
public void setup() {
transportClientFatory = new WebClientTransportClientFactory();
}
@Test
public void testWithoutUserInfo() {
transportClientFatory.newClient(new DefaultEndpoint("http://localhost:8761"));
}
@Test
public void testInvalidUserInfo() {
transportClientFatory
.newClient(new DefaultEndpoint("http://test@localhost:8761"));
}
@Test
public void testUserInfo() {
transportClientFatory
.newClient(new DefaultEndpoint("http://test:test@localhost:8761"));
}
@After
public void shutdown() {
transportClientFatory.shutdown();
}
}