diff --git a/docs/src/main/asciidoc/spring-cloud-netflix.adoc b/docs/src/main/asciidoc/spring-cloud-netflix.adoc index eeb10eef1..fbbf31147 100644 --- a/docs/src/main/asciidoc/spring-cloud-netflix.adoc +++ b/docs/src/main/asciidoc/spring-cloud-netflix.adoc @@ -84,18 +84,21 @@ NOTE: Because of a limitation in Eureka, it is not possible to support per-serve === Status Page and Health Indicator The status page and health indicators for a Eureka instance default to `/info` and `/health` respectively, which are the default locations of useful endpoints in a Spring Boot Actuator application. -You need to change these, even for an Actuator application if you use a non-default context path or servlet path (such as `server.servletPath=/custom`) or management endpoint path (such as `management.contextPath=/admin`). The following example shows the default values for the two settings: +You need to change these, even for an Actuator application if you use a non-default context path or servlet path (such as `server.servletPath=/custom`). The following example shows the default values for the two settings: .application.yml ---- eureka: instance: - statusPageUrlPath: ${management.server.servlet.context-path}/info - healthCheckUrlPath: ${management.server.servlet.context-path}/health + statusPageUrlPath: ${server.servletPath}/info + healthCheckUrlPath: ${server.servletPath}/health ---- These links show up in the metadata that is consumed by clients and are used in some scenarios to decide whether to send requests to your application, so it is helpful if they are accurate. +NOTE: In Dalston it was also required to set the status and health check URLs when changing +that management context path. This requirement was removed beginning in Edgware. + === Registering a Secure Application If your app wants to be contacted over HTTPS, you can set two flags in the `EurekaInstanceConfig`: @@ -747,6 +750,24 @@ Circuits are prefixed by their respective `serviceId`, followed by a dot (`.`), Spring Cloud provides a `spring-cloud-starter-netflix-turbine-stream` that has all the dependencies you need to get a Turbine Stream server running. You can then add the Stream binder of your choice -- such as `spring-cloud-starter-stream-rabbit`. +Turbine Stream server also supports the `cluster` parameter. +Unlike Turbine server, Turbine Stream uses eureka serviceIds as cluster names and these are not configurable. + +If Turbine Stream server is running on port 8989 on `my.turbine.server` and you have two eureka serviceIds `customers` and `products` in your environment, the following URLs will be available on your Turbine Stream server. `default` and empty cluster name will provide all metrics that Turbine Stream server receives. + +---- +http://my.turbine.sever:8989/turbine.stream?cluster=customers +http://my.turbine.sever:8989/turbine.stream?cluster=products +http://my.turbine.sever:8989/turbine.stream?cluster=default +http://my.turbine.sever:8989/turbine.stream +---- + +So, you can use eureka serviceIds as cluster names for your Turbine dashboard (or any compatible dashboard). +You don’t need to configure any properties like `turbine.appConfig`, `turbine.clusterNameExpression` and `turbine.aggregator.clusterConfig` for your Turbine Stream server. + +NOTE: Turbine Stream server gathers all metrics from the configured input channel with Spring Cloud Stream. It means that it doesn’t gather Hystrix metrics actively from each instance. It just can provide metrics that were already gathered into the input channel by each instance. + + [[spring-cloud-ribbon]] == Client Side Load Balancer: Ribbon diff --git a/spring-cloud-netflix-eureka-client/src/main/java/org/springframework/cloud/netflix/eureka/EurekaClientAutoConfiguration.java b/spring-cloud-netflix-eureka-client/src/main/java/org/springframework/cloud/netflix/eureka/EurekaClientAutoConfiguration.java index b9e33a1d6..c145586ff 100644 --- a/spring-cloud-netflix-eureka-client/src/main/java/org/springframework/cloud/netflix/eureka/EurekaClientAutoConfiguration.java +++ b/spring-cloud-netflix-eureka-client/src/main/java/org/springframework/cloud/netflix/eureka/EurekaClientAutoConfiguration.java @@ -23,8 +23,6 @@ import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; import java.util.Map; -import org.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; import org.springframework.beans.factory.ObjectProvider; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.actuate.autoconfigure.health.ConditionalOnEnabledHealthIndicator; @@ -206,21 +204,22 @@ public class EurekaClientAutoConfiguration { return new EurekaServiceRegistry(); } - @Bean - @ConditionalOnBean(AutoServiceRegistrationProperties.class) - @ConditionalOnProperty(value = "spring.cloud.service-registry.auto-registration.enabled", matchIfMissing = true) - public EurekaRegistration eurekaRegistration(EurekaClient eurekaClient, CloudEurekaInstanceConfig instanceConfig, ApplicationInfoManager applicationInfoManager, ObjectProvider healthCheckHandler) { - return EurekaRegistration.builder(instanceConfig) - .with(applicationInfoManager) - .with(eurekaClient) - .with(healthCheckHandler) - .build(); - } +// @Bean +// @ConditionalOnBean(AutoServiceRegistrationProperties.class) +// @ConditionalOnProperty(value = "spring.cloud.service-registry.auto-registration.enabled", matchIfMissing = true) +// public EurekaRegistration eurekaRegistration(EurekaClient eurekaClient, CloudEurekaInstanceConfig instanceConfig, ApplicationInfoManager applicationInfoManager, ObjectProvider healthCheckHandler) { +// return EurekaRegistration.builder(instanceConfig) +// .with(applicationInfoManager) +// .with(eurekaClient) +// .with(healthCheckHandler) +// .build(); +// } @Bean @ConditionalOnBean(AutoServiceRegistrationProperties.class) @ConditionalOnProperty(value = "spring.cloud.service-registry.auto-registration.enabled", matchIfMissing = true) - public EurekaAutoServiceRegistration eurekaAutoServiceRegistration(ApplicationContext context, EurekaServiceRegistry registry, EurekaRegistration registration) { + public EurekaAutoServiceRegistration eurekaAutoServiceRegistration(ApplicationContext context, EurekaServiceRegistry registry, + EurekaRegistration registration) { return new EurekaAutoServiceRegistration(context, registry, registration); } @@ -248,6 +247,20 @@ public class EurekaClientAutoConfiguration { InstanceInfo instanceInfo = new InstanceInfoFactory().create(config); return new ApplicationInfoManager(config, instanceInfo); } + + @Bean + @ConditionalOnBean(AutoServiceRegistrationProperties.class) + @ConditionalOnProperty(value = "spring.cloud.service-registry.auto-registration.enabled", matchIfMissing = true) + public EurekaRegistration eurekaRegistration(EurekaClient eurekaClient, + CloudEurekaInstanceConfig instanceConfig, + ApplicationInfoManager applicationInfoManager, + @Autowired(required = false) ObjectProvider healthCheckHandler) { + return EurekaRegistration.builder(instanceConfig) + .with(applicationInfoManager) + .with(eurekaClient) + .with(healthCheckHandler) + .build(); + } } @Configuration @@ -279,6 +292,21 @@ public class EurekaClientAutoConfiguration { return new ApplicationInfoManager(config, instanceInfo); } + @Bean + @org.springframework.cloud.context.config.annotation.RefreshScope + @ConditionalOnBean(AutoServiceRegistrationProperties.class) + @ConditionalOnProperty(value = "spring.cloud.service-registry.auto-registration.enabled", matchIfMissing = true) + public EurekaRegistration eurekaRegistration(EurekaClient eurekaClient, + CloudEurekaInstanceConfig instanceConfig, + ApplicationInfoManager applicationInfoManager, + @Autowired(required = false) ObjectProvider healthCheckHandler) { + return EurekaRegistration.builder(instanceConfig) + .with(applicationInfoManager) + .with(eurekaClient) + .with(healthCheckHandler) + .build(); + } + } @Target({ ElementType.TYPE, ElementType.METHOD }) diff --git a/spring-cloud-netflix-eureka-client/src/main/java/org/springframework/cloud/netflix/eureka/http/RestTemplateTransportClientFactory.java b/spring-cloud-netflix-eureka-client/src/main/java/org/springframework/cloud/netflix/eureka/http/RestTemplateTransportClientFactory.java index 9258a3116..ffb181de0 100644 --- a/spring-cloud-netflix-eureka-client/src/main/java/org/springframework/cloud/netflix/eureka/http/RestTemplateTransportClientFactory.java +++ b/spring-cloud-netflix-eureka-client/src/main/java/org/springframework/cloud/netflix/eureka/http/RestTemplateTransportClientFactory.java @@ -19,8 +19,10 @@ package org.springframework.cloud.netflix.eureka.http; import java.net.URI; import java.net.URISyntaxException; +import org.springframework.http.HttpStatus; import org.springframework.http.client.support.BasicAuthorizationInterceptor; import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter; +import org.springframework.web.client.DefaultResponseErrorHandler; import org.springframework.web.client.RestTemplate; import com.fasterxml.jackson.databind.BeanDescription; @@ -74,6 +76,7 @@ public class RestTemplateTransportClientFactory implements TransportClientFactor } restTemplate.getMessageConverters().add(0, mappingJacksonHttpMessageConverter()); + restTemplate.setErrorHandler(new ErrorHanlder()); return restTemplate; } @@ -134,4 +137,20 @@ public class RestTemplateTransportClientFactory implements TransportClientFactor public void shutdown() { } + class ErrorHanlder extends DefaultResponseErrorHandler { + @Override + protected boolean hasError(HttpStatus statusCode) { + /** + * When the Eureka server restarts and a client tries to sent a heartbeat the server + * will respond with a 404. By default RestTemplate will throw an exception in this case. + * What we want is to return the 404 to the upstream code so it will send another registration + * request to the server. + */ + if(statusCode.is4xxClientError()) { + return false; + } + return super.hasError(statusCode); + } + } + } diff --git a/spring-cloud-netflix-eureka-client/src/main/java/org/springframework/cloud/netflix/eureka/metadata/DefaultManagementMetadataProvider.java b/spring-cloud-netflix-eureka-client/src/main/java/org/springframework/cloud/netflix/eureka/metadata/DefaultManagementMetadataProvider.java index 2dc5c623f..745c8c1e7 100644 --- a/spring-cloud-netflix-eureka-client/src/main/java/org/springframework/cloud/netflix/eureka/metadata/DefaultManagementMetadataProvider.java +++ b/spring-cloud-netflix-eureka-client/src/main/java/org/springframework/cloud/netflix/eureka/metadata/DefaultManagementMetadataProvider.java @@ -40,7 +40,7 @@ public class DefaultManagementMetadataProvider implements ManagementMetadataProv return port != null && port == RANDOM_PORT; } - private String getHealthCheckUrl(EurekaInstanceConfigBean instance, int serverPort, String serverContextPath, + protected String getHealthCheckUrl(EurekaInstanceConfigBean instance, int serverPort, String serverContextPath, String managementContextPath, Integer managementPort, boolean isSecure) { String healthCheckUrlPath = instance.getHealthCheckUrlPath(); String healthCheckUrl = getUrl(instance, serverPort, serverContextPath, managementContextPath, diff --git a/spring-cloud-netflix-eureka-client/src/main/java/org/springframework/cloud/netflix/eureka/serviceregistry/EurekaRegistration.java b/spring-cloud-netflix-eureka-client/src/main/java/org/springframework/cloud/netflix/eureka/serviceregistry/EurekaRegistration.java index 76e0b9a69..7d212130f 100644 --- a/spring-cloud-netflix-eureka-client/src/main/java/org/springframework/cloud/netflix/eureka/serviceregistry/EurekaRegistration.java +++ b/spring-cloud-netflix-eureka-client/src/main/java/org/springframework/cloud/netflix/eureka/serviceregistry/EurekaRegistration.java @@ -45,7 +45,7 @@ import com.netflix.discovery.EurekaClientConfig; /** * @author Spencer Gibb */ -public class EurekaRegistration implements Registration, Closeable { +public class EurekaRegistration implements Registration { private static final Log log = LogFactory.getLog(EurekaRegistration.class); private final EurekaClient eurekaClient; @@ -202,8 +202,4 @@ public class EurekaRegistration implements Registration, Closeable { return this.instanceConfig.getSecurePort(); } - @Override - public void close() throws IOException { - this.eurekaClient.shutdown(); - } } diff --git a/spring-cloud-netflix-eureka-client/src/test/java/org/springframework/cloud/netflix/eureka/EurekaClientAutoConfigurationTests.java b/spring-cloud-netflix-eureka-client/src/test/java/org/springframework/cloud/netflix/eureka/EurekaClientAutoConfigurationTests.java index 32cb9cd64..e2f6c4e87 100644 --- a/spring-cloud-netflix-eureka-client/src/test/java/org/springframework/cloud/netflix/eureka/EurekaClientAutoConfigurationTests.java +++ b/spring-cloud-netflix-eureka-client/src/test/java/org/springframework/cloud/netflix/eureka/EurekaClientAutoConfigurationTests.java @@ -16,14 +16,14 @@ package org.springframework.cloud.netflix.eureka; -import java.io.IOException; -import java.util.concurrent.CountDownLatch; import java.util.HashMap; import java.util.Map; - +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.atomic.AtomicBoolean; import org.junit.After; import org.junit.Test; import org.mockito.Mockito; +import org.springframework.aop.framework.Advised; import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; import org.springframework.boot.autoconfigure.condition.SearchStrategy; import org.springframework.boot.autoconfigure.context.PropertyPlaceholderAutoConfiguration; @@ -31,9 +31,10 @@ import org.springframework.boot.context.properties.EnableConfigurationProperties import org.springframework.boot.context.properties.source.ConfigurationPropertySources; import org.springframework.boot.test.util.TestPropertyValues; import org.springframework.cloud.autoconfigure.RefreshAutoConfiguration; +import org.springframework.cloud.client.serviceregistry.AutoServiceRegistrationProperties; import org.springframework.cloud.commons.util.UtilAutoConfiguration; +import org.springframework.cloud.context.refresh.ContextRefresher; import org.springframework.cloud.context.scope.GenericScope; -import org.springframework.cloud.netflix.eureka.serviceregistry.EurekaRegistration; import org.springframework.context.ApplicationContext; import org.springframework.context.annotation.AnnotationConfigApplicationContext; import org.springframework.context.annotation.Bean; @@ -42,8 +43,9 @@ import org.springframework.context.annotation.Import; import org.springframework.core.env.ConfigurableEnvironment; import org.springframework.core.env.MutablePropertySources; import org.springframework.core.env.SystemEnvironmentPropertySource; - +import org.springframework.test.util.ReflectionTestUtils; import com.netflix.appinfo.ApplicationInfoManager; +import com.netflix.appinfo.HealthCheckHandler; import com.netflix.discovery.EurekaClient; import com.netflix.discovery.EurekaClientConfig; import com.netflix.discovery.shared.transport.jersey.EurekaJerseyClient; @@ -52,8 +54,6 @@ import com.sun.jersey.client.apache4.ApacheHttpClient4; import static org.assertj.core.api.Assertions.assertThat; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertTrue; -import static org.mockito.Mockito.spy; -import static org.mockito.Mockito.verify; import static org.springframework.boot.test.util.EnvironmentTestUtils.addEnvironment; /** @@ -377,6 +377,43 @@ public class EurekaClientAutoConfigurationTests { .startsWith(GenericScope.class.getName()+"$LockedScopedProxyFactoryBean"); } + @Test + public void shouldReregisterHealthCheckHandlerAfterRefresh() throws Exception { + addEnvironment(this.context, "eureka.client.healthcheck.enabled=true"); + setupContext(RefreshAutoConfiguration.class, AutoServiceRegistrationConfiguration.class); + + EurekaClient oldEurekaClient = getLazyInitEurekaClient(); + + HealthCheckHandler healthCheckHandler = this.context.getBean("eurekaHealthCheckHandler", HealthCheckHandler.class); + + assertThat(healthCheckHandler).isInstanceOf(EurekaHealthCheckHandler.class); + assertThat(oldEurekaClient.getHealthCheckHandler()).isSameAs(healthCheckHandler); + + ContextRefresher refresher = this.context.getBean("contextRefresher", ContextRefresher.class); + refresher.refresh(); + + EurekaClient newEurekaClient = getLazyInitEurekaClient(); + HealthCheckHandler newHealthCheckHandler = this.context.getBean("eurekaHealthCheckHandler", HealthCheckHandler.class); + + assertThat(healthCheckHandler).isSameAs(newHealthCheckHandler); + assertThat(oldEurekaClient).isNotSameAs(newEurekaClient); + assertThat(newEurekaClient.getHealthCheckHandler()).isSameAs(healthCheckHandler); + } + + @Test + public void shouldCloseDiscoveryClient() throws Exception { + addEnvironment(this.context, "eureka.client.healthcheck.enabled=true"); + setupContext(RefreshAutoConfiguration.class, AutoServiceRegistrationConfiguration.class); + + AtomicBoolean isShutdown = (AtomicBoolean) ReflectionTestUtils.getField(getLazyInitEurekaClient(), "isShutdown"); + + assertThat(isShutdown.get()).isFalse(); + + this.context.close(); + + assertThat(isShutdown.get()).isTrue(); + } + @Test public void basicAuth() { TestPropertyValues.of( "server.port=8989", @@ -470,16 +507,6 @@ public class EurekaClientAutoConfigurationTests { } } - @Test - public void eurekaRegistrationClosed() throws IOException { - setupContext(TestEurekaRegistrationConfiguration.class); - if (this.context != null) { - EurekaRegistration registration = this.context.getBean(EurekaRegistration.class); - this.context.close(); - verify(registration).close(); - } - } - private void testNonSecurePortSystemProp(String propName) { addSystemEnvironment(this.context.getEnvironment(), propName + ":8888"); setupContext(); @@ -502,6 +529,10 @@ public class EurekaClientAutoConfigurationTests { return this.context.getBean(EurekaInstanceConfigBean.class); } + private EurekaClient getLazyInitEurekaClient() throws Exception { + return (EurekaClient)((Advised) this.context.getBean("eurekaClient", EurekaClient.class)).getTargetSource().getTarget(); + } + @Configuration @EnableConfigurationProperties @Import({ UtilAutoConfiguration.class, EurekaClientAutoConfiguration.class }) @@ -531,18 +562,6 @@ public class EurekaClientAutoConfigurationTests { } } - @Configuration - protected static class TestEurekaRegistrationConfiguration { - - @Bean - public EurekaRegistration eurekaRegistration(EurekaClient eurekaClient, CloudEurekaInstanceConfig instanceConfig, ApplicationInfoManager applicationInfoManager) { - return spy(EurekaRegistration.builder(instanceConfig) - .with(applicationInfoManager) - .with(eurekaClient) - .build()); - } - } - @Configuration protected static class MockClientConfiguration { @@ -565,4 +584,10 @@ public class EurekaClientAutoConfigurationTests { return Mockito.mock(ApacheHttpClient4.class); } } + + @Configuration + @EnableConfigurationProperties(AutoServiceRegistrationProperties.class) + public static class AutoServiceRegistrationConfiguration { + + } } diff --git a/spring-cloud-netflix-eureka-client/src/test/java/org/springframework/cloud/netflix/eureka/http/EurekaServerMockApplication.java b/spring-cloud-netflix-eureka-client/src/test/java/org/springframework/cloud/netflix/eureka/http/EurekaServerMockApplication.java index 373f90be9..8f528e79b 100644 --- a/spring-cloud-netflix-eureka-client/src/test/java/org/springframework/cloud/netflix/eureka/http/EurekaServerMockApplication.java +++ b/spring-cloud-netflix-eureka-client/src/test/java/org/springframework/cloud/netflix/eureka/http/EurekaServerMockApplication.java @@ -32,6 +32,7 @@ import org.springframework.context.annotation.Configuration; import org.springframework.core.Ordered; import org.springframework.core.annotation.Order; import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter; import org.springframework.security.config.annotation.web.builders.HttpSecurity; import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter; @@ -132,13 +133,16 @@ public class EurekaServerMockApplication { @ResponseStatus(HttpStatus.OK) @PutMapping(value = "/apps/{appName}/{id}", params = { "status", "lastDirtyTimestamp" }) - public InstanceInfo sendHeartBeat(@PathVariable String appName, - @PathVariable String id, @RequestParam String status, - @RequestParam String lastDirtyTimestamp, - @RequestParam(required = false) String overriddenstatus) { - return new InstanceInfo(null, null, null, null, null, null, null, null, null, + public ResponseEntity sendHeartBeat(@PathVariable String appName, + @PathVariable String id, @RequestParam String status, + @RequestParam String lastDirtyTimestamp, + @RequestParam(required = false) String overriddenstatus) { + if("fourOFour".equals(appName)) { + return new ResponseEntity(HttpStatus.NOT_FOUND); + } + return new ResponseEntity(new InstanceInfo(null, null, null, null, null, null, null, null, null, null, null, null, null, 0, null, null, null, null, null, null, null, new HashMap(), 0l, - 0l, null, null); + 0l, null, null), HttpStatus.OK); } @ResponseStatus(HttpStatus.OK) diff --git a/spring-cloud-netflix-eureka-client/src/test/java/org/springframework/cloud/netflix/eureka/http/RestTemplateEurekaHttpClientTest.java b/spring-cloud-netflix-eureka-client/src/test/java/org/springframework/cloud/netflix/eureka/http/RestTemplateEurekaHttpClientTest.java index 164076b84..3f20a92f5 100644 --- a/spring-cloud-netflix-eureka-client/src/test/java/org/springframework/cloud/netflix/eureka/http/RestTemplateEurekaHttpClientTest.java +++ b/spring-cloud-netflix-eureka-client/src/test/java/org/springframework/cloud/netflix/eureka/http/RestTemplateEurekaHttpClientTest.java @@ -92,6 +92,12 @@ public class RestTemplateEurekaHttpClientTest { .sendHeartBeat("test", "test", info, null).getStatusCode()); } + @Test + public void testSendHeartBeatFourOFour() { + Assert.assertEquals(HttpStatus.NOT_FOUND.value(), eurekaHttpClient + .sendHeartBeat("fourOFour", "test", info, null).getStatusCode()); + } + @Test public void testStatusUpdate() { Assert.assertEquals(HttpStatus.OK.value(), eurekaHttpClient diff --git a/spring-cloud-netflix-sidecar/src/main/java/org/springframework/cloud/netflix/sidecar/SidecarConfiguration.java b/spring-cloud-netflix-sidecar/src/main/java/org/springframework/cloud/netflix/sidecar/SidecarConfiguration.java index 4efd3b4a4..096d0cc09 100644 --- a/spring-cloud-netflix-sidecar/src/main/java/org/springframework/cloud/netflix/sidecar/SidecarConfiguration.java +++ b/spring-cloud-netflix-sidecar/src/main/java/org/springframework/cloud/netflix/sidecar/SidecarConfiguration.java @@ -20,13 +20,16 @@ import static org.springframework.cloud.commons.util.IdUtils.getDefaultInstanceI import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; -import org.springframework.boot.autoconfigure.AutoConfigureAfter; import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; +import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; import org.springframework.boot.context.properties.EnableConfigurationProperties; import org.springframework.cloud.client.actuator.HasFeatures; import org.springframework.cloud.commons.util.InetUtils; import org.springframework.cloud.netflix.eureka.EurekaInstanceConfigBean; +import org.springframework.cloud.netflix.eureka.metadata.DefaultManagementMetadataProvider; +import org.springframework.cloud.netflix.eureka.metadata.ManagementMetadata; +import org.springframework.cloud.netflix.eureka.metadata.ManagementMetadataProvider; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.core.env.ConfigurableEnvironment; @@ -35,8 +38,7 @@ import org.springframework.util.StringUtils; import com.netflix.appinfo.HealthCheckHandler; import com.netflix.discovery.EurekaClientConfig; -import java.net.InetAddress; -import java.net.UnknownHostException; +import java.util.Map; /** * Sidecar Configuration that setting up {@link com.netflix.appinfo.EurekaInstanceConfig}. @@ -79,8 +81,17 @@ public class SidecarConfiguration { @Autowired private InetUtils inetUtils; - @Value("${management.port:${MANAGEMENT_PORT:${server.port:${SERVER_PORT:${PORT:8080}}}}}") - private int managementPort = 8080; + @Value(value = "${management.port:${MANAGEMENT_PORT:#{null}}}") + private Integer managementPort; + + @Value("${server.port:${SERVER_PORT:${PORT:8080}}}") + private int serverPort = 8080; + + @Value("${management.context-path:${MANAGEMENT_CONTEXT_PATH:#{null}}}") + private String managementContextPath; + + @Value("${server.context-path:${SERVER_CONTEXT_PATH:/}}") + private String serverContextPath = "/"; @Value("${eureka.instance.hostname:${EUREKA_INSTANCE_HOSTNAME:}}") private String hostname; @@ -89,7 +100,13 @@ public class SidecarConfiguration { private ConfigurableEnvironment env; @Bean - public EurekaInstanceConfigBean eurekaInstanceConfigBean() { + @ConditionalOnMissingBean + public ManagementMetadataProvider serviceManagementMetadataProvider() { + return new DefaultManagementMetadataProvider(); + } + + @Bean + public EurekaInstanceConfigBean eurekaInstanceConfigBean(ManagementMetadataProvider managementMetadataProvider) { EurekaInstanceConfigBean config = new EurekaInstanceConfigBean(inetUtils); String springAppName = this.env.getProperty("spring.application.name", ""); int port = this.sidecarProperties.getPort(); @@ -112,10 +129,20 @@ public class SidecarConfiguration { config.setIpAddress(ipAddress); } String scheme = config.getSecurePortEnabled() ? "https" : "http"; - config.setStatusPageUrl(scheme + "://" + config.getHostname() + ":" - + this.managementPort + config.getStatusPageUrlPath()); - config.setHealthCheckUrl(scheme + "://" + config.getHostname() + ":" - + this.managementPort + config.getHealthCheckUrlPath()); + ManagementMetadata metadata = managementMetadataProvider.get(config, serverPort, + serverContextPath, managementContextPath, managementPort); + + if(metadata != null) { + config.setStatusPageUrl(metadata.getStatusPageUrl()); + config.setHealthCheckUrl(metadata.getHealthCheckUrl()); + if(config.isSecurePortEnabled()) { + config.setSecureHealthCheckUrl(metadata.getSecureHealthCheckUrl()); + } + Map metadataMap = config.getMetadataMap(); + if (metadataMap.get("management.port") == null) { + metadataMap.put("management.port", String.valueOf(metadata.getManagementPort())); + } + } config.setHomePageUrl(scheme + "://" + config.getHostname() + ":" + port + config.getHomePageUrlPath()); return config; diff --git a/spring-cloud-netflix-sidecar/src/test/java/org/springframework/cloud/netflix/sidecar/SidecarApplication.java b/spring-cloud-netflix-sidecar/src/test/java/org/springframework/cloud/netflix/sidecar/SidecarApplication.java index 63fe51291..27fe3f660 100644 --- a/spring-cloud-netflix-sidecar/src/test/java/org/springframework/cloud/netflix/sidecar/SidecarApplication.java +++ b/spring-cloud-netflix-sidecar/src/test/java/org/springframework/cloud/netflix/sidecar/SidecarApplication.java @@ -18,6 +18,11 @@ package org.springframework.cloud.netflix.sidecar; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.cloud.netflix.eureka.EurekaInstanceConfigBean; +import org.springframework.cloud.netflix.eureka.metadata.DefaultManagementMetadataProvider; +import org.springframework.cloud.netflix.eureka.metadata.ManagementMetadata; +import org.springframework.cloud.netflix.eureka.metadata.ManagementMetadataProvider; +import org.springframework.context.annotation.Bean; import org.springframework.web.bind.annotation.RestController; @SpringBootApplication @@ -29,4 +34,25 @@ public class SidecarApplication { SpringApplication.run(SidecarApplication.class, args); } + @Bean + public ManagementMetadataProvider managementMetadataProvider() { + //The default management metadata provider checks for random ports, we dont care about this in tests + return new DefaultManagementMetadataProvider() { + @Override + public ManagementMetadata get(EurekaInstanceConfigBean instance, int serverPort, String serverContextPath, String managementContextPath, Integer managementPort) { + String healthCheckUrl = getHealthCheckUrl(instance, serverPort, serverContextPath, + managementContextPath, managementPort, false); + String statusPageUrl = getStatusPageUrl(instance, serverPort, serverContextPath, + managementContextPath, managementPort); + + ManagementMetadata metadata = new ManagementMetadata(healthCheckUrl, statusPageUrl, managementPort == null ? serverPort : managementPort); + if(instance.isSecurePortEnabled()) { + metadata.setSecureHealthCheckUrl(getHealthCheckUrl(instance, serverPort, serverContextPath, + managementContextPath, managementPort, true)); + } + return metadata; + } + }; + } + } diff --git a/spring-cloud-netflix-sidecar/src/test/java/org/springframework/cloud/netflix/sidecar/SidecarApplicationTests.java b/spring-cloud-netflix-sidecar/src/test/java/org/springframework/cloud/netflix/sidecar/SidecarApplicationTests.java index d442ad3b0..2d8da11a7 100644 --- a/spring-cloud-netflix-sidecar/src/test/java/org/springframework/cloud/netflix/sidecar/SidecarApplicationTests.java +++ b/spring-cloud-netflix-sidecar/src/test/java/org/springframework/cloud/netflix/sidecar/SidecarApplicationTests.java @@ -98,4 +98,34 @@ public class SidecarApplicationTests { } } + @RunWith(SpringRunner.class) + @SpringBootTest(classes = SidecarApplication.class, webEnvironment = RANDOM_PORT, value = { + "spring.application.name=mytest", "spring.cloud.client.hostname=mhhost", "spring.application.instance_id=1", + "eureka.instance.hostname=mhhost1", "sidecar.hostname=mhhost2", "sidecar.port=7000", "sidecar.ipAddress=127.0.0.1", + "management.context-path=/foo"}) + public static class ManagementContextPathStatusAndHealthCheckUrls { + @Autowired + EurekaInstanceConfigBean config; + + public void testStatusAndHealthCheckUrls() { + assertThat(this.config.getStatusPageUrl(), equalTo("http://mhhost2:0/foo/info")); + assertThat(this.config.getHealthCheckUrl(), equalTo("http://mhhost2:0/foo/health")); + } + } + + @RunWith(SpringRunner.class) + @SpringBootTest(classes = SidecarApplication.class, webEnvironment = RANDOM_PORT, value = { + "spring.application.name=mytest", "spring.cloud.client.hostname=mhhost", "spring.application.instance_id=1", + "eureka.instance.hostname=mhhost1", "sidecar.hostname=mhhost2", "sidecar.port=7000", "sidecar.ipAddress=127.0.0.1", + "server.context-path=/foo"}) + public static class ServerContextPathStatusAndHealthCheckUrls { + @Autowired + EurekaInstanceConfigBean config; + + @Test + public void testStatusAndHealthCheckUrls() { + assertThat(this.config.getStatusPageUrl(), equalTo("http://mhhost2:0/foo/info")); + assertThat(this.config.getHealthCheckUrl(), equalTo("http://mhhost2:0/foo/health")); + } + } } diff --git a/spring-cloud-netflix-turbine-stream/.jdk8 b/spring-cloud-netflix-turbine-stream/.jdk8 new file mode 100644 index 000000000..e69de29bb diff --git a/spring-cloud-netflix-turbine-stream/src/test/java/org/springframework/cloud/netflix/turbine/stream/TurbineStreamConfigurationTest.java b/spring-cloud-netflix-turbine-stream/src/test/java/org/springframework/cloud/netflix/turbine/stream/TurbineStreamConfigurationTest.java new file mode 100644 index 000000000..382795607 --- /dev/null +++ b/spring-cloud-netflix-turbine-stream/src/test/java/org/springframework/cloud/netflix/turbine/stream/TurbineStreamConfigurationTest.java @@ -0,0 +1,74 @@ +/* + * Copyright 2013-2018 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 + * + * http://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.turbine.stream; + +import org.junit.Before; +import org.junit.Test; +import rx.Observable; +import rx.functions.Func1; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.NoSuchElementException; + +import static org.junit.Assert.assertEquals; + +/** + * @author Yongsung Yoon + */ +public class TurbineStreamConfigurationTest { + TurbineStreamConfiguration turbineStreamConfiguration; + List> testMetricList; + + @Before + public void setUp() { + turbineStreamConfiguration = new TurbineStreamConfiguration(); + testMetricList = createBasicTestMetricList(); + } + + private List> createBasicTestMetricList() { + List> testDataList = new ArrayList<>(); + HashMap map = new HashMap<>(); + map.put("instanceId", "abc:127.0.0.1:8080"); + map.put("type", "HystrixCommand"); + testDataList.add(map); + + map = new HashMap<>(); + map.put("instanceId", "def:127.0.0.1:8080"); + map.put("type", "HystrixCommand"); + testDataList.add(map); + + map = new HashMap<>(); + map.put("instanceId", "xyz:127.0.0.1:8080"); + map.put("type", "HystrixThreadPool"); + testDataList.add(map); + + map = new HashMap<>(); + map.put("type", "ping"); + testDataList.add(map); + + map = new HashMap<>(); + map.put("dummy", "data"); + testDataList.add(map); + + return testDataList; + } +} diff --git a/spring-cloud-netflix-zuul/src/main/java/org/springframework/cloud/netflix/zuul/filters/ProxyRequestHelper.java b/spring-cloud-netflix-zuul/src/main/java/org/springframework/cloud/netflix/zuul/filters/ProxyRequestHelper.java index 0747f0314..72e3cd07f 100644 --- a/spring-cloud-netflix-zuul/src/main/java/org/springframework/cloud/netflix/zuul/filters/ProxyRequestHelper.java +++ b/spring-cloud-netflix-zuul/src/main/java/org/springframework/cloud/netflix/zuul/filters/ProxyRequestHelper.java @@ -145,7 +145,9 @@ public class ProxyRequestHelper { for (String header : zuulRequestHeaders.keySet()) { headers.set(header, zuulRequestHeaders.get(header)); } - headers.set(HttpHeaders.ACCEPT_ENCODING, "gzip"); + if(!headers.containsKey(HttpHeaders.ACCEPT_ENCODING)) { + headers.set(HttpHeaders.ACCEPT_ENCODING, "gzip"); + } return headers; } diff --git a/spring-cloud-netflix-zuul/src/test/java/org/springframework/cloud/netflix/zuul/filters/ProxyRequestHelperTests.java b/spring-cloud-netflix-zuul/src/test/java/org/springframework/cloud/netflix/zuul/filters/ProxyRequestHelperTests.java index 356d3071a..0c3d2ea44 100644 --- a/spring-cloud-netflix-zuul/src/test/java/org/springframework/cloud/netflix/zuul/filters/ProxyRequestHelperTests.java +++ b/spring-cloud-netflix-zuul/src/test/java/org/springframework/cloud/netflix/zuul/filters/ProxyRequestHelperTests.java @@ -210,6 +210,20 @@ public class ProxyRequestHelperTests { assertThat(acceptEncodings, contains("gzip")); } + @Test + public void buildZuulRequestHeadersRequestsAcceptEncoding() { + MockHttpServletRequest request = new MockHttpServletRequest("", "/"); + request.addHeader("accept-encoding", "identity"); + + ProxyRequestHelper helper = new ProxyRequestHelper(); + + MultiValueMap headers = helper.buildZuulRequestHeaders(request); + + List acceptEncodings = headers.get("accept-encoding"); + assertThat(acceptEncodings, hasSize(1)); + assertThat(acceptEncodings, contains("identity")); + } + @Test public void setResponseLowercase() throws IOException { MockHttpServletRequest request = new MockHttpServletRequest("POST", "/");