Skip to content

Commit

Permalink
IDEX-3244 Allow to detect the ip to use for the container host. And t…
Browse files Browse the repository at this point in the history
…hen add this container host as an extra host when starting a container

Change-Id: I953539a7a29e257d89a6604bfe9efb885965e508
Signed-off-by: Florent BENOIT <fbenoit@codenvy.com>
  • Loading branch information
benoitf committed Oct 27, 2015
1 parent 99661f6 commit 14a393e
Show file tree
Hide file tree
Showing 9 changed files with 455 additions and 12 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@
import org.eclipse.che.plugin.docker.client.connection.TcpConnection;
import org.eclipse.che.plugin.docker.client.connection.UnixSocketConnection;
import org.eclipse.che.plugin.docker.client.dto.AuthConfigs;
import org.eclipse.che.plugin.docker.client.helper.NetworkFinder;
import org.eclipse.che.plugin.docker.client.json.ContainerCommited;
import org.eclipse.che.plugin.docker.client.json.ContainerConfig;
import org.eclipse.che.plugin.docker.client.json.ContainerCreated;
Expand Down Expand Up @@ -125,17 +126,20 @@ public class DockerConnector {
private final DockerCertificates dockerCertificates;
private final InitialAuthConfig initialAuthConfig;
private final ExecutorService executor;
private final String dockerHostIp;

public DockerConnector(InitialAuthConfig initialAuthConfig) {
this(new DockerConnectorConfiguration(initialAuthConfig));
public DockerConnector(InitialAuthConfig initialAuthConfig, NetworkFinder networkFinder) {
this(new DockerConnectorConfiguration(initialAuthConfig, networkFinder));
}

public DockerConnector(URI dockerDaemonUri,
DockerCertificates dockerCertificates,
InitialAuthConfig initialAuthConfig) {
InitialAuthConfig initialAuthConfig,
String dockerHostIp) {
this.dockerDaemonUri = dockerDaemonUri;
this.dockerCertificates = dockerCertificates;
this.initialAuthConfig = initialAuthConfig;
this.dockerHostIp = dockerHostIp;
executor = Executors.newCachedThreadPool(new ThreadFactoryBuilder()
.setNameFormat("DockerApiConnector-%d")
.setDaemon(true)
Expand All @@ -146,7 +150,8 @@ public DockerConnector(URI dockerDaemonUri,
private DockerConnector(DockerConnectorConfiguration connectorConfiguration) {
this(connectorConfiguration.getDockerDaemonUri(),
connectorConfiguration.getDockerCertificates(),
connectorConfiguration.getAuthConfigs());
connectorConfiguration.getAuthConfigs(),
connectorConfiguration.getDockerHostIp());
}

/**
Expand Down Expand Up @@ -1170,4 +1175,14 @@ static boolean isUnixSocketUri(URI uri) {
private void createTarArchive(File tar, File... files) throws IOException {
TarUtils.tarFiles(tar, 0, files);
}

/**
* Gets the Docker host ip address. This is host that can be reached from a docker container.
*
* @return docker host IP address
* @see <a href="https://docs.docker.com/articles/networking/">Docker Networking</a>
*/
public String getDockerHostIp() {
return dockerHostIp;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -14,14 +14,19 @@
import com.google.inject.name.Named;

import org.eclipse.che.api.core.util.SystemInfo;
import org.eclipse.che.plugin.docker.client.helper.NetworkFinder;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.validation.constraints.NotNull;
import java.io.File;
import java.net.InetAddress;
import java.net.URI;
import java.net.URISyntaxException;
import java.util.Map;
import java.util.Optional;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import static org.eclipse.che.plugin.docker.client.DockerConnector.DEFAULT_DOCKER_MACHINE_CERTS_DIR;
import static org.eclipse.che.plugin.docker.client.DockerConnector.DEFAULT_DOCKER_MACHINE_URI;
Expand All @@ -36,6 +41,32 @@
*/
public class DockerConnectorConfiguration {

/**
* Docker bridge name on Linux.
*/
protected static final String BRIDGE_LINUX_INTERFACE_NAME = "bridge0";

/**
* Default ip of docker host (Linux system).
*/
protected static final String DEFAULT_LINUX_DOCKER_HOST_IP = "172.17.42.1";

/**
* Default ip of docker host (Linux system).
*/
protected static final String DEFAULT_DOCKER_MACHINE_DOCKER_HOST_IP = "192.168.99.1";

/**
* Docker host regexp to get the ip address.
*/
protected static final Pattern HOST_REGEXP_PATTERN = Pattern.compile(".*?//(.*?):.*?");

/**
* Pattern allowing to get every IPv4 digit.
*/
protected static final Pattern IPV4_ADDRESS_PATTERN = Pattern.compile("(\\d{1,3})\\.(\\d{1,3})\\.(\\d{1,3})\\.(\\d{1,3})");


private static final Logger LOG = LoggerFactory.getLogger(DockerConnectorConfiguration.class);

@Inject(optional = true)
Expand All @@ -46,20 +77,29 @@ public class DockerConnectorConfiguration {
@Named("docker.client.certificates_folder")
private String dockerCertificatesDirectoryPath = dockerMachineCertsDirectoryPath();

/**
* Helper used to resolve ip address of the docker host ip from a docker container.
*/
@Inject
private NetworkFinder networkFinder;

@Inject
private InitialAuthConfig authConfigs;

@Inject
public DockerConnectorConfiguration(InitialAuthConfig initialAuthConfig) {
public DockerConnectorConfiguration(InitialAuthConfig initialAuthConfig, NetworkFinder networkFinder) {
this.authConfigs = initialAuthConfig;
this.networkFinder = networkFinder;
}

public DockerConnectorConfiguration(URI dockerDaemonUri,
String dockerCertificatesDirectoryPath,
InitialAuthConfig authConfigs) {
InitialAuthConfig authConfigs,
NetworkFinder networkFinder) {
this.authConfigs = authConfigs;
this.dockerDaemonUri = dockerDaemonUri;
this.dockerCertificatesDirectoryPath = dockerCertificatesDirectoryPath;
this.networkFinder = networkFinder;
}

public static String getExpectedLocalHost() {
Expand Down Expand Up @@ -117,6 +157,51 @@ protected static URI dockerDaemonUri(final boolean isLinux, @NotNull final Map<S
return DEFAULT_DOCKER_MACHINE_URI;
}

protected String getDockerHostIp() {
return getDockerHostIp(SystemInfo.isLinux(), System.getenv());
}

/**
* Provides the IP address that is accessible from the docker container to reach the docker host
*
* @param isLinux
* if System is running on Linux
* @param env
* should contain System environment
* @return docker Ip address host to be used from docker container
*/
protected String getDockerHostIp(final boolean isLinux, @NotNull final Map<String, String> env) {
if (isLinux) {
// search "docker0" bridge
Optional<InetAddress> dockerHostInetAddress = networkFinder.getIPAddress(BRIDGE_LINUX_INTERFACE_NAME);
if (dockerHostInetAddress.isPresent()) {
return dockerHostInetAddress.get().getHostAddress();
}
// return default Docker host ip address
return DEFAULT_LINUX_DOCKER_HOST_IP;
}

// on other env, Windows/Mac, search the docker machine ip and then find the bridge used
String host = env.get(DOCKER_HOST_PROPERTY);
if (host != null) {
Matcher matcher = HOST_REGEXP_PATTERN.matcher(host);
if (matcher.matches()) {
String dockerIpAddress = matcher.group(1);
Matcher ipv4Matcher = IPV4_ADDRESS_PATTERN.matcher(dockerIpAddress);
if (ipv4Matcher.matches()) {
String subnet = ipv4Matcher.group(1).concat(".").concat(ipv4Matcher.group(2)).concat(".").concat(ipv4Matcher.group(3));
// now try to find a network interface matching this
Optional<InetAddress> matchingIpAddress = networkFinder.foundInetAddressMatching(subnet);
// return the bridge that is matching the host
if (matchingIpAddress.isPresent()) {
return matchingIpAddress.get().getHostAddress();
}
}
}
}
return DEFAULT_DOCKER_MACHINE_DOCKER_HOST_IP;
}

private static String dockerMachineCertsDirectoryPath() {
return dockerMachineCertsDirectoryPath(SystemInfo.isLinux(), System.getenv());
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
/*******************************************************************************
* Copyright (c) 2012-2015 Codenvy, S.A.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Codenvy, S.A. - initial API and implementation
*******************************************************************************/
package org.eclipse.che.plugin.docker.client.helper;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.inject.Singleton;
import java.net.Inet4Address;
import java.net.InetAddress;
import java.net.NetworkInterface;
import java.net.SocketException;
import java.util.Enumeration;
import java.util.Optional;

/**
* Default Implementation of the {@link NetworkFinder}
*
* @author Florent Benoit
*/
@Singleton
public class DefaultNetworkFinder implements NetworkFinder {

/**
* Logger.
*/
private static final Logger LOG = LoggerFactory.getLogger(DefaultNetworkFinder.class);

/**
* Gets the first inet address of a given network interface if it's found
*
* @param bridgeName
* name of the network interface
* @return only ipv4 ip of the given bridge
*/
@Override
public Optional<InetAddress> getIPAddress(String bridgeName) {

NetworkInterface docker0 = null;
try {
docker0 = NetworkInterface.getByName(bridgeName);
} catch (SocketException e) {
LOG.error("Unable to list the network interfaces", e);
}

if (docker0 != null) {
// first ipv4 ip
Enumeration<InetAddress> addresses = docker0.getInetAddresses();
while (addresses.hasMoreElements()) {
InetAddress inetAddress = addresses.nextElement();
if (inetAddress instanceof Inet4Address) {
return Optional.of(inetAddress);
}
}
}
return Optional.empty();
}

/**
* Search if a given network interface is matching the given subnet
*
* @param subnet
* the first digits of an ip address. Like 123.123.123
* @return optional ipv4 internet address if there was a matching one
*/
@Override
public Optional<InetAddress> foundInetAddressMatching(String subnet) {

Enumeration<NetworkInterface> interfacesEnum = null;
try {
interfacesEnum = NetworkInterface.getNetworkInterfaces();
} catch (SocketException e) {
LOG.error("Unable to list the network interfaces", e);
return Optional.empty();
}

while (interfacesEnum.hasMoreElements()) {
NetworkInterface networkInterface = interfacesEnum.nextElement();
Enumeration<InetAddress> inetAddressEnumeration = networkInterface.getInetAddresses();
while (inetAddressEnumeration.hasMoreElements()) {
InetAddress inetAddress = inetAddressEnumeration.nextElement();
if (inetAddress instanceof Inet4Address) {
if (inetAddress.getHostAddress().startsWith(subnet)) {
return Optional.of(inetAddress);
}
}
}
}
return Optional.empty();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
/*******************************************************************************
* Copyright (c) 2012-2015 Codenvy, S.A.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Codenvy, S.A. - initial API and implementation
*******************************************************************************/
package org.eclipse.che.plugin.docker.client.helper;

import com.google.inject.ImplementedBy;

import java.net.InetAddress;
import java.util.Optional;

/**
* Provides helper method for the network
*
* @author Florent Benoit
*/
@ImplementedBy(DefaultNetworkFinder.class)
public interface NetworkFinder {

/**
* Gets the first inet address of a given network interface
*
* @param bridgeName
* name of the network interface
* @return only ipv4 ip of the given bridge if found
*/
Optional<InetAddress> getIPAddress(String bridgeName);

/**
* Search if a given network interface is matching the given subnet
*
* @param subnet
* the first digits of an ip address. Like 123.123.123
* @return optional ipv4 internet address if there was a matching one
*/
Optional<InetAddress> foundInetAddressMatching(String subnet);
}
Loading

0 comments on commit 14a393e

Please sign in to comment.