/******************************************************************************* * 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.model.machine.Machine; import org.eclipse.che.api.core.model.machine.MachineConfig; import org.eclipse.che.api.core.model.machine.MachineSource; import org.eclipse.che.api.core.model.machine.MachineStatus; import org.eclipse.che.api.core.util.LineConsumer; import org.eclipse.che.api.machine.server.exception.MachineException; import org.eclipse.che.api.machine.server.model.impl.MachineLimitsImpl; import org.eclipse.che.api.machine.server.model.impl.MachineConfigImpl; import org.eclipse.che.api.machine.server.model.impl.MachineImpl; import org.eclipse.che.api.machine.server.model.impl.MachineSourceImpl; import org.eclipse.che.plugin.docker.client.DockerConnector; import org.eclipse.che.plugin.docker.client.DockerConnectorProvider; import org.eclipse.che.plugin.docker.client.Exec; import org.eclipse.che.plugin.docker.client.LogMessage; import org.eclipse.che.plugin.docker.client.MessageProcessor; import org.eclipse.che.plugin.docker.client.ProgressMonitor; import org.eclipse.che.plugin.docker.client.params.CommitParams; import org.eclipse.che.plugin.docker.client.params.CreateExecParams; import org.eclipse.che.plugin.docker.client.params.PushParams; import org.eclipse.che.plugin.docker.client.params.StartExecParams; import org.eclipse.che.plugin.docker.machine.node.DockerNode; import org.mockito.Mock; import org.mockito.testng.MockitoTestNGListener; import org.testng.annotations.BeforeMethod; import org.testng.annotations.Listeners; import org.testng.annotations.Test; import java.io.IOException; import static java.lang.String.format; import static org.mockito.Matchers.any; import static org.mockito.Mockito.doAnswer; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; import static org.testng.Assert.assertEquals; import static org.testng.Assert.assertNotNull; import static org.testng.Assert.assertTrue; /** * Tests for {@link DockerInstance}. * * @author Anton Korneta */ @Listeners(MockitoTestNGListener.class) public class DockerInstanceTest { private static final String FILE_PATH = "/tmp"; private static final String CONTAINER = "container144"; private static final String OWNER = "owner12"; private static final String IMAGE = "image12"; private static final String EXEC_ID = "exec_id12"; private static final String MACHINE_ID = "machine12"; private static final String WORKSPACE_ID = "workspace12"; private static final String NAME = "suse-jdk"; private static final String TYPE = "docker"; private static final String REGISTRY = "registry"; private static final String USERNAME = "username"; private static final String REPOSITORY = "eclipse-che"; private static final String TAG = "latest"; private static final MachineStatus STATUS = MachineStatus.RUNNING; @Mock private LogMessage logMessageMock; @Mock private Exec execMock; @Mock private DockerConnector dockerConnectorMock; @Mock private DockerConnectorProvider dockerConnectorProviderMock; @Mock private DockerInstanceStopDetector dockerInstanceStopDetectorMock; @Mock private LineConsumer outputConsumer; private DockerInstance dockerInstance; @BeforeMethod public void setUp() throws IOException, MachineException { when(dockerConnectorProviderMock.get()).thenReturn(dockerConnectorMock); dockerInstance = getDockerInstance(); when(dockerConnectorMock.createExec(any(CreateExecParams.class))).thenReturn(execMock); when(execMock.getId()).thenReturn(EXEC_ID); doAnswer(invoke -> { @SuppressWarnings("unchecked") MessageProcessor<LogMessage> msgProc = (MessageProcessor<LogMessage>)invoke.getArguments()[1]; msgProc.process(logMessageMock); return msgProc; }).when(dockerConnectorMock) .startExec(any(StartExecParams.class), any()); } @Test(expectedExceptions = MachineException.class) public void shouldThrowMachineExceptionWhenStartFromIsNegative() throws Exception { dockerInstance.readFileContent(FILE_PATH, -1, -10); } @Test(expectedExceptions = MachineException.class) public void shouldThrowMachineExceptionWhenExecProblemOccurs() throws Exception { when(dockerConnectorMock.createExec(any(CreateExecParams.class))).thenThrow(new IOException("File not found")); dockerInstance.readFileContent(FILE_PATH, -1, 10); } @Test(expectedExceptions = MachineException.class, expectedExceptionsMessageRegExp = "File with path " + FILE_PATH + " not found") public void shouldThrowFileNotFoundDuringSedCommand() throws Exception { when(logMessageMock.getContent()).thenReturn("sed: can't read " + FILE_PATH + ": No such file or directory"); dockerInstance.readFileContent(FILE_PATH, 1, 10); } @Test(expectedExceptions = MachineException.class, expectedExceptionsMessageRegExp = "File with path " + FILE_PATH + " not found") public void shouldThrowFileNotFoundDuringCatCommand() throws Exception { when(logMessageMock.getContent()).thenReturn("cat: " + FILE_PATH + ": No such file or directory"); dockerInstance.readFileContent(FILE_PATH, 1, 10); } @Test public void shouldFullyReadFileContent() throws Exception { final String content = "content"; when(logMessageMock.getContent()).thenReturn(content); final String res = dockerInstance.readFileContent(FILE_PATH, 1, 10); assertEquals(res.trim(), content); } @Test public void shouldCreateDockerImageLocally() throws Exception { final String comment = format("Suspended at %1$ta %1$tb %1$td %1$tT %1$tZ %1$tY", System.currentTimeMillis()); dockerInstance.commitContainer(REPOSITORY, TAG); verify(dockerConnectorMock, times(1)).commit(CommitParams.create(CONTAINER) .withRepository(REPOSITORY) .withTag(TAG) .withComment(comment)); } @Test public void shouldSaveDockerInstanceStateIntoLocalImage() throws Exception { final MachineSource result = dockerInstance.saveToSnapshot(); assertTrue(result instanceof DockerMachineSource); DockerMachineSource dockerMachineSource = (DockerMachineSource) result; assertEquals(dockerMachineSource.getTag(), TAG); assertNotNull(dockerMachineSource.getRepository()); assertEquals(dockerMachineSource.getRegistry(), null); } @Test public void shouldCloseOutputConsumerOnDestroy() throws Exception { dockerInstance.destroy(); verify(outputConsumer).close(); } @Test public void shouldSaveDockerInstanceStateIntoRepository() throws Exception { final String digest = "image12"; dockerInstance = getDockerInstance(getMachine(), REGISTRY, CONTAINER, IMAGE, true); when(dockerConnectorMock.push(any(PushParams.class), any(ProgressMonitor.class))).thenReturn(digest); final MachineSource result = dockerInstance.saveToSnapshot(); assertTrue(result instanceof DockerMachineSource); DockerMachineSource dockerMachineSource = (DockerMachineSource) result; assertEquals(dockerMachineSource.getTag(), TAG); assertEquals(dockerMachineSource.getDigest(), digest); assertEquals(dockerMachineSource.getRegistry(), REGISTRY); } @Test(expectedExceptions = MachineException.class) public void shouldThrowMachineExceptionWhenDockerCommitFailed() throws Exception{ when(dockerConnectorMock.commit(any(CommitParams.class))).thenThrow(new IOException("err")); dockerInstance.saveToSnapshot(); } @Test(expectedExceptions = MachineException.class) public void shouldThrowMachineExceptionWhenDockerPushInterrupted() throws Exception { dockerInstance = getDockerInstance(getMachine(), REGISTRY, CONTAINER, IMAGE, true); when(dockerConnectorMock.push(any(PushParams.class), any(ProgressMonitor.class))).thenThrow(new IOException("err")); dockerInstance.saveToSnapshot(); } private DockerInstance getDockerInstance() throws MachineException { return getDockerInstance(getMachine(), REGISTRY, CONTAINER, IMAGE, false); } private DockerInstance getDockerInstance(Machine machine, String registry, String container, String image, boolean snapshotUseRegistry) throws MachineException { DockerMachineFactory machineFactory = mock(DockerMachineFactory.class); when(machineFactory.createMetadata(any(), any(), any())).thenReturn(mock(DockerInstanceRuntimeInfo.class)); return new DockerInstance(dockerConnectorProviderMock, registry, USERNAME, machineFactory, machine, container, image, mock(DockerNode.class), outputConsumer, dockerInstanceStopDetectorMock, mock(DockerInstanceProcessesCleaner.class), snapshotUseRegistry); } private Machine getMachine() { return getMachine(getMachineConfig(), OWNER, MACHINE_ID, WORKSPACE_ID, STATUS); } private Machine getMachine(MachineConfig config, String owner, String machineId, String wsId, MachineStatus status) { return MachineImpl.builder() .setConfig(config) .setId(machineId) .setOwner(owner) .setWorkspaceId(wsId) .setEnvName("env") .setStatus(status) .build(); } private MachineConfig getMachineConfig() { return getMachineConfig(true, NAME, TYPE); } private MachineConfig getMachineConfig(boolean isDev, String name, String type) { return MachineConfigImpl.builder() .setDev(isDev) .setName(name) .setType(type) .setSource(new MachineSourceImpl("docker").setLocation("location")) .setLimits(new MachineLimitsImpl(64)) .build(); } }