Skip to content

Commit

Permalink
Merge pull request #92 from lordofthejars/master
Browse files Browse the repository at this point in the history
Resolves issues #77 and #88 and adds ss command.
  • Loading branch information
lordofthejars committed Feb 1, 2015
2 parents f172189 + e900b51 commit ded85a8
Show file tree
Hide file tree
Showing 8 changed files with 132 additions and 27 deletions.
Expand Up @@ -28,7 +28,7 @@ public static final AwaitStrategy create(DockerClientExecutor dockerClientExecut

String strategy = ((String) awaitOptions.get(STRATEGY)).toLowerCase();
switch(strategy) {
case PollingAwaitStrategy.TAG: return new PollingAwaitStrategy(cube, awaitOptions);
case PollingAwaitStrategy.TAG: return new PollingAwaitStrategy(cube, dockerClientExecutor, awaitOptions);
case NativeAwaitStrategy.TAG: return new NativeAwaitStrategy(cube, dockerClientExecutor);
case StaticAwaitStrategy.TAG: return new StaticAwaitStrategy(cube, awaitOptions);
case SleepingAwaitStrategy.TAG: return new SleepingAwaitStrategy(cube, awaitOptions);
Expand Down
@@ -1,9 +1,12 @@
package org.arquillian.cube.impl.await;

import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.TimeUnit;
import java.util.logging.Logger;

import org.arquillian.cube.impl.docker.DockerClientExecutor;
import org.arquillian.cube.impl.util.IOUtil;
import org.arquillian.cube.impl.util.Ping;
import org.arquillian.cube.spi.Binding;
import org.arquillian.cube.spi.Binding.PortBinding;
Expand All @@ -18,24 +21,33 @@ public class PollingAwaitStrategy implements AwaitStrategy {
private static final int DEFAULT_POLL_ITERATIONS = 10;
private static final int DEFAULT_SLEEP_POLL_TIME = 500;
private static final TimeUnit DEFAULT_TIME_UNIT = TimeUnit.MILLISECONDS;
private static final String DEFAULT_POLL_TYPE = "ping";
private static final String POLLING_TIME = "sleepPollingTime";
private static final String ITERATIONS = "iterations";
private static final String POLL_TYPE = "type";

private int pollIterations = DEFAULT_POLL_ITERATIONS;
private int sleepPollTime = DEFAULT_SLEEP_POLL_TIME;
private TimeUnit timeUnit = DEFAULT_TIME_UNIT;

private String type = DEFAULT_POLL_TYPE;

private DockerClientExecutor dockerClientExecutor;
private Cube cube;

public PollingAwaitStrategy(Cube cube, Map<String, Object> params) {
public PollingAwaitStrategy(Cube cube, DockerClientExecutor dockerClientExecutor, Map<String, Object> params) {
this.cube = cube;
this.dockerClientExecutor = dockerClientExecutor;
if (params.containsKey(POLLING_TIME)) {
configurePollingTime(params);
}

if (params.containsKey(ITERATIONS)) {
this.pollIterations = (Integer) params.get(ITERATIONS);
}

if(params.containsKey(POLL_TYPE)) {
this.type = (String) params.get(POLL_TYPE);
}
}

private void configurePollingTime(Map<String, Object> params) {
Expand Down Expand Up @@ -70,18 +82,42 @@ public TimeUnit getTimeUnit() {
return timeUnit;
}

public String getType() {
return type;
}

@Override
public boolean await() {
Binding bindings = cube.bindings();

for (PortBinding ports : bindings.getPortBindings()) {
log.fine(String.format("Pinging host (gateway) %s and port %s", bindings.getIP(), ports.getBindingPort()));
if (!Ping.ping(bindings.getIP(), ports.getBindingPort(), this.pollIterations, this.sleepPollTime,
this.timeUnit)) {
return false;
log.fine(String.format("Pinging host %s and port %s with type", bindings.getIP(), ports.getBindingPort(), this.type));

switch(this.type) {
case "ping": {
if (!Ping.ping(bindings.getIP(), ports.getBindingPort(), this.pollIterations, this.sleepPollTime,
this.timeUnit)) {
return false;
}
}

break;
case "sscommand": {
if(!Ping.ping(dockerClientExecutor, cube.getId(), resolveCommand("ss", ports.getExposedPort()), this.pollIterations, this.sleepPollTime, this.timeUnit)) {
return false;
}
}
}

}

return true;
}

private String resolveCommand(String command, int port) {
Map<String, String> values = new HashMap<String, String>();
values.put("port", Integer.toString(port));
String templateContent = IOUtil.asStringPreservingNewLines(PollingAwaitStrategy.class.getResourceAsStream(command+".sh"));
return IOUtil.replacePlaceholders(templateContent, values);
}
}
Expand Up @@ -4,6 +4,7 @@
import org.arquillian.cube.spi.CubeRegistry;
import org.arquillian.cube.spi.event.CreateCube;
import org.arquillian.cube.spi.event.DestroyCube;
import org.arquillian.cube.spi.event.PreRunningCube;
import org.arquillian.cube.spi.event.StartCube;
import org.arquillian.cube.spi.event.StopCube;
import org.jboss.arquillian.core.api.annotation.Observes;
Expand All @@ -26,6 +27,10 @@ public void destroy(@Observes DestroyCube event, CubeRegistry registry) {
validateAndGet(registry, event.getCubeId()).destroy();
}

public void changeToPreRunning(@Observes PreRunningCube event, CubeRegistry registry) {
validateAndGet(registry, event.getCubeId()).changeToPreRunning();
}

private Cube validateAndGet(CubeRegistry registry, String cubeId) {
Cube cube = registry.getCube(cubeId);
if(cube == null) {
Expand Down
Expand Up @@ -468,6 +468,10 @@ public void pullImage(String imageName) {
IOUtil.asString(exec);
}

public String execStart(String containerId, String...commands) {
//TODO implement when docker-java API is updated.
return "1";
}

/**
* Get the URI of the docker host
Expand Down
@@ -1,5 +1,6 @@
package org.arquillian.cube.impl.util;

import java.net.URI;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
Expand All @@ -13,6 +14,8 @@

public final class BindingUtil {

private static final String LOCALHOST = "localhost";
private static final String UNIX_SOCKET_SCHEME = "unix";
public static final String PORTS_SEPARATOR = "->";
private static final String NO_GATEWAY = null;

Expand All @@ -24,15 +27,7 @@ public static Binding binding(DockerClientExecutor executor, String cubeId) {

HostConfig hostConfig = inspectResponse.getHostConfig();

/**
* This isn't actually the gateway IP of the host, it's the ip of the gateway within the docker runtime,
* which may not be accessible to us. We need the IP the client uses, that will be routable, and will
* allow us to validate the ports in later phases
String gatewayIp = inspectResponse.getNetworkSettings().getGateway();
**/

final String dockerIp = executor.getDockerUri().getHost();
String dockerIp = getDockerServerIp(executor);
Binding binding = new Binding(dockerIp);

for (Entry<ExposedPort, com.github.dockerjava.api.model.Ports.Binding[]> bind : hostConfig.getPortBindings()
Expand All @@ -45,6 +40,12 @@ public static Binding binding(DockerClientExecutor executor, String cubeId) {
return binding;
}

private static String getDockerServerIp(DockerClientExecutor executor) {
URI dockerUri = executor.getDockerUri();
String dockerIp = UNIX_SOCKET_SCHEME.equalsIgnoreCase(dockerUri.getScheme()) ? LOCALHOST : dockerUri.getHost();
return dockerIp;
}

public static Binding binding(Map<String, Object> cubeConfiguration) {

Binding binding = new Binding(NO_GATEWAY);
Expand Down
33 changes: 33 additions & 0 deletions docker/src/main/java/org/arquillian/cube/impl/util/Ping.java
Expand Up @@ -5,12 +5,32 @@
import java.net.UnknownHostException;
import java.util.concurrent.TimeUnit;

import org.arquillian.cube.impl.docker.DockerClientExecutor;

public final class Ping {

private Ping() {
super();
}

public static boolean ping(DockerClientExecutor dockerClientExecutor, String containerId, String command, int totalIterations, long sleep, TimeUnit timeUnit) {
boolean result = false;
int iteration = 0;

do {
result = execContainerPing(dockerClientExecutor, containerId, command);
if(!result) {
iteration++;
try {
timeUnit.sleep(sleep);
} catch (InterruptedException e) {
}
}
} while(!result && iteration < totalIterations);

return result;
}

public static boolean ping(String host, int port, int totalIterations, long sleep, TimeUnit timeUnit) {
boolean result = false;
int iteration = 0;
Expand All @@ -29,6 +49,19 @@ public static boolean ping(String host, int port, int totalIterations, long slee
return result;
}


private static boolean execContainerPing(DockerClientExecutor dockerClientExecutor, String containerId, String command) {
String result = dockerClientExecutor.execStart(containerId, command);
try {
int numberOfListenConnectons = Integer.parseInt(result.trim());
//This number is based in that a port will be opened only as tcp or as udp.
//We will need another issue to modify cube internals to save if port is udp or tcp.
return numberOfListenConnectons > 0;
} catch(NumberFormatException e) {
return false;
}
}

private static boolean ping(String host, int port) {
Socket socket = null;
try {
Expand Down
@@ -0,0 +1 @@
ss -lntu | grep '${port}' | grep LISTEN -c
Expand Up @@ -68,14 +68,6 @@ public class AwaitStrategyTest {
" sleepPollingTime: 200\n" +
" iterations: 3";

private static final String CONTENT_WITH_POLLING_STRATEGY_WITHOUT_DEFAULTS_AND_UNIT = "tomcat:\n" +
" image: tutum/tomcat:7.0\n" +
" exposedPorts: [8089/tcp]\n" +
" await:\n" +
" strategy: polling\n" +
" sleepPollingTime: 200 s\n" +
" iterations: 3";

private static final String CONTENT_WITH_NATIVE_STRATEGY = "tomcat:\n" +
" image: tutum/tomcat:7.0\n" +
" exposedPorts: [8089/tcp]\n" +
Expand All @@ -89,7 +81,24 @@ public class AwaitStrategyTest {
" await:\n" +
" strategy: sleeping\n" +
" sleepTime: 200 s\n";


private static final String CONTENT_WITH_POLLING_STRATEGY_WITHOUT_DEFAULTS_AND_UNIT = "tomcat:\n" +
" image: tutum/tomcat:7.0\n" +
" exposedPorts: [8089/tcp]\n" +
" await:\n" +
" strategy: polling\n" +
" sleepPollingTime: 200 s\n" +
" iterations: 3";

private static final String CONTENT_WITH_SSCOMMAND_STRATEGY = "tomcat:\n" +
" image: tutum/tomcat:7.0\n" +
" exposedPorts: [8089/tcp]\n" +
" await:\n" +
" strategy: polling\n" +
" sleepPollingTime: 200 s\n" +
" iterations: 3\n" +
" type: sscommand";

@Mock
private Cube cube;

Expand Down Expand Up @@ -206,7 +215,7 @@ public void should_create_sleeping_await_strategy_with_specific_times() {
assertThat(strategy, instanceOf(SleepingAwaitStrategy.class));
assertThat(((SleepingAwaitStrategy)strategy).getSleepTime(), is(200));
}

@Test
public void should_create_polling_await_strategy_with_specific_times_and_unit() {

Expand All @@ -221,8 +230,23 @@ public void should_create_polling_await_strategy_with_specific_times_and_unit()
assertThat(((PollingAwaitStrategy)strategy).getPollIterations(), is(3));
assertThat(((PollingAwaitStrategy)strategy).getSleepPollTime(), is(200));
assertThat(((PollingAwaitStrategy)strategy).getTimeUnit(), is(TimeUnit.SECONDS));
assertThat(((PollingAwaitStrategy)strategy).getType(), is("ping"));
}


@Test
public void should_create_polling_await_strategy_with_specific_type() {

@SuppressWarnings("unchecked")
Map<String, Object> content = (Map<String, Object>) new Yaml().load(CONTENT_WITH_SSCOMMAND_STRATEGY);
@SuppressWarnings("unchecked")
Map<String, Object> tomcatConfig = (Map<String, Object>) content.get("tomcat");

AwaitStrategy strategy = AwaitStrategyFactory.create(null, cube, tomcatConfig);

assertThat(strategy, instanceOf(PollingAwaitStrategy.class));
assertThat(((PollingAwaitStrategy)strategy).getType(), is("sscommand"));
}

@Test
public void should_create_native_await_strategy() {

Expand All @@ -235,4 +259,5 @@ public void should_create_native_await_strategy() {

assertThat(strategy, instanceOf(NativeAwaitStrategy.class));
}

}

0 comments on commit ded85a8

Please sign in to comment.