Skip to content

Commit

Permalink
Incorrect statusPageUrl and healthCheckUrl are reported to Eureka if …
Browse files Browse the repository at this point in the history
…management.context-path is not set (#2145)
  • Loading branch information
Aloren authored and ryanjbaxter committed Nov 14, 2017
1 parent 89441ac commit 62a8f72
Show file tree
Hide file tree
Showing 6 changed files with 369 additions and 33 deletions.
Expand Up @@ -24,7 +24,6 @@
import java.lang.annotation.RetentionPolicy; import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target; import java.lang.annotation.Target;
import java.net.MalformedURLException; import java.net.MalformedURLException;
import java.net.URL;
import java.util.Map; import java.util.Map;


import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
Expand All @@ -50,6 +49,9 @@
import org.springframework.cloud.commons.util.InetUtils; import org.springframework.cloud.commons.util.InetUtils;
import org.springframework.cloud.context.scope.refresh.RefreshScope; import org.springframework.cloud.context.scope.refresh.RefreshScope;
import org.springframework.cloud.netflix.eureka.config.DiscoveryClientOptionalArgsConfiguration; import org.springframework.cloud.netflix.eureka.config.DiscoveryClientOptionalArgsConfiguration;
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.cloud.netflix.eureka.serviceregistry.EurekaAutoServiceRegistration; import org.springframework.cloud.netflix.eureka.serviceregistry.EurekaAutoServiceRegistration;
import org.springframework.cloud.netflix.eureka.serviceregistry.EurekaRegistration; import org.springframework.cloud.netflix.eureka.serviceregistry.EurekaRegistration;
import org.springframework.cloud.netflix.eureka.serviceregistry.EurekaServiceRegistry; import org.springframework.cloud.netflix.eureka.serviceregistry.EurekaServiceRegistry;
Expand Down Expand Up @@ -119,60 +121,68 @@ public EurekaClientConfigBean eurekaClientConfigBean() {
return client; return client;
} }


@Bean
@ConditionalOnMissingBean
public ManagementMetadataProvider serviceManagementMetadataProvider() {
return new DefaultManagementMetadataProvider();
}

@Bean @Bean
@ConditionalOnMissingBean(value = EurekaInstanceConfig.class, search = SearchStrategy.CURRENT) @ConditionalOnMissingBean(value = EurekaInstanceConfig.class, search = SearchStrategy.CURRENT)
public EurekaInstanceConfigBean eurekaInstanceConfigBean(InetUtils inetUtils) throws MalformedURLException { public EurekaInstanceConfigBean eurekaInstanceConfigBean(InetUtils inetUtils,
ManagementMetadataProvider managementMetadataProvider) throws MalformedURLException {
PropertyResolver eurekaPropertyResolver = new RelaxedPropertyResolver(this.env, "eureka.instance."); PropertyResolver eurekaPropertyResolver = new RelaxedPropertyResolver(this.env, "eureka.instance.");
String hostname = eurekaPropertyResolver.getProperty("hostname"); String hostname = eurekaPropertyResolver.getProperty("hostname");


boolean preferIpAddress = Boolean.parseBoolean(eurekaPropertyResolver.getProperty("preferIpAddress")); boolean preferIpAddress = Boolean.parseBoolean(eurekaPropertyResolver.getProperty("preferIpAddress"));
boolean isSecurePortEnabled = Boolean.parseBoolean(eurekaPropertyResolver.getProperty("securePortEnabled")); boolean isSecurePortEnabled = Boolean.parseBoolean(eurekaPropertyResolver.getProperty("securePortEnabled"));
int nonSecurePort = Integer.valueOf(propertyResolver.getProperty("server.port", propertyResolver.getProperty("port", "8080"))); String serverContextPath = propertyResolver.getProperty("server.contextPath", "/");
int serverPort = Integer.valueOf(propertyResolver.getProperty("server.port", propertyResolver.getProperty("port", "8080")));


int managementPort = Integer.valueOf(propertyResolver.getProperty("management.port", String.valueOf(nonSecurePort))); Integer managementPort = propertyResolver.getProperty("management.port", Integer.class);// nullable. should be wrapped into optional
String managementContextPath = propertyResolver.getProperty("management.contextPath", propertyResolver.getProperty("server.contextPath", "/")); String managementContextPath = propertyResolver.getProperty("management.contextPath");// nullable. should be wrapped into optional
Integer jmxPort = propertyResolver.getProperty("com.sun.management.jmxremote.port", Integer.class);//nullable Integer jmxPort = propertyResolver.getProperty("com.sun.management.jmxremote.port", Integer.class);//nullable
EurekaInstanceConfigBean instance = new EurekaInstanceConfigBean(inetUtils); EurekaInstanceConfigBean instance = new EurekaInstanceConfigBean(inetUtils);
instance.setNonSecurePort(nonSecurePort);
instance.setNonSecurePort(serverPort);
instance.setInstanceId(getDefaultInstanceId(propertyResolver)); instance.setInstanceId(getDefaultInstanceId(propertyResolver));
instance.setPreferIpAddress(preferIpAddress); instance.setPreferIpAddress(preferIpAddress);


if(isSecurePortEnabled) { if(isSecurePortEnabled) {
int securePort = Integer.valueOf(propertyResolver.getProperty("server.port", propertyResolver.getProperty("port", "8080"))); instance.setSecurePort(serverPort);
instance.setSecurePort(securePort);
} }


if (managementPort != nonSecurePort && managementPort != 0) { if (StringUtils.hasText(hostname)) {
if (StringUtils.hasText(hostname)) { instance.setHostname(hostname);
instance.setHostname(hostname); }
} String statusPageUrlPath = eurekaPropertyResolver.getProperty("statusPageUrlPath");
String statusPageUrlPath = eurekaPropertyResolver.getProperty("statusPageUrlPath"); String healthCheckUrlPath = eurekaPropertyResolver.getProperty("healthCheckUrlPath");
String healthCheckUrlPath = eurekaPropertyResolver.getProperty("healthCheckUrlPath");
if (!managementContextPath.endsWith("/")) { if (StringUtils.hasText(statusPageUrlPath)) {
managementContextPath = managementContextPath + "/"; instance.setStatusPageUrlPath(statusPageUrlPath);
} }
if (StringUtils.hasText(statusPageUrlPath)) { if (StringUtils.hasText(healthCheckUrlPath)) {
instance.setStatusPageUrlPath(statusPageUrlPath); instance.setHealthCheckUrlPath(healthCheckUrlPath);
} }
if (StringUtils.hasText(healthCheckUrlPath)) {
instance.setHealthCheckUrlPath(healthCheckUrlPath);
}


String scheme = instance.getSecurePortEnabled() ? "https" : "http"; ManagementMetadata metadata = managementMetadataProvider.get(instance, serverPort,
URL base = new URL(scheme, instance.getHostname(), managementPort, managementContextPath); serverContextPath, managementContextPath, managementPort);
instance.setStatusPageUrl(new URL(base, StringUtils.trimLeadingCharacter(instance.getStatusPageUrlPath(), '/')).toString());
instance.setHealthCheckUrl(new URL(base, StringUtils.trimLeadingCharacter(instance.getHealthCheckUrlPath(), '/')).toString()); if(metadata != null) {
instance.setStatusPageUrl(metadata.getStatusPageUrl());
instance.setHealthCheckUrl(metadata.getHealthCheckUrl());
Map<String, String> metadataMap = instance.getMetadataMap();
if (metadataMap.get("management.port") == null) {
metadataMap.put("management.port", String.valueOf(metadata.getManagementPort()));
}
} }
setupMetadataMap(instance, managementPort, jmxPort);
setupJmxPort(instance, jmxPort);
return instance; return instance;
} }


