diff options
author | Haakon Dybdahl <dybis@users.noreply.github.com> | 2016-10-13 14:38:25 +0200 |
---|---|---|
committer | GitHub <noreply@github.com> | 2016-10-13 14:38:25 +0200 |
commit | 4781e77d144f932913d0e37924ed931bbd880e13 (patch) | |
tree | c46acd24b27f5f076d81020cbc638afe296f96e5 /docker-api | |
parent | 9fb11324f8aa114d71889400887e9ffb26f58057 (diff) | |
parent | 91f0a2832dec65d04b8c26dc85fb810aa20b15e5 (diff) |
Merge pull request #855 from yahoo/freva/DockerTest-linux-fix-with-network
Freva/docker test linux fix with network
Diffstat (limited to 'docker-api')
4 files changed, 63 insertions, 90 deletions
diff --git a/docker-api/src/main/java/com/yahoo/vespa/hosted/dockerapi/CreateContainerCommandImpl.java b/docker-api/src/main/java/com/yahoo/vespa/hosted/dockerapi/CreateContainerCommandImpl.java index 883c94cc5b7..609896a01bb 100644 --- a/docker-api/src/main/java/com/yahoo/vespa/hosted/dockerapi/CreateContainerCommandImpl.java +++ b/docker-api/src/main/java/com/yahoo/vespa/hosted/dockerapi/CreateContainerCommandImpl.java @@ -130,7 +130,7 @@ class CreateContainerCommandImpl implements Docker.CreateContainerCommand { List<String> labelList = labels.entrySet().stream() .map(entry -> entry.getKey() + "=" + entry.getValue()).collect(Collectors.toList()); - return "--name " + containerName + " " + return "--name " + containerName.asString() + " " + "--hostname " + hostName + " " + toRepeatedOption("--label", labelList) + toRepeatedOption("--env", environmentAssignments) @@ -139,7 +139,7 @@ class CreateContainerCommandImpl implements Docker.CreateContainerCommand { + toOptionalOption("--net", networkMode) + toOptionalOption("--ip", ipv4Address) + toOptionalOption("--ip6", ipv6Address) - + dockerImage; + + dockerImage.asString(); } private String generateRandomMACAddress() { diff --git a/docker-api/src/main/java/com/yahoo/vespa/hosted/dockerapi/DockerImpl.java b/docker-api/src/main/java/com/yahoo/vespa/hosted/dockerapi/DockerImpl.java index 9142009a48f..5e5185bc6be 100644 --- a/docker-api/src/main/java/com/yahoo/vespa/hosted/dockerapi/DockerImpl.java +++ b/docker-api/src/main/java/com/yahoo/vespa/hosted/dockerapi/DockerImpl.java @@ -79,9 +79,6 @@ public class DockerImpl implements Docker { private CounterWrapper numberOfDockerDaemonFails; private final boolean hackAroundPullImageDueToJerseyConflicts; - private JerseyDockerCmdExecFactory dockerFactory; - private RemoteApiVersion remoteApiVersion; - // For testing DockerImpl(final DockerClient dockerClient) { hackAroundPullImageDueToJerseyConflicts = false; @@ -89,7 +86,7 @@ public class DockerImpl implements Docker { } // For testing - public DockerImpl(final DockerConfig config) { + DockerImpl(final DockerConfig config) { hackAroundPullImageDueToJerseyConflicts = false; // Fail fast initDockerConnection(config, 100 /* connect timeout millis */, false /* fallback to 1.23 on errors */); @@ -532,12 +529,13 @@ public class DockerImpl implements Docker { } } - private void initDockerConnection(final DockerConfig config, int connectTimeousMs, boolean fallbackTo123orErrors) { - dockerFactory = new JerseyDockerCmdExecFactory() + private void initDockerConnection(final DockerConfig config, int connectTimeoutMs, boolean fallbackTo123orErrors) { + JerseyDockerCmdExecFactory dockerFactory = new JerseyDockerCmdExecFactory() .withMaxPerRouteConnections(DOCKER_MAX_PER_ROUTE_CONNECTIONS) .withMaxTotalConnections(DOCKER_MAX_TOTAL_CONNECTIONS) - .withConnectTimeout(connectTimeousMs) + .withConnectTimeout(connectTimeoutMs) .withReadTimeout(DOCKER_READ_TIMEOUT_MILLIS); + RemoteApiVersion remoteApiVersion; try { remoteApiVersion = RemoteApiVersion.parseConfig(DockerClientImpl.getInstance( buildDockerClientConfig(config).build()) @@ -571,7 +569,7 @@ public class DockerImpl implements Docker { numberOfRunningContainersGauge = metricReceiver.declareGauge(dimensions, "containers.running"); numberOfDockerDaemonFails = metricReceiver.declareCounter(dimensions, "daemon.api_fails"); - // Some containers could already be running, count them and intialize to that value + // Some containers could already be running, count them and initialize to that value numberOfRunningContainersGauge.sample(getAllManagedContainers().size()); } } diff --git a/docker-api/src/test/java/com/yahoo/vespa/hosted/dockerapi/DockerTest.java b/docker-api/src/test/java/com/yahoo/vespa/hosted/dockerapi/DockerTest.java index 95c71d0780e..a77776f53dc 100644 --- a/docker-api/src/test/java/com/yahoo/vespa/hosted/dockerapi/DockerTest.java +++ b/docker-api/src/test/java/com/yahoo/vespa/hosted/dockerapi/DockerTest.java @@ -3,19 +3,12 @@ package com.yahoo.vespa.hosted.dockerapi; import com.github.dockerjava.api.model.Network; import com.github.dockerjava.core.command.BuildImageResultCallback; -import com.yahoo.metrics.simple.MetricReceiver; -import com.yahoo.vespa.hosted.dockerapi.metrics.MetricReceiverWrapper; -import org.junit.Ignore; import org.junit.Test; import java.io.File; import java.io.IOException; import java.io.InputStream; -import java.net.Inet6Address; import java.net.InetAddress; -import java.net.NetworkInterface; -import java.net.SocketException; -import java.util.Collections; import java.util.HashSet; import java.util.List; import java.util.Set; @@ -35,33 +28,23 @@ import static org.junit.Assume.assumeTrue; * 1. Install Docker Toolbox, and start it (Docker Quick Start Terminal) (you can close terminal window afterwards) * 2. Run tests from IDE/mvn. * - * LINUX: - * 1. Remove Ignore annotations - * 2. Change ownership of docker.sock - * $ sudo chown <your username> /var/run/docker.sock - * 3. (Temporary) Manually create the docker network used by DockerImpl by running: - * $ sudo docker network create --ipv6 --gateway=<your local IPv6 address> --subnet=fe80::1/16 habla - * 4. (Temporary) Manually build docker test image. Inside src/test/resources/simple-ipv6-server run: - * $ sudo docker build -t "simple-ipv6-server:Dockerfile" . - * 5. (Temporary) Comment out createDockerImage() and shutdown() - * * @author valerijf * @author dybdahl */ public class DockerTest { private DockerImpl docker; - private static final boolean isMacOSX = System.getProperty("os.name").equals("Mac OS X"); + private static final OS operatingSystem = getSystemOS(); private static final String prefix = "/Users/" + System.getProperty("user.name") + "/.docker/machine/machines/default/"; private static final DockerConfig dockerConfig = new DockerConfig(new DockerConfig.Builder() - .caCertPath(isMacOSX ? prefix + "ca.pem" : "") - .clientCertPath(isMacOSX ? prefix + "cert.pem" : "") - .clientKeyPath(isMacOSX ? prefix + "key.pem" : "") - .uri(isMacOSX ? "tcp://192.168.99.100:2376" : "unix:///var/run/docker.sock")); + .caCertPath(operatingSystem == OS.Mac_OS_X ? prefix + "ca.pem" : "") + .clientCertPath(operatingSystem == OS.Mac_OS_X ? prefix + "cert.pem" : "") + .clientKeyPath(operatingSystem == OS.Mac_OS_X ? prefix + "key.pem" : "") + .uri(operatingSystem == OS.Mac_OS_X ? "tcp://192.168.99.100:2376" : "tcp://localhost:2376")); private static final DockerImage dockerImage = new DockerImage("simple-ipv6-server:Dockerfile"); @Test public void testGetAllManagedContainersNoContainersRunning() { - assumeTrue(isMacOSX); + assumeTrue(operatingSystem != OS.Unsupported); assumeTrue(dockerDaemonIsPresent()); List<Container> containers = docker.getAllManagedContainers(); @@ -94,6 +77,7 @@ public class DockerTest { public void testCreateDeleteImageCreateDeleteContainer() throws IOException, InterruptedException, ExecutionException { assumeTrue(dockerDaemonIsPresent()); createDockerImage(docker); + ContainerName containerName = new ContainerName("foo"); docker.stopContainer(containerName); docker.deleteContainer(containerName); @@ -117,25 +101,32 @@ public class DockerTest { assertFalse("Failed to delete " + dockerImage.asString() + " image", docker.imageIsDownloaded(dockerImage)); } - @Ignore @Test public void testDockerNetworking() throws InterruptedException, ExecutionException, IOException { + assumeTrue(dockerDaemonIsPresent()); + createDockerImage(docker); + String hostName1 = "docker10.test.yahoo.com"; String hostName2 = "docker11.test.yahoo.com"; ContainerName containerName1 = new ContainerName("test-container-1"); ContainerName containerName2 = new ContainerName("test-container-2"); - InetAddress inetAddress1 = Inet6Address.getByName("fe80::10"); - InetAddress inetAddress2 = Inet6Address.getByName("fe80::11"); - DockerImpl docker = new DockerImpl(dockerConfig, new MetricReceiverWrapper(MetricReceiver.nullImplementation)); + InetAddress inetAddress1 = InetAddress.getByName("172.18.0.10"); + InetAddress inetAddress2 = InetAddress.getByName("172.18.0.11"); + + docker.createContainerCommand(dockerImage, containerName1, hostName1) + .withNetworkMode(DockerImpl.DOCKER_CUSTOM_MACVLAN_NETWORK_NAME).withIpAddress(inetAddress1).create(); + + docker.createContainerCommand(dockerImage, containerName2, hostName2) + .withNetworkMode(DockerImpl.DOCKER_CUSTOM_MACVLAN_NETWORK_NAME).withIpAddress(inetAddress2).create(); - docker.createContainerCommand(dockerImage, containerName1, hostName1).withIpAddress(inetAddress1).create(); - docker.createContainerCommand(dockerImage, containerName2, hostName2).withIpAddress(inetAddress2).create(); + docker.startContainer(containerName1); + docker.startContainer(containerName2); try { testReachabilityFromHost(containerName1, inetAddress1); testReachabilityFromHost(containerName2, inetAddress2); - String[] curlFromNodeToNode = new String[]{"curl", "-g", "http://[" + inetAddress2 + "%eth0]/ping"}; + String[] curlFromNodeToNode = new String[]{"curl", "-g", "http://" + inetAddress2 + "/ping"}; while (! docker.executeInContainer(containerName1, curlFromNodeToNode).isSuccess()) { Thread.sleep(20); } @@ -151,41 +142,40 @@ public class DockerTest { } } + private void createDockerTestNetworkIfNeeded() { + if (! docker.dockerClient.listNetworksCmd().withNameFilter(DockerImpl.DOCKER_CUSTOM_MACVLAN_NETWORK_NAME).exec().isEmpty()) return; + + Network.Ipam ipam = new Network.Ipam().withConfig(new Network.Ipam.Config().withSubnet("172.18.0.0/16")); + docker.dockerClient.createNetworkCmd() + .withName(DockerImpl.DOCKER_CUSTOM_MACVLAN_NETWORK_NAME).withDriver("bridge").withIpam(ipam).exec(); + } + private boolean dockerDaemonIsPresent() { - if (!isMacOSX) { + if (docker != null) return true; + if (operatingSystem == OS.Unsupported) { System.out.println("This test does not support " + System.getProperty("os.name") + " yet, ignoring test."); return false; } + try { - docker = new DockerImpl(dockerConfig, new MetricReceiverWrapper(MetricReceiver.nullImplementation)); + docker = new DockerImpl(dockerConfig); + createDockerTestNetworkIfNeeded(); return true; } catch (Exception e) { System.out.println("Please install Docker Toolbox and start Docker Quick Start Terminal once, ignoring test."); + e.printStackTrace(); return false; } } private void testReachabilityFromHost(ContainerName containerName, InetAddress target) throws IOException, InterruptedException { - String[] curlNodeFromHost = {"curl", "-g", "http://[" + target.getHostAddress() + "%" + getInterfaceName() + "]/ping"}; + String[] curlNodeFromHost = {"curl", "-g", "http://" + target.getHostAddress() + "/ping"}; while (!exec(curlNodeFromHost).equals("pong\n")) { Thread.sleep(20); } assertTrue("Could not reach " + containerName.asString() + " from host", exec(curlNodeFromHost).equals("pong\n")); } - - /** - * Returns the display name of the bridge used by our custom docker network. This is used for routing in the - * network tests. The bridge is assumed to be the only IPv6 interface starting with "br-" - */ - private static String getInterfaceName() throws SocketException { - return Collections.list(NetworkInterface.getNetworkInterfaces()).stream() - .filter(networkInterface -> networkInterface.getDisplayName().startsWith("br-") && - networkInterface.getInterfaceAddresses().stream() - .anyMatch(ip -> ip.getAddress() instanceof Inet6Address)) - .findFirst().orElseThrow(RuntimeException::new).getDisplayName(); - } - /** * Synchronously executes a system process and returns its stdout. Based of {@link com.yahoo.system.ProcessExecuter} * but could not be reused because of import errors. @@ -208,38 +198,11 @@ public class DockerTest { return ret.toString(); } - /** - * Returns IPv6 address of on the "docker0" interface that can be reached by the containers - */ - private static String getLocalIPv6Address() throws SocketException { - return Collections.list(NetworkInterface.getNetworkInterfaces()).stream() - .filter(networkInterface -> networkInterface.getDisplayName().equals("docker0")) - .flatMap(i -> Collections.list(i.getInetAddresses()).stream()) - .filter(ip -> ip instanceof Inet6Address && ip.isLinkLocalAddress()) - .findFirst().orElseThrow(RuntimeException::new) - .getHostAddress().split("%")[0]; - } - - private void createSomeNetork() throws SocketException { - Network.Ipam ipam = new Network.Ipam().withConfig(new Network.Ipam.Config() - .withSubnet("fe80::1/16").withGateway(getLocalIPv6Address())); - // TODO: This needs to match the network name in DockerOperations!? - docker.dockerClient.createNetworkCmd().withDriver("bridge").withName("habla") - .withIpam(ipam).exec(); - } - - private void remoteNetwork() { - // Remove the network we created earlier - // TODO: This needs to match the network name in DockerOperations!? - docker.dockerClient.removeNetworkCmd("habla").exec(); - } - - // TODO: Do we need this? Rather use network created by DockerImpl, right? - void createDockerImage(DockerImpl docker) throws IOException, ExecutionException, InterruptedException { + private void createDockerImage(DockerImpl docker) throws IOException, ExecutionException, InterruptedException { try { docker.deleteImage(new DockerImage(dockerImage.asString())); } catch (Exception e) { - assertThat(e.getMessage(), is("Failed to delete docker image simple-ipv6-server:Dockerfile")); + assertThat(e.getMessage(), is("Failed to delete docker image " + dockerImage.asString())); } // Build the image locally @@ -248,4 +211,14 @@ public class DockerTest { .buildImageCmd(dockerFilePath) .withTag(dockerImage.asString()).exec(new BuildImageResultCallback()).awaitCompletion(); } + + private enum OS {Linux, Mac_OS_X, Unsupported} + + private static OS getSystemOS() { + switch (System.getProperty("os.name").toLowerCase()) { + case "linux": return OS.Linux; + case "mac os x": return OS.Mac_OS_X; + default: return OS.Unsupported; + } + } } diff --git a/docker-api/src/test/resources/simple-ipv6-server/src/server.py b/docker-api/src/test/resources/simple-ipv6-server/src/server.py index 320f311e0fc..c28c288bbca 100644 --- a/docker-api/src/test/resources/simple-ipv6-server/src/server.py +++ b/docker-api/src/test/resources/simple-ipv6-server/src/server.py @@ -28,14 +28,16 @@ class MyHandler(SimpleHTTPRequestHandler): return -class HTTPServerV6(HTTPServer): - address_family = socket.AF_INET6 +class DualHTTPServer(HTTPServer): + def __init__(self, address, handler): + self.address_family = socket.AF_INET6 if (':' in address[0]) else socket.AF_INET + HTTPServer.__init__(self, address, handler) -def main(): - server = HTTPServerV6(('::', 80), MyHandler) +def main(ipv6): + server = DualHTTPServer(('::' if ipv6 else '', 80), MyHandler) server.serve_forever() if __name__ == '__main__': - main() + main(False) |