Allow configurable ignoring of network interfaces.

fixes gh-67
This commit is contained in:
Spencer Gibb
2015-12-21 15:54:00 -07:00
parent aa2ddce31e
commit a42e8eaab5
4 changed files with 209 additions and 3 deletions

View File

@@ -0,0 +1,31 @@
package org.springframework.cloud;
import org.springframework.boot.autoconfigure.AutoConfigureOrder;
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.util.InetUtils;
import org.springframework.cloud.util.InetUtilsProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
/**
* @author Spencer Gibb
*/
@Configuration
@ConditionalOnProperty(value = "spring.cloud.util.enabled", matchIfMissing = true)
@AutoConfigureOrder(0)
@EnableConfigurationProperties
public class UtilAutoConfiguration {
@Bean
public InetUtilsProperties inetUtilsProperties() {
return new InetUtilsProperties();
}
@Bean
@ConditionalOnMissingBean
public InetUtils inetUtils(InetUtilsProperties properties) {
return new InetUtils(properties);
}
}

View File

@@ -26,6 +26,103 @@ import lombok.extern.apachecommons.CommonsLog;
@CommonsLog
public class InetUtils {
private final ExecutorService executorService;
private final InetUtilsProperties properties;
public InetUtils(final InetUtilsProperties properties) {
this.properties = properties;
this.executorService = Executors
.newSingleThreadExecutor(new ThreadFactory() {
@Override
public Thread newThread(Runnable r) {
Thread thread = new Thread(r);
thread.setName(properties.getExecutorThreadName());
thread.setDaemon(true);
return thread;
}
});
}
public HostInfo findFirstNonLoopbackHostInfo() {
InetAddress address = findFirstNonLoopbackAddress();
if (address != null) {
return convertAddress(address);
}
HostInfo hostInfo = new HostInfo();
hostInfo.setHostname(this.properties.getDefaultHostname());
hostInfo.setIpAddress(this.properties.getDefaultIpAddress());
return hostInfo;
}
public InetAddress findFirstNonLoopbackAddress() {
try {
for (Enumeration<NetworkInterface> nics = NetworkInterface
.getNetworkInterfaces(); nics.hasMoreElements();) {
NetworkInterface ifc = nics.nextElement();
if (ifc.isUp()) {
log.debug("Testing interface: " + ifc.getDisplayName());
// @formatter:off
if (!ignoreInterface(ifc.getDisplayName())) {
for (Enumeration<InetAddress> addrs = ifc .getInetAddresses(); addrs.hasMoreElements(); ) {
InetAddress address = addrs.nextElement();
if (address instanceof Inet4Address && !address.isLoopbackAddress()) {
log.debug("Found non-loopback interface: " + ifc.getDisplayName());
return address;
}
}
}
// @formatter:on
}
}
}
catch (IOException ex) {
log.error("Cannot get first non-loopback address", ex);
}
try {
return InetAddress.getLocalHost();
}
catch (UnknownHostException e) {
log.warn("Unable to retrieve localhost");
}
return null;
}
boolean ignoreInterface(String interfaceName) {
for (String regex : this.properties.getIgnoredInterfaces()) {
if (interfaceName.matches(regex)) {
log.debug("Ignoring interface: " + interfaceName);
return true;
}
}
return false;
}
public HostInfo convertAddress(final InetAddress address) {
HostInfo hostInfo = new HostInfo();
Future<String> result = this.executorService.submit(new Callable<String>() {
@Override
public String call() throws Exception {
return address.getHostName();
}
});
String hostname;
try {
hostname = result.get(this.properties.getTimeoutSeconds(), TimeUnit.SECONDS);
}
catch (Exception e) {
log.info("Cannot determine local hostname");
hostname = "localhost";
}
hostInfo.setHostname(hostname);
hostInfo.setIpAddress(address.getHostAddress());
return hostInfo;
}
@Deprecated
private static ExecutorService executor = Executors
.newSingleThreadExecutor(new ThreadFactory() {
@Override
@@ -41,6 +138,7 @@ public class InetUtils {
* Find the first non-loopback host info. If there were errors return a hostinfo with
* 'localhost' and '127.0.0.1' for hostname and ipAddress respectively.
*/
@Deprecated
public static HostInfo getFirstNonLoopbackHostInfo() {
InetAddress address = getFirstNonLoopbackAddress();
if (address != null) {
@@ -56,6 +154,7 @@ public class InetUtils {
* Find the first non-loopback InetAddress
*/
@SneakyThrows
@Deprecated
public static InetAddress getFirstNonLoopbackAddress() {
try {
for (Enumeration<NetworkInterface> enumNic = NetworkInterface
@@ -90,6 +189,7 @@ public class InetUtils {
return null;
}
@Deprecated
public static HostInfo convert(final InetAddress address) {
HostInfo hostInfo = new HostInfo();
Future<String> result = executor.submit(new Callable<String>() {

View File

@@ -0,0 +1,44 @@
package org.springframework.cloud.util;
import java.util.ArrayList;
import java.util.List;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.context.properties.ConfigurationProperties;
import lombok.Data;
/**
* @author Spencer Gibb
*/
@Data
@ConfigurationProperties(InetUtilsProperties.PREFIX)
public class InetUtilsProperties {
public static final String PREFIX = "spring.cloud.inetutils";
/**
* Name of InetUtils executor thread
*/
private String executorThreadName = PREFIX;
/**
* The default hostname. Used in case of errors.
*/
private String defaultHostname = "localhost";
/**
* The default ipaddress. Used in case of errors.
*/
private String defaultIpAddress = "127.0.0.1";
/**
* Timeout in seconds for calculating hostname.
*/
@Value("${spring.util.timeout.sec:${SPRING_UTIL_TIMEOUT_SEC:1}}")
private int timeoutSeconds = 1;
/**
* List of Java regex expressions for network interfaces that will be ignored.
*/
private List<String> ignoredInterfaces = new ArrayList<>();
}

View File

@@ -16,13 +16,16 @@
package org.springframework.cloud.util;
import static org.junit.Assert.assertNotNull;
import java.net.InetAddress;
import java.util.Arrays;
import org.junit.Test;
import org.springframework.cloud.util.InetUtils.HostInfo;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
/**
* @author Dave Syer
*
@@ -31,23 +34,51 @@ public class InetUtilsTests {
@Test
public void testGetFirstNonLoopbackHostInfo() {
assertNotNull(new InetUtils(new InetUtilsProperties())
.findFirstNonLoopbackHostInfo());
assertNotNull(InetUtils.getFirstNonLoopbackHostInfo());
}
@Test
public void testGetFirstNonLoopbackAddress() {
assertNotNull(new InetUtils(new InetUtilsProperties())
.findFirstNonLoopbackAddress());
assertNotNull(InetUtils.getFirstNonLoopbackAddress());
}
@Test
public void testConvert() throws Exception {
assertNotNull(new InetUtils(new InetUtilsProperties()).convertAddress(InetAddress
.getByName("localhost")));
assertNotNull(InetUtils.convert(InetAddress.getByName("localhost")));
}
@Test
public void testHostInfo() throws Exception {
HostInfo info = InetUtils.getFirstNonLoopbackHostInfo();
HostInfo info = new InetUtils(new InetUtilsProperties())
.findFirstNonLoopbackHostInfo();
assertNotNull(info.getIpAddressAsInt());
info = InetUtils.getFirstNonLoopbackHostInfo();
assertNotNull(info.getIpAddressAsInt());
}
@Test
public void testIgnoreInterface() {
InetUtilsProperties properties = new InetUtilsProperties();
// These interfaces are not usable for "outside" communication, so they're
// probably not a good fit for the simple strategy of getting the first "usable"
// interface.
// https://docs.docker.com/v1.7/articles/networking/
properties.setIgnoredInterfaces(Arrays.asList("docker0", "veth.*"));
InetUtils inetUtils = new InetUtils(properties);
assertTrue("docker0 not ignored", inetUtils.ignoreInterface("docker0"));
assertTrue("vethAQI2QT0 not ignored", inetUtils.ignoreInterface("vethAQI2QT"));
assertFalse("docker1 ignored", inetUtils.ignoreInterface("docker1"));
assertFalse("docker0 ignored",
new InetUtils(new InetUtilsProperties()).ignoreInterface("docker0"));
}
}