Commit 7fe777db by Johannes Stelzer

Add spring.boot.admin.client.preferIp

In conjuntion with server.address / management.address this plays more nicely with interfaces with multiple ipAdrresses. Also remove spring.boot.admin.client.useAddressOf. fixes #112
parent 8bed0d18
...@@ -21,7 +21,7 @@ The main service that is used is a registrar that registeres the application at ...@@ -21,7 +21,7 @@ The main service that is used is a registrar that registeres the application at
| spring.boot.admin.client.managementUrl | Client-management-URL to register with. Can be overriden in case the reachable URL is different (e.g. Docker). Must be unique in registry.<br>Default: is guessed based on serviceUrl management.port and management.context-path| | spring.boot.admin.client.managementUrl | Client-management-URL to register with. Can be overriden in case the reachable URL is different (e.g. Docker). Must be unique in registry.<br>Default: is guessed based on serviceUrl management.port and management.context-path|
| spring.boot.admin.client.healthUrl | Client-management-URL to register with. Can be overriden in case the reachable URL is different (e.g. Docker). Must be unique in registry.<br>Default: is guessed based on managementUrl and endpoints.health.id | | spring.boot.admin.client.healthUrl | Client-management-URL to register with. Can be overriden in case the reachable URL is different (e.g. Docker). Must be unique in registry.<br>Default: is guessed based on managementUrl and endpoints.health.id |
| spring.boot.admin.client.name | Name to register with. Defaults to the ApplicationContexts name. Only set when it should differ.<br>Default: _${spring.application.name}_ if set, spring-boot-application otherwise. | | spring.boot.admin.client.name | Name to register with. Defaults to the ApplicationContexts name. Only set when it should differ.<br>Default: _${spring.application.name}_ if set, spring-boot-application otherwise. |
| spring.boot.admin.client.useIpAddressOf | If an network-interface name is specified, its ip-address wil be used for the guessed url (instead of hostname).| | spring.boot.admin.client.preferIpAddress | Use the ip-address rather then the hostname in the guessed urls. It's required to set `server.address` and `management.address`respectively. |
### Other configuration properties ### Other configuration properties
Options from other spring boot features. These should be set to enable all features. Options from other spring boot features. These should be set to enable all features.
......
...@@ -16,9 +16,6 @@ ...@@ -16,9 +16,6 @@
package de.codecentric.boot.admin.config; package de.codecentric.boot.admin.config;
import java.net.InetAddress; import java.net.InetAddress;
import java.net.InterfaceAddress;
import java.net.NetworkInterface;
import java.net.SocketException;
import java.net.UnknownHostException; import java.net.UnknownHostException;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
...@@ -34,27 +31,27 @@ import org.springframework.context.event.ApplicationContextEvent; ...@@ -34,27 +31,27 @@ import org.springframework.context.event.ApplicationContextEvent;
import org.springframework.context.event.ContextRefreshedEvent; import org.springframework.context.event.ContextRefreshedEvent;
import org.springframework.core.Ordered; import org.springframework.core.Ordered;
import org.springframework.core.annotation.Order; import org.springframework.core.annotation.Order;
import org.springframework.util.Assert;
import org.springframework.util.StringUtils; import org.springframework.util.StringUtils;
@ConfigurationProperties(prefix = "spring.boot.admin.client", ignoreUnknownFields = false) @ConfigurationProperties(prefix = "spring.boot.admin.client", ignoreUnknownFields = false)
@Order(Ordered.LOWEST_PRECEDENCE - 100) @Order(Ordered.LOWEST_PRECEDENCE - 100)
public class AdminClientProperties implements ApplicationListener<ApplicationEvent> { public class AdminClientProperties implements ApplicationListener<ApplicationEvent> {
/** /**
* Client-management-URL to register with. Inferred at runtime, can be overriden in * Client-management-URL to register with. Inferred at runtime, can be overriden in case the
* case the reachable URL is different (e.g. Docker). * reachable URL is different (e.g. Docker).
*/ */
private String managementUrl; private String managementUrl;
/** /**
* Client-service-URL register with. Inferred at runtime, can be overriden in case the * Client-service-URL register with. Inferred at runtime, can be overriden in case the reachable
* reachable URL is different (e.g. Docker). * URL is different (e.g. Docker).
*/ */
private String serviceUrl; private String serviceUrl;
/** /**
* Client-health-URL to register with. Inferred at runtime, can be overriden in case * Client-health-URL to register with. Inferred at runtime, can be overriden in case the
* the reachable URL is different (e.g. Docker). Must be unique in registry. * reachable URL is different (e.g. Docker). Must be unique in registry.
*/ */
private String healthUrl; private String healthUrl;
...@@ -68,10 +65,9 @@ public class AdminClientProperties implements ApplicationListener<ApplicationEve ...@@ -68,10 +65,9 @@ public class AdminClientProperties implements ApplicationListener<ApplicationEve
private String healthEndpointId; private String healthEndpointId;
/** /**
* If set, the address of the specified interface is used in url inference * Should the registered urls be built with server.address or with hostname.
* instead of the hostname.
*/ */
private String useIpAddressOf = null; private boolean preferIp = false;
@Autowired @Autowired
private ManagementServerProperties management; private ManagementServerProperties management;
...@@ -117,17 +113,21 @@ public class AdminClientProperties implements ApplicationListener<ApplicationEve ...@@ -117,17 +113,21 @@ public class AdminClientProperties implements ApplicationListener<ApplicationEve
} }
public String getManagementUrl() { public String getManagementUrl() {
if (managementUrl == null) { if (managementUrl != null) {
if (managementPort != -1) { return managementUrl;
return createLocalUri(managementPort,
management.getContextPath());
}
else {
return append(getServiceUrl(), management.getContextPath());
}
} }
if (managementPort == -1) {
return append(getServiceUrl(), management.getContextPath());
}
if (preferIp) {
Assert.notNull(management.getAddress(),
"management.address must be set when using preferIp");
return append(createLocalUri(management.getAddress().getHostAddress(), managementPort),
management.getContextPath());
return managementUrl; }
return append(createLocalUri(getHostname(), managementPort), management.getContextPath());
} }
public void setManagementUrl(String managementUrl) { public void setManagementUrl(String managementUrl) {
...@@ -135,10 +135,10 @@ public class AdminClientProperties implements ApplicationListener<ApplicationEve ...@@ -135,10 +135,10 @@ public class AdminClientProperties implements ApplicationListener<ApplicationEve
} }
public String getHealthUrl() { public String getHealthUrl() {
if (healthUrl == null) { if (healthUrl != null) {
return append(getManagementUrl(), healthEndpointId); return healthUrl;
} }
return healthUrl; return append(getManagementUrl(), healthEndpointId);
} }
public void setHealthUrl(String healthUrl) { public void setHealthUrl(String healthUrl) {
...@@ -146,16 +146,22 @@ public class AdminClientProperties implements ApplicationListener<ApplicationEve ...@@ -146,16 +146,22 @@ public class AdminClientProperties implements ApplicationListener<ApplicationEve
} }
public String getServiceUrl() { public String getServiceUrl() {
if (serviceUrl == null) { if (serviceUrl != null) {
if (serverPort != -1){ return serviceUrl;
return createLocalUri(serverPort, server.getContextPath());
} else {
throw new IllegalStateException(
"EmbeddedServletContainer has not been initialized yet!");
}
} }
return serviceUrl; if (serverPort == -1) {
throw new IllegalStateException(
"EmbeddedServletContainer has not been initialized yet!");
}
if (preferIp) {
Assert.notNull(server.getAddress(), "server.address must be set when using preferIp");
return append(createLocalUri(server.getAddress().getHostAddress(), serverPort),
server.getContextPath());
}
return append(createLocalUri(getHostname(), serverPort), server.getContextPath());
} }
public void setServiceUrl(String serviceUrl) { public void setServiceUrl(String serviceUrl) {
...@@ -174,14 +180,13 @@ public class AdminClientProperties implements ApplicationListener<ApplicationEve ...@@ -174,14 +180,13 @@ public class AdminClientProperties implements ApplicationListener<ApplicationEve
this.name = name; this.name = name;
} }
public void setUseIpAddressOf(String useIpAddressOf) { public void setPreferIp(boolean preferIp) {
this.useIpAddressOf = useIpAddressOf; this.preferIp = preferIp;
} }
private String createLocalUri(int port, String path) { private String createLocalUri(String host, int port) {
String scheme = server.getSsl() != null && server.getSsl().isEnabled() ? "https" String scheme = server.getSsl() != null && server.getSsl().isEnabled() ? "https" : "http";
: "http"; return scheme + "://" + host + ":" + port;
return append(scheme + "://" + getHost() + ":" + port, path);
} }
private String append(String uri, String path) { private String append(String uri, String path) {
...@@ -194,15 +199,6 @@ public class AdminClientProperties implements ApplicationListener<ApplicationEve ...@@ -194,15 +199,6 @@ public class AdminClientProperties implements ApplicationListener<ApplicationEve
return baseUri + "/" + normPath; return baseUri + "/" + normPath;
} }
private String getHost() {
if (useIpAddressOf == null) {
return getHostname();
} else {
return getHostIp();
}
}
private String getHostname() { private String getHostname() {
try { try {
return InetAddress.getLocalHost().getCanonicalHostName(); return InetAddress.getLocalHost().getCanonicalHostName();
...@@ -211,47 +207,4 @@ public class AdminClientProperties implements ApplicationListener<ApplicationEve ...@@ -211,47 +207,4 @@ public class AdminClientProperties implements ApplicationListener<ApplicationEve
} }
} }
private String getHostIp() {
NetworkInterface nic;
try {
nic = NetworkInterface.getByName(useIpAddressOf);
} catch (SocketException ex) {
throw new IllegalArgumentException(ex.getMessage(), ex);
}
if (nic != null) {
InetAddress address = findIp(nic);
if (address != null) {
return address.getHostAddress();
}
throw new IllegalStateException(
"Couldn't determin InetAdress for network interface '"
+ useIpAddressOf + "'");
} else {
throw new IllegalArgumentException(
"Network interface"
+ useIpAddressOf
+ " not found! Please specify correct interface for spring.boot.admin.client.useIpAddressOf");
}
}
private InetAddress findIp(NetworkInterface nic) {
InetAddress candidate = null;
for (InterfaceAddress address : nic.getInterfaceAddresses()) {
if (!address.getAddress().isLoopbackAddress()) {
if (address.getAddress().isSiteLocalAddress()) {
return address.getAddress();
}
candidate = address.getAddress();
}
}
if (candidate != null) {
return candidate;
}
return null;
}
} }
\ No newline at end of file
...@@ -69,12 +69,11 @@ public class AdminClientPropertiesTest { ...@@ -69,12 +69,11 @@ public class AdminClientPropertiesTest {
publishServletContainerInitializedEvent(clientProperties, 8080, null); publishServletContainerInitializedEvent(clientProperties, 8080, null);
publishServletContainerInitializedEvent(clientProperties, 8081, "management"); publishServletContainerInitializedEvent(clientProperties, 8081, "management");
assertThat(clientProperties.getManagementUrl(), is("http://" + getHostname() assertThat(clientProperties.getManagementUrl(),
+ ":8081/admin")); is("http://" + getHostname() + ":8081/admin"));
assertThat(clientProperties.getHealthUrl(), is("http://" + getHostname() assertThat(clientProperties.getHealthUrl(),
+ ":8081/admin/alive")); is("http://" + getHostname() + ":8081/admin/alive"));
assertThat(clientProperties.getServiceUrl(), is("http://" + getHostname() assertThat(clientProperties.getServiceUrl(), is("http://" + getHostname() + ":8080"));
+ ":8080"));
} }
@Test @Test
...@@ -86,29 +85,24 @@ public class AdminClientPropertiesTest { ...@@ -86,29 +85,24 @@ public class AdminClientPropertiesTest {
publishServletContainerInitializedEvent(clientProperties, 8080, null); publishServletContainerInitializedEvent(clientProperties, 8080, null);
publishServletContainerInitializedEvent(clientProperties, 8081, "management"); publishServletContainerInitializedEvent(clientProperties, 8081, "management");
assertThat(clientProperties.getManagementUrl(), is("http://" + getHostname() assertThat(clientProperties.getManagementUrl(), is("http://" + getHostname() + ":8081"));
+ ":8081")); assertThat(clientProperties.getHealthUrl(), is("http://" + getHostname() + ":8081/health"));
assertThat(clientProperties.getHealthUrl(), is("http://" + getHostname() assertThat(clientProperties.getServiceUrl(), is("http://" + getHostname() + ":8080"));
+ ":8081/health"));
assertThat(clientProperties.getServiceUrl(), is("http://" + getHostname()
+ ":8080"));
} }
@Test @Test
public void test_contextPath_mgmtPath() { public void test_contextPath_mgmtPath() {
load("server.context-path=app", load("server.context-path=app", "management.context-path=/admin");
"management.context-path=/admin");
AdminClientProperties clientProperties = new AdminClientProperties(); AdminClientProperties clientProperties = new AdminClientProperties();
context.getAutowireCapableBeanFactory().autowireBean(clientProperties); context.getAutowireCapableBeanFactory().autowireBean(clientProperties);
publishServletContainerInitializedEvent(clientProperties, 8080, null); publishServletContainerInitializedEvent(clientProperties, 8080, null);
assertThat(clientProperties.getManagementUrl(), is("http://" + getHostname() assertThat(clientProperties.getManagementUrl(),
+ ":8080/app/admin")); is("http://" + getHostname() + ":8080/app/admin"));
assertThat(clientProperties.getHealthUrl(), is("http://" + getHostname() assertThat(clientProperties.getHealthUrl(),
+ ":8080/app/admin/health")); is("http://" + getHostname() + ":8080/app/admin/health"));
assertThat(clientProperties.getServiceUrl(), is("http://" + getHostname() assertThat(clientProperties.getServiceUrl(), is("http://" + getHostname() + ":8080/app"));
+ ":8080/app"));
} }
@Test @Test
...@@ -119,12 +113,10 @@ public class AdminClientPropertiesTest { ...@@ -119,12 +113,10 @@ public class AdminClientPropertiesTest {
publishServletContainerInitializedEvent(clientProperties, 80, null); publishServletContainerInitializedEvent(clientProperties, 80, null);
assertThat(clientProperties.getManagementUrl(), is("http://" + getHostname() assertThat(clientProperties.getManagementUrl(), is("http://" + getHostname() + ":80/app"));
+ ":80/app")); assertThat(clientProperties.getHealthUrl(),
assertThat(clientProperties.getHealthUrl(), is("http://" + getHostname() is("http://" + getHostname() + ":80/app/health"));
+ ":80/app/health")); assertThat(clientProperties.getServiceUrl(), is("http://" + getHostname() + ":80/app"));
assertThat(clientProperties.getServiceUrl(), is("http://" + getHostname()
+ ":80/app"));
} }
@Test @Test
...@@ -135,12 +127,9 @@ public class AdminClientPropertiesTest { ...@@ -135,12 +127,9 @@ public class AdminClientPropertiesTest {
publishServletContainerInitializedEvent(clientProperties, 8080, null); publishServletContainerInitializedEvent(clientProperties, 8080, null);
assertThat(clientProperties.getManagementUrl(), is("http://" + getHostname() assertThat(clientProperties.getManagementUrl(), is("http://" + getHostname() + ":8080"));
+ ":8080")); assertThat(clientProperties.getHealthUrl(), is("http://" + getHostname() + ":8080/health"));
assertThat(clientProperties.getHealthUrl(), is("http://" + getHostname() assertThat(clientProperties.getServiceUrl(), is("http://" + getHostname() + ":8080"));
+ ":8080/health"));
assertThat(clientProperties.getServiceUrl(), is("http://" + getHostname()
+ ":8080"));
} }
@Test @Test
...@@ -151,51 +140,75 @@ public class AdminClientPropertiesTest { ...@@ -151,51 +140,75 @@ public class AdminClientPropertiesTest {
publishServletContainerInitializedEvent(clientProperties, 8080, null); publishServletContainerInitializedEvent(clientProperties, 8080, null);
assertThat(clientProperties.getManagementUrl(), is("https://" + getHostname() assertThat(clientProperties.getManagementUrl(), is("https://" + getHostname() + ":8080"));
+ ":8080")); assertThat(clientProperties.getHealthUrl(),
assertThat(clientProperties.getHealthUrl(), is("https://" + getHostname() is("https://" + getHostname() + ":8080/health"));
+ ":8080/health")); assertThat(clientProperties.getServiceUrl(), is("https://" + getHostname() + ":8080"));
assertThat(clientProperties.getServiceUrl(), is("https://" + getHostname()
+ ":8080"));
} }
@Test(expected = IllegalArgumentException.class) @Test(expected = IllegalArgumentException.class)
public void test_preferIpAddress_nic_not_exsts() { public void test_preferIpAddress_serveraddress_missing() {
load(); load();
AdminClientProperties clientProperties = new AdminClientProperties(); AdminClientProperties clientProperties = new AdminClientProperties();
clientProperties.setUseIpAddressOf("eth-not-exist"); clientProperties.setPreferIp(true);
context.getAutowireCapableBeanFactory().autowireBean(clientProperties); context.getAutowireCapableBeanFactory().autowireBean(clientProperties);
publishServletContainerInitializedEvent(clientProperties, 8080, null); publishServletContainerInitializedEvent(clientProperties, 8080, null);
clientProperties.getServiceUrl();
}
@Test(expected = IllegalArgumentException.class)
public void test_preferIpAddress_managementaddress_missing() {
load();
AdminClientProperties clientProperties = new AdminClientProperties();
clientProperties.setPreferIp(true);
context.getAutowireCapableBeanFactory().autowireBean(clientProperties);
publishServletContainerInitializedEvent(clientProperties, 8080, null);
publishServletContainerInitializedEvent(clientProperties, 8081, "management");
clientProperties.getManagementUrl(); clientProperties.getManagementUrl();
} }
@Test
public void test_preferIpAddress() {
load("server.address=127.0.0.1", "management.address=127.0.0.2");
AdminClientProperties clientProperties = new AdminClientProperties();
clientProperties.setPreferIp(true);
context.getAutowireCapableBeanFactory().autowireBean(clientProperties);
publishServletContainerInitializedEvent(clientProperties, 8080, null);
publishServletContainerInitializedEvent(clientProperties, 8081, "management");
assertThat(clientProperties.getManagementUrl(), is("http://127.0.0.2:8081"));
assertThat(clientProperties.getHealthUrl(), is("http://127.0.0.2:8081/health"));
assertThat(clientProperties.getServiceUrl(), is("http://127.0.0.1:8080"));
}
private String getHostname() { private String getHostname() {
try { try {
return InetAddress.getLocalHost().getCanonicalHostName(); return InetAddress.getLocalHost().getCanonicalHostName();
} } catch (UnknownHostException e) {
catch (UnknownHostException e) {
throw new RuntimeException(e); throw new RuntimeException(e);
} }
} }
private void publishServletContainerInitializedEvent(AdminClientProperties client, private void publishServletContainerInitializedEvent(AdminClientProperties client, int port,
int port, String namespace) { String namespace) {
EmbeddedServletContainer eventSource = mock(EmbeddedServletContainer.class); EmbeddedServletContainer eventSource = mock(EmbeddedServletContainer.class);
when(eventSource.getPort()).thenReturn(port); when(eventSource.getPort()).thenReturn(port);
EmbeddedWebApplicationContext eventContext = mock(EmbeddedWebApplicationContext.class); EmbeddedWebApplicationContext eventContext = mock(EmbeddedWebApplicationContext.class);
when(eventContext.getNamespace()).thenReturn(namespace); when(eventContext.getNamespace()).thenReturn(namespace);
when(eventContext.getEmbeddedServletContainer()).thenReturn(eventSource); when(eventContext.getEmbeddedServletContainer()).thenReturn(eventSource);
client.onApplicationEvent(new EmbeddedServletContainerInitializedEvent( client.onApplicationEvent(
eventContext, new EmbeddedServletContainerInitializedEvent(eventContext, eventSource));
eventSource));
} }
private void publishContextRefreshedEvent(AdminClientProperties client) { private void publishContextRefreshedEvent(AdminClientProperties client) {
client.onApplicationEvent(new ContextRefreshedEvent( client.onApplicationEvent(
mock(EmbeddedWebApplicationContext.class))); new ContextRefreshedEvent(mock(EmbeddedWebApplicationContext.class)));
} }
private void load(String... environment) { private void load(String... environment) {
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment