CHE-221 Add export workspace portability flow
It creates a remote workspace, start it and import projects that have a pre-defined remote location Change-Id: I8b57c8656138ecb1832f10dd0401babf20a58e0c Signed-off-by: Florent BENOIT <fbenoit@codenvy.com>6.19.x
parent
ec9f7ea5b7
commit
d598ecab2d
|
|
@ -0,0 +1,86 @@
|
|||
<md-dialog flex="50">
|
||||
<che-panel che-title="Export Workspace">
|
||||
<md-dialog-content>
|
||||
<md-tabs md-dynamic-height md-stretch-tabs="always" md-selected="selectedIndex">
|
||||
<md-tab>
|
||||
<md-tab-label>
|
||||
<md-icon md-font-icon="fa fa-download" class="che-tab-label-icon"></md-icon>
|
||||
<span class="che-tab-label-title">As a File</span>
|
||||
</md-tab-label>
|
||||
<md-tab-body>
|
||||
<div layout="row" flex>
|
||||
<ui-codemirror flex class="workspace-editor"
|
||||
ui-codemirror="exportWorkspaceDialogController.editorOptions"
|
||||
ng-model="exportWorkspaceDialogController.exportWorkspaceContent"></ui-codemirror>
|
||||
</div>
|
||||
<div layout="row" layout-align="end top">
|
||||
<che-button-default che-button-title="download"
|
||||
che-button-icon="fa fa-download"
|
||||
ng-href="{{exportWorkspaceDialogController.downloadLink}}" ></che-button-default>
|
||||
|
||||
<che-button-default che-button-title="clipboard"
|
||||
che-button-icon="fa fa-clipboard"
|
||||
clip-copy="exportWorkspaceDialogController.exportWorkspaceContent"></che-button-default>
|
||||
</div>
|
||||
</md-tab-body>
|
||||
</md-tab>
|
||||
<md-tab>
|
||||
<md-tab-label>
|
||||
<md-icon md-font-icon="fa fa-cloud-upload" class="che-tab-label-icon"></md-icon>
|
||||
<span class="che-tab-label-title">To Private Cloud</span>
|
||||
</md-tab-label>
|
||||
<md-tab-body>
|
||||
<ng-form flex layout="column" name="privateCloudForm">
|
||||
<che-input che-form="privateCloudForm"
|
||||
che-name="url"
|
||||
che-label-name="Host"
|
||||
che-place-holder="URL of the remote cloud"
|
||||
ng-model="exportWorkspaceDialogController.privateCloudUrl"
|
||||
required>
|
||||
<div ng-message="required">An URL is required.</div>
|
||||
</che-input>
|
||||
<che-input che-form="privateCloudForm"
|
||||
che-name="username"
|
||||
che-label-name="Login"
|
||||
che-place-holder="Username used to login on the remote cloud"
|
||||
ng-model="exportWorkspaceDialogController.privateCloudLogin"
|
||||
required
|
||||
ng-maxlength="128">
|
||||
<div ng-message="required">A name is required.</div>
|
||||
<div ng-message="maxlength">The name has to be less than 128 characters long.</div>
|
||||
<div ng-message="md-maxlength">The name has to be less than 128 characters long.</div>
|
||||
</che-input>
|
||||
<che-input che-form="privateCloudForm"
|
||||
che-name="password"
|
||||
che-label-name="Password"
|
||||
che-place-holder="Password used to login on the remote cloud"
|
||||
ng-model="exportWorkspaceDialogController.privateCloudPassword"
|
||||
required
|
||||
type="password"
|
||||
ng-maxlength="128">
|
||||
<div ng-message="required">A password is required.</div>
|
||||
<div ng-message="maxlength">The name has to be less than 128 characters long.</div>
|
||||
<div ng-message="md-maxlength">The name has to be less than 128 characters long.</div>
|
||||
</che-input>
|
||||
</ng-form>
|
||||
<div layout="row" layout-align="end top">
|
||||
<che-button-default che-button-title="export"
|
||||
che-button-icon="fa fa-cloud-upload"
|
||||
ng-disabled="privateCloudForm.$invalid || exportWorkspaceDialogController.importInProgress"
|
||||
ng-click="exportWorkspaceDialogController.exportToPrivateCloud()"></che-button-default>
|
||||
|
||||
</div>
|
||||
<div layout="row" layout-align="start center" flex ng-if="exportWorkspaceDialogController.importInProgress">
|
||||
<div><md-icon md-svg-src="assets/images/loader.svg" class="export-workspace-progress-icon" aria-label="loader"></md-icon></div>
|
||||
<div ng-bind-html="exportWorkspaceDialogController.exportInCloudSteps"></div>
|
||||
</div>
|
||||
</md-tab-body>
|
||||
</md-tab>
|
||||
</md-tabs>
|
||||
</md-dialog-content>
|
||||
</che-panel>
|
||||
|
||||
<md-dialog-actions>
|
||||
<md-button ng-hide="exportWorkspaceDialogController.importInProgress" ng-click="exportWorkspaceDialogController.hide()" tabindex="0" type="button">HIDE</md-button>
|
||||
</md-dialog-actions>
|
||||
</md-dialog>
|
||||
|
|
@ -0,0 +1,208 @@
|
|||
/*
|
||||
* Copyright (c) 2015-2016 Codenvy, S.A.
|
||||
* 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:
|
||||
* Codenvy, S.A. - initial API and implementation
|
||||
*/
|
||||
'use strict';
|
||||
|
||||
/**
|
||||
* @ngdoc controller
|
||||
* @name workspace.export.controller:ExportWorkspaceDialogController
|
||||
* @description This class is handling the controller for the dialog box about the export of workspace
|
||||
* @author Florent Benoit
|
||||
*/
|
||||
export class ExportWorkspaceDialogController {
|
||||
|
||||
/**
|
||||
* Default constructor that is using resource
|
||||
* @ngInject for Dependency injection
|
||||
*/
|
||||
constructor($q, $filter, lodash, cheRemote, cheNotification, $http, cheRecipeTemplate, $mdDialog, $log) {
|
||||
this.$q = $q;
|
||||
this.$filter = $filter;
|
||||
this.$http = $http;
|
||||
this.lodash = lodash;
|
||||
this.cheRemote = cheRemote;
|
||||
this.cheNotification = cheNotification;
|
||||
this.cheRecipeTemplate = cheRecipeTemplate;
|
||||
this.$mdDialog = $mdDialog;
|
||||
this.$log = $log;
|
||||
this.editorOptions = {
|
||||
lineWrapping : true,
|
||||
lineNumbers: false,
|
||||
matchBrackets: true,
|
||||
readOnly: 'nocursor',
|
||||
mode: 'application/json'
|
||||
};
|
||||
this.privateCloudUrl = '';
|
||||
this.privateCloudLogin = '';
|
||||
this.privateCloudPassword = '';
|
||||
this.retrieveWorkspace();
|
||||
this.importInProgress = false;
|
||||
}
|
||||
|
||||
/**
|
||||
* It will hide the dialog box.
|
||||
*/
|
||||
hide() {
|
||||
this.$mdDialog.hide();
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the workspace details and links to get download the JSON file
|
||||
*/
|
||||
retrieveWorkspace() {
|
||||
let copyOfWorkspace = angular.copy(this.workspaceDetails);
|
||||
this.downloadLink = '/api/workspace/' + this.workspaceId + '?downloadAsFile=' + this.workspaceDetails.name + '.json';
|
||||
|
||||
//remove links
|
||||
delete copyOfWorkspace.links;
|
||||
this.exportWorkspaceContent = this.$filter('json')(angular.fromJson(copyOfWorkspace), 2);
|
||||
}
|
||||
|
||||
/**
|
||||
* Start the process to export to the private Cloud
|
||||
*/
|
||||
exportToPrivateCloud() {
|
||||
this.exportInCloudSteps = '';
|
||||
this.importInProgress = true;
|
||||
let login = this.cheRemote.newAuth(this.privateCloudUrl, this.privateCloudLogin, this.privateCloudPassword);
|
||||
|
||||
login.then((authData) => {
|
||||
let copyOfWorkspace = angular.copy(this.workspaceDetails);
|
||||
copyOfWorkspace.name = 'import-' + copyOfWorkspace.name;
|
||||
|
||||
// get content of the recipe
|
||||
let environments = copyOfWorkspace.environments;
|
||||
let defaultEnvName = copyOfWorkspace.defaultEnv;
|
||||
let defaultEnvironment = this.lodash.find(environments, (environment) => {
|
||||
return environment.name === defaultEnvName;
|
||||
});
|
||||
|
||||
let recipeLocation = defaultEnvironment.machineConfigs[0].source.location;
|
||||
|
||||
// get content of recipe
|
||||
this.$http.get(recipeLocation).then((response) => {
|
||||
|
||||
let recipeScriptContent = response.data;
|
||||
// now upload the recipe to the remote service
|
||||
let remoteRecipeAPI = this.cheRemote.newRecipe(authData);
|
||||
|
||||
let recipeContent = this.cheRecipeTemplate.getDefaultRecipe();
|
||||
recipeContent.name = 'recipe-' + copyOfWorkspace.name;
|
||||
recipeContent.script = recipeScriptContent;
|
||||
|
||||
let createRecipePromise = remoteRecipeAPI.create(recipeContent);
|
||||
createRecipePromise.then((recipe) => {
|
||||
let findLink = this.lodash.find(recipe.links, (link) => {
|
||||
return link.rel === 'get recipe script';
|
||||
});
|
||||
|
||||
// update copy of workspace with new recipe link
|
||||
let recipeLink = findLink.href;
|
||||
defaultEnvironment.machineConfigs[0].source.location = recipeLink;
|
||||
|
||||
|
||||
let remoteWorkspaceAPI = this.cheRemote.newWorkspace(authData);
|
||||
let remoteProjectAPI = this.cheRemote.newProject(authData);
|
||||
this.exportInCloudSteps += 'Creating remote workspace...';
|
||||
let createWorkspacePromise = remoteWorkspaceAPI.createWorkspaceFromConfig(null, copyOfWorkspace);
|
||||
createWorkspacePromise.then((remoteWorkspace) => {
|
||||
this.exportInCloudSteps += 'ok !<br>';
|
||||
// ok now we've to import each project with a location into the remote workspace
|
||||
let importProjectsPromise = this.importProjectsIntoWorkspace(remoteWorkspaceAPI, remoteProjectAPI, remoteWorkspace, authData);
|
||||
importProjectsPromise.then(() => {
|
||||
this.exportInCloudSteps += 'Export of workspace ' + copyOfWorkspace.name + 'finished <br>';
|
||||
this.cheNotification.showInfo('Successfully exported the workspace to ' + copyOfWorkspace.name + ' on ' + this.privateCloudUrl);
|
||||
this.hide();
|
||||
}, (error) => {
|
||||
this.handleError(error);
|
||||
})
|
||||
|
||||
}, (error) => {
|
||||
this.handleError(error);
|
||||
})
|
||||
});
|
||||
}, (error) => {
|
||||
this.handleError(error);
|
||||
});
|
||||
|
||||
}, (error) => {
|
||||
this.handleError(error);
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Import all projects with a given source location into the remote workspace
|
||||
* @param workspace the remote workspace
|
||||
*/
|
||||
importProjectsIntoWorkspace(remoteWorkspaceAPI, remoteProjectAPI, workspace, authData) {
|
||||
|
||||
var projectPromises = [];
|
||||
|
||||
// ok so
|
||||
workspace.projects.forEach((project) => {
|
||||
if (project.source && project.source.location && project.source.location.length > 0) {
|
||||
let deferred = this.$q.defer();
|
||||
let deferredPromise = deferred.promise;
|
||||
projectPromises.push(deferredPromise);
|
||||
this.exportInCloudSteps += 'Starting remote workspace...';
|
||||
|
||||
// compute WS url
|
||||
let remoteURL = authData.url;
|
||||
let remoteWsURL = remoteURL.replace('http', 'ws') + '/api/ws/';
|
||||
|
||||
let startWorkspacePromise = remoteWorkspaceAPI.startWorkspace(remoteWsURL, workspace.id, workspace.defaultEnv);
|
||||
|
||||
startWorkspacePromise.then(() => {
|
||||
this.exportInCloudSteps += 'ok !<br>';
|
||||
let importProjectPromise = remoteProjectAPI.importProject(workspace.id, project.name, project.source);
|
||||
|
||||
importProjectPromise.then(() => {
|
||||
this.exportInCloudSteps += 'Importing project ' + project.name + '...<br>';
|
||||
let updateProjectPromise = remoteProjectAPI.updateProject(workspace.id, project.name, project);
|
||||
updateProjectPromise.then(() => {
|
||||
deferred.resolve(workspace);
|
||||
}, (error) => {
|
||||
deferred.reject(error);
|
||||
});
|
||||
}, (error) => {
|
||||
deferred.reject(error);
|
||||
});
|
||||
}, (error) => {
|
||||
this.handleError(error);
|
||||
});
|
||||
}
|
||||
|
||||
});
|
||||
return this.$q.all(projectPromises);
|
||||
}
|
||||
|
||||
/**
|
||||
* Notify user about the error.
|
||||
* @param error the error message to display
|
||||
*/
|
||||
handleError(error) {
|
||||
this.importInProgress = false;
|
||||
var message;
|
||||
if (error.data) {
|
||||
if (error.data.message) {
|
||||
message = error.data.message;
|
||||
} else {
|
||||
message = error.data;
|
||||
}
|
||||
} else {
|
||||
message = 'unable to connect to ' + error.config.url;
|
||||
}
|
||||
this.cheNotification.showError('Exporting workspace failed: ' + message);
|
||||
this.$log.error('error', message, error);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,5 @@
|
|||
.export-workspace-progress-icon
|
||||
width 52px
|
||||
height 52px
|
||||
fill $primary-color
|
||||
margin-right 15px
|
||||
|
|
@ -0,0 +1,43 @@
|
|||
/*
|
||||
* Copyright (c) 2015-2016 Codenvy, S.A.
|
||||
* 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:
|
||||
* Codenvy, S.A. - initial API and implementation
|
||||
*/
|
||||
'use strict';
|
||||
|
||||
/**
|
||||
* @ngdoc controller
|
||||
* @name workspace.export.controller:ExportWorkspaceController
|
||||
* @description This class is handling the controller for the export of workspace
|
||||
* @author Florent Benoit
|
||||
*/
|
||||
export class ExportWorkspaceController {
|
||||
|
||||
/**
|
||||
* Default constructor that is using resource
|
||||
* @ngInject for Dependency injection
|
||||
*/
|
||||
constructor($mdDialog) {
|
||||
this.$mdDialog = $mdDialog;
|
||||
}
|
||||
|
||||
showExport($event) {
|
||||
this.$mdDialog.show({
|
||||
targetEvent: $event,
|
||||
controller: 'ExportWorkspaceDialogController',
|
||||
controllerAs: 'exportWorkspaceDialogController',
|
||||
bindToController: true,
|
||||
clickOutsideToClose: true,
|
||||
locals: {workspaceId: this.workspaceId,
|
||||
workspaceDetails: this.workspaceDetails, callbackController: this},
|
||||
templateUrl: 'app/workspaces/workspace-details/export-workspace/dialog/export-tab-dialog.html'
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,48 @@
|
|||
/*
|
||||
* Copyright (c) 2015-2016 Codenvy, S.A.
|
||||
* 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:
|
||||
* Codenvy, S.A. - initial API and implementation
|
||||
*/
|
||||
'use strict';
|
||||
|
||||
/**
|
||||
* @ngdoc directive
|
||||
* @name workspaces.details.directive:workspaceDetailsProjects
|
||||
* @restrict E
|
||||
* @element
|
||||
*
|
||||
* @description
|
||||
* <export-workspace workspace-id="workspaceID" workspace-details="workspaceDetails"></export-workspace>
|
||||
*
|
||||
* @usage
|
||||
* <export-workspace workspace-id="workspaceID" workspace-details="workspaceDetails"></export-workspace>
|
||||
*
|
||||
* @author Florent Benoit
|
||||
*/
|
||||
export class ExportWorkspace {
|
||||
|
||||
/**
|
||||
* Default constructor that is using resource
|
||||
* @ngInject for Dependency injection
|
||||
*/
|
||||
constructor () {
|
||||
this.restrict = 'E';
|
||||
this.templateUrl = 'app/workspaces/workspace-details/export-workspace/export-workspace.html';
|
||||
|
||||
this.controller = 'ExportWorkspaceController';
|
||||
this.controllerAs = 'exportWorkspaceCtrl';
|
||||
this.bindToController = true;
|
||||
|
||||
// scope values
|
||||
this.scope = {
|
||||
workspaceId: '@workspaceId',
|
||||
workspaceDetails: '=workspaceDetails'
|
||||
};
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,10 @@
|
|||
<che-panel che-title="Export Workspace" class="workspace-details-content">
|
||||
<div ng-if="!exportWorkspaceCtrl.exportWorkspaceContent" layout="row" flex layout-align="space-around start">
|
||||
<label flex="15" class="workspace-details-description">
|
||||
Export your workspace.
|
||||
</label>
|
||||
<div layout="row" flex="85">
|
||||
<che-button-default che-button-title="export" ng-click="exportWorkspaceCtrl.showExport($event)"></che-button-default>
|
||||
</div>
|
||||
</div>
|
||||
</che-panel>
|
||||
|
|
@ -20,24 +20,15 @@ export class WorkspaceDetailsCtrl {
|
|||
* Default constructor that is using resource injection
|
||||
* @ngInject for Dependency injection
|
||||
*/
|
||||
constructor($route, $location, cheWorkspace, cheAPI, $mdDialog, cheNotification, $filter) {
|
||||
constructor($route, $location, cheWorkspace, cheAPI, $mdDialog, cheNotification) {
|
||||
this.cheNotification = cheNotification;
|
||||
this.cheAPI = cheAPI;
|
||||
this.cheWorkspace = cheWorkspace;
|
||||
this.$mdDialog = $mdDialog;
|
||||
this.$location = $location;
|
||||
this.$filter = $filter;
|
||||
|
||||
this.workspaceId = $route.current.params.workspaceId;
|
||||
|
||||
this.editorOptions = {
|
||||
lineWrapping : true,
|
||||
lineNumbers: false,
|
||||
matchBrackets: true,
|
||||
readOnly: 'nocursor',
|
||||
mode: 'application/json'
|
||||
};
|
||||
|
||||
this.loading = true;
|
||||
|
||||
if (!this.cheWorkspace.getWorkspacesById().get(this.workspaceId)) {
|
||||
|
|
@ -120,15 +111,6 @@ export class WorkspaceDetailsCtrl {
|
|||
});
|
||||
}
|
||||
|
||||
exportWorkspace() {
|
||||
let copyOfWorkspace = angular.copy(this.workspaceDetails);
|
||||
this.downloadLink = '/api/workspace/' + this.workspaceId + '?downloadAsFile=' + this.workspaceDetails.name + '.json';
|
||||
|
||||
//remove links
|
||||
delete copyOfWorkspace.links;
|
||||
this.exportWorkspaceContent = this.$filter('json')(angular.fromJson(copyOfWorkspace), 2);
|
||||
}
|
||||
|
||||
runWorkspace() {
|
||||
let promise = this.cheAPI.getWorkspace().startWorkspace(this.workspaceId, this.workspaceDetails.defaultEnv);
|
||||
|
||||
|
|
|
|||
|
|
@ -55,35 +55,7 @@
|
|||
</div>
|
||||
</div>
|
||||
</che-panel>
|
||||
<che-panel che-title="Export Workspace" class="workspace-details-content">
|
||||
<div ng-if="!workspaceDetailsCtrl.exportWorkspaceContent" layout="row" flex layout-align="space-around start">
|
||||
<label flex="15" class="workspace-details-description">
|
||||
Export your workspace to a JSON file.
|
||||
</label>
|
||||
|
||||
<div layout="column" flex="85">
|
||||
<che-button-default che-button-title="export" ng-click="workspaceDetailsCtrl.exportWorkspace($event)"/>
|
||||
</div>
|
||||
</div>
|
||||
<div ng-if="workspaceDetailsCtrl.exportWorkspaceContent.length > 0">
|
||||
<div layout="row" flex>
|
||||
<div flex="90">
|
||||
<ui-codemirror class="workspace-editor"
|
||||
ui-codemirror="workspaceDetailsCtrl.editorOptions"
|
||||
ng-model="workspaceDetailsCtrl.exportWorkspaceContent"></ui-codemirror>
|
||||
</div>
|
||||
<div layout="row" layout-align="center center"><che-clipboard class="copy-clipboard" che-value="workspaceDetailsCtrl.exportWorkspaceContent"></che-clipboard></div>
|
||||
</div>
|
||||
<div layout="column">
|
||||
<che-button-default che-button-title="download"
|
||||
che-button-icon="fa fa-download"
|
||||
ng-href="{{workspaceDetailsCtrl.downloadLink}}"/>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
</che-panel>
|
||||
<export-workspace workspace-id="{{workspaceDetailsCtrl.workspaceId}}" workspace-details="workspaceDetailsCtrl.workspaceDetails"></export-workspace>
|
||||
<che-panel che-title="Delete Workspace" class="workspace-details-content">
|
||||
<div layout="row" flex layout-align="space-around start">
|
||||
<label flex="15" class="workspace-details-description">
|
||||
|
|
|
|||
|
|
@ -18,6 +18,9 @@ import {UsageChart} from './list-workspaces/workspace-item/usage-chart.directive
|
|||
import {WorkspaceItemCtrl} from './list-workspaces/workspace-item/workspace-item.controller';
|
||||
import {WorkspaceDetailsCtrl} from './workspace-details/workspace-details.controller';
|
||||
import {WorkspaceDetailsProjectsCtrl} from './workspace-details/workspace-projects/workspace-details-projects.controller';
|
||||
import {ExportWorkspaceController} from './workspace-details/export-workspace/export-workspace.controller';
|
||||
import {ExportWorkspace} from './workspace-details/export-workspace/export-workspace.directive';
|
||||
import {ExportWorkspaceDialogController} from './workspace-details/export-workspace/dialog/export-workspace-dialog.controller';
|
||||
import {WorkspaceDetailsProjects} from './workspace-details/workspace-projects/workspace-details-projects.directive';
|
||||
import {ReadyToGoStacksCtrl} from './create-workspace/select-stack/ready-to-go-stacks/ready-to-go-stacks.controller';
|
||||
import {ReadyToGoStacks} from './create-workspace/select-stack/ready-to-go-stacks/ready-to-go-stacks.directive';
|
||||
|
|
@ -62,6 +65,10 @@ export class WorkspacesConfig {
|
|||
register.controller('WorkspaceDetailsProjectsCtrl', WorkspaceDetailsProjectsCtrl);
|
||||
register.directive('workspaceDetailsProjects', WorkspaceDetailsProjects);
|
||||
|
||||
register.controller('ExportWorkspaceDialogController', ExportWorkspaceDialogController);
|
||||
register.controller('ExportWorkspaceController', ExportWorkspaceController);
|
||||
register.directive('exportWorkspace', ExportWorkspace);
|
||||
|
||||
register.controller('WorkspaceRecipeCtrl', WorkspaceRecipeCtrl);
|
||||
register.directive('cheWorkspaceRecipe', WorkspaceRecipe);
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue