diff --git a/core/che-core-api-model/src/main/java/org/eclipse/che/api/core/model/workspace/config/MachineConfig.java b/core/che-core-api-model/src/main/java/org/eclipse/che/api/core/model/workspace/config/MachineConfig.java index b0212d87da..162377c7de 100644 --- a/core/che-core-api-model/src/main/java/org/eclipse/che/api/core/model/workspace/config/MachineConfig.java +++ b/core/che-core-api-model/src/main/java/org/eclipse/che/api/core/model/workspace/config/MachineConfig.java @@ -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. * diff --git a/dockerfiles/cli/tests/cmd_backup_restore_tests.bats b/dockerfiles/cli/tests/cmd_backup_restore_tests.bats index 596d6523f4..c455a02f8b 100644 --- a/dockerfiles/cli/tests/cmd_backup_restore_tests.bats +++ b/dockerfiles/cli/tests/cmd_backup_restore_tests.bats @@ -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 diff --git a/dockerfiles/lib/src/api/wsmaster/workspace/workspace.ts b/dockerfiles/lib/src/api/wsmaster/workspace/workspace.ts index 278ef61bfe..ce97b6aec3 100644 --- a/dockerfiles/lib/src/api/wsmaster/workspace/workspace.ts +++ b/dockerfiles/lib/src/api/wsmaster/workspace/workspace.ts @@ -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(); diff --git a/dockerfiles/lib/src/internal/action/impl/get-ssh-action.ts b/dockerfiles/lib/src/internal/action/impl/get-ssh-action.ts index 365fe40128..7adb51907f 100644 --- a/dockerfiles/lib/src/internal/action/impl/get-ssh-action.ts +++ b/dockerfiles/lib/src/internal/action/impl/get-ssh-action.ts @@ -69,7 +69,7 @@ export class GetSshDataAction { // Check ssh agent is there let defaultEnv:string = workspaceDto.getConfig().getDefaultEnv(); - let agents:Array = workspaceDto.getConfig().getEnvironments().get(defaultEnv).getMachines().get("dev-machine").getAgents(); + let agents:Array = 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.") diff --git a/dockerfiles/lib/src/internal/action/impl/workspace-ssh-action.ts b/dockerfiles/lib/src/internal/action/impl/workspace-ssh-action.ts index f66f70d2f9..570ba7fac0 100644 --- a/dockerfiles/lib/src/internal/action/impl/workspace-ssh-action.ts +++ b/dockerfiles/lib/src/internal/action/impl/workspace-ssh-action.ts @@ -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 = machineConfig.getAgents(); + let agents:Array = 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.") diff --git a/dockerfiles/lib/src/internal/dir/che-dir.ts b/dockerfiles/lib/src/internal/dir/che-dir.ts index cd45b05224..95d16f3fb4 100644 --- a/dockerfiles/lib/src/internal/dir/che-dir.ts +++ b/dockerfiles/lib/src/internal/dir/che-dir.ts @@ -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 = workspaceDto.getConfig().getEnvironments().get(defaultEnv).getMachines().get("dev-machine").getAgents(); + let agents : Array = 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.") diff --git a/infrastructures/docker/infrastructure/pom.xml b/infrastructures/docker/infrastructure/pom.xml index 8b16923f2f..0182ce4521 100644 --- a/infrastructures/docker/infrastructure/pom.xml +++ b/infrastructures/docker/infrastructure/pom.xml @@ -86,10 +86,6 @@ org.eclipse.che.core che-core-api-installer - - org.eclipse.che.core - che-core-api-installer-shared - org.eclipse.che.core che-core-api-model diff --git a/infrastructures/docker/infrastructure/src/main/java/org/eclipse/che/workspace/infrastructure/docker/DockerMachineStarter.java b/infrastructures/docker/infrastructure/src/main/java/org/eclipse/che/workspace/infrastructure/docker/DockerMachineStarter.java index c28611ec8b..ee6d989957 100644 --- a/infrastructures/docker/infrastructure/src/main/java/org/eclipse/che/workspace/infrastructure/docker/DockerMachineStarter.java +++ b/infrastructures/docker/infrastructure/src/main/java/org/eclipse/che/workspace/infrastructure/docker/DockerMachineStarter.java @@ -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 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; } diff --git a/infrastructures/docker/infrastructure/src/main/java/org/eclipse/che/workspace/infrastructure/docker/environment/EnvironmentParser.java b/infrastructures/docker/infrastructure/src/main/java/org/eclipse/che/workspace/infrastructure/docker/environment/EnvironmentParser.java index a5b163d000..3c0ddb23ce 100644 --- a/infrastructures/docker/infrastructure/src/main/java/org/eclipse/che/workspace/infrastructure/docker/environment/EnvironmentParser.java +++ b/infrastructures/docker/infrastructure/src/main/java/org/eclipse/che/workspace/infrastructure/docker/environment/EnvironmentParser.java @@ -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()); - } } diff --git a/infrastructures/docker/infrastructure/src/main/java/org/eclipse/che/workspace/infrastructure/docker/environment/EnvironmentValidator.java b/infrastructures/docker/infrastructure/src/main/java/org/eclipse/che/workspace/infrastructure/docker/environment/EnvironmentValidator.java index 15d0eb67df..3635af3618 100644 --- a/infrastructures/docker/infrastructure/src/main/java/org/eclipse/che/workspace/infrastructure/docker/environment/EnvironmentValidator.java +++ b/infrastructures/docker/infrastructure/src/main/java/org/eclipse/che/workspace/infrastructure/docker/environment/EnvironmentValidator.java @@ -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 serverEntry : diff --git a/infrastructures/docker/infrastructure/src/main/java/org/eclipse/che/workspace/infrastructure/docker/local/LocalCheInfrastructureProvisioner.java b/infrastructures/docker/infrastructure/src/main/java/org/eclipse/che/workspace/infrastructure/docker/local/LocalCheInfrastructureProvisioner.java index 34069c5956..99d63fefff 100644 --- a/infrastructures/docker/infrastructure/src/main/java/org/eclipse/che/workspace/infrastructure/docker/local/LocalCheInfrastructureProvisioner.java +++ b/infrastructures/docker/infrastructure/src/main/java/org/eclipse/che/workspace/infrastructure/docker/local/LocalCheInfrastructureProvisioner.java @@ -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); } } diff --git a/infrastructures/docker/infrastructure/src/main/java/org/eclipse/che/workspace/infrastructure/docker/local/LocalDockerModule.java b/infrastructures/docker/infrastructure/src/main/java/org/eclipse/che/workspace/infrastructure/docker/local/LocalDockerModule.java index c781a4100a..01edf2714f 100644 --- a/infrastructures/docker/infrastructure/src/main/java/org/eclipse/che/workspace/infrastructure/docker/local/LocalDockerModule.java +++ b/infrastructures/docker/infrastructure/src/main/java/org/eclipse/che/workspace/infrastructure/docker/local/LocalDockerModule.java @@ -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; diff --git a/infrastructures/docker/infrastructure/src/main/java/org/eclipse/che/workspace/infrastructure/docker/local/installer/LocalInstallersConfigProvisioner.java b/infrastructures/docker/infrastructure/src/main/java/org/eclipse/che/workspace/infrastructure/docker/local/installer/LocalInstallersBinariesVolumeProvisioner.java similarity index 73% rename from infrastructures/docker/infrastructure/src/main/java/org/eclipse/che/workspace/infrastructure/docker/local/installer/LocalInstallersConfigProvisioner.java rename to infrastructures/docker/infrastructure/src/main/java/org/eclipse/che/workspace/infrastructure/docker/local/installer/LocalInstallersBinariesVolumeProvisioner.java index 37c858b93e..3a82de8b55 100644 --- a/infrastructures/docker/infrastructure/src/main/java/org/eclipse/che/workspace/infrastructure/docker/local/installer/LocalInstallersConfigProvisioner.java +++ b/infrastructures/docker/infrastructure/src/main/java/org/eclipse/che/workspace/infrastructure/docker/local/installer/LocalInstallersBinariesVolumeProvisioner.java @@ -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 localInstallerProvisioners; @Inject - public LocalInstallersConfigProvisioner( - InstallerConfigApplier installerConfigApplier, + public LocalInstallersBinariesVolumeProvisioner( @Named(LOCAL_INSTALLERS_PROVISIONERS) Set 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); } diff --git a/infrastructures/docker/infrastructure/src/main/java/org/eclipse/che/workspace/infrastructure/docker/provisioner/env/EnvVarsConverter.java b/infrastructures/docker/infrastructure/src/main/java/org/eclipse/che/workspace/infrastructure/docker/provisioner/env/EnvVarsConverter.java new file mode 100644 index 0000000000..51bed910fd --- /dev/null +++ b/infrastructures/docker/infrastructure/src/main/java/org/eclipse/che/workspace/infrastructure/docker/provisioner/env/EnvVarsConverter.java @@ -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())); + } +} diff --git a/infrastructures/docker/infrastructure/src/main/java/org/eclipse/che/workspace/infrastructure/docker/provisioner/installer/InstallerConfigApplier.java b/infrastructures/docker/infrastructure/src/main/java/org/eclipse/che/workspace/infrastructure/docker/provisioner/installer/InstallerConfigApplier.java deleted file mode 100644 index 30eee27475..0000000000 --- a/infrastructures/docker/infrastructure/src/main/java/org/eclipse/che/workspace/infrastructure/docker/provisioner/installer/InstallerConfigApplier.java +++ /dev/null @@ -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.
The list of supported properties are: - *
  • 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 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 servers) { - container.getLabels().putAll(Labels.newSerializer().servers(servers).labels()); - } - - private void addEnv(DockerContainerConfig container, Map properties) { - String environment = properties.get(ENVIRONMENT.toString()); - if (isNullOrEmpty(environment)) { - return; - } - - Map 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 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; - } - } -} diff --git a/infrastructures/docker/infrastructure/src/main/java/org/eclipse/che/workspace/infrastructure/docker/provisioner/installer/InstallersConfigProvisioner.java b/infrastructures/docker/infrastructure/src/main/java/org/eclipse/che/workspace/infrastructure/docker/provisioner/installer/InstallersConfigProvisioner.java deleted file mode 100644 index d0295d87ed..0000000000 --- a/infrastructures/docker/infrastructure/src/main/java/org/eclipse/che/workspace/infrastructure/docker/provisioner/installer/InstallersConfigProvisioner.java +++ /dev/null @@ -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); - } - } -} diff --git a/infrastructures/docker/infrastructure/src/main/java/org/eclipse/che/workspace/infrastructure/docker/provisioner/labels/LabelsProvisioner.java b/infrastructures/docker/infrastructure/src/main/java/org/eclipse/che/workspace/infrastructure/docker/provisioner/labels/RuntimeLabelsProvisioner.java similarity index 80% rename from infrastructures/docker/infrastructure/src/main/java/org/eclipse/che/workspace/infrastructure/docker/provisioner/labels/LabelsProvisioner.java rename to infrastructures/docker/infrastructure/src/main/java/org/eclipse/che/workspace/infrastructure/docker/provisioner/labels/RuntimeLabelsProvisioner.java index 263b4c0c9c..6948937c66 100644 --- a/infrastructures/docker/infrastructure/src/main/java/org/eclipse/che/workspace/infrastructure/docker/provisioner/labels/LabelsProvisioner.java +++ b/infrastructures/docker/infrastructure/src/main/java/org/eclipse/che/workspace/infrastructure/docker/provisioner/labels/RuntimeLabelsProvisioner.java @@ -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()); } } } diff --git a/infrastructures/docker/infrastructure/src/main/java/org/eclipse/che/workspace/infrastructure/docker/provisioner/memory/MemoryAttributeConverter.java b/infrastructures/docker/infrastructure/src/main/java/org/eclipse/che/workspace/infrastructure/docker/provisioner/memory/MemoryAttributeConverter.java new file mode 100644 index 0000000000..c124e138d2 --- /dev/null +++ b/infrastructures/docker/infrastructure/src/main/java/org/eclipse/che/workspace/infrastructure/docker/provisioner/memory/MemoryAttributeConverter.java @@ -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 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)); + } + } + } + } +} diff --git a/infrastructures/docker/infrastructure/src/main/java/org/eclipse/che/workspace/infrastructure/docker/provisioner/server/ServersConverter.java b/infrastructures/docker/infrastructure/src/main/java/org/eclipse/che/workspace/infrastructure/docker/provisioner/server/ServersConverter.java new file mode 100644 index 0000000000..27805cd47d --- /dev/null +++ b/infrastructures/docker/infrastructure/src/main/java/org/eclipse/che/workspace/infrastructure/docker/provisioner/server/ServersConverter.java @@ -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. + * + *

    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()); + }); + }); + } +} diff --git a/infrastructures/docker/infrastructure/src/main/java/org/eclipse/che/workspace/infrastructure/docker/server/mapping/ServersMapper.java b/infrastructures/docker/infrastructure/src/main/java/org/eclipse/che/workspace/infrastructure/docker/server/mapping/ServersMapper.java index 0c5b94770d..9d2a75ddd1 100644 --- a/infrastructures/docker/infrastructure/src/main/java/org/eclipse/che/workspace/infrastructure/docker/server/mapping/ServersMapper.java +++ b/infrastructures/docker/infrastructure/src/main/java/org/eclipse/che/workspace/infrastructure/docker/server/mapping/ServersMapper.java @@ -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); diff --git a/infrastructures/docker/infrastructure/src/test/java/org/eclipse/che/workspace/infrastructure/docker/environment/EnvironmentParserTest.java b/infrastructures/docker/infrastructure/src/test/java/org/eclipse/che/workspace/infrastructure/docker/environment/EnvironmentParserTest.java index a2094387ec..91aabc8a69 100644 --- a/infrastructures/docker/infrastructure/src/test/java/org/eclipse/che/workspace/infrastructure/docker/environment/EnvironmentParserTest.java +++ b/infrastructures/docker/infrastructure/src/test/java/org/eclipse/che/workspace/infrastructure/docker/environment/EnvironmentParserTest.java @@ -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 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 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: diff --git a/infrastructures/docker/infrastructure/src/test/java/org/eclipse/che/workspace/infrastructure/docker/environment/EnvironmentValidatorTest.java b/infrastructures/docker/infrastructure/src/test/java/org/eclipse/che/workspace/infrastructure/docker/environment/EnvironmentValidatorTest.java index 13338af241..f53855e4f5 100644 --- a/infrastructures/docker/infrastructure/src/test/java/org/eclipse/che/workspace/infrastructure/docker/environment/EnvironmentValidatorTest.java +++ b/infrastructures/docker/infrastructure/src/test/java/org/eclipse/che/workspace/infrastructure/docker/environment/EnvironmentValidatorTest.java @@ -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 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 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 = diff --git a/infrastructures/docker/infrastructure/src/test/java/org/eclipse/che/workspace/infrastructure/docker/local/LocalCheInfrastructureProvisionerTest.java b/infrastructures/docker/infrastructure/src/test/java/org/eclipse/che/workspace/infrastructure/docker/local/LocalCheInfrastructureProvisionerTest.java index 7e0cf3a696..74b6855fc9 100644 --- a/infrastructures/docker/infrastructure/src/test/java/org/eclipse/che/workspace/infrastructure/docker/local/LocalCheInfrastructureProvisionerTest.java +++ b/infrastructures/docker/infrastructure/src/test/java/org/eclipse/che/workspace/infrastructure/docker/local/LocalCheInfrastructureProvisionerTest.java @@ -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(); } } diff --git a/infrastructures/docker/infrastructure/src/test/java/org/eclipse/che/workspace/infrastructure/docker/provisioner/installer/InstallerConfigApplierTest.java b/infrastructures/docker/infrastructure/src/test/java/org/eclipse/che/workspace/infrastructure/docker/provisioner/installer/InstallerConfigApplierTest.java deleted file mode 100644 index 8810881034..0000000000 --- a/infrastructures/docker/infrastructure/src/test/java/org/eclipse/che/workspace/infrastructure/docker/provisioner/installer/InstallerConfigApplierTest.java +++ /dev/null @@ -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 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 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 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 env = containerConf.getEnvironment(); - assertEquals(env.size(), 0); - } -} diff --git a/infrastructures/openshift/pom.xml b/infrastructures/openshift/pom.xml index e7d06e583d..c40b770e3a 100644 --- a/infrastructures/openshift/pom.xml +++ b/infrastructures/openshift/pom.xml @@ -74,10 +74,6 @@ org.eclipse.che.core che-core-api-installer - - org.eclipse.che.core - che-core-api-installer-shared - org.eclipse.che.core che-core-api-model diff --git a/infrastructures/openshift/src/main/java/org/eclipse/che/workspace/infrastructure/openshift/OpenShiftInfrastructureProvisioner.java b/infrastructures/openshift/src/main/java/org/eclipse/che/workspace/infrastructure/openshift/OpenShiftInfrastructureProvisioner.java index 2ff93fb995..6f289843b0 100644 --- a/infrastructures/openshift/src/main/java/org/eclipse/che/workspace/infrastructure/openshift/OpenShiftInfrastructureProvisioner.java +++ b/infrastructures/openshift/src/main/java/org/eclipse/che/workspace/infrastructure/openshift/OpenShiftInfrastructureProvisioner.java @@ -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 } diff --git a/infrastructures/openshift/src/main/java/org/eclipse/che/workspace/infrastructure/openshift/environment/OpenShiftEnvironmentParser.java b/infrastructures/openshift/src/main/java/org/eclipse/che/workspace/infrastructure/openshift/environment/OpenShiftEnvironmentParser.java index 4e600c5577..9bb3aa761c 100644 --- a/infrastructures/openshift/src/main/java/org/eclipse/che/workspace/infrastructure/openshift/environment/OpenShiftEnvironmentParser.java +++ b/infrastructures/openshift/src/main/java/org/eclipse/che/workspace/infrastructure/openshift/environment/OpenShiftEnvironmentParser.java @@ -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 envEntry : machineConfig.getEnv().entrySet()) { - putEnv(containerConfig.getEnv(), envEntry.getKey(), envEntry.getValue()); - } - } - } - } - } - - private Map getLabels(Pod pod) { - ObjectMeta metadata = pod.getMetadata(); - if (metadata == null) { - metadata = new ObjectMeta(); - pod.setMetadata(metadata); - } - - Map 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 envs, String key, String value) { - envs.removeIf(env -> key.equals(env.getName())); - envs.add(new EnvVar(key, value, null)); - } } diff --git a/infrastructures/openshift/src/main/java/org/eclipse/che/workspace/infrastructure/openshift/provision/env/EnvVarsConverter.java b/infrastructures/openshift/src/main/java/org/eclipse/che/workspace/infrastructure/openshift/provision/env/EnvVarsConverter.java new file mode 100644 index 0000000000..91c58b5b6c --- /dev/null +++ b/infrastructures/openshift/src/main/java/org/eclipse/che/workspace/infrastructure/openshift/provision/env/EnvVarsConverter.java @@ -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)); + }); + } + } + } + } +} diff --git a/infrastructures/openshift/src/main/java/org/eclipse/che/workspace/infrastructure/openshift/provision/installer/InstallerConfigProvisioner.java b/infrastructures/openshift/src/main/java/org/eclipse/che/workspace/infrastructure/openshift/provision/installer/InstallerConfigProvisioner.java index b8b76d90b1..eda7809f4e 100644 --- a/infrastructures/openshift/src/main/java/org/eclipse/che/workspace/infrastructure/openshift/provision/installer/InstallerConfigProvisioner.java +++ b/infrastructures/openshift/src/main/java/org/eclipse/che/workspace/infrastructure/openshift/provision/installer/InstallerConfigProvisioner.java @@ -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}. - * - *

    This class must be called before OpenShift environment is started, otherwise changing - * configuration has no effect. - * - *

    This class performs following changes to environment:
    - * - adds environment variable to {@link Container containers} that are required by installers or - * agents.
    - * - 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,...";
    - * - 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 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 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 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)); - } - } } diff --git a/infrastructures/openshift/src/main/java/org/eclipse/che/workspace/infrastructure/openshift/provision/labels/PodNameLabelProvisioner.java b/infrastructures/openshift/src/main/java/org/eclipse/che/workspace/infrastructure/openshift/provision/labels/PodNameLabelProvisioner.java new file mode 100644 index 0000000000..4758d90d47 --- /dev/null +++ b/infrastructures/openshift/src/main/java/org/eclipse/che/workspace/infrastructure/openshift/provision/labels/PodNameLabelProvisioner.java @@ -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 getLabels(Pod pod) { + ObjectMeta metadata = pod.getMetadata(); + if (metadata == null) { + metadata = new ObjectMeta(); + pod.setMetadata(metadata); + } + + Map labels = metadata.getLabels(); + if (labels == null) { + labels = new HashMap<>(); + metadata.setLabels(labels); + } + return labels; + } +} diff --git a/infrastructures/openshift/src/main/java/org/eclipse/che/workspace/infrastructure/openshift/provision/restartpolicy/RestartPolicyRewriter.java b/infrastructures/openshift/src/main/java/org/eclipse/che/workspace/infrastructure/openshift/provision/restartpolicy/RestartPolicyRewriter.java new file mode 100644 index 0000000000..95c3cd9c94 --- /dev/null +++ b/infrastructures/openshift/src/main/java/org/eclipse/che/workspace/infrastructure/openshift/provision/restartpolicy/RestartPolicyRewriter.java @@ -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); + } +} diff --git a/infrastructures/openshift/src/main/java/org/eclipse/che/workspace/infrastructure/openshift/provision/server/ServersConverter.java b/infrastructures/openshift/src/main/java/org/eclipse/che/workspace/infrastructure/openshift/provision/server/ServersConverter.java new file mode 100644 index 0000000000..c6517c6569 --- /dev/null +++ b/infrastructures/openshift/src/main/java/org/eclipse/che/workspace/infrastructure/openshift/provision/server/ServersConverter.java @@ -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. + * + *

    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()); + } + } + } + } +} diff --git a/infrastructures/openshift/src/test/java/org/eclipse/che/workspace/infrastructure/openshift/OpenShiftInfrastructureProvisionerTest.java b/infrastructures/openshift/src/test/java/org/eclipse/che/workspace/infrastructure/openshift/OpenShiftInfrastructureProvisionerTest.java index ce25520ac9..3bef232faa 100644 --- a/infrastructures/openshift/src/test/java/org/eclipse/che/workspace/infrastructure/openshift/OpenShiftInfrastructureProvisionerTest.java +++ b/infrastructures/openshift/src/test/java/org/eclipse/che/workspace/infrastructure/openshift/OpenShiftInfrastructureProvisionerTest.java @@ -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)); diff --git a/infrastructures/openshift/src/test/java/org/eclipse/che/workspace/infrastructure/openshift/environment/OpenShiftEnvironmentParserTest.java b/infrastructures/openshift/src/test/java/org/eclipse/che/workspace/infrastructure/openshift/environment/OpenShiftEnvironmentParserTest.java index a0a6ac7558..e58fab9089 100644 --- a/infrastructures/openshift/src/test/java/org/eclipse/che/workspace/infrastructure/openshift/environment/OpenShiftEnvironmentParserTest.java +++ b/infrastructures/openshift/src/test/java/org/eclipse/che/workspace/infrastructure/openshift/environment/OpenShiftEnvironmentParserTest.java @@ -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 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 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(); - } } diff --git a/infrastructures/openshift/src/test/java/org/eclipse/che/workspace/infrastructure/openshift/provision/installer/InstallerConfigProvisionerTest.java b/infrastructures/openshift/src/test/java/org/eclipse/che/workspace/infrastructure/openshift/provision/installer/InstallerConfigProvisionerTest.java index b25cb1e3ac..43aa1aea4f 100644 --- a/infrastructures/openshift/src/test/java/org/eclipse/che/workspace/infrastructure/openshift/provision/installer/InstallerConfigProvisionerTest.java +++ b/infrastructures/openshift/src/test/java/org/eclipse/che/workspace/infrastructure/openshift/provision/installer/InstallerConfigProvisionerTest.java @@ -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 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 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 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 envs = container.getEnv(); - verifyContainsEnv(envs, "CHE_API", CHE_SERVER_ENDPOINT); - verifyContainsEnv(envs, "USER_TOKEN", "superToken"); - verifyContainsEnv(envs, "CHE_WORKSPACE_ID", WORKSPACE_ID); + Map 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 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 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 machines) { @@ -156,18 +106,10 @@ public class InstallerConfigProvisionerTest { return environment; } - private void verifyDoesNotContainEnv(List envs, String name) { - Optional env = envs.stream().filter(e -> e.getName().equals(name)).findAny(); + private void verifyContainsEnv(Map 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 envs, String name, String expectedValue) { - Optional 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 installers = new ArrayList<>(); - private Map 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 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 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; - } - } } diff --git a/infrastructures/openshift/src/test/java/org/eclipse/che/workspace/infrastructure/openshift/provision/restartpolicy/RestartPolicyRewriterTest.java b/infrastructures/openshift/src/test/java/org/eclipse/che/workspace/infrastructure/openshift/provision/restartpolicy/RestartPolicyRewriterTest.java new file mode 100644 index 0000000000..a5bd5e71c1 --- /dev/null +++ b/infrastructures/openshift/src/test/java/org/eclipse/che/workspace/infrastructure/openshift/provision/restartpolicy/RestartPolicyRewriterTest.java @@ -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 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 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 captureWarnings() { + verify(environment, atLeastOnce()).addWarning(warningCaptor.capture()); + return warningCaptor.getAllValues(); + } +} diff --git a/plugins/plugin-urlfactory/pom.xml b/plugins/plugin-urlfactory/pom.xml index e6a6a2b644..159dc7563d 100644 --- a/plugins/plugin-urlfactory/pom.xml +++ b/plugins/plugin-urlfactory/pom.xml @@ -70,6 +70,10 @@ org.eclipse.che.core che-core-api-factory-shared + + org.eclipse.che.core + che-core-api-model + org.eclipse.che.core che-core-api-workspace-shared diff --git a/plugins/plugin-urlfactory/src/test/java/org/eclipse/che/plugin/urlfactory/URLFactoryBuilderTest.java b/plugins/plugin-urlfactory/src/test/java/org/eclipse/che/plugin/urlfactory/URLFactoryBuilderTest.java index f038723c41..366c06f1e5 100644 --- a/plugins/plugin-urlfactory/src/test/java/org/eclipse/che/plugin/urlfactory/URLFactoryBuilderTest.java +++ b/plugins/plugin-urlfactory/src/test/java/org/eclipse/che/plugin/urlfactory/URLFactoryBuilderTest.java @@ -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 = diff --git a/selenium/che-selenium-core/src/main/java/org/eclipse/che/selenium/core/client/TestWorkspaceServiceClient.java b/selenium/che-selenium-core/src/main/java/org/eclipse/che/selenium/core/client/TestWorkspaceServiceClient.java index 86018f500e..3cfe7ec30c 100644 --- a/selenium/che-selenium-core/src/main/java/org/eclipse/che/selenium/core/client/TestWorkspaceServiceClient.java +++ b/selenium/che-selenium-core/src/main/java/org/eclipse/che/selenium/core/client/TestWorkspaceServiceClient.java @@ -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); diff --git a/wsmaster/che-core-api-factory/src/test/java/org/eclipse/che/api/factory/server/builder/FactoryBuilderTest.java b/wsmaster/che-core-api-factory/src/test/java/org/eclipse/che/api/factory/server/builder/FactoryBuilderTest.java index fe14321182..a1ca562d67 100644 --- a/wsmaster/che-core-api-factory/src/test/java/org/eclipse/che/api/factory/server/builder/FactoryBuilderTest.java +++ b/wsmaster/che-core-api-factory/src/test/java/org/eclipse/che/api/factory/server/builder/FactoryBuilderTest.java @@ -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) diff --git a/wsmaster/che-core-api-installer-shared/src/main/java/org/eclipse/che/api/installer/shared/model/Installer.java b/wsmaster/che-core-api-installer-shared/src/main/java/org/eclipse/che/api/installer/shared/model/Installer.java index 30cc53122c..bfedd4a709 100644 --- a/wsmaster/che-core-api-installer-shared/src/main/java/org/eclipse/che/api/installer/shared/model/Installer.java +++ b/wsmaster/che-core-api-installer-shared/src/main/java/org/eclipse/che/api/installer/shared/model/Installer.java @@ -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. + * + *

    Example: { "environment" : "envVar1=value1,envVar2=value2" } + */ + String ENVIRONMENT_PROPERTY = "environment"; + /** Returns the id of the installer. */ String getId(); diff --git a/wsmaster/che-core-api-workspace/src/main/java/org/eclipse/che/api/workspace/server/WorkspaceValidator.java b/wsmaster/che-core-api-workspace/src/main/java/org/eclipse/che/api/workspace/server/WorkspaceValidator.java index 05b5293352..75dc850a47 100644 --- a/wsmaster/che-core-api-workspace/src/main/java/org/eclipse/che/api/workspace/server/WorkspaceValidator.java +++ b/wsmaster/che-core-api-workspace/src/main/java/org/eclipse/che/api/workspace/server/WorkspaceValidator.java @@ -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 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}. diff --git a/wsmaster/che-core-api-workspace/src/main/java/org/eclipse/che/api/workspace/server/adapter/WorkspaceConfigJsonAdapter.java b/wsmaster/che-core-api-workspace/src/main/java/org/eclipse/che/api/workspace/server/adapter/WorkspaceConfigJsonAdapter.java index 47ad8ef6f3..89113ead33 100644 --- a/wsmaster/che-core-api-workspace/src/main/java/org/eclipse/che/api/workspace/server/adapter/WorkspaceConfigJsonAdapter.java +++ b/wsmaster/che-core-api-workspace/src/main/java/org/eclipse/che/api/workspace/server/adapter/WorkspaceConfigJsonAdapter.java @@ -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); } } diff --git a/wsmaster/che-core-api-workspace/src/main/java/org/eclipse/che/api/workspace/server/spi/InternalMachineConfig.java b/wsmaster/che-core-api-workspace/src/main/java/org/eclipse/che/api/workspace/server/spi/InternalMachineConfig.java index 1f82cf978b..a188b8b656 100644 --- a/wsmaster/che-core-api-workspace/src/main/java/org/eclipse/che/api/workspace/server/spi/InternalMachineConfig.java +++ b/wsmaster/che-core-api-workspace/src/main/java/org/eclipse/che/api/workspace/server/spi/InternalMachineConfig.java @@ -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 sortedInstallers = installerRegistry.getOrderedInstallers(installersKeys); for (Installer installer : sortedInstallers) { - this.installers.add(new InstallerImpl(installer)); - for (Map.Entry 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 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]); + } + } } diff --git a/wsmaster/che-core-api-workspace/src/test/java/org/eclipse/che/api/workspace/server/WorkspaceConfigJsonAdapterTest.java b/wsmaster/che-core-api-workspace/src/test/java/org/eclipse/che/api/workspace/server/WorkspaceConfigJsonAdapterTest.java index b4d704d569..c637c0c05e 100644 --- a/wsmaster/che-core-api-workspace/src/test/java/org/eclipse/che/api/workspace/server/WorkspaceConfigJsonAdapterTest.java +++ b/wsmaster/che-core-api-workspace/src/test/java/org/eclipse/che/api/workspace/server/WorkspaceConfigJsonAdapterTest.java @@ -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"); diff --git a/wsmaster/che-core-api-workspace/src/test/java/org/eclipse/che/api/workspace/server/WorkspaceManagerTest.java b/wsmaster/che-core-api-workspace/src/test/java/org/eclipse/che/api/workspace/server/WorkspaceManagerTest.java index 61676878e4..020ed87570 100644 --- a/wsmaster/che-core-api-workspace/src/test/java/org/eclipse/che/api/workspace/server/WorkspaceManagerTest.java +++ b/wsmaster/che-core-api-workspace/src/test/java/org/eclipse/che/api/workspace/server/WorkspaceManagerTest.java @@ -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), diff --git a/wsmaster/che-core-api-workspace/src/test/java/org/eclipse/che/api/workspace/server/WorkspaceServiceTest.java b/wsmaster/che-core-api-workspace/src/test/java/org/eclipse/che/api/workspace/server/WorkspaceServiceTest.java index 09d8234bc4..e3ba9f0393 100644 --- a/wsmaster/che-core-api-workspace/src/test/java/org/eclipse/che/api/workspace/server/WorkspaceServiceTest.java +++ b/wsmaster/che-core-api-workspace/src/test/java/org/eclipse/che/api/workspace/server/WorkspaceServiceTest.java @@ -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( diff --git a/wsmaster/che-core-api-workspace/src/test/java/org/eclipse/che/api/workspace/server/WorkspaceValidatorTest.java b/wsmaster/che-core-api-workspace/src/test/java/org/eclipse/che/api/workspace/server/WorkspaceValidatorTest.java index 9de356e897..d2c44b29a9 100644 --- a/wsmaster/che-core-api-workspace/src/test/java/org/eclipse/che/api/workspace/server/WorkspaceValidatorTest.java +++ b/wsmaster/che-core-api-workspace/src/test/java/org/eclipse/che/api/workspace/server/WorkspaceValidatorTest.java @@ -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") diff --git a/wsmaster/che-core-api-workspace/src/test/java/org/eclipse/che/api/workspace/server/stack/StackLoaderTest.java b/wsmaster/che-core-api-workspace/src/test/java/org/eclipse/che/api/workspace/server/stack/StackLoaderTest.java index 7b1ee30497..aa8647aa05 100644 --- a/wsmaster/che-core-api-workspace/src/test/java/org/eclipse/che/api/workspace/server/stack/StackLoaderTest.java +++ b/wsmaster/che-core-api-workspace/src/test/java/org/eclipse/che/api/workspace/server/stack/StackLoaderTest.java @@ -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);