diff --git a/assembly/assembly-wsmaster-war/src/main/resources/META-INF/persistence.xml b/assembly/assembly-wsmaster-war/src/main/resources/META-INF/persistence.xml index 712e1fe5d2..e0a3cc5c63 100644 --- a/assembly/assembly-wsmaster-war/src/main/resources/META-INF/persistence.xml +++ b/assembly/assembly-wsmaster-war/src/main/resources/META-INF/persistence.xml @@ -30,6 +30,7 @@ org.eclipse.che.api.workspace.server.model.impl.ProjectConfigImpl$Attribute org.eclipse.che.api.workspace.server.model.impl.SourceStorageImpl org.eclipse.che.api.workspace.server.model.impl.ServerConfigImpl + org.eclipse.che.api.workspace.server.model.impl.VolumeImpl org.eclipse.che.api.workspace.server.model.impl.stack.StackImpl org.eclipse.che.api.workspace.server.model.impl.CommandImpl 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 162377c7de..e8595f73f7 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 @@ -44,4 +44,7 @@ public interface MachineConfig { /** Returns attributes of resources of machine. */ Map getAttributes(); + + /** Returns volumes of machine */ + Map getVolumes(); } diff --git a/core/che-core-api-model/src/main/java/org/eclipse/che/api/core/model/workspace/config/Volume.java b/core/che-core-api-model/src/main/java/org/eclipse/che/api/core/model/workspace/config/Volume.java new file mode 100644 index 0000000000..1eaedf08be --- /dev/null +++ b/core/che-core-api-model/src/main/java/org/eclipse/che/api/core/model/workspace/config/Volume.java @@ -0,0 +1,22 @@ +/* + * 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.api.core.model.workspace.config; + +/** + * Machine volume configuration. + * + * @author Alexander Garagatyi + */ +public interface Volume { + + /** Mount path of the volume in the machine */ + String getPath(); +} diff --git a/ide/che-core-ide-api/src/main/java/org/eclipse/che/ide/api/workspace/model/MachineConfigImpl.java b/ide/che-core-ide-api/src/main/java/org/eclipse/che/ide/api/workspace/model/MachineConfigImpl.java index 7e119c1338..4b8ca2ddce 100644 --- a/ide/che-core-ide-api/src/main/java/org/eclipse/che/ide/api/workspace/model/MachineConfigImpl.java +++ b/ide/che-core-ide-api/src/main/java/org/eclipse/che/ide/api/workspace/model/MachineConfigImpl.java @@ -17,6 +17,7 @@ import java.util.Map; import java.util.stream.Collectors; import org.eclipse.che.api.core.model.workspace.config.MachineConfig; import org.eclipse.che.api.core.model.workspace.config.ServerConfig; +import org.eclipse.che.api.core.model.workspace.config.Volume; public class MachineConfigImpl implements MachineConfig { @@ -24,12 +25,14 @@ public class MachineConfigImpl implements MachineConfig { private Map env; private Map attributes; private Map servers; + private Map volumes; public MachineConfigImpl( List installers, Map servers, Map env, - Map attributes) { + Map attributes, + Map volumes) { if (installers != null) { this.installers = new ArrayList<>(installers); } @@ -48,10 +51,23 @@ public class MachineConfigImpl implements MachineConfig { if (attributes != null) { this.attributes = new HashMap<>(attributes); } + if (volumes != null) { + this.volumes = + volumes + .entrySet() + .stream() + .collect( + Collectors.toMap(Map.Entry::getKey, entry -> new VolumeImpl(entry.getValue()))); + } } public MachineConfigImpl(MachineConfig machine) { - this(machine.getInstallers(), machine.getServers(), machine.getEnv(), machine.getAttributes()); + this( + machine.getInstallers(), + machine.getServers(), + machine.getEnv(), + machine.getAttributes(), + machine.getVolumes()); } @Override @@ -86,6 +102,14 @@ public class MachineConfigImpl implements MachineConfig { return attributes; } + @Override + public Map getVolumes() { + if (volumes == null) { + volumes = new HashMap<>(); + } + return volumes; + } + @Override public boolean equals(Object obj) { if (this == obj) { @@ -98,7 +122,8 @@ public class MachineConfigImpl implements MachineConfig { return getInstallers().equals(that.getInstallers()) && getEnv().equals(that.getEnv()) && getAttributes().equals(that.getAttributes()) - && getServers().equals(that.getServers()); + && getServers().equals(that.getServers()) + && getVolumes().equals(that.getVolumes()); } @Override @@ -108,6 +133,7 @@ public class MachineConfigImpl implements MachineConfig { hash = 31 * hash + getEnv().hashCode(); hash = 31 * hash + getAttributes().hashCode(); hash = 31 * hash + getServers().hashCode(); + hash = 31 * hash + getVolumes().hashCode(); return hash; } @@ -122,6 +148,8 @@ public class MachineConfigImpl implements MachineConfig { + attributes + ", servers=" + servers + + ", volumes=" + + volumes + '}'; } } diff --git a/ide/che-core-ide-api/src/main/java/org/eclipse/che/ide/api/workspace/model/VolumeImpl.java b/ide/che-core-ide-api/src/main/java/org/eclipse/che/ide/api/workspace/model/VolumeImpl.java new file mode 100644 index 0000000000..833055ba0f --- /dev/null +++ b/ide/che-core-ide-api/src/main/java/org/eclipse/che/ide/api/workspace/model/VolumeImpl.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.ide.api.workspace.model; + +import java.util.Objects; +import org.eclipse.che.api.core.model.workspace.config.Volume; + +/** @author Alexander Garagatyi */ +public class VolumeImpl implements Volume { + private final String path; + + public VolumeImpl(String path) { + this.path = path; + } + + public VolumeImpl(Volume value) { + path = value.getPath(); + } + + @Override + public String getPath() { + return path; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + if (!(obj instanceof VolumeImpl)) { + return false; + } + final VolumeImpl that = (VolumeImpl) obj; + return Objects.equals(path, that.path); + } + + @Override + public int hashCode() { + int hash = 7; + hash = 31 * hash + Objects.hashCode(path); + return hash; + } + + @Override + public String toString() { + return "VolumeImpl{" + "path='" + path + '\'' + '}'; + } +} diff --git a/multiuser/integration-tests/che-multiuser-cascade-removal/src/test/resources/META-INF/persistence.xml b/multiuser/integration-tests/che-multiuser-cascade-removal/src/test/resources/META-INF/persistence.xml index 6b20e5a992..3e7b06c196 100644 --- a/multiuser/integration-tests/che-multiuser-cascade-removal/src/test/resources/META-INF/persistence.xml +++ b/multiuser/integration-tests/che-multiuser-cascade-removal/src/test/resources/META-INF/persistence.xml @@ -29,6 +29,7 @@ org.eclipse.che.api.workspace.server.model.impl.ProjectConfigImpl$Attribute org.eclipse.che.api.workspace.server.model.impl.SourceStorageImpl org.eclipse.che.api.workspace.server.model.impl.ServerConfigImpl + org.eclipse.che.api.workspace.server.model.impl.VolumeImpl org.eclipse.che.api.workspace.server.model.impl.stack.StackImpl org.eclipse.che.api.workspace.server.model.impl.CommandImpl diff --git a/multiuser/integration-tests/che-multiuser-postgresql-tck/src/test/resources/META-INF/persistence.xml b/multiuser/integration-tests/che-multiuser-postgresql-tck/src/test/resources/META-INF/persistence.xml index f9f8f38c0d..e6387da621 100644 --- a/multiuser/integration-tests/che-multiuser-postgresql-tck/src/test/resources/META-INF/persistence.xml +++ b/multiuser/integration-tests/che-multiuser-postgresql-tck/src/test/resources/META-INF/persistence.xml @@ -30,6 +30,7 @@ org.eclipse.che.api.workspace.server.model.impl.SourceStorageImpl org.eclipse.che.api.workspace.server.model.impl.ServerConfigImpl org.eclipse.che.api.workspace.server.model.impl.stack.StackImpl + org.eclipse.che.api.workspace.server.model.impl.VolumeImpl org.eclipse.che.api.workspace.server.model.impl.CommandImpl org.eclipse.che.workspace.infrastructure.docker.snapshot.MachineSourceImpl diff --git a/multiuser/permission/che-multiuser-permission-workspace/src/test/java/org/eclipse/che/multiuser/permission/workspace/server/jpa/WorkspaceTckModule.java b/multiuser/permission/che-multiuser-permission-workspace/src/test/java/org/eclipse/che/multiuser/permission/workspace/server/jpa/WorkspaceTckModule.java index c771e8f0c2..aee87ba352 100644 --- a/multiuser/permission/che-multiuser-permission-workspace/src/test/java/org/eclipse/che/multiuser/permission/workspace/server/jpa/WorkspaceTckModule.java +++ b/multiuser/permission/che-multiuser-permission-workspace/src/test/java/org/eclipse/che/multiuser/permission/workspace/server/jpa/WorkspaceTckModule.java @@ -23,6 +23,7 @@ import org.eclipse.che.api.workspace.server.model.impl.ProjectConfigImpl; import org.eclipse.che.api.workspace.server.model.impl.RecipeImpl; import org.eclipse.che.api.workspace.server.model.impl.ServerConfigImpl; import org.eclipse.che.api.workspace.server.model.impl.SourceStorageImpl; +import org.eclipse.che.api.workspace.server.model.impl.VolumeImpl; import org.eclipse.che.api.workspace.server.model.impl.WorkspaceConfigImpl; import org.eclipse.che.api.workspace.server.model.impl.WorkspaceImpl; import org.eclipse.che.api.workspace.server.model.impl.stack.StackImpl; @@ -75,7 +76,8 @@ public class WorkspaceTckModule extends TckModule { ServerConfigImpl.class, StackImpl.class, CommandImpl.class, - RecipeImpl.class) + RecipeImpl.class, + VolumeImpl.class) .addEntityClass( "org.eclipse.che.api.workspace.server.model.impl.ProjectConfigImpl$Attribute") .setExceptionHandler(H2ExceptionHandler.class) diff --git a/multiuser/permission/che-multiuser-permission-workspace/src/test/java/org/eclipse/che/multiuser/permission/workspace/server/spi/jpa/JpaTckModule.java b/multiuser/permission/che-multiuser-permission-workspace/src/test/java/org/eclipse/che/multiuser/permission/workspace/server/spi/jpa/JpaTckModule.java index ace4264ffe..7018583d9e 100644 --- a/multiuser/permission/che-multiuser-permission-workspace/src/test/java/org/eclipse/che/multiuser/permission/workspace/server/spi/jpa/JpaTckModule.java +++ b/multiuser/permission/che-multiuser-permission-workspace/src/test/java/org/eclipse/che/multiuser/permission/workspace/server/spi/jpa/JpaTckModule.java @@ -20,6 +20,7 @@ import org.eclipse.che.api.workspace.server.model.impl.ProjectConfigImpl; import org.eclipse.che.api.workspace.server.model.impl.RecipeImpl; import org.eclipse.che.api.workspace.server.model.impl.ServerConfigImpl; import org.eclipse.che.api.workspace.server.model.impl.SourceStorageImpl; +import org.eclipse.che.api.workspace.server.model.impl.VolumeImpl; import org.eclipse.che.api.workspace.server.model.impl.WorkspaceConfigImpl; import org.eclipse.che.api.workspace.server.model.impl.WorkspaceImpl; import org.eclipse.che.api.workspace.server.model.impl.stack.StackImpl; @@ -67,7 +68,8 @@ public class JpaTckModule extends TckModule { ServerConfigImpl.class, StackImpl.class, CommandImpl.class, - RecipeImpl.class) + RecipeImpl.class, + VolumeImpl.class) .addEntityClass( "org.eclipse.che.api.workspace.server.model.impl.ProjectConfigImpl$Attribute") .setExceptionHandler(H2ExceptionHandler.class) diff --git a/wsmaster/che-core-api-factory/src/test/java/org/eclipse/che/api/factory/server/jpa/FactoryTckModule.java b/wsmaster/che-core-api-factory/src/test/java/org/eclipse/che/api/factory/server/jpa/FactoryTckModule.java index 87a44314af..6d4c87263a 100644 --- a/wsmaster/che-core-api-factory/src/test/java/org/eclipse/che/api/factory/server/jpa/FactoryTckModule.java +++ b/wsmaster/che-core-api-factory/src/test/java/org/eclipse/che/api/factory/server/jpa/FactoryTckModule.java @@ -29,6 +29,7 @@ import org.eclipse.che.api.workspace.server.model.impl.ProjectConfigImpl; import org.eclipse.che.api.workspace.server.model.impl.RecipeImpl; import org.eclipse.che.api.workspace.server.model.impl.ServerConfigImpl; import org.eclipse.che.api.workspace.server.model.impl.SourceStorageImpl; +import org.eclipse.che.api.workspace.server.model.impl.VolumeImpl; import org.eclipse.che.api.workspace.server.model.impl.WorkspaceConfigImpl; import org.eclipse.che.commons.test.db.H2DBTestServer; import org.eclipse.che.commons.test.db.H2JpaCleaner; @@ -75,7 +76,8 @@ public class FactoryTckModule extends TckModule { MachineConfigImpl.class, SourceStorageImpl.class, ServerConfigImpl.class, - CommandImpl.class) + CommandImpl.class, + VolumeImpl.class) .addEntityClass( "org.eclipse.che.api.workspace.server.model.impl.ProjectConfigImpl$Attribute") .setExceptionHandler(H2ExceptionHandler.class) diff --git a/wsmaster/che-core-api-workspace-shared/src/main/java/org/eclipse/che/api/workspace/shared/dto/MachineConfigDto.java b/wsmaster/che-core-api-workspace-shared/src/main/java/org/eclipse/che/api/workspace/shared/dto/MachineConfigDto.java index d34ce3cc82..74b71a0dcc 100644 --- a/wsmaster/che-core-api-workspace-shared/src/main/java/org/eclipse/che/api/workspace/shared/dto/MachineConfigDto.java +++ b/wsmaster/che-core-api-workspace-shared/src/main/java/org/eclipse/che/api/workspace/shared/dto/MachineConfigDto.java @@ -52,4 +52,12 @@ public interface MachineConfigDto extends MachineConfig { void setAttributes(Map attributes); MachineConfigDto withAttributes(Map attributes); + + @Override + @FactoryParameter(obligation = OPTIONAL) + Map getVolumes(); + + void setVolumes(Map volumes); + + MachineConfigDto withVolumes(Map volumes); } diff --git a/wsmaster/che-core-api-workspace-shared/src/main/java/org/eclipse/che/api/workspace/shared/dto/VolumeDto.java b/wsmaster/che-core-api-workspace-shared/src/main/java/org/eclipse/che/api/workspace/shared/dto/VolumeDto.java new file mode 100644 index 0000000000..8837aafc6b --- /dev/null +++ b/wsmaster/che-core-api-workspace-shared/src/main/java/org/eclipse/che/api/workspace/shared/dto/VolumeDto.java @@ -0,0 +1,25 @@ +/* + * 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.api.workspace.shared.dto; + +import org.eclipse.che.api.core.model.workspace.config.Volume; +import org.eclipse.che.dto.shared.DTO; + +/** @author Alexander Garagatyi */ +@DTO +public interface VolumeDto extends Volume { + @Override + String getPath(); + + void setPath(String path); + + VolumeDto withPath(String path); +} diff --git a/wsmaster/che-core-api-workspace/src/main/java/org/eclipse/che/api/workspace/server/DtoConverter.java b/wsmaster/che-core-api-workspace/src/main/java/org/eclipse/che/api/workspace/server/DtoConverter.java index 1dea864a5a..26eb6ecbb9 100644 --- a/wsmaster/che-core-api-workspace/src/main/java/org/eclipse/che/api/workspace/server/DtoConverter.java +++ b/wsmaster/che-core-api-workspace/src/main/java/org/eclipse/che/api/workspace/server/DtoConverter.java @@ -26,6 +26,7 @@ import org.eclipse.che.api.core.model.workspace.config.MachineConfig; import org.eclipse.che.api.core.model.workspace.config.ProjectConfig; import org.eclipse.che.api.core.model.workspace.config.ServerConfig; import org.eclipse.che.api.core.model.workspace.config.SourceStorage; +import org.eclipse.che.api.core.model.workspace.config.Volume; import org.eclipse.che.api.core.model.workspace.runtime.Machine; import org.eclipse.che.api.core.model.workspace.runtime.RuntimeIdentity; import org.eclipse.che.api.core.model.workspace.runtime.Server; @@ -41,6 +42,7 @@ import org.eclipse.che.api.workspace.shared.dto.RuntimeIdentityDto; import org.eclipse.che.api.workspace.shared.dto.ServerConfigDto; import org.eclipse.che.api.workspace.shared.dto.ServerDto; import org.eclipse.che.api.workspace.shared.dto.SourceStorageDto; +import org.eclipse.che.api.workspace.shared.dto.VolumeDto; import org.eclipse.che.api.workspace.shared.dto.WorkspaceConfigDto; import org.eclipse.che.api.workspace.shared.dto.WorkspaceDto; import org.eclipse.che.api.workspace.shared.dto.stack.StackComponentDto; @@ -204,6 +206,14 @@ public final class DtoConverter { if (machine.getAttributes() != null) { machineDto.setAttributes(machine.getAttributes()); } + if (machine.getVolumes() != null) { + machineDto.setVolumes( + machine + .getVolumes() + .entrySet() + .stream() + .collect(toMap(Map.Entry::getKey, entry -> asDto(entry.getValue())))); + } return machineDto; } @@ -254,5 +264,10 @@ public final class DtoConverter { .withOwner(identity.getOwner()); } + /** Converts {@link Volume} to {@link VolumeDto}. */ + public static VolumeDto asDto(Volume volume) { + return newDto(VolumeDto.class).withPath(volume.getPath()); + } + private DtoConverter() {} } 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 a645e25d84..294f8f93b5 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 @@ -28,6 +28,7 @@ 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.core.model.workspace.config.Volume; import org.eclipse.che.api.workspace.server.spi.InfrastructureException; /** @@ -45,6 +46,9 @@ public class WorkspaceValidator { private static final Pattern WS_NAME = Pattern.compile("[a-zA-Z0-9][-_.a-zA-Z0-9]{1,98}[a-zA-Z0-9]"); + private static final Pattern VOLUME_NAME = Pattern.compile("[a-z][a-z0-9]{1,18}"); + private static final Pattern VOLUME_PATH = Pattern.compile("/.+"); + private final WorkspaceRuntimes runtimes; @Inject @@ -146,6 +150,27 @@ public class WorkspaceValidator { memoryAttribute, MEMORY_LIMIT_ATTRIBUTE, name)); } } + + for (Entry volumeEntry : machine.getVolumes().entrySet()) { + String volumeName = volumeEntry.getKey(); + check( + VOLUME_NAME.matcher(volumeName).matches(), + "Volume name '%s' in machine '%s' is invalid", + volumeName, + name); + Volume volume = volumeEntry.getValue(); + check( + volume != null && !isNullOrEmpty(volume.getPath()), + "Path of volume '%s' in machine '%s' is invalid. It should not be empty", + volumeName, + name); + check( + VOLUME_PATH.matcher(volume.getPath()).matches(), + "Path '%s' of volume '%s' in machine '%s' is invalid. It should be absolute", + volume.getPath(), + volumeName, + name); + } } /** diff --git a/wsmaster/che-core-api-workspace/src/main/java/org/eclipse/che/api/workspace/server/model/impl/MachineConfigImpl.java b/wsmaster/che-core-api-workspace/src/main/java/org/eclipse/che/api/workspace/server/model/impl/MachineConfigImpl.java index 0d978a2b07..258fd5fc6e 100644 --- a/wsmaster/che-core-api-workspace/src/main/java/org/eclipse/che/api/workspace/server/model/impl/MachineConfigImpl.java +++ b/wsmaster/che-core-api-workspace/src/main/java/org/eclipse/che/api/workspace/server/model/impl/MachineConfigImpl.java @@ -30,6 +30,7 @@ import javax.persistence.OneToMany; import javax.persistence.Table; import org.eclipse.che.api.core.model.workspace.config.MachineConfig; import org.eclipse.che.api.core.model.workspace.config.ServerConfig; +import org.eclipse.che.api.core.model.workspace.config.Volume; /** @author Alexander Garagatyi */ @Entity(name = "ExternalMachine") @@ -72,13 +73,19 @@ public class MachineConfigImpl implements MachineConfig { @MapKeyColumn(name = "servers_key") private Map servers; + @OneToMany(cascade = CascadeType.ALL, orphanRemoval = true, fetch = FetchType.EAGER) + @JoinColumn(name = "machine_id") + @MapKeyColumn(name = "name") + private Map volumes; + public MachineConfigImpl() {} public MachineConfigImpl( List installers, Map servers, Map env, - Map attributes) { + Map attributes, + Map volumes) { if (installers != null) { this.installers = new ArrayList<>(installers); } @@ -97,10 +104,23 @@ public class MachineConfigImpl implements MachineConfig { if (attributes != null) { this.attributes = new HashMap<>(attributes); } + if (volumes != null) { + this.volumes = + volumes + .entrySet() + .stream() + .collect( + Collectors.toMap(Map.Entry::getKey, entry -> new VolumeImpl(entry.getValue()))); + } } public MachineConfigImpl(MachineConfig machine) { - this(machine.getInstallers(), machine.getServers(), machine.getEnv(), machine.getAttributes()); + this( + machine.getInstallers(), + machine.getServers(), + machine.getEnv(), + machine.getAttributes(), + machine.getVolumes()); } @Override @@ -171,6 +191,23 @@ public class MachineConfigImpl implements MachineConfig { return this; } + @Override + public Map getVolumes() { + if (volumes == null) { + volumes = new HashMap<>(); + } + return volumes; + } + + public void setVolumes(Map volumes) { + this.volumes = volumes; + } + + public MachineConfigImpl withVolumes(Map volumes) { + this.volumes = volumes; + return this; + } + @Override public boolean equals(Object obj) { if (this == obj) { @@ -184,7 +221,8 @@ public class MachineConfigImpl implements MachineConfig { && getInstallers().equals(that.getInstallers()) && getEnv().equals(that.getEnv()) && getAttributes().equals(that.getAttributes()) - && getServers().equals(that.getServers()); + && getServers().equals(that.getServers()) + && getVolumes().equals(that.getVolumes()); } @Override @@ -195,6 +233,7 @@ public class MachineConfigImpl implements MachineConfig { hash = 31 * hash + getEnv().hashCode(); hash = 31 * hash + getAttributes().hashCode(); hash = 31 * hash + getServers().hashCode(); + hash = 31 * hash + getVolumes().hashCode(); return hash; } @@ -211,6 +250,8 @@ public class MachineConfigImpl implements MachineConfig { + attributes + ", servers=" + servers + + ", volumes=" + + volumes + '}'; } } diff --git a/wsmaster/che-core-api-workspace/src/main/java/org/eclipse/che/api/workspace/server/model/impl/VolumeImpl.java b/wsmaster/che-core-api-workspace/src/main/java/org/eclipse/che/api/workspace/server/model/impl/VolumeImpl.java new file mode 100644 index 0000000000..77018c5f49 --- /dev/null +++ b/wsmaster/che-core-api-workspace/src/main/java/org/eclipse/che/api/workspace/server/model/impl/VolumeImpl.java @@ -0,0 +1,75 @@ +/* + * 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.api.workspace.server.model.impl; + +import java.util.Objects; +import javax.persistence.Column; +import javax.persistence.Entity; +import javax.persistence.GeneratedValue; +import javax.persistence.Id; +import javax.persistence.Table; +import org.eclipse.che.api.core.model.workspace.config.Volume; + +/** @author Alexander Garagatyi */ +@Entity(name = "MachineVolume") +@Table(name = "machine_volume") +public class VolumeImpl implements Volume { + + @Id + @GeneratedValue + @Column(name = "id") + private Long id; + + @Column(name = "path") + private String path; + + public VolumeImpl() {} + + public VolumeImpl(Volume value) { + path = value.getPath(); + } + + @Override + public String getPath() { + return path; + } + + public void setPath(String path) { + this.path = path; + } + + public VolumeImpl withPath(String path) { + this.path = path; + return this; + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (!(o instanceof VolumeImpl)) { + return false; + } + VolumeImpl volume = (VolumeImpl) o; + return Objects.equals(id, volume.id) && Objects.equals(getPath(), volume.getPath()); + } + + @Override + public int hashCode() { + return Objects.hash(id, getPath()); + } + + @Override + public String toString() { + return "VolumeImpl{" + "id=" + id + ", path='" + path + '\'' + '}'; + } +} 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 216eaf26ee..b0e350ba65 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 @@ -558,7 +558,8 @@ public class WorkspaceManagerTest { singletonList("org.eclipse.che.ws-agent"), null, singletonMap("CHE_ENV", "value"), - singletonMap(MEMORY_LIMIT_ATTRIBUTE, "10000")); + singletonMap(MEMORY_LIMIT_ATTRIBUTE, "10000"), + emptyMap()); 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 fde6280225..44fffb91bd 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 @@ -915,7 +915,8 @@ public class WorkspaceServiceTest { singletonList("org.eclipse.che.ws-agent"), null, singletonMap("CHE_ENV", "value"), - singletonMap(MEMORY_LIMIT_ATTRIBUTE, "10000")); + singletonMap(MEMORY_LIMIT_ATTRIBUTE, "10000"), + emptyMap()); 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 e29f0722c3..1b178cb806 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 @@ -27,6 +27,7 @@ import org.eclipse.che.api.workspace.shared.dto.EnvironmentDto; import org.eclipse.che.api.workspace.shared.dto.MachineConfigDto; import org.eclipse.che.api.workspace.shared.dto.RecipeDto; import org.eclipse.che.api.workspace.shared.dto.ServerConfigDto; +import org.eclipse.che.api.workspace.shared.dto.VolumeDto; import org.eclipse.che.api.workspace.shared.dto.WorkspaceConfigDto; import org.mockito.InjectMocks; import org.mockito.Mock; @@ -242,6 +243,89 @@ public class WorkspaceValidatorTest { wsValidator.validateConfig(config); } + @Test( + expectedExceptions = ValidationException.class, + expectedExceptionsMessageRegExp = "Volume name '.*' in machine '.*' is invalid", + dataProvider = "illegalVolumeNameProvider" + ) + public void shouldFailValidationIfVolumeNameDoesNotMatchCriteria(String volumeName) + throws Exception { + final WorkspaceConfigDto config = createConfig(); + EnvironmentDto env = config.getEnvironments().values().iterator().next(); + MachineConfigDto machine = env.getMachines().values().iterator().next(); + machine.getVolumes().put(volumeName, newDto(VolumeDto.class).withPath("/path")); + + wsValidator.validateConfig(config); + } + + @DataProvider(name = "illegalVolumeNameProvider") + public static Object[][] illegalVolumeNameProvider() { + return new Object[][] { + {"0volume"}, + {"CAPITAL"}, + {"veryveryveryveryveryveryverylongname"}, + {"volume/name"}, + {"volume_name"}, + {"volume-name"} + }; + } + + @Test( + expectedExceptions = ValidationException.class, + expectedExceptionsMessageRegExp = + "Path of volume '.*' in machine '.*' is invalid. It should not be empty" + ) + public void shouldFailValidationIfVolumeValueIsEmpty() throws Exception { + final WorkspaceConfigDto config = createConfig(); + EnvironmentDto env = config.getEnvironments().values().iterator().next(); + MachineConfigDto machine = env.getMachines().values().iterator().next(); + machine.getVolumes().put("volume1", null); + + wsValidator.validateConfig(config); + } + + @Test( + expectedExceptions = ValidationException.class, + expectedExceptionsMessageRegExp = + "Path of volume '.*' in machine '.*' is invalid. It should not be empty" + ) + public void shouldFailValidationIfVolumePathIsEmpty() throws Exception { + final WorkspaceConfigDto config = createConfig(); + EnvironmentDto env = config.getEnvironments().values().iterator().next(); + MachineConfigDto machine = env.getMachines().values().iterator().next(); + machine.getVolumes().put("volume1", newDto(VolumeDto.class).withPath("")); + + wsValidator.validateConfig(config); + } + + @Test( + expectedExceptions = ValidationException.class, + expectedExceptionsMessageRegExp = + "Path of volume '.*' in machine '.*' is invalid. It should not be empty" + ) + public void shouldFailValidationIfVolumePathIsNull() throws Exception { + final WorkspaceConfigDto config = createConfig(); + EnvironmentDto env = config.getEnvironments().values().iterator().next(); + MachineConfigDto machine = env.getMachines().values().iterator().next(); + machine.getVolumes().put("volume1", newDto(VolumeDto.class).withPath(null)); + + wsValidator.validateConfig(config); + } + + @Test( + expectedExceptions = ValidationException.class, + expectedExceptionsMessageRegExp = + "Path '.*' of volume '.*' in machine '.*' is invalid. It should be absolute" + ) + public void shouldFailValidationIfVolumePathIsNotAbsolute() throws Exception { + final WorkspaceConfigDto config = createConfig(); + EnvironmentDto env = config.getEnvironments().values().iterator().next(); + MachineConfigDto machine = env.getMachines().values().iterator().next(); + machine.getVolumes().put("volume1", newDto(VolumeDto.class).withPath("not/absolute/path")); + + wsValidator.validateConfig(config); + } + private static WorkspaceConfigDto createConfig() { final WorkspaceConfigDto workspaceConfigDto = newDto(WorkspaceConfigDto.class).withName("ws-name").withDefaultEnv("dev-env"); diff --git a/wsmaster/che-core-api-workspace/src/test/java/org/eclipse/che/api/workspace/server/jpa/WorkspaceTckModule.java b/wsmaster/che-core-api-workspace/src/test/java/org/eclipse/che/api/workspace/server/jpa/WorkspaceTckModule.java index 01e6f7528c..509162f5ff 100644 --- a/wsmaster/che-core-api-workspace/src/test/java/org/eclipse/che/api/workspace/server/jpa/WorkspaceTckModule.java +++ b/wsmaster/che-core-api-workspace/src/test/java/org/eclipse/che/api/workspace/server/jpa/WorkspaceTckModule.java @@ -20,6 +20,7 @@ import org.eclipse.che.api.workspace.server.model.impl.ProjectConfigImpl; import org.eclipse.che.api.workspace.server.model.impl.RecipeImpl; import org.eclipse.che.api.workspace.server.model.impl.ServerConfigImpl; import org.eclipse.che.api.workspace.server.model.impl.SourceStorageImpl; +import org.eclipse.che.api.workspace.server.model.impl.VolumeImpl; import org.eclipse.che.api.workspace.server.model.impl.WorkspaceConfigImpl; import org.eclipse.che.api.workspace.server.model.impl.WorkspaceImpl; import org.eclipse.che.api.workspace.server.model.impl.stack.StackImpl; @@ -61,7 +62,8 @@ public class WorkspaceTckModule extends TckModule { ServerConfigImpl.class, StackImpl.class, CommandImpl.class, - RecipeImpl.class) + RecipeImpl.class, + VolumeImpl.class) .addEntityClass( "org.eclipse.che.api.workspace.server.model.impl.ProjectConfigImpl$Attribute") .setExceptionHandler(H2ExceptionHandler.class) diff --git a/wsmaster/che-core-api-workspace/src/test/java/org/eclipse/che/api/workspace/server/spi/tck/WorkspaceDaoTest.java b/wsmaster/che-core-api-workspace/src/test/java/org/eclipse/che/api/workspace/server/spi/tck/WorkspaceDaoTest.java index 2dc709f182..6763ca310c 100644 --- a/wsmaster/che-core-api-workspace/src/test/java/org/eclipse/che/api/workspace/server/spi/tck/WorkspaceDaoTest.java +++ b/wsmaster/che-core-api-workspace/src/test/java/org/eclipse/che/api/workspace/server/spi/tck/WorkspaceDaoTest.java @@ -46,6 +46,7 @@ import org.eclipse.che.api.workspace.server.model.impl.ProjectConfigImpl; import org.eclipse.che.api.workspace.server.model.impl.RecipeImpl; import org.eclipse.che.api.workspace.server.model.impl.ServerConfigImpl; import org.eclipse.che.api.workspace.server.model.impl.SourceStorageImpl; +import org.eclipse.che.api.workspace.server.model.impl.VolumeImpl; import org.eclipse.che.api.workspace.server.model.impl.WorkspaceConfigImpl; import org.eclipse.che.api.workspace.server.model.impl.WorkspaceImpl; import org.eclipse.che.api.workspace.server.spi.WorkspaceDao; @@ -554,6 +555,12 @@ public class WorkspaceDaoTest { exMachine1.setInstallers(ImmutableList.of("agent5", "agent4")); exMachine1.setAttributes(singletonMap("att1", "val")); exMachine1.setEnv(ImmutableMap.of("CHE_ENV1", "value", "CHE_ENV2", "value")); + exMachine1.setVolumes( + ImmutableMap.of( + "vol1", + new VolumeImpl().withPath("/path/1"), + "vol2", + new VolumeImpl().withPath("/path/2"))); final MachineConfigImpl exMachine2 = new MachineConfigImpl(); final ServerConfigImpl serverConf3 = new ServerConfigImpl("2333", "https", "path3"); @@ -562,6 +569,7 @@ public class WorkspaceDaoTest { exMachine2.setInstallers(ImmutableList.of("agent2", "agent1")); exMachine2.setAttributes(singletonMap("att1", "val")); exMachine2.setEnv(singletonMap("CHE_ENV2", "value")); + exMachine2.setVolumes(ImmutableMap.of("vol2", new VolumeImpl().withPath("/path/2"))); final MachineConfigImpl exMachine3 = new MachineConfigImpl(); final ServerConfigImpl serverConf5 = new ServerConfigImpl("2333", "https", "path5"); @@ -569,6 +577,7 @@ public class WorkspaceDaoTest { exMachine3.setInstallers(ImmutableList.of("agent6", "agent2")); exMachine3.setAttributes(singletonMap("att1", "val")); exMachine3.setEnv(singletonMap("CHE_ENV3", "value")); + exMachine3.setVolumes(ImmutableMap.of("vol3", new VolumeImpl().withPath("/path/3"))); // Environments final RecipeImpl recipe1 = new RecipeImpl(); diff --git a/wsmaster/che-core-sql-schema/src/main/resources/che-schema/6.0.0/7__add_machine_volumes.sql b/wsmaster/che-core-sql-schema/src/main/resources/che-schema/6.0.0/7__add_machine_volumes.sql new file mode 100644 index 0000000000..7d3ddf39f8 --- /dev/null +++ b/wsmaster/che-core-sql-schema/src/main/resources/che-schema/6.0.0/7__add_machine_volumes.sql @@ -0,0 +1,25 @@ +-- +-- 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 +-- + +-- Machine volumes configuration ----------------------------------------------- +CREATE TABLE machine_volume ( + id BIGINT NOT NULL, + name VARCHAR(255), + path VARCHAR(255) NOT NULL, + machine_id BIGINT, + + PRIMARY KEY (id) +); +--constraints +ALTER TABLE machine_volume ADD CONSTRAINT fk_machine_volume_id FOREIGN KEY (machine_id) REFERENCES externalmachine (id); +-------------------------------------------------------------------------------- +--indexes +CREATE INDEX index_machine_volume_machine_id ON machine_volume (machine_id); diff --git a/wsmaster/integration-tests/cascade-removal/src/test/java/org/eclipse/che/core/db/jpa/CascadeRemovalTest.java b/wsmaster/integration-tests/cascade-removal/src/test/java/org/eclipse/che/core/db/jpa/CascadeRemovalTest.java index 53ebcad7a2..8d46c01ed0 100644 --- a/wsmaster/integration-tests/cascade-removal/src/test/java/org/eclipse/che/core/db/jpa/CascadeRemovalTest.java +++ b/wsmaster/integration-tests/cascade-removal/src/test/java/org/eclipse/che/core/db/jpa/CascadeRemovalTest.java @@ -71,6 +71,7 @@ import org.eclipse.che.api.workspace.server.model.impl.ProjectConfigImpl; import org.eclipse.che.api.workspace.server.model.impl.RecipeImpl; import org.eclipse.che.api.workspace.server.model.impl.ServerConfigImpl; import org.eclipse.che.api.workspace.server.model.impl.SourceStorageImpl; +import org.eclipse.che.api.workspace.server.model.impl.VolumeImpl; import org.eclipse.che.api.workspace.server.model.impl.WorkspaceConfigImpl; import org.eclipse.che.api.workspace.server.model.impl.WorkspaceImpl; import org.eclipse.che.api.workspace.server.model.impl.stack.StackImpl; @@ -160,7 +161,8 @@ public class CascadeRemovalTest { StackImpl.class, CommandImpl.class, RecipeImpl.class, - SshPairImpl.class) + SshPairImpl.class, + VolumeImpl.class) .addEntityClass( "org.eclipse.che.api.workspace.server.model.impl.ProjectConfigImpl$Attribute") .setExceptionHandler(H2ExceptionHandler.class) diff --git a/wsmaster/integration-tests/postgresql-tck/src/test/java/PostgreSqlTckModule.java b/wsmaster/integration-tests/postgresql-tck/src/test/java/PostgreSqlTckModule.java index 94dba7778e..6f9caac38a 100644 --- a/wsmaster/integration-tests/postgresql-tck/src/test/java/PostgreSqlTckModule.java +++ b/wsmaster/integration-tests/postgresql-tck/src/test/java/PostgreSqlTckModule.java @@ -50,6 +50,7 @@ import org.eclipse.che.api.workspace.server.model.impl.ProjectConfigImpl; import org.eclipse.che.api.workspace.server.model.impl.RecipeImpl; import org.eclipse.che.api.workspace.server.model.impl.ServerConfigImpl; import org.eclipse.che.api.workspace.server.model.impl.SourceStorageImpl; +import org.eclipse.che.api.workspace.server.model.impl.VolumeImpl; import org.eclipse.che.api.workspace.server.model.impl.WorkspaceConfigImpl; import org.eclipse.che.api.workspace.server.model.impl.WorkspaceImpl; import org.eclipse.che.api.workspace.server.model.impl.stack.StackImpl; @@ -116,7 +117,8 @@ public class PostgreSqlTckModule extends TckModule { CommandImpl.class, SshPairImpl.class, InstallerImpl.class, - InstallerServerConfigImpl.class) + InstallerServerConfigImpl.class, + VolumeImpl.class) .addEntityClass( "org.eclipse.che.api.workspace.server.model.impl.ProjectConfigImpl$Attribute") .build());