[SCB-2285]find publish address may get wrong docker address (#2428)

diff --git a/foundations/foundation-common/src/main/java/org/apache/servicecomb/foundation/common/net/NetUtils.java b/foundations/foundation-common/src/main/java/org/apache/servicecomb/foundation/common/net/NetUtils.java
index 0ec4c2d..90d6301 100644
--- a/foundations/foundation-common/src/main/java/org/apache/servicecomb/foundation/common/net/NetUtils.java
+++ b/foundations/foundation-common/src/main/java/org/apache/servicecomb/foundation/common/net/NetUtils.java
@@ -29,10 +29,13 @@
 import java.util.Enumeration;
 import java.util.HashMap;
 import java.util.Map;
+import java.util.Map.Entry;
 
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
+import com.google.common.annotations.VisibleForTesting;
+
 public final class NetUtils {
 
   private static final Logger LOGGER = LoggerFactory.getLogger(NetUtils.class);
@@ -41,6 +44,8 @@
 
   private static final String IPV6_KEY = "_v6";
 
+  private static final String PREFERRED_INTERFACE = "eth";
+
   // one interface can bind to multiple address
   // we only save one ip for each interface name.
   // eg:
@@ -70,27 +75,23 @@
       // getLocalHost will throw exception in some docker image and sometimes will do a hostname lookup and time consuming
       InetAddress localHost = InetAddress.getLocalHost();
       hostName = localHost.getHostName();
-      if ((localHost.isAnyLocalAddress() || localHost.isLoopbackAddress() || localHost.isMulticastAddress())
-          && !allInterfaceAddresses.isEmpty()) {
-        allInterfaceAddresses.forEach((key, val) -> {
-          if (key.endsWith(IPV4_KEY)) {
-            hostAddress = val.getHostAddress();
-            LOGGER.warn("cannot find a proper ipv4 host address, choose {} , may not be correct.", hostAddress);
-          } else {
-            hostAddressIpv6 = val.getHostAddress();
-            int index = hostAddressIpv6.indexOf("%");
-            if (index > 0) {
-              hostAddressIpv6 = hostAddressIpv6.substring(0, index);
-            }
-            LOGGER.warn("cannot find a proper ipv6 host address, choose {} , may not be correct.", hostAddressIpv6);
-          }
-        });
-      } else {
-        LOGGER.info("get localhost address: {}", localHost.getHostAddress());
-        hostAddress = localHost.getHostAddress();
-      }
+      LOGGER.info("localhost hostName={}, hostAddress={}.", hostName, localHost.getHostAddress());
 
-      LOGGER.info("add host name from localhost:" + hostName + ",host address:" + hostAddress);
+      if (!isLocalAddress(localHost)) {
+        if (Inet6Address.class.isInstance(localHost)) {
+          hostAddressIpv6 = trimIpv6(localHost.getHostAddress());
+          hostAddress = tryGetHostAddressFromNetworkInterface(false, localHost);
+          LOGGER.info("Host address info ipV4={}, ipV6={}.", hostAddress, hostAddressIpv6);
+          return;
+        }
+        hostAddress = localHost.getHostAddress();
+        hostAddressIpv6 = trimIpv6(tryGetHostAddressFromNetworkInterface(true, localHost));
+        LOGGER.info("Host address info ipV4={}, ipV6={}.", hostAddress, hostAddressIpv6);
+        return;
+      }
+      hostAddressIpv6 = trimIpv6(tryGetHostAddressFromNetworkInterface(true, localHost));
+      hostAddress = tryGetHostAddressFromNetworkInterface(false, localHost);
+      LOGGER.info("Host address info ipV4={}, ipV6={}.", hostAddress, hostAddressIpv6);
     } catch (Exception e) {
       LOGGER.error("got exception when trying to get addresses:", e);
       if (allInterfaceAddresses.size() >= 1) {
@@ -103,6 +104,29 @@
     }
   }
 
+  private static String tryGetHostAddressFromNetworkInterface(boolean isIpv6, InetAddress localhost) {
+    InetAddress result = null;
+    for (Entry<String, InetAddress> entry : allInterfaceAddresses.entrySet()) {
+      if (isIpv6 && entry.getKey().endsWith(IPV6_KEY)) {
+        result = entry.getValue();
+        if (entry.getKey().startsWith(PREFERRED_INTERFACE)) {
+          return result.getHostAddress();
+        }
+      } else if (!isIpv6 && entry.getKey().endsWith(IPV4_KEY)) {
+        result = entry.getValue();
+        if (entry.getKey().startsWith(PREFERRED_INTERFACE)) {
+          return result.getHostAddress();
+        }
+      }
+    }
+
+    if (result == null) {
+      return localhost.getHostAddress();
+    }
+
+    return result.getHostAddress();
+  }
+
   private NetUtils() {
   }
 
@@ -111,36 +135,46 @@
    * 此时,通过遍历网卡接口的方式规避,出来的数据不一定对
    */
   private static void doGetAddressFromNetworkInterface() throws SocketException {
-    Enumeration<NetworkInterface> iterNetwork = NetworkInterface.getNetworkInterfaces();
+    Enumeration<NetworkInterface> networkInterfaces = NetworkInterface.getNetworkInterfaces();
 
-    while (iterNetwork.hasMoreElements()) {
-      NetworkInterface network = iterNetwork.nextElement();
+    while (networkInterfaces.hasMoreElements()) {
+      NetworkInterface network = networkInterfaces.nextElement();
 
       if (!network.isUp() || network.isLoopback() || network.isVirtual()) {
         continue;
       }
 
-      Enumeration<InetAddress> iterAddress = network.getInetAddresses();
-      while (iterAddress.hasMoreElements()) {
-        InetAddress address = iterAddress.nextElement();
+      Enumeration<InetAddress> addresses = network.getInetAddresses();
+      while (addresses.hasMoreElements()) {
+        InetAddress address = addresses.nextElement();
 
-        if (address.isAnyLocalAddress() || address.isLoopbackAddress() || address.isMulticastAddress()) {
+        if (isLocalAddress(address)) {
           continue;
         }
 
-        if (Inet4Address.class.isInstance(address)) {
-          LOGGER.info(
-              "add network interface name:" + network.getName() + ",ipv4 host address:" + address.getHostAddress());
+        if (address instanceof Inet4Address) {
+          LOGGER.info("add ipv4 network interface:" + network.getName() + ",host address:" + address.getHostAddress());
           allInterfaceAddresses.put(network.getName() + IPV4_KEY, address);
-        } else if (Inet6Address.class.isInstance(address)) {
-          LOGGER.info(
-              "add network interface name:" + network.getName() + ",ipv6 host address:" + address.getHostAddress());
+        } else if (address instanceof Inet6Address) {
+          LOGGER.info("add ipv6 network interface:" + network.getName() + ",host address:" + address.getHostAddress());
           allInterfaceAddresses.put(network.getName() + IPV6_KEY, address);
         }
       }
     }
   }
 
+  private static String trimIpv6(String hostAddress) {
+    int index = hostAddress.indexOf("%");
+    if (index >= 0) {
+      return hostAddress.substring(0, index);
+    }
+    return hostAddress;
+  }
+
+  private static boolean isLocalAddress(InetAddress address) {
+    return address.isAnyLocalAddress() || address.isLoopbackAddress() || address.isMulticastAddress();
+  }
+
   /**
    * The format of address should be {@code IPv4:port} or {@code [IPv6]:port}, or {@code host:port},
    * or you will not get expected result.
@@ -237,11 +271,9 @@
     }
     try {
       URI originalURI = new URI(schema + "://" + address);
-      IpPort ipPort = NetUtils.parseIpPort(originalURI);
-      if (ipPort == null) {
-        LOGGER.error("address {} is not valid.", address);
-        return null;
-      }
+      // validate original url
+      NetUtils.parseIpPort(originalURI);
+
       return originalURI.toString();
     } catch (URISyntaxException e) {
       LOGGER.error("address {} is not valid.", address);
@@ -258,6 +290,11 @@
     return hostName;
   }
 
+  @VisibleForTesting
+  static void resetHostName() {
+    hostName = null;
+  }
+
   public static String getHostAddress() {
     //If failed to get host address ,micro-service will registry failed
     //So I add retry mechanism
@@ -303,7 +340,7 @@
       return String.valueOf(bytes);
     }
     int exp = (int) (Math.log(bytes) / Math.log(unit));
-    char pre = "KMGTPE".charAt(exp - 1);
+    char pre = "KMGTPE" .charAt(exp - 1);
     return String.format("%.3f%c", bytes / Math.pow(unit, exp), pre);
   }
 }
diff --git a/foundations/foundation-common/src/test/java/org/apache/servicecomb/foundation/common/net/TestNetUtils.java b/foundations/foundation-common/src/test/java/org/apache/servicecomb/foundation/common/net/TestNetUtils.java
index a363747..90bce85 100644
--- a/foundations/foundation-common/src/test/java/org/apache/servicecomb/foundation/common/net/TestNetUtils.java
+++ b/foundations/foundation-common/src/test/java/org/apache/servicecomb/foundation/common/net/TestNetUtils.java
@@ -34,6 +34,24 @@
 
 public class TestNetUtils {
   @Test
+  public void testFindProperHostAddress() {
+    NetUtils.resetHostName();
+    String result = NetUtils.getHostName();
+    System.out.println(result);
+    Assert.assertNotNull(result);
+
+    result = NetUtils.getHostAddress();
+    System.out.println(result);
+    Assert.assertNotNull(result);
+
+    result = NetUtils.getIpv6HostAddress();
+    System.out.println(result);
+    if (result != null) {
+      Assert.assertFalse(result.contains("%"));
+    }
+  }
+
+  @Test
   public void testIpPort() {
     IpPort oIPPort = new IpPort("10.145.154.45", 8080);
     Assert.assertEquals("10.145.154.45", oIPPort.getHostOrIp());