diff --git a/src/generic_core/uproxy_core.ts b/src/generic_core/uproxy_core.ts index 192e444ea2..3a914b6f4f 100644 --- a/src/generic_core/uproxy_core.ts +++ b/src/generic_core/uproxy_core.ts @@ -70,6 +70,13 @@ export class uProxyCore implements uproxy_core_api.CoreApi { private connectedNetworks_ = new StoredValue('connectedNetworks', []); + private cloudInterfaces_ :{ + [cloudProvider: string] :{ + installer :any, + provisioner :any + } + } = {}; + constructor() { log.debug('Preparing uProxy Core'); copyPasteConnection = new remote_connection.RemoteConnection( @@ -643,13 +650,16 @@ export class uProxyCore implements uproxy_core_api.CoreApi { return Promise.reject(new Error('unsupported cloud provider')); } + this.cloudInterfaces_[args.providerName] = this.cloudInterfaces_[args.providerName] || { installer: undefined, provisioner: undefined}; const provisionerName = CLOUD_PROVIDER_MODULE_PREFIX + args.providerName; - const provisioner = freedom[provisionerName](); - const installer = freedom['cloudinstall'](); + const provisioner = this.cloudInterfaces_[args.providerName].provisioner || freedom[provisionerName](); + const installer = this.cloudInterfaces_[args.providerName].installer || freedom['cloudinstall'](); + this.cloudInterfaces_[args.providerName] = { installer: installer, provisioner: provisioner}; const destroyModules = () => { freedom[provisionerName].close(provisioner); freedom['cloudinstall'].close(installer); + delete this.cloudInterfaces_[args.providerName]; }; switch (args.operation) { @@ -709,8 +719,15 @@ export class uProxyCore implements uproxy_core_api.CoreApi { }); }); }, (installError: any) => { + // When we cancel the cloud install we close the connection + // to the installer by destroying the modules + if (installError === 'closed') { + return Promise.reject(new Error('canceled')); + } + // Tell user if the server already exists if (installError.errcode === "VM_AE") { + destroyModules(); return Promise.reject(new Error('server already exists')); } diff --git a/src/generic_ui/locales/en/messages.json b/src/generic_ui/locales/en/messages.json index 24abd6550c..c0bfd7b639 100644 --- a/src/generic_ui/locales/en/messages.json +++ b/src/generic_ui/locales/en/messages.json @@ -1374,5 +1374,21 @@ "REMOVING_UPROXY_CLOUD_STATUS": { "description": "Label to indicate that we are removing a uproxy cloud server.", "message": "Removing uProxy cloud server..." + }, + "CLOUD_INSTALL_CANCEL_TITLE": { + "description": "Title shown on overlay when user cancels a cloud server install.", + "message": "Hold tight! We're canceling the creation of a cloud server." + }, + "CLOUD_INSTALL_CANCEL_MESSAGE": { + "description": "Message shown on overlay when user cancels a cloud server install.", + "message": "Canceling the creation of a cloud server can take up to 2 minutes. Sorry for the wait." + }, + "CLOUD_INSTALL_CANCEL_SUCCESS": { + "description": "Message shown on toast when a could server install is completed.", + "message": "Successfully canceled cloud server creation." + }, + "CLOUD_INSTALL_CANCEL_FAILURE": { + "description": "Message shown on toast when a could server install fails.", + "message": "We cannot cancel the creation of your cloud server right now. You can delete the server once it's ready." } } diff --git a/src/generic_ui/polymer/cloud-install.html b/src/generic_ui/polymer/cloud-install.html index 6e2258829f..c417964abe 100644 --- a/src/generic_ui/polymer/cloud-install.html +++ b/src/generic_ui/polymer/cloud-install.html @@ -163,6 +163,7 @@

{{ 'CLOUD_INSTALL_INSTALLING_TITLE' | $$ }}

{{ ui.cloudInstallStatus }}

+ {{ 'CANCEL' | $$ }} @@ -223,6 +224,21 @@

{{ 'CLOUD_INSTALL_ERROR_EXISTING_SERVER_TITLE' | $$ }}

+ + + + {{ 'CREATE_A_CLOUD_SERVER' | $$ }} + + +
+
+ +
+

{{ 'CLOUD_INSTALL_CANCEL_TITLE' | $$ }}

+

{{ 'CLOUD_INSTALL_CANCEL_MESSAGE' | $$ }}

+
+
+ diff --git a/src/generic_ui/polymer/cloud-install.ts b/src/generic_ui/polymer/cloud-install.ts index 0e5b8b332c..918e2324ec 100644 --- a/src/generic_ui/polymer/cloud-install.ts +++ b/src/generic_ui/polymer/cloud-install.ts @@ -26,7 +26,8 @@ Polymer({ this.injectBoundHTML( ui.i18nSanitizeHtml(ui.i18n_t('CLOUD_INSTALL_LOGIN_MESSAGE')), this.$.loginMessage); - + + ui.cloudInstallCancelDisabled = false; this.$.getStartedOverlay.open(); }, showDigitalOceanAccountHelpOverlay: function() { @@ -60,6 +61,7 @@ Polymer({ this.$.successOverlay.close(); this.$.failureOverlay.close(); this.$.serverExistsOverlay.close(); + this.$.cancelingOverlay.close(); }, loginTapped: function() { if (!this.$.installingOverlay.opened) { @@ -79,7 +81,7 @@ Polymer({ // TODO: Figure out why e.message is not set if (e === 'Error: server already exists') { this.$.serverExistsOverlay.open(); - } else { + } else if (e !== 'Error: canceled') { this.$.failureOverlay.open(); } }); @@ -87,6 +89,7 @@ Polymer({ removeServerAndInstallAgain: function() { this.closeOverlays(); ui.cloudInstallStatus = ui.i18n_t('REMOVING_UPROXY_CLOUD_STATUS'); + ui.cloudInstallCancelDisabled = true; this.$.installingOverlay.open(); // Destroy uProxy cloud server return ui.cloudUpdate({ @@ -108,6 +111,19 @@ Polymer({ return this.loginTapped(); }); }, + cancelCloudInstall: function() { + this.$.cancelingOverlay.open(); + return ui.cloudUpdate({ + operation: uproxy_core_api.CloudOperationType.CLOUD_DESTROY, + providerName: DEFAULT_PROVIDER + }).then(() => { + this.closeOverlays(); + ui.toastMessage = ui.i18n_t('CLOUD_INSTALL_CANCEL_SUCCESS'); + }).catch((e: Error) => { + this.$.cancelingOverlay.close(); + ui.toastMessage = ui.i18n_t('CLOUD_INSTALL_CANCEL_FAILURE'); + }); + }, select: function(e: Event, d: Object, input: HTMLInputElement) { input.focus(); input.select(); diff --git a/src/generic_ui/scripts/ui.ts b/src/generic_ui/scripts/ui.ts index d851b2ba46..b65fb32c5a 100644 --- a/src/generic_ui/scripts/ui.ts +++ b/src/generic_ui/scripts/ui.ts @@ -132,6 +132,7 @@ export class UserInterface implements ui_constants.UiApi { public cloudInstallStatus :string = ''; public cloudInstallProgress = 0; + public cloudInstallCancelDisabled :boolean = false; /** * UI must be constructed with hooks to Notifications and Core. @@ -307,6 +308,9 @@ export class UserInterface implements ui_constants.UiApi { core.onUpdate(uproxy_core_api.Update.CLOUD_INSTALL_STATUS, (status: string) => { this.cloudInstallStatus = this.i18n_t(status); + // Don't allow user to cancel during last stage of cloud install + // because user may have already accepted cloud invitation + this.cloudInstallCancelDisabled = (status === 'CLOUD_INSTALL_STATUS_CONFIGURING_SSH') ? true : false; }); core.onUpdate(uproxy_core_api.Update.CLOUD_INSTALL_PROGRESS, (progress: number) => {