From dad807d62d25f76c44dae1deddf42df44a700c1a Mon Sep 17 00:00:00 2001 From: Alan Ren Date: Fri, 1 Mar 2019 11:12:57 -0800 Subject: [PATCH] select cluster page and status update for tool when installing (#4251) --- extensions/big-data-cluster/src/interfaces.ts | 3 +- .../create-cluster/createClusterModel.ts | 3 +- .../create-cluster/createClusterWizard.ts | 2 +- .../pages/clusterProfilePage.ts | 6 + .../pages/selectExistingClusterPage.ts | 117 +++++++----------- .../pages/selectTargetClusterTypePage.ts | 98 +++++++++------ .../create-cluster/pages/summaryPage.ts | 8 ++ 7 files changed, 122 insertions(+), 115 deletions(-) diff --git a/extensions/big-data-cluster/src/interfaces.ts b/extensions/big-data-cluster/src/interfaces.ts index e02bb245b7..8831d9efd9 100644 --- a/extensions/big-data-cluster/src/interfaces.ts +++ b/extensions/big-data-cluster/src/interfaces.ts @@ -67,7 +67,8 @@ export interface ToolInfo { export enum ToolInstallationStatus { Installed, NotInstalled, - Installing + Installing, + FailedToInstall } export enum ClusterType { diff --git a/extensions/big-data-cluster/src/wizards/create-cluster/createClusterModel.ts b/extensions/big-data-cluster/src/wizards/create-cluster/createClusterModel.ts index 485ff5cfea..81566d284a 100644 --- a/extensions/big-data-cluster/src/wizards/create-cluster/createClusterModel.ts +++ b/extensions/big-data-cluster/src/wizards/create-cluster/createClusterModel.ts @@ -102,9 +102,10 @@ export class CreateClusterModel implements Scriptable { return promise; } - public installTools(): Thenable { + public installTool(tool: ToolInfo): Thenable { let promise = new Promise(resolve => { setTimeout(() => { + tool.status = ToolInstallationStatus.Installed; this._tmp_tools_installed = true; resolve(); }, 2000); diff --git a/extensions/big-data-cluster/src/wizards/create-cluster/createClusterWizard.ts b/extensions/big-data-cluster/src/wizards/create-cluster/createClusterWizard.ts index 847d9db455..af5c85b848 100644 --- a/extensions/big-data-cluster/src/wizards/create-cluster/createClusterWizard.ts +++ b/extensions/big-data-cluster/src/wizards/create-cluster/createClusterWizard.ts @@ -31,7 +31,7 @@ export class CreateClusterWizard extends WizardBase { wizard); } + public onEnter() { + this.wizard.wizardObject.registerNavigationValidator(() => { + return true; + }); + } + protected initialize(view: sqlops.ModelView): Thenable { let formBuilder = view.modelBuilder.formContainer(); let form = formBuilder.component(); diff --git a/extensions/big-data-cluster/src/wizards/create-cluster/pages/selectExistingClusterPage.ts b/extensions/big-data-cluster/src/wizards/create-cluster/pages/selectExistingClusterPage.ts index e601414529..f86537d652 100644 --- a/extensions/big-data-cluster/src/wizards/create-cluster/pages/selectExistingClusterPage.ts +++ b/extensions/big-data-cluster/src/wizards/create-cluster/pages/selectExistingClusterPage.ts @@ -9,61 +9,31 @@ import * as vscode from 'vscode'; import * as os from 'os'; import { WizardPageBase } from '../../wizardPageBase'; import { CreateClusterWizard } from '../createClusterWizard'; -import { TargetClusterType } from '../../../interfaces'; import { setActiveKubeconfig } from '../../../config/config'; import * as nls from 'vscode-nls'; const localize = nls.loadMessageBundle(); -const ClusterTypeRadioButtonGroupName = 'SelectClusterType'; +const ClusterRadioButtonGroupName = 'cluster'; export class SelectExistingClusterPage extends WizardPageBase { - constructor(wizard: CreateClusterWizard) { - super(localize('bdc-create.selectTargetClusterPageTitle', 'Where do you want to deploy this SQL Server big data cluster?'), - localize('bdc-create.selectTargetClusterPageDescription', 'Select an existing Kubernetes cluster or choose a cluster type you want to deploy'), - wizard); - } - - private existingClusterOption: sqlops.RadioButtonComponent; - private createAksClusterOption: sqlops.RadioButtonComponent; - private pageContainer: sqlops.DivContainer; private existingClusterControl: sqlops.FlexContainer; - private createAksClusterControl: sqlops.FlexContainer; private clusterContextsLabel: sqlops.TextComponent; private errorLoadingClustersLabel: sqlops.TextComponent; private clusterContextContainer: sqlops.DivContainer; - private cards: sqlops.CardComponent[]; + constructor(wizard: CreateClusterWizard) { + super(localize('bdc-create.selectTargetClusterPageTitle', 'Where do you want to deploy this SQL Server big data cluster?'), + localize('bdc-create.selectTargetClusterPageDescription', 'Select the kubeconfig file and then select a cluster context from the list'), + wizard); + } protected initialize(view: sqlops.ModelView): Thenable { - let self = this; - this.wizard.model.targetClusterType = TargetClusterType.ExistingKubernetesCluster; - this.existingClusterOption = this.createTargetTypeRadioButton(view, localize('bdc-create.existingK8sCluster', 'Existing Kubernetes cluster'), true); - this.createAksClusterOption = this.createTargetTypeRadioButton(view, localize('bdc-create.createAksCluster', 'Create new Azure Kubernetes Service cluster')); - - this.existingClusterOption.onDidClick(() => { - self.pageContainer.clearItems(); - self.pageContainer.addItem(self.existingClusterControl); - self.wizard.model.targetClusterType = TargetClusterType.ExistingKubernetesCluster; - }); - - this.createAksClusterOption.onDidClick(() => { - self.pageContainer.clearItems(); - self.pageContainer.addItem(self.createAksClusterControl); - self.wizard.model.targetClusterType = TargetClusterType.NewAksCluster; - }); - - let optionGroup = view.modelBuilder.divContainer().withItems([this.existingClusterOption, this.createAksClusterOption], - { CSSStyles: { 'margin-right': '30px' } }).withLayout({ width: 'auto' }).component(); this.initExistingClusterControl(view); - this.initAksClusterControl(view); - this.pageContainer = view.modelBuilder.divContainer().withItems([this.existingClusterControl]).withLayout({ width: '100%' }).component(); - let container = view.modelBuilder.flexContainer().withItems([optionGroup, this.pageContainer], { flex: '0 0 auto' }).withLayout({ flexFlow: 'row', alignItems: 'left' }).component(); - let formBuilder = view.modelBuilder.formContainer().withFormItems( [ { - component: container, + component: this.existingClusterControl, title: '' } ], @@ -76,15 +46,28 @@ export class SelectExistingClusterPage extends WizardPageBase { + if (e.lastPage > e.newPage) { + this.wizard.wizardObject.message = null; + return true; + } + let clusterSelected = this.wizard.model.selectedCluster !== undefined; + if (!clusterSelected) { + this.wizard.wizardObject.message = { + text: localize('bdc-create.ClusterContextNotSelectedMessage', 'Please select a cluster context.'), + level: sqlops.window.MessageLevel.Error + }; + } + return clusterSelected; + }); } private initExistingClusterControl(view: sqlops.ModelView): void { let self = this; - let sectionDescription = view.modelBuilder.text().withProperties({ value: localize('bdc-create.existingClusterSectionDescription', 'Select the cluster context you want to install the SQL Server big data cluster') }).component(); - let configFileLabel = view.modelBuilder.text().withProperties({ value: localize('bdc-create.kubeConfigFileLabelText', 'KubeConfig File') }).component(); + let configFileLabel = view.modelBuilder.text().withProperties({ value: localize('bdc-create.kubeConfigFileLabelText', 'Kube config file path') }).component(); let configFileInput = view.modelBuilder.inputBox().withProperties({ width: '300px' }).component(); + configFileInput.enabled = false; let browseFileButton = view.modelBuilder.button().withProperties({ label: localize('bdc-browseText', 'Browse'), width: '100px' }).component(); let configFileContainer = view.modelBuilder.flexContainer() .withLayout({ flexFlow: 'row', alignItems: 'baseline' }) @@ -92,7 +75,7 @@ export class SelectExistingClusterPage extends WizardPageBase { let fileUris = await vscode.window.showOpenDialog( @@ -119,45 +102,31 @@ export class SelectExistingClusterPage extends WizardPageBase { + let option = view.modelBuilder.radioButton().withProperties({ + label: cluster.contextName, + checked: cluster.active, + name: ClusterRadioButtonGroupName }).component(); - card.onCardSelectedChanged(() => { - if (card.selected) { - self.cards.forEach(c => { - if (c !== card) { - c.selected = false; - } - }); - self.wizard.model.selectedCluster = cluster; - } + + if (cluster.active) { + self.wizard.model.selectedCluster = cluster; + self.wizard.wizardObject.message = null; + } + + option.onDidClick(() => { + self.wizard.model.selectedCluster = cluster; + self.wizard.wizardObject.message = null; }); - self.cards.push(card); - } + return option; + }); self.clusterContextContainer.addItem(self.clusterContextsLabel); - self.clusterContextContainer.addItems(self.cards); + self.clusterContextContainer.addItems(options); } else { self.clusterContextContainer.addItem(this.errorLoadingClustersLabel); } }); } - - private initAksClusterControl(view: sqlops.ModelView): void { - let placeholder = view.modelBuilder.text().withProperties({ value: 'AKS cluster place holder' }).component(); - this.createAksClusterControl = view.modelBuilder.divContainer().withItems([placeholder]).component(); - } -} +} \ No newline at end of file diff --git a/extensions/big-data-cluster/src/wizards/create-cluster/pages/selectTargetClusterTypePage.ts b/extensions/big-data-cluster/src/wizards/create-cluster/pages/selectTargetClusterTypePage.ts index 7c564c7075..f96309ebb4 100644 --- a/extensions/big-data-cluster/src/wizards/create-cluster/pages/selectTargetClusterTypePage.ts +++ b/extensions/big-data-cluster/src/wizards/create-cluster/pages/selectTargetClusterTypePage.ts @@ -6,7 +6,7 @@ import * as sqlops from 'sqlops'; import { WizardPageBase } from '../../wizardPageBase'; -import { TargetClusterTypeInfo, ToolInstallationStatus } from '../../../interfaces'; +import { TargetClusterTypeInfo, ToolInstallationStatus, ToolInfo } from '../../../interfaces'; import * as nls from 'vscode-nls'; import { CreateClusterWizard } from '../createClusterWizard'; @@ -25,6 +25,7 @@ export class SelectTargetClusterTypePage extends WizardPageBase { - this.toolsLoadingWrapper.loading = true; + this.installToolsButton.onClick(async () => { this.wizard.wizardObject.message = null; this.installToolsButton.label = InstallingButtonText; this.installToolsButton.enabled = false; - this.wizard.model.installTools().then(() => { - this.installToolsButton.label = InstallToolsButtonText; - this.installToolsButton.enabled = true; - return this.updateRequiredToolStatus(); - }); + this.refreshToolsButton.enabled = false; + if (this.requiredTools) { + for (let i = 0; i < this.requiredTools.length; i++) { + let tool = this.requiredTools[i]; + if (tool.status === ToolInstallationStatus.NotInstalled) { + tool.status = ToolInstallationStatus.Installing; + this.updateToolStatusTable(); + await this.wizard.model.installTool(tool); + } + } + } + + this.installToolsButton.label = InstallToolsButtonText; + this.updateRequiredToolStatus(); }); this.wizard.addButton(this.installToolsButton); @@ -51,31 +60,11 @@ export class SelectTargetClusterTypePage extends WizardPageBase { - if (this.isLoading) { - let messageText = localize('bdc-create.ToolsRefreshingText', 'Please wait while the required tools status is being refreshed.'); - let messageLevel = sqlops.window.MessageLevel.Information; - this.wizard.wizardObject.message = { - level: messageLevel, - text: messageText - }; - return false; - } - if (!this.isValid) { - let messageText = this.cards.filter(c => { return c.selected; }).length === 0 ? - localize('bdc-create.TargetClusterTypeNotSelectedText', 'Please select a target cluster type') : - localize('bdc-create.MissingToolsText', 'Please install the missing tools'); - this.wizard.wizardObject.message = { - level: sqlops.window.MessageLevel.Error, - text: messageText - }; - } - return this.isValid; - }); } protected initialize(view: sqlops.ModelView): Thenable { let self = this; + self.registerNavigationValidator(); return self.wizard.model.getAllTargetClusterTypeInfo().then((clusterTypes) => { self.cards = []; @@ -129,6 +118,31 @@ export class SelectTargetClusterTypePage extends WizardPageBase { + if (this.isLoading) { + let messageText = localize('bdc-create.ToolsRefreshingText', 'Please wait while the required tools status is being refreshed.'); + let messageLevel = sqlops.window.MessageLevel.Information; + this.wizard.wizardObject.message = { + level: messageLevel, + text: messageText + }; + return false; + } + if (!this.isValid) { + let messageText = this.cards.filter(c => { return c.selected; }).length === 0 ? + localize('bdc-create.TargetClusterTypeNotSelectedText', 'Please select a target cluster type.') : + localize('bdc-create.MissingToolsText', 'Please install the required tools.'); + this.wizard.wizardObject.message = { + level: sqlops.window.MessageLevel.Error, + text: messageText + }; + } + return this.isValid; + }); } public onLeave(): void { @@ -136,7 +150,6 @@ export class SelectTargetClusterTypePage extends WizardPageBase({ @@ -165,7 +178,7 @@ export class SelectTargetClusterTypePage extends WizardPageBase { return c !== card && c.selected }).length === 0) { + if (self.cards.filter(c => { return c !== card && c.selected; }).length === 0) { card.selected = true; } } @@ -181,29 +194,38 @@ export class SelectTargetClusterTypePage extends WizardPageBase { + this.requiredTools = tools; this.isLoading = false; this.toolsLoadingWrapper.loading = false; this.refreshToolsButton.enabled = true; this.installToolsButton.enabled = tools.filter(tool => tool.status !== ToolInstallationStatus.Installed).length !== 0; this.isValid = !this.installToolsButton.enabled; this.wizard.wizardObject.message = null; - let tableData = tools.map(tool => { - return [tool.name, tool.description, this.getStatusText(tool.status)]; - }); - this.toolsTable.data = tableData; + this.updateToolStatusTable(); }); } private getStatusText(status: ToolInstallationStatus): string { switch (status) { case ToolInstallationStatus.Installed: - return localize('bdc-create.InstalledText', 'Installed'); + return '✔️ ' + localize('bdc-create.InstalledText', 'Installed'); case ToolInstallationStatus.NotInstalled: - return localize('bdc-create.NotInstalledText', 'Not Installed'); + return '❌ ' + localize('bdc-create.NotInstalledText', 'Not Installed'); case ToolInstallationStatus.Installing: - return localize('bdc-create.InstallingText', 'Installing'); + return '⌛ ' + localize('bdc-create.InstallingText', 'Installing...'); + case ToolInstallationStatus.FailedToInstall: + return '❌ ' + localize('bdc-create.FailedToInstallText', 'Install Failed'); default: return 'unknown status'; } } + + private updateToolStatusTable(): void { + if (this.requiredTools) { + let tableData = this.requiredTools.map(tool => { + return [tool.name, tool.description, this.getStatusText(tool.status)]; + }); + this.toolsTable.data = tableData; + } + } } diff --git a/extensions/big-data-cluster/src/wizards/create-cluster/pages/summaryPage.ts b/extensions/big-data-cluster/src/wizards/create-cluster/pages/summaryPage.ts index ff1f04aa70..02043d3bfd 100644 --- a/extensions/big-data-cluster/src/wizards/create-cluster/pages/summaryPage.ts +++ b/extensions/big-data-cluster/src/wizards/create-cluster/pages/summaryPage.ts @@ -21,4 +21,12 @@ export class SummaryPage extends WizardPageBase { let form = formBuilder.component(); return view.initializeModel(form); } + + public onEnter(): void { + this.wizard.wizardObject.generateScriptButton.hidden = false; + } + + public onLeave(): void { + this.wizard.wizardObject.generateScriptButton.hidden = true; + } }