Skip to content

Commit

Permalink
Merge pull request #111 from lordofthejars/master
Browse files Browse the repository at this point in the history
resolves issue #110 by adding connection mode
  • Loading branch information
lordofthejars committed Feb 23, 2015
2 parents b6d8e47 + d38b7d0 commit d83fd56
Show file tree
Hide file tree
Showing 8 changed files with 163 additions and 32 deletions.
@@ -0,0 +1,21 @@
package org.arquillian.cube.impl.client;

public enum ConnectionMode {
STARTANDSTOP(false, true), STARTORCONNECT(true, true), STARTORCONNECTANDLEAVE(true, false);

private boolean allowReconnect = false;
private boolean stoppable = true;

private ConnectionMode(boolean allowReconnect, boolean stoppable) {
this.allowReconnect = allowReconnect;
this.stoppable = stoppable;
}

public boolean isAllowReconnect() {
return allowReconnect;
}

public boolean isStoppable() {
return stoppable;
}
}
Expand Up @@ -19,13 +19,13 @@ public class CubeConfiguration {
private static final String DOCKER_CONTAINERS_FILE = "dockerContainersFile";
private static final String DOCKER_REGISTRY = "dockerRegistry";
private static final String AUTO_START_CONTAINERS = "autoStartContainers";
private static final String SHOULD_ALLOW_TO_CONNECT_TO_RUNNING_CONTAINERS = "shouldAllowToConnectToRunningContainers";
private static final String CONNECTION_MODE = "connectionMode";
private static final String BOOT2DOCKER_PATH = "boot2dockerPath";

private String dockerServerVersion;
private String dockerServerUri;
private String dockerRegistry;
private boolean shouldAllowToConnectToRunningContainers = false;
private ConnectionMode connectionMode = ConnectionMode.STARTANDSTOP;
private String boot2DockerPath;
private String username;
private String password;
Expand All @@ -35,8 +35,8 @@ public class CubeConfiguration {

private Map<String, Object> dockerContainersContent;

public boolean shouldAllowToConnectToRunningContainers() {
return shouldAllowToConnectToRunningContainers;
public ConnectionMode getConnectionMode() {
return connectionMode;
}

public String getDockerServerUri() {
Expand Down Expand Up @@ -133,8 +133,8 @@ public static CubeConfiguration fromMap(Map<String, String> map) {
cubeConfiguration.autoStartContainers = ConfigUtil.trim(map.get(AUTO_START_CONTAINERS).split(","));
}

if(map.containsKey(SHOULD_ALLOW_TO_CONNECT_TO_RUNNING_CONTAINERS)) {
cubeConfiguration.shouldAllowToConnectToRunningContainers = Boolean.parseBoolean(map.get(SHOULD_ALLOW_TO_CONNECT_TO_RUNNING_CONTAINERS));
if(map.containsKey(CONNECTION_MODE)) {
cubeConfiguration.connectionMode = ConnectionMode.valueOf(ConnectionMode.class, map.get(CONNECTION_MODE));
}
return cubeConfiguration;
}
Expand Down
Expand Up @@ -35,21 +35,21 @@ public class CubeSuiteLifecycleController {

public void startAutoContainers(@Observes(precedence = 100) BeforeSuite event, final CubeConfiguration configuration) {
List<String[]> autoStartSteps = AutoStartOrderUtil.getAutoStartOrder(configuration);
startAllSteps(autoStartSteps, configuration.shouldAllowToConnectToRunningContainers());
startAllSteps(autoStartSteps, configuration.getConnectionMode());
}

public void stopAutoContainers(@Observes(precedence = -100) AfterSuite event, CubeConfiguration configuration) {
List<String[]> autoStopSteps = AutoStartOrderUtil.getAutoStopOrder(configuration);
stopAllSteps(autoStopSteps);
}

private void startAllSteps(List<String[]> autoStartSteps, boolean allowToConnectToRunningContainers) {
private void startAllSteps(List<String[]> autoStartSteps, ConnectionMode connectionMode) {
for(final String[] cubeIds : autoStartSteps) {
Map<String, Future<RuntimeException>> stepStatus = new HashMap<>();

// Start
for(final String cubeId : cubeIds) {
Future<RuntimeException> result = executorServiceInst.get().submit(new StartCubes(cubeId, allowToConnectToRunningContainers));
Future<RuntimeException> result = executorServiceInst.get().submit(new StartCubes(cubeId, connectionMode));
stepStatus.put(cubeId, result);
}

Expand Down Expand Up @@ -100,22 +100,28 @@ private boolean isCubeRunning(String cube) {
}

private final class StartCubes implements Callable<RuntimeException> {
private final boolean allowToConnectToRunningContainers;
private final ConnectionMode connectionMode;
private final String cubeId;

private StartCubes(String cubeId, boolean shouldAllowToConnectToRunningContainers) {
private StartCubes(String cubeId, ConnectionMode connectionMode) {
this.cubeId = cubeId;
this.allowToConnectToRunningContainers = shouldAllowToConnectToRunningContainers;
this.connectionMode = connectionMode;
}

@Override
public RuntimeException call() throws Exception {
try {
if(allowToConnectToRunningContainers && isCubeRunning(cubeId)) {
if(connectionMode.isAllowReconnect() && isCubeRunning(cubeId)) {
controlEvent.fire(new PreRunningCube(cubeId));
} else {
controlEvent.fire(new CreateCube(cubeId));
controlEvent.fire(new StartCube(cubeId));
return null;
}
controlEvent.fire(new CreateCube(cubeId));
controlEvent.fire(new StartCube(cubeId));

if(connectionMode.isAllowReconnect() && !connectionMode.isStoppable()) {
// If we allow reconnections and containers are none stoppable which means that they will be able to be
// reused in next executions then at this point we can assume that the container is a prerunning container.
controlEvent.fire(new PreRunningCube(cubeId));
}
} catch(RuntimeException e) {
return e;
Expand Down
Expand Up @@ -2,6 +2,7 @@

import java.util.List;

import org.arquillian.cube.impl.client.ConnectionMode;
import org.arquillian.cube.impl.client.CubeConfiguration;
import org.arquillian.cube.impl.docker.DockerClientExecutor;
import org.arquillian.cube.impl.util.ContainerUtil;
Expand Down Expand Up @@ -29,7 +30,7 @@ public class CubeContainerLifecycleController {

@Inject
private Instance<DockerClientExecutor> dockerClientExecutor;

public void startCubeMappedContainer(@Observes BeforeStart event, CubeRegistry cubeRegistry,
ContainerRegistry containerRegistry, CubeConfiguration cubeConfiguration) {
Container container = ContainerUtil.getContainerByDeployableContainer(containerRegistry,
Expand All @@ -42,14 +43,22 @@ public void startCubeMappedContainer(@Observes BeforeStart event, CubeRegistry c
if (cube == null) {
return; // No Cube found matching Container name, not managed by Cube
}
ConnectionMode connectionMode = cubeConfiguration.getConnectionMode();

if(cubeConfiguration.shouldAllowToConnectToRunningContainers() && isCubeRunning(cube)) {
if (connectionMode.isAllowReconnect() && isCubeRunning(cube)) {
controlEvent.fire(new PreRunningCube(cube));
return; //Container is already running and user has configured to reuse it.
return;
}

controlEvent.fire(new CreateCube(cube));
controlEvent.fire(new StartCube(cube));

if (connectionMode.isAllowReconnect() && !connectionMode.isStoppable()) {
// If we allow reconnections and containers are none stoppable which means that they will be able to be
// reused in next executions then at this point we can assume that the container is a prerunning container.

controlEvent.fire(new PreRunningCube(cube));
}
}

public void stopCubeMappedContainer(@Observes AfterStop event, CubeRegistry cubeRegistry,
Expand All @@ -68,19 +77,22 @@ public void stopCubeMappedContainer(@Observes AfterStop event, CubeRegistry cube
controlEvent.fire(new StopCube(cube));
controlEvent.fire(new DestroyCube(cube));
}

private boolean isCubeRunning(Cube cube) {
//TODO should we create an adapter class so we don't expose client classes in this part?
List<com.github.dockerjava.api.model.Container> runningContainers = dockerClientExecutor.get().listRunningContainers();
// TODO should we create an adapter class so we don't expose client classes in this part?
List<com.github.dockerjava.api.model.Container> runningContainers = dockerClientExecutor.get()
.listRunningContainers();
for (com.github.dockerjava.api.model.Container container : runningContainers) {
for (String name : container.getNames()) {
if(name.startsWith("/")) name = name.substring(1); //Names array adds an slash to the docker name container.
if(name.equals(cube.getId())) { //cube id is the container name in docker0 Id in docker is the hash that identifies it.
if (name.startsWith("/"))
name = name.substring(1); // Names array adds an slash to the docker name container.
if (name.equals(cube.getId())) { // cube id is the container name in docker0 Id in docker is the hash
// that identifies it.
return true;
}
}
}

return false;
}
}
Expand Up @@ -54,6 +54,7 @@
import com.github.dockerjava.api.model.Ports.Binding;
import com.github.dockerjava.api.model.RestartPolicy;
import com.github.dockerjava.api.model.Volume;
import com.github.dockerjava.api.model.VolumesFrom;
import com.github.dockerjava.core.DockerClientBuilder;
import com.github.dockerjava.core.DockerClientConfig;
import com.github.dockerjava.core.DockerClientConfig.DockerClientConfigBuilder;
Expand Down Expand Up @@ -282,7 +283,7 @@ public String createContainer(String name, Map<String, Object> containerConfigur

if (containerConfiguration.containsKey(VOLUMES_FROM)) {
List<String> volumesFrom = asListOfString(containerConfiguration, VOLUMES_FROM);
createContainerCmd.withVolumesFrom(volumesFrom.toArray(new String[volumesFrom.size()]));
createContainerCmd.withVolumesFrom(toVolumesFrom(volumesFrom));
}

try {
Expand Down Expand Up @@ -755,6 +756,15 @@ private static final Volume[] toVolumes(List<String> volumesList) {
return volumes;
}

private static final VolumesFrom[] toVolumesFrom(List<String> volumesFromList) {
VolumesFrom[] volumesFrom = new VolumesFrom[volumesFromList.size()];

for(int i = 0; i < volumesFromList.size(); i++) {
volumesFrom[i] = VolumesFrom.parse(volumesFromList.get(i));
}
return volumesFrom;
}

@SuppressWarnings("unchecked")
private static final List<Map<String, Object>> asListOfMap(Map<String, Object> map, String property) {
return (List<Map<String, Object>>) map.get(property);
Expand Down
Expand Up @@ -77,7 +77,7 @@ public void shouldStopAndDestroyAutoContainers() {
public void shouldUsePreRunningContainers() {
Map<String, String> data = new HashMap<String, String>();
data.put("autoStartContainers", "a,b");
data.put("shouldAllowToConnectToRunningContainers", "true");
data.put("connectionMode", ConnectionMode.STARTORCONNECT.name());
data.put("dockerContainers", "a:\n image: a\nb:\n image: a\n");

CubeConfiguration configuration = CubeConfiguration.fromMap(data);
Expand All @@ -96,4 +96,51 @@ public void shouldUsePreRunningContainers() {
assertEventFiredOnOtherThread(StartCube.class);
assertEventFiredOnOtherThread(PreRunningCube.class);
}

@Test
public void shouldStartAContainerInStartOrConnectModeAndStopIt() {
Map<String, String> data = new HashMap<String, String>();
data.put("autoStartContainers", "a,b");
data.put("connectionMode", ConnectionMode.STARTORCONNECT.name());
data.put("dockerContainers", "a:\n image: a\nb:\n image: a\n");

CubeConfiguration configuration = CubeConfiguration.fromMap(data);
bind(ApplicationScoped.class, CubeConfiguration.class, configuration);
Container container = mock(Container.class);
when(container.getNames()).thenReturn(new String[]{"alreadyrun"});
when(executor.listRunningContainers()).thenReturn(Arrays.asList(container));
bind(ApplicationScoped.class, DockerClientExecutor.class, executor);

fire(new BeforeSuite());

assertEventFired(CreateCube.class, 2);
assertEventFired(StartCube.class, 2);
assertEventFired(PreRunningCube.class, 0);
assertEventFiredOnOtherThread(CreateCube.class);
assertEventFiredOnOtherThread(StartCube.class);
}

@Test
public void shouldStartAContainerInStartOrConnectAndLeaveModeAndNotStopIt() {
Map<String, String> data = new HashMap<String, String>();
data.put("autoStartContainers", "a,b");
data.put("connectionMode", ConnectionMode.STARTORCONNECTANDLEAVE.name());
data.put("dockerContainers", "a:\n image: a\nb:\n image: a\n");

CubeConfiguration configuration = CubeConfiguration.fromMap(data);
bind(ApplicationScoped.class, CubeConfiguration.class, configuration);
Container container = mock(Container.class);
when(container.getNames()).thenReturn(new String[]{"alreadyrun"});
when(executor.listRunningContainers()).thenReturn(Arrays.asList(container));
bind(ApplicationScoped.class, DockerClientExecutor.class, executor);

fire(new BeforeSuite());

assertEventFired(CreateCube.class, 2);
assertEventFired(StartCube.class, 2);
assertEventFired(PreRunningCube.class, 2);
assertEventFiredOnOtherThread(CreateCube.class);
assertEventFiredOnOtherThread(StartCube.class);
assertEventFiredOnOtherThread(PreRunningCube.class);
}
}
Expand Up @@ -8,6 +8,7 @@
import java.util.List;
import java.util.Map;

import org.arquillian.cube.impl.client.ConnectionMode;
import org.arquillian.cube.impl.client.CubeConfiguration;
import org.arquillian.cube.impl.docker.DockerClientExecutor;
import org.arquillian.cube.impl.model.DockerCubeRegistry;
Expand Down Expand Up @@ -81,9 +82,9 @@ public void setup() {
}

@Test
public void shouldUsePreRunningContainer() {
public void shouldUsePreRunningContainerInStartOrConnectMode() {
Map<String, String> data = new HashMap<String, String>();
data.put("shouldAllowToConnectToRunningContainers", "true");
data.put("connectionMode", ConnectionMode.STARTORCONNECT.name());
bind(ApplicationScoped.class, CubeConfiguration.class, CubeConfiguration.fromMap(data));

com.github.dockerjava.api.model.Container container = mock(com.github.dockerjava.api.model.Container.class);
Expand All @@ -94,7 +95,41 @@ public void shouldUsePreRunningContainer() {
fire(new BeforeStart(deployableContainer));
assertEventFired(PreRunningCube.class, 1);
}


@Test
public void shouldStartAContainerInStartOrConnectModeAndStopIt() {
Map<String, String> data = new HashMap<String, String>();
data.put("connectionMode", ConnectionMode.STARTORCONNECT.name());
bind(ApplicationScoped.class, CubeConfiguration.class, CubeConfiguration.fromMap(data));

com.github.dockerjava.api.model.Container container = mock(com.github.dockerjava.api.model.Container.class);
when(container.getNames()).thenReturn(new String[]{CUBE_ID+"1"});
when(executor.listRunningContainers()).thenReturn(Arrays.asList(container));
bind(ApplicationScoped.class, DockerClientExecutor.class, executor);

fire(new BeforeStart(deployableContainer));
assertEventFired(CreateCube.class, 1);
assertEventFired(StartCube.class, 1);
assertEventFired(PreRunningCube.class, 0);
}

@Test
public void shouldStartAContainerInStartOrConnectLeaveModeAndNotStopIt() {
Map<String, String> data = new HashMap<String, String>();
data.put("connectionMode", ConnectionMode.STARTORCONNECTANDLEAVE.name());
bind(ApplicationScoped.class, CubeConfiguration.class, CubeConfiguration.fromMap(data));

com.github.dockerjava.api.model.Container container = mock(com.github.dockerjava.api.model.Container.class);
when(container.getNames()).thenReturn(new String[]{CUBE_ID+"1"});
when(executor.listRunningContainers()).thenReturn(Arrays.asList(container));
bind(ApplicationScoped.class, DockerClientExecutor.class, executor);

fire(new BeforeStart(deployableContainer));
assertEventFired(CreateCube.class, 1);
assertEventFired(StartCube.class, 1);
assertEventFired(PreRunningCube.class, 1);
}

@Test
public void shouldCreateAndStartCubeDuringBeforeStart() {
fire(new BeforeStart(deployableContainer));
Expand Down
2 changes: 1 addition & 1 deletion pom.xml
Expand Up @@ -63,7 +63,7 @@
<properties>
<version.arquillian_core>1.1.7.Final</version.arquillian_core>
<version.junit>4.11</version.junit>
<version.docker-java>0.10.5</version.docker-java>
<version.docker-java>1.0.0</version.docker-java>
<version.snakeyaml>1.14</version.snakeyaml>
<version.mockito>1.10.19</version.mockito>
</properties>
Expand Down

0 comments on commit d83fd56

Please sign in to comment.