diff --git a/plugins/plugin-docker/che-plugin-docker-machine/src/main/java/org/eclipse/che/plugin/docker/machine/DockerInstanceRuntimeInfo.java b/plugins/plugin-docker/che-plugin-docker-machine/src/main/java/org/eclipse/che/plugin/docker/machine/DockerInstanceRuntimeInfo.java index 7e6f106797a..24252fd98bc 100644 --- a/plugins/plugin-docker/che-plugin-docker-machine/src/main/java/org/eclipse/che/plugin/docker/machine/DockerInstanceRuntimeInfo.java +++ b/plugins/plugin-docker/che-plugin-docker-machine/src/main/java/org/eclipse/che/plugin/docker/machine/DockerInstanceRuntimeInfo.java @@ -68,6 +68,12 @@ public class DockerInstanceRuntimeInfo implements MachineRuntimeInfo { */ public static final String CHE_WORKSPACE_ID = "CHE_WORKSPACE_ID"; + /** + * Env variable that indicates to DockerInstanceRuntimeInfo that the direct address of wsagent containers + * should be used, as available in ContainerInfo. + */ + public static final String CHE_DOCKER_USE_INTERNAL_CONTAINER_ADDRESS = "CHE_DOCKER_USE_INTERNAL_CONTAINER_ADDRESS"; + /** * Default HOSTNAME that will be added in all docker containers that are started. This host will container the Docker host's ip * reachable inside the container. @@ -310,16 +316,33 @@ protected Map addRefAndUrlToServers(final Map getServersWithFilledPorts(final String externalHostame, final String internalHostname, final Map> exposedPorts) { final HashMap servers = new LinkedHashMap<>(); + boolean useMappedPorts = true; + if (useInternalContainerAddresses()) { + if (info.getNetworkSettings() != null && internalHostname.equals(info.getNetworkSettings().getIpAddress())) { + useMappedPorts = false; + } + } + for (Map.Entry> portEntry : exposedPorts.entrySet()) { // in form 1234/tcp String portProtocol = portEntry.getKey(); // we are assigning ports automatically, so have 1 to 1 binding (at least per protocol) String externalPort = portEntry.getValue().get(0).getHostPort(); + + // If we are sending messages to the container directly, we don't want to use the mapped ports. + String internalHostnameAndPort; + if (useMappedPorts) { + internalHostnameAndPort = internalHostname + ":" + externalPort; + } else { + String internalPort = portEntry.getKey().split("/")[0]; + internalHostnameAndPort = internalHostname + ":" + internalPort; + } + servers.put(portProtocol, new ServerImpl(null, null, externalHostame + ":" + externalPort, null, - new ServerPropertiesImpl(null, internalHostname + ":" + externalPort, null))); + new ServerPropertiesImpl(null, internalHostnameAndPort, null))); } return servers; @@ -355,4 +378,18 @@ private Map getServersConfFromLabels(final Set p return serversConf; } + + /** + * Checks environment variable {@code CHE_DOCKER_USE_INTERNAL_CONTAINER_ADDRESS} to determine if + * direct addresses of containers should be used. + * @return true if {@code CHE_DOCKER_USE_INTERNAL_CONTAINER_ADDRESS} is "true", false otherwise. + */ + protected boolean useInternalContainerAddresses() { + String useInternalContainerAddresses = System.getenv(CHE_DOCKER_USE_INTERNAL_CONTAINER_ADDRESS); + if (useInternalContainerAddresses == null) { + return false; + } else { + return useInternalContainerAddresses.equals("true"); + } + } } diff --git a/plugins/plugin-docker/che-plugin-docker-machine/src/main/java/org/eclipse/che/plugin/docker/machine/local/LocalDockerInstanceRuntimeInfo.java b/plugins/plugin-docker/che-plugin-docker-machine/src/main/java/org/eclipse/che/plugin/docker/machine/local/LocalDockerInstanceRuntimeInfo.java index 7928369bd18..d29a8c1b342 100644 --- a/plugins/plugin-docker/che-plugin-docker-machine/src/main/java/org/eclipse/che/plugin/docker/machine/local/LocalDockerInstanceRuntimeInfo.java +++ b/plugins/plugin-docker/che-plugin-docker-machine/src/main/java/org/eclipse/che/plugin/docker/machine/local/LocalDockerInstanceRuntimeInfo.java @@ -16,6 +16,7 @@ import org.eclipse.che.api.core.model.machine.ServerConf; import org.eclipse.che.commons.annotation.Nullable; import org.eclipse.che.plugin.docker.client.json.ContainerInfo; +import org.eclipse.che.plugin.docker.client.json.NetworkSettings; import org.eclipse.che.plugin.docker.machine.DockerInstanceRuntimeInfo; import javax.inject.Inject; @@ -23,11 +24,13 @@ import java.util.Set; /** - * Gets predefined docker containers host (internal and external addresses) for machine servers instead of evaluating it + * By default, gets predefined docker containers host (internal and external addresses) for machine servers instead of evaluating it * from docker configuration. External address is needed when clients (UD, IDE, etc...) can't ping machine servers * directly (e.g. when running on Docker for Mac or behind a NAT). If the external address is not provided it defaults * to the internal one. * + *

If environment variable {@code CHE_DOCKER_USE_INTERNAL_CONTAINER_ADDRESS} is true, the IP Address from + * {@link ContainerInfo}, if available, is used for internal address instead.
*

Value of host can be retrieved from property ${code machine.docker.local_node_host} or * from environment variable {@code CHE_DOCKER_MACHINE_HOST}.
*

Value of external hostname can be retrieved from property ${code machine.docker.local_node_host.external} or @@ -50,6 +53,12 @@ public class LocalDockerInstanceRuntimeInfo extends DockerInstanceRuntimeInfo { */ public static final String CHE_DOCKER_MACHINE_HOST_EXTERNAL = "CHE_DOCKER_MACHINE_HOST_EXTERNAL"; + /** + * Env variable that indicates to DockerInstanceRuntimeInfo that the direct address of wsagent containers + * should be used, as available in ContainerInfo. + */ + public static final String CHE_DOCKER_USE_INTERNAL_CONTAINER_ADDRESS = "CHE_DOCKER_USE_INTERNAL_CONTAINER_ADDRESS"; + @Inject public LocalDockerInstanceRuntimeInfo(@Assisted ContainerInfo containerInfo, @Assisted("externalhost") @Nullable String containerExternalHostname, @@ -60,23 +69,23 @@ public LocalDockerInstanceRuntimeInfo(@Assisted ContainerInfo containerInfo, @Named("machine.docker.dev_machine.machine_servers") Set devMachineServers, @Named("machine.docker.machine_servers") Set allMachinesServers) { - super(containerInfo, externalHostnameWithPrecedence(dockerNodeExternalHostname, - containerExternalHostname, - dockerNodeInternalHostname, - containerInternalHostname), + containerExternalHostname, + dockerNodeInternalHostname, + containerInternalHostname), internalHostnameWithPrecedence(dockerNodeInternalHostname, - containerInternalHostname), + containerInternalHostname, + containerInfo.getNetworkSettings()), machineConfig, devMachineServers, allMachinesServers); } private static String externalHostnameWithPrecedence(String externalHostnameProperty, - String externalHostnameAssisted, - String internalHostnameProperty, - String internalHostnameAssisted) { + String externalHostnameAssisted, + String internalHostnameProperty, + String internalHostnameAssisted) { String externalHostnameEnvVar = System.getenv(CHE_DOCKER_MACHINE_HOST_EXTERNAL); if (externalHostnameEnvVar != null) { @@ -86,12 +95,26 @@ private static String externalHostnameWithPrecedence(String externalHostnameProp } else if (externalHostnameAssisted != null) { return externalHostnameAssisted; } else { - return internalHostnameWithPrecedence(internalHostnameProperty,internalHostnameAssisted); + return internalHostnameWithPrecedence(internalHostnameProperty, + internalHostnameAssisted, + null); } } private static String internalHostnameWithPrecedence(String internalHostnameProperty, - String internalHostnameAssisted) { + String internalHostnameAssisted, + NetworkSettings networkSettings) { + + String useInternalContainerAddress = System.getenv(CHE_DOCKER_USE_INTERNAL_CONTAINER_ADDRESS); + if (useInternalContainerAddress != null && useInternalContainerAddress.equals("true")) { + String containerHostName = null; + if (networkSettings != null) { + containerHostName = networkSettings.getIpAddress(); + } + if (containerHostName != null && !containerHostName.isEmpty()) { + return containerHostName; + } + } String internalHostNameEnvVariable = System.getenv(CHE_DOCKER_MACHINE_HOST_INTERNAL); if (internalHostNameEnvVariable != null) { diff --git a/plugins/plugin-docker/che-plugin-docker-machine/src/test/java/org/eclipse/che/plugin/docker/machine/DockerInstanceRuntimeInfoTest.java b/plugins/plugin-docker/che-plugin-docker-machine/src/test/java/org/eclipse/che/plugin/docker/machine/DockerInstanceRuntimeInfoTest.java index 3f0ab084a3e..4a4ae74db9c 100644 --- a/plugins/plugin-docker/che-plugin-docker-machine/src/test/java/org/eclipse/che/plugin/docker/machine/DockerInstanceRuntimeInfoTest.java +++ b/plugins/plugin-docker/che-plugin-docker-machine/src/test/java/org/eclipse/che/plugin/docker/machine/DockerInstanceRuntimeInfoTest.java @@ -20,6 +20,7 @@ import org.eclipse.che.plugin.docker.client.json.NetworkSettings; import org.eclipse.che.plugin.docker.client.json.PortBinding; import org.mockito.Mock; +import org.mockito.Mockito; import org.mockito.testng.MockitoTestNGListener; import org.testng.annotations.BeforeMethod; import org.testng.annotations.Listeners; @@ -47,6 +48,7 @@ public class DockerInstanceRuntimeInfoTest { private static final String CONTAINER_HOST = "container-host.com"; private static final String CONTAINER_HOST_EXTERNAL = "container-host-ext.com"; private static final String DEFAULT_ADDRESS = "192.168.1.1"; + private static final String DEFAULT_ADDRESS_INTERNAL = "172.17.0.1"; @Mock private ContainerInfo containerInfo; @@ -678,4 +680,100 @@ public void shouldSetExternalAddressDistinctFromInternalWhenExternalHostnameIsNo // then assertEquals(servers, expectedServers); } + + @Test + public void shouldUseUnmappedPortsWhenUsingInternalAddresses() throws Exception { + //given + when(networkSettings.getIpAddress()).thenReturn(DEFAULT_ADDRESS_INTERNAL); + Map> ports = new HashMap<>(); + when(networkSettings.getPorts()).thenReturn(ports); + ports.put("4301/tcp", Collections.singletonList(new PortBinding().withHostIp(DEFAULT_ADDRESS) + .withHostPort("32100"))); + ports.put("4305/udp", Collections.singletonList(new PortBinding().withHostIp(DEFAULT_ADDRESS) + .withHostPort("32103"))); + Set commonSystemServersConfigs = new HashSet<>(); + commonSystemServersConfigs.add(new ServerConfImpl("sysServer1-tcp", "4301/tcp", "http", "/some/path1")); + Set devSystemServersConfigs = new HashSet<>(); + devSystemServersConfigs.add(new ServerConfImpl("devSysServer1-udp", "4305/udp", null, "some/path4")); + when(machineConfig.isDev()).thenReturn(true); + + runtimeInfo = Mockito.spy(new DockerInstanceRuntimeInfo(containerInfo, + DEFAULT_ADDRESS, + DEFAULT_ADDRESS_INTERNAL, + machineConfig, + devSystemServersConfigs, + commonSystemServersConfigs)); + + doReturn(true).when(runtimeInfo).useInternalContainerAddresses(); + + final HashMap expectedServers = new HashMap<>(); + expectedServers.put("4301/tcp", new ServerImpl("sysServer1-tcp", + "http", + DEFAULT_ADDRESS + ":32100", + "http://" + DEFAULT_ADDRESS + ":32100/some/path1", + new ServerPropertiesImpl("/some/path1", + DEFAULT_ADDRESS_INTERNAL + ":4301", + "http://" + DEFAULT_ADDRESS_INTERNAL + ":4301/some/path1"))); + expectedServers.put("4305/udp", new ServerImpl("devSysServer1-udp", + null, + DEFAULT_ADDRESS + ":32103", + null, + new ServerPropertiesImpl("some/path4", + DEFAULT_ADDRESS_INTERNAL + ":4305", + null))); + + //when + final Map servers = runtimeInfo.getServers(); + + //then + assertEquals(servers, expectedServers, "Expected servers to use internal container address and ports"); + } + + @Test + public void shouldUseMappedPortsWhenNotUsingInternalAddresses() throws Exception { + //given + when(networkSettings.getIpAddress()).thenReturn(DEFAULT_ADDRESS_INTERNAL); + Map> ports = new HashMap<>(); + when(networkSettings.getPorts()).thenReturn(ports); + ports.put("4301/tcp", Collections.singletonList(new PortBinding().withHostIp(DEFAULT_ADDRESS) + .withHostPort("32100"))); + ports.put("4305/udp", Collections.singletonList(new PortBinding().withHostIp(DEFAULT_ADDRESS) + .withHostPort("32103"))); + Set commonSystemServersConfigs = new HashSet<>(); + commonSystemServersConfigs.add(new ServerConfImpl("sysServer1-tcp", "4301/tcp", "http", "/some/path1")); + Set devSystemServersConfigs = new HashSet<>(); + devSystemServersConfigs.add(new ServerConfImpl("devSysServer1-udp", "4305/udp", null, "some/path4")); + when(machineConfig.isDev()).thenReturn(true); + + runtimeInfo = Mockito.spy(new DockerInstanceRuntimeInfo(containerInfo, + DEFAULT_ADDRESS, + DEFAULT_ADDRESS_INTERNAL, + machineConfig, + devSystemServersConfigs, + commonSystemServersConfigs)); + + doReturn(false).when(runtimeInfo).useInternalContainerAddresses(); + + final HashMap expectedServers = new HashMap<>(); + expectedServers.put("4301/tcp", new ServerImpl("sysServer1-tcp", + "http", + DEFAULT_ADDRESS + ":32100", + "http://" + DEFAULT_ADDRESS + ":32100/some/path1", + new ServerPropertiesImpl("/some/path1", + DEFAULT_ADDRESS_INTERNAL + ":32100", + "http://" + DEFAULT_ADDRESS_INTERNAL + ":32100/some/path1"))); + expectedServers.put("4305/udp", new ServerImpl("devSysServer1-udp", + null, + DEFAULT_ADDRESS + ":32103", + null, + new ServerPropertiesImpl("some/path4", + DEFAULT_ADDRESS_INTERNAL + ":32103", + null))); + + //when + final Map servers = runtimeInfo.getServers(); + + //then + assertEquals(servers, expectedServers, "Expected servers to not use internal container address and ports"); + } }