From 4413e2bd8df3aa6ff640b88863b1d0ab0cf16caf Mon Sep 17 00:00:00 2001 From: Michal Vala Date: Thu, 22 Aug 2019 13:04:55 +0200 Subject: [PATCH] Add `generateName` to devfile metadata (#14157) Signed-off-by: Michal Vala --- .../model/workspace/devfile/Metadata.java | 3 ++ .../che/api/workspace/shared/Constants.java | 3 ++ .../shared/dto/devfile/MetadataDto.java | 2 + .../workspace/server/WorkspaceManager.java | 26 ++++++++++++ .../model/impl/devfile/MetadataImpl.java | 31 ++++++++++++-- .../src/main/resources/schema/devfile.json | 18 ++++++++- .../server/WorkspaceManagerTest.java | 40 +++++++++++++++++++ .../validator/DevfileSchemaValidatorTest.java | 10 +++-- .../src/test/resources/devfile/devfile.yaml | 1 + .../devfile/devfile_just_generatename.yaml | 16 ++++++++ ...evfile_missing_name_and_generatename.yaml} | 0 .../devfile_name_and_generatename.yaml | 17 ++++++++ 12 files changed, 159 insertions(+), 8 deletions(-) create mode 100644 wsmaster/che-core-api-workspace/src/test/resources/devfile/schema_test/devfile/devfile_just_generatename.yaml rename wsmaster/che-core-api-workspace/src/test/resources/devfile/schema_test/devfile/{devfile_missing_name.yaml => devfile_missing_name_and_generatename.yaml} (100%) create mode 100644 wsmaster/che-core-api-workspace/src/test/resources/devfile/schema_test/devfile/devfile_name_and_generatename.yaml diff --git a/core/che-core-api-model/src/main/java/org/eclipse/che/api/core/model/workspace/devfile/Metadata.java b/core/che-core-api-model/src/main/java/org/eclipse/che/api/core/model/workspace/devfile/Metadata.java index 46b3a353dff..6fc0fc7d60a 100644 --- a/core/che-core-api-model/src/main/java/org/eclipse/che/api/core/model/workspace/devfile/Metadata.java +++ b/core/che-core-api-model/src/main/java/org/eclipse/che/api/core/model/workspace/devfile/Metadata.java @@ -16,4 +16,7 @@ public interface Metadata { /** @return the name of the devfile */ String getName(); + + /** 'generateName' is used as a base string for generated name, when 'name' is not defined. */ + String getGenerateName(); } diff --git a/wsmaster/che-core-api-workspace-shared/src/main/java/org/eclipse/che/api/workspace/shared/Constants.java b/wsmaster/che-core-api-workspace-shared/src/main/java/org/eclipse/che/api/workspace/shared/Constants.java index 4c8122b0ba2..df3f3c10e74 100644 --- a/wsmaster/che-core-api-workspace-shared/src/main/java/org/eclipse/che/api/workspace/shared/Constants.java +++ b/wsmaster/che-core-api-workspace-shared/src/main/java/org/eclipse/che/api/workspace/shared/Constants.java @@ -188,5 +188,8 @@ public final class Constants { /** Attribute of {@link Server} that specifies exposure of which port created the server */ public static final String SERVER_PORT_ATTRIBUTE = "port"; + /** When generating workspace name from generateName, append this many characters. */ + public static final int WORKSPACE_GENERATE_NAME_CHARS_APPEND = 5; + private Constants() {} } diff --git a/wsmaster/che-core-api-workspace-shared/src/main/java/org/eclipse/che/api/workspace/shared/dto/devfile/MetadataDto.java b/wsmaster/che-core-api-workspace-shared/src/main/java/org/eclipse/che/api/workspace/shared/dto/devfile/MetadataDto.java index c3fa010fafa..bd9661a1e0d 100644 --- a/wsmaster/che-core-api-workspace-shared/src/main/java/org/eclipse/che/api/workspace/shared/dto/devfile/MetadataDto.java +++ b/wsmaster/che-core-api-workspace-shared/src/main/java/org/eclipse/che/api/workspace/shared/dto/devfile/MetadataDto.java @@ -23,4 +23,6 @@ public interface MetadataDto extends Metadata { void setName(String name); MetadataDto withName(String name); + + MetadataDto withGenerateName(String generateName); } diff --git a/wsmaster/che-core-api-workspace/src/main/java/org/eclipse/che/api/workspace/server/WorkspaceManager.java b/wsmaster/che-core-api-workspace/src/main/java/org/eclipse/che/api/workspace/server/WorkspaceManager.java index 13cb3666b9f..10603f0d744 100644 --- a/wsmaster/che-core-api-workspace/src/main/java/org/eclipse/che/api/workspace/server/WorkspaceManager.java +++ b/wsmaster/che-core-api-workspace/src/main/java/org/eclipse/che/api/workspace/server/WorkspaceManager.java @@ -23,6 +23,7 @@ import static org.eclipse.che.api.workspace.shared.Constants.STOPPED_ABNORMALLY_ATTRIBUTE_NAME; import static org.eclipse.che.api.workspace.shared.Constants.STOPPED_ATTRIBUTE_NAME; import static org.eclipse.che.api.workspace.shared.Constants.UPDATED_ATTRIBUTE_NAME; +import static org.eclipse.che.api.workspace.shared.Constants.WORKSPACE_GENERATE_NAME_CHARS_APPEND; import com.google.inject.Inject; import java.util.Collections; @@ -48,11 +49,14 @@ 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.devfile.DevfileImpl; +import org.eclipse.che.api.workspace.server.model.impl.devfile.MetadataImpl; import org.eclipse.che.api.workspace.server.spi.WorkspaceDao; +import org.eclipse.che.api.workspace.shared.Constants; import org.eclipse.che.api.workspace.shared.event.WorkspaceCreatedEvent; import org.eclipse.che.commons.annotation.Nullable; import org.eclipse.che.commons.annotation.Traced; import org.eclipse.che.commons.env.EnvironmentContext; +import org.eclipse.che.commons.lang.NameGenerator; import org.eclipse.che.commons.subject.Subject; import org.eclipse.che.commons.tracing.TracingTags; import org.slf4j.Logger; @@ -159,6 +163,8 @@ public WorkspaceImpl createWorkspace( requireNonNull(namespace, "Required non-null namespace"); validator.validateAttributes(attributes); + devfile = generateNameIfNeeded(devfile); + try { devfileIntegrityValidator.validateContentReferences(devfile, contentProvider); } catch (DevfileFormatException e) { @@ -569,6 +575,26 @@ private WorkspaceImpl doCreateWorkspace( return workspace; } + /** + * If 'generateName' is defined and 'name' is not, we generate name using 'generateName' as a + * prefix following {@link Constants#WORKSPACE_GENERATE_NAME_CHARS_APPEND} random characters and + * set it to 'name'. + */ + private Devfile generateNameIfNeeded(Devfile origDevfile) { + if (origDevfile.getMetadata() != null) { + MetadataImpl metadata = new MetadataImpl(origDevfile.getMetadata()); + if (metadata.getName() == null && metadata.getGenerateName() != null) { + metadata.setName( + NameGenerator.generate( + metadata.getGenerateName(), WORKSPACE_GENERATE_NAME_CHARS_APPEND)); + DevfileImpl devfileWithGeneratedName = new DevfileImpl(origDevfile); + devfileWithGeneratedName.setMetadata(metadata); + return devfileWithGeneratedName; + } + } + return origDevfile; + } + private WorkspaceImpl getByKey(String key) throws NotFoundException, ServerException { int lastColonIndex = key.indexOf(":"); diff --git a/wsmaster/che-core-api-workspace/src/main/java/org/eclipse/che/api/workspace/server/model/impl/devfile/MetadataImpl.java b/wsmaster/che-core-api-workspace/src/main/java/org/eclipse/che/api/workspace/server/model/impl/devfile/MetadataImpl.java index 11aefb14b2b..78b035abab3 100644 --- a/wsmaster/che-core-api-workspace/src/main/java/org/eclipse/che/api/workspace/server/model/impl/devfile/MetadataImpl.java +++ b/wsmaster/che-core-api-workspace/src/main/java/org/eclipse/che/api/workspace/server/model/impl/devfile/MetadataImpl.java @@ -14,6 +14,7 @@ import java.util.Objects; import javax.persistence.Column; import javax.persistence.Embeddable; +import javax.persistence.Transient; import org.eclipse.che.api.core.model.workspace.devfile.Metadata; @Embeddable @@ -22,6 +23,12 @@ public class MetadataImpl implements Metadata { @Column(name = "meta_name") private String name; + /** + * generateName is used just at workspace create time, when name is generated from it and stored + * into {@link MetadataImpl#name}, thus it's not needed to persist + */ + @Transient private String generateName; + public MetadataImpl() {} public MetadataImpl(String name) { @@ -30,6 +37,7 @@ public MetadataImpl(String name) { public MetadataImpl(Metadata metadata) { this.name = metadata.getName(); + this.generateName = metadata.getGenerateName(); } @Override @@ -41,6 +49,15 @@ public void setName(String name) { this.name = name; } + @Override + public String getGenerateName() { + return generateName; + } + + public void setGenerateName(String generateName) { + this.generateName = generateName; + } + @Override public boolean equals(Object o) { if (this == o) { @@ -50,16 +67,24 @@ public boolean equals(Object o) { return false; } MetadataImpl metadata = (MetadataImpl) o; - return name.equals(metadata.name); + return Objects.equals(name, metadata.name) + && Objects.equals(generateName, metadata.generateName); } @Override public int hashCode() { - return Objects.hash(name); + return Objects.hash(name, generateName); } @Override public String toString() { - return "MetadataImpl{'name='" + name + '\'' + '}'; + return "MetadataImpl{" + + "name='" + + name + + '\'' + + ", generateName='" + + generateName + + '\'' + + '}'; } } diff --git a/wsmaster/che-core-api-workspace/src/main/resources/schema/devfile.json b/wsmaster/che-core-api-workspace/src/main/resources/schema/devfile.json index 965fb9779c4..6cda1d3a12d 100644 --- a/wsmaster/che-core-api-workspace/src/main/resources/schema/devfile.json +++ b/wsmaster/che-core-api-workspace/src/main/resources/schema/devfile.json @@ -50,10 +50,26 @@ "examples": [ "petclinic-dev-environment" ] + }, + "generateName": { + "type": "string", + "minLength": 1, + "title": "Devfile Generate Name", + "description": "Workspaces created from devfile, will use it as base and append random suffix. It's used when name is not defined.", + "examples": [ + "petclinic-" + ] } }, "additionalProperties": false, - "required": ["name"] + "anyOf": [ + { + "required": ["name"] + }, + { + "required": ["generateName"] + } + ] }, "projects": { "type": "array", 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 a4f65213dc8..e46956041b7 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 @@ -20,11 +20,13 @@ 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.devfile.Constants.CURRENT_API_VERSION; import static org.eclipse.che.api.workspace.shared.Constants.CREATED_ATTRIBUTE_NAME; import static org.eclipse.che.api.workspace.shared.Constants.ERROR_MESSAGE_ATTRIBUTE_NAME; import static org.eclipse.che.api.workspace.shared.Constants.STOPPED_ABNORMALLY_ATTRIBUTE_NAME; import static org.eclipse.che.api.workspace.shared.Constants.STOPPED_ATTRIBUTE_NAME; import static org.eclipse.che.api.workspace.shared.Constants.UPDATED_ATTRIBUTE_NAME; +import static org.eclipse.che.api.workspace.shared.Constants.WORKSPACE_GENERATE_NAME_CHARS_APPEND; import static org.eclipse.che.dto.server.DtoFactory.newDto; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyInt; @@ -177,6 +179,44 @@ public void createsWorkspace() throws Exception { verify(workspaceDao).create(workspace); } + @Test + public void createsWorkspaceFromDevfile() + throws ValidationException, ConflictException, NotFoundException, ServerException { + final DevfileImpl devfile = new DevfileImpl(); + devfile.setApiVersion(CURRENT_API_VERSION); + devfile.setName("ws"); + Workspace workspace = workspaceManager.createWorkspace(devfile, NAMESPACE_1, null, null); + assertEquals(workspace.getDevfile(), devfile); + } + + @Test + public void createsWorkspaceFromDevfileWithGenerateName() + throws ValidationException, ConflictException, NotFoundException, ServerException { + final String testDevfileGenerateName = "ws-"; + final DevfileImpl devfile = new DevfileImpl(); + devfile.setApiVersion(CURRENT_API_VERSION); + devfile.getMetadata().setGenerateName(testDevfileGenerateName); + Workspace workspace = workspaceManager.createWorkspace(devfile, NAMESPACE_1, null, null); + + assertTrue(workspace.getDevfile().getName().startsWith(testDevfileGenerateName)); + assertEquals( + workspace.getDevfile().getName().length(), + testDevfileGenerateName.length() + WORKSPACE_GENERATE_NAME_CHARS_APPEND); + } + + @Test + public void nameIsUsedWhenNameAndGenerateNameSet() + throws ValidationException, ConflictException, NotFoundException, ServerException { + final String devfileName = "workspacename"; + final DevfileImpl devfile = new DevfileImpl(); + devfile.setApiVersion(CURRENT_API_VERSION); + devfile.getMetadata().setName(devfileName); + devfile.getMetadata().setGenerateName("this_will_not_be_set_as_a_name"); + Workspace workspace = workspaceManager.createWorkspace(devfile, NAMESPACE_1, null, null); + + assertEquals(workspace.getDevfile().getName(), devfileName); + } + @Test public void getsWorkspaceByIdWithoutRuntime() throws Exception { WorkspaceImpl workspace = createAndMockWorkspace(); diff --git a/wsmaster/che-core-api-workspace/src/test/java/org/eclipse/che/api/workspace/server/devfile/validator/DevfileSchemaValidatorTest.java b/wsmaster/che-core-api-workspace/src/test/java/org/eclipse/che/api/workspace/server/devfile/validator/DevfileSchemaValidatorTest.java index f825e4e6d21..8ff9864a135 100644 --- a/wsmaster/che-core-api-workspace/src/test/java/org/eclipse/che/api/workspace/server/devfile/validator/DevfileSchemaValidatorTest.java +++ b/wsmaster/che-core-api-workspace/src/test/java/org/eclipse/che/api/workspace/server/devfile/validator/DevfileSchemaValidatorTest.java @@ -64,7 +64,9 @@ public Object[][] validDevfiles() { {"dockerimage_component/devfile_dockerimage_component_without_entry_point.yaml"}, {"editor_plugin_component/devfile_editor_component_with_custom_registry.yaml"}, {"editor_plugin_component/devfile_editor_plugins_components_with_memory_limit.yaml"}, - {"editor_plugin_component/devfile_plugin_component_with_reference.yaml"} + {"editor_plugin_component/devfile_plugin_component_with_reference.yaml"}, + {"devfile/devfile_just_generatename.yaml"}, + {"devfile/devfile_name_and_generatename.yaml"} }; } @@ -89,7 +91,7 @@ public Object[][] invalidDevfiles() { // Devfile model testing { "devfile/devfile_empty_metadata.yaml", - "(/metadata):The object must have a property whose name is \"name\"." + "At least one of the following sets of problems must be resolved.: [(/metadata):The object must have a property whose name is \"name\".(/metadata):The object must have a property whose name is \"generateName\".]" }, { "devfile/devfile_null_metadata.yaml", @@ -100,8 +102,8 @@ public Object[][] invalidDevfiles() { "The object must have a property whose name is \"metadata\"." }, { - "devfile/devfile_missing_name.yaml", - "(/metadata/something):The object must not have a property whose name is \"something\".(/metadata):The object must have a property whose name is \"name\"." + "devfile/devfile_missing_name_and_generatename.yaml", + "(/metadata/something):The object must not have a property whose name is \"something\".At least one of the following sets of problems must be resolved.: [(/metadata):The object must have a property whose name is \"name\".(/metadata):The object must have a property whose name is \"generateName\".]" }, { "devfile/devfile_missing_api_version.yaml", diff --git a/wsmaster/che-core-api-workspace/src/test/resources/devfile/devfile.yaml b/wsmaster/che-core-api-workspace/src/test/resources/devfile/devfile.yaml index 2ce3868e2ff..7f90d41feba 100644 --- a/wsmaster/che-core-api-workspace/src/test/resources/devfile/devfile.yaml +++ b/wsmaster/che-core-api-workspace/src/test/resources/devfile/devfile.yaml @@ -14,6 +14,7 @@ apiVersion: 1.0.0 metadata: name: petclinic-dev-environment + generateName: petclinic- projects: - name: petclinic source: diff --git a/wsmaster/che-core-api-workspace/src/test/resources/devfile/schema_test/devfile/devfile_just_generatename.yaml b/wsmaster/che-core-api-workspace/src/test/resources/devfile/schema_test/devfile/devfile_just_generatename.yaml new file mode 100644 index 00000000000..ecf01ad9406 --- /dev/null +++ b/wsmaster/che-core-api-workspace/src/test/resources/devfile/schema_test/devfile/devfile_just_generatename.yaml @@ -0,0 +1,16 @@ +# +# Copyright (c) 2012-2018 Red Hat, Inc. +# This program and the accompanying materials are made +# available under the terms of the Eclipse Public License 2.0 +# which is available at https://www.eclipse.org/legal/epl-2.0/ +# +# SPDX-License-Identifier: EPL-2.0 +# +# Contributors: +# Red Hat, Inc. - initial API and implementation +# + +--- +apiVersion: 1.0.0 +metadata: + generateName: test- diff --git a/wsmaster/che-core-api-workspace/src/test/resources/devfile/schema_test/devfile/devfile_missing_name.yaml b/wsmaster/che-core-api-workspace/src/test/resources/devfile/schema_test/devfile/devfile_missing_name_and_generatename.yaml similarity index 100% rename from wsmaster/che-core-api-workspace/src/test/resources/devfile/schema_test/devfile/devfile_missing_name.yaml rename to wsmaster/che-core-api-workspace/src/test/resources/devfile/schema_test/devfile/devfile_missing_name_and_generatename.yaml diff --git a/wsmaster/che-core-api-workspace/src/test/resources/devfile/schema_test/devfile/devfile_name_and_generatename.yaml b/wsmaster/che-core-api-workspace/src/test/resources/devfile/schema_test/devfile/devfile_name_and_generatename.yaml new file mode 100644 index 00000000000..e0334ed3817 --- /dev/null +++ b/wsmaster/che-core-api-workspace/src/test/resources/devfile/schema_test/devfile/devfile_name_and_generatename.yaml @@ -0,0 +1,17 @@ +# +# Copyright (c) 2012-2018 Red Hat, Inc. +# This program and the accompanying materials are made +# available under the terms of the Eclipse Public License 2.0 +# which is available at https://www.eclipse.org/legal/epl-2.0/ +# +# SPDX-License-Identifier: EPL-2.0 +# +# Contributors: +# Red Hat, Inc. - initial API and implementation +# + +--- +apiVersion: 1.0.0 +metadata: + name: testName + generateName: testGenerateName-