Make a workspace run without any user env (#11890)

Allows running Che 7 workspace without any user environment but with Che 7 tooling set.
Workspace without environment has an empty list of environments and null value in `defaultEnv` field.
Field `activeEnv` is supposed to be `null` too.
What is changed:
- Migration of DB
- make defaultEnv nullable
- remove env_name from runtimes primary key
- make env_name in runtimes nullable
- Make code respect the fact that there are workspaces with no env
- settings API that returns the list of supported environment types returns no-environment type that - indicates that infrastructure support workspace with no environment at all.
Signed-off-by: Oleksandr Garagatyi <ogaragat@redhat.com>
6.19.x
Oleksandr Garagatyi 2018-11-21 16:39:54 +02:00 committed by GitHub
parent 8fe1172cb1
commit aaa8f424cd
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
55 changed files with 632 additions and 299 deletions

View File

@ -70,7 +70,6 @@
<class>org.eclipse.che.multiuser.machine.authentication.server.signature.model.impl.SignatureKeyPairImpl</class>
<class>org.eclipse.che.workspace.infrastructure.kubernetes.model.KubernetesRuntimeState</class>
<class>org.eclipse.che.workspace.infrastructure.kubernetes.model.KubernetesRuntimeState.RuntimeId</class>
<class>org.eclipse.che.workspace.infrastructure.kubernetes.model.KubernetesMachineImpl</class>
<class>org.eclipse.che.workspace.infrastructure.kubernetes.model.KubernetesMachineImpl.MachineId</class>

View File

@ -14,6 +14,7 @@ package org.eclipse.che.api.core.model.workspace;
import java.util.List;
import java.util.Map;
import org.eclipse.che.api.core.model.workspace.runtime.Machine;
import org.eclipse.che.commons.annotation.Nullable;
/**
* Defines a contract for workspace runtime.
@ -34,6 +35,7 @@ public interface Runtime {
* Returns an active environment name. The environment with such name must exist in {@link
* WorkspaceConfig#getEnvironments()}.
*/
@Nullable
String getActiveEnv();
/**

View File

@ -37,6 +37,7 @@ public interface WorkspaceConfig {
* Returns default environment name. It is mandatory, implementation should guarantee that
* environment with returned name exists for current workspace config.
*/
@Nullable
String getDefaultEnv();
/**

View File

@ -11,10 +11,13 @@
*/
package org.eclipse.che.api.core.model.workspace.runtime;
import org.eclipse.che.commons.annotation.Nullable;
/** @author gazarenkov */
public interface RuntimeIdentity {
String getWorkspaceId();
@Nullable
String getEnvName();
String getOwnerId();

View File

@ -52,6 +52,7 @@ import org.eclipse.che.ide.api.selection.SelectionChangedHandler;
import org.eclipse.che.ide.api.workspace.WorkspaceReadyEvent;
import org.eclipse.che.ide.api.workspace.WsAgentServerUtil;
import org.eclipse.che.ide.api.workspace.event.WorkspaceStoppedEvent;
import org.eclipse.che.ide.api.workspace.model.EnvironmentImpl;
import org.eclipse.che.ide.api.workspace.model.ServerImpl;
import org.eclipse.che.ide.api.workspace.model.VolumeImpl;
import org.eclipse.che.ide.api.workspace.model.WorkspaceImpl;
@ -286,17 +287,16 @@ public class AppContextImpl
wsAgentServerUtilProvider.get().getWsAgentServerMachine().get().getName();
String activeEnv = workspace.getRuntime().getActiveEnv();
VolumeImpl vol =
workspace
.getConfig()
.getEnvironments()
.get(activeEnv)
.getMachines()
.get(machineName)
.getVolume("projects");
EnvironmentImpl environment = workspace.getConfig().getEnvironments().get(activeEnv);
VolumeImpl vol = null;
if (environment != null) {
vol = environment.getMachines().get(machineName).getVolume("projects");
}
// if voulme exists return its path, otherwise use backward compatible path (/projects)
if (vol != null) projectsRoot = Path.valueOf(vol.getPath());
if (vol != null) {
projectsRoot = Path.valueOf(vol.getPath());
}
}
Log.debug(

View File

@ -162,6 +162,9 @@ public class WorkspaceLoadingTrackerImpl
EnvironmentImpl defaultEnvironment =
appContext.getWorkspace().getConfig().getEnvironments().get(defaultEnvironmentName);
if (defaultEnvironment == null) {
return;
}
Map<String, MachineConfigImpl> environmentMachines = defaultEnvironment.getMachines();
for (final String machineName : environmentMachines.keySet()) {
MachineConfigImpl machineConfig = environmentMachines.get(machineName);
@ -178,8 +181,10 @@ public class WorkspaceLoadingTrackerImpl
EnvironmentImpl defaultEnvironment =
appContext.getWorkspace().getConfig().getEnvironments().get(defaultEnvironmentName);
if (defaultEnvironment == null) {
return;
}
Map<String, MachineConfigImpl> machines = defaultEnvironment.getMachines();
for (final String machineName : machines.keySet()) {
MachineConfigImpl machineConfig = machines.get(machineName);
view.addMachine(machineName);
@ -219,6 +224,9 @@ public class WorkspaceLoadingTrackerImpl
EnvironmentImpl defaultEnvironment =
appContext.getWorkspace().getConfig().getEnvironments().get(defaultEnvironmentName);
if (defaultEnvironment == null) {
return;
}
Map<String, MachineConfigImpl> machines = defaultEnvironment.getMachines();
for (final String machineName : machines.keySet()) {
MachineConfigImpl machineConfig = machines.get(machineName);

View File

@ -57,6 +57,10 @@ public class ContextBasedRuntimeInfoProvider implements RuntimeInfoProvider {
return emptyList();
}
if (workspace.getConfig().getDefaultEnv() == null) {
return emptyList();
}
// map with servers where probably port is set
MachineConfigImpl preConfiguredRuntime =
workspace

View File

@ -23,6 +23,7 @@ import org.eclipse.che.api.promises.client.Function;
import org.eclipse.che.api.promises.client.Promise;
import org.eclipse.che.api.workspace.shared.dto.CommandDto;
import org.eclipse.che.api.workspace.shared.dto.WorkspaceDto;
import org.eclipse.che.commons.annotation.Nullable;
import org.eclipse.che.ide.api.app.AppContext;
import org.eclipse.che.ide.api.command.CommandImpl;
import org.eclipse.che.ide.api.workspace.model.WorkspaceImpl;
@ -88,8 +89,11 @@ public class WorkspaceServiceClient {
* @param envName the name of the workspace environment that should be used for start
* @return a promise that resolves to the {@link WorkspaceImpl}, or rejects with an error
*/
Promise<WorkspaceImpl> startById(String id, String envName) {
String url = baseHttpUrl + "/" + id + "/runtime" + "?environment=" + envName;
Promise<WorkspaceImpl> startById(String id, @Nullable String envName) {
String url = baseHttpUrl + "/" + id + "/runtime";
if (envName != null) {
url += "?environment=" + envName;
}
return asyncRequestFactory
.createPostRequest(url, null)

View File

@ -71,6 +71,10 @@
<groupId>org.eclipse.che.core</groupId>
<artifactId>che-core-api-workspace</artifactId>
</dependency>
<dependency>
<groupId>org.eclipse.che.core</groupId>
<artifactId>che-core-commons-annotations</artifactId>
</dependency>
<dependency>
<groupId>org.eclipse.che.core</groupId>
<artifactId>che-core-commons-lang</artifactId>

View File

@ -11,7 +11,6 @@
*/
package org.eclipse.che.workspace.infrastructure.docker.environment.compose;
import static com.google.common.base.Preconditions.checkNotNull;
import static java.lang.String.format;
import static java.util.stream.Collectors.joining;
@ -30,7 +29,13 @@ import org.eclipse.che.api.core.ValidationException;
import org.eclipse.che.api.core.model.workspace.Warning;
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.environment.*;
import org.eclipse.che.api.workspace.server.spi.environment.InternalEnvironmentFactory;
import org.eclipse.che.api.workspace.server.spi.environment.InternalMachineConfig;
import org.eclipse.che.api.workspace.server.spi.environment.InternalRecipe;
import org.eclipse.che.api.workspace.server.spi.environment.MachineConfigsValidator;
import org.eclipse.che.api.workspace.server.spi.environment.MemoryAttributeProvisioner;
import org.eclipse.che.api.workspace.server.spi.environment.RecipeRetriever;
import org.eclipse.che.commons.annotation.Nullable;
import org.eclipse.che.workspace.infrastructure.docker.environment.compose.model.ComposeRecipe;
import org.eclipse.che.workspace.infrastructure.docker.environment.compose.model.ComposeService;
@ -63,8 +68,11 @@ public class ComposeEnvironmentFactory extends InternalEnvironmentFactory<Compos
@Override
protected ComposeEnvironment doCreate(
InternalRecipe recipe, Map<String, InternalMachineConfig> machines, List<Warning> warnings)
@Nullable InternalRecipe recipe,
Map<String, InternalMachineConfig> machines,
List<Warning> warnings)
throws InfrastructureException, ValidationException {
checkNotNull(recipe, "Null recipe is not supported by compose environment factory");
String contentType = recipe.getContentType();
checkNotNull(contentType, "Recipe content type should not be null");

View File

@ -23,6 +23,7 @@ import org.eclipse.che.api.core.model.workspace.Warning;
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.environment.*;
import org.eclipse.che.commons.annotation.Nullable;
/** @author Sergii Leshchenko */
@Singleton
@ -43,8 +44,11 @@ public class DockerfileEnvironmentFactory
@Override
protected DockerfileEnvironment doCreate(
InternalRecipe recipe, Map<String, InternalMachineConfig> machines, List<Warning> warnings)
@Nullable InternalRecipe recipe,
Map<String, InternalMachineConfig> machines,
List<Warning> warnings)
throws InfrastructureException, ValidationException {
checkNotNull(recipe, "Null recipe is not supported by docker file environment factory");
if (!DockerfileEnvironment.TYPE.equals(recipe.getType())) {
throw new ValidationException(
format(
@ -66,4 +70,12 @@ public class DockerfileEnvironmentFactory
memoryProvisioner.provision(machineConfig, 0L, 0L);
}
}
private static void checkNotNull(
Object object, String errorMessageTemplate, Object... errorMessageParams)
throws ValidationException {
if (object == null) {
throw new ValidationException(format(errorMessageTemplate, errorMessageParams));
}
}
}

View File

@ -25,6 +25,7 @@ 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.environment.*;
import org.eclipse.che.commons.annotation.Nullable;
/** @author Sergii Leshchenko */
@Singleton
@ -46,6 +47,8 @@ public class DockerImageEnvironmentFactory
@Override
public DockerImageEnvironment create(Environment sourceEnv)
throws InfrastructureException, ValidationException {
checkNotNull(
sourceEnv, "Null environment is not supported by docker image environment factory");
if (sourceEnv.getRecipe().getLocation() != null) {
// move image from location to content
EnvironmentImpl envCopy = new EnvironmentImpl(sourceEnv);
@ -58,8 +61,11 @@ public class DockerImageEnvironmentFactory
@Override
protected DockerImageEnvironment doCreate(
InternalRecipe recipe, Map<String, InternalMachineConfig> machines, List<Warning> warnings)
@Nullable InternalRecipe recipe,
Map<String, InternalMachineConfig> machines,
List<Warning> warnings)
throws InfrastructureException, ValidationException {
checkNotNull(recipe, "Null recipe is not supported by docker image environment factory");
if (!DockerImageEnvironment.TYPE.equals(recipe.getType())) {
throw new ValidationException(
format(
@ -81,4 +87,12 @@ public class DockerImageEnvironmentFactory
memoryProvisioner.provision(machineConfig, 0L, 0L);
}
}
private static void checkNotNull(
Object object, String errorMessageTemplate, Object... errorMessageParams)
throws ValidationException {
if (object == null) {
throw new ValidationException(format(errorMessageTemplate, errorMessageParams));
}
}
}

View File

@ -26,12 +26,14 @@ import com.google.inject.multibindings.Multibinder;
import com.google.inject.name.Names;
import java.util.Map;
import org.eclipse.che.api.system.server.ServiceTermination;
import org.eclipse.che.api.workspace.server.NoEnvironmentFactory;
import org.eclipse.che.api.workspace.server.spi.RuntimeInfrastructure;
import org.eclipse.che.api.workspace.server.spi.environment.InternalEnvironmentFactory;
import org.eclipse.che.api.workspace.server.spi.provision.env.CheApiExternalEnvVarProvider;
import org.eclipse.che.api.workspace.server.spi.provision.env.CheApiInternalEnvVarProvider;
import org.eclipse.che.api.workspace.server.spi.provision.env.EnvVarProvider;
import org.eclipse.che.api.workspace.server.wsplugins.ChePluginsApplier;
import org.eclipse.che.api.workspace.shared.Constants;
import org.eclipse.che.workspace.infrastructure.docker.environment.dockerimage.DockerImageEnvironment;
import org.eclipse.che.workspace.infrastructure.docker.environment.dockerimage.DockerImageEnvironmentFactory;
import org.eclipse.che.workspace.infrastructure.kubernetes.bootstrapper.KubernetesBootstrapperFactory;
@ -73,6 +75,7 @@ public class KubernetesInfraModule extends AbstractModule {
factories.addBinding(KubernetesEnvironment.TYPE).to(KubernetesEnvironmentFactory.class);
factories.addBinding(DockerImageEnvironment.TYPE).to(DockerImageEnvironmentFactory.class);
factories.addBinding(Constants.NO_ENVIRONMENT_RECIPE_TYPE).to(NoEnvironmentFactory.class);
bind(RuntimeInfrastructure.class).to(KubernetesInfrastructure.class);

View File

@ -20,11 +20,13 @@ import javax.inject.Inject;
import javax.inject.Singleton;
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.NoEnvironmentFactory.NoEnvInternalEnvironment;
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.RuntimeInfrastructure;
import org.eclipse.che.api.workspace.server.spi.environment.InternalEnvironment;
import org.eclipse.che.api.workspace.server.spi.provision.InternalEnvironmentProvisioner;
import org.eclipse.che.api.workspace.shared.Constants;
import org.eclipse.che.workspace.infrastructure.docker.environment.dockerimage.DockerImageEnvironment;
import org.eclipse.che.workspace.infrastructure.kubernetes.cache.KubernetesRuntimeStateCache;
import org.eclipse.che.workspace.infrastructure.kubernetes.environment.KubernetesEnvironment;
@ -49,7 +51,10 @@ public class KubernetesInfrastructure extends RuntimeInfrastructure {
KubernetesRuntimeStateCache runtimeStatusesCache) {
super(
NAME,
ImmutableSet.of(KubernetesEnvironment.TYPE, DockerImageEnvironment.TYPE),
ImmutableSet.of(
KubernetesEnvironment.TYPE,
DockerImageEnvironment.TYPE,
Constants.NO_ENVIRONMENT_RECIPE_TYPE),
eventService,
internalEnvProvisioners);
this.runtimeContextFactory = runtimeContextFactory;
@ -70,10 +75,14 @@ public class KubernetesInfrastructure extends RuntimeInfrastructure {
private KubernetesEnvironment asKubernetesEnv(InternalEnvironment source)
throws InfrastructureException {
if (source instanceof KubernetesEnvironment) {
if (source instanceof NoEnvInternalEnvironment) {
return KubernetesEnvironment.builder()
.setAttributes(source.getAttributes())
.setWarnings(source.getWarnings())
.build();
} else if (source instanceof KubernetesEnvironment) {
return (KubernetesEnvironment) source;
}
if (source instanceof DockerImageEnvironment) {
} else if (source instanceof DockerImageEnvironment) {
return dockerImageEnvConverter.convert((DockerImageEnvironment) source);
}

View File

@ -24,7 +24,6 @@ import javax.inject.Provider;
import javax.inject.Singleton;
import javax.persistence.EntityExistsException;
import javax.persistence.EntityManager;
import javax.persistence.NoResultException;
import org.eclipse.che.api.core.ServerException;
import org.eclipse.che.api.core.model.workspace.WorkspaceStatus;
import org.eclipse.che.api.core.model.workspace.runtime.RuntimeIdentity;
@ -36,7 +35,6 @@ import org.eclipse.che.core.db.jpa.DuplicateKeyException;
import org.eclipse.che.workspace.infrastructure.kubernetes.cache.BeforeKubernetesRuntimeStateRemovedEvent;
import org.eclipse.che.workspace.infrastructure.kubernetes.cache.KubernetesRuntimeStateCache;
import org.eclipse.che.workspace.infrastructure.kubernetes.model.KubernetesRuntimeState;
import org.eclipse.che.workspace.infrastructure.kubernetes.model.KubernetesRuntimeState.RuntimeId;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@ -109,7 +107,7 @@ public class JpaKubernetesRuntimeStateCache implements KubernetesRuntimeStateCac
throws InfrastructureException {
try {
return Optional.ofNullable(
managerProvider.get().find(KubernetesRuntimeState.class, new RuntimeId(runtimeId)));
managerProvider.get().find(KubernetesRuntimeState.class, runtimeId.getWorkspaceId()));
} catch (RuntimeException x) {
throw new InfrastructureException(x.getMessage(), x);
}
@ -152,15 +150,8 @@ public class JpaKubernetesRuntimeStateCache implements KubernetesRuntimeStateCac
protected Optional<KubernetesRuntimeState> find(String workspaceId)
throws InfrastructureException {
try {
KubernetesRuntimeState queried =
managerProvider
.get()
.createNamedQuery("KubernetesRuntime.getByWorkspaceId", KubernetesRuntimeState.class)
.setParameter("workspaceId", workspaceId)
.getSingleResult();
return Optional.of(queried);
} catch (NoResultException e) {
return Optional.empty();
return Optional.ofNullable(
managerProvider.get().find(KubernetesRuntimeState.class, workspaceId));
} catch (RuntimeException x) {
throw new InfrastructureException(x.getMessage(), x);
}
@ -171,7 +162,7 @@ public class JpaKubernetesRuntimeStateCache implements KubernetesRuntimeStateCac
EntityManager em = managerProvider.get();
KubernetesRuntimeState runtime =
em.find(KubernetesRuntimeState.class, new RuntimeId(runtimeIdentity));
em.find(KubernetesRuntimeState.class, runtimeIdentity.getWorkspaceId());
if (runtime != null) {
eventService
@ -245,7 +236,7 @@ public class JpaKubernetesRuntimeStateCache implements KubernetesRuntimeStateCac
k8sRuntimes.find(event.getWorkspace().getId());
if (k8sRuntimeStateOpt.isPresent()) {
KubernetesRuntimeState existingK8sRuntimeState = k8sRuntimeStateOpt.get();
RuntimeId runtimeId = existingK8sRuntimeState.getRuntimeId();
RuntimeIdentity runtimeId = existingK8sRuntimeState.getRuntimeId();
// It is not normal case when non STOPPED workspace is going to be removed.
// Need to log error to investigate why it may happen

View File

@ -36,6 +36,7 @@ import org.eclipse.che.api.installer.server.InstallerRegistry;
import org.eclipse.che.api.workspace.server.model.impl.WarningImpl;
import org.eclipse.che.api.workspace.server.spi.InfrastructureException;
import org.eclipse.che.api.workspace.server.spi.environment.*;
import org.eclipse.che.commons.annotation.Nullable;
import org.eclipse.che.workspace.infrastructure.kubernetes.KubernetesClientFactory;
import org.eclipse.che.workspace.infrastructure.kubernetes.Names;
import org.eclipse.che.workspace.infrastructure.kubernetes.util.Containers;
@ -85,10 +86,11 @@ public class KubernetesEnvironmentFactory
@Override
protected KubernetesEnvironment doCreate(
InternalRecipe recipe,
@Nullable InternalRecipe recipe,
Map<String, InternalMachineConfig> machines,
List<Warning> sourceWarnings)
throws InfrastructureException, ValidationException {
checkNotNull(recipe, "Null recipe is not supported by kubernetes environment factory");
List<Warning> warnings = new ArrayList<>();
if (sourceWarnings != null) {
warnings.addAll(sourceWarnings);

View File

@ -13,26 +13,31 @@ package org.eclipse.che.workspace.infrastructure.kubernetes.model;
import java.util.Objects;
import javax.persistence.Column;
import javax.persistence.Embeddable;
import javax.persistence.EmbeddedId;
import javax.persistence.Entity;
import javax.persistence.Id;
import javax.persistence.NamedQueries;
import javax.persistence.NamedQuery;
import javax.persistence.Table;
import org.eclipse.che.api.core.model.workspace.WorkspaceStatus;
import org.eclipse.che.api.core.model.workspace.runtime.RuntimeIdentity;
import org.eclipse.che.api.workspace.server.model.impl.RuntimeIdentityImpl;
/** @author Sergii Leshchenko */
@Entity(name = "KubernetesRuntime")
@Table(name = "che_k8s_runtime")
@NamedQueries({
@NamedQuery(name = "KubernetesRuntime.getAll", query = "SELECT r FROM KubernetesRuntime r"),
@NamedQuery(
name = "KubernetesRuntime.getByWorkspaceId",
query = "SELECT r FROM KubernetesRuntime r WHERE r.runtimeId.workspaceId = :workspaceId")
@NamedQuery(name = "KubernetesRuntime.getAll", query = "SELECT r FROM KubernetesRuntime r")
})
public class KubernetesRuntimeState {
@EmbeddedId private RuntimeId runtimeId;
@Id
@Column(name = "workspace_id")
private String workspaceId;
@Column(name = "env_name")
private String envName;
@Column(name = "owner_id")
private String ownerId;
@Column(name = "namespace")
private String namespace;
@ -44,11 +49,9 @@ public class KubernetesRuntimeState {
public KubernetesRuntimeState(
RuntimeIdentity runtimeIdentity, String namespace, WorkspaceStatus status) {
this.runtimeId =
new RuntimeId(
runtimeIdentity.getWorkspaceId(),
runtimeIdentity.getEnvName(),
runtimeIdentity.getOwnerId());
this.envName = runtimeIdentity.getEnvName();
this.workspaceId = runtimeIdentity.getWorkspaceId();
this.ownerId = runtimeIdentity.getOwnerId();
this.namespace = namespace;
this.status = status;
}
@ -61,8 +64,8 @@ public class KubernetesRuntimeState {
return namespace;
}
public RuntimeId getRuntimeId() {
return runtimeId;
public RuntimeIdentity getRuntimeId() {
return new RuntimeIdentityImpl(workspaceId, envName, ownerId);
}
public WorkspaceStatus getStatus() {
@ -79,33 +82,38 @@ public class KubernetesRuntimeState {
}
@Override
public boolean equals(Object obj) {
if (this == obj) {
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (!(obj instanceof KubernetesRuntimeState)) {
if (!(o instanceof KubernetesRuntimeState)) {
return false;
}
final KubernetesRuntimeState that = (KubernetesRuntimeState) obj;
return Objects.equals(runtimeId, that.runtimeId)
&& Objects.equals(namespace, that.namespace)
&& Objects.equals(status, that.status);
KubernetesRuntimeState that = (KubernetesRuntimeState) o;
return Objects.equals(workspaceId, that.workspaceId)
&& Objects.equals(envName, that.envName)
&& Objects.equals(ownerId, that.ownerId)
&& Objects.equals(getNamespace(), that.getNamespace())
&& getStatus() == that.getStatus();
}
@Override
public int hashCode() {
int hash = 7;
hash = 31 * hash + Objects.hashCode(runtimeId);
hash = 31 * hash + Objects.hashCode(namespace);
hash = 31 * hash + Objects.hashCode(status);
return hash;
return Objects.hash(workspaceId, envName, ownerId, getNamespace(), getStatus());
}
@Override
public String toString() {
return "KubernetesRuntimeState{"
+ "runtimeId="
+ runtimeId
+ "workspaceId='"
+ workspaceId
+ '\''
+ ", envName='"
+ envName
+ '\''
+ ", ownerId='"
+ ownerId
+ '\''
+ ", namespace='"
+ namespace
+ '\''
@ -113,82 +121,4 @@ public class KubernetesRuntimeState {
+ status
+ '}';
}
@Embeddable
public static class RuntimeId implements RuntimeIdentity {
@Column(name = "workspace_id")
private String workspaceId;
@Column(name = "env_name")
private String envName;
@Column(name = "owner_id")
private String ownerId;
public RuntimeId() {}
public RuntimeId(String workspaceId, String envName, String ownerId) {
this.workspaceId = workspaceId;
this.envName = envName;
this.ownerId = ownerId;
}
public RuntimeId(RuntimeIdentity identity) {
this(identity.getWorkspaceId(), identity.getEnvName(), identity.getOwnerId());
}
@Override
public String getWorkspaceId() {
return workspaceId;
}
@Override
public String getEnvName() {
return envName;
}
@Override
public String getOwnerId() {
return ownerId;
}
@Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (!(obj instanceof RuntimeId)) {
return false;
}
final RuntimeId that = (RuntimeId) obj;
return Objects.equals(workspaceId, that.workspaceId)
&& Objects.equals(envName, that.envName)
&& Objects.equals(ownerId, that.ownerId);
}
@Override
public int hashCode() {
int hash = 7;
hash = 31 * hash + Objects.hashCode(workspaceId);
hash = 31 * hash + Objects.hashCode(envName);
hash = 31 * hash + Objects.hashCode(ownerId);
return hash;
}
@Override
public String toString() {
return "RuntimeId{"
+ "workspaceId='"
+ workspaceId
+ '\''
+ ", envName='"
+ envName
+ '\''
+ ", ownerId='"
+ ownerId
+ '\''
+ '}';
}
}
}

View File

@ -19,6 +19,7 @@ import com.google.common.collect.Sets;
import io.fabric8.kubernetes.api.model.Container;
import io.fabric8.kubernetes.api.model.EnvVar;
import io.fabric8.kubernetes.api.model.Pod;
import io.fabric8.kubernetes.api.model.PodBuilder;
import io.fabric8.kubernetes.api.model.Service;
import java.util.Collection;
import java.util.List;
@ -49,6 +50,8 @@ public class KubernetesPluginsToolingApplier implements ChePluginsApplier {
private static final Set<String> validImagePullPolicies =
Sets.newHashSet("Always", "Never", "IfNotPresent");
private static final String CHE_WORKSPACE_POD = "che-workspace-pod";
private final String defaultSidecarMemoryLimitBytes;
private final String sidecarImagePullPolicy;
private final boolean isAuthEnabled;
@ -74,9 +77,15 @@ public class KubernetesPluginsToolingApplier implements ChePluginsApplier {
KubernetesEnvironment kubernetesEnvironment = (KubernetesEnvironment) internalEnvironment;
Map<String, Pod> pods = kubernetesEnvironment.getPods();
if (pods.size() != 1) {
throw new InfrastructureException(
"Che plugins tooling configuration can be applied to a workspace with one pod only");
switch (pods.size()) {
case 0:
addToolingPod(kubernetesEnvironment);
break;
case 1:
break;
default:
throw new InfrastructureException(
"Che plugins tooling configuration can be applied to a workspace with one pod only");
}
Pod pod = pods.values().iterator().next();
@ -94,6 +103,19 @@ public class KubernetesPluginsToolingApplier implements ChePluginsApplier {
}
}
private void addToolingPod(KubernetesEnvironment kubernetesEnvironment) {
Pod pod =
new PodBuilder()
.withNewMetadata()
.withName(CHE_WORKSPACE_POD)
.endMetadata()
.withNewSpec()
.endSpec()
.build();
kubernetesEnvironment.getPods().put(CHE_WORKSPACE_POD, pod);
}
private void populateWorkspaceEnvVars(
ChePlugin chePlugin, KubernetesEnvironment kubernetesEnvironment) {

View File

@ -16,6 +16,7 @@ import static java.lang.String.format;
import static java.util.Collections.singletonMap;
import com.google.common.annotations.Beta;
import com.google.common.base.MoreObjects;
import com.google.common.collect.ArrayListMultimap;
import com.google.common.collect.Multimap;
import com.google.gson.Gson;
@ -166,7 +167,9 @@ public abstract class BrokerEnvironmentFactory<E extends KubernetesEnvironment>
"-runtime-id",
String.format(
"%s:%s:%s",
runtimeId.getWorkspaceId(), runtimeId.getEnvName(), runtimeId.getOwnerId()))
runtimeId.getWorkspaceId(),
MoreObjects.firstNonNull(runtimeId.getEnvName(), ""),
runtimeId.getOwnerId()))
.withImagePullPolicy(brokerPullPolicy)
.withVolumeMounts(
new VolumeMount(CONF_FOLDER + "/", null, brokerVolumeName, true, null))

View File

@ -110,7 +110,6 @@ import org.eclipse.che.workspace.infrastructure.kubernetes.environment.Kubernete
import org.eclipse.che.workspace.infrastructure.kubernetes.model.KubernetesMachineImpl;
import org.eclipse.che.workspace.infrastructure.kubernetes.model.KubernetesMachineImpl.MachineId;
import org.eclipse.che.workspace.infrastructure.kubernetes.model.KubernetesRuntimeState;
import org.eclipse.che.workspace.infrastructure.kubernetes.model.KubernetesRuntimeState.RuntimeId;
import org.eclipse.che.workspace.infrastructure.kubernetes.model.KubernetesServerImpl;
import org.eclipse.che.workspace.infrastructure.kubernetes.namespace.KubernetesConfigsMaps;
import org.eclipse.che.workspace.infrastructure.kubernetes.namespace.KubernetesDeployments;
@ -859,7 +858,7 @@ public class KubernetesInternalRuntimeTest {
}
private static class MapBasedRuntimeStateCache implements KubernetesRuntimeStateCache {
private Map<RuntimeId, KubernetesRuntimeState> runtimesStates = new HashMap<>();
private Map<RuntimeIdentity, KubernetesRuntimeState> runtimesStates = new HashMap<>();
@Override
public Set<RuntimeIdentity> getIdentities() throws InfrastructureException {
@ -874,14 +873,14 @@ public class KubernetesInternalRuntimeTest {
@Override
public void updateStatus(RuntimeIdentity runtimeId, WorkspaceStatus newStatus)
throws InfrastructureException {
runtimesStates.get(new RuntimeId(runtimeId)).setStatus(newStatus);
runtimesStates.get(new RuntimeIdentityImpl(runtimeId)).setStatus(newStatus);
}
@Override
public boolean updateStatus(
RuntimeIdentity identity, Predicate<WorkspaceStatus> predicate, WorkspaceStatus newStatus)
throws InfrastructureException {
KubernetesRuntimeState state = runtimesStates.get(new RuntimeId(identity));
KubernetesRuntimeState state = runtimesStates.get(new RuntimeIdentityImpl(identity));
if (predicate.test(state.getStatus())) {
state.setStatus(newStatus);
return true;
@ -891,18 +890,18 @@ public class KubernetesInternalRuntimeTest {
@Override
public WorkspaceStatus getStatus(RuntimeIdentity runtimeId) throws InfrastructureException {
return runtimesStates.get(new RuntimeId(runtimeId)).getStatus();
return runtimesStates.get(new RuntimeIdentityImpl(runtimeId)).getStatus();
}
@Override
public Optional<KubernetesRuntimeState> get(RuntimeIdentity runtimeId)
throws InfrastructureException {
return Optional.ofNullable(runtimesStates.get(new RuntimeId(runtimeId)));
return Optional.ofNullable(runtimesStates.get(new RuntimeIdentityImpl(runtimeId)));
}
@Override
public void remove(RuntimeIdentity runtimeId) throws InfrastructureException {
runtimesStates.remove(new RuntimeId(runtimeId));
runtimesStates.remove(new RuntimeIdentityImpl(runtimeId));
}
}

View File

@ -38,7 +38,6 @@ import org.eclipse.che.workspace.infrastructure.kubernetes.cache.KubernetesRunti
import org.eclipse.che.workspace.infrastructure.kubernetes.model.KubernetesMachineImpl;
import org.eclipse.che.workspace.infrastructure.kubernetes.model.KubernetesMachineImpl.MachineId;
import org.eclipse.che.workspace.infrastructure.kubernetes.model.KubernetesRuntimeState;
import org.eclipse.che.workspace.infrastructure.kubernetes.model.KubernetesRuntimeState.RuntimeId;
import org.eclipse.che.workspace.infrastructure.kubernetes.model.KubernetesServerImpl;
import org.eclipse.che.workspace.infrastructure.kubernetes.model.KubernetesServerImpl.ServerId;
import org.h2.Driver;
@ -65,7 +64,6 @@ public class JpaTckModule extends TckModule {
CommandImpl.class,
AccountImpl.class,
KubernetesRuntimeState.class,
RuntimeId.class,
KubernetesMachineImpl.class,
MachineId.class,
KubernetesServerImpl.class,

View File

@ -12,7 +12,10 @@
package org.eclipse.che.workspace.infrastructure.kubernetes.cache.tck;
import static java.util.Arrays.asList;
import static org.eclipse.che.workspace.infrastructure.kubernetes.cache.tck.TestObjects.*;
import static org.eclipse.che.workspace.infrastructure.kubernetes.cache.tck.TestObjects.createMachine;
import static org.eclipse.che.workspace.infrastructure.kubernetes.cache.tck.TestObjects.createRuntimeState;
import static org.eclipse.che.workspace.infrastructure.kubernetes.cache.tck.TestObjects.createServer;
import static org.eclipse.che.workspace.infrastructure.kubernetes.cache.tck.TestObjects.createWorkspace;
import static org.testng.Assert.assertEquals;
import static org.testng.Assert.assertTrue;
@ -23,6 +26,7 @@ import java.util.Optional;
import javax.inject.Inject;
import org.eclipse.che.account.spi.AccountImpl;
import org.eclipse.che.api.core.model.workspace.runtime.MachineStatus;
import org.eclipse.che.api.core.model.workspace.runtime.RuntimeIdentity;
import org.eclipse.che.api.core.model.workspace.runtime.ServerStatus;
import org.eclipse.che.api.workspace.server.model.impl.WorkspaceImpl;
import org.eclipse.che.api.workspace.server.spi.InfrastructureException;
@ -32,7 +36,6 @@ import org.eclipse.che.commons.test.tck.repository.TckRepositoryException;
import org.eclipse.che.workspace.infrastructure.kubernetes.cache.KubernetesMachineCache;
import org.eclipse.che.workspace.infrastructure.kubernetes.model.KubernetesMachineImpl;
import org.eclipse.che.workspace.infrastructure.kubernetes.model.KubernetesRuntimeState;
import org.eclipse.che.workspace.infrastructure.kubernetes.model.KubernetesRuntimeState.RuntimeId;
import org.eclipse.che.workspace.infrastructure.kubernetes.model.KubernetesServerImpl;
import org.testng.annotations.AfterMethod;
import org.testng.annotations.BeforeMethod;
@ -150,7 +153,7 @@ public class KubernetesMachinesCacheTest {
@Test
public void shouldGetMachines() throws Exception {
// given
RuntimeId runtimeId = runtimeStates[0].getRuntimeId();
RuntimeIdentity runtimeId = runtimeStates[0].getRuntimeId();
// when
machineCache.getMachines(runtimeId);
@ -165,7 +168,7 @@ public class KubernetesMachinesCacheTest {
@Test
public void shouldGetServer() throws Exception {
// given
RuntimeId runtimeId = runtimeStates[0].getRuntimeId();
RuntimeIdentity runtimeId = runtimeStates[0].getRuntimeId();
KubernetesMachineImpl machine = machines[0];
Entry<String, KubernetesServerImpl> serverToFetch =
machine.getServers().entrySet().iterator().next();
@ -183,7 +186,7 @@ public class KubernetesMachinesCacheTest {
expectedExceptionsMessageRegExp = "Server with name 'non-existing' was not found")
public void shouldThrowExceptionWhenServerWasNotFoundOnGetting() throws Exception {
// given
RuntimeId runtimeId = runtimeStates[0].getRuntimeId();
RuntimeIdentity runtimeId = runtimeStates[0].getRuntimeId();
KubernetesMachineImpl machine = machines[0];
// when
@ -193,7 +196,7 @@ public class KubernetesMachinesCacheTest {
@Test
public void shouldUpdateMachineStatusServerStatus() throws Exception {
// given
RuntimeId runtimeId = runtimeStates[0].getRuntimeId();
RuntimeIdentity runtimeId = runtimeStates[0].getRuntimeId();
// when
machineCache.updateServerStatus(
@ -209,7 +212,7 @@ public class KubernetesMachinesCacheTest {
expectedExceptionsMessageRegExp = "Server with name 'non-existing' was not found")
public void shouldThrowExceptionWhenServerWasNotFoundOnStatusUpdating() throws Exception {
// given
RuntimeId runtimeId = runtimeStates[0].getRuntimeId();
RuntimeIdentity runtimeId = runtimeStates[0].getRuntimeId();
KubernetesMachineImpl machine = machines[0];
// when
@ -220,7 +223,7 @@ public class KubernetesMachinesCacheTest {
@Test
public void shouldUpdateMachineStatus() throws Exception {
// given
RuntimeId runtimeId = runtimeStates[0].getRuntimeId();
RuntimeIdentity runtimeId = runtimeStates[0].getRuntimeId();
KubernetesMachineImpl machine = machines[0];
String machineName = machine.getName();
@ -243,7 +246,7 @@ public class KubernetesMachinesCacheTest {
@Test
public void shouldUpdateServerStatus() throws Exception {
// given
RuntimeId runtimeId = runtimeStates[0].getRuntimeId();
RuntimeIdentity runtimeId = runtimeStates[0].getRuntimeId();
// when
machineCache.updateServerStatus(
@ -257,7 +260,7 @@ public class KubernetesMachinesCacheTest {
@Test
public void shouldRemoveMachines() throws Exception {
// given
RuntimeId runtimeId = runtimeStates[0].getRuntimeId();
RuntimeIdentity runtimeId = runtimeStates[0].getRuntimeId();
// when
machineCache.remove(runtimeId);

View File

@ -25,6 +25,7 @@ import javax.inject.Inject;
import org.eclipse.che.account.spi.AccountImpl;
import org.eclipse.che.api.core.model.workspace.WorkspaceStatus;
import org.eclipse.che.api.core.model.workspace.runtime.RuntimeIdentity;
import org.eclipse.che.api.workspace.server.model.impl.RuntimeIdentityImpl;
import org.eclipse.che.api.workspace.server.model.impl.WorkspaceImpl;
import org.eclipse.che.api.workspace.server.spi.InfrastructureException;
import org.eclipse.che.commons.test.tck.TckListener;
@ -32,7 +33,6 @@ import org.eclipse.che.commons.test.tck.repository.TckRepository;
import org.eclipse.che.commons.test.tck.repository.TckRepositoryException;
import org.eclipse.che.workspace.infrastructure.kubernetes.cache.KubernetesRuntimeStateCache;
import org.eclipse.che.workspace.infrastructure.kubernetes.model.KubernetesRuntimeState;
import org.eclipse.che.workspace.infrastructure.kubernetes.model.KubernetesRuntimeState.RuntimeId;
import org.testng.annotations.AfterMethod;
import org.testng.annotations.BeforeMethod;
import org.testng.annotations.Listeners;
@ -140,7 +140,7 @@ public class KubernetesRuntimeStateCacheTest {
public void shouldThrowExceptionWhenThereIsNotRuntimeStateWhileStatusRetrieving()
throws Exception {
// when
runtimesStatesCache.getStatus(new RuntimeId("non-existent-ws", "defEnv", "acc1"));
runtimesStatesCache.getStatus(new RuntimeIdentityImpl("non-existent-ws", "defEnv", "acc1"));
}
@Test(dependsOnMethods = "shouldReturnRuntimeStatus")
@ -203,7 +203,7 @@ public class KubernetesRuntimeStateCacheTest {
throws Exception {
// when
runtimesStatesCache.updateStatus(
new RuntimeId("non-existent-ws", "defEnv", "acc1"), WorkspaceStatus.STOPPED);
new RuntimeIdentityImpl("non-existent-ws", "defEnv", "acc1"), WorkspaceStatus.STOPPED);
}
@Test(
@ -214,7 +214,7 @@ public class KubernetesRuntimeStateCacheTest {
throws Exception {
// when
runtimesStatesCache.updateStatus(
new RuntimeId("non-existent-ws", "defEnv", "acc1"),
new RuntimeIdentityImpl("non-existent-ws", "defEnv", "acc1"),
s -> s.equals(WorkspaceStatus.STOPPING),
WorkspaceStatus.STOPPED);
}
@ -255,7 +255,7 @@ public class KubernetesRuntimeStateCacheTest {
public void shouldRemoveRuntimeState() throws Exception {
// given
KubernetesRuntimeState runtimeState = createRuntimeState(workspaces[0]);
RuntimeId toRemove = runtimeState.getRuntimeId();
RuntimeIdentity toRemove = runtimeState.getRuntimeId();
// when
runtimesStatesCache.remove(toRemove);
@ -268,7 +268,7 @@ public class KubernetesRuntimeStateCacheTest {
public void shouldDoNothingIfStateIsAlreadyRemove() throws Exception {
// given
KubernetesRuntimeState runtimeState = createRuntimeState(workspaces[2]);
RuntimeId toRemove = runtimeState.getRuntimeId();
RuntimeIdentity toRemove = runtimeState.getRuntimeId();
// when
runtimesStatesCache.remove(toRemove);

View File

@ -21,12 +21,12 @@ import org.eclipse.che.account.spi.AccountImpl;
import org.eclipse.che.api.core.model.workspace.WorkspaceStatus;
import org.eclipse.che.api.core.model.workspace.runtime.MachineStatus;
import org.eclipse.che.api.core.model.workspace.runtime.ServerStatus;
import org.eclipse.che.api.workspace.server.model.impl.RuntimeIdentityImpl;
import org.eclipse.che.api.workspace.server.model.impl.ServerImpl;
import org.eclipse.che.api.workspace.server.model.impl.WorkspaceConfigImpl;
import org.eclipse.che.api.workspace.server.model.impl.WorkspaceImpl;
import org.eclipse.che.workspace.infrastructure.kubernetes.model.KubernetesMachineImpl;
import org.eclipse.che.workspace.infrastructure.kubernetes.model.KubernetesRuntimeState;
import org.eclipse.che.workspace.infrastructure.kubernetes.model.KubernetesRuntimeState.RuntimeId;
/** @author Sergii Leshchenko */
public class TestObjects {
@ -51,7 +51,7 @@ public class TestObjects {
public static KubernetesRuntimeState createRuntimeState(WorkspaceImpl workspace) {
return new KubernetesRuntimeState(
new RuntimeId(workspace.getId(), "defEnv", workspace.getAccount().getId()),
new RuntimeIdentityImpl(workspace.getId(), "defEnv", workspace.getAccount().getId()),
generate("namespace", 5),
WorkspaceStatus.RUNNING);
}

View File

@ -21,9 +21,8 @@ import static org.eclipse.che.api.core.model.workspace.config.MachineConfig.MEMO
import static org.eclipse.che.commons.lang.NameGenerator.generate;
import static org.eclipse.che.workspace.infrastructure.kubernetes.Constants.CHE_ORIGINAL_NAME_LABEL;
import static org.eclipse.che.workspace.infrastructure.kubernetes.server.secure.SecureServerExposerFactoryProvider.SECURE_EXPOSER_IMPL_PROPERTY;
import static org.mockito.Mockito.lenient;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verifyZeroInteractions;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.when;
import static org.testng.Assert.assertEquals;
import static org.testng.Assert.assertNull;
@ -83,40 +82,29 @@ public class KubernetesPluginsToolingApplierTest {
@Mock Pod pod;
@Mock PodSpec podSpec;
@Mock ObjectMeta meta;
@Mock KubernetesEnvironment internalEnvironment;
@Mock Container userContainer;
@Mock InternalMachineConfig userMachineConfig;
List<Container> containers;
KubernetesPluginsToolingApplier applier;
private KubernetesEnvironment internalEnvironment;
private List<Container> containers;
private KubernetesPluginsToolingApplier applier;
@BeforeMethod
public void setUp() {
internalEnvironment = spy(KubernetesEnvironment.builder().build());
applier = new KubernetesPluginsToolingApplier(TEST_IMAGE_POLICY, MEMORY_LIMIT_MB, false);
Map<String, InternalMachineConfig> machines = new HashMap<>();
containers = new ArrayList<>();
Map<String, Service> services = new HashMap<>();
containers.add(userContainer);
machines.put(USER_MACHINE_NAME, userMachineConfig);
when(internalEnvironment.getPods()).thenReturn(of(POD_NAME, pod));
internalEnvironment.getPods().put(POD_NAME, pod);
when(pod.getSpec()).thenReturn(podSpec);
when(podSpec.getContainers()).thenReturn(containers);
when(pod.getMetadata()).thenReturn(meta);
when(meta.getName()).thenReturn(POD_NAME);
when(internalEnvironment.getMachines()).thenReturn(machines);
lenient().when(internalEnvironment.getServices()).thenReturn(services);
Map<String, String> attributes = new HashMap<>();
when(internalEnvironment.getAttributes()).thenReturn(attributes);
}
@Test
public void doesNothingIfChePluginsListIsEmpty() throws Exception {
applier.apply(internalEnvironment, emptyList());
verifyZeroInteractions(internalEnvironment);
internalEnvironment.getMachines().putAll(machines);
}
@Test(
@ -138,6 +126,17 @@ public class KubernetesPluginsToolingApplierTest {
verifyContainer(toolingContainer);
}
@Test
public void createsPodAndAddToolingIfNoPodIsPresent() throws Exception {
internalEnvironment.getPods().clear();
applier.apply(internalEnvironment, singletonList(createChePlugin()));
verifyPodAndContainersNumber(1);
Container toolingContainer = getOneAndOnlyNonUserContainer(internalEnvironment);
verifyContainer(toolingContainer);
}
@Test
public void canAddMultipleToolingContainersToAPodFromOnePlugin() throws Exception {
applier.apply(internalEnvironment, singletonList(createChePluginWith2Containers()));

View File

@ -22,12 +22,14 @@ import com.google.inject.multibindings.MapBinder;
import com.google.inject.multibindings.Multibinder;
import com.google.inject.name.Names;
import org.eclipse.che.api.system.server.ServiceTermination;
import org.eclipse.che.api.workspace.server.NoEnvironmentFactory;
import org.eclipse.che.api.workspace.server.spi.RuntimeInfrastructure;
import org.eclipse.che.api.workspace.server.spi.environment.InternalEnvironmentFactory;
import org.eclipse.che.api.workspace.server.spi.provision.env.CheApiExternalEnvVarProvider;
import org.eclipse.che.api.workspace.server.spi.provision.env.CheApiInternalEnvVarProvider;
import org.eclipse.che.api.workspace.server.spi.provision.env.EnvVarProvider;
import org.eclipse.che.api.workspace.server.wsplugins.ChePluginsApplier;
import org.eclipse.che.api.workspace.shared.Constants;
import org.eclipse.che.workspace.infrastructure.docker.environment.dockerimage.DockerImageEnvironment;
import org.eclipse.che.workspace.infrastructure.docker.environment.dockerimage.DockerImageEnvironmentFactory;
import org.eclipse.che.workspace.infrastructure.kubernetes.KubernetesClientTermination;
@ -73,6 +75,7 @@ public class OpenShiftInfraModule extends AbstractModule {
factories.addBinding(OpenShiftEnvironment.TYPE).to(OpenShiftEnvironmentFactory.class);
factories.addBinding(KubernetesEnvironment.TYPE).to(KubernetesEnvironmentFactory.class);
factories.addBinding(DockerImageEnvironment.TYPE).to(DockerImageEnvironmentFactory.class);
factories.addBinding(Constants.NO_ENVIRONMENT_RECIPE_TYPE).to(NoEnvironmentFactory.class);
bind(RuntimeInfrastructure.class).to(OpenShiftInfrastructure.class);

View File

@ -20,11 +20,13 @@ import javax.inject.Inject;
import javax.inject.Singleton;
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.NoEnvironmentFactory.NoEnvInternalEnvironment;
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.RuntimeInfrastructure;
import org.eclipse.che.api.workspace.server.spi.environment.InternalEnvironment;
import org.eclipse.che.api.workspace.server.spi.provision.InternalEnvironmentProvisioner;
import org.eclipse.che.api.workspace.shared.Constants;
import org.eclipse.che.workspace.infrastructure.docker.environment.dockerimage.DockerImageEnvironment;
import org.eclipse.che.workspace.infrastructure.kubernetes.cache.KubernetesRuntimeStateCache;
import org.eclipse.che.workspace.infrastructure.kubernetes.environment.KubernetesEnvironment;
@ -51,7 +53,10 @@ public class OpenShiftInfrastructure extends RuntimeInfrastructure {
super(
NAME,
ImmutableSet.of(
OpenShiftEnvironment.TYPE, KubernetesEnvironment.TYPE, DockerImageEnvironment.TYPE),
OpenShiftEnvironment.TYPE,
KubernetesEnvironment.TYPE,
DockerImageEnvironment.TYPE,
Constants.NO_ENVIRONMENT_RECIPE_TYPE),
eventService,
internalEnvProvisioners);
this.runtimeContextFactory = runtimeContextFactory;
@ -72,15 +77,16 @@ public class OpenShiftInfrastructure extends RuntimeInfrastructure {
private OpenShiftEnvironment asOpenShiftEnv(InternalEnvironment source)
throws InfrastructureException {
if (source instanceof OpenShiftEnvironment) {
if (source instanceof NoEnvInternalEnvironment) {
return OpenShiftEnvironment.builder()
.setAttributes(source.getAttributes())
.setWarnings(source.getWarnings())
.build();
} else if (source instanceof OpenShiftEnvironment) {
return (OpenShiftEnvironment) source;
}
if (source instanceof KubernetesEnvironment) {
} else if (source instanceof KubernetesEnvironment) {
return new OpenShiftEnvironment((KubernetesEnvironment) source);
}
if (source instanceof DockerImageEnvironment) {
} else if (source instanceof DockerImageEnvironment) {
KubernetesEnvironment k8sEnv =
dockerImageEnvConverter.convert((DockerImageEnvironment) source);
return new OpenShiftEnvironment(k8sEnv);

View File

@ -135,6 +135,12 @@ public class OpenShiftEnvironment extends KubernetesEnvironment {
return this;
}
@Override
public Builder setAttributes(Map<String, String> attributes) {
this.attributes.putAll(attributes);
return this;
}
public Builder setRoutes(Map<String, Route> route) {
this.routes.putAll(route);
return this;

View File

@ -37,6 +37,7 @@ import org.eclipse.che.api.installer.server.InstallerRegistry;
import org.eclipse.che.api.workspace.server.model.impl.WarningImpl;
import org.eclipse.che.api.workspace.server.spi.InfrastructureException;
import org.eclipse.che.api.workspace.server.spi.environment.*;
import org.eclipse.che.commons.annotation.Nullable;
import org.eclipse.che.workspace.infrastructure.kubernetes.Names;
import org.eclipse.che.workspace.infrastructure.kubernetes.environment.KubernetesEnvironmentValidator;
import org.eclipse.che.workspace.infrastructure.kubernetes.util.Containers;
@ -86,10 +87,11 @@ public class OpenShiftEnvironmentFactory extends InternalEnvironmentFactory<Open
@Override
protected OpenShiftEnvironment doCreate(
InternalRecipe recipe,
@Nullable InternalRecipe recipe,
Map<String, InternalMachineConfig> machines,
List<Warning> sourceWarnings)
throws InfrastructureException, ValidationException {
checkNotNull(recipe, "Null recipe is not supported by openshift environment factory");
List<Warning> warnings = new ArrayList<>();
if (sourceWarnings != null) {
warnings.addAll(sourceWarnings);

View File

@ -25,6 +25,7 @@ import org.eclipse.che.api.core.model.workspace.config.MachineConfig;
import org.eclipse.che.api.workspace.server.spi.InfrastructureException;
import org.eclipse.che.api.workspace.server.spi.environment.InternalEnvironment;
import org.eclipse.che.api.workspace.server.spi.environment.InternalEnvironmentFactory;
import org.eclipse.che.commons.annotation.Nullable;
/**
* Helps to calculate amount of RAM defined in {@link Environment environment}
@ -46,7 +47,10 @@ public class EnvironmentRamCalculator {
* Parses (and fetches if needed) recipe of environment and sums RAM size of all machines in
* environment in megabytes.
*/
public long calculate(Environment environment) throws ServerException {
public long calculate(@Nullable Environment environment) throws ServerException {
if (environment == null) {
return 0;
}
try {
return getInternalEnvironment(environment)
.getMachines()

View File

@ -77,7 +77,9 @@ public class RamResourceUsageTracker implements ResourceUsageTracker {
.getConfig()
.getEnvironments()
.get(activeWorkspace.getRuntime().getActiveEnv());
currentlyUsedRamMB += environmentRamCalculator.calculate(startingEnvironment);
if (startingEnvironment != null) {
currentlyUsedRamMB += environmentRamCalculator.calculate(startingEnvironment);
}
} else {
currentlyUsedRamMB += environmentRamCalculator.calculate(activeWorkspace.getRuntime());
}

View File

@ -157,6 +157,9 @@ public class LimitsCheckingWorkspaceManager extends WorkspaceManager {
if (maxRamPerEnvMB < 0) {
return;
}
if (config.getEnvironments().isEmpty()) {
return;
}
for (Map.Entry<String, ? extends Environment> envEntry : config.getEnvironments().entrySet()) {
Environment env = envEntry.getValue();
final long workspaceRam = environmentRamCalculator.calculate(env);
@ -181,6 +184,9 @@ public class LimitsCheckingWorkspaceManager extends WorkspaceManager {
String accountId, String namespace, WorkspaceConfig config, @Nullable String envName)
throws NotFoundException, ServerException, ConflictException {
if (config.getEnvironments().isEmpty()) {
return;
}
final Environment environment =
config.getEnvironments().get(firstNonNull(envName, config.getDefaultEnv()));
final ResourceImpl ramToUse =

View File

@ -65,7 +65,6 @@ public class CheTestWorkspaceServiceClient extends AbstractTestWorkspaceServiceC
workspace.getEnvironments().put(workspaceName, environment);
workspace.setName(workspaceName);
workspace.setDefaultEnv(workspaceName);
WorkspaceDto workspaceDto =
requestFactory
.fromUrl(getBaseUrl())

View File

@ -75,7 +75,6 @@ public class CheckSimpleGwtAppTest {
WorkspaceConfigDto workspace =
workspaceDtoDeserializer.deserializeWorkspaceTemplate(UBUNTU_JDK8);
workspace
.getEnvironments()
.get("replaced_name")

View File

@ -160,5 +160,7 @@ public final class Constants {
public static final String SUPPORTED_RECIPE_TYPES = "supportedRecipeTypes";
public static final String NO_ENVIRONMENT_RECIPE_TYPE = "no-environment";
private Constants() {}
}

View File

@ -35,7 +35,7 @@ public interface WorkspaceConfigDto extends WorkspaceConfig, Hyperlinks {
void setName(String name);
@Override
@FactoryParameter(obligation = MANDATORY)
@FactoryParameter(obligation = OPTIONAL)
String getDefaultEnv();
void setDefaultEnv(String defaultEnvironment);
@ -67,7 +67,7 @@ public interface WorkspaceConfigDto extends WorkspaceConfig, Hyperlinks {
WorkspaceConfigDto withProjects(List<ProjectConfigDto> projects);
@Override
@FactoryParameter(obligation = MANDATORY)
@FactoryParameter(obligation = OPTIONAL)
Map<String, EnvironmentDto> getEnvironments();
void setEnvironments(Map<String, EnvironmentDto> environments);

View File

@ -0,0 +1,57 @@
/*
* Copyright (c) 2012-2018 Red Hat, Inc.
* This program and the accompanying materials are made
* available under the terms of the Eclipse Public License 2.0
* which is available at https://www.eclipse.org/legal/epl-2.0/
*
* SPDX-License-Identifier: EPL-2.0
*
* Contributors:
* Red Hat, Inc. - initial API and implementation
*/
package org.eclipse.che.api.workspace.server;
import java.util.List;
import java.util.Map;
import javax.inject.Inject;
import org.eclipse.che.api.core.model.workspace.Warning;
import org.eclipse.che.api.installer.server.InstallerRegistry;
import org.eclipse.che.api.workspace.server.spi.InternalInfrastructureException;
import org.eclipse.che.api.workspace.server.spi.environment.InternalEnvironment;
import org.eclipse.che.api.workspace.server.spi.environment.InternalEnvironmentFactory;
import org.eclipse.che.api.workspace.server.spi.environment.InternalMachineConfig;
import org.eclipse.che.api.workspace.server.spi.environment.InternalRecipe;
import org.eclipse.che.api.workspace.server.spi.environment.MachineConfigsValidator;
import org.eclipse.che.api.workspace.server.spi.environment.RecipeRetriever;
import org.eclipse.che.commons.annotation.Nullable;
/**
* Fake environment factory for a case when sidecar-based workspace has no environment.
*
* @author Alexander Garagatyi
*/
public class NoEnvironmentFactory extends InternalEnvironmentFactory<InternalEnvironment> {
@Inject
public NoEnvironmentFactory(
InstallerRegistry installerRegistry,
RecipeRetriever recipeRetriever,
MachineConfigsValidator machinesValidator) {
super(installerRegistry, recipeRetriever, machinesValidator);
}
@Override
protected InternalEnvironment doCreate(
@Nullable InternalRecipe recipe,
Map<String, InternalMachineConfig> machines,
List<Warning> warnings)
throws InternalInfrastructureException {
if (recipe != null) {
throw new InternalInfrastructureException(
"No environment factory doesn't accept non-null workspace recipes");
}
return new NoEnvInternalEnvironment();
}
public static class NoEnvInternalEnvironment extends InternalEnvironment {}
}

View File

@ -0,0 +1,40 @@
/*
* Copyright (c) 2012-2018 Red Hat, Inc.
* This program and the accompanying materials are made
* available under the terms of the Eclipse Public License 2.0
* which is available at https://www.eclipse.org/legal/epl-2.0/
*
* SPDX-License-Identifier: EPL-2.0
*
* Contributors:
* Red Hat, Inc. - initial API and implementation
*/
package org.eclipse.che.api.workspace.server;
import com.google.common.base.Strings;
import java.util.Map;
import org.eclipse.che.api.core.model.workspace.WorkspaceConfig;
import org.eclipse.che.api.workspace.shared.Constants;
/**
* Util class to deal with sidecar based workspaces.
*
* @author Alexander Garagatyi
*/
public class SidecarToolingWorkspaceUtil {
/**
* Checks whether provided workspace config attributes {@link WorkspaceConfig#getAttributes()}
* contains configuration of sidecars. <br>
* This indicates whether this workspace is Che6 or Che7 compatible.
*/
public static boolean isSidecarBasedWorkspace(Map<String, String> attributes) {
boolean hasPlugins =
!Strings.isNullOrEmpty(
attributes.getOrDefault(Constants.WORKSPACE_TOOLING_PLUGINS_ATTRIBUTE, null));
boolean hasEditor =
!Strings.isNullOrEmpty(
attributes.getOrDefault(Constants.WORKSPACE_TOOLING_EDITOR_ATTRIBUTE, null));
return hasPlugins || hasEditor;
}
}

View File

@ -11,7 +11,6 @@
*/
package org.eclipse.che.api.workspace.server;
import static com.google.common.base.MoreObjects.firstNonNull;
import static java.lang.String.format;
import static java.lang.System.currentTimeMillis;
import static java.util.Objects.requireNonNull;
@ -351,24 +350,11 @@ public class WorkspaceManager {
}
/** Asynchronously starts given workspace. */
private void startAsync(WorkspaceImpl workspace, String envName, Map<String, String> options)
private void startAsync(
WorkspaceImpl workspace, @Nullable String envName, Map<String, String> options)
throws ConflictException, NotFoundException, ServerException {
if (envName != null && !workspace.getConfig().getEnvironments().containsKey(envName)) {
throw new NotFoundException(
format(
"Workspace '%s:%s' doesn't contain environment '%s'",
workspace.getNamespace(), workspace.getConfig().getName(), envName));
}
String env = getValidatedEnvironmentName(workspace, envName);
workspace.getAttributes().put(UPDATED_ATTRIBUTE_NAME, Long.toString(currentTimeMillis()));
final String env = firstNonNull(envName, workspace.getConfig().getDefaultEnv());
// validate environment in advance
try {
runtimes.validate(workspace.getConfig().getEnvironments().get(env));
} catch (InfrastructureException | ValidationException e) {
throw new ServerException(e);
}
workspaceDao.update(workspace);
runtimes
.startAsync(workspace, env, firstNonNull(options, Collections.emptyMap()))
@ -384,6 +370,45 @@ public class WorkspaceManager {
});
}
private String getValidatedEnvironmentName(WorkspaceImpl workspace, @Nullable String envName)
throws NotFoundException, ServerException {
if (envName != null && !workspace.getConfig().getEnvironments().containsKey(envName)) {
throw new NotFoundException(
format(
"Workspace '%s:%s' doesn't contain environment '%s'",
workspace.getNamespace(), workspace.getConfig().getName(), envName));
}
envName = firstNonNull(envName, workspace.getConfig().getDefaultEnv());
if (envName == null
&& SidecarToolingWorkspaceUtil.isSidecarBasedWorkspace(
workspace.getConfig().getAttributes())) {
// Sidecar-based workspaces are allowed not to have any environments
return null;
}
// validate environment in advance
if (envName == null) {
throw new NotFoundException(
format(
"Workspace %s:%s can't use null environment",
workspace.getNamespace(), workspace.getConfig().getName()));
}
try {
runtimes.validate(workspace.getConfig().getEnvironments().get(envName));
} catch (InfrastructureException | ValidationException e) {
throw new ServerException(e);
}
return envName;
}
/** Returns first non-null argument or null if both are null. */
private <T> T firstNonNull(T first, T second) {
return first != null ? first : second;
}
private void checkWorkspaceIsRunningOrStarting(WorkspaceImpl workspace) throws ConflictException {
if (workspace.getStatus() != RUNNING && workspace.getStatus() != STARTING) {
throw new ConflictException(

View File

@ -70,7 +70,9 @@ import org.eclipse.che.api.workspace.server.spi.RuntimeStartInterruptedException
import org.eclipse.che.api.workspace.server.spi.WorkspaceDao;
import org.eclipse.che.api.workspace.server.spi.environment.InternalEnvironment;
import org.eclipse.che.api.workspace.server.spi.environment.InternalEnvironmentFactory;
import org.eclipse.che.api.workspace.shared.Constants;
import org.eclipse.che.api.workspace.shared.dto.event.WorkspaceStatusEvent;
import org.eclipse.che.commons.annotation.Nullable;
import org.eclipse.che.commons.env.EnvironmentContext;
import org.eclipse.che.commons.lang.NameGenerator;
import org.eclipse.che.commons.lang.concurrent.ThreadLocalPropagateContext;
@ -281,16 +283,19 @@ public class WorkspaceRuntimes {
* @see WorkspaceStatus#RUNNING
*/
public CompletableFuture<Void> startAsync(
Workspace workspace, String envName, Map<String, String> options)
Workspace workspace, @Nullable String envName, Map<String, String> options)
throws ConflictException, NotFoundException, ServerException {
final EnvironmentImpl environment = copyEnv(workspace, envName);
final String workspaceId = workspace.getId();
requireNonNull(environment, "Environment should not be null " + workspaceId);
requireNonNull(environment.getRecipe(), "Recipe should not be null " + workspaceId);
requireNonNull(
environment.getRecipe().getType(), "Recipe type should not be null " + workspaceId);
// Sidecar-based workspaces allowed not to have environments
EnvironmentImpl environment = null;
if (envName != null) {
environment = copyEnv(workspace, envName);
requireNonNull(environment, "Environment should not be null " + workspaceId);
requireNonNull(environment.getRecipe(), "Recipe should not be null " + workspaceId);
requireNonNull(
environment.getRecipe().getType(), "Recipe type should not be null " + workspaceId);
}
if (isStartRefused.get()) {
throw new ConflictException(
@ -620,12 +625,15 @@ public class WorkspaceRuntimes {
identity.getWorkspaceId(), identity.getEnvName()));
}
Environment environment = workspace.getConfig().getEnvironments().get(identity.getEnvName());
if (environment == null) {
throw new ServerException(
format(
"Environment configuration is missing for the runtime '%s:%s'. Runtime won't be recovered",
identity.getWorkspaceId(), identity.getEnvName()));
Environment environment = null;
if (identity.getEnvName() != null) {
environment = workspace.getConfig().getEnvironments().get(identity.getEnvName());
if (environment == null) {
throw new ServerException(
format(
"Environment configuration is missing for the runtime '%s:%s'. Runtime won't be recovered",
identity.getWorkspaceId(), identity.getEnvName()));
}
}
InternalRuntime runtime;
@ -785,10 +793,16 @@ public class WorkspaceRuntimes {
return environmentFactories.keySet();
}
private InternalEnvironment createInternalEnvironment(
Environment environment, Map<String, String> workspaceConfigAttributes)
@VisibleForTesting
InternalEnvironment createInternalEnvironment(
@Nullable Environment environment, Map<String, String> workspaceConfigAttributes)
throws InfrastructureException, ValidationException, NotFoundException {
String recipeType = environment.getRecipe().getType();
String recipeType;
if (environment == null) {
recipeType = Constants.NO_ENVIRONMENT_RECIPE_TYPE;
} else {
recipeType = environment.getRecipe().getType();
}
InternalEnvironmentFactory factory = environmentFactories.get(recipeType);
if (factory == null) {
throw new NotFoundException(

View File

@ -15,8 +15,8 @@ import static com.google.common.base.Strings.isNullOrEmpty;
import static java.lang.String.format;
import static org.eclipse.che.api.core.model.workspace.config.MachineConfig.MEMORY_LIMIT_ATTRIBUTE;
import static org.eclipse.che.api.core.model.workspace.config.MachineConfig.MEMORY_REQUEST_ATTRIBUTE;
import static org.eclipse.che.api.workspace.server.SidecarToolingWorkspaceUtil.isSidecarBasedWorkspace;
import com.google.common.base.Strings;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Optional;
@ -31,7 +31,6 @@ 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.config.Recipe;
import org.eclipse.che.api.core.model.workspace.config.Volume;
import org.eclipse.che.api.workspace.shared.Constants;
/**
* Validator for {@link Workspace}.
@ -69,24 +68,7 @@ public class WorkspaceValidator {
+ "latin letters, underscores, dots, dashes and must start and end only with digits, "
+ "latin letters or underscores");
// environments
check(!isNullOrEmpty(config.getDefaultEnv()), "Workspace default environment name required");
checkNotNull(config.getEnvironments(), "Workspace must contain at least one environment");
check(
config.getEnvironments().containsKey(config.getDefaultEnv()),
"Workspace default environment configuration required");
for (Environment environment : config.getEnvironments().values()) {
checkNotNull(environment, "Environment must not be null");
Recipe recipe = environment.getRecipe();
checkNotNull(recipe, "Environment recipe must not be null");
checkNotNull(recipe.getType(), "Environment recipe type must not be null");
for (Entry<String, ? extends MachineConfig> machineEntry :
environment.getMachines().entrySet()) {
validateMachine(machineEntry.getKey(), machineEntry.getValue());
}
}
validateEnvironments(config);
// commands
for (Command command : config.getCommands()) {
@ -127,6 +109,34 @@ public class WorkspaceValidator {
}
}
private void validateEnvironments(WorkspaceConfig config) throws ValidationException {
boolean environmentIsNotSet =
(config.getEnvironments() == null || config.getEnvironments().isEmpty())
&& isNullOrEmpty(config.getDefaultEnv());
boolean isSidecarWorkspace = isSidecarBasedWorkspace(config.getAttributes());
if (environmentIsNotSet && isSidecarWorkspace) {
// sidecar based workspaces allowed not to have environment
return;
}
check(!isNullOrEmpty(config.getDefaultEnv()), "Workspace default environment name required");
checkNotNull(config.getEnvironments(), "Workspace must contain at least one environment");
check(
config.getEnvironments().containsKey(config.getDefaultEnv()),
"Workspace default environment configuration required");
for (Environment environment : config.getEnvironments().values()) {
checkNotNull(environment, "Environment must not be null");
Recipe recipe = environment.getRecipe();
checkNotNull(recipe, "Environment recipe must not be null");
checkNotNull(recipe.getType(), "Environment recipe type must not be null");
for (Entry<String, ? extends MachineConfig> machineEntry :
environment.getMachines().entrySet()) {
validateMachine(machineEntry.getKey(), machineEntry.getValue());
}
}
}
private void validateMachine(String machineName, MachineConfig machine)
throws ValidationException {
validateLongAttribute(
@ -189,13 +199,7 @@ public class WorkspaceValidator {
.flatMap(machine -> machine.getInstallers().stream())
.findAny();
Map<String, String> attributes = config.getAttributes();
boolean hasPlugins =
!Strings.isNullOrEmpty(
attributes.getOrDefault(Constants.WORKSPACE_TOOLING_PLUGINS_ATTRIBUTE, null));
boolean hasEditor =
!Strings.isNullOrEmpty(
attributes.getOrDefault(Constants.WORKSPACE_TOOLING_EDITOR_ATTRIBUTE, null));
if ((hasPlugins || hasEditor) && installers.isPresent()) {
if (isSidecarBasedWorkspace(attributes) && installers.isPresent()) {
throw new ValidationException("Workspace config cannot have both plugins and installers.");
}
}

View File

@ -11,6 +11,7 @@
*/
package org.eclipse.che.api.workspace.server.bootstrap;
import java.util.Objects;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeUnit;
@ -55,7 +56,7 @@ public abstract class AbstractBootstrapper {
// check bootstrapper belongs to current runtime and machine
RuntimeIdentityDto runtimeId = event.getRuntimeId();
if (event.getMachineName().equals(machineName)
&& runtimeIdentity.getEnvName().equals(runtimeId.getEnvName())
&& Objects.equals(runtimeIdentity.getEnvName(), runtimeId.getEnvName())
&& runtimeIdentity.getWorkspaceId().equals(runtimeId.getWorkspaceId())) {
finishEventFuture.complete(event);

View File

@ -11,6 +11,7 @@
*/
package org.eclipse.che.api.workspace.server.model.impl;
import java.util.Objects;
import org.eclipse.che.api.core.model.workspace.runtime.RuntimeIdentity;
/** @author gazarenkov */
@ -20,6 +21,10 @@ public final class RuntimeIdentityImpl implements RuntimeIdentity {
private final String envName;
private final String ownerId;
public RuntimeIdentityImpl(RuntimeIdentity id) {
this(id.getWorkspaceId(), id.getEnvName(), id.getOwnerId());
}
public RuntimeIdentityImpl(String workspaceId, String envName, String ownerId) {
this.workspaceId = workspaceId;
this.envName = envName;
@ -43,14 +48,16 @@ public final class RuntimeIdentityImpl implements RuntimeIdentity {
@Override
public int hashCode() {
return (workspaceId + envName).hashCode();
return Objects.hash(workspaceId, envName, ownerId);
}
@Override
public boolean equals(Object obj) {
if (!(obj instanceof RuntimeIdentityImpl)) return false;
RuntimeIdentityImpl other = (RuntimeIdentityImpl) obj;
return workspaceId.equals(other.workspaceId) && envName.equals(other.envName);
return workspaceId.equals(other.workspaceId)
&& Objects.equals(envName, other.envName)
&& Objects.equals(ownerId, other.ownerId);
}
@Override

View File

@ -63,7 +63,7 @@ public class WorkspaceConfigImpl implements WorkspaceConfig {
@Column(name = "description", columnDefinition = "TEXT")
private String description;
@Column(name = "defaultenv", nullable = false)
@Column(name = "defaultenv", nullable = true)
private String defaultEnv;
@OneToMany(cascade = CascadeType.ALL, orphanRemoval = true, fetch = FetchType.EAGER)

View File

@ -79,6 +79,7 @@ public abstract class InternalRuntime<T extends RuntimeContext> {
}
/** Returns name of the active environment. */
@Nullable
public String getActiveEnv() {
return context.getIdentity().getEnvName();
}

View File

@ -28,6 +28,7 @@ import org.eclipse.che.api.installer.server.exception.InstallerException;
import org.eclipse.che.api.installer.shared.model.Installer;
import org.eclipse.che.api.workspace.server.model.impl.ServerConfigImpl;
import org.eclipse.che.api.workspace.server.spi.InfrastructureException;
import org.eclipse.che.commons.annotation.Nullable;
/**
* Creates a valid instance of InternalEnvironment.
@ -78,36 +79,40 @@ public abstract class InternalEnvironmentFactory<T extends InternalEnvironment>
* @throws InfrastructureException if infrastructure specific error occurs
* @throws ValidationException if validation fails
*/
public T create(final Environment sourceEnv) throws InfrastructureException, ValidationException {
public T create(@Nullable final Environment sourceEnv)
throws InfrastructureException, ValidationException {
Map<String, InternalMachineConfig> machines = new HashMap<>();
List<Warning> warnings = new ArrayList<>();
InternalRecipe recipe = null;
InternalRecipe recipe = recipeRetriever.getRecipe(sourceEnv.getRecipe());
if (sourceEnv != null) {
recipe = recipeRetriever.getRecipe(sourceEnv.getRecipe());
for (Map.Entry<String, ? extends MachineConfig> machineEntry :
sourceEnv.getMachines().entrySet()) {
MachineConfig machineConfig = machineEntry.getValue();
for (Map.Entry<String, ? extends MachineConfig> machineEntry :
sourceEnv.getMachines().entrySet()) {
MachineConfig machineConfig = machineEntry.getValue();
List<Installer> installers;
try {
installers = installerRegistry.getOrderedInstallers(machineConfig.getInstallers());
} catch (InstallerException e) {
throw new InfrastructureException(e);
List<Installer> installers;
try {
installers = installerRegistry.getOrderedInstallers(machineConfig.getInstallers());
} catch (InstallerException e) {
throw new InfrastructureException(e);
}
machines.put(
machineEntry.getKey(),
new InternalMachineConfig(
installers,
normalizeServers(machineConfig.getServers()),
machineConfig.getEnv(),
machineConfig.getAttributes(),
machineConfig.getVolumes()));
}
machines.put(
machineEntry.getKey(),
new InternalMachineConfig(
installers,
normalizeServers(machineConfig.getServers()),
machineConfig.getEnv(),
machineConfig.getAttributes(),
machineConfig.getVolumes()));
machinesValidator.validate(machines);
}
machinesValidator.validate(machines);
return doCreate(recipe, machines, warnings);
}
@ -127,7 +132,9 @@ public abstract class InternalEnvironmentFactory<T extends InternalEnvironment>
* @throws ValidationException if validation fails
*/
protected abstract T doCreate(
InternalRecipe recipe, Map<String, InternalMachineConfig> machines, List<Warning> warnings)
@Nullable InternalRecipe recipe,
Map<String, InternalMachineConfig> machines,
List<Warning> warnings)
throws InfrastructureException, ValidationException;
@VisibleForTesting

View File

@ -14,6 +14,7 @@ package org.eclipse.che.api.workspace.server;
import static java.util.Collections.emptyMap;
import static java.util.Collections.emptySet;
import static org.eclipse.che.api.workspace.shared.Constants.ERROR_MESSAGE_ATTRIBUTE_NAME;
import static org.eclipse.che.api.workspace.shared.Constants.NO_ENVIRONMENT_RECIPE_TYPE;
import static org.eclipse.che.api.workspace.shared.Constants.STOPPED_ABNORMALLY_ATTRIBUTE_NAME;
import static org.eclipse.che.api.workspace.shared.Constants.STOPPED_ATTRIBUTE_NAME;
import static org.mockito.ArgumentMatchers.any;
@ -119,6 +120,53 @@ public class WorkspaceRuntimesTest {
lockService);
}
@Test
public void internalEnvironmentCreationShouldRespectNoEnvironmentCase() throws Exception {
InternalEnvironmentFactory<InternalEnvironment> noEnvFactory =
mock(InternalEnvironmentFactory.class);
runtimes =
new WorkspaceRuntimes(
eventService,
ImmutableMap.of(
TEST_ENVIRONMENT_TYPE, testEnvFactory, NO_ENVIRONMENT_RECIPE_TYPE, noEnvFactory),
infrastructure,
sharedPool,
workspaceDao,
dbInitializer,
probeScheduler,
statuses,
lockService);
InternalEnvironment expectedEnvironment = mock(InternalEnvironment.class);
when(noEnvFactory.create(eq(null))).thenReturn(expectedEnvironment);
InternalEnvironment actualEnvironment = runtimes.createInternalEnvironment(null, emptyMap());
assertEquals(actualEnvironment, expectedEnvironment);
}
@Test(
expectedExceptions = NotFoundException.class,
expectedExceptionsMessageRegExp =
"InternalEnvironmentFactory is not configured for recipe type: 'not-supported-type'")
public void internalEnvironmentShouldThrowExceptionWhenNoEnvironmentFactoryFoundForRecipeType()
throws Exception {
EnvironmentImpl environment = new EnvironmentImpl();
environment.setRecipe(new RecipeImpl("not-supported-type", "", "", null));
runtimes.createInternalEnvironment(environment, emptyMap());
}
@Test(
expectedExceptions = NotFoundException.class,
expectedExceptionsMessageRegExp =
"InternalEnvironmentFactory is not configured for recipe type: '"
+ NO_ENVIRONMENT_RECIPE_TYPE
+ "'")
public void
internalEnvironmentShouldThrowExceptionWhenNoEnvironmentFactoryFoundForNoEnvironmentWorkspaceCase()
throws Exception {
runtimes.createInternalEnvironment(null, emptyMap());
}
@Test
public void runtimeIsRecovered() throws Exception {
RuntimeIdentity identity = new RuntimeIdentityImpl("workspace123", "my-env", "myId");

View File

@ -143,7 +143,7 @@ public class WorkspaceValidatorTest {
@Test(
expectedExceptions = ValidationException.class,
expectedExceptionsMessageRegExp = "Workspace default environment name required")
public void shouldFailValidationIfDefaultEnvNameIsNull() throws Exception {
public void shouldFailValidationOfChe6WSIfDefaultEnvNameIsNull() throws Exception {
final WorkspaceConfigDto config = createConfig();
config.setDefaultEnv(null);
@ -153,13 +153,24 @@ public class WorkspaceValidatorTest {
@Test(
expectedExceptions = ValidationException.class,
expectedExceptionsMessageRegExp = "Workspace default environment name required")
public void shouldFailValidationIfDefaultEnvNameIsEmpty() throws Exception {
public void shouldFailValidationOfChe6WSIfDefaultEnvNameIsEmpty() throws Exception {
final WorkspaceConfigDto config = createConfig();
config.setDefaultEnv("");
wsValidator.validateConfig(config);
}
@Test
public void shouldNotFailValidationOfChe7WSIfDefaultEnvNameIsNullAndNoEnvIsPresent()
throws Exception {
final WorkspaceConfigDto config = createConfig();
config.setDefaultEnv(null);
config.getEnvironments().clear();
config.getAttributes().put(Constants.WORKSPACE_TOOLING_EDITOR_ATTRIBUTE, "something");
wsValidator.validateConfig(config);
}
@Test(
expectedExceptions = ValidationException.class,
expectedExceptionsMessageRegExp = "Workspace default environment configuration required")
@ -370,7 +381,7 @@ public class WorkspaceValidatorTest {
.withType("type")
.withContent("content")
.withContentType("content type"));
workspaceConfigDto.setEnvironments(singletonMap("dev-env", env));
workspaceConfigDto.setEnvironments(new HashMap<>(singletonMap("dev-env", env)));
List<CommandDto> commandDtos = new ArrayList<>();
commandDtos.add(

View File

@ -155,6 +155,20 @@ public class InternalEnvironmentFactoryTest {
assertEquals(createdEnv, expectedEnv);
}
@Test
public void shouldPassNullRecipeIfEnvironmentIsNull() throws Exception {
// given
InternalEnvironment expectedEnv = mock(InternalEnvironment.class);
when(environmentFactory.doCreate(any(), any(), any())).thenReturn(expectedEnv);
// when
InternalEnvironment createdEnv = environmentFactory.create(null);
// then
assertEquals(createdEnv, expectedEnv);
verify(environmentFactory).doCreate(eq(null), any(), any());
}
@Test
public void normalizeServersProtocols() throws InfrastructureException {
ServerConfigImpl serverWithoutProtocol =

View File

@ -0,0 +1,20 @@
--
-- Copyright (c) 2012-2018 Red Hat, Inc.
-- This program and the accompanying materials are made
-- available under the terms of the Eclipse Public License 2.0
-- which is available at https://www.eclipse.org/legal/epl-2.0/
--
-- SPDX-License-Identifier: EPL-2.0
--
-- Contributors:
-- Red Hat, Inc. - initial API and implementation
--
-- Allow null as defaultEnv field in workspace config
ALTER TABLE workspaceconfig ALTER COLUMN defaultenv DROP NOT NULL;
ALTER TABLE che_k8s_runtime DROP PRIMARY KEY;
ALTER TABLE che_k8s_runtime ADD PRIMARY KEY (workspace_id);
-- Allow null as env_name field in kubernetes runtime
ALTER TABLE che_k8s_runtime ALTER COLUMN env_name DROP NOT NULL;

View File

@ -0,0 +1,20 @@
--
-- Copyright (c) 2012-2018 Red Hat, Inc.
-- This program and the accompanying materials are made
-- available under the terms of the Eclipse Public License 2.0
-- which is available at https://www.eclipse.org/legal/epl-2.0/
--
-- SPDX-License-Identifier: EPL-2.0
--
-- Contributors:
-- Red Hat, Inc. - initial API and implementation
--
-- Allow null as defaultEnv field in workspace config
ALTER TABLE workspaceconfig MODIFY COLUMN defaultenv VARCHAR(255);
ALTER TABLE che_k8s_runtime DROP PRIMARY KEY;
ALTER TABLE che_k8s_runtime ADD PRIMARY KEY (workspace_id);
-- Allow null as env_name field in kubernetes runtime
ALTER TABLE che_k8s_runtime MODIFY COLUMN env_name VARCHAR(255);

View File

@ -0,0 +1,20 @@
--
-- Copyright (c) 2012-2018 Red Hat, Inc.
-- This program and the accompanying materials are made
-- available under the terms of the Eclipse Public License 2.0
-- which is available at https://www.eclipse.org/legal/epl-2.0/
--
-- SPDX-License-Identifier: EPL-2.0
--
-- Contributors:
-- Red Hat, Inc. - initial API and implementation
--
-- Allow null as defaultEnv field in workspace config
ALTER TABLE workspaceconfig ALTER COLUMN defaultenv DROP NOT NULL;
ALTER TABLE che_k8s_runtime DROP CONSTRAINT che_k8s_runtime_pkey;
ALTER TABLE che_k8s_runtime ADD PRIMARY KEY (workspace_id);
-- Allow null as env_name field in kubernetes runtime
ALTER TABLE che_k8s_runtime ALTER COLUMN env_name DROP NOT NULL;

View File

@ -185,7 +185,6 @@ public class CascadeRemovalTest {
SshPairImpl.class,
VolumeImpl.class,
KubernetesRuntimeState.class,
KubernetesRuntimeState.RuntimeId.class,
KubernetesMachineImpl.class,
KubernetesMachineImpl.MachineId.class,
KubernetesServerImpl.class,

View File

@ -136,7 +136,6 @@ public class MySqlTckModule extends TckModule {
SignatureKeyPairImpl.class,
// k8s-runtimes
KubernetesRuntimeState.class,
KubernetesRuntimeState.RuntimeId.class,
KubernetesMachineImpl.class,
KubernetesMachineImpl.MachineId.class,
KubernetesServerImpl.class,

View File

@ -131,7 +131,6 @@ public class PostgreSqlTckModule extends TckModule {
VolumeImpl.class,
// k8s-runtimes
KubernetesRuntimeState.class,
KubernetesRuntimeState.RuntimeId.class,
KubernetesMachineImpl.class,
KubernetesMachineImpl.MachineId.class,
KubernetesServerImpl.class,