Recover runtimes on workspace master startup
parent
50f3b4508a
commit
0d6c7795fa
|
|
@ -26,4 +26,12 @@ public class ValidationException extends Exception {
|
|||
public ValidationException(String message, Throwable cause) {
|
||||
super(message, cause);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates an exception with a formatted message.
|
||||
* Please follow {@link String#format(String, Object...)} formatting patterns.
|
||||
*/
|
||||
public ValidationException(String fmt, Object... args) {
|
||||
this(String.format(fmt, args));
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -10,13 +10,16 @@
|
|||
*******************************************************************************/
|
||||
package org.eclipse.che.workspace.infrastructure.docker;
|
||||
|
||||
import org.eclipse.che.api.installer.server.exception.InstallerException;
|
||||
import org.eclipse.che.api.core.model.workspace.config.Environment;
|
||||
import org.eclipse.che.api.core.model.workspace.config.MachineConfig;
|
||||
import org.eclipse.che.api.core.model.workspace.runtime.RuntimeIdentity;
|
||||
import org.eclipse.che.api.installer.server.exception.InstallerException;
|
||||
import org.eclipse.che.api.workspace.server.spi.InfrastructureException;
|
||||
import org.eclipse.che.workspace.infrastructure.docker.model.DockerContainerConfig;
|
||||
import org.eclipse.che.workspace.infrastructure.docker.model.DockerEnvironment;
|
||||
|
||||
import javax.inject.Inject;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* Implementation of CHE infrastructure provisioner that adds agent-specific infrastructure to internal environment representation.
|
||||
|
|
@ -41,5 +44,15 @@ public class DefaultInfrastructureProvisioner implements InfrastructureProvision
|
|||
} catch (InstallerException e) {
|
||||
throw new InfrastructureException(e.getLocalizedMessage(), e);
|
||||
}
|
||||
|
||||
for (Map.Entry<String, ? extends MachineConfig> entry : envConfig.getMachines().entrySet()) {
|
||||
String name = entry.getKey();
|
||||
DockerContainerConfig container = dockerEnvironment.getContainers().get(name);
|
||||
container.getLabels().putAll(Labels.newSerializer()
|
||||
.machineName(name)
|
||||
.runtimeId(runtimeIdentity)
|
||||
.servers(entry.getValue().getServers())
|
||||
.labels());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,80 @@
|
|||
/*******************************************************************************
|
||||
* Copyright (c) 2012-2017 Codenvy, S.A.
|
||||
* All rights reserved. This program and the accompanying materials
|
||||
* are made available under the terms of the Eclipse Public License v1.0
|
||||
* which accompanies this distribution, and is available at
|
||||
* http://www.eclipse.org/legal/epl-v10.html
|
||||
*
|
||||
* Contributors:
|
||||
* Codenvy, S.A. - initial API and implementation
|
||||
*******************************************************************************/
|
||||
package org.eclipse.che.workspace.infrastructure.docker;
|
||||
|
||||
import org.eclipse.che.api.core.model.workspace.runtime.RuntimeIdentity;
|
||||
import org.eclipse.che.api.workspace.server.spi.InternalInfrastructureException;
|
||||
import org.eclipse.che.plugin.docker.client.DockerConnector;
|
||||
import org.eclipse.che.plugin.docker.client.json.ContainerListEntry;
|
||||
import org.eclipse.che.plugin.docker.client.json.Filters;
|
||||
import org.eclipse.che.plugin.docker.client.params.ListContainersParams;
|
||||
|
||||
import javax.inject.Inject;
|
||||
import javax.inject.Singleton;
|
||||
import java.io.IOException;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/** Facade for operations with docker containers in infrastructure domain context. */
|
||||
@Singleton
|
||||
public class DockerContainers {
|
||||
|
||||
private final DockerConnector docker;
|
||||
|
||||
@Inject
|
||||
public DockerContainers(DockerConnector docker) {
|
||||
this.docker = docker;
|
||||
}
|
||||
|
||||
/**
|
||||
* Lookups all containers owned by runtime with given identity.
|
||||
*
|
||||
* @param id
|
||||
* identity of runtime which owns containers
|
||||
* @return list of running containers owned by runtime with given id
|
||||
* @throws InternalInfrastructureException
|
||||
* when any error occurs during lookup
|
||||
*/
|
||||
public List<ContainerListEntry> find(RuntimeIdentity id) throws InternalInfrastructureException {
|
||||
return listNonStoppedContainers(Labels.newSerializer()
|
||||
.runtimeId(id)
|
||||
.labels()
|
||||
.entrySet()
|
||||
.stream()
|
||||
.map(entry -> entry.getKey() + '=' + entry.getValue())
|
||||
.toArray(String[]::new));
|
||||
}
|
||||
|
||||
/**
|
||||
* Lookups all runtime identifiers provided by running containers labels.
|
||||
*
|
||||
* @return unique runtime identifiers of running containers
|
||||
* @throws InternalInfrastructureException
|
||||
* when any error occurs during lookup
|
||||
*/
|
||||
public Set<RuntimeIdentity> findIdentities() throws InternalInfrastructureException {
|
||||
return listNonStoppedContainers(Labels.LABEL_WORKSPACE_ID, Labels.LABEL_WORKSPACE_ID, Labels.LABEL_WORKSPACE_OWNER)
|
||||
.stream()
|
||||
.map(e -> Labels.newDeserializer(e.getLabels()).runtimeId())
|
||||
.collect(Collectors.toSet());
|
||||
}
|
||||
|
||||
private List<ContainerListEntry> listNonStoppedContainers(String... labelFilterValues) throws InternalInfrastructureException {
|
||||
try {
|
||||
return docker.listContainers(ListContainersParams.create()
|
||||
.withAll(false)
|
||||
.withFilters(Filters.label(labelFilterValues)));
|
||||
} catch (IOException x) {
|
||||
throw new InternalInfrastructureException(x.getMessage(), x);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -21,6 +21,8 @@ import org.eclipse.che.api.workspace.server.spi.RuntimeInfrastructure;
|
|||
import org.eclipse.che.plugin.docker.client.DockerRegistryDynamicAuthResolver;
|
||||
import org.eclipse.che.plugin.docker.client.NoOpDockerRegistryDynamicAuthResolverImpl;
|
||||
import org.eclipse.che.workspace.infrastructure.docker.bootstrap.DockerBootstrapperFactory;
|
||||
import org.eclipse.che.workspace.infrastructure.docker.bootstrap.DockerBootstrapper;
|
||||
import org.eclipse.che.workspace.infrastructure.docker.bootstrap.DockerBootstrapperFactory;
|
||||
import org.eclipse.che.workspace.infrastructure.docker.config.DockerExtraHostsFromPropertyProvider;
|
||||
import org.eclipse.che.workspace.infrastructure.docker.config.dns.DnsResolversModule;
|
||||
import org.eclipse.che.workspace.infrastructure.docker.config.env.ApiEndpointEnvVariableProvider;
|
||||
|
|
@ -106,8 +108,8 @@ public class DockerInfraModule extends AbstractModule {
|
|||
bind(DockerRegistryDynamicAuthResolver.class).to(NoOpDockerRegistryDynamicAuthResolverImpl.class);
|
||||
|
||||
install(new FactoryModuleBuilder().build(DockerRuntimeFactory.class));
|
||||
|
||||
install(new FactoryModuleBuilder().build(DockerBootstrapperFactory.class));
|
||||
install(new FactoryModuleBuilder().build(DockerRuntimeContextFactory.class));
|
||||
|
||||
bind(ServerCheckerFactory.class).to(ServerCheckerFactoryImpl.class);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -11,6 +11,7 @@
|
|||
package org.eclipse.che.workspace.infrastructure.docker;
|
||||
|
||||
import com.google.inject.assistedinject.Assisted;
|
||||
import com.google.inject.assistedinject.AssistedInject;
|
||||
|
||||
import org.eclipse.che.api.core.NotFoundException;
|
||||
import org.eclipse.che.api.core.model.workspace.runtime.Machine;
|
||||
|
|
@ -29,12 +30,15 @@ import org.eclipse.che.api.workspace.shared.dto.event.MachineStatusEvent;
|
|||
import org.eclipse.che.api.workspace.shared.dto.event.RuntimeStatusEvent;
|
||||
import org.eclipse.che.api.workspace.shared.dto.event.ServerStatusEvent;
|
||||
import org.eclipse.che.dto.server.DtoFactory;
|
||||
import org.eclipse.che.plugin.docker.client.ProgressMonitor;
|
||||
import org.eclipse.che.plugin.docker.client.json.ContainerListEntry;
|
||||
import org.eclipse.che.workspace.infrastructure.docker.bootstrap.DockerBootstrapperFactory;
|
||||
import org.eclipse.che.workspace.infrastructure.docker.exception.SourceNotFoundException;
|
||||
import org.eclipse.che.workspace.infrastructure.docker.model.DockerBuildContext;
|
||||
import org.eclipse.che.workspace.infrastructure.docker.model.DockerContainerConfig;
|
||||
import org.eclipse.che.workspace.infrastructure.docker.model.DockerEnvironment;
|
||||
import org.eclipse.che.workspace.infrastructure.docker.monit.AbnormalMachineStopHandler;
|
||||
import org.eclipse.che.workspace.infrastructure.docker.monit.DockerMachineStopDetector;
|
||||
import org.eclipse.che.workspace.infrastructure.docker.server.ServerCheckerFactory;
|
||||
import org.eclipse.che.workspace.infrastructure.docker.server.ServersReadinessChecker;
|
||||
import org.eclipse.che.workspace.infrastructure.docker.snapshot.MachineSource;
|
||||
|
|
@ -44,7 +48,6 @@ import org.eclipse.che.workspace.infrastructure.docker.snapshot.SnapshotExceptio
|
|||
import org.eclipse.che.workspace.infrastructure.docker.snapshot.SnapshotImpl;
|
||||
import org.slf4j.Logger;
|
||||
|
||||
import javax.inject.Inject;
|
||||
import java.util.ArrayDeque;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
|
|
@ -68,53 +71,115 @@ public class DockerInternalRuntime extends InternalRuntime<DockerRuntimeContext>
|
|||
|
||||
private final StartSynchronizer startSynchronizer;
|
||||
private final Map<String, String> properties;
|
||||
private final Queue<String> startQueue;
|
||||
private final NetworkLifecycle dockerNetworkLifecycle;
|
||||
private final String devMachineName;
|
||||
private final DockerEnvironment dockerEnvironment;
|
||||
private final NetworkLifecycle networks;
|
||||
private final DockerMachineStarter containerStarter;
|
||||
private final SnapshotDao snapshotDao;
|
||||
private final DockerRegistryClient dockerRegistryClient;
|
||||
private final RuntimeIdentity identity;
|
||||
private final EventService eventService;
|
||||
private final DockerBootstrapperFactory bootstrapperFactory;
|
||||
private final ServerCheckerFactory serverCheckerFactory;
|
||||
private final MachineLoggersFactory loggers;
|
||||
|
||||
@Inject
|
||||
/**
|
||||
* Creates non running runtime.
|
||||
* Normally created by {@link DockerRuntimeFactory#create(DockerRuntimeContext)}.
|
||||
*/
|
||||
@AssistedInject
|
||||
public DockerInternalRuntime(@Assisted DockerRuntimeContext context,
|
||||
@Assisted String devMachineName,
|
||||
@Assisted List<String> orderedContainers,
|
||||
@Assisted DockerEnvironment dockerEnvironment,
|
||||
@Assisted RuntimeIdentity identity,
|
||||
URLRewriter urlRewriter,
|
||||
NetworkLifecycle dockerNetworkLifecycle,
|
||||
DockerMachineStarter containerStarter,
|
||||
NetworkLifecycle networks,
|
||||
DockerMachineStarter machineStarter,
|
||||
SnapshotDao snapshotDao,
|
||||
DockerRegistryClient dockerRegistryClient,
|
||||
EventService eventService,
|
||||
DockerBootstrapperFactory bootstrapperFactory,
|
||||
ServerCheckerFactory serverCheckerFactory) {
|
||||
super(context, urlRewriter);
|
||||
this.devMachineName = devMachineName;
|
||||
this.dockerEnvironment = dockerEnvironment;
|
||||
this.identity = identity;
|
||||
ServerCheckerFactory serverCheckerFactory,
|
||||
MachineLoggersFactory loggers) {
|
||||
this(context,
|
||||
urlRewriter,
|
||||
false, // <- non running
|
||||
networks,
|
||||
machineStarter,
|
||||
snapshotDao,
|
||||
dockerRegistryClient,
|
||||
eventService,
|
||||
bootstrapperFactory,
|
||||
serverCheckerFactory,
|
||||
loggers);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a running runtime from the list of given containers.
|
||||
* Normally created by {@link DockerRuntimeFactory#create(DockerRuntimeContext, List)}.
|
||||
*/
|
||||
@AssistedInject
|
||||
public DockerInternalRuntime(@Assisted DockerRuntimeContext context,
|
||||
@Assisted List<ContainerListEntry> containers,
|
||||
URLRewriter urlRewriter,
|
||||
NetworkLifecycle networks,
|
||||
DockerMachineStarter machineStarter,
|
||||
SnapshotDao snapshotDao,
|
||||
DockerRegistryClient dockerRegistryClient,
|
||||
EventService eventService,
|
||||
DockerBootstrapperFactory bootstrapperFactory,
|
||||
ServerCheckerFactory serverCheckerFactory,
|
||||
MachineLoggersFactory loggers,
|
||||
DockerMachineCreator machineCreator,
|
||||
DockerMachineStopDetector stopDetector) throws InfrastructureException {
|
||||
this(context,
|
||||
urlRewriter,
|
||||
true, // <- running
|
||||
networks,
|
||||
machineStarter,
|
||||
snapshotDao,
|
||||
dockerRegistryClient,
|
||||
eventService,
|
||||
bootstrapperFactory,
|
||||
serverCheckerFactory,
|
||||
loggers);
|
||||
|
||||
for (ContainerListEntry container : containers) {
|
||||
DockerMachine machine = machineCreator.create(container);
|
||||
String name = Labels.newDeserializer(container.getLabels()).machineName();
|
||||
|
||||
startSynchronizer.addMachine(name, machine);
|
||||
stopDetector.startDetection(container.getId(), name, new AbnormalMachineStopHandlerImpl());
|
||||
streamLogsAsync(name, container.getId());
|
||||
}
|
||||
}
|
||||
|
||||
private DockerInternalRuntime(DockerRuntimeContext context,
|
||||
URLRewriter urlRewriter,
|
||||
boolean running,
|
||||
NetworkLifecycle networks,
|
||||
DockerMachineStarter machineStarter,
|
||||
SnapshotDao snapshotDao,
|
||||
DockerRegistryClient dockerRegistryClient,
|
||||
EventService eventService,
|
||||
DockerBootstrapperFactory bootstrapperFactory,
|
||||
ServerCheckerFactory serverCheckerFactory,
|
||||
MachineLoggersFactory loggers) {
|
||||
super(context, urlRewriter, running);
|
||||
this.networks = networks;
|
||||
this.containerStarter = machineStarter;
|
||||
this.snapshotDao = snapshotDao;
|
||||
this.dockerRegistryClient = dockerRegistryClient;
|
||||
this.eventService = eventService;
|
||||
this.bootstrapperFactory = bootstrapperFactory;
|
||||
this.serverCheckerFactory = serverCheckerFactory;
|
||||
this.properties = new HashMap<>();
|
||||
this.startSynchronizer = new StartSynchronizer();
|
||||
this.dockerNetworkLifecycle = dockerNetworkLifecycle;
|
||||
this.containerStarter = containerStarter;
|
||||
this.snapshotDao = snapshotDao;
|
||||
this.dockerRegistryClient = dockerRegistryClient;
|
||||
this.startQueue = new ArrayDeque<>(orderedContainers);
|
||||
this.loggers = loggers;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void internalStart(Map<String, String> startOptions) throws InfrastructureException {
|
||||
startSynchronizer.setStartThread();
|
||||
|
||||
DockerEnvironment dockerEnvironment = getContext().getDockerEnvironment();
|
||||
Queue<String> startQueue = new ArrayDeque<>(getContext().getOrderedContainers());
|
||||
try {
|
||||
dockerNetworkLifecycle.createNetwork(dockerEnvironment.getNetwork());
|
||||
networks.createNetwork(getContext().getDockerEnvironment().getNetwork());
|
||||
|
||||
boolean restore = isRestoreEnabled(startOptions);
|
||||
|
||||
|
|
@ -178,6 +243,7 @@ public class DockerInternalRuntime extends InternalRuntime<DockerRuntimeContext>
|
|||
|
||||
private void restoreMachine(String name, DockerContainerConfig originalConfig)
|
||||
throws InfrastructureException, InterruptedException {
|
||||
RuntimeIdentity identity = getContext().getIdentity();
|
||||
try {
|
||||
SnapshotImpl snapshot = snapshotDao.getSnapshot(identity.getWorkspaceId(), identity.getEnvName(), name);
|
||||
startMachine(name, configForSnapshot(snapshot, originalConfig));
|
||||
|
|
@ -192,23 +258,22 @@ public class DockerInternalRuntime extends InternalRuntime<DockerRuntimeContext>
|
|||
}
|
||||
}
|
||||
|
||||
/** Checks servers availability on all the machines. */
|
||||
void checkServers() throws InfrastructureException {
|
||||
for (Map.Entry<String, ? extends DockerMachine> entry : startSynchronizer.getMachines().entrySet()) {
|
||||
new ServersReadinessChecker(entry.getKey(), entry.getValue().getServers(), serverCheckerFactory).checkOnce();
|
||||
}
|
||||
}
|
||||
|
||||
private void startMachine(String name, DockerContainerConfig containerConfig)
|
||||
throws InfrastructureException, InterruptedException {
|
||||
InternalMachineConfig machineCfg = getContext().getMachineConfigs().get(name);
|
||||
|
||||
Map<String, String> labels = new HashMap<>(containerConfig.getLabels());
|
||||
labels.putAll(Labels.newSerializer()
|
||||
.machineName(name)
|
||||
.runtimeId(identity)
|
||||
.servers(machineCfg.getServers())
|
||||
.labels());
|
||||
containerConfig.setLabels(labels);
|
||||
|
||||
DockerMachine machine = containerStarter.startContainer(dockerEnvironment.getNetwork(),
|
||||
DockerMachine machine = containerStarter.startContainer(getContext().getDockerEnvironment().getNetwork(),
|
||||
name,
|
||||
containerConfig,
|
||||
identity,
|
||||
devMachineName.equals(name),
|
||||
getContext().getIdentity(),
|
||||
getContext().getDevMachineName().equals(name),
|
||||
new AbnormalMachineStopHandlerImpl());
|
||||
try {
|
||||
startSynchronizer.addMachine(name, machine);
|
||||
|
|
@ -218,13 +283,12 @@ public class DockerInternalRuntime extends InternalRuntime<DockerRuntimeContext>
|
|||
destroyMachineQuietly(name, machine);
|
||||
throw e;
|
||||
}
|
||||
bootstrapperFactory.create(name, identity, machineCfg.getInstallers(), machine).bootstrap();
|
||||
bootstrapperFactory.create(name, getContext().getIdentity(), machineCfg.getInstallers(), machine).bootstrap();
|
||||
|
||||
ServersReadinessChecker readinessChecker = new ServersReadinessChecker(name,
|
||||
machine.getServers(),
|
||||
new ServerReadinessHandler(name),
|
||||
serverCheckerFactory);
|
||||
readinessChecker.startAsync();
|
||||
readinessChecker.startAsync(new ServerReadinessHandler(name));
|
||||
readinessChecker.await();
|
||||
}
|
||||
|
||||
|
|
@ -238,6 +302,14 @@ public class DockerInternalRuntime extends InternalRuntime<DockerRuntimeContext>
|
|||
return stopOpts != null && Boolean.parseBoolean(stopOpts.get("create-snapshot"));
|
||||
}
|
||||
|
||||
// TODO stream bootstrapper logs as well
|
||||
private void streamLogsAsync(String name, String containerId) {
|
||||
containerStarter.readContainerLogsInSeparateThread(containerId,
|
||||
getContext().getIdentity().getWorkspaceId(),
|
||||
name,
|
||||
loggers.newLogsProcessor(name, getContext().getIdentity()));
|
||||
}
|
||||
|
||||
private class ServerReadinessHandler implements Consumer<String> {
|
||||
private String machineName;
|
||||
|
||||
|
|
@ -255,7 +327,7 @@ public class DockerInternalRuntime extends InternalRuntime<DockerRuntimeContext>
|
|||
machine.setServerStatus(serverRef, ServerStatus.RUNNING);
|
||||
|
||||
eventService.publish(DtoFactory.newDto(ServerStatusEvent.class)
|
||||
.withIdentity(DtoConverter.asDto(identity))
|
||||
.withIdentity(DtoConverter.asDto(getContext().getIdentity()))
|
||||
.withMachineName(machineName)
|
||||
.withServerName(serverRef)
|
||||
.withStatus(ServerStatus.RUNNING)
|
||||
|
|
@ -307,10 +379,14 @@ public class DockerInternalRuntime extends InternalRuntime<DockerRuntimeContext>
|
|||
}
|
||||
for (Map.Entry<String, DockerMachine> entry : machines.entrySet()) {
|
||||
destroyMachineQuietly(entry.getKey(), entry.getValue());
|
||||
eventService.publish(DtoFactory.newDto(MachineStatusEvent.class)
|
||||
.withEventType(MachineStatus.STOPPED)
|
||||
.withIdentity(DtoConverter.asDto(getContext().getIdentity()))
|
||||
.withMachineName(entry.getKey()));
|
||||
sendStoppedEvent(entry.getKey());
|
||||
}
|
||||
// TODO what happens when context throws exception here
|
||||
dockerNetworkLifecycle.destroyNetwork(dockerEnvironment.getNetwork());
|
||||
networks.destroyNetwork(getContext().getDockerEnvironment().getNetwork());
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -337,6 +413,7 @@ public class DockerInternalRuntime extends InternalRuntime<DockerRuntimeContext>
|
|||
private void snapshotMachines(Map<String, DockerMachine> machines) {
|
||||
List<SnapshotImpl> newSnapshots = new ArrayList<>();
|
||||
final RuntimeIdentity identity = getContext().getIdentity();
|
||||
String devMachineName = getContext().getDevMachineName();
|
||||
for (Map.Entry<String, DockerMachine> dockerMachineEntry : machines.entrySet()) {
|
||||
SnapshotImpl snapshot = SnapshotImpl.builder()
|
||||
.generateId()
|
||||
|
|
@ -349,7 +426,8 @@ public class DockerInternalRuntime extends InternalRuntime<DockerRuntimeContext>
|
|||
.useCurrentCreationDate()
|
||||
.build();
|
||||
try {
|
||||
DockerMachineSource machineSource = dockerMachineEntry.getValue().saveToSnapshot();
|
||||
ProgressMonitor monitor = loggers.newProgressMonitor(dockerMachineEntry.getKey(), identity);
|
||||
DockerMachineSource machineSource = dockerMachineEntry.getValue().saveToSnapshot(monitor);
|
||||
snapshot.setMachineSource(new MachineSourceImpl(machineSource));
|
||||
newSnapshots.add(snapshot);
|
||||
} catch (SnapshotException e) {
|
||||
|
|
@ -401,7 +479,7 @@ public class DockerInternalRuntime extends InternalRuntime<DockerRuntimeContext>
|
|||
LOG.error(e.getLocalizedMessage(), e);
|
||||
} finally {
|
||||
eventService.publish(DtoFactory.newDto(RuntimeStatusEvent.class)
|
||||
.withIdentity(DtoConverter.asDto(identity))
|
||||
.withIdentity(DtoConverter.asDto(getContext().getIdentity()))
|
||||
.withStatus("STOPPED")
|
||||
.withPrevStatus("RUNNING")
|
||||
.withFailed(true)
|
||||
|
|
@ -412,21 +490,21 @@ public class DockerInternalRuntime extends InternalRuntime<DockerRuntimeContext>
|
|||
|
||||
private void sendStartingEvent(String machineName) {
|
||||
eventService.publish(DtoFactory.newDto(MachineStatusEvent.class)
|
||||
.withIdentity(DtoConverter.asDto(identity))
|
||||
.withIdentity(DtoConverter.asDto(getContext().getIdentity()))
|
||||
.withEventType(MachineStatus.STARTING)
|
||||
.withMachineName(machineName));
|
||||
}
|
||||
|
||||
private void sendRunningEvent(String machineName) {
|
||||
eventService.publish(DtoFactory.newDto(MachineStatusEvent.class)
|
||||
.withIdentity(DtoConverter.asDto(identity))
|
||||
.withIdentity(DtoConverter.asDto(getContext().getIdentity()))
|
||||
.withEventType(MachineStatus.RUNNING)
|
||||
.withMachineName(machineName));
|
||||
}
|
||||
|
||||
private void sendFailedEvent(String machineName, String message) {
|
||||
eventService.publish(DtoFactory.newDto(MachineStatusEvent.class)
|
||||
.withIdentity(DtoConverter.asDto(identity))
|
||||
.withIdentity(DtoConverter.asDto(getContext().getIdentity()))
|
||||
.withEventType(MachineStatus.FAILED)
|
||||
.withMachineName(machineName)
|
||||
.withError(message));
|
||||
|
|
@ -435,7 +513,7 @@ public class DockerInternalRuntime extends InternalRuntime<DockerRuntimeContext>
|
|||
private void sendStoppedEvent(String machineName) {
|
||||
eventService.publish(DtoFactory.newDto(MachineStatusEvent.class)
|
||||
.withEventType(MachineStatus.STOPPED)
|
||||
.withIdentity(DtoConverter.asDto(identity))
|
||||
.withIdentity(DtoConverter.asDto(getContext().getIdentity()))
|
||||
.withMachineName(machineName));
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -10,8 +10,6 @@
|
|||
*******************************************************************************/
|
||||
package org.eclipse.che.workspace.infrastructure.docker;
|
||||
|
||||
import com.google.inject.assistedinject.Assisted;
|
||||
|
||||
import org.eclipse.che.api.core.model.workspace.runtime.Machine;
|
||||
import org.eclipse.che.api.core.model.workspace.runtime.ServerStatus;
|
||||
import org.eclipse.che.api.workspace.server.model.impl.ServerImpl;
|
||||
|
|
@ -23,7 +21,6 @@ import org.eclipse.che.plugin.docker.client.Exec;
|
|||
import org.eclipse.che.plugin.docker.client.LogMessage;
|
||||
import org.eclipse.che.plugin.docker.client.MessageProcessor;
|
||||
import org.eclipse.che.plugin.docker.client.ProgressMonitor;
|
||||
import org.eclipse.che.plugin.docker.client.json.ContainerInfo;
|
||||
import org.eclipse.che.plugin.docker.client.params.CommitParams;
|
||||
import org.eclipse.che.plugin.docker.client.params.CreateExecParams;
|
||||
import org.eclipse.che.plugin.docker.client.params.PushParams;
|
||||
|
|
@ -33,11 +30,8 @@ import org.eclipse.che.plugin.docker.client.params.RemoveImageParams;
|
|||
import org.eclipse.che.plugin.docker.client.params.StartExecParams;
|
||||
import org.eclipse.che.workspace.infrastructure.docker.monit.DockerMachineStopDetector;
|
||||
import org.eclipse.che.workspace.infrastructure.docker.snapshot.SnapshotException;
|
||||
import org.eclipse.che.workspace.infrastructure.docker.strategy.ServerEvaluationStrategy;
|
||||
import org.eclipse.che.workspace.infrastructure.docker.strategy.ServerEvaluationStrategyProvider;
|
||||
import org.slf4j.Logger;
|
||||
|
||||
import javax.inject.Inject;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.util.Collections;
|
||||
|
|
@ -57,11 +51,11 @@ public class DockerMachine implements Machine {
|
|||
/**
|
||||
* Name of the latest tag used in Docker image.
|
||||
*/
|
||||
public static final String LATEST_TAG = "latest";
|
||||
public static final String LATEST_TAG = "latest";
|
||||
/**
|
||||
* Env variable that points to root folder of projects in dev machine
|
||||
*/
|
||||
public static final String PROJECTS_ROOT_VARIABLE = "CHE_PROJECTS_ROOT";
|
||||
public static final String PROJECTS_ROOT_VARIABLE = "CHE_PROJECTS_ROOT";
|
||||
|
||||
/**
|
||||
* Env variable for jvm settings
|
||||
|
|
@ -89,43 +83,31 @@ public class DockerMachine implements Machine {
|
|||
*/
|
||||
public static final String USER_TOKEN = "USER_TOKEN";
|
||||
|
||||
private final String container;
|
||||
private final DockerConnector docker;
|
||||
private final String image;
|
||||
private final DockerMachineStopDetector dockerMachineStopDetector;
|
||||
private final String registry;
|
||||
private final String registryNamespace;
|
||||
private final boolean snapshotUseRegistry;
|
||||
private final ContainerInfo info;
|
||||
private final ServerEvaluationStrategyProvider provider;
|
||||
private final ProgressMonitor progressMonitor;
|
||||
private final String container;
|
||||
private final DockerConnector docker;
|
||||
private final String image;
|
||||
private final DockerMachineStopDetector dockerMachineStopDetector;
|
||||
private final String registry;
|
||||
private final String registryNamespace;
|
||||
private final boolean snapshotUseRegistry;
|
||||
private final Map<String, ServerImpl> servers;
|
||||
|
||||
private Map<String, ServerImpl> servers;
|
||||
|
||||
@Inject
|
||||
public DockerMachine(DockerConnector docker,
|
||||
public DockerMachine(String containerId,
|
||||
String image,
|
||||
DockerConnector docker,
|
||||
Map<String, ServerImpl> servers,
|
||||
String registry,
|
||||
String registryNamespace,
|
||||
boolean snapshotUseRegistry,
|
||||
@Assisted("container") String container,
|
||||
@Assisted("image") String image,
|
||||
ServerEvaluationStrategyProvider provider,
|
||||
DockerMachineStopDetector dockerMachineStopDetector,
|
||||
ProgressMonitor progressMonitor) throws InfrastructureException {
|
||||
this.container = container;
|
||||
String registryNamespace,
|
||||
DockerMachineStopDetector dockerMachineStopDetector) {
|
||||
this.container = containerId;
|
||||
this.docker = docker;
|
||||
this.image = image;
|
||||
this.registry = registry;
|
||||
this.registryNamespace = registryNamespace;
|
||||
this.snapshotUseRegistry = snapshotUseRegistry;
|
||||
this.dockerMachineStopDetector = dockerMachineStopDetector;
|
||||
this.progressMonitor = progressMonitor;
|
||||
try {
|
||||
this.info = docker.inspectContainer(container);
|
||||
} catch (IOException e) {
|
||||
throw new InfrastructureException(e.getLocalizedMessage(), e);
|
||||
}
|
||||
this.provider = provider;
|
||||
this.servers = servers;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
@ -135,10 +117,6 @@ public class DockerMachine implements Machine {
|
|||
|
||||
@Override
|
||||
public Map<String, ServerImpl> getServers() {
|
||||
if(servers == null) {
|
||||
ServerEvaluationStrategy strategy = provider.get();
|
||||
servers = strategy.getServers(info, "localhost", Collections.emptyMap());
|
||||
}
|
||||
return servers;
|
||||
}
|
||||
|
||||
|
|
@ -203,17 +181,16 @@ public class DockerMachine implements Machine {
|
|||
", image='" + image + '\'' +
|
||||
", registry='" + registry + '\'' +
|
||||
", registryNamespace='" + registryNamespace + '\'' +
|
||||
", snapshotUseRegistry='" + snapshotUseRegistry +
|
||||
", info=" + info +
|
||||
", provider=" + provider +
|
||||
", snapshotUseRegistry='" + snapshotUseRegistry +
|
||||
", container=" + container +
|
||||
'}';
|
||||
}
|
||||
|
||||
|
||||
public DockerMachineSource saveToSnapshot() throws SnapshotException {
|
||||
public DockerMachineSource saveToSnapshot(ProgressMonitor progressMonitor) throws SnapshotException {
|
||||
try {
|
||||
String image = generateRepository();
|
||||
if(!snapshotUseRegistry) {
|
||||
if (!snapshotUseRegistry) {
|
||||
commitContainer(image, LATEST_TAG);
|
||||
return new DockerMachineSource(image).withTag(LATEST_TAG);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,76 @@
|
|||
/*******************************************************************************
|
||||
* Copyright (c) 2012-2017 Codenvy, S.A.
|
||||
* All rights reserved. This program and the accompanying materials
|
||||
* are made available under the terms of the Eclipse Public License v1.0
|
||||
* which accompanies this distribution, and is available at
|
||||
* http://www.eclipse.org/legal/epl-v10.html
|
||||
*
|
||||
* Contributors:
|
||||
* Codenvy, S.A. - initial API and implementation
|
||||
*******************************************************************************/
|
||||
package org.eclipse.che.workspace.infrastructure.docker;
|
||||
|
||||
import org.eclipse.che.api.core.model.workspace.config.ServerConfig;
|
||||
import org.eclipse.che.api.workspace.server.spi.InfrastructureException;
|
||||
import org.eclipse.che.commons.annotation.Nullable;
|
||||
import org.eclipse.che.plugin.docker.client.DockerConnector;
|
||||
import org.eclipse.che.plugin.docker.client.json.ContainerInfo;
|
||||
import org.eclipse.che.plugin.docker.client.json.ContainerListEntry;
|
||||
import org.eclipse.che.plugin.docker.client.json.NetworkSettings;
|
||||
import org.eclipse.che.workspace.infrastructure.docker.monit.DockerMachineStopDetector;
|
||||
import org.eclipse.che.workspace.infrastructure.docker.strategy.ServersMapper;
|
||||
|
||||
import javax.inject.Inject;
|
||||
import javax.inject.Named;
|
||||
import javax.inject.Singleton;
|
||||
import java.io.IOException;
|
||||
import java.util.Map;
|
||||
|
||||
/** Helps to create {@link DockerMachine} instances. */
|
||||
@Singleton
|
||||
public class DockerMachineCreator {
|
||||
|
||||
private final DockerConnector docker;
|
||||
private final String registry;
|
||||
private final boolean snapshotUseRegistry;
|
||||
private final String registryNamespace;
|
||||
private final DockerMachineStopDetector dockerMachineStopDetector;
|
||||
|
||||
@Inject
|
||||
public DockerMachineCreator(DockerConnector docker,
|
||||
@Named("che.docker.registry") String registry,
|
||||
@Named("che.docker.registry_for_snapshots") boolean snapshotUseRegistry,
|
||||
@Named("che.docker.namespace") @Nullable String registryNamespace,
|
||||
DockerMachineStopDetector dockerMachineStopDetector) {
|
||||
this.docker = docker;
|
||||
this.registry = registry;
|
||||
this.snapshotUseRegistry = snapshotUseRegistry;
|
||||
this.registryNamespace = registryNamespace;
|
||||
this.dockerMachineStopDetector = dockerMachineStopDetector;
|
||||
}
|
||||
|
||||
/** Creates new docker machine instance from the short container description. */
|
||||
public DockerMachine create(ContainerListEntry container) throws InfrastructureException {
|
||||
try {
|
||||
return create(docker.inspectContainer(container.getId()));
|
||||
} catch (IOException x) {
|
||||
throw new InfrastructureException(x.getMessage(), x);
|
||||
}
|
||||
}
|
||||
|
||||
/** Creates new docker machine instance from the full container description. */
|
||||
public DockerMachine create(ContainerInfo container) {
|
||||
NetworkSettings networkSettings = container.getNetworkSettings();
|
||||
String hostname = networkSettings.getGateway();
|
||||
Map<String, ServerConfig> configs = Labels.newDeserializer(container.getConfig().getLabels()).servers();
|
||||
|
||||
return new DockerMachine(container.getId(),
|
||||
container.getImage(),
|
||||
docker,
|
||||
new ServersMapper(hostname).map(networkSettings.getPorts(), configs),
|
||||
registry,
|
||||
snapshotUseRegistry,
|
||||
registryNamespace,
|
||||
dockerMachineStopDetector);
|
||||
}
|
||||
}
|
||||
|
|
@ -58,7 +58,6 @@ import org.eclipse.che.workspace.infrastructure.docker.exception.SourceNotFoundE
|
|||
import org.eclipse.che.workspace.infrastructure.docker.model.DockerContainerConfig;
|
||||
import org.eclipse.che.workspace.infrastructure.docker.monit.AbnormalMachineStopHandler;
|
||||
import org.eclipse.che.workspace.infrastructure.docker.monit.DockerMachineStopDetector;
|
||||
import org.eclipse.che.workspace.infrastructure.docker.strategy.ServerEvaluationStrategyProvider;
|
||||
import org.slf4j.Logger;
|
||||
|
||||
import javax.inject.Inject;
|
||||
|
|
@ -91,6 +90,40 @@ import static org.eclipse.che.workspace.infrastructure.docker.DockerMachine.LATE
|
|||
import static org.eclipse.che.workspace.infrastructure.docker.DockerMachine.USER_TOKEN;
|
||||
import static org.slf4j.LoggerFactory.getLogger;
|
||||
|
||||
/*
|
||||
TODO:
|
||||
|
||||
1. Decompose this component on:
|
||||
- DockerNetworks(NetworkLifecycle <-) - deals with docker networks.
|
||||
- DockerImages - deals with docker images.
|
||||
- DockerContainers - deals with docker containers.
|
||||
Then DockerRuntimeContext may use these components to run consistent
|
||||
and clear flow of calls to achieve its needs, e.g.:
|
||||
|
||||
DockerNetworks networks;
|
||||
DockerImages images;
|
||||
DockerContainer containers;
|
||||
|
||||
start runtime {
|
||||
networks.create(environment.getNetwork())
|
||||
for (config: environment.getConfigs()) {
|
||||
String image
|
||||
if (isBuildable(config)) {
|
||||
image = images.build(config)
|
||||
} else {
|
||||
image = images.pull(config)
|
||||
}
|
||||
String id = containers.create(image)
|
||||
Container container = containers.start(id);
|
||||
DockerMachine machine = machineCreator.create(container);
|
||||
...
|
||||
}
|
||||
}
|
||||
|
||||
2. Move the logic related to containers configuration modification to
|
||||
InfrastructureProvisioner implementation.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @author Alexander Garagatyi
|
||||
*/
|
||||
|
|
@ -115,7 +148,7 @@ public class DockerMachineStarter {
|
|||
singletonList("sh"),
|
||||
Arrays.asList("/bin/sh", "-c", "/bin/sh"),
|
||||
Arrays.asList("/bin/sh", "-c",
|
||||
"/bin/bash"),
|
||||
"/bin/bash"),
|
||||
Arrays.asList("/bin/sh", "-c", "bash"),
|
||||
Arrays.asList("/bin/sh", "-c", "sh"));
|
||||
|
||||
|
|
@ -146,15 +179,13 @@ public class DockerMachineStarter {
|
|||
private final Set<String> additionalNetworks;
|
||||
private final String parentCgroup;
|
||||
private final String cpusetCpus;
|
||||
private final String registry;
|
||||
private final String registryNamespace;
|
||||
private final long cpuPeriod;
|
||||
private final long cpuQuota;
|
||||
private final WindowsPathEscaper windowsPathEscaper;
|
||||
private final String[] dnsResolvers;
|
||||
private ServerEvaluationStrategyProvider serverEvaluationStrategyProvider;
|
||||
private final Map<String, String> buildArgs;
|
||||
private final MachineLoggersFactory machineLoggerFactory;
|
||||
private final DockerMachineCreator machineCreator;
|
||||
|
||||
@Inject
|
||||
public DockerMachineStarter(DockerConnector docker,
|
||||
|
|
@ -167,8 +198,6 @@ public class DockerMachineStarter {
|
|||
@Named("che.docker.always_pull_image") boolean doForcePullImage,
|
||||
@Named("che.docker.privileged") boolean privilegedMode,
|
||||
@Named("che.docker.pids_limit") int pidsLimit,
|
||||
@Named("che.docker.registry") String registry,
|
||||
@Named("che.docker.namespace") @Nullable String registryNamespace,
|
||||
@Named("machine.docker.dev_machine.machine_env") Set<String> devMachineEnvVariables,
|
||||
@Named("machine.docker.machine_env") Set<String> allMachinesEnvVariables,
|
||||
@Named("che.docker.registry_for_snapshots") boolean snapshotUseRegistry,
|
||||
|
|
@ -181,9 +210,10 @@ public class DockerMachineStarter {
|
|||
WindowsPathEscaper windowsPathEscaper,
|
||||
@Named("che.docker.extra_hosts") Set<Set<String>> additionalHosts,
|
||||
@Nullable @Named("che.docker.dns_resolvers") String[] dnsResolvers,
|
||||
ServerEvaluationStrategyProvider serverEvaluationStrategyProvider,
|
||||
@Named("che.docker.build_args") Map<String, String> buildArgs,
|
||||
MachineLoggersFactory machineLogger) {
|
||||
MachineLoggersFactory machineLogger,
|
||||
DockerMachineCreator machineCreator) {
|
||||
this.machineCreator = machineCreator;
|
||||
// TODO spi should we move all configuration stuff into infrastructure provisioner and left logic of container start here only
|
||||
this.docker = docker;
|
||||
this.dockerCredentials = dockerCredentials;
|
||||
|
|
@ -205,12 +235,9 @@ public class DockerMachineStarter {
|
|||
this.cpuPeriod = cpuPeriod;
|
||||
this.cpuQuota = cpuQuota;
|
||||
this.windowsPathEscaper = windowsPathEscaper;
|
||||
this.registryNamespace = registryNamespace;
|
||||
this.registry = registry;
|
||||
this.pidsLimit = pidsLimit;
|
||||
this.dnsResolvers = dnsResolvers;
|
||||
this.buildArgs = buildArgs;
|
||||
this.serverEvaluationStrategyProvider = serverEvaluationStrategyProvider;
|
||||
this.machineLoggerFactory = machineLogger;
|
||||
|
||||
allMachinesSystemVolumes = removeEmptyAndNullValues(allMachinesSystemVolumes);
|
||||
|
|
@ -327,7 +354,7 @@ public class DockerMachineStarter {
|
|||
|
||||
docker.startContainer(StartContainerParams.create(container));
|
||||
|
||||
checkContainerIsRunning(container);
|
||||
ContainerInfo runningContainer = getRunningContainer(container);
|
||||
|
||||
readContainerLogsInSeparateThread(container,
|
||||
workspaceId,
|
||||
|
|
@ -336,15 +363,7 @@ public class DockerMachineStarter {
|
|||
|
||||
dockerInstanceStopDetector.startDetection(container, machineName, abnormalMachineStopHandler);
|
||||
|
||||
return new DockerMachine(docker,
|
||||
registry,
|
||||
registryNamespace,
|
||||
snapshotUseRegistry,
|
||||
container,
|
||||
image,
|
||||
serverEvaluationStrategyProvider,
|
||||
dockerInstanceStopDetector,
|
||||
progressMonitor);
|
||||
return machineCreator.create(runningContainer);
|
||||
} catch (RuntimeException | IOException | InfrastructureException e) {
|
||||
cleanUpContainer(container);
|
||||
if (e instanceof InfrastructureException) {
|
||||
|
|
@ -650,11 +669,12 @@ public class DockerMachineStarter {
|
|||
|
||||
// Inspect container right after start to check if it is running,
|
||||
// otherwise throw error that command should not exit right after container start
|
||||
protected void checkContainerIsRunning(String container) throws IOException, InfrastructureException {
|
||||
protected ContainerInfo getRunningContainer(String container) throws IOException, InfrastructureException {
|
||||
ContainerInfo containerInfo = docker.inspectContainer(container);
|
||||
if ("exited".equals(containerInfo.getState().getStatus())) {
|
||||
throw new InfrastructureException(CONTAINER_EXITED_ERROR);
|
||||
}
|
||||
return containerInfo;
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
|
|
|
|||
|
|
@ -10,7 +10,9 @@
|
|||
*******************************************************************************/
|
||||
package org.eclipse.che.workspace.infrastructure.docker;
|
||||
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import com.google.inject.assistedinject.Assisted;
|
||||
import com.google.inject.assistedinject.AssistedInject;
|
||||
|
||||
import org.eclipse.che.api.core.ValidationException;
|
||||
import org.eclipse.che.api.core.model.workspace.config.Environment;
|
||||
|
|
@ -18,14 +20,19 @@ import org.eclipse.che.api.core.model.workspace.runtime.RuntimeIdentity;
|
|||
import org.eclipse.che.api.installer.server.InstallerRegistry;
|
||||
import org.eclipse.che.api.workspace.server.spi.InfrastructureException;
|
||||
import org.eclipse.che.api.workspace.server.spi.InternalInfrastructureException;
|
||||
import org.eclipse.che.api.workspace.server.spi.InternalRuntime;
|
||||
import org.eclipse.che.api.workspace.server.spi.RuntimeContext;
|
||||
import org.eclipse.che.api.workspace.shared.Utils;
|
||||
import org.eclipse.che.plugin.docker.client.json.ContainerListEntry;
|
||||
import org.eclipse.che.workspace.infrastructure.docker.environment.ContainersStartStrategy;
|
||||
import org.eclipse.che.workspace.infrastructure.docker.model.DockerEnvironment;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import javax.inject.Named;
|
||||
import javax.ws.rs.core.UriBuilder;
|
||||
import javax.ws.rs.core.UriBuilderException;
|
||||
import java.net.URI;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
import static org.eclipse.che.api.workspace.server.OutputEndpoint.OUTPUT_WEBSOCKET_ENDPOINT_BASE;
|
||||
|
|
@ -34,31 +41,53 @@ import static org.eclipse.che.api.workspace.server.OutputEndpoint.OUTPUT_WEBSOCK
|
|||
* Docker specific implementation of {@link RuntimeContext}.
|
||||
*
|
||||
* @author Alexander Garagatyi
|
||||
* @author Yevhenii Voievodin
|
||||
*/
|
||||
public class DockerRuntimeContext extends RuntimeContext {
|
||||
private final DockerEnvironment dockerEnvironment;
|
||||
private final DockerRuntimeFactory dockerRuntimeFactory;
|
||||
private final List<String> orderedContainers;
|
||||
private final String devMachineName;
|
||||
private final String websocketEndpointBase;
|
||||
|
||||
private static final Logger LOG = LoggerFactory.getLogger(DockerRuntimeContext.class);
|
||||
|
||||
private final DockerEnvironment dockerEnvironment;
|
||||
private final List<String> orderedContainers;
|
||||
private final String devMachineName;
|
||||
private final String websocketEndpointBase;
|
||||
private final DockerRuntimeFactory runtimeFactory;
|
||||
private final DockerContainers containers;
|
||||
private final RuntimeConsistencyChecker consistencyChecker;
|
||||
private final DockerSharedPool sharedPool;
|
||||
|
||||
@AssistedInject
|
||||
public DockerRuntimeContext(@Assisted DockerRuntimeInfrastructure infrastructure,
|
||||
@Assisted RuntimeIdentity identity,
|
||||
@Assisted Environment environment,
|
||||
@Assisted DockerEnvironment dockerEnvironment,
|
||||
@Assisted List<String> orderedContainers,
|
||||
@Assisted DockerEnvironment dockerEnv,
|
||||
InstallerRegistry installerRegistry,
|
||||
DockerRuntimeFactory dockerRuntimeFactory,
|
||||
String websocketEndpointBase)
|
||||
throws ValidationException, InfrastructureException {
|
||||
ContainersStartStrategy startStrategy,
|
||||
DockerRuntimeFactory runtimeFactory,
|
||||
DockerContainers containers,
|
||||
DockerSharedPool sharedPool,
|
||||
RuntimeConsistencyChecker consistencyChecker,
|
||||
@Named("che.websocket.endpoint.base") String websocketEndpointBase) throws InfrastructureException, ValidationException {
|
||||
super(environment, identity, infrastructure, installerRegistry);
|
||||
this.devMachineName = Utils.getDevMachineName(environment);
|
||||
this.orderedContainers = orderedContainers;
|
||||
this.dockerEnvironment = dockerEnvironment;
|
||||
this.dockerRuntimeFactory = dockerRuntimeFactory;
|
||||
this.dockerEnvironment = dockerEnv;
|
||||
this.orderedContainers = ImmutableList.copyOf(startStrategy.order(dockerEnvironment));
|
||||
this.websocketEndpointBase = websocketEndpointBase;
|
||||
this.runtimeFactory = runtimeFactory;
|
||||
this.containers = containers;
|
||||
this.sharedPool = sharedPool;
|
||||
this.consistencyChecker = consistencyChecker;
|
||||
}
|
||||
|
||||
/** Returns docker environment which based on normalized context environment configuration. */
|
||||
public DockerEnvironment getDockerEnvironment() { return dockerEnvironment;}
|
||||
|
||||
/** Returns the name of the dev machine. */
|
||||
public String getDevMachineName() { return devMachineName; }
|
||||
|
||||
/** Returns the list of the ordered containers, machines must be started in the same order. */
|
||||
public List<String> getOrderedContainers() { return orderedContainers; }
|
||||
|
||||
@Override
|
||||
public URI getOutputChannel() throws InfrastructureException {
|
||||
try {
|
||||
|
|
@ -72,11 +101,35 @@ public class DockerRuntimeContext extends RuntimeContext {
|
|||
}
|
||||
|
||||
@Override
|
||||
public InternalRuntime getRuntime() {
|
||||
return dockerRuntimeFactory.createRuntime(this,
|
||||
devMachineName,
|
||||
orderedContainers,
|
||||
dockerEnvironment,
|
||||
identity);
|
||||
public DockerInternalRuntime getRuntime() throws InfrastructureException {
|
||||
List<ContainerListEntry> runningContainers = containers.find(identity);
|
||||
if (runningContainers.isEmpty()) {
|
||||
return runtimeFactory.create(this);
|
||||
}
|
||||
|
||||
DockerInternalRuntime runtime = runtimeFactory.create(this, runningContainers);
|
||||
try {
|
||||
consistencyChecker.check(environment, runtime);
|
||||
runtime.checkServers();
|
||||
} catch (InfrastructureException | ValidationException x) {
|
||||
LOG.warn("Runtime '{}:{}' will be stopped as it is not consistent with its configuration. " +
|
||||
"The problem: {}",
|
||||
identity.getWorkspaceId(),
|
||||
identity.getEnvName(),
|
||||
x.getMessage());
|
||||
stopAsync(runtime);
|
||||
throw new InfrastructureException(x.getMessage(), x);
|
||||
}
|
||||
return runtime;
|
||||
}
|
||||
|
||||
private void stopAsync(DockerInternalRuntime runtime) {
|
||||
sharedPool.execute(() -> {
|
||||
try {
|
||||
runtime.stop(Collections.emptyMap());
|
||||
} catch (Exception x) {
|
||||
LOG.error("Couldn't stop workspace runtime due to error: {}", x.getMessage());
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,25 @@
|
|||
/*******************************************************************************
|
||||
* Copyright (c) 2012-2017 Codenvy, S.A.
|
||||
* All rights reserved. This program and the accompanying materials
|
||||
* are made available under the terms of the Eclipse Public License v1.0
|
||||
* which accompanies this distribution, and is available at
|
||||
* http://www.eclipse.org/legal/epl-v10.html
|
||||
*
|
||||
* Contributors:
|
||||
* Codenvy, S.A. - initial API and implementation
|
||||
*******************************************************************************/
|
||||
package org.eclipse.che.workspace.infrastructure.docker;
|
||||
|
||||
import org.eclipse.che.api.core.ValidationException;
|
||||
import org.eclipse.che.api.core.model.workspace.config.Environment;
|
||||
import org.eclipse.che.api.core.model.workspace.runtime.RuntimeIdentity;
|
||||
import org.eclipse.che.api.workspace.server.spi.InfrastructureException;
|
||||
import org.eclipse.che.workspace.infrastructure.docker.model.DockerEnvironment;
|
||||
|
||||
/** Helps to create {@link DockerRuntimeContext} instances. */
|
||||
public interface DockerRuntimeContextFactory {
|
||||
DockerRuntimeContext create(DockerRuntimeInfrastructure infra,
|
||||
RuntimeIdentity identity,
|
||||
Environment environment,
|
||||
DockerEnvironment dockerEnv) throws InfrastructureException, ValidationException;
|
||||
}
|
||||
|
|
@ -10,10 +10,7 @@
|
|||
*******************************************************************************/
|
||||
package org.eclipse.che.workspace.infrastructure.docker;
|
||||
|
||||
import com.google.inject.assistedinject.Assisted;
|
||||
|
||||
import org.eclipse.che.api.core.model.workspace.runtime.RuntimeIdentity;
|
||||
import org.eclipse.che.workspace.infrastructure.docker.model.DockerEnvironment;
|
||||
import org.eclipse.che.plugin.docker.client.json.ContainerListEntry;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
|
|
@ -21,9 +18,6 @@ import java.util.List;
|
|||
* @author Alexander Garagatyi
|
||||
*/
|
||||
public interface DockerRuntimeFactory {
|
||||
DockerInternalRuntime createRuntime(@Assisted DockerRuntimeContext context,
|
||||
@Assisted String devMachineName,
|
||||
@Assisted List<String> orderedContainers,
|
||||
@Assisted DockerEnvironment dockerEnvironment,
|
||||
@Assisted RuntimeIdentity identity);
|
||||
DockerInternalRuntime create(DockerRuntimeContext context);
|
||||
DockerInternalRuntime create(DockerRuntimeContext context, List<ContainerListEntry> containers);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -16,7 +16,6 @@ import org.eclipse.che.api.core.ValidationException;
|
|||
import org.eclipse.che.api.core.model.workspace.config.Environment;
|
||||
import org.eclipse.che.api.core.model.workspace.runtime.RuntimeIdentity;
|
||||
import org.eclipse.che.api.core.notification.EventService;
|
||||
import org.eclipse.che.api.installer.server.InstallerRegistry;
|
||||
import org.eclipse.che.api.workspace.server.model.impl.EnvironmentImpl;
|
||||
import org.eclipse.che.api.workspace.server.spi.InfrastructureException;
|
||||
import org.eclipse.che.api.workspace.server.spi.RuntimeInfrastructure;
|
||||
|
|
@ -27,9 +26,8 @@ import org.eclipse.che.workspace.infrastructure.docker.environment.EnvironmentPa
|
|||
import org.eclipse.che.workspace.infrastructure.docker.environment.EnvironmentValidator;
|
||||
import org.eclipse.che.workspace.infrastructure.docker.model.DockerEnvironment;
|
||||
|
||||
import javax.inject.Named;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* Implementation of {@link RuntimeInfrastructure} that
|
||||
|
|
@ -38,14 +36,13 @@ import java.util.Map;
|
|||
* @author Alexander Garagatyi
|
||||
*/
|
||||
public class DockerRuntimeInfrastructure extends RuntimeInfrastructure {
|
||||
private final EnvironmentValidator dockerEnvironmentValidator;
|
||||
private final EnvironmentParser dockerEnvironmentParser;
|
||||
private final ContainersStartStrategy startStrategy;
|
||||
private final InfrastructureProvisioner infrastructureProvisioner;
|
||||
private final EnvironmentNormalizer environmentNormalizer;
|
||||
private final DockerRuntimeFactory runtimeFactory;
|
||||
private final InstallerRegistry installerRegistry;
|
||||
private final String websocketEndpointBase;
|
||||
private final EnvironmentValidator dockerEnvironmentValidator;
|
||||
private final EnvironmentParser dockerEnvironmentParser;
|
||||
private final ContainersStartStrategy startStrategy;
|
||||
private final InfrastructureProvisioner infrastructureProvisioner;
|
||||
private final EnvironmentNormalizer environmentNormalizer;
|
||||
private final DockerRuntimeContextFactory contextFactory;
|
||||
private final DockerContainers containers;
|
||||
|
||||
@Inject
|
||||
public DockerRuntimeInfrastructure(EnvironmentParser dockerEnvironmentParser,
|
||||
|
|
@ -54,19 +51,17 @@ public class DockerRuntimeInfrastructure extends RuntimeInfrastructure {
|
|||
InfrastructureProvisioner infrastructureProvisioner,
|
||||
EnvironmentNormalizer environmentNormalizer,
|
||||
Map<String, DockerConfigSourceSpecificEnvironmentParser> environmentParsers,
|
||||
DockerRuntimeFactory runtimeFactory,
|
||||
InstallerRegistry installerRegistry,
|
||||
EventService eventService,
|
||||
@Named("che.websocket.endpoint.base") String websocketEndpointBase) {
|
||||
DockerRuntimeContextFactory contextFactory,
|
||||
DockerContainers containers) {
|
||||
super("docker", environmentParsers.keySet(), eventService);
|
||||
this.dockerEnvironmentValidator = dockerEnvironmentValidator;
|
||||
this.dockerEnvironmentParser = dockerEnvironmentParser;
|
||||
this.startStrategy = startStrategy;
|
||||
this.infrastructureProvisioner = infrastructureProvisioner;
|
||||
this.environmentNormalizer = environmentNormalizer;
|
||||
this.runtimeFactory = runtimeFactory;
|
||||
this.installerRegistry = installerRegistry;
|
||||
this.websocketEndpointBase = websocketEndpointBase;
|
||||
this.contextFactory = contextFactory;
|
||||
this.containers = containers;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
@ -92,21 +87,17 @@ public class DockerRuntimeInfrastructure extends RuntimeInfrastructure {
|
|||
EnvironmentImpl environment = new EnvironmentImpl(originEnv);
|
||||
DockerEnvironment dockerEnvironment = dockerEnvironmentParser.parse(environment);
|
||||
dockerEnvironmentValidator.validate(environment, dockerEnvironment);
|
||||
// check that containers start order can be resolved
|
||||
List<String> orderedContainers = startStrategy.order(dockerEnvironment);
|
||||
|
||||
// modify environment with everything needed to use docker machines on particular (cloud) infrastructure
|
||||
infrastructureProvisioner.provision(environment, dockerEnvironment, identity);
|
||||
// normalize env to provide environment description with absolutely everything expected in
|
||||
environmentNormalizer.normalize(environment, dockerEnvironment, identity);
|
||||
|
||||
return new DockerRuntimeContext(this,
|
||||
identity,
|
||||
environment,
|
||||
dockerEnvironment,
|
||||
orderedContainers,
|
||||
installerRegistry,
|
||||
runtimeFactory,
|
||||
websocketEndpointBase);
|
||||
return contextFactory.create(this, identity, environment, dockerEnvironment);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<RuntimeIdentity> getIdentities() throws InfrastructureException {
|
||||
return containers.findIdentities();
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,60 @@
|
|||
/*******************************************************************************
|
||||
* Copyright (c) 2012-2017 Codenvy, S.A.
|
||||
* All rights reserved. This program and the accompanying materials
|
||||
* are made available under the terms of the Eclipse Public License v1.0
|
||||
* which accompanies this distribution, and is available at
|
||||
* http://www.eclipse.org/legal/epl-v10.html
|
||||
*
|
||||
* Contributors:
|
||||
* Codenvy, S.A. - initial API and implementation
|
||||
*******************************************************************************/
|
||||
package org.eclipse.che.workspace.infrastructure.docker;
|
||||
|
||||
import com.google.common.util.concurrent.ThreadFactoryBuilder;
|
||||
|
||||
import org.eclipse.che.commons.lang.concurrent.LoggingUncaughtExceptionHandler;
|
||||
import org.eclipse.che.commons.lang.concurrent.ThreadLocalPropagateContext;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import javax.annotation.PostConstruct;
|
||||
import javax.annotation.PreDestroy;
|
||||
import javax.inject.Singleton;
|
||||
import java.util.concurrent.ExecutorService;
|
||||
import java.util.concurrent.Executors;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
/**
|
||||
* Provides a single non-daemon {@link ExecutorService}
|
||||
* instance for docker infrastructure components.
|
||||
*/
|
||||
@Singleton
|
||||
public class DockerSharedPool {
|
||||
|
||||
private final ExecutorService executor =
|
||||
Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors(), // <- experimental value
|
||||
new ThreadFactoryBuilder().setNameFormat("DockerSharedPool-%d")
|
||||
.setUncaughtExceptionHandler(LoggingUncaughtExceptionHandler.getInstance())
|
||||
.setDaemon(false)
|
||||
.build());
|
||||
|
||||
/**
|
||||
* Delegates call to {@link ExecutorService#execute(Runnable)}
|
||||
* and propagates thread locals to it like defined by {@link ThreadLocalPropagateContext}.
|
||||
*/
|
||||
public void execute(Runnable runnable) {
|
||||
executor.execute(ThreadLocalPropagateContext.wrap(runnable));
|
||||
}
|
||||
|
||||
@PreDestroy
|
||||
private void terminate() throws InterruptedException {
|
||||
if (!executor.isShutdown()) {
|
||||
executor.shutdown();
|
||||
if (!executor.awaitTermination(30, TimeUnit.SECONDS)) {
|
||||
executor.shutdownNow();
|
||||
if (!executor.awaitTermination(30, TimeUnit.SECONDS)) {
|
||||
LoggerFactory.getLogger(DockerSharedPool.class).error("Couldn't terminate docker infrastructure thread pool");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -105,18 +105,9 @@ public class InstallerConfigApplier {
|
|||
}
|
||||
|
||||
private void addLabels(DockerContainerConfig container, Map<String, ? extends ServerConfig> servers) {
|
||||
for (Map.Entry<String, ? extends ServerConfig> entry : servers.entrySet()) {
|
||||
String ref = entry.getKey();
|
||||
ServerConfig conf = entry.getValue();
|
||||
|
||||
container.getLabels().put("che:server:" + conf.getPort() + ":protocol", conf.getProtocol());
|
||||
container.getLabels().put("che:server:" + conf.getPort() + ":ref", ref);
|
||||
|
||||
String path = conf.getPath();
|
||||
if (!isNullOrEmpty(path)) {
|
||||
container.getLabels().put("che:server:" + conf.getPort() + ":path", path);
|
||||
}
|
||||
}
|
||||
container.getLabels().putAll(Labels.newSerializer()
|
||||
.servers(servers)
|
||||
.labels());
|
||||
}
|
||||
|
||||
private void addEnv(DockerContainerConfig container, Map<String, String> properties) {
|
||||
|
|
|
|||
|
|
@ -30,11 +30,12 @@ import java.util.regex.Pattern;
|
|||
*/
|
||||
public final class Labels {
|
||||
|
||||
private static final String LABEL_PREFIX = "org.eclipse.che.";
|
||||
public static final String LABEL_PREFIX = "org.eclipse.che.";
|
||||
public static final String LABEL_WORKSPACE_ID = LABEL_PREFIX + "workspace.id";
|
||||
public static final String LABEL_WORKSPACE_ENV = LABEL_PREFIX + "workspace.env";
|
||||
public static final String LABEL_WORKSPACE_OWNER = LABEL_PREFIX + "workspace.owner";
|
||||
|
||||
private static final String LABEL_MACHINE_NAME = LABEL_PREFIX + "machine.name";
|
||||
private static final String LABEL_WORKSPACE_ID = LABEL_PREFIX + "workspace.id";
|
||||
private static final String LABEL_WORKSPACE_ENV = LABEL_PREFIX + "workspace.env";
|
||||
private static final String LABEL_WORKSPACE_OWNER = LABEL_PREFIX + "workspace.owner";
|
||||
private static final String SERVER_PORT_LABEL_FMT = LABEL_PREFIX + "server.%s.port";
|
||||
private static final String SERVER_PROTOCOL_LABEL_FMT = LABEL_PREFIX + "server.%s.protocol";
|
||||
private static final String SERVER_PATH_LABEL_FMT = LABEL_PREFIX + "server.%s.path";
|
||||
|
|
|
|||
|
|
@ -0,0 +1,39 @@
|
|||
/*******************************************************************************
|
||||
* Copyright (c) 2012-2017 Codenvy, S.A.
|
||||
* All rights reserved. This program and the accompanying materials
|
||||
* are made available under the terms of the Eclipse Public License v1.0
|
||||
* which accompanies this distribution, and is available at
|
||||
* http://www.eclipse.org/legal/epl-v10.html
|
||||
*
|
||||
* Contributors:
|
||||
* Codenvy, S.A. - initial API and implementation
|
||||
*******************************************************************************/
|
||||
package org.eclipse.che.workspace.infrastructure.docker;
|
||||
|
||||
import org.eclipse.che.api.core.ValidationException;
|
||||
import org.eclipse.che.api.core.model.workspace.config.Environment;
|
||||
import org.eclipse.che.api.core.model.workspace.config.MachineConfig;
|
||||
import org.eclipse.che.api.core.model.workspace.runtime.Machine;
|
||||
|
||||
import javax.inject.Singleton;
|
||||
import java.util.Map;
|
||||
|
||||
/** Checks whether runtime is consistent with its configuration. */
|
||||
@Singleton
|
||||
class RuntimeConsistencyChecker {
|
||||
void check(Environment environment, DockerInternalRuntime runtime) throws ValidationException {
|
||||
Map<String, ? extends MachineConfig> configs = environment.getMachines();
|
||||
Map<String, ? extends Machine> machines = runtime.getMachines();
|
||||
if (configs.size() != machines.size()) {
|
||||
throw new ValidationException("Runtime has '%d' machines while configuration defines '%d'." +
|
||||
"Runtime machines: %s. Configuration machines: %s",
|
||||
machines.size(), configs.size(),
|
||||
machines.keySet(), configs.keySet());
|
||||
}
|
||||
if (!configs.keySet().containsAll(machines.keySet())) {
|
||||
throw new ValidationException("Runtime has different set of machines than defined by configuration. " +
|
||||
"Runtime machines: %s. Configuration machines: %s",
|
||||
machines.keySet(), configs.keySet());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -53,7 +53,7 @@ public class EnvironmentNormalizer {
|
|||
|
||||
public void normalize(Environment environment, DockerEnvironment dockerEnvironment, RuntimeIdentity identity)
|
||||
throws InfrastructureException {
|
||||
String networkId = NameGenerator.generate(identity.getWorkspaceId() + "_", 16);
|
||||
String networkId = identity.getWorkspaceId() + "_" + identity.getEnvName();
|
||||
dockerEnvironment.setNetwork(networkId);
|
||||
|
||||
Map<String, DockerContainerConfig> containers = dockerEnvironment.getContainers();
|
||||
|
|
|
|||
|
|
@ -20,6 +20,7 @@ import org.eclipse.che.api.core.ServerException;
|
|||
import org.eclipse.che.api.core.model.workspace.Workspace;
|
||||
import org.eclipse.che.api.core.util.SystemInfo;
|
||||
import org.eclipse.che.api.workspace.server.WorkspaceManager;
|
||||
import org.eclipse.che.api.workspace.server.spi.WorkspaceDao;
|
||||
import org.eclipse.che.workspace.infrastructure.docker.WindowsHostUtils;
|
||||
|
||||
import javax.annotation.PostConstruct;
|
||||
|
|
@ -44,7 +45,7 @@ public class LocalWorkspaceFolderPathProvider implements WorkspaceFolderPathProv
|
|||
public static final String ALLOW_FOLDERS_CREATION_ENV_VARIABLE = "CHE_WORKSPACE_STORAGE_CREATE_FOLDERS";
|
||||
public static final String WORKSPACE_STORAGE_PATH_ENV_VARIABLE = "CHE_WORKSPACE_STORAGE";
|
||||
|
||||
private final Provider<WorkspaceManager> workspaceManager;
|
||||
private final WorkspaceDao workspaceDao;
|
||||
private final boolean isWindows;
|
||||
|
||||
/**
|
||||
|
|
@ -81,9 +82,9 @@ public class LocalWorkspaceFolderPathProvider implements WorkspaceFolderPathProv
|
|||
|
||||
@Inject
|
||||
public LocalWorkspaceFolderPathProvider(@Named("che.workspace.storage") String workspacesMountPoint,
|
||||
Provider<WorkspaceManager> workspaceManager) throws IOException {
|
||||
WorkspaceDao workspaceDao) throws IOException {
|
||||
this.workspacesMountPoint = workspacesMountPoint;
|
||||
this.workspaceManager = workspaceManager;
|
||||
this.workspaceDao = workspaceDao;
|
||||
this.isWindows = SystemInfo.isWindows();
|
||||
}
|
||||
|
||||
|
|
@ -91,10 +92,10 @@ public class LocalWorkspaceFolderPathProvider implements WorkspaceFolderPathProv
|
|||
protected LocalWorkspaceFolderPathProvider(String workspacesMountPoint,
|
||||
String oldWorkspacesMountPoint,
|
||||
String projectsFolder,
|
||||
Provider<WorkspaceManager> workspaceManager,
|
||||
boolean createFolders,
|
||||
WorkspaceDao workspaceDao,
|
||||
boolean isWindows) throws IOException {
|
||||
this.workspaceManager = workspaceManager;
|
||||
this.workspaceDao = workspaceDao;
|
||||
this.workspacesMountPoint = workspacesMountPoint;
|
||||
this.hostProjectsFolder = projectsFolder;
|
||||
this.createFolders = createFolders;
|
||||
|
|
@ -108,8 +109,7 @@ public class LocalWorkspaceFolderPathProvider implements WorkspaceFolderPathProv
|
|||
return hostProjectsFolder;
|
||||
}
|
||||
try {
|
||||
WorkspaceManager workspaceManager = this.workspaceManager.get();
|
||||
Workspace workspace = workspaceManager.getWorkspace(workspaceId);
|
||||
Workspace workspace = workspaceDao.get(workspaceId);
|
||||
String wsName = workspace.getConfig().getName();
|
||||
return doGetPathByName(wsName);
|
||||
} catch (NotFoundException | ServerException e) {
|
||||
|
|
|
|||
|
|
@ -74,6 +74,16 @@ public abstract class ServerChecker {
|
|||
timer.schedule(new ServerCheckingTask(), 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks server availability, throws {@link InfrastructureException}
|
||||
* if the server is not available.
|
||||
*/
|
||||
public void checkOnce() throws InfrastructureException {
|
||||
if (!isAvailable()) {
|
||||
throw new InfrastructureException(String.format("Server '%s' in machine '%s' not available.", serverRef, machineName));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Shows whether the server is treated as available.
|
||||
*
|
||||
|
|
|
|||
|
|
@ -42,10 +42,9 @@ public class ServersReadinessChecker {
|
|||
"terminal", "/");
|
||||
private final String machineName;
|
||||
private final Map<String, ServerImpl> servers;
|
||||
private final Consumer<String> serverReadinessHandler;
|
||||
private final ServerCheckerFactory serverCheckerFactory;
|
||||
private final Timer timer;
|
||||
|
||||
private Timer timer;
|
||||
private long resultTimeoutSeconds;
|
||||
private CompletableFuture result;
|
||||
|
||||
|
|
@ -56,16 +55,12 @@ public class ServersReadinessChecker {
|
|||
* name of machine whose servers will be checked by this method
|
||||
* @param servers
|
||||
* map of servers in a machine
|
||||
* @param serverReadinessHandler
|
||||
* consumer which will be called with server reference as the argument when server become available
|
||||
*/
|
||||
public ServersReadinessChecker(String machineName,
|
||||
Map<String, ServerImpl> servers,
|
||||
Consumer<String> serverReadinessHandler,
|
||||
ServerCheckerFactory serverCheckerFactory) {
|
||||
this.machineName = machineName;
|
||||
this.servers = servers;
|
||||
this.serverReadinessHandler = serverReadinessHandler;
|
||||
this.serverCheckerFactory = serverCheckerFactory;
|
||||
this.timer = new Timer("ServerReadinessChecker", true);
|
||||
}
|
||||
|
|
@ -73,12 +68,15 @@ public class ServersReadinessChecker {
|
|||
/**
|
||||
* Asynchronously starts checking readiness of servers of a machine.
|
||||
*
|
||||
* @param serverReadinessHandler
|
||||
* consumer which will be called with server reference as the argument when server become available
|
||||
* @throws InternalInfrastructureException
|
||||
* if check of a server failed due to an unexpected error
|
||||
* @throws InfrastructureException
|
||||
* if check of a server failed due to an error
|
||||
*/
|
||||
public void startAsync() throws InfrastructureException {
|
||||
public void startAsync(Consumer<String> serverReadinessHandler) throws InfrastructureException {
|
||||
timer = new Timer("ServerReadinessChecker", true);
|
||||
List<ServerChecker> serverCheckers = getServerCheckers();
|
||||
// should be completed with an exception if a server considered unavailable
|
||||
CompletableFuture<Void> firstNonAvailable = new CompletableFuture<>();
|
||||
|
|
@ -103,6 +101,16 @@ public class ServersReadinessChecker {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Synchronously checks whether servers are available,
|
||||
* throws {@link InfrastructureException} if any is not.
|
||||
*/
|
||||
public void checkOnce() throws InfrastructureException {
|
||||
for (ServerChecker checker : getServerCheckers()) {
|
||||
checker.checkOnce();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Waits until servers are considered available or one of them is considered as unavailable.
|
||||
*
|
||||
|
|
|
|||
|
|
@ -54,8 +54,9 @@ public class ServersMapper {
|
|||
Map<String, List<String>> port2refs = new HashMap<>();
|
||||
for (Map.Entry<String, ServerConfig> entry : configs.entrySet()) {
|
||||
port2refs.compute(entry.getValue().getPort(), (port, list) -> {
|
||||
if (list == null)
|
||||
if (list == null) {
|
||||
list = new ArrayList<>();
|
||||
}
|
||||
list.add(entry.getKey());
|
||||
return list;
|
||||
});
|
||||
|
|
|
|||
|
|
@ -0,0 +1,113 @@
|
|||
/*******************************************************************************
|
||||
* Copyright (c) 2012-2017 Codenvy, S.A.
|
||||
* All rights reserved. This program and the accompanying materials
|
||||
* are made available under the terms of the Eclipse Public License v1.0
|
||||
* which accompanies this distribution, and is available at
|
||||
* http://www.eclipse.org/legal/epl-v10.html
|
||||
*
|
||||
* Contributors:
|
||||
* Codenvy, S.A. - initial API and implementation
|
||||
*******************************************************************************/
|
||||
package org.eclipse.che.workspace.infrastructure.docker;
|
||||
|
||||
import org.eclipse.che.api.core.model.workspace.runtime.RuntimeIdentity;
|
||||
import org.eclipse.che.api.workspace.server.spi.InfrastructureException;
|
||||
import org.eclipse.che.api.workspace.server.spi.InternalInfrastructureException;
|
||||
import org.eclipse.che.api.workspace.server.spi.RuntimeIdentityImpl;
|
||||
import org.eclipse.che.plugin.docker.client.DockerConnector;
|
||||
import org.eclipse.che.plugin.docker.client.json.ContainerConfig;
|
||||
import org.eclipse.che.plugin.docker.client.json.ContainerInfo;
|
||||
import org.eclipse.che.plugin.docker.client.json.ContainerListEntry;
|
||||
import org.eclipse.che.plugin.docker.client.params.ListContainersParams;
|
||||
import org.mockito.InjectMocks;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.testng.MockitoTestNGListener;
|
||||
import org.testng.annotations.Listeners;
|
||||
import org.testng.annotations.Test;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import static com.google.common.collect.Sets.newHashSet;
|
||||
import static java.util.Arrays.asList;
|
||||
import static org.mockito.Matchers.anyString;
|
||||
import static org.mockito.Mockito.when;
|
||||
import static org.testng.Assert.assertEquals;
|
||||
|
||||
/** Tests {@link DockerContainers}. */
|
||||
@Listeners(MockitoTestNGListener.class)
|
||||
public class DockerContainersTest {
|
||||
|
||||
@Mock
|
||||
private DockerConnector docker;
|
||||
|
||||
@InjectMocks
|
||||
private DockerContainers containers;
|
||||
|
||||
@Test
|
||||
public void findsIdentifiers() throws Exception {
|
||||
RuntimeIdentity id1 = new RuntimeIdentityImpl("workspace123", "default", "test");
|
||||
RuntimeIdentity id2 = new RuntimeIdentityImpl("workspace234", "default", "test");
|
||||
|
||||
List<ContainerListEntry> entries = asList(mockContainer(id1, "container1"), mockContainer(id2, "container2"));
|
||||
when(docker.listContainers(ListContainersParams.create().withAll(false))).thenReturn(entries);
|
||||
|
||||
assertEquals(containers.findIdentities(), newHashSet(id1, id2));
|
||||
}
|
||||
|
||||
@Test(expectedExceptions = InfrastructureException.class, expectedExceptionsMessageRegExp = "oops")
|
||||
public void findsIdentitiesRethrowsIOExceptionThrownWhileListingContainersAsInternalInfraException() throws Exception {
|
||||
when(docker.listContainers(ListContainersParams.create().withAll(false))).thenThrow(new IOException("oops"));
|
||||
|
||||
containers.findIdentities();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void findContainers() throws Exception {
|
||||
RuntimeIdentity id1 = new RuntimeIdentityImpl("workspace123", "default", "test");
|
||||
ContainerListEntry entry1 = mockContainer(id1, "container1");
|
||||
ContainerListEntry entry2 = mockContainer(id1, "container2");
|
||||
|
||||
RuntimeIdentity id2 = new RuntimeIdentityImpl("workspace234", "default", "test");
|
||||
ContainerListEntry entry3 = mockContainer(id2, "container3");
|
||||
|
||||
List<ContainerListEntry> entries = asList(entry1, entry2, entry3);
|
||||
when(docker.listContainers(ListContainersParams.create().withAll(false))).thenReturn(entries);
|
||||
|
||||
assertEquals(containers.find(id1)
|
||||
.stream()
|
||||
.map(ContainerListEntry::getId)
|
||||
.collect(Collectors.toSet()), newHashSet(entry1.getId(), entry2.getId()));
|
||||
assertEquals(containers.find(id2)
|
||||
.stream()
|
||||
.map(ContainerListEntry::getId)
|
||||
.collect(Collectors.toSet()), newHashSet(entry3.getId()));
|
||||
}
|
||||
|
||||
@Test(expectedExceptions = InternalInfrastructureException.class, expectedExceptionsMessageRegExp = "oops")
|
||||
public void findContainersRethrowsIOExceptionThrownWhileListingContainersAsInternalInfraException() throws Exception {
|
||||
when(docker.listContainers(ListContainersParams.create().withAll(false))).thenThrow(new IOException("oops"));
|
||||
|
||||
containers.find(new RuntimeIdentityImpl("workspace123", "default", "test"));
|
||||
}
|
||||
|
||||
@Test(expectedExceptions = InternalInfrastructureException.class, expectedExceptionsMessageRegExp = "oops")
|
||||
public void findContainersRethrowsIOExceptionThrownWhileInspectingContainersAsInternalInfraException() throws Exception {
|
||||
RuntimeIdentity id = new RuntimeIdentityImpl("workspace123", "default", "test");
|
||||
List<ContainerListEntry> entries = Collections.singletonList(mockContainer(id, "container"));
|
||||
when(docker.listContainers(ListContainersParams.create().withAll(false))).thenReturn(entries);
|
||||
|
||||
when(docker.inspectContainer(anyString())).thenThrow(new IOException("oops"));
|
||||
|
||||
containers.find(id);
|
||||
}
|
||||
|
||||
private ContainerListEntry mockContainer(RuntimeIdentity runtimeId, String containerId) throws IOException {
|
||||
ContainerListEntry entry = new ContainerListEntry();
|
||||
entry.setLabels(Labels.newSerializer().runtimeId(runtimeId).labels());
|
||||
entry.setId(containerId);
|
||||
return entry;
|
||||
}
|
||||
}
|
||||
|
|
@ -84,9 +84,9 @@ public class InstallerConfigApplierTest {
|
|||
|
||||
Map<String, String> labels = service.getLabels();
|
||||
assertEquals(labels.size(), 3);
|
||||
assertEquals(labels.get("che:server:1111/udp:ref"), "a");
|
||||
assertEquals(labels.get("che:server:1111/udp:protocol"), "http");
|
||||
assertEquals(labels.get("che:server:1111/udp:path"), "b");
|
||||
assertEquals(labels.get("org.eclipse.che.server.a.port"), "1111/udp");
|
||||
assertEquals(labels.get("org.eclipse.che.server.a.protocol"), "http");
|
||||
assertEquals(labels.get("org.eclipse.che.server.a.path"), "b");
|
||||
}
|
||||
|
||||
@Test
|
||||
|
|
|
|||
|
|
@ -0,0 +1,82 @@
|
|||
/*******************************************************************************
|
||||
* Copyright (c) 2012-2017 Codenvy, S.A.
|
||||
* All rights reserved. This program and the accompanying materials
|
||||
* are made available under the terms of the Eclipse Public License v1.0
|
||||
* which accompanies this distribution, and is available at
|
||||
* http://www.eclipse.org/legal/epl-v10.html
|
||||
*
|
||||
* Contributors:
|
||||
* Codenvy, S.A. - initial API and implementation
|
||||
*******************************************************************************/
|
||||
package org.eclipse.che.workspace.infrastructure.docker;
|
||||
|
||||
import org.eclipse.che.api.core.ValidationException;
|
||||
import org.eclipse.che.api.core.model.workspace.config.Environment;
|
||||
import org.eclipse.che.api.core.model.workspace.config.MachineConfig;
|
||||
import org.testng.annotations.DataProvider;
|
||||
import org.testng.annotations.Test;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import static org.mockito.Mockito.doReturn;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
/** Tests {@link RuntimeConsistencyChecker}. */
|
||||
public class RuntimeConsistencyCheckerTest {
|
||||
|
||||
@Test(dataProvider = "consistentRuntimesProvider")
|
||||
public void consistentRuntimes(Environment environment, DockerInternalRuntime runtime) throws ValidationException {
|
||||
new RuntimeConsistencyChecker().check(environment, runtime);
|
||||
}
|
||||
|
||||
@Test(dataProvider = "inconsistentRuntimesProvider", expectedExceptions = ValidationException.class)
|
||||
public void inconsistentRuntimes(Environment environment, DockerInternalRuntime runtime) throws ValidationException {
|
||||
new RuntimeConsistencyChecker().check(environment, runtime);
|
||||
}
|
||||
|
||||
@DataProvider
|
||||
private static Object[][] consistentRuntimesProvider() {
|
||||
return new Object[][] {
|
||||
{ environment("a", "b"), runtime("b", "a") },
|
||||
{ environment("b", "a"), runtime("b", "a") }
|
||||
};
|
||||
}
|
||||
|
||||
@DataProvider
|
||||
private static Object[][] inconsistentRuntimesProvider() {
|
||||
return new Object[][] {
|
||||
{ environment("a", "b"), runtime("a") },
|
||||
{ environment("a", "b"), runtime("a", "c") },
|
||||
{ environment("a", "b"), runtime("a", "b", "c") },
|
||||
{ environment("a", "b"), runtime("a") },
|
||||
{ environment("a"), runtime("a", "b") }
|
||||
};
|
||||
}
|
||||
|
||||
private static Environment environment(String... names) {
|
||||
Map<String, MachineConfig> configs = new HashMap<>();
|
||||
for (String name : names) {
|
||||
configs.put(name, mock(MachineConfig.class));
|
||||
}
|
||||
|
||||
Environment environment = mock(Environment.class);
|
||||
doReturn(configs).when(environment).getMachines();
|
||||
when(environment.toString()).thenReturn(Arrays.toString(names));
|
||||
return environment;
|
||||
}
|
||||
|
||||
private static DockerInternalRuntime runtime(String... names) {
|
||||
Map<String, DockerMachine> machines = new HashMap<>();
|
||||
for (String name : names) {
|
||||
machines.put(name, mock(DockerMachine.class));
|
||||
}
|
||||
|
||||
DockerInternalRuntime runtime = mock(DockerInternalRuntime.class);
|
||||
doReturn(machines).when(runtime).getMachines();
|
||||
when(runtime.toString()).thenReturn(Arrays.toString(names));
|
||||
return runtime;
|
||||
}
|
||||
}
|
||||
|
|
@ -104,6 +104,11 @@ public class ServerCheckerTest {
|
|||
}
|
||||
}
|
||||
|
||||
@Test(expectedExceptions = InfrastructureException.class)
|
||||
public void checkOnceThrowsExceptionIfServerIsNotAvailable() throws InfrastructureException {
|
||||
new TestServerChecker("test", "test", 1, 1, TimeUnit.SECONDS, null).checkOnce();
|
||||
}
|
||||
|
||||
private static class TestServerChecker extends ServerChecker {
|
||||
protected TestServerChecker(String machineName, String serverRef, long period, long timeout,
|
||||
TimeUnit timeUnit, Timer timer) {
|
||||
|
|
|
|||
|
|
@ -29,6 +29,7 @@ import java.util.function.Consumer;
|
|||
|
||||
import static org.mockito.Matchers.any;
|
||||
import static org.mockito.Matchers.anyString;
|
||||
import static org.mockito.Mockito.doThrow;
|
||||
import static org.mockito.Mockito.never;
|
||||
import static org.mockito.Mockito.spy;
|
||||
import static org.mockito.Mockito.timeout;
|
||||
|
|
@ -63,7 +64,7 @@ public class ServersReadinessCheckerTest {
|
|||
.thenReturn(connectionChecker);
|
||||
when(connectionChecker.getReportCompFuture()).thenReturn(compFuture);
|
||||
|
||||
checker = new ServersReadinessChecker(MACHINE_NAME, getDefaultServers(), readinessHandler, factory);
|
||||
checker = new ServersReadinessChecker(MACHINE_NAME, getDefaultServers(), factory);
|
||||
}
|
||||
|
||||
@AfterMethod(timeOut = 1000)
|
||||
|
|
@ -75,7 +76,7 @@ public class ServersReadinessCheckerTest {
|
|||
|
||||
@Test(timeOut = 1000)
|
||||
public void shouldNotifyReadinessHandlerAboutEachServerReadiness() throws Exception {
|
||||
checker.startAsync();
|
||||
checker.startAsync(readinessHandler);
|
||||
|
||||
verify(readinessHandler, timeout(500).never()).accept(anyString());
|
||||
|
||||
|
|
@ -86,7 +87,7 @@ public class ServersReadinessCheckerTest {
|
|||
|
||||
@Test(timeOut = 1000)
|
||||
public void shouldThrowExceptionIfAServerIsUnavailable() throws Exception {
|
||||
checker.startAsync();
|
||||
checker.startAsync(readinessHandler);
|
||||
|
||||
connectionChecker.getReportCompFuture()
|
||||
.completeExceptionally(new InfrastructureException("my exception"));
|
||||
|
|
@ -102,9 +103,9 @@ public class ServersReadinessCheckerTest {
|
|||
public void shouldNotCheckNotHardcodedServers() throws Exception {
|
||||
Map<String, ServerImpl> servers = ImmutableMap.of("wsagent", new ServerImpl("http://localhost"),
|
||||
"not-hardcoded", new ServerImpl("http://localhost"));
|
||||
checker = new ServersReadinessChecker(MACHINE_NAME, servers, readinessHandler, factory);
|
||||
checker = new ServersReadinessChecker(MACHINE_NAME, servers, factory);
|
||||
|
||||
checker.startAsync();
|
||||
checker.startAsync(readinessHandler);
|
||||
connectionChecker.getReportCompFuture().complete("test_ref");
|
||||
checker.await();
|
||||
|
||||
|
|
@ -120,7 +121,7 @@ public class ServersReadinessCheckerTest {
|
|||
.thenReturn(future2)
|
||||
.thenReturn(future3);
|
||||
|
||||
checker.startAsync();
|
||||
checker.startAsync(readinessHandler);
|
||||
|
||||
future2.completeExceptionally(new InfrastructureException("error"));
|
||||
|
||||
|
|
@ -137,6 +138,13 @@ public class ServersReadinessCheckerTest {
|
|||
}
|
||||
}
|
||||
|
||||
@Test(expectedExceptions = InfrastructureException.class, expectedExceptionsMessageRegExp = "oops!")
|
||||
public void throwsExceptionIfAnyServerIsNotAvailable() throws InfrastructureException {
|
||||
doThrow(new InfrastructureException("oops!")).when(connectionChecker).checkOnce();
|
||||
|
||||
checker.checkOnce();
|
||||
}
|
||||
|
||||
Map<String, ServerImpl> getDefaultServers() {
|
||||
return ImmutableMap.of("wsagent", new ServerImpl("http://localhost"),
|
||||
"exec-agent", new ServerImpl("http://localhost"),
|
||||
|
|
|
|||
|
|
@ -82,7 +82,7 @@ public class OpenshiftInternalRuntime extends InternalRuntime<OpenshiftRuntimeCo
|
|||
EventService eventService,
|
||||
OpenshiftBootstrapperFactory openshiftBootstrapperFactory,
|
||||
@Named("che.infra.openshift.machine_start_timeout_min") int machineStartTimeoutMin) {
|
||||
super(context, urlRewriter);
|
||||
super(context, urlRewriter, false);
|
||||
this.identity = identity;
|
||||
this.kubernetesEnvironment = openshiftEnvironment;
|
||||
this.clientFactory = clientFactory;
|
||||
|
|
|
|||
|
|
@ -30,6 +30,7 @@ public class ContainerListEntry {
|
|||
private Map<String, String> labels;
|
||||
private int sizeRw;
|
||||
private int sizeRootFs;
|
||||
private NetworkSettings networkSettings;
|
||||
|
||||
/**
|
||||
* Returns unique container identifier
|
||||
|
|
@ -144,7 +145,7 @@ public class ContainerListEntry {
|
|||
}
|
||||
|
||||
/**
|
||||
* Returns the virtual size of the container
|
||||
* Returns the virtual size of the container
|
||||
*/
|
||||
public int getSizeRootFs() {
|
||||
return sizeRootFs;
|
||||
|
|
@ -154,6 +155,14 @@ public class ContainerListEntry {
|
|||
this.sizeRootFs = sizeRootFs;
|
||||
}
|
||||
|
||||
public NetworkSettings getNetworkSettings() {
|
||||
return networkSettings;
|
||||
}
|
||||
|
||||
public void setNetworkSettings(NetworkSettings networkSettings) {
|
||||
this.networkSettings = networkSettings;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "ContainerListEntry{" +
|
||||
|
|
@ -168,6 +177,7 @@ public class ContainerListEntry {
|
|||
", labels=" + labels +
|
||||
", sizeRw=" + sizeRw +
|
||||
", sizeRootFs=" + sizeRootFs +
|
||||
", networkSettings=" + networkSettings +
|
||||
'}';
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -24,6 +24,14 @@ import java.util.Map;
|
|||
* @author Alexander Garagatyi
|
||||
*/
|
||||
public class Filters {
|
||||
|
||||
private static final String LABEL_FILTER = "label";
|
||||
|
||||
/** Creates filters with {@value #LABEL_FILTER} filter initialized to the given values. */
|
||||
public static Filters label(String... values) {
|
||||
return new Filters().withFilter(LABEL_FILTER, values);
|
||||
}
|
||||
|
||||
private final Map<String, List<String>> filters = new HashMap<>();
|
||||
|
||||
public Map<String, List<String>> getFilters() {
|
||||
|
|
|
|||
|
|
@ -15,6 +15,13 @@ public class PortBinding {
|
|||
private String hostIp;
|
||||
private String hostPort;
|
||||
|
||||
public PortBinding() {}
|
||||
|
||||
public PortBinding(String hostIp, String hostPort) {
|
||||
this.hostIp = hostIp;
|
||||
this.hostPort = hostPort;
|
||||
}
|
||||
|
||||
public String getHostIp() {
|
||||
return hostIp;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -11,9 +11,7 @@
|
|||
"wsagent": {
|
||||
"port": "4401/tcp",
|
||||
"protocol": "http",
|
||||
"properties": {
|
||||
"path": "/api"
|
||||
}
|
||||
"path" : "/api/"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -10,6 +10,7 @@
|
|||
*******************************************************************************/
|
||||
package org.eclipse.che.api.workspace.server;
|
||||
|
||||
import com.google.common.annotations.VisibleForTesting;
|
||||
import com.google.common.collect.ImmutableMap;
|
||||
|
||||
import org.eclipse.che.api.core.ConflictException;
|
||||
|
|
@ -30,11 +31,13 @@ import org.eclipse.che.api.workspace.server.spi.InternalRuntime;
|
|||
import org.eclipse.che.api.workspace.server.spi.RuntimeContext;
|
||||
import org.eclipse.che.api.workspace.server.spi.RuntimeIdentityImpl;
|
||||
import org.eclipse.che.api.workspace.server.spi.RuntimeInfrastructure;
|
||||
import org.eclipse.che.api.workspace.server.spi.WorkspaceDao;
|
||||
import org.eclipse.che.api.workspace.shared.dto.event.RuntimeStatusEvent;
|
||||
import org.eclipse.che.api.workspace.shared.dto.event.WorkspaceStatusEvent;
|
||||
import org.eclipse.che.commons.env.EnvironmentContext;
|
||||
import org.eclipse.che.commons.lang.concurrent.ThreadLocalPropagateContext;
|
||||
import org.eclipse.che.commons.subject.Subject;
|
||||
import org.eclipse.che.core.db.DBInitializer;
|
||||
import org.eclipse.che.dto.server.DtoFactory;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
|
@ -74,14 +77,18 @@ public class WorkspaceRuntimes {
|
|||
private final ConcurrentMap<String, RuntimeState> runtimes;
|
||||
private final EventService eventService;
|
||||
private final WorkspaceSharedPool sharedPool;
|
||||
private final WorkspaceDao workspaceDao;
|
||||
|
||||
@Inject
|
||||
public WorkspaceRuntimes(EventService eventService,
|
||||
Set<RuntimeInfrastructure> infrastructures,
|
||||
WorkspaceSharedPool sharedPool) {
|
||||
WorkspaceSharedPool sharedPool,
|
||||
WorkspaceDao workspaceDao,
|
||||
@SuppressWarnings("unused") DBInitializer ignored) {
|
||||
this.runtimes = new ConcurrentHashMap<>();
|
||||
this.eventService = eventService;
|
||||
this.sharedPool = sharedPool;
|
||||
this.workspaceDao = workspaceDao;
|
||||
|
||||
// TODO: consider extracting to a strategy interface(1. pick the last, 2. fail with conflict)
|
||||
Map<String, RuntimeInfrastructure> tmp = new HashMap<>();
|
||||
|
|
@ -101,6 +108,12 @@ public class WorkspaceRuntimes {
|
|||
infraByRecipe = ImmutableMap.copyOf(tmp);
|
||||
}
|
||||
|
||||
@PostConstruct
|
||||
private void init() {
|
||||
subscribeCleanupOnAbnormalRuntimeStopEvent();
|
||||
recover();
|
||||
}
|
||||
|
||||
// TODO Doesn't look like correct place for this logic. Where this code should be?
|
||||
public Environment estimate(Environment environment) throws NotFoundException, InfrastructureException, ValidationException {
|
||||
// TODO decide whether throw exception when dev machine not found
|
||||
|
|
@ -317,43 +330,69 @@ public class WorkspaceRuntimes {
|
|||
return runtimes.containsKey(workspaceId);
|
||||
}
|
||||
|
||||
@PostConstruct
|
||||
private void init() {
|
||||
recover();
|
||||
subscribeCleanupOnAbnormalRuntimeStopEvent();
|
||||
}
|
||||
|
||||
private void recover() {
|
||||
for (RuntimeInfrastructure infra : infraByRecipe.values()) {
|
||||
try {
|
||||
for (RuntimeIdentity id : infra.getIdentities()) {
|
||||
// TODO how to identify correct state of runtime
|
||||
if (runtimes.putIfAbsent(id.getWorkspaceId(),
|
||||
new RuntimeState(validate(infra.getRuntime(id)), RUNNING)) != null) {
|
||||
// should not happen, violation of SPI contract
|
||||
LOG.error("More than 1 runtime of workspace found. " +
|
||||
"Runtime identity of duplicate is '{}'. Skipping duplicate.", id);
|
||||
}
|
||||
for (RuntimeIdentity identity : infra.getIdentities()) {
|
||||
recoverOne(infra, identity);
|
||||
}
|
||||
} catch (UnsupportedOperationException x) {
|
||||
LOG.warn("Not recoverable infrastructure: '{}'", infra.getName());
|
||||
} catch (InfrastructureException x) {
|
||||
} catch (ServerException | InfrastructureException x) {
|
||||
LOG.error("An error occurred while attempted to recover runtimes using infrastructure '{}'. Reason: '{}'",
|
||||
infra.getName(),
|
||||
x.getMessage());
|
||||
infra.getName(), x.getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
void recoverOne(RuntimeInfrastructure infra, RuntimeIdentity identity) throws ServerException {
|
||||
Workspace workspace;
|
||||
try {
|
||||
workspace = workspaceDao.get(identity.getWorkspaceId());
|
||||
} catch (NotFoundException x) {
|
||||
LOG.error("Workspace configuration is missing for the runtime '{}:{}'. Runtime won't be recovered",
|
||||
identity.getWorkspaceId(),
|
||||
identity.getEnvName());
|
||||
return;
|
||||
}
|
||||
|
||||
Environment environment = workspace.getConfig().getEnvironments().get(identity.getEnvName());
|
||||
if (environment == null) {
|
||||
LOG.error("Environment configuration is missing for the runtime '{}:{}'. Runtime won't be recovered",
|
||||
identity.getWorkspaceId(),
|
||||
identity.getEnvName());
|
||||
return;
|
||||
}
|
||||
|
||||
InternalRuntime runtime;
|
||||
try {
|
||||
runtime = infra.prepare(identity, environment).getRuntime();
|
||||
} catch (InfrastructureException | ValidationException x) {
|
||||
LOG.error("Couldn't recover runtime '{}:{}'. Error: {}",
|
||||
identity.getWorkspaceId(),
|
||||
identity.getEnvName(),
|
||||
x.getMessage());
|
||||
return;
|
||||
}
|
||||
|
||||
RuntimeState prev = runtimes.putIfAbsent(identity.getWorkspaceId(), new RuntimeState(runtime, RUNNING));
|
||||
if (prev == null) {
|
||||
LOG.info("Successfully recovered workspace runtime '{}'", identity.getWorkspaceId(), identity.getEnvName());
|
||||
} else {
|
||||
LOG.error("More than 1 runtime with id '{}:{}' found. " +
|
||||
"Duplicate provided by infrastructure '{}' will be skipped",
|
||||
identity.getWorkspaceId(),
|
||||
identity.getEnvName(),
|
||||
prev.runtime.getContext().getInfrastructure().getName(),
|
||||
infra.getName());
|
||||
}
|
||||
}
|
||||
|
||||
private void subscribeCleanupOnAbnormalRuntimeStopEvent() {
|
||||
eventService.subscribe(new CleanupRuntimeOnAbnormalRuntimeStop());
|
||||
}
|
||||
|
||||
//TODO do we need some validation on start?
|
||||
private InternalRuntime validate(InternalRuntime runtime) {
|
||||
return runtime;
|
||||
}
|
||||
|
||||
private static EnvironmentImpl copyEnv(Workspace workspace, String envName) {
|
||||
|
||||
requireNonNull(workspace, "Workspace should not be null.");
|
||||
|
|
@ -375,7 +414,7 @@ public class WorkspaceRuntimes {
|
|||
if (state != null) {
|
||||
eventService.publish(DtoFactory.newDto(WorkspaceStatusEvent.class)
|
||||
.withWorkspaceId(state.runtime.getContext().getIdentity()
|
||||
.getWorkspaceId())
|
||||
.getWorkspaceId())
|
||||
.withPrevStatus(WorkspaceStatus.RUNNING)
|
||||
.withStatus(STOPPED)
|
||||
.withError("Error occurs on workspace runtime stop. Error: " +
|
||||
|
|
|
|||
|
|
@ -16,6 +16,9 @@ import org.eclipse.che.api.core.model.workspace.runtime.Server;
|
|||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import static java.util.stream.Collectors.toMap;
|
||||
|
||||
/**
|
||||
* Data object for {@link Machine}.
|
||||
|
|
@ -27,10 +30,6 @@ public class MachineImpl implements Machine {
|
|||
private Map<String, String> properties;
|
||||
private Map<String, ServerImpl> servers;
|
||||
|
||||
public static MachineRuntimeInfoImplBuilder builder() {
|
||||
return new MachineRuntimeInfoImplBuilder();
|
||||
}
|
||||
|
||||
public MachineImpl(Machine machineRuntime) {
|
||||
this(machineRuntime.getProperties(), machineRuntime.getServers());
|
||||
}
|
||||
|
|
@ -38,17 +37,18 @@ public class MachineImpl implements Machine {
|
|||
public MachineImpl(Map<String, String> properties,
|
||||
Map<String, ? extends Server> servers) {
|
||||
// this.envVariables = new HashMap<>(envVariables);
|
||||
this(servers);
|
||||
this.properties = new HashMap<>(properties);
|
||||
}
|
||||
|
||||
public MachineImpl(Map<String, ? extends Server> servers) {
|
||||
if (servers != null) {
|
||||
this.servers = servers.entrySet()
|
||||
.stream()
|
||||
.collect(HashMap::new,
|
||||
(map, entry) -> map.put(entry.getKey(), new ServerImpl(entry.getValue())),
|
||||
HashMap::putAll);
|
||||
.collect(toMap(Map.Entry::getKey, entry -> new ServerImpl(entry.getValue().getUrl())));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// public Map<String, String> getEnvVariables() {
|
||||
// if (envVariables == null) {
|
||||
// envVariables = new HashMap<>();
|
||||
|
|
@ -78,45 +78,12 @@ public class MachineImpl implements Machine {
|
|||
if (!(o instanceof MachineImpl)) return false;
|
||||
MachineImpl that = (MachineImpl)o;
|
||||
return //Objects.equals(getEnvVariables(), that.getEnvVariables()) &&
|
||||
Objects.equals(getProperties(), that.getProperties()) &&
|
||||
Objects.equals(getServers(), that.getServers());
|
||||
Objects.equals(getProperties(), that.getProperties()) &&
|
||||
Objects.equals(getServers(), that.getServers());
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(/*getEnvVariables(),*/ getProperties(), getServers());
|
||||
}
|
||||
|
||||
public static class MachineRuntimeInfoImplBuilder {
|
||||
private Map<String, ? extends Server> servers;
|
||||
private Map<String, String> properties;
|
||||
// private Map<String, String> envVariables;
|
||||
|
||||
public MachineImpl build() {
|
||||
return new MachineImpl(properties, servers);
|
||||
}
|
||||
|
||||
public MachineRuntimeInfoImplBuilder setServers(Map<String, ? extends Server> servers) {
|
||||
this.servers = servers;
|
||||
return this;
|
||||
}
|
||||
|
||||
public MachineRuntimeInfoImplBuilder setProperties(Map<String, String> properties) {
|
||||
this.properties = properties;
|
||||
return this;
|
||||
}
|
||||
|
||||
// public MachineRuntimeInfoImplBuilder setEnvVariables(Map<String, String> envVariables) {
|
||||
// this.envVariables = envVariables;
|
||||
// return this;
|
||||
// }
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "MachineImpl{" +
|
||||
"properties=" + properties +
|
||||
", servers=" + servers +
|
||||
'}';
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -13,12 +13,14 @@ package org.eclipse.che.api.workspace.server.model.impl;
|
|||
import org.eclipse.che.api.core.model.workspace.runtime.Server;
|
||||
import org.eclipse.che.api.core.model.workspace.runtime.ServerStatus;
|
||||
|
||||
import java.util.Objects;
|
||||
|
||||
/**
|
||||
* @author gazarenkov
|
||||
*/
|
||||
public class ServerImpl implements Server {
|
||||
|
||||
private String url;
|
||||
private String url;
|
||||
private ServerStatus status;
|
||||
|
||||
public ServerImpl(String url) {
|
||||
|
|
@ -51,4 +53,30 @@ public class ServerImpl implements Server {
|
|||
public void setStatus(ServerStatus status) {
|
||||
this.status = status;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return url;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if (this == obj) {
|
||||
return true;
|
||||
}
|
||||
if (!(obj instanceof Server)) {
|
||||
return false;
|
||||
}
|
||||
final Server that = (Server)obj;
|
||||
return Objects.equals(url, that.getUrl())
|
||||
&& Objects.equals(status, that.getStatus());
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
int hash = 7;
|
||||
hash = 31 * hash + Objects.hashCode(url);
|
||||
hash = 31 * hash + Objects.hashCode(status);
|
||||
return hash;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -45,9 +45,12 @@ public abstract class InternalRuntime <T extends RuntimeContext> implements Runt
|
|||
private final List<Warning> warnings = new ArrayList<>();
|
||||
private WorkspaceStatus status;
|
||||
|
||||
public InternalRuntime(T context, URLRewriter urlRewriter) {
|
||||
public InternalRuntime(T context, URLRewriter urlRewriter, boolean running) {
|
||||
this.context = context;
|
||||
this.urlRewriter = urlRewriter != null ? urlRewriter : new URLRewriter.NoOpURLRewriter();
|
||||
if (running) {
|
||||
status = WorkspaceStatus.RUNNING;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
@ -155,7 +158,7 @@ public abstract class InternalRuntime <T extends RuntimeContext> implements Runt
|
|||
/**
|
||||
* @return the Context
|
||||
*/
|
||||
public final RuntimeContext getContext() {
|
||||
public final T getContext() {
|
||||
return context;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -60,8 +60,10 @@ public abstract class RuntimeContext {
|
|||
* Context must return the Runtime object whatever its status is (STOPPED status including)
|
||||
*
|
||||
* @return Runtime object
|
||||
* @throws InfrastructureException
|
||||
* when any error during runtime retrieving/creation
|
||||
*/
|
||||
public abstract InternalRuntime getRuntime();
|
||||
public abstract InternalRuntime getRuntime() throws InfrastructureException;
|
||||
|
||||
/**
|
||||
* Infrastructure should assign channel (usual WebSocket) to push long lived processes messages
|
||||
|
|
|
|||
|
|
@ -100,24 +100,6 @@ public abstract class RuntimeInfrastructure {
|
|||
throw new UnsupportedOperationException("The implementation does not track runtimes");
|
||||
}
|
||||
|
||||
/**
|
||||
* An Infrastructure MAY track Runtimes. In this case the method should be overridden.
|
||||
* <p>
|
||||
* One of the reason for infrastructure to support this is ability to recover infrastructure
|
||||
* after shutting down Master server.
|
||||
*
|
||||
* @param id
|
||||
* the RuntimeIdentityImpl
|
||||
* @return the Runtime
|
||||
* @throws UnsupportedOperationException
|
||||
* if implementation does not support runtimes tracking
|
||||
* @throws InfrastructureException
|
||||
* if any other error occurred
|
||||
*/
|
||||
public InternalRuntime getRuntime(RuntimeIdentity id) throws InfrastructureException {
|
||||
throw new UnsupportedOperationException("The implementation does not track runtimes");
|
||||
}
|
||||
|
||||
/**
|
||||
* Making Runtime is a two phase process.
|
||||
* On the first phase implementation MUST prepare RuntimeContext, this is supposedly "fast" method
|
||||
|
|
|
|||
|
|
@ -0,0 +1,233 @@
|
|||
/*******************************************************************************
|
||||
* Copyright (c) 2012-2017 Codenvy, S.A.
|
||||
* All rights reserved. This program and the accompanying materials
|
||||
* are made available under the terms of the Eclipse Public License v1.0
|
||||
* which accompanies this distribution, and is available at
|
||||
* http://www.eclipse.org/legal/epl-v10.html
|
||||
*
|
||||
* Contributors:
|
||||
* Codenvy, S.A. - initial API and implementation
|
||||
*******************************************************************************/
|
||||
package org.eclipse.che.api.workspace.server;
|
||||
|
||||
import com.google.common.collect.ImmutableMap;
|
||||
|
||||
import org.eclipse.che.api.core.NotFoundException;
|
||||
import org.eclipse.che.api.core.ServerException;
|
||||
import org.eclipse.che.api.core.ValidationException;
|
||||
import org.eclipse.che.api.core.model.workspace.config.Environment;
|
||||
import org.eclipse.che.api.core.model.workspace.runtime.Machine;
|
||||
import org.eclipse.che.api.core.model.workspace.runtime.RuntimeIdentity;
|
||||
import org.eclipse.che.api.core.notification.EventService;
|
||||
import org.eclipse.che.api.workspace.server.model.impl.EnvironmentImpl;
|
||||
import org.eclipse.che.api.workspace.server.model.impl.WorkspaceConfigImpl;
|
||||
import org.eclipse.che.api.workspace.server.model.impl.WorkspaceImpl;
|
||||
import org.eclipse.che.api.workspace.server.spi.InfrastructureException;
|
||||
import org.eclipse.che.api.workspace.server.spi.InternalRuntime;
|
||||
import org.eclipse.che.api.workspace.server.spi.RuntimeContext;
|
||||
import org.eclipse.che.api.workspace.server.spi.RuntimeIdentityImpl;
|
||||
import org.eclipse.che.api.workspace.server.spi.RuntimeInfrastructure;
|
||||
import org.eclipse.che.api.workspace.server.spi.WorkspaceDao;
|
||||
import org.eclipse.che.core.db.DBInitializer;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.testng.MockitoTestNGListener;
|
||||
import org.testng.annotations.BeforeMethod;
|
||||
import org.testng.annotations.Listeners;
|
||||
import org.testng.annotations.Test;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.Map;
|
||||
|
||||
import static org.mockito.Matchers.any;
|
||||
import static org.mockito.Matchers.anyObject;
|
||||
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.spy;
|
||||
import static org.mockito.Mockito.when;
|
||||
import static org.testng.Assert.assertEquals;
|
||||
import static org.testng.Assert.assertFalse;
|
||||
import static org.testng.Assert.assertNotNull;
|
||||
|
||||
/** Tests {@link WorkspaceRuntimes}. */
|
||||
@Listeners(MockitoTestNGListener.class)
|
||||
public class WorkspaceRuntimesTest {
|
||||
|
||||
@Mock
|
||||
private EventService eventService;
|
||||
|
||||
@Mock
|
||||
private WorkspaceDao workspaceDao;
|
||||
|
||||
@Mock
|
||||
private DBInitializer dbInitializer;
|
||||
|
||||
@Mock
|
||||
private WorkspaceSharedPool sharedPool;
|
||||
|
||||
private RuntimeInfrastructure infrastructure;
|
||||
private WorkspaceRuntimes runtimes;
|
||||
|
||||
@BeforeMethod
|
||||
public void setUp() {
|
||||
infrastructure = spy(new TestInfrastructure(eventService));
|
||||
|
||||
runtimes = new WorkspaceRuntimes(eventService,
|
||||
Collections.singleton(infrastructure),
|
||||
sharedPool,
|
||||
workspaceDao,
|
||||
dbInitializer);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void runtimeIsRecovered() throws Exception {
|
||||
RuntimeIdentity identity = new RuntimeIdentityImpl("workspace123", "my-env", "me");
|
||||
|
||||
mockWorkspace(identity);
|
||||
RuntimeContext context = mockContext(identity);
|
||||
when(context.getRuntime()).thenReturn(new TestInternalRuntime(context));
|
||||
doReturn(context).when(infrastructure).prepare(eq(identity), anyObject());
|
||||
|
||||
// try recover
|
||||
runtimes.recoverOne(infrastructure, identity);
|
||||
|
||||
WorkspaceImpl workspace = new WorkspaceImpl(identity.getWorkspaceId(), null, null);
|
||||
runtimes.injectRuntime(workspace);
|
||||
assertNotNull(workspace.getRuntime());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void runtimeIsNotRecoveredIfNoWorkspaceFound() throws Exception {
|
||||
RuntimeIdentity identity = new RuntimeIdentityImpl("workspace123", "my-env", "me");
|
||||
when(workspaceDao.get(identity.getWorkspaceId())).thenThrow(new NotFoundException("no!"));
|
||||
|
||||
// try recover
|
||||
runtimes.recoverOne(infrastructure, identity);
|
||||
|
||||
assertFalse(runtimes.hasRuntime(identity.getWorkspaceId()));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void runtimeIsNotRecoveredIfNoEnvironmentFound() throws Exception {
|
||||
RuntimeIdentity identity = new RuntimeIdentityImpl("workspace123", "my-env", "me");
|
||||
WorkspaceImpl workspace = mockWorkspace(identity);
|
||||
when(workspace.getConfig().getEnvironments()).thenReturn(Collections.emptyMap());
|
||||
|
||||
// try recover
|
||||
runtimes.recoverOne(infrastructure, identity);
|
||||
|
||||
assertFalse(runtimes.hasRuntime(identity.getWorkspaceId()));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void runtimeIsNotRecoveredIfInfraPreparationFailed() throws Exception {
|
||||
RuntimeIdentity identity = new RuntimeIdentityImpl("workspace123", "my-env", "me");
|
||||
|
||||
EnvironmentImpl env = mockWorkspace(identity).getConfig().getEnvironments().get(identity.getEnvName());
|
||||
doThrow(new InfrastructureException("oops!")).when(infrastructure).prepare(identity, env);
|
||||
|
||||
// try recover
|
||||
runtimes.recoverOne(infrastructure, identity);
|
||||
|
||||
assertFalse(runtimes.hasRuntime(identity.getWorkspaceId()));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void runtimeIsNotRecoveredIfAnotherRuntimeWithTheSameIdentityAlreadyExists() throws Exception {
|
||||
RuntimeIdentity identity = new RuntimeIdentityImpl("workspace123", "my-env", "me");
|
||||
|
||||
mockWorkspace(identity);
|
||||
RuntimeContext context = mockContext(identity);
|
||||
|
||||
// runtime 1(has 1 machine) must be successfully saved
|
||||
Map<String, Machine> r1machines = ImmutableMap.of("m1", mock(Machine.class));
|
||||
InternalRuntime runtime1 = spy(new TestInternalRuntime(context, r1machines));
|
||||
when(context.getRuntime()).thenReturn(runtime1);
|
||||
runtimes.recoverOne(infrastructure, identity);
|
||||
|
||||
// runtime 2 must not be saved
|
||||
Map<String, Machine> r2machines = ImmutableMap.of("m1", mock(Machine.class), "m2", mock(Machine.class));
|
||||
InternalRuntime runtime2 = new TestInternalRuntime(context, r2machines);
|
||||
when(context.getRuntime()).thenReturn(runtime2);
|
||||
runtimes.recoverOne(infrastructure, identity);
|
||||
|
||||
WorkspaceImpl workspace = new WorkspaceImpl(identity.getWorkspaceId(), null, null);
|
||||
runtimes.injectRuntime(workspace);
|
||||
|
||||
assertNotNull(workspace.getRuntime());
|
||||
assertEquals(workspace.getRuntime().getMachines().keySet(), r1machines.keySet());
|
||||
}
|
||||
|
||||
private RuntimeContext mockContext(RuntimeIdentity identity) throws ValidationException, InfrastructureException {
|
||||
RuntimeContext context = mock(RuntimeContext.class);
|
||||
doReturn(context).when(infrastructure).prepare(eq(identity), anyObject());
|
||||
when(context.getInfrastructure()).thenReturn(infrastructure);
|
||||
when(context.getIdentity()).thenReturn(identity);
|
||||
return context;
|
||||
}
|
||||
|
||||
private WorkspaceImpl mockWorkspace(RuntimeIdentity identity) throws NotFoundException, ServerException {
|
||||
EnvironmentImpl environment = mock(EnvironmentImpl.class);
|
||||
|
||||
WorkspaceConfigImpl config = mock(WorkspaceConfigImpl.class);
|
||||
when(config.getEnvironments()).thenReturn(ImmutableMap.of(identity.getEnvName(), environment));
|
||||
|
||||
WorkspaceImpl workspace = mock(WorkspaceImpl.class);
|
||||
when(workspace.getConfig()).thenReturn(config);
|
||||
when(workspace.getId()).thenReturn(identity.getWorkspaceId());
|
||||
|
||||
when(workspaceDao.get(identity.getWorkspaceId())).thenReturn(workspace);
|
||||
|
||||
return workspace;
|
||||
}
|
||||
|
||||
private static class TestInfrastructure extends RuntimeInfrastructure {
|
||||
public TestInfrastructure(EventService eventService) {
|
||||
super("test", Collections.singleton("test"), eventService);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Environment estimate(Environment environment) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public RuntimeContext prepare(RuntimeIdentity id, Environment environment) throws InfrastructureException {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
}
|
||||
|
||||
private static class TestInternalRuntime extends InternalRuntime<RuntimeContext> {
|
||||
final Map<String, Machine> machines;
|
||||
|
||||
TestInternalRuntime(RuntimeContext context, Map<String, Machine> machines) {
|
||||
super(context, null, false);
|
||||
this.machines = machines;
|
||||
}
|
||||
|
||||
TestInternalRuntime(RuntimeContext context) {
|
||||
this(context, Collections.emptyMap());
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Map<String, Machine> getInternalMachines() {
|
||||
return machines;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<String, String> getProperties() {
|
||||
return Collections.emptyMap();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void internalStop(Map stopOptions) throws InfrastructureException {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void internalStart(Map startOptions) throws InfrastructureException {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -175,7 +175,11 @@ public class CascadeRemovalTest {
|
|||
// install(new MachineJpaModule());
|
||||
bind(WorkspaceManager.class);
|
||||
|
||||
WorkspaceRuntimes wR = spy(new WorkspaceRuntimes(mock(EventService.class), Collections.emptySet(), mock(WorkspaceSharedPool.class)));
|
||||
WorkspaceRuntimes wR = spy(new WorkspaceRuntimes(mock(EventService.class),
|
||||
Collections.emptySet(),
|
||||
mock(WorkspaceSharedPool.class),
|
||||
mock(WorkspaceDao.class),
|
||||
mock(DBInitializer.class)));
|
||||
when(wR.hasRuntime(anyString())).thenReturn(false);
|
||||
bind(WorkspaceRuntimes.class).toInstance(wR);
|
||||
bind(AccountManager.class);
|
||||
|
|
|
|||
Loading…
Reference in New Issue