/*******************************************************************************
* Copyright (c) 2012-2017 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.machine;
import org.eclipse.che.api.core.ServerException;
import org.eclipse.che.api.core.jsonrpc.commons.RequestTransmitter;
import org.eclipse.che.api.core.model.machine.Machine;
import org.eclipse.che.api.core.model.machine.MachineConfig;
import org.eclipse.che.api.core.model.machine.ServerConf;
import org.eclipse.che.api.core.util.JsonRpcEndpointToMachineNameHolder;
import org.eclipse.che.api.core.util.LineConsumer;
import org.eclipse.che.api.environment.server.model.CheServiceImpl;
import org.eclipse.che.api.machine.server.model.impl.ServerConfImpl;
import org.eclipse.che.api.machine.server.recipe.RecipeImpl;
import org.eclipse.che.api.machine.server.util.RecipeRetriever;
import org.eclipse.che.commons.env.EnvironmentContext;
import org.eclipse.che.commons.lang.os.WindowsPathEscaper;
import org.eclipse.che.commons.subject.SubjectImpl;
import org.eclipse.che.plugin.docker.client.DockerConnector;
import org.eclipse.che.plugin.docker.client.DockerConnectorConfiguration;
import org.eclipse.che.plugin.docker.client.DockerConnectorProvider;
import org.eclipse.che.plugin.docker.client.ProgressMonitor;
import org.eclipse.che.plugin.docker.client.UserSpecificDockerRegistryCredentialsProvider;
import org.eclipse.che.plugin.docker.client.json.ContainerConfig;
import org.eclipse.che.plugin.docker.client.json.ContainerCreated;
import org.eclipse.che.plugin.docker.client.json.ContainerInfo;
import org.eclipse.che.plugin.docker.client.json.ContainerState;
import org.eclipse.che.plugin.docker.client.json.ImageConfig;
import org.eclipse.che.plugin.docker.client.json.ImageInfo;
import org.eclipse.che.plugin.docker.client.json.Volume;
import org.eclipse.che.plugin.docker.client.params.CreateContainerParams;
import org.eclipse.che.plugin.docker.client.params.InspectContainerParams;
import org.eclipse.che.plugin.docker.client.params.PullParams;
import org.eclipse.che.plugin.docker.client.params.RemoveContainerParams;
import org.eclipse.che.plugin.docker.client.params.RemoveImageParams;
import org.eclipse.che.plugin.docker.client.params.StartContainerParams;
import org.eclipse.che.plugin.docker.client.params.TagParams;
import org.eclipse.che.plugin.docker.machine.node.DockerNode;
import org.mockito.ArgumentCaptor;
import org.mockito.Mock;
import org.mockito.testng.MockitoTestNGListener;
import org.testng.annotations.AfterMethod;
import org.testng.annotations.BeforeMethod;
import org.testng.annotations.DataProvider;
import org.testng.annotations.Listeners;
import org.testng.annotations.Test;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.function.Function;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import static java.util.Arrays.asList;
import static java.util.Collections.emptyMap;
import static java.util.Collections.emptySet;
import static java.util.Collections.singleton;
import static java.util.Collections.singletonMap;
import static java.util.stream.Collectors.toMap;
import static org.eclipse.che.plugin.docker.machine.DockerInstanceProvider.DOCKER_FILE_TYPE;
import static org.eclipse.che.plugin.docker.machine.DockerInstanceProvider.MACHINE_SNAPSHOT_PREFIX;
import static org.mockito.Matchers.any;
import static org.mockito.Matchers.anyString;
import static org.mockito.Matchers.eq;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.doThrow;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import static org.testng.Assert.assertEquals;
import static org.testng.Assert.assertEqualsNoOrder;
import static org.testng.Assert.assertFalse;
import static org.testng.Assert.assertNull;
import static org.testng.Assert.assertTrue;
@Listeners(MockitoTestNGListener.class)
public class MachineProviderImplTest {
private static final String CONTAINER_ID = "containerId";
private static final String WORKSPACE_ID = "wsId";
private static final String MACHINE_NAME = "machineName";
private static final String USER_TOKEN = "userToken";
private static final String USER_NAME = "user";
private static final boolean SNAPSHOT_USE_REGISTRY = true;
private static final int MEMORY_SWAP_MULTIPLIER = 0;
private static final String ENV_NAME = "env";
private static final String NETWORK_NAME = "networkName";
private static final String[] DEFAULT_CMD = new String[] {"some", "command"};
private static final String[] DEFAULT_ENTRYPOINT = new String[] {"entry", "point"};
@Mock
private DockerConnector dockerConnector;
@Mock
private DockerConnectorConfiguration dockerConnectorConfiguration;
@Mock
private DockerMachineFactory dockerMachineFactory;
@Mock
private DockerInstanceStopDetector dockerInstanceStopDetector;
@Mock
private RequestTransmitter transmitter;
@Mock
private JsonRpcEndpointToMachineNameHolder jsonRpcEndpointToMachineNameHolder;
@Mock
private DockerNode dockerNode;
@Mock
private UserSpecificDockerRegistryCredentialsProvider credentialsReader;
@Mock
private ContainerInfo containerInfo;
@Mock
private ContainerState containerState;
@Mock
private ImageInfo imageInfo;
@Mock
private ImageConfig imageConfig;
@Mock
private RecipeRetriever recipeRetriever;
@Mock
private WindowsPathEscaper pathEscaper;
private MachineProviderImpl provider;
private class MockConnectorProvider extends DockerConnectorProvider {
public MockConnectorProvider() {
super(Collections.emptyMap(), "default");
}
@Override
public DockerConnector get() {
return dockerConnector;
}
}
@BeforeMethod
public void setUp() throws Exception {
when(dockerConnectorConfiguration.getDockerHostIp()).thenReturn("123.123.123.123");
provider = spy(new MachineProviderBuilder().build());
EnvironmentContext envCont = new EnvironmentContext();
envCont.setSubject(new SubjectImpl(USER_NAME, "userId", USER_TOKEN, false));
EnvironmentContext.setCurrent(envCont);
when(recipeRetriever.getRecipe(any(MachineConfig.class)))
.thenReturn(new RecipeImpl().withType(DOCKER_FILE_TYPE).withScript("FROM codenvy"));
when(dockerMachineFactory.createNode(anyString(), anyString())).thenReturn(dockerNode);
when(dockerConnector.createContainer(any(CreateContainerParams.class)))
.thenReturn(new ContainerCreated(CONTAINER_ID, new String[0]));
when(dockerConnector.inspectContainer(any(InspectContainerParams.class))).thenReturn(containerInfo);
when(dockerConnector.inspectContainer(anyString())).thenReturn(containerInfo);
when(containerInfo.getState()).thenReturn(containerState);
when(containerState.getStatus()).thenReturn("running");
when(dockerConnector.inspectImage(anyString())).thenReturn(imageInfo);
when(imageInfo.getConfig()).thenReturn(imageConfig);
when(imageConfig.getCmd()).thenReturn(new String[] {"tail", "-f", "/dev/null"});
}
@AfterMethod
public void tearDown() throws Exception {
EnvironmentContext.reset();
}
@Test
public void shouldPullDockerImageOnInstanceCreationFromSnapshotFromRegistry() throws Exception {
String repo = MACHINE_SNAPSHOT_PREFIX + "repo";
String tag = "latest";
String registry = "localhost:1234";
createInstanceFromSnapshot(repo, tag, registry);
PullParams pullParams = PullParams.create(repo).withRegistry(registry).withTag(tag);
verify(dockerConnector).pull(eq(pullParams), any(ProgressMonitor.class));
}
@Test
public void shouldNotPullDockerImageOnInstanceCreationFromLocalSnapshot() throws Exception {
String repo = MACHINE_SNAPSHOT_PREFIX + "repo";
String tag = "latest";
String registry = "localhost:1234";
provider = spy(new MachineProviderBuilder().setSnapshotUseRegistry(false)
.build());
createInstanceFromSnapshot(repo, tag, registry);
verify(dockerConnector, never()).pull(eq(PullParams.create(repo).withTag(tag)), any(ProgressMonitor.class));
}
@Test
public void shouldPullDockerImageIfAlwaysPullIsTrueEvenIfImageExistsLocally() throws Exception {
provider = spy(new MachineProviderBuilder().setDoForcePullImage(true)
.build());
doReturn(true).when(provider).isDockerImageExistLocally(anyString());
createInstanceFromRecipe();
verify(dockerConnector).pull(any(PullParams.class), any(ProgressMonitor.class));
}
@Test
public void shouldPullDockerImageIfAlwaysPullIsFalseButImageDoesNotExist() throws Exception {
provider = spy(new MachineProviderBuilder().setDoForcePullImage(false)
.build());
doReturn(false).when(provider).isDockerImageExistLocally(anyString());
createInstanceFromRecipe();
verify(dockerConnector).pull(any(PullParams.class), any(ProgressMonitor.class));
}
@Test
public void shouldNotPullDockerImageIfAlwaysPullIsFalseAndTheImageExistLocally() throws Exception {
provider = spy(new MachineProviderBuilder().setDoForcePullImage(false)
.build());
doReturn(true).when(provider).isDockerImageExistLocally(anyString());
createInstanceFromRecipe();
verify(dockerConnector, never()).pull(any(PullParams.class), any(ProgressMonitor.class));
}
@Test
public void shouldUseLocalImageOnInstanceCreationFromSnapshot() throws Exception {
final String repo = MACHINE_SNAPSHOT_PREFIX + "repo";
final String tag = "latest";
provider = spy(new MachineProviderBuilder().setSnapshotUseRegistry(false)
.build());
CheServiceImpl machine = createService();
machine.setImage(repo + ":" + tag);
machine.setBuild(null);
provider.startService(USER_NAME,
WORKSPACE_ID,
ENV_NAME,
MACHINE_NAME,
false,
NETWORK_NAME,
machine,
LineConsumer.DEV_NULL);
verify(dockerConnector, never()).pull(any(PullParams.class), any(ProgressMonitor.class));
}
@Test
public void shouldNotRemoveImageAfterRestoreFromLocalSnapshot() throws Exception {
String repo = MACHINE_SNAPSHOT_PREFIX + "repo";
String tag = "latest";
provider = spy(new MachineProviderBuilder().setSnapshotUseRegistry(false)
.build());
createInstanceFromSnapshot(repo, tag, null);
verify(dockerConnector, never()).removeImage(any(RemoveImageParams.class));
}
@Test
public void shouldNotRemoveImageWhenCreatingInstanceFromLocalImage() throws Exception {
String repo = "repo1";
String tag = "latest";
MachineProviderImpl provider = spy(new MachineProviderBuilder().setSnapshotUseRegistry(false)
.build());
CheServiceImpl machine = createService();
machine.setBuild(null);
machine.setImage(repo + ":" + tag + "@digest");
provider.startService(USER_NAME,
WORKSPACE_ID,
ENV_NAME,
MACHINE_NAME,
false,
NETWORK_NAME,
machine,
LineConsumer.DEV_NULL);
verify(dockerConnector, never()).removeImage(any(RemoveImageParams.class));
}
@Test
public void shouldReTagBuiltImageWithPredictableOnInstanceCreationFromRecipe() throws Exception {
// given
String repo = MACHINE_SNAPSHOT_PREFIX + "repo1";
String tag = "tag1";
String registry = "registry1";
// when
CheServiceImpl machine = createInstanceFromSnapshot(repo, tag, registry);
// then
TagParams tagParams = TagParams.create(registry + "/" + repo + ":" + tag,
"eclipse-che/" + machine.getContainerName());
verify(dockerConnector).tag(eq(tagParams));
ArgumentCaptor<RemoveImageParams> argumentCaptor = ArgumentCaptor.forClass(RemoveImageParams.class);
verify(dockerConnector).removeImage(argumentCaptor.capture());
RemoveImageParams imageParams = argumentCaptor.getValue();
assertEquals(imageParams.getImage(), registry + "/" + repo + ":" + tag);
assertFalse(imageParams.isForce());
}
@Test
public void shouldCreateContainerOnInstanceCreationFromRecipe() throws Exception {
// when
CheServiceImpl machine = createInstanceFromRecipe();
// then
ArgumentCaptor<CreateContainerParams> argumentCaptor = ArgumentCaptor.forClass(CreateContainerParams.class);
verify(dockerConnector).createContainer(argumentCaptor.capture());
assertEquals(argumentCaptor.getValue().getContainerConfig().getImage(),
"eclipse-che/" + machine.getContainerName());
}
@Test
public void shouldPublishAllExposedPortsOnCreateContainerOnInstanceCreationFromRecipe() throws Exception {
// when
createInstanceFromRecipe();
// then
ArgumentCaptor<CreateContainerParams> argumentCaptor = ArgumentCaptor.forClass(CreateContainerParams.class);
verify(dockerConnector).createContainer(argumentCaptor.capture());
assertTrue(argumentCaptor.getValue().getContainerConfig().getHostConfig().isPublishAllPorts());
}
@Test
public void shouldStartContainerOnCreateInstanceFromRecipe() throws Exception {
createInstanceFromRecipe();
ArgumentCaptor<StartContainerParams> argumentCaptor = ArgumentCaptor.forClass(StartContainerParams.class);
verify(dockerConnector).startContainer(argumentCaptor.capture());
assertEquals(argumentCaptor.getValue().getContainer(), CONTAINER_ID);
}
@Test
public void shouldCreateContainerOnInstanceCreationFromSnapshot() throws Exception {
// when
CheServiceImpl machine = createInstanceFromSnapshot();
// then
ArgumentCaptor<CreateContainerParams> argumentCaptor = ArgumentCaptor.forClass(CreateContainerParams.class);
verify(dockerConnector).createContainer(argumentCaptor.capture());
assertEquals(argumentCaptor.getValue().getContainerConfig().getImage(),
"eclipse-che/" + machine.getContainerName());
}
@Test
public void shouldPublishAllExposedPortsOnCreateContainerOnInstanceCreationFromSnapshot() throws Exception {
// when
createInstanceFromSnapshot();
// then
ArgumentCaptor<CreateContainerParams> argumentCaptor = ArgumentCaptor.forClass(CreateContainerParams.class);
verify(dockerConnector).createContainer(argumentCaptor.capture());
assertTrue(argumentCaptor.getValue().getContainerConfig().getHostConfig().isPublishAllPorts());
}
@Test
public void shouldBeAbleToCreateContainerWithPrivilegeMode() throws Exception {
provider = spy(new MachineProviderBuilder().setPrivilegedMode(true)
.build());
createInstanceFromRecipe();
ArgumentCaptor<CreateContainerParams> argumentCaptor = ArgumentCaptor.forClass(CreateContainerParams.class);
verify(dockerConnector).createContainer(argumentCaptor.capture());
assertTrue(argumentCaptor.getValue().getContainerConfig().getHostConfig().isPrivileged());
}
@Test
public void shouldBeAbleToCreateContainerWithCpuSet() throws Exception {
provider = spy(new MachineProviderBuilder().setCpuSet("0-3")
.build());
createInstanceFromRecipe();
ArgumentCaptor<CreateContainerParams> argumentCaptor = ArgumentCaptor.forClass(CreateContainerParams.class);
verify(dockerConnector).createContainer(argumentCaptor.capture());
assertEquals(argumentCaptor.getValue().getContainerConfig().getHostConfig().getCpusetCpus(), "0-3");
}
@Test
public void shouldBeAbleToCreateContainerWithCpuPeriod() throws Exception {
provider = spy(new MachineProviderBuilder().setCpuPeriod(200)
.build());
createInstanceFromRecipe();
ArgumentCaptor<CreateContainerParams> argumentCaptor = ArgumentCaptor.forClass(CreateContainerParams.class);
verify(dockerConnector).createContainer(argumentCaptor.capture());
assertEquals(((long)argumentCaptor.getValue().getContainerConfig().getHostConfig().getCpuPeriod()), 200);
}
@Test(dataProvider = "dnsResolverTestProvider")
public void shouldSetDnsResolversOnContainerCreation(String[] dnsResolvers) throws Exception {
provider = spy(new MachineProviderBuilder().setDnsResolvers(dnsResolvers)
.build());
createInstanceFromRecipe();
ArgumentCaptor<CreateContainerParams> argumentCaptor = ArgumentCaptor.forClass(CreateContainerParams.class);
verify(dockerConnector).createContainer(argumentCaptor.capture());
assertEqualsNoOrder(argumentCaptor.getValue().getContainerConfig().getHostConfig().getDns(), dnsResolvers);
}
@DataProvider(name = "dnsResolverTestProvider")
public static Object[][] dnsResolverTestProvider() {
return new Object[][] {
{new String[]{}},
{new String[]{"8.8.8.8", "7.7.7.7", "9.9.9.9"}},
{new String[]{"9.9.9.9"}},
{null},
};
}
@Test
public void shouldSetNullDnsResolversOnContainerCreationByDefault() throws Exception {
provider = spy(new MachineProviderBuilder().build());
createInstanceFromRecipe();
ArgumentCaptor<CreateContainerParams> argumentCaptor = ArgumentCaptor.forClass(CreateContainerParams.class);
verify(dockerConnector).createContainer(argumentCaptor.capture());
assertEqualsNoOrder(argumentCaptor.getValue().getContainerConfig().getHostConfig().getDns(), null);
}
@Test
public void shouldBeAbleToCreateContainerWithCgroupParent() throws Exception {
provider = spy(new MachineProviderBuilder().setParentCgroup("some_parent")
.build());
createInstanceFromRecipe();
ArgumentCaptor<CreateContainerParams> argumentCaptor = ArgumentCaptor.forClass(CreateContainerParams.class);
verify(dockerConnector).createContainer(argumentCaptor.capture());
assertEquals(argumentCaptor.getValue().getContainerConfig().getHostConfig().getCgroupParent(), "some_parent");
}
@Test
public void shouldCreateContainerWithPidsLimit() throws Exception {
provider = spy(new MachineProviderBuilder().setPidsLimit(512)
.build());
createInstanceFromRecipe();
ArgumentCaptor<CreateContainerParams> argumentCaptor = ArgumentCaptor.forClass(CreateContainerParams.class);
verify(dockerConnector).createContainer(argumentCaptor.capture());
assertEquals(argumentCaptor.getValue().getContainerConfig().getHostConfig().getPidsLimit(), 512);
}
@Test(expectedExceptions = ServerException.class)
public void shouldRemoveContainerInCaseFailedStartContainer() throws Exception {
doThrow(IOException.class).when(dockerConnector).startContainer(StartContainerParams.create(CONTAINER_ID));
createInstanceFromRecipe(false, WORKSPACE_ID);
verify(dockerConnector)
.removeContainer(RemoveContainerParams.create(CONTAINER_ID).withRemoveVolumes(true).withForce(true));
}
@Test(expectedExceptions = ServerException.class)
public void shouldRemoveContainerInCaseFailedGetCreateNode() throws Exception {
doThrow(IOException.class).when(dockerMachineFactory).createNode(any(), any());
createInstanceFromRecipe(false, WORKSPACE_ID);
verify(dockerConnector)
.removeContainer(RemoveContainerParams.create(CONTAINER_ID).withRemoveVolumes(true).withForce(true));
}
@Test(expectedExceptions = ServerException.class)
public void shouldRemoveContainerInCaseFailedCreateInstanceOnTheDockerMachineFactory() throws Exception {
doThrow(IOException.class).when(dockerMachineFactory).createInstance(any(), any(), any(), any(), any());
createInstanceFromRecipe(false, WORKSPACE_ID);
verify(dockerConnector)
.removeContainer(RemoveContainerParams.create(CONTAINER_ID).withRemoveVolumes(true).withForce(true));
}
@Test
public void shouldStartContainerOnCreateInstanceFromSnapshot() throws Exception {
createInstanceFromSnapshot();
ArgumentCaptor<StartContainerParams> argumentCaptor = ArgumentCaptor.forClass(StartContainerParams.class);
verify(dockerConnector).startContainer(argumentCaptor.capture());
assertEquals(argumentCaptor.getValue().getContainer(), CONTAINER_ID);
}
@Test
public void shouldCallCreationDockerInstanceWithFactoryOnCreateInstanceFromRecipe() throws Exception {
CheServiceImpl service = createService();
createInstanceFromRecipe(service);
verify(dockerMachineFactory).createInstance(any(Machine.class),
eq(CONTAINER_ID),
eq("eclipse-che/" + service.getContainerName()),
eq(dockerNode),
any(LineConsumer.class));
}
@Test
public void shouldSetMemorySizeInContainersOnInstanceCreationFromRecipe() throws Exception {
int memorySizeMB = 234;
createInstanceFromRecipe(memorySizeMB);
ArgumentCaptor<CreateContainerParams> argumentCaptor = ArgumentCaptor.forClass(CreateContainerParams.class);
verify(dockerConnector).createContainer(argumentCaptor.capture());
verify(dockerConnector).startContainer(any(StartContainerParams.class));
// docker accepts memory size in bytes
assertEquals(argumentCaptor.getValue().getContainerConfig().getHostConfig().getMemory(),
memorySizeMB * 1024 * 1024);
}
@Test
public void shouldSetMemorySizeInContainersOnInstanceCreationFromSnapshot() throws Exception {
int memorySizeMB = 234;
createInstanceFromSnapshot(memorySizeMB);
ArgumentCaptor<CreateContainerParams> argumentCaptor = ArgumentCaptor.forClass(CreateContainerParams.class);
verify(dockerConnector).createContainer(argumentCaptor.capture());
verify(dockerConnector).startContainer(any(StartContainerParams.class));
// docker accepts memory size in bytes
assertEquals(argumentCaptor.getValue().getContainerConfig().getHostConfig().getMemory(),
memorySizeMB * 1024 * 1024);
}
@Test(dataProvider = "swapTestProvider")
public void shouldBeAbleToSetCorrectSwapSize(double swapMultiplier, int memoryMB, long expectedSwapSize)
throws Exception {
// given
provider = spy(new MachineProviderBuilder().setMemorySwapMultiplier(swapMultiplier)
.build());
// when
createInstanceFromRecipe(memoryMB);
// then
ArgumentCaptor<CreateContainerParams> argumentCaptor = ArgumentCaptor.forClass(CreateContainerParams.class);
verify(dockerConnector).createContainer(argumentCaptor.capture());
assertEquals(argumentCaptor.getValue().getContainerConfig().getHostConfig().getMemorySwap(), expectedSwapSize);
}
@DataProvider(name = "swapTestProvider")
public static Object[][] swapTestProvider() {
return new Object[][] {
{-1, 1000, -1},
{0, 1000, 1000L * 1024 * 1024},
{0.7, 1000, (long)(1.7 * 1000 * 1024 * 1024)},
{1, 1000, 2L * 1000 * 1024 * 1024},
{2, 1000, 3L * 1000 * 1024 * 1024},
{2.5, 1000, (long)(3.5 * 1000 * 1024 * 1024)}
};
}
@Test
public void shouldExposeCommonAndDevPortsToContainerOnDevInstanceCreationFromRecipe() throws Exception {
List<String> expectedExposedPorts = new ArrayList<>();
final Set<ServerConf> commonServers =
new HashSet<>(asList(new ServerConfImpl("reference1", "8080", "http", null),
new ServerConfImpl("reference2", "8081", "ftp", null)));
expectedExposedPorts.addAll(commonServers.stream()
.map(ServerConf::getPort)
.collect(Collectors.toList()));
final Set<ServerConf> devServers = new HashSet<>(asList(new ServerConfImpl("reference3", "8082", "https", null),
new ServerConfImpl("reference4", "8083", "sftp",
null)));
expectedExposedPorts.addAll(devServers.stream()
.map(ServerConf::getPort)
.collect(Collectors.toList()));
provider = new MachineProviderBuilder().setDevMachineServers(devServers)
.setAllMachineServers(commonServers)
.build();
final boolean isDev = true;
createInstanceFromRecipe(isDev);
ArgumentCaptor<CreateContainerParams> argumentCaptor = ArgumentCaptor.forClass(CreateContainerParams.class);
verify(dockerConnector).createContainer(argumentCaptor.capture());
assertTrue(new ArrayList<>(argumentCaptor.getValue()
.getContainerConfig()
.getExposedPorts()
.keySet())
.containsAll(expectedExposedPorts));
}
@Test
public void shouldExposeOnlyCommonPortsToContainerOnNonDevInstanceCreationFromRecipe() throws Exception {
List<String> expectedExposedPorts = new ArrayList<>();
final Set<ServerConf> commonServers =
new HashSet<>(asList(new ServerConfImpl("reference1", "8080", "http", null),
new ServerConfImpl("reference2", "8081", "ftp", null)));
expectedExposedPorts.addAll(commonServers.stream()
.map(ServerConf::getPort)
.collect(Collectors.toList()));
provider = new MachineProviderBuilder().setAllMachineServers(commonServers)
.build();
final boolean isDev = false;
createInstanceFromRecipe(isDev);
ArgumentCaptor<CreateContainerParams> argumentCaptor = ArgumentCaptor.forClass(CreateContainerParams.class);
verify(dockerConnector).createContainer(argumentCaptor.capture());
assertTrue(new ArrayList<>(argumentCaptor.getValue()
.getContainerConfig()
.getExposedPorts()
.keySet())
.containsAll(expectedExposedPorts));
}
@Test
public void shouldExposeCommonAndDevPortsToContainerOnDevInstanceCreationFromSnapshot() throws Exception {
List<String> expectedExposedPorts = new ArrayList<>();
final Set<ServerConf> commonServers =
new HashSet<>(asList(new ServerConfImpl("reference1", "8080", "http", null),
new ServerConfImpl("reference2", "8081", "ftp", null)));
expectedExposedPorts.addAll(commonServers.stream()
.map(ServerConf::getPort)
.collect(Collectors.toList()));
final Set<ServerConf> devServers = new HashSet<>(asList(new ServerConfImpl("reference3", "8082", "https", null),
new ServerConfImpl("reference4", "8083", "sftp",
null)));
expectedExposedPorts.addAll(devServers.stream()
.map(ServerConf::getPort)
.collect(Collectors.toList()));
provider = new MachineProviderBuilder().setDevMachineServers(devServers)
.setAllMachineServers(commonServers)
.build();
final boolean isDev = true;
createInstanceFromSnapshot(isDev);
ArgumentCaptor<CreateContainerParams> argumentCaptor = ArgumentCaptor.forClass(CreateContainerParams.class);
verify(dockerConnector).createContainer(argumentCaptor.capture());
assertTrue(new ArrayList<>(argumentCaptor.getValue()
.getContainerConfig()
.getExposedPorts()
.keySet())
.containsAll(expectedExposedPorts));
}
@Test
public void shouldExposeOnlyCommonPortsToContainerOnNonDevInstanceCreationFromSnapshot() throws Exception {
List<String> expectedExposedPorts = new ArrayList<>();
final Set<ServerConf> commonServers =
new HashSet<>(asList(new ServerConfImpl("reference1", "8080", "http", null),
new ServerConfImpl("reference2", "8081", "ftp", null)));
expectedExposedPorts.addAll(commonServers.stream()
.map(ServerConf::getPort)
.collect(Collectors.toList()));
provider = new MachineProviderBuilder().setAllMachineServers(commonServers)
.build();
final boolean isDev = false;
createInstanceFromSnapshot(isDev);
ArgumentCaptor<CreateContainerParams> argumentCaptor = ArgumentCaptor.forClass(CreateContainerParams.class);
verify(dockerConnector).createContainer(argumentCaptor.capture());
assertTrue(new ArrayList<>(argumentCaptor.getValue()
.getContainerConfig()
.getExposedPorts()
.keySet())
.containsAll(expectedExposedPorts));
}
@Test
public void shouldAddServersConfsPortsFromMachineConfigToExposedPortsOnNonDevInstanceCreationFromRecipe()
throws Exception {
// given
final boolean isDev = false;
CheServiceImpl machine = createService();
machine.setExpose(asList("9090", "8080"));
List<String> expectedExposedPorts = asList("9090", "8080");
// when
createInstanceFromRecipe(machine, isDev);
// then
ArgumentCaptor<CreateContainerParams> argumentCaptor = ArgumentCaptor.forClass(CreateContainerParams.class);
verify(dockerConnector).createContainer(argumentCaptor.capture());
assertTrue(new ArrayList<>(argumentCaptor.getValue()
.getContainerConfig()
.getExposedPorts()
.keySet())
.containsAll(expectedExposedPorts));
}
@Test
public void shouldAddServersConfigsPortsFromMachineConfigToExposedPortsOnDevInstanceCreationFromRecipe()
throws Exception {
// given
final boolean isDev = true;
CheServiceImpl machine = createService();
machine.setExpose(asList("9090", "8080"));
// when
createInstanceFromRecipe(machine, isDev);
// then
ArgumentCaptor<CreateContainerParams> argumentCaptor = ArgumentCaptor.forClass(CreateContainerParams.class);
verify(dockerConnector).createContainer(argumentCaptor.capture());
assertTrue(new ArrayList<>(argumentCaptor.getValue()
.getContainerConfig()
.getExposedPorts()
.keySet())
.containsAll(asList("9090", "8080")));
}
@Test
public void shouldAddBindMountAndRegularVolumesOnInstanceCreationFromRecipe() throws Exception {
String[] bindMountVolumesFromMachine = new String[] {"/my/bind/mount1:/from/host1",
"/my/bind/mount2:/from/host2:ro",
"/my/bind/mount3:/from/host3:ro,Z"};
String[] volumesFromMachine = new String[] {"/projects",
"/something",
"/something/else"};
String[] expectedBindMountVolumes = new String[] {"/my/bind/mount1:/from/host1",
"/my/bind/mount2:/from/host2:ro",
"/my/bind/mount3:/from/host3:ro,Z"};
Map<String, Volume> expectedVolumes = Stream.of("/projects",
"/something",
"/something/else")
.collect(toMap(Function.identity(), v -> new Volume()));
provider = new MachineProviderBuilder().setDevMachineVolumes(emptySet())
.setAllMachineVolumes(emptySet())
.build();
CheServiceImpl service = createService();
service.setVolumes(Stream.concat(Stream.of(bindMountVolumesFromMachine), Stream.of(volumesFromMachine))
.collect(Collectors.toList()));
createInstanceFromRecipe(service, true);
ArgumentCaptor<CreateContainerParams> argumentCaptor = ArgumentCaptor.forClass(CreateContainerParams.class);
verify(dockerConnector).createContainer(argumentCaptor.capture());
String[] actualBindMountVolumes = argumentCaptor.getValue().getContainerConfig().getHostConfig().getBinds();
Map<String, Volume> actualVolumes = argumentCaptor.getValue().getContainerConfig().getVolumes();
assertEquals(actualVolumes, expectedVolumes);
assertEqualsNoOrder(actualBindMountVolumes, expectedBindMountVolumes);
}
@Test
public void shouldAddBindMountAndRegularVolumesOnInstanceCreationFromSnapshot() throws Exception {
String[] bindMountVolumesFromMachine = new String[] {"/my/bind/mount1:/from/host1",
"/my/bind/mount2:/from/host2:ro",
"/my/bind/mount3:/from/host3:ro,Z"};
String[] volumesFromMachine = new String[] {"/projects",
"/something",
"/something/else"};
String[] expectedBindMountVolumes = new String[] {"/my/bind/mount1:/from/host1",
"/my/bind/mount2:/from/host2:ro",
"/my/bind/mount3:/from/host3:ro,Z"};
Map<String, Volume> expectedVolumes = Stream.of("/projects",
"/something",
"/something/else")
.collect(toMap(Function.identity(), v -> new Volume()));
provider = new MachineProviderBuilder().setDevMachineVolumes(emptySet())
.setAllMachineVolumes(emptySet())
.build();
CheServiceImpl service = createService();
service.setVolumes(Stream.concat(Stream.of(bindMountVolumesFromMachine), Stream.of(volumesFromMachine))
.collect(Collectors.toList()));
createInstanceFromSnapshot(service, true);
ArgumentCaptor<CreateContainerParams> argumentCaptor = ArgumentCaptor.forClass(CreateContainerParams.class);
verify(dockerConnector).createContainer(argumentCaptor.capture());
String[] actualBindMountVolumes = argumentCaptor.getValue().getContainerConfig().getHostConfig().getBinds();
Map<String, Volume> actualVolumes = argumentCaptor.getValue().getContainerConfig().getVolumes();
assertEquals(actualVolumes, expectedVolumes);
assertEqualsNoOrder(actualBindMountVolumes, expectedBindMountVolumes);
}
@Test
public void shouldAddAllVolumesOnDevInstanceCreationFromRecipe() throws Exception {
String[] bindMountVolumesFromMachine = new String[] {"/my/bind/mount1:/from/host1",
"/my/bind/mount2:/from/host2:ro",
"/my/bind/mount3:/from/host3:ro,Z"};
String[] volumesFromMachine = new String[] {"/projects",
"/something",
"/something/else"};
String[] allMachinesSystemVolumes = new String[] {"/some/thing/else:/home/some/thing/else",
"/other/path:/home/other/path",
"/home/other/path2"};
String[] devMachinesSystemVolumes = new String[] {"/etc:/tmp/etc:ro",
"/some/thing:/home/some/thing",
"/some/thing2:/home/some/thing2:ro,z",
"/home/some/thing3"};
String[] expectedBindMountVolumes = new String[] {"/my/bind/mount1:/from/host1",
"/my/bind/mount2:/from/host2:ro",
"/my/bind/mount3:/from/host3:ro,Z",
"/some/thing/else:/home/some/thing/else",
"/other/path:/home/other/path",
"/etc:/tmp/etc:ro",
"/some/thing:/home/some/thing",
"/some/thing2:/home/some/thing2:ro,z"};
Map<String, Volume> expectedVolumes = Stream.of("/projects",
"/something",
"/something/else",
"/home/other/path2",
"/home/some/thing3")
.collect(toMap(Function.identity(), v -> new Volume()));
provider = new MachineProviderBuilder()
.setDevMachineVolumes(new HashSet<>(asList(devMachinesSystemVolumes)))
.setAllMachineVolumes(new HashSet<>(asList(allMachinesSystemVolumes)))
.build();
CheServiceImpl service = createService();
service.setVolumes(Stream.concat(Stream.of(bindMountVolumesFromMachine), Stream.of(volumesFromMachine))
.collect(Collectors.toList()));
createInstanceFromRecipe(service, true);
ArgumentCaptor<CreateContainerParams> argumentCaptor = ArgumentCaptor.forClass(CreateContainerParams.class);
verify(dockerConnector).createContainer(argumentCaptor.capture());
String[] actualBindMountVolumes = argumentCaptor.getValue().getContainerConfig().getHostConfig().getBinds();
Map<String, Volume> actualVolumes = argumentCaptor.getValue().getContainerConfig().getVolumes();
assertEquals(actualVolumes, expectedVolumes);
assertEqualsNoOrder(actualBindMountVolumes, expectedBindMountVolumes);
}
@Test
public void shouldAddAllVolumesOnDevInstanceCreationFromSnapshot() throws Exception {
String[] bindMountVolumesFromMachine = new String[] {"/my/bind/mount1:/from/host1",
"/my/bind/mount2:/from/host2:ro",
"/my/bind/mount3:/from/host3:ro,Z"};
String[] volumesFromMachine = new String[] {"/projects",
"/something",
"/something/else"};
String[] allMachinesSystemVolumes = new String[] {"/some/thing/else:/home/some/thing/else",
"/other/path:/home/other/path",
"/home/other/path2"};
String[] devMachinesSystemVolumes = new String[] {"/etc:/tmp/etc:ro",
"/some/thing:/home/some/thing",
"/some/thing2:/home/some/thing2:ro,z",
"/home/some/thing3"};
String[] expectedBindMountVolumes = new String[] {"/my/bind/mount1:/from/host1",
"/my/bind/mount2:/from/host2:ro",
"/my/bind/mount3:/from/host3:ro,Z",
"/some/thing/else:/home/some/thing/else",
"/other/path:/home/other/path",
"/etc:/tmp/etc:ro",
"/some/thing:/home/some/thing",
"/some/thing2:/home/some/thing2:ro,z"};
Map<String, Volume> expectedVolumes = Stream.of("/projects",
"/something",
"/something/else",
"/home/other/path2",
"/home/some/thing3")
.collect(toMap(Function.identity(), v -> new Volume()));
provider = new MachineProviderBuilder()
.setDevMachineVolumes(new HashSet<>(asList(devMachinesSystemVolumes)))
.setAllMachineVolumes(new HashSet<>(asList(allMachinesSystemVolumes)))
.build();
CheServiceImpl service = createService();
service.setVolumes(Stream.concat(Stream.of(bindMountVolumesFromMachine), Stream.of(volumesFromMachine))
.collect(Collectors.toList()));
createInstanceFromSnapshot(service, true);
ArgumentCaptor<CreateContainerParams> argumentCaptor = ArgumentCaptor.forClass(CreateContainerParams.class);
verify(dockerConnector).createContainer(argumentCaptor.capture());
String[] actualBindMountVolumes = argumentCaptor.getValue().getContainerConfig().getHostConfig().getBinds();
Map<String, Volume> actualVolumes = argumentCaptor.getValue().getContainerConfig().getVolumes();
assertEquals(actualVolumes, expectedVolumes);
assertEqualsNoOrder(actualBindMountVolumes, expectedBindMountVolumes);
}
@Test
public void shouldAddCommonsSystemVolumesOnlyOnNonDevInstanceCreationFromRecipe() throws Exception {
String[] bindMountVolumesFromMachine = new String[] {"/my/bind/mount1:/from/host1",
"/my/bind/mount2:/from/host2:ro",
"/my/bind/mount3:/from/host3:ro,Z"};
String[] volumesFromMachine = new String[] {"/projects",
"/something",
"/something/else"};
String[] allMachinesSystemVolumes = new String[] {"/some/thing/else:/home/some/thing/else",
"/other/path:/home/other/path",
"/home/other/path2"};
String[] devMachinesSystemVolumes = new String[] {"/etc:/tmp/etc:ro",
"/some/thing:/home/some/thing",
"/some/thing2:/home/some/thing2:ro,z",
"/home/some/thing3"};
String[] expectedBindMountVolumes = new String[] {"/my/bind/mount1:/from/host1",
"/my/bind/mount2:/from/host2:ro",
"/my/bind/mount3:/from/host3:ro,Z",
"/some/thing/else:/home/some/thing/else",
"/other/path:/home/other/path"};
Map<String, Volume> expectedVolumes = Stream.of("/projects",
"/something",
"/something/else",
"/home/other/path2")
.collect(toMap(Function.identity(), v -> new Volume()));
provider = new MachineProviderBuilder()
.setDevMachineVolumes(new HashSet<>(asList(devMachinesSystemVolumes)))
.setAllMachineVolumes(new HashSet<>(asList(allMachinesSystemVolumes)))
.build();
CheServiceImpl service = createService();
service.setVolumes(Stream.concat(Stream.of(bindMountVolumesFromMachine), Stream.of(volumesFromMachine))
.collect(Collectors.toList()));
createInstanceFromRecipe(service, false);
ArgumentCaptor<CreateContainerParams> argumentCaptor = ArgumentCaptor.forClass(CreateContainerParams.class);
verify(dockerConnector).createContainer(argumentCaptor.capture());
String[] actualBindMountVolumes = argumentCaptor.getValue().getContainerConfig().getHostConfig().getBinds();
Map<String, Volume> actualVolumes = argumentCaptor.getValue().getContainerConfig().getVolumes();
assertEquals(actualVolumes, expectedVolumes);
assertEqualsNoOrder(actualBindMountVolumes, expectedBindMountVolumes);
}
@Test
public void shouldAddCommonsSystemVolumesOnlyOnNonDevInstanceCreationFromSnapshot() throws Exception {
String[] bindMountVolumesFromMachine = new String[] {"/my/bind/mount1:/from/host1",
"/my/bind/mount2:/from/host2:ro",
"/my/bind/mount3:/from/host3:ro,Z"};
String[] volumesFromMachine = new String[] {"/projects",
"/something",
"/something/else"};
String[] allMachinesSystemVolumes = new String[] {"/some/thing/else:/home/some/thing/else",
"/other/path:/home/other/path",
"/home/other/path2"};
String[] devMachinesSystemVolumes = new String[] {"/etc:/tmp/etc:ro",
"/some/thing:/home/some/thing",
"/some/thing2:/home/some/thing2:ro,z",
"/home/some/thing3"};
String[] expectedBindMountVolumes = new String[] {"/my/bind/mount1:/from/host1",
"/my/bind/mount2:/from/host2:ro",
"/my/bind/mount3:/from/host3:ro,Z",
"/some/thing/else:/home/some/thing/else",
"/other/path:/home/other/path"};
Map<String, Volume> expectedVolumes = Stream.of("/projects",
"/something",
"/something/else",
"/home/other/path2")
.collect(toMap(Function.identity(), v -> new Volume()));
provider = new MachineProviderBuilder()
.setDevMachineVolumes(new HashSet<>(asList(devMachinesSystemVolumes)))
.setAllMachineVolumes(new HashSet<>(asList(allMachinesSystemVolumes)))
.build();
CheServiceImpl service = createService();
service.setVolumes(Stream.concat(Stream.of(bindMountVolumesFromMachine), Stream.of(volumesFromMachine))
.collect(Collectors.toList()));
createInstanceFromSnapshot(service, false);
ArgumentCaptor<CreateContainerParams> argumentCaptor = ArgumentCaptor.forClass(CreateContainerParams.class);
verify(dockerConnector).createContainer(argumentCaptor.capture());
String[] actualBindMountVolumes = argumentCaptor.getValue().getContainerConfig().getHostConfig().getBinds();
Map<String, Volume> actualVolumes = argumentCaptor.getValue().getContainerConfig().getVolumes();
assertEquals(actualVolumes, expectedVolumes);
assertEqualsNoOrder(actualBindMountVolumes, expectedBindMountVolumes);
}
@Test
public void shouldAddExtraHostOnDevInstanceCreationFromRecipe() throws Exception {
//given
provider = new MachineProviderBuilder().setExtraHosts("dev.box.com:192.168.0.1")
.build();
final boolean isDev = true;
//when
createInstanceFromRecipe(isDev);
//then
ArgumentCaptor<CreateContainerParams> argumentCaptor = ArgumentCaptor.forClass(CreateContainerParams.class);
verify(dockerConnector).createContainer(argumentCaptor.capture());
verify(dockerConnector).startContainer(any(StartContainerParams.class));
final String[] extraHosts = argumentCaptor.getValue().getContainerConfig().getHostConfig().getExtraHosts();
assertEquals(extraHosts.length, 1);
assertEquals(extraHosts[0], "dev.box.com:192.168.0.1");
}
@Test
public void shouldAddExtraHostOnDevInstanceCreationFromSnapshot() throws Exception {
//given
provider = new MachineProviderBuilder().setExtraHosts("dev.box.com:192.168.0.1", "codenvy.com.com:185")
.build();
final boolean isDev = true;
//when
createInstanceFromSnapshot(isDev);
//then
ArgumentCaptor<CreateContainerParams> argumentCaptor = ArgumentCaptor.forClass(CreateContainerParams.class);
verify(dockerConnector).createContainer(argumentCaptor.capture());
verify(dockerConnector).startContainer(any(StartContainerParams.class));
final String[] extraHosts = argumentCaptor.getValue().getContainerConfig().getHostConfig().getExtraHosts();
assertEquals(extraHosts.length, 2);
assertEquals(extraHosts[0], "dev.box.com:192.168.0.1");
assertEquals(extraHosts[1], "codenvy.com.com:185");
}
@Test
public void shouldAddExtraHostOnNonDevInstanceCreationFromRecipe() throws Exception {
//given
provider = new MachineProviderBuilder().setExtraHosts("dev.box.com:192.168.0.1")
.build();
final boolean isDev = false;
//when
createInstanceFromRecipe(isDev);
//then
ArgumentCaptor<CreateContainerParams> argumentCaptor = ArgumentCaptor.forClass(CreateContainerParams.class);
verify(dockerConnector).createContainer(argumentCaptor.capture());
verify(dockerConnector).startContainer(any(StartContainerParams.class));
final String[] extraHosts = argumentCaptor.getValue().getContainerConfig().getHostConfig().getExtraHosts();
assertEquals(extraHosts.length, 1);
assertEquals(extraHosts[0], "dev.box.com:192.168.0.1");
}
@Test
public void shouldAddExtraHostOnNonDevInstanceCreationFromSnapshot() throws Exception {
//given
provider = new MachineProviderBuilder().setExtraHosts("dev.box.com:192.168.0.1", "codenvy.com.com:185")
.build();
final boolean isDev = false;
//when
createInstanceFromSnapshot(isDev);
//then
ArgumentCaptor<CreateContainerParams> argumentCaptor = ArgumentCaptor.forClass(CreateContainerParams.class);
verify(dockerConnector).createContainer(argumentCaptor.capture());
verify(dockerConnector).startContainer(any(StartContainerParams.class));
final String[] extraHosts = argumentCaptor.getValue().getContainerConfig().getHostConfig().getExtraHosts();
assertEquals(extraHosts.length, 2);
assertEquals(extraHosts[0], "dev.box.com:192.168.0.1");
assertEquals(extraHosts[1], "codenvy.com.com:185");
}
@Test
public void shouldAddWorkspaceIdEnvVariableOnDevInstanceCreationFromRecipe() throws Exception {
String wsId = "myWs";
createInstanceFromRecipe(true, wsId);
ArgumentCaptor<CreateContainerParams> argumentCaptor = ArgumentCaptor.forClass(CreateContainerParams.class);
verify(dockerConnector).createContainer(argumentCaptor.capture());
assertTrue(asList(argumentCaptor.getValue().getContainerConfig().getEnv())
.contains(DockerInstanceRuntimeInfo.CHE_WORKSPACE_ID + "=" + wsId),
"Workspace Id variable is missing. Required " + DockerInstanceRuntimeInfo.CHE_WORKSPACE_ID + "=" +
wsId +
". Found " + Arrays.toString(argumentCaptor.getValue().getContainerConfig().getEnv()));
}
@Test
public void shouldAddMachineNameEnvVariableOnDevInstanceCreationFromRecipe() throws Exception {
String wsId = "myWs";
createInstanceFromRecipe(true, wsId);
ArgumentCaptor<CreateContainerParams> argumentCaptor = ArgumentCaptor.forClass(CreateContainerParams.class);
verify(dockerConnector).createContainer(argumentCaptor.capture());
assertTrue(asList(argumentCaptor.getValue().getContainerConfig().getEnv())
.contains(DockerInstanceRuntimeInfo.CHE_MACHINE_NAME + "=" + MACHINE_NAME),
"Machine Name variable is missing. Required " + DockerInstanceRuntimeInfo.CHE_MACHINE_NAME + "=" +
MACHINE_NAME +
". Found " + Arrays.toString(argumentCaptor.getValue().getContainerConfig().getEnv()));
}
@Test
public void shouldAddMachineNameEnvVariableOnNonDevInstanceCreationFromRecipe() throws Exception {
String wsId = "myWs";
createInstanceFromRecipe(false, wsId);
ArgumentCaptor<CreateContainerParams> argumentCaptor = ArgumentCaptor.forClass(CreateContainerParams.class);
verify(dockerConnector).createContainer(argumentCaptor.capture());
assertTrue(asList(argumentCaptor.getValue().getContainerConfig().getEnv())
.contains(DockerInstanceRuntimeInfo.CHE_MACHINE_NAME + "=" + MACHINE_NAME),
"Machine Name variable is missing. Required " + DockerInstanceRuntimeInfo.CHE_MACHINE_NAME + "=" +
MACHINE_NAME +
". Found " + Arrays.toString(argumentCaptor.getValue().getContainerConfig().getEnv()));
}
@Test
public void shouldAddWorkspaceIdEnvVariableOnDevInstanceCreationFromSnapshot() throws Exception {
String wsId = "myWs";
createInstanceFromSnapshot(true, wsId);
ArgumentCaptor<CreateContainerParams> argumentCaptor = ArgumentCaptor.forClass(CreateContainerParams.class);
verify(dockerConnector).createContainer(argumentCaptor.capture());
assertTrue(asList(argumentCaptor.getValue().getContainerConfig().getEnv())
.contains(DockerInstanceRuntimeInfo.CHE_WORKSPACE_ID + "=" + wsId),
"Workspace Id variable is missing. Required " + DockerInstanceRuntimeInfo.CHE_WORKSPACE_ID + "=" +
wsId +
". Found " + Arrays.toString(argumentCaptor.getValue().getContainerConfig().getEnv()));
}
@Test
public void shouldAddWorkspaceIdEnvVariableOnNonDevInstanceCreationFromRecipe() throws Exception {
String wsId = "myWs";
createInstanceFromRecipe(false, wsId);
ArgumentCaptor<CreateContainerParams> argumentCaptor = ArgumentCaptor.forClass(CreateContainerParams.class);
verify(dockerConnector).createContainer(argumentCaptor.capture());
assertTrue(asList(argumentCaptor.getValue().getContainerConfig().getEnv())
.contains(DockerInstanceRuntimeInfo.CHE_WORKSPACE_ID + "=" + wsId),
"Non dev machine should contains " + DockerInstanceRuntimeInfo.CHE_WORKSPACE_ID);
}
@Test
public void shouldAddCommonAndDevEnvVariablesToContainerOnDevInstanceCreationFromRecipe() throws Exception {
Set<String> commonEnv = new HashSet<>(asList("ENV_VAR1=123", "ENV_VAR2=234"));
Set<String> devEnv = new HashSet<>(asList("DEV_ENV_VAR1=345", "DEV_ENV_VAR2=456", "DEV_ENV_VAR3=567"));
Set<String> expectedEnv = new HashSet<>();
expectedEnv.addAll(commonEnv);
expectedEnv.addAll(devEnv);
expectedEnv.add(DockerInstanceRuntimeInfo.USER_TOKEN + "=" + USER_TOKEN);
expectedEnv.add(DockerInstanceRuntimeInfo.CHE_WORKSPACE_ID + "=" + WORKSPACE_ID);
provider = new MachineProviderBuilder().setDevMachineEnvVars(devEnv)
.setAllMachineEnvVars(commonEnv)
.build();
final boolean isDev = true;
createInstanceFromRecipe(isDev);
ArgumentCaptor<CreateContainerParams> argumentCaptor = ArgumentCaptor.forClass(CreateContainerParams.class);
verify(dockerConnector).createContainer(argumentCaptor.capture());
assertTrue(new HashSet<>(asList(argumentCaptor.getValue().getContainerConfig().getEnv()))
.containsAll(expectedEnv));
}
@Test
public void shouldNotAddDevEnvToCommonEnvVariablesToContainerOnNonDevInstanceCreationFromRecipe() throws Exception {
Set<String> commonEnv = new HashSet<>(asList("ENV_VAR1=123", "ENV_VAR2=234"));
Set<String> devEnv = new HashSet<>(asList("DEV_ENV_VAR1=345", "DEV_ENV_VAR2=456", "DEV_ENV_VAR3=567"));
provider = new MachineProviderBuilder().setDevMachineEnvVars(devEnv)
.setAllMachineEnvVars(commonEnv)
.build();
final boolean isDev = false;
createInstanceFromRecipe(isDev);
ArgumentCaptor<CreateContainerParams> argumentCaptor = ArgumentCaptor.forClass(CreateContainerParams.class);
verify(dockerConnector).createContainer(argumentCaptor.capture());
assertTrue(
new HashSet<>(asList(argumentCaptor.getValue().getContainerConfig().getEnv())).containsAll(commonEnv));
}
@Test
public void shouldAddCommonAndDevEnvVariablesToContainerOnDevInstanceCreationFromSnapshot() throws Exception {
Set<String> commonEnv = new HashSet<>(asList("ENV_VAR1=123", "ENV_VAR2=234"));
Set<String> devEnv = new HashSet<>(asList("DEV_ENV_VAR1=345", "DEV_ENV_VAR2=456", "DEV_ENV_VAR3=567"));
Set<String> expectedEnv = new HashSet<>();
expectedEnv.addAll(commonEnv);
expectedEnv.addAll(devEnv);
expectedEnv.add(DockerInstanceRuntimeInfo.USER_TOKEN + "=" + USER_TOKEN);
expectedEnv.add(DockerInstanceRuntimeInfo.CHE_WORKSPACE_ID + "=" + WORKSPACE_ID);
provider = new MachineProviderBuilder().setDevMachineEnvVars(devEnv)
.setAllMachineEnvVars(commonEnv)
.build();
final boolean isDev = true;
createInstanceFromSnapshot(isDev);
ArgumentCaptor<CreateContainerParams> argumentCaptor = ArgumentCaptor.forClass(CreateContainerParams.class);
verify(dockerConnector).createContainer(argumentCaptor.capture());
assertTrue(new HashSet<>(asList(argumentCaptor.getValue().getContainerConfig().getEnv()))
.containsAll(expectedEnv));
}
@Test
public void shouldNotAddDevEnvToCommonEnvVariablesToContainerOnNonDevInstanceCreationFromSnapshot()
throws Exception {
Set<String> commonEnv = new HashSet<>(asList("ENV_VAR1=123", "ENV_VAR2=234"));
Set<String> devEnv = new HashSet<>(asList("DEV_ENV_VAR1=345", "DEV_ENV_VAR2=456", "DEV_ENV_VAR3=567"));
provider = new MachineProviderBuilder().setDevMachineEnvVars(devEnv)
.setAllMachineEnvVars(commonEnv)
.build();
final boolean isDev = false;
createInstanceFromSnapshot(isDev);
ArgumentCaptor<CreateContainerParams> argumentCaptor = ArgumentCaptor.forClass(CreateContainerParams.class);
verify(dockerConnector).createContainer(argumentCaptor.capture());
assertTrue(
new HashSet<>(asList(argumentCaptor.getValue().getContainerConfig().getEnv())).containsAll(commonEnv));
}
@Test
public void shouldAddEnvVarsFromMachineConfigToContainerOnNonDevInstanceCreationFromSnapshot() throws Exception {
// given
Map<String, String> envVarsFromConfig = new HashMap<>();
envVarsFromConfig.put("ENV_VAR1", "123");
envVarsFromConfig.put("ENV_VAR2", "234");
final boolean isDev = false;
CheServiceImpl machine = createService();
machine.setEnvironment(envVarsFromConfig);
// when
createInstanceFromSnapshot(machine, isDev);
// then
ArgumentCaptor<CreateContainerParams> argumentCaptor = ArgumentCaptor.forClass(CreateContainerParams.class);
verify(dockerConnector).createContainer(argumentCaptor.capture());
assertTrue(asList(argumentCaptor.getValue()
.getContainerConfig()
.getEnv())
.containsAll(envVarsFromConfig.entrySet()
.stream()
.map(entry -> entry.getKey() +
"=" +
entry.getValue())
.collect(Collectors.toList())));
}
@Test
public void shouldAddEnvVarsFromMachineConfigToContainerOnDevInstanceCreationFromSnapshot() throws Exception {
// given
Map<String, String> envVarsFromConfig = new HashMap<>();
envVarsFromConfig.put("ENV_VAR1", "123");
envVarsFromConfig.put("ENV_VAR2", "234");
final boolean isDev = true;
CheServiceImpl machine = createService();
machine.setEnvironment(envVarsFromConfig);
// when
createInstanceFromSnapshot(machine, isDev);
// then
ArgumentCaptor<CreateContainerParams> argumentCaptor = ArgumentCaptor.forClass(CreateContainerParams.class);
verify(dockerConnector).createContainer(argumentCaptor.capture());
assertTrue(asList(argumentCaptor.getValue()
.getContainerConfig()
.getEnv())
.containsAll(envVarsFromConfig.entrySet()
.stream()
.map(entry -> entry.getKey() +
"=" +
entry.getValue())
.collect(Collectors.toList())));
}
@Test
public void shouldAddEnvVarsFromMachineConfigToContainerOnNonDevInstanceCreationFromRecipe() throws Exception {
// given
Map<String, String> envVarsFromConfig = new HashMap<>();
envVarsFromConfig.put("ENV_VAR1", "123");
envVarsFromConfig.put("ENV_VAR2", "234");
final boolean isDev = false;
CheServiceImpl service = createService();
service.setEnvironment(envVarsFromConfig);
// when
createInstanceFromRecipe(service, isDev);
// then
ArgumentCaptor<CreateContainerParams> argumentCaptor = ArgumentCaptor.forClass(CreateContainerParams.class);
verify(dockerConnector).createContainer(argumentCaptor.capture());
assertTrue(asList(argumentCaptor.getValue()
.getContainerConfig()
.getEnv())
.containsAll(envVarsFromConfig.entrySet()
.stream()
.map(entry -> entry.getKey() +
"=" +
entry.getValue())
.collect(Collectors.toList())));
}
@Test
public void shouldAddEnvVarsFromMachineConfigToContainerOnDevInstanceCreationFromRecipe() throws Exception {
// given
Map<String, String> envVarsFromConfig = new HashMap<>();
envVarsFromConfig.put("ENV_VAR1", "123");
envVarsFromConfig.put("ENV_VAR2", "234");
final boolean isDev = true;
CheServiceImpl machine = createService();
machine.setEnvironment(envVarsFromConfig);
// when
createInstanceFromRecipe(machine, isDev);
// then
ArgumentCaptor<CreateContainerParams> argumentCaptor = ArgumentCaptor.forClass(CreateContainerParams.class);
verify(dockerConnector).createContainer(argumentCaptor.capture());
assertTrue(asList(argumentCaptor.getValue()
.getContainerConfig()
.getEnv())
.containsAll(envVarsFromConfig.entrySet()
.stream()
.map(entry -> entry.getKey() +
"=" +
entry.getValue())
.collect(Collectors.toList())));
}
@Test
public void shouldAddLinksToContainerOnCreation() throws Exception {
// given
String links[] = new String[] {"container1", "container2:alias"};
CheServiceImpl service = createService();
service.setLinks(asList(links));
// when
createInstanceFromRecipe(service, true);
// then
ArgumentCaptor<CreateContainerParams> argumentCaptor = ArgumentCaptor.forClass(CreateContainerParams.class);
verify(dockerConnector).createContainer(argumentCaptor.capture());
ContainerConfig containerConfig = argumentCaptor.getValue().getContainerConfig();
assertEquals(containerConfig.getHostConfig().getLinks(), links);
assertEquals(containerConfig.getNetworkingConfig().getEndpointsConfig().get(NETWORK_NAME).getLinks(), links);
}
@Test
public void shouldBeAbleToCreateContainerWithCpuQuota() throws Exception {
// given
provider = spy(new MachineProviderBuilder().setCpuQuota(200)
.build());
// when
createInstanceFromRecipe();
// then
ArgumentCaptor<CreateContainerParams> argumentCaptor = ArgumentCaptor.forClass(CreateContainerParams.class);
verify(dockerConnector).createContainer(argumentCaptor.capture());
assertEquals(((long)argumentCaptor.getValue().getContainerConfig().getHostConfig().getCpuQuota()), 200);
}
@Test(dataProvider = "terminatingContainerEntrypointCmd")
public void shouldChangeEntrypointCmdToTailfDevNullIfTheyAreIdentifiedAsTerminating(String[] entrypoint,
String[] cmd)
throws Exception {
// given
when(imageConfig.getCmd()).thenReturn(cmd);
when(imageConfig.getEntrypoint()).thenReturn(entrypoint);
// when
createInstanceFromRecipe();
// then
ArgumentCaptor<CreateContainerParams> argumentCaptor = ArgumentCaptor.forClass(CreateContainerParams.class);
verify(dockerConnector).createContainer(argumentCaptor.capture());
assertNull(argumentCaptor.getValue().getContainerConfig().getEntrypoint());
assertEquals(argumentCaptor.getValue().getContainerConfig().getCmd(), new String[] {"tail", "-f", "/dev/null"});
}
@DataProvider(name = "terminatingContainerEntrypointCmd")
public static Object[][] terminatingContainerEntrypointCmd() {
return new Object[][] {
// entrypoint and cmd are unset
{null, null},
// entrypoint is unset
{null, new String[] {"/bin/bash"}},
{null, new String[] {"/bin/sh"}},
{null, new String[] {"bash"}},
{null, new String[] {"sh"}},
{null, new String[] {"/bin/sh", "-c", "/bin/bash"}},
{null, new String[] {"/bin/sh", "-c", "/bin/sh"}},
{null, new String[] {"/bin/sh", "-c", "bash"}},
{null, new String[] {"/bin/sh", "-c", "sh"}},
// cmd is unset
{new String[] {"/bin/sh", "-c"}, null},
{new String[] {"/bin/bash", "-c"}, null},
{new String[] {"bash", "-c"}, null},
{new String[] {"sh", "-c"}, null},
{new String[] {"/bin/bash"}, null},
{new String[] {"/bin/sh"}, null},
{new String[] {"bash"}, null},
{new String[] {"sh"}, null},
{new String[] {"/bin/sh", "-c", "/bin/bash"}, null},
{new String[] {"/bin/sh", "-c", "/bin/sh"}, null},
{new String[] {"/bin/sh", "-c", "bash"}, null},
{new String[] {"/bin/sh", "-c", "sh"}, null},
// entrypoint and cmd are set
{new String[] {"/bin/sh", "-c"}, new String[] {"bash"}},
{new String[] {"/bin/bash", "-c"}, new String[] {"sh"}},
{new String[] {"bash", "-c"}, new String[] {"/bin/bash"}},
{new String[] {"sh", "-c"}, new String[] {"/bin/sh"}},
{new String[] {"/bin/bash"}, new String[] {"/bin/bash"}},
{new String[] {"/bin/sh"}, new String[] {"/bin/bash"}},
{new String[] {"bash"}, new String[] {"/bin/bash"}},
{new String[] {"sh"}, new String[] {"/bin/bash"}},
{new String[] {"/bin/sh", "-c", "/bin/bash"}, new String[] {"/bin/bash"}},
{new String[] {"/bin/sh", "-c", "/bin/sh"}, new String[] {"/bin/bash"}},
{new String[] {"/bin/sh", "-c", "bash"}, new String[] {"/bin/bash"}},
{new String[] {"/bin/sh", "-c", "sh"}, new String[] {"/bin/bash"}},
};
}
@Test(dataProvider = "nonTerminatingContainerEntrypointCmd")
public void shouldNotChangeEntrypointCmdIfTheyAreNotIdentified(String[] entrypoint,
String[] cmd) throws Exception {
// given
when(imageConfig.getCmd()).thenReturn(cmd);
when(imageConfig.getEntrypoint()).thenReturn(entrypoint);
// when
createInstanceFromRecipe();
// then
ArgumentCaptor<CreateContainerParams> argumentCaptor = ArgumentCaptor.forClass(CreateContainerParams.class);
verify(dockerConnector).createContainer(argumentCaptor.capture());
assertEqualsNoOrder(argumentCaptor.getValue().getContainerConfig().getEntrypoint(), DEFAULT_ENTRYPOINT);
assertEqualsNoOrder(argumentCaptor.getValue().getContainerConfig().getCmd(), DEFAULT_CMD);
}
@DataProvider(name = "nonTerminatingContainerEntrypointCmd")
public static Object[][] nonTerminatingContainerEntrypointCmd() {
return new Object[][] {
{new String[] {"/bin/sh", "-c"}, new String[] {"tail", "-f", "/dev/null"}},
{new String[] {"/bin/sh", "-c"}, new String[] {"tailf", "/dev/null"}},
{new String[] {"/bin/sh", "-c"}, new String[] {"./entrypoint.sh", "something"}},
{new String[] {"/bin/sh", "-c"}, new String[] {"./entrypoint.sh"}},
{new String[] {"/bin/sh", "-c"}, new String[] {"ping google.com"}},
{new String[] {"sh", "-c"}, new String[] {"./entrypoint.sh"}},
{new String[] {"bash", "-c"}, new String[] {"./entrypoint.sh"}},
{new String[] {"/bin/bash", "-c"}, new String[] {"./entrypoint.sh"}},
// terminating cmd but we don't recognize it since it is not used luckily and we should limit
// list of handled variants
{new String[] {"/bin/sh", "-c"}, new String[] {"echo", "something"}},
{new String[] {"/bin/sh", "-c"}, new String[] {"ls"}},
};
}
@Test(dataProvider = "acceptableStartedContainerStatus")
public void shouldNotThrowExceptionIfContainerStatusIsAcceptable(String status) throws Exception {
// given
when(containerState.getStatus()).thenReturn(status);
// when
createInstanceFromRecipe();
// then
verify(dockerConnector).inspectContainer(CONTAINER_ID);
verify(containerState).getStatus();
}
@DataProvider(name = "acceptableStartedContainerStatus")
public static Object[][] acceptableStartedContainerStatus() {
return new Object[][] {
// in case status is not returned for some reason, e.g. docker doesn't provide it
{null},
// expected status
{"running"},
// unknown status, pass for compatibility
{"some thing"}
};
}
@Test(expectedExceptions = ServerException.class,
expectedExceptionsMessageRegExp = MachineProviderImpl.CONTAINER_EXITED_ERROR)
public void shouldThrowExceptionIfContainerExitedRightAfterStart() throws Exception {
// given
when(containerState.getStatus()).thenReturn("exited");
// when
createInstanceFromRecipe();
}
private CheServiceImpl createInstanceFromRecipe() throws Exception {
CheServiceImpl service = createService();
createInstanceFromRecipe(service);
return service;
}
private void createInstanceFromRecipe(boolean isDev) throws Exception {
createInstanceFromRecipe(createService(), isDev, WORKSPACE_ID);
}
private void createInstanceFromRecipe(boolean isDev, String workspaceId) throws Exception {
createInstanceFromRecipe(createService(), isDev, workspaceId);
}
private void createInstanceFromRecipe(int memorySizeInMB) throws Exception {
CheServiceImpl machine = createService();
machine.setMemLimit(memorySizeInMB * 1024L * 1024L);
createInstanceFromRecipe(machine);
}
private CheServiceImpl createInstanceFromSnapshot(String repo, String tag, String registry) throws ServerException {
CheServiceImpl machine = createService();
machine.setImage(registry + "/" + repo + ":" + tag);
machine.setBuild(null);
createInstanceFromSnapshot(machine);
return machine;
}
private void createInstanceFromRecipe(CheServiceImpl service, boolean isDev) throws Exception {
createInstanceFromRecipe(service, isDev, WORKSPACE_ID);
}
private void createInstanceFromRecipe(CheServiceImpl service) throws Exception {
createInstanceFromRecipe(service, false, WORKSPACE_ID);
}
private void createInstanceFromRecipe(CheServiceImpl service,
boolean isDev,
String workspaceId) throws Exception {
provider.startService(USER_NAME,
workspaceId,
ENV_NAME,
MACHINE_NAME,
isDev,
NETWORK_NAME,
service,
LineConsumer.DEV_NULL);
}
private CheServiceImpl createInstanceFromSnapshot() throws ServerException {
CheServiceImpl service = createService();
createInstanceFromSnapshot(service, false, WORKSPACE_ID);
return service;
}
private void createInstanceFromSnapshot(CheServiceImpl service) throws ServerException {
createInstanceFromSnapshot(service, false, WORKSPACE_ID);
}
private void createInstanceFromSnapshot(int memorySizeInMB) throws ServerException {
CheServiceImpl machine = createService();
machine.setMemLimit(memorySizeInMB * 1024L * 1024L);
createInstanceFromSnapshot(machine, false, WORKSPACE_ID);
}
private void createInstanceFromSnapshot(boolean isDev) throws ServerException {
createInstanceFromSnapshot(createService(), isDev, WORKSPACE_ID);
}
private void createInstanceFromSnapshot(boolean isDev, String workspaceId) throws ServerException {
createInstanceFromSnapshot(createService(), isDev, workspaceId);
}
private void createInstanceFromSnapshot(CheServiceImpl service, boolean isDev) throws ServerException {
createInstanceFromSnapshot(service, isDev, WORKSPACE_ID);
}
private void createInstanceFromSnapshot(CheServiceImpl service, boolean isDev, String workspaceId)
throws ServerException {
provider.startService(USER_NAME,
workspaceId,
ENV_NAME,
MACHINE_NAME,
isDev,
NETWORK_NAME,
service,
LineConsumer.DEV_NULL);
}
private CheServiceImpl createService() {
CheServiceImpl service = new CheServiceImpl();
service.setId("testId");
service.setImage("image");
service.setCommand(asList(DEFAULT_CMD));
service.setContainerName("cont_name");
service.setDependsOn(asList("dep1", "dep2"));
service.setEntrypoint(asList(DEFAULT_ENTRYPOINT));
service.setExpose(asList("1010", "1111"));
service.setEnvironment(singletonMap("some", "var"));
service.setLabels(singletonMap("some", "label"));
service.setLinks(asList("link1", "link2:alias"));
service.setMemLimit(1000000000L);
service.setPorts(asList("port1", "port2"));
service.setVolumes(asList("vol1", "vol2"));
service.setVolumesFrom(asList("from1", "from2"));
return service;
}
private class MachineProviderBuilder {
private Set<ServerConf> devMachineServers;
private Set<ServerConf> allMachineServers;
private Set<String> devMachineVolumes;
private Set<String> allMachineVolumes;
private Set<Set<String>> extraHosts;
private boolean doForcePullImage;
private boolean privilegedMode;
private int pidsLimit;
private Set<String> devMachineEnvVars;
private Set<String> allMachineEnvVars;
private boolean snapshotUseRegistry;
private Set<Set<String>> additionalNetworks;
private double memorySwapMultiplier;
private String networkDriver;
private String parentCgroup;
private String cpuSet;
private long cpuPeriod;
private long cpuQuota;
private String[] dnsResolvers;
public MachineProviderBuilder() {
devMachineEnvVars = emptySet();
allMachineEnvVars = emptySet();
snapshotUseRegistry = SNAPSHOT_USE_REGISTRY;
privilegedMode = false;
doForcePullImage = false;
additionalNetworks = emptySet();
devMachineServers = emptySet();
allMachineServers = emptySet();
devMachineVolumes = emptySet();
allMachineVolumes = emptySet();
extraHosts = emptySet();
memorySwapMultiplier = MEMORY_SWAP_MULTIPLIER;
pidsLimit = -1;
}
public MachineProviderBuilder setDevMachineEnvVars(Set<String> devMachineEnvVars) {
this.devMachineEnvVars = devMachineEnvVars;
return this;
}
public MachineProviderBuilder setAllMachineEnvVars(Set<String> allMachineEnvVars) {
this.allMachineEnvVars = allMachineEnvVars;
return this;
}
public MachineProviderBuilder setSnapshotUseRegistry(boolean snapshotUseRegistry) {
this.snapshotUseRegistry = snapshotUseRegistry;
return this;
}
public MachineProviderBuilder setDoForcePullImage(boolean doForcePullImage) {
this.doForcePullImage = doForcePullImage;
return this;
}
public MachineProviderBuilder setPrivilegedMode(boolean privilegedMode) {
this.privilegedMode = privilegedMode;
return this;
}
public MachineProviderBuilder setPidsLimit(int pidsLimit) {
this.pidsLimit = pidsLimit;
return this;
}
public MachineProviderBuilder setMemorySwapMultiplier(double memorySwapMultiplier) {
this.memorySwapMultiplier = memorySwapMultiplier;
return this;
}
public MachineProviderBuilder setDevMachineServers(Set<ServerConf> devMachineServers) {
this.devMachineServers = devMachineServers;
return this;
}
public MachineProviderBuilder setAllMachineServers(Set<ServerConf> allMachineServers) {
this.allMachineServers = allMachineServers;
return this;
}
public MachineProviderBuilder setAllMachineVolumes(Set<String> allMachineVolumes) {
this.allMachineVolumes = allMachineVolumes;
return this;
}
public MachineProviderBuilder setDevMachineVolumes(Set<String> devMachineVolumes) {
this.devMachineVolumes = devMachineVolumes;
return this;
}
public MachineProviderBuilder setExtraHosts(String... extraHosts) {
this.extraHosts = singleton(new HashSet<>(Arrays.asList(extraHosts)));
return this;
}
public MachineProviderBuilder setNetworkDriver(String networkDriver) {
this.networkDriver = networkDriver;
return this;
}
public MachineProviderBuilder setParentCgroup(String parentCgroup) {
this.parentCgroup = parentCgroup;
return this;
}
public MachineProviderBuilder setCpuSet(String cpuSet) {
this.cpuSet = cpuSet;
return this;
}
public MachineProviderBuilder setCpuPeriod(long cpuPeriod) {
this.cpuPeriod = cpuPeriod;
return this;
}
public MachineProviderBuilder setCpuQuota(long cpuQuota) {
this.cpuQuota = cpuQuota;
return this;
}
public MachineProviderBuilder setDnsResolvers(String[] dnsResolvers) {
this.dnsResolvers = dnsResolvers;
return this;
}
MachineProviderImpl build() throws IOException {
return new MachineProviderImpl(new MockConnectorProvider(),
credentialsReader,
dockerMachineFactory,
dockerInstanceStopDetector,
transmitter,
jsonRpcEndpointToMachineNameHolder,
devMachineServers,
allMachineServers,
devMachineVolumes,
allMachineVolumes,
doForcePullImage,
privilegedMode,
pidsLimit,
devMachineEnvVars,
allMachineEnvVars,
snapshotUseRegistry,
memorySwapMultiplier,
additionalNetworks,
networkDriver,
parentCgroup,
cpuSet,
cpuPeriod,
cpuQuota,
pathEscaper,
extraHosts,
dnsResolvers,
emptyMap());
}
}
}