/******************************************************************************* * Copyright (c) 2012-2015 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.api.runner; import org.eclipse.che.api.builder.BuildStatus; import org.eclipse.che.api.builder.RemoteBuilderServer; import org.eclipse.che.api.builder.dto.BuildOptions; import org.eclipse.che.api.builder.dto.BuildTaskDescriptor; import org.eclipse.che.api.core.notification.EventService; import org.eclipse.che.api.core.rest.HttpJsonHelper; import org.eclipse.che.api.core.rest.RemoteServiceDescriptor; import org.eclipse.che.api.core.rest.ServiceContext; import org.eclipse.che.api.core.rest.shared.dto.Link; import org.eclipse.che.api.core.util.ValueHolder; import org.eclipse.che.api.project.shared.dto.BuildersDescriptor; import org.eclipse.che.api.project.shared.dto.ItemReference; import org.eclipse.che.api.project.shared.dto.ProjectDescriptor; import org.eclipse.che.api.project.shared.dto.RunnerEnvironment; import org.eclipse.che.api.project.shared.dto.RunnersDescriptor; import org.eclipse.che.api.runner.dto.RunOptions; import org.eclipse.che.api.runner.dto.RunRequest; import org.eclipse.che.api.runner.dto.RunnerDescriptor; import org.eclipse.che.api.runner.dto.RunnerMetric; import org.eclipse.che.api.runner.dto.RunnerServerAccessCriteria; import org.eclipse.che.api.runner.dto.RunnerServerDescriptor; import org.eclipse.che.api.runner.dto.RunnerServerLocation; import org.eclipse.che.api.runner.dto.RunnerServerRegistration; import org.eclipse.che.api.runner.dto.RunnerState; import org.eclipse.che.api.runner.dto.ServerState; import org.eclipse.che.api.runner.internal.Constants; import org.eclipse.che.api.runner.internal.RunnerEvent; import org.eclipse.che.api.workspace.shared.dto.WorkspaceDescriptor; import org.eclipse.che.commons.env.EnvironmentContext; import org.eclipse.che.commons.user.User; import org.eclipse.che.dto.server.DtoFactory; import org.mockito.ArgumentCaptor; import org.mockito.invocation.InvocationOnMock; import org.mockito.stubbing.Answer; import org.mockito.testng.MockitoTestNGListener; import org.testng.annotations.AfterMethod; import org.testng.annotations.BeforeMethod; import org.testng.annotations.Listeners; import org.testng.annotations.Test; import javax.ws.rs.core.UriBuilder; import java.lang.reflect.Field; import java.util.ArrayList; import java.util.Arrays; import java.util.HashMap; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Set; import java.util.concurrent.Callable; import java.util.concurrent.CopyOnWriteArrayList; import java.util.concurrent.TimeUnit; import static org.mockito.Matchers.any; import static org.mockito.Matchers.anyInt; import static org.mockito.Matchers.anyListOf; import static org.mockito.Matchers.eq; import static org.mockito.Mockito.doAnswer; import static org.mockito.Mockito.doNothing; import static org.mockito.Mockito.doReturn; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.never; import static org.mockito.Mockito.spy; import static org.mockito.Mockito.timeout; 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.assertFalse; import static org.testng.Assert.assertNotNull; import static org.testng.Assert.assertNull; import static org.testng.Assert.assertTrue; import static org.testng.Assert.fail; /** * @author andrew00x */ @Listeners(value = {MockitoTestNGListener.class}) public class RunQueueTest { private DtoFactory dtoFactory = DtoFactory.getInstance(); private HttpJsonHelper.HttpJsonHelperImpl httpJsonHelper; private RunQueue runQueue; private String wsId = "my_ws"; private String wsName = wsId; private String pName = "my_project"; private String pPath = "/" + pName; private ProjectDescriptor project; private WorkspaceDescriptor workspace; private EnvironmentContext codenvyContext; private List<RunnerEvent> events = new CopyOnWriteArrayList<>(); @BeforeMethod public void beforeMethod() throws Exception { httpJsonHelper = mock(HttpJsonHelper.HttpJsonHelperImpl.class); Field field = HttpJsonHelper.class.getDeclaredField("httpJsonHelperImpl"); field.setAccessible(true); field.set(null, httpJsonHelper); EventService eventService = mock(EventService.class); doAnswer(new Answer() { @Override public Object answer(InvocationOnMock invocation) throws Throwable { events.add((RunnerEvent)invocation.getArguments()[0]); return null; } }).when(eventService).publish(any(RunnerEvent.class)); RunnerSelectionStrategy selectionStrategy = new LastInUseRunnerSelectionStrategy(); runQueue = spy(new RunQueue("http://localhost:8080/api/workspace", "http://localhost:8080/api/project", "http://localhost:8080/api/builder", 256, 5, 5, 5, selectionStrategy, eventService)); runQueue.cleanerPeriod = 1000; // run cleaner every second runQueue.checkAvailableRunnerPeriod = 1000; runQueue.checkBuildResultPeriod = 1000; runQueue.start(); verify(runQueue, timeout(1000).times(1)).start(); project = dto(ProjectDescriptor.class).withName(pName).withPath(pPath); project.getLinks().add(dto(Link.class).withMethod("GET") .withHref(String.format("http://localhost:8080/api/project/%s/%s", wsId, pPath)) .withRel(org.eclipse.che.api.project.server.Constants.LINK_REL_EXPORT_ZIP)); workspace = dto(WorkspaceDescriptor.class).withId(wsId).withName(wsName).withTemporary(false).withAccountId("my_account"); events.clear(); codenvyContext = mock(EnvironmentContext.class); User user = mock(User.class); doReturn(user).when(codenvyContext).getUser(); String secureToken = "secret"; doReturn(secureToken).when(user).getToken(); EnvironmentContext.setCurrent(codenvyContext); } @AfterMethod public void afterMethod() { runQueue.stop(); } @Test public void testRegisterSlaveRunner() throws Exception { String remoteUrl = "http://localhost:8080/api/internal/runner"; RunnerDescriptor runnerDescriptor = dto(RunnerDescriptor.class).withName("java/web").withDescription("test description"); runnerDescriptor.getEnvironments().add(dto(RunnerEnvironment.class).withId("tomcat7")); RemoteRunnerServer runnerServer = registerRunnerServer(remoteUrl, runnerDescriptor, null); RemoteRunner runner = runnerServer.getRemoteRunner("java/web"); verify(runQueue, times(1)).createRemoteRunnerServer(eq(remoteUrl)); verify(runQueue, times(1)).doRegisterRunnerServer(eq(runnerServer)); List<RemoteRunnerServer> registerRunnerServers = runQueue.getRegisterRunnerServers(); assertEquals(registerRunnerServers.size(), 1); assertEquals(registerRunnerServers.get(0), runnerServer); Set<RemoteRunner> runnerList = runQueue.getRunnerList("community", null, null); assertNotNull(runnerList); assertEquals(runnerList.size(), 1); assertTrue(runnerList.contains(runner)); runnerList = runQueue.getRunnerList("community", wsId, null); assertNotNull(runnerList); assertEquals(runnerList.size(), 1); assertTrue(runnerList.contains(runner)); runnerList = runQueue.getRunnerList("community", wsId, pPath); assertNotNull(runnerList); assertEquals(runnerList.size(), 1); assertTrue(runnerList.contains(runner)); assertNull(runQueue.getRunnerList("paid", null, null)); assertNull(runQueue.getRunnerList("paid", wsId, null)); assertNull(runQueue.getRunnerList("paid", wsId, pPath)); } @Test public void testRegisterSlaveRunnerWithRunnerServerAccessCriteria() throws Exception { String remoteUrl = "http://localhost:8080/api/internal/runner"; RunnerDescriptor runnerDescriptor = dto(RunnerDescriptor.class).withName("java/web").withDescription("test description"); runnerDescriptor.getEnvironments().add(dto(RunnerEnvironment.class).withId("tomcat7")); RunnerServerAccessCriteria accessRules = dto(RunnerServerAccessCriteria.class) .withWorkspace(wsId) .withProject(pPath) .withInfra("paid"); RemoteRunnerServer runnerServer = registerRunnerServer(remoteUrl, runnerDescriptor, accessRules); RemoteRunner runner = runnerServer.getRemoteRunner("java/web"); verify(runnerServer, times(1)).setAssignedWorkspace(wsId); verify(runnerServer, times(1)).setAssignedProject(pPath); verify(runnerServer, times(1)).setInfra("paid"); verify(runQueue, times(1)).createRemoteRunnerServer(eq(remoteUrl)); verify(runQueue, times(1)).doRegisterRunnerServer(eq(runnerServer)); List<RemoteRunnerServer> registerRunnerServers = runQueue.getRegisterRunnerServers(); assertEquals(registerRunnerServers.size(), 1); assertEquals(registerRunnerServers.get(0), runnerServer); Set<RemoteRunner> runnerList = runQueue.getRunnerList("paid", wsId, pPath); assertNotNull(runnerList); assertTrue(runnerList.contains(runner)); assertNull(runQueue.getRunnerList("paid", wsId, null)); assertNull(runQueue.getRunnerList("paid", null, null)); assertNull(runQueue.getRunnerList("community", wsId, pPath)); assertNull(runQueue.getRunnerList("community", wsId, null)); assertNull(runQueue.getRunnerList("community", null, null)); } @Test public void testRegisterSlaveRunnerWithMoreThenOneInfra() throws Exception { String remoteUrl = "http://localhost:8080/api/internal/runner"; RunnerDescriptor runnerDescriptor = dto(RunnerDescriptor.class).withName("java/web").withDescription("test description"); runnerDescriptor.getEnvironments().add(dto(RunnerEnvironment.class).withId("tomcat7")); registerRunnerServer(remoteUrl, runnerDescriptor, dto(RunnerServerAccessCriteria.class).withInfra("paid")); registerRunnerServer(remoteUrl, runnerDescriptor, dto(RunnerServerAccessCriteria.class).withInfra("community")); assertNotNull(runQueue.getRunnerList("paid", wsId, pPath)); assertNotNull(runQueue.getRunnerList("paid", wsId, null)); assertNotNull(runQueue.getRunnerList("paid", null, null)); assertNotNull(runQueue.getRunnerList("community", wsId, pPath)); assertNotNull(runQueue.getRunnerList("community", wsId, null)); assertNotNull(runQueue.getRunnerList("community", null, null)); assertEquals(runQueue.getRunnerList("paid", wsId, pPath).size(), 1); assertEquals(runQueue.getRunnerList("paid", wsId, null).size(), 1); assertEquals(runQueue.getRunnerList("paid", null, null).size(), 1); assertEquals(runQueue.getRunnerList("community", wsId, pPath).size(), 1); assertEquals(runQueue.getRunnerList("community", wsId, null).size(), 1); assertEquals(runQueue.getRunnerList("community", null, null).size(), 1); } @Test public void testUnregisterSlaveRunner() throws Exception { RemoteRunnerServer runnerServer = registerDefaultRunnerServer(); runQueue.unregisterRunnerServer(dto(RunnerServerLocation.class).withUrl(runnerServer.getBaseUrl())); verify(runQueue, times(1)).doUnregisterRunners(eq(runnerServer.getBaseUrl())); List<RemoteRunnerServer> registerRunnerServers = runQueue.getRegisterRunnerServers(); assertEquals(registerRunnerServers.size(), 0); assertNull(runQueue.getRunnerList("community", null, null)); } @Test(expectedExceptions = {RunnerException.class}, expectedExceptionsMessageRegExp = "Runner environment 'system:/java/web/jboss7' is not available for workspace 'my_ws' on infra 'community'.") public void testRunWhenReadRunnerConfigurationFromProject_RunnerIsNotAvailable() throws Exception { registerDefaultRunnerServer(); // doesn't have what we need ServiceContext serviceContext = newServiceContext(); RunOptions runOptions = mock(RunOptions.class); project.withRunners(dto(RunnersDescriptor.class).withDefault("system:/java/web/jboss7")); doReturn(workspace).when(runQueue).getWorkspaceDescriptor(wsId, serviceContext); doReturn(project).when(runQueue).getProjectDescriptor(wsId, pPath, serviceContext); runQueue.run(wsId, pPath, serviceContext, runOptions); } @Test public void testRunWhenReadRunnerConfigurationFromProject() throws Exception { RemoteRunnerServer runnerServer = registerDefaultRunnerServer(); RemoteRunner runner = runnerServer.getRemoteRunner("java/web"); // Free memory should be more than 256. doReturn(dto(RunnerState.class).withServerState(dto(ServerState.class).withFreeMemory(512))).when(runner).getRemoteRunnerState(); RemoteRunnerProcess process = spy(new RemoteRunnerProcess(runnerServer.getBaseUrl(), runner.getName(), 1l)); doReturn(process).when(runner).run(any(RunRequest.class)); ServiceContext serviceContext = newServiceContext(); project.withRunners(dto(RunnersDescriptor.class).withDefault("system:/java/web/tomcat7")); doReturn(project).when(runQueue).getProjectDescriptor(wsId, pPath, serviceContext); doReturn(workspace).when(runQueue).getWorkspaceDescriptor(wsId, serviceContext); doNothing().when(runQueue).checkResources(eq(workspace), any(RunRequest.class)); runQueue.run(wsId, pPath, serviceContext, null); ArgumentCaptor<RunRequest> runRequestCaptor = ArgumentCaptor.forClass(RunRequest.class); // need timeout for executor verify(runner, timeout(1000)).run(runRequestCaptor.capture()); RunRequest request = runRequestCaptor.getValue(); assertEquals(request.getMemorySize(), 256); // default mem size assertEquals(request.getEnvironmentId(), "tomcat7"); assertEquals(request.getRunner(), "java/web"); assertTrue(request.getOptions().isEmpty()); assertTrue(request.getVariables().isEmpty()); assertEquals(request.getUserToken(), codenvyContext.getUser().getToken()); checkEvents(RunnerEvent.EventType.RUN_TASK_ADDED_IN_QUEUE); } @Test public void testRunWithRunOptions() throws Exception { RemoteRunnerServer runnerServer = registerDefaultRunnerServer(); RemoteRunner runner = runnerServer.getRemoteRunner("java/web"); // Free memory should be more than 256. doReturn(dto(RunnerState.class).withServerState(dto(ServerState.class).withFreeMemory(512))).when(runner).getRemoteRunnerState(); RemoteRunnerProcess process = spy(new RemoteRunnerProcess(runnerServer.getBaseUrl(), runner.getName(), 1l)); doReturn(process).when(runner).run(any(RunRequest.class)); ServiceContext serviceContext = newServiceContext(); RunOptions runOptions = mock(RunOptions.class); doReturn("system:/java/web/tomcat7").when(runOptions).getEnvironmentId(); doReturn(384).when(runOptions).getMemorySize(); Map<String, String> options = new HashMap<>(4); options.put("jpda", ""); options.put("run", ""); doReturn(options).when(runOptions).getOptions(); Map<String, String> envVar = new HashMap<>(4); envVar.put("jpda", ""); envVar.put("run", ""); doReturn(envVar).when(runOptions).getVariables(); doReturn(project).when(runQueue).getProjectDescriptor(wsId, pPath, serviceContext); doReturn(workspace).when(runQueue).getWorkspaceDescriptor(wsId, serviceContext); doNothing().when(runQueue).checkResources(eq(workspace), any(RunRequest.class)); runQueue.run(wsId, pPath, serviceContext, runOptions); ArgumentCaptor<RunRequest> runRequestCaptor = ArgumentCaptor.forClass(RunRequest.class); // need timeout for executor verify(runner, timeout(1000)).run(runRequestCaptor.capture()); // check was RunOptions in use. verify(runOptions, times(1)).getMemorySize(); verify(runOptions, times(1)).getEnvironmentId(); verify(runOptions, times(1)).getOptions(); verify(runOptions, times(1)).getVariables(); RunRequest request = runRequestCaptor.getValue(); assertEquals(request.getMemorySize(), 384); assertEquals(request.getEnvironmentId(), "tomcat7"); assertEquals(request.getRunner(), "java/web"); assertEquals(request.getOptions(), options); assertEquals(request.getVariables(), envVar); assertEquals(request.getUserToken(), codenvyContext.getUser().getToken()); checkEvents(RunnerEvent.EventType.RUN_TASK_ADDED_IN_QUEUE); } @Test public void testRunWithCustomEnvironment() throws Exception { String remoteUrl = "http://localhost:8080/api/internal/runner"; RunnerDescriptor runnerDescriptor = dto(RunnerDescriptor.class).withName("docker"); RemoteRunnerServer runnerServer = registerRunnerServer(remoteUrl, runnerDescriptor, null); RemoteRunner runner = runnerServer.getRemoteRunner(runnerDescriptor.getName()); doReturn(dto(RunnerState.class).withServerState(dto(ServerState.class).withFreeMemory(512))).when(runner).getRemoteRunnerState(); RemoteRunnerProcess process = spy(new RemoteRunnerProcess(runnerServer.getBaseUrl(), runner.getName(), 1l)); doReturn(process).when(runner).run(any(RunRequest.class)); ServiceContext serviceContext = newServiceContext(); String envName = "my_env_1"; RunOptions runOptions = mock(RunOptions.class); doReturn("project://" + envName).when(runOptions).getEnvironmentId(); doReturn(project).when(runQueue).getProjectDescriptor(wsId, pPath, serviceContext); doReturn(workspace).when(runQueue).getWorkspaceDescriptor(wsId, serviceContext); doNothing().when(runQueue).checkResources(eq(workspace), any(RunRequest.class)); ItemReference recipe = dto(ItemReference.class).withName("Dockerfile").withType("file"); String recipeUrl = String.format("http://localhost:8080/api/project/%s/.codenvy/runners/environments/%s", wsId, envName); recipe.getLinks() .add(dto(Link.class).withRel(org.eclipse.che.api.project.server.Constants.LINK_REL_GET_CONTENT).withHref(recipeUrl)); doReturn(Arrays.asList(recipe)).when(runQueue).getProjectRunnerRecipes(eq(project), eq(envName)); runQueue.run(wsId, pPath, serviceContext, runOptions); ArgumentCaptor<RunRequest> runRequestCaptor = ArgumentCaptor.forClass(RunRequest.class); // need timeout for executor verify(runner, timeout(1000)).run(runRequestCaptor.capture()); RunRequest request = runRequestCaptor.getValue(); assertNull(request.getEnvironmentId()); assertEquals(request.getRunner(), "docker"); List<String> recipeUrls = request.getRecipeUrls(); assertEquals(recipeUrls.size(), 1); assertEquals(request.getUserToken(), codenvyContext.getUser().getToken()); checkEvents(RunnerEvent.EventType.RUN_TASK_ADDED_IN_QUEUE); } @Test public void testTimeOutWhileWaitingForRunner() throws Exception { RemoteRunnerServer runnerServer = registerDefaultRunnerServer(); RemoteRunner runner = runnerServer.getRemoteRunner("java/web"); // Free memory should be less (!!!) than 256. doReturn(dto(RunnerState.class).withServerState(dto(ServerState.class).withFreeMemory(128))).when(runner).getRemoteRunnerState(); ServiceContext serviceContext = newServiceContext(); project.withRunners(dto(RunnersDescriptor.class).withDefault("system:/java/web/tomcat7")); doReturn(project).when(runQueue).getProjectDescriptor(wsId, pPath, serviceContext); doReturn(workspace).when(runQueue).getWorkspaceDescriptor(wsId, serviceContext); doNothing().when(runQueue).checkResources(eq(workspace), any(RunRequest.class)); RunQueueTask task = runQueue.run(wsId, pPath, serviceContext, null); assertTrue(task.isWaiting()); // sleep - max waiting time + 2 sec TimeUnit.SECONDS.sleep(7); verify(runner, never()).run(any(RunRequest.class)); assertFalse(task.isWaiting()); assertTrue(task.isCancelled()); checkEvents(RunnerEvent.EventType.RUN_TASK_ADDED_IN_QUEUE, RunnerEvent.EventType.RUN_TASK_QUEUE_TIME_EXCEEDED); } @Test public void testRunWithBuildBefore() throws Exception { RemoteRunnerServer runnerServer = registerDefaultRunnerServer(); RemoteRunner runner = runnerServer.getRemoteRunner("java/web"); // Free memory should be more than 256. doReturn(dto(RunnerState.class).withServerState(dto(ServerState.class).withFreeMemory(512))).when(runner).getRemoteRunnerState(); RemoteRunnerProcess process = spy(new RemoteRunnerProcess(runnerServer.getBaseUrl(), runner.getName(), 1l)); doReturn(process).when(runner).run(any(RunRequest.class)); ServiceContext serviceContext = newServiceContext(); project.withBuilders(dto(BuildersDescriptor.class).withDefault("maven")) .withRunners(dto(RunnersDescriptor.class).withDefault("system:/java/web/tomcat7")); doReturn(project).when(runQueue).getProjectDescriptor(wsId, pPath, serviceContext); doReturn(workspace).when(runQueue).getWorkspaceDescriptor(wsId, serviceContext); doNothing().when(runQueue).checkResources(eq(workspace), any(RunRequest.class)); mockBuilderApi(3); runQueue.run(wsId, pPath, serviceContext, null); ArgumentCaptor<RunRequest> runRequestCaptor = ArgumentCaptor.forClass(RunRequest.class); // timeout - max waiting time + 1 sec verify(runner, timeout(6000)).run(runRequestCaptor.capture()); RunRequest request = runRequestCaptor.getValue(); assertEquals(request.getMemorySize(), 256); // default mem size assertEquals(request.getEnvironmentId(), "tomcat7"); assertEquals(request.getRunner(), "java/web"); assertTrue(request.getOptions().isEmpty()); assertTrue(request.getVariables().isEmpty()); assertEquals(request.getUserToken(), codenvyContext.getUser().getToken()); checkEvents(RunnerEvent.EventType.RUN_TASK_ADDED_IN_QUEUE); } @Test public void testOverrideBuilderWithRunOptions() throws Exception { RemoteRunnerServer runnerServer = registerDefaultRunnerServer(); RemoteRunner runner = runnerServer.getRemoteRunner("java/web"); // Free memory should be more than 256. doReturn(dto(RunnerState.class).withServerState(dto(ServerState.class).withFreeMemory(512))).when(runner).getRemoteRunnerState(); RemoteRunnerProcess process = spy(new RemoteRunnerProcess(runnerServer.getBaseUrl(), runner.getName(), 1l)); doReturn(process).when(runner).run(any(RunRequest.class)); ServiceContext serviceContext = newServiceContext(); project.withBuilders(dto(BuildersDescriptor.class).withDefault("maven")) .withRunners(dto(RunnersDescriptor.class).withDefault("system:/java/web/tomcat7")); doReturn(project).when(runQueue).getProjectDescriptor(wsId, pPath, serviceContext); doReturn(workspace).when(runQueue).getWorkspaceDescriptor(wsId, serviceContext); doNothing().when(runQueue).checkResources(eq(workspace), any(RunRequest.class)); mockBuilderApi(1); runQueue.run(wsId, pPath, serviceContext, dto(RunOptions.class).withBuildOptions(dto(BuildOptions.class).withBuilderName("ant"))); // timeout for start executor and check build result once (checked every seconds) verify(runner, timeout(3000)).run(any(RunRequest.class)); ArgumentCaptor<BuildOptions> buildOptionsCaptor = ArgumentCaptor.forClass(BuildOptions.class); verify(runQueue, times(1)).startBuild(any(RemoteBuilderServer.class), eq(pPath), buildOptionsCaptor.capture()); BuildOptions buildOptions = buildOptionsCaptor.getValue(); assertEquals(buildOptions.getBuilderName(), "ant"); // overridden with options even maven is set in project configuration } @Test public void testSkipBuildNoBuilderName() throws Exception { RemoteRunnerServer runnerServer = registerDefaultRunnerServer(); RemoteRunner runner = runnerServer.getRemoteRunner("java/web"); // Free memory should be more than 256. doReturn(dto(RunnerState.class).withServerState(dto(ServerState.class).withFreeMemory(512))).when(runner).getRemoteRunnerState(); RemoteRunnerProcess process = spy(new RemoteRunnerProcess(runnerServer.getBaseUrl(), runner.getName(), 1l)); doReturn(process).when(runner).run(any(RunRequest.class)); ServiceContext serviceContext = newServiceContext(); project.withRunners(dto(RunnersDescriptor.class).withDefault("system:/java/web/tomcat7")) .withBuilders(dto(BuildersDescriptor.class)); // set builders model but don't set any builder name doReturn(project).when(runQueue).getProjectDescriptor(wsId, pPath, serviceContext); doReturn(workspace).when(runQueue).getWorkspaceDescriptor(wsId, serviceContext); doNothing().when(runQueue).checkResources(eq(workspace), any(RunRequest.class)); runQueue.run(wsId, pPath, serviceContext, null); verify(runner, timeout(1000)).run(any(RunRequest.class)); verify(runQueue, never()).getBuilderServiceDescriptor(eq(wsId), eq(serviceContext)); verify(runQueue, never()).startBuild(any(RemoteBuilderServer.class), eq(pPath), any(BuildOptions.class)); } @Test public void testSkipBuildWithSkipBuildOptions() throws Exception { RemoteRunnerServer runnerServer = registerDefaultRunnerServer(); RemoteRunner runner = runnerServer.getRemoteRunner("java/web"); // Free memory should be more than 256. doReturn(dto(RunnerState.class).withServerState(dto(ServerState.class).withFreeMemory(512))).when(runner).getRemoteRunnerState(); RemoteRunnerProcess process = spy(new RemoteRunnerProcess(runnerServer.getBaseUrl(), runner.getName(), 1l)); doReturn(process).when(runner).run(any(RunRequest.class)); ServiceContext serviceContext = newServiceContext(); project.withRunners(dto(RunnersDescriptor.class).withDefault("system:/java/web/tomcat7")) .withBuilders(dto(BuildersDescriptor.class).withDefault("maven")); // set builders fully doReturn(project).when(runQueue).getProjectDescriptor(wsId, pPath, serviceContext); doReturn(workspace).when(runQueue).getWorkspaceDescriptor(wsId, serviceContext); doNothing().when(runQueue).checkResources(eq(workspace), any(RunRequest.class)); runQueue.run(wsId, pPath, serviceContext, dto(RunOptions.class).withSkipBuild(true)); verify(runner, timeout(1000)).run(any(RunRequest.class)); verify(runQueue, never()).getBuilderServiceDescriptor(eq(wsId), eq(serviceContext)); verify(runQueue, never()).startBuild(any(RemoteBuilderServer.class), eq(pPath), any(BuildOptions.class)); } @Test public void testTimeOutWhileWaitingForLongBuildProcess() throws Exception { RemoteRunnerServer runnerServer = registerDefaultRunnerServer(); RemoteRunner runner = runnerServer.getRemoteRunner("java/web"); // Free memory should be more than 256. doReturn(dto(RunnerState.class).withServerState(dto(ServerState.class).withFreeMemory(512))).when(runner).getRemoteRunnerState(); ServiceContext serviceContext = newServiceContext(); project.withBuilders(dto(BuildersDescriptor.class).withDefault("maven")) .withRunners(dto(RunnersDescriptor.class).withDefault("system:/java/web/tomcat7")); doReturn(project).when(runQueue).getProjectDescriptor(wsId, pPath, serviceContext); doReturn(workspace).when(runQueue).getWorkspaceDescriptor(wsId, serviceContext); doNothing().when(runQueue).checkResources(eq(workspace), any(RunRequest.class)); mockBuilderApi(7); RunQueueTask task = runQueue.run(wsId, pPath, serviceContext, null); assertTrue(task.isWaiting()); // sleep - max waiting time + 2 sec TimeUnit.SECONDS.sleep(7); verify(runner, never()).run(any(RunRequest.class)); assertFalse(task.isWaiting()); assertTrue(task.isCancelled()); checkEvents(RunnerEvent.EventType.RUN_TASK_ADDED_IN_QUEUE, RunnerEvent.EventType.RUN_TASK_QUEUE_TIME_EXCEEDED); } @Test(expectedExceptions = {RunnerException.class}, expectedExceptionsMessageRegExp = "Not enough resources to start application. Available memory 128M but 256M required.") public void testErrorWhenNotEnoughMemoryAssignedToWorkspace() throws Exception { RemoteRunnerServer runnerServer = registerDefaultRunnerServer(); RemoteRunner runner = runnerServer.getRemoteRunner("java/web"); // Free memory should be more than 256. doReturn(dto(RunnerState.class).withServerState(dto(ServerState.class).withFreeMemory(512))).when(runner).getRemoteRunnerState(); ServiceContext serviceContext = newServiceContext(); project.withRunners(dto(RunnersDescriptor.class).withDefault("system:/java/web/tomcat7")); doReturn(project).when(runQueue).getProjectDescriptor(wsId, pPath, serviceContext); // limit memory workspace.getAttributes().put(Constants.RUNNER_MAX_MEMORY_SIZE, "128"); doReturn(workspace).when(runQueue).getWorkspaceDescriptor(wsId, serviceContext); try { runQueue.run(wsId, pPath, serviceContext, null); } catch (RunnerException e) { verify(runQueue, never()).checkMemory(eq(wsId), anyInt(), anyInt()); throw e; } } @Test(expectedExceptions = {RunnerException.class}, expectedExceptionsMessageRegExp = "Run action for this workspace is locked") public void testErrorWhenRunIsBlockedForWorkspace() throws Exception { RemoteRunnerServer runnerServer = registerDefaultRunnerServer(); RemoteRunner runner = runnerServer.getRemoteRunner("java/web"); // Free memory should be more than 256. doReturn(dto(RunnerState.class).withServerState(dto(ServerState.class).withFreeMemory(512))).when(runner).getRemoteRunnerState(); ServiceContext serviceContext = newServiceContext(); project.withRunners(dto(RunnersDescriptor.class).withDefault("system:/java/web/tomcat7")); doReturn(project).when(runQueue).getProjectDescriptor(wsId, pPath, serviceContext); // limit memory workspace.getAttributes().put(Constants.RUNNER_MAX_MEMORY_SIZE, "1024"); workspace.getAttributes().put(org.eclipse.che.api.account.server.Constants.RESOURCES_LOCKED_PROPERTY, "true"); doReturn(workspace).when(runQueue).getWorkspaceDescriptor(wsId, serviceContext); try { runQueue.run(wsId, pPath, serviceContext, null); } catch (RunnerException e) { verify(runQueue, never()).checkMemory(eq(wsId), anyInt(), anyInt()); throw e; } } @Test(expectedExceptions = {RunnerException.class}, expectedExceptionsMessageRegExp = "Not enough resources to start application. Available memory 128M but 129M required.") public void testErrorWhenNotEnoughMemoryToRunNewApplication() throws Exception { RemoteRunnerServer runnerServer = registerDefaultRunnerServer(); RemoteRunner runner = runnerServer.getRemoteRunner("java/web"); doReturn(dto(RunnerState.class).withServerState(dto(ServerState.class).withFreeMemory(256))).when(runner).getRemoteRunnerState(); ServiceContext serviceContext = newServiceContext(); project.withRunners(dto(RunnersDescriptor.class).withDefault("system:/java/web/tomcat7")); doReturn(project).when(runQueue).getProjectDescriptor(wsId, pPath, serviceContext); // limit memory workspace.getAttributes().put(Constants.RUNNER_MAX_MEMORY_SIZE, "256"); doReturn(workspace).when(runQueue).getWorkspaceDescriptor(wsId, serviceContext); doReturn(new Callable<RemoteRunnerProcess>() { @Override public RemoteRunnerProcess call() throws Exception { Thread.sleep(5000); // need to have first task in waiting status return null; } }).when(runQueue).createTaskFor(anyListOf(RemoteRunner.class), any(RunRequest.class), any(ValueHolder.class)); runQueue.run(wsId, pPath, serviceContext, dto(RunOptions.class).withMemorySize(128)); runQueue.run(wsId, pPath, serviceContext, dto(RunOptions.class).withMemorySize(129)); } @Test public void testWhenRunningOutOfDiskSpace() throws Exception { RemoteRunnerServer runnerServer = registerDefaultRunnerServer(); RemoteRunner runner = runnerServer.getRemoteRunner("java/web"); List<RunnerMetric> metrics = new ArrayList<>(2); metrics.add(dto(RunnerMetric.class).withName(RunnerMetric.DISK_SPACE_TOTAL).withValue("1000000")); metrics.add(dto(RunnerMetric.class).withName(RunnerMetric.DISK_SPACE_USED).withValue("980000")); doReturn(dto(RunnerState.class).withServerState(dto(ServerState.class).withFreeMemory(256)).withStats(metrics)) .when(runner).getRemoteRunnerState(); ServiceContext serviceContext = newServiceContext(); project.withRunners(dto(RunnersDescriptor.class).withDefault("system:/java/web/tomcat7")); doReturn(project).when(runQueue).getProjectDescriptor(wsId, pPath, serviceContext); doReturn(workspace).when(runQueue).getWorkspaceDescriptor(wsId, serviceContext); doNothing().when(runQueue).checkResources(eq(workspace), any(RunRequest.class)); RunQueueTask task = runQueue.run(wsId, pPath, serviceContext, null); assertTrue(task.isWaiting()); // sleep - max waiting time + 2 sec TimeUnit.SECONDS.sleep(7); verify(runner, never()).run(any(RunRequest.class)); assertFalse(task.isWaiting()); assertTrue(task.isCancelled()); checkEvents(RunnerEvent.EventType.RUN_TASK_ADDED_IN_QUEUE, RunnerEvent.EventType.RUN_TASK_QUEUE_TIME_EXCEEDED); } private String mockBuilderApi(final int inProgressNum) throws Exception { assertTrue(inProgressNum >= 1); final BuildTaskDescriptor buildTaskQueue = dto(BuildTaskDescriptor.class).withStatus(BuildStatus.IN_QUEUE); String statusLink = String.format("http://localhost:8080/api/builder/%s/status/%d", wsId, 1); buildTaskQueue.getLinks().add(dto(Link.class).withMethod("GET") .withHref(statusLink) .withRel(org.eclipse.che.api.builder.internal.Constants.LINK_REL_GET_STATUS)); doReturn(buildTaskQueue).when(runQueue).startBuild(any(RemoteServiceDescriptor.class), eq(pPath), any(BuildOptions.class)); final BuildTaskDescriptor buildTaskProgress = dtoFactory.clone(buildTaskQueue).withStatus(BuildStatus.IN_PROGRESS); String cancelLink = String.format("http://localhost:8080/api/builder/%s/cancel/%d", wsId, 1); buildTaskProgress.getLinks().add(dto(Link.class).withMethod("GET") .withHref(cancelLink) .withRel(org.eclipse.che.api.builder.internal.Constants.LINK_REL_CANCEL)); final BuildTaskDescriptor buildTaskDone = dtoFactory.clone(buildTaskQueue).withStatus(BuildStatus.SUCCESSFUL); String downloadLink = String.format("http://localhost:8080/api/builder/%s/download/%d?path=artifact", wsId, 1); buildTaskDone.getLinks().add(dto(Link.class).withMethod("GET") .withHref(downloadLink) .withRel(org.eclipse.che.api.builder.internal.Constants.LINK_REL_DOWNLOAD_RESULT)); doAnswer(new Answer<BuildTaskDescriptor>() { int tick = 0; @Override public BuildTaskDescriptor answer(InvocationOnMock invocation) throws Throwable { if (tick == 0) { ++tick; return buildTaskQueue; } else if (tick < inProgressNum) { ++tick; return buildTaskProgress; } return buildTaskDone; } }).when(httpJsonHelper).request(eq(BuildTaskDescriptor.class), eq(statusLink), eq("GET"), any()); return downloadLink; } private void checkEvents(RunnerEvent.EventType... expected) { List<RunnerEvent.EventType> list = new ArrayList<>(expected.length); java.util.Collections.addAll(list, expected); for (RunnerEvent event : events) { for (Iterator<RunnerEvent.EventType> iterator = list.iterator(); iterator.hasNext(); ) { RunnerEvent.EventType eventType = iterator.next(); if (eventType.equals(event.getType())) { iterator.remove(); } } } if (!list.isEmpty()) { fail("Missing events: " + list); } } // private ServiceContext newServiceContext() { ServiceContext sc = mock(ServiceContext.class); doReturn(UriBuilder.fromUri("http://localhost:8080/api")).when(sc).getBaseUriBuilder(); doReturn(UriBuilder.fromUri("http://localhost:8080/api/runner/" + wsId)).when(sc).getServiceUriBuilder(); return sc; } private <T> T dto(Class<T> type) { return dtoFactory.createDto(type); } private RemoteRunnerServer registerDefaultRunnerServer() throws Exception { String remoteUrl = "http://localhost:8080/api/internal/runner"; RunnerDescriptor runnerDescriptor = dto(RunnerDescriptor.class).withName("java/web").withDescription("test description"); runnerDescriptor.getEnvironments().add(dto(RunnerEnvironment.class).withId("tomcat7")); return registerRunnerServer(remoteUrl, runnerDescriptor, null); } private RemoteRunnerServer registerRunnerServer(String remoteUrl, RunnerDescriptor runnerDescriptor, RunnerServerAccessCriteria accessRules) throws Exception { RemoteRunnerServer runnerServer = spy(new RemoteRunnerServer(remoteUrl)); doReturn(dto(RunnerServerDescriptor.class)).when(runnerServer).getServiceDescriptor(); doReturn(Arrays.asList(runnerDescriptor)).when(runnerServer).getRunnerDescriptors(); RemoteRunner runner = spy(new RemoteRunner(remoteUrl, runnerDescriptor.getName(), new ArrayList<Link>())); doReturn(runnerDescriptor.getEnvironments()).when(runner).getEnvironments(); doReturn(Arrays.asList(runner)).when(runnerServer).getRemoteRunners(); doReturn(runner).when(runnerServer).getRemoteRunner(eq(runnerDescriptor.getName())); when(runQueue.createRemoteRunnerServer(remoteUrl)).thenReturn(runnerServer); RunnerServerRegistration registration = dto(RunnerServerRegistration.class) .withRunnerServerLocation(dto(RunnerServerLocation.class).withUrl(remoteUrl)) .withRunnerServerAccessCriteria(accessRules); runQueue.registerRunnerServer(registration); return runnerServer; } }