CHE-6124: Do not publish exposed ports in Docker machines

Publish port in Docker machines only in case there is a server
in the workspace config that represents the port.
Refactor OpenShift infrastructure code.
Refactor Docker infrastructure code.
Add constant of memory attribute and reuse it everywhere it is
hardcoded.
Add constant for installer environment attribute and reuse it
everywhere it is hardcoded.
Add machine memory attribute validation to workspace validation.
Move provisioning of servers, environment variables from
infrastructure implementations to runtime SPI abstract level.
Signed-off-by: Oleksandr Garagatyi <ogaragat@redhat.com>
6.19.x
Oleksandr Garagatyi 2017-10-23 16:02:08 +03:00
parent a8aae87905
commit cd1301d115
49 changed files with 825 additions and 890 deletions

View File

@ -19,6 +19,14 @@ import java.util.Map;
* @author Alexander Garagatyi
*/
public interface MachineConfig {
/**
* Name of the attribute from {@link #getAttributes()} which if present sets memory limit of the
* machine in bytes. If memory limit is set in environment specific recipe this attribute should
* override value from recipe.
*/
String MEMORY_LIMIT_ATTRIBUTE = "memoryLimitBytes";
/**
* Returns configured installers.
*

View File

@ -90,7 +90,7 @@ teardown() {
check_che_state
#create a workspace
ws_create=$(curl 'http://'${ip_address}':8080/api/workspace?namespace=che&attribute=stackId:java-default' -H 'Content-Type: application/json;charset=UTF-8' -H 'Accept: application/json, text/plain, */*' --data-binary '{"defaultEnv":"wksp-1p0b","environments":{"wksp-1p0b":{"recipe":{"location":"eclipse/ubuntu_jdk8","type":"dockerimage"},"machines":{"dev-machine":{"servers":{},"agents":["org.eclipse.che.exec","org.eclipse.che.terminal","org.eclipse.che.ws-agent","org.eclipse.che.ssh"],"attributes":{"memoryLimitBytes":"2147483648"}}}}},"projects":[],"commands":[{"commandLine":"mvn clean install -f ${current.project.path}","name":"build","type":"mvn","attributes":{"goal":"Build","previewUrl":""}}],"name":"backup-restore","links":[]}' --compressed)
ws_create=$(curl 'http://'${ip_address}':8080/api/workspace?namespace=che&attribute=stackId:java-default' -H 'Content-Type: application/json;charset=UTF-8' -H 'Accept: application/json, text/plain, */*' --data-binary '{"defaultEnv":"wksp-1p0b","environments":{"wksp-1p0b":{"recipe":{"location":"eclipse/ubuntu_jdk8","type":"dockerimage"},"machines":{"dev-machine":{"servers":{},"installers":["org.eclipse.che.exec","org.eclipse.che.terminal","org.eclipse.che.ws-agent","org.eclipse.che.ssh"],"attributes":{"memoryLimitBytes":"2147483648"}}}}},"projects":[],"commands":[{"commandLine":"mvn clean install -f ${current.project.path}","name":"build","type":"mvn","attributes":{"goal":"Build","previewUrl":""}}],"name":"backup-restore","links":[]}' --compressed)
[[ "$ws_create" == *"created"* ]]
[[ "$ws_create" == *"STOPPED"* ]]
#stop che

View File

@ -63,10 +63,10 @@ export class Workspace {
*/
getWorkspaceConfigDto(createWorkspaceConfig:CreateWorkspaceConfig) : org.eclipse.che.api.workspace.shared.dto.WorkspaceConfigDto {
let devMachine : org.eclipse.che.api.workspace.shared.dto.ExtendedMachineDto = new org.eclipse.che.api.workspace.shared.dto.ExtendedMachineDtoImpl();
devMachine.getAgents().push("org.eclipse.che.exec");
devMachine.getAgents().push("org.eclipse.che.terminal");
devMachine.getAgents().push("org.eclipse.che.ws-agent");
devMachine.getAgents().push("org.eclipse.che.ssh");
devMachine.getInstallers().push("org.eclipse.che.exec");
devMachine.getInstallers().push("org.eclipse.che.terminal");
devMachine.getInstallers().push("org.eclipse.che.ws-agent");
devMachine.getInstallers().push("org.eclipse.che.ssh");
devMachine.getAttributes().set("memoryLimitBytes", "2147483648");
let defaultEnvironment : org.eclipse.che.api.workspace.shared.dto.EnvironmentDto = new org.eclipse.che.api.workspace.shared.dto.EnvironmentDtoImpl();

View File

@ -69,7 +69,7 @@ export class GetSshDataAction {
// Check ssh agent is there
let defaultEnv:string = workspaceDto.getConfig().getDefaultEnv();
let agents:Array<string> = workspaceDto.getConfig().getEnvironments().get(defaultEnv).getMachines().get("dev-machine").getAgents();
let agents:Array<string> = workspaceDto.getConfig().getEnvironments().get(defaultEnv).getMachines().get("dev-machine").getInstallers();
if (agents.indexOf('org.eclipse.che.ssh') === -1) {
return Promise.reject("The SSH agent (org.eclipse.che.ssh) has been disabled for this workspace.")

View File

@ -87,7 +87,7 @@ export class WorkspaceSshAction {
throw new Error("Unable to find a machine named " + this.machineName + " in the workspace '" + this.workspaceName)
}
let agents:Array<string> = machineConfig.getAgents();
let agents:Array<string> = machineConfig.getInstallers();
if (agents.indexOf('org.eclipse.che.ssh') === -1) {
return Promise.reject("The SSH agent (org.eclipse.che.ssh) has been disabled for this workspace.")

View File

@ -830,7 +830,7 @@ setupSSHKeys(workspaceDto: org.eclipse.che.api.workspace.shared.dto.WorkspaceDto
// Check ssh agent is there
let defaultEnv : string = workspaceDto.getConfig().getDefaultEnv();
let agents : Array<string> = workspaceDto.getConfig().getEnvironments().get(defaultEnv).getMachines().get("dev-machine").getAgents();
let agents : Array<string> = workspaceDto.getConfig().getEnvironments().get(defaultEnv).getMachines().get("dev-machine").getInstallers();
if (agents.indexOf('org.eclipse.che.ssh') === - 1) {
return Promise.reject("The SSH agent (org.eclipse.che.ssh) has been disabled for this workspace.")

View File

@ -86,10 +86,6 @@
<groupId>org.eclipse.che.core</groupId>
<artifactId>che-core-api-installer</artifactId>
</dependency>
<dependency>
<groupId>org.eclipse.che.core</groupId>
<artifactId>che-core-api-installer-shared</artifactId>
</dependency>
<dependency>
<groupId>org.eclipse.che.core</groupId>
<artifactId>che-core-api-model</artifactId>

View File

@ -463,8 +463,6 @@ public class DockerMachineStarter {
hostConfig.setBinds(bindMountVolumes.toArray(new String[bindMountVolumes.size()]));
config.setVolumes(nonBindMountVolumes);
config.getHostConfig().withPublishAllPorts(true);
setNonExitingContainerCommandIfNeeded(config);
return docker
@ -479,12 +477,15 @@ public class DockerMachineStarter {
Map<String, PortBinding[]> portsBindings = Maps.newHashMapWithExpectedSize(portsSpecs.size());
for (String portSpec : portsSpecs) {
String[] portMapping = portSpec.split(":");
if (portMapping.length != 2) {
if (portMapping.length == 1) {
portsBindings.put(portMapping[0], new PortBinding[] {new PortBinding()});
} else if (portMapping.length == 2) {
portsBindings.put(
portMapping[0], new PortBinding[] {new PortBinding().withHostPort(portMapping[1])});
} else {
throw new InternalInfrastructureException(
format("Invalid port specification '%s' found machine '%s'", portsSpecs, machineName));
}
portsBindings.put(
portMapping[0], new PortBinding[] {new PortBinding().withHostPort(portMapping[1])});
}
return portsBindings;
}

View File

@ -16,11 +16,9 @@ import static org.eclipse.che.workspace.infrastructure.docker.ArgumentsValidator
import com.google.common.base.Joiner;
import java.util.Map;
import java.util.stream.Collectors;
import javax.inject.Inject;
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.ServerConfig;
import org.eclipse.che.api.workspace.server.spi.InfrastructureException;
import org.eclipse.che.api.workspace.server.spi.InternalEnvironment;
import org.eclipse.che.api.workspace.server.spi.InternalEnvironment.InternalRecipe;
@ -75,41 +73,19 @@ public class EnvironmentParser {
InternalMachineConfig machineConfig = environment.getMachines().get(entry.getKey());
if (machineConfig != null) {
normalizeMachine(entry.getKey(), entry.getValue(), machineConfig);
entry
.getValue()
.setExpose(
entry
.getValue()
.getExpose()
.stream()
.map(expose -> expose.contains("/") ? expose : expose + "/tcp")
.distinct()
.collect(toList()));
}
}
return dockerEnvironment;
}
private void normalizeMachine(
String name, DockerContainerConfig container, InternalMachineConfig machineConfig)
throws ValidationException {
if (machineConfig.getAttributes().containsKey("memoryLimitBytes")) {
try {
container.setMemLimit(
Long.parseLong(machineConfig.getAttributes().get("memoryLimitBytes")));
} catch (NumberFormatException e) {
throw new ValidationException(
format("Value of attribute 'memoryLimitBytes' of machine '%s' is illegal", name));
}
}
container.setExpose(
container
.getExpose()
.stream()
.map(expose -> expose.contains("/") ? expose : expose + "/tcp")
.collect(toList()));
for (ServerConfig serverConfig : machineConfig.getServers().values()) {
String normalizedPort =
serverConfig.getPort().contains("/")
? serverConfig.getPort()
: serverConfig.getPort() + "/tcp";
container.getExpose().add(normalizedPort);
}
container.setExpose(container.getExpose().stream().distinct().collect(Collectors.toList()));
container.getEnvironment().putAll(machineConfig.getEnv());
}
}

View File

@ -11,7 +11,6 @@
package org.eclipse.che.workspace.infrastructure.docker.environment;
import static com.google.common.base.Strings.isNullOrEmpty;
import static java.lang.String.format;
import static java.util.stream.Collectors.toList;
import static org.eclipse.che.workspace.infrastructure.docker.ArgumentsValidator.checkArgument;
@ -233,23 +232,6 @@ public class EnvironmentValidator {
private void validateExtendedMachine(InternalMachineConfig machineConfig, String machineName)
throws ValidationException {
if (machineConfig.getAttributes() != null
&& machineConfig.getAttributes().get("memoryLimitBytes") != null) {
try {
long memoryLimitBytes =
Long.parseLong(machineConfig.getAttributes().get("memoryLimitBytes"));
checkArgument(
memoryLimitBytes > 0,
"Value of attribute 'memoryLimitBytes' of machine '%s' in environment is illegal",
machineName);
} catch (NumberFormatException e) {
throw new ValidationException(
format(
"Value of attribute 'memoryLimitBytes' of machine '%s' in environment is illegal",
machineName));
}
}
if (machineConfig.getServers() != null) {
for (Map.Entry<String, ? extends ServerConfig> serverEntry :

View File

@ -17,12 +17,15 @@ import org.eclipse.che.api.workspace.server.spi.InfrastructureException;
import org.eclipse.che.api.workspace.server.spi.InternalEnvironment;
import org.eclipse.che.workspace.infrastructure.docker.InfrastructureProvisioner;
import org.eclipse.che.workspace.infrastructure.docker.local.dod.DockerApiHostEnvVariableProvisioner;
import org.eclipse.che.workspace.infrastructure.docker.local.installer.LocalInstallersConfigProvisioner;
import org.eclipse.che.workspace.infrastructure.docker.local.installer.LocalInstallersBinariesVolumeProvisioner;
import org.eclipse.che.workspace.infrastructure.docker.local.installer.WsAgentServerConfigProvisioner;
import org.eclipse.che.workspace.infrastructure.docker.local.projects.ProjectsVolumeProvisioner;
import org.eclipse.che.workspace.infrastructure.docker.model.DockerEnvironment;
import org.eclipse.che.workspace.infrastructure.docker.provisioner.ContainerSystemSettingsProvisionersApplier;
import org.eclipse.che.workspace.infrastructure.docker.provisioner.labels.LabelsProvisioner;
import org.eclipse.che.workspace.infrastructure.docker.provisioner.env.EnvVarsConverter;
import org.eclipse.che.workspace.infrastructure.docker.provisioner.labels.RuntimeLabelsProvisioner;
import org.eclipse.che.workspace.infrastructure.docker.provisioner.memory.MemoryAttributeConverter;
import org.eclipse.che.workspace.infrastructure.docker.provisioner.server.ServersConverter;
import org.eclipse.che.workspace.infrastructure.docker.provisioner.server.ToolingServersEnvVarsProvisioner;
import org.eclipse.che.workspace.infrastructure.docker.provisioner.snapshot.ExcludeFoldersFromSnapshotProvisioner;
@ -34,34 +37,44 @@ import org.eclipse.che.workspace.infrastructure.docker.provisioner.snapshot.Excl
*/
@Singleton
public class LocalCheInfrastructureProvisioner implements InfrastructureProvisioner {
private final ContainerSystemSettingsProvisionersApplier settingsProvisioners;
private final ExcludeFoldersFromSnapshotProvisioner snapshotProvisioner;
private final ContainerSystemSettingsProvisionersApplier dockerSettingsProvisioners;
private final ExcludeFoldersFromSnapshotProvisioner snapshotVolumeProvisioner;
private final ProjectsVolumeProvisioner projectsVolumeProvisioner;
private final LocalInstallersConfigProvisioner installerConfigProvisioner;
private final LabelsProvisioner labelsProvisioner;
private final LocalInstallersBinariesVolumeProvisioner installersBinariesVolumeProvisioner;
private final RuntimeLabelsProvisioner runtimeLabelsProvisioner;
private final DockerApiHostEnvVariableProvisioner dockerApiEnvProvisioner;
private final ToolingServersEnvVarsProvisioner toolingServersEnvVarsProvisioner;
private final ToolingServersEnvVarsProvisioner agentsServersEnvVarsProvisioner;
private final WsAgentServerConfigProvisioner wsAgentServerConfigProvisioner;
private final ServersConverter serversConverter;
private final EnvVarsConverter envVarsConverter;
private final MemoryAttributeConverter memoryAttributeConverter;
@Inject
public LocalCheInfrastructureProvisioner(
ContainerSystemSettingsProvisionersApplier settingsProvisioners,
ExcludeFoldersFromSnapshotProvisioner snapshotProvisioner,
ContainerSystemSettingsProvisionersApplier dockerSettingsProvisioners,
ExcludeFoldersFromSnapshotProvisioner snapshotVolumeProvisioner,
ProjectsVolumeProvisioner projectsVolumeProvisioner,
LocalInstallersConfigProvisioner installerConfigProvisioner,
LabelsProvisioner labelsProvisioner,
LocalInstallersBinariesVolumeProvisioner installersBinariesVolumeProvisioner,
RuntimeLabelsProvisioner runtimeLabelsProvisioner,
DockerApiHostEnvVariableProvisioner dockerApiEnvProvisioner,
ToolingServersEnvVarsProvisioner toolingServersEnvVarsProvisioner,
WsAgentServerConfigProvisioner wsAgentServerConfigProvisioner) {
ToolingServersEnvVarsProvisioner agentsServersEnvVarsProvisioner,
WsAgentServerConfigProvisioner wsAgentServerConfigProvisioner,
ServersConverter serversConverter,
EnvVarsConverter envVarsConverter,
MemoryAttributeConverter memoryAttributeConverter) {
this.settingsProvisioners = settingsProvisioners;
this.snapshotProvisioner = snapshotProvisioner;
this.dockerSettingsProvisioners = dockerSettingsProvisioners;
this.snapshotVolumeProvisioner = snapshotVolumeProvisioner;
this.projectsVolumeProvisioner = projectsVolumeProvisioner;
this.installerConfigProvisioner = installerConfigProvisioner;
this.labelsProvisioner = labelsProvisioner;
this.installersBinariesVolumeProvisioner = installersBinariesVolumeProvisioner;
this.runtimeLabelsProvisioner = runtimeLabelsProvisioner;
this.dockerApiEnvProvisioner = dockerApiEnvProvisioner;
this.toolingServersEnvVarsProvisioner = toolingServersEnvVarsProvisioner;
this.agentsServersEnvVarsProvisioner = agentsServersEnvVarsProvisioner;
this.wsAgentServerConfigProvisioner = wsAgentServerConfigProvisioner;
this.serversConverter = serversConverter;
this.envVarsConverter = envVarsConverter;
this.memoryAttributeConverter = memoryAttributeConverter;
}
@Override
@ -69,13 +82,20 @@ public class LocalCheInfrastructureProvisioner implements InfrastructureProvisio
InternalEnvironment envConfig, DockerEnvironment internalEnv, RuntimeIdentity identity)
throws InfrastructureException {
snapshotProvisioner.provision(envConfig, internalEnv, identity);
installerConfigProvisioner.provision(envConfig, internalEnv, identity);
// 1 stage - add Che business logic items to Che model env
// 2 stage - converting Che model env to docker env
serversConverter.provision(envConfig, internalEnv, identity);
envVarsConverter.provision(envConfig, internalEnv, identity);
memoryAttributeConverter.provision(envConfig, internalEnv, identity);
// 3 stage - add docker env items
runtimeLabelsProvisioner.provision(envConfig, internalEnv, identity);
snapshotVolumeProvisioner.provision(envConfig, internalEnv, identity);
installersBinariesVolumeProvisioner.provision(envConfig, internalEnv, identity);
projectsVolumeProvisioner.provision(envConfig, internalEnv, identity);
settingsProvisioners.provision(envConfig, internalEnv, identity);
labelsProvisioner.provision(envConfig, internalEnv, identity);
dockerApiEnvProvisioner.provision(envConfig, internalEnv, identity);
toolingServersEnvVarsProvisioner.provision(envConfig, internalEnv, identity);
wsAgentServerConfigProvisioner.provision(envConfig, internalEnv, identity);
dockerSettingsProvisioners.provision(envConfig, internalEnv, identity);
dockerApiEnvProvisioner.provision(envConfig, internalEnv, identity);
// TODO move to abstract code
agentsServersEnvVarsProvisioner.provision(envConfig, internalEnv, identity);
}
}

View File

@ -10,7 +10,7 @@
*/
package org.eclipse.che.workspace.infrastructure.docker.local;
import static org.eclipse.che.workspace.infrastructure.docker.local.installer.LocalInstallersConfigProvisioner.LOCAL_INSTALLERS_PROVISIONERS;
import static org.eclipse.che.workspace.infrastructure.docker.local.installer.LocalInstallersBinariesVolumeProvisioner.LOCAL_INSTALLERS_PROVISIONERS;
import com.google.inject.AbstractModule;
import com.google.inject.multibindings.Multibinder;

View File

@ -18,27 +18,23 @@ import org.eclipse.che.api.workspace.server.spi.InfrastructureException;
import org.eclipse.che.api.workspace.server.spi.InternalEnvironment;
import org.eclipse.che.workspace.infrastructure.docker.model.DockerEnvironment;
import org.eclipse.che.workspace.infrastructure.docker.provisioner.ConfigurationProvisioner;
import org.eclipse.che.workspace.infrastructure.docker.provisioner.installer.InstallerConfigApplier;
import org.eclipse.che.workspace.infrastructure.docker.provisioner.installer.InstallersConfigProvisioner;
/**
* Provisions an environment with configuration and binaries that comes from installers of machines
* in the environment.
* Provisions an environment with binaries that comes from installers of machines in the
* environment.
*
* @author Alexander Garagatyi
*/
public class LocalInstallersConfigProvisioner extends InstallersConfigProvisioner {
public class LocalInstallersBinariesVolumeProvisioner implements ConfigurationProvisioner {
public static final String LOCAL_INSTALLERS_PROVISIONERS =
"infrastructure.docker.local_installers_provisioners";
private final Set<ConfigurationProvisioner> localInstallerProvisioners;
@Inject
public LocalInstallersConfigProvisioner(
InstallerConfigApplier installerConfigApplier,
public LocalInstallersBinariesVolumeProvisioner(
@Named(LOCAL_INSTALLERS_PROVISIONERS)
Set<ConfigurationProvisioner> localInstallerProvisioners) {
super(installerConfigApplier);
this.localInstallerProvisioners = localInstallerProvisioners;
}
@ -47,7 +43,6 @@ public class LocalInstallersConfigProvisioner extends InstallersConfigProvisione
InternalEnvironment envConfig, DockerEnvironment internalEnv, RuntimeIdentity identity)
throws InfrastructureException {
super.provision(envConfig, internalEnv, identity);
for (ConfigurationProvisioner infrastructureProvisioner : localInstallerProvisioners) {
infrastructureProvisioner.provision(envConfig, internalEnv, identity);
}

View File

@ -0,0 +1,42 @@
/*
* Copyright (c) 2012-2017 Red Hat, Inc.
* 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:
* Red Hat, Inc. - initial API and implementation
*/
package org.eclipse.che.workspace.infrastructure.docker.provisioner.env;
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.workspace.server.spi.InfrastructureException;
import org.eclipse.che.api.workspace.server.spi.InternalEnvironment;
import org.eclipse.che.workspace.infrastructure.docker.InfrastructureProvisioner;
import org.eclipse.che.workspace.infrastructure.docker.model.DockerEnvironment;
/**
* Converts environment variables in {@link MachineConfig} to Docker environment variables.
*
* @author Alexander Garagatyi
*/
public class EnvVarsConverter implements InfrastructureProvisioner {
@Override
public void provision(
InternalEnvironment environment, DockerEnvironment internalEnv, RuntimeIdentity identity)
throws InfrastructureException {
environment
.getMachines()
.forEach(
(machineName, machineConfig) ->
internalEnv
.getContainers()
.get(machineName)
.getEnvironment()
.putAll(machineConfig.getEnv()));
}
}

View File

@ -1,140 +0,0 @@
/*
* Copyright (c) 2012-2017 Red Hat, Inc.
* 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:
* Red Hat, Inc. - initial API and implementation
*/
package org.eclipse.che.workspace.infrastructure.docker.provisioner.installer;
import static com.google.common.base.Strings.isNullOrEmpty;
import static java.lang.String.format;
import static org.eclipse.che.workspace.infrastructure.docker.provisioner.installer.InstallerConfigApplier.PROPERTIES.ENVIRONMENT;
import static org.slf4j.LoggerFactory.getLogger;
import java.util.HashMap;
import java.util.Map;
import org.eclipse.che.api.core.model.workspace.config.ServerConfig;
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.spi.InternalEnvironment;
import org.eclipse.che.api.workspace.server.spi.InternalMachineConfig;
import org.eclipse.che.commons.annotation.Nullable;
import org.eclipse.che.workspace.infrastructure.docker.Labels;
import org.eclipse.che.workspace.infrastructure.docker.model.DockerContainerConfig;
import org.eclipse.che.workspace.infrastructure.docker.model.DockerEnvironment;
import org.slf4j.Logger;
/**
* Applies docker specific properties of the installers to {@link DockerContainerConfig} or {@link
* DockerEnvironment}. Dependencies between installers are respected. This class must be called
* before machines represented by {@link DockerContainerConfig} is started, otherwise changing
* configuration has no effect. </br> The list of supported properties are:
* <li>environment The {@code environment} property contains command separated environment variables
* to set respecting the following format: "name=value".
*
* @author Anatolii Bazko
* @author Alexander Garagatyi
* @see Installer#getProperties()
* @see DockerContainerConfig#getEnvironment()
* @see DockerContainerConfig#getPorts()
* @see DockerContainerConfig#getLabels()
*/
public class InstallerConfigApplier {
private static final Logger LOG = getLogger(InstallerConfigApplier.class);
/**
* Applies docker specific properties to an environment of machines.
*
* @param envConfig environment config with the list of installers that should be injected into
* machine
* @param dockerEnvironment affected environment of machines
* @throws InstallerException if any error occurs
*/
public void apply(InternalEnvironment envConfig, DockerEnvironment dockerEnvironment)
throws InstallerException {
for (Map.Entry<String, InternalMachineConfig> machineEntry :
envConfig.getMachines().entrySet()) {
String machineName = machineEntry.getKey();
InternalMachineConfig machineConf = machineEntry.getValue();
DockerContainerConfig dockerContainer = dockerEnvironment.getContainers().get(machineName);
apply(machineConf, dockerContainer);
}
}
/**
* Applies docker specific properties to a machine.
*
* @param machineConf machine config with the list of installer that should be injected into
* machine
* @param machine affected machine
* @throws InstallerException if any error occurs
*/
public void apply(@Nullable InternalMachineConfig machineConf, DockerContainerConfig machine)
throws InstallerException {
if (machineConf != null) {
for (Installer installer : machineConf.getInstallers()) {
addEnv(machine, installer.getProperties());
addExposedPorts(machine, installer.getServers());
addLabels(machine, installer.getServers());
}
}
}
private void addLabels(
DockerContainerConfig container, Map<String, ? extends ServerConfig> servers) {
container.getLabels().putAll(Labels.newSerializer().servers(servers).labels());
}
private void addEnv(DockerContainerConfig container, Map<String, String> properties) {
String environment = properties.get(ENVIRONMENT.toString());
if (isNullOrEmpty(environment)) {
return;
}
Map<String, String> newEnv = new HashMap<>();
if (container.getEnvironment() != null) {
newEnv.putAll(container.getEnvironment());
}
for (String env : environment.split(",")) {
String[] items = env.split("=");
if (items.length != 2) {
LOG.warn(format("Illegal environment variable '%s' format", env));
continue;
}
String var = items[0];
String name = items[1];
newEnv.put(var, name);
}
container.setEnvironment(newEnv);
}
private void addExposedPorts(
DockerContainerConfig container, Map<String, ? extends ServerConfig> servers) {
for (ServerConfig server : servers.values()) {
container.getExpose().add(server.getPort());
}
}
public enum PROPERTIES {
ENVIRONMENT("environment");
private final String value;
PROPERTIES(String value) {
this.value = value;
}
@Override
public String toString() {
return value;
}
}
}

View File

@ -1,45 +0,0 @@
/*
* Copyright (c) 2012-2017 Red Hat, Inc.
* 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:
* Red Hat, Inc. - initial API and implementation
*/
package org.eclipse.che.workspace.infrastructure.docker.provisioner.installer;
import javax.inject.Inject;
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.api.workspace.server.spi.InternalEnvironment;
import org.eclipse.che.workspace.infrastructure.docker.model.DockerEnvironment;
import org.eclipse.che.workspace.infrastructure.docker.provisioner.ConfigurationProvisioner;
/**
* Provisions an environment with configuration that comes from installers of machines in the
* environment.
*
* @author Alexander Garagatyi
*/
public class InstallersConfigProvisioner implements ConfigurationProvisioner {
private final InstallerConfigApplier installerConfigApplier;
@Inject
public InstallersConfigProvisioner(InstallerConfigApplier installerConfigApplier) {
this.installerConfigApplier = installerConfigApplier;
}
@Override
public void provision(
InternalEnvironment envConfig, DockerEnvironment internalEnv, RuntimeIdentity identity)
throws InfrastructureException {
try {
installerConfigApplier.apply(envConfig, internalEnv);
} catch (InstallerException e) {
throw new InfrastructureException(e.getLocalizedMessage(), e);
}
}
}

View File

@ -21,11 +21,11 @@ import org.eclipse.che.workspace.infrastructure.docker.model.DockerEnvironment;
import org.eclipse.che.workspace.infrastructure.docker.provisioner.ConfigurationProvisioner;
/**
* Provision labels related to workspace configuration to docker environment.
* Provision labels related to workspace runtime to docker environment.
*
* @author Alexander Garagatyi
*/
public class LabelsProvisioner implements ConfigurationProvisioner {
public class RuntimeLabelsProvisioner implements ConfigurationProvisioner {
@Override
public void provision(
InternalEnvironment envConfig, DockerEnvironment internalEnv, RuntimeIdentity identity)
@ -36,12 +36,7 @@ public class LabelsProvisioner implements ConfigurationProvisioner {
DockerContainerConfig container = internalEnv.getContainers().get(name);
container
.getLabels()
.putAll(
Labels.newSerializer()
.machineName(name)
.runtimeId(identity)
.servers(entry.getValue().getServers())
.labels());
.putAll(Labels.newSerializer().machineName(name).runtimeId(identity).labels());
}
}
}

View File

@ -0,0 +1,49 @@
/*
* Copyright (c) 2012-2017 Red Hat, Inc.
* 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:
* Red Hat, Inc. - initial API and implementation
*/
package org.eclipse.che.workspace.infrastructure.docker.provisioner.memory;
import static java.lang.String.format;
import java.util.Map.Entry;
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.workspace.server.spi.InfrastructureException;
import org.eclipse.che.api.workspace.server.spi.InternalEnvironment;
import org.eclipse.che.api.workspace.server.spi.InternalMachineConfig;
import org.eclipse.che.workspace.infrastructure.docker.InfrastructureProvisioner;
import org.eclipse.che.workspace.infrastructure.docker.model.DockerEnvironment;
/** @author Alexander Garagatyi */
public class MemoryAttributeConverter implements InfrastructureProvisioner {
@Override
public void provision(
InternalEnvironment environment, DockerEnvironment internalEnv, RuntimeIdentity identity)
throws InfrastructureException {
for (Entry<String, ? extends InternalMachineConfig> machineEntry :
environment.getMachines().entrySet()) {
String machineName = machineEntry.getKey();
InternalMachineConfig machineConfig = machineEntry.getValue();
String memory = machineConfig.getAttributes().get(MachineConfig.MEMORY_LIMIT_ATTRIBUTE);
if (memory != null) {
try {
internalEnv.getContainers().get(machineName).setMemLimit(Long.parseLong(memory));
} catch (NumberFormatException e) {
throw new InfrastructureException(
format(
"Value of attribute 'memoryLimitBytes' of machine '%s' is illegal", machineName));
}
}
}
}
}

View File

@ -0,0 +1,57 @@
/*
* Copyright (c) 2012-2017 Red Hat, Inc.
* 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:
* Red Hat, Inc. - initial API and implementation
*/
package org.eclipse.che.workspace.infrastructure.docker.provisioner.server;
import org.eclipse.che.api.core.model.workspace.config.ServerConfig;
import org.eclipse.che.api.core.model.workspace.runtime.RuntimeIdentity;
import org.eclipse.che.api.core.model.workspace.runtime.Server;
import org.eclipse.che.api.workspace.server.spi.InfrastructureException;
import org.eclipse.che.api.workspace.server.spi.InternalEnvironment;
import org.eclipse.che.workspace.infrastructure.docker.InfrastructureProvisioner;
import org.eclipse.che.workspace.infrastructure.docker.Labels;
import org.eclipse.che.workspace.infrastructure.docker.model.DockerContainerConfig;
import org.eclipse.che.workspace.infrastructure.docker.model.DockerEnvironment;
/**
* Converts {@link ServerConfig} to Docker related objects to add a server into Docker runtime.
*
* <p>Adds ports mapping, exposes, labels to Docker container config to be able to evaluate {@link
* Server}.
*
* @author Alexander Garagatyi
*/
public class ServersConverter implements InfrastructureProvisioner {
@Override
public void provision(
InternalEnvironment environment, DockerEnvironment internalEnv, RuntimeIdentity identity)
throws InfrastructureException {
environment
.getMachines()
.forEach(
(machineName, machineConfig) -> {
DockerContainerConfig container = internalEnv.getContainers().get(machineName);
container
.getLabels()
.putAll(Labels.newSerializer().servers(machineConfig.getServers()).labels());
machineConfig
.getServers()
.forEach(
(key, value) -> {
container.getPorts().add(value.getPort());
container.getExpose().add(value.getPort());
});
});
}
}

View File

@ -108,7 +108,7 @@ public class ServersMapper {
ports
.entrySet()
.stream()
.filter(entry -> entry.getValue().size() == 1)
.filter(entry -> entry.getValue() != null && entry.getValue().size() == 1)
.map(entry -> toContainerPort(entry.getKey(), entry.getValue().get(0)))
.toArray(ContainerPort[]::new),
configs);

View File

@ -18,11 +18,9 @@ import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import static org.testng.Assert.assertEquals;
import static org.testng.Assert.assertEqualsNoOrder;
import com.google.common.collect.ImmutableMap;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
@ -133,70 +131,6 @@ public class EnvironmentParserTest {
verify(envParser).parse(environment);
}
@Test
public void shouldOverrideMemoryLimitFromExtendedMachineInAnEnv() throws Exception {
// given
Map<String, InternalMachineConfig> machines =
ImmutableMap.of(
"machine1", mockMachine(singletonMap("memoryLimitBytes", "101010")),
"machine2", mockMachine());
when(environment.getMachines()).thenReturn(machines);
DockerEnvironment dockerEnv = new DockerEnvironment();
dockerEnv.getContainers().put("machine1", new DockerContainerConfig());
dockerEnv.getContainers().put("machine2", new DockerContainerConfig());
when(envParser.parse(environment)).thenReturn(dockerEnv);
DockerEnvironment expected = new DockerEnvironment();
expected.getContainers().put("machine1", new DockerContainerConfig().setMemLimit(101010L));
expected.getContainers().put("machine2", new DockerContainerConfig());
// when
DockerEnvironment actual = parser.parse(environment);
// then
assertEquals(actual, expected);
}
@Test(
expectedExceptions = ValidationException.class,
expectedExceptionsMessageRegExp =
"Value of attribute 'memoryLimitBytes' of machine '.*' is illegal"
)
public void shouldThrowExceptionInCaseFailedParseMemoryLimit() throws Exception {
// given
when(machine.getAttributes()).thenReturn(singletonMap("memoryLimitBytes", "AF"));
// when
parser.parse(environment);
}
@Test(dataProvider = "environmentWithServersProvider")
public void shouldAddExposesFromExtendedMachineServers(
InternalMachineConfig machineConfig,
DockerContainerConfig parsedContainer,
List<String> expectedExposes)
throws Exception {
// given
when(environment.getMachines()).thenReturn(singletonMap(DEFAULT_MACHINE_NAME, machineConfig));
when(dockerEnv.getContainers()).thenReturn(singletonMap(DEFAULT_MACHINE_NAME, parsedContainer));
// when
DockerEnvironment actual = parser.parse(environment);
// then
// prevent failures because of reordered entries of expose field
assertEquals(actual.getContainers().size(), 1);
assertEqualsNoOrder(
actual.getContainers().get(DEFAULT_MACHINE_NAME).getExpose().toArray(),
expectedExposes.toArray(),
String.format(
"Arrays are not equal. Actual: '%s'. Expected: '%s'",
Arrays.toString(actual.getContainers().get(DEFAULT_MACHINE_NAME).getExpose().toArray()),
Arrays.toString(expectedExposes.toArray())));
}
@DataProvider(name = "environmentWithServersProvider")
public static Object[][] environmentWithServersProvider() {
// Format of result array:

View File

@ -15,6 +15,7 @@ import static java.util.Collections.emptyList;
import static java.util.Collections.emptyMap;
import static java.util.Collections.singletonList;
import static java.util.Collections.singletonMap;
import static org.eclipse.che.api.core.model.workspace.config.MachineConfig.MEMORY_LIMIT_ATTRIBUTE;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
@ -81,7 +82,7 @@ public class EnvironmentValidatorTest {
when(machineConfig.getServers())
.thenReturn(singletonMap(Constants.SERVER_WS_AGENT_HTTP_REFERENCE, server));
Map<String, String> attributes =
ImmutableMap.of("testKey", "value", "memoryLimitBytes", "1000000000");
ImmutableMap.of("testKey", "value", MEMORY_LIMIT_ATTRIBUTE, "1000000000");
when(machineConfig.getAttributes()).thenReturn(attributes);
when(container.getExpose()).thenReturn(asList("8090", "9090/tcp", "7070/udp"));
when(container.getLinks()).thenReturn(singletonList(machine2Name + ":alias1"));
@ -277,27 +278,6 @@ public class EnvironmentValidatorTest {
environmentValidator.validate(environment, dockerEnvironment);
}
@Test(
expectedExceptions = ValidationException.class,
expectedExceptionsMessageRegExp =
"Value of attribute 'memoryLimitBytes' of machine '.*' in environment is illegal",
dataProvider = "memoryAttributeValue"
)
public void shouldFailIfMemoryAttributeIsIllegal(String memoryAttributeValue) throws Exception {
// given
Map<String, String> attributes =
ImmutableMap.of("testKey", "value", "memoryLimitBytes", memoryAttributeValue);
when(machineConfig.getAttributes()).thenReturn(attributes);
// when
environmentValidator.validate(environment, dockerEnvironment);
}
@DataProvider(name = "memoryAttributeValue")
public static Object[][] memoryAttributeValue() {
return new Object[][] {{"aa"}, {""}, {"!"}, {"156a"}, {"0"}, {"-1"}};
}
@Test(
expectedExceptions = ValidationException.class,
expectedExceptionsMessageRegExp =

View File

@ -15,16 +15,18 @@ import static org.mockito.ArgumentMatchers.eq;
import org.eclipse.che.api.core.model.workspace.runtime.RuntimeIdentity;
import org.eclipse.che.api.workspace.server.spi.InternalEnvironment;
import org.eclipse.che.workspace.infrastructure.docker.local.dod.DockerApiHostEnvVariableProvisioner;
import org.eclipse.che.workspace.infrastructure.docker.local.installer.LocalInstallersConfigProvisioner;
import org.eclipse.che.workspace.infrastructure.docker.local.installer.LocalInstallersBinariesVolumeProvisioner;
import org.eclipse.che.workspace.infrastructure.docker.local.installer.WsAgentServerConfigProvisioner;
import org.eclipse.che.workspace.infrastructure.docker.local.projects.ProjectsVolumeProvisioner;
import org.eclipse.che.workspace.infrastructure.docker.model.DockerEnvironment;
import org.eclipse.che.workspace.infrastructure.docker.provisioner.ContainerSystemSettingsProvisionersApplier;
import org.eclipse.che.workspace.infrastructure.docker.provisioner.labels.LabelsProvisioner;
import org.eclipse.che.workspace.infrastructure.docker.provisioner.env.EnvVarsConverter;
import org.eclipse.che.workspace.infrastructure.docker.provisioner.labels.RuntimeLabelsProvisioner;
import org.eclipse.che.workspace.infrastructure.docker.provisioner.memory.MemoryAttributeConverter;
import org.eclipse.che.workspace.infrastructure.docker.provisioner.server.ServersConverter;
import org.eclipse.che.workspace.infrastructure.docker.provisioner.server.ToolingServersEnvVarsProvisioner;
import org.eclipse.che.workspace.infrastructure.docker.provisioner.snapshot.ExcludeFoldersFromSnapshotProvisioner;
import org.mockito.InOrder;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.Mockito;
import org.mockito.testng.MockitoTestNGListener;
@ -35,23 +37,42 @@ import org.testng.annotations.Test;
/** @author Alexander Garagatyi */
@Listeners(MockitoTestNGListener.class)
public class LocalCheInfrastructureProvisionerTest {
@Mock private ContainerSystemSettingsProvisionersApplier settingsProvisioners;
@Mock private ExcludeFoldersFromSnapshotProvisioner snapshotProvisioner;
@Mock private ProjectsVolumeProvisioner projectsVolumeProvisioner;
@Mock private LocalInstallersConfigProvisioner installerConfigProvisioner;
@Mock private LabelsProvisioner labelsProvisioner;
@Mock private LocalInstallersBinariesVolumeProvisioner installerConfigProvisioner;
@Mock private RuntimeLabelsProvisioner labelsProvisioner;
@Mock private DockerApiHostEnvVariableProvisioner dockerApiEnvProvisioner;
@Mock private ToolingServersEnvVarsProvisioner toolingServersEnvVarsProvisioner;
@Mock private InternalEnvironment environment;
@Mock private DockerEnvironment dockerEnvironment;
@Mock private RuntimeIdentity runtimeIdentity;
@Mock private WsAgentServerConfigProvisioner wsAgentServerConfigProvisioner;
@InjectMocks private LocalCheInfrastructureProvisioner provisioner;
@Mock private ServersConverter serversConverter;
@Mock private EnvVarsConverter envVarsConverter;
@Mock private MemoryAttributeConverter memoryAttributeConverter;
private LocalCheInfrastructureProvisioner provisioner;
private Object[] allInnerProvisioners;
@BeforeMethod
public void setUp() throws Exception {
provisioner =
new LocalCheInfrastructureProvisioner(
settingsProvisioners,
snapshotProvisioner,
projectsVolumeProvisioner,
installerConfigProvisioner,
labelsProvisioner,
dockerApiEnvProvisioner,
toolingServersEnvVarsProvisioner,
wsAgentServerConfigProvisioner,
serversConverter,
envVarsConverter,
memoryAttributeConverter);
allInnerProvisioners =
new Object[] {
settingsProvisioners,
@ -61,7 +82,10 @@ public class LocalCheInfrastructureProvisionerTest {
labelsProvisioner,
dockerApiEnvProvisioner,
toolingServersEnvVarsProvisioner,
wsAgentServerConfigProvisioner
wsAgentServerConfigProvisioner,
serversConverter,
envVarsConverter,
memoryAttributeConverter
};
}
@ -72,6 +96,18 @@ public class LocalCheInfrastructureProvisionerTest {
// then
InOrder inOrder = Mockito.inOrder((Object[]) allInnerProvisioners);
inOrder
.verify(serversConverter)
.provision(eq(environment), eq(dockerEnvironment), eq(runtimeIdentity));
inOrder
.verify(envVarsConverter)
.provision(eq(environment), eq(dockerEnvironment), eq(runtimeIdentity));
inOrder
.verify(memoryAttributeConverter)
.provision(eq(environment), eq(dockerEnvironment), eq(runtimeIdentity));
inOrder
.verify(labelsProvisioner)
.provision(eq(environment), eq(dockerEnvironment), eq(runtimeIdentity));
inOrder
.verify(snapshotProvisioner)
.provision(eq(environment), eq(dockerEnvironment), eq(runtimeIdentity));
@ -82,10 +118,10 @@ public class LocalCheInfrastructureProvisionerTest {
.verify(projectsVolumeProvisioner)
.provision(eq(environment), eq(dockerEnvironment), eq(runtimeIdentity));
inOrder
.verify(settingsProvisioners)
.verify(wsAgentServerConfigProvisioner)
.provision(eq(environment), eq(dockerEnvironment), eq(runtimeIdentity));
inOrder
.verify(labelsProvisioner)
.verify(settingsProvisioners)
.provision(eq(environment), eq(dockerEnvironment), eq(runtimeIdentity));
inOrder
.verify(dockerApiEnvProvisioner)
@ -93,9 +129,6 @@ public class LocalCheInfrastructureProvisionerTest {
inOrder
.verify(toolingServersEnvVarsProvisioner)
.provision(eq(environment), eq(dockerEnvironment), eq(runtimeIdentity));
inOrder
.verify(wsAgentServerConfigProvisioner)
.provision(eq(environment), eq(dockerEnvironment), eq(runtimeIdentity));
inOrder.verifyNoMoreInteractions();
}
}

View File

@ -1,127 +0,0 @@
/*
* Copyright (c) 2012-2017 Red Hat, Inc.
* 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:
* Red Hat, Inc. - initial API and implementation
*/
package org.eclipse.che.workspace.infrastructure.docker.provisioner.installer;
import static java.util.Arrays.asList;
import static java.util.Collections.emptyMap;
import static java.util.Collections.singletonList;
import static java.util.Collections.singletonMap;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
import static org.testng.Assert.assertEquals;
import static org.testng.Assert.assertTrue;
import java.util.List;
import java.util.Map;
import org.eclipse.che.api.core.model.workspace.config.ServerConfig;
import org.eclipse.che.api.installer.server.model.impl.InstallerImpl;
import org.eclipse.che.api.workspace.server.spi.InternalMachineConfig;
import org.eclipse.che.workspace.infrastructure.docker.model.DockerContainerConfig;
import org.mockito.Mock;
import org.mockito.testng.MockitoTestNGListener;
import org.testng.annotations.BeforeMethod;
import org.testng.annotations.Listeners;
import org.testng.annotations.Test;
/** @author Anatolii Bazko */
@Listeners(value = {MockitoTestNGListener.class})
public class InstallerConfigApplierTest {
@Mock private InstallerImpl installer1;
@Mock private InstallerImpl installer2;
@Mock private InstallerImpl installer3;
private InstallerConfigApplier installerConfigApplier;
@BeforeMethod
public void setUp() throws Exception {
installerConfigApplier = new InstallerConfigApplier();
when(installer1.getScript()).thenReturn("script1");
when(installer1.getDependencies()).thenReturn(singletonList("installer3"));
when(installer2.getScript()).thenReturn("script2");
when(installer2.getDependencies()).thenReturn(singletonList("installer2"));
when(installer3.getScript()).thenReturn("script3");
}
@Test
public void shouldAddLabels() throws Exception {
final ServerConfig serverConf1 = mock(ServerConfig.class);
when(serverConf1.getPort()).thenReturn("1111/udp");
when(serverConf1.getProtocol()).thenReturn("http");
when(serverConf1.getPath()).thenReturn("b");
when(installer1.getServers()).thenAnswer(invocation -> singletonMap("a", serverConf1));
DockerContainerConfig containerConf = new DockerContainerConfig();
InternalMachineConfig machineConfig = mock(InternalMachineConfig.class);
when(machineConfig.getInstallers()).thenReturn(singletonList(installer1));
installerConfigApplier.apply(machineConfig, containerConf);
Map<String, String> labels = containerConf.getLabels();
assertEquals(labels.size(), 3);
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
public void shouldAddExposedPorts() throws Exception {
final ServerConfig serverConf1 = mock(ServerConfig.class);
final ServerConfig serverConfig = mock(ServerConfig.class);
when(serverConf1.getPort()).thenReturn("1111/udp");
when(serverConfig.getPort()).thenReturn("2222/tcp");
when(installer1.getServers()).thenAnswer(invocation -> singletonMap("a", serverConf1));
when(installer2.getServers()).thenAnswer(invocation -> singletonMap("b", serverConfig));
when(installer3.getServers()).thenReturn(emptyMap());
DockerContainerConfig containerConf = new DockerContainerConfig();
InternalMachineConfig machineConfig = mock(InternalMachineConfig.class);
when(machineConfig.getInstallers()).thenReturn(asList(installer1, installer2, installer3));
installerConfigApplier.apply(machineConfig, containerConf);
List<String> exposedPorts = containerConf.getExpose();
assertTrue(exposedPorts.contains("1111/udp"));
assertTrue(exposedPorts.contains("2222/tcp"));
}
@Test
public void shouldAddEnvVariables() throws Exception {
when(installer1.getProperties()).thenReturn(singletonMap("environment", "p1=v1,p2=v2"));
when(installer2.getProperties()).thenReturn(singletonMap("environment", "p3=v3"));
DockerContainerConfig containerConf = new DockerContainerConfig();
InternalMachineConfig machineConfig = mock(InternalMachineConfig.class);
when(machineConfig.getInstallers()).thenReturn(asList(installer1, installer2));
installerConfigApplier.apply(machineConfig, containerConf);
Map<String, String> env = containerConf.getEnvironment();
assertEquals(env.size(), 3);
assertEquals(env.get("p1"), "v1");
assertEquals(env.get("p2"), "v2");
assertEquals(env.get("p3"), "v3");
}
@Test
public void shouldIgnoreEnvironmentIfIllegalFormat() throws Exception {
when(installer1.getProperties()).thenReturn(singletonMap("environment", "p1"));
DockerContainerConfig containerConf = new DockerContainerConfig();
InternalMachineConfig machineConfig = mock(InternalMachineConfig.class);
when(machineConfig.getInstallers()).thenReturn(singletonList(installer1));
installerConfigApplier.apply(machineConfig, containerConf);
Map<String, String> env = containerConf.getEnvironment();
assertEquals(env.size(), 0);
}
}

View File

@ -74,10 +74,6 @@
<groupId>org.eclipse.che.core</groupId>
<artifactId>che-core-api-installer</artifactId>
</dependency>
<dependency>
<groupId>org.eclipse.che.core</groupId>
<artifactId>che-core-api-installer-shared</artifactId>
</dependency>
<dependency>
<groupId>org.eclipse.che.core</groupId>
<artifactId>che-core-api-model</artifactId>

View File

@ -17,8 +17,12 @@ import org.eclipse.che.api.workspace.server.spi.InfrastructureException;
import org.eclipse.che.api.workspace.server.spi.InternalEnvironment;
import org.eclipse.che.workspace.infrastructure.openshift.environment.OpenShiftEnvironment;
import org.eclipse.che.workspace.infrastructure.openshift.provision.UniqueNamesProvisioner;
import org.eclipse.che.workspace.infrastructure.openshift.provision.env.EnvVarsConverter;
import org.eclipse.che.workspace.infrastructure.openshift.provision.installer.InstallerConfigProvisioner;
import org.eclipse.che.workspace.infrastructure.openshift.provision.labels.PodNameLabelProvisioner;
import org.eclipse.che.workspace.infrastructure.openshift.provision.restartpolicy.RestartPolicyRewriter;
import org.eclipse.che.workspace.infrastructure.openshift.provision.route.TlsRouteProvisioner;
import org.eclipse.che.workspace.infrastructure.openshift.provision.server.ServersConverter;
import org.eclipse.che.workspace.infrastructure.openshift.provision.volume.PersistentVolumeClaimProvisioner;
/**
@ -26,33 +30,54 @@ import org.eclipse.che.workspace.infrastructure.openshift.provision.volume.Persi
* the desired order, which corresponds to the needs of the OpenShift infrastructure.
*
* @author Anton Korneta
* @author Alexander Garagatyi
*/
@Singleton
public class OpenShiftInfrastructureProvisioner {
private final InstallerConfigProvisioner installerConfigProvisioner;
private final PersistentVolumeClaimProvisioner persistentVolumeClaimProvisioner;
private final UniqueNamesProvisioner uniqueNamesProvisioner;
private final TlsRouteProvisioner tlsRouteProvisioner;
private final ServersConverter serversConverter;
private final EnvVarsConverter envVarsConverter;
private final RestartPolicyRewriter restartPolicyRewriter;
private final PodNameLabelProvisioner podNameLabelProvisioner;
@Inject
public OpenShiftInfrastructureProvisioner(
InstallerConfigProvisioner installerConfigProvisioner,
PersistentVolumeClaimProvisioner projectVolumeProvisioner,
UniqueNamesProvisioner uniqueNamesProvisioner,
TlsRouteProvisioner tlsRouteProvisioner) {
TlsRouteProvisioner tlsRouteProvisioner,
ServersConverter serversConverter,
EnvVarsConverter envVarsConverter,
RestartPolicyRewriter restartPolicyRewriter,
PodNameLabelProvisioner podNameLabelProvisioner) {
this.installerConfigProvisioner = installerConfigProvisioner;
this.persistentVolumeClaimProvisioner = projectVolumeProvisioner;
this.uniqueNamesProvisioner = uniqueNamesProvisioner;
this.tlsRouteProvisioner = tlsRouteProvisioner;
this.serversConverter = serversConverter;
this.envVarsConverter = envVarsConverter;
this.restartPolicyRewriter = restartPolicyRewriter;
this.podNameLabelProvisioner = podNameLabelProvisioner;
}
public void provision(
InternalEnvironment environment, OpenShiftEnvironment osEnv, RuntimeIdentity identity)
throws InfrastructureException {
// 1 stage - add Che business logic items to Che model env
installerConfigProvisioner.provision(environment, osEnv, identity);
// 2 stage - converting Che model env to OpenShift env
serversConverter.provision(environment, osEnv, identity);
envVarsConverter.provision(environment, osEnv, identity);
// 3 stage - add OpenShift env items
podNameLabelProvisioner.provision(environment, osEnv, identity);
restartPolicyRewriter.provision(environment, osEnv, identity);
persistentVolumeClaimProvisioner.provision(environment, osEnv, identity);
uniqueNamesProvisioner.provision(environment, osEnv, identity);
tlsRouteProvisioner.provision(environment, osEnv, identity);
}
// TODO memory attribute provisioner
}

View File

@ -11,34 +11,25 @@
package org.eclipse.che.workspace.infrastructure.openshift.environment;
import static java.lang.String.format;
import static org.eclipse.che.workspace.infrastructure.openshift.Constants.CHE_POD_NAME_LABEL;
import io.fabric8.kubernetes.api.model.Container;
import io.fabric8.kubernetes.api.model.EnvVar;
import io.fabric8.kubernetes.api.model.HasMetadata;
import io.fabric8.kubernetes.api.model.KubernetesList;
import io.fabric8.kubernetes.api.model.ObjectMeta;
import io.fabric8.kubernetes.api.model.PersistentVolumeClaim;
import io.fabric8.kubernetes.api.model.Pod;
import io.fabric8.kubernetes.api.model.PodSpec;
import io.fabric8.kubernetes.api.model.Service;
import io.fabric8.openshift.api.model.DeploymentConfig;
import io.fabric8.openshift.api.model.Route;
import io.fabric8.openshift.client.OpenShiftClient;
import java.io.ByteArrayInputStream;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import javax.inject.Inject;
import org.eclipse.che.api.core.ValidationException;
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.InternalEnvironment;
import org.eclipse.che.api.workspace.server.spi.InternalEnvironment.InternalRecipe;
import org.eclipse.che.api.workspace.server.spi.InternalMachineConfig;
import org.eclipse.che.workspace.infrastructure.openshift.OpenShiftClientFactory;
import org.eclipse.che.workspace.infrastructure.openshift.ServerExposer;
import org.eclipse.che.workspace.infrastructure.openshift.environment.OpenShiftEnvironment.Builder;
/**
@ -64,8 +55,6 @@ public class OpenShiftEnvironmentParser {
static final String PVC_IGNORED_WARNING_MESSAGE =
"Persistent volume claims specified in OpenShift recipe are ignored.";
static final String DEFAULT_RESTART_POLICY = "Never";
private final OpenShiftClientFactory clientFactory;
@Inject
@ -144,63 +133,7 @@ public class OpenShiftEnvironmentParser {
new WarningImpl(PVC_IGNORED_WARNING_CODE, PVC_IGNORED_WARNING_MESSAGE));
}
OpenShiftEnvironment openShiftEnv = openShiftEnvBuilder.build();
normalizeEnvironment(openShiftEnv, environment);
return openShiftEnv;
}
private void normalizeEnvironment(
OpenShiftEnvironment openShiftEnvironment, InternalEnvironment environment)
throws ValidationException {
for (Pod podConfig : openShiftEnvironment.getPods().values()) {
final String podName = podConfig.getMetadata().getName();
getLabels(podConfig).put(CHE_POD_NAME_LABEL, podName);
final PodSpec podSpec = podConfig.getSpec();
rewriteRestartPolicy(podSpec, podName, environment);
for (Container containerConfig : podSpec.getContainers()) {
String machineName = podName + '/' + containerConfig.getName();
InternalMachineConfig machineConfig = environment.getMachines().get(machineName);
if (machineConfig != null && !machineConfig.getServers().isEmpty()) {
ServerExposer serverExposer =
new ServerExposer(machineName, containerConfig, openShiftEnvironment);
serverExposer.expose(machineConfig.getServers());
for (Entry<String, String> envEntry : machineConfig.getEnv().entrySet()) {
putEnv(containerConfig.getEnv(), envEntry.getKey(), envEntry.getValue());
}
}
}
}
}
private Map<String, String> getLabels(Pod pod) {
ObjectMeta metadata = pod.getMetadata();
if (metadata == null) {
metadata = new ObjectMeta();
pod.setMetadata(metadata);
}
Map<String, String> labels = metadata.getLabels();
if (labels == null) {
labels = new HashMap<>();
metadata.setLabels(labels);
}
return labels;
}
private void rewriteRestartPolicy(PodSpec podSpec, String podName, InternalEnvironment env) {
final String restartPolicy = podSpec.getRestartPolicy();
if (restartPolicy != null && !DEFAULT_RESTART_POLICY.equalsIgnoreCase(restartPolicy)) {
final String warnMsg =
format(
"Restart policy '%s' for pod '%s' is rewritten with %s",
restartPolicy, podName, DEFAULT_RESTART_POLICY);
env.addWarning(new WarningImpl(101, warnMsg));
}
podSpec.setRestartPolicy(DEFAULT_RESTART_POLICY);
return openShiftEnvBuilder.build();
}
private void checkNotNull(Object object, String errorMessage) throws ValidationException {
@ -208,9 +141,4 @@ public class OpenShiftEnvironmentParser {
throw new ValidationException(errorMessage);
}
}
private void putEnv(List<EnvVar> envs, String key, String value) {
envs.removeIf(env -> key.equals(env.getName()));
envs.add(new EnvVar(key, value, null));
}
}

View File

@ -0,0 +1,56 @@
/*
* Copyright (c) 2012-2017 Red Hat, Inc.
* 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:
* Red Hat, Inc. - initial API and implementation
*/
package org.eclipse.che.workspace.infrastructure.openshift.provision.env;
import io.fabric8.kubernetes.api.model.Container;
import io.fabric8.kubernetes.api.model.EnvVar;
import io.fabric8.kubernetes.api.model.Pod;
import javax.inject.Singleton;
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.workspace.server.spi.InfrastructureException;
import org.eclipse.che.api.workspace.server.spi.InternalEnvironment;
import org.eclipse.che.api.workspace.server.spi.InternalMachineConfig;
import org.eclipse.che.workspace.infrastructure.openshift.environment.OpenShiftEnvironment;
import org.eclipse.che.workspace.infrastructure.openshift.provision.ConfigurationProvisioner;
/**
* Converts environment variables in {@link MachineConfig} to OpenShift environment variables.
*
* @author Alexander Garagatyi
*/
@Singleton
public class EnvVarsConverter implements ConfigurationProvisioner {
@Override
public void provision(
InternalEnvironment environment, OpenShiftEnvironment osEnv, RuntimeIdentity identity)
throws InfrastructureException {
for (Pod pod : osEnv.getPods().values()) {
String podName = pod.getMetadata().getName();
for (Container container : pod.getSpec().getContainers()) {
String containerName = container.getName();
String machineName = podName + "/" + containerName;
InternalMachineConfig machineConf = environment.getMachines().get(machineName);
if (machineConf != null) {
machineConf
.getEnv()
.forEach(
(key, value) -> {
container.getEnv().removeIf(env -> key.equals(env.getName()));
container.getEnv().add(new EnvVar(key, value, null));
});
}
}
}
}
}

View File

@ -10,19 +10,11 @@
*/
package org.eclipse.che.workspace.infrastructure.openshift.provision.installer;
import static com.google.common.base.Strings.isNullOrEmpty;
import static java.lang.String.format;
import static org.slf4j.LoggerFactory.getLogger;
import io.fabric8.kubernetes.api.model.Container;
import io.fabric8.kubernetes.api.model.EnvVar;
import io.fabric8.kubernetes.api.model.Pod;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import javax.inject.Inject;
import javax.inject.Named;
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.shared.model.Installer;
import org.eclipse.che.api.workspace.server.WsAgentMachineFinderUtil;
import org.eclipse.che.api.workspace.server.spi.InfrastructureException;
import org.eclipse.che.api.workspace.server.spi.InternalEnvironment;
@ -30,30 +22,15 @@ import org.eclipse.che.api.workspace.server.spi.InternalMachineConfig;
import org.eclipse.che.api.workspace.server.token.MachineTokenProvider;
import org.eclipse.che.workspace.infrastructure.openshift.environment.OpenShiftEnvironment;
import org.eclipse.che.workspace.infrastructure.openshift.provision.ConfigurationProvisioner;
import org.slf4j.Logger;
/**
* Applies OpenShift specific properties of the installers to {@link OpenShiftEnvironment}.
*
* <p>This class must be called before OpenShift environment is started, otherwise changing
* configuration has no effect.
*
* <p>This class performs following changes to environment: <br>
* - adds environment variable to {@link Container containers} that are required by installers or
* agents. <br>
* - adds environment variables which are specified in properties of installer configuration. The
* environment property contains environment variables in the following format:
* "env1=value1,env2=value2,..."; <br>
* - performs all required changes that are needed for exposing installers' servers.
* Adds environment variable to {@link MachineConfig} that are required by installers or agents.
*
* @author Sergii Leshchenko
* @author Alexander Garagatyi
*/
public class InstallerConfigProvisioner implements ConfigurationProvisioner {
private static final Logger LOG = getLogger(InstallerConfigProvisioner.class);
private static final String ENVIRONMENT_PROPERTY = "environment";
private final MachineTokenProvider machineTokenProvider;
private final String cheServerEndpoint;
@ -73,56 +50,20 @@ public class InstallerConfigProvisioner implements ConfigurationProvisioner {
WsAgentMachineFinderUtil.getWsAgentServerMachine(environment)
.orElseThrow(() -> new InfrastructureException("Machine with wsagent not found"));
for (Pod pod : osEnv.getPods().values()) {
String podName = pod.getMetadata().getName();
for (Container container : pod.getSpec().getContainers()) {
String containerName = container.getName();
String machineName = podName + "/" + containerName;
InternalMachineConfig machineConf = environment.getMachines().get(machineName);
for (Entry<String, InternalMachineConfig> machineEntry : environment.getMachines().entrySet()) {
InternalMachineConfig config = machineEntry.getValue();
for (Installer installer : machineConf.getInstallers()) {
provisionEnv(container, installer.getProperties());
}
// CHE_API is used by installers for agent binary downloading
config.getEnv().put("CHE_API", cheServerEndpoint);
// CHE_API is used by installers for agent binary downloading
putEnv(container.getEnv(), "CHE_API", cheServerEndpoint);
config.getEnv().put("USER_TOKEN", machineTokenProvider.getToken(identity.getWorkspaceId()));
putEnv(
container.getEnv(),
"USER_TOKEN",
machineTokenProvider.getToken(identity.getWorkspaceId()));
// TODO incorrect place for env variable addition. workspace ID is needed for wsagent
// server, not installer
// WORKSPACE_ID is required only by workspace agent
if (devMachineName.equals(machineName)) {
putEnv(container.getEnv(), "CHE_WORKSPACE_ID", identity.getWorkspaceId());
}
// TODO incorrect place for env variable addition. workspace ID is needed for wsagent
// server, not installer
// WORKSPACE_ID is required only by workspace agent
if (devMachineName.equals(machineEntry.getKey())) {
config.getEnv().put("CHE_WORKSPACE_ID", identity.getWorkspaceId());
}
}
}
private void putEnv(List<EnvVar> envs, String key, String value) {
envs.removeIf(env -> key.equals(env.getName()));
envs.add(new EnvVar(key, value, null));
}
private void provisionEnv(Container container, Map<String, String> properties) {
String environment = properties.get(ENVIRONMENT_PROPERTY);
if (isNullOrEmpty(environment)) {
return;
}
for (String env : environment.split(",")) {
String[] items = env.split("=");
if (items.length != 2) {
LOG.warn(format("Illegal environment variable '%s' format", env));
continue;
}
String name = items[0];
String value = items[1];
container.getEnv().add(new EnvVar(name, value, null));
}
}
}

View File

@ -0,0 +1,53 @@
/*
* Copyright (c) 2012-2017 Red Hat, Inc.
* 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:
* Red Hat, Inc. - initial API and implementation
*/
package org.eclipse.che.workspace.infrastructure.openshift.provision.labels;
import static org.eclipse.che.workspace.infrastructure.openshift.Constants.CHE_POD_NAME_LABEL;
import io.fabric8.kubernetes.api.model.ObjectMeta;
import io.fabric8.kubernetes.api.model.Pod;
import java.util.HashMap;
import java.util.Map;
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.InternalEnvironment;
import org.eclipse.che.workspace.infrastructure.openshift.environment.OpenShiftEnvironment;
import org.eclipse.che.workspace.infrastructure.openshift.provision.ConfigurationProvisioner;
/** @author Alexander Garagatyi */
public class PodNameLabelProvisioner implements ConfigurationProvisioner {
@Override
public void provision(
InternalEnvironment environment, OpenShiftEnvironment osEnv, RuntimeIdentity identity)
throws InfrastructureException {
for (Pod podConfig : osEnv.getPods().values()) {
final String podName = podConfig.getMetadata().getName();
getLabels(podConfig).put(CHE_POD_NAME_LABEL, podName);
}
}
private Map<String, String> getLabels(Pod pod) {
ObjectMeta metadata = pod.getMetadata();
if (metadata == null) {
metadata = new ObjectMeta();
pod.setMetadata(metadata);
}
Map<String, String> labels = metadata.getLabels();
if (labels == null) {
labels = new HashMap<>();
metadata.setLabels(labels);
}
return labels;
}
}

View File

@ -0,0 +1,56 @@
/*
* Copyright (c) 2012-2017 Red Hat, Inc.
* 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:
* Red Hat, Inc. - initial API and implementation
*/
package org.eclipse.che.workspace.infrastructure.openshift.provision.restartpolicy;
import static java.lang.String.format;
import io.fabric8.kubernetes.api.model.Pod;
import io.fabric8.kubernetes.api.model.PodSpec;
import org.eclipse.che.api.core.model.workspace.runtime.RuntimeIdentity;
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.InternalEnvironment;
import org.eclipse.che.workspace.infrastructure.openshift.environment.OpenShiftEnvironment;
import org.eclipse.che.workspace.infrastructure.openshift.provision.ConfigurationProvisioner;
/**
* Rewrites restart policy to supported one - 'Never'.
*
* @author Alexander Garagatyi
*/
public class RestartPolicyRewriter implements ConfigurationProvisioner {
static final String DEFAULT_RESTART_POLICY = "Never";
@Override
public void provision(
InternalEnvironment environment, OpenShiftEnvironment osEnv, RuntimeIdentity identity)
throws InfrastructureException {
for (Pod podConfig : osEnv.getPods().values()) {
final String podName = podConfig.getMetadata().getName();
final PodSpec podSpec = podConfig.getSpec();
rewriteRestartPolicy(podSpec, podName, environment);
}
}
private void rewriteRestartPolicy(PodSpec podSpec, String podName, InternalEnvironment env) {
final String restartPolicy = podSpec.getRestartPolicy();
if (restartPolicy != null && !DEFAULT_RESTART_POLICY.equalsIgnoreCase(restartPolicy)) {
final String warnMsg =
format(
"Restart policy '%s' for pod '%s' is rewritten with %s",
restartPolicy, podName, DEFAULT_RESTART_POLICY);
env.addWarning(new WarningImpl(101, warnMsg));
}
podSpec.setRestartPolicy(DEFAULT_RESTART_POLICY);
}
}

View File

@ -0,0 +1,57 @@
/*
* Copyright (c) 2012-2017 Red Hat, Inc.
* 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:
* Red Hat, Inc. - initial API and implementation
*/
package org.eclipse.che.workspace.infrastructure.openshift.provision.server;
import io.fabric8.kubernetes.api.model.Container;
import io.fabric8.kubernetes.api.model.Pod;
import io.fabric8.kubernetes.api.model.PodSpec;
import java.util.Map;
import javax.inject.Singleton;
import org.eclipse.che.api.core.model.workspace.config.ServerConfig;
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.InternalEnvironment;
import org.eclipse.che.api.workspace.server.spi.InternalMachineConfig;
import org.eclipse.che.workspace.infrastructure.openshift.ServerExposer;
import org.eclipse.che.workspace.infrastructure.openshift.environment.OpenShiftEnvironment;
import org.eclipse.che.workspace.infrastructure.openshift.provision.ConfigurationProvisioner;
/**
* Converts {@link ServerConfig} to OpenShift related objects to add a server into OpenShift
* runtime.
*
* <p>Adds OpenShift objects by calling {@link ServerExposer#expose(Map)} on each machine with
* servers.
*
* @author Alexander Garagatyi
*/
@Singleton
public class ServersConverter implements ConfigurationProvisioner {
@Override
public void provision(
InternalEnvironment environment, OpenShiftEnvironment osEnv, RuntimeIdentity identity)
throws InfrastructureException {
for (Pod podConfig : osEnv.getPods().values()) {
final String podName = podConfig.getMetadata().getName();
final PodSpec podSpec = podConfig.getSpec();
for (Container containerConfig : podSpec.getContainers()) {
String machineName = podName + '/' + containerConfig.getName();
InternalMachineConfig machineConfig = environment.getMachines().get(machineName);
if (machineConfig != null && !machineConfig.getServers().isEmpty()) {
ServerExposer serverExposer = new ServerExposer(machineName, containerConfig, osEnv);
serverExposer.expose(machineConfig.getServers());
}
}
}
}
}

View File

@ -17,8 +17,12 @@ import org.eclipse.che.api.core.model.workspace.runtime.RuntimeIdentity;
import org.eclipse.che.api.workspace.server.spi.InternalEnvironment;
import org.eclipse.che.workspace.infrastructure.openshift.environment.OpenShiftEnvironment;
import org.eclipse.che.workspace.infrastructure.openshift.provision.UniqueNamesProvisioner;
import org.eclipse.che.workspace.infrastructure.openshift.provision.env.EnvVarsConverter;
import org.eclipse.che.workspace.infrastructure.openshift.provision.installer.InstallerConfigProvisioner;
import org.eclipse.che.workspace.infrastructure.openshift.provision.labels.PodNameLabelProvisioner;
import org.eclipse.che.workspace.infrastructure.openshift.provision.restartpolicy.RestartPolicyRewriter;
import org.eclipse.che.workspace.infrastructure.openshift.provision.route.TlsRouteProvisioner;
import org.eclipse.che.workspace.infrastructure.openshift.provision.server.ServersConverter;
import org.eclipse.che.workspace.infrastructure.openshift.provision.volume.PersistentVolumeClaimProvisioner;
import org.mockito.InOrder;
import org.mockito.Mock;
@ -42,6 +46,10 @@ public class OpenShiftInfrastructureProvisionerTest {
@Mock private OpenShiftEnvironment osEnv;
@Mock private RuntimeIdentity runtimeIdentity;
@Mock private TlsRouteProvisioner tlsRouteProvisioner;
@Mock private EnvVarsConverter envVarsProvisioner;
@Mock private ServersConverter serversProvisioner;
@Mock private RestartPolicyRewriter restartPolicyRewriter;
@Mock private PodNameLabelProvisioner labelsProvisioner;
private OpenShiftInfrastructureProvisioner osInfraProvisioner;
@ -51,9 +59,24 @@ public class OpenShiftInfrastructureProvisionerTest {
public void setUp() {
osInfraProvisioner =
new OpenShiftInfrastructureProvisioner(
installerProvisioner, pvcProvisioner, uniqueNamesProvisioner, tlsRouteProvisioner);
installerProvisioner,
pvcProvisioner,
uniqueNamesProvisioner,
tlsRouteProvisioner,
serversProvisioner,
envVarsProvisioner,
restartPolicyRewriter,
labelsProvisioner);
provisionOrder =
inOrder(installerProvisioner, pvcProvisioner, uniqueNamesProvisioner, tlsRouteProvisioner);
inOrder(
installerProvisioner,
pvcProvisioner,
uniqueNamesProvisioner,
tlsRouteProvisioner,
serversProvisioner,
envVarsProvisioner,
restartPolicyRewriter,
labelsProvisioner);
}
@Test
@ -63,6 +86,18 @@ public class OpenShiftInfrastructureProvisionerTest {
provisionOrder
.verify(installerProvisioner)
.provision(eq(environment), eq(osEnv), eq(runtimeIdentity));
provisionOrder
.verify(serversProvisioner)
.provision(eq(environment), eq(osEnv), eq(runtimeIdentity));
provisionOrder
.verify(envVarsProvisioner)
.provision(eq(environment), eq(osEnv), eq(runtimeIdentity));
provisionOrder
.verify(labelsProvisioner)
.provision(eq(environment), eq(osEnv), eq(runtimeIdentity));
provisionOrder
.verify(restartPolicyRewriter)
.provision(eq(environment), eq(osEnv), eq(runtimeIdentity));
provisionOrder
.verify(pvcProvisioner)
.provision(eq(environment), eq(osEnv), eq(runtimeIdentity));

View File

@ -10,10 +10,8 @@
*/
package org.eclipse.che.workspace.infrastructure.openshift.environment;
import static java.lang.String.format;
import static java.util.Arrays.asList;
import static java.util.Collections.singletonList;
import static org.eclipse.che.workspace.infrastructure.openshift.environment.OpenShiftEnvironmentParser.DEFAULT_RESTART_POLICY;
import static org.eclipse.che.workspace.infrastructure.openshift.environment.OpenShiftEnvironmentParser.PVC_IGNORED_WARNING_CODE;
import static org.eclipse.che.workspace.infrastructure.openshift.environment.OpenShiftEnvironmentParser.PVC_IGNORED_WARNING_MESSAGE;
import static org.eclipse.che.workspace.infrastructure.openshift.environment.OpenShiftEnvironmentParser.ROUTES_IGNORED_WARNING_MESSAGE;
@ -26,17 +24,10 @@ import static org.testng.Assert.assertEquals;
import static org.testng.Assert.assertTrue;
import static org.testng.Assert.fail;
import io.fabric8.kubernetes.api.model.Container;
import io.fabric8.kubernetes.api.model.DoneableKubernetesList;
import io.fabric8.kubernetes.api.model.HasMetadata;
import io.fabric8.kubernetes.api.model.KubernetesList;
import io.fabric8.kubernetes.api.model.ObjectMeta;
import io.fabric8.kubernetes.api.model.ObjectMetaBuilder;
import io.fabric8.kubernetes.api.model.PersistentVolumeClaim;
import io.fabric8.kubernetes.api.model.Pod;
import io.fabric8.kubernetes.api.model.PodBuilder;
import io.fabric8.kubernetes.api.model.PodSpec;
import io.fabric8.kubernetes.api.model.PodSpecBuilder;
import io.fabric8.kubernetes.client.dsl.KubernetesListMixedOperation;
import io.fabric8.kubernetes.client.dsl.RecreateFromServerGettable;
import io.fabric8.openshift.api.model.Route;
@ -64,8 +55,6 @@ import org.testng.annotations.Test;
public class OpenShiftEnvironmentParserTest {
private static final String YAML_RECIPE = "application/x-yaml";
private static final String TEST_POD_NAME = "app";
private static final String ALWAYS_RESTART_POLICY = "Always";
private OpenShiftEnvironmentParser osEnvironmentParser;
@ -95,23 +84,6 @@ public class OpenShiftEnvironmentParserTest {
when(internalRecipe.getContent()).thenReturn("recipe content");
}
@Test
public void rewritesRestartPolicyWhenItsDifferentWithDefaultOne() throws Exception {
final List<HasMetadata> pods = singletonList(newPod(TEST_POD_NAME, ALWAYS_RESTART_POLICY));
when(validatedObjects.getItems()).thenReturn(pods);
final OpenShiftEnvironment parsed = osEnvironmentParser.parse(internalEnvironment);
assertEquals(
parsed.getPods().get(TEST_POD_NAME).getSpec().getRestartPolicy(), DEFAULT_RESTART_POLICY);
verifyWarnings(
new WarningImpl(
101,
format(
"Restart policy '%s' for pod '%s' is rewritten with %s",
ALWAYS_RESTART_POLICY, TEST_POD_NAME, DEFAULT_RESTART_POLICY)));
}
@Test
public void ignoreRoutesWhenRecipeContainsThem() throws Exception {
final List<HasMetadata> objects = asList(new Route(), new Route());
@ -152,11 +124,4 @@ public class OpenShiftEnvironmentParserTest {
verify(internalEnvironment, atLeastOnce()).addWarning(warningCaptor.capture());
return warningCaptor.getAllValues();
}
private static Pod newPod(String podName, String restartPolicy, Container... containers) {
final ObjectMeta podMetadata = new ObjectMetaBuilder().withName(podName).build();
final PodSpec podSpec =
new PodSpecBuilder().withRestartPolicy(restartPolicy).withContainers(containers).build();
return new PodBuilder().withMetadata(podMetadata).withSpec(podSpec).build();
}
}

View File

@ -11,6 +11,7 @@
package org.eclipse.che.workspace.infrastructure.openshift.provision.installer;
import static java.lang.String.format;
import static java.util.Collections.singletonMap;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
import static org.testng.Assert.assertEquals;
@ -18,21 +19,10 @@ import static org.testng.Assert.assertFalse;
import static org.testng.Assert.assertTrue;
import com.google.common.collect.ImmutableMap;
import io.fabric8.kubernetes.api.model.Container;
import io.fabric8.kubernetes.api.model.EnvVar;
import io.fabric8.kubernetes.api.model.ObjectMeta;
import io.fabric8.kubernetes.api.model.Pod;
import io.fabric8.kubernetes.api.model.PodSpec;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import org.eclipse.che.api.core.model.workspace.config.ServerConfig;
import org.eclipse.che.api.core.model.workspace.runtime.RuntimeIdentity;
import org.eclipse.che.api.installer.server.model.impl.InstallerImpl;
import org.eclipse.che.api.workspace.server.model.impl.ServerConfigImpl;
import org.eclipse.che.api.workspace.server.spi.InternalEnvironment;
import org.eclipse.che.api.workspace.server.spi.InternalMachineConfig;
import org.eclipse.che.api.workspace.server.token.MachineTokenProvider;
@ -49,6 +39,7 @@ import org.testng.annotations.Test;
*
* @author Anton Korneta
* @author Sergii Leshchenko
* @author Alexander Garagatyi
*/
@Listeners(MockitoTestNGListener.class)
public class InstallerConfigProvisionerTest {
@ -57,6 +48,7 @@ public class InstallerConfigProvisionerTest {
@Mock private MachineTokenProvider machineTokenProvider;
@Mock private RuntimeIdentity runtimeIdentity;
@Mock protected OpenShiftEnvironment osEnvironment;
private InstallerConfigProvisioner installerConfigProvisioner;
@ -68,68 +60,16 @@ public class InstallerConfigProvisionerTest {
when(runtimeIdentity.getWorkspaceId()).thenReturn(WORKSPACE_ID);
}
@Test
public void provisionWithEnvsFromInstallersAttributes() throws Exception {
// given
final Pod pod = new PodBuilder().setName("test").setContainers("machine").build();
OpenShiftEnvironment osEnvironment =
OpenShiftEnvironment.builder()
.setPods(ImmutableMap.of(pod.getMetadata().getName(), pod))
.build();
final Map<String, InternalMachineConfig> machines =
ImmutableMap.of(
"test/machine",
new MachineConfigBuilder()
.setInstallers(
new InstallerImpl()
.withProperties(ImmutableMap.of("environment", "INSTALLER1=localhost")),
new InstallerImpl()
.withProperties(ImmutableMap.of("environment", "INSTALLER2=agent")))
.setServer(Constants.SERVER_WS_AGENT_HTTP_REFERENCE, new ServerConfigImpl())
.build());
InternalEnvironment environment = createEnvironment(machines);
// when
installerConfigProvisioner.provision(environment, osEnvironment, runtimeIdentity);
// then
Container container = pod.getSpec().getContainers().get(0);
List<EnvVar> envs = container.getEnv();
verifyContainsEnv(envs, "INSTALLER1", "localhost");
verifyContainsEnv(envs, "INSTALLER2", "agent");
}
@Test
public void provisionWithAgentsRequiredEnvs() throws Exception {
// given
when(machineTokenProvider.getToken(WORKSPACE_ID)).thenReturn("superToken");
final Pod podWithAgent = new PodBuilder().setName("pod1").setContainers("wsagent").build();
final Pod pod = new PodBuilder().setName("pod2").setContainers("machine").build();
OpenShiftEnvironment osEnvironment =
OpenShiftEnvironment.builder()
.setPods(
ImmutableMap.of(
podWithAgent.getMetadata().getName(),
podWithAgent,
pod.getMetadata().getName(),
pod))
.build();
InternalMachineConfig machine1 =
createMachine(new HashMap<>(singletonMap("env1", "val1")), true);
InternalMachineConfig machine2 = createMachine(new HashMap<>(), false);
final Map<String, InternalMachineConfig> machines =
ImmutableMap.of(
"pod1/wsagent",
new MachineConfigBuilder()
.setServer(Constants.SERVER_WS_AGENT_HTTP_REFERENCE, new ServerConfigImpl())
.build(),
"pod2/machine",
new MachineConfigBuilder()
.setServer(Constants.SERVER_TERMINAL_REFERENCE, new ServerConfigImpl())
.build());
ImmutableMap.of("pod1/wsagent", machine1, "pod2/machine", machine2);
InternalEnvironment environment = createEnvironment(machines);
@ -137,17 +77,27 @@ public class InstallerConfigProvisionerTest {
installerConfigProvisioner.provision(environment, osEnvironment, runtimeIdentity);
// then
Container container = podWithAgent.getSpec().getContainers().get(0);
List<EnvVar> envs = container.getEnv();
verifyContainsEnv(envs, "CHE_API", CHE_SERVER_ENDPOINT);
verifyContainsEnv(envs, "USER_TOKEN", "superToken");
verifyContainsEnv(envs, "CHE_WORKSPACE_ID", WORKSPACE_ID);
Map<String, String> env = machine1.getEnv();
verifyContainsEnv(env, "CHE_API", CHE_SERVER_ENDPOINT);
verifyContainsEnv(env, "USER_TOKEN", "superToken");
verifyContainsEnv(env, "CHE_WORKSPACE_ID", WORKSPACE_ID);
Container container2 = pod.getSpec().getContainers().get(0);
List<EnvVar> envs2 = container2.getEnv();
verifyContainsEnv(envs2, "CHE_API", CHE_SERVER_ENDPOINT);
verifyContainsEnv(envs, "USER_TOKEN", "superToken");
verifyDoesNotContainEnv(envs2, "CHE_WORKSPACE_ID");
env = machine2.getEnv();
verifyContainsEnv(env, "CHE_API", CHE_SERVER_ENDPOINT);
verifyContainsEnv(env, "USER_TOKEN", "superToken");
assertFalse(
env.containsKey("CHE_WORKSPACE_ID"), "Environment variable '%s' found CHE_WORKSPACE_ID");
}
private InternalMachineConfig createMachine(Map<String, String> env, boolean isDev) {
InternalMachineConfig machineConfig = mock(InternalMachineConfig.class);
when(machineConfig.getEnv()).thenReturn(env);
if (isDev) {
when(machineConfig.getServers())
.thenReturn(
singletonMap(Constants.SERVER_WS_AGENT_HTTP_REFERENCE, mock(ServerConfig.class)));
}
return machineConfig;
}
private InternalEnvironment createEnvironment(Map<String, InternalMachineConfig> machines) {
@ -156,18 +106,10 @@ public class InstallerConfigProvisionerTest {
return environment;
}
private void verifyDoesNotContainEnv(List<EnvVar> envs, String name) {
Optional<EnvVar> env = envs.stream().filter(e -> e.getName().equals(name)).findAny();
private void verifyContainsEnv(Map<String, String> env, String name, String expectedValue) {
assertTrue(env.containsKey(name), format("Expected environment variable '%s' not found", name));
assertFalse(env.isPresent(), format("Environment variable '%s' found", name));
}
private void verifyContainsEnv(List<EnvVar> envs, String name, String expectedValue) {
Optional<EnvVar> env = envs.stream().filter(e -> e.getName().equals(name)).findAny();
assertTrue(env.isPresent(), format("Expected environment variable '%s' not found", name));
String actualValue = env.get().getValue();
String actualValue = env.get(name);
assertEquals(
actualValue,
expectedValue,
@ -175,66 +117,4 @@ public class InstallerConfigProvisionerTest {
"Environment variable '%s' expected with " + "value '%s' but found with '%s'",
name, expectedValue, actualValue));
}
private static class MachineConfigBuilder {
private List<InstallerImpl> installers = new ArrayList<>();
private Map<String, ServerConfig> servers = new HashMap<>();
MachineConfigBuilder setInstallers(InstallerImpl... installers) {
this.installers = Arrays.asList(installers);
return this;
}
MachineConfigBuilder setServer(String name, ServerConfig server) {
this.servers.put(name, server);
return this;
}
InternalMachineConfig build() {
final InternalMachineConfig machineConfig = mock(InternalMachineConfig.class);
when(machineConfig.getInstallers()).thenReturn(installers);
when(machineConfig.getServers()).thenReturn(servers);
return machineConfig;
}
}
private static class PodBuilder {
private String name;
private List<String> containersNames;
PodBuilder setName(String name) {
this.name = name;
return this;
}
PodBuilder setContainers(String... names) {
this.containersNames = Arrays.asList(names);
return this;
}
Pod build() {
final Pod pod = mock(Pod.class);
final ObjectMeta podMeta = mock(ObjectMeta.class);
when(pod.getMetadata()).thenReturn(podMeta);
when(podMeta.getName()).thenReturn(name);
final PodSpec podSpec = mock(PodSpec.class);
when(pod.getSpec()).thenReturn(podSpec);
final List<Container> containers = new ArrayList<>();
for (String containerName : containersNames) {
final Container container = mock(Container.class);
when(container.getName()).thenReturn(containerName);
when(container.getEnv()).thenReturn(new ArrayList<>());
containers.add(container);
}
when(podSpec.getContainers()).thenReturn(containers);
return pod;
}
}
}

View File

@ -0,0 +1,99 @@
/*
* Copyright (c) 2012-2017 Red Hat, Inc.
* 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:
* Red Hat, Inc. - initial API and implementation
*/
package org.eclipse.che.workspace.infrastructure.openshift.provision.restartpolicy;
import static java.lang.String.format;
import static java.util.Collections.singletonMap;
import static org.eclipse.che.workspace.infrastructure.openshift.provision.restartpolicy.RestartPolicyRewriter.DEFAULT_RESTART_POLICY;
import static org.mockito.Mockito.atLeastOnce;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import static org.testng.Assert.assertEquals;
import static org.testng.Assert.fail;
import io.fabric8.kubernetes.api.model.Container;
import io.fabric8.kubernetes.api.model.ObjectMeta;
import io.fabric8.kubernetes.api.model.ObjectMetaBuilder;
import io.fabric8.kubernetes.api.model.Pod;
import io.fabric8.kubernetes.api.model.PodBuilder;
import io.fabric8.kubernetes.api.model.PodSpec;
import io.fabric8.kubernetes.api.model.PodSpecBuilder;
import java.util.Iterator;
import java.util.List;
import org.eclipse.che.api.core.model.workspace.Warning;
import org.eclipse.che.api.core.model.workspace.runtime.RuntimeIdentity;
import org.eclipse.che.api.workspace.server.model.impl.WarningImpl;
import org.eclipse.che.api.workspace.server.spi.InternalEnvironment;
import org.eclipse.che.workspace.infrastructure.openshift.environment.OpenShiftEnvironment;
import org.mockito.ArgumentCaptor;
import org.mockito.Captor;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.testng.MockitoTestNGListener;
import org.testng.annotations.Listeners;
import org.testng.annotations.Test;
/** @author Alexander Garagatyi */
@Listeners(MockitoTestNGListener.class)
public class RestartPolicyRewriterTest {
private static final String TEST_POD_NAME = "app";
private static final String ALWAYS_RESTART_POLICY = "Always";
@Mock private InternalEnvironment environment;
@Mock private OpenShiftEnvironment osEnv;
@Mock private RuntimeIdentity runtimeIdentity;
@InjectMocks private RestartPolicyRewriter restartPolicyRewriter;
@Captor private ArgumentCaptor<Warning> warningCaptor;
@Test
public void rewritesRestartPolicyWhenItsDifferentWithDefaultOne() throws Exception {
when(osEnv.getPods())
.thenReturn(singletonMap(TEST_POD_NAME, newPod(TEST_POD_NAME, ALWAYS_RESTART_POLICY)));
restartPolicyRewriter.provision(environment, osEnv, runtimeIdentity);
assertEquals(
osEnv.getPods().get(TEST_POD_NAME).getSpec().getRestartPolicy(), DEFAULT_RESTART_POLICY);
verifyWarnings(
new WarningImpl(
101,
format(
"Restart policy '%s' for pod '%s' is rewritten with %s",
ALWAYS_RESTART_POLICY, TEST_POD_NAME, DEFAULT_RESTART_POLICY)));
}
private static Pod newPod(String podName, String restartPolicy, Container... containers) {
final ObjectMeta podMetadata = new ObjectMetaBuilder().withName(podName).build();
final PodSpec podSpec =
new PodSpecBuilder().withRestartPolicy(restartPolicy).withContainers(containers).build();
return new PodBuilder().withMetadata(podMetadata).withSpec(podSpec).build();
}
private void verifyWarnings(Warning... expectedWarnings) {
final Iterator<Warning> actualWarnings = captureWarnings().iterator();
for (Warning expected : expectedWarnings) {
if (!actualWarnings.hasNext()) {
fail("It is expected to receive environment warning");
}
final Warning actual = actualWarnings.next();
assertEquals(actual, expected);
}
if (actualWarnings.hasNext()) {
fail("No more warnings expected");
}
}
private List<Warning> captureWarnings() {
verify(environment, atLeastOnce()).addWarning(warningCaptor.capture());
return warningCaptor.getAllValues();
}
}

View File

@ -70,6 +70,10 @@
<groupId>org.eclipse.che.core</groupId>
<artifactId>che-core-api-factory-shared</artifactId>
</dependency>
<dependency>
<groupId>org.eclipse.che.core</groupId>
<artifactId>che-core-api-model</artifactId>
</dependency>
<dependency>
<groupId>org.eclipse.che.core</groupId>
<artifactId>che-core-api-workspace-shared</artifactId>

View File

@ -13,6 +13,7 @@ package org.eclipse.che.plugin.urlfactory;
import static java.lang.Boolean.FALSE;
import static java.util.Collections.singletonList;
import static java.util.Collections.singletonMap;
import static org.eclipse.che.api.core.model.workspace.config.MachineConfig.MEMORY_LIMIT_ATTRIBUTE;
import static org.eclipse.che.dto.server.DtoFactory.newDto;
import static org.eclipse.che.plugin.urlfactory.URLFactoryBuilder.DEFAULT_DOCKER_IMAGE;
import static org.eclipse.che.plugin.urlfactory.URLFactoryBuilder.MACHINE_NAME;
@ -64,7 +65,7 @@ public class URLFactoryBuilderTest {
MachineConfigDto machine =
newDto(MachineConfigDto.class)
.withInstallers(singletonList("org.eclipse.che.ws-agent"))
.withAttributes(singletonMap("memoryLimitBytes", MEMORY_LIMIT_BYTES));
.withAttributes(singletonMap(MEMORY_LIMIT_ATTRIBUTE, MEMORY_LIMIT_BYTES));
// setup environment
EnvironmentDto environmentDto =
@ -101,7 +102,7 @@ public class URLFactoryBuilderTest {
MachineConfigDto machine =
newDto(MachineConfigDto.class)
.withInstallers(singletonList("org.eclipse.che.ws-agent"))
.withAttributes(singletonMap("memoryLimitBytes", MEMORY_LIMIT_BYTES));
.withAttributes(singletonMap(MEMORY_LIMIT_ATTRIBUTE, MEMORY_LIMIT_BYTES));
// setup environment
EnvironmentDto environmentDto =
@ -137,7 +138,7 @@ public class URLFactoryBuilderTest {
MachineConfigDto machine =
newDto(MachineConfigDto.class)
.withInstallers(singletonList("org.eclipse.che.ws-agent"))
.withAttributes(singletonMap("memoryLimitBytes", MEMORY_LIMIT_BYTES));
.withAttributes(singletonMap(MEMORY_LIMIT_ATTRIBUTE, MEMORY_LIMIT_BYTES));
// setup environment
EnvironmentDto environmentDto =

View File

@ -14,6 +14,7 @@ import static java.lang.String.format;
import static java.lang.String.valueOf;
import static org.eclipse.che.api.core.model.workspace.WorkspaceStatus.RUNNING;
import static org.eclipse.che.api.core.model.workspace.WorkspaceStatus.STOPPED;
import static org.eclipse.che.api.core.model.workspace.config.MachineConfig.MEMORY_LIMIT_ATTRIBUTE;
import static org.eclipse.che.api.workspace.server.WsAgentMachineFinderUtil.containsWsAgentServer;
import com.google.inject.Inject;
@ -188,7 +189,7 @@ public class TestWorkspaceServiceClient {
.forEach(
m ->
m.getAttributes()
.put("memoryLimitBytes", Long.toString(convertToByte(memory, memoryUnit))));
.put(MEMORY_LIMIT_ATTRIBUTE, Long.toString(convertToByte(memory, memoryUnit))));
workspace.getEnvironments().remove("replaced_name");
workspace.getEnvironments().put(workspaceName, environment);
workspace.setName(workspaceName);

View File

@ -14,6 +14,7 @@ import static java.util.Arrays.asList;
import static java.util.Collections.singletonList;
import static java.util.Collections.singletonMap;
import static java.util.Objects.requireNonNull;
import static org.eclipse.che.api.core.model.workspace.config.MachineConfig.MEMORY_LIMIT_ATTRIBUTE;
import static org.eclipse.che.dto.server.DtoFactory.newDto;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.eq;
@ -191,7 +192,7 @@ public class FactoryBuilderTest {
newDto(MachineConfigDto.class)
.withInstallers(singletonList("org.eclipse.che.ws-agent"))
.withAttributes(
singletonMap("memoryLimitBytes", "" + 512L * 1024L * 1024L))));
singletonMap(MEMORY_LIMIT_ATTRIBUTE, "" + 512L * 1024L * 1024L))));
WorkspaceConfigDto workspaceConfig =
dto.createDto(WorkspaceConfigDto.class)

View File

@ -18,9 +18,18 @@ import org.eclipse.che.api.core.model.workspace.config.ServerConfig;
* An entity that might additionally injected into machine and brings functionality.
*
* @author Anatoliy Bazko
* @author Alexander Garagatyi
*/
public interface Installer {
/**
* Name of a property from {@link #getProperties()} that can contain environment variables that
* should be injected into machine.
*
* <p>Example: { "environment" : "envVar1=value1,envVar2=value2" }
*/
String ENVIRONMENT_PROPERTY = "environment";
/** Returns the id of the installer. */
String getId();

View File

@ -12,8 +12,10 @@ package org.eclipse.che.api.workspace.server;
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 java.util.Map;
import java.util.Map.Entry;
import java.util.regex.Pattern;
import javax.inject.Inject;
import javax.inject.Singleton;
@ -24,6 +26,7 @@ import org.eclipse.che.api.core.model.workspace.Workspace;
import org.eclipse.che.api.core.model.workspace.WorkspaceConfig;
import org.eclipse.che.api.core.model.workspace.config.Command;
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.workspace.server.spi.InfrastructureException;
@ -83,7 +86,11 @@ public class WorkspaceValidator {
checkNotNull(recipe, "Environment recipe must not be null");
checkNotNull(recipe.getType(), "Environment recipe type must not be null");
// TODO: spi: deal with exceptions
for (Entry<String, ? extends MachineConfig> machineEntry :
environment.getMachines().entrySet()) {
validateMachine(machineEntry.getKey(), machineEntry.getValue());
}
try {
runtimes.validate(environment);
} catch (InfrastructureException e) {
@ -127,6 +134,20 @@ public class WorkspaceValidator {
}
}
private void validateMachine(String name, MachineConfig machine) throws ValidationException {
String memoryAttribute = machine.getAttributes().get(MEMORY_LIMIT_ATTRIBUTE);
if (memoryAttribute != null) {
try {
Long.parseLong(memoryAttribute);
} catch (NumberFormatException e) {
throw new ValidationException(
format(
"Value '%s' of attribute '%s' in machine '%s' is illegal",
memoryAttribute, MEMORY_LIMIT_ATTRIBUTE, name));
}
}
}
/**
* Checks that object reference is not null, throws {@link ValidationException} in the case of
* null {@code object} with given {@code message}.

View File

@ -12,6 +12,7 @@ package org.eclipse.che.api.workspace.server.adapter;
import static com.google.common.primitives.Ints.tryParse;
import static java.lang.String.format;
import static org.eclipse.che.api.core.model.workspace.config.MachineConfig.MEMORY_LIMIT_ATTRIBUTE;
import com.google.gson.JsonArray;
import com.google.gson.JsonElement;
@ -174,7 +175,7 @@ public class WorkspaceConfigJsonAdapter {
envName));
}
final JsonObject attributes = new JsonObject();
attributes.addProperty("memoryLimitBytes", Long.toString(1024L * 1024L * ram));
attributes.addProperty(MEMORY_LIMIT_ATTRIBUTE, Long.toString(1024L * 1024L * ram));
newMachine.add("attributes", attributes);
}
}

View File

@ -10,6 +10,7 @@
*/
package org.eclipse.che.api.workspace.server.spi;
import static com.google.common.base.Strings.isNullOrEmpty;
import static java.lang.String.format;
import java.util.ArrayList;
@ -88,21 +89,43 @@ public class InternalMachineConfig {
try {
List<Installer> sortedInstallers = installerRegistry.getOrderedInstallers(installersKeys);
for (Installer installer : sortedInstallers) {
this.installers.add(new InstallerImpl(installer));
for (Map.Entry<String, ? extends ServerConfig> serverEntry :
installer.getServers().entrySet()) {
if (servers.putIfAbsent(serverEntry.getKey(), serverEntry.getValue()) != null
&& servers.get(serverEntry.getKey()).equals(serverEntry.getValue())) {
throw new InfrastructureException(
format(
"Installer '%s' contains server '%s' conflicting with machine configuration",
installer.getId(), serverEntry.getKey()));
}
}
applyInstaller(installer);
}
} catch (InstallerException e) {
// TODO installers has circular dependency or missing, what should we throw in that case?
throw new InfrastructureException(e.getLocalizedMessage(), e);
}
}
private void applyInstaller(Installer installer) throws InfrastructureException {
this.installers.add(new InstallerImpl(installer));
for (Map.Entry<String, ? extends ServerConfig> serverEntry :
installer.getServers().entrySet()) {
if (servers.putIfAbsent(serverEntry.getKey(), serverEntry.getValue()) != null
&& !servers.get(serverEntry.getKey()).equals(serverEntry.getValue())) {
throw new InfrastructureException(
format(
"Installer '%s' contains server '%s' conflicting with machine configuration",
installer.getId(), serverEntry.getKey()));
}
}
addEnvVars(installer);
}
private void addEnvVars(Installer installer) {
String environment = installer.getProperties().get(Installer.ENVIRONMENT_PROPERTY);
if (isNullOrEmpty(environment)) {
return;
}
for (String env : environment.split(",")) {
String[] items = env.split("=");
if (items.length != 2) {
// TODO add warning
// LOG.warn(format("Illegal environment variable '%s' format", env));
continue;
}
this.env.put(items[0], items[1]);
}
}
}

View File

@ -10,6 +10,7 @@
*/
package org.eclipse.che.api.workspace.server;
import static org.eclipse.che.api.core.model.workspace.config.MachineConfig.MEMORY_LIMIT_ATTRIBUTE;
import static org.testng.Assert.assertEquals;
import static org.testng.Assert.assertNotNull;
import static org.testng.Assert.assertTrue;
@ -85,8 +86,8 @@ public class WorkspaceConfigJsonAdapterTest {
assertTrue(
devMachineObj.get("attributes").isJsonObject(), "dev machine attributes is json object");
final JsonObject attributes = devMachineObj.getAsJsonObject("attributes");
assertTrue(attributes.has("memoryLimitBytes"), "has memory limit");
assertEquals(attributes.get("memoryLimitBytes").getAsString(), "2147483648");
assertTrue(attributes.has(MEMORY_LIMIT_ATTRIBUTE), "has memory limit");
assertEquals(attributes.get(MEMORY_LIMIT_ATTRIBUTE).getAsString(), "2147483648");
// check environment recipe
assertTrue(environmentObj.has("recipe"), "environment contains recipe");

View File

@ -18,6 +18,7 @@ import static java.util.Collections.singletonMap;
import static org.eclipse.che.api.core.model.workspace.WorkspaceStatus.RUNNING;
import static org.eclipse.che.api.core.model.workspace.WorkspaceStatus.STARTING;
import static org.eclipse.che.api.core.model.workspace.WorkspaceStatus.STOPPED;
import static org.eclipse.che.api.core.model.workspace.config.MachineConfig.MEMORY_LIMIT_ATTRIBUTE;
import static org.eclipse.che.api.workspace.server.WorkspaceManager.UPDATED_ATTRIBUTE_NAME;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyObject;
@ -557,7 +558,7 @@ public class WorkspaceManagerTest {
singletonList("org.eclipse.che.ws-agent"),
null,
singletonMap("CHE_ENV", "value"),
singletonMap("memoryLimitBytes", "10000"));
singletonMap(MEMORY_LIMIT_ATTRIBUTE, "10000"));
EnvironmentImpl environment =
new EnvironmentImpl(
new RecipeImpl("type", "contentType", "content", null),

View File

@ -19,6 +19,7 @@ import static java.util.Collections.singletonList;
import static java.util.Collections.singletonMap;
import static java.util.stream.Collectors.toList;
import static org.eclipse.che.api.core.model.workspace.WorkspaceStatus.STARTING;
import static org.eclipse.che.api.core.model.workspace.config.MachineConfig.MEMORY_LIMIT_ATTRIBUTE;
import static org.eclipse.che.dto.server.DtoFactory.newDto;
import static org.everrest.assured.JettyHttpServer.ADMIN_USER_NAME;
import static org.everrest.assured.JettyHttpServer.ADMIN_USER_PASSWORD;
@ -917,7 +918,7 @@ public class WorkspaceServiceTest {
singletonList("org.eclipse.che.ws-agent"),
null,
singletonMap("CHE_ENV", "value"),
singletonMap("memoryLimitBytes", "10000"));
singletonMap(MEMORY_LIMIT_ATTRIBUTE, "10000"));
return DtoConverter.asDto(
new EnvironmentImpl(

View File

@ -12,6 +12,7 @@ package org.eclipse.che.api.workspace.server;
import static java.util.Collections.singletonList;
import static java.util.Collections.singletonMap;
import static org.eclipse.che.api.core.model.workspace.config.MachineConfig.MEMORY_LIMIT_ATTRIBUTE;
import static org.eclipse.che.dto.server.DtoFactory.newDto;
import com.google.common.collect.ImmutableMap;
@ -20,6 +21,7 @@ import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.eclipse.che.api.core.ValidationException;
import org.eclipse.che.api.core.model.workspace.config.MachineConfig;
import org.eclipse.che.api.workspace.shared.dto.CommandDto;
import org.eclipse.che.api.workspace.shared.dto.EnvironmentDto;
import org.eclipse.che.api.workspace.shared.dto.MachineConfigDto;
@ -184,6 +186,27 @@ public class WorkspaceValidatorTest {
wsValidator.validateConfig(config);
}
@Test(
expectedExceptions = ValidationException.class,
expectedExceptionsMessageRegExp =
"Value '.*' of attribute '" + MEMORY_LIMIT_ATTRIBUTE + "' in machine '.*' is illegal",
dataProvider = "illegalMemoryAttributeValueProvider"
)
public void shouldFailValidationIfMemoryMachineAttributeHasIllegalValue(String attributeValue)
throws Exception {
final WorkspaceConfigDto config = createConfig();
EnvironmentDto env = config.getEnvironments().values().iterator().next();
MachineConfigDto machine = env.getMachines().values().iterator().next();
machine.getAttributes().put(MachineConfig.MEMORY_LIMIT_ATTRIBUTE, attributeValue);
wsValidator.validateConfig(config);
}
@DataProvider(name = "illegalMemoryAttributeValueProvider")
public static Object[][] illegalMemoryAttributeValueProvider() {
return new Object[][] {{"text"}, {""}, {"123MB"}, {"123GB"}, {"123KB"}};
}
@Test(
expectedExceptions = ValidationException.class,
expectedExceptionsMessageRegExp = "Workspace ws-name contains command with null or empty name"
@ -221,17 +244,17 @@ public class WorkspaceValidatorTest {
final WorkspaceConfigDto workspaceConfigDto =
newDto(WorkspaceConfigDto.class).withName("ws-name").withDefaultEnv("dev-env");
MachineConfigDto extendedMachine =
MachineConfigDto machineConfig =
newDto(MachineConfigDto.class)
.withInstallers(singletonList("org.eclipse.che.ws-agent"))
.withServers(
singletonMap(
"ref1",
newDto(ServerConfigDto.class).withPort("8080/tcp").withProtocol("https")))
.withAttributes(singletonMap("memoryLimitBytes", "1000000"));
.withAttributes(new HashMap<>(singletonMap(MEMORY_LIMIT_ATTRIBUTE, "1000000")));
EnvironmentDto env =
newDto(EnvironmentDto.class)
.withMachines(singletonMap("devmachine1", extendedMachine))
.withMachines(singletonMap("devmachine1", machineConfig))
.withRecipe(
newDto(RecipeDto.class)
.withType("type")

View File

@ -11,6 +11,7 @@
package org.eclipse.che.api.workspace.server.stack;
import static java.util.Collections.singletonMap;
import static org.eclipse.che.api.core.model.workspace.config.MachineConfig.MEMORY_LIMIT_ATTRIBUTE;
import static org.eclipse.che.dto.server.DtoFactory.newDto;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.doThrow;
@ -194,7 +195,7 @@ public class StackLoaderTest {
newDto(MachineConfigDto.class)
.withInstallers(Arrays.asList("agent1", "agent2"))
.withServers(servers)
.withAttributes(singletonMap("memoryLimitBytes", "" + 512L * 1024L * 1024L)));
.withAttributes(singletonMap(MEMORY_LIMIT_ATTRIBUTE, "" + 512L * 1024L * 1024L)));
EnvironmentDto environmentDto =
newDto(EnvironmentDto.class).withRecipe(environmentRecipe).withMachines(machines);