Deprecate workspaces with Devfile stored as workspace config (#13588)

7.20.x
Mykhailo Kuznietsov 2019-07-03 15:34:23 +03:00 committed by GitHub
parent edfda1398c
commit 8d3cc8054a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
21 changed files with 276 additions and 232 deletions

View File

@ -32,10 +32,7 @@ public interface Factory {
/** Returns creator of this factory instance. */
Author getCreator();
/**
* Returns a workspace configuration of this factory instance, it is mandatory for every factory
* instance.
*/
/** Returns a workspace configuration of this factory instance. */
WorkspaceConfig getWorkspace();
/** Returns restrictions of this factory instance. */

View File

@ -90,7 +90,7 @@ public interface Component {
* optional and applicable only for 'dockerimage' component type. `CHE_PROJECTS_ROOT` environment
* variable should contains a path where projects sources are mount.
*/
boolean getMountSources();
Boolean getMountSources();
/**
* Returns the command to run in the dockerimage component instead of the default one provided in

View File

@ -11,6 +11,7 @@
*/
'use strict';
import {CheAPI} from '../../../components/api/che-api.factory';
import {CheWorkspace} from '../../../components/api/workspace/che-workspace.factory';
import {LoadFactoryService, FactoryLoadingStep} from './load-factory.service';
import {CheNotification} from '../../../components/notification/che-notification.factory';
import {RouteHistory} from '../../../components/routing/route-history.service';
@ -25,9 +26,10 @@ const WS_AGENT_STEP: number = 4;
*/
export class LoadFactoryController {
static $inject = ['cheAPI', 'cheJsonRpcApi', '$route', '$timeout', '$mdDialog', 'loadFactoryService', 'lodash', 'cheNotification', '$location', 'routeHistory', '$window', 'loadFactoryService'];
static $inject = ['cheAPI', 'cheWorkspace','cheJsonRpcApi', '$route', '$timeout', '$mdDialog', 'loadFactoryService', 'lodash', 'cheNotification', '$location', 'routeHistory', '$window', 'loadFactoryService'];
private cheAPI: CheAPI;
cheWorkspace: CheWorkspace;
private $timeout: ng.ITimeoutService;
private $mdDialog: ng.material.IDialogService;
private loadFactoryService: LoadFactoryService;
@ -49,6 +51,7 @@ export class LoadFactoryController {
* Default constructor that is using resource
*/
constructor(cheAPI: CheAPI,
cheWorkspace: CheWorkspace,
cheJsonRpcApi: CheJsonRpcApi,
$route: ng.route.IRouteService,
$timeout: ng.ITimeoutService,
@ -60,6 +63,7 @@ export class LoadFactoryController {
routeHistory: RouteHistory,
$window: ng.IWindowService) {
this.cheAPI = cheAPI;
this.cheWorkspace = cheWorkspace;
this.$timeout = $timeout;
this.$mdDialog = $mdDialog;
this.loadFactoryService = loadFactoryService;
@ -121,9 +125,9 @@ export class LoadFactoryController {
}
// check factory contains compatible workspace config:
if (!this.factory.workspace || !this.isSupportedVersion()) {
if (!(Boolean(this.factory.workspace) !== Boolean(this.factory.devfile)) || !this.isSupportedVersion()) {
this.getLoadingSteps()[this.getCurrentProgressStep()].hasError = true;
this.getLoadingSteps()[this.getCurrentProgressStep()].logs = 'Factory has no compatible workspace config.';
this.getLoadingSteps()[this.getCurrentProgressStep()].logs = 'Factory has no compatible workspace config or devfile.';
} else {
this.loadFactoryService.goToNextStep();
this.$timeout(() => {
@ -272,20 +276,35 @@ export class LoadFactoryController {
* Create workspace from factory config.
*/
createWorkspace(): any {
let config = this.factory.workspace;
// set factory attribute:
let attrs = {factoryId: this.factory.id};
config.name = this.getWorkspaceName(config.name);
if (this.factory.workspace) {
let config = this.factory.workspace;
// set factory attribute:
let attrs = {factoryId: this.factory.id};
config.name = this.getWorkspaceName(config.name);
// todo: fix account when ready:
let creationPromise = this.cheAPI.getWorkspace().createWorkspaceFromConfig(null, config, attrs);
creationPromise.then((data: any) => {
this.$timeout(() => {
this.startWorkspace(data);
}, 1000);
}, (error: any) => {
this.handleError(error);
});
// todo: fix account when ready:
let creationPromise = this.cheAPI.getWorkspace().createWorkspaceFromConfig(null, config, attrs);
creationPromise.then((data: any) => {
this.$timeout(() => {
this.startWorkspace(data);
}, 1000);
}, (error: any) => {
this.handleError(error);
});
} else if (this.factory.devfile) {
let devfile = this.factory.devfile;
// set factory attribute:
let attrs = {factoryId: this.factory.id};
let creationPromise = this.cheAPI.getWorkspace().createWorkspaceFromDevfile(null, devfile, attrs);
creationPromise.then((data: any) => {
this.$timeout(() => {
this.startWorkspace(data);
}, 1000);
}, (error: any) => {
this.handleError(error);
});
}
}
/**
@ -340,7 +359,7 @@ export class LoadFactoryController {
* @param workspace
*/
doStartWorkspace(workspace: che.IWorkspace): void {
let startWorkspacePromise = this.cheAPI.getWorkspace().startWorkspace(workspace.id, workspace.config.defaultEnv);
let startWorkspacePromise = this.cheAPI.getWorkspace().startWorkspace(workspace.id);
this.loadFactoryService.goToNextStep();
startWorkspacePromise.then((data: any) => {
@ -668,7 +687,7 @@ export class LoadFactoryController {
* @returns {string} IDE application link
*/
getIDELink() {
return '/ide/' + this.workspace.namespace + '/' + this.workspace.config.name;
return '/ide/' + this.workspace.namespace + '/' + this.cheWorkspace.getWorkspaceDataManager().getName(this.workspace);
}
/**

View File

@ -57,8 +57,12 @@ export class CheAccordion implements ng.IDirective {
continue;
}
let siblingBodyEl = siblingEl.find('.che-accordion-body'),
siblingBodyHeight = siblingBodyEl[0].clientHeight;
let siblingBodyEl = siblingEl.find('.che-accordion-body');
if (siblingBodyEl.length === 0) {
continue;
}
let siblingBodyHeight = siblingBodyEl[0].clientHeight;
siblingBodyEl.css('height', siblingBodyHeight);
panesToClose.push(siblingEl);
}

View File

@ -128,7 +128,7 @@ public class DockerimageComponentToWorkspaceApplier implements ComponentToWorksp
.getVolumes()
.put(v.getName(), new VolumeImpl().withPath(v.getContainerPath())));
if (dockerimageComponent.getMountSources()) {
if (Boolean.TRUE.equals(dockerimageComponent.getMountSources())) {
machineConfig
.getVolumes()
.put(PROJECTS_VOLUME_NAME, new VolumeImpl().withPath(projectFolderPath));

View File

@ -146,7 +146,7 @@ public class KubernetesComponentToWorkspaceApplier implements ComponentToWorkspa
estimateCommandsMachineName(workspaceConfig, k8sComponent, podsData);
if (k8sComponent.getMountSources()) {
if (Boolean.TRUE.equals(k8sComponent.getMountSources())) {
applyProjectsVolumes(podsData, componentObjects);
}

View File

@ -31,6 +31,7 @@ import org.eclipse.che.api.factory.shared.dto.FactoryDto;
import org.eclipse.che.api.factory.shared.dto.IdeDto;
import org.eclipse.che.api.factory.shared.dto.PoliciesDto;
import org.eclipse.che.api.workspace.shared.dto.WorkspaceConfigDto;
import org.eclipse.che.api.workspace.shared.dto.devfile.DevfileDto;
import org.eclipse.che.commons.lang.IoUtil;
import org.eclipse.che.commons.lang.NameGenerator;
import org.eclipse.che.dto.server.DtoFactory;
@ -177,6 +178,21 @@ public class TestFactoryInitializer {
return factoryDto.withV(v);
}
@Override
public DevfileDto getDevfile() {
return factoryDto.getDevfile();
}
@Override
public void setDevfile(DevfileDto workspace) {
factoryDto.setDevfile(workspace);
}
@Override
public FactoryDto withDevfile(DevfileDto devfileDto) {
return factoryDto.withDevfile(devfileDto);
}
@Override
public WorkspaceConfigDto getWorkspace() {
return factoryDto.getWorkspace();

View File

@ -15,6 +15,7 @@ import static org.eclipse.che.api.factory.shared.Constants.CURRENT_VERSION;
import static org.eclipse.che.api.factory.shared.Constants.URL_PARAMETER_NAME;
import static org.eclipse.che.dto.server.DtoFactory.newDto;
import java.util.Collections;
import java.util.Map;
import javax.inject.Inject;
import javax.inject.Singleton;
@ -27,6 +28,7 @@ import org.eclipse.che.api.factory.server.urlfactory.URLFactoryBuilder;
import org.eclipse.che.api.factory.shared.dto.FactoryDto;
import org.eclipse.che.api.workspace.server.devfile.URLFetcher;
import org.eclipse.che.api.workspace.shared.dto.ProjectConfigDto;
import org.eclipse.che.api.workspace.shared.dto.devfile.ProjectDto;
/**
* Provides Factory Parameters resolver for github repositories.
@ -105,21 +107,28 @@ public class GithubFactoryParametersResolver implements FactoryParametersResolve
newDto(FactoryDto.class)
.withV(CURRENT_VERSION)
.withSource("repo")));
// add workspace configuration if not defined
if (factory.getWorkspace() == null) {
factory.setWorkspace(
urlFactoryBuilder.buildDefaultWorkspaceConfig(githubUrl.getRepository()));
}
// apply merging operation from existing and computed settings if needed
return projectConfigDtoMerger.merge(
factory,
() -> {
// Compute project configuration
return newDto(ProjectConfigDto.class)
.withSource(githubSourceStorageBuilder.build(githubUrl))
.withName(githubUrl.getRepository())
.withPath("/".concat(githubUrl.getRepository()));
});
if (factory.getWorkspace() != null) {
return projectConfigDtoMerger.merge(
factory,
() -> {
// Compute project configuration
return newDto(ProjectConfigDto.class)
.withSource(githubSourceStorageBuilder.buildWorkspaceConfigSource(githubUrl))
.withName(githubUrl.getRepository())
.withPath("/".concat(githubUrl.getRepository()));
});
} else if (factory.getDevfile() == null) {
// initialize default devfile and github project
factory.setDevfile(urlFactoryBuilder.buildDefaultDevfile(githubUrl.getRepository()));
factory
.getDevfile()
.setProjects(
Collections.singletonList(
newDto(ProjectDto.class)
.withSource(githubSourceStorageBuilder.buildDevfileSource(githubUrl))
.withName(githubUrl.getRepository())));
}
return factory;
}
}

View File

@ -19,6 +19,7 @@ import java.util.Map;
import javax.inject.Singleton;
import org.eclipse.che.api.workspace.shared.dto.ProjectConfigDto;
import org.eclipse.che.api.workspace.shared.dto.SourceStorageDto;
import org.eclipse.che.api.workspace.shared.dto.devfile.SourceDto;
/**
* Create {@link ProjectConfigDto} object from objects
@ -34,7 +35,7 @@ public class GithubSourceStorageBuilder {
* @param githubUrl an instance of {@link GithubUrl}
* @return newly created source storage DTO object
*/
public SourceStorageDto build(GithubUrl githubUrl) {
public SourceStorageDto buildWorkspaceConfigSource(GithubUrl githubUrl) {
// Create map for source storage dto
Map<String, String> parameters = new HashMap<>(2);
parameters.put("branch", githubUrl.getBranch());
@ -47,4 +48,18 @@ public class GithubSourceStorageBuilder {
.withType("github")
.withParameters(parameters);
}
/**
* Create SourceStorageDto DTO by using data of a github url
*
* @param githubUrl an instance of {@link GithubUrl}
* @return newly created source DTO object
*/
public SourceDto buildDevfileSource(GithubUrl githubUrl) {
// T_O_D_O add keepDir support when Devfile will support it
return newDto(SourceDto.class)
.withLocation(githubUrl.repositoryLocation())
.withType("github")
.withBranch(githubUrl.getBranch());
}
}

View File

@ -14,27 +14,29 @@ package org.eclipse.che.api.factory.server.github;
import static java.util.Collections.singletonMap;
import static org.eclipse.che.api.factory.shared.Constants.CURRENT_VERSION;
import static org.eclipse.che.api.factory.shared.Constants.URL_PARAMETER_NAME;
import static org.eclipse.che.api.workspace.server.devfile.Constants.CURRENT_API_VERSION;
import static org.eclipse.che.dto.server.DtoFactory.newDto;
import static org.mockito.AdditionalAnswers.returnsFirstArg;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import static org.testng.Assert.assertEquals;
import static org.testng.Assert.assertFalse;
import static org.testng.Assert.assertNotNull;
import static org.testng.Assert.assertNull;
import static org.testng.Assert.assertTrue;
import java.util.Map;
import java.util.Optional;
import java.util.function.Supplier;
import org.eclipse.che.api.factory.server.urlfactory.ProjectConfigDtoMerger;
import org.eclipse.che.api.factory.server.urlfactory.RemoteFactoryUrl;
import org.eclipse.che.api.factory.server.urlfactory.URLFactoryBuilder;
import org.eclipse.che.api.factory.shared.dto.FactoryDto;
import org.eclipse.che.api.workspace.server.devfile.FileContentProvider;
import org.eclipse.che.api.workspace.shared.dto.ProjectConfigDto;
import org.eclipse.che.api.workspace.shared.dto.SourceStorageDto;
import org.eclipse.che.api.workspace.shared.dto.WorkspaceConfigDto;
import org.eclipse.che.api.workspace.shared.dto.devfile.DevfileDto;
import org.eclipse.che.api.workspace.shared.dto.devfile.MetadataDto;
import org.mockito.ArgumentCaptor;
import org.mockito.Captor;
import org.mockito.InjectMocks;
@ -65,9 +67,6 @@ public class GithubFactoryParametersResolverTest {
/** Parser which will allow to check validity of URLs and create objects. */
@Mock private URLFactoryBuilder urlFactoryBuilder;
/** Capturing the project config DTO supplier parameter. */
@Captor private ArgumentCaptor<Supplier<ProjectConfigDto>> projectConfigDtoArgumentCaptor;
/**
* Capturing the location parameter when calling {@link
* URLFactoryBuilder#createFactoryFromJson(RemoteFactoryUrl)} or {@link
@ -106,109 +105,60 @@ public class GithubFactoryParametersResolverTest {
assertTrue(accept);
}
/** Check that with a simple valid URL github url it works */
@Test
public void shouldReturnGitHubSimpleRepoFactory() throws Exception {
public void shouldGenerateDevfileForFactoryWithNoDevfileOrJson() throws Exception {
String githubUrl = "https://github.com/eclipse/che";
FactoryDto computedFactory = newDto(FactoryDto.class).withV(CURRENT_VERSION).withSource("repo");
FactoryDto computedFactory = generateDevfileFactory();
when(urlFactoryBuilder.buildDefaultDevfile(any())).thenReturn(computedFactory.getDevfile());
when(urlFactoryBuilder.createFactoryFromJson(any(RemoteFactoryUrl.class)))
.thenReturn(Optional.empty());
when(urlFactoryBuilder.createFactoryFromDevfile(any(RemoteFactoryUrl.class), any()))
.thenReturn(Optional.empty());
when(projectConfigDtoMerger.merge(any(FactoryDto.class), any())).then(returnsFirstArg());
FactoryDto factory =
githubFactoryParametersResolver.createFactory(singletonMap(URL_PARAMETER_NAME, githubUrl));
// check we provide dockerfile and correct env
verify(urlFactoryBuilder).buildDefaultWorkspaceConfig(eq("che"));
verify(urlFactoryBuilder).buildDefaultDevfile(eq("che"));
assertEquals(factory, computedFactory);
}
/** Check that with a simple valid URL github url it works */
@Test
public void shouldReturnGitHubSimpleJsonFactory() throws Exception {
public void shouldReturnFactoryFromRepositoryWithDevfile() throws Exception {
String githubUrl = "https://github.com/eclipse/che";
FactoryDto computedFactory = newDto(FactoryDto.class).withV(CURRENT_VERSION);
when(urlFactoryBuilder.createFactoryFromJson(any(RemoteFactoryUrl.class)))
.thenReturn(Optional.of(computedFactory));
FactoryDto computedFactory = generateDevfileFactory();
githubFactoryParametersResolver.createFactory(singletonMap(URL_PARAMETER_NAME, githubUrl));
// check we called the builder with the following factory json file
verify(urlFactoryBuilder).createFactoryFromJson(factoryUrlArgumentCaptor.capture());
assertEquals(
factoryUrlArgumentCaptor.getValue().factoryFileLocation(),
"https://raw.githubusercontent.com/eclipse/che/master/.factory.json");
assertEquals(factoryUrlArgumentCaptor.getValue().getFactoryFilename(), ".factory.json");
// check we provide dockerfile and correct env
verify(urlFactoryBuilder).buildDefaultWorkspaceConfig(eq("che"));
// check project config built
verify(projectConfigDtoMerger)
.merge(any(FactoryDto.class), projectConfigDtoArgumentCaptor.capture());
ProjectConfigDto projectConfigDto = projectConfigDtoArgumentCaptor.getValue().get();
SourceStorageDto sourceStorageDto = projectConfigDto.getSource();
assertNotNull(sourceStorageDto);
assertEquals(sourceStorageDto.getType(), "github");
assertEquals(sourceStorageDto.getLocation(), githubUrl);
Map<String, String> sourceParameters = sourceStorageDto.getParameters();
assertEquals(sourceParameters.size(), 1);
assertEquals(sourceParameters.get("branch"), "master");
}
/** Check that with a simple valid URL github url it works */
@Test
public void shouldReturnGitHubDevfileFactory() throws Exception {
String githubUrl = "https://github.com/eclipse/che";
FactoryDto computedFactory = newDto(FactoryDto.class).withV(CURRENT_VERSION);
when(urlFactoryBuilder.createFactoryFromDevfile(any(RemoteFactoryUrl.class), any()))
.thenReturn(Optional.of(computedFactory));
githubFactoryParametersResolver.createFactory(singletonMap(URL_PARAMETER_NAME, githubUrl));
FactoryDto factory =
githubFactoryParametersResolver.createFactory(singletonMap(URL_PARAMETER_NAME, githubUrl));
// check we called the builder with the following devfile
assertNotNull(factory.getDevfile());
assertNull(factory.getWorkspace());
// check we called the builder with the following devfile file
verify(urlFactoryBuilder).createFactoryFromDevfile(factoryUrlArgumentCaptor.capture(), any());
verify(urlFactoryBuilder, never()).buildDefaultDevfile(eq("che"));
assertEquals(
factoryUrlArgumentCaptor.getValue().devfileFileLocation(),
"https://raw.githubusercontent.com/eclipse/che/master/devfile.yaml");
assertEquals(factoryUrlArgumentCaptor.getValue().getDevfileFilename(), "devfile.yaml");
// check project config built
verify(projectConfigDtoMerger)
.merge(any(FactoryDto.class), projectConfigDtoArgumentCaptor.capture());
ProjectConfigDto projectConfigDto = projectConfigDtoArgumentCaptor.getValue().get();
SourceStorageDto sourceStorageDto = projectConfigDto.getSource();
assertNotNull(sourceStorageDto);
assertEquals(sourceStorageDto.getType(), "github");
assertEquals(sourceStorageDto.getLocation(), githubUrl);
Map<String, String> sourceParameters = sourceStorageDto.getParameters();
assertEquals(sourceParameters.size(), 1);
assertEquals(sourceParameters.get("branch"), "master");
}
/** Check that we've expected branch when url contains a branch name */
@Test
public void shouldReturnGitHubBranchFactory() throws Exception {
public void shouldReturnFactoryFromRepositoryWithFactoryJson() throws Exception {
String githubUrl = "https://github.com/eclipse/che/tree/4.2.x";
String githubCloneUrl = "https://github.com/eclipse/che";
String githubBranch = "4.2.x";
String githubUrl = "https://github.com/eclipse/che";
FactoryDto computedFactory = generateWsConfigFactory();
FactoryDto computedFactory = newDto(FactoryDto.class).withV(CURRENT_VERSION);
when(urlFactoryBuilder.createFactoryFromJson(any(RemoteFactoryUrl.class)))
.thenReturn(Optional.of(computedFactory));
@ -216,67 +166,28 @@ public class GithubFactoryParametersResolverTest {
// check we called the builder with the following factory json file
verify(urlFactoryBuilder).createFactoryFromJson(factoryUrlArgumentCaptor.capture());
verify(urlFactoryBuilder, never()).buildDefaultDevfile(eq("che"));
assertEquals(
factoryUrlArgumentCaptor.getValue().factoryFileLocation(),
"https://raw.githubusercontent.com/eclipse/che/4.2.x/.factory.json");
"https://raw.githubusercontent.com/eclipse/che/master/.factory.json");
assertEquals(factoryUrlArgumentCaptor.getValue().getFactoryFilename(), ".factory.json");
// check we provide dockerfile and correct env
verify(urlFactoryBuilder).buildDefaultWorkspaceConfig(eq("che"));
// check project config built
verify(projectConfigDtoMerger)
.merge(any(FactoryDto.class), projectConfigDtoArgumentCaptor.capture());
ProjectConfigDto projectConfigDto = projectConfigDtoArgumentCaptor.getValue().get();
SourceStorageDto sourceStorageDto = projectConfigDto.getSource();
assertNotNull(sourceStorageDto);
assertEquals(sourceStorageDto.getType(), "github");
assertEquals(sourceStorageDto.getLocation(), githubCloneUrl);
Map<String, String> sourceParameters = sourceStorageDto.getParameters();
assertEquals(sourceParameters.size(), 1);
assertEquals(sourceParameters.get("branch"), githubBranch);
}
/** Check that we have a sparse checkout "keepDir" if url contains branch and subtree. */
@Test
public void shouldReturnGitHubBranchAndKeepdirFactory() throws Exception {
private FactoryDto generateDevfileFactory() {
return newDto(FactoryDto.class)
.withV(CURRENT_VERSION)
.withSource("repo")
.withDevfile(
newDto(DevfileDto.class)
.withApiVersion(CURRENT_API_VERSION)
.withMetadata(newDto(MetadataDto.class).withName("che")));
}
String githubUrl = "https://github.com/eclipse/che/tree/4.2.x/dashboard";
String githubCloneUrl = "https://github.com/eclipse/che";
String githubBranch = "4.2.x";
String githubKeepdir = "dashboard";
FactoryDto computedFactory = newDto(FactoryDto.class).withV(CURRENT_VERSION);
when(urlFactoryBuilder.createFactoryFromJson(any(RemoteFactoryUrl.class)))
.thenReturn(Optional.of(computedFactory));
githubFactoryParametersResolver.createFactory(singletonMap(URL_PARAMETER_NAME, githubUrl));
// check we called the builder with the following factory json file
verify(urlFactoryBuilder).createFactoryFromJson(factoryUrlArgumentCaptor.capture());
assertEquals(
factoryUrlArgumentCaptor.getValue().factoryFileLocation(),
"https://raw.githubusercontent.com/eclipse/che/4.2.x/.factory.json");
assertEquals(factoryUrlArgumentCaptor.getValue().getFactoryFilename(), ".factory.json");
// check we provide dockerfile and correct env
verify(urlFactoryBuilder).buildDefaultWorkspaceConfig(eq("che"));
// check project config built
verify(projectConfigDtoMerger)
.merge(any(FactoryDto.class), projectConfigDtoArgumentCaptor.capture());
ProjectConfigDto projectConfigDto = projectConfigDtoArgumentCaptor.getValue().get();
SourceStorageDto sourceStorageDto = projectConfigDto.getSource();
assertNotNull(sourceStorageDto);
assertEquals(sourceStorageDto.getType(), "github");
assertEquals(sourceStorageDto.getLocation(), githubCloneUrl);
Map<String, String> sourceParameters = sourceStorageDto.getParameters();
assertEquals(sourceParameters.size(), 2);
assertEquals(sourceParameters.get("branch"), githubBranch);
assertEquals(sourceParameters.get("keepDir"), githubKeepdir);
private FactoryDto generateWsConfigFactory() {
return newDto(FactoryDto.class)
.withV(CURRENT_VERSION)
.withSource("repo")
.withWorkspace(newDto(WorkspaceConfigDto.class).withName("che"));
}
}

View File

@ -20,6 +20,7 @@ import org.eclipse.che.api.core.model.factory.Factory;
import org.eclipse.che.api.core.rest.shared.dto.Hyperlinks;
import org.eclipse.che.api.core.rest.shared.dto.Link;
import org.eclipse.che.api.workspace.shared.dto.WorkspaceConfigDto;
import org.eclipse.che.api.workspace.shared.dto.devfile.DevfileDto;
import org.eclipse.che.dto.shared.DTO;
/**
@ -38,8 +39,16 @@ public interface FactoryDto extends Factory, Hyperlinks {
FactoryDto withV(String v);
@FactoryParameter(obligation = OPTIONAL)
DevfileDto getDevfile();
void setDevfile(DevfileDto workspace);
FactoryDto withDevfile(DevfileDto devfileDto);
/** because factory DTO may have devfile, in that case, workspace may be optional */
@Override
@FactoryParameter(obligation = MANDATORY)
@FactoryParameter(obligation = OPTIONAL)
WorkspaceConfigDto getWorkspace();
void setWorkspace(WorkspaceConfigDto workspace);

View File

@ -47,6 +47,11 @@ public class ProjectConfigDtoMerger {
*/
public FactoryDto merge(FactoryDto factory, Supplier<ProjectConfigDto> configSupplier) {
if (factory.getWorkspace() == null) {
// factory is created with devfile. There is no need to provision projects
return factory;
}
final List<ProjectConfigDto> projects = factory.getWorkspace().getProjects();
if (projects == null || projects.isEmpty()) {
factory.getWorkspace().setProjects(singletonList(configSupplier.get()));

View File

@ -13,6 +13,7 @@ package org.eclipse.che.api.factory.server.urlfactory;
import static com.google.common.base.Strings.isNullOrEmpty;
import static org.eclipse.che.api.factory.shared.Constants.CURRENT_VERSION;
import static org.eclipse.che.api.workspace.server.devfile.Constants.CURRENT_API_VERSION;
import static org.eclipse.che.api.workspace.shared.Constants.WORKSPACE_TOOLING_EDITOR_ATTRIBUTE;
import static org.eclipse.che.api.workspace.shared.Constants.WORKSPACE_TOOLING_PLUGINS_ATTRIBUTE;
import static org.eclipse.che.dto.server.DtoFactory.newDto;
@ -31,9 +32,10 @@ import org.eclipse.che.api.workspace.server.devfile.DevfileManager;
import org.eclipse.che.api.workspace.server.devfile.FileContentProvider;
import org.eclipse.che.api.workspace.server.devfile.URLFetcher;
import org.eclipse.che.api.workspace.server.devfile.exception.DevfileException;
import org.eclipse.che.api.workspace.server.model.impl.WorkspaceConfigImpl;
import org.eclipse.che.api.workspace.server.model.impl.devfile.DevfileImpl;
import org.eclipse.che.api.workspace.shared.dto.WorkspaceConfigDto;
import org.eclipse.che.api.workspace.shared.dto.devfile.DevfileDto;
import org.eclipse.che.api.workspace.shared.dto.devfile.MetadataDto;
import org.eclipse.che.dto.server.DtoFactory;
/**
@ -105,12 +107,12 @@ public class URLFactoryBuilder {
}
try {
DevfileImpl devfile = devfileManager.parseYaml(devfileYamlContent);
WorkspaceConfigImpl wsConfig =
devfileManager.createWorkspaceConfig(devfile, fileContentProvider);
devfileManager.resolveReference(devfile, fileContentProvider);
FactoryDto factoryDto =
newDto(FactoryDto.class)
.withV(CURRENT_VERSION)
.withWorkspace(DtoConverter.asDto(wsConfig))
.withDevfile(DtoConverter.asDto(devfile))
.withSource(remoteFactoryUrl.getDevfileFilename());
return Optional.of(factoryDto);
} catch (DevfileException e) {
@ -137,4 +139,18 @@ public class URLFactoryBuilder {
// workspace configuration using the environment
return newDto(WorkspaceConfigDto.class).withName(name).withAttributes(attributes);
}
/**
* Help to generate default workspace devfile. Also initialise project in it
*
* @param name the name of the workspace
* @return a workspace devfile
*/
public DevfileDto buildDefaultDevfile(String name) {
// workspace configuration using the environment
return newDto(DevfileDto.class)
.withApiVersion(CURRENT_API_VERSION)
.withMetadata(newDto(MetadataDto.class).withName(name));
}
}

View File

@ -16,36 +16,23 @@ import static org.eclipse.che.api.workspace.server.devfile.Constants.EDITOR_COMP
import static org.eclipse.che.api.workspace.server.devfile.Constants.KUBERNETES_COMPONENT_TYPE;
import static org.eclipse.che.api.workspace.server.devfile.Constants.OPENSHIFT_COMPONENT_TYPE;
import static org.eclipse.che.api.workspace.server.devfile.Constants.PLUGIN_COMPONENT_TYPE;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import org.eclipse.che.api.factory.server.urlfactory.URLFactoryBuilder;
import org.eclipse.che.api.workspace.server.WorkspaceManager;
import org.eclipse.che.api.workspace.server.devfile.DevfileManager;
import org.eclipse.che.api.workspace.server.devfile.FileContentProvider;
import org.eclipse.che.api.workspace.server.devfile.URLFetcher;
import org.eclipse.che.api.workspace.server.devfile.convert.CommandConverter;
import org.eclipse.che.api.workspace.server.devfile.convert.DefaultEditorProvisioner;
import org.eclipse.che.api.workspace.server.devfile.convert.DevfileConverter;
import org.eclipse.che.api.workspace.server.devfile.convert.ProjectConverter;
import org.eclipse.che.api.workspace.server.devfile.convert.component.ComponentFQNParser;
import org.eclipse.che.api.workspace.server.devfile.convert.component.ComponentProvisioner;
import org.eclipse.che.api.workspace.server.devfile.convert.component.ComponentToWorkspaceApplier;
import org.eclipse.che.api.workspace.server.devfile.schema.DevfileSchemaProvider;
import org.eclipse.che.api.workspace.server.devfile.validator.ComponentIntegrityValidator;
import org.eclipse.che.api.workspace.server.devfile.validator.ComponentIntegrityValidator.NoopComponentIntegrityValidator;
import org.eclipse.che.api.workspace.server.devfile.validator.DevfileIntegrityValidator;
import org.eclipse.che.api.workspace.server.devfile.validator.DevfileSchemaValidator;
import org.eclipse.che.api.workspace.server.model.impl.devfile.ComponentImpl;
import org.eclipse.che.api.workspace.server.wsplugins.PluginFQNParser;
import org.mockito.Mock;
import org.mockito.testng.MockitoTestNGListener;
import org.testng.annotations.Listeners;
@ -65,7 +52,6 @@ public class DefaultFactoryParameterResolverTest {
+ " reference: ../localfile\n";
@Mock private URLFetcher urlFetcher;
private ComponentFQNParser componentFQNParser = new ComponentFQNParser(new PluginFQNParser());
@Test
public void shouldResolveRelativeFiles() throws Exception {
@ -81,32 +67,8 @@ public class DefaultFactoryParameterResolverTest {
validators.put(OPENSHIFT_COMPONENT_TYPE, new NoopComponentIntegrityValidator());
DevfileIntegrityValidator integrityValidator = new DevfileIntegrityValidator(validators);
Set<ComponentProvisioner> componentProvisioners = new HashSet<>();
Map<String, ComponentToWorkspaceApplier> appliers = new HashMap<>();
ComponentToWorkspaceApplier applier = mock(ComponentToWorkspaceApplier.class);
appliers.put("kubernetes", applier);
doAnswer(
i -> {
// in here we mock that the component applier requests the contents of the referenced
// local file. That's all we need to happen
FileContentProvider p = i.getArgument(2);
ComponentImpl component = i.getArgument(1);
p.fetchContent(component.getReference());
return null;
})
.when(applier)
.apply(any(), any(), any());
DevfileConverter devfileConverter =
new DevfileConverter(
new ProjectConverter(),
new CommandConverter(),
componentProvisioners,
appliers,
new DefaultEditorProvisioner(null, new String[] {}, componentFQNParser),
new URLFetcher());
DevfileConverter devfileConverter = mock(DevfileConverter.class);
WorkspaceManager workspaceManager = mock(WorkspaceManager.class);
DevfileManager devfileManager =

View File

@ -14,12 +14,10 @@ package org.eclipse.che.api.factory.server.urlfactory;
import static java.util.Collections.emptyMap;
import static java.util.Collections.singletonMap;
import static org.eclipse.che.api.factory.shared.Constants.CURRENT_VERSION;
import static org.eclipse.che.api.workspace.server.DtoConverter.asDto;
import static org.eclipse.che.api.workspace.server.devfile.Constants.KUBERNETES_COMPONENT_TYPE;
import static org.eclipse.che.api.workspace.shared.Constants.WORKSPACE_TOOLING_EDITOR_ATTRIBUTE;
import static org.eclipse.che.api.workspace.shared.Constants.WORKSPACE_TOOLING_PLUGINS_ATTRIBUTE;
import static org.eclipse.che.dto.server.DtoFactory.newDto;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.Mockito.when;
import static org.testng.Assert.assertEquals;
@ -120,8 +118,6 @@ public class URLFactoryBuilderTest {
when(urlFetcher.fetchSafely(anyString())).thenReturn("random_content");
when(devfileManager.parseYaml(anyString())).thenReturn(devfile);
when(devfileManager.createWorkspaceConfig(any(DevfileImpl.class), any()))
.thenReturn(workspaceConfigImpl);
FactoryDto factory =
urlFactoryBuilder
@ -132,8 +128,6 @@ public class URLFactoryBuilderTest {
s -> myLocation + ".list")
.get();
WorkspaceConfigDto expectedWorkspaceConfig = asDto(workspaceConfigImpl);
assertEquals(factory.getWorkspace(), expectedWorkspaceConfig);
assertEquals(factory.getSource(), "devfile.yml");
}
}

View File

@ -98,11 +98,11 @@ public interface ComponentDto extends Component {
ComponentDto withMemoryLimit(String memoryLimit);
@Override
boolean getMountSources();
Boolean getMountSources();
void setMountSources(boolean mountSources);
void setMountSources(Boolean mountSources);
ComponentDto withMountSources(boolean mountSources);
ComponentDto withMountSources(Boolean mountSources);
@Override
List<String> getCommand();

View File

@ -104,7 +104,7 @@ public final class DtoConverter {
return workspaceDto;
}
private static DevfileDto asDto(Devfile devfile) {
public static DevfileDto asDto(Devfile devfile) {
List<DevfileCommandDto> commands =
devfile.getCommands().stream().map(DtoConverter::asDto).collect(toList());
List<ComponentDto> components =

View File

@ -11,8 +11,13 @@
*/
package org.eclipse.che.api.workspace.server.devfile;
import static com.google.common.base.MoreObjects.firstNonNull;
import static com.google.common.base.Preconditions.checkArgument;
import static com.google.common.base.Strings.isNullOrEmpty;
import static java.lang.String.format;
import static java.util.Collections.emptyMap;
import static org.eclipse.che.api.workspace.server.devfile.Constants.KUBERNETES_COMPONENT_TYPE;
import static org.eclipse.che.api.workspace.server.devfile.Constants.OPENSHIFT_COMPONENT_TYPE;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.JsonNode;
@ -20,6 +25,9 @@ import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.dataformat.yaml.YAMLFactory;
import com.google.common.annotations.Beta;
import com.google.common.annotations.VisibleForTesting;
import java.io.IOException;
import java.util.List;
import java.util.stream.Collectors;
import javax.inject.Inject;
import javax.inject.Singleton;
import org.eclipse.che.api.core.ConflictException;
@ -35,6 +43,7 @@ import org.eclipse.che.api.workspace.server.devfile.validator.DevfileIntegrityVa
import org.eclipse.che.api.workspace.server.devfile.validator.DevfileSchemaValidator;
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.ComponentImpl;
import org.eclipse.che.api.workspace.server.model.impl.devfile.DevfileImpl;
import org.eclipse.che.commons.env.EnvironmentContext;
@ -107,6 +116,37 @@ public class DevfileManager {
return parse(devfileContent, schemaValidator::validateJson);
}
/**
* Resolve devfile component references into their reference content.
*
* @param devfile input devfile
* @param fileContentProvider provider to fetch reference content
*/
public void resolveReference(DevfileImpl devfile, FileContentProvider fileContentProvider)
throws DevfileException {
List<ComponentImpl> toResolve =
devfile
.getComponents()
.stream()
.filter(
c ->
c.getType().equals(KUBERNETES_COMPONENT_TYPE)
|| c.getType().equals(OPENSHIFT_COMPONENT_TYPE))
.filter(c -> !isNullOrEmpty(c.getReference()))
.collect(Collectors.toList());
for (ComponentImpl c : toResolve) {
try {
c.setReferenceContent(fileContentProvider.fetchContent(c.getReference()));
} catch (IOException e) {
throw new DevfileException(
format(
"Unable to resolve reference of component: %s",
firstNonNull(c.getAlias(), c.getReference())),
e);
}
}
}
private DevfileImpl parse(String content, ValidationFunction validationFunction)
throws DevfileFormatException {
JsonNode parsed = validationFunction.validate(content);

View File

@ -26,6 +26,7 @@ import io.swagger.annotations.ApiResponses;
import io.swagger.annotations.Example;
import io.swagger.annotations.ExampleProperty;
import java.io.IOException;
import java.util.List;
import javax.inject.Inject;
import javax.ws.rs.Consumes;
import javax.ws.rs.GET;
@ -41,11 +42,13 @@ import org.eclipse.che.api.core.ServerException;
import org.eclipse.che.api.core.ValidationException;
import org.eclipse.che.api.core.rest.Service;
import org.eclipse.che.api.workspace.server.WorkspaceLinksGenerator;
import org.eclipse.che.api.workspace.server.WorkspaceService;
import org.eclipse.che.api.workspace.server.devfile.exception.DevfileException;
import org.eclipse.che.api.workspace.server.devfile.schema.DevfileSchemaProvider;
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.shared.dto.WorkspaceDto;
import org.eclipse.che.api.workspace.shared.dto.devfile.DevfileDto;
@Api(value = "/devfile", description = "Devfile REST API")
@Path("/devfile")
@ -95,6 +98,8 @@ public class DevfileService extends Service {
*
* @param data devfile content
* @return created workspace configuration
* @deprecated This method of workspace creation is deprecated in favor of {@link
* WorkspaceService#create(DevfileDto, List, Boolean, String)}
*/
@POST
@Consumes({"text/yaml", "text/x-yaml", "application/yaml", "application/json"})
@ -114,6 +119,7 @@ public class DevfileService extends Service {
@ApiResponse(code = 403, message = "The user does not have access to create a new workspace"),
@ApiResponse(code = 500, message = "Internal server error occurred")
})
@Deprecated
public Response createFromYaml(String data)
throws ServerException, ConflictException, NotFoundException, ValidationException,
BadRequestException {

View File

@ -87,7 +87,7 @@ public class ComponentImpl implements Component {
private String memoryLimit;
@Column(name = "mount_sources")
private boolean mountSources;
private Boolean mountSources;
@ElementCollection(fetch = FetchType.EAGER)
@CollectionTable(
@ -160,7 +160,7 @@ public class ComponentImpl implements Component {
String alias,
String image,
String memoryLimit,
boolean mountSources,
Boolean mountSources,
List<String> command,
List<String> args,
List<? extends Volume> volumes,
@ -198,7 +198,7 @@ public class ComponentImpl implements Component {
List<? extends Entrypoint> entrypoints,
String image,
String memoryLimit,
boolean mountSources,
Boolean mountSources,
List<String> command,
List<String> args,
List<? extends Volume> volumes,
@ -366,11 +366,11 @@ public class ComponentImpl implements Component {
}
@Override
public boolean getMountSources() {
public Boolean getMountSources() {
return mountSources;
}
public void setMountSources(boolean mountSources) {
public void setMountSources(Boolean mountSources) {
this.mountSources = mountSources;
}

View File

@ -11,6 +11,7 @@
*/
package org.eclipse.che.api.workspace.server.devfile;
import static org.eclipse.che.api.workspace.server.devfile.Constants.KUBERNETES_COMPONENT_TYPE;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyMap;
import static org.mockito.ArgumentMatchers.anyString;
@ -33,6 +34,7 @@ import org.eclipse.che.api.core.NotFoundException;
import org.eclipse.che.api.core.model.workspace.WorkspaceStatus;
import org.eclipse.che.api.workspace.server.WorkspaceManager;
import org.eclipse.che.api.workspace.server.devfile.convert.DevfileConverter;
import org.eclipse.che.api.workspace.server.devfile.exception.DevfileException;
import org.eclipse.che.api.workspace.server.devfile.exception.DevfileFormatException;
import org.eclipse.che.api.workspace.server.devfile.validator.DevfileIntegrityValidator;
import org.eclipse.che.api.workspace.server.devfile.validator.DevfileSchemaValidator;
@ -68,6 +70,7 @@ public class DevfileManagerTest {
@Mock private DevfileConverter devfileConverter;
@Mock private WorkspaceManager workspaceManager;
@Mock private ObjectMapper objectMapper;
@Mock private FileContentProvider contentProvider;
@Mock private JsonNode devfileJsonNode;
private DevfileImpl devfile;
@ -114,6 +117,44 @@ public class DevfileManagerTest {
assertNotNull(parsed.getComponents().get(0).getEndpoints().get(0).getAttributes());
}
@Test
public void shouldResolveReferencesIntoReferenceContentForFactories() throws Exception {
String referenceContent = "my_content_yaml_v3";
when(contentProvider.fetchContent(anyString())).thenReturn(referenceContent);
ComponentImpl component = new ComponentImpl();
component.setType(KUBERNETES_COMPONENT_TYPE);
component.setReference("myfile.yaml");
devfile.getComponents().add(component);
// when
devfileManager.resolveReference(devfile, contentProvider);
// then
verify(contentProvider).fetchContent(eq("myfile.yaml"));
assertEquals(referenceContent, devfile.getComponents().get(0).getReferenceContent());
}
@Test(
expectedExceptions = DevfileException.class,
expectedExceptionsMessageRegExp = "Unable to resolve reference of component: test")
public void shouldThrowDevfileExceptionWhenReferenceIsNotResolvable() throws Exception {
when(contentProvider.fetchContent(anyString())).thenThrow(IOException.class);
ComponentImpl component = new ComponentImpl();
component.setType(KUBERNETES_COMPONENT_TYPE);
component.setAlias("test");
component.setReference("myfile.yaml");
devfile.getComponents().add(component);
// when
devfileManager.resolveReference(devfile, contentProvider);
// then exception is thrown
}
@Test(
expectedExceptions = DevfileFormatException.class,
expectedExceptionsMessageRegExp = "non valid")