CHE-1370: refactor workspace environment bottstrapping and handling (#2108)
Signed-off-by: Alexander Garagatyi <agaragatyi@codenvy.com>6.19.x
parent
cf56410975
commit
0e9718e016
|
|
@ -15,7 +15,6 @@ import com.google.inject.multibindings.Multibinder;
|
|||
import com.google.inject.name.Names;
|
||||
|
||||
import org.eclipse.che.api.machine.shared.Constants;
|
||||
import org.eclipse.che.api.user.server.ProfileService;
|
||||
import org.eclipse.che.inject.DynaModule;
|
||||
|
||||
/** @author andrew00x */
|
||||
|
|
@ -62,7 +61,7 @@ public class WsMasterModule extends AbstractModule {
|
|||
.toInstance("predefined-recipes.json");
|
||||
|
||||
|
||||
bindConstant().annotatedWith(Names.named(org.eclipse.che.api.machine.server.wsagent.WsAgentLauncherImpl.WS_AGENT_PROCESS_START_COMMAND))
|
||||
bindConstant().annotatedWith(Names.named(org.eclipse.che.api.agent.server.wsagent.WsAgentLauncherImpl.WS_AGENT_PROCESS_START_COMMAND))
|
||||
.to("rm -rf ~/che && mkdir -p ~/che && unzip -qq /mnt/che/ws-agent.zip -d ~/che/ws-agent && " +
|
||||
"sudo sh -c \"chown -R $(id -u -n) /projects || true\" && " +
|
||||
"export JPDA_ADDRESS=\"4403\" && ~/che/ws-agent/bin/catalina.sh jpda run");
|
||||
|
|
@ -75,10 +74,10 @@ public class WsMasterModule extends AbstractModule {
|
|||
|
||||
bind(org.eclipse.che.api.workspace.server.event.MachineStateListener.class).asEagerSingleton();
|
||||
|
||||
bind(org.eclipse.che.api.machine.server.wsagent.WsAgentLauncher.class)
|
||||
.to(org.eclipse.che.api.machine.server.wsagent.WsAgentLauncherImpl.class);
|
||||
bind(org.eclipse.che.api.agent.server.wsagent.WsAgentLauncher.class)
|
||||
.to(org.eclipse.che.api.agent.server.wsagent.WsAgentLauncherImpl.class);
|
||||
|
||||
bind(org.eclipse.che.api.machine.server.terminal.MachineTerminalLauncher.class);
|
||||
bind(org.eclipse.che.api.agent.server.terminal.MachineTerminalLauncher.class);
|
||||
bind(org.eclipse.che.api.deploy.WsMasterAnalyticsAddresser.class);
|
||||
|
||||
Multibinder<org.eclipse.che.api.machine.server.spi.InstanceProvider> machineImageProviderMultibinder =
|
||||
|
|
|
|||
|
|
@ -18,8 +18,7 @@ import org.eclipse.che.api.core.rest.annotations.Required;
|
|||
import org.eclipse.che.api.core.rest.annotations.Valid;
|
||||
import org.eclipse.che.api.core.rest.shared.dto.Link;
|
||||
import org.eclipse.che.api.core.rest.shared.dto.ServiceDescriptor;
|
||||
import org.eclipse.che.commons.test.SelfReturningAnswer;
|
||||
import org.eclipse.che.dto.server.DtoFactory;
|
||||
import org.eclipse.che.commons.test.mockito.answer.SelfReturningAnswer;
|
||||
import org.everrest.assured.EverrestJetty;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.Spy;
|
||||
|
|
@ -144,4 +143,4 @@ public class RemoteServiceDescriptorTest {
|
|||
private String getServerUrl(ITestContext ctx) {
|
||||
return "http://localhost:" + ctx.getAttribute(EverrestJetty.JETTY_PORT) + "/rest";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -26,6 +26,4 @@ public interface Recipe {
|
|||
* Returns recipe script, which is used to instantiate new machine
|
||||
*/
|
||||
String getScript();
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -10,16 +10,8 @@
|
|||
*******************************************************************************/
|
||||
package org.eclipse.che.api.core.model.workspace;
|
||||
|
||||
import org.eclipse.che.api.core.model.machine.MachineStatus;
|
||||
|
||||
/**
|
||||
* Defines the contract between workspace and its dev-machine.
|
||||
*
|
||||
* <p>Workspace status itself shows the state of the workspace dev-machine.
|
||||
* If workspace environment which is running contains not only the dev-machine, those machines
|
||||
* states won't affect workspace status at all.
|
||||
*
|
||||
* <p>{@link MachineStatus} is responsible for states of machines different from the dev-machine.
|
||||
* Defines the contract between workspace and its active environment.
|
||||
*
|
||||
* <p>Workspace is rather part of the {@link Workspace} than {@link WorkspaceRuntime} or {@link WorkspaceConfig},
|
||||
* as it shows the state of <b>certain</b> user's workspace and exists <b>earlier</b> than runtime workspace instance
|
||||
|
|
@ -31,7 +23,7 @@ import org.eclipse.che.api.core.model.machine.MachineStatus;
|
|||
public enum WorkspaceStatus {
|
||||
|
||||
/**
|
||||
* Workspace considered as starting if and only if its dev-machine is booting(creating).
|
||||
* Workspace considered as starting if and only if its active environment is booting.
|
||||
*
|
||||
* <p>Workspace becomes starting only if it was {@link #STOPPED}.
|
||||
* The status map:
|
||||
|
|
@ -39,45 +31,39 @@ public enum WorkspaceStatus {
|
|||
* STOPPED -> <b>STARTING</b> -> RUNNING (normal behaviour)
|
||||
* STOPPED -> <b>STARTING</b> -> STOPPED (failed to start)
|
||||
* </pre>
|
||||
*
|
||||
* @see MachineStatus#CREATING
|
||||
*/
|
||||
STARTING,
|
||||
|
||||
/**
|
||||
* Workspace considered as running if and only if its dev-machine was successfully started and it is running.
|
||||
* Workspace considered as running if and only if its environment is running.
|
||||
*
|
||||
* <p>Workspace becomes running after it was {@link #STARTING}.
|
||||
* The status map:
|
||||
* <pre>
|
||||
* STARTING -> <b>RUNNING</b> -> STOPPING (normal behaviour)
|
||||
* STARTING -> <b>RUNNING</b> -> STOPPED (dev-machine was interrupted)
|
||||
* STARTING -> <b>RUNNING</b> -> STOPPED (environment start was interrupted)
|
||||
* </pre>
|
||||
*
|
||||
* @see MachineStatus#RUNNING
|
||||
*/
|
||||
RUNNING,
|
||||
|
||||
/**
|
||||
* Workspace considered as stopping if and only if its dev-machine is shutting down(destroying).
|
||||
* Workspace considered as stopping if and only if its active environment is shutting down.
|
||||
*
|
||||
* <p>Workspace is in stopping status only if it was in {@link #RUNNING} status before.
|
||||
* The status map:
|
||||
* <pre>
|
||||
* RUNNING -> <b>STOPPING</b> -> STOPPED (normal behaviour)/(error while stopping)
|
||||
* </pre>
|
||||
*
|
||||
* @see MachineStatus#DESTROYING
|
||||
*/
|
||||
STOPPING,
|
||||
|
||||
/**
|
||||
* Workspace considered as stopped when:
|
||||
* <ul>
|
||||
* <li>Dev-machine was successfully destroyed(stopped)</li>
|
||||
* <li>Error occurred while dev-machine was stopping</li>
|
||||
* <li>Environment was successfully stopped</li>
|
||||
* <li>Error occurred while environment was stopping</li>
|
||||
* <li>Dev-machine failed to start</li>
|
||||
* <li>Running dev-machine was interrupted by internal problem(e.g. OOM)</li>
|
||||
* <li>Running environment machine was stopped by internal problem(e.g. OOM of a machine)</li>
|
||||
* <li>Workspace hasn't been started yet(e.g stopped is the status of the user's workspace instance without its runtime)</li>
|
||||
* </ul>
|
||||
*
|
||||
|
|
@ -85,7 +71,7 @@ public enum WorkspaceStatus {
|
|||
* <pre>
|
||||
* STOPPING -> <b>STOPPED</b> (normal behaviour)/(error while stopping)
|
||||
* STARTING -> <b>STOPPED</b> (failed to start)
|
||||
* RUNNING -> <b>STOPPED</b> (dev-machine was interrupted)
|
||||
* RUNNING -> <b>STOPPED</b> (environment machine was interrupted)
|
||||
* </pre>
|
||||
*/
|
||||
STOPPED
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@
|
|||
* Contributors:
|
||||
* Codenvy, S.A. - initial API and implementation
|
||||
*******************************************************************************/
|
||||
package org.eclipse.che.commons.test;
|
||||
package org.eclipse.che.commons.test.mockito.answer;
|
||||
|
||||
import org.mockito.Mockito;
|
||||
import org.mockito.invocation.InvocationOnMock;
|
||||
|
|
@ -0,0 +1,160 @@
|
|||
/*******************************************************************************
|
||||
* Copyright (c) 2012-2016 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.commons.test.mockito.answer;
|
||||
|
||||
import org.mockito.invocation.InvocationOnMock;
|
||||
import org.mockito.stubbing.Answer;
|
||||
|
||||
import java.util.concurrent.CountDownLatch;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
/**
|
||||
* Answer class that helps to lock execution of test in separate threads for testing purposes.
|
||||
* <p/>
|
||||
* It can hold waiting thread until this answer is called.<br/>
|
||||
* It can hold waiting thread that uses mock until answering to mock call is allowed.
|
||||
*
|
||||
* Here is an example of complex test that ensures that (for example) locked area is not called
|
||||
* when other thread try to access it.
|
||||
* <pre class="code"><code class="java">
|
||||
* // given
|
||||
* WaitingAnswer<Void> waitingAnswer = new WaitingAnswer<>();
|
||||
* doAnswer(waitingAnswer).when(someClassUsedInTestedClass).someMethod(eq(param1), eq(param2));
|
||||
*
|
||||
* // start doing something in a separate thread
|
||||
* executor.execute(() -> testedClass.doSomething());
|
||||
* // wait until separate thread call answer to find the moment when critical area is occupied
|
||||
* // to make test fast wait not more that provided timeout
|
||||
* waitingAnswer.waitAnswerCall(1, TimeUnit.SECONDS);
|
||||
*
|
||||
* // when
|
||||
* try {
|
||||
* // start doing something in current thread
|
||||
* testedClass.doSomething()
|
||||
* // this area should not be reachable until answer is completed!
|
||||
* fail("Error message");
|
||||
* } finally {
|
||||
* // In this case exception can be suppressed
|
||||
* // Test is simplified to provide clean example
|
||||
*
|
||||
* // then
|
||||
* // complete waiting answer
|
||||
* waitingAnswer.completeAnswer();
|
||||
* // ensure that someMethod is called only once to confirm that testedClass.doSomething()
|
||||
* // doesn't call it this particular test
|
||||
* verify(someClassUsedInTestedClass, timeout(100).times(1)).someMethod(any(), any());
|
||||
* }
|
||||
* </code></pre>
|
||||
*
|
||||
* @author Alexander Garagatyi
|
||||
*/
|
||||
public class WaitingAnswer<T> implements Answer<T> {
|
||||
|
||||
private final CountDownLatch answerIsCalledLatch;
|
||||
private final CountDownLatch answerResultIsUnlockedLatch;
|
||||
|
||||
private long maxWaitingTime;
|
||||
private TimeUnit maxWaitingUnit;
|
||||
private T result;
|
||||
|
||||
private volatile String error;
|
||||
|
||||
public WaitingAnswer() {
|
||||
this.maxWaitingTime = 1;
|
||||
this.maxWaitingUnit = TimeUnit.SECONDS;
|
||||
this.result = null;
|
||||
this.answerIsCalledLatch = new CountDownLatch(1);
|
||||
this.answerResultIsUnlockedLatch = new CountDownLatch(1);
|
||||
this.error = null;
|
||||
}
|
||||
|
||||
public WaitingAnswer(long maxWaitingTime, TimeUnit maxWaitingUnit) {
|
||||
this();
|
||||
this.maxWaitingTime = maxWaitingTime;
|
||||
this.maxWaitingUnit = maxWaitingUnit;
|
||||
}
|
||||
|
||||
public WaitingAnswer(T result) {
|
||||
this();
|
||||
this.result = result;
|
||||
}
|
||||
|
||||
public WaitingAnswer(T result,
|
||||
long maxWaitingTime,
|
||||
TimeUnit maxWaitingUnit) {
|
||||
this();
|
||||
this.result = result;
|
||||
this.maxWaitingTime = maxWaitingTime;
|
||||
this.maxWaitingUnit = maxWaitingUnit;
|
||||
}
|
||||
|
||||
/**
|
||||
* Waits until answer is called in method {@link #answer(InvocationOnMock)}.
|
||||
*
|
||||
* @param maxWaitingTime
|
||||
* max time to wait
|
||||
* @param maxWaitingUnit
|
||||
* time unit of the max waiting time argument
|
||||
* @throws Exception
|
||||
* if the waiting time elapsed before this answer is called
|
||||
* @see #answer(InvocationOnMock)
|
||||
*/
|
||||
public void waitAnswerCall(long maxWaitingTime, TimeUnit maxWaitingUnit) throws Exception {
|
||||
if (!answerIsCalledLatch.await(maxWaitingTime, maxWaitingUnit)) {
|
||||
error = "Waiting time elapsed but answer is not called";
|
||||
throw new Exception(error);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Stops process of waiting returning result of answer in method {@link #answer(InvocationOnMock)}.
|
||||
*
|
||||
* @throws Exception
|
||||
* if this answer waiting time elapsed before this method is called
|
||||
* @see #answer(InvocationOnMock)
|
||||
*/
|
||||
public void completeAnswer() throws Exception {
|
||||
answerResultIsUnlockedLatch.countDown();
|
||||
if (error != null) {
|
||||
throw new Exception(error);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Stops waiting until answer is called in method {@link #waitAnswerCall(long, TimeUnit)} and
|
||||
* then waits until method {@link #completeAnswer()} is called.
|
||||
*
|
||||
* @param invocationOnMock
|
||||
* see {@link Answer#answer(InvocationOnMock)}
|
||||
* @return returns answer result if provided in constructor or null otherwise
|
||||
* @throws Exception
|
||||
* if answer call or answer result waiting time is elapsed
|
||||
* @throws Throwable
|
||||
* in the same cases as in {@link Answer#answer(InvocationOnMock)}
|
||||
* @see #waitAnswerCall(long, TimeUnit)
|
||||
* @see #completeAnswer()
|
||||
* @see Answer#answer(InvocationOnMock)
|
||||
*/
|
||||
@Override
|
||||
public T answer(InvocationOnMock invocationOnMock) throws Throwable {
|
||||
// report start of answer call
|
||||
answerIsCalledLatch.countDown();
|
||||
if (error != null) {
|
||||
throw new Exception(error);
|
||||
}
|
||||
// wait until another thread unlocks returning of answer
|
||||
if (!answerResultIsUnlockedLatch.await(maxWaitingTime, maxWaitingUnit)) {
|
||||
error = "Waiting time elapsed but completeAnswer is not called";
|
||||
throw new Exception(error);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
|
@ -30,11 +30,13 @@ public interface MachineServiceClient {
|
|||
/**
|
||||
* Get machine information by it's id.
|
||||
*
|
||||
* @param workspaceId
|
||||
* ID of workspace
|
||||
* @param machineId
|
||||
* ID of the machine
|
||||
* @return a promise that resolves to the {@link MachineDto}, or rejects with an error
|
||||
*/
|
||||
Promise<MachineDto> getMachine(@NotNull String machineId);
|
||||
Promise<MachineDto> getMachine(@NotNull String workspaceId, @NotNull String machineId);
|
||||
|
||||
/**
|
||||
* Returns list of machines which are bounded to the specified workspace.
|
||||
|
|
@ -48,15 +50,19 @@ public interface MachineServiceClient {
|
|||
/**
|
||||
* Destroy machine with the specified ID.
|
||||
*
|
||||
* @param workspaceId
|
||||
* ID of workspace
|
||||
* @param machineId
|
||||
* ID of machine that should be destroyed
|
||||
* @return a promise that will resolve when the machine has been destroyed, or rejects with an error
|
||||
*/
|
||||
Promise<Void> destroyMachine(@NotNull String machineId);
|
||||
Promise<Void> destroyMachine(@NotNull String workspaceId, @NotNull String machineId);
|
||||
|
||||
/**
|
||||
* Execute a command in machine.
|
||||
*
|
||||
* @param workspaceId
|
||||
* ID of workspace
|
||||
* @param machineId
|
||||
* ID of the machine where command should be executed
|
||||
* @param command
|
||||
|
|
@ -65,42 +71,34 @@ public interface MachineServiceClient {
|
|||
* websocket chanel for execution logs
|
||||
* @return a promise that resolves to the {@link MachineProcessDto}, or rejects with an error
|
||||
*/
|
||||
Promise<MachineProcessDto> executeCommand(@NotNull String machineId,
|
||||
Promise<MachineProcessDto> executeCommand(@NotNull String workspaceId,
|
||||
@NotNull String machineId,
|
||||
@NotNull Command command,
|
||||
@Nullable String outputChannel);
|
||||
|
||||
/**
|
||||
* Get processes from the specified machine.
|
||||
*
|
||||
* @param workspaceId
|
||||
* ID of workspace
|
||||
* @param machineId
|
||||
* ID of machine to get processes information from
|
||||
* @return a promise that will provide a list of {@link MachineProcessDto}s for the given machine ID
|
||||
*/
|
||||
Promise<List<MachineProcessDto>> getProcesses(@NotNull String machineId);
|
||||
Promise<List<MachineProcessDto>> getProcesses(@NotNull String workspaceId, @NotNull String machineId);
|
||||
|
||||
/**
|
||||
* Stop process in machine.
|
||||
*
|
||||
* @param workspaceId
|
||||
* ID of workspace
|
||||
* @param machineId
|
||||
* ID of the machine where process should be stopped
|
||||
* @param processId
|
||||
* ID of the process to stop
|
||||
* @return a promise that will resolve when the process has been stopped, or rejects with an error
|
||||
*/
|
||||
Promise<Void> stopProcess(@NotNull String machineId, int processId);
|
||||
|
||||
/**
|
||||
* Get file content.
|
||||
*
|
||||
* @param machineId
|
||||
* ID of the machine
|
||||
* @param path
|
||||
* path to file on machine instance
|
||||
* @param startFrom
|
||||
* line number to start reading from
|
||||
* @param limit
|
||||
* limitation on line
|
||||
* @return a promise that will provide the file content, or rejects with an error
|
||||
*/
|
||||
Promise<String> getFileContent(@NotNull String machineId, @NotNull String path, int startFrom, int limit);
|
||||
Promise<Void> stopProcess(@NotNull String workspaceId,
|
||||
@NotNull String machineId,
|
||||
int processId);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -20,7 +20,6 @@ import org.eclipse.che.commons.annotation.Nullable;
|
|||
import org.eclipse.che.ide.rest.AsyncRequestFactory;
|
||||
import org.eclipse.che.ide.rest.DtoUnmarshallerFactory;
|
||||
import org.eclipse.che.ide.rest.RestContext;
|
||||
import org.eclipse.che.ide.rest.StringUnmarshaller;
|
||||
import org.eclipse.che.ide.ui.loaders.request.LoaderFactory;
|
||||
|
||||
import javax.validation.constraints.NotNull;
|
||||
|
|
@ -50,12 +49,14 @@ public class MachineServiceClientImpl implements MachineServiceClient {
|
|||
this.dtoUnmarshallerFactory = dtoUnmarshallerFactory;
|
||||
this.asyncRequestFactory = asyncRequestFactory;
|
||||
this.loaderFactory = loaderFactory;
|
||||
this.baseHttpUrl = restContext + "/machine";
|
||||
this.baseHttpUrl = restContext + "/workspace/";
|
||||
}
|
||||
|
||||
@Override
|
||||
public Promise<MachineDto> getMachine(@NotNull final String machineId) {
|
||||
return asyncRequestFactory.createGetRequest(baseHttpUrl + '/' + machineId)
|
||||
public Promise<MachineDto> getMachine(@NotNull final String workspaceId,
|
||||
@NotNull final String machineId) {
|
||||
return asyncRequestFactory.createGetRequest(baseHttpUrl + workspaceId +
|
||||
"/machine/" + machineId)
|
||||
.header(ACCEPT, APPLICATION_JSON)
|
||||
.loader(loaderFactory.newLoader("Getting info about machine..."))
|
||||
.send(dtoUnmarshallerFactory.newUnmarshaller(MachineDto.class));
|
||||
|
|
@ -63,50 +64,57 @@ public class MachineServiceClientImpl implements MachineServiceClient {
|
|||
|
||||
@Override
|
||||
public Promise<List<MachineDto>> getMachines(@NotNull String workspaceId) {
|
||||
return asyncRequestFactory.createGetRequest(baseHttpUrl + "?workspace=" + workspaceId)
|
||||
return asyncRequestFactory.createGetRequest(baseHttpUrl + workspaceId + "/machine")
|
||||
.header(ACCEPT, APPLICATION_JSON)
|
||||
.loader(loaderFactory.newLoader("Getting info about bound machines..."))
|
||||
.send(dtoUnmarshallerFactory.newListUnmarshaller(MachineDto.class));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Promise<Void> destroyMachine(@NotNull final String machineId) {
|
||||
return asyncRequestFactory.createRequest(DELETE, baseHttpUrl + '/' + machineId, null, false)
|
||||
public Promise<Void> destroyMachine(@NotNull final String workspaceId,
|
||||
@NotNull final String machineId) {
|
||||
return asyncRequestFactory.createRequest(DELETE,
|
||||
baseHttpUrl + workspaceId +
|
||||
"/machine/" + machineId,
|
||||
null,
|
||||
false)
|
||||
.loader(loaderFactory.newLoader("Destroying machine..."))
|
||||
.send();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Promise<MachineProcessDto> executeCommand(@NotNull final String machineId,
|
||||
public Promise<MachineProcessDto> executeCommand(@NotNull final String workspaceId,
|
||||
@NotNull final String machineId,
|
||||
@NotNull final Command command,
|
||||
@Nullable final String outputChannel) {
|
||||
return asyncRequestFactory.createPostRequest(baseHttpUrl + '/' + machineId + "/command?outputChannel=" + outputChannel, command)
|
||||
return asyncRequestFactory.createPostRequest(baseHttpUrl + workspaceId +
|
||||
"/machine/" + machineId +
|
||||
"/command?outputChannel=" + outputChannel,
|
||||
command)
|
||||
.header(ACCEPT, APPLICATION_JSON)
|
||||
.loader(loaderFactory.newLoader("Executing command..."))
|
||||
.send(dtoUnmarshallerFactory.newUnmarshaller(MachineProcessDto.class));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Promise<List<MachineProcessDto>> getProcesses(@NotNull final String machineId) {
|
||||
return asyncRequestFactory.createGetRequest(baseHttpUrl + "/" + machineId + "/process")
|
||||
public Promise<List<MachineProcessDto>> getProcesses(@NotNull final String workspaceId,
|
||||
@NotNull final String machineId) {
|
||||
return asyncRequestFactory.createGetRequest(baseHttpUrl + workspaceId +
|
||||
"/machine/" + machineId +
|
||||
"/process")
|
||||
.header(ACCEPT, APPLICATION_JSON)
|
||||
.loader(loaderFactory.newLoader("Getting machine processes..."))
|
||||
.send(dtoUnmarshallerFactory.newListUnmarshaller(MachineProcessDto.class));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Promise<Void> stopProcess(@NotNull final String machineId, final int processId) {
|
||||
return asyncRequestFactory.createDeleteRequest(baseHttpUrl + '/' + machineId + "/process/" + processId)
|
||||
public Promise<Void> stopProcess(@NotNull final String workspaceId,
|
||||
@NotNull final String machineId,
|
||||
final int processId) {
|
||||
return asyncRequestFactory.createDeleteRequest(baseHttpUrl + workspaceId +
|
||||
"/machine/" + machineId +
|
||||
"/process/" + processId)
|
||||
.loader(loaderFactory.newLoader("Stopping process..."))
|
||||
.send();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Promise<String> getFileContent(@NotNull final String machineId, final @NotNull String path, final int startFrom,
|
||||
final int limit) {
|
||||
String url = baseHttpUrl + "/" + machineId + "/filepath/" + path + "?startFrom=" + startFrom + "&limit=" + limit;
|
||||
return asyncRequestFactory.createGetRequest(url)
|
||||
.loader(loaderFactory.newLoader("Loading file content..."))
|
||||
.send(new StringUnmarshaller());
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -27,7 +27,7 @@ import org.eclipse.che.ide.api.workspace.WorkspaceServiceClient;
|
|||
import org.eclipse.che.api.workspace.shared.dto.EnvironmentDto;
|
||||
import org.eclipse.che.api.workspace.shared.dto.WorkspaceConfigDto;
|
||||
import org.eclipse.che.api.workspace.shared.dto.WorkspaceDto;
|
||||
import org.eclipse.che.commons.test.SelfReturningAnswer;
|
||||
import org.eclipse.che.commons.test.mockito.answer.SelfReturningAnswer;
|
||||
import org.eclipse.che.ide.CoreLocalizationConstant;
|
||||
import org.eclipse.che.ide.workspace.DefaultWorkspaceComponent;
|
||||
import org.eclipse.che.ide.workspace.WorkspaceComponent;
|
||||
|
|
|
|||
|
|
@ -18,7 +18,7 @@ import com.google.gson.GsonBuilder;
|
|||
|
||||
import org.eclipse.che.commons.json.JsonParseException;
|
||||
import org.eclipse.che.commons.lang.ws.rs.ExtMediaType;
|
||||
import org.eclipse.che.commons.test.SelfReturningAnswer;
|
||||
import org.eclipse.che.commons.test.mockito.answer.SelfReturningAnswer;
|
||||
import org.eclipse.che.dto.server.DtoFactory;
|
||||
import org.eclipse.che.plugin.docker.client.connection.CloseConnectionInputStream;
|
||||
import org.eclipse.che.plugin.docker.client.connection.DockerConnection;
|
||||
|
|
|
|||
|
|
@ -211,11 +211,11 @@ public class DockerInstance extends AbstractInstance {
|
|||
}
|
||||
|
||||
@Override
|
||||
public MachineSource saveToSnapshot(String owner) throws MachineException {
|
||||
public MachineSource saveToSnapshot() throws MachineException {
|
||||
try {
|
||||
String image = generateRepository();
|
||||
if(!snapshotUseRegistry) {
|
||||
commitContainer(owner, image, LATEST_TAG);
|
||||
commitContainer(image, LATEST_TAG);
|
||||
return new DockerMachineSource(image).withTag(LATEST_TAG);
|
||||
}
|
||||
|
||||
|
|
@ -224,7 +224,7 @@ public class DockerInstance extends AbstractInstance {
|
|||
.withTag(LATEST_TAG);
|
||||
|
||||
final String fullRepo = pushParams.getFullRepo();
|
||||
commitContainer(owner, fullRepo, LATEST_TAG);
|
||||
commitContainer(fullRepo, LATEST_TAG);
|
||||
//TODO fix this workaround. Docker image is not visible after commit when using swarm
|
||||
Thread.sleep(2000);
|
||||
final ProgressLineFormatterImpl lineFormatter = new ProgressLineFormatterImpl();
|
||||
|
|
@ -246,10 +246,9 @@ public class DockerInstance extends AbstractInstance {
|
|||
}
|
||||
|
||||
@VisibleForTesting
|
||||
void commitContainer(String owner, String repository, String tag) throws IOException {
|
||||
void commitContainer(String repository, String tag) throws IOException {
|
||||
String comment = format("Suspended at %1$ta %1$tb %1$td %1$tT %1$tZ %1$tY",
|
||||
System.currentTimeMillis());
|
||||
comment = owner == null ? comment : comment + " by " + owner;
|
||||
// !! We SHOULD NOT pause container before commit because all execs will fail
|
||||
// to push image to private registry it should be tagged with registry in repo name
|
||||
// https://docs.docker.com/reference/api/docker_remote_api_v1.16/#push-an-image-on-the-registry
|
||||
|
|
|
|||
|
|
@ -79,7 +79,6 @@ import java.util.HashMap;
|
|||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.ExecutorService;
|
||||
import java.util.concurrent.Executors;
|
||||
|
|
@ -506,7 +505,7 @@ public class DockerInstanceProvider implements InstanceProvider {
|
|||
final String imageName,
|
||||
final LineConsumer outputConsumer)
|
||||
throws MachineException {
|
||||
Optional<String> containerIdOptional = null;
|
||||
String containerIdCopy = null;
|
||||
try {
|
||||
final Map<String, Map<String, String>> portsToExpose;
|
||||
final String[] volumes;
|
||||
|
|
@ -558,7 +557,7 @@ public class DockerInstanceProvider implements InstanceProvider {
|
|||
final String containerId = docker.createContainer(CreateContainerParams.create(config)
|
||||
.withContainerName(containerName))
|
||||
.getId();
|
||||
containerIdOptional = Optional.ofNullable(containerId);
|
||||
containerIdCopy = containerId;
|
||||
|
||||
docker.startContainer(StartContainerParams.create(containerId));
|
||||
|
||||
|
|
@ -593,7 +592,9 @@ public class DockerInstanceProvider implements InstanceProvider {
|
|||
machine.getId(), containerId, node.getHost());
|
||||
}
|
||||
|
||||
dockerInstanceStopDetector.startDetection(containerId, machine.getId());
|
||||
dockerInstanceStopDetector.startDetection(containerId,
|
||||
machine.getId(),
|
||||
machine.getWorkspaceId());
|
||||
|
||||
return dockerMachineFactory.createInstance(machine,
|
||||
containerId,
|
||||
|
|
@ -601,18 +602,17 @@ public class DockerInstanceProvider implements InstanceProvider {
|
|||
node,
|
||||
outputConsumer);
|
||||
} catch (IOException e) {
|
||||
cleanUpContainer(containerIdOptional);
|
||||
cleanUpContainer(containerIdCopy);
|
||||
throw new MachineException(e.getLocalizedMessage(), e);
|
||||
} catch (MachineException e) {
|
||||
cleanUpContainer(containerIdOptional);
|
||||
cleanUpContainer(containerIdCopy);
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
|
||||
private void cleanUpContainer(Optional<String> containerIdOptional) {
|
||||
private void cleanUpContainer(@Nullable String containerId) {
|
||||
try {
|
||||
if (containerIdOptional.isPresent()) {
|
||||
String containerId = containerIdOptional.get();
|
||||
if (containerId != null) {
|
||||
docker.removeContainer(RemoveContainerParams.create(containerId).withRemoveVolumes(true).withForce(true));
|
||||
}
|
||||
} catch (Exception ex) {
|
||||
|
|
|
|||
|
|
@ -16,6 +16,7 @@ import com.google.common.util.concurrent.ThreadFactoryBuilder;
|
|||
|
||||
import org.eclipse.che.api.core.notification.EventService;
|
||||
import org.eclipse.che.api.machine.server.event.InstanceStateEvent;
|
||||
import org.eclipse.che.commons.lang.Pair;
|
||||
import org.eclipse.che.plugin.docker.client.DockerConnector;
|
||||
import org.eclipse.che.plugin.docker.client.MessageProcessor;
|
||||
import org.eclipse.che.plugin.docker.client.json.Event;
|
||||
|
|
@ -43,10 +44,10 @@ import java.util.concurrent.TimeUnit;
|
|||
public class DockerInstanceStopDetector {
|
||||
private static final Logger LOG = LoggerFactory.getLogger(DockerInstanceStopDetector.class);
|
||||
|
||||
private final EventService eventService;
|
||||
private final DockerConnector dockerConnector;
|
||||
private final ExecutorService executorService;
|
||||
private final Map<String, String> instances;
|
||||
private final EventService eventService;
|
||||
private final DockerConnector dockerConnector;
|
||||
private final ExecutorService executorService;
|
||||
private final Map<String, Pair<String, String>> instances;
|
||||
/*
|
||||
Helps differentiate container main process OOM from other processes OOM
|
||||
Algorithm:
|
||||
|
|
@ -59,7 +60,7 @@ public class DockerInstanceStopDetector {
|
|||
That's why cache expires in X seconds.
|
||||
X was set as 10 empirically.
|
||||
*/
|
||||
private final Cache<String, String> containersOomTimestamps;
|
||||
private final Cache<String, String> containersOomTimestamps;
|
||||
|
||||
private long lastProcessedEventDate = 0;
|
||||
|
||||
|
|
@ -84,9 +85,13 @@ public class DockerInstanceStopDetector {
|
|||
* id of a container to start detection for
|
||||
* @param machineId
|
||||
* id of a machine which container implements
|
||||
* @param workspaceId
|
||||
* id of a workspace that owns machine
|
||||
*/
|
||||
public void startDetection(String containerId, String machineId) {
|
||||
instances.put(containerId, machineId);
|
||||
public void startDetection(String containerId,
|
||||
String machineId,
|
||||
String workspaceId) {
|
||||
instances.put(containerId, Pair.of(machineId, workspaceId));
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -140,9 +145,11 @@ public class DockerInstanceStopDetector {
|
|||
} else {
|
||||
instanceStateChangeType = InstanceStateEvent.Type.DIE;
|
||||
}
|
||||
final String instanceId = instances.get(message.getId());
|
||||
if (instanceId != null) {
|
||||
eventService.publish(new InstanceStateEvent(instanceId, instanceStateChangeType));
|
||||
Pair<String, String> instanceIds = instances.get(message.getId());
|
||||
if (instanceIds != null) {
|
||||
eventService.publish(new InstanceStateEvent(instanceIds.first,
|
||||
instanceIds.second,
|
||||
instanceStateChangeType));
|
||||
lastProcessedEventDate = message.getTime();
|
||||
}
|
||||
break;
|
||||
|
|
|
|||
|
|
@ -12,7 +12,7 @@ package org.eclipse.che.plugin.docker.machine;
|
|||
|
||||
import org.eclipse.che.api.machine.server.exception.MachineException;
|
||||
import org.eclipse.che.api.machine.server.spi.Instance;
|
||||
import org.eclipse.che.api.machine.server.terminal.MachineImplSpecificTerminalLauncher;
|
||||
import org.eclipse.che.api.agent.server.terminal.MachineImplSpecificTerminalLauncher;
|
||||
import org.eclipse.che.plugin.docker.client.DockerConnector;
|
||||
import org.eclipse.che.plugin.docker.client.Exec;
|
||||
import org.eclipse.che.plugin.docker.client.LogMessage;
|
||||
|
|
|
|||
|
|
@ -13,7 +13,8 @@ package org.eclipse.che.plugin.docker.machine.cleaner;
|
|||
import com.google.inject.Inject;
|
||||
import com.google.inject.Singleton;
|
||||
|
||||
import org.eclipse.che.api.machine.server.MachineRegistry;
|
||||
import org.eclipse.che.api.core.NotFoundException;
|
||||
import org.eclipse.che.api.environment.server.CheEnvironmentEngine;
|
||||
import org.eclipse.che.commons.schedule.ScheduleRate;
|
||||
import org.eclipse.che.plugin.docker.client.DockerConnector;
|
||||
import org.eclipse.che.plugin.docker.client.json.ContainerListEntry;
|
||||
|
|
@ -39,15 +40,16 @@ public class DockerContainerCleaner implements Runnable {
|
|||
|
||||
private static final Logger LOG = LoggerFactory.getLogger(DockerContainerCleaner.class);
|
||||
|
||||
private final MachineRegistry machineRegistry;
|
||||
// TODO replace with WorkspaceManager
|
||||
private final CheEnvironmentEngine environmentEngine;
|
||||
private final DockerConnector dockerConnector;
|
||||
private final DockerContainerNameGenerator nameGenerator;
|
||||
|
||||
@Inject
|
||||
public DockerContainerCleaner(MachineRegistry machineRegistry,
|
||||
public DockerContainerCleaner(CheEnvironmentEngine environmentEngine,
|
||||
DockerConnector dockerConnector,
|
||||
DockerContainerNameGenerator nameGenerator) {
|
||||
this.machineRegistry = machineRegistry;
|
||||
this.environmentEngine = environmentEngine;
|
||||
this.dockerConnector = dockerConnector;
|
||||
this.nameGenerator = nameGenerator;
|
||||
}
|
||||
|
|
@ -60,8 +62,17 @@ public class DockerContainerCleaner implements Runnable {
|
|||
try {
|
||||
for (ContainerListEntry container : dockerConnector.listContainers()) {
|
||||
Optional<ContainerNameInfo> optional = nameGenerator.parse(container.getNames()[0]);
|
||||
if (optional.isPresent() && !machineRegistry.isExist(optional.get().getMachineId())) {
|
||||
cleanUp(container);
|
||||
if (optional.isPresent()) {
|
||||
try {
|
||||
// container is orphaned if not found exception is thrown
|
||||
environmentEngine.getMachine(optional.get().getWorkspaceId(),
|
||||
optional.get().getMachineId());
|
||||
|
||||
} catch (NotFoundException e) {
|
||||
cleanUp(container);
|
||||
} catch (Exception e) {
|
||||
LOG.error("Failed to clean up inactive container. " + e.getLocalizedMessage(), e);
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (IOException e) {
|
||||
|
|
|
|||
|
|
@ -15,7 +15,7 @@ import com.google.inject.multibindings.Multibinder;
|
|||
import com.google.inject.name.Names;
|
||||
|
||||
import org.eclipse.che.api.core.model.machine.ServerConf;
|
||||
import org.eclipse.che.api.machine.server.terminal.MachineImplSpecificTerminalLauncher;
|
||||
import org.eclipse.che.api.agent.server.terminal.MachineImplSpecificTerminalLauncher;
|
||||
import org.eclipse.che.plugin.docker.machine.DockerMachineImplTerminalLauncher;
|
||||
import org.eclipse.che.plugin.docker.machine.ext.provider.TerminalServerConfProvider;
|
||||
|
||||
|
|
|
|||
|
|
@ -15,7 +15,7 @@ import com.google.inject.assistedinject.FactoryModuleBuilder;
|
|||
import com.google.inject.multibindings.Multibinder;
|
||||
import com.google.inject.name.Names;
|
||||
|
||||
import org.eclipse.che.api.machine.server.MachineService;
|
||||
import org.eclipse.che.api.environment.server.MachineService;
|
||||
import org.eclipse.che.api.machine.server.spi.Instance;
|
||||
import org.eclipse.che.api.machine.server.spi.InstanceProcess;
|
||||
import org.eclipse.che.plugin.docker.machine.DockerInstance;
|
||||
|
|
|
|||
|
|
@ -139,9 +139,9 @@ public class DockerInstanceTest {
|
|||
@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()) + " by " + OWNER;
|
||||
System.currentTimeMillis());
|
||||
|
||||
dockerInstance.commitContainer(OWNER, REPOSITORY, TAG);
|
||||
dockerInstance.commitContainer(REPOSITORY, TAG);
|
||||
|
||||
verify(dockerConnectorMock, times(1)).commit(CommitParams.create(CONTAINER)
|
||||
.withRepository(REPOSITORY)
|
||||
|
|
@ -151,7 +151,7 @@ public class DockerInstanceTest {
|
|||
|
||||
@Test
|
||||
public void shouldSaveDockerInstanceStateIntoLocalImage() throws Exception {
|
||||
final MachineSource result = dockerInstance.saveToSnapshot(OWNER);
|
||||
final MachineSource result = dockerInstance.saveToSnapshot();
|
||||
|
||||
assertTrue(result instanceof DockerMachineSource);
|
||||
DockerMachineSource dockerMachineSource = (DockerMachineSource) result;
|
||||
|
|
@ -173,7 +173,7 @@ public class DockerInstanceTest {
|
|||
dockerInstance = getDockerInstance(getMachine(), REGISTRY, CONTAINER, IMAGE, true);
|
||||
when(dockerConnectorMock.push(any(PushParams.class), any(ProgressMonitor.class))).thenReturn(digest);
|
||||
|
||||
final MachineSource result = dockerInstance.saveToSnapshot(OWNER);
|
||||
final MachineSource result = dockerInstance.saveToSnapshot();
|
||||
|
||||
assertTrue(result instanceof DockerMachineSource);
|
||||
DockerMachineSource dockerMachineSource = (DockerMachineSource) result;
|
||||
|
|
@ -186,7 +186,7 @@ public class DockerInstanceTest {
|
|||
public void shouldThrowMachineExceptionWhenDockerCommitFailed() throws Exception{
|
||||
when(dockerConnectorMock.commit(any(CommitParams.class))).thenThrow(new IOException("err"));
|
||||
|
||||
dockerInstance.saveToSnapshot(OWNER);
|
||||
dockerInstance.saveToSnapshot();
|
||||
}
|
||||
|
||||
@Test(expectedExceptions = MachineException.class)
|
||||
|
|
@ -195,7 +195,7 @@ public class DockerInstanceTest {
|
|||
when(dockerConnectorMock.push(any(PushParams.class),
|
||||
any(ProgressMonitor.class))).thenThrow(new IOException("err"));
|
||||
|
||||
dockerInstance.saveToSnapshot(OWNER);
|
||||
dockerInstance.saveToSnapshot();
|
||||
}
|
||||
|
||||
private DockerInstance getDockerInstance() {
|
||||
|
|
|
|||
|
|
@ -10,9 +10,10 @@
|
|||
*******************************************************************************/
|
||||
package org.eclipse.che.plugin.docker.machine.cleaner;
|
||||
|
||||
import org.eclipse.che.api.machine.server.MachineRegistry;
|
||||
import org.eclipse.che.api.machine.server.exception.MachineException;
|
||||
import org.eclipse.che.api.core.NotFoundException;
|
||||
import org.eclipse.che.api.environment.server.CheEnvironmentEngine;
|
||||
import org.eclipse.che.api.machine.server.model.impl.MachineImpl;
|
||||
import org.eclipse.che.api.machine.server.spi.Instance;
|
||||
import org.eclipse.che.plugin.docker.client.DockerConnector;
|
||||
import org.eclipse.che.plugin.docker.client.json.ContainerListEntry;
|
||||
import org.eclipse.che.plugin.docker.client.params.RemoveContainerParams;
|
||||
|
|
@ -28,8 +29,8 @@ import org.testng.annotations.Test;
|
|||
import java.io.IOException;
|
||||
import java.util.Optional;
|
||||
|
||||
import static java.util.Optional.of;
|
||||
import static java.util.Arrays.asList;
|
||||
import static java.util.Optional.of;
|
||||
import static org.eclipse.che.plugin.docker.machine.DockerContainerNameGenerator.ContainerNameInfo;
|
||||
import static org.mockito.Matchers.anyString;
|
||||
import static org.mockito.Mockito.never;
|
||||
|
|
@ -64,12 +65,14 @@ public class DockerContainerCleanerTest {
|
|||
private static final String RUNNING_STATUS = "Up 6 hour ago";
|
||||
|
||||
@Mock
|
||||
private MachineRegistry machineRegistry;
|
||||
private CheEnvironmentEngine environmentEngine;
|
||||
@Mock
|
||||
private DockerConnector dockerConnector;
|
||||
@Mock
|
||||
private DockerContainerNameGenerator nameGenerator;
|
||||
|
||||
@Mock
|
||||
private Instance instance;
|
||||
@Mock
|
||||
private MachineImpl machineImpl1;
|
||||
@Mock
|
||||
|
|
@ -93,8 +96,9 @@ public class DockerContainerCleanerTest {
|
|||
private DockerContainerCleaner cleaner;
|
||||
|
||||
@BeforeMethod
|
||||
public void setUp() throws MachineException, IOException {
|
||||
when(machineRegistry.isExist(machineId1)).thenReturn(true);
|
||||
public void setUp() throws Exception {
|
||||
when(environmentEngine.getMachine(workspaceId1, machineId1)).thenReturn(instance);
|
||||
when(environmentEngine.getMachine(workspaceId2, machineId2)).thenThrow(new NotFoundException("test"));
|
||||
when(machineImpl1.getId()).thenReturn(machineId1);
|
||||
when(machineImpl1.getWorkspaceId()).thenReturn(workspaceId1);
|
||||
|
||||
|
|
@ -121,27 +125,30 @@ public class DockerContainerCleanerTest {
|
|||
|
||||
when(containerNameInfo2.getMachineId()).thenReturn(machineId2);
|
||||
when(containerNameInfo2.getWorkspaceId()).thenReturn(workspaceId2);
|
||||
|
||||
when(containerNameInfo3.getMachineId()).thenReturn(machineId2);
|
||||
when(containerNameInfo3.getWorkspaceId()).thenReturn(workspaceId2);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void cleanerShouldKillAndRemoveContainerIfThisContainerIsRunningAndContainerNameInfoIsNotEmptyAndContainerIsNotExistInTheAPI()
|
||||
throws MachineException, IOException {
|
||||
throws Exception {
|
||||
cleaner.run();
|
||||
|
||||
verify(dockerConnector).listContainers();
|
||||
|
||||
verify(nameGenerator, times(3)).parse(anyString());
|
||||
verify(machineRegistry, times(3)).isExist(anyString());
|
||||
verify(environmentEngine, times(3)).getMachine(anyString(), anyString());
|
||||
|
||||
verify(dockerConnector, times(2)).killContainer(anyString());
|
||||
verify(dockerConnector, times(2)).removeContainer(Matchers.<RemoveContainerParams>anyObject());
|
||||
verify(dockerConnector, times(2)).removeContainer(Matchers.anyObject());
|
||||
|
||||
verify(dockerConnector, never()).killContainer(containerId1);
|
||||
verify(dockerConnector, never()).removeContainer(RemoveContainerParams.create(containerId1).withForce(true).withRemoveVolumes(true));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void cleanerShouldRemoveButShouldNotKillContainerWithStatusNotRunning() throws IOException, MachineException {
|
||||
public void cleanerShouldRemoveButShouldNotKillContainerWithStatusNotRunning() throws Exception {
|
||||
when(container2.getStatus()).thenReturn(EXITED_STATUS);
|
||||
cleaner.run();
|
||||
|
||||
|
|
@ -150,14 +157,14 @@ public class DockerContainerCleanerTest {
|
|||
}
|
||||
|
||||
@Test
|
||||
public void cleanerShouldNotKillAndRemoveContainerIfMachineManagerDetectedExistingThisContainerInTheAPI() throws IOException {
|
||||
when(machineRegistry.isExist(anyString())).thenReturn(true);
|
||||
public void cleanerShouldNotKillAndRemoveContainerIfMachineManagerDetectedExistingThisContainerInTheAPI() throws Exception {
|
||||
when(environmentEngine.getMachine(anyString(), anyString())).thenReturn(instance);
|
||||
|
||||
cleaner.run();
|
||||
|
||||
verify(dockerConnector, never()).killContainer(anyString());
|
||||
|
||||
verify(dockerConnector, never()).removeContainer(Matchers.<RemoveContainerParams>anyObject());
|
||||
verify(dockerConnector, never()).removeContainer(Matchers.anyObject());
|
||||
}
|
||||
|
||||
@Test
|
||||
|
|
@ -168,6 +175,6 @@ public class DockerContainerCleanerTest {
|
|||
|
||||
verify(dockerConnector, never()).killContainer(anyString());
|
||||
|
||||
verify(dockerConnector, never()).removeContainer(Matchers.<RemoveContainerParams>anyObject());
|
||||
verify(dockerConnector, never()).removeContainer(Matchers.anyObject());
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,462 +0,0 @@
|
|||
/*******************************************************************************
|
||||
* Copyright (c) 2012-2016 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.integration;
|
||||
|
||||
/*import org.eclipse.che.api.core.NotFoundException;
|
||||
import org.eclipse.che.api.core.model.machine.MachineStatus;
|
||||
import org.eclipse.che.api.core.notification.EventService;
|
||||
import org.eclipse.che.api.core.util.LineConsumer;
|
||||
import org.eclipse.che.api.core.util.ValueHolder;
|
||||
import org.eclipse.che.api.machine.server.MachineInstanceProviders;
|
||||
import org.eclipse.che.api.machine.server.MachineManager;
|
||||
import org.eclipse.che.api.machine.server.MachineRegistry;
|
||||
import org.eclipse.che.api.machine.server.MachineService;
|
||||
import org.eclipse.che.api.machine.server.spi.SnapshotDao;
|
||||
import org.eclipse.che.api.machine.server.exception.MachineException;
|
||||
import org.eclipse.che.api.machine.server.spi.impl.SnapshotImpl;
|
||||
import org.eclipse.che.api.machine.server.model.impl.MachineImpl;
|
||||
import org.eclipse.che.api.machine.server.model.impl.MachineStateImpl;
|
||||
import org.eclipse.che.api.machine.server.spi.InstanceProvider;
|
||||
import org.eclipse.che.api.machine.shared.dto.CommandDto;
|
||||
import org.eclipse.che.api.machine.shared.dto.MachineProcessDto;
|
||||
import org.eclipse.che.api.machine.shared.dto.recipe.MachineRecipe;
|
||||
import org.eclipse.che.commons.env.EnvironmentContext;
|
||||
import org.eclipse.che.commons.user.User;
|
||||
import org.eclipse.che.commons.user.UserImpl;
|
||||
import org.eclipse.che.inject.ConfigurationProperties;
|
||||
import org.eclipse.che.plugin.docker.client.DockerConnector;
|
||||
import org.eclipse.che.plugin.docker.client.InitialAuthConfig;
|
||||
import org.eclipse.che.plugin.docker.client.ProgressLineFormatterImpl;
|
||||
import org.eclipse.che.plugin.docker.client.json.ContainerConfig;
|
||||
import org.eclipse.che.plugin.docker.client.json.HostConfig;
|
||||
import org.eclipse.che.plugin.docker.client.json.PortBinding;
|
||||
import org.eclipse.che.plugin.docker.machine.DockerInstanceKey;
|
||||
import org.eclipse.che.plugin.docker.machine.DockerInstanceProvider;
|
||||
import org.eclipse.che.plugin.docker.machine.DockerInstanceStopDetector;
|
||||
import org.eclipse.che.plugin.docker.machine.DockerMachineFactory;
|
||||
import org.eclipse.che.plugin.docker.machine.node.DockerNode;
|
||||
import org.eclipse.che.plugin.docker.machine.TestDockerMachineFactory;
|
||||
import org.eclipse.che.plugin.docker.machine.WorkspaceFolderNodePathProvider;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.testng.MockitoTestNGListener;
|
||||
import org.testng.annotations.AfterClass;
|
||||
import org.testng.annotations.AfterMethod;
|
||||
import org.testng.annotations.BeforeClass;
|
||||
import org.testng.annotations.BeforeMethod;
|
||||
import org.testng.annotations.Listeners;
|
||||
import org.testng.annotations.Test;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import static java.util.Collections.singletonMap;
|
||||
import static org.eclipse.che.dto.server.DtoFactory.newDto;
|
||||
import static org.mockito.Matchers.any;
|
||||
import static org.mockito.Matchers.anyString;
|
||||
import static org.mockito.Mockito.mock;
|
||||
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.assertFalse;
|
||||
import static org.testng.Assert.assertNotNull;
|
||||
import static org.testng.Assert.assertTrue;
|
||||
import static org.testng.Assert.fail;*/
|
||||
|
||||
// TODO rework authentication
|
||||
// TODO check removeSnapshotTest with https and password
|
||||
// TODO bind, unbind
|
||||
// TODO should we check result of tests with native calls?
|
||||
|
||||
/**
|
||||
* @author Alexander Garagatyi
|
||||
*/
|
||||
//@Listeners(value = {MockitoTestNGListener.class})
|
||||
public class ServiceTest {
|
||||
/* private static final String USER = "userId";
|
||||
private static final String SNAPSHOT_ID = "someSnapshotId";
|
||||
private static LineConsumer lineConsumer = new StdErrLineConsumer();
|
||||
|
||||
// set in method {@link saveSnapshotTest}
|
||||
// used in methods {@link createMachineFromSnapshotTest} and {@link removeSnapshotTest}
|
||||
private DockerInstanceKey pushedImage;
|
||||
|
||||
private SnapshotDao snapshotDao;
|
||||
private MachineRegistry machineRegistry;
|
||||
private DockerConnector docker;
|
||||
private MachineManager machineManager;
|
||||
private MachineService machineService;
|
||||
private String registryContainerId;
|
||||
@Mock
|
||||
private WorkspaceFolderNodePathProvider workspaceFolderNodePathProvider;
|
||||
@Mock
|
||||
private ConfigurationProperties configurationProperties;
|
||||
@Mock
|
||||
private DockerInstanceStopDetector dockerInstanceStopDetector;
|
||||
|
||||
private DockerMachineFactory dockerMachineFactory;
|
||||
|
||||
@BeforeClass
|
||||
public void setUpClass() throws Exception {
|
||||
when(configurationProperties.getProperties(anyString())).thenReturn(Collections.<String, String>emptyMap());
|
||||
InitialAuthConfig authConfigs = new InitialAuthConfig(configurationProperties);
|
||||
|
||||
docker = new DockerConnector(authConfigs);
|
||||
|
||||
machineRegistry = new MachineRegistry();
|
||||
|
||||
assertTrue(pull("registry", "latest", null));
|
||||
|
||||
dockerMachineFactory = new TestDockerMachineFactory(docker);
|
||||
|
||||
final ContainerConfig containerConfig = new ContainerConfig()
|
||||
.withImage("registry")
|
||||
.withExposedPorts(singletonMap("5000/tcp", Collections.<String, String>emptyMap()))
|
||||
.withHostConfig(new HostConfig().withPortBindings(
|
||||
singletonMap("5000/tcp", new PortBinding[]{new PortBinding().withHostPort("5000")})));
|
||||
|
||||
registryContainerId = docker.createContainer(containerConfig, null).getUserId();
|
||||
|
||||
docker.startContainer(registryContainerId, null);
|
||||
}
|
||||
|
||||
@AfterClass
|
||||
public void tearDownClass() throws IOException {
|
||||
docker.killContainer(registryContainerId);
|
||||
docker.removeContainer(registryContainerId, true, false);
|
||||
}
|
||||
|
||||
@BeforeMethod
|
||||
public void setUp() throws Exception {
|
||||
snapshotDao = mock(SnapshotDao.class);
|
||||
|
||||
DockerNode dockerNode = mock(DockerNode.class);
|
||||
|
||||
EventService eventService = mock(EventService.class);
|
||||
RuntimeWorkspaceRegistry runtimeWorkspaceRegistry = mock(RuntimeWorkspaceRegistry.class);
|
||||
EnvironmentContext envCont = new EnvironmentContext();
|
||||
envCont.setUser(new UserImpl("user", null, null, null, false));
|
||||
EnvironmentContext.setCurrent(envCont);
|
||||
RuntimeWorkspaceImpl runtimeWorkspaceImpl = mock(RuntimeWorkspaceImpl.class);
|
||||
when(runtimeWorkspaceRegistry.get(any())).thenReturn(runtimeWorkspaceImpl);
|
||||
when(runtimeWorkspaceImpl.getName()).thenReturn("workspace");
|
||||
|
||||
|
||||
InstanceProvider dockerInstanceProvider = new DockerInstanceProvider(docker,
|
||||
dockerMachineFactory,
|
||||
dockerInstanceStopDetector,
|
||||
Collections.emptySet(),
|
||||
Collections.emptySet(),
|
||||
Collections.emptySet(),
|
||||
Collections.emptySet(),
|
||||
null,
|
||||
"fake",
|
||||
workspaceFolderNodePathProvider);
|
||||
|
||||
machineManager = new MachineManager(snapshotDao,
|
||||
machineRegistry,
|
||||
new MachineInstanceProviders(Collections.singleton(dockerInstanceProvider)),
|
||||
"/tmp",
|
||||
eventService,
|
||||
100);
|
||||
|
||||
machineService = spy(new MachineService(machineManager));
|
||||
|
||||
EnvironmentContext.getCurrent().setUser(new User() {
|
||||
@Override
|
||||
public String getName() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isMemberOf(String s) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getToken() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getUserId() {
|
||||
return USER;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isTemporary() {
|
||||
return false;
|
||||
}
|
||||
});
|
||||
|
||||
when(dockerNode.getProjectsFolder()).thenReturn(System.getProperty("user.dir"));
|
||||
}
|
||||
|
||||
@AfterMethod
|
||||
public void tearDown() throws Exception {
|
||||
for (MachineStateImpl machine : new ArrayList<>(machineManager.getMachinesStates())) {
|
||||
machineManager.destroy(machine.getUserId(), false);
|
||||
}
|
||||
EnvironmentContext.reset();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void createFromRecipeTest() throws Exception {
|
||||
final MachineStateDescriptor machine = machineService.createMachineFromRecipe(
|
||||
newDto(RecipeMachineCreationMetadata.class)
|
||||
.withType("docker")
|
||||
.withDisplayName("MachineDisplayName")
|
||||
.withWorkspaceId("wsId")
|
||||
.withRecipe(newDto(MachineRecipe.class)
|
||||
.withType("Dockerfile")
|
||||
.withScript("FROM ubuntu\nCMD tail -f /dev/null\n")));
|
||||
|
||||
waitMachineIsRunning(machine.getUserId());
|
||||
}
|
||||
|
||||
@Test(dependsOnMethods = "saveSnapshotTest", enabled = false)
|
||||
public void createMachineFromSnapshotTest() throws Exception {
|
||||
// remove local copy of image to check pulling
|
||||
docker.removeImage(pushedImage.getImageId(), true);
|
||||
|
||||
SnapshotImpl snapshot = mock(SnapshotImpl.class);
|
||||
when(snapshotDao.getSnapshot(SNAPSHOT_ID)).thenReturn(snapshot);
|
||||
when(snapshot.getType()).thenReturn("docker");
|
||||
when(snapshot.getWorkspaceId()).thenReturn("wsId");
|
||||
when(snapshot.getInstanceKey()).thenReturn(pushedImage);
|
||||
when(snapshot.getNamespace()).thenReturn(USER);
|
||||
|
||||
final MachineStateDescriptor machine = machineService
|
||||
.createMachineFromSnapshot(newDto(SnapshotMachineCreationMetadata.class).withSnapshotId(SNAPSHOT_ID));
|
||||
|
||||
waitMachineIsRunning(machine.getUserId());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getMachineTest() throws Exception {
|
||||
final MachineImpl machine = createMachineAndWaitRunningState();
|
||||
|
||||
final MachineDescriptor machineById = machineService.getMachineById(machine.getUserId());
|
||||
|
||||
assertEquals(machineById.getUserId(), machine.getUserId());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getMachinesTest() throws Exception {
|
||||
Set<String> expected = new HashSet<>();
|
||||
expected.add(createMachineAndWaitRunningState().getUserId());
|
||||
expected.add(createMachineAndWaitRunningState().getUserId());
|
||||
|
||||
Set<String> actual = machineManager.getMachinesStates()
|
||||
.stream()
|
||||
.map(MachineImpl::getUserId)
|
||||
.collect(Collectors.toSet());
|
||||
assertEquals(actual, expected);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void destroyMachineTest() throws Exception {
|
||||
final MachineImpl machine = createMachineAndWaitRunningState();
|
||||
|
||||
machineService.destroyMachine(machine.getUserId());
|
||||
|
||||
assertEquals(machineService.getMachineStateById(machine.getUserId()).getStatus(), MachineStatus.DESTROYING);
|
||||
|
||||
int counter = 0;
|
||||
while (++counter < 1000) {
|
||||
try {
|
||||
machineManager.getMachine(machine.getUserId());
|
||||
} catch (NotFoundException e) {
|
||||
return;
|
||||
}
|
||||
Thread.sleep(500);
|
||||
}
|
||||
fail();
|
||||
}
|
||||
|
||||
@Test(enabled = false)// TODO Add ability to check when snapshot creation is finishes or fails
|
||||
public void saveSnapshotTest() throws Exception {
|
||||
final MachineImpl machine = createMachineAndWaitRunningState();
|
||||
|
||||
// use machine manager instead of machine service because it returns future with snapshot
|
||||
// that allows check operation result
|
||||
final SnapshotImpl snapshot = machineManager.save(machine.getUserId(), USER, "test description");
|
||||
|
||||
for (int i = 0; snapshot.getInstanceKey() == null && i < 10; ++i) {
|
||||
Thread.sleep(500);
|
||||
}
|
||||
assertNotNull(snapshot.getInstanceKey());
|
||||
|
||||
final DockerInstanceKey instanceKey = (DockerInstanceKey)snapshot.getInstanceKey();
|
||||
|
||||
final boolean pullIsSuccessful = pull(instanceKey.getRepository(), instanceKey.getTag(), instanceKey.getRegistry());
|
||||
|
||||
assertTrue(pullIsSuccessful);
|
||||
|
||||
pushedImage = instanceKey;
|
||||
}
|
||||
|
||||
// depends on saveSnapshotTest to be able to remove image from registry
|
||||
// actually doesn't depend on createMachineFromSnapshotTest,
|
||||
// but this test will fail createMachineFromSnapshotTest if called before
|
||||
@Test(dependsOnMethods = {"saveSnapshotTest", "createMachineFromSnapshotTest"}, enabled = false)// TODO
|
||||
public void removeSnapshotTest() throws Exception {
|
||||
SnapshotImpl snapshot = mock(SnapshotImpl.class);
|
||||
when(snapshotDao.getSnapshot(SNAPSHOT_ID)).thenReturn(snapshot);
|
||||
when(snapshot.getType()).thenReturn("docker");
|
||||
when(snapshot.getNamespace()).thenReturn(USER);
|
||||
when(snapshot.getInstanceKey()).thenReturn(pushedImage);
|
||||
|
||||
machineService.removeSnapshot(SNAPSHOT_ID);
|
||||
|
||||
verify(snapshotDao).removeSnapshot(SNAPSHOT_ID);
|
||||
|
||||
try {
|
||||
final boolean isPullSuccessful = pull(pushedImage.getRepository(), pushedImage.getTag(), pushedImage.getRegistry());
|
||||
assertFalse(isPullSuccessful);
|
||||
} catch (Exception e) {
|
||||
fail(e.getLocalizedMessage(), e);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void executeTest() throws Exception {
|
||||
final MachineImpl machine = createMachineAndWaitRunningState();
|
||||
|
||||
String commandInMachine = "echo \"command in machine\" && tail -f /dev/null";
|
||||
machineService.executeCommandInMachine(machine.getUserId(),
|
||||
DtoFactory.newDto(CommandDto.class).withCommandLine(commandInMachine),
|
||||
null);
|
||||
|
||||
Thread.sleep(500);
|
||||
|
||||
final List<MachineProcessDto> processes = machineService.getProcesses(machine.getUserId());
|
||||
assertEquals(processes.size(), 1);
|
||||
assertEquals(processes.get(0).getCommandLine(), commandInMachine);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getProcessesTest() throws Exception {
|
||||
final MachineImpl machine = createMachineAndWaitRunningState();
|
||||
|
||||
Set<String> commands = new HashSet<>(2);
|
||||
commands.add("tail -f /dev/null");
|
||||
commands.add("sleep 10000");
|
||||
|
||||
for (String command : commands) {
|
||||
machineService.executeCommandInMachine(machine.getUserId(), DtoFactory.newDto(CommandDto.class).withCommandLine(command), null);
|
||||
}
|
||||
|
||||
Thread.sleep(500);
|
||||
|
||||
final List<MachineProcessDto> processes = machineService.getProcesses(machine.getUserId());
|
||||
assertEquals(processes.size(), 2);
|
||||
Set<String> actualCommandLines = new HashSet<>(2);
|
||||
for (MachineProcessDto process : processes) {
|
||||
assertTrue(process.getPid() > 0);
|
||||
actualCommandLines.add(process.getCommandLine());
|
||||
}
|
||||
assertEquals(actualCommandLines, commands);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void stopProcessTest() throws Exception {
|
||||
final MachineImpl machine = createMachineAndWaitRunningState();
|
||||
|
||||
String commandInMachine = "echo \"command in machine\" && tail -f /dev/null";
|
||||
machineService.executeCommandInMachine(machine.getUserId(),
|
||||
DtoFactory.newDto(CommandDto.class).withCommandLine(commandInMachine),
|
||||
null);
|
||||
|
||||
Thread.sleep(500);
|
||||
|
||||
final List<MachineProcessDto> processes = machineService.getProcesses(machine.getUserId());
|
||||
assertEquals(processes.size(), 1);
|
||||
assertEquals(processes.get(0).getCommandLine(), commandInMachine);
|
||||
|
||||
machineService.stopProcess(machine.getUserId(), processes.get(0).getPid());
|
||||
|
||||
assertTrue(machineService.getProcesses(machine.getUserId()).isEmpty());
|
||||
}
|
||||
|
||||
@Test(expectedExceptions = NotFoundException.class, expectedExceptionsMessageRegExp = "Process with pid .* not found")
|
||||
public void shouldThrowNotFoundExceptionOnProcessKillIfProcessPidMissing() throws Exception {
|
||||
final MachineImpl machine = createMachineAndWaitRunningState();
|
||||
|
||||
String commandInMachine = "echo \"command in machine\" && tail -f /dev/null";
|
||||
machineService.executeCommandInMachine(machine.getUserId(),
|
||||
DtoFactory.newDto(CommandDto.class).withCommandLine(commandInMachine),
|
||||
null);
|
||||
|
||||
Thread.sleep(500);
|
||||
|
||||
final List<MachineProcessDto> processes = machineService.getProcesses(machine.getUserId());
|
||||
assertEquals(processes.size(), 1);
|
||||
assertEquals(processes.get(0).getCommandLine(), commandInMachine);
|
||||
|
||||
machineService.stopProcess(machine.getUserId(), processes.get(0).getPid() + 100);
|
||||
}
|
||||
|
||||
private MachineImpl createMachineAndWaitRunningState() throws Exception {
|
||||
final MachineImpl machine = machineManager.create(newDto(RecipeMachineCreationMetadata.class)
|
||||
.withWorkspaceId("wsId")
|
||||
.withType("docker")
|
||||
.withDisplayName("MachineDisplayName")
|
||||
.withRecipe(newDto(MachineRecipe.class)
|
||||
.withType("Dockerfile")
|
||||
.withScript(
|
||||
"FROM ubuntu\nCMD tail -f " +
|
||||
"/dev/null\n"))
|
||||
.withDev(false)
|
||||
.withDisplayName("displayName" + System.currentTimeMillis())
|
||||
, false);
|
||||
waitMachineIsRunning(machine.getUserId());
|
||||
return machine;
|
||||
}
|
||||
|
||||
private void waitMachineIsRunning(String machineId) throws NotFoundException, InterruptedException, MachineException {
|
||||
while (MachineStatus.RUNNING != machineManager.getMachineState(machineId).getStatus()) {
|
||||
Thread.sleep(500);
|
||||
}
|
||||
}
|
||||
|
||||
private boolean pull(String image, String tag, String registry) throws Exception {
|
||||
final ValueHolder<Boolean> isSuccessfulValueHolder = new ValueHolder<>(true);
|
||||
final ProgressLineFormatterImpl progressLineFormatter = new ProgressLineFormatterImpl();
|
||||
docker.pull(image, tag, registry, currentProgressStatus -> {
|
||||
try {
|
||||
if (currentProgressStatus.getError() != null) {
|
||||
isSuccessfulValueHolder.set(false);
|
||||
}
|
||||
lineConsumer.writeLine(progressLineFormatter.format(currentProgressStatus));
|
||||
} catch (IOException ignored) {
|
||||
}
|
||||
});
|
||||
|
||||
return isSuccessfulValueHolder.get();
|
||||
}
|
||||
|
||||
private static class StdErrLineConsumer implements LineConsumer {
|
||||
@Override
|
||||
public void writeLine(String line) throws IOException {
|
||||
System.err.println(line);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() throws IOException {
|
||||
}
|
||||
}*/
|
||||
}
|
||||
|
|
@ -13,12 +13,15 @@ package org.eclipse.che.plugin.docker.machine.local.interceptor;
|
|||
import com.google.inject.AbstractModule;
|
||||
import com.google.inject.Guice;
|
||||
import com.google.inject.Injector;
|
||||
import com.google.inject.multibindings.Multibinder;
|
||||
import com.google.inject.name.Names;
|
||||
import com.google.inject.spi.ConstructorBinding;
|
||||
|
||||
import org.aopalliance.intercept.MethodInvocation;
|
||||
import org.eclipse.che.api.core.model.machine.MachineConfig;
|
||||
import org.eclipse.che.api.core.model.machine.Recipe;
|
||||
import org.eclipse.che.api.machine.server.dao.SnapshotDao;
|
||||
import org.eclipse.che.api.machine.server.spi.InstanceProvider;
|
||||
import org.eclipse.che.api.machine.server.util.RecipeDownloader;
|
||||
import org.eclipse.che.api.machine.server.util.RecipeRetriever;
|
||||
import org.eclipse.che.api.workspace.server.WorkspaceManager;
|
||||
|
|
@ -187,6 +190,12 @@ public class EnableOfflineDockerMachineBuildInterceptorTest {
|
|||
bind(WorkspaceManager.class).toInstance(workspaceManager);
|
||||
bind(RecipeRetriever.class).toInstance(recipeRetriever);
|
||||
bind(RecipeDownloader.class).toInstance(mock(RecipeDownloader.class));
|
||||
bind(SnapshotDao.class).toInstance(mock(SnapshotDao.class));
|
||||
Multibinder<InstanceProvider> machineImageProviderMultibinder =
|
||||
Multibinder.newSetBinder(binder(),
|
||||
org.eclipse.che.api.machine.server.spi.InstanceProvider.class);
|
||||
machineImageProviderMultibinder.addBinding()
|
||||
.to(org.eclipse.che.plugin.docker.machine.DockerInstanceProvider.class);
|
||||
|
||||
bindConstant().annotatedWith(Names.named("machine.docker.privilege_mode")).to(false);
|
||||
bindConstant().annotatedWith(Names.named("machine.docker.pull_image")).to(true);
|
||||
|
|
@ -195,6 +204,8 @@ public class EnableOfflineDockerMachineBuildInterceptorTest {
|
|||
bindConstant().annotatedWith(Names.named("che.machine.projects.internal.storage")).to("/tmp");
|
||||
bindConstant().annotatedWith(Names.named("machine.docker.machine_extra_hosts")).to("");
|
||||
bindConstant().annotatedWith(Names.named("che.workspace.storage")).to("/tmp");
|
||||
bindConstant().annotatedWith(Names.named("machine.default_mem_size_mb")).to("1024");
|
||||
bindConstant().annotatedWith(Names.named("machine.logs.location")).to("/tmp");
|
||||
install(new DockerMachineModule());
|
||||
|
||||
install(new AllowOfflineMachineCreationModule());
|
||||
|
|
|
|||
|
|
@ -84,7 +84,8 @@ public class JavaDebugConfigurationPagePresenter implements JavaDebugConfigurati
|
|||
}
|
||||
|
||||
private void setPortsList() {
|
||||
machineServiceClient.getMachine(appContext.getDevMachine().getId()).then(new Operation<MachineDto>() {
|
||||
machineServiceClient.getMachine(appContext.getWorkspaceId(),
|
||||
appContext.getDevMachine().getId()).then(new Operation<MachineDto>() {
|
||||
@Override
|
||||
public void apply(MachineDto machineDto) throws OperationException {
|
||||
Machine machine = entityFactory.createMachine(machineDto);
|
||||
|
|
|
|||
|
|
@ -78,7 +78,7 @@ public class JavaDebugConfigurationPagePresenterTest {
|
|||
|
||||
@Test
|
||||
public void testGo() throws Exception {
|
||||
when(machineServiceClient.getMachine(anyString())).thenReturn(mock(Promise.class));
|
||||
when(machineServiceClient.getMachine(anyString(), anyString())).thenReturn(mock(Promise.class));
|
||||
|
||||
AcceptsOneWidget container = Mockito.mock(AcceptsOneWidget.class);
|
||||
|
||||
|
|
|
|||
|
|
@ -35,7 +35,7 @@ public class RecipeScriptDownloadServiceClientImpl implements RecipeScriptDownlo
|
|||
@Override
|
||||
public Promise<String> getRecipeScript(Machine machine) {
|
||||
return asyncRequestFactory
|
||||
.createGetRequest(restContext + "/recipe/script/" + machine.getId())
|
||||
.createGetRequest(restContext + "/recipe/script/" + machine.getWorkspaceId() + "/" + machine.getId())
|
||||
.send(new StringUnmarshaller());
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -119,7 +119,11 @@ public class CommandManager {
|
|||
.withCommandLine(arg)
|
||||
.withType(configuration.getType().getId());
|
||||
|
||||
final Promise<MachineProcessDto> processPromise = machineServiceClient.executeCommand(machine.getId(), command, outputChannel);
|
||||
final Promise<MachineProcessDto> processPromise =
|
||||
machineServiceClient.executeCommand(machine.getWorkspaceId(),
|
||||
machine.getId(),
|
||||
command,
|
||||
outputChannel);
|
||||
processPromise.then(new Operation<MachineProcessDto>() {
|
||||
@Override
|
||||
public void apply(MachineProcessDto process) throws OperationException {
|
||||
|
|
|
|||
|
|
@ -15,9 +15,6 @@ import com.google.inject.Inject;
|
|||
import com.google.inject.Singleton;
|
||||
import com.google.web.bindery.event.shared.EventBus;
|
||||
|
||||
import org.eclipse.che.ide.api.machine.MachineServiceClient;
|
||||
import org.eclipse.che.ide.api.machine.events.WsAgentStateEvent;
|
||||
import org.eclipse.che.ide.api.machine.events.WsAgentStateHandler;
|
||||
import org.eclipse.che.api.machine.shared.dto.MachineDto;
|
||||
import org.eclipse.che.api.machine.shared.dto.ServerDto;
|
||||
import org.eclipse.che.api.promises.client.Operation;
|
||||
|
|
@ -25,6 +22,9 @@ import org.eclipse.che.api.promises.client.OperationException;
|
|||
import org.eclipse.che.api.promises.client.Promise;
|
||||
import org.eclipse.che.api.promises.client.js.Promises;
|
||||
import org.eclipse.che.ide.api.app.AppContext;
|
||||
import org.eclipse.che.ide.api.machine.MachineServiceClient;
|
||||
import org.eclipse.che.ide.api.machine.events.WsAgentStateEvent;
|
||||
import org.eclipse.che.ide.api.machine.events.WsAgentStateHandler;
|
||||
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
|
@ -70,7 +70,7 @@ public class ServerPortProvider implements WsAgentStateHandler {
|
|||
private void registerProviders() {
|
||||
String devMachineId = appContext.getDevMachine().getId();
|
||||
if (devMachineId != null) {
|
||||
machineServiceClient.getMachine(devMachineId).then(registerProviders);
|
||||
machineServiceClient.getMachine(appContext.getWorkspaceId(), devMachineId).then(registerProviders);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -146,7 +146,8 @@ public class MachineManagerImpl implements MachineManager {
|
|||
|
||||
@Override
|
||||
public Promise<Void> destroyMachine(final Machine machineState) {
|
||||
return machineServiceClient.destroyMachine(machineState.getId()).then(new Operation<Void>() {
|
||||
return machineServiceClient.destroyMachine(machineState.getWorkspaceId(),
|
||||
machineState.getId()).then(new Operation<Void>() {
|
||||
@Override
|
||||
public void apply(Void arg) throws OperationException {
|
||||
eventBus.fireEvent(new MachineStateEvent(machineState, DESTROYED));
|
||||
|
|
|
|||
|
|
@ -66,13 +66,14 @@ public class MachineStatusNotifier implements MachineStatusChangedEvent.Handler
|
|||
public void onMachineStatusChanged(final MachineStatusChangedEvent event) {
|
||||
final String machineName = event.getMachineName();
|
||||
final String machineId = event.getMachineId();
|
||||
final String workspaceId = event.getWorkspaceId();
|
||||
|
||||
switch (event.getEventType()) {
|
||||
case CREATING:
|
||||
getMachine(machineId).then(notifyMachineCreating());
|
||||
getMachine(workspaceId, machineId).then(notifyMachineCreating());
|
||||
break;
|
||||
case RUNNING:
|
||||
getMachine(machineId).then(notifyMachineRunning());
|
||||
getMachine(workspaceId, machineId).then(notifyMachineRunning());
|
||||
break;
|
||||
case DESTROYED:
|
||||
notificationManager.notify(locale.notificationMachineDestroyed(machineName), SUCCESS, EMERGE_MODE);
|
||||
|
|
@ -83,8 +84,8 @@ public class MachineStatusNotifier implements MachineStatusChangedEvent.Handler
|
|||
}
|
||||
}
|
||||
|
||||
private Promise<MachineDto> getMachine(final String machineId) {
|
||||
return machineServiceClient.getMachine(machineId).catchError(new Operation<PromiseError>() {
|
||||
private Promise<MachineDto> getMachine(final String workspaceId, final String machineId) {
|
||||
return machineServiceClient.getMachine(workspaceId, machineId).catchError(new Operation<PromiseError>() {
|
||||
@Override
|
||||
public void apply(PromiseError arg) throws OperationException {
|
||||
notificationManager.notify(locale.failedToFindMachine(machineId));
|
||||
|
|
|
|||
|
|
@ -136,7 +136,8 @@ public class CreateMachinePresenter implements CreateMachineView.ActionDelegate
|
|||
final String recipeURL = view.getRecipeURL();
|
||||
|
||||
if (appContext.getDevMachine() != null) {
|
||||
machineServiceClient.getMachine(appContext.getDevMachine().getId()).then(new Operation<MachineDto>() {
|
||||
machineServiceClient.getMachine(appContext.getWorkspaceId(),
|
||||
appContext.getDevMachine().getId()).then(new Operation<MachineDto>() {
|
||||
@Override
|
||||
public void apply(MachineDto machine) throws OperationException {
|
||||
machineManager.destroyMachine(machine);
|
||||
|
|
|
|||
|
|
@ -20,8 +20,8 @@ import org.eclipse.che.api.machine.shared.dto.MachineProcessDto;
|
|||
import org.eclipse.che.api.machine.shared.dto.event.MachineProcessEvent;
|
||||
import org.eclipse.che.api.promises.client.Operation;
|
||||
import org.eclipse.che.api.promises.client.OperationException;
|
||||
import org.eclipse.che.ide.api.machine.MachineServiceClient;
|
||||
import org.eclipse.che.ide.api.machine.CommandOutputMessageUnmarshaller;
|
||||
import org.eclipse.che.ide.api.machine.MachineServiceClient;
|
||||
import org.eclipse.che.ide.extension.machine.client.MachineResources;
|
||||
import org.eclipse.che.ide.extension.machine.client.command.CommandConfiguration;
|
||||
import org.eclipse.che.ide.extension.machine.client.command.CommandManager;
|
||||
|
|
@ -231,7 +231,9 @@ public class CommandOutputConsolePresenter implements CommandOutputConsole, Outp
|
|||
|
||||
@Override
|
||||
public void stop() {
|
||||
machineServiceClient.stopProcess(machine.getId(), pid);
|
||||
machineServiceClient.stopProcess(machine.getWorkspaceId(),
|
||||
machine.getId(),
|
||||
pid);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
@ -249,7 +251,9 @@ public class CommandOutputConsolePresenter implements CommandOutputConsole, Outp
|
|||
if (isFinished()) {
|
||||
commandManager.executeCommand(commandConfiguration, machine);
|
||||
} else {
|
||||
machineServiceClient.stopProcess(machine.getId(), pid).then(new Operation<Void>() {
|
||||
machineServiceClient.stopProcess(machine.getWorkspaceId(),
|
||||
machine.getId(),
|
||||
pid).then(new Operation<Void>() {
|
||||
@Override
|
||||
public void apply(Void arg) throws OperationException {
|
||||
commandManager.executeCommand(commandConfiguration, machine);
|
||||
|
|
|
|||
|
|
@ -50,8 +50,8 @@ public class ProcessesPresenter implements TabPresenter, ProcessesView.ActionDel
|
|||
* @param machineId
|
||||
* machine identifier for which need get processes
|
||||
*/
|
||||
public void showProcesses(@NotNull String machineId) {
|
||||
Promise<List<MachineProcessDto>> processesPromise = service.getProcesses(machineId);
|
||||
public void showProcesses(@NotNull String workspaceId, @NotNull String machineId) {
|
||||
Promise<List<MachineProcessDto>> processesPromise = service.getProcesses(workspaceId, machineId);
|
||||
|
||||
processesPromise.then(new Operation<List<MachineProcessDto>>() {
|
||||
@Override
|
||||
|
|
|
|||
|
|
@ -17,19 +17,19 @@ import com.google.inject.Singleton;
|
|||
import com.google.web.bindery.event.shared.EventBus;
|
||||
|
||||
import org.eclipse.che.api.core.model.machine.MachineStatus;
|
||||
import org.eclipse.che.ide.api.machine.MachineServiceClient;
|
||||
import org.eclipse.che.api.machine.shared.dto.MachineDto;
|
||||
import org.eclipse.che.api.promises.client.Operation;
|
||||
import org.eclipse.che.api.promises.client.OperationException;
|
||||
import org.eclipse.che.api.promises.client.Promise;
|
||||
import org.eclipse.che.api.promises.client.PromiseError;
|
||||
import org.eclipse.che.ide.api.workspace.event.WorkspaceStartedEvent;
|
||||
import org.eclipse.che.ide.api.workspace.event.WorkspaceStoppedEvent;
|
||||
import org.eclipse.che.commons.annotation.Nullable;
|
||||
import org.eclipse.che.ide.api.app.AppContext;
|
||||
import org.eclipse.che.ide.api.event.ActivePartChangedEvent;
|
||||
import org.eclipse.che.ide.api.event.ActivePartChangedHandler;
|
||||
import org.eclipse.che.ide.api.machine.MachineServiceClient;
|
||||
import org.eclipse.che.ide.api.parts.base.BasePresenter;
|
||||
import org.eclipse.che.ide.api.workspace.event.WorkspaceStartedEvent;
|
||||
import org.eclipse.che.ide.api.workspace.event.WorkspaceStoppedEvent;
|
||||
import org.eclipse.che.ide.extension.machine.client.MachineLocalizationConstant;
|
||||
import org.eclipse.che.ide.extension.machine.client.MachineResources;
|
||||
import org.eclipse.che.ide.extension.machine.client.inject.factories.EntityFactory;
|
||||
|
|
@ -163,7 +163,8 @@ public class MachinePanelPresenter extends BasePresenter implements MachinePanel
|
|||
return;
|
||||
}
|
||||
|
||||
service.getMachine(selectedMachine.getId()).then(new Operation<MachineDto>() {
|
||||
service.getMachine(selectedMachine.getWorkspaceId(),
|
||||
selectedMachine.getId()).then(new Operation<MachineDto>() {
|
||||
@Override
|
||||
public void apply(MachineDto machineDto) throws OperationException {
|
||||
if (machineDto.getStatus() == MachineStatus.RUNNING) {
|
||||
|
|
|
|||
|
|
@ -25,5 +25,5 @@ public interface AddTerminalClickHandler {
|
|||
* @param machineId
|
||||
* id of machine in which the terminal will be added
|
||||
*/
|
||||
void onAddTerminalClick(@NotNull String machineId);
|
||||
void onAddTerminalClick(@NotNull String workspaceId, @NotNull String machineId);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -322,7 +322,8 @@ public class ConsolesPanelPresenter implements ConsolesPanelView.ActionDelegate,
|
|||
}
|
||||
|
||||
private void restoreState(final org.eclipse.che.api.core.model.machine.Machine machine) {
|
||||
machineService.getProcesses(machine.getId()).then(new Operation<List<MachineProcessDto>>() {
|
||||
machineService.getProcesses(machine.getWorkspaceId(),
|
||||
machine.getId()).then(new Operation<List<MachineProcessDto>>() {
|
||||
@Override
|
||||
public void apply(List<MachineProcessDto> arg) throws OperationException {
|
||||
for (MachineProcessDto machineProcessDto : arg) {
|
||||
|
|
@ -418,17 +419,17 @@ public class ConsolesPanelPresenter implements ConsolesPanelView.ActionDelegate,
|
|||
|
||||
if (selectedTreeNode == null) {
|
||||
if (appContext.getDevMachine() != null) {
|
||||
onAddTerminal(appContext.getDevMachine().getId());
|
||||
onAddTerminal(appContext.getWorkspaceId(), appContext.getDevMachine().getId());
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (selectedTreeNode.getType() == MACHINE_NODE) {
|
||||
onAddTerminal(selectedTreeNode.getId());
|
||||
onAddTerminal(appContext.getWorkspaceId(), selectedTreeNode.getId());
|
||||
} else {
|
||||
if (selectedTreeNode.getParent() != null &&
|
||||
selectedTreeNode.getParent().getType() == MACHINE_NODE) {
|
||||
onAddTerminal(appContext.getDevMachine().getId());
|
||||
onAddTerminal(appContext.getWorkspaceId(), appContext.getDevMachine().getId());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -440,8 +441,8 @@ public class ConsolesPanelPresenter implements ConsolesPanelView.ActionDelegate,
|
|||
* id of machine in which the terminal will be added
|
||||
*/
|
||||
@Override
|
||||
public void onAddTerminal(@NotNull final String machineId) {
|
||||
machineService.getMachine(machineId).then(new Operation<MachineDto>() {
|
||||
public void onAddTerminal(@NotNull final String workspaceId, @NotNull final String machineId) {
|
||||
machineService.getMachine(workspaceId, machineId).then(new Operation<MachineDto>() {
|
||||
@Override
|
||||
public void apply(MachineDto arg) throws OperationException {
|
||||
org.eclipse.che.ide.extension.machine.client.machine.Machine machine = entityFactory.createMachine(arg);
|
||||
|
|
|
|||
|
|
@ -89,7 +89,7 @@ public interface ConsolesPanelView extends View<ConsolesPanelView.ActionDelegate
|
|||
* @param machineId
|
||||
* id of machine in which the terminal will be added
|
||||
*/
|
||||
void onAddTerminal(@NotNull String machineId);
|
||||
void onAddTerminal(@NotNull String workspaceId, @NotNull String machineId);
|
||||
|
||||
/**
|
||||
* Will be called when user clicks 'Preview Ssh' button
|
||||
|
|
|
|||
|
|
@ -91,8 +91,8 @@ public class ConsolesPanelViewImpl extends Composite implements ConsolesPanelVie
|
|||
|
||||
renderer.setAddTerminalClickHandler(new AddTerminalClickHandler() {
|
||||
@Override
|
||||
public void onAddTerminalClick(@NotNull String machineId) {
|
||||
delegate.onAddTerminal(machineId);
|
||||
public void onAddTerminalClick(@NotNull String workspaceId, @NotNull String machineId) {
|
||||
delegate.onAddTerminal(workspaceId, machineId);
|
||||
}
|
||||
});
|
||||
|
||||
|
|
|
|||
|
|
@ -171,7 +171,7 @@ public class ProcessTreeRenderer implements NodeRenderer<ProcessTreeNode> {
|
|||
event.preventDefault();
|
||||
|
||||
if (addTerminalClickHandler != null) {
|
||||
addTerminalClickHandler.onAddTerminalClick(machine.getId());
|
||||
addTerminalClickHandler.onAddTerminalClick(machine.getWorkspaceId(), machine.getId());
|
||||
}
|
||||
}
|
||||
}, true);
|
||||
|
|
|
|||
|
|
@ -143,7 +143,8 @@ public class DockerCategoryPresenter implements CategoryPage, TargetManager, Doc
|
|||
return;
|
||||
}
|
||||
|
||||
machineService.destroyMachine(machine.getId()).then(new Operation<Void>() {
|
||||
machineService.destroyMachine(machine.getWorkspaceId(),
|
||||
machine.getId()).then(new Operation<Void>() {
|
||||
@Override
|
||||
public void apply(Void arg) throws OperationException {
|
||||
eventBus.fireEvent(new MachineStateEvent(machine, MachineStateEvent.MachineAction.DESTROYED));
|
||||
|
|
|
|||
|
|
@ -462,7 +462,8 @@ public class SshCategoryPresenter implements CategoryPage, TargetManager, SshVie
|
|||
}
|
||||
sshView.setConnectButtonText(null);
|
||||
|
||||
machineService.destroyMachine(machine.getId()).then(new Operation<Void>() {
|
||||
machineService.destroyMachine(machine.getWorkspaceId(),
|
||||
machine.getId()).then(new Operation<Void>() {
|
||||
@Override
|
||||
public void apply(Void arg) throws OperationException {
|
||||
eventBus.fireEvent(new MachineStateEvent(machine, MachineStateEvent.MachineAction.DESTROYED));
|
||||
|
|
@ -509,7 +510,8 @@ public class SshCategoryPresenter implements CategoryPage, TargetManager, SshVie
|
|||
return;
|
||||
}
|
||||
|
||||
machineService.destroyMachine(machine.getId()).then(new Operation<Void>() {
|
||||
machineService.destroyMachine(machine.getWorkspaceId(),
|
||||
machine.getId()).then(new Operation<Void>() {
|
||||
@Override
|
||||
public void apply(Void arg) throws OperationException {
|
||||
eventBus.fireEvent(new MachineStateEvent(machine, MachineStateEvent.MachineAction.DESTROYED));
|
||||
|
|
@ -557,14 +559,14 @@ public class SshCategoryPresenter implements CategoryPage, TargetManager, SshVie
|
|||
/**
|
||||
* Ensures machine is started.
|
||||
*/
|
||||
private void onConnected(final String machineId) {
|
||||
private void onConnected(final String workspaceId, final String machineId) {
|
||||
// There is a little bug in machine service on the server side.
|
||||
// The machine info is updated with a little delay after running a machine.
|
||||
// Using timer must fix the problem.
|
||||
new Timer() {
|
||||
@Override
|
||||
public void run() {
|
||||
machineService.getMachine(machineId).then(new Operation<MachineDto>() {
|
||||
machineService.getMachine(workspaceId, machineId).then(new Operation<MachineDto>() {
|
||||
@Override
|
||||
public void apply(MachineDto machineDto) throws OperationException {
|
||||
if (machineDto.getStatus() == RUNNING) {
|
||||
|
|
@ -644,7 +646,7 @@ public class SshCategoryPresenter implements CategoryPage, TargetManager, SshVie
|
|||
if (MachineStatusEvent.EventType.RUNNING == event.getEventType()
|
||||
&& connectNotification != null && connectTargetName != null
|
||||
&& connectTargetName.equals(event.getMachineName())) {
|
||||
onConnected(event.getMachineId());
|
||||
onConnected(event.getWorkspaceId(), event.getMachineId());
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -108,6 +108,7 @@ public class MachineManagerImplTest {
|
|||
@Test
|
||||
public void checkUseValidSource() throws OperationException {
|
||||
final String ID = "id";
|
||||
final String WORKSPACE_ID = "testWorkspaceId";
|
||||
final String DISPLAY_NAME = "my-display-name";
|
||||
final boolean IS_DEV = true;
|
||||
|
||||
|
|
@ -117,9 +118,10 @@ public class MachineManagerImplTest {
|
|||
|
||||
org.eclipse.che.api.core.model.machine.Machine machineState = mock(org.eclipse.che.api.core.model.machine.Machine.class);
|
||||
when(machineState.getId()).thenReturn(ID);
|
||||
when(machineState.getWorkspaceId()).thenReturn(WORKSPACE_ID);
|
||||
Promise<Void> promise = mock(Promise.class);
|
||||
Promise<Void> promiseThen = mock(Promise.class);
|
||||
when(machineServiceClient.destroyMachine(eq(ID))).thenReturn(promise);
|
||||
when(machineServiceClient.destroyMachine(eq(WORKSPACE_ID), eq(ID))).thenReturn(promise);
|
||||
when(promise.then(Matchers.<Operation<Void>>anyObject())).thenReturn(promiseThen);
|
||||
|
||||
MachineSource machineSource = mock(MachineSource.class);
|
||||
|
|
|
|||
|
|
@ -54,6 +54,7 @@ import static org.mockito.Mockito.when;
|
|||
public class MachineStateNotifierTest {
|
||||
private static final String MACHINE_NAME = "machineName";
|
||||
private static final String MACHINE_ID = "machineId";
|
||||
private static final String WORKSPACE_ID = "workspaceId";
|
||||
|
||||
//constructor mocks
|
||||
@Mock
|
||||
|
|
@ -90,8 +91,9 @@ public class MachineStateNotifierTest {
|
|||
when(machine.getConfig()).thenReturn(machineConfig);
|
||||
when(machineConfig.getName()).thenReturn(MACHINE_NAME);
|
||||
when(machineStatusChangedEvent.getMachineId()).thenReturn(MACHINE_ID);
|
||||
when(machineStatusChangedEvent.getWorkspaceId()).thenReturn(WORKSPACE_ID);
|
||||
when(machineStatusChangedEvent.getMachineName()).thenReturn(MACHINE_NAME);
|
||||
when(machineServiceClient.getMachine(MACHINE_ID)).thenReturn(machinePromise);
|
||||
when(machineServiceClient.getMachine(WORKSPACE_ID, MACHINE_ID)).thenReturn(machinePromise);
|
||||
when(machinePromise.then(Matchers.<Operation<MachineDto>>anyObject())).thenReturn(machinePromise);
|
||||
when(machinePromise.catchError(Matchers.<Operation<PromiseError>>anyObject())).thenReturn(machinePromise);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -10,14 +10,14 @@
|
|||
*******************************************************************************/
|
||||
package org.eclipse.che.ide.extension.machine.client.machine.create;
|
||||
|
||||
import org.eclipse.che.ide.api.machine.DevMachine;
|
||||
import org.eclipse.che.ide.api.machine.MachineManager;
|
||||
import org.eclipse.che.ide.api.machine.MachineServiceClient;
|
||||
import org.eclipse.che.api.machine.shared.dto.MachineDto;
|
||||
import org.eclipse.che.ide.api.project.ProjectTypeServiceClient;
|
||||
import org.eclipse.che.api.promises.client.Operation;
|
||||
import org.eclipse.che.api.promises.client.Promise;
|
||||
import org.eclipse.che.ide.api.app.AppContext;
|
||||
import org.eclipse.che.ide.api.machine.DevMachine;
|
||||
import org.eclipse.che.ide.api.machine.MachineManager;
|
||||
import org.eclipse.che.ide.api.machine.MachineServiceClient;
|
||||
import org.eclipse.che.ide.api.project.ProjectTypeServiceClient;
|
||||
import org.eclipse.che.ide.extension.machine.client.inject.factories.EntityFactory;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
|
|
@ -42,6 +42,7 @@ public class CreateMachinePresenterTest {
|
|||
|
||||
private final static String RECIPE_URL = "http://www.host.com/recipe";
|
||||
private final static String MACHINE_NAME = "machine";
|
||||
private final static String WORKSPACE_ID = "testWorkspace123";
|
||||
|
||||
private final static String SOME_TEXT = "someText";
|
||||
|
||||
|
|
@ -122,15 +123,17 @@ public class CreateMachinePresenterTest {
|
|||
public void shouldReplaceDevMachine() throws Exception {
|
||||
DevMachine devMachine = mock(DevMachine.class);
|
||||
when(appContext.getDevMachine()).thenReturn(devMachine);
|
||||
when(appContext.getWorkspaceId()).thenReturn(WORKSPACE_ID);
|
||||
when(devMachine.getId()).thenReturn(SOME_TEXT);
|
||||
when(machineServiceClient.getMachine(SOME_TEXT)).thenReturn(machineDescriptorPromise);
|
||||
when(devMachine.getWorkspace()).thenReturn(WORKSPACE_ID);
|
||||
when(machineServiceClient.getMachine(WORKSPACE_ID, SOME_TEXT)).thenReturn(machineDescriptorPromise);
|
||||
|
||||
presenter.onReplaceDevMachineClicked();
|
||||
|
||||
verify(view).getMachineName();
|
||||
verify(view).getRecipeURL();
|
||||
verify(appContext, times(2)).getDevMachine();
|
||||
verify(machineServiceClient).getMachine(SOME_TEXT);
|
||||
verify(machineServiceClient).getMachine(WORKSPACE_ID, SOME_TEXT);
|
||||
verify(machineDescriptorPromise).then(machineCaptor.capture());
|
||||
machineCaptor.getValue().apply(mock(MachineDto.class));
|
||||
verify(machineManager).destroyMachine(any(MachineDto.class));
|
||||
|
|
@ -141,7 +144,7 @@ public class CreateMachinePresenterTest {
|
|||
@Test
|
||||
public void shouldStartNewDevMachine() throws Exception {
|
||||
when(appContext.getDevMachine()).thenReturn(null);
|
||||
when(machineServiceClient.getMachine(SOME_TEXT)).thenReturn(machineDescriptorPromise);
|
||||
when(machineServiceClient.getMachine(WORKSPACE_ID, SOME_TEXT)).thenReturn(machineDescriptorPromise);
|
||||
|
||||
presenter.onReplaceDevMachineClicked();
|
||||
|
||||
|
|
@ -150,7 +153,7 @@ public class CreateMachinePresenterTest {
|
|||
verify(appContext).getDevMachine();
|
||||
verify(machineManager).startDevMachine(eq(RECIPE_URL), eq(MACHINE_NAME));
|
||||
verify(view).close();
|
||||
verify(machineServiceClient, never()).getMachine(SOME_TEXT);
|
||||
verify(machineServiceClient, never()).getMachine(WORKSPACE_ID, SOME_TEXT);
|
||||
verify(machineManager, never()).destroyMachine(any(MachineDto.class));
|
||||
verify(machineManager).startDevMachine(eq(RECIPE_URL), eq(MACHINE_NAME));
|
||||
}
|
||||
|
|
|
|||
|
|
@ -38,6 +38,7 @@ import static org.mockito.Mockito.when;
|
|||
public class ProcessesPresenterTest {
|
||||
|
||||
private static final String MACHINE_ID = "someId";
|
||||
private static final String WORKSPACE_ID = "wsId";
|
||||
|
||||
//constructor mocks
|
||||
@Mock
|
||||
|
|
@ -66,11 +67,11 @@ public class ProcessesPresenterTest {
|
|||
|
||||
@Test
|
||||
public void processesShouldBeGot() throws Exception {
|
||||
when(service.getProcesses(MACHINE_ID)).thenReturn(processPromise);
|
||||
when(service.getProcesses(WORKSPACE_ID, MACHINE_ID)).thenReturn(processPromise);
|
||||
|
||||
presenter.showProcesses(MACHINE_ID);
|
||||
presenter.showProcesses(WORKSPACE_ID, MACHINE_ID);
|
||||
|
||||
verify(service).getProcesses(MACHINE_ID);
|
||||
verify(service).getProcesses(WORKSPACE_ID, MACHINE_ID);
|
||||
|
||||
verify(processPromise).then(operationCaptor.capture());
|
||||
operationCaptor.getValue().apply(descriptors);
|
||||
|
|
|
|||
|
|
@ -162,7 +162,7 @@ public class MachinePanelPresenterTest {
|
|||
when(service.getMachines(anyString())).thenReturn(machinesPromise);
|
||||
when(machinesPromise.then(Matchers.<Operation<List<MachineDto>>>anyObject())).thenReturn(machinesPromise);
|
||||
|
||||
when(service.getMachine(anyString())).thenReturn(machinePromise);
|
||||
when(service.getMachine(anyString(), anyString())).thenReturn(machinePromise);
|
||||
when(machinePromise.then(Matchers.<Operation<MachineDto>>anyObject())).thenReturn(machinePromise);
|
||||
|
||||
when(appContext.getWorkspace()).thenReturn(usersWorkspaceDto);
|
||||
|
|
@ -239,7 +239,7 @@ public class MachinePanelPresenterTest {
|
|||
|
||||
verify(appliance).showAppliance(machine1);
|
||||
|
||||
verify(service, never()).getMachine(anyString());
|
||||
verify(service, never()).getMachine(anyString(), anyString());
|
||||
|
||||
assertThat(selectedMachine1, is(equalTo(presenter.getSelectedMachineState())));
|
||||
assertThat(presenter.isMachineRunning(), is(true));
|
||||
|
|
|
|||
|
|
@ -155,10 +155,10 @@ public class ConsolesPanelPresenterTest {
|
|||
when(appContext.getDevMachine()).thenReturn(devMachine);
|
||||
|
||||
when(machineService.getMachines(anyString())).thenReturn(machinesPromise);
|
||||
when(machineService.getMachine(anyString())).thenReturn(machinePromise);
|
||||
when(machineService.getMachine(anyString(), anyString())).thenReturn(machinePromise);
|
||||
when(machinePromise.then(Matchers.<Operation<MachineDto>>anyObject())).thenReturn(machinePromise);
|
||||
|
||||
when(machineService.getProcesses(anyString())).thenReturn(processesPromise);
|
||||
when(machineService.getProcesses(anyString(), anyString())).thenReturn(processesPromise);
|
||||
when(processesPromise.then(Matchers.<Operation<List<MachineProcessDto>>>anyObject())).thenReturn(processesPromise);
|
||||
when(commandConsoleFactory.create(anyString())).thenReturn(mock(OutputConsole.class));
|
||||
|
||||
|
|
@ -334,7 +334,7 @@ public class ConsolesPanelPresenterTest {
|
|||
when(terminal.getView()).thenReturn(terminalWidget);
|
||||
|
||||
presenter.addCommandOutput(MACHINE_ID, outputConsole);
|
||||
presenter.onAddTerminal(MACHINE_ID);
|
||||
presenter.onAddTerminal(WORKSPACE_ID, MACHINE_ID);
|
||||
|
||||
verify(machinePromise).then(machineCaptor.capture());
|
||||
machineCaptor.getValue().apply(machineDto);
|
||||
|
|
@ -409,7 +409,7 @@ public class ConsolesPanelPresenterTest {
|
|||
IsWidget terminalWidget = mock(IsWidget.class);
|
||||
when(terminal.getView()).thenReturn(terminalWidget);
|
||||
|
||||
presenter.onAddTerminal(MACHINE_ID);
|
||||
presenter.onAddTerminal(WORKSPACE_ID, MACHINE_ID);
|
||||
|
||||
verify(machinePromise).then(machineCaptor.capture());
|
||||
machineCaptor.getValue().apply(machineDto);
|
||||
|
|
|
|||
|
|
@ -111,9 +111,10 @@ public class DockerCategoryPresenterTest {
|
|||
when(target.getName()).thenReturn(deletingTargetName);
|
||||
when(targetsTreeManager.getMachineByName(deletingTargetName)).thenReturn(machine);
|
||||
when(machine.getId()).thenReturn(deletingMachineId);
|
||||
when(machine.getWorkspaceId()).thenReturn("WS_ID");
|
||||
when(machine.getStatus()).thenReturn(RUNNING);
|
||||
when(machineLocale.targetsViewDeleteConfirm(deletingTargetName)).thenReturn(deleteProposal);
|
||||
when(machineService.destroyMachine(deletingMachineId)).thenReturn(promise);
|
||||
when(machineService.destroyMachine("WS_ID", deletingMachineId)).thenReturn(promise);
|
||||
|
||||
|
||||
arbitraryCategoryPresenter.onDeleteClicked(target);
|
||||
|
|
@ -124,7 +125,7 @@ public class DockerCategoryPresenterTest {
|
|||
confirmCaptor.getValue().accepted();
|
||||
|
||||
verify(targetsTreeManager).getMachineByName(deletingTargetName);
|
||||
verify(machineService).destroyMachine(deletingMachineId);
|
||||
verify(machineService).destroyMachine("WS_ID", deletingMachineId);
|
||||
|
||||
operationSuccessCapture.getValue().apply(null);
|
||||
|
||||
|
|
|
|||
|
|
@ -32,10 +32,6 @@
|
|||
<groupId>javax.inject</groupId>
|
||||
<artifactId>javax.inject</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>javax.ws.rs</groupId>
|
||||
<artifactId>javax.ws.rs-api</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.eclipse.che.core</groupId>
|
||||
<artifactId>che-core-api-core</artifactId>
|
||||
|
|
@ -56,6 +52,10 @@
|
|||
<groupId>org.eclipse.che.core</groupId>
|
||||
<artifactId>che-core-api-ssh</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.eclipse.che.core</groupId>
|
||||
<artifactId>che-core-api-workspace</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.eclipse.che.core</groupId>
|
||||
<artifactId>che-core-commons-inject</artifactId>
|
||||
|
|
|
|||
|
|
@ -12,7 +12,7 @@ package org.eclipse.che.ide.ext.machine.server;
|
|||
|
||||
import com.google.inject.AbstractModule;
|
||||
|
||||
import org.eclipse.che.api.machine.server.MachineService;
|
||||
import org.eclipse.che.api.workspace.server.RecipeScriptDownloadService;
|
||||
import org.eclipse.che.ide.ext.machine.server.ssh.KeysInjector;
|
||||
import org.eclipse.che.inject.DynaModule;
|
||||
|
||||
|
|
|
|||
|
|
@ -14,7 +14,7 @@ import org.eclipse.che.api.core.NotFoundException;
|
|||
import org.eclipse.che.api.core.ServerException;
|
||||
import org.eclipse.che.api.core.notification.EventService;
|
||||
import org.eclipse.che.api.core.notification.EventSubscriber;
|
||||
import org.eclipse.che.api.machine.server.MachineManager;
|
||||
import org.eclipse.che.api.environment.server.CheEnvironmentEngine;
|
||||
import org.eclipse.che.api.machine.server.spi.Instance;
|
||||
import org.eclipse.che.api.machine.shared.dto.event.MachineStatusEvent;
|
||||
import org.eclipse.che.api.ssh.server.SshManager;
|
||||
|
|
@ -43,20 +43,21 @@ import java.util.stream.Collectors;
|
|||
public class KeysInjector {
|
||||
private static final Logger LOG = LoggerFactory.getLogger(KeysInjector.class);
|
||||
|
||||
private final EventService eventService;
|
||||
private final DockerConnector docker;
|
||||
private final MachineManager machineManager;
|
||||
private final SshManager sshManager;
|
||||
private final EventService eventService;
|
||||
private final DockerConnector docker;
|
||||
private final SshManager sshManager;
|
||||
// TODO replace with WorkspaceManager
|
||||
private final CheEnvironmentEngine environmentEngine;
|
||||
|
||||
@Inject
|
||||
public KeysInjector(EventService eventService,
|
||||
DockerConnector docker,
|
||||
MachineManager machineManager,
|
||||
SshManager sshManager) {
|
||||
SshManager sshManager,
|
||||
CheEnvironmentEngine environmentEngine) {
|
||||
this.eventService = eventService;
|
||||
this.docker = docker;
|
||||
this.machineManager = machineManager;
|
||||
this.sshManager = sshManager;
|
||||
this.environmentEngine = environmentEngine;
|
||||
}
|
||||
|
||||
@PostConstruct
|
||||
|
|
@ -66,7 +67,8 @@ public class KeysInjector {
|
|||
public void onEvent(MachineStatusEvent event) {
|
||||
if (event.getEventType() == MachineStatusEvent.EventType.RUNNING) {
|
||||
try {
|
||||
final Instance machine = machineManager.getInstance(event.getMachineId());
|
||||
final Instance machine = environmentEngine.getMachine(event.getWorkspaceId(),
|
||||
event.getMachineId());
|
||||
List<SshPairImpl> sshPairs = sshManager.getPairs(machine.getOwner(), "machine");
|
||||
final List<String> publicKeys = sshPairs.stream()
|
||||
.filter(sshPair -> sshPair.getPublicKey() != null)
|
||||
|
|
|
|||
|
|
@ -14,7 +14,7 @@ import org.eclipse.che.api.core.model.machine.MachineRuntimeInfo;
|
|||
import org.eclipse.che.api.core.notification.EventService;
|
||||
import org.eclipse.che.api.core.notification.EventSubscriber;
|
||||
import org.eclipse.che.api.core.util.LineConsumer;
|
||||
import org.eclipse.che.api.machine.server.MachineManager;
|
||||
import org.eclipse.che.api.environment.server.CheEnvironmentEngine;
|
||||
import org.eclipse.che.api.machine.server.spi.Instance;
|
||||
import org.eclipse.che.api.machine.shared.dto.event.MachineStatusEvent;
|
||||
import org.eclipse.che.api.ssh.server.SshManager;
|
||||
|
|
@ -56,6 +56,7 @@ import static org.testng.Assert.assertEquals;
|
|||
*/
|
||||
@Listeners(MockitoTestNGListener.class)
|
||||
public class KeysInjectorTest {
|
||||
private static final String WORKSPACE_ID = "workspace123";
|
||||
private static final String MACHINE_ID = "machine123";
|
||||
private static final String OWNER_ID = "user123";
|
||||
private static final String CONTAINER_ID = "container123";
|
||||
|
|
@ -82,7 +83,7 @@ public class KeysInjectorTest {
|
|||
@Mock
|
||||
DockerConnector docker;
|
||||
@Mock
|
||||
MachineManager machineManager;
|
||||
CheEnvironmentEngine environmentEngine;
|
||||
@Mock
|
||||
SshManager sshManager;
|
||||
|
||||
|
|
@ -97,7 +98,7 @@ public class KeysInjectorTest {
|
|||
metadataProperties.put("id", CONTAINER_ID);
|
||||
when(machineRuntime.getProperties()).thenReturn(metadataProperties);
|
||||
|
||||
when(machineManager.getInstance(MACHINE_ID)).thenReturn(instance);
|
||||
when(environmentEngine.getMachine(WORKSPACE_ID, MACHINE_ID)).thenReturn(instance);
|
||||
when(instance.getOwner()).thenReturn(OWNER_ID);
|
||||
when(instance.getRuntime()).thenReturn(machineRuntime);
|
||||
when(instance.getLogger()).thenReturn(lineConsumer);
|
||||
|
|
@ -114,7 +115,7 @@ public class KeysInjectorTest {
|
|||
public void shouldNotDoAnythingIfEventTypeDoesNotEqualToRunning() {
|
||||
subscriber.onEvent(newDto(MachineStatusEvent.class).withEventType(MachineStatusEvent.EventType.DESTROYING));
|
||||
|
||||
verifyZeroInteractions(docker, machineManager, sshManager);
|
||||
verifyZeroInteractions(docker, environmentEngine, sshManager);
|
||||
}
|
||||
|
||||
@Test
|
||||
|
|
@ -122,11 +123,12 @@ public class KeysInjectorTest {
|
|||
when(sshManager.getPairs(anyString(), anyString())).thenReturn(Collections.emptyList());
|
||||
|
||||
subscriber.onEvent(newDto(MachineStatusEvent.class).withEventType(MachineStatusEvent.EventType.RUNNING)
|
||||
.withMachineId(MACHINE_ID));
|
||||
.withMachineId(MACHINE_ID)
|
||||
.withWorkspaceId(WORKSPACE_ID));
|
||||
|
||||
verify(machineManager).getInstance(eq(MACHINE_ID));
|
||||
verify(environmentEngine).getMachine(eq(WORKSPACE_ID), eq(MACHINE_ID));
|
||||
verify(sshManager).getPairs(eq(OWNER_ID), eq("machine"));
|
||||
verifyZeroInteractions(docker, machineManager, sshManager);
|
||||
verifyZeroInteractions(docker, environmentEngine, sshManager);
|
||||
}
|
||||
|
||||
@Test
|
||||
|
|
@ -135,11 +137,12 @@ public class KeysInjectorTest {
|
|||
.thenReturn(Collections.singletonList(new SshPairImpl("machine", "myPair", null, null)));
|
||||
|
||||
subscriber.onEvent(newDto(MachineStatusEvent.class).withEventType(MachineStatusEvent.EventType.RUNNING)
|
||||
.withMachineId(MACHINE_ID));
|
||||
.withMachineId(MACHINE_ID)
|
||||
.withWorkspaceId(WORKSPACE_ID));
|
||||
|
||||
verify(machineManager).getInstance(eq(MACHINE_ID));
|
||||
verify(environmentEngine).getMachine(eq(WORKSPACE_ID), eq(MACHINE_ID));
|
||||
verify(sshManager).getPairs(eq(OWNER_ID), eq("machine"));
|
||||
verifyZeroInteractions(docker, machineManager, sshManager);
|
||||
verifyZeroInteractions(docker, environmentEngine, sshManager);
|
||||
}
|
||||
|
||||
@Test
|
||||
|
|
@ -149,9 +152,10 @@ public class KeysInjectorTest {
|
|||
new SshPairImpl("machine", "myPair", "publicKey2", null)));
|
||||
|
||||
subscriber.onEvent(newDto(MachineStatusEvent.class).withEventType(MachineStatusEvent.EventType.RUNNING)
|
||||
.withMachineId(MACHINE_ID));
|
||||
.withMachineId(MACHINE_ID)
|
||||
.withWorkspaceId(WORKSPACE_ID));
|
||||
|
||||
verify(machineManager).getInstance(eq(MACHINE_ID));
|
||||
verify(environmentEngine).getMachine(eq(WORKSPACE_ID), eq(MACHINE_ID));
|
||||
verify(sshManager).getPairs(eq(OWNER_ID), eq("machine"));
|
||||
|
||||
ArgumentCaptor<CreateExecParams> argumentCaptor = ArgumentCaptor.forClass(CreateExecParams.class);
|
||||
|
|
@ -160,7 +164,7 @@ public class KeysInjectorTest {
|
|||
"&& echo 'publicKey1' >> ~/.ssh/authorized_keys" +
|
||||
"&& echo 'publicKey2' >> ~/.ssh/authorized_keys"});
|
||||
verify(docker).startExec(eq(StartExecParams.create(EXEC_ID)), anyObject());
|
||||
verifyZeroInteractions(docker, machineManager, sshManager);
|
||||
verifyZeroInteractions(docker, environmentEngine, sshManager);
|
||||
}
|
||||
|
||||
@Test
|
||||
|
|
@ -171,7 +175,8 @@ public class KeysInjectorTest {
|
|||
when(logMessage.getContent()).thenReturn("FAILED");
|
||||
|
||||
subscriber.onEvent(newDto(MachineStatusEvent.class).withEventType(MachineStatusEvent.EventType.RUNNING)
|
||||
.withMachineId(MACHINE_ID));
|
||||
.withMachineId(MACHINE_ID)
|
||||
.withWorkspaceId(WORKSPACE_ID));
|
||||
|
||||
verify(docker).startExec(eq(StartExecParams.create(EXEC_ID)), messageProcessorCaptor.capture());
|
||||
final MessageProcessor<LogMessage> value = messageProcessorCaptor.getValue();
|
||||
|
|
|
|||
|
|
@ -23,7 +23,7 @@ import org.eclipse.che.api.project.server.type.ProjectTypeRegistry;
|
|||
import org.eclipse.che.api.workspace.shared.dto.ProjectConfigDto;
|
||||
import org.eclipse.che.api.workspace.shared.dto.WorkspaceConfigDto;
|
||||
import org.eclipse.che.api.workspace.shared.dto.WorkspaceDto;
|
||||
import org.eclipse.che.commons.test.SelfReturningAnswer;
|
||||
import org.eclipse.che.commons.test.mockito.answer.SelfReturningAnswer;
|
||||
import org.eclipse.che.dto.server.DtoFactory;
|
||||
import org.eclipse.che.plugin.java.server.projecttype.JavaProjectType;
|
||||
import org.eclipse.che.plugin.java.server.projecttype.JavaValueProviderFactory;
|
||||
|
|
|
|||
|
|
@ -66,6 +66,10 @@
|
|||
<groupId>org.eclipse.che.core</groupId>
|
||||
<artifactId>che-core-api-model</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.eclipse.che.core</groupId>
|
||||
<artifactId>che-core-api-workspace</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.eclipse.che.core</groupId>
|
||||
<artifactId>che-core-commons-annotations</artifactId>
|
||||
|
|
|
|||
|
|
@ -10,6 +10,8 @@
|
|||
*******************************************************************************/
|
||||
package org.eclipse.che.plugin.machine.ssh;
|
||||
|
||||
import org.eclipse.che.api.agent.server.terminal.MachineImplSpecificTerminalLauncher;
|
||||
import org.eclipse.che.api.agent.server.terminal.WebsocketTerminalFilesPathProvider;
|
||||
import org.eclipse.che.api.core.ConflictException;
|
||||
import org.eclipse.che.api.core.util.AbstractLineConsumer;
|
||||
import org.eclipse.che.api.core.util.ListLineConsumer;
|
||||
|
|
@ -17,8 +19,6 @@ import org.eclipse.che.api.machine.server.exception.MachineException;
|
|||
import org.eclipse.che.api.machine.server.model.impl.CommandImpl;
|
||||
import org.eclipse.che.api.machine.server.spi.Instance;
|
||||
import org.eclipse.che.api.machine.server.spi.InstanceProcess;
|
||||
import org.eclipse.che.api.machine.server.terminal.MachineImplSpecificTerminalLauncher;
|
||||
import org.eclipse.che.api.machine.server.terminal.WebsocketTerminalFilesPathProvider;
|
||||
import org.slf4j.Logger;
|
||||
|
||||
import javax.inject.Inject;
|
||||
|
|
|
|||
|
|
@ -141,7 +141,7 @@ public class SshMachineInstance extends AbstractInstance {
|
|||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
public MachineSource saveToSnapshot(String owner) throws MachineException {
|
||||
public MachineSource saveToSnapshot() throws MachineException {
|
||||
throw new MachineException("Snapshot feature is unsupported for ssh machine implementation");
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -15,7 +15,7 @@ import com.google.inject.assistedinject.FactoryModuleBuilder;
|
|||
import com.google.inject.multibindings.Multibinder;
|
||||
import com.google.inject.name.Names;
|
||||
|
||||
import org.eclipse.che.api.machine.server.terminal.MachineImplSpecificTerminalLauncher;
|
||||
import org.eclipse.che.api.agent.server.terminal.MachineImplSpecificTerminalLauncher;
|
||||
|
||||
/**
|
||||
* Provides bindings needed for ssh machine implementation usage.
|
||||
|
|
|
|||
|
|
@ -54,7 +54,7 @@ import org.eclipse.che.commons.json.JsonHelper;
|
|||
import org.eclipse.che.commons.lang.IoUtil;
|
||||
import org.eclipse.che.commons.lang.ws.rs.ExtMediaType;
|
||||
import org.eclipse.che.commons.subject.SubjectImpl;
|
||||
import org.eclipse.che.commons.test.SelfReturningAnswer;
|
||||
import org.eclipse.che.commons.test.mockito.answer.SelfReturningAnswer;
|
||||
import org.eclipse.che.dto.server.DtoFactory;
|
||||
import org.everrest.core.ApplicationContext;
|
||||
import org.everrest.core.ResourceBinder;
|
||||
|
|
|
|||
|
|
@ -17,7 +17,7 @@ import org.eclipse.che.api.core.rest.HttpJsonRequest;
|
|||
import org.eclipse.che.api.core.rest.HttpJsonRequestFactory;
|
||||
import org.eclipse.che.api.core.rest.HttpJsonResponse;
|
||||
import org.eclipse.che.api.core.rest.shared.dto.Link;
|
||||
import org.eclipse.che.commons.test.SelfReturningAnswer;
|
||||
import org.eclipse.che.commons.test.mockito.answer.SelfReturningAnswer;
|
||||
import org.eclipse.che.dto.server.DtoFactory;
|
||||
import org.mockito.ArgumentCaptor;
|
||||
import org.mockito.Mock;
|
||||
|
|
|
|||
|
|
@ -22,19 +22,13 @@ public class Constants {
|
|||
public static final String LINK_REL_UPDATE_RECIPE = "update recipe";
|
||||
|
||||
public static final String LINK_REL_SELF = "self link";
|
||||
public static final String LINK_REL_GET_MACHINE = "get machine";
|
||||
public static final String LINK_REL_GET_MACHINES = "get machines";
|
||||
public static final String LINK_REL_DESTROY_MACHINE = "destroy machine";
|
||||
public static final String LINK_REL_GET_SNAPSHOTS = "get snapshots";
|
||||
public static final String LINK_REL_SAVE_SNAPSHOT = "save snapshot";
|
||||
public static final String LINK_REL_REMOVE_SNAPSHOT = "remove snapshot";
|
||||
public static final String LINK_REL_EXECUTE_COMMAND = "execute command";
|
||||
public static final String LINK_REL_GET_PROCESSES = "get processes";
|
||||
public static final String LINK_REL_STOP_PROCESS = "stop process";
|
||||
public static final String LINK_REL_GET_MACHINE_LOGS = "get machine logs";
|
||||
public static final String LINK_REL_GET_PROCESS_LOGS = "get process logs";
|
||||
public static final String LINK_REL_GET_MACHINE_LOGS_CHANNEL = "get machine logs channel";
|
||||
public static final String LINK_REL_GET_MACHINE_STATUS_CHANNEL = "get machine status channel";
|
||||
|
||||
public static final String WSAGENT_REFERENCE = "wsagent";
|
||||
public static final String WSAGENT_WEBSOCKET_REFERENCE = "wsagent.websocket";
|
||||
|
|
|
|||
|
|
@ -37,10 +37,6 @@
|
|||
<groupId>com.google.inject</groupId>
|
||||
<artifactId>guice</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>io.swagger</groupId>
|
||||
<artifactId>swagger-annotations</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>javax.annotation</groupId>
|
||||
<artifactId>javax.annotation-api</artifactId>
|
||||
|
|
@ -77,10 +73,6 @@
|
|||
<groupId>org.eclipse.che.core</groupId>
|
||||
<artifactId>che-core-commons-annotations</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.eclipse.che.core</groupId>
|
||||
<artifactId>che-core-commons-inject</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.eclipse.che.core</groupId>
|
||||
<artifactId>che-core-commons-lang</artifactId>
|
||||
|
|
|
|||
|
|
@ -1,52 +0,0 @@
|
|||
/*******************************************************************************
|
||||
* Copyright (c) 2012-2016 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.machine.server;
|
||||
|
||||
/**
|
||||
* @author Alexander Garagatyi
|
||||
*/
|
||||
public class ChannelsImpl {
|
||||
private final String outputChannel;
|
||||
private final String statusChannel;
|
||||
|
||||
public ChannelsImpl(String outputChannel, String statusChannel) {
|
||||
this.outputChannel = outputChannel;
|
||||
this.statusChannel = statusChannel;
|
||||
}
|
||||
|
||||
public String getOutput() {
|
||||
return outputChannel;
|
||||
}
|
||||
|
||||
public String getStatus() {
|
||||
return statusChannel;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) return true;
|
||||
if (!(o instanceof ChannelsImpl)) return false;
|
||||
|
||||
ChannelsImpl channels = (ChannelsImpl)o;
|
||||
|
||||
if (outputChannel != null ? !outputChannel.equals(channels.outputChannel) : channels.outputChannel != null) return false;
|
||||
return !(statusChannel != null ? !statusChannel.equals(channels.statusChannel) : channels.statusChannel != null);
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
int result = outputChannel != null ? outputChannel.hashCode() : 0;
|
||||
result = 31 * result + (statusChannel != null ? statusChannel.hashCode() : 0);
|
||||
return result;
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
|
|
@ -1,191 +0,0 @@
|
|||
/*******************************************************************************
|
||||
* Copyright (c) 2012-2016 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.machine.server;
|
||||
|
||||
import org.eclipse.che.api.core.ConflictException;
|
||||
import org.eclipse.che.api.core.NotFoundException;
|
||||
import org.eclipse.che.api.machine.server.exception.MachineException;
|
||||
import org.eclipse.che.api.machine.server.model.impl.MachineImpl;
|
||||
import org.eclipse.che.api.machine.server.spi.Instance;
|
||||
|
||||
import javax.inject.Singleton;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* Holds active machines
|
||||
*
|
||||
* @author Alexander Garagatyi
|
||||
*/
|
||||
@Singleton
|
||||
public class MachineRegistry {
|
||||
// TODO add locks per workspace or machine
|
||||
private final HashMap<String, Instance> instances;
|
||||
private final HashMap<String, MachineImpl> machines;
|
||||
|
||||
public MachineRegistry() {
|
||||
instances = new HashMap<>();
|
||||
machines = new HashMap<>();
|
||||
}
|
||||
|
||||
//TODO return unmodifiable lists
|
||||
|
||||
/**
|
||||
* Get all active machines
|
||||
*
|
||||
* @throws MachineException
|
||||
* if any error occurs
|
||||
*/
|
||||
public synchronized List<MachineImpl> getMachines() throws MachineException {
|
||||
final List<MachineImpl> list = new ArrayList<>(machines.size() + instances.size());
|
||||
list.addAll(machines.values());
|
||||
list.addAll(instances.values().stream().map(this::toMachine).collect(Collectors.toList()));
|
||||
return Collections.unmodifiableList(list);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get machine by ID, machine can be in running or not
|
||||
*
|
||||
* @param machineId
|
||||
* id of machine
|
||||
* @throws NotFoundException
|
||||
* if machine was not found
|
||||
* @throws MachineException
|
||||
* if other error occurs
|
||||
*/
|
||||
public synchronized MachineImpl getMachine(String machineId) throws NotFoundException, MachineException {
|
||||
MachineImpl machine = machines.get(machineId);
|
||||
if (machine == null) {
|
||||
final Instance instance = instances.get(machineId);
|
||||
if (instance == null) {
|
||||
throw new NotFoundException("Machine " + machineId + " is not found");
|
||||
}
|
||||
machine = toMachine(instance);
|
||||
}
|
||||
|
||||
return machine;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if machine with unique {@code machineId} is exist, or false otherwise.
|
||||
*
|
||||
* @param machineId
|
||||
* unique machine identifier
|
||||
*/
|
||||
public synchronized boolean isExist(String machineId) {
|
||||
return machines.containsKey(machineId) || instances.containsKey(machineId);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get dev machine of specific workspace. Dev machine should be in RUNNING state
|
||||
*
|
||||
* @param workspaceId
|
||||
* id of workspace
|
||||
* @throws NotFoundException
|
||||
* if dev machine was not found or it is not in RUNNING
|
||||
* @throws MachineException
|
||||
* if other error occurs
|
||||
*/
|
||||
public synchronized MachineImpl getDevMachine(String workspaceId) throws NotFoundException, MachineException {
|
||||
for (Instance instance : instances.values()) {
|
||||
if (instance.getWorkspaceId().equals(workspaceId) && instance.getConfig().isDev()) {
|
||||
return toMachine(instance);
|
||||
}
|
||||
}
|
||||
|
||||
throw new NotFoundException("Dev machine of workspace " + workspaceId + " is not running.");
|
||||
}
|
||||
|
||||
/**
|
||||
* Get machine by id. It should be in RUNNING state
|
||||
*
|
||||
* @param machineId
|
||||
* id of machine
|
||||
* @return machine with specified id
|
||||
* @throws NotFoundException
|
||||
* if machine with specified id not found
|
||||
* @throws MachineException
|
||||
* if other error occurs
|
||||
*/
|
||||
public synchronized Instance getInstance(String machineId) throws NotFoundException, MachineException {
|
||||
final Instance instance = instances.get(machineId);
|
||||
if (instance == null) {
|
||||
throw new NotFoundException("Machine " + machineId + " is not found");
|
||||
} else {
|
||||
return instance;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Add not yet running machine
|
||||
*
|
||||
* @param machine
|
||||
* machine
|
||||
* @throws ConflictException
|
||||
* if machine with the same ID already exists
|
||||
* @throws MachineException
|
||||
* if any other error occurs
|
||||
*/
|
||||
public synchronized void addMachine(MachineImpl machine) throws MachineException, ConflictException {
|
||||
if (machines.containsKey(machine.getId())) {
|
||||
throw new ConflictException("Machine with id " + machine.getId() + " is already exist");
|
||||
}
|
||||
machines.put(machine.getId(), machine);
|
||||
}
|
||||
|
||||
/**
|
||||
* Replace not running machine with instance of running machine
|
||||
*
|
||||
* @param instance
|
||||
* running machine
|
||||
* @throws NotFoundException
|
||||
* if not running machine is not found
|
||||
* @throws MachineException
|
||||
* if any other error occurs
|
||||
*/
|
||||
public synchronized void update(Instance instance) throws NotFoundException, MachineException {
|
||||
if (!instances.containsKey(instance.getId()) && !machines.containsKey(instance.getId())) {
|
||||
throw new NotFoundException("Machine " + instance.getId() + " not found");
|
||||
} else {
|
||||
instances.put(instance.getId(), instance);
|
||||
machines.remove(instance.getId());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove machine by id
|
||||
*
|
||||
* @param machineId
|
||||
* id of machine that should be removed
|
||||
* @throws NotFoundException
|
||||
* if machine with specified id not found
|
||||
*/
|
||||
public synchronized void remove(String machineId) throws NotFoundException {
|
||||
final Instance instance = instances.remove(machineId);
|
||||
final MachineImpl machine = machines.remove(machineId);
|
||||
if (null == instance && null == machine) {
|
||||
throw new NotFoundException("Machine " + machineId + " is not found");
|
||||
}
|
||||
}
|
||||
|
||||
private MachineImpl toMachine(Instance instance) {
|
||||
return new MachineImpl(instance.getConfig(),
|
||||
instance.getId(),
|
||||
instance.getWorkspaceId(),
|
||||
instance.getEnvName(),
|
||||
instance.getOwner(),
|
||||
instance.getStatus(),
|
||||
instance.getRuntime());
|
||||
}
|
||||
}
|
||||
|
|
@ -1,438 +0,0 @@
|
|||
/*******************************************************************************
|
||||
* Copyright (c) 2012-2016 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.machine.server;
|
||||
|
||||
import io.swagger.annotations.Api;
|
||||
import io.swagger.annotations.ApiOperation;
|
||||
import io.swagger.annotations.ApiParam;
|
||||
import io.swagger.annotations.ApiResponse;
|
||||
import io.swagger.annotations.ApiResponses;
|
||||
|
||||
import com.google.common.io.CharStreams;
|
||||
|
||||
import org.eclipse.che.api.core.BadRequestException;
|
||||
import org.eclipse.che.api.core.ForbiddenException;
|
||||
import org.eclipse.che.api.core.NotFoundException;
|
||||
import org.eclipse.che.api.core.ServerException;
|
||||
import org.eclipse.che.api.core.model.machine.Machine;
|
||||
import org.eclipse.che.api.core.rest.Service;
|
||||
import org.eclipse.che.api.machine.server.exception.MachineException;
|
||||
import org.eclipse.che.api.machine.server.model.impl.SnapshotImpl;
|
||||
import org.eclipse.che.api.machine.server.spi.Instance;
|
||||
import org.eclipse.che.api.machine.shared.dto.CommandDto;
|
||||
import org.eclipse.che.api.machine.shared.dto.MachineDto;
|
||||
import org.eclipse.che.api.machine.shared.dto.MachineProcessDto;
|
||||
import org.eclipse.che.api.machine.shared.dto.NewSnapshotDescriptor;
|
||||
import org.eclipse.che.api.machine.shared.dto.SnapshotDto;
|
||||
import org.eclipse.che.commons.env.EnvironmentContext;
|
||||
|
||||
import javax.inject.Inject;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
import javax.ws.rs.Consumes;
|
||||
import javax.ws.rs.DELETE;
|
||||
import javax.ws.rs.DefaultValue;
|
||||
import javax.ws.rs.GET;
|
||||
import javax.ws.rs.POST;
|
||||
import javax.ws.rs.Path;
|
||||
import javax.ws.rs.PathParam;
|
||||
import javax.ws.rs.Produces;
|
||||
import javax.ws.rs.QueryParam;
|
||||
import javax.ws.rs.core.Context;
|
||||
import javax.ws.rs.core.MediaType;
|
||||
import java.io.IOException;
|
||||
import java.io.Reader;
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* Machine API
|
||||
*
|
||||
* @author Alexander Garagatyi
|
||||
* @author Anton Korneta
|
||||
*/
|
||||
@Api(value = "/machine", description = "Machine REST API")
|
||||
@Path("/machine")
|
||||
public class MachineService extends Service {
|
||||
private MachineManager machineManager;
|
||||
|
||||
private final MachineServiceLinksInjector linksInjector;
|
||||
|
||||
@Inject
|
||||
public MachineService(MachineManager machineManager, MachineServiceLinksInjector linksInjector) {
|
||||
this.machineManager = machineManager;
|
||||
this.linksInjector = linksInjector;
|
||||
}
|
||||
|
||||
@GET
|
||||
@Path("/{machineId}")
|
||||
@Produces(MediaType.APPLICATION_JSON)
|
||||
@ApiOperation(value = "Get machine by ID")
|
||||
@ApiResponses({@ApiResponse(code = 200, message = "The response contains requested machine entity"),
|
||||
@ApiResponse(code = 404, message = "Machine with specified id does not exist"),
|
||||
@ApiResponse(code = 500, message = "Internal server error occurred")})
|
||||
public MachineDto getMachineById(@ApiParam(value = "Machine ID")
|
||||
@PathParam("machineId")
|
||||
String machineId)
|
||||
throws ServerException,
|
||||
ForbiddenException,
|
||||
NotFoundException {
|
||||
|
||||
final Machine machine = machineManager.getMachine(machineId);
|
||||
return linksInjector.injectLinks(DtoConverter.asDto(machine), getServiceContext());
|
||||
}
|
||||
|
||||
@GET
|
||||
@Produces(MediaType.APPLICATION_JSON)
|
||||
@ApiOperation(value = "Get all machines of workspace with specified ID",
|
||||
response = MachineDto.class,
|
||||
responseContainer = "List")
|
||||
@ApiResponses({@ApiResponse(code = 200, message = "The response contains requested list of machine entities"),
|
||||
@ApiResponse(code = 400, message = "Workspace ID is not specified"),
|
||||
@ApiResponse(code = 500, message = "Internal server error occurred")})
|
||||
public List<MachineDto> getMachines(@ApiParam(value = "Workspace ID", required = true)
|
||||
@QueryParam("workspace")
|
||||
String workspaceId)
|
||||
throws ServerException,
|
||||
BadRequestException {
|
||||
|
||||
requiredNotNull(workspaceId, "Parameter workspace");
|
||||
|
||||
return machineManager.getMachines(workspaceId)
|
||||
.stream()
|
||||
.map(DtoConverter::asDto)
|
||||
.map(machineDto -> linksInjector.injectLinks(machineDto, getServiceContext()))
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
@DELETE
|
||||
@Path("/{machineId}")
|
||||
@Produces(MediaType.APPLICATION_JSON)
|
||||
@ApiOperation(value = "Destroy machine")
|
||||
@ApiResponses({@ApiResponse(code = 204, message = "Machine was successfully destroyed"),
|
||||
@ApiResponse(code = 404, message = "Machine with specified id does not exist"),
|
||||
@ApiResponse(code = 500, message = "Internal server error occurred")})
|
||||
public void destroyMachine(@ApiParam(value = "Machine ID")
|
||||
@PathParam("machineId")
|
||||
String machineId)
|
||||
throws NotFoundException,
|
||||
ServerException,
|
||||
ForbiddenException {
|
||||
|
||||
machineManager.destroy(machineId, true);
|
||||
}
|
||||
|
||||
@GET
|
||||
@Path("/snapshot")
|
||||
@Produces(MediaType.APPLICATION_JSON)
|
||||
@ApiOperation(value = "Get all snapshots of machines in workspace",
|
||||
response = SnapshotDto.class,
|
||||
responseContainer = "List")
|
||||
@ApiResponses({@ApiResponse(code = 200, message = "The response contains requested list of snapshot entities"),
|
||||
@ApiResponse(code = 400, message = "Workspace ID is not specified"),
|
||||
@ApiResponse(code = 500, message = "Internal server error occurred")})
|
||||
public List<SnapshotDto> getSnapshots(@ApiParam(value = "Workspace ID", required = true)
|
||||
@QueryParam("workspace")
|
||||
String workspaceId)
|
||||
throws ServerException,
|
||||
BadRequestException {
|
||||
|
||||
requiredNotNull(workspaceId, "Parameter workspace");
|
||||
|
||||
final List<SnapshotImpl> snapshots = machineManager.getSnapshots(EnvironmentContext.getCurrent().getSubject().getUserId(), workspaceId);
|
||||
|
||||
return snapshots.stream()
|
||||
.map(DtoConverter::asDto)
|
||||
.map(snapshotDto -> linksInjector.injectLinks(snapshotDto, getServiceContext()))
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
@POST
|
||||
@Path("/{machineId}/snapshot")
|
||||
@Consumes(MediaType.APPLICATION_JSON)
|
||||
@Produces(MediaType.APPLICATION_JSON)
|
||||
@ApiOperation(value = "Save snapshot of machine")
|
||||
@ApiResponses({@ApiResponse(code = 200, message = "The response contains requested snapshot entity"),
|
||||
@ApiResponse(code = 400, message = "Snapshot description is not specified"),
|
||||
@ApiResponse(code = 404, message = "Snapshot with specified ID does not exist"),
|
||||
@ApiResponse(code = 500, message = "Internal server error occurred")})
|
||||
public SnapshotDto saveSnapshot(@ApiParam(value = "Machine ID")
|
||||
@PathParam("machineId")
|
||||
String machineId,
|
||||
@ApiParam(value = "Snapshot description", required = true)
|
||||
NewSnapshotDescriptor newSnapshotDescriptor)
|
||||
throws NotFoundException,
|
||||
ServerException,
|
||||
ForbiddenException,
|
||||
BadRequestException {
|
||||
|
||||
requiredNotNull(newSnapshotDescriptor, "Snapshot description");
|
||||
return linksInjector.injectLinks(DtoConverter.asDto(machineManager.save(machineId,
|
||||
EnvironmentContext.getCurrent()
|
||||
.getSubject()
|
||||
.getUserId(),
|
||||
newSnapshotDescriptor.getDescription())),
|
||||
getServiceContext());
|
||||
}
|
||||
|
||||
@DELETE
|
||||
@Path("/snapshot/{snapshotId}")
|
||||
@ApiOperation(value = "Remove snapshot of machine")
|
||||
@ApiResponses({@ApiResponse(code = 204, message = "Snapshot was successfully removed"),
|
||||
@ApiResponse(code = 404, message = "Snapshot with specified ID does not exist"),
|
||||
@ApiResponse(code = 500, message = "Internal server error occurred")})
|
||||
public void removeSnapshot(@ApiParam(value = "Snapshot ID")
|
||||
@PathParam("snapshotId")
|
||||
String snapshotId)
|
||||
throws ForbiddenException,
|
||||
NotFoundException,
|
||||
ServerException {
|
||||
|
||||
machineManager.removeSnapshot(snapshotId);
|
||||
}
|
||||
|
||||
@POST
|
||||
@Path("/{machineId}/command")
|
||||
@Consumes(MediaType.APPLICATION_JSON)
|
||||
@ApiOperation(value = "Start specified command in machine")
|
||||
@ApiResponses({@ApiResponse(code = 200, message = "The response contains entity of created machine process"),
|
||||
@ApiResponse(code = 400, message = "Command entity is invalid"),
|
||||
@ApiResponse(code = 404, message = "Machine with specified ID does not exist"),
|
||||
@ApiResponse(code = 500, message = "Internal server error occurred")})
|
||||
public MachineProcessDto executeCommandInMachine(@ApiParam(value = "Machine ID")
|
||||
@PathParam("machineId")
|
||||
String machineId,
|
||||
@ApiParam(value = "Command to execute", required = true)
|
||||
final CommandDto command,
|
||||
@ApiParam(value = "Channel for command output", required = false)
|
||||
@QueryParam("outputChannel")
|
||||
String outputChannel)
|
||||
throws NotFoundException,
|
||||
ServerException,
|
||||
ForbiddenException,
|
||||
BadRequestException {
|
||||
|
||||
requiredNotNull(command, "Command description");
|
||||
requiredNotNull(command.getCommandLine(), "Commandline");
|
||||
return linksInjector.injectLinks(DtoConverter.asDto(machineManager.exec(machineId, command, outputChannel)),
|
||||
machineId,
|
||||
getServiceContext());
|
||||
}
|
||||
|
||||
@GET
|
||||
@Path("/{machineId}/process")
|
||||
@Produces(MediaType.APPLICATION_JSON)
|
||||
@ApiOperation(value = "Get processes of machine",
|
||||
response = MachineProcessDto.class,
|
||||
responseContainer = "List")
|
||||
@ApiResponses({@ApiResponse(code = 200, message = "The response contains machine process entities"),
|
||||
@ApiResponse(code = 404, message = "Machine with specified ID does not exist"),
|
||||
@ApiResponse(code = 500, message = "Internal server error occurred")})
|
||||
public List<MachineProcessDto> getProcesses(@ApiParam(value = "Machine ID")
|
||||
@PathParam("machineId")
|
||||
String machineId)
|
||||
throws NotFoundException,
|
||||
ServerException,
|
||||
ForbiddenException {
|
||||
|
||||
return machineManager.getProcesses(machineId)
|
||||
.stream()
|
||||
.map(DtoConverter::asDto)
|
||||
.map(machineProcess -> linksInjector.injectLinks(machineProcess,
|
||||
machineId,
|
||||
getServiceContext()))
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
@DELETE
|
||||
@Path("/{machineId}/process/{processId}")
|
||||
@ApiOperation(value = "Stop process in machine")
|
||||
@ApiResponses({@ApiResponse(code = 204, message = "Process was successfully stopped"),
|
||||
@ApiResponse(code = 404, message = "Machine with specified ID does not exist"),
|
||||
@ApiResponse(code = 500, message = "Internal server error occurred")})
|
||||
public void stopProcess(@ApiParam(value = "Machine ID")
|
||||
@PathParam("machineId")
|
||||
String machineId,
|
||||
@ApiParam(value = "Process ID")
|
||||
@PathParam("processId")
|
||||
int processId)
|
||||
throws NotFoundException,
|
||||
ForbiddenException,
|
||||
ServerException {
|
||||
|
||||
machineManager.stopProcess(machineId, processId);
|
||||
}
|
||||
|
||||
@GET
|
||||
@Path("/{machineId}/logs")
|
||||
@Produces(MediaType.TEXT_PLAIN)
|
||||
@ApiOperation(value = "Get logs of machine")
|
||||
@ApiResponses({@ApiResponse(code = 200, message = "The response contains logs"),
|
||||
@ApiResponse(code = 404, message = "Machine with specified ID does not exist"),
|
||||
@ApiResponse(code = 500, message = "Internal server error occurred")})
|
||||
public void getMachineLogs(@ApiParam(value = "Machine ID")
|
||||
@PathParam("machineId")
|
||||
String machineId,
|
||||
@Context
|
||||
HttpServletResponse httpServletResponse)
|
||||
throws NotFoundException,
|
||||
ForbiddenException,
|
||||
ServerException,
|
||||
IOException {
|
||||
|
||||
addLogsToResponse(machineManager.getMachineLogReader(machineId), httpServletResponse);
|
||||
}
|
||||
|
||||
@GET
|
||||
@Path("/{machineId}/process/{pid}/logs")
|
||||
@Produces(MediaType.TEXT_PLAIN)
|
||||
@ApiOperation(value = "Get logs of machine process")
|
||||
@ApiResponses({@ApiResponse(code = 200, message = "The response contains logs"),
|
||||
@ApiResponse(code = 404, message = "Machine or process with specified ID does not exist"),
|
||||
@ApiResponse(code = 500, message = "Internal server error occurred")})
|
||||
public void getProcessLogs(@ApiParam(value = "Machine ID")
|
||||
@PathParam("machineId")
|
||||
String machineId,
|
||||
@ApiParam(value = "Process ID")
|
||||
@PathParam("pid")
|
||||
int pid,
|
||||
@Context
|
||||
HttpServletResponse httpServletResponse)
|
||||
throws NotFoundException,
|
||||
ForbiddenException,
|
||||
ServerException,
|
||||
IOException {
|
||||
|
||||
addLogsToResponse(machineManager.getProcessLogReader(machineId, pid), httpServletResponse);
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads file content by specified file path.
|
||||
*
|
||||
* @param path
|
||||
* path to file on machine instance
|
||||
* @param startFrom
|
||||
* line number to start reading from
|
||||
* @param limit
|
||||
* limitation on line if not specified will used 2000 lines
|
||||
* @return file content.
|
||||
* @throws MachineException
|
||||
* if any error occurs with file reading
|
||||
*/
|
||||
@GET
|
||||
@Path("/{machineId}/filepath/{path:.*}")
|
||||
@Produces(MediaType.TEXT_PLAIN)
|
||||
@ApiOperation(value = "Get content of file in machine")
|
||||
@ApiResponses({@ApiResponse(code = 200, message = "The response contains file content"),
|
||||
@ApiResponse(code = 404, message = "Machine with specified ID does not exist"),
|
||||
@ApiResponse(code = 500, message = "Internal server error occurred")})
|
||||
public String getFileContent(@ApiParam(value = "Machine ID")
|
||||
@PathParam("machineId")
|
||||
String machineId,
|
||||
@ApiParam(value = "Path of file")
|
||||
@PathParam("path")
|
||||
String path,
|
||||
@ApiParam(value = "From line")
|
||||
@QueryParam("startFrom")
|
||||
@DefaultValue("1")
|
||||
Integer startFrom,
|
||||
@ApiParam(value = "Number of lines")
|
||||
@QueryParam("limit")
|
||||
@DefaultValue("2000")
|
||||
Integer limit)
|
||||
throws NotFoundException,
|
||||
ForbiddenException,
|
||||
ServerException {
|
||||
|
||||
final Instance machine = machineManager.getInstance(machineId);
|
||||
return machine.readFileContent(path, startFrom, limit);
|
||||
}
|
||||
|
||||
/**
|
||||
* Copies files from specified machine into current machine.
|
||||
*
|
||||
* @param sourceMachineId
|
||||
* source machine id
|
||||
* @param targetMachineId
|
||||
* target machine id
|
||||
* @param sourcePath
|
||||
* path to file or directory inside specified machine
|
||||
* @param targetPath
|
||||
* path to destination file or directory inside machine
|
||||
* @param overwrite
|
||||
* If "false" then it will be an error if unpacking the given content would cause
|
||||
* an existing directory to be replaced with a non-directory and vice versa.
|
||||
* @throws MachineException
|
||||
* if any error occurs when files are being copied
|
||||
* @throws NotFoundException
|
||||
* if any error occurs with getting source machine
|
||||
*/
|
||||
@POST
|
||||
@Path("/copy")
|
||||
@ApiOperation(value = "Copy files from one machine to another")
|
||||
@ApiResponses({@ApiResponse(code = 200, message = "Files were copied successfully"),
|
||||
@ApiResponse(code = 400, message = "Machine ID or path is not specified"),
|
||||
@ApiResponse(code = 404, message = "Source machine does not exist"),
|
||||
@ApiResponse(code = 404, message = "Target machine does not exist"),
|
||||
@ApiResponse(code = 500, message = "Internal server error occurred")})
|
||||
public void copyFilesBetweenMachines(@ApiParam(value = "Source machine ID", required = true)
|
||||
@QueryParam("sourceMachineId")
|
||||
String sourceMachineId,
|
||||
@ApiParam(value = "Target machine ID", required = true)
|
||||
@QueryParam("targetMachineId")
|
||||
String targetMachineId,
|
||||
@ApiParam(value = "Source path", required = true)
|
||||
@QueryParam("sourcePath")
|
||||
String sourcePath,
|
||||
@ApiParam(value = "Target path", required = true)
|
||||
@QueryParam("targetPath")
|
||||
String targetPath,
|
||||
@ApiParam(value = "Is files overwriting allowed")
|
||||
@QueryParam("overwrite")
|
||||
@DefaultValue("false")
|
||||
Boolean overwrite)
|
||||
throws NotFoundException,
|
||||
ServerException,
|
||||
ForbiddenException,
|
||||
BadRequestException {
|
||||
|
||||
requiredNotNull(sourceMachineId, "Source machine id");
|
||||
requiredNotNull(targetMachineId, "Target machine id");
|
||||
requiredNotNull(sourcePath, "Source path");
|
||||
requiredNotNull(targetPath, "Target path");
|
||||
|
||||
final Instance sourceMachine = machineManager.getInstance(sourceMachineId);
|
||||
final Instance targetMachine = machineManager.getInstance(targetMachineId);
|
||||
targetMachine.copy(sourceMachine, sourcePath, targetPath, overwrite);
|
||||
}
|
||||
|
||||
private void addLogsToResponse(Reader logsReader, HttpServletResponse httpServletResponse) throws IOException {
|
||||
// Response is written directly to the servlet request stream
|
||||
httpServletResponse.setContentType("text/plain");
|
||||
CharStreams.copy(logsReader, httpServletResponse.getWriter());
|
||||
httpServletResponse.getWriter().flush();
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks object reference is not {@code null}
|
||||
*
|
||||
* @param object
|
||||
* object reference to check
|
||||
* @param subject
|
||||
* used as subject of exception message "{subject} required"
|
||||
* @throws BadRequestException
|
||||
* when object reference is {@code null}
|
||||
*/
|
||||
private void requiredNotNull(Object object, String subject) throws BadRequestException {
|
||||
if (object == null) {
|
||||
throw new BadRequestException(subject + " required");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -26,10 +26,14 @@ public class InstanceStateEvent {
|
|||
}
|
||||
|
||||
private String machineId;
|
||||
private String workspaceId;
|
||||
private Type type;
|
||||
|
||||
public InstanceStateEvent(String machineId, Type type) {
|
||||
public InstanceStateEvent(String machineId,
|
||||
String workspaceId,
|
||||
Type type) {
|
||||
this.machineId = machineId;
|
||||
this.workspaceId = workspaceId;
|
||||
this.type = type;
|
||||
}
|
||||
|
||||
|
|
@ -40,4 +44,8 @@ public class InstanceStateEvent {
|
|||
public Type getType() {
|
||||
return type;
|
||||
}
|
||||
|
||||
public String getWorkspaceId() {
|
||||
return workspaceId;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -126,7 +126,7 @@ public class SnapshotImpl implements Snapshot {
|
|||
return this.isDev;
|
||||
}
|
||||
|
||||
public void setMachineSourceImpl(MachineSource machineSource) {
|
||||
public void setMachineSource(MachineSource machineSource) {
|
||||
this.machineSource = machineSource != null ? new MachineSourceImpl(machineSource) : null;
|
||||
}
|
||||
|
||||
|
|
@ -192,16 +192,16 @@ public class SnapshotImpl implements Snapshot {
|
|||
*/
|
||||
public static class SnapshotBuilder {
|
||||
|
||||
private String workspaceId;
|
||||
private String machineName;
|
||||
private String envName;
|
||||
private String id;
|
||||
private String type;
|
||||
private String namespace;
|
||||
private String description;
|
||||
private String workspaceId;
|
||||
private String machineName;
|
||||
private String envName;
|
||||
private String id;
|
||||
private String type;
|
||||
private String namespace;
|
||||
private String description;
|
||||
private MachineSource machineSource;
|
||||
private boolean isDev;
|
||||
private long creationDate;
|
||||
private boolean isDev;
|
||||
private long creationDate;
|
||||
|
||||
public SnapshotBuilder fromConfig(MachineConfig machineConfig) {
|
||||
machineName = machineConfig.getName();
|
||||
|
|
|
|||
|
|
@ -71,13 +71,11 @@ public interface Instance extends Machine {
|
|||
/**
|
||||
* Save state of the instance
|
||||
*
|
||||
* @param owner
|
||||
* id of the user that is owner of the snapshot
|
||||
* @return {@code InstanceSnapshotKey} that describe implementation specific keys of snapshot
|
||||
* @throws MachineException
|
||||
* if error occurs on storing state of the instance
|
||||
*/
|
||||
MachineSource saveToSnapshot(String owner) throws MachineException;
|
||||
MachineSource saveToSnapshot() throws MachineException;
|
||||
|
||||
/**
|
||||
* Destroy instance
|
||||
|
|
|
|||
|
|
@ -1,345 +0,0 @@
|
|||
/*******************************************************************************
|
||||
* Copyright (c) 2012-2016 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.machine.server;
|
||||
|
||||
import org.eclipse.che.api.core.BadRequestException;
|
||||
import org.eclipse.che.api.core.ConflictException;
|
||||
import org.eclipse.che.api.core.model.machine.Command;
|
||||
import org.eclipse.che.api.core.model.machine.Limits;
|
||||
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.notification.EventService;
|
||||
import org.eclipse.che.api.core.util.LineConsumer;
|
||||
import org.eclipse.che.api.machine.server.dao.SnapshotDao;
|
||||
import org.eclipse.che.api.machine.server.exception.MachineException;
|
||||
import org.eclipse.che.api.machine.server.exception.SourceNotFoundException;
|
||||
import org.eclipse.che.api.machine.server.model.impl.LimitsImpl;
|
||||
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.api.machine.server.model.impl.ServerConfImpl;
|
||||
import org.eclipse.che.api.machine.server.model.impl.SnapshotImpl;
|
||||
import org.eclipse.che.api.machine.server.recipe.RecipeImpl;
|
||||
import org.eclipse.che.api.machine.server.spi.Instance;
|
||||
import org.eclipse.che.api.machine.server.spi.InstanceProcess;
|
||||
import org.eclipse.che.api.machine.server.spi.InstanceProvider;
|
||||
import org.eclipse.che.api.machine.server.wsagent.WsAgentLauncher;
|
||||
import org.eclipse.che.commons.env.EnvironmentContext;
|
||||
import org.eclipse.che.commons.lang.IoUtil;
|
||||
import org.eclipse.che.commons.subject.SubjectImpl;
|
||||
import org.mockito.Mock;
|
||||
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 java.io.File;
|
||||
import java.net.URL;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.Paths;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.HashSet;
|
||||
import java.util.concurrent.ThreadPoolExecutor;
|
||||
|
||||
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.mock;
|
||||
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.assertNotNull;
|
||||
|
||||
/**
|
||||
* Unit tests for {@link MachineManager}
|
||||
*
|
||||
* @author Anton Korneta
|
||||
* @author Alexander Garagatyi
|
||||
*/
|
||||
@Listeners(MockitoTestNGListener.class)
|
||||
public class MachineManagerTest {
|
||||
|
||||
private static final int DEFAULT_MACHINE_MEMORY_SIZE_MB = 1000;
|
||||
private static final String WS_ID = "testWsId";
|
||||
private static final String ENVIRONMENT_NAME = "testEnvName";
|
||||
private static final String USER_ID = "userId";
|
||||
private static final String MACHINE_ID = "machineId";
|
||||
private static final String NAMESPACE = "namespace";
|
||||
|
||||
private static final SubjectImpl CREATOR = new SubjectImpl("name", USER_ID, "token", false);
|
||||
|
||||
@Mock
|
||||
private MachineInstanceProviders machineInstanceProviders;
|
||||
@Mock
|
||||
private InstanceProvider instanceProvider;
|
||||
@Mock
|
||||
private MachineRegistry machineRegistry;
|
||||
@Mock
|
||||
private WsAgentLauncher wsAgentLauncher;
|
||||
@Mock
|
||||
private Instance instance;
|
||||
@Mock
|
||||
private Limits limits;
|
||||
@Mock
|
||||
private Command command;
|
||||
@Mock
|
||||
private InstanceProcess instanceProcess;
|
||||
@Mock
|
||||
private LineConsumer logConsumer;
|
||||
@Mock
|
||||
private SnapshotDao snapshotDao;
|
||||
|
||||
private MachineManager manager;
|
||||
|
||||
@BeforeMethod
|
||||
public void setUp() throws Exception {
|
||||
final EventService eventService = mock(EventService.class);
|
||||
final String machineLogsDir = targetDir().resolve("logs-dir").toString();
|
||||
IoUtil.deleteRecursive(new File(machineLogsDir));
|
||||
manager = spy(new MachineManager(snapshotDao,
|
||||
machineRegistry,
|
||||
machineInstanceProviders,
|
||||
machineLogsDir,
|
||||
eventService,
|
||||
DEFAULT_MACHINE_MEMORY_SIZE_MB,
|
||||
wsAgentLauncher));
|
||||
|
||||
EnvironmentContext envCont = new EnvironmentContext();
|
||||
envCont.setSubject(CREATOR);
|
||||
EnvironmentContext.setCurrent(envCont);
|
||||
|
||||
RecipeImpl recipe = new RecipeImpl().withScript("script").withType("Dockerfile");
|
||||
// doNothing().when(manager).createMachineLogsDir(anyString());
|
||||
doReturn(MACHINE_ID).when(manager).generateMachineId();
|
||||
doReturn(logConsumer).when(manager).getProcessLogger(MACHINE_ID, 111, "outputChannel");
|
||||
when(machineInstanceProviders.getProvider(anyString())).thenReturn(instanceProvider);
|
||||
HashSet<String> recipeTypes = new HashSet<>();
|
||||
recipeTypes.add("test type 1");
|
||||
recipeTypes.add("snapshot");
|
||||
recipeTypes.add("dockerfile");
|
||||
when(instanceProvider.getRecipeTypes()).thenReturn(recipeTypes);
|
||||
when(instanceProvider.createInstance(any(Machine.class), any(LineConsumer.class))).thenReturn(instance);
|
||||
when(machineRegistry.getInstance(anyString())).thenReturn(instance);
|
||||
when(command.getCommandLine()).thenReturn("CommandLine");
|
||||
when(command.getName()).thenReturn("CommandName");
|
||||
when(command.getType()).thenReturn("CommandType");
|
||||
when(machineRegistry.getInstance(MACHINE_ID)).thenReturn(instance);
|
||||
when(instance.createProcess(command, "outputChannel")).thenReturn(instanceProcess);
|
||||
when(instanceProcess.getPid()).thenReturn(111);
|
||||
}
|
||||
|
||||
@AfterMethod
|
||||
public void tearDown() throws Exception {
|
||||
EnvironmentContext.reset();
|
||||
}
|
||||
|
||||
@Test(expectedExceptions = BadRequestException.class, expectedExceptionsMessageRegExp = "Invalid machine name @name!")
|
||||
public void shouldThrowExceptionOnMachineCreationIfMachineNameIsInvalid() throws Exception {
|
||||
MachineConfig machineConfig = new MachineConfigImpl(false,
|
||||
"@name!",
|
||||
"machineType",
|
||||
new MachineSourceImpl("Dockerfile").setLocation("location"),
|
||||
new LimitsImpl(1024),
|
||||
Arrays.asList(new ServerConfImpl("ref1",
|
||||
"8080",
|
||||
"https",
|
||||
"some/path"),
|
||||
new ServerConfImpl("ref2",
|
||||
"9090/udp",
|
||||
"someprotocol",
|
||||
"/some/path")),
|
||||
Collections.singletonMap("key1", "value1"));
|
||||
String workspaceId = "wsId";
|
||||
String environmentName = "env1";
|
||||
|
||||
manager.createMachineSync(machineConfig, workspaceId, environmentName, logConsumer);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldBeAbleToCreateMachineWithValidName() throws Exception {
|
||||
String expectedName = "validMachineName";
|
||||
final MachineConfigImpl machineConfig = MachineConfigImpl.builder()
|
||||
.fromConfig(createMachineConfig())
|
||||
.setName(expectedName)
|
||||
.build();
|
||||
MachineImpl expectedMachine = new MachineImpl(machineConfig,
|
||||
MACHINE_ID,
|
||||
WS_ID,
|
||||
ENVIRONMENT_NAME,
|
||||
USER_ID,
|
||||
MachineStatus.CREATING,
|
||||
null);
|
||||
|
||||
manager.createMachineSync(machineConfig, WS_ID, ENVIRONMENT_NAME, logConsumer);
|
||||
|
||||
verify(machineRegistry).addMachine(eq(expectedMachine));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldCallWsAgentLauncherAfterDevMachineStart() throws Exception {
|
||||
final MachineConfigImpl machineConfig = MachineConfigImpl.builder()
|
||||
.fromConfig(createMachineConfig())
|
||||
.setDev(true)
|
||||
.build();
|
||||
|
||||
manager.createMachineSync(machineConfig, WS_ID, ENVIRONMENT_NAME, logConsumer);
|
||||
|
||||
verify(wsAgentLauncher).startWsAgent(WS_ID);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldNotCallWsAgentLauncherAfterNonDevMachineStart() throws Exception {
|
||||
final MachineConfigImpl machineConfig = createMachineConfig();
|
||||
|
||||
manager.createMachineSync(machineConfig, WS_ID, ENVIRONMENT_NAME, logConsumer);
|
||||
|
||||
verify(wsAgentLauncher, never()).startWsAgent(WS_ID);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldRemoveMachineFromRegistryIfInstanceDestroyingFailsOnDestroy() throws Exception {
|
||||
final MachineConfigImpl machineConfig = createMachineConfig();
|
||||
when(instance.getConfig()).thenReturn(machineConfig);
|
||||
when(instance.getWorkspaceId()).thenReturn(WS_ID);
|
||||
doThrow(new MachineException("test")).when(instance).destroy();
|
||||
|
||||
try {
|
||||
manager.destroy(MACHINE_ID, false);
|
||||
} catch (Exception e) {
|
||||
verify(machineRegistry).remove(MACHINE_ID);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldCloseProcessLoggerIfExecIsSuccess() throws Exception {
|
||||
//when
|
||||
manager.exec(MACHINE_ID, command, "outputChannel");
|
||||
waitForExecutorIsCompletedTask();
|
||||
|
||||
//then
|
||||
verify(logConsumer).close();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldCloseProcessLoggerIfExecFails() throws Exception {
|
||||
//given
|
||||
doThrow(Exception.class).when(instanceProcess).start();
|
||||
|
||||
//when
|
||||
manager.exec(MACHINE_ID, command, "outputChannel");
|
||||
waitForExecutorIsCompletedTask();
|
||||
|
||||
//then
|
||||
verify(logConsumer).close();
|
||||
}
|
||||
|
||||
@Test(expectedExceptions = MachineException.class)
|
||||
public void shouldCloseMachineLoggerIfMachineCreationFails() throws Exception {
|
||||
//given
|
||||
MachineConfig machineConfig = mock(MachineConfig.class);
|
||||
MachineSource machineSource = mock(MachineSource.class);
|
||||
LineConsumer machineLogger = mock(LineConsumer.class);
|
||||
doReturn(machineLogger).when(manager).getMachineLogger(eq(MACHINE_ID), any(LineConsumer.class));
|
||||
when(machineConfig.getSource()).thenReturn(machineSource);
|
||||
when(machineConfig.getName()).thenReturn("Name");
|
||||
when(machineSource.getType()).thenReturn("dockerfile");
|
||||
doThrow(ConflictException.class).when(machineRegistry).addMachine(any());
|
||||
|
||||
//when
|
||||
manager.createMachineSync(machineConfig, "workspaceId", "environmentName", logConsumer);
|
||||
|
||||
//then
|
||||
verify(machineLogger).close();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldCreateMachineFromOriginalSourceWhenMachineRecoverFails() throws Exception {
|
||||
final SnapshotImpl snapshot = createSnapshot();
|
||||
final MachineConfigImpl config = createMachineConfig();
|
||||
when(manager.generateMachineId()).thenReturn(MACHINE_ID);
|
||||
final MachineImpl machine = new MachineImpl(config,
|
||||
MACHINE_ID,
|
||||
WS_ID,
|
||||
ENVIRONMENT_NAME,
|
||||
CREATOR.getUserId(),
|
||||
MachineStatus.CREATING,
|
||||
null);
|
||||
machine.getConfig().setSource(snapshot.getMachineSource());
|
||||
when(snapshotDao.getSnapshot(WS_ID, ENVIRONMENT_NAME, config.getName())).thenReturn(snapshot);
|
||||
when(instanceProvider.createInstance(eq(machine), any(LineConsumer.class))).thenThrow(new SourceNotFoundException(""));
|
||||
when(machineRegistry.getMachine(MACHINE_ID)).thenReturn(machine);
|
||||
|
||||
final MachineImpl result = manager.recoverMachine(config, WS_ID, ENVIRONMENT_NAME, logConsumer);
|
||||
|
||||
machine.getConfig().setSource(config.getSource());
|
||||
assertEquals(result, machine);
|
||||
}
|
||||
|
||||
@Test(expectedExceptions = MachineException.class)
|
||||
public void shouldThrowExceptionWhenMachineCreationFromOriginSourceFailed() throws Exception {
|
||||
final MachineConfigImpl config = createMachineConfig();
|
||||
when(manager.generateMachineId()).thenReturn(MACHINE_ID);
|
||||
|
||||
when(instanceProvider.createInstance(any(MachineImpl.class),
|
||||
any(LineConsumer.class))).thenThrow(new MachineException(""));
|
||||
|
||||
manager.recoverMachine(config, WS_ID, ENVIRONMENT_NAME, logConsumer);
|
||||
}
|
||||
|
||||
private void waitForExecutorIsCompletedTask() throws Exception {
|
||||
for (int i = 0; ((ThreadPoolExecutor)manager.executor).getCompletedTaskCount() == 0 && i < 10; i++) {
|
||||
Thread.sleep(300);
|
||||
}
|
||||
}
|
||||
|
||||
private static Path targetDir() throws Exception {
|
||||
final URL url = Thread.currentThread().getContextClassLoader().getResource(".");
|
||||
assertNotNull(url);
|
||||
return Paths.get(url.toURI()).getParent();
|
||||
}
|
||||
|
||||
private MachineConfigImpl createMachineConfig() {
|
||||
return new MachineConfigImpl(false,
|
||||
"MachineName",
|
||||
"docker",
|
||||
new MachineSourceImpl("Dockerfile").setLocation("location"),
|
||||
new LimitsImpl(1024),
|
||||
Arrays.asList(new ServerConfImpl("ref1",
|
||||
"8080",
|
||||
"https",
|
||||
"some/path"),
|
||||
new ServerConfImpl("ref2",
|
||||
"9090/udp",
|
||||
"someprotocol",
|
||||
"/some/path")),
|
||||
Collections.singletonMap("key1", "value1"));
|
||||
}
|
||||
|
||||
private SnapshotImpl createSnapshot() {
|
||||
return SnapshotImpl.builder()
|
||||
.setId("snapshot12")
|
||||
.fromConfig(createMachineConfig())
|
||||
.setWorkspaceId(WS_ID)
|
||||
.setEnvName(ENVIRONMENT_NAME)
|
||||
.setNamespace(NAMESPACE)
|
||||
.setMachineSource(new MachineSourceImpl("snapshot").setLocation("location"))
|
||||
.setDev(true)
|
||||
.build();
|
||||
}
|
||||
}
|
||||
|
|
@ -11,7 +11,6 @@
|
|||
package org.eclipse.che.api.workspace.shared.dto;
|
||||
|
||||
import org.eclipse.che.api.core.factory.FactoryParameter;
|
||||
import org.eclipse.che.api.core.model.machine.Recipe;
|
||||
import org.eclipse.che.api.core.model.workspace.Environment;
|
||||
import org.eclipse.che.api.machine.shared.dto.MachineConfigDto;
|
||||
import org.eclipse.che.dto.shared.DTO;
|
||||
|
|
|
|||
|
|
@ -53,6 +53,10 @@
|
|||
<groupId>javax.inject</groupId>
|
||||
<artifactId>javax.inject</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>javax.servlet</groupId>
|
||||
<artifactId>javax.servlet-api</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.eclipse.che.core</groupId>
|
||||
<artifactId>che-core-api-core</artifactId>
|
||||
|
|
@ -81,6 +85,10 @@
|
|||
<groupId>org.eclipse.che.core</groupId>
|
||||
<artifactId>che-core-commons-annotations</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.eclipse.che.core</groupId>
|
||||
<artifactId>che-core-commons-inject</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.eclipse.che.core</groupId>
|
||||
<artifactId>che-core-commons-lang</artifactId>
|
||||
|
|
@ -113,6 +121,11 @@
|
|||
<artifactId>rest-assured</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.eclipse.che.core</groupId>
|
||||
<artifactId>che-core-commons-test</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.everrest</groupId>
|
||||
<artifactId>everrest-assured</artifactId>
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@
|
|||
* Contributors:
|
||||
* Codenvy, S.A. - initial API and implementation
|
||||
*******************************************************************************/
|
||||
package org.eclipse.che.api.machine.server.terminal;
|
||||
package org.eclipse.che.api.agent.server.terminal;
|
||||
|
||||
import org.eclipse.che.api.machine.server.exception.MachineException;
|
||||
import org.eclipse.che.api.machine.server.spi.Instance;
|
||||
|
|
@ -8,14 +8,14 @@
|
|||
* Contributors:
|
||||
* Codenvy, S.A. - initial API and implementation
|
||||
*******************************************************************************/
|
||||
package org.eclipse.che.api.machine.server.terminal;
|
||||
package org.eclipse.che.api.agent.server.terminal;
|
||||
|
||||
import com.google.common.util.concurrent.ThreadFactoryBuilder;
|
||||
|
||||
import org.eclipse.che.api.core.NotFoundException;
|
||||
import org.eclipse.che.api.core.notification.EventService;
|
||||
import org.eclipse.che.api.core.notification.EventSubscriber;
|
||||
import org.eclipse.che.api.machine.server.MachineManager;
|
||||
import org.eclipse.che.api.environment.server.CheEnvironmentEngine;
|
||||
import org.eclipse.che.api.machine.server.exception.MachineException;
|
||||
import org.eclipse.che.api.machine.server.spi.Instance;
|
||||
import org.eclipse.che.api.machine.shared.dto.event.MachineStatusEvent;
|
||||
|
|
@ -43,19 +43,20 @@ public class MachineTerminalLauncher {
|
|||
private static final Logger LOG = LoggerFactory.getLogger(MachineTerminalLauncher.class);
|
||||
|
||||
private final EventService eventService;
|
||||
private final MachineManager machineManager;
|
||||
// TODO replace with WorkspaceManager
|
||||
private final CheEnvironmentEngine cheEnvironmentEngine;
|
||||
private final Map<String, MachineImplSpecificTerminalLauncher> terminalLaunchers;
|
||||
private final ExecutorService executor;
|
||||
|
||||
@Inject
|
||||
public MachineTerminalLauncher(EventService eventService,
|
||||
MachineManager machineManager,
|
||||
Set<MachineImplSpecificTerminalLauncher> machineImplLaunchers) {
|
||||
Set<MachineImplSpecificTerminalLauncher> machineImplLaunchers,
|
||||
CheEnvironmentEngine cheEnvironmentEngine) {
|
||||
this.eventService = eventService;
|
||||
this.machineManager = machineManager;
|
||||
this.terminalLaunchers = machineImplLaunchers.stream()
|
||||
.collect(toMap(MachineImplSpecificTerminalLauncher::getMachineType,
|
||||
Function.identity()));
|
||||
this.cheEnvironmentEngine = cheEnvironmentEngine;
|
||||
this.executor = Executors.newCachedThreadPool(new ThreadFactoryBuilder().setNameFormat("MachineTerminalLauncher-%d")
|
||||
.setDaemon(true)
|
||||
.build());
|
||||
|
|
@ -70,10 +71,11 @@ public class MachineTerminalLauncher {
|
|||
if (event.getEventType() == MachineStatusEvent.EventType.RUNNING) {
|
||||
executor.execute(() -> {
|
||||
try {
|
||||
final Instance machine = machineManager.getInstance(event.getMachineId());
|
||||
final Instance machine = cheEnvironmentEngine.getMachine(event.getWorkspaceId(),
|
||||
event.getMachineId());
|
||||
|
||||
MachineImplSpecificTerminalLauncher terminalLauncher = terminalLaunchers.get(machine.getConfig()
|
||||
.getType());
|
||||
MachineImplSpecificTerminalLauncher terminalLauncher =
|
||||
terminalLaunchers.get(machine.getConfig().getType());
|
||||
if (terminalLauncher == null) {
|
||||
LOG.warn("Terminal launcher implementation was not found for machine {} with type {}.",
|
||||
machine.getId(),
|
||||
|
|
@ -8,7 +8,7 @@
|
|||
* Contributors:
|
||||
* Codenvy, S.A. - initial API and implementation
|
||||
*******************************************************************************/
|
||||
package org.eclipse.che.api.machine.server.terminal;
|
||||
package org.eclipse.che.api.agent.server.terminal;
|
||||
|
||||
import org.eclipse.che.commons.annotation.Nullable;
|
||||
import org.eclipse.che.inject.ConfigurationProperties;
|
||||
|
|
@ -8,10 +8,11 @@
|
|||
* Contributors:
|
||||
* Codenvy, S.A. - initial API and implementation
|
||||
*******************************************************************************/
|
||||
package org.eclipse.che.api.machine.server.wsagent;
|
||||
package org.eclipse.che.api.agent.server.wsagent;
|
||||
|
||||
import org.eclipse.che.api.core.NotFoundException;
|
||||
import org.eclipse.che.api.machine.server.exception.MachineException;
|
||||
import org.eclipse.che.api.core.ServerException;
|
||||
import org.eclipse.che.api.core.model.machine.Machine;
|
||||
|
||||
/**
|
||||
* Starts ws agent in the machine and wait until ws agent sends notification about its start
|
||||
|
|
@ -19,5 +20,6 @@ import org.eclipse.che.api.machine.server.exception.MachineException;
|
|||
* @author Alexander Garagatyi
|
||||
*/
|
||||
public interface WsAgentLauncher {
|
||||
void startWsAgent(String workspaceId) throws NotFoundException, MachineException, InterruptedException;
|
||||
void startWsAgent(Machine devMachine) throws NotFoundException,
|
||||
ServerException;
|
||||
}
|
||||
|
|
@ -8,18 +8,18 @@
|
|||
* Contributors:
|
||||
* Codenvy, S.A. - initial API and implementation
|
||||
*******************************************************************************/
|
||||
package org.eclipse.che.api.machine.server.wsagent;
|
||||
package org.eclipse.che.api.agent.server.wsagent;
|
||||
|
||||
import org.eclipse.che.api.core.ApiException;
|
||||
import org.eclipse.che.api.core.BadRequestException;
|
||||
import org.eclipse.che.api.core.NotFoundException;
|
||||
import org.eclipse.che.api.core.ServerException;
|
||||
import org.eclipse.che.api.core.model.machine.Machine;
|
||||
import org.eclipse.che.api.core.model.machine.Server;
|
||||
import org.eclipse.che.api.core.rest.HttpJsonRequest;
|
||||
import org.eclipse.che.api.core.rest.HttpJsonRequestFactory;
|
||||
import org.eclipse.che.api.core.rest.HttpJsonResponse;
|
||||
import org.eclipse.che.api.machine.server.MachineManager;
|
||||
import org.eclipse.che.api.machine.server.exception.MachineException;
|
||||
import org.eclipse.che.api.environment.server.MachineProcessManager;
|
||||
import org.eclipse.che.api.machine.server.model.impl.CommandImpl;
|
||||
import org.eclipse.che.api.machine.shared.Constants;
|
||||
import org.slf4j.Logger;
|
||||
|
|
@ -49,23 +49,23 @@ public class WsAgentLauncherImpl implements WsAgentLauncher {
|
|||
private static final String WS_AGENT_PROCESS_OUTPUT_CHANNEL = "workspace:%s:ext-server:output";
|
||||
private static final String WS_AGENT_SERVER_NOT_FOUND_ERROR = "Workspace agent server not found in dev machine.";
|
||||
|
||||
private final Provider<MachineManager> machineManagerProvider;
|
||||
private final HttpJsonRequestFactory httpJsonRequestFactory;
|
||||
private final String wsAgentStartCommandLine;
|
||||
private final long wsAgentMaxStartTimeMs;
|
||||
private final long wsAgentPingDelayMs;
|
||||
private final int wsAgentPingConnectionTimeoutMs;
|
||||
private final String pingTimedOutErrorMessage;
|
||||
private final Provider<MachineProcessManager> machineProcessManagerProvider;
|
||||
private final HttpJsonRequestFactory httpJsonRequestFactory;
|
||||
private final String wsAgentStartCommandLine;
|
||||
private final long wsAgentMaxStartTimeMs;
|
||||
private final long wsAgentPingDelayMs;
|
||||
private final int wsAgentPingConnectionTimeoutMs;
|
||||
private final String pingTimedOutErrorMessage;
|
||||
|
||||
@Inject
|
||||
public WsAgentLauncherImpl(Provider<MachineManager> machineManagerProvider,
|
||||
public WsAgentLauncherImpl(Provider<MachineProcessManager> machineProcessManagerProvider,
|
||||
HttpJsonRequestFactory httpJsonRequestFactory,
|
||||
@Named(WS_AGENT_PROCESS_START_COMMAND) String wsAgentStartCommandLine,
|
||||
@Named("machine.ws_agent.max_start_time_ms") long wsAgentMaxStartTimeMs,
|
||||
@Named("machine.ws_agent.ping_delay_ms") long wsAgentPingDelayMs,
|
||||
@Named("machine.ws_agent.ping_conn_timeout_ms") int wsAgentPingConnectionTimeoutMs,
|
||||
@Named("machine.ws_agent.ping_timed_out_error_msg") String pingTimedOutErrorMessage) {
|
||||
this.machineManagerProvider = machineManagerProvider;
|
||||
this.machineProcessManagerProvider = machineProcessManagerProvider;
|
||||
this.httpJsonRequestFactory = httpJsonRequestFactory;
|
||||
this.wsAgentStartCommandLine = wsAgentStartCommandLine;
|
||||
this.wsAgentMaxStartTimeMs = wsAgentMaxStartTimeMs;
|
||||
|
|
@ -79,17 +79,20 @@ public class WsAgentLauncherImpl implements WsAgentLauncher {
|
|||
}
|
||||
|
||||
@Override
|
||||
public void startWsAgent(String workspaceId) throws NotFoundException, MachineException, InterruptedException {
|
||||
final Machine devMachine = getMachineManager().getDevMachine(workspaceId);
|
||||
public void startWsAgent(Machine devMachine) throws NotFoundException,
|
||||
ServerException {
|
||||
final HttpJsonRequest wsAgentPingRequest = createPingRequest(devMachine);
|
||||
final String wsAgentPingUrl = wsAgentPingRequest.getUrl();
|
||||
try {
|
||||
getMachineManager().exec(devMachine.getId(),
|
||||
new CommandImpl(WS_AGENT_PROCESS_NAME, wsAgentStartCommandLine, "Arbitrary"),
|
||||
getWsAgentProcessOutputChannel(workspaceId));
|
||||
machineProcessManagerProvider.get().exec(devMachine.getWorkspaceId(),
|
||||
devMachine.getId(),
|
||||
new CommandImpl(WS_AGENT_PROCESS_NAME,
|
||||
wsAgentStartCommandLine,
|
||||
"Arbitrary"),
|
||||
getWsAgentProcessOutputChannel(devMachine.getWorkspaceId()));
|
||||
final long pingStartTimestamp = System.currentTimeMillis();
|
||||
LOG.debug("Starts pinging ws agent. Workspace ID:{}. Url:{}. Timestamp:{}",
|
||||
workspaceId,
|
||||
devMachine.getWorkspaceId(),
|
||||
wsAgentPingUrl,
|
||||
pingStartTimestamp);
|
||||
|
||||
|
|
@ -101,20 +104,23 @@ public class WsAgentLauncherImpl implements WsAgentLauncher {
|
|||
}
|
||||
}
|
||||
} catch (BadRequestException wsAgentLaunchingExc) {
|
||||
throw new MachineException(wsAgentLaunchingExc.getLocalizedMessage(), wsAgentLaunchingExc);
|
||||
throw new ServerException(wsAgentLaunchingExc.getLocalizedMessage(), wsAgentLaunchingExc);
|
||||
} catch (InterruptedException e) {
|
||||
Thread.currentThread().interrupt();
|
||||
throw new ServerException("Ws agent pinging is interrupted");
|
||||
}
|
||||
LOG.error("Fail pinging ws agent. Workspace ID:{}. Url:{}. Timestamp:{}", workspaceId, wsAgentPingUrl);
|
||||
throw new MachineException(pingTimedOutErrorMessage);
|
||||
LOG.error("Fail pinging ws agent. Workspace ID:{}. Url:{}. Timestamp:{}", devMachine.getWorkspaceId(), wsAgentPingUrl);
|
||||
throw new ServerException(pingTimedOutErrorMessage);
|
||||
}
|
||||
|
||||
// forms the ping request based on information about the machine.
|
||||
protected HttpJsonRequest createPingRequest(Machine machine) throws MachineException {
|
||||
protected HttpJsonRequest createPingRequest(Machine machine) throws ServerException {
|
||||
Map<String, ? extends Server> servers = machine.getRuntime().getServers();
|
||||
Server wsAgentServer = servers.get(Constants.WS_AGENT_PORT);
|
||||
if (wsAgentServer == null) {
|
||||
LOG.error("{} WorkspaceId: {}, DevMachine Id: {}, found servers: {}",
|
||||
WS_AGENT_SERVER_NOT_FOUND_ERROR, machine.getWorkspaceId(), machine.getId(), servers);
|
||||
throw new MachineException(WS_AGENT_SERVER_NOT_FOUND_ERROR);
|
||||
throw new ServerException(WS_AGENT_SERVER_NOT_FOUND_ERROR);
|
||||
}
|
||||
String wsAgentPingUrl = wsAgentServer.getUrl();
|
||||
// since everrest mapped on the slash in case of it absence
|
||||
|
|
@ -127,7 +133,7 @@ public class WsAgentLauncherImpl implements WsAgentLauncher {
|
|||
.setTimeout(wsAgentPingConnectionTimeoutMs);
|
||||
}
|
||||
|
||||
private boolean pingWsAgent(HttpJsonRequest wsAgentPingRequest) throws MachineException {
|
||||
private boolean pingWsAgent(HttpJsonRequest wsAgentPingRequest) throws ServerException {
|
||||
try {
|
||||
final HttpJsonResponse pingResponse = wsAgentPingRequest.request();
|
||||
if (pingResponse.getResponseCode() == HttpURLConnection.HTTP_OK) {
|
||||
|
|
@ -137,8 +143,4 @@ public class WsAgentLauncherImpl implements WsAgentLauncher {
|
|||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private MachineManager getMachineManager() {
|
||||
return machineManagerProvider.get();
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,955 @@
|
|||
/*******************************************************************************
|
||||
* Copyright (c) 2012-2016 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.environment.server;
|
||||
|
||||
import com.google.common.annotations.VisibleForTesting;
|
||||
import com.google.common.base.Strings;
|
||||
|
||||
import org.eclipse.che.api.core.ApiException;
|
||||
import org.eclipse.che.api.core.ConflictException;
|
||||
import org.eclipse.che.api.core.NotFoundException;
|
||||
import org.eclipse.che.api.core.ServerException;
|
||||
import org.eclipse.che.api.core.model.machine.MachineConfig;
|
||||
import org.eclipse.che.api.core.model.machine.MachineLogMessage;
|
||||
import org.eclipse.che.api.core.model.machine.MachineSource;
|
||||
import org.eclipse.che.api.core.model.machine.MachineStatus;
|
||||
import org.eclipse.che.api.core.model.workspace.Environment;
|
||||
import org.eclipse.che.api.core.notification.EventService;
|
||||
import org.eclipse.che.api.core.notification.EventSubscriber;
|
||||
import org.eclipse.che.api.core.util.AbstractLineConsumer;
|
||||
import org.eclipse.che.api.core.util.CompositeLineConsumer;
|
||||
import org.eclipse.che.api.core.util.FileLineConsumer;
|
||||
import org.eclipse.che.api.core.util.LineConsumer;
|
||||
import org.eclipse.che.api.core.util.MessageConsumer;
|
||||
import org.eclipse.che.api.environment.server.exception.EnvironmentNotRunningException;
|
||||
import org.eclipse.che.api.machine.server.MachineInstanceProviders;
|
||||
import org.eclipse.che.api.machine.server.dao.SnapshotDao;
|
||||
import org.eclipse.che.api.machine.server.event.InstanceStateEvent;
|
||||
import org.eclipse.che.api.machine.server.exception.MachineException;
|
||||
import org.eclipse.che.api.machine.server.exception.SourceNotFoundException;
|
||||
import org.eclipse.che.api.machine.server.model.impl.LimitsImpl;
|
||||
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.MachineLogMessageImpl;
|
||||
import org.eclipse.che.api.machine.server.model.impl.MachineSourceImpl;
|
||||
import org.eclipse.che.api.machine.server.model.impl.SnapshotImpl;
|
||||
import org.eclipse.che.api.machine.server.spi.Instance;
|
||||
import org.eclipse.che.api.machine.server.spi.InstanceProvider;
|
||||
import org.eclipse.che.api.machine.shared.dto.event.MachineStatusEvent;
|
||||
import org.eclipse.che.api.workspace.server.StripedLocks;
|
||||
import org.eclipse.che.commons.env.EnvironmentContext;
|
||||
import org.eclipse.che.commons.lang.IoUtil;
|
||||
import org.eclipse.che.commons.lang.NameGenerator;
|
||||
import org.slf4j.Logger;
|
||||
|
||||
import javax.annotation.PostConstruct;
|
||||
import javax.annotation.PreDestroy;
|
||||
import javax.inject.Inject;
|
||||
import javax.inject.Named;
|
||||
import javax.inject.Singleton;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayDeque;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.Queue;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.CopyOnWriteArrayList;
|
||||
import java.util.function.Predicate;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import static java.lang.String.format;
|
||||
import static org.eclipse.che.api.machine.server.event.InstanceStateEvent.Type.DIE;
|
||||
import static org.eclipse.che.api.machine.server.event.InstanceStateEvent.Type.OOM;
|
||||
import static org.eclipse.che.dto.server.DtoFactory.newDto;
|
||||
import static org.slf4j.LoggerFactory.getLogger;
|
||||
|
||||
/**
|
||||
* Facade for implementation specific operations with environment runtimes.
|
||||
*
|
||||
* @author Alexander Garagatyi
|
||||
* @author Yevhenii Voevodin
|
||||
*/
|
||||
@Singleton
|
||||
public class CheEnvironmentEngine {
|
||||
|
||||
private static final Logger LOG = getLogger(CheEnvironmentEngine.class);
|
||||
|
||||
private final Map<String, EnvironmentHolder> environments;
|
||||
private final StripedLocks stripedLocks;
|
||||
private final SnapshotDao snapshotDao;
|
||||
private final File machineLogsDir;
|
||||
private final MachineInstanceProviders machineInstanceProviders;
|
||||
private final int defaultMachineMemorySizeMB;
|
||||
private final EventService eventService;
|
||||
|
||||
private volatile boolean isPreDestroyInvoked;
|
||||
|
||||
@Inject
|
||||
public CheEnvironmentEngine(SnapshotDao snapshotDao,
|
||||
MachineInstanceProviders machineInstanceProviders,
|
||||
@Named("machine.logs.location") String machineLogsDir,
|
||||
@Named("machine.default_mem_size_mb") int defaultMachineMemorySizeMB,
|
||||
EventService eventService) {
|
||||
this.eventService = eventService;
|
||||
this.environments = new ConcurrentHashMap<>();
|
||||
this.snapshotDao = snapshotDao;
|
||||
this.machineInstanceProviders = machineInstanceProviders;
|
||||
this.machineLogsDir = new File(machineLogsDir);
|
||||
this.defaultMachineMemorySizeMB = defaultMachineMemorySizeMB;
|
||||
// 16 - experimental value for stripes count, it comes from default hash map size
|
||||
this.stripedLocks = new StripedLocks(16);
|
||||
eventService.subscribe(new MachineCleaner());
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns all machines from environment of specific workspace.
|
||||
*
|
||||
* @param workspaceId
|
||||
* ID of workspace that owns environment machines
|
||||
* @return list of machines
|
||||
* @throws EnvironmentNotRunningException
|
||||
* if environment is not running
|
||||
*/
|
||||
public List<Instance> getMachines(String workspaceId) throws EnvironmentNotRunningException {
|
||||
EnvironmentHolder environment;
|
||||
try (StripedLocks.ReadLock lock = stripedLocks.acquireReadLock(workspaceId)) {
|
||||
environment = environments.get(workspaceId);
|
||||
if (environment == null) {
|
||||
throw new EnvironmentNotRunningException("Environment with ID '" + workspaceId + "' is not found");
|
||||
}
|
||||
return new ArrayList<>(environment.machines);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns specific machine from environment of specific workspace.
|
||||
*
|
||||
* @param workspaceId
|
||||
* ID of workspace that owns environment machines
|
||||
* @param machineId
|
||||
* ID of requested machine
|
||||
* @return requested machine
|
||||
* @throws EnvironmentNotRunningException
|
||||
* if environment is not running
|
||||
* @throws NotFoundException
|
||||
* if machine is not found in the environment
|
||||
*/
|
||||
public Instance getMachine(String workspaceId, String machineId) throws NotFoundException {
|
||||
EnvironmentHolder environment;
|
||||
try (StripedLocks.ReadLock lock = stripedLocks.acquireReadLock(workspaceId)) {
|
||||
environment = environments.get(workspaceId);
|
||||
}
|
||||
if (environment == null) {
|
||||
throw new EnvironmentNotRunningException("Environment with ID '" + workspaceId + "' is not found");
|
||||
}
|
||||
return environment.machines.stream()
|
||||
.filter(instance -> instance.getId().equals(machineId))
|
||||
.findAny()
|
||||
.orElseThrow(() -> new NotFoundException(
|
||||
format("Machine with ID '%s' is not found in the environment of workspace '%s'",
|
||||
machineId, workspaceId)));
|
||||
}
|
||||
|
||||
/**
|
||||
* Starts provided environment.
|
||||
* <p/>
|
||||
* Environment starts if and only all machines in environment definition start successfully.<br/>
|
||||
* Otherwise exception is thrown by this method.<br/>
|
||||
* It is not defined whether environment start fails right after first failure or in the end of the process.<br/>
|
||||
* Starting order of machines is not guarantied. Machines can start sequentially or in parallel.
|
||||
*
|
||||
* @param workspaceId
|
||||
* ID of workspace that owns provided environment
|
||||
* @param env
|
||||
* environment to start
|
||||
* @param recover
|
||||
* whether machines from environment should be recovered or not
|
||||
* @param messageConsumer
|
||||
* consumer of log messages from machines in the environment
|
||||
* @return list of running machines of this environment
|
||||
* @throws ServerException
|
||||
* if other error occurs
|
||||
*/
|
||||
public List<Instance> start(String workspaceId,
|
||||
Environment env,
|
||||
boolean recover,
|
||||
MessageConsumer<MachineLogMessage> messageConsumer) throws ServerException,
|
||||
ConflictException {
|
||||
|
||||
// Create a new start queue with a dev machine in the queue head
|
||||
List<MachineConfigImpl> startConfigs = env.getMachineConfigs()
|
||||
.stream()
|
||||
.map(MachineConfigImpl::new)
|
||||
.collect(Collectors.toList());
|
||||
final MachineConfigImpl devCfg = removeFirstMatching(startConfigs, MachineConfig::isDev);
|
||||
startConfigs.add(0, devCfg);
|
||||
|
||||
EnvironmentHolder environmentHolder = new EnvironmentHolder(new ArrayDeque<>(startConfigs),
|
||||
new CopyOnWriteArrayList<>(),
|
||||
messageConsumer,
|
||||
EnvStatus.STARTING,
|
||||
env.getName());
|
||||
|
||||
try (StripedLocks.WriteLock lock = stripedLocks.acquireWriteLock(workspaceId)) {
|
||||
if (environments.putIfAbsent(workspaceId, environmentHolder) != null) {
|
||||
throw new ConflictException(format("Environment of workspace '%s' already exists", workspaceId));
|
||||
}
|
||||
}
|
||||
|
||||
startQueue(workspaceId, env.getName(), recover, messageConsumer);
|
||||
|
||||
try (StripedLocks.WriteLock lock = stripedLocks.acquireWriteLock(workspaceId)) {
|
||||
environmentHolder = environments.get(workspaceId);
|
||||
// possible only if environment was stopped during its start
|
||||
if (environmentHolder == null) {
|
||||
throw new ServerException("Environment start was interrupted by environment stopping");
|
||||
}
|
||||
environmentHolder.status = EnvStatus.RUNNING;
|
||||
return new ArrayList<>(environmentHolder.machines);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Stops running environment of specified workspace.
|
||||
*
|
||||
* @param workspaceId
|
||||
* ID of workspace that owns environment
|
||||
* @throws EnvironmentNotRunningException
|
||||
* when environment is not running
|
||||
* @throws ServerException
|
||||
* if other error occurs
|
||||
*/
|
||||
public void stop(String workspaceId) throws EnvironmentNotRunningException,
|
||||
ServerException {
|
||||
List<Instance> machinesCopy = null;
|
||||
try (StripedLocks.WriteLock lock = stripedLocks.acquireWriteLock(workspaceId)) {
|
||||
EnvironmentHolder environmentHolder = environments.get(workspaceId);
|
||||
if (environmentHolder == null || environmentHolder.status != EnvStatus.RUNNING) {
|
||||
throw new EnvironmentNotRunningException("Environment with ID '" + workspaceId + "' is not found");
|
||||
}
|
||||
environments.remove(workspaceId);
|
||||
List<Instance> machines = environmentHolder.machines;
|
||||
if (machines != null && !machines.isEmpty()) {
|
||||
machinesCopy = new ArrayList<>(machines);
|
||||
}
|
||||
}
|
||||
|
||||
// long operation - perform out of lock
|
||||
if (machinesCopy != null) {
|
||||
stopMachines(machinesCopy);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Starts machine in running environment.
|
||||
*
|
||||
* @param workspaceId
|
||||
* ID of workspace that owns environment in which machine should be started
|
||||
* @param machineConfig
|
||||
* configuration of machine that should be started
|
||||
* @return running machine
|
||||
* @throws EnvironmentNotRunningException
|
||||
* if environment is not running
|
||||
* @throws ConflictException
|
||||
* if machine with the same name already exists in the environment
|
||||
* @throws ServerException
|
||||
* if any other error occurs
|
||||
*/
|
||||
public Instance startMachine(String workspaceId,
|
||||
MachineConfig machineConfig) throws ServerException,
|
||||
EnvironmentNotRunningException,
|
||||
ConflictException {
|
||||
MachineConfig machineConfigCopy = new MachineConfigImpl(machineConfig);
|
||||
EnvironmentHolder environmentHolder;
|
||||
try (StripedLocks.ReadLock lock = stripedLocks.acquireReadLock(workspaceId)) {
|
||||
environmentHolder = environments.get(workspaceId);
|
||||
if (environmentHolder == null || environmentHolder.status != EnvStatus.RUNNING) {
|
||||
throw new EnvironmentNotRunningException(format("Environment '%s' is not running", workspaceId));
|
||||
}
|
||||
for (Instance machine : environmentHolder.machines) {
|
||||
if (machine.getConfig().getName().equals(machineConfigCopy.getName())) {
|
||||
throw new ConflictException(
|
||||
format("Machine with name '%s' already exists in environment of workspace '%s'",
|
||||
machineConfigCopy.getName(), workspaceId));
|
||||
}
|
||||
}
|
||||
}
|
||||
String machineId = generateMachineId();
|
||||
final String creator = EnvironmentContext.getCurrent().getSubject().getUserId();
|
||||
|
||||
Instance instance = null;
|
||||
try {
|
||||
addMachine(workspaceId,
|
||||
machineId,
|
||||
environmentHolder.name,
|
||||
creator,
|
||||
machineConfigCopy);
|
||||
|
||||
eventService.publish(newDto(MachineStatusEvent.class)
|
||||
.withEventType(MachineStatusEvent.EventType.CREATING)
|
||||
.withDev(machineConfigCopy.isDev())
|
||||
.withMachineName(machineConfigCopy.getName())
|
||||
.withMachineId(machineId)
|
||||
.withWorkspaceId(workspaceId));
|
||||
|
||||
instance = startMachineInstance(machineConfigCopy,
|
||||
workspaceId,
|
||||
machineId,
|
||||
environmentHolder.name,
|
||||
creator,
|
||||
false,
|
||||
environmentHolder.logger);
|
||||
|
||||
replaceMachine(instance);
|
||||
|
||||
eventService.publish(newDto(MachineStatusEvent.class)
|
||||
.withEventType(MachineStatusEvent.EventType.RUNNING)
|
||||
.withDev(machineConfigCopy.isDev())
|
||||
.withMachineName(machineConfigCopy.getName())
|
||||
.withMachineId(instance.getId())
|
||||
.withWorkspaceId(workspaceId));
|
||||
|
||||
return instance;
|
||||
} catch (Exception e) {
|
||||
removeMachine(workspaceId, machineId);
|
||||
|
||||
if (instance != null) {
|
||||
try {
|
||||
instance.destroy();
|
||||
} catch (Exception destroyingExc) {
|
||||
LOG.error(destroyingExc.getLocalizedMessage(), destroyingExc);
|
||||
}
|
||||
}
|
||||
|
||||
eventService.publish(newDto(MachineStatusEvent.class)
|
||||
.withEventType(MachineStatusEvent.EventType.ERROR)
|
||||
.withDev(machineConfigCopy.isDev())
|
||||
.withMachineName(machineConfigCopy.getName())
|
||||
.withMachineId(machineId)
|
||||
.withWorkspaceId(workspaceId));
|
||||
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Stops machine in running environment.
|
||||
*
|
||||
* @param workspaceId
|
||||
* ID of workspace of environment that owns machine
|
||||
* @param machineId
|
||||
* ID of machine that should be stopped
|
||||
* @throws NotFoundException
|
||||
* if machine in not found in environment
|
||||
* @throws EnvironmentNotRunningException
|
||||
* if environment is not running
|
||||
* @throws ConflictException
|
||||
* if stop of dev machine is requested
|
||||
* @throws ServerException
|
||||
* if other error occurs
|
||||
*/
|
||||
public void stopMachine(String workspaceId, String machineId) throws NotFoundException,
|
||||
ServerException,
|
||||
ConflictException {
|
||||
Instance targetMachine = null;
|
||||
try (StripedLocks.WriteLock lock = stripedLocks.acquireWriteLock(workspaceId)) {
|
||||
EnvironmentHolder environmentHolder = environments.get(workspaceId);
|
||||
if (environmentHolder == null || environmentHolder.status != EnvStatus.RUNNING) {
|
||||
throw new EnvironmentNotRunningException(format("Environment '%s' is not running", workspaceId));
|
||||
}
|
||||
for (Instance machine : environmentHolder.machines) {
|
||||
if (machine.getId().equals(machineId)) {
|
||||
if (machine.getConfig().isDev()) {
|
||||
throw new ConflictException(
|
||||
"Stop of dev machine is not allowed. Please, stop whole environment");
|
||||
}
|
||||
targetMachine = machine;
|
||||
break;
|
||||
}
|
||||
}
|
||||
environmentHolder.machines.remove(targetMachine);
|
||||
}
|
||||
if (targetMachine == null) {
|
||||
throw new NotFoundException(format("Machine with ID '%s' is not found in environment of workspace '%s'",
|
||||
machineId, workspaceId));
|
||||
}
|
||||
|
||||
// out of lock to prevent blocking on event processing by subscribers
|
||||
eventService.publish(newDto(MachineStatusEvent.class)
|
||||
.withEventType(MachineStatusEvent.EventType.DESTROYING)
|
||||
.withDev(targetMachine.getConfig().isDev())
|
||||
.withMachineName(targetMachine.getConfig().getName())
|
||||
.withMachineId(machineId)
|
||||
.withWorkspaceId(workspaceId));
|
||||
|
||||
targetMachine.destroy();
|
||||
|
||||
eventService.publish(newDto(MachineStatusEvent.class)
|
||||
.withEventType(MachineStatusEvent.EventType.DESTROYED)
|
||||
.withDev(targetMachine.getConfig().isDev())
|
||||
.withMachineName(targetMachine.getConfig().getName())
|
||||
.withMachineId(machineId)
|
||||
.withWorkspaceId(workspaceId));
|
||||
}
|
||||
|
||||
/**
|
||||
* Saves machine into snapshot.
|
||||
*
|
||||
* @param namespace namespace of the workspace
|
||||
* @param workspaceId ID of workspace that owns environment
|
||||
* @param machineId ID of machine to save
|
||||
* @return snapshot
|
||||
* @throws EnvironmentNotRunningException
|
||||
* if environment of machine is not running
|
||||
* @throws NotFoundException
|
||||
* if machine is not running
|
||||
* @throws ServerException
|
||||
* if another error occurs
|
||||
*/
|
||||
public SnapshotImpl saveSnapshot(String namespace,
|
||||
String workspaceId,
|
||||
String machineId) throws ServerException,
|
||||
NotFoundException {
|
||||
EnvironmentHolder environmentHolder;
|
||||
SnapshotImpl snapshot = null;
|
||||
Instance instance = null;
|
||||
try (StripedLocks.ReadLock lock = stripedLocks.acquireReadLock(workspaceId)) {
|
||||
environmentHolder = environments.get(workspaceId);
|
||||
if (environmentHolder == null || environmentHolder.status != EnvStatus.RUNNING) {
|
||||
throw new EnvironmentNotRunningException(format("Environment '%s' is not running", workspaceId));
|
||||
}
|
||||
for (Instance machine : environmentHolder.machines) {
|
||||
if (machine.getId().equals(machineId)) {
|
||||
instance = machine;
|
||||
snapshot = SnapshotImpl.builder()
|
||||
.generateId()
|
||||
.setType(machine.getConfig().getType())
|
||||
.setNamespace(namespace)
|
||||
.setWorkspaceId(machine.getWorkspaceId())
|
||||
.setDescription(machine.getEnvName())
|
||||
.setDev(machine.getConfig().isDev())
|
||||
.setEnvName(machine.getEnvName())
|
||||
.setMachineName(machine.getConfig().getName())
|
||||
.useCurrentCreationDate()
|
||||
.build();
|
||||
}
|
||||
}
|
||||
}
|
||||
if (instance == null) {
|
||||
throw new NotFoundException(format("Machine with id '%s' is not found in environment of workspace '%s'",
|
||||
machineId, workspaceId));
|
||||
}
|
||||
try {
|
||||
MachineSource machineSource = instance.saveToSnapshot();
|
||||
snapshot.setMachineSource(machineSource);
|
||||
return snapshot;
|
||||
} catch (ServerException e) {
|
||||
try {
|
||||
instance.getLogger().writeLine("Snapshot storing failed. " + e.getLocalizedMessage());
|
||||
} catch (IOException ignore) {
|
||||
}
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes snapshot of machine.
|
||||
*
|
||||
* @param snapshot
|
||||
* description of snapshot that should be removed
|
||||
* @throws ServerException
|
||||
* if error occurs on snapshot removal
|
||||
*/
|
||||
public void removeSnapshot(SnapshotImpl snapshot) throws ServerException {
|
||||
final String instanceType = snapshot.getType();
|
||||
try {
|
||||
final InstanceProvider instanceProvider = machineInstanceProviders.getProvider(instanceType);
|
||||
instanceProvider.removeInstanceSnapshot(snapshot.getMachineSource());
|
||||
} catch (NotFoundException e) {
|
||||
throw new ServerException(e.getLocalizedMessage(), e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Starts all machine from machine queue of environment.
|
||||
*/
|
||||
private void startQueue(String workspaceId,
|
||||
String envName,
|
||||
boolean recover,
|
||||
MessageConsumer<MachineLogMessage> messageConsumer) throws ServerException {
|
||||
// Starting all machines in environment one by one by getting configs
|
||||
// from the corresponding starting queue.
|
||||
// Config will be null only if there are no machines left in the queue
|
||||
MachineConfigImpl config = queuePeekOrFail(workspaceId);
|
||||
while (config != null) {
|
||||
// Environment start is failed when any machine start is failed, so if any error
|
||||
// occurs during machine creation then environment start fail is reported and
|
||||
// start resources such as queue and descriptor must be cleaned up
|
||||
try {
|
||||
String machineId = generateMachineId();
|
||||
final String creator = EnvironmentContext.getCurrent().getSubject().getUserId();
|
||||
|
||||
addMachine(workspaceId,
|
||||
machineId,
|
||||
envName,
|
||||
creator,
|
||||
config);
|
||||
|
||||
eventService.publish(newDto(MachineStatusEvent.class)
|
||||
.withEventType(MachineStatusEvent.EventType.CREATING)
|
||||
.withDev(config.isDev())
|
||||
.withMachineName(config.getName())
|
||||
.withWorkspaceId(workspaceId)
|
||||
.withMachineId(machineId));
|
||||
|
||||
Instance machine = startMachineInstance(config,
|
||||
workspaceId,
|
||||
machineId,
|
||||
envName,
|
||||
creator,
|
||||
recover,
|
||||
messageConsumer);
|
||||
|
||||
// Machine destroying is an expensive operation which must be
|
||||
// performed outside of the lock, this section checks if
|
||||
// the environment wasn't stopped while it is starting and sets
|
||||
// polled flag to true if the environment wasn't stopped.
|
||||
// Also polls the proceeded machine configuration from the queue
|
||||
boolean queuePolled = false;
|
||||
try (StripedLocks.WriteLock lock = stripedLocks.acquireWriteLock(workspaceId)) {
|
||||
ensurePreDestroyIsNotExecuted();
|
||||
EnvironmentHolder environmentHolder = environments.get(workspaceId);
|
||||
if (environmentHolder != null) {
|
||||
final Queue<MachineConfigImpl> queue = environmentHolder.startQueue;
|
||||
if (queue != null) {
|
||||
queue.poll();
|
||||
queuePolled = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// If machine config is not polled from the queue
|
||||
// then environment was stopped and newly created machine
|
||||
// must be destroyed
|
||||
if (!queuePolled) {
|
||||
try {
|
||||
eventService.publish(newDto(MachineStatusEvent.class)
|
||||
.withEventType(MachineStatusEvent.EventType.DESTROYING)
|
||||
.withDev(config.isDev())
|
||||
.withMachineName(config.getName())
|
||||
.withMachineId(machine.getId())
|
||||
.withWorkspaceId(workspaceId));
|
||||
|
||||
machine.destroy();
|
||||
|
||||
eventService.publish(newDto(MachineStatusEvent.class)
|
||||
.withEventType(MachineStatusEvent.EventType.DESTROYED)
|
||||
.withDev(config.isDev())
|
||||
.withMachineName(config.getName())
|
||||
.withMachineId(machine.getId())
|
||||
.withWorkspaceId(workspaceId));
|
||||
} catch (MachineException e) {
|
||||
LOG.error(e.getLocalizedMessage(), e);
|
||||
}
|
||||
throw new ServerException("Workspace '" + workspaceId +
|
||||
"' start interrupted. Workspace stopped before all its machines started");
|
||||
}
|
||||
|
||||
replaceMachine(machine);
|
||||
|
||||
eventService.publish(newDto(MachineStatusEvent.class)
|
||||
.withEventType(MachineStatusEvent.EventType.RUNNING)
|
||||
.withDev(config.isDev())
|
||||
.withMachineName(config.getName())
|
||||
.withMachineId(machine.getId())
|
||||
.withWorkspaceId(workspaceId));
|
||||
|
||||
config = queuePeekOrFail(workspaceId);
|
||||
} catch (RuntimeException | ServerException e) {
|
||||
EnvironmentHolder env;
|
||||
try (StripedLocks.WriteLock lock = stripedLocks.acquireWriteLock(workspaceId)) {
|
||||
env = environments.remove(workspaceId);
|
||||
}
|
||||
try {
|
||||
stopMachines(env.machines);
|
||||
} catch (Exception remEx) {
|
||||
LOG.error(remEx.getLocalizedMessage(), remEx);
|
||||
}
|
||||
throw new ServerException(e.getLocalizedMessage(), e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void addMachine(String workspaceId,
|
||||
String machineId,
|
||||
String envName,
|
||||
String creator,
|
||||
MachineConfig machineConfig) throws ServerException {
|
||||
Instance machine = new NoOpMachineInstance(MachineImpl.builder()
|
||||
.setConfig(machineConfig)
|
||||
.setId(machineId)
|
||||
.setWorkspaceId(workspaceId)
|
||||
.setStatus(MachineStatus.CREATING)
|
||||
.setEnvName(envName)
|
||||
.setOwner(creator)
|
||||
.build());
|
||||
try (StripedLocks.WriteLock lock = stripedLocks.acquireWriteLock(workspaceId)) {
|
||||
ensurePreDestroyIsNotExecuted();
|
||||
EnvironmentHolder environmentHolder = environments.get(workspaceId);
|
||||
if (environmentHolder != null && environmentHolder.status != EnvStatus.STOPPING) {
|
||||
environmentHolder.machines.add(machine);
|
||||
} else {
|
||||
throw new ServerException(
|
||||
format("Can't add machine into environment. Environment of workspace '%s' is missing",
|
||||
workspaceId));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void removeMachine(String workspaceId,
|
||||
String machineId) {
|
||||
try (StripedLocks.WriteLock lock = stripedLocks.acquireWriteLock(workspaceId)) {
|
||||
EnvironmentHolder environmentHolder = environments.get(workspaceId);
|
||||
if (environmentHolder != null) {
|
||||
removeFirstMatching(environmentHolder.machines, m -> m.getId().equals(machineId));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void replaceMachine(Instance machine) throws ServerException {
|
||||
try (StripedLocks.WriteLock lock = stripedLocks.acquireWriteLock(machine.getWorkspaceId())) {
|
||||
ensurePreDestroyIsNotExecuted();
|
||||
EnvironmentHolder environmentHolder = environments.get(machine.getWorkspaceId());
|
||||
if (environmentHolder != null) {
|
||||
for (int i = 0; i < environmentHolder.machines.size(); i++) {
|
||||
if (environmentHolder.machines.get(i).getId().equals(machine.getId())) {
|
||||
environmentHolder.machines.set(i, machine);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// if this area is reachable then environment/machine is not found and machine should be stopped
|
||||
try {
|
||||
machine.destroy();
|
||||
} catch (MachineException e) {
|
||||
LOG.error(e.getLocalizedMessage(), e);
|
||||
}
|
||||
// should not happen
|
||||
throw new ServerException(format(
|
||||
"Machine with ID '%s' and name '%s' has been stopped because its configuration is not found in the environment of workspace '%s'",
|
||||
machine.getId(), machine.getConfig().getName(), machine.getWorkspaceId()));
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets head config from the queue associated with the given {@code workspaceId}.
|
||||
*
|
||||
* <p>Note that this method won't actually poll the queue.
|
||||
*
|
||||
* <p>Fails if environment start was interrupted by stop(queue doesn't exist).
|
||||
*
|
||||
* @return machine config which is in the queue head, or null
|
||||
* if there are no machine configs left
|
||||
* @throws ServerException
|
||||
* if queue doesn't exist which means that {@link #stop(String)} executed
|
||||
* before all the machines started
|
||||
* @throws ServerException
|
||||
* if pre destroy has been invoked before peek config retrieved
|
||||
*/
|
||||
private MachineConfigImpl queuePeekOrFail(String workspaceId) throws ServerException {
|
||||
try (StripedLocks.ReadLock lock = stripedLocks.acquireReadLock(workspaceId)) {
|
||||
ensurePreDestroyIsNotExecuted();
|
||||
EnvironmentHolder environmentHolder = environments.get(workspaceId);
|
||||
if (environmentHolder == null || environmentHolder.startQueue == null) {
|
||||
throw new ServerException("Workspace " + workspaceId +
|
||||
" start interrupted. Workspace was stopped before all its machines were started");
|
||||
}
|
||||
return environmentHolder.startQueue.peek();
|
||||
}
|
||||
}
|
||||
|
||||
private Instance startMachineInstance(MachineConfig originMachineConfig,
|
||||
String workspaceId,
|
||||
String machineId,
|
||||
String environmentName,
|
||||
String creator,
|
||||
boolean recover,
|
||||
MessageConsumer<MachineLogMessage> environmentLogger)
|
||||
throws ServerException {
|
||||
|
||||
final MachineImpl machine = new MachineImpl(originMachineConfig,
|
||||
machineId,
|
||||
workspaceId,
|
||||
environmentName,
|
||||
creator,
|
||||
MachineStatus.CREATING,
|
||||
null);
|
||||
if ("recipe".equalsIgnoreCase(machine.getConfig().getSource().getType())) {
|
||||
machine.getConfig().getSource().setType("dockerfile");
|
||||
}
|
||||
if (originMachineConfig.getLimits().getRam() == 0) {
|
||||
machine.getConfig().setLimits(new LimitsImpl(defaultMachineMemorySizeMB));
|
||||
}
|
||||
final MachineSourceImpl sourceCopy = machine.getConfig().getSource();
|
||||
|
||||
LineConsumer machineLogger = null;
|
||||
try {
|
||||
machineLogger = getMachineLogger(environmentLogger, machineId, originMachineConfig.getName());
|
||||
if (recover) {
|
||||
final SnapshotImpl snapshot = snapshotDao.getSnapshot(workspaceId,
|
||||
environmentName,
|
||||
machine.getConfig().getName());
|
||||
machine.getConfig().setSource(snapshot.getMachineSource());
|
||||
}
|
||||
|
||||
final InstanceProvider instanceProvider =
|
||||
machineInstanceProviders.getProvider(machine.getConfig().getType());
|
||||
|
||||
if (!instanceProvider.getRecipeTypes().contains(machine.getConfig()
|
||||
.getSource()
|
||||
.getType()
|
||||
.toLowerCase())) {
|
||||
throw new ServerException(format("Recipe type %s of %s machine is unsupported",
|
||||
machine.getConfig().getSource().getType(),
|
||||
machine.getConfig().getName()));
|
||||
}
|
||||
|
||||
try {
|
||||
Instance instance;
|
||||
try {
|
||||
instance = instanceProvider.createInstance(machine, machineLogger);
|
||||
} catch (SourceNotFoundException e) {
|
||||
if (recover) {
|
||||
LOG.error("Image of snapshot for machine " + machine.getConfig().getName() + " not found. " +
|
||||
"Machine will be created from origin source");
|
||||
machine.getConfig().setSource(sourceCopy);
|
||||
instance = instanceProvider.createInstance(machine, machineLogger);
|
||||
} else {
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
instance.setStatus(MachineStatus.RUNNING);
|
||||
return instance;
|
||||
} catch (ApiException creationEx) {
|
||||
try {
|
||||
machineLogger.writeLine("[ERROR] " + creationEx.getLocalizedMessage());
|
||||
} catch (IOException ioEx) {
|
||||
LOG.error(ioEx.getLocalizedMessage());
|
||||
}
|
||||
|
||||
throw new MachineException(creationEx.getLocalizedMessage(), creationEx);
|
||||
}
|
||||
} catch (ApiException apiEx) {
|
||||
if (machineLogger != null) {
|
||||
try {
|
||||
machineLogger.close();
|
||||
} catch (IOException ioEx) {
|
||||
LOG.error(ioEx.getLocalizedMessage(), ioEx);
|
||||
}
|
||||
}
|
||||
|
||||
throw new MachineException(apiEx.getLocalizedMessage(), apiEx);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Stops workspace by destroying all its machines and removing it from in memory storage.
|
||||
*/
|
||||
private void stopMachines(List<Instance> machines) {
|
||||
for (Instance machine : machines) {
|
||||
try {
|
||||
machine.destroy();
|
||||
} catch (RuntimeException | MachineException ex) {
|
||||
LOG.error(format("Could not destroy machine '%s' of workspace '%s'",
|
||||
machine.getId(),
|
||||
machine.getWorkspaceId()),
|
||||
ex);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
@PostConstruct
|
||||
private void createLogsDir() {
|
||||
if (!(machineLogsDir.exists() || machineLogsDir.mkdirs())) {
|
||||
throw new IllegalStateException("Unable create directory " + machineLogsDir.getAbsolutePath());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes all descriptors from the in-memory storage, while
|
||||
* {@link MachineProcessManager#cleanup()} is responsible for machines destroying.
|
||||
*/
|
||||
@PreDestroy
|
||||
@VisibleForTesting
|
||||
void cleanup() {
|
||||
isPreDestroyInvoked = true;
|
||||
final java.io.File[] files = machineLogsDir.listFiles();
|
||||
if (files != null && files.length > 0) {
|
||||
for (java.io.File f : files) {
|
||||
if (!IoUtil.deleteRecursive(f)) {
|
||||
LOG.warn("Failed delete {}", f);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private LineConsumer getMachineLogger(MessageConsumer<MachineLogMessage> environmentLogger,
|
||||
String machineId,
|
||||
String machineName) throws ServerException {
|
||||
createMachineLogsDir(machineId);
|
||||
|
||||
LineConsumer lineConsumer = new AbstractLineConsumer() {
|
||||
@Override
|
||||
public void writeLine(String line) throws IOException {
|
||||
environmentLogger.consume(new MachineLogMessageImpl(machineName, line));
|
||||
}
|
||||
};
|
||||
try {
|
||||
return new CompositeLineConsumer(new FileLineConsumer(getMachineLogsFile(machineId)),
|
||||
lineConsumer);
|
||||
} catch (IOException e) {
|
||||
throw new MachineException(format("Unable create log file '%s' for machine '%s'.",
|
||||
e.getLocalizedMessage(),
|
||||
machineId));
|
||||
}
|
||||
}
|
||||
|
||||
private void createMachineLogsDir(String machineId) throws MachineException {
|
||||
File dir = new File(machineLogsDir, machineId);
|
||||
if (!dir.exists() && !dir.mkdirs()) {
|
||||
throw new MachineException("Can't create folder for the logs of machine");
|
||||
}
|
||||
}
|
||||
|
||||
private File getMachineLogsFile(String machineId) {
|
||||
return new File(new File(machineLogsDir, machineId), "machineId.logs");
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
String generateMachineId() {
|
||||
return NameGenerator.generate("machine", 16);
|
||||
}
|
||||
|
||||
private static <T> T removeFirstMatching(List<? extends T> elements, Predicate<T> predicate) {
|
||||
T element = null;
|
||||
for (final Iterator<? extends T> it = elements.iterator(); it.hasNext() && element == null; ) {
|
||||
final T next = it.next();
|
||||
if (predicate.test(next)) {
|
||||
element = next;
|
||||
it.remove();
|
||||
}
|
||||
}
|
||||
return element;
|
||||
}
|
||||
|
||||
private void ensurePreDestroyIsNotExecuted() throws ServerException {
|
||||
if (isPreDestroyInvoked) {
|
||||
throw new ServerException("Could not perform operation because application server is stopping");
|
||||
}
|
||||
}
|
||||
|
||||
private enum EnvStatus {
|
||||
STARTING,
|
||||
RUNNING,
|
||||
STOPPING
|
||||
}
|
||||
|
||||
private static class EnvironmentHolder {
|
||||
Queue<MachineConfigImpl> startQueue;
|
||||
List<Instance> machines;
|
||||
EnvStatus status;
|
||||
MessageConsumer<MachineLogMessage> logger;
|
||||
String name;
|
||||
|
||||
EnvironmentHolder(Queue<MachineConfigImpl> startQueue,
|
||||
List<Instance> machines,
|
||||
MessageConsumer<MachineLogMessage> envLogger,
|
||||
EnvStatus envStatus,
|
||||
String name) {
|
||||
this.startQueue = startQueue;
|
||||
this.machines = machines;
|
||||
this.logger = envLogger;
|
||||
this.status = envStatus;
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public EnvironmentHolder(EnvironmentHolder environmentHolder) {
|
||||
this.startQueue = environmentHolder.startQueue;
|
||||
this.machines = environmentHolder.machines;
|
||||
this.logger = environmentHolder.logger;
|
||||
this.status = environmentHolder.status;
|
||||
this.name = environmentHolder.name;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) return true;
|
||||
if (!(o instanceof EnvironmentHolder)) return false;
|
||||
EnvironmentHolder that = (EnvironmentHolder)o;
|
||||
return Objects.equals(startQueue, that.startQueue) &&
|
||||
Objects.equals(machines, that.machines) &&
|
||||
status == that.status &&
|
||||
Objects.equals(logger, that.logger) &&
|
||||
Objects.equals(name, that.name);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(startQueue, machines, status, logger, name);
|
||||
}
|
||||
}
|
||||
|
||||
// cleanup machine if event about instance failure comes
|
||||
private class MachineCleaner implements EventSubscriber<InstanceStateEvent> {
|
||||
@Override
|
||||
public void onEvent(InstanceStateEvent event) {
|
||||
if ((event.getType() == OOM) || (event.getType() == DIE)) {
|
||||
EnvironmentHolder environmentHolder;
|
||||
try (StripedLocks.ReadLock lock = stripedLocks.acquireReadLock("workspaceId")) {
|
||||
environmentHolder = environments.get(event.getWorkspaceId());
|
||||
}
|
||||
if (environmentHolder != null) {
|
||||
for (Instance instance : environmentHolder.machines) {
|
||||
if (instance.getId().equals(event.getMachineId())) {
|
||||
String message = "Machine is destroyed. ";
|
||||
if (event.getType() == OOM) {
|
||||
message = message +
|
||||
"The processes in this machine need more RAM. This machine started with " +
|
||||
instance.getConfig().getLimits().getRam() +
|
||||
"MB. Create a new machine configuration that allocates additional RAM or increase " +
|
||||
"the workspace RAM limit in the user dashboard.";
|
||||
}
|
||||
|
||||
try {
|
||||
if (!Strings.isNullOrEmpty(message)) {
|
||||
instance.getLogger().writeLine(message);
|
||||
}
|
||||
} catch (IOException ignore) {
|
||||
}
|
||||
|
||||
eventService.publish(newDto(MachineStatusEvent.class)
|
||||
.withEventType(MachineStatusEvent.EventType.DESTROYED)
|
||||
.withDev(instance.getConfig().isDev())
|
||||
.withMachineId(instance.getId())
|
||||
.withWorkspaceId(instance.getWorkspaceId())
|
||||
.withMachineName(instance.getConfig().getName()));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,159 @@
|
|||
/*******************************************************************************
|
||||
* Copyright (c) 2012-2016 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.environment.server;
|
||||
|
||||
import com.google.common.base.Joiner;
|
||||
|
||||
import org.eclipse.che.api.core.model.machine.MachineConfig;
|
||||
import org.eclipse.che.api.core.model.machine.ServerConf;
|
||||
import org.eclipse.che.api.core.model.workspace.Environment;
|
||||
import org.eclipse.che.api.machine.server.MachineInstanceProviders;
|
||||
|
||||
import javax.inject.Inject;
|
||||
import java.net.MalformedURLException;
|
||||
import java.net.URL;
|
||||
import java.util.Map;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
import static com.google.common.base.Strings.isNullOrEmpty;
|
||||
import static java.lang.String.format;
|
||||
|
||||
/**
|
||||
* Validates description of environment of workspace.
|
||||
*
|
||||
* @author Alexander Garagatyi
|
||||
*/
|
||||
public class CheEnvironmentValidator {
|
||||
/* machine name must contain only {a-zA-Z0-9_-} characters and it's needed for validation machine names */
|
||||
private static final Pattern MACHINE_NAME_PATTERN = Pattern.compile("^/?[a-zA-Z0-9_-]+$");
|
||||
private static final Pattern SERVER_PORT = Pattern.compile("[1-9]+[0-9]*/(?:tcp|udp)");
|
||||
private static final Pattern SERVER_PROTOCOL = Pattern.compile("[a-z][a-z0-9-+.]*");
|
||||
|
||||
private final MachineInstanceProviders machineInstanceProviders;
|
||||
|
||||
@Inject
|
||||
public CheEnvironmentValidator(MachineInstanceProviders machineInstanceProviders) {
|
||||
this.machineInstanceProviders = machineInstanceProviders;
|
||||
}
|
||||
|
||||
public void validate(Environment env) throws IllegalArgumentException {
|
||||
String envName = env.getName();
|
||||
checkArgument(envName != null && !envName.isEmpty(),
|
||||
"Environment name should not be neither null nor empty");
|
||||
checkArgument(env.getMachineConfigs() != null && !env.getMachineConfigs().isEmpty(),
|
||||
"Environment '%s' should contain at least 1 machine",
|
||||
envName);
|
||||
|
||||
final long devCount = env.getMachineConfigs()
|
||||
.stream()
|
||||
.filter(MachineConfig::isDev)
|
||||
.count();
|
||||
checkArgument(devCount == 1,
|
||||
"Environment '%s' should contain exactly 1 dev machine, but contains '%d'",
|
||||
envName,
|
||||
devCount);
|
||||
for (MachineConfig machineCfg : env.getMachineConfigs()) {
|
||||
validateMachine(machineCfg, envName);
|
||||
}
|
||||
}
|
||||
|
||||
private void validateMachine(MachineConfig machineCfg, String envName) throws IllegalArgumentException {
|
||||
String machineName = machineCfg.getName();
|
||||
checkArgument(!isNullOrEmpty(machineName), "Environment '%s' contains machine with null or empty name", envName);
|
||||
checkArgument(MACHINE_NAME_PATTERN.matcher(machineName).matches(),
|
||||
"Environment '%s' contains machine with invalid name '%s'", envName, machineName);
|
||||
checkNotNull(machineCfg.getSource(), "Machine '%s' in environment '%s' doesn't have source", machineName, envName);
|
||||
checkArgument(machineCfg.getSource().getContent() != null || machineCfg.getSource().getLocation() != null,
|
||||
"Source of machine '%s' in environment '%s' must contain location or content", machineName, envName);
|
||||
checkArgument(machineCfg.getSource().getContent() == null || machineCfg.getSource().getLocation() == null,
|
||||
"Source of machine '%s' in environment '%s' contains mutually exclusive fields location and content",
|
||||
machineName, envName);
|
||||
checkArgument(machineInstanceProviders.hasProvider(machineCfg.getType()),
|
||||
"Type '%s' of machine '%s' in environment '%s' is not supported. Supported values are: %s.",
|
||||
machineCfg.getType(),
|
||||
machineName,
|
||||
envName,
|
||||
Joiner.on(", ").join(machineInstanceProviders.getProviderTypes()));
|
||||
|
||||
if (machineCfg.getSource().getType().equals("dockerfile") && machineCfg.getSource().getLocation() != null) {
|
||||
try {
|
||||
final String protocol = new URL(machineCfg.getSource().getLocation()).getProtocol();
|
||||
checkArgument(protocol.equals("http") || protocol.equals("https"),
|
||||
"Environment '%s' contains machine '%s' with invalid source location protocol: %s",
|
||||
envName,
|
||||
machineName,
|
||||
machineCfg.getSource().getLocation());
|
||||
} catch (MalformedURLException e) {
|
||||
throw new IllegalArgumentException(format("Environment '%s' contains machine '%s' with invalid source location: '%s'",
|
||||
envName,
|
||||
machineName,
|
||||
machineCfg.getSource().getLocation()));
|
||||
}
|
||||
}
|
||||
for (ServerConf serverConf : machineCfg.getServers()) {
|
||||
checkArgument(serverConf.getPort() != null && SERVER_PORT.matcher(serverConf.getPort()).matches(),
|
||||
"Machine '%s' in environment '%s' contains server conf with invalid port '%s'",
|
||||
machineName,
|
||||
envName,
|
||||
serverConf.getPort());
|
||||
checkArgument(serverConf.getProtocol() == null || SERVER_PROTOCOL.matcher(serverConf.getProtocol()).matches(),
|
||||
"Machine '%s' in environment '%s' contains server conf with invalid protocol '%s'",
|
||||
machineName,
|
||||
envName,
|
||||
serverConf.getProtocol());
|
||||
}
|
||||
for (Map.Entry<String, String> envVariable : machineCfg.getEnvVariables().entrySet()) {
|
||||
checkArgument(!isNullOrEmpty(envVariable.getKey()),
|
||||
"Machine '%s' in environment '%s' contains environment variable with null or empty name",
|
||||
machineName,
|
||||
envName);
|
||||
checkNotNull(envVariable.getValue(),
|
||||
"Machine '%s' in environment '%s' contains environment variable '%s' with null value",
|
||||
machineName,
|
||||
envName,
|
||||
envVariable.getKey());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks that object reference is not null, throws {@link IllegalArgumentException} otherwise.
|
||||
*
|
||||
* <p>Exception uses error message built from error message template and error message parameters.
|
||||
*/
|
||||
private static void checkNotNull(Object object, String errorMessageTemplate, Object... errorMessageParams) {
|
||||
if (object == null) {
|
||||
throw new IllegalArgumentException(format(errorMessageTemplate, errorMessageParams));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks that expression is true, throws {@link IllegalArgumentException} otherwise.
|
||||
*
|
||||
* <p>Exception uses error message built from error message template and error message parameters.
|
||||
*/
|
||||
private static void checkArgument(boolean expression, String errorMessage) {
|
||||
if (!expression) {
|
||||
throw new IllegalArgumentException(errorMessage);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks that expression is true, throws {@link IllegalArgumentException} otherwise.
|
||||
*
|
||||
* <p>Exception uses error message built from error message template and error message parameters.
|
||||
*/
|
||||
private static void checkArgument(boolean expression, String errorMessageTemplate, Object... errorMessageParams)
|
||||
throws IllegalArgumentException {
|
||||
if (!expression) {
|
||||
throw new IllegalArgumentException(format(errorMessageTemplate, errorMessageParams));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,282 @@
|
|||
/*******************************************************************************
|
||||
* Copyright (c) 2012-2016 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.environment.server;
|
||||
|
||||
import com.google.common.annotations.VisibleForTesting;
|
||||
import com.google.common.util.concurrent.ThreadFactoryBuilder;
|
||||
|
||||
import org.eclipse.che.api.core.BadRequestException;
|
||||
import org.eclipse.che.api.core.ConflictException;
|
||||
import org.eclipse.che.api.core.ForbiddenException;
|
||||
import org.eclipse.che.api.core.NotFoundException;
|
||||
import org.eclipse.che.api.core.model.machine.Command;
|
||||
import org.eclipse.che.api.core.notification.EventService;
|
||||
import org.eclipse.che.api.core.util.CompositeLineConsumer;
|
||||
import org.eclipse.che.api.core.util.FileLineConsumer;
|
||||
import org.eclipse.che.api.core.util.LineConsumer;
|
||||
import org.eclipse.che.api.core.util.WebsocketLineConsumer;
|
||||
import org.eclipse.che.api.machine.server.exception.MachineException;
|
||||
import org.eclipse.che.api.machine.server.spi.Instance;
|
||||
import org.eclipse.che.api.machine.server.spi.InstanceProcess;
|
||||
import org.eclipse.che.api.machine.shared.dto.event.MachineProcessEvent;
|
||||
import org.eclipse.che.commons.annotation.Nullable;
|
||||
import org.eclipse.che.commons.lang.concurrent.ThreadLocalPropagateContext;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import javax.annotation.PreDestroy;
|
||||
import javax.inject.Inject;
|
||||
import javax.inject.Named;
|
||||
import javax.inject.Singleton;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.io.Reader;
|
||||
import java.nio.charset.Charset;
|
||||
import java.nio.file.Files;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.ExecutorService;
|
||||
import java.util.concurrent.Executors;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import static org.eclipse.che.dto.server.DtoFactory.newDto;
|
||||
|
||||
/**
|
||||
* Facade for Machine process operations.
|
||||
*
|
||||
* @author gazarenkov
|
||||
* @author Alexander Garagatyi
|
||||
* @author Yevhenii Voevodin
|
||||
*/
|
||||
@Singleton
|
||||
public class MachineProcessManager {
|
||||
private static final Logger LOG = LoggerFactory.getLogger(MachineProcessManager.class);
|
||||
|
||||
private final File machineLogsDir;
|
||||
private final CheEnvironmentEngine environmentEngine;
|
||||
private final EventService eventService;
|
||||
|
||||
@VisibleForTesting
|
||||
final ExecutorService executor;
|
||||
|
||||
@Inject
|
||||
public MachineProcessManager(@Named("machine.logs.location") String machineLogsDir,
|
||||
EventService eventService,
|
||||
CheEnvironmentEngine environmentEngine) {
|
||||
this.eventService = eventService;
|
||||
this.machineLogsDir = new File(machineLogsDir);
|
||||
this.environmentEngine = environmentEngine;
|
||||
|
||||
executor = Executors.newCachedThreadPool(new ThreadFactoryBuilder().setNameFormat("MachineProcessManager-%d")
|
||||
.setDaemon(false)
|
||||
.build());
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute a command in machine
|
||||
*
|
||||
* @param machineId
|
||||
* id of the machine where command should be executed
|
||||
* @param command
|
||||
* command that should be executed in the machine
|
||||
* @return {@link org.eclipse.che.api.machine.server.spi.InstanceProcess} that represents started process in machine
|
||||
* @throws NotFoundException
|
||||
* if machine with specified id not found
|
||||
* @throws BadRequestException
|
||||
* if value of required parameter is invalid
|
||||
* @throws MachineException
|
||||
* if other error occur
|
||||
*/
|
||||
public InstanceProcess exec(String workspaceId,
|
||||
String machineId,
|
||||
Command command,
|
||||
@Nullable String outputChannel)
|
||||
throws NotFoundException, MachineException, BadRequestException {
|
||||
requiredNotNull(machineId, "Machine ID is required");
|
||||
requiredNotNull(command, "Command is required");
|
||||
requiredNotNull(command.getCommandLine(), "Command line is required");
|
||||
requiredNotNull(command.getName(), "Command name is required");
|
||||
requiredNotNull(command.getType(), "Command type is required");
|
||||
|
||||
final Instance machine = environmentEngine.getMachine(workspaceId, machineId);
|
||||
final InstanceProcess instanceProcess = machine.createProcess(command, outputChannel);
|
||||
final int pid = instanceProcess.getPid();
|
||||
|
||||
final LineConsumer processLogger = getProcessLogger(machineId, pid, outputChannel);
|
||||
|
||||
executor.execute(ThreadLocalPropagateContext.wrap(() -> {
|
||||
try {
|
||||
eventService.publish(newDto(MachineProcessEvent.class)
|
||||
.withEventType(MachineProcessEvent.EventType.STARTED)
|
||||
.withMachineId(machineId)
|
||||
.withProcessId(pid));
|
||||
|
||||
instanceProcess.start(processLogger);
|
||||
|
||||
eventService.publish(newDto(MachineProcessEvent.class)
|
||||
.withEventType(MachineProcessEvent.EventType.STOPPED)
|
||||
.withMachineId(machineId)
|
||||
.withProcessId(pid));
|
||||
} catch (ConflictException | MachineException error) {
|
||||
eventService.publish(newDto(MachineProcessEvent.class)
|
||||
.withEventType(MachineProcessEvent.EventType.ERROR)
|
||||
.withMachineId(machineId)
|
||||
.withProcessId(pid)
|
||||
.withError(error.getLocalizedMessage()));
|
||||
|
||||
try {
|
||||
processLogger.writeLine(String.format("[ERROR] %s", error.getMessage()));
|
||||
} catch (IOException ignored) {
|
||||
}
|
||||
} finally {
|
||||
try {
|
||||
processLogger.close();
|
||||
} catch (IOException ignored) {
|
||||
}
|
||||
}
|
||||
}));
|
||||
return instanceProcess;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get list of active processes from specific machine
|
||||
*
|
||||
* @param machineId
|
||||
* id of machine to get processes information from
|
||||
* @return list of {@link org.eclipse.che.api.machine.server.spi.InstanceProcess}
|
||||
* @throws NotFoundException
|
||||
* if machine with specified id not found
|
||||
* @throws MachineException
|
||||
* if other error occur
|
||||
*/
|
||||
public List<InstanceProcess> getProcesses(String workspaceId, String machineId) throws NotFoundException, MachineException {
|
||||
return environmentEngine.getMachine(workspaceId, machineId).getProcesses();
|
||||
}
|
||||
|
||||
/**
|
||||
* Stop process in machine
|
||||
*
|
||||
* @param machineId
|
||||
* if of the machine where process should be stopped
|
||||
* @param pid
|
||||
* id of the process that should be stopped in machine
|
||||
* @throws NotFoundException
|
||||
* if machine or process with specified id not found
|
||||
* @throws ForbiddenException
|
||||
* if process is finished already
|
||||
* @throws MachineException
|
||||
* if other error occur
|
||||
*/
|
||||
public void stopProcess(String workspaceId,
|
||||
String machineId,
|
||||
int pid) throws NotFoundException, MachineException, ForbiddenException {
|
||||
final InstanceProcess process = environmentEngine.getMachine(workspaceId, machineId).getProcess(pid);
|
||||
if (!process.isAlive()) {
|
||||
throw new ForbiddenException("Process finished already");
|
||||
}
|
||||
|
||||
process.kill();
|
||||
|
||||
eventService.publish(newDto(MachineProcessEvent.class)
|
||||
.withEventType(MachineProcessEvent.EventType.STOPPED)
|
||||
.withMachineId(machineId)
|
||||
.withProcessId(pid));
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets process reader from machine by specified id.
|
||||
*
|
||||
* @param machineId
|
||||
* machine id whose process reader will be returned
|
||||
* @param pid
|
||||
* process id
|
||||
* @return reader for specified process on machine
|
||||
* @throws NotFoundException
|
||||
* if machine with specified id not found
|
||||
* @throws MachineException
|
||||
* if other error occur
|
||||
*/
|
||||
public Reader getProcessLogReader(String machineId, int pid) throws NotFoundException, MachineException {
|
||||
final File processLogsFile = getProcessLogsFile(machineId, pid);
|
||||
if (processLogsFile.isFile()) {
|
||||
try {
|
||||
return Files.newBufferedReader(processLogsFile.toPath(), Charset.defaultCharset());
|
||||
} catch (IOException e) {
|
||||
throw new MachineException(
|
||||
String.format("Unable read log file for process '%s' of machine '%s'. %s", pid, machineId, e.getMessage()));
|
||||
}
|
||||
}
|
||||
throw new NotFoundException(String.format("Logs for process '%s' of machine '%s' are not available", pid, machineId));
|
||||
}
|
||||
|
||||
private File getProcessLogsFile(String machineId, int pid) {
|
||||
return new File(new File(machineLogsDir, machineId), Integer.toString(pid));
|
||||
}
|
||||
|
||||
private FileLineConsumer getProcessFileLogger(String machineId, int pid) throws MachineException {
|
||||
try {
|
||||
return new FileLineConsumer(getProcessLogsFile(machineId, pid));
|
||||
} catch (IOException e) {
|
||||
throw new MachineException(
|
||||
String.format("Unable create log file for process '%s' of machine '%s'. %s", pid, machineId, e.getMessage()));
|
||||
}
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
LineConsumer getProcessLogger(String machineId, int pid, String outputChannel) throws MachineException {
|
||||
return getLogger(getProcessFileLogger(machineId, pid), outputChannel);
|
||||
}
|
||||
|
||||
private LineConsumer getLogger(LineConsumer fileLogger, String outputChannel) throws MachineException {
|
||||
if (outputChannel != null) {
|
||||
return new CompositeLineConsumer(fileLogger, new WebsocketLineConsumer(outputChannel));
|
||||
}
|
||||
return fileLogger;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks object reference is not {@code null}
|
||||
*
|
||||
* @param object
|
||||
* object reference to check
|
||||
* @param message
|
||||
* used as subject of exception message "{subject} required"
|
||||
* @throws org.eclipse.che.api.core.BadRequestException
|
||||
* when object reference is {@code null}
|
||||
*/
|
||||
private void requiredNotNull(Object object, String message) throws BadRequestException {
|
||||
if (object == null) {
|
||||
throw new BadRequestException(message + " required");
|
||||
}
|
||||
}
|
||||
|
||||
@PreDestroy
|
||||
private void cleanup() {
|
||||
boolean interrupted = false;
|
||||
|
||||
executor.shutdown();
|
||||
|
||||
try {
|
||||
if (!executor.awaitTermination(10, TimeUnit.SECONDS)) {
|
||||
executor.shutdownNow();
|
||||
if (!executor.awaitTermination(10, TimeUnit.SECONDS)) {
|
||||
LOG.warn("Unable terminate main pool");
|
||||
}
|
||||
}
|
||||
} catch (InterruptedException e) {
|
||||
interrupted = true;
|
||||
executor.shutdownNow();
|
||||
}
|
||||
|
||||
if (interrupted) {
|
||||
Thread.currentThread().interrupt();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,333 @@
|
|||
/*******************************************************************************
|
||||
* Copyright (c) 2012-2016 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.environment.server;
|
||||
|
||||
import io.swagger.annotations.Api;
|
||||
import io.swagger.annotations.ApiOperation;
|
||||
import io.swagger.annotations.ApiParam;
|
||||
import io.swagger.annotations.ApiResponse;
|
||||
import io.swagger.annotations.ApiResponses;
|
||||
|
||||
import com.google.common.io.CharStreams;
|
||||
|
||||
import org.eclipse.che.api.core.BadRequestException;
|
||||
import org.eclipse.che.api.core.ConflictException;
|
||||
import org.eclipse.che.api.core.ForbiddenException;
|
||||
import org.eclipse.che.api.core.NotFoundException;
|
||||
import org.eclipse.che.api.core.ServerException;
|
||||
import org.eclipse.che.api.core.model.machine.Machine;
|
||||
import org.eclipse.che.api.core.rest.Service;
|
||||
import org.eclipse.che.api.machine.server.DtoConverter;
|
||||
import org.eclipse.che.api.machine.shared.dto.CommandDto;
|
||||
import org.eclipse.che.api.machine.shared.dto.MachineConfigDto;
|
||||
import org.eclipse.che.api.machine.shared.dto.MachineDto;
|
||||
import org.eclipse.che.api.machine.shared.dto.MachineProcessDto;
|
||||
import org.eclipse.che.api.workspace.server.WorkspaceManager;
|
||||
import org.eclipse.che.api.workspace.server.model.impl.WorkspaceImpl;
|
||||
|
||||
import javax.inject.Inject;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
import javax.ws.rs.Consumes;
|
||||
import javax.ws.rs.DELETE;
|
||||
import javax.ws.rs.GET;
|
||||
import javax.ws.rs.POST;
|
||||
import javax.ws.rs.Path;
|
||||
import javax.ws.rs.PathParam;
|
||||
import javax.ws.rs.Produces;
|
||||
import javax.ws.rs.QueryParam;
|
||||
import javax.ws.rs.core.Context;
|
||||
import javax.ws.rs.core.MediaType;
|
||||
import java.io.IOException;
|
||||
import java.io.Reader;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* Machine API
|
||||
*
|
||||
* @author Alexander Garagatyi
|
||||
* @author Anton Korneta
|
||||
*/
|
||||
@Api(value = "/machine", description = "Machine REST API")
|
||||
@Path("/workspace/{workspaceId}/machine")
|
||||
public class MachineService extends Service {
|
||||
private final MachineProcessManager machineProcessManager;
|
||||
private final MachineServiceLinksInjector linksInjector;
|
||||
private final WorkspaceManager workspaceManager;
|
||||
|
||||
@Inject
|
||||
public MachineService(MachineProcessManager machineProcessManager,
|
||||
MachineServiceLinksInjector linksInjector,
|
||||
WorkspaceManager workspaceManager) {
|
||||
this.machineProcessManager = machineProcessManager;
|
||||
this.linksInjector = linksInjector;
|
||||
this.workspaceManager = workspaceManager;
|
||||
}
|
||||
|
||||
@GET
|
||||
@Path("/{machineId}")
|
||||
@Produces(MediaType.APPLICATION_JSON)
|
||||
@ApiOperation(value = "Get machine by ID")
|
||||
@ApiResponses({@ApiResponse(code = 200, message = "The response contains requested machine entity"),
|
||||
@ApiResponse(code = 404, message = "Machine with specified id does not exist"),
|
||||
@ApiResponse(code = 500, message = "Internal server error occurred")})
|
||||
@Deprecated
|
||||
public MachineDto getMachineById(@ApiParam(value = "Workspace ID")
|
||||
@PathParam("workspaceId")
|
||||
String workspaceId,
|
||||
@ApiParam(value = "Machine ID")
|
||||
@PathParam("machineId")
|
||||
String machineId)
|
||||
throws ServerException,
|
||||
ForbiddenException,
|
||||
NotFoundException {
|
||||
|
||||
final Machine machine = workspaceManager.getMachineInstance(workspaceId, machineId);
|
||||
return linksInjector.injectLinks(DtoConverter.asDto(machine), getServiceContext());
|
||||
}
|
||||
|
||||
@GET
|
||||
@Produces(MediaType.APPLICATION_JSON)
|
||||
@ApiOperation(value = "Get all machines of workspace with specified ID",
|
||||
response = MachineDto.class,
|
||||
responseContainer = "List")
|
||||
@ApiResponses({@ApiResponse(code = 200, message = "The response contains requested list of machine entities"),
|
||||
@ApiResponse(code = 400, message = "Workspace ID is not specified"),
|
||||
@ApiResponse(code = 500, message = "Internal server error occurred")})
|
||||
@Deprecated
|
||||
public List<MachineDto> getMachines(@ApiParam(value = "Workspace ID")
|
||||
@PathParam("workspaceId")
|
||||
String workspaceId)
|
||||
throws ServerException,
|
||||
BadRequestException,
|
||||
NotFoundException {
|
||||
|
||||
requiredNotNull(workspaceId, "Parameter workspace");
|
||||
|
||||
WorkspaceImpl workspace = workspaceManager.getWorkspace(workspaceId);
|
||||
if (workspace.getRuntime() == null) {
|
||||
return Collections.emptyList();
|
||||
} else {
|
||||
return workspace.getRuntime()
|
||||
.getMachines()
|
||||
.stream()
|
||||
.map(DtoConverter::asDto)
|
||||
.map(machineDto -> linksInjector.injectLinks(machineDto, getServiceContext()))
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
}
|
||||
|
||||
@POST
|
||||
@Consumes(MediaType.APPLICATION_JSON)
|
||||
@Produces(MediaType.APPLICATION_JSON)
|
||||
@ApiOperation(value = "Create a new machine based on the configuration",
|
||||
notes = "This operation can be performed only by authorized user")
|
||||
@ApiResponses({@ApiResponse(code = 204, message = "The machine successfully created"),
|
||||
@ApiResponse(code = 400, message = "Missed required parameters, parameters are not valid"),
|
||||
@ApiResponse(code = 403, message = "The user does not have access to create the new machine"),
|
||||
@ApiResponse(code = 409, message = "Conflict error occurred during the machine creation" +
|
||||
"(e.g. The machine with such name already exists)." +
|
||||
"Workspace is not in RUNNING state"),
|
||||
@ApiResponse(code = 500, message = "Internal server error occurred")})
|
||||
public void startMachine(@ApiParam("The workspace id")
|
||||
@PathParam("workspaceId")
|
||||
String workspaceId,
|
||||
@ApiParam(value = "The new machine configuration", required = true)
|
||||
MachineConfigDto machineConfig) throws ForbiddenException,
|
||||
NotFoundException,
|
||||
ServerException,
|
||||
ConflictException,
|
||||
BadRequestException {
|
||||
requiredNotNull(machineConfig, "Machine configuration");
|
||||
requiredNotNull(machineConfig.getType(), "Machine type");
|
||||
requiredNotNull(machineConfig.getSource(), "Machine source");
|
||||
requiredNotNull(machineConfig.getSource().getType(), "Machine source type");
|
||||
// definition of source should come either with a content or with location
|
||||
requiredOnlyOneNotNull(machineConfig.getSource().getLocation(), machineConfig.getSource().getContent(),
|
||||
"Machine source should provide either location or content");
|
||||
|
||||
workspaceManager.startMachine(machineConfig, workspaceId);
|
||||
}
|
||||
|
||||
@DELETE
|
||||
@Path("/{machineId}")
|
||||
@Produces(MediaType.APPLICATION_JSON)
|
||||
@ApiOperation(value = "Stop machine")
|
||||
@ApiResponses({@ApiResponse(code = 204, message = "Machine was successfully stopped"),
|
||||
@ApiResponse(code = 404, message = "Machine with specified id does not exist"),
|
||||
@ApiResponse(code = 500, message = "Internal server error occurred")})
|
||||
public void stopMachine(@ApiParam(value = "Workspace ID")
|
||||
@PathParam("workspaceId") String workspaceId,
|
||||
@ApiParam(value = "Machine ID")
|
||||
@PathParam("machineId") String machineId) throws NotFoundException,
|
||||
ServerException,
|
||||
ForbiddenException,
|
||||
ConflictException {
|
||||
workspaceManager.stopMachine(workspaceId, machineId);
|
||||
}
|
||||
|
||||
@POST
|
||||
@Path("/{machineId}/command")
|
||||
@Consumes(MediaType.APPLICATION_JSON)
|
||||
@ApiOperation(value = "Start specified command in machine")
|
||||
@ApiResponses({@ApiResponse(code = 200, message = "The response contains entity of created machine process"),
|
||||
@ApiResponse(code = 400, message = "Command entity is invalid"),
|
||||
@ApiResponse(code = 404, message = "Machine with specified ID does not exist"),
|
||||
@ApiResponse(code = 500, message = "Internal server error occurred")})
|
||||
public MachineProcessDto executeCommandInMachine(@ApiParam(value = "Workspace ID")
|
||||
@PathParam("workspaceId")
|
||||
String workspaceId,
|
||||
@ApiParam(value = "Machine ID")
|
||||
@PathParam("machineId")
|
||||
String machineId,
|
||||
@ApiParam(value = "Command to execute", required = true)
|
||||
final CommandDto command,
|
||||
@ApiParam(value = "Channel for command output")
|
||||
@QueryParam("outputChannel")
|
||||
String outputChannel)
|
||||
throws NotFoundException,
|
||||
ServerException,
|
||||
ForbiddenException,
|
||||
BadRequestException {
|
||||
|
||||
requiredNotNull(command, "Command description");
|
||||
requiredNotNull(command.getCommandLine(), "Commandline");
|
||||
return linksInjector.injectLinks(DtoConverter.asDto(machineProcessManager.exec(workspaceId,
|
||||
machineId,
|
||||
command,
|
||||
outputChannel)),
|
||||
workspaceId,
|
||||
machineId,
|
||||
getServiceContext());
|
||||
}
|
||||
|
||||
@GET
|
||||
@Path("/{machineId}/process")
|
||||
@Produces(MediaType.APPLICATION_JSON)
|
||||
@ApiOperation(value = "Get processes of machine",
|
||||
response = MachineProcessDto.class,
|
||||
responseContainer = "List")
|
||||
@ApiResponses({@ApiResponse(code = 200, message = "The response contains machine process entities"),
|
||||
@ApiResponse(code = 404, message = "Machine with specified ID does not exist"),
|
||||
@ApiResponse(code = 500, message = "Internal server error occurred")})
|
||||
public List<MachineProcessDto> getProcesses(@ApiParam(value = "Workspace ID")
|
||||
@PathParam("workspaceId")
|
||||
String workspaceId,
|
||||
@ApiParam(value = "Machine ID")
|
||||
@PathParam("machineId")
|
||||
String machineId)
|
||||
throws NotFoundException,
|
||||
ServerException,
|
||||
ForbiddenException {
|
||||
|
||||
return machineProcessManager.getProcesses(workspaceId, machineId)
|
||||
.stream()
|
||||
.map(DtoConverter::asDto)
|
||||
.map(machineProcess -> linksInjector.injectLinks(machineProcess,
|
||||
workspaceId,
|
||||
machineId,
|
||||
getServiceContext()))
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
@DELETE
|
||||
@Path("/{machineId}/process/{processId}")
|
||||
@ApiOperation(value = "Stop process in machine")
|
||||
@ApiResponses({@ApiResponse(code = 204, message = "Process was successfully stopped"),
|
||||
@ApiResponse(code = 404, message = "Machine with specified ID does not exist"),
|
||||
@ApiResponse(code = 500, message = "Internal server error occurred")})
|
||||
public void stopProcess(@ApiParam(value = "Workspace ID")
|
||||
@PathParam("workspaceId")
|
||||
String workspaceId,
|
||||
@ApiParam(value = "Machine ID")
|
||||
@PathParam("machineId")
|
||||
String machineId,
|
||||
@ApiParam(value = "Process ID")
|
||||
@PathParam("processId")
|
||||
int processId)
|
||||
throws NotFoundException,
|
||||
ForbiddenException,
|
||||
ServerException {
|
||||
|
||||
machineProcessManager.stopProcess(workspaceId, machineId, processId);
|
||||
}
|
||||
|
||||
@GET
|
||||
@Path("/{machineId}/process/{pid}/logs")
|
||||
@Produces(MediaType.TEXT_PLAIN)
|
||||
@ApiOperation(value = "Get logs of machine process")
|
||||
@ApiResponses({@ApiResponse(code = 200, message = "The response contains logs"),
|
||||
@ApiResponse(code = 404, message = "Machine or process with specified ID does not exist"),
|
||||
@ApiResponse(code = 500, message = "Internal server error occurred")})
|
||||
public void getProcessLogs(@ApiParam(value = "Workspace ID")
|
||||
@PathParam("workspaceId")
|
||||
String workspaceId,
|
||||
@ApiParam(value = "Machine ID")
|
||||
@PathParam("machineId")
|
||||
String machineId,
|
||||
@ApiParam(value = "Process ID")
|
||||
@PathParam("pid")
|
||||
int pid,
|
||||
@Context
|
||||
HttpServletResponse httpServletResponse)
|
||||
throws NotFoundException,
|
||||
ForbiddenException,
|
||||
ServerException,
|
||||
IOException {
|
||||
|
||||
addLogsToResponse(machineProcessManager.getProcessLogReader(machineId, pid), httpServletResponse);
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks only one of the given object reference is {@code null}
|
||||
*
|
||||
* @param object1
|
||||
* object reference to check
|
||||
* @param object2
|
||||
* object reference to check
|
||||
* @param subject
|
||||
* used as subject of exception message "{subject} required"
|
||||
* @throws BadRequestException
|
||||
* when objects are both null or have both a value reference is {@code null}
|
||||
*/
|
||||
private void requiredOnlyOneNotNull(Object object1, Object object2, String subject) throws BadRequestException {
|
||||
if (object1 == null && object2 == null) {
|
||||
throw new BadRequestException(subject + " required");
|
||||
}
|
||||
if (object1 != null && object2 != null) {
|
||||
throw new BadRequestException(subject + " required");
|
||||
}
|
||||
}
|
||||
|
||||
private void addLogsToResponse(Reader logsReader, HttpServletResponse httpServletResponse) throws IOException {
|
||||
// Response is written directly to the servlet request stream
|
||||
httpServletResponse.setContentType("text/plain");
|
||||
CharStreams.copy(logsReader, httpServletResponse.getWriter());
|
||||
httpServletResponse.getWriter().flush();
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks object reference is not {@code null}
|
||||
*
|
||||
* @param object
|
||||
* object reference to check
|
||||
* @param subject
|
||||
* used as subject of exception message "{subject} required"
|
||||
* @throws BadRequestException
|
||||
* when object reference is {@code null}
|
||||
*/
|
||||
private void requiredNotNull(Object object, String subject) throws BadRequestException {
|
||||
if (object == null) {
|
||||
throw new BadRequestException(subject + " required");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -8,7 +8,7 @@
|
|||
* Contributors:
|
||||
* Codenvy, S.A. - initial API and implementation
|
||||
*******************************************************************************/
|
||||
package org.eclipse.che.api.machine.server;
|
||||
package org.eclipse.che.api.environment.server;
|
||||
|
||||
import com.google.common.collect.Lists;
|
||||
|
||||
|
|
@ -19,7 +19,6 @@ import org.eclipse.che.api.machine.shared.Constants;
|
|||
import org.eclipse.che.api.machine.shared.dto.MachineDto;
|
||||
import org.eclipse.che.api.machine.shared.dto.MachineProcessDto;
|
||||
import org.eclipse.che.api.machine.shared.dto.ServerDto;
|
||||
import org.eclipse.che.api.machine.shared.dto.SnapshotDto;
|
||||
|
||||
import javax.inject.Singleton;
|
||||
import javax.ws.rs.HttpMethod;
|
||||
|
|
@ -35,8 +34,8 @@ import static javax.ws.rs.core.MediaType.APPLICATION_JSON;
|
|||
import static javax.ws.rs.core.MediaType.TEXT_PLAIN;
|
||||
import static org.eclipse.che.api.core.util.LinksHelper.createLink;
|
||||
import static org.eclipse.che.api.machine.shared.Constants.ENVIRONMENT_OUTPUT_CHANNEL_TEMPLATE;
|
||||
import static org.eclipse.che.api.machine.shared.Constants.LINK_REL_ENVIRONMENT_OUTPUT_CHANNEL;
|
||||
import static org.eclipse.che.api.machine.shared.Constants.ENVIRONMENT_STATUS_CHANNEL_TEMPLATE;
|
||||
import static org.eclipse.che.api.machine.shared.Constants.LINK_REL_ENVIRONMENT_OUTPUT_CHANNEL;
|
||||
import static org.eclipse.che.api.machine.shared.Constants.TERMINAL_REFERENCE;
|
||||
import static org.eclipse.che.dto.server.DtoFactory.cloneDto;
|
||||
import static org.eclipse.che.dto.server.DtoFactory.newDto;
|
||||
|
|
@ -56,67 +55,41 @@ public class MachineServiceLinksInjector {
|
|||
links.add(createLink(HttpMethod.GET,
|
||||
uriBuilder.clone()
|
||||
.path(MachineService.class, "getMachineById")
|
||||
.build(machine.getId())
|
||||
.build(machine.getWorkspaceId(), machine.getId())
|
||||
.toString(),
|
||||
APPLICATION_JSON,
|
||||
"self link"));
|
||||
links.add(createLink(HttpMethod.GET,
|
||||
uriBuilder.clone()
|
||||
.path(MachineService.class, "getMachines")
|
||||
.build()
|
||||
.build(machine.getWorkspaceId())
|
||||
.toString(),
|
||||
null,
|
||||
APPLICATION_JSON,
|
||||
Constants.LINK_REL_GET_MACHINES,
|
||||
newDto(LinkParameter.class).withName("workspace")
|
||||
.withRequired(true)
|
||||
.withDefaultValue(machine.getWorkspaceId())));
|
||||
Constants.LINK_REL_GET_MACHINES));
|
||||
links.add(createLink(HttpMethod.DELETE,
|
||||
uriBuilder.clone()
|
||||
.path(MachineService.class, "destroyMachine")
|
||||
.build(machine.getId())
|
||||
.path(MachineService.class, "stopMachine")
|
||||
.build(machine.getWorkspaceId(), machine.getId())
|
||||
.toString(),
|
||||
Constants.LINK_REL_DESTROY_MACHINE));
|
||||
links.add(createLink(HttpMethod.GET,
|
||||
uriBuilder.clone()
|
||||
.path(MachineService.class, "getSnapshots")
|
||||
.build()
|
||||
.toString(),
|
||||
null,
|
||||
APPLICATION_JSON,
|
||||
Constants.LINK_REL_GET_SNAPSHOTS,
|
||||
newDto(LinkParameter.class).withName("workspace")
|
||||
.withRequired(true)
|
||||
.withDefaultValue(machine.getWorkspaceId())));
|
||||
links.add(createLink(HttpMethod.POST,
|
||||
uriBuilder.clone()
|
||||
.path(MachineService.class, "saveSnapshot")
|
||||
.build(machine.getId())
|
||||
.toString(),
|
||||
APPLICATION_JSON,
|
||||
APPLICATION_JSON,
|
||||
Constants.LINK_REL_SAVE_SNAPSHOT));
|
||||
links.add(createLink(HttpMethod.POST,
|
||||
uriBuilder.clone()
|
||||
.path(MachineService.class, "executeCommandInMachine")
|
||||
.build(machine.getId())
|
||||
.build(machine.getWorkspaceId(), machine.getId())
|
||||
.toString(),
|
||||
APPLICATION_JSON,
|
||||
APPLICATION_JSON,
|
||||
Constants.LINK_REL_EXECUTE_COMMAND,
|
||||
newDto(LinkParameter.class).withName("outputChannel")
|
||||
.withRequired(false)));
|
||||
URI getProcessesUri = uriBuilder.clone()
|
||||
.path(MachineService.class, "getProcesses")
|
||||
.build(machine.getWorkspaceId(), machine.getId());
|
||||
links.add(createLink(HttpMethod.GET,
|
||||
uriBuilder.clone()
|
||||
.path(MachineService.class, "getProcesses")
|
||||
.build(machine.getId())
|
||||
.toString(),
|
||||
getProcessesUri.toString(),
|
||||
APPLICATION_JSON,
|
||||
Constants.LINK_REL_GET_PROCESSES));
|
||||
final URI getLogsUri = uriBuilder.clone()
|
||||
.path(MachineService.class, "getMachineLogs")
|
||||
.build(machine.getId());
|
||||
links.add(createLink(HttpMethod.GET, getLogsUri.toString(), TEXT_PLAIN, Constants.LINK_REL_GET_MACHINE_LOGS));
|
||||
|
||||
injectTerminalLink(machine, serviceContext, links);
|
||||
|
||||
|
|
@ -125,7 +98,7 @@ public class MachineServiceLinksInjector {
|
|||
serviceContext.getBaseUriBuilder()
|
||||
.path("ws")
|
||||
.path(machine.getWorkspaceId())
|
||||
.scheme("https".equals(getLogsUri.getScheme()) ? "wss" : "ws")
|
||||
.scheme("https".equals(getProcessesUri.getScheme()) ? "wss" : "ws")
|
||||
.build()
|
||||
.toString(),
|
||||
null);
|
||||
|
|
@ -163,41 +136,39 @@ public class MachineServiceLinksInjector {
|
|||
}
|
||||
}
|
||||
|
||||
public MachineProcessDto injectLinks(MachineProcessDto process, String machineId, ServiceContext serviceContext) {
|
||||
public MachineProcessDto injectLinks(MachineProcessDto process,
|
||||
String workspaceId,
|
||||
String machineId,
|
||||
ServiceContext serviceContext) {
|
||||
final UriBuilder uriBuilder = serviceContext.getServiceUriBuilder();
|
||||
final List<Link> links = Lists.newArrayListWithExpectedSize(3);
|
||||
|
||||
links.add(createLink(HttpMethod.DELETE,
|
||||
uriBuilder.clone()
|
||||
.path(MachineService.class, "stopProcess")
|
||||
.build(machineId, process.getPid())
|
||||
.build(workspaceId,
|
||||
machineId,
|
||||
process.getPid())
|
||||
.toString(),
|
||||
Constants.LINK_REL_STOP_PROCESS));
|
||||
links.add(createLink(HttpMethod.GET,
|
||||
uriBuilder.clone()
|
||||
.path(MachineService.class, "getProcessLogs")
|
||||
.build(machineId, process.getPid())
|
||||
.build(workspaceId,
|
||||
machineId,
|
||||
process.getPid())
|
||||
.toString(),
|
||||
TEXT_PLAIN,
|
||||
Constants.LINK_REL_GET_PROCESS_LOGS));
|
||||
links.add(createLink(HttpMethod.GET,
|
||||
uriBuilder.clone()
|
||||
.path(MachineService.class, "getProcesses")
|
||||
.build(machineId)
|
||||
.build(workspaceId,
|
||||
machineId)
|
||||
.toString(),
|
||||
APPLICATION_JSON,
|
||||
Constants.LINK_REL_GET_PROCESSES));
|
||||
|
||||
return process.withLinks(links);
|
||||
}
|
||||
|
||||
public SnapshotDto injectLinks(SnapshotDto snapshot, ServiceContext serviceContext) {
|
||||
final UriBuilder uriBuilder = serviceContext.getServiceUriBuilder();
|
||||
return snapshot.withLinks(singletonList(createLink(HttpMethod.DELETE,
|
||||
uriBuilder.clone()
|
||||
.path(MachineService.class, "removeSnapshot")
|
||||
.build(snapshot.getId())
|
||||
.toString(),
|
||||
Constants.LINK_REL_REMOVE_SNAPSHOT)));
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,90 @@
|
|||
/*******************************************************************************
|
||||
* Copyright (c) 2012-2016 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.environment.server;
|
||||
|
||||
import org.eclipse.che.api.core.NotFoundException;
|
||||
import org.eclipse.che.api.core.model.machine.Command;
|
||||
import org.eclipse.che.api.core.model.machine.Machine;
|
||||
import org.eclipse.che.api.core.model.machine.MachineSource;
|
||||
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.MachineRuntimeInfoImpl;
|
||||
import org.eclipse.che.api.machine.server.spi.Instance;
|
||||
import org.eclipse.che.api.machine.server.spi.InstanceNode;
|
||||
import org.eclipse.che.api.machine.server.spi.InstanceProcess;
|
||||
import org.eclipse.che.api.machine.server.spi.impl.AbstractInstance;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* @author Alexander Garagatyi
|
||||
*/
|
||||
public class NoOpMachineInstance extends AbstractInstance {
|
||||
public NoOpMachineInstance(Machine machine) {
|
||||
super(machine);
|
||||
}
|
||||
|
||||
@Override
|
||||
public MachineRuntimeInfoImpl getRuntime() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public LineConsumer getLogger() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public InstanceProcess getProcess(int pid) throws NotFoundException, MachineException {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<InstanceProcess> getProcesses() throws MachineException {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
@Override
|
||||
public InstanceProcess createProcess(Command command, String outputChannel) throws MachineException {
|
||||
throw new MachineException("This machine has state that doesn't support process creation");
|
||||
}
|
||||
|
||||
@Override
|
||||
public MachineSource saveToSnapshot() throws MachineException {
|
||||
throw new MachineException("This machine has state that doesn't support saving its state");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void destroy() throws MachineException {
|
||||
}
|
||||
|
||||
@Override
|
||||
public InstanceNode getNode() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String readFileContent(String filePath, int startFrom, int limit) throws MachineException {
|
||||
throw new MachineException("This machine has state that doesn't support process reading files");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void copy(Instance sourceMachine, String sourcePath, String targetPath, boolean overwriteDirNonDir)
|
||||
throws MachineException {
|
||||
throw new MachineException("This machine has state that doesn't support copying of files");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void copy(String sourcePath, String targetPath) throws MachineException {
|
||||
throw new MachineException("This machine has state that doesn't support copying of files");
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,29 @@
|
|||
/*******************************************************************************
|
||||
* Copyright (c) 2012-2016 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.environment.server.exception;
|
||||
|
||||
import org.eclipse.che.api.core.NotFoundException;
|
||||
import org.eclipse.che.api.core.rest.shared.dto.ServiceError;
|
||||
|
||||
/**
|
||||
* Exception thrown in case environment stop is called but no matching environment is running.
|
||||
*
|
||||
* @author Alexander Garagatyi
|
||||
*/
|
||||
public class EnvironmentNotRunningException extends NotFoundException {
|
||||
public EnvironmentNotRunningException(String message) {
|
||||
super(message);
|
||||
}
|
||||
|
||||
public EnvironmentNotRunningException(ServiceError serviceError) {
|
||||
super(serviceError);
|
||||
}
|
||||
}
|
||||
|
|
@ -10,21 +10,15 @@
|
|||
*******************************************************************************/
|
||||
package org.eclipse.che.api.workspace.server;
|
||||
|
||||
import com.google.common.base.Joiner;
|
||||
|
||||
import org.eclipse.che.api.core.BadRequestException;
|
||||
import org.eclipse.che.api.core.model.machine.Command;
|
||||
import org.eclipse.che.api.core.model.machine.MachineConfig;
|
||||
import org.eclipse.che.api.core.model.machine.ServerConf;
|
||||
import org.eclipse.che.api.core.model.workspace.Environment;
|
||||
import org.eclipse.che.api.core.model.workspace.Workspace;
|
||||
import org.eclipse.che.api.core.model.workspace.WorkspaceConfig;
|
||||
import org.eclipse.che.api.machine.server.MachineInstanceProviders;
|
||||
import org.eclipse.che.api.environment.server.CheEnvironmentValidator;
|
||||
|
||||
import javax.inject.Inject;
|
||||
import javax.inject.Singleton;
|
||||
import java.net.MalformedURLException;
|
||||
import java.net.URL;
|
||||
import java.util.Map;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
|
|
@ -39,15 +33,13 @@ import static java.lang.String.format;
|
|||
@Singleton
|
||||
public class DefaultWorkspaceValidator implements WorkspaceValidator {
|
||||
/* should contain [3, 20] characters, first and last character is letter or digit, available characters {A-Za-z0-9.-_}*/
|
||||
private static final Pattern WS_NAME = Pattern.compile("[a-zA-Z0-9][-_.a-zA-Z0-9]{1,18}[a-zA-Z0-9]");
|
||||
private static final Pattern SERVER_PORT = Pattern.compile("[1-9]+[0-9]*/(?:tcp|udp)");
|
||||
private static final Pattern SERVER_PROTOCOL = Pattern.compile("[a-z][a-z0-9-+.]*");
|
||||
|
||||
private final MachineInstanceProviders machineInstanceProviders;
|
||||
|
||||
private static final Pattern WS_NAME = Pattern.compile("[a-zA-Z0-9][-_.a-zA-Z0-9]{1,18}[a-zA-Z0-9]");
|
||||
|
||||
private final CheEnvironmentValidator environmentValidator;
|
||||
|
||||
@Inject
|
||||
public DefaultWorkspaceValidator(MachineInstanceProviders machineInstanceProviders) {
|
||||
this.machineInstanceProviders = machineInstanceProviders;
|
||||
public DefaultWorkspaceValidator(CheEnvironmentValidator environmentValidator) {
|
||||
this.environmentValidator = environmentValidator;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
@ -68,13 +60,18 @@ public class DefaultWorkspaceValidator implements WorkspaceValidator {
|
|||
|
||||
//environments
|
||||
checkArgument(!isNullOrEmpty(config.getDefaultEnv()), "Workspace default environment name required");
|
||||
checkNotNull(config.getEnvironments(), "Workspace should contain at least one environment");
|
||||
checkArgument(config.getEnvironments()
|
||||
.stream()
|
||||
.anyMatch(env -> config.getDefaultEnv().equals(env.getName())),
|
||||
"Workspace default environment configuration required");
|
||||
|
||||
for (Environment environment : config.getEnvironments()) {
|
||||
validateEnv(environment, config.getName());
|
||||
try {
|
||||
environmentValidator.validate(environment);
|
||||
} catch (IllegalArgumentException e) {
|
||||
throw new BadRequestException(e.getLocalizedMessage());
|
||||
}
|
||||
}
|
||||
|
||||
//commands
|
||||
|
|
@ -102,68 +99,6 @@ public class DefaultWorkspaceValidator implements WorkspaceValidator {
|
|||
}
|
||||
}
|
||||
|
||||
private void validateEnv(Environment environment, String workspaceName) throws BadRequestException {
|
||||
final String envName = environment.getName();
|
||||
checkArgument(!isNullOrEmpty(envName), "Environment name should be neither null nor empty");
|
||||
|
||||
//machine configs
|
||||
checkArgument(!environment.getMachineConfigs().isEmpty(), "Environment '%s' should contain at least 1 machine", envName);
|
||||
|
||||
final long devCount = environment.getMachineConfigs()
|
||||
.stream()
|
||||
.filter(MachineConfig::isDev)
|
||||
.count();
|
||||
checkArgument(devCount == 1,
|
||||
"Environment should contain exactly 1 dev machine, but '%s' contains '%d'",
|
||||
envName,
|
||||
devCount);
|
||||
for (MachineConfig machineCfg : environment.getMachineConfigs()) {
|
||||
validateMachine(machineCfg, envName);
|
||||
}
|
||||
}
|
||||
|
||||
private void validateMachine(MachineConfig machineCfg, String envName) throws BadRequestException {
|
||||
checkArgument(!isNullOrEmpty(machineCfg.getName()), "Environment %s contains machine with null or empty name", envName);
|
||||
checkNotNull(machineCfg.getSource(), "Environment " + envName + " contains machine without source");
|
||||
checkArgument(!(machineCfg.getSource().getContent() == null && machineCfg.getSource().getLocation() == null),
|
||||
"Environment " + envName + " contains machine with source but this source doesn't define a location or content");
|
||||
|
||||
|
||||
checkArgument(machineInstanceProviders.hasProvider(machineCfg.getType()),
|
||||
"Type %s of machine %s in environment %s is not supported. Supported values: %s.",
|
||||
machineCfg.getType(),
|
||||
machineCfg.getName(),
|
||||
envName,
|
||||
Joiner.on(", ").join(machineInstanceProviders.getProviderTypes()));
|
||||
|
||||
if (machineCfg.getSource().getType().equals("dockerfile") && machineCfg.getSource().getLocation() != null) {
|
||||
try {
|
||||
final String protocol = new URL(machineCfg.getSource().getLocation()).getProtocol();
|
||||
checkArgument(protocol.equals("http") || protocol.equals("https"),
|
||||
"Environment " + envName + " contains machine with invalid source location protocol: " +
|
||||
machineCfg.getSource().getLocation());
|
||||
} catch (MalformedURLException e) {
|
||||
throw new BadRequestException("Environment " + envName + " contains machine with invalid source location: " +
|
||||
machineCfg.getSource().getLocation());
|
||||
}
|
||||
}
|
||||
|
||||
for (ServerConf serverConf : machineCfg.getServers()) {
|
||||
checkArgument(serverConf.getPort() != null && SERVER_PORT.matcher(serverConf.getPort()).matches(),
|
||||
"Machine %s contains server conf with invalid port %s",
|
||||
machineCfg.getName(),
|
||||
serverConf.getPort());
|
||||
checkArgument(serverConf.getProtocol() == null || SERVER_PROTOCOL.matcher(serverConf.getProtocol()).matches(),
|
||||
"Machine %s contains server conf with invalid protocol %s",
|
||||
machineCfg.getName(),
|
||||
serverConf.getProtocol());
|
||||
}
|
||||
for (Map.Entry<String, String> envVariable : machineCfg.getEnvVariables().entrySet()) {
|
||||
checkArgument(!isNullOrEmpty(envVariable.getKey()), "Machine %s contains environment variable with null or empty name");
|
||||
checkNotNull(envVariable.getValue(), "Machine %s contains environment variable with null value");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks that object reference is not null, throws {@link BadRequestException}
|
||||
* in the case of null {@code object} with given {@code message}.
|
||||
|
|
|
|||
|
|
@ -149,11 +149,7 @@ public final class DtoConverter {
|
|||
envDto.withRecipe(newDto(RecipeDto.class).withType(env.getRecipe().getType())
|
||||
.withScript(env.getRecipe().getScript()));
|
||||
}
|
||||
return newDto(EnvironmentDto.class).withName(env.getName())
|
||||
.withMachineConfigs(env.getMachineConfigs()
|
||||
.stream()
|
||||
.map(org.eclipse.che.api.machine.server.DtoConverter::asDto)
|
||||
.collect(toList()));
|
||||
return envDto;
|
||||
}
|
||||
|
||||
/** Converts {@link WorkspaceRuntime} to {@link WorkspaceRuntimeDto}. */
|
||||
|
|
@ -183,7 +179,7 @@ public final class DtoConverter {
|
|||
.withType(snapshot.getType())
|
||||
.withWorkspaceId(snapshot.getWorkspaceId())
|
||||
.withEnvName(snapshot.getEnvName())
|
||||
.withMachineName(snapshot.getEnvName());
|
||||
.withMachineName(snapshot.getMachineName());
|
||||
}
|
||||
|
||||
private DtoConverter() {}
|
||||
|
|
|
|||
|
|
@ -8,13 +8,12 @@
|
|||
* Contributors:
|
||||
* Codenvy, S.A. - initial API and implementation
|
||||
*******************************************************************************/
|
||||
package org.eclipse.che.ide.ext.machine.server;
|
||||
|
||||
package org.eclipse.che.api.workspace.server;
|
||||
|
||||
import org.eclipse.che.api.core.NotFoundException;
|
||||
import org.eclipse.che.api.core.ServerException;
|
||||
import org.eclipse.che.api.core.rest.Service;
|
||||
import org.eclipse.che.api.machine.server.MachineManager;
|
||||
import org.eclipse.che.api.machine.server.spi.Instance;
|
||||
import org.eclipse.che.api.machine.server.util.RecipeRetriever;
|
||||
|
||||
import javax.inject.Inject;
|
||||
|
|
@ -27,24 +26,27 @@ import javax.ws.rs.core.MediaType;
|
|||
/**
|
||||
* Service for downloading recipe script for machine.
|
||||
*
|
||||
* @author Mihail Kuznyetsov.
|
||||
* @author Mihail Kuznyetsov
|
||||
* @author Alexander Garagatyi
|
||||
*/
|
||||
@Path("/recipe/script")
|
||||
public class RecipeScriptDownloadService extends Service {
|
||||
|
||||
private final MachineManager machineManager;
|
||||
private final RecipeRetriever recipeRetriever;
|
||||
private final WorkspaceManager workspaceManager;
|
||||
private final RecipeRetriever recipeRetriever;
|
||||
|
||||
@Inject
|
||||
public RecipeScriptDownloadService(MachineManager machineManager, RecipeRetriever recipeRetriever) {
|
||||
this.machineManager = machineManager;
|
||||
public RecipeScriptDownloadService(WorkspaceManager workspaceManager, RecipeRetriever recipeRetriever) {
|
||||
this.workspaceManager = workspaceManager;
|
||||
this.recipeRetriever = recipeRetriever;
|
||||
}
|
||||
|
||||
@GET
|
||||
@Path("/{machineId}")
|
||||
@Path("/{workspaceId}/{machineId}")
|
||||
@Produces(MediaType.TEXT_PLAIN)
|
||||
public String getRecipeScript(@PathParam("machineId") String machineId) throws ServerException, NotFoundException {
|
||||
return recipeRetriever.getRecipe(machineManager.getMachine(machineId).getConfig()).getScript();
|
||||
public String getRecipeScript(@PathParam("workspaceId") String workspaceId,
|
||||
@PathParam("machineId") String machineId) throws ServerException,
|
||||
NotFoundException {
|
||||
Instance machineInstance = workspaceManager.getMachineInstance(workspaceId, machineId);
|
||||
return recipeRetriever.getRecipe(machineInstance.getConfig()).getScript();
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,125 @@
|
|||
/*******************************************************************************
|
||||
* Copyright (c) 2012-2016 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.workspace.server;
|
||||
|
||||
import com.google.common.util.concurrent.Striped;
|
||||
|
||||
import java.io.Closeable;
|
||||
import java.util.concurrent.locks.ReadWriteLock;
|
||||
|
||||
/**
|
||||
* Helper class to use striped locks in try-with-resources construction.
|
||||
* </p>
|
||||
* Examples of usage:
|
||||
* <pre class="code"><code class="java">
|
||||
* StripedLocks stripedLocks = new StripedLocks(16);
|
||||
* try (StripedLocks.WriteLock lock = stripedLocks.acquireWriteLock(myKey)) {
|
||||
* syncedObject.write();
|
||||
* }
|
||||
*
|
||||
* try (StripedLocks.ReadLock lock = stripedLocks.acquireReadLock(myKey)) {
|
||||
* syncedObject.read();
|
||||
* }
|
||||
*
|
||||
* try (StripedLocks.WriteAllLock lock = stripedLocks.acquireWriteAllLock(myKey)) {
|
||||
* for (ObjectToSync objectToSync : allObjectsToSync) {
|
||||
* objectToSync.write();
|
||||
* }
|
||||
* }
|
||||
* </pre>
|
||||
*
|
||||
* @author Alexander Garagatyi
|
||||
*/
|
||||
// TODO consider usage of plain map with locks instead of Guava's Striped
|
||||
// TODO consider moving to the util module of Che core
|
||||
public class StripedLocks {
|
||||
private final Striped<ReadWriteLock> striped;
|
||||
|
||||
public StripedLocks(int stripesCount) {
|
||||
striped = Striped.readWriteLock(stripesCount);
|
||||
}
|
||||
|
||||
/**
|
||||
* Acquire read lock for provided key.
|
||||
*/
|
||||
public ReadLock acquireReadLock(String key) {
|
||||
return new ReadLock(key);
|
||||
}
|
||||
|
||||
/**
|
||||
* Acquire write lock for provided key.
|
||||
*/
|
||||
public WriteLock acquireWriteLock(String key) {
|
||||
return new WriteLock(key);
|
||||
}
|
||||
|
||||
/**
|
||||
* Acquire write lock for all possible keys.
|
||||
*/
|
||||
public WriteAllLock acquireWriteAllLock() {
|
||||
return new WriteAllLock();
|
||||
}
|
||||
|
||||
/**
|
||||
* Represents read lock for the provided key.
|
||||
* Can be used as {@link AutoCloseable} to release lock.
|
||||
*/
|
||||
public class ReadLock implements Closeable {
|
||||
private String key;
|
||||
|
||||
private ReadLock(String key) {
|
||||
this.key = key;
|
||||
striped.get(key).readLock().lock();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() {
|
||||
striped.get(key).readLock().unlock();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Represents write lock for the provided key.
|
||||
* Can be used as {@link AutoCloseable} to release lock.
|
||||
*/
|
||||
public class WriteLock implements Closeable {
|
||||
private String key;
|
||||
|
||||
private WriteLock(String key) {
|
||||
this.key = key;
|
||||
striped.get(key).readLock().lock();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() {
|
||||
striped.get(key).readLock().unlock();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Represents write lock for all possible keys.
|
||||
* Can be used as {@link AutoCloseable} to release locks.
|
||||
*/
|
||||
public class WriteAllLock implements Closeable {
|
||||
private WriteAllLock() {
|
||||
for (int i = 0; i < striped.size(); i++) {
|
||||
striped.getAt(i).writeLock().lock();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() {
|
||||
for (int i = 0; i < striped.size(); i++) {
|
||||
striped.getAt(i).writeLock().unlock();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -17,7 +17,6 @@ import com.google.inject.Inject;
|
|||
import org.eclipse.che.api.core.ApiException;
|
||||
import org.eclipse.che.api.core.BadRequestException;
|
||||
import org.eclipse.che.api.core.ConflictException;
|
||||
import org.eclipse.che.api.core.ForbiddenException;
|
||||
import org.eclipse.che.api.core.NotFoundException;
|
||||
import org.eclipse.che.api.core.ServerException;
|
||||
import org.eclipse.che.api.core.model.machine.MachineConfig;
|
||||
|
|
@ -25,9 +24,10 @@ import org.eclipse.che.api.core.model.workspace.Workspace;
|
|||
import org.eclipse.che.api.core.model.workspace.WorkspaceConfig;
|
||||
import org.eclipse.che.api.core.model.workspace.WorkspaceStatus;
|
||||
import org.eclipse.che.api.core.notification.EventService;
|
||||
import org.eclipse.che.api.machine.server.MachineManager;
|
||||
import org.eclipse.che.api.machine.server.dao.SnapshotDao;
|
||||
import org.eclipse.che.api.machine.server.model.impl.MachineImpl;
|
||||
import org.eclipse.che.api.machine.server.model.impl.SnapshotImpl;
|
||||
import org.eclipse.che.api.machine.server.spi.Instance;
|
||||
import org.eclipse.che.api.workspace.server.WorkspaceRuntimes.RuntimeDescriptor;
|
||||
import org.eclipse.che.api.workspace.server.event.WorkspaceCreatedEvent;
|
||||
import org.eclipse.che.api.workspace.server.event.WorkspaceRemovedEvent;
|
||||
|
|
@ -90,9 +90,9 @@ public class WorkspaceManager {
|
|||
private final WorkspaceRuntimes runtimes;
|
||||
private final EventService eventService;
|
||||
private final ExecutorService executor;
|
||||
private final MachineManager machineManager;
|
||||
private final boolean defaultAutoSnapshot;
|
||||
private final boolean defaultAutoRestore;
|
||||
private final SnapshotDao snapshotDao;
|
||||
|
||||
private WorkspaceHooks hooks = new NoopWorkspaceHooks();
|
||||
|
||||
|
|
@ -100,15 +100,15 @@ public class WorkspaceManager {
|
|||
public WorkspaceManager(WorkspaceDao workspaceDao,
|
||||
WorkspaceRuntimes workspaceRegistry,
|
||||
EventService eventService,
|
||||
MachineManager machineManager,
|
||||
@Named("workspace.runtime.auto_snapshot") boolean defaultAutoSnapshot,
|
||||
@Named("workspace.runtime.auto_restore") boolean defaultAutoRestore) {
|
||||
@Named("workspace.runtime.auto_restore") boolean defaultAutoRestore,
|
||||
SnapshotDao snapshotDao) {
|
||||
this.workspaceDao = workspaceDao;
|
||||
this.runtimes = workspaceRegistry;
|
||||
this.eventService = eventService;
|
||||
this.machineManager = machineManager;
|
||||
this.defaultAutoSnapshot = defaultAutoSnapshot;
|
||||
this.defaultAutoRestore = defaultAutoRestore;
|
||||
this.snapshotDao = snapshotDao;
|
||||
|
||||
executor = Executors.newCachedThreadPool(new ThreadFactoryBuilder().setNameFormat("WorkspaceManager-%d")
|
||||
.setDaemon(true)
|
||||
|
|
@ -262,7 +262,9 @@ public class WorkspaceManager {
|
|||
public List<WorkspaceImpl> getWorkspaces(String user) throws ServerException {
|
||||
requireNonNull(user, "Required non-null user id");
|
||||
final List<WorkspaceImpl> workspaces = workspaceDao.getWorkspaces(user);
|
||||
workspaces.forEach(this::normalizeState);
|
||||
for (WorkspaceImpl workspace : workspaces) {
|
||||
normalizeState(workspace);
|
||||
}
|
||||
return workspaces;
|
||||
}
|
||||
|
||||
|
|
@ -283,7 +285,9 @@ public class WorkspaceManager {
|
|||
public List<WorkspaceImpl> getByNamespace(String namespace) throws ServerException {
|
||||
requireNonNull(namespace, "Required non-null namespace");
|
||||
final List<WorkspaceImpl> workspaces = workspaceDao.getByNamespace(namespace);
|
||||
workspaces.forEach(this::normalizeState);
|
||||
for (WorkspaceImpl workspace : workspaces) {
|
||||
normalizeState(workspace);
|
||||
}
|
||||
return workspaces;
|
||||
}
|
||||
|
||||
|
|
@ -431,7 +435,6 @@ public class WorkspaceManager {
|
|||
*
|
||||
* @param machineConfig configuration of machine to start
|
||||
* @param workspaceId id of workspace in which machine should be started
|
||||
* @return starting machine instance
|
||||
* @throws NotFoundException
|
||||
* if machine type from recipe is unsupported
|
||||
* @throws NotFoundException
|
||||
|
|
@ -445,21 +448,18 @@ public class WorkspaceManager {
|
|||
* @throws ServerException
|
||||
* if any other exception occurs during starting
|
||||
*/
|
||||
public MachineImpl startMachine(MachineConfig machineConfig, String workspaceId)
|
||||
throws ServerException,
|
||||
ConflictException,
|
||||
BadRequestException,
|
||||
NotFoundException {
|
||||
public void startMachine(MachineConfig machineConfig,
|
||||
String workspaceId) throws ServerException,
|
||||
ConflictException,
|
||||
BadRequestException,
|
||||
NotFoundException {
|
||||
|
||||
final WorkspaceImpl workspace = getWorkspace(workspaceId);
|
||||
if (RUNNING != workspace.getStatus()) {
|
||||
throw new ConflictException(format("Workspace '%s' is not running, new machine can't be started", workspaceId));
|
||||
}
|
||||
|
||||
return machineManager.createMachineAsync(machineConfig,
|
||||
workspaceId,
|
||||
workspace.getRuntime().getActiveEnv(),
|
||||
runtimes.getMachineLogger(workspaceId, machineConfig.getName()));
|
||||
performAsyncStart(machineConfig, workspaceId);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -474,9 +474,13 @@ public class WorkspaceManager {
|
|||
* @throws NotFoundException
|
||||
* when workspace {@code workspaceId} doesn't have runtime
|
||||
*/
|
||||
public void stopWorkspace(String workspaceId) throws ServerException, NotFoundException, ConflictException {
|
||||
public void stopWorkspace(String workspaceId) throws ServerException,
|
||||
NotFoundException,
|
||||
ConflictException {
|
||||
requireNonNull(workspaceId, "Required non-null workspace id");
|
||||
performAsyncStop(normalizeState(workspaceDao.get(workspaceId)));
|
||||
final WorkspaceImpl workspace = normalizeState(workspaceDao.get(workspaceId));
|
||||
checkWorkspaceIsRunning(workspace, "stop");
|
||||
performAsyncStop(workspace);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -504,12 +508,16 @@ public class WorkspaceManager {
|
|||
* @throws ConflictException
|
||||
* when workspace is not running
|
||||
*/
|
||||
public void createSnapshot(String workspaceId) throws NotFoundException, ServerException, ConflictException {
|
||||
public void createSnapshot(String workspaceId) throws NotFoundException,
|
||||
ServerException,
|
||||
ConflictException {
|
||||
requireNonNull(workspaceId, "Required non-null workspace id");
|
||||
final WorkspaceImpl workspace = normalizeState(workspaceDao.get(workspaceId));
|
||||
checkWorkspaceIsRunning(workspace, "create a snapshot of");
|
||||
executor.execute(ThreadLocalPropagateContext.wrap(() -> {
|
||||
createSnapshotSync(workspace.getRuntime(), workspace.getNamespace(), workspaceId);
|
||||
createSnapshotSync(workspace.getRuntime(),
|
||||
workspace.getNamespace(),
|
||||
workspaceId);
|
||||
}));
|
||||
}
|
||||
|
||||
|
|
@ -530,11 +538,12 @@ public class WorkspaceManager {
|
|||
requireNonNull(workspaceId, "Required non-null workspace id");
|
||||
// check if workspace exists
|
||||
final WorkspaceImpl workspace = workspaceDao.get(workspaceId);
|
||||
return machineManager.getSnapshots(workspace.getNamespace(), workspaceId);
|
||||
return snapshotDao.findSnapshots(workspace.getNamespace(), workspaceId);
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes all snapshots of workspace machines
|
||||
* Removes all snapshots of workspace machines.
|
||||
* Continues to remove snapshots even when removal of some of them fails.
|
||||
*
|
||||
* @param workspaceId workspace id to remove machine snapshots
|
||||
* @throws NotFoundException
|
||||
|
|
@ -543,7 +552,64 @@ public class WorkspaceManager {
|
|||
* when any other error occurs
|
||||
*/
|
||||
public void removeSnapshots(String workspaceId) throws NotFoundException, ServerException {
|
||||
machineManager.removeSnapshots(getWorkspace(workspaceId).getNamespace(), workspaceId);
|
||||
List<SnapshotImpl> snapshots = getSnapshot(workspaceId);
|
||||
for (SnapshotImpl snapshot : snapshots) {
|
||||
try {
|
||||
runtimes.removeSnapshot(snapshot);
|
||||
snapshotDao.removeSnapshot(snapshot.getId());
|
||||
} catch (Exception e) {
|
||||
LOG.error(e.getLocalizedMessage(), e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Stops machine in running workspace.
|
||||
*
|
||||
* @param workspaceId
|
||||
* ID of workspace that owns machine
|
||||
* @param machineId
|
||||
* ID of machine that should be stopped
|
||||
* @throws NotFoundException
|
||||
* if machine is not found in running workspace
|
||||
* @throws ConflictException
|
||||
* if workspace is not running
|
||||
* @throws ConflictException
|
||||
* if machine stop is forbidden (e.g. machine is dev-machine)
|
||||
* @throws ServerException
|
||||
* if other error occurs
|
||||
*/
|
||||
public void stopMachine(String workspaceId,
|
||||
String machineId) throws NotFoundException,
|
||||
ServerException,
|
||||
ConflictException {
|
||||
requireNonNull(workspaceId, "Required non-null workspace id");
|
||||
requireNonNull(machineId, "Required non-null machine id");
|
||||
final WorkspaceImpl workspace = normalizeState(workspaceDao.get(workspaceId));
|
||||
checkWorkspaceIsRunning(workspace, format("stop machine with ID '%s' of", machineId));
|
||||
runtimes.stopMachine(workspaceId, machineId);
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves machine instance that allows to execute commands in a machine.
|
||||
*
|
||||
* @param workspaceId
|
||||
* ID of workspace that owns machine
|
||||
* @param machineId
|
||||
* ID of requested machine
|
||||
* @return instance of requested machine
|
||||
* @throws NotFoundException
|
||||
* if workspace is not running
|
||||
* @throws NotFoundException
|
||||
* if machine is not found in the workspace
|
||||
*/
|
||||
public Instance getMachineInstance(String workspaceId,
|
||||
String machineId) throws NotFoundException,
|
||||
ServerException {
|
||||
requireNonNull(workspaceId, "Required non-null workspace id");
|
||||
requireNonNull(machineId, "Required non-null machine id");
|
||||
normalizeState(workspaceDao.get(workspaceId));
|
||||
return runtimes.getMachine(workspaceId, machineId);
|
||||
}
|
||||
|
||||
/** Asynchronously starts given workspace. */
|
||||
|
|
@ -563,7 +629,7 @@ public class WorkspaceManager {
|
|||
}
|
||||
// WorkspaceRuntimes performs this check as well
|
||||
// but this check needed here because permanent workspace start performed asynchronously
|
||||
// which means that even if registry won't start workspace client receives workspace object
|
||||
// which means that even if workspace runtimes won't start workspace client receives workspace object
|
||||
// with starting status, this check prevents it and throws appropriate exception
|
||||
try {
|
||||
final RuntimeDescriptor descriptor = runtimes.get(workspace.getId());
|
||||
|
|
@ -571,7 +637,7 @@ public class WorkspaceManager {
|
|||
workspace.getConfig().getName(),
|
||||
descriptor.getRuntimeStatus()));
|
||||
} catch (NotFoundException ignored) {
|
||||
// it is okay if workspace does not exist
|
||||
// it is okay if workspace does not exist in runtimes
|
||||
}
|
||||
|
||||
workspace.getAttributes().put(UPDATED_ATTRIBUTE_NAME, Long.toString(currentTimeMillis()));
|
||||
|
|
@ -587,7 +653,7 @@ public class WorkspaceManager {
|
|||
workspace.getConfig().getName(),
|
||||
workspace.getId(),
|
||||
sessionUserNameOr("undefined"));
|
||||
} catch (RuntimeException | ServerException | NotFoundException | ConflictException | ForbiddenException ex) {
|
||||
} catch (RuntimeException | ApiException ex) {
|
||||
if (workspace.isTemporary()) {
|
||||
try {
|
||||
removeWorkspace(workspace.getId());
|
||||
|
|
@ -644,6 +710,16 @@ public class WorkspaceManager {
|
|||
}));
|
||||
}
|
||||
|
||||
private void performAsyncStart(MachineConfig machineConfig, String workspaceId) {
|
||||
executor.execute(ThreadLocalPropagateContext.wrap(() -> {
|
||||
try {
|
||||
runtimes.startMachine(workspaceId, machineConfig);
|
||||
} catch (ApiException e) {
|
||||
LOG.error(e.getLocalizedMessage(), e);
|
||||
}
|
||||
}));
|
||||
}
|
||||
|
||||
/**
|
||||
* Synchronously creates snapshot of the workspace.
|
||||
*
|
||||
|
|
@ -658,7 +734,10 @@ public class WorkspaceManager {
|
|||
String devMachineSnapshotFailMessage = null;
|
||||
for (MachineImpl machine : runtime.getMachines()) {
|
||||
try {
|
||||
machineManager.saveSync(machine.getId(), namespace, runtime.getActiveEnv());
|
||||
SnapshotImpl snapshot = runtimes.saveMachine(namespace,
|
||||
workspaceId,
|
||||
machine.getId());
|
||||
snapshotDao.saveSnapshot(snapshot);
|
||||
} catch (ApiException apiEx) {
|
||||
if (machine.getConfig().isDev()) {
|
||||
devMachineSnapshotFailMessage = apiEx.getLocalizedMessage();
|
||||
|
|
@ -679,7 +758,8 @@ public class WorkspaceManager {
|
|||
return devMachineSnapshotFailMessage == null;
|
||||
}
|
||||
|
||||
private void checkWorkspaceIsRunning(WorkspaceImpl workspace, String operation) throws ConflictException {
|
||||
@VisibleForTesting
|
||||
void checkWorkspaceIsRunning(WorkspaceImpl workspace, String operation) throws ConflictException {
|
||||
if (workspace.getStatus() != RUNNING) {
|
||||
throw new ConflictException(format("Could not %s the workspace '%s:%s' because its status is '%s'.",
|
||||
operation,
|
||||
|
|
@ -701,7 +781,7 @@ public class WorkspaceManager {
|
|||
return nameIfNoUser;
|
||||
}
|
||||
|
||||
private WorkspaceImpl normalizeState(WorkspaceImpl workspace) {
|
||||
private WorkspaceImpl normalizeState(WorkspaceImpl workspace) throws ServerException {
|
||||
try {
|
||||
return normalizeState(workspace, runtimes.get(workspace.getId()));
|
||||
} catch (NotFoundException e) {
|
||||
|
|
@ -712,7 +792,7 @@ public class WorkspaceManager {
|
|||
private WorkspaceImpl normalizeState(WorkspaceImpl workspace, RuntimeDescriptor descriptor) {
|
||||
if (descriptor != null) {
|
||||
workspace.setStatus(descriptor.getRuntimeStatus());
|
||||
workspace.setRuntime(descriptor.getRuntime());
|
||||
workspace.setRuntime(new WorkspaceRuntimeImpl(descriptor.getRuntime()));
|
||||
} else {
|
||||
workspace.setStatus(STOPPED);
|
||||
}
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load Diff
|
|
@ -28,9 +28,7 @@ import org.eclipse.che.api.core.ServerException;
|
|||
import org.eclipse.che.api.core.rest.Service;
|
||||
import org.eclipse.che.api.core.rest.annotations.GenerateLink;
|
||||
import org.eclipse.che.api.machine.server.model.impl.CommandImpl;
|
||||
import org.eclipse.che.api.machine.server.model.impl.MachineImpl;
|
||||
import org.eclipse.che.api.machine.shared.dto.CommandDto;
|
||||
import org.eclipse.che.api.machine.shared.dto.MachineConfigDto;
|
||||
import org.eclipse.che.api.machine.shared.dto.SnapshotDto;
|
||||
import org.eclipse.che.api.workspace.server.model.impl.EnvironmentImpl;
|
||||
import org.eclipse.che.api.workspace.server.model.impl.ProjectConfigImpl;
|
||||
|
|
@ -53,7 +51,6 @@ import javax.ws.rs.PathParam;
|
|||
import javax.ws.rs.Produces;
|
||||
import javax.ws.rs.QueryParam;
|
||||
import javax.ws.rs.core.Context;
|
||||
import javax.ws.rs.core.MediaType;
|
||||
import javax.ws.rs.core.Response;
|
||||
import javax.ws.rs.core.SecurityContext;
|
||||
import java.util.List;
|
||||
|
|
@ -235,9 +232,7 @@ public class WorkspaceService extends Service {
|
|||
NotFoundException,
|
||||
ConflictException,
|
||||
ForbiddenException {
|
||||
if (!workspaceManager.getSnapshot(id).isEmpty()) {
|
||||
workspaceManager.removeSnapshots(id);
|
||||
}
|
||||
workspaceManager.removeSnapshots(id);
|
||||
workspaceManager.removeWorkspace(id);
|
||||
}
|
||||
|
||||
|
|
@ -355,10 +350,12 @@ public class WorkspaceService extends Service {
|
|||
"The snapshot doesn't exist for the workspace"),
|
||||
@ApiResponse(code = 403, message = "The user is not workspace owner"),
|
||||
@ApiResponse(code = 500, message = "Internal server error occurred")})
|
||||
public List<SnapshotDto> getSnapshot(@ApiParam("The id of the workspace") @PathParam("id") String workspaceId) throws ServerException,
|
||||
BadRequestException,
|
||||
NotFoundException,
|
||||
ForbiddenException {
|
||||
public List<SnapshotDto> getSnapshot(@ApiParam("The id of the workspace") @PathParam("id") String workspaceId)
|
||||
throws ServerException,
|
||||
BadRequestException,
|
||||
NotFoundException,
|
||||
ForbiddenException {
|
||||
|
||||
return workspaceManager.getSnapshot(workspaceId)
|
||||
.stream()
|
||||
.map(DtoConverter::asDto)
|
||||
|
|
@ -450,9 +447,9 @@ public class WorkspaceService extends Service {
|
|||
if (workspace.getConfig().getCommands().removeIf(command -> command.getName().equals(commandName))) {
|
||||
workspaceManager.updateWorkspace(id, workspace);
|
||||
} else {
|
||||
throw new NotFoundException(
|
||||
String.format("Command with name '%s' was not found in workspace '%s'", commandName,
|
||||
workspace.getConfig().getName()));
|
||||
throw new NotFoundException(format("Command with name '%s' was not found in workspace '%s'",
|
||||
commandName,
|
||||
workspace.getConfig().getName()));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -632,44 +629,6 @@ public class WorkspaceService extends Service {
|
|||
}
|
||||
}
|
||||
|
||||
@POST
|
||||
@Path("/{id}/machine")
|
||||
@Consumes(MediaType.APPLICATION_JSON)
|
||||
@Produces(MediaType.APPLICATION_JSON)
|
||||
@ApiOperation(value = "Create a new machine based on the configuration",
|
||||
notes = "This operation can be performed only by authorized user")
|
||||
@ApiResponses({@ApiResponse(code = 201, message = "The machine successfully created"),
|
||||
@ApiResponse(code = 400, message = "Missed required parameters, parameters are not valid"),
|
||||
@ApiResponse(code = 403, message = "The user does not have access to create the new machine"),
|
||||
@ApiResponse(code = 409, message = "Conflict error occurred during the machine creation" +
|
||||
"(e.g. The machine with such name already exists)." +
|
||||
"Workspace is not in RUNNING state"),
|
||||
@ApiResponse(code = 500, message = "Internal server error occurred")})
|
||||
public Response createMachine(@ApiParam("The workspace id")
|
||||
@PathParam("id")
|
||||
String workspaceId,
|
||||
@ApiParam(value = "The new machine configuration", required = true)
|
||||
MachineConfigDto machineConfig) throws ForbiddenException,
|
||||
NotFoundException,
|
||||
ServerException,
|
||||
ConflictException,
|
||||
BadRequestException {
|
||||
requiredNotNull(machineConfig, "Machine configuration");
|
||||
requiredNotNull(machineConfig.getType(), "Machine type");
|
||||
requiredNotNull(machineConfig.getSource(), "Machine source");
|
||||
requiredNotNull(machineConfig.getSource().getType(), "Machine source type");
|
||||
// definition of source should come either with a content or with location
|
||||
requiredOnlyOneNotNull(machineConfig.getSource().getLocation(), machineConfig.getSource().getContent(),
|
||||
"Machine source should provide either location or content");
|
||||
|
||||
final MachineImpl machine = workspaceManager.startMachine(machineConfig, workspaceId);
|
||||
|
||||
return Response.status(201)
|
||||
.entity(linksInjector.injectMachineLinks(org.eclipse.che.api.machine.server.DtoConverter.asDto(machine),
|
||||
getServiceContext()))
|
||||
.build();
|
||||
}
|
||||
|
||||
private static Map<String, String> parseAttrs(List<String> attributes) throws BadRequestException {
|
||||
if (attributes == null) {
|
||||
return emptyMap();
|
||||
|
|
@ -703,27 +662,6 @@ public class WorkspaceService extends Service {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks only one of the given object reference is {@code null}
|
||||
*
|
||||
* @param object1
|
||||
* object reference to check
|
||||
* @param object2
|
||||
* object reference to check
|
||||
* @param subject
|
||||
* used as subject of exception message "{subject} required"
|
||||
* @throws BadRequestException
|
||||
* when objects are both null or have both a value reference is {@code null}
|
||||
*/
|
||||
private void requiredOnlyOneNotNull(Object object1, Object object2, String subject) throws BadRequestException {
|
||||
if (object1 == null && object2 == null) {
|
||||
throw new BadRequestException(subject + " required");
|
||||
}
|
||||
if (object1 != null && object2 != null) {
|
||||
throw new BadRequestException(subject + " required");
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Validate composite key.
|
||||
*
|
||||
|
|
|
|||
|
|
@ -13,7 +13,7 @@ package org.eclipse.che.api.workspace.server;
|
|||
import org.eclipse.che.api.core.rest.ServiceContext;
|
||||
import org.eclipse.che.api.core.rest.shared.dto.Link;
|
||||
import org.eclipse.che.api.core.rest.shared.dto.LinkParameter;
|
||||
import org.eclipse.che.api.machine.server.MachineServiceLinksInjector;
|
||||
import org.eclipse.che.api.environment.server.MachineServiceLinksInjector;
|
||||
import org.eclipse.che.api.machine.shared.dto.MachineDto;
|
||||
import org.eclipse.che.api.machine.shared.dto.ServerDto;
|
||||
import org.eclipse.che.api.machine.shared.dto.SnapshotDto;
|
||||
|
|
@ -21,7 +21,6 @@ import org.eclipse.che.api.workspace.shared.dto.WorkspaceDto;
|
|||
import org.eclipse.che.api.workspace.shared.dto.WorkspaceRuntimeDto;
|
||||
|
||||
import javax.inject.Inject;
|
||||
import javax.inject.Named;
|
||||
import javax.inject.Singleton;
|
||||
import javax.ws.rs.core.UriBuilder;
|
||||
import java.net.URI;
|
||||
|
|
|
|||
|
|
@ -15,7 +15,6 @@ import org.eclipse.che.api.core.model.machine.Recipe;
|
|||
import org.eclipse.che.api.core.model.workspace.Environment;
|
||||
import org.eclipse.che.api.machine.server.model.impl.MachineConfigImpl;
|
||||
import org.eclipse.che.api.machine.server.recipe.RecipeImpl;
|
||||
import org.eclipse.che.commons.annotation.Nullable;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
|
@ -55,12 +54,19 @@ public class EnvironmentImpl implements Environment {
|
|||
return name;
|
||||
}
|
||||
|
||||
public void setName(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
@Override
|
||||
@Nullable
|
||||
public Recipe getRecipe() {
|
||||
return recipe;
|
||||
}
|
||||
|
||||
public void setRecipe(RecipeImpl recipe) {
|
||||
this.recipe = recipe;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<MachineConfigImpl> getMachineConfigs() {
|
||||
if (machineConfigs == null) {
|
||||
|
|
@ -69,23 +75,23 @@ public class EnvironmentImpl implements Environment {
|
|||
return machineConfigs;
|
||||
}
|
||||
|
||||
public void setMachineConfigs(List<MachineConfigImpl> machineConfigs) {
|
||||
this.machineConfigs = machineConfigs;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if (this == obj) return true;
|
||||
if (!(obj instanceof EnvironmentImpl)) return false;
|
||||
final EnvironmentImpl other = (EnvironmentImpl)obj;
|
||||
return Objects.equals(name, other.name) &&
|
||||
Objects.equals(recipe, other.recipe) &&
|
||||
getMachineConfigs().equals(other.getMachineConfigs());
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) return true;
|
||||
if (!(o instanceof EnvironmentImpl)) return false;
|
||||
EnvironmentImpl that = (EnvironmentImpl)o;
|
||||
return Objects.equals(name, that.name) &&
|
||||
Objects.equals(recipe, that.recipe) &&
|
||||
Objects.equals(machineConfigs, that.machineConfigs);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
int hash = 7;
|
||||
hash = hash * 31 + Objects.hashCode(name);
|
||||
hash = hash * 31 + Objects.hashCode(recipe);
|
||||
hash = hash * 31 + getMachineConfigs().hashCode();
|
||||
return hash;
|
||||
return Objects.hash(name, recipe, machineConfigs);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
|||
|
|
@ -95,28 +95,22 @@ public class WorkspaceRuntimeImpl implements WorkspaceRuntime {
|
|||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if (obj == this) {
|
||||
return true;
|
||||
}
|
||||
if (!(obj instanceof WorkspaceRuntimeImpl)) {
|
||||
return false;
|
||||
}
|
||||
final WorkspaceRuntimeImpl other = (WorkspaceRuntimeImpl)obj;
|
||||
return Objects.equals(activeEnv, other.activeEnv)
|
||||
&& Objects.equals(rootFolder, other.rootFolder)
|
||||
&& Objects.equals(devMachine, other.devMachine)
|
||||
&& getMachines().equals(getMachines());
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) return true;
|
||||
if (!(o instanceof WorkspaceRuntimeImpl)) return false;
|
||||
WorkspaceRuntimeImpl that = (WorkspaceRuntimeImpl)o;
|
||||
return Objects.equals(activeEnv, that.activeEnv) &&
|
||||
Objects.equals(rootFolder, that.rootFolder) &&
|
||||
Objects.equals(devMachine, that.devMachine) &&
|
||||
Objects.equals(machines, that.machines);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
int hash = 7;
|
||||
hash = hash * 31 + Objects.hashCode(activeEnv);
|
||||
hash = 31 * hash + Objects.hashCode(rootFolder);
|
||||
hash = 31 * hash + Objects.hashCode(devMachine);
|
||||
hash = 31 * hash + getMachines().hashCode();
|
||||
return hash;
|
||||
return Objects.hash(activeEnv,
|
||||
rootFolder,
|
||||
devMachine,
|
||||
machines);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@
|
|||
* Contributors:
|
||||
* Codenvy, S.A. - initial API and implementation
|
||||
*******************************************************************************/
|
||||
package org.eclipse.che.api.machine.server.wsagent;
|
||||
package org.eclipse.che.api.agent.server.wsagent;
|
||||
|
||||
import org.eclipse.che.api.core.BadRequestException;
|
||||
import org.eclipse.che.api.core.NotFoundException;
|
||||
|
|
@ -18,15 +18,16 @@ import org.eclipse.che.api.core.model.machine.Server;
|
|||
import org.eclipse.che.api.core.rest.HttpJsonRequest;
|
||||
import org.eclipse.che.api.core.rest.HttpJsonRequestFactory;
|
||||
import org.eclipse.che.api.core.rest.HttpJsonResponse;
|
||||
import org.eclipse.che.api.machine.server.MachineManager;
|
||||
import org.eclipse.che.api.environment.server.MachineProcessManager;
|
||||
import org.eclipse.che.api.machine.server.exception.MachineException;
|
||||
import org.eclipse.che.api.machine.server.model.impl.CommandImpl;
|
||||
import org.eclipse.che.api.machine.server.model.impl.MachineImpl;
|
||||
import org.eclipse.che.api.machine.server.model.impl.MachineRuntimeInfoImpl;
|
||||
import org.eclipse.che.api.machine.server.model.impl.ServerImpl;
|
||||
import org.eclipse.che.api.machine.shared.Constants;
|
||||
import org.eclipse.che.commons.test.SelfReturningAnswer;
|
||||
import org.eclipse.che.commons.test.mockito.answer.SelfReturningAnswer;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.Mockito;
|
||||
import org.mockito.testng.MockitoTestNGListener;
|
||||
import org.testng.annotations.BeforeMethod;
|
||||
import org.testng.annotations.Listeners;
|
||||
|
|
@ -42,31 +43,30 @@ 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.mock;
|
||||
import static org.mockito.Mockito.times;
|
||||
import static org.mockito.Mockito.verify;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
@Listeners(MockitoTestNGListener.class)
|
||||
public class WsAgentLauncherImplTest {
|
||||
private static final String WS_ID = "wsId";
|
||||
private static final String MACHINE_ID = "machineId";
|
||||
private static final String WS_AGENT_START_CMD_LINE = "cmdLine";
|
||||
private static final String WS_AGENT_PORT = Constants.WS_AGENT_PORT;
|
||||
private static final long WS_AGENT_MAX_START_TIME_MS = 1000;
|
||||
private static final long WS_AGENT_PING_DELAY_MS = 1;
|
||||
private static final int WS_AGENT_PING_CONN_TIMEOUT_MS = 1;
|
||||
private static final String WS_AGENT_SERVER_LOCATION = "ws-agent.com:456789/";
|
||||
private static final String WS_AGENT_SERVER_URL = "http://" + WS_AGENT_SERVER_LOCATION;
|
||||
private static final String MACHINE_ID = "machineId";
|
||||
private static final String WORKSPACE_ID = "testWorkspaceId";
|
||||
private static final String WS_AGENT_START_CMD_LINE = "cmdLine";
|
||||
private static final String WS_AGENT_PORT = Constants.WS_AGENT_PORT;
|
||||
private static final long WS_AGENT_MAX_START_TIME_MS = 1000;
|
||||
private static final long WS_AGENT_PING_DELAY_MS = 1;
|
||||
private static final int WS_AGENT_PING_CONN_TIMEOUT_MS = 1;
|
||||
private static final String WS_AGENT_SERVER_LOCATION = "ws-agent.com:456789/";
|
||||
private static final String WS_AGENT_SERVER_URL = "http://" + WS_AGENT_SERVER_LOCATION;
|
||||
private static final ServerImpl SERVER = new ServerImpl("ref",
|
||||
"http",
|
||||
WS_AGENT_SERVER_LOCATION,
|
||||
null,
|
||||
WS_AGENT_SERVER_URL);
|
||||
private static final String WS_AGENT_TIMED_OUT_MESSAGE = "timeout error message";
|
||||
private static final String WS_AGENT_TIMED_OUT_MESSAGE = "timeout error message";
|
||||
|
||||
@Mock
|
||||
private MachineManager machineManager;
|
||||
private MachineProcessManager machineProcessManager;
|
||||
@Mock
|
||||
private HttpJsonRequestFactory requestFactory;
|
||||
@Mock
|
||||
|
|
@ -81,16 +81,16 @@ public class WsAgentLauncherImplTest {
|
|||
|
||||
@BeforeMethod
|
||||
public void setUp() throws Exception {
|
||||
wsAgentLauncher = new WsAgentLauncherImpl(() -> machineManager,
|
||||
wsAgentLauncher = new WsAgentLauncherImpl(() -> machineProcessManager,
|
||||
requestFactory,
|
||||
WS_AGENT_START_CMD_LINE,
|
||||
WS_AGENT_MAX_START_TIME_MS,
|
||||
WS_AGENT_PING_DELAY_MS,
|
||||
WS_AGENT_PING_CONN_TIMEOUT_MS,
|
||||
WS_AGENT_TIMED_OUT_MESSAGE);
|
||||
pingRequest = mock(HttpJsonRequest.class, new SelfReturningAnswer());
|
||||
when(machineManager.getDevMachine(WS_ID)).thenReturn(machine);
|
||||
pingRequest = Mockito.mock(HttpJsonRequest.class, new SelfReturningAnswer());
|
||||
when(machine.getId()).thenReturn(MACHINE_ID);
|
||||
when(machine.getWorkspaceId()).thenReturn(WORKSPACE_ID);
|
||||
when(machine.getRuntime()).thenReturn(machineRuntime);
|
||||
doReturn(Collections.<String, Server>singletonMap(WS_AGENT_PORT, SERVER)).when(machineRuntime).getServers();
|
||||
when(requestFactory.fromUrl(anyString())).thenReturn(pingRequest);
|
||||
|
|
@ -100,19 +100,20 @@ public class WsAgentLauncherImplTest {
|
|||
|
||||
@Test
|
||||
public void shouldStartWsAgentUsingMachineExec() throws Exception {
|
||||
wsAgentLauncher.startWsAgent(WS_ID);
|
||||
wsAgentLauncher.startWsAgent(machine);
|
||||
|
||||
verify(machineManager).exec(eq(MACHINE_ID),
|
||||
eq(new CommandImpl(WsAgentLauncherImpl.WS_AGENT_PROCESS_NAME,
|
||||
verify(machineProcessManager).exec(eq(WORKSPACE_ID),
|
||||
eq(MACHINE_ID),
|
||||
eq(new CommandImpl(WsAgentLauncherImpl.WS_AGENT_PROCESS_NAME,
|
||||
WS_AGENT_START_CMD_LINE,
|
||||
"Arbitrary")),
|
||||
eq(WsAgentLauncherImpl.getWsAgentProcessOutputChannel(WS_ID)));
|
||||
eq(WsAgentLauncherImpl.getWsAgentProcessOutputChannel(WORKSPACE_ID)));
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldPingWsAgentAfterStart() throws Exception {
|
||||
wsAgentLauncher.startWsAgent(WS_ID);
|
||||
wsAgentLauncher.startWsAgent(machine);
|
||||
|
||||
verify(requestFactory).fromUrl(UriBuilder.fromUri(WS_AGENT_SERVER_URL)
|
||||
.build()
|
||||
|
|
@ -130,7 +131,7 @@ public class WsAgentLauncherImplTest {
|
|||
new IOException())
|
||||
.thenReturn(pingResponse);
|
||||
|
||||
wsAgentLauncher.startWsAgent(WS_ID);
|
||||
wsAgentLauncher.startWsAgent(machine);
|
||||
|
||||
verify(requestFactory).fromUrl(WS_AGENT_SERVER_URL);
|
||||
verify(pingRequest).setMethod(HttpMethod.GET);
|
||||
|
|
@ -145,7 +146,7 @@ public class WsAgentLauncherImplTest {
|
|||
HttpURLConnection.HTTP_NO_CONTENT,
|
||||
HttpURLConnection.HTTP_OK);
|
||||
|
||||
wsAgentLauncher.startWsAgent(WS_ID);
|
||||
wsAgentLauncher.startWsAgent(machine);
|
||||
|
||||
verify(requestFactory).fromUrl(WS_AGENT_SERVER_URL);
|
||||
verify(pingRequest).setMethod(HttpMethod.GET);
|
||||
|
|
@ -159,57 +160,58 @@ public class WsAgentLauncherImplTest {
|
|||
when(pingRequest.request()).thenThrow(new ServerException(""))
|
||||
.thenReturn(pingResponse);
|
||||
|
||||
wsAgentLauncher.startWsAgent(WS_ID);
|
||||
wsAgentLauncher.startWsAgent(machine);
|
||||
|
||||
verify(pingRequest, times(2)).request();
|
||||
verify(pingResponse).getResponseCode();
|
||||
}
|
||||
|
||||
@Test(expectedExceptions = NotFoundException.class, expectedExceptionsMessageRegExp = "Test exception")
|
||||
public void shouldThrowNotFoundExceptionIfMachineManagerGetDevMachineForWsThrowsNotFoundException() throws Exception {
|
||||
final String notExistingWsId = "notExistingWsId";
|
||||
when(machineManager.getDevMachine(notExistingWsId)).thenThrow(new NotFoundException("Test exception"));
|
||||
|
||||
wsAgentLauncher.startWsAgent(notExistingWsId);
|
||||
|
||||
verify(machineManager).getDevMachine(eq(notExistingWsId));
|
||||
}
|
||||
|
||||
@Test(expectedExceptions = MachineException.class, expectedExceptionsMessageRegExp = "Test exception")
|
||||
public void shouldThrowMachineExceptionIfMachineManagerGetDevMachineForWsThrowsMachineException() throws Exception {
|
||||
final String notExistingWsId = "notExistingWsId";
|
||||
when(machineManager.getDevMachine(notExistingWsId)).thenThrow(new MachineException("Test exception"));
|
||||
|
||||
wsAgentLauncher.startWsAgent(notExistingWsId);
|
||||
|
||||
verify(machineManager).getDevMachine(eq(notExistingWsId));
|
||||
}
|
||||
|
||||
@Test(expectedExceptions = NotFoundException.class, expectedExceptionsMessageRegExp = "Test exception")
|
||||
public void shouldThrowNotFoundExceptionIfMachineManagerExecInDevMachineThrowsNotFoundException() throws Exception {
|
||||
when(machineManager.exec(anyString(), any(Command.class), anyString())).thenThrow(new NotFoundException("Test exception"));
|
||||
when(machineProcessManager.exec(anyString(),
|
||||
anyString(),
|
||||
any(Command.class),
|
||||
anyString()))
|
||||
.thenThrow(new NotFoundException("Test exception"));
|
||||
|
||||
wsAgentLauncher.startWsAgent(WS_ID);
|
||||
wsAgentLauncher.startWsAgent(machine);
|
||||
|
||||
verify(machineManager).exec(anyString(), any(Command.class), anyString());
|
||||
verify(machineProcessManager).exec(anyString(),
|
||||
anyString(),
|
||||
any(Command.class),
|
||||
anyString());
|
||||
}
|
||||
|
||||
@Test(expectedExceptions = MachineException.class, expectedExceptionsMessageRegExp = "Test exception")
|
||||
public void shouldThrowMachineExceptionIfMachineManagerExecInDevMachineThrowsMachineException() throws Exception {
|
||||
when(machineManager.exec(anyString(), any(Command.class), anyString())).thenThrow(new MachineException("Test exception"));
|
||||
when(machineProcessManager.exec(anyString(),
|
||||
anyString(),
|
||||
any(Command.class),
|
||||
anyString()))
|
||||
.thenThrow(new MachineException("Test exception"));
|
||||
|
||||
wsAgentLauncher.startWsAgent(WS_ID);
|
||||
wsAgentLauncher.startWsAgent(machine);
|
||||
|
||||
verify(machineManager).exec(anyString(), any(Command.class), anyString());
|
||||
verify(machineProcessManager).exec(anyString(),
|
||||
anyString(),
|
||||
any(Command.class),
|
||||
anyString());
|
||||
}
|
||||
|
||||
@Test(expectedExceptions = MachineException.class, expectedExceptionsMessageRegExp = "Test exception")
|
||||
public void shouldThrowMachineExceptionIfMachineManagerExecInDevMachineThrowsBadRequestException() throws Exception {
|
||||
when(machineManager.exec(anyString(), any(Command.class), anyString())).thenThrow(new BadRequestException("Test exception"));
|
||||
@Test(expectedExceptions = ServerException.class, expectedExceptionsMessageRegExp = "Test exception")
|
||||
public void shouldThrowExceptionIfMachineManagerExecInDevMachineThrowsBadRequestException() throws Exception {
|
||||
when(machineProcessManager.exec(anyString(),
|
||||
anyString(),
|
||||
any(Command.class),
|
||||
anyString()))
|
||||
.thenThrow(new BadRequestException("Test exception"));
|
||||
|
||||
wsAgentLauncher.startWsAgent(WS_ID);
|
||||
wsAgentLauncher.startWsAgent(machine);
|
||||
|
||||
verify(machineManager).exec(anyString(), any(Command.class), anyString());
|
||||
verify(machineProcessManager).exec(anyString(),
|
||||
anyString(),
|
||||
any(Command.class),
|
||||
anyString());
|
||||
}
|
||||
|
||||
@Test(expectedExceptions = ServerException.class,
|
||||
|
|
@ -217,14 +219,14 @@ public class WsAgentLauncherImplTest {
|
|||
public void shouldThrowMachineExceptionIfPingsWereUnsuccessfulTooLong() throws Exception {
|
||||
when(pingRequest.request()).thenThrow(new ServerException(""));
|
||||
|
||||
wsAgentLauncher.startWsAgent(WS_ID);
|
||||
wsAgentLauncher.startWsAgent(machine);
|
||||
}
|
||||
|
||||
@Test(expectedExceptions = MachineException.class,
|
||||
@Test(expectedExceptions = ServerException.class,
|
||||
expectedExceptionsMessageRegExp = "Workspace agent server not found in dev machine.")
|
||||
public void shouldThrowMachineExceptionIfwsAgentNotFound() throws Exception {
|
||||
public void shouldThrowExceptionIfWsAgentNotFound() throws Exception {
|
||||
doReturn(Collections.emptyMap()).when(machineRuntime).getServers();
|
||||
|
||||
wsAgentLauncher.startWsAgent(WS_ID);
|
||||
wsAgentLauncher.startWsAgent(machine);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,484 @@
|
|||
/*******************************************************************************
|
||||
* Copyright (c) 2012-2016 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.environment.server;
|
||||
|
||||
import org.eclipse.che.api.core.ConflictException;
|
||||
import org.eclipse.che.api.core.NotFoundException;
|
||||
import org.eclipse.che.api.core.model.machine.Machine;
|
||||
import org.eclipse.che.api.core.model.machine.MachineLogMessage;
|
||||
import org.eclipse.che.api.core.notification.EventService;
|
||||
import org.eclipse.che.api.core.util.LineConsumer;
|
||||
import org.eclipse.che.api.core.util.MessageConsumer;
|
||||
import org.eclipse.che.api.environment.server.exception.EnvironmentNotRunningException;
|
||||
import org.eclipse.che.api.machine.server.MachineInstanceProviders;
|
||||
import org.eclipse.che.api.machine.server.dao.SnapshotDao;
|
||||
import org.eclipse.che.api.machine.server.exception.MachineException;
|
||||
import org.eclipse.che.api.machine.server.model.impl.LimitsImpl;
|
||||
import org.eclipse.che.api.machine.server.model.impl.MachineConfigImpl;
|
||||
import org.eclipse.che.api.machine.server.model.impl.MachineSourceImpl;
|
||||
import org.eclipse.che.api.machine.server.model.impl.SnapshotImpl;
|
||||
import org.eclipse.che.api.machine.server.spi.Instance;
|
||||
import org.eclipse.che.api.machine.server.spi.InstanceProvider;
|
||||
import org.eclipse.che.api.machine.shared.dto.event.MachineStatusEvent;
|
||||
import org.eclipse.che.api.workspace.server.model.impl.EnvironmentImpl;
|
||||
import org.eclipse.che.commons.env.EnvironmentContext;
|
||||
import org.eclipse.che.commons.subject.SubjectImpl;
|
||||
import org.mockito.ArgumentCaptor;
|
||||
import org.mockito.InOrder;
|
||||
import org.mockito.Mock;
|
||||
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 java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import java.util.UUID;
|
||||
|
||||
import static org.eclipse.che.dto.server.DtoFactory.newDto;
|
||||
import static org.mockito.Matchers.any;
|
||||
import static org.mockito.Mockito.doReturn;
|
||||
import static org.mockito.Mockito.doThrow;
|
||||
import static org.mockito.Mockito.inOrder;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.spy;
|
||||
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.assertTrue;
|
||||
|
||||
/**
|
||||
* @author Alexander Garagatyi
|
||||
*/
|
||||
@Listeners(MockitoTestNGListener.class)
|
||||
public class CheEnvironmentEngineTest {
|
||||
@Mock
|
||||
MessageConsumer<MachineLogMessage> messageConsumer;
|
||||
@Mock
|
||||
InstanceProvider instanceProvider;
|
||||
|
||||
@Mock
|
||||
SnapshotDao snapshotDao;
|
||||
@Mock
|
||||
MachineInstanceProviders machineInstanceProviders;
|
||||
@Mock
|
||||
EventService eventService;
|
||||
|
||||
CheEnvironmentEngine engine;
|
||||
|
||||
@BeforeMethod
|
||||
public void setUp() throws Exception {
|
||||
engine = spy(new CheEnvironmentEngine(snapshotDao,
|
||||
machineInstanceProviders,
|
||||
"/tmp",
|
||||
256,
|
||||
eventService));
|
||||
|
||||
when(machineInstanceProviders.getProvider("docker")).thenReturn(instanceProvider);
|
||||
when(instanceProvider.getRecipeTypes()).thenReturn(Collections.singleton("dockerfile"));
|
||||
|
||||
EnvironmentContext.getCurrent().setSubject(new SubjectImpl("name", "id", "token", false));
|
||||
}
|
||||
|
||||
@AfterMethod
|
||||
public void tearDown() throws Exception {
|
||||
EnvironmentContext.reset();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldBeAbleToGetMachinesOfEnv() throws Exception {
|
||||
// given
|
||||
List<Instance> instances = startEnv();
|
||||
String workspaceId = instances.get(0).getWorkspaceId();
|
||||
|
||||
// when
|
||||
List<Instance> actualMachines = engine.getMachines(workspaceId);
|
||||
|
||||
// then
|
||||
assertEquals(actualMachines, instances);
|
||||
}
|
||||
|
||||
@Test(expectedExceptions = EnvironmentNotRunningException.class,
|
||||
expectedExceptionsMessageRegExp = "Environment with ID '.*' is not found")
|
||||
public void shouldThrowExceptionOnGetMachinesIfEnvironmentIsNotFound() throws Exception {
|
||||
engine.getMachines("wsIdOfNotRunningEnv");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldBeAbleToGetMachineOfEnv() throws Exception {
|
||||
// given
|
||||
List<Instance> instances = startEnv();
|
||||
Instance instance = instances.get(0);
|
||||
String workspaceId = instance.getWorkspaceId();
|
||||
|
||||
// when
|
||||
Instance actualInstance = engine.getMachine(workspaceId, instance.getId());
|
||||
|
||||
// then
|
||||
assertEquals(actualInstance, instance);
|
||||
}
|
||||
|
||||
@Test(expectedExceptions = EnvironmentNotRunningException.class,
|
||||
expectedExceptionsMessageRegExp = "Environment with ID '.*' is not found")
|
||||
public void shouldThrowExceptionOnGetMachineIfEnvironmentIsNotFound() throws Exception {
|
||||
// when
|
||||
engine.getMachine("wsIdOfNotRunningEnv", "nonExistingInstanceId");
|
||||
}
|
||||
|
||||
@Test(expectedExceptions = NotFoundException.class,
|
||||
expectedExceptionsMessageRegExp = "Machine with ID .* is not found in the environment of workspace .*")
|
||||
public void shouldThrowExceptionOnGetMachineIfMachineIsNotFound() throws Exception {
|
||||
// given
|
||||
List<Instance> instances = startEnv();
|
||||
Instance instance = instances.get(0);
|
||||
String workspaceId = instance.getWorkspaceId();
|
||||
|
||||
// when
|
||||
engine.getMachine(workspaceId, "nonExistingInstanceId");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldBeAbleToStartEnvironment() throws Exception {
|
||||
// given
|
||||
EnvironmentImpl env = createEnv();
|
||||
String workspaceId = "wsId";
|
||||
List<Instance> expectedMachines = new ArrayList<>();
|
||||
when(instanceProvider.createInstance(any(Machine.class),
|
||||
any(LineConsumer.class)))
|
||||
.thenAnswer(invocationOnMock -> {
|
||||
Object[] arguments = invocationOnMock.getArguments();
|
||||
Machine machine = (Machine)arguments[0];
|
||||
Instance instance = spy(new NoOpMachineInstance(machine));
|
||||
expectedMachines.add(instance);
|
||||
return instance;
|
||||
});
|
||||
|
||||
// when
|
||||
List<Instance> machines = engine.start(workspaceId, env, false, messageConsumer);
|
||||
|
||||
// then
|
||||
assertEquals(machines, expectedMachines);
|
||||
verify(instanceProvider, times(env.getMachineConfigs().size()))
|
||||
.createInstance(any(Machine.class), any(LineConsumer.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void envStartShouldFireEvents() throws Exception {
|
||||
// when
|
||||
List<Instance> instances = startEnv();
|
||||
assertTrue(instances.size() > 1, "This test requires at least 2 instances in environment");
|
||||
|
||||
// then
|
||||
for (Instance instance : instances) {
|
||||
verify(eventService).publish(newDto(MachineStatusEvent.class)
|
||||
.withEventType(MachineStatusEvent.EventType.CREATING)
|
||||
.withDev(instance.getConfig().isDev())
|
||||
.withMachineName(instance.getConfig().getName())
|
||||
.withMachineId(instance.getId())
|
||||
.withWorkspaceId(instance.getWorkspaceId()));
|
||||
verify(eventService).publish(newDto(MachineStatusEvent.class)
|
||||
.withEventType(MachineStatusEvent.EventType.RUNNING)
|
||||
.withDev(instance.getConfig().isDev())
|
||||
.withMachineName(instance.getConfig().getName())
|
||||
.withMachineId(instance.getId())
|
||||
.withWorkspaceId(instance.getWorkspaceId()));
|
||||
}
|
||||
}
|
||||
|
||||
@Test(expectedExceptions = ConflictException.class,
|
||||
expectedExceptionsMessageRegExp = "Environment of workspace '.*' already exists")
|
||||
public void envStartShouldThrowsExceptionIfSameEnvironmentExists() throws Exception {
|
||||
// given
|
||||
List<Instance> instances = startEnv();
|
||||
Instance instance = instances.get(0);
|
||||
EnvironmentImpl env = createEnv();
|
||||
|
||||
// when
|
||||
engine.start(instance.getWorkspaceId(),
|
||||
env,
|
||||
false,
|
||||
messageConsumer);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldDestroyMachinesOnEnvStop() throws Exception {
|
||||
// given
|
||||
List<Instance> instances = startEnv();
|
||||
Instance instance = instances.get(0);
|
||||
|
||||
// when
|
||||
engine.stop(instance.getWorkspaceId());
|
||||
|
||||
// then
|
||||
for (Instance instance1 : instances) {
|
||||
verify(instance1).destroy();
|
||||
}
|
||||
}
|
||||
|
||||
@Test(expectedExceptions = EnvironmentNotRunningException.class,
|
||||
expectedExceptionsMessageRegExp = "Environment with ID '.*' is not found")
|
||||
public void shouldThrowExceptionOnEnvStopIfItIsNotRunning() throws Exception {
|
||||
engine.stop("wsIdOFNonExistingEnv");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void destroyOfMachineOnEnvStopShouldNotPreventStopOfOthers() throws Exception {
|
||||
// given
|
||||
List<Instance> instances = startEnv();
|
||||
Instance instance = instances.get(0);
|
||||
doThrow(new MachineException("test exception")).when(instance).destroy();
|
||||
assertTrue(instances.size() > 1, "This test requires at least 2 instances in environment");
|
||||
|
||||
// when
|
||||
engine.stop(instance.getWorkspaceId());
|
||||
|
||||
// then
|
||||
InOrder inOrder = inOrder(instances.toArray());
|
||||
for (Instance instance1 : instances) {
|
||||
inOrder.verify(instance1).destroy();
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldBeAbleToStartMachine() throws Exception {
|
||||
// given
|
||||
List<Instance> instances = startEnv();
|
||||
String workspaceId = instances.get(0).getWorkspaceId();
|
||||
|
||||
when(engine.generateMachineId()).thenReturn("newMachineId");
|
||||
Instance newMachine = mock(Instance.class);
|
||||
when(newMachine.getId()).thenReturn("newMachineId");
|
||||
when(newMachine.getWorkspaceId()).thenReturn(workspaceId);
|
||||
doReturn(newMachine).when(instanceProvider).createInstance(any(Machine.class), any(LineConsumer.class));
|
||||
|
||||
MachineConfigImpl config = createConfig(false);
|
||||
|
||||
// when
|
||||
Instance actualInstance = engine.startMachine(workspaceId, config);
|
||||
|
||||
// then
|
||||
assertEquals(actualInstance, newMachine);
|
||||
ArgumentCaptor<Machine> argumentCaptor = ArgumentCaptor.forClass(Machine.class);
|
||||
verify(instanceProvider, times(3)).createInstance(argumentCaptor.capture(), any(LineConsumer.class));
|
||||
assertEquals(argumentCaptor.getValue().getConfig(), config);
|
||||
}
|
||||
|
||||
@Test(expectedExceptions = EnvironmentNotRunningException.class,
|
||||
expectedExceptionsMessageRegExp = "Environment '.*' is not running")
|
||||
public void shouldThrowExceptionOnMachineStartIfEnvironmentIsNotRunning() throws Exception {
|
||||
MachineConfigImpl config = createConfig(false);
|
||||
|
||||
// when
|
||||
engine.startMachine("wsIdOfNotRunningEnv", config);
|
||||
}
|
||||
|
||||
@Test(expectedExceptions = ConflictException.class,
|
||||
expectedExceptionsMessageRegExp = "Machine with name '.*' already exists in environment of workspace '.*'")
|
||||
public void machineStartShouldThrowExceptionIfMachineWithTheSameNameAlreadyExistsInEnvironment() throws Exception {
|
||||
// given
|
||||
List<Instance> instances = startEnv();
|
||||
Instance instance = instances.get(0);
|
||||
|
||||
MachineConfigImpl config = createConfig(false);
|
||||
config.setName(instance.getConfig().getName());
|
||||
|
||||
// when
|
||||
engine.startMachine(instance.getWorkspaceId(), config);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void machineStartShouldPublishEvents() throws Exception {
|
||||
// given
|
||||
List<Instance> instances = startEnv();
|
||||
Instance instance = instances.get(0);
|
||||
|
||||
MachineConfigImpl config = createConfig(false);
|
||||
when(engine.generateMachineId()).thenReturn("newMachineId");
|
||||
|
||||
// when
|
||||
engine.startMachine(instance.getWorkspaceId(), config);
|
||||
|
||||
// then
|
||||
verify(eventService).publish(newDto(MachineStatusEvent.class)
|
||||
.withEventType(MachineStatusEvent.EventType.CREATING)
|
||||
.withDev(config.isDev())
|
||||
.withMachineName(config.getName())
|
||||
.withMachineId("newMachineId")
|
||||
.withWorkspaceId(instance.getWorkspaceId()));
|
||||
verify(eventService).publish(newDto(MachineStatusEvent.class)
|
||||
.withEventType(MachineStatusEvent.EventType.RUNNING)
|
||||
.withDev(config.isDev())
|
||||
.withMachineName(config.getName())
|
||||
.withMachineId("newMachineId")
|
||||
.withWorkspaceId(instance.getWorkspaceId()));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldBeAbleToStopMachine() throws Exception {
|
||||
// given
|
||||
List<Instance> instances = startEnv();
|
||||
Optional<Instance> instanceOpt = instances.stream()
|
||||
.filter(machine -> !machine.getConfig().isDev())
|
||||
.findAny();
|
||||
assertTrue(instanceOpt.isPresent(), "Required for test non-dev machine is not found");
|
||||
Instance instance = instanceOpt.get();
|
||||
|
||||
// when
|
||||
engine.stopMachine(instance.getWorkspaceId(), instance.getId());
|
||||
|
||||
// then
|
||||
verify(instance).destroy();
|
||||
}
|
||||
|
||||
@Test(expectedExceptions = EnvironmentNotRunningException.class,
|
||||
expectedExceptionsMessageRegExp = "Environment '.*' is not running")
|
||||
public void machineStopShouldThrowExceptionIfEnvDoesNotExist() throws Exception {
|
||||
engine.stopMachine("wsIdOfNotRunningEnv", "testMachineID");
|
||||
}
|
||||
|
||||
@Test(expectedExceptions = ConflictException.class,
|
||||
expectedExceptionsMessageRegExp = "Stop of dev machine is not allowed. Please, stop whole environment")
|
||||
public void devMachineStopShouldThrowException() throws Exception {
|
||||
// given
|
||||
List<Instance> instances = startEnv();
|
||||
Optional<Instance> instanceOpt = instances.stream()
|
||||
.filter(machine -> machine.getConfig().isDev())
|
||||
.findAny();
|
||||
assertTrue(instanceOpt.isPresent(), "Required for test dev machine is not found");
|
||||
Instance instance = instanceOpt.get();
|
||||
|
||||
// when
|
||||
engine.stopMachine(instance.getWorkspaceId(), instance.getId());
|
||||
}
|
||||
|
||||
@Test(expectedExceptions = NotFoundException.class,
|
||||
expectedExceptionsMessageRegExp = "Machine with ID '.*' is not found in environment of workspace '.*'")
|
||||
public void machineStopOfNonExistingMachineShouldThrowsException() throws Exception {
|
||||
// given
|
||||
List<Instance> instances = startEnv();
|
||||
Instance instance = instances.get(0);
|
||||
|
||||
// when
|
||||
engine.stopMachine(instance.getWorkspaceId(), "idOfNonExistingMachine");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void machineStopShouldFireEvents() throws Exception {
|
||||
// given
|
||||
List<Instance> instances = startEnv();
|
||||
Optional<Instance> instanceOpt = instances.stream()
|
||||
.filter(machine -> !machine.getConfig().isDev())
|
||||
.findAny();
|
||||
assertTrue(instanceOpt.isPresent(), "Required for test non-dev machine is not found");
|
||||
Instance instance = instanceOpt.get();
|
||||
|
||||
// when
|
||||
engine.stopMachine(instance.getWorkspaceId(), instance.getId());
|
||||
|
||||
// then
|
||||
verify(eventService).publish(newDto(MachineStatusEvent.class)
|
||||
.withEventType(MachineStatusEvent.EventType.CREATING)
|
||||
.withDev(instance.getConfig().isDev())
|
||||
.withMachineName(instance.getConfig().getName())
|
||||
.withMachineId(instance.getId())
|
||||
.withWorkspaceId(instance.getWorkspaceId()));
|
||||
verify(eventService).publish(newDto(MachineStatusEvent.class)
|
||||
.withEventType(MachineStatusEvent.EventType.RUNNING)
|
||||
.withDev(instance.getConfig().isDev())
|
||||
.withMachineName(instance.getConfig().getName())
|
||||
.withMachineId(instance.getId())
|
||||
.withWorkspaceId(instance.getWorkspaceId()));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldBeAbleToSaveMachineSnapshot() throws Exception {
|
||||
// given
|
||||
List<Instance> instances = startEnv();
|
||||
Instance instance = instances.get(0);
|
||||
doReturn(new MachineSourceImpl("someType").setContent("some content")).when(instance).saveToSnapshot();
|
||||
|
||||
// when
|
||||
engine.saveSnapshot("someNamespace", instance.getWorkspaceId(), instance.getId());
|
||||
|
||||
// then
|
||||
verify(instance).saveToSnapshot();
|
||||
}
|
||||
|
||||
@Test(expectedExceptions = EnvironmentNotRunningException.class,
|
||||
expectedExceptionsMessageRegExp = "Environment .*' is not running")
|
||||
public void shouldThrowExceptionOnSaveSnapshotIfEnvIsNotRunning() throws Exception {
|
||||
engine.saveSnapshot("someNamespace", "wsIdOfNotRunningEnv", "someId");
|
||||
}
|
||||
|
||||
@Test(expectedExceptions = NotFoundException.class,
|
||||
expectedExceptionsMessageRegExp = "Machine with id '.*' is not found in environment of workspace '.*'")
|
||||
public void shouldThrowExceptionOnSaveSnapshotIfMachineIsNotFound() throws Exception {
|
||||
// given
|
||||
List<Instance> instances = startEnv();
|
||||
Instance instance = instances.get(0);
|
||||
|
||||
// when
|
||||
engine.saveSnapshot("someNamespace", instance.getWorkspaceId(), "idOfNonExistingMachine");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldBeAbleToRemoveSnapshot() throws Exception {
|
||||
// given
|
||||
SnapshotImpl snapshot = mock(SnapshotImpl.class);
|
||||
MachineSourceImpl machineSource = mock(MachineSourceImpl.class);
|
||||
when(snapshot.getType()).thenReturn("docker");
|
||||
when(snapshot.getMachineSource()).thenReturn(machineSource);
|
||||
|
||||
// when
|
||||
engine.removeSnapshot(snapshot);
|
||||
|
||||
// then
|
||||
verify(instanceProvider).removeInstanceSnapshot(machineSource);
|
||||
}
|
||||
|
||||
private List<Instance> startEnv() throws Exception {
|
||||
EnvironmentImpl env = createEnv();
|
||||
String workspaceId = "wsId";
|
||||
when(instanceProvider.createInstance(any(Machine.class),
|
||||
any(LineConsumer.class)))
|
||||
.thenAnswer(invocationOnMock -> {
|
||||
Object[] arguments = invocationOnMock.getArguments();
|
||||
Machine machine = (Machine)arguments[0];
|
||||
return spy(new NoOpMachineInstance(machine));
|
||||
});
|
||||
|
||||
// when
|
||||
return engine.start(workspaceId, env, false, messageConsumer);
|
||||
}
|
||||
|
||||
private static MachineConfigImpl createConfig(boolean isDev) {
|
||||
return MachineConfigImpl.builder()
|
||||
.setDev(isDev)
|
||||
.setType("docker")
|
||||
.setLimits(new LimitsImpl(1024))
|
||||
.setSource(new MachineSourceImpl("dockerfile").setLocation("location"))
|
||||
.setName(UUID.randomUUID().toString())
|
||||
.build();
|
||||
}
|
||||
|
||||
private EnvironmentImpl createEnv() {
|
||||
List<MachineConfigImpl> machines = new ArrayList<>();
|
||||
machines.add(createConfig(true));
|
||||
machines.add(createConfig(false));
|
||||
return new EnvironmentImpl("envName",
|
||||
null,
|
||||
machines);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,345 @@
|
|||
/*******************************************************************************
|
||||
* Copyright (c) 2012-2016 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.environment.server;
|
||||
|
||||
import org.eclipse.che.api.machine.server.MachineInstanceProviders;
|
||||
import org.eclipse.che.api.machine.shared.dto.MachineConfigDto;
|
||||
import org.eclipse.che.api.machine.shared.dto.MachineSourceDto;
|
||||
import org.eclipse.che.api.machine.shared.dto.ServerConfDto;
|
||||
import org.eclipse.che.api.workspace.shared.dto.EnvironmentDto;
|
||||
import org.eclipse.che.api.workspace.shared.dto.RecipeDto;
|
||||
import org.mockito.InjectMocks;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.testng.MockitoTestNGListener;
|
||||
import org.testng.annotations.BeforeMethod;
|
||||
import org.testng.annotations.DataProvider;
|
||||
import org.testng.annotations.Listeners;
|
||||
import org.testng.annotations.Test;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
|
||||
import static java.util.Collections.singletonList;
|
||||
import static java.util.Collections.singletonMap;
|
||||
import static org.eclipse.che.dto.server.DtoFactory.newDto;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
/**
|
||||
* @author Alexander Garagatyi
|
||||
*/
|
||||
@Listeners(MockitoTestNGListener.class)
|
||||
public class CheEnvironmentValidatorTest {
|
||||
@Mock
|
||||
MachineInstanceProviders machineInstanceProviders;
|
||||
@InjectMocks
|
||||
CheEnvironmentValidator environmentValidator;
|
||||
|
||||
@BeforeMethod
|
||||
public void prepare() throws Exception {
|
||||
when(machineInstanceProviders.hasProvider("docker")).thenReturn(true);
|
||||
when(machineInstanceProviders.getProviderTypes()).thenReturn(Arrays.asList("docker", "ssh"));
|
||||
}
|
||||
|
||||
|
||||
@Test(expectedExceptions = IllegalArgumentException.class,
|
||||
expectedExceptionsMessageRegExp = "Environment name should not be neither null nor empty")
|
||||
public void shouldFailValidationIfEnvNameIsNull() throws Exception {
|
||||
EnvironmentDto environment = createConfig();
|
||||
environment.setName(null);
|
||||
|
||||
|
||||
environmentValidator.validate(environment);
|
||||
}
|
||||
|
||||
@Test(expectedExceptions = IllegalArgumentException.class,
|
||||
expectedExceptionsMessageRegExp = "Environment name should not be neither null nor empty")
|
||||
public void shouldFailValidationIfEnvNameIsEmpty() throws Exception {
|
||||
EnvironmentDto environment = createConfig();
|
||||
environment.setName("");
|
||||
|
||||
|
||||
environmentValidator.validate(environment);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldNotFailValidationIfEnvironmentRecipeTypeIsDocker() throws Exception {
|
||||
EnvironmentDto config = createConfig();
|
||||
config.withRecipe(newDto(RecipeDto.class).withType("docker"));
|
||||
|
||||
|
||||
environmentValidator.validate(config);
|
||||
}
|
||||
|
||||
@Test(expectedExceptions = IllegalArgumentException.class,
|
||||
expectedExceptionsMessageRegExp = "Environment '.*' should contain at least 1 machine")
|
||||
public void shouldFailValidationIfMachinesListIsEmpty() throws Exception {
|
||||
EnvironmentDto config = createConfig();
|
||||
config.withMachineConfigs(null);
|
||||
|
||||
|
||||
environmentValidator.validate(config);
|
||||
}
|
||||
|
||||
@Test(expectedExceptions = IllegalArgumentException.class,
|
||||
expectedExceptionsMessageRegExp = "Environment '.*' should contain exactly 1 dev machine, but contains '0'")
|
||||
public void shouldFailValidationIfNoDevMachineFound() throws Exception {
|
||||
EnvironmentDto config = createConfig();
|
||||
config.getMachineConfigs()
|
||||
.stream()
|
||||
.filter(MachineConfigDto::isDev)
|
||||
.forEach(machine -> machine.withDev(false));
|
||||
|
||||
|
||||
environmentValidator.validate(config);
|
||||
}
|
||||
|
||||
@Test(expectedExceptions = IllegalArgumentException.class,
|
||||
expectedExceptionsMessageRegExp = "Environment '.*' should contain exactly 1 dev machine, but contains '2'")
|
||||
public void shouldFailValidationIf2DevMachinesFound() throws Exception {
|
||||
EnvironmentDto config = createConfig();
|
||||
final Optional<MachineConfigDto> devMachine = config.getMachineConfigs()
|
||||
.stream()
|
||||
.filter(MachineConfigDto::isDev)
|
||||
.findAny();
|
||||
config.getMachineConfigs()
|
||||
.add(devMachine.get().withName("other-name"));
|
||||
|
||||
|
||||
environmentValidator.validate(config);
|
||||
}
|
||||
|
||||
@Test(expectedExceptions = IllegalArgumentException.class,
|
||||
expectedExceptionsMessageRegExp = "Environment .* contains machine with null or empty name")
|
||||
public void shouldFailValidationIfMachineNameIsNull() throws Exception {
|
||||
EnvironmentDto config = createConfig();
|
||||
config.getMachineConfigs()
|
||||
.get(0)
|
||||
.withName(null);
|
||||
|
||||
|
||||
environmentValidator.validate(config);
|
||||
}
|
||||
|
||||
@Test(expectedExceptions = IllegalArgumentException.class,
|
||||
expectedExceptionsMessageRegExp = "Environment .* contains machine with null or empty name")
|
||||
public void shouldFailValidationIfMachineNameIsEmpty() throws Exception {
|
||||
EnvironmentDto config = createConfig();
|
||||
config.getMachineConfigs()
|
||||
.get(0)
|
||||
.withName("");
|
||||
|
||||
|
||||
environmentValidator.validate(config);
|
||||
}
|
||||
|
||||
@Test(expectedExceptions = IllegalArgumentException.class,
|
||||
expectedExceptionsMessageRegExp = "Machine '.*' in environment '.*' doesn't have source")
|
||||
public void shouldFailValidationIfMachineSourceIsNull() throws Exception {
|
||||
EnvironmentDto config = createConfig();
|
||||
config.getMachineConfigs()
|
||||
.get(0)
|
||||
.withSource(null);
|
||||
|
||||
|
||||
environmentValidator.validate(config);
|
||||
}
|
||||
|
||||
@Test(expectedExceptions = IllegalArgumentException.class,
|
||||
expectedExceptionsMessageRegExp = "Type 'null' of machine '.*' in environment '.*' is not supported. Supported values are: docker, ssh.")
|
||||
public void shouldFailValidationIfMachineTypeIsNull() throws Exception {
|
||||
EnvironmentDto config = createConfig();
|
||||
config.getMachineConfigs()
|
||||
.get(0)
|
||||
.withType(null);
|
||||
|
||||
|
||||
environmentValidator.validate(config);
|
||||
}
|
||||
|
||||
@Test(expectedExceptions = IllegalArgumentException.class,
|
||||
expectedExceptionsMessageRegExp = "Type 'compose' of machine '.*' in environment '.*' is not supported. Supported values are: docker, ssh.")
|
||||
public void shouldFailValidationIfMachineTypeIsNotDocker() throws Exception {
|
||||
EnvironmentDto config = createConfig();
|
||||
config.getMachineConfigs()
|
||||
.get(0)
|
||||
.withType("compose");
|
||||
|
||||
|
||||
environmentValidator.validate(config);
|
||||
}
|
||||
|
||||
@Test(expectedExceptions = IllegalArgumentException.class,
|
||||
expectedExceptionsMessageRegExp = "Machine .* contains server conf with invalid port .*",
|
||||
dataProvider = "invalidPortProvider")
|
||||
public void shouldFailValidationIfServerConfPortIsInvalid(String invalidPort) throws Exception {
|
||||
EnvironmentDto config = createConfig();
|
||||
config.getMachineConfigs()
|
||||
.get(0)
|
||||
.getServers()
|
||||
.add(newDto(ServerConfDto.class).withPort(invalidPort));
|
||||
|
||||
|
||||
environmentValidator.validate(config);
|
||||
}
|
||||
|
||||
@DataProvider(name = "invalidPortProvider")
|
||||
public static Object[][] invalidPortProvider() {
|
||||
return new Object[][] {
|
||||
{"0"},
|
||||
{"0123"},
|
||||
{"012/tcp"},
|
||||
{"8080"},
|
||||
{"8080/pct"},
|
||||
{"8080/pdu"},
|
||||
{"/tcp"},
|
||||
{"tcp"},
|
||||
{""},
|
||||
{"8080/tcp1"},
|
||||
{"8080/tcpp"},
|
||||
{"8080tcp"},
|
||||
{"8080/tc"},
|
||||
{"8080/ud"},
|
||||
{"8080/udpp"},
|
||||
{"8080/udp/"},
|
||||
{"8080/tcp/"},
|
||||
{"8080/tcp/udp"},
|
||||
{"8080/tcp/tcp"},
|
||||
{"8080/tcp/8080"},
|
||||
{null}
|
||||
};
|
||||
}
|
||||
|
||||
@Test(expectedExceptions = IllegalArgumentException.class,
|
||||
expectedExceptionsMessageRegExp = "Machine .* contains server conf with invalid protocol .*",
|
||||
dataProvider = "invalidProtocolProvider")
|
||||
public void shouldFailValidationIfServerConfProtocolIsInvalid(String invalidProtocol) throws Exception {
|
||||
EnvironmentDto config = createConfig();
|
||||
config.getMachineConfigs()
|
||||
.get(0)
|
||||
.getServers()
|
||||
.add(newDto(ServerConfDto.class).withPort("8080/tcp")
|
||||
.withProtocol(invalidProtocol));
|
||||
|
||||
|
||||
environmentValidator.validate(config);
|
||||
}
|
||||
|
||||
@DataProvider(name = "invalidProtocolProvider")
|
||||
public static Object[][] invalidProtocolProvider() {
|
||||
return new Object[][] {
|
||||
{""},
|
||||
{"http!"},
|
||||
{"2http"},
|
||||
{"http:"},
|
||||
};
|
||||
}
|
||||
|
||||
@Test(expectedExceptions = IllegalArgumentException.class,
|
||||
expectedExceptionsMessageRegExp = "Machine '.*' in environment '.*' contains environment variable with null or empty name")
|
||||
public void shouldFailValidationIfEnvVarNameIsNull() throws Exception {
|
||||
EnvironmentDto config = createConfig();
|
||||
config.getMachineConfigs()
|
||||
.get(0)
|
||||
.getEnvVariables()
|
||||
.put(null, "value");
|
||||
|
||||
|
||||
environmentValidator.validate(config);
|
||||
}
|
||||
|
||||
@Test(expectedExceptions = IllegalArgumentException.class,
|
||||
expectedExceptionsMessageRegExp = "Machine '.*' in environment '.*' contains environment variable with null or empty name")
|
||||
public void shouldFailValidationIfEnvVarNameIsEmpty() throws Exception {
|
||||
EnvironmentDto config = createConfig();
|
||||
config.getMachineConfigs()
|
||||
.get(0)
|
||||
.getEnvVariables()
|
||||
.put("", "value");
|
||||
|
||||
|
||||
environmentValidator.validate(config);
|
||||
}
|
||||
|
||||
@Test(expectedExceptions = IllegalArgumentException.class,
|
||||
expectedExceptionsMessageRegExp = "Machine '.*' in environment '.*' contains environment variable '.*' with null value")
|
||||
public void shouldFailValidationIfEnvVarValueIsNull() throws Exception {
|
||||
EnvironmentDto config = createConfig();
|
||||
config.getMachineConfigs()
|
||||
.get(0)
|
||||
.getEnvVariables()
|
||||
.put("key", null);
|
||||
|
||||
|
||||
environmentValidator.validate(config);
|
||||
}
|
||||
|
||||
@Test(expectedExceptions = IllegalArgumentException.class,
|
||||
expectedExceptionsMessageRegExp = "Source of machine '.*' in environment '.*' must contain location or content")
|
||||
public void shouldFailValidationIfMissingSourceLocationAndContent() throws Exception {
|
||||
EnvironmentDto config = createConfig();
|
||||
config.getMachineConfigs()
|
||||
.get(0)
|
||||
.withSource(newDto(MachineSourceDto.class).withType("dockerfile"));
|
||||
|
||||
environmentValidator.validate(config);
|
||||
}
|
||||
|
||||
@Test(expectedExceptions = IllegalArgumentException.class,
|
||||
expectedExceptionsMessageRegExp = "Environment '.*' contains machine '.*' with invalid source location: 'localhost'")
|
||||
public void shouldFailValidationIfLocationIsInvalidUrl() throws Exception {
|
||||
EnvironmentDto config = createConfig();
|
||||
config.getMachineConfigs()
|
||||
.get(0)
|
||||
.withSource(newDto(MachineSourceDto.class).withType("dockerfile").withLocation("localhost"));
|
||||
|
||||
environmentValidator.validate(config);
|
||||
}
|
||||
|
||||
@Test(expectedExceptions = IllegalArgumentException.class,
|
||||
expectedExceptionsMessageRegExp = "Environment '.*' contains machine '.*' with invalid source location protocol: ftp://localhost")
|
||||
public void shouldFailValidationIfLocationHasInvalidProtocol() throws Exception {
|
||||
EnvironmentDto config = createConfig();
|
||||
config.getMachineConfigs()
|
||||
.get(0)
|
||||
.withSource(newDto(MachineSourceDto.class).withType("dockerfile").withLocation("ftp://localhost"));
|
||||
|
||||
environmentValidator.validate(config);
|
||||
}
|
||||
|
||||
private EnvironmentDto createConfig() {
|
||||
final List<ServerConfDto> serversConf =
|
||||
new ArrayList<>(Arrays.asList(newDto(ServerConfDto.class).withRef("ref1")
|
||||
.withPort("8080/tcp")
|
||||
.withProtocol("https")
|
||||
.withPath("some/path"),
|
||||
newDto(ServerConfDto.class).withRef("ref2")
|
||||
.withPort("9090/udp")
|
||||
.withProtocol("protocol")
|
||||
.withPath("/some/path")));
|
||||
MachineConfigDto devMachine = newDto(MachineConfigDto.class).withDev(true)
|
||||
.withName("dev-machine")
|
||||
.withType("docker")
|
||||
.withSource(newDto(MachineSourceDto.class)
|
||||
.withLocation("http://location")
|
||||
.withType("dockerfile"))
|
||||
.withServers(serversConf)
|
||||
.withEnvVariables(new HashMap<>(
|
||||
singletonMap("key1", "value1")));
|
||||
|
||||
return newDto(EnvironmentDto.class).withName("dev-env")
|
||||
.withMachineConfigs(
|
||||
new ArrayList<>(singletonList(devMachine)))
|
||||
.withRecipe(null);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,131 @@
|
|||
/*******************************************************************************
|
||||
* Copyright (c) 2012-2016 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.environment.server;
|
||||
|
||||
import org.eclipse.che.api.core.model.machine.Command;
|
||||
import org.eclipse.che.api.core.notification.EventService;
|
||||
import org.eclipse.che.api.core.util.LineConsumer;
|
||||
import org.eclipse.che.api.machine.server.spi.Instance;
|
||||
import org.eclipse.che.api.machine.server.spi.InstanceProcess;
|
||||
import org.eclipse.che.commons.env.EnvironmentContext;
|
||||
import org.eclipse.che.commons.lang.IoUtil;
|
||||
import org.eclipse.che.commons.subject.SubjectImpl;
|
||||
import org.mockito.Mock;
|
||||
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 java.io.File;
|
||||
import java.net.URL;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.Paths;
|
||||
import java.util.concurrent.ThreadPoolExecutor;
|
||||
|
||||
import static org.mockito.Mockito.doReturn;
|
||||
import static org.mockito.Mockito.doThrow;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.spy;
|
||||
import static org.mockito.Mockito.verify;
|
||||
import static org.mockito.Mockito.when;
|
||||
import static org.testng.Assert.assertNotNull;
|
||||
|
||||
/**
|
||||
* Unit tests for {@link MachineProcessManager}
|
||||
*
|
||||
* @author Anton Korneta
|
||||
* @author Alexander Garagatyi
|
||||
*/
|
||||
@Listeners(MockitoTestNGListener.class)
|
||||
public class MachineProcessManagerTest {
|
||||
|
||||
private static final String USER_ID = "userId";
|
||||
private static final String MACHINE_ID = "machineId";
|
||||
private static final String WORKSPACE_ID = "testWorkspaceId";
|
||||
|
||||
private static final SubjectImpl CREATOR = new SubjectImpl("name", USER_ID, "token", false);
|
||||
|
||||
@Mock
|
||||
Instance instance;
|
||||
@Mock
|
||||
Command command;
|
||||
@Mock
|
||||
InstanceProcess instanceProcess;
|
||||
@Mock
|
||||
LineConsumer logConsumer;
|
||||
@Mock
|
||||
CheEnvironmentEngine environmentEngine;
|
||||
|
||||
private MachineProcessManager manager;
|
||||
|
||||
@BeforeMethod
|
||||
public void setUp() throws Exception {
|
||||
final EventService eventService = mock(EventService.class);
|
||||
final String machineLogsDir = targetDir().resolve("logs-dir").toString();
|
||||
IoUtil.deleteRecursive(new File(machineLogsDir));
|
||||
manager = spy(new MachineProcessManager(machineLogsDir,
|
||||
eventService,
|
||||
environmentEngine));
|
||||
|
||||
EnvironmentContext envCont = new EnvironmentContext();
|
||||
envCont.setSubject(CREATOR);
|
||||
EnvironmentContext.setCurrent(envCont);
|
||||
|
||||
doReturn(logConsumer).when(manager).getProcessLogger(MACHINE_ID, 111, "outputChannel");
|
||||
when(command.getCommandLine()).thenReturn("CommandLine");
|
||||
when(command.getName()).thenReturn("CommandName");
|
||||
when(command.getType()).thenReturn("CommandType");
|
||||
when(instance.createProcess(command, "outputChannel")).thenReturn(instanceProcess);
|
||||
when(instanceProcess.getPid()).thenReturn(111);
|
||||
when(environmentEngine.getMachine(WORKSPACE_ID, MACHINE_ID)).thenReturn(instance);
|
||||
}
|
||||
|
||||
@AfterMethod
|
||||
public void tearDown() throws Exception {
|
||||
EnvironmentContext.reset();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldCloseProcessLoggerIfExecIsSuccess() throws Exception {
|
||||
//when
|
||||
manager.exec(WORKSPACE_ID, MACHINE_ID, command, "outputChannel");
|
||||
waitForExecutorIsCompletedTask();
|
||||
|
||||
//then
|
||||
verify(logConsumer).close();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldCloseProcessLoggerIfExecFails() throws Exception {
|
||||
//given
|
||||
doThrow(Exception.class).when(instanceProcess).start();
|
||||
|
||||
//when
|
||||
manager.exec(WORKSPACE_ID, MACHINE_ID, command, "outputChannel");
|
||||
waitForExecutorIsCompletedTask();
|
||||
|
||||
//then
|
||||
verify(logConsumer).close();
|
||||
}
|
||||
|
||||
private void waitForExecutorIsCompletedTask() throws Exception {
|
||||
for (int i = 0; ((ThreadPoolExecutor)manager.executor).getCompletedTaskCount() == 0 && i < 10; i++) {
|
||||
Thread.sleep(300);
|
||||
}
|
||||
}
|
||||
|
||||
private static Path targetDir() throws Exception {
|
||||
final URL url = Thread.currentThread().getContextClassLoader().getResource(".");
|
||||
assertNotNull(url);
|
||||
return Paths.get(url.toURI()).getParent();
|
||||
}
|
||||
}
|
||||
|
|
@ -8,7 +8,7 @@
|
|||
* Contributors:
|
||||
* Codenvy, S.A. - initial API and implementation
|
||||
*******************************************************************************/
|
||||
package org.eclipse.che.api.machine.server;
|
||||
package org.eclipse.che.api.environment.server;
|
||||
|
||||
import com.google.common.collect.ImmutableMap;
|
||||
import com.google.common.collect.ImmutableSet;
|
||||
|
|
@ -20,7 +20,6 @@ import org.eclipse.che.api.machine.shared.dto.MachineDto;
|
|||
import org.eclipse.che.api.machine.shared.dto.MachineProcessDto;
|
||||
import org.eclipse.che.api.machine.shared.dto.MachineRuntimeInfoDto;
|
||||
import org.eclipse.che.api.machine.shared.dto.ServerDto;
|
||||
import org.eclipse.che.api.machine.shared.dto.SnapshotDto;
|
||||
import org.eclipse.che.commons.lang.Pair;
|
||||
import org.eclipse.che.dto.server.DtoFactory;
|
||||
import org.everrest.core.impl.uri.UriBuilderImpl;
|
||||
|
|
@ -36,17 +35,13 @@ import java.util.Set;
|
|||
import java.util.stream.Collectors;
|
||||
|
||||
import static java.util.Arrays.asList;
|
||||
import static org.eclipse.che.api.machine.shared.Constants.LINK_REL_ENVIRONMENT_OUTPUT_CHANNEL;
|
||||
import static org.eclipse.che.api.machine.shared.Constants.ENVIRONMENT_STATUS_CHANNEL_TEMPLATE;
|
||||
import static org.eclipse.che.api.machine.shared.Constants.LINK_REL_DESTROY_MACHINE;
|
||||
import static org.eclipse.che.api.machine.shared.Constants.LINK_REL_ENVIRONMENT_OUTPUT_CHANNEL;
|
||||
import static org.eclipse.che.api.machine.shared.Constants.LINK_REL_EXECUTE_COMMAND;
|
||||
import static org.eclipse.che.api.machine.shared.Constants.LINK_REL_GET_MACHINES;
|
||||
import static org.eclipse.che.api.machine.shared.Constants.LINK_REL_GET_MACHINE_LOGS;
|
||||
import static org.eclipse.che.api.machine.shared.Constants.LINK_REL_GET_PROCESSES;
|
||||
import static org.eclipse.che.api.machine.shared.Constants.LINK_REL_GET_PROCESS_LOGS;
|
||||
import static org.eclipse.che.api.machine.shared.Constants.LINK_REL_GET_SNAPSHOTS;
|
||||
import static org.eclipse.che.api.machine.shared.Constants.LINK_REL_REMOVE_SNAPSHOT;
|
||||
import static org.eclipse.che.api.machine.shared.Constants.LINK_REL_SAVE_SNAPSHOT;
|
||||
import static org.eclipse.che.api.machine.shared.Constants.LINK_REL_SELF;
|
||||
import static org.eclipse.che.api.machine.shared.Constants.LINK_REL_STOP_PROCESS;
|
||||
import static org.eclipse.che.api.machine.shared.Constants.TERMINAL_REFERENCE;
|
||||
|
|
@ -102,10 +97,7 @@ public class MachineServiceLinksInjectorTest {
|
|||
Pair.of("GET", LINK_REL_GET_MACHINES),
|
||||
Pair.of("POST", LINK_REL_EXECUTE_COMMAND),
|
||||
Pair.of("DELETE", LINK_REL_DESTROY_MACHINE),
|
||||
Pair.of("GET", LINK_REL_GET_SNAPSHOTS),
|
||||
Pair.of("GET", LINK_REL_GET_PROCESSES),
|
||||
Pair.of("POST", LINK_REL_SAVE_SNAPSHOT),
|
||||
Pair.of("GET", LINK_REL_GET_MACHINE_LOGS),
|
||||
Pair.of("GET", LINK_REL_ENVIRONMENT_OUTPUT_CHANNEL),
|
||||
Pair.of("GET", ENVIRONMENT_STATUS_CHANNEL_TEMPLATE)));
|
||||
|
||||
|
|
@ -115,7 +107,7 @@ public class MachineServiceLinksInjectorTest {
|
|||
@Test
|
||||
public void shouldInjectLinksIntoMachineProcessDto() {
|
||||
final MachineProcessDto machineProcessDto = DtoFactory.newDto(MachineProcessDto.class);
|
||||
machineLinksInjector.injectLinks(machineProcessDto, "machineId", serviceContextMock);
|
||||
machineLinksInjector.injectLinks(machineProcessDto, "workspaceId", "machineId", serviceContextMock);
|
||||
final Set<Pair<String, String>> links = machineProcessDto.getLinks()
|
||||
.stream()
|
||||
.map(link -> Pair.of(link.getMethod(), link.getRel()))
|
||||
|
|
@ -126,18 +118,4 @@ public class MachineServiceLinksInjectorTest {
|
|||
|
||||
assertEquals(links, expectedLinks, "Difference " + Sets.symmetricDifference(links, expectedLinks) + "\n");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldInjectLinksIntoSnapshotDto() {
|
||||
final SnapshotDto snapshotDto = DtoFactory.newDto(SnapshotDto.class)
|
||||
.withId("id");
|
||||
machineLinksInjector.injectLinks(snapshotDto, serviceContextMock);
|
||||
final Set<Pair<String, String>> links = snapshotDto.getLinks()
|
||||
.stream()
|
||||
.map(link -> Pair.of(link.getMethod(), link.getRel()))
|
||||
.collect(Collectors.toSet());
|
||||
final Set<Pair<String, String>> expectedLinks = ImmutableSet.of(Pair.of("DELETE", LINK_REL_REMOVE_SNAPSHOT));
|
||||
|
||||
assertEquals(links, expectedLinks, "Difference " + Sets.symmetricDifference(links, expectedLinks) + "\n");
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,142 @@
|
|||
/*******************************************************************************
|
||||
* Copyright (c) 2012-2016 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.environment.server;
|
||||
|
||||
import com.jayway.restassured.http.ContentType;
|
||||
import com.jayway.restassured.response.Response;
|
||||
|
||||
import org.eclipse.che.api.core.model.machine.MachineConfig;
|
||||
import org.eclipse.che.api.core.rest.ApiExceptionMapper;
|
||||
import org.eclipse.che.api.machine.server.model.impl.MachineConfigImpl;
|
||||
import org.eclipse.che.api.machine.server.model.impl.MachineSourceImpl;
|
||||
import org.eclipse.che.api.workspace.server.WorkspaceManager;
|
||||
import org.eclipse.che.api.workspace.server.WorkspaceServiceTest;
|
||||
import org.everrest.assured.EverrestJetty;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.testng.MockitoTestNGListener;
|
||||
import org.testng.annotations.BeforeMethod;
|
||||
import org.testng.annotations.DataProvider;
|
||||
import org.testng.annotations.Listeners;
|
||||
import org.testng.annotations.Test;
|
||||
|
||||
import static com.jayway.restassured.RestAssured.given;
|
||||
import static org.everrest.assured.JettyHttpServer.ADMIN_USER_NAME;
|
||||
import static org.everrest.assured.JettyHttpServer.ADMIN_USER_PASSWORD;
|
||||
import static org.everrest.assured.JettyHttpServer.SECURE_PATH;
|
||||
import static org.mockito.Matchers.any;
|
||||
import static org.mockito.Matchers.anyString;
|
||||
import static org.mockito.Matchers.eq;
|
||||
import static org.mockito.Mockito.never;
|
||||
import static org.mockito.Mockito.verify;
|
||||
import static org.testng.Assert.assertEquals;
|
||||
|
||||
/**
|
||||
* @author Alexander Garagatyi
|
||||
*/
|
||||
@Listeners(value = {EverrestJetty.class, MockitoTestNGListener.class})
|
||||
public class MachineServiceTest {
|
||||
@SuppressWarnings("unused")
|
||||
private static final ApiExceptionMapper MAPPER = new ApiExceptionMapper();
|
||||
@SuppressWarnings("unused")
|
||||
private static final WorkspaceServiceTest.EnvironmentFilter FILTER = new WorkspaceServiceTest.EnvironmentFilter();
|
||||
@Mock
|
||||
private WorkspaceManager wsManager;
|
||||
@Mock
|
||||
private MachineProcessManager machineProcessManager;
|
||||
|
||||
private MachineService service;
|
||||
|
||||
@BeforeMethod
|
||||
public void setup() {
|
||||
service = new MachineService(machineProcessManager,
|
||||
new MachineServiceLinksInjector(),
|
||||
wsManager);
|
||||
}
|
||||
|
||||
@Test(dataProvider = "illegalMachineConfigProvider")
|
||||
public void shouldReturnErrorOnStartMachineIfBodyIsInvalid(MachineConfig machineConfig) throws Exception {
|
||||
// given
|
||||
String workspaceId = "wsId";
|
||||
|
||||
// when
|
||||
final Response response = given().auth()
|
||||
.basic(ADMIN_USER_NAME, ADMIN_USER_PASSWORD)
|
||||
.when()
|
||||
.body(machineConfig)
|
||||
.contentType(ContentType.JSON)
|
||||
.post(SECURE_PATH + "/workspace/" + workspaceId + "/machine");
|
||||
|
||||
// then
|
||||
assertEquals(response.getStatusCode(), 400);
|
||||
verify(wsManager, never()).startMachine(any(MachineConfig.class), anyString());
|
||||
}
|
||||
|
||||
@DataProvider(name = "illegalMachineConfigProvider")
|
||||
public static Object[][] illegalMachineConfigProvider() {
|
||||
MachineConfigImpl.MachineConfigImplBuilder builder =
|
||||
MachineConfigImpl.builder()
|
||||
.setDev(false)
|
||||
.setName("name")
|
||||
.setType("type")
|
||||
.setSource(new MachineSourceImpl("type1").setContent("content"));
|
||||
return new Object[][] {
|
||||
{builder.setType(null)
|
||||
.build()},
|
||||
{builder.setSource(null)
|
||||
.build()},
|
||||
{builder.setSource(new MachineSourceImpl((String)null).setContent("content"))
|
||||
.build()},
|
||||
{builder.setSource(new MachineSourceImpl("type").setContent("content")
|
||||
.setLocation("location"))
|
||||
.build()},
|
||||
};
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldStartMachine() throws Exception {
|
||||
// given
|
||||
String workspaceId = "wsId";
|
||||
MachineConfig machineConfig = MachineConfigImpl.builder()
|
||||
.setDev(false)
|
||||
.setName("name")
|
||||
.setType("type")
|
||||
.setSource(new MachineSourceImpl("type1").setContent("content"))
|
||||
.build();
|
||||
|
||||
// when
|
||||
final Response response = given().auth()
|
||||
.basic(ADMIN_USER_NAME, ADMIN_USER_PASSWORD)
|
||||
.when()
|
||||
.body(machineConfig)
|
||||
.contentType(ContentType.JSON)
|
||||
.post(SECURE_PATH + "/workspace/" + workspaceId + "/machine");
|
||||
|
||||
// then
|
||||
assertEquals(response.getStatusCode(), 204);
|
||||
verify(wsManager).startMachine(any(MachineConfig.class), eq(workspaceId));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldStopMachine() throws Exception {
|
||||
// given
|
||||
String workspaceId = "wsId";
|
||||
String machineId = "mcId";
|
||||
|
||||
// when
|
||||
final Response response = given().auth()
|
||||
.basic(ADMIN_USER_NAME, ADMIN_USER_PASSWORD)
|
||||
.when()
|
||||
.delete(SECURE_PATH + "/workspace/" + workspaceId + "/machine/" + machineId);
|
||||
|
||||
// then
|
||||
assertEquals(response.getStatusCode(), 204);
|
||||
}
|
||||
}
|
||||
|
|
@ -11,33 +11,26 @@
|
|||
package org.eclipse.che.api.workspace.server;
|
||||
|
||||
import org.eclipse.che.api.core.BadRequestException;
|
||||
import org.eclipse.che.api.machine.server.MachineInstanceProviders;
|
||||
import org.eclipse.che.api.environment.server.CheEnvironmentValidator;
|
||||
import org.eclipse.che.api.machine.shared.dto.CommandDto;
|
||||
import org.eclipse.che.api.machine.shared.dto.MachineConfigDto;
|
||||
import org.eclipse.che.api.machine.shared.dto.MachineSourceDto;
|
||||
import org.eclipse.che.api.machine.shared.dto.ServerConfDto;
|
||||
import org.eclipse.che.api.workspace.server.model.impl.WorkspaceImpl;
|
||||
import org.eclipse.che.api.workspace.shared.dto.EnvironmentDto;
|
||||
import org.eclipse.che.api.workspace.shared.dto.RecipeDto;
|
||||
import org.eclipse.che.api.workspace.shared.dto.WorkspaceConfigDto;
|
||||
import org.mockito.InjectMocks;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.testng.MockitoTestNGListener;
|
||||
import org.testng.annotations.BeforeMethod;
|
||||
import org.testng.annotations.DataProvider;
|
||||
import org.testng.annotations.Listeners;
|
||||
import org.testng.annotations.Test;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
|
||||
import static java.util.Collections.emptyList;
|
||||
import static java.util.Collections.singletonList;
|
||||
import static java.util.Collections.singletonMap;
|
||||
import static org.eclipse.che.dto.server.DtoFactory.newDto;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
/**
|
||||
* Tests for {@link WorkspaceValidator} and {@link DefaultWorkspaceValidator}
|
||||
|
|
@ -48,15 +41,9 @@ import static org.mockito.Mockito.when;
|
|||
public class DefaultWorkspaceValidatorTest {
|
||||
|
||||
@Mock
|
||||
private MachineInstanceProviders machineInstanceProviders;
|
||||
CheEnvironmentValidator environmentValidator;
|
||||
@InjectMocks
|
||||
private DefaultWorkspaceValidator wsValidator;
|
||||
|
||||
@BeforeMethod
|
||||
public void prepare() throws Exception {
|
||||
when(machineInstanceProviders.hasProvider("docker")).thenReturn(true);
|
||||
when(machineInstanceProviders.getProviderTypes()).thenReturn(Arrays.asList(new String[] { "docker", "ssh" }));
|
||||
}
|
||||
DefaultWorkspaceValidator wsValidator;
|
||||
|
||||
@Test
|
||||
public void shouldValidateCorrectWorkspace() throws Exception {
|
||||
|
|
@ -184,161 +171,6 @@ public class DefaultWorkspaceValidatorTest {
|
|||
wsValidator.validateConfig(config);
|
||||
}
|
||||
|
||||
@Test(expectedExceptions = BadRequestException.class,
|
||||
expectedExceptionsMessageRegExp = "Environment name should be neither null nor empty")
|
||||
public void shouldFailValidationIfEnvNameIsNull() throws Exception {
|
||||
final WorkspaceConfigDto config = createConfig();
|
||||
config.getEnvironments()
|
||||
.add(newDto(EnvironmentDto.class).withName(null)
|
||||
.withMachineConfigs(config.getEnvironments()
|
||||
.get(0)
|
||||
.getMachineConfigs()));
|
||||
|
||||
|
||||
wsValidator.validateConfig(config);
|
||||
}
|
||||
|
||||
@Test(expectedExceptions = BadRequestException.class,
|
||||
expectedExceptionsMessageRegExp = "Environment name should be neither null nor empty")
|
||||
public void shouldFailValidationIfEnvNameIsEmpty() throws Exception {
|
||||
final WorkspaceConfigDto config = createConfig();
|
||||
config.getEnvironments()
|
||||
.add(newDto(EnvironmentDto.class).withName("")
|
||||
.withMachineConfigs(config.getEnvironments()
|
||||
.get(0)
|
||||
.getMachineConfigs()));
|
||||
|
||||
|
||||
wsValidator.validateConfig(config);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldNotFailValidationIfEnvironmentRecipeTypeIsDocker() throws Exception {
|
||||
final WorkspaceConfigDto config = createConfig();
|
||||
config.getEnvironments()
|
||||
.get(0)
|
||||
.withRecipe(newDto(RecipeDto.class).withType("docker"));
|
||||
|
||||
|
||||
wsValidator.validateConfig(config);
|
||||
}
|
||||
|
||||
@Test(expectedExceptions = BadRequestException.class,
|
||||
expectedExceptionsMessageRegExp = "Environment '.*' should contain at least 1 machine")
|
||||
public void shouldFailValidationIfMachinesListIsEmpty() throws Exception {
|
||||
final WorkspaceConfigDto config = createConfig();
|
||||
config.getEnvironments()
|
||||
.get(0)
|
||||
.withMachineConfigs(null);
|
||||
|
||||
|
||||
wsValidator.validateConfig(config);
|
||||
}
|
||||
|
||||
@Test(expectedExceptions = BadRequestException.class,
|
||||
expectedExceptionsMessageRegExp = "Environment should contain exactly 1 dev machine, but '.*' contains '0'")
|
||||
public void shouldFailValidationIfNoDevMachineFound() throws Exception {
|
||||
final WorkspaceConfigDto config = createConfig();
|
||||
config.getEnvironments()
|
||||
.get(0)
|
||||
.getMachineConfigs()
|
||||
.stream()
|
||||
.filter(MachineConfigDto::isDev)
|
||||
.forEach(machine -> machine.withDev(false));
|
||||
|
||||
|
||||
wsValidator.validateConfig(config);
|
||||
}
|
||||
|
||||
@Test(expectedExceptions = BadRequestException.class,
|
||||
expectedExceptionsMessageRegExp = "Environment should contain exactly 1 dev machine, but '.*' contains '2'")
|
||||
public void shouldFailValidationIf2DevMachinesFound() throws Exception {
|
||||
final WorkspaceConfigDto config = createConfig();
|
||||
final Optional<MachineConfigDto> devMachine = config.getEnvironments()
|
||||
.get(0)
|
||||
.getMachineConfigs()
|
||||
.stream()
|
||||
.filter(MachineConfigDto::isDev)
|
||||
.findAny();
|
||||
config.getEnvironments()
|
||||
.get(0)
|
||||
.getMachineConfigs()
|
||||
.add(devMachine.get().withName("other-name"));
|
||||
|
||||
|
||||
wsValidator.validateConfig(config);
|
||||
}
|
||||
|
||||
@Test(expectedExceptions = BadRequestException.class,
|
||||
expectedExceptionsMessageRegExp = "Environment .* contains machine with null or empty name")
|
||||
public void shouldFailValidationIfMachineNameIsNull() throws Exception {
|
||||
final WorkspaceConfigDto config = createConfig();
|
||||
config.getEnvironments()
|
||||
.get(0)
|
||||
.getMachineConfigs()
|
||||
.get(0)
|
||||
.withName(null);
|
||||
|
||||
|
||||
wsValidator.validateConfig(config);
|
||||
}
|
||||
|
||||
@Test(expectedExceptions = BadRequestException.class,
|
||||
expectedExceptionsMessageRegExp = "Environment .* contains machine with null or empty name")
|
||||
public void shouldFailValidationIfMachineNameIsEmpty() throws Exception {
|
||||
final WorkspaceConfigDto config = createConfig();
|
||||
config.getEnvironments()
|
||||
.get(0)
|
||||
.getMachineConfigs()
|
||||
.get(0)
|
||||
.withName("");
|
||||
|
||||
|
||||
wsValidator.validateConfig(config);
|
||||
}
|
||||
|
||||
@Test(expectedExceptions = BadRequestException.class,
|
||||
expectedExceptionsMessageRegExp = "Environment .* contains machine without source")
|
||||
public void shouldFailValidationIfMachineSourceIsNull() throws Exception {
|
||||
final WorkspaceConfigDto config = createConfig();
|
||||
config.getEnvironments()
|
||||
.get(0)
|
||||
.getMachineConfigs()
|
||||
.get(0)
|
||||
.withSource(null);
|
||||
|
||||
|
||||
wsValidator.validateConfig(config);
|
||||
}
|
||||
|
||||
@Test(expectedExceptions = BadRequestException.class,
|
||||
expectedExceptionsMessageRegExp = "Type .* of machine .* in environment .* is not supported. Supported values: docker, ssh.")
|
||||
public void shouldFailValidationIfMachineTypeIsNull() throws Exception {
|
||||
final WorkspaceConfigDto config = createConfig();
|
||||
config.getEnvironments()
|
||||
.get(0)
|
||||
.getMachineConfigs()
|
||||
.get(0)
|
||||
.withType(null);
|
||||
|
||||
|
||||
wsValidator.validateConfig(config);
|
||||
}
|
||||
|
||||
@Test(expectedExceptions = BadRequestException.class,
|
||||
expectedExceptionsMessageRegExp = "Type .* of machine .* in environment .* is not supported. Supported values: docker, ssh.")
|
||||
public void shouldFailValidationIfMachineTypeIsNotDocker() throws Exception {
|
||||
final WorkspaceConfigDto config = createConfig();
|
||||
config.getEnvironments()
|
||||
.get(0)
|
||||
.getMachineConfigs()
|
||||
.get(0)
|
||||
.withType("compose");
|
||||
|
||||
|
||||
wsValidator.validateConfig(config);
|
||||
}
|
||||
|
||||
@Test(expectedExceptions = BadRequestException.class,
|
||||
expectedExceptionsMessageRegExp = "Workspace ws-name contains command with null or empty name")
|
||||
public void shouldFailValidationIfCommandNameIsNull() throws Exception {
|
||||
|
|
@ -387,181 +219,12 @@ public class DefaultWorkspaceValidatorTest {
|
|||
wsValidator.validateConfig(config);
|
||||
}
|
||||
|
||||
@Test(expectedExceptions = BadRequestException.class,
|
||||
expectedExceptionsMessageRegExp = "Machine .* contains server conf with invalid port .*",
|
||||
dataProvider = "invalidPortProvider")
|
||||
public void shouldFailValidationIfServerConfPortIsInvalid(String invalidPort) throws Exception {
|
||||
final WorkspaceConfigDto config = createConfig();
|
||||
config.getEnvironments()
|
||||
.get(0)
|
||||
.getMachineConfigs()
|
||||
.get(0)
|
||||
.getServers()
|
||||
.add(newDto(ServerConfDto.class).withPort(invalidPort));
|
||||
|
||||
|
||||
wsValidator.validateConfig(config);
|
||||
}
|
||||
|
||||
@DataProvider(name = "invalidPortProvider")
|
||||
public static Object[][] invalidPortProvider() {
|
||||
return new Object[][] {
|
||||
{"0"},
|
||||
{"0123"},
|
||||
{"012/tcp"},
|
||||
{"8080"},
|
||||
{"8080/pct"},
|
||||
{"8080/pdu"},
|
||||
{"/tcp"},
|
||||
{"tcp"},
|
||||
{""},
|
||||
{"8080/tcp1"},
|
||||
{"8080/tcpp"},
|
||||
{"8080tcp"},
|
||||
{"8080/tc"},
|
||||
{"8080/ud"},
|
||||
{"8080/udpp"},
|
||||
{"8080/udp/"},
|
||||
{"8080/tcp/"},
|
||||
{"8080/tcp/udp"},
|
||||
{"8080/tcp/tcp"},
|
||||
{"8080/tcp/8080"},
|
||||
{null}
|
||||
};
|
||||
}
|
||||
|
||||
@Test(expectedExceptions = BadRequestException.class,
|
||||
expectedExceptionsMessageRegExp = "Machine .* contains server conf with invalid protocol .*",
|
||||
dataProvider = "invalidProtocolProvider")
|
||||
public void shouldFailValidationIfServerConfProtocolIsInvalid(String invalidProtocol) throws Exception {
|
||||
final WorkspaceConfigDto config = createConfig();
|
||||
config.getEnvironments()
|
||||
.get(0)
|
||||
.getMachineConfigs()
|
||||
.get(0)
|
||||
.getServers()
|
||||
.add(newDto(ServerConfDto.class).withPort("8080/tcp")
|
||||
.withProtocol(invalidProtocol));
|
||||
|
||||
|
||||
wsValidator.validateConfig(config);
|
||||
}
|
||||
|
||||
@DataProvider(name = "invalidProtocolProvider")
|
||||
public static Object[][] invalidProtocolProvider() {
|
||||
return new Object[][] {
|
||||
{""},
|
||||
{"http!"},
|
||||
{"2http"},
|
||||
{"http:"},
|
||||
};
|
||||
}
|
||||
|
||||
@Test(expectedExceptions = BadRequestException.class,
|
||||
expectedExceptionsMessageRegExp = "Machine %s contains environment variable with null or empty name")
|
||||
public void shouldFailValidationIfEnvVarNameIsNull() throws Exception {
|
||||
final WorkspaceConfigDto config = createConfig();
|
||||
config.getEnvironments()
|
||||
.get(0)
|
||||
.getMachineConfigs()
|
||||
.get(0)
|
||||
.getEnvVariables()
|
||||
.put(null, "value");
|
||||
|
||||
|
||||
wsValidator.validateConfig(config);
|
||||
}
|
||||
|
||||
@Test(expectedExceptions = BadRequestException.class,
|
||||
expectedExceptionsMessageRegExp = "Machine %s contains environment variable with null or empty name")
|
||||
public void shouldFailValidationIfEnvVarNameIsEmpty() throws Exception {
|
||||
final WorkspaceConfigDto config = createConfig();
|
||||
config.getEnvironments()
|
||||
.get(0)
|
||||
.getMachineConfigs()
|
||||
.get(0)
|
||||
.getEnvVariables()
|
||||
.put("", "value");
|
||||
|
||||
|
||||
wsValidator.validateConfig(config);
|
||||
}
|
||||
|
||||
@Test(expectedExceptions = BadRequestException.class,
|
||||
expectedExceptionsMessageRegExp = "Machine %s contains environment variable with null value")
|
||||
public void shouldFailValidationIfEnvVarValueIsNull() throws Exception {
|
||||
final WorkspaceConfigDto config = createConfig();
|
||||
config.getEnvironments()
|
||||
.get(0)
|
||||
.getMachineConfigs()
|
||||
.get(0)
|
||||
.getEnvVariables()
|
||||
.put("key", null);
|
||||
|
||||
|
||||
wsValidator.validateConfig(config);
|
||||
}
|
||||
|
||||
@Test(expectedExceptions = BadRequestException.class,
|
||||
expectedExceptionsMessageRegExp = "Environment dev-env contains machine with source but this source doesn't define a location or content")
|
||||
public void shouldFailValidationIfMissingLocationOrContent() throws Exception {
|
||||
final WorkspaceConfigDto config = createConfig();
|
||||
config.getEnvironments()
|
||||
.get(0)
|
||||
.getMachineConfigs()
|
||||
.get(0)
|
||||
.withSource(newDto(MachineSourceDto.class).withType("dockerfile"));
|
||||
|
||||
wsValidator.validateConfig(config);
|
||||
}
|
||||
|
||||
@Test(expectedExceptions = BadRequestException.class,
|
||||
expectedExceptionsMessageRegExp = "Environment dev-env contains machine with invalid source location: localhost")
|
||||
public void shouldFailValidationIfLocationIsInvalidUrl() throws Exception {
|
||||
final WorkspaceConfigDto config = createConfig();
|
||||
config.getEnvironments()
|
||||
.get(0)
|
||||
.getMachineConfigs()
|
||||
.get(0)
|
||||
.withSource(newDto(MachineSourceDto.class).withType("dockerfile").withLocation("localhost"));
|
||||
|
||||
wsValidator.validateConfig(config);
|
||||
}
|
||||
|
||||
@Test(expectedExceptions = BadRequestException.class,
|
||||
expectedExceptionsMessageRegExp = "Environment dev-env contains machine with invalid source location protocol: ftp://localhost")
|
||||
public void shouldFailValidationIfLocationHasInvalidProtocol() throws Exception {
|
||||
final WorkspaceConfigDto config = createConfig();
|
||||
config.getEnvironments()
|
||||
.get(0)
|
||||
.getMachineConfigs()
|
||||
.get(0)
|
||||
.withSource(newDto(MachineSourceDto.class).withType("dockerfile").withLocation("ftp://localhost"));
|
||||
|
||||
wsValidator.validateConfig(config);
|
||||
}
|
||||
|
||||
private static WorkspaceConfigDto createConfig() {
|
||||
final WorkspaceConfigDto workspaceConfigDto = newDto(WorkspaceConfigDto.class).withName("ws-name")
|
||||
.withDefaultEnv("dev-env");
|
||||
|
||||
final List<ServerConfDto> serversConf = new ArrayList<>(Arrays.asList(newDto(ServerConfDto.class).withRef("ref1")
|
||||
.withPort("8080/tcp")
|
||||
.withProtocol("https")
|
||||
.withPath("some/path"),
|
||||
newDto(ServerConfDto.class).withRef("ref2")
|
||||
.withPort("9090/udp")
|
||||
.withProtocol("protocol")
|
||||
.withPath("/some/path")));
|
||||
MachineConfigDto devMachine = newDto(MachineConfigDto.class).withDev(true)
|
||||
.withName("dev-machine")
|
||||
.withType("docker")
|
||||
.withSource(newDto(MachineSourceDto.class).withLocation("http://location")
|
||||
.withType("dockerfile"))
|
||||
.withServers(serversConf)
|
||||
.withEnvVariables(new HashMap<>(singletonMap("key1", "value1")));
|
||||
EnvironmentDto devEnv = newDto(EnvironmentDto.class).withName("dev-env")
|
||||
.withMachineConfigs(new ArrayList<>(singletonList(devMachine)))
|
||||
.withMachineConfigs(emptyList())
|
||||
.withRecipe(null);
|
||||
workspaceConfigDto.setEnvironments(new ArrayList<>(singletonList(devEnv)));
|
||||
|
||||
|
|
@ -569,7 +232,8 @@ public class DefaultWorkspaceValidatorTest {
|
|||
commandDtos.add(newDto(CommandDto.class).withName("command_name")
|
||||
.withType("maven")
|
||||
.withCommandLine("mvn clean install")
|
||||
.withAttributes(new HashMap<>(singletonMap("cmd-attribute-name", "cmd-attribute-value"))));
|
||||
.withAttributes(new HashMap<>(singletonMap("cmd-attribute-name",
|
||||
"cmd-attribute-value"))));
|
||||
workspaceConfigDto.setCommands(commandDtos);
|
||||
|
||||
return workspaceConfigDto;
|
||||
|
|
|
|||
|
|
@ -15,9 +15,9 @@ import org.eclipse.che.api.core.NotFoundException;
|
|||
import org.eclipse.che.api.core.model.workspace.WorkspaceConfig;
|
||||
import org.eclipse.che.api.core.model.workspace.WorkspaceStatus;
|
||||
import org.eclipse.che.api.core.notification.EventService;
|
||||
import org.eclipse.che.api.core.util.LineConsumer;
|
||||
import org.eclipse.che.api.machine.server.MachineManager;
|
||||
import org.eclipse.che.api.machine.server.exception.MachineException;
|
||||
import org.eclipse.che.api.environment.server.MachineProcessManager;
|
||||
import org.eclipse.che.api.machine.server.dao.SnapshotDao;
|
||||
import org.eclipse.che.api.machine.server.exception.SnapshotException;
|
||||
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.MachineRuntimeInfoImpl;
|
||||
|
|
@ -37,6 +37,7 @@ import org.eclipse.che.commons.subject.Subject;
|
|||
import org.eclipse.che.commons.subject.SubjectImpl;
|
||||
import org.mockito.ArgumentCaptor;
|
||||
import org.mockito.Captor;
|
||||
import org.mockito.InOrder;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.testng.MockitoTestNGListener;
|
||||
import org.testng.annotations.BeforeMethod;
|
||||
|
|
@ -44,6 +45,7 @@ import org.testng.annotations.Listeners;
|
|||
import org.testng.annotations.Test;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
|
||||
import static com.google.common.base.Strings.isNullOrEmpty;
|
||||
import static java.util.Arrays.asList;
|
||||
|
|
@ -62,6 +64,8 @@ import static org.mockito.Matchers.anyObject;
|
|||
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.inOrder;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.spy;
|
||||
import static org.mockito.Mockito.timeout;
|
||||
|
|
@ -70,7 +74,7 @@ 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.AssertJUnit.assertTrue;
|
||||
import static org.testng.Assert.assertTrue;
|
||||
|
||||
/**
|
||||
* Covers main cases of {@link WorkspaceManager}.
|
||||
|
|
@ -90,13 +94,15 @@ public class WorkspaceManagerTest {
|
|||
@Mock
|
||||
private WorkspaceValidator workspaceConfigValidator;
|
||||
@Mock
|
||||
private MachineManager client;
|
||||
private MachineProcessManager client;
|
||||
@Mock
|
||||
private WorkspaceHooks workspaceHooks;
|
||||
@Mock
|
||||
private MachineManager machineManager;
|
||||
private MachineProcessManager machineProcessManager;
|
||||
@Mock
|
||||
private WorkspaceRuntimes runtimes;
|
||||
@Mock
|
||||
private SnapshotDao snapshotDao;
|
||||
@Captor
|
||||
private ArgumentCaptor<WorkspaceImpl> workspaceCaptor;
|
||||
|
||||
|
|
@ -107,9 +113,9 @@ public class WorkspaceManagerTest {
|
|||
workspaceManager = spy(new WorkspaceManager(workspaceDao,
|
||||
runtimes,
|
||||
eventService,
|
||||
machineManager,
|
||||
false,
|
||||
false));
|
||||
false,
|
||||
snapshotDao));
|
||||
workspaceManager.setHooks(workspaceHooks);
|
||||
|
||||
when(workspaceDao.create(any(WorkspaceImpl.class))).thenAnswer(invocation -> invocation.getArguments()[0]);
|
||||
|
|
@ -349,26 +355,60 @@ public class WorkspaceManagerTest {
|
|||
public void shouldRecoverWorkspaceWhenRecoverParameterIsNullAndAutoRestoreAttributeIsSetAndSnapshotExists() throws Exception {
|
||||
final WorkspaceImpl workspace = workspaceManager.createWorkspace(createConfig(), "user123", "account");
|
||||
workspace.getAttributes().put(AUTO_RESTORE_FROM_SNAPSHOT, "true");
|
||||
when(machineManager.getSnapshots(any(), any())).thenReturn(singletonList(mock(SnapshotImpl.class)));
|
||||
when(workspaceDao.get(workspace.getId())).thenReturn(workspace);
|
||||
when(runtimes.get(any())).thenThrow(new NotFoundException(""));
|
||||
SnapshotImpl.SnapshotBuilder snapshotBuilder = SnapshotImpl.builder()
|
||||
.generateId()
|
||||
.setEnvName("env")
|
||||
.setDev(true)
|
||||
.setMachineName("machine1")
|
||||
.setNamespace(workspace.getNamespace())
|
||||
.setWorkspaceId(workspace.getId())
|
||||
.setType("docker")
|
||||
.setMachineSource(new MachineSourceImpl("image"));
|
||||
SnapshotImpl snapshot1 = snapshotBuilder.build();
|
||||
SnapshotImpl snapshot2 = snapshotBuilder.generateId()
|
||||
.setDev(false)
|
||||
.setMachineName("machine2")
|
||||
.build();
|
||||
when(snapshotDao.findSnapshots(workspace.getNamespace(), workspace.getId()))
|
||||
.thenReturn(asList(snapshot1, snapshot2));
|
||||
|
||||
workspaceManager.startWorkspace(workspace.getId(),
|
||||
workspace.getConfig().getDefaultEnv(),
|
||||
"account",
|
||||
null);
|
||||
|
||||
verify(runtimes, timeout(2000)).start(workspace, workspace.getConfig().getDefaultEnv(), true);
|
||||
verify(workspaceHooks, timeout(2000)).beforeStart(workspace, workspace.getConfig().getDefaultEnv(), "account");
|
||||
verify(runtimes, timeout(2000)).start(workspace,
|
||||
workspace.getConfig().getDefaultEnv(),
|
||||
true);
|
||||
verify(workspaceHooks, timeout(2000)).beforeStart(workspace,
|
||||
workspace.getConfig().getDefaultEnv(),
|
||||
"account");
|
||||
assertNotNull(workspace.getAttributes().get(UPDATED_ATTRIBUTE_NAME));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldRecoverWorkspaceWhenRecoverParameterIsTrueAndSnapshotExists() throws Exception {
|
||||
final WorkspaceImpl workspace = workspaceManager.createWorkspace(createConfig(), "user123", "account");
|
||||
when(machineManager.getSnapshots(any(), any())).thenReturn(singletonList(mock(SnapshotImpl.class)));
|
||||
when(workspaceDao.get(workspace.getId())).thenReturn(workspace);
|
||||
when(runtimes.get(any())).thenThrow(new NotFoundException(""));
|
||||
SnapshotImpl.SnapshotBuilder snapshotBuilder = SnapshotImpl.builder()
|
||||
.generateId()
|
||||
.setEnvName("env")
|
||||
.setDev(true)
|
||||
.setMachineName("machine1")
|
||||
.setNamespace(workspace.getNamespace())
|
||||
.setWorkspaceId(workspace.getId())
|
||||
.setType("docker")
|
||||
.setMachineSource(new MachineSourceImpl("image"));
|
||||
SnapshotImpl snapshot1 = snapshotBuilder.build();
|
||||
SnapshotImpl snapshot2 = snapshotBuilder.generateId()
|
||||
.setDev(false)
|
||||
.setMachineName("machine2")
|
||||
.build();
|
||||
when(snapshotDao.findSnapshots(workspace.getNamespace(), workspace.getId()))
|
||||
.thenReturn(asList(snapshot1, snapshot2));
|
||||
|
||||
workspaceManager.startWorkspace(workspace.getId(),
|
||||
workspace.getConfig().getDefaultEnv(),
|
||||
|
|
@ -417,7 +457,6 @@ public class WorkspaceManagerTest {
|
|||
public void shouldNotRecoverWorkspaceWhenRecoverParameterIsFalseAndAutoRestoreAttributeIsSetAndSnapshotExists() throws Exception {
|
||||
final WorkspaceImpl workspace = workspaceManager.createWorkspace(createConfig(), "user123", "account");
|
||||
workspace.getAttributes().put(AUTO_RESTORE_FROM_SNAPSHOT, "true");
|
||||
when(machineManager.getSnapshots(any(), any())).thenReturn(singletonList(mock(SnapshotImpl.class)));
|
||||
when(workspaceDao.get(workspace.getId())).thenReturn(workspace);
|
||||
when(runtimes.get(any())).thenThrow(new NotFoundException(""));
|
||||
|
||||
|
|
@ -555,8 +594,6 @@ public class WorkspaceManagerTest {
|
|||
when(workspaceDao.get(workspace.getId())).thenReturn(workspace);
|
||||
final RuntimeDescriptor descriptor = createDescriptor(workspace, RUNNING);
|
||||
when(runtimes.get(any())).thenReturn(descriptor);
|
||||
// force createSnapshotSync to return true
|
||||
when(machineManager.saveSync(anyString(), anyString(), anyString())).thenThrow(new MachineException("test"));
|
||||
|
||||
workspaceManager.stopWorkspace(workspace.getId());
|
||||
|
||||
|
|
@ -595,7 +632,7 @@ public class WorkspaceManagerTest {
|
|||
final String wsId = "workspace123";
|
||||
final WorkspaceImpl workspace = workspaceManager.createWorkspace(createConfig(), NAMESPACE, "account");
|
||||
when(workspaceDao.get(wsId)).thenReturn(workspace);
|
||||
when(machineManager.getSnapshots(NAMESPACE, wsId)).thenReturn(singletonList(any()));
|
||||
when(snapshotDao.findSnapshots(NAMESPACE, wsId)).thenReturn(singletonList(any()));
|
||||
|
||||
final List<SnapshotImpl> snapshots = workspaceManager.getSnapshot("workspace123");
|
||||
|
||||
|
|
@ -607,9 +644,9 @@ public class WorkspaceManagerTest {
|
|||
workspaceManager = spy(new WorkspaceManager(workspaceDao,
|
||||
runtimes,
|
||||
eventService,
|
||||
machineManager,
|
||||
true,
|
||||
false));
|
||||
false,
|
||||
snapshotDao));
|
||||
final WorkspaceImpl workspace = workspaceManager.createWorkspace(createConfig(), "user123", "account");
|
||||
when(workspaceDao.get(workspace.getId())).thenReturn(workspace);
|
||||
final RuntimeDescriptor descriptor = createDescriptor(workspace, RUNNING);
|
||||
|
|
@ -626,13 +663,28 @@ public class WorkspaceManagerTest {
|
|||
workspaceManager = spy(new WorkspaceManager(workspaceDao,
|
||||
runtimes,
|
||||
eventService,
|
||||
machineManager,
|
||||
false,
|
||||
true));
|
||||
true,
|
||||
snapshotDao));
|
||||
final WorkspaceImpl workspace = workspaceManager.createWorkspace(createConfig(), "user123", "account");
|
||||
when(machineManager.getSnapshots(any(), any())).thenReturn(singletonList(mock(SnapshotImpl.class)));
|
||||
when(workspaceDao.get(workspace.getId())).thenReturn(workspace);
|
||||
when(runtimes.get(any())).thenThrow(new NotFoundException(""));
|
||||
SnapshotImpl.SnapshotBuilder snapshotBuilder = SnapshotImpl.builder()
|
||||
.generateId()
|
||||
.setEnvName("env")
|
||||
.setDev(true)
|
||||
.setMachineName("machine1")
|
||||
.setNamespace(workspace.getNamespace())
|
||||
.setWorkspaceId(workspace.getId())
|
||||
.setType("docker")
|
||||
.setMachineSource(new MachineSourceImpl("image"));
|
||||
SnapshotImpl snapshot1 = snapshotBuilder.build();
|
||||
SnapshotImpl snapshot2 = snapshotBuilder.generateId()
|
||||
.setDev(false)
|
||||
.setMachineName("machine2")
|
||||
.build();
|
||||
when(snapshotDao.findSnapshots(workspace.getNamespace(), workspace.getId()))
|
||||
.thenReturn(asList(snapshot1, snapshot2));
|
||||
|
||||
workspaceManager.startWorkspace(workspace.getId(), workspace.getConfig().getDefaultEnv(), "account", null);
|
||||
|
||||
|
|
@ -645,14 +697,69 @@ public class WorkspaceManagerTest {
|
|||
String testWsId = "testWsId";
|
||||
String testNamespace = "testNamespace";
|
||||
WorkspaceImpl workspaceMock = mock(WorkspaceImpl.class);
|
||||
doReturn(workspaceMock).when(workspaceManager).getWorkspace(testWsId);
|
||||
when(workspaceDao.get(testWsId)).thenReturn(workspaceMock);
|
||||
when(workspaceMock.getNamespace()).thenReturn(testNamespace);
|
||||
SnapshotImpl.SnapshotBuilder snapshotBuilder = SnapshotImpl.builder()
|
||||
.generateId()
|
||||
.setEnvName("env")
|
||||
.setDev(true)
|
||||
.setMachineName("machine1")
|
||||
.setNamespace(testNamespace)
|
||||
.setWorkspaceId(testWsId)
|
||||
.setType("docker")
|
||||
.setMachineSource(new MachineSourceImpl("image"));
|
||||
SnapshotImpl snapshot1 = snapshotBuilder.build();
|
||||
SnapshotImpl snapshot2 = snapshotBuilder.generateId()
|
||||
.setDev(false)
|
||||
.setMachineName("machine2")
|
||||
.build();
|
||||
when(snapshotDao.findSnapshots(testNamespace, testWsId)).thenReturn(asList(snapshot1, snapshot2));
|
||||
|
||||
// when
|
||||
workspaceManager.removeSnapshots(testWsId);
|
||||
|
||||
// then
|
||||
verify(machineManager).removeSnapshots(testNamespace, testWsId);
|
||||
InOrder runtimesInOrder = inOrder(runtimes);
|
||||
runtimesInOrder.verify(runtimes).removeSnapshot(snapshot1);
|
||||
runtimesInOrder.verify(runtimes).removeSnapshot(snapshot2);
|
||||
InOrder snapshotDaoInOrder = inOrder(snapshotDao);
|
||||
snapshotDaoInOrder.verify(snapshotDao).removeSnapshot(snapshot1.getId());
|
||||
snapshotDaoInOrder.verify(snapshotDao).removeSnapshot(snapshot2.getId());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldRemoveMachinesSnapshotsEvenSomeRemovalFails() throws Exception {
|
||||
// given
|
||||
String testWsId = "testWsId";
|
||||
String testNamespace = "testNamespace";
|
||||
WorkspaceImpl workspaceMock = mock(WorkspaceImpl.class);
|
||||
when(workspaceDao.get(testWsId)).thenReturn(workspaceMock);
|
||||
when(workspaceMock.getNamespace()).thenReturn(testNamespace);
|
||||
SnapshotImpl.SnapshotBuilder snapshotBuilder = SnapshotImpl.builder()
|
||||
.generateId()
|
||||
.setEnvName("env")
|
||||
.setDev(true)
|
||||
.setMachineName("machine1")
|
||||
.setNamespace(testNamespace)
|
||||
.setWorkspaceId(testWsId)
|
||||
.setType("docker")
|
||||
.setMachineSource(new MachineSourceImpl("image"));
|
||||
SnapshotImpl snapshot1 = snapshotBuilder.build();
|
||||
SnapshotImpl snapshot2 = snapshotBuilder.generateId()
|
||||
.setDev(false)
|
||||
.setMachineName("machine2")
|
||||
.build();
|
||||
when(snapshotDao.findSnapshots(testNamespace, testWsId)).thenReturn(asList(snapshot1, snapshot2));
|
||||
doThrow(new SnapshotException("test")).when(snapshotDao).removeSnapshot(snapshot1.getId());
|
||||
|
||||
// when
|
||||
workspaceManager.removeSnapshots(testWsId);
|
||||
|
||||
// then
|
||||
verify(runtimes).removeSnapshot(snapshot1);
|
||||
verify(runtimes).removeSnapshot(snapshot2);
|
||||
verify(snapshotDao).removeSnapshot(snapshot1.getId());
|
||||
verify(snapshotDao).removeSnapshot(snapshot2.getId());
|
||||
}
|
||||
|
||||
@Test
|
||||
|
|
@ -666,15 +773,13 @@ public class WorkspaceManagerTest {
|
|||
when(workspaceMock.getStatus()).thenReturn(RUNNING);
|
||||
when(workspaceMock.getRuntime()).thenReturn(wsRuntimeMock);
|
||||
when(wsRuntimeMock.getActiveEnv()).thenReturn(testActiveEnv);
|
||||
LineConsumer lineConsumerMock = mock(LineConsumer.class);
|
||||
MachineConfigImpl machineConfig = createConfig().getEnvironments().get(0).getMachineConfigs().get(0);
|
||||
when(runtimes.getMachineLogger(testWsId, machineConfig.getName())).thenReturn(lineConsumerMock);
|
||||
|
||||
// when
|
||||
workspaceManager.startMachine(machineConfig, testWsId);
|
||||
|
||||
// then
|
||||
verify(machineManager).createMachineAsync(eq(machineConfig), eq(testWsId), eq(testActiveEnv), eq(lineConsumerMock));
|
||||
verify(runtimes, timeout(2000)).startMachine(testWsId, machineConfig);
|
||||
}
|
||||
|
||||
@Test(expectedExceptions = ConflictException.class, expectedExceptionsMessageRegExp = "Workspace .* is not running, new machine can't be started")
|
||||
|
|
@ -690,16 +795,107 @@ public class WorkspaceManagerTest {
|
|||
workspaceManager.startMachine(machineConfig, testWsId);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldBeAbleToCreateSnapshot() throws Exception {
|
||||
// then
|
||||
final WorkspaceImpl workspace = workspaceManager.createWorkspace(createConfig(), "user123", "account");
|
||||
when(workspaceDao.get(workspace.getId())).thenReturn(workspace);
|
||||
RuntimeDescriptor descriptor = createDescriptor(workspace, RUNNING);
|
||||
when(runtimes.get(any())).thenReturn(descriptor);
|
||||
|
||||
// when
|
||||
workspaceManager.createSnapshot(workspace.getId());
|
||||
|
||||
// then
|
||||
verify(workspaceManager, timeout(1_000)).createSnapshotSync(any(WorkspaceRuntimeImpl.class),
|
||||
eq(workspace.getNamespace()),
|
||||
eq(workspace.getId()));
|
||||
}
|
||||
|
||||
@Test(expectedExceptions = ConflictException.class,
|
||||
expectedExceptionsMessageRegExp = "Could not .* the workspace '.*' because its status is '.*'.")
|
||||
public void shouldNotCreateSnapshotIfWorkspaceIsNotRunning() throws Exception {
|
||||
// then
|
||||
final WorkspaceImpl workspace = workspaceManager.createWorkspace(createConfig(), "user123", "account");
|
||||
when(workspaceDao.get(workspace.getId())).thenReturn(workspace);
|
||||
RuntimeDescriptor descriptor = createDescriptor(workspace, STARTING);
|
||||
when(runtimes.get(any())).thenReturn(descriptor);
|
||||
|
||||
// when
|
||||
workspaceManager.createSnapshot(workspace.getId());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldBeAbleToStopMachine() throws Exception {
|
||||
// given
|
||||
final WorkspaceImpl workspace = workspaceManager.createWorkspace(createConfig(), "user123", "account");
|
||||
when(workspaceDao.get(workspace.getId())).thenReturn(workspace);
|
||||
RuntimeDescriptor descriptor = createDescriptor(workspace, RUNNING);
|
||||
when(runtimes.get(any())).thenReturn(descriptor);
|
||||
MachineImpl machine = descriptor.getRuntime().getMachines().get(0);
|
||||
|
||||
// when
|
||||
workspaceManager.stopMachine(workspace.getId(), machine.getId());
|
||||
|
||||
// then
|
||||
verify(runtimes).stopMachine(workspace.getId(), machine.getId());
|
||||
}
|
||||
|
||||
@Test(expectedExceptions = ConflictException.class,
|
||||
expectedExceptionsMessageRegExp = "Could not .* the workspace '.*' because its status is '.*'.")
|
||||
public void shouldNotStopMachineIfWorkspaceIsNotRunning() throws Exception {
|
||||
// given
|
||||
final WorkspaceImpl workspace = workspaceManager.createWorkspace(createConfig(), "user123", "account");
|
||||
when(workspaceDao.get(workspace.getId())).thenReturn(workspace);
|
||||
RuntimeDescriptor descriptor = createDescriptor(workspace, STARTING);
|
||||
when(runtimes.get(any())).thenReturn(descriptor);
|
||||
|
||||
// when
|
||||
workspaceManager.stopMachine(workspace.getId(), "someId");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldBeAbleToGetMachineInstanceIfWorkspaceIsRunning() throws Exception {
|
||||
// given
|
||||
final WorkspaceImpl workspace = workspaceManager.createWorkspace(createConfig(), "user123", "account");
|
||||
when(workspaceDao.get(workspace.getId())).thenReturn(workspace);
|
||||
RuntimeDescriptor descriptor = createDescriptor(workspace, RUNNING);
|
||||
when(runtimes.get(any())).thenReturn(descriptor);
|
||||
MachineImpl machine = descriptor.getRuntime().getMachines().get(0);
|
||||
|
||||
// when
|
||||
workspaceManager.getMachineInstance(workspace.getId(), machine.getId());
|
||||
|
||||
// then
|
||||
verify(runtimes).getMachine(workspace.getId(), machine.getId());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldBeAbleToGetMachineInstanceIfWorkspaceIsStarting() throws Exception {
|
||||
// given
|
||||
final WorkspaceImpl workspace = workspaceManager.createWorkspace(createConfig(), "user123", "account");
|
||||
when(workspaceDao.get(workspace.getId())).thenReturn(workspace);
|
||||
RuntimeDescriptor descriptor = createDescriptor(workspace, STARTING);
|
||||
when(runtimes.get(any())).thenReturn(descriptor);
|
||||
MachineImpl machine = descriptor.getRuntime().getMachines().get(0);
|
||||
|
||||
// when
|
||||
workspaceManager.getMachineInstance(workspace.getId(), machine.getId());
|
||||
|
||||
// then
|
||||
verify(runtimes).getMachine(workspace.getId(), machine.getId());
|
||||
}
|
||||
|
||||
private RuntimeDescriptor createDescriptor(WorkspaceImpl workspace, WorkspaceStatus status) {
|
||||
final WorkspaceRuntimeImpl runtime = new WorkspaceRuntimeImpl(workspace.getConfig().getDefaultEnv());
|
||||
final String env = workspace.getConfig().getDefaultEnv();
|
||||
for (MachineConfigImpl machineConfig : workspace.getConfig()
|
||||
.getEnvironment(workspace.getConfig().getDefaultEnv())
|
||||
.get()
|
||||
.getMachineConfigs()) {
|
||||
Optional<EnvironmentImpl> environmentOpt = workspace.getConfig().getEnvironment(workspace.getConfig().getDefaultEnv());
|
||||
assertTrue(environmentOpt.isPresent());
|
||||
EnvironmentImpl environment = environmentOpt.get();
|
||||
|
||||
final WorkspaceRuntimeImpl runtime = new WorkspaceRuntimeImpl(environment.getName());
|
||||
for (MachineConfigImpl machineConfig : environment.getMachineConfigs()) {
|
||||
final MachineImpl machine = MachineImpl.builder()
|
||||
.setConfig(machineConfig)
|
||||
.setEnvName(env)
|
||||
.setEnvName(environment.getName())
|
||||
.setId(NameGenerator.generate("machine", 10))
|
||||
.setOwner(workspace.getNamespace())
|
||||
.setRuntime(new MachineRuntimeInfoImpl(emptyMap(), emptyMap(), emptyMap()))
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load Diff
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue