Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

resolves issue #110 by adding connection mode #111

Merged
merged 3 commits into from Feb 23, 2015
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
@@ -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));
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Add some better Exception handling then just the default IllegalArgumentException("No Enum constant " + type...) coming from valueOf

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Agreed

}
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));
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmm.. are we missing some else or something here? Why create/start then call PreRunningCube?

}
} 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 @@ -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