fix: Do not accept devfiles v2.x if DevWorkspaces feature is disabled (#90)

pull/94/head
Max Shaposhnik 2021-08-27 10:34:18 +03:00 committed by GitHub
parent 1c9391cdf5
commit 74ebf45ec0
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 62 additions and 23 deletions

View File

@ -12,6 +12,7 @@
package org.eclipse.che.api.factory.server.urlfactory;
import static com.google.common.base.Strings.isNullOrEmpty;
import static java.lang.String.format;
import static org.eclipse.che.api.factory.server.DevfileToApiExceptionMapper.toApiException;
import static org.eclipse.che.api.factory.shared.Constants.CURRENT_VERSION;
import static org.eclipse.che.api.workspace.server.devfile.Constants.CURRENT_API_VERSION;
@ -62,6 +63,7 @@ public class URLFactoryBuilder {
private final String defaultCheEditor;
private final String defaultChePlugins;
private final boolean devWorskspacesEnabled;
private final DevfileParser devfileParser;
private final DevfileVersionDetector devfileVersionDetector;
@ -69,10 +71,12 @@ public class URLFactoryBuilder {
public URLFactoryBuilder(
@Named("che.factory.default_editor") String defaultCheEditor,
@Nullable @Named("che.factory.default_plugins") String defaultChePlugins,
@Named("che.devworkspaces.enabled") boolean devWorskspacesEnabled,
DevfileParser devfileParser,
DevfileVersionDetector devfileVersionDetector) {
this.defaultCheEditor = defaultCheEditor;
this.defaultChePlugins = defaultChePlugins;
this.devWorskspacesEnabled = devWorskspacesEnabled;
this.devfileParser = devfileParser;
this.devfileVersionDetector = devfileVersionDetector;
}
@ -156,11 +160,16 @@ public class URLFactoryBuilder {
.withDevfile(DtoConverter.asDto(devfile))
.withSource(location.filename().isPresent() ? location.filename().get() : null);
} else {
} else if (devWorskspacesEnabled) {
return newDto(FactoryDevfileV2Dto.class)
.withV(CURRENT_VERSION)
.withDevfile(devfileParser.convertYamlToMap(devfileJson))
.withSource(location.filename().isPresent() ? location.filename().get() : null);
} else {
throw new DevfileException(
format(
"Devfile of version %s cannot be used in current deployment, because of DevWorkspaces feature is disabled. Only '1.0.0' version devfiles are supported for such installations.",
devfileVersionDetector.devfileVersion(devfileJson)));
}
}

View File

@ -81,7 +81,8 @@ public class DefaultFactoryParameterResolverTest {
DevfileParser devfileParser = new DevfileParser(validator, integrityValidator);
URLFactoryBuilder factoryBuilder =
new URLFactoryBuilder("editor", "plugin", devfileParser, new DevfileVersionDetector());
new URLFactoryBuilder(
"editor", "plugin", false, devfileParser, new DevfileVersionDetector());
DefaultFactoryParameterResolver res =
new DefaultFactoryParameterResolver(factoryBuilder, urlFetcher);

View File

@ -38,6 +38,7 @@ import java.util.List;
import java.util.Map;
import java.util.Optional;
import org.eclipse.che.api.core.ApiException;
import org.eclipse.che.api.core.BadRequestException;
import org.eclipse.che.api.core.ServerException;
import org.eclipse.che.api.core.UnauthorizedException;
import org.eclipse.che.api.core.rest.shared.dto.ExtendedError;
@ -91,7 +92,8 @@ public class URLFactoryBuilderTest {
@BeforeMethod
public void setUp() {
this.urlFactoryBuilder =
new URLFactoryBuilder(defaultEditor, defaultPlugin, devfileParser, devfileVersionDetector);
new URLFactoryBuilder(
defaultEditor, defaultPlugin, true, devfileParser, devfileVersionDetector);
}
@Test
@ -222,6 +224,33 @@ public class URLFactoryBuilderTest {
assertEquals(((FactoryDevfileV2Dto) factory).getDevfile(), devfileAsMap);
}
@Test(
expectedExceptions = BadRequestException.class,
expectedExceptionsMessageRegExp =
"Error occurred during creation a workspace from devfile located at `http://foo-location/`. Cause: Devfile of version 2.0.1 cannot be used in current deployment, because of DevWorkspaces feature is disabled. Only '1.0.0' version devfiles are supported for such installations.")
public void testShouldThrowExceptionOnDevfileV2WithDevworkspacesDisabled()
throws ApiException, DevfileException {
String myLocation = "http://foo-location/";
Map<String, Object> devfileAsMap = Map.of("hello", "there", "how", "are", "you", "?");
JsonNode devfile = new ObjectNode(JsonNodeFactory.instance);
when(devfileParser.parseYamlRaw(anyString())).thenReturn(devfile);
when(devfileParser.convertYamlToMap(devfile)).thenReturn(devfileAsMap);
when(devfileVersionDetector.devfileMajorVersion(devfile)).thenReturn(2);
when(devfileVersionDetector.devfileVersion(devfile)).thenReturn("2.0.1");
URLFactoryBuilder localUrlFactoryBuilder =
new URLFactoryBuilder(
defaultEditor, defaultPlugin, false, devfileParser, devfileVersionDetector);
localUrlFactoryBuilder
.createFactoryFromDevfile(
new DefaultFactoryUrl().withDevfileFileLocation(myLocation),
s -> myLocation + ".list",
emptyMap())
.get();
}
@DataProvider
public Object[][] devfiles() {
final String NAME = "name";

View File

@ -89,7 +89,7 @@ public class DevfileParser {
*/
public DevfileImpl parseYaml(String devfileContent) throws DevfileFormatException {
try {
return parse(parseYamlRaw(devfileContent, false), yamlMapper, emptyMap());
return parse(parseYamlRaw(devfileContent), yamlMapper, emptyMap());
} catch (OverrideParameterException e) {
// should never happen as we send empty overrides map
throw new RuntimeException(e.getMessage());
@ -97,28 +97,18 @@ public class DevfileParser {
}
/**
* Tries to parse given `yaml` into {@link JsonNode} and validates it with devfile schema.
* Tries to parse given `yaml` into {@link JsonNode}.
*
* @param yaml to parse
* @return parsed yaml
* @throws DevfileFormatException if given yaml is empty or is not valid devfile
* @throws DevfileFormatException if given yaml is empty or invalid
*/
public JsonNode parseYamlRaw(String yaml) throws DevfileFormatException {
return parseYamlRaw(yaml, true);
}
private JsonNode parseYamlRaw(String yaml, boolean validate) throws DevfileFormatException {
try {
JsonNode devfileJson =
Optional.ofNullable(yamlMapper.readTree(yaml))
.orElseThrow(
() ->
new DevfileFormatException(
"Unable to parse Devfile - provided source is empty"));
if (validate) {
schemaValidator.validate(devfileJson);
}
return devfileJson;
return Optional.ofNullable(yamlMapper.readTree(yaml))
.orElseThrow(
() ->
new DevfileFormatException("Unable to parse Devfile - provided source is empty"));
} catch (JsonProcessingException jpe) {
throw new DevfileFormatException("Can't parse devfile yaml.", jpe);
}
@ -130,7 +120,8 @@ public class DevfileParser {
* @param devfileJson json with devfile content
* @return devfile in simple Map structure
*/
public Map<String, Object> convertYamlToMap(JsonNode devfileJson) {
public Map<String, Object> convertYamlToMap(JsonNode devfileJson) throws DevfileFormatException {
schemaValidator.validate(devfileJson);
return yamlMapper.convertValue(devfileJson, new TypeReference<>() {});
}

View File

@ -24,6 +24,7 @@ import static org.testng.Assert.assertEquals;
import static org.testng.Assert.assertNotNull;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import java.io.IOException;
@ -83,9 +84,7 @@ public class DevfileParserTest {
@Test
public void testParseRaw() throws DevfileFormatException {
JsonNode parsed = devfileParser.parseYamlRaw(DEVFILE_YAML_CONTENT);
assertEquals(parsed, devfileJsonNode);
verify(schemaValidator).validate(eq(devfileJsonNode));
}
@Test(expectedExceptions = DevfileFormatException.class)
@ -200,4 +199,14 @@ public class DevfileParserTest {
// when
devfileParser.parseJson(DEVFILE_YAML_CONTENT);
}
@Test
public void shouldConvertYamlToMapAndValidate() throws Exception {
// when
devfileParser.convertYamlToMap(devfileJsonNode);
// then
verify(schemaValidator).validate(eq(devfileJsonNode));
verify(yamlMapper).convertValue(eq(devfileJsonNode), any(TypeReference.class));
}
}