CHE-1368: rework machine's output/statuses events subscribing (#1600)

Rework output/status events of machine to be per workspace as a
part of implementation of new multi-machine environments.
Get rid of usage of machine manager in workspace service
Refactor code of machine manager.

Signed-off-by: Alexander Garagatyi <agaragatyi@codenvy.com>
6.19.x
Alexander Garagatyi 2016-08-03 13:22:04 +03:00 committed by GitHub
parent 58ca02cd73
commit a27b62df9f
25 changed files with 523 additions and 178 deletions

View File

@ -0,0 +1,26 @@
/*******************************************************************************
* 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.core.util;
import java.io.IOException;
/**
* No-op implementation of {@link LineConsumer}
*
* @author Alexander Garagatyi
*/
public abstract class AbstractLineConsumer implements LineConsumer {
@Override
public void writeLine(String line) throws IOException {}
@Override
public void close() throws IOException {}
}

View File

@ -0,0 +1,26 @@
/*******************************************************************************
* 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.core.util;
import java.io.IOException;
/**
* No-op implementation of {@link MessageConsumer}
*
* @author Alexander Garagatyi
*/
public class AbstractMessageConsumer<T> implements MessageConsumer<T> {
@Override
public void consume(T message) throws IOException {}
@Override
public void close() throws IOException {}
}

View File

@ -17,18 +17,11 @@ import java.io.IOException;
* Consumes text line by line for analysing, writing, storing, etc.
*
* @author andrew00x
* @see AbstractLineConsumer
*/
public interface LineConsumer extends Closeable {
/** Consumes single line. */
void writeLine(String line) throws IOException;
LineConsumer DEV_NULL = new LineConsumer() {
@Override
public void writeLine(String line) {
}
@Override
public void close() {
}
};
LineConsumer DEV_NULL = new AbstractLineConsumer() {};
}

View File

@ -0,0 +1,23 @@
/*******************************************************************************
* 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.core.util;
import java.io.Closeable;
import java.io.IOException;
/**
* Consumes messages one by one for analysing, writing, storing, etc.
*
* @author Alexander Garagatyi
*/
public interface MessageConsumer<T> extends Closeable {
void consume(T message) throws IOException;
}

View File

@ -10,7 +10,6 @@
*******************************************************************************/
package org.eclipse.che.api.core.util;
import org.everrest.core.impl.provider.json.JsonUtils;
import org.everrest.websockets.WSConnectionContext;
import org.everrest.websockets.message.ChannelBroadcastMessage;
import org.slf4j.Logger;
@ -36,7 +35,7 @@ public class WebsocketLineConsumer implements LineConsumer {
public void writeLine(String line) throws IOException {
final ChannelBroadcastMessage bm = new ChannelBroadcastMessage();
bm.setChannel(channel);
bm.setBody(JsonUtils.getJsonString(line));
bm.setBody(line);
try {
WSConnectionContext.sendMessage(bm);
} catch (Exception e) {

View File

@ -0,0 +1,36 @@
/*******************************************************************************
* 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.core.util;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import java.io.IOException;
/**
* Message consumer that sends messages to specified websocket channel
*
* @author Alexander Garagatyi
*/
public class WebsocketMessageConsumer<T> extends AbstractMessageConsumer<T> {
private static final Gson GSON = new GsonBuilder().disableHtmlEscaping().create();
private final LineConsumer messageSender;
public WebsocketMessageConsumer(String channel) {
this.messageSender = new WebsocketLineConsumer(channel);
}
@Override
public void consume(T message) throws IOException {
messageSender.writeLine(GSON.toJson(message));
}
}

View File

@ -0,0 +1,28 @@
/*******************************************************************************
* 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.core.model.machine;
/**
* Represents log message from machine
*
* @author Alexander Garagatyi
*/
public interface MachineLogMessage {
/**
* Content of log message
*/
String getContent();
/**
* Machine name
*/
String getMachineName();
}

View File

@ -10,9 +10,6 @@
*******************************************************************************/
package org.eclipse.che.ide.api.machine;
import com.google.gwt.json.client.JSONParser;
import com.google.gwt.json.client.JSONString;
import org.eclipse.che.ide.websocket.Message;
import org.eclipse.che.ide.websocket.rest.Unmarshallable;
@ -32,8 +29,7 @@ public class CommandOutputMessageUnmarshaller implements Unmarshallable<String>
@Override
public void unmarshal(Message message) {
final JSONString jsonString = JSONParser.parseStrict(message.getBody()).isString();
payload = jsonString.stringValue();
payload = message.getBody();
if (payload.startsWith("[STDOUT]")) {
payload = payload.substring(9);

View File

@ -10,9 +10,6 @@
*******************************************************************************/
package org.eclipse.che.ide.api.machine;
import com.google.gwt.json.client.JSONParser;
import com.google.gwt.json.client.JSONString;
import org.eclipse.che.ide.websocket.Message;
import org.eclipse.che.ide.websocket.rest.Unmarshallable;
@ -26,9 +23,7 @@ public class OutputMessageUnmarshaller implements Unmarshallable<String> {
@Override
public void unmarshal(Message message) {
final JSONString jsonString = JSONParser.parseStrict(message.getBody()).isString();
payload = jsonString.stringValue();
payload = message.getBody();
if (payload.startsWith("[STDOUT]") || payload.startsWith("[STDERR]")) {
payload = payload.substring(9);
}

View File

@ -11,7 +11,7 @@
package org.eclipse.che.plugin.machine.ssh;
import org.eclipse.che.api.core.ConflictException;
import org.eclipse.che.api.core.util.LineConsumer;
import org.eclipse.che.api.core.util.AbstractLineConsumer;
import org.eclipse.che.api.core.util.ListLineConsumer;
import org.eclipse.che.api.machine.server.exception.MachineException;
import org.eclipse.che.api.machine.server.model.impl.CommandImpl;
@ -164,14 +164,11 @@ public class SshMachineImplTerminalLauncher implements MachineImplSpecificTermin
null),
null);
startTerminal.start(new LineConsumer() {
startTerminal.start(new AbstractLineConsumer() {
@Override
public void writeLine(String line) throws IOException {
machine.getLogger().writeLine("[Terminal] " + line);
}
@Override
public void close() throws IOException {}
});
}
}

View File

@ -40,6 +40,11 @@ public class Constants {
public static final String WSAGENT_WEBSOCKET_REFERENCE = "wsagent.websocket";
public static final String WSAGENT_DEBUG_REFERENCE = "wsagent.debug";
public static final String LINK_REL_ENVIRONMENT_OUTPUT_CHANNEL = "environment.output_channel";
public static final String ENVIRONMENT_OUTPUT_CHANNEL_TEMPLATE = "workspace:%s:environment_output";
public static final String LINK_REL_ENVIRONMENT_STATUS_CHANNEL = "environment.status_channel";
public static final String ENVIRONMENT_STATUS_CHANNEL_TEMPLATE = "workspace:%s:machines_statuses";
public static final String TERMINAL_REFERENCE = "terminal";
public static final String WS_AGENT_PORT = "4401/tcp";

View File

@ -0,0 +1,28 @@
/*******************************************************************************
* 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.shared.dto;
import org.eclipse.che.api.core.model.machine.MachineLogMessage;
import org.eclipse.che.dto.shared.DTO;
/**
* @author Alexander Garagatyi
*/
@DTO
public interface MachineLogMessageDto extends MachineLogMessage {
void setContent(String content);
MachineLogMessageDto withContent(String content);
void setMachineName(String machineName);
MachineLogMessageDto withMachineName(String machineName);
}

View File

@ -14,6 +14,7 @@ import com.google.common.annotations.VisibleForTesting;
import com.google.common.base.Strings;
import com.google.common.util.concurrent.ThreadFactoryBuilder;
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;
@ -135,6 +136,8 @@ public class MachineManager {
* id of the workspace the created machine will belong to
* @param environmentName
* environment name the created machine will belongs to
* @param outputConsumer
* output consumer of machine
* @return new machine
* @throws NotFoundException
* if machine type from recipe is unsupported
@ -153,7 +156,8 @@ public class MachineManager {
*/
public MachineImpl createMachineSync(MachineConfig machineConfig,
final String workspaceId,
final String environmentName)
final String environmentName,
LineConsumer outputConsumer)
throws NotFoundException,
SnapshotException,
ConflictException,
@ -167,7 +171,8 @@ public class MachineManager {
workspaceId,
environmentName,
this::createInstance,
null);
null,
outputConsumer);
LOG.info("Machine [ws = {}: env = {}: machine = {}] was successfully created, its id is '{}'",
workspaceId,
environmentName,
@ -186,6 +191,8 @@ public class MachineManager {
* workspace id
* @param envName
* name of environment
* @param outputConsumer
* output consumer of machine
* @return machine instance
* @throws NotFoundException
* when snapshot doesn't exist
@ -198,11 +205,15 @@ public class MachineManager {
* @throws BadRequestException
* when either machineConfig or workspace id, or environment name is not valid
*/
public MachineImpl recoverMachine(MachineConfig machineConfig, String workspaceId, String envName) throws NotFoundException,
SnapshotException,
MachineException,
ConflictException,
BadRequestException {
public MachineImpl recoverMachine(MachineConfig machineConfig,
String workspaceId,
String envName,
LineConsumer outputConsumer) throws NotFoundException,
SnapshotException,
MachineException,
ConflictException,
BadRequestException {
final SnapshotImpl snapshot = snapshotDao.getSnapshot(workspaceId, envName, machineConfig.getName());
LOG.info("Recovering machine [ws = {}: env = {}: machine = {}] from snapshot", workspaceId, envName, machineConfig.getName());
@ -210,7 +221,8 @@ public class MachineManager {
workspaceId,
envName,
this::createInstance,
snapshot);
snapshot,
outputConsumer);
LOG.info("Machine [ws = {}: env = {}: machine = {}] was successfully recovered, its id '{}'",
workspaceId,
envName,
@ -229,6 +241,8 @@ public class MachineManager {
* id of the workspace the created machine will belong to
* @param environmentName
* environment name the created machine will belongs to
* @param outputConsumer
* output consumer of machine
* @return new machine
* @throws NotFoundException
* if machine type from recipe is unsupported
@ -247,7 +261,8 @@ public class MachineManager {
*/
public MachineImpl createMachineAsync(MachineConfig machineConfig,
final String workspaceId,
final String environmentName)
final String environmentName,
LineConsumer outputConsumer)
throws NotFoundException,
SnapshotException,
ConflictException,
@ -269,14 +284,16 @@ public class MachineManager {
// todo what should we do in that case?
}
})),
null);
null,
outputConsumer);
}
private MachineImpl createMachine(MachineConfigImpl machineConfig,
String workspaceId,
String environmentName,
MachineInstanceCreator instanceCreator,
SnapshotImpl snapshot)
SnapshotImpl snapshot,
LineConsumer outputConsumer)
throws NotFoundException,
SnapshotException,
ConflictException,
@ -329,38 +346,43 @@ public class MachineManager {
null);
createMachineLogsDir(machineId);
final LineConsumer machineLogger = getMachineLogger(machineId, getMachineChannels(machine.getConfig().getName(),
machine.getWorkspaceId(),
machine.getEnvName())
.getOutput());
final LineConsumer machineLogger = getMachineLogger(machineId, outputConsumer);
try {
machineRegistry.addMachine(machine);
try {
machineRegistry.addMachine(machine);
instanceCreator.createInstance(instanceProvider, machine, machineLogger);
} catch (MachineException ex) {
if (snapshot == null) {
throw ex;
}
if (ex.getCause() instanceof SourceNotFoundException) {
final LineConsumer logger = getMachineLogger(machineId,
getMachineChannels(machine.getConfig().getName(),
machine.getWorkspaceId(),
machine.getEnvName()).getOutput());
LOG.error("Image of snapshot for machine " + machineConfig.getName() + " not found. " +
"Machine will be created from origin source");
machine.getConfig().setSource(sourceCopy);
try {
machineRegistry.remove(machineId);
} catch (NotFoundException ignored) {
// machine is already removed, should never happen
}
machineRegistry.addMachine(machine);
instanceCreator.createInstance(instanceProvider, machine, logger);
instanceCreator.createInstance(instanceProvider, machine, outputConsumer);
}
}
return machine;
} catch (ConflictException e) {
} catch (ApiException apiEx) {
try {
machineLogger.close();
} catch (IOException ignored) {
} catch (IOException ioEx) {
LOG.error(ioEx.getLocalizedMessage(), ioEx);
}
throw new MachineException(e.getLocalizedMessage(), e);
try {
machineRegistry.remove(machineId);
} catch (NotFoundException ignored) {
// machine is already removed
}
throw new MachineException(apiEx.getLocalizedMessage(), apiEx);
}
}
@ -393,27 +415,28 @@ public class MachineManager {
.withWorkspaceId(machine.getWorkspaceId())
.withMachineName(machine.getConfig().getName()));
} catch (ServerException | InterruptedException e) {
if (instance != null) {
instance.destroy();
}
} catch (ServerException | InterruptedException creationEx) {
eventService.publish(DtoFactory.newDto(MachineStatusEvent.class)
.withEventType(MachineStatusEvent.EventType.ERROR)
.withMachineId(machine.getId())
.withDev(machine.getConfig().isDev())
.withWorkspaceId(machine.getWorkspaceId())
.withMachineName(machine.getConfig().getName())
.withError(e.getLocalizedMessage()));
.withError(creationEx.getLocalizedMessage()));
try {
machineLogger.writeLine(String.format("[ERROR] %s", creationEx.getLocalizedMessage()));
} catch (IOException ioEx) {
LOG.error(ioEx.getLocalizedMessage());
}
try {
machineRegistry.remove(machine.getId());
machineLogger.writeLine(String.format("[ERROR] %s", e.getLocalizedMessage()));
machineLogger.close();
} catch (IOException | NotFoundException e1) {
LOG.error(e1.getLocalizedMessage());
if (instance != null) {
instance.destroy();
}
} catch (MachineException destroyingEx) {
LOG.error(destroyingEx.getLocalizedMessage(), destroyingEx);
}
throw new MachineException(e.getLocalizedMessage(), e);
throw new MachineException(creationEx.getLocalizedMessage(), creationEx);
}
}
@ -930,8 +953,8 @@ public class MachineManager {
}
@VisibleForTesting
LineConsumer getMachineLogger(String machineId, String outputChannel) throws MachineException {
return getLogger(getMachineFileLogger(machineId), outputChannel);
LineConsumer getMachineLogger(String machineId, LineConsumer outputConsumer) throws MachineException {
return new CompositeLineConsumer(getMachineFileLogger(machineId), outputConsumer);
}
@VisibleForTesting
@ -946,11 +969,6 @@ public class MachineManager {
return fileLogger;
}
static ChannelsImpl getMachineChannels(String machineName, String workspaceId, String envName) {
return new ChannelsImpl(workspaceId + ':' + envName + ':' + machineName,
"machine:status:" + workspaceId + ':' + machineName);
}
// cleanup machine if event about instance failure comes
private class MachineCleaner implements EventSubscriber<InstanceStateEvent> {
@Override

View File

@ -16,7 +16,6 @@ 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.shared.Constants;
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.machine.shared.dto.ServerDto;
@ -30,11 +29,14 @@ import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import static java.util.Arrays.asList;
import static java.lang.String.format;
import static java.util.Collections.singletonList;
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.TERMINAL_REFERENCE;
import static org.eclipse.che.dto.server.DtoFactory.cloneDto;
import static org.eclipse.che.dto.server.DtoFactory.newDto;
@ -118,23 +120,27 @@ public class MachineServiceLinksInjector {
injectTerminalLink(machine, serviceContext, links);
// add links to websocket channels
final Link machineChannelLink = createLink("GET",
serviceContext.getBaseUriBuilder()
.path("ws")
.path(machine.getWorkspaceId())
.scheme("https".equals(getLogsUri.getScheme()) ? "wss" : "ws")
.build()
.toString(),
null);
// add workspace channel links
final Link workspaceChannelLink = createLink("GET",
serviceContext.getBaseUriBuilder()
.path("ws")
.path(machine.getWorkspaceId())
.scheme("https".equals(getLogsUri.getScheme()) ? "wss" : "ws")
.build()
.toString(),
null);
final LinkParameter channelParameter = newDto(LinkParameter.class).withName("channel")
.withRequired(true);
injectMachineChannelsLinks(machine.getConfig(),
machine.getWorkspaceId(),
machine.getEnvName(),
machineChannelLink,
channelParameter);
links.add(cloneDto(workspaceChannelLink).withRel(LINK_REL_ENVIRONMENT_OUTPUT_CHANNEL)
.withParameters(singletonList(cloneDto(channelParameter)
.withDefaultValue(format(ENVIRONMENT_OUTPUT_CHANNEL_TEMPLATE,
machine.getWorkspaceId())))));
links.add(cloneDto(workspaceChannelLink).withRel(ENVIRONMENT_STATUS_CHANNEL_TEMPLATE)
.withParameters(singletonList(cloneDto(channelParameter)
.withDefaultValue(format(ENVIRONMENT_STATUS_CHANNEL_TEMPLATE,
machine.getWorkspaceId())))));
return machine.withLinks(links);
}
@ -157,25 +163,6 @@ public class MachineServiceLinksInjector {
}
}
public void injectMachineChannelsLinks(MachineConfigDto machineConfig,
String workspaceId,
String envName,
Link machineChannelLink,
LinkParameter channelParameter) {
final ChannelsImpl channels = MachineManager.getMachineChannels(machineConfig.getName(),
workspaceId,
envName);
final Link getLogsLink = cloneDto(machineChannelLink)
.withRel(org.eclipse.che.api.machine.shared.Constants.LINK_REL_GET_MACHINE_LOGS_CHANNEL)
.withParameters(singletonList(cloneDto(channelParameter).withDefaultValue(channels.getOutput())));
final Link getStatusLink = cloneDto(machineChannelLink)
.withRel(org.eclipse.che.api.machine.shared.Constants.LINK_REL_GET_MACHINE_STATUS_CHANNEL)
.withParameters(singletonList(cloneDto(channelParameter).withDefaultValue(channels.getStatus())));
machineConfig.withLinks(asList(getLogsLink, getStatusLink));
}
public MachineProcessDto injectLinks(MachineProcessDto process, String machineId, ServiceContext serviceContext) {
final UriBuilder uriBuilder = serviceContext.getServiceUriBuilder();
final List<Link> links = Lists.newArrayListWithExpectedSize(3);

View File

@ -24,6 +24,9 @@ import javax.annotation.PreDestroy;
import javax.inject.Inject;
import javax.inject.Singleton;
import static java.lang.String.format;
import static org.eclipse.che.api.machine.shared.Constants.ENVIRONMENT_STATUS_CHANNEL_TEMPLATE;
/**
* Send machine state events using websocket channel to the clients
*
@ -44,7 +47,7 @@ public class MachineStateMessenger implements EventSubscriber<MachineStatusEvent
public void onEvent(MachineStatusEvent event) {
try {
final ChannelBroadcastMessage bm = new ChannelBroadcastMessage();
bm.setChannel("machine:status:" + event.getWorkspaceId() + ':' + event.getMachineName());
bm.setChannel(format(ENVIRONMENT_STATUS_CHANNEL_TEMPLATE, event.getWorkspaceId()));
bm.setBody(DtoFactory.getInstance().toJson(event));
WSConnectionContext.sendMessage(bm);
} catch (Exception e) {

View File

@ -0,0 +1,68 @@
/*******************************************************************************
* 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.model.impl;
import org.eclipse.che.api.core.model.machine.MachineLogMessage;
import java.util.Objects;
/**
* @author Alexander Garagatyi
*/
public class MachineLogMessageImpl implements MachineLogMessage {
private String machineName;
private String content;
public MachineLogMessageImpl() {}
public MachineLogMessageImpl(String machineName, String content) {
this.machineName = machineName;
this.content = content;
}
@Override
public String getMachineName() {
return machineName;
}
public void setMachineName(String machine) {
this.machineName = machine;
}
@Override
public String getContent() {
return content;
}
public void setContent(String content) {
this.content = content;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (!(o instanceof MachineLogMessageImpl)) return false;
MachineLogMessageImpl that = (MachineLogMessageImpl)o;
return Objects.equals(machineName, that.machineName) &&
Objects.equals(content, that.content);
}
@Override
public int hashCode() {
return Objects.hash(machineName, content);
}
@Override
public String toString() {
return "MachineLogMessageImpl{machineName='" + machineName +
"', content='" + content + "'}";
}
}

View File

@ -101,7 +101,7 @@ public class MachineManagerTest {
@Mock
private InstanceProcess instanceProcess;
@Mock
private LineConsumer processLogger;
private LineConsumer logConsumer;
@Mock
private SnapshotDao snapshotDao;
@ -127,7 +127,7 @@ public class MachineManagerTest {
RecipeImpl recipe = new RecipeImpl().withScript("script").withType("Dockerfile");
// doNothing().when(manager).createMachineLogsDir(anyString());
doReturn(MACHINE_ID).when(manager).generateMachineId();
doReturn(processLogger).when(manager).getProcessLogger(MACHINE_ID, 111, "outputChannel");
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");
@ -168,7 +168,7 @@ public class MachineManagerTest {
String workspaceId = "wsId";
String environmentName = "env1";
manager.createMachineSync(machineConfig, workspaceId, environmentName);
manager.createMachineSync(machineConfig, workspaceId, environmentName, logConsumer);
}
@Test
@ -186,7 +186,7 @@ public class MachineManagerTest {
MachineStatus.CREATING,
null);
manager.createMachineSync(machineConfig, WS_ID, ENVIRONMENT_NAME);
manager.createMachineSync(machineConfig, WS_ID, ENVIRONMENT_NAME, logConsumer);
verify(machineRegistry).addMachine(eq(expectedMachine));
}
@ -198,7 +198,7 @@ public class MachineManagerTest {
.setDev(true)
.build();
manager.createMachineSync(machineConfig, WS_ID, ENVIRONMENT_NAME);
manager.createMachineSync(machineConfig, WS_ID, ENVIRONMENT_NAME, logConsumer);
verify(wsAgentLauncher).startWsAgent(WS_ID);
}
@ -207,7 +207,7 @@ public class MachineManagerTest {
public void shouldNotCallWsAgentLauncherAfterNonDevMachineStart() throws Exception {
final MachineConfigImpl machineConfig = createMachineConfig();
manager.createMachineSync(machineConfig, WS_ID, ENVIRONMENT_NAME);
manager.createMachineSync(machineConfig, WS_ID, ENVIRONMENT_NAME, logConsumer);
verify(wsAgentLauncher, never()).startWsAgent(WS_ID);
}
@ -233,7 +233,7 @@ public class MachineManagerTest {
waitForExecutorIsCompletedTask();
//then
verify(processLogger).close();
verify(logConsumer).close();
}
@Test
@ -246,7 +246,7 @@ public class MachineManagerTest {
waitForExecutorIsCompletedTask();
//then
verify(processLogger).close();
verify(logConsumer).close();
}
@Test(expectedExceptions = MachineException.class)
@ -255,14 +255,14 @@ public class MachineManagerTest {
MachineConfig machineConfig = mock(MachineConfig.class);
MachineSource machineSource = mock(MachineSource.class);
LineConsumer machineLogger = mock(LineConsumer.class);
doReturn(machineLogger).when(manager).getMachineLogger(MACHINE_ID, "outputChannel");
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");
manager.createMachineSync(machineConfig, "workspaceId", "environmentName", logConsumer);
//then
verify(machineLogger).close();
@ -285,7 +285,7 @@ public class MachineManagerTest {
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);
final MachineImpl result = manager.recoverMachine(config, WS_ID, ENVIRONMENT_NAME, logConsumer);
machine.getConfig().setSource(config.getSource());
assertEquals(result, machine);
@ -299,7 +299,7 @@ public class MachineManagerTest {
when(instanceProvider.createInstance(any(MachineImpl.class),
any(LineConsumer.class))).thenThrow(new MachineException(""));
manager.recoverMachine(config, WS_ID, ENVIRONMENT_NAME);
manager.recoverMachine(config, WS_ID, ENVIRONMENT_NAME, logConsumer);
}
private void waitForExecutorIsCompletedTask() throws Exception {

View File

@ -36,6 +36,8 @@ 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_EXECUTE_COMMAND;
import static org.eclipse.che.api.machine.shared.Constants.LINK_REL_GET_MACHINES;
@ -103,7 +105,9 @@ public class MachineServiceLinksInjectorTest {
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_GET_MACHINE_LOGS),
Pair.of("GET", LINK_REL_ENVIRONMENT_OUTPUT_CHANNEL),
Pair.of("GET", ENVIRONMENT_STATUS_CHANNEL_TEMPLATE)));
assertEquals(links, expectedLinks, "Difference " + Sets.symmetricDifference(links, expectedLinks) + "\n");
}

View File

@ -15,10 +15,12 @@ import com.google.common.util.concurrent.ThreadFactoryBuilder;
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;
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;
@ -424,6 +426,42 @@ public class WorkspaceManager {
return normalizeState(workspace);
}
/**
* Starts machine in running workspace
*
* @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
* if no instance provider implementation found for provided machine type
* @throws ConflictException
* if machine with given name already exists
* @throws ConflictException
* if workspace is not in RUNNING state
* @throws BadRequestException
* if machine name is invalid
* @throws ServerException
* if any other exception occurs during starting
*/
public MachineImpl 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()));
}
/**
* Asynchronously stops the workspace.
*
@ -495,6 +533,19 @@ public class WorkspaceManager {
return machineManager.getSnapshots(workspace.getNamespace(), workspaceId);
}
/**
* Removes all snapshots of workspace machines
*
* @param workspaceId workspace id to remove machine snapshots
* @throws NotFoundException
* when workspace with given id doesn't exists
* @throws ServerException
* when any other error occurs
*/
public void removeSnapshots(String workspaceId) throws NotFoundException, ServerException {
machineManager.removeSnapshots(getWorkspace(workspaceId).getNamespace(), workspaceId);
}
/** Asynchronously starts given workspace. */
@VisibleForTesting
WorkspaceImpl performAsyncStart(WorkspaceImpl workspace,

View File

@ -19,15 +19,20 @@ 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.MachineConfig;
import org.eclipse.che.api.core.model.machine.MachineLogMessage;
import org.eclipse.che.api.core.model.machine.MachineStatus;
import org.eclipse.che.api.core.model.workspace.WorkspaceRuntime;
import org.eclipse.che.api.core.model.workspace.WorkspaceStatus;
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.LineConsumer;
import org.eclipse.che.api.core.util.WebsocketMessageConsumer;
import org.eclipse.che.api.machine.server.MachineManager;
import org.eclipse.che.api.machine.server.exception.MachineException;
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.shared.dto.event.MachineStatusEvent;
import org.eclipse.che.api.workspace.server.model.impl.EnvironmentImpl;
import org.eclipse.che.api.workspace.server.model.impl.WorkspaceImpl;
@ -41,6 +46,7 @@ import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;
import javax.inject.Inject;
import javax.inject.Singleton;
import java.io.IOException;
import java.util.ArrayDeque;
import java.util.HashMap;
import java.util.Iterator;
@ -52,6 +58,7 @@ import java.util.concurrent.locks.ReadWriteLock;
import java.util.function.Predicate;
import static java.lang.String.format;
import static org.eclipse.che.api.machine.shared.Constants.ENVIRONMENT_OUTPUT_CHANNEL_TEMPLATE;
import static org.eclipse.che.dto.server.DtoFactory.newDto;
/**
@ -164,7 +171,7 @@ public class WorkspaceRuntimes {
* @throws ServerException
* when component {@link #isPreDestroyInvoked is stopped} or any
* other error occurs during environment start
* @see MachineManager#createMachineSync(MachineConfig, String, String)
* @see MachineManager#createMachineSync(MachineConfig, String, String, org.eclipse.che.api.core.util.LineConsumer)
* @see WorkspaceStatus#STARTING
* @see WorkspaceStatus#RUNNING
*/
@ -574,12 +581,15 @@ public class WorkspaceRuntimes {
boolean recover) throws ServerException,
NotFoundException,
ConflictException {
LineConsumer machineLogger = getMachineLogger(workspaceId, config.getName());
MachineImpl machine;
try {
if (recover) {
machine = machineManager.recoverMachine(config, workspaceId, envName);
machine = machineManager.recoverMachine(config, workspaceId, envName, machineLogger);
} else {
machine = machineManager.createMachineSync(config, workspaceId, envName);
machine = machineManager.createMachineSync(config, workspaceId, envName, machineLogger);
}
} catch (ConflictException x) {
// The conflict is because of the already running machine
@ -614,6 +624,17 @@ public class WorkspaceRuntimes {
return machine;
}
protected LineConsumer getMachineLogger(String workspaceId, String machineName) throws ServerException {
WebsocketMessageConsumer<MachineLogMessage> envMessageConsumer =
new WebsocketMessageConsumer<>(format(ENVIRONMENT_OUTPUT_CHANNEL_TEMPLATE, workspaceId));
return new AbstractLineConsumer() {
@Override
public void writeLine(String line) throws IOException {
envMessageConsumer.consume(new MachineLogMessageImpl(machineName, line));
}
};
}
/**
* Wrapper for the {@link WorkspaceRuntime} instance.
* Knows the state of the started workspace runtime,

View File

@ -27,7 +27,6 @@ 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.core.rest.annotations.GenerateLink;
import org.eclipse.che.api.machine.server.MachineManager;
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;
@ -81,7 +80,6 @@ public class WorkspaceService extends Service {
private final WorkspaceManager workspaceManager;
private final WorkspaceValidator validator;
private final MachineManager machineManager;
private final WorkspaceServiceLinksInjector linksInjector;
@Context
@ -89,11 +87,9 @@ public class WorkspaceService extends Service {
@Inject
public WorkspaceService(WorkspaceManager workspaceManager,
MachineManager machineManager,
WorkspaceValidator validator,
WorkspaceServiceLinksInjector workspaceServiceLinksInjector) {
this.workspaceManager = workspaceManager;
this.machineManager = machineManager;
this.validator = validator;
this.linksInjector = workspaceServiceLinksInjector;
}
@ -240,7 +236,7 @@ public class WorkspaceService extends Service {
ConflictException,
ForbiddenException {
if (!workspaceManager.getSnapshot(id).isEmpty()) {
machineManager.removeSnapshots(workspaceManager.getWorkspace(id).getNamespace(), id);
workspaceManager.removeSnapshots(id);
}
workspaceManager.removeWorkspace(id);
}
@ -646,7 +642,8 @@ public class WorkspaceService extends Service {
@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)"),
"(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")
@ -665,14 +662,7 @@ public class WorkspaceService extends Service {
requiredOnlyOneNotNull(machineConfig.getSource().getLocation(), machineConfig.getSource().getContent(),
"Machine source should provide either location or content");
final WorkspaceImpl workspace = workspaceManager.getWorkspace(workspaceId);
if (workspace.getRuntime() == null) {
throw new NotFoundException(format("Workspace '%s' is not running, new machine can't be started", workspaceId));
}
final MachineImpl machine = machineManager.createMachineAsync(machineConfig,
workspaceId,
workspace.getRuntime().getActiveEnv());
final MachineImpl machine = workspaceManager.startMachine(machineConfig, workspaceId);
return Response.status(201)
.entity(linksInjector.injectMachineLinks(org.eclipse.che.api.machine.server.DtoConverter.asDto(machine),

View File

@ -14,11 +14,9 @@ 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.machine.shared.dto.MachineConfigDto;
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;
import org.eclipse.che.api.workspace.shared.dto.EnvironmentDto;
import org.eclipse.che.api.workspace.shared.dto.WorkspaceDto;
import org.eclipse.che.api.workspace.shared.dto.WorkspaceRuntimeDto;
@ -31,12 +29,17 @@ import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import static java.lang.String.format;
import static java.util.Arrays.asList;
import static java.util.Collections.singletonList;
import static javax.ws.rs.core.MediaType.APPLICATION_JSON;
import static javax.ws.rs.core.MediaType.TEXT_HTML;
import static org.eclipse.che.api.core.model.workspace.WorkspaceStatus.RUNNING;
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.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.LINK_REL_ENVIRONMENT_STATUS_CHANNEL;
import static org.eclipse.che.api.machine.shared.Constants.TERMINAL_REFERENCE;
import static org.eclipse.che.api.machine.shared.Constants.WSAGENT_REFERENCE;
import static org.eclipse.che.api.machine.shared.Constants.WSAGENT_WEBSOCKET_REFERENCE;
@ -116,7 +119,7 @@ public class WorkspaceServiceLinksInjector {
.build();
links.add(createLink("GET", ideUri.toString(), TEXT_HTML, LINK_REL_IDE_URL));
// add workspace channel link
// add workspace channel links
final Link workspaceChannelLink = createLink("GET",
serviceContext.getBaseUriBuilder()
.path("ws")
@ -132,14 +135,16 @@ public class WorkspaceServiceLinksInjector {
.withParameters(singletonList(
cloneDto(channelParameter).withDefaultValue("workspace:" + workspace.getId()))));
// add machine channels links to machines configs
workspace.getConfig()
.getEnvironments()
.stream()
.forEach(environmentDto -> injectMachineChannelsLinks(environmentDto,
workspace.getId(),
workspaceChannelLink,
channelParameter));
links.add(cloneDto(workspaceChannelLink).withRel(LINK_REL_ENVIRONMENT_OUTPUT_CHANNEL)
.withParameters(singletonList(cloneDto(channelParameter)
.withDefaultValue(format(ENVIRONMENT_OUTPUT_CHANNEL_TEMPLATE,
workspace.getId())))));
links.add(cloneDto(workspaceChannelLink).withRel(LINK_REL_ENVIRONMENT_STATUS_CHANNEL)
.withParameters(singletonList(cloneDto(channelParameter)
.withDefaultValue(format(ENVIRONMENT_STATUS_CHANNEL_TEMPLATE,
workspace.getId())))));
// add links for running workspace
injectRuntimeLinks(workspace, ideUri, uriBuilder);
return workspace.withLinks(links);
@ -233,17 +238,4 @@ public class WorkspaceServiceLinksInjector {
public MachineDto injectMachineLinks(MachineDto machine, ServiceContext serviceContext) {
return machineLinksInjector.injectLinks(machine, serviceContext);
}
private void injectMachineChannelsLinks(EnvironmentDto environmentDto,
String workspaceId,
Link machineChannelLink,
LinkParameter channelParameter) {
for (MachineConfigDto machineConfigDto : environmentDto.getMachineConfigs()) {
machineLinksInjector.injectMachineChannelsLinks(machineConfigDto,
workspaceId,
environmentDto.getName(),
machineChannelLink,
channelParameter);
}
}
}

View File

@ -15,6 +15,7 @@ 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.machine.server.model.impl.MachineConfigImpl;
@ -59,6 +60,7 @@ import static org.mockito.Matchers.any;
import static org.mockito.Matchers.anyBoolean;
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.mock;
import static org.mockito.Mockito.spy;
@ -637,6 +639,57 @@ public class WorkspaceManagerTest {
verify(runtimes, timeout(2000)).start(workspace, workspace.getConfig().getDefaultEnv(), true);
}
@Test
public void shouldBeAbleToRemoveMachinesSnapshots() throws Exception {
// given
String testWsId = "testWsId";
String testNamespace = "testNamespace";
WorkspaceImpl workspaceMock = mock(WorkspaceImpl.class);
doReturn(workspaceMock).when(workspaceManager).getWorkspace(testWsId);
when(workspaceMock.getNamespace()).thenReturn(testNamespace);
// when
workspaceManager.removeSnapshots(testWsId);
// then
verify(machineManager).removeSnapshots(testNamespace, testWsId);
}
@Test
public void shouldBeAbleToStartMachineInRunningWs() throws Exception {
// given
String testWsId = "testWsId";
String testActiveEnv = "testActiveEnv";
WorkspaceImpl workspaceMock = mock(WorkspaceImpl.class);
doReturn(workspaceMock).when(workspaceManager).getWorkspace(testWsId);
WorkspaceRuntimeImpl wsRuntimeMock = mock(WorkspaceRuntimeImpl.class);
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));
}
@Test(expectedExceptions = ConflictException.class, expectedExceptionsMessageRegExp = "Workspace .* is not running, new machine can't be started")
public void shouldThrowExceptionOnStartMachineInNonRunningWs() throws Exception {
// given
String testWsId = "testWsId";
WorkspaceImpl workspaceMock = mock(WorkspaceImpl.class);
doReturn(workspaceMock).when(workspaceManager).getWorkspace(testWsId);
when(workspaceMock.getStatus()).thenReturn(STARTING);
MachineConfigImpl machineConfig = createConfig().getEnvironments().get(0).getMachineConfigs().get(0);
// when
workspaceManager.startMachine(machineConfig, testWsId);
}
private RuntimeDescriptor createDescriptor(WorkspaceImpl workspace, WorkspaceStatus status) {
final WorkspaceRuntimeImpl runtime = new WorkspaceRuntimeImpl(workspace.getConfig().getDefaultEnv());
final String env = workspace.getConfig().getDefaultEnv();

View File

@ -17,6 +17,7 @@ import org.eclipse.che.api.core.model.machine.MachineConfig;
import org.eclipse.che.api.core.model.machine.MachineStatus;
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.machine.server.model.impl.LimitsImpl;
@ -53,6 +54,7 @@ import static org.mockito.Matchers.any;
import static org.mockito.Matchers.anyBoolean;
import static org.mockito.Matchers.anyObject;
import static org.mockito.Matchers.anyString;
import static org.mockito.Matchers.eq;
import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.doNothing;
import static org.mockito.Mockito.doReturn;
@ -90,7 +92,7 @@ public class WorkspaceRuntimesTest {
@BeforeMethod
public void setUp() throws Exception {
when(machineManager.createMachineSync(any(), any(), any()))
when(machineManager.createMachineSync(any(), any(), any(), any(LineConsumer.class)))
.thenAnswer(invocation -> createMachine((MachineConfig)invocation.getArguments()[0]));
runtimes = new WorkspaceRuntimes(machineManager, eventService);
}
@ -117,7 +119,7 @@ public class WorkspaceRuntimesTest {
final WorkspaceImpl workspace = createWorkspace();
// check if workspace in starting status before dev machine is started
when(machineManagerMock.createMachineSync(anyObject(), anyString(), anyString()))
when(machineManagerMock.createMachineSync(anyObject(), anyString(), anyString(), any(LineConsumer.class)))
.thenAnswer(invocationOnMock -> {
final RuntimeDescriptor descriptor = runtimes.get(workspace.getId());
final MachineConfig cfg = (MachineConfig)invocationOnMock.getArguments()[0];
@ -129,7 +131,7 @@ public class WorkspaceRuntimesTest {
runtimes.start(workspace, workspace.getConfig().getDefaultEnv(), false);
verify(machineManagerMock, times(2)).createMachineSync(anyObject(), anyString(), anyString());
verify(machineManagerMock, times(2)).createMachineSync(anyObject(), anyString(), anyString(), any(LineConsumer.class));
}
@Test
@ -137,7 +139,7 @@ public class WorkspaceRuntimesTest {
final MachineManager machineManagerMock = mock(MachineManager.class);
final WorkspaceRuntimes runtimes = new WorkspaceRuntimes(machineManagerMock, eventService);
final WorkspaceImpl workspaceMock = createWorkspace();
when(machineManagerMock.createMachineSync(any(), any(), any()))
when(machineManagerMock.createMachineSync(any(), any(), any(), any(LineConsumer.class)))
.thenThrow(new MachineException("Creation error"));
try {
@ -175,7 +177,7 @@ public class WorkspaceRuntimesTest {
final WorkspaceRuntimes registry = new WorkspaceRuntimes(machineManagerMock, eventService);
final WorkspaceImpl workspace = createWorkspace();
when(machineManagerMock.createMachineSync(any(), any(), any())).thenAnswer(invocationOnMock -> {
when(machineManagerMock.createMachineSync(any(), any(), any(), any(LineConsumer.class))).thenAnswer(invocationOnMock -> {
registry.stop(workspace.getId());
return createMachine((MachineConfig)invocationOnMock.getArguments()[0]);
});
@ -193,7 +195,7 @@ public class WorkspaceRuntimesTest {
runtimes.stop(workspace.getId());
}
return createMachine((MachineConfig)invocation.getArguments()[0]);
}).when(machineManager).createMachineSync(any(), anyString(), anyString());
}).when(machineManager).createMachineSync(any(), anyString(), anyString(), any(LineConsumer.class));
try {
runtimes.start(workspace, workspace.getConfig().getDefaultEnv());
@ -224,7 +226,7 @@ public class WorkspaceRuntimesTest {
throw new MachineException("Failed to start");
}
return createMachine((MachineConfig)invocation.getArguments()[0]);
}).when(machineManager).createMachineSync(any(), anyString(), anyString());
}).when(machineManager).createMachineSync(any(), anyString(), anyString(), any(LineConsumer.class));
runtimes.start(workspace, workspace.getConfig().getDefaultEnv());
@ -232,7 +234,7 @@ public class WorkspaceRuntimesTest {
final RuntimeDescriptor descriptor = runtimes.get(workspace.getId());
assertEquals(descriptor.getRuntime().getMachines().size(), 1);
assertEquals(descriptor.getRuntimeStatus(), RUNNING);
verify(machineManager, times(2)).createMachineSync(any(), any(), any());
verify(machineManager, times(2)).createMachineSync(any(), any(), any(), any(LineConsumer.class));
}
@Test
@ -245,7 +247,7 @@ public class WorkspaceRuntimesTest {
runtimes.cleanup();
}
return createMachine((MachineConfig)invocation.getArguments()[0]);
}).when(machineManager).createMachineSync(any(), anyString(), anyString());
}).when(machineManager).createMachineSync(any(), anyString(), anyString(), any(LineConsumer.class));
try {
runtimes.start(workspace, workspace.getConfig().getDefaultEnv());
@ -323,7 +325,7 @@ public class WorkspaceRuntimesTest {
doAnswer(invocation -> {
verify(runtimes).publishEvent(EventType.STARTING, workspace.getId(), null);
return null;
}).when(machineManager).createMachineSync(any(), any(), any());
}).when(machineManager).createMachineSync(any(), any(), any(), any(LineConsumer.class));
runtimes.start(workspace, workspace.getConfig().getDefaultEnv());
}
@ -340,7 +342,7 @@ public class WorkspaceRuntimesTest {
verify(runtimes).publishEvent(EventType.RUNNING, workspace.getId(), null);
}
return createMachine(cfg);
}).when(machineManager).createMachineSync(any(), any(), any());
}).when(machineManager).createMachineSync(any(), any(), any(), any(LineConsumer.class));
runtimes.start(workspace, workspace.getConfig().getDefaultEnv());
}
@ -358,7 +360,7 @@ public class WorkspaceRuntimesTest {
throw new MachineException("Start error");
}
return createMachine(cfg);
}).when(machineManager).createMachineSync(any(), any(), any());
}).when(machineManager).createMachineSync(any(), any(), any(), any(LineConsumer.class));
try {
runtimes.start(workspace, workspace.getConfig().getDefaultEnv());
@ -652,9 +654,10 @@ public class WorkspaceRuntimesTest {
// force machine manager to throw conflict exception
final RuntimeDescriptor descriptorMock = mock(RuntimeDescriptor.class);
when(descriptorMock.getRuntimeStatus()).thenReturn(WorkspaceStatus.RUNNING);
doThrow(new ConflictException("already exists")).when(machineManager).createMachineSync(machine.getConfig(),
machine.getWorkspaceId(),
workspace.getConfig().getDefaultEnv());
doThrow(new ConflictException("already exists")).when(machineManager).createMachineSync(eq(machine.getConfig()),
eq(machine.getWorkspaceId()),
eq(workspace.getConfig().getDefaultEnv()),
any(LineConsumer.class));
final RuntimeDescriptor descriptor = runtimes.start(workspace, workspace.getConfig().getDefaultEnv());

View File

@ -73,6 +73,8 @@ import static java.util.stream.Collectors.toList;
import static java.util.stream.Collectors.toSet;
import static org.eclipse.che.api.core.model.workspace.WorkspaceStatus.RUNNING;
import static org.eclipse.che.api.core.model.workspace.WorkspaceStatus.STARTING;
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_ENVIRONMENT_STATUS_CHANNEL;
import static org.eclipse.che.api.machine.shared.Constants.WSAGENT_REFERENCE;
import static org.eclipse.che.api.machine.shared.Constants.WSAGENT_WEBSOCKET_REFERENCE;
import static org.eclipse.che.api.workspace.shared.Constants.GET_ALL_USER_WORKSPACES;
@ -127,7 +129,6 @@ public class WorkspaceServiceTest {
@BeforeMethod
public void setup() {
service = new WorkspaceService(wsManager,
machineManager,
validator,
new WorkspaceServiceLinksInjector(new MachineServiceLinksInjector()));
}
@ -295,7 +296,7 @@ public class WorkspaceServiceTest {
.delete(SECURE_PATH + "/workspace/" + workspace.getId());
assertEquals(response.getStatusCode(), 204);
verify(machineManager).removeSnapshots(NAMESPACE, workspace.getId());
verify(wsManager).removeSnapshots(workspace.getId());
verify(wsManager).removeWorkspace(workspace.getId());
}
@ -681,7 +682,9 @@ public class WorkspaceServiceTest {
LINK_REL_GET_SNAPSHOT,
LINK_REL_GET_WORKSPACE_EVENTS_CHANNEL,
LINK_REL_IDE_URL,
LINK_REL_SELF));
LINK_REL_SELF,
LINK_REL_ENVIRONMENT_OUTPUT_CHANNEL,
LINK_REL_ENVIRONMENT_STATUS_CHANNEL));
assertTrue(actualRels.equals(expectedRels), format("Links difference: '%s'. \n" +
"Returned links: '%s', \n" +
"Expected links: '%s'.",