use of org.eclipse.che.api.environment.server.exception.EnvironmentStartInterruptedException in project che by eclipse.
the class CheEnvironmentEngine method startEnvironmentQueue.
/**
* Starts all machine from machine queue of environment.
*/
private void startEnvironmentQueue(String namespace, String workspaceId, String devMachineName, String networkId, boolean recover, MachineStartedHandler startedHandler) throws ServerException, EnvironmentException {
// 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
String envName;
MessageConsumer<MachineLogMessage> envLogger;
String creator = EnvironmentContext.getCurrent().getSubject().getUserId();
try (@SuppressWarnings("unused") Unlocker u = stripedLocks.readLock(workspaceId)) {
EnvironmentHolder environmentHolder = environments.get(workspaceId);
if (environmentHolder == null) {
throw new ServerException("Environment start is interrupted.");
}
envName = environmentHolder.name;
envLogger = environmentHolder.logger;
}
try {
machineProvider.createNetwork(networkId);
String machineName = queuePeekOrFail(workspaceId);
while (machineName != null) {
boolean isDev = devMachineName.equals(machineName);
// 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
CheServiceImpl service;
@Nullable ExtendedMachine extendedMachine;
try (@SuppressWarnings("unused") Unlocker u = stripedLocks.readLock(workspaceId)) {
EnvironmentHolder environmentHolder = environments.get(workspaceId);
if (environmentHolder == null) {
throw new ServerException("Environment start is interrupted.");
}
service = environmentHolder.environment.getServices().get(machineName);
extendedMachine = environmentHolder.environmentConfig.getMachines().get(machineName);
}
// should not happen
if (service == null) {
LOG.error("Start of machine with name {} in workspace {} failed. Machine not found in start queue", machineName, workspaceId);
throw new ServerException(format("Environment of workspace with ID '%s' failed due to internal error", workspaceId));
}
final String finalMachineName = machineName;
// needed to reuse startInstance method and
// create machine instances by different implementation-specific providers
MachineStarter machineStarter = (machineLogger, machineSource) -> {
CheServiceImpl serviceWithNormalizedSource = normalizeServiceSource(service, machineSource);
return machineProvider.startService(namespace, workspaceId, envName, finalMachineName, isDev, networkId, serviceWithNormalizedSource, machineLogger);
};
MachineImpl machine = MachineImpl.builder().setConfig(MachineConfigImpl.builder().setDev(isDev).setLimits(new MachineLimitsImpl(bytesToMB(service.getMemLimit()))).setType("docker").setName(machineName).setEnvVariables(service.getEnvironment()).build()).setId(service.getId()).setWorkspaceId(workspaceId).setStatus(MachineStatus.CREATING).setEnvName(envName).setOwner(creator).build();
checkInterruption(workspaceId, envName);
Instance instance = startInstance(recover, envLogger, machine, machineStarter);
checkInterruption(workspaceId, envName);
startedHandler.started(instance, extendedMachine);
checkInterruption(workspaceId, envName);
// 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 (@SuppressWarnings("unused") Unlocker u = stripedLocks.writeLock(workspaceId)) {
ensurePreDestroyIsNotExecuted();
EnvironmentHolder environmentHolder = environments.get(workspaceId);
if (environmentHolder != null) {
final Queue<String> queue = environmentHolder.startQueue;
if (queue != null) {
queue.poll();
queuePolled = true;
}
}
}
// must be destroyed
if (!queuePolled) {
try {
eventService.publish(newDto(MachineStatusEvent.class).withEventType(MachineStatusEvent.EventType.DESTROYING).withDev(isDev).withMachineName(machineName).withMachineId(instance.getId()).withWorkspaceId(workspaceId));
instance.destroy();
removeMachine(workspaceId, instance.getId());
eventService.publish(newDto(MachineStatusEvent.class).withEventType(MachineStatusEvent.EventType.DESTROYED).withDev(isDev).withMachineName(machineName).withMachineId(instance.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");
}
machineName = queuePeekOrFail(workspaceId);
}
} catch (RuntimeException | ServerException | EnvironmentStartInterruptedException e) {
boolean interrupted = Thread.interrupted();
EnvironmentHolder env;
try (@SuppressWarnings("unused") Unlocker u = stripedLocks.writeLock(workspaceId)) {
env = environments.remove(workspaceId);
}
try {
destroyEnvironment(env.networkId, env.machines);
} catch (Exception remEx) {
LOG.error(remEx.getLocalizedMessage(), remEx);
}
if (interrupted) {
throw new EnvironmentStartInterruptedException(workspaceId, envName);
}
try {
throw e;
} catch (ServerException | EnvironmentStartInterruptedException rethrow) {
throw rethrow;
} catch (Exception wrap) {
throw new ServerException(wrap.getMessage(), wrap);
}
}
}
use of org.eclipse.che.api.environment.server.exception.EnvironmentStartInterruptedException in project che by eclipse.
the class WorkspaceRuntimes method startEnvironmentAndPublishEvents.
/**
* Starts the environment publishing all the necessary events.
* Respects task interruption & stops the workspace if starting task is cancelled.
*/
private void startEnvironmentAndPublishEvents(EnvironmentImpl environment, String workspaceId, String envName, boolean recover) throws ServerException, EnvironmentException, ConflictException {
try {
envEngine.start(workspaceId, envName, environment, recover, new WebsocketMessageConsumer<>(format(ENVIRONMENT_OUTPUT_CHANNEL_TEMPLATE, workspaceId)), machineAgentsLauncher);
} catch (EnvironmentStartInterruptedException x) {
// environment start was interrupted, it's either shutdown or direct stop
// in the case of shutdown make sure the status is correct,
// otherwise workspace is already stopping
compareAndSetStatus(workspaceId, WorkspaceStatus.STARTING, WorkspaceStatus.STOPPING);
removeStateAndPublishStopEvents(workspaceId);
throw x;
} catch (EnvironmentException | ServerException | ConflictException x) {
// environment can't be started for some reason, STARTING -> STOPPED
removeState(workspaceId);
eventsService.publish(DtoFactory.newDto(WorkspaceStatusEvent.class).withWorkspaceId(workspaceId).withEventType(EventType.ERROR).withPrevStatus(WorkspaceStatus.STARTING).withStatus(WorkspaceStatus.STOPPED).withError("Start of environment '" + envName + "' failed. Error: " + x.getMessage()));
throw x;
}
// disallow direct start cancellation, STARTING -> RUNNING
WorkspaceStatus prevStatus;
try (@SuppressWarnings("unused") Unlocker u = locks.writeLock(workspaceId)) {
checkIsNotTerminated("finish workspace start");
RuntimeState state = states.get(workspaceId);
prevStatus = state.status;
if (state.status == WorkspaceStatus.STARTING) {
state.status = WorkspaceStatus.RUNNING;
state.startTask = null;
state.startFuture = null;
}
}
// or stop is called directly, anyway stop the environment
if (Thread.interrupted() || prevStatus != WorkspaceStatus.STARTING) {
try {
stopEnvironmentAndPublishEvents(workspaceId, WorkspaceStatus.STARTING);
} catch (Exception x) {
LOG.error("Couldn't stop the environment '{}' of the workspace '{}'. Error: {}", envName, workspaceId, x.getMessage());
}
throw new EnvironmentStartInterruptedException(workspaceId, envName);
}
// normally started, notify clients
eventsService.publish(DtoFactory.newDto(WorkspaceStatusEvent.class).withWorkspaceId(workspaceId).withStatus(WorkspaceStatus.RUNNING).withEventType(EventType.RUNNING).withPrevStatus(WorkspaceStatus.STARTING));
}
use of org.eclipse.che.api.environment.server.exception.EnvironmentStartInterruptedException in project che by eclipse.
the class CheEnvironmentEngineTest method stopsTheEnvironmentWhileStartOfMachineIsInterrupted.
@Test
public void stopsTheEnvironmentWhileStartOfMachineIsInterrupted() throws Exception {
// given
EnvironmentImpl env = createEnv();
String envName = "env-1";
String workspaceId = "wsId";
int[] counter = new int[] { env.getMachines().size() };
ArrayList<Instance> created = new ArrayList<>();
when(machineProvider.startService(anyString(), eq(workspaceId), eq(envName), anyString(), anyBoolean(), anyString(), any(CheServiceImpl.class), any(LineConsumer.class))).thenAnswer(invocationOnMock -> {
if (--counter[0] == 0) {
Thread.currentThread().interrupt();
throw new ServerException("interrupted!");
}
Object[] arguments = invocationOnMock.getArguments();
NoOpMachineInstance instance = spy(new NoOpMachineInstance(createMachine(workspaceId, envName, (CheServiceImpl) arguments[6], (String) arguments[3], (boolean) arguments[4])));
created.add(instance);
return instance;
});
when(environmentParser.parse(env)).thenReturn(createCheServicesEnv());
// when, then
try {
engine.start(workspaceId, envName, env, false, messageConsumer, startedHandler);
fail("environment must not be running");
} catch (EnvironmentStartInterruptedException x) {
assertEquals(x.getMessage(), format("Start of environment '%s' in workspace '%s' is interrupted", envName, workspaceId));
}
// environment must not be running
try {
engine.getMachines(workspaceId);
fail("environment must not be running");
} catch (EnvironmentNotRunningException x) {
assertEquals(x.getMessage(), format("Environment with ID '%s' is not found", workspaceId));
}
// all the machines expect of the last one must be destroyed
for (Instance instance : created) {
verify(instance).destroy();
}
}
use of org.eclipse.che.api.environment.server.exception.EnvironmentStartInterruptedException in project che by eclipse.
the class WorkspaceRuntimes method stop.
/**
* Stops running workspace runtime.
*
* <p>Stops environment in an implementation specific way.
* During the stop of the workspace its runtime is accessible with {@link WorkspaceStatus#STOPPING stopping} status.
* Workspace may be stopped only if its status is {@link WorkspaceStatus#RUNNING}.
*
* @param workspaceId
* identifier of workspace which should be stopped
* @throws NotFoundException
* when workspace with specified identifier is not running
* @throws ServerException
* when any error occurs during workspace stopping
* @throws ConflictException
* when running workspace status is different from {@link WorkspaceStatus#RUNNING}
* @see CheEnvironmentEngine#stop(String)
* @see WorkspaceStatus#STOPPING
*/
public void stop(String workspaceId) throws NotFoundException, ServerException, ConflictException, EnvironmentException {
requireNonNull(workspaceId, "Required not-null workspace id");
RuntimeState prevState;
try (@SuppressWarnings("unused") Unlocker u = locks.writeLock(workspaceId)) {
checkIsNotTerminated("stop the workspace");
RuntimeState state = getExistingState(workspaceId);
if (state.status != WorkspaceStatus.RUNNING && state.status != WorkspaceStatus.STARTING) {
throw new ConflictException(format("Couldn't stop the workspace '%s' because its status is '%s'. " + "Workspace can be stopped only if it is 'RUNNING' or 'STARTING'", workspaceId, state.status));
}
prevState = new RuntimeState(state);
state.status = WorkspaceStatus.STOPPING;
}
// workspace is running, stop normally
if (prevState.status == WorkspaceStatus.RUNNING) {
stopEnvironmentAndPublishEvents(workspaceId, WorkspaceStatus.RUNNING);
return;
}
// interrupt workspace start thread
prevState.startFuture.cancel(true);
// if task wasn't called by executor service, then
// no real machines were started but, the clients still
// have to be notified about the workspace shut down
StartTask startTask = prevState.startTask;
if (startTask.markAsUsed()) {
removeStateAndPublishStopEvents(workspaceId);
prevState.startTask.earlyComplete();
return;
}
// otherwise stop will be triggered by the start task, wait for it to finish
try {
startTask.await();
} catch (EnvironmentStartInterruptedException ignored) {
// environment start successfully interrupted
} catch (InterruptedException x) {
Thread.currentThread().interrupt();
throw new ServerException("Interrupted while waiting for start task cancellation", x);
}
}
use of org.eclipse.che.api.environment.server.exception.EnvironmentStartInterruptedException in project che by eclipse.
the class WorkspaceRuntimesTest method cancellationOfRunningStartTask.
@Test
public void cancellationOfRunningStartTask() throws Exception {
setRuntime("workspace", WorkspaceStatus.STARTING, "env-name", runtimeFuture, startTask);
doThrow(new EnvironmentStartInterruptedException("workspace", "env-name")).when(startTask).await();
runtimes.stop("workspace");
verify(runtimeFuture).cancel(true);
verify(startTask).await();
}
Aggregations