private void setupMetadataMap(EurekaInstanceConfigBean instance, int managementPort, private void setupJmxPort(EurekaInstanceConfigBean instance, Integer jmxPort) {
Integer jmxPort) {
Map<String, String> metadataMap = instance.getMetadataMap(); Map<String, String> metadataMap = instance.getMetadataMap();
if (metadataMap.get("management.port") == null && managementPort != 0) {
metadataMap.put("management.port", String.valueOf(managementPort));
}
if (metadataMap.get("jmx.port") == null && jmxPort != null) { if (metadataMap.get("jmx.port") == null && jmxPort != null) {
metadataMap.put("jmx.port", String.valueOf(jmxPort)); metadataMap.put("jmx.port", String.valueOf(jmxPort));
} }
Expand Down
@@ -0,0 +1,97 @@
package org.springframework.cloud.netflix.eureka.metadata;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.cloud.netflix.eureka.EurekaInstanceConfigBean;
import org.springframework.util.StringUtils;

import java.net.MalformedURLException;
import java.net.URL;

public class DefaultManagementMetadataProvider implements ManagementMetadataProvider {

private static final int RANDOM_PORT = 0;
private static final Log log = LogFactory.getLog(DefaultManagementMetadataProvider.class);

@Override
public ManagementMetadata get(EurekaInstanceConfigBean instance, int serverPort,
String serverContextPath, String managementContextPath,
Integer managementPort) {
if (isRandom(managementPort)) {
return null;
}
if (managementPort == null && isRandom(serverPort)) {
return null;
}
String healthCheckUrl = getHealthCheckUrl(instance, serverPort, serverContextPath,
managementContextPath, managementPort);
String statusPageUrl = getStatusPageUrl(instance, serverPort, serverContextPath,
managementContextPath, managementPort);

return new ManagementMetadata(healthCheckUrl, statusPageUrl, managementPort == null ? serverPort : managementPort);
}

private boolean isRandom(Integer port) {
return port != null && port == RANDOM_PORT;
}

private String getHealthCheckUrl(EurekaInstanceConfigBean instance, int serverPort, String serverContextPath,
String managementContextPath, Integer managementPort) {
String healthCheckUrlPath = instance.getHealthCheckUrlPath();
String healthCheckUrl = getUrl(instance, serverPort, serverContextPath, managementContextPath,
managementPort, healthCheckUrlPath);
log.debug("Constructed eureka meta-data healthcheckUrl: " + healthCheckUrl);
return healthCheckUrl;
}

public String getStatusPageUrl(EurekaInstanceConfigBean instance, int serverPort, String serverContextPath,
String managementContextPath, Integer managementPort) {
String statusPageUrlPath = instance.getStatusPageUrlPath();
String statusPageUrl = getUrl(instance, serverPort, serverContextPath, managementContextPath,
managementPort, statusPageUrlPath);
log.debug("Constructed eureka meta-data statusPageUrl: " + statusPageUrl);
return statusPageUrl;
}

private String getUrl(EurekaInstanceConfigBean instance, int serverPort,
String serverContextPath, String managementContextPath,
Integer managementPort, String urlPath) {
managementContextPath = refineManagementContextPath(serverContextPath, managementContextPath, managementPort);
if (managementPort == null) {
managementPort = serverPort;
}
String scheme = instance.getSecurePortEnabled() ? "https" : "http";
return constructValidUrl(scheme, instance.getHostname(), managementPort, managementContextPath, urlPath);
}

private String refineManagementContextPath(String serverContextPath, String managementContextPath,
Integer managementPort) {
if(managementContextPath != null) {
return managementContextPath;
}
if(managementPort != null) {
return "/";
}
return serverContextPath;
}

private String constructValidUrl(String scheme, String hostname, int port,
String contextPath, String statusPath) {
try {
if (!contextPath.endsWith("/")) {
contextPath = contextPath + "/";
}
URL base = new URL(scheme, hostname, port, contextPath);
String refinedStatusPath = StringUtils.trimLeadingCharacter(statusPath, '/');
return new URL(base, refinedStatusPath).toString();
} catch (MalformedURLException e) {
String message = getErrorMessage(scheme, hostname, port, contextPath, statusPath);
throw new IllegalStateException(message, e);
}
}

private String getErrorMessage(String scheme, String hostname, int port, String contextPath, String statusPath) {
return String.format("Failed to construct url for scheme: %s, hostName: %s port: %s contextPath: %s statusPath: %s",
scheme, hostname, port, contextPath, statusPath);
}
}
@@ -0,0 +1,53 @@
package org.springframework.cloud.netflix.eureka.metadata;

import java.util.Objects;

public class ManagementMetadata {

private final String healthCheckUrl;
private final String statusPageUrl;
private final Integer managementPort;

public ManagementMetadata(String healthCheckUrl, String statusPageUrl, Integer managementPort) {
this.healthCheckUrl = healthCheckUrl;
this.statusPageUrl = statusPageUrl;
this.managementPort = managementPort;
}

public String getHealthCheckUrl() {
return healthCheckUrl;
}

public String getStatusPageUrl() {
return statusPageUrl;
}

public Integer getManagementPort() {
return managementPort;
}

@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
ManagementMetadata that = (ManagementMetadata) o;
return Objects.equals(healthCheckUrl, that.healthCheckUrl) &&
Objects.equals(statusPageUrl, that.statusPageUrl) &&
Objects.equals(managementPort, that.managementPort);
}

@Override
public int hashCode() {
return Objects.hash(healthCheckUrl, statusPageUrl, managementPort);
}

@Override
public String toString() {
final StringBuilder sb = new StringBuilder("ManagementMetadata{");
sb.append("healthCheckUrl='").append(healthCheckUrl).append('\'');
sb.append(", statusPageUrl='").append(statusPageUrl).append('\'');
sb.append(", managementPort=").append(managementPort);
sb.append('}');
return sb.toString();
}
}
@@ -0,0 +1,10 @@
package org.springframework.cloud.netflix.eureka.metadata;

import org.springframework.cloud.netflix.eureka.EurekaInstanceConfigBean;

public interface ManagementMetadataProvider {

ManagementMetadata get(EurekaInstanceConfigBean instance, int serverPort,
String serverContextPath, String managementContextPath, Integer managementPort);

}
Expand Up @@ -189,6 +189,34 @@ public void healthCheckUrlPathAndManagementPort() {
instance.getHealthCheckUrl().contains("/myHealthCheck")); instance.getHealthCheckUrl().contains("/myHealthCheck"));
} }


@Test
public void statusPageUrl_and_healthCheckUrl_do_not_contain_server_context_path() throws Exception {
addEnvironment(this.context, "server.port=8989",
"management.port=9999", "server.contextPath=/service");

setupContext(RefreshAutoConfiguration.class);
EurekaInstanceConfigBean instance = this.context
.getBean(EurekaInstanceConfigBean.class);
assertTrue("Wrong status page: " + instance.getStatusPageUrl(),
instance.getStatusPageUrl().endsWith(":9999/info"));
assertTrue("Wrong health check: " + instance.getHealthCheckUrl(),
instance.getHealthCheckUrl().endsWith(":9999/health"));
}

@Test
public void statusPageUrl_and_healthCheckUrl_contain_management_context_path() throws Exception {
addEnvironment(this.context,
"server.port=8989", "management.contextPath=/management");

setupContext(RefreshAutoConfiguration.class);
EurekaInstanceConfigBean instance = this.context
.getBean(EurekaInstanceConfigBean.class);
assertTrue("Wrong status page: " + instance.getStatusPageUrl(),
instance.getStatusPageUrl().endsWith(":8989/management/info"));
assertTrue("Wrong health check: " + instance.getHealthCheckUrl(),
instance.getHealthCheckUrl().endsWith(":8989/management/health"));
}

@Test @Test
public void statusPageUrlPathAndManagementPortAndContextPath() { public void statusPageUrlPathAndManagementPortAndContextPath() {
addEnvironment(this.context, "server.port=8989", addEnvironment(this.context, "server.port=8989",
Expand Down

0 comments on commit 62a8f72

Please sign in to comment.