wizard for deploying bdc (#7183)

* wip

* wip2

* wip eod 820

* wip 822

* text component improvements and misc changes

* aria-label

* targetClusterPage wip

* target cluster page

* target cluster page

* wip 827

* wip deployment profile page

* profile page

* service settings page

* wip 0903

* 0909 wip

* 0910

* 0911

* sql instance and working directory

* notebooks

* docker version on windows

* EULA env var

* 917 updates

* address comments

* use async file access

* fix the summary page display issue for ad auth

* add save json file buttons

* use promise for private methds

* review feedbacks

* refactor

* pass json to notebooks

* fix no tool scenario

* bypass tool check if installed

* update hint text

* update notebooks

* workaround azdata first time use

* comments

* accept eula and some text update

* fix the error in package.json

* promise instead of thenable

* comments

* fix typo
This commit is contained in:
Alan Ren
2019-09-25 10:04:13 -07:00
committed by GitHub
parent 6a6048d40f
commit a0e31fc723
51 changed files with 4137 additions and 855 deletions

View File

@@ -0,0 +1,60 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
export const DeploymentProfile_VariableName = 'AZDATA_NB_VAR_BDC_DEPLOYMENT_PROFILE';
export const ClusterName_VariableName = 'AZDATA_NB_VAR_BDC_CLUSTER_NAME';
export const AdminUserName_VariableName = 'AZDATA_NB_VAR_BDC_CONTROLLER_USERNAME';
export const AdminPassword_VariableName = 'AZDATA_NB_VAR_BDC_ADMIN_PASSWORD';
export const AuthenticationMode_VariableName = 'AZDATA_NB_VAR_BDC_AUTHENTICATION_MODE';
export const DistinguishedName_VariableName = 'AZDATA_NB_VAR_BDC_AD_DN';
export const AdminPrincipals_VariableName = 'AZDATA_NB_VAR_BDC_AD_ADMIN_PRINCIPALS';
export const UserPrincipals_VariableName = 'AZDATA_NB_VAR_BDC_AD_USER_PRINCIPALS';
export const UpstreamIPAddresses_VariableName = 'AZDATA_NB_VAR_BDC_AD_UPSTREAM_IPADDRESSES';
export const DnsName_VariableName = 'AZDATA_NB_VAR_BDC_AD_DNS_NAME';
export const Realm_VariableName = 'AZDATA_NB_VAR_BDC_AD_REALM';
export const AppOwnerPrincipals_VariableName = 'AZDATA_NB_VAR_AD_BDC_APP_OWNER_PRINCIPALS';
export const AppReaderPrincipals_VariableName = 'AZDATA_NB_VAR_AD_BDC_APP_READER_PRINCIPALS';
export const SubscriptionId_VariableName = 'AZDATA_NB_VAR_BDC_AZURE_SUBSCRIPTION';
export const ResourceGroup_VariableName = 'AZDATA_NB_VAR_BDC_RESOURCEGROUP_NAME';
export const Region_VariableName = 'AZDATA_NB_VAR_BDC_AZURE_REGION';
export const AksName_VariableName = 'AZDATA_NB_VAR_BDC_AKS_NAME';
export const VMSize_VariableName = 'AZDATA_NB_VAR_BDC_AZURE_VM_SIZE';
export const VMCount_VariableName = 'AZDATA_NB_VAR_BDC_VM_COUNT';
export const KubeConfigPath_VariableName = 'AZDATA_NB_VAR_BDC_KUBECONFIG_PATH';
export const ClusterContext_VariableName = 'AZDATA_NB_VAR_BDC_CLUSTER_CONTEXT';
export const SQLServerScale_VariableName = 'AZDATA_NB_VAR_BDC_SQLSERVER_SCALE';
export const HDFSPoolScale_VariableName = 'AZDATA_NB_VAR_BDC_HDFSPOOL_SCALE';
export const HDFSNameNodeScale_VariableName = 'AZDATA_NB_VAR_BDC_NAMENODE_SCALE';
export const ZooKeeperScale_VariableName = 'AZDATA_NB_VAR_BDC_ZOOKEEPER_SCALE';
export const SparkHeadScale_VariableName = 'AZDATA_NB_VAR_BDC_SPARKHEAD_SCALE';
export const IncludeSpark_VariableName = 'AZDATA_NB_VAR_BDC_INCLUDESPARK';
export const ComputePoolScale_VariableName = 'AZDATA_NB_VAR_BDC_COMPUTEPOOL_SCALE';
export const DataPoolScale_VariableName = 'AZDATA_NB_VAR_BDC_DATAPOOL_SCALE';
export const SparkPoolScale_VariableName = 'AZDATA_NB_VAR_BDC_SPARKPOOL_SCALE';
export const ControllerDataStorageClassName_VariableName = 'AZDATA_NB_VAR_BDC_CONTROLLER_DATA_STORAGE_CLASS';
export const ControllerDataStorageSize_VariableName = 'AZDATA_NB_VAR_BDC_CONTROLLER_DATA_STORAGE_SIZE';
export const ControllerLogsStorageClassName_VariableName = 'AZDATA_NB_VAR_BDC_CONTROLLER_LOGS_STORAGE_CLASS';
export const ControllerLogsStorageSize_VariableName = 'AZDATA_NB_VAR_BDC_CONTROLLER_LOGS_STORAGE_SIZE';
export const DataPoolDataStorageClassName_VariableName = 'AZDATA_NB_VAR_BDC_DATA_DATA_STORAGE_CLASS';
export const DataPoolDataStorageSize_VariableName = 'AZDATA_NB_VAR_BDC_DATA_DATA_STORAGE_SIZE';
export const DataPoolLogsStorageClassName_VariableName = 'AZDATA_NB_VAR_BDC_DATA_LOGS_STORAGE_CLASS';
export const DataPoolLogsStorageSize_VariableName = 'AZDATA_NB_VAR_BDC_DATA_LOGS_STORAGE_SIZE';
export const HDFSDataStorageClassName_VariableName = 'AZDATA_NB_VAR_BDC_HDFS_DATA_STORAGE_CLASS';
export const HDFSDataStorageSize_VariableName = 'AZDATA_NB_VAR_BDC_HDFS_DATA_STORAGE_SIZE';
export const HDFSLogsStorageClassName_VariableName = 'AZDATA_NB_VAR_BDC_HDFS_LOGS_STORAGE_CLASS';
export const HDFSLogsStorageSize_VariableName = 'AZDATA_NB_VAR_BDC_HDFS_LOGS_STORAGE_SIZE';
export const SQLServerDataStorageClassName_VariableName = 'AZDATA_NB_VAR_BDC_SQL_DATA_STORAGE_CLASS';
export const SQLServerDataStorageSize_VariableName = 'AZDATA_NB_VAR_BDC_SQL_DATA_STORAGE_SIZE';
export const SQLServerLogsStorageClassName_VariableName = 'AZDATA_NB_VAR_BDC_SQL_LOGS_STORAGE_CLASS';
export const SQLServerLogsStorageSize_VariableName = 'AZDATA_NB_VAR_BDC_SQL_LOGS_STORAGE_SIZE';
export const ControllerDNSName_VariableName = 'AZDATA_NB_VAR_BDC_CONTROLLER_DNS';
export const ControllerPort_VariableName = 'AZDATA_NB_VAR_BDC_CONTROLLER_PORT';
export const SQLServerDNSName_VariableName = 'AZDATA_NB_VAR_BDC_SQL_DNS';
export const SQLServerPort_VariableName = 'AZDATA_NB_VAR_BDC_SQL_PORT';
export const GatewayDNSName_VariableName = 'AZDATA_NB_VAR_BDC_GATEWAY_DNS';
export const GateWayPort_VariableName = 'AZDATA_NB_VAR_BDC_GATEWAY_PORT';
export const ReadableSecondaryDNSName_VariableName = 'AZDATA_NB_VAR_BDC_READABLE_SECONDARY_DNS';
export const ReadableSecondaryPort_VariableName = 'AZDATA_NB_VAR_BDC_READABLE_SECONDARY_PORT';
export const EnableHADR_VariableName = 'AZDATA_NB_VAR_BDC_ENABLE_HADR';

View File

@@ -0,0 +1,115 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import * as azdata from 'azdata';
import * as vscode from 'vscode';
import { SummaryPage } from './pages/summaryPage';
import { WizardBase } from '../wizardBase';
import * as nls from 'vscode-nls';
import { WizardInfo, BdcDeploymentType } from '../../interfaces';
import { WizardPageBase } from '../wizardPageBase';
import { AzureSettingsPage } from './pages/azureSettingsPage';
import { ClusterSettingsPage } from './pages/clusterSettingsPage';
import { ServiceSettingsPage } from './pages/serviceSettingsPage';
import { TargetClusterContextPage } from './pages/targetClusterPage';
import { IKubeService } from '../../services/kubeService';
import { IAzdataService } from '../../services/azdataService';
import { DeploymentProfilePage } from './pages/deploymentProfilePage';
import { INotebookService } from '../../services/notebookService';
import { DeployClusterWizardModel } from './deployClusterWizardModel';
import * as VariableNames from './constants';
const localize = nls.loadMessageBundle();
export class DeployClusterWizard extends WizardBase<DeployClusterWizard, DeployClusterWizardModel> {
public get kubeService(): IKubeService {
return this._kubeService;
}
public get azdataService(): IAzdataService {
return this._azdataService;
}
public get notebookService(): INotebookService {
return this._notebookService;
}
constructor(private wizardInfo: WizardInfo, private _kubeService: IKubeService, private _azdataService: IAzdataService, private _notebookService: INotebookService) {
super(DeployClusterWizard.getTitle(wizardInfo.type), new DeployClusterWizardModel(wizardInfo.type));
}
public get deploymentType(): BdcDeploymentType {
return this.wizardInfo.type;
}
protected initialize(): void {
this.setPages(this.getPages());
this.wizardObject.generateScriptButton.hidden = true;
this.wizardObject.doneButton.label = localize('deployCluster.openNotebook', 'Open Notebook');
}
protected onCancel(): void {
}
protected onOk(): void {
process.env[VariableNames.AdminPassword_VariableName] = this.model.getStringValue(VariableNames.AdminPassword_VariableName);
this.notebookService.launchNotebook(this.wizardInfo.notebook).then((notebook: azdata.nb.NotebookEditor) => {
notebook.edit((editBuilder: azdata.nb.NotebookEditorEdit) => {
editBuilder.insertCell({
cell_type: 'code',
source: this.model.getCodeCellContentForNotebook()
}, 7);
});
}, (error) => {
vscode.window.showErrorMessage(error);
});
}
private getPages(): WizardPageBase<DeployClusterWizard>[] {
const pages: WizardPageBase<DeployClusterWizard>[] = [];
switch (this.deploymentType) {
case BdcDeploymentType.NewAKS:
pages.push(
new DeploymentProfilePage(this),
new AzureSettingsPage(this),
new ClusterSettingsPage(this),
new ServiceSettingsPage(this),
new SummaryPage(this));
break;
case BdcDeploymentType.ExistingAKS:
pages.push(
new DeploymentProfilePage(this),
new TargetClusterContextPage(this),
new ClusterSettingsPage(this),
new ServiceSettingsPage(this),
new SummaryPage(this));
break;
case BdcDeploymentType.ExistingKubeAdm:
pages.push(
new DeploymentProfilePage(this),
new TargetClusterContextPage(this),
new ClusterSettingsPage(this),
new ServiceSettingsPage(this),
new SummaryPage(this));
break;
default:
throw new Error(`Unknown deployment type: ${this.deploymentType}`);
}
return pages;
}
static getTitle(type: BdcDeploymentType): string {
switch (type) {
case BdcDeploymentType.NewAKS:
return localize('deployCluster.NewAKSWizardTitle', "Deploy SQL Server 2019 Big Data Cluster on a new AKS cluster");
case BdcDeploymentType.ExistingAKS:
return localize('deployCluster.ExistingAKSWizardTitle', "Deploy SQL Server 2019 Big Data Cluster on an existing AKS cluster");
case BdcDeploymentType.ExistingKubeAdm:
return localize('deployCluster.ExistingKubeAdm', "Deploy SQL Server 2019 Big Data Cluster on an existing kubeadm cluster");
default:
throw new Error(`Unknown deployment type: ${type}`);
}
}
}

View File

@@ -0,0 +1,152 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { Model } from '../model';
import * as VariableNames from './constants';
import { BigDataClusterDeploymentProfile, DataResource, SqlServerMasterResource, HdfsResource } from '../../services/bigDataClusterDeploymentProfile';
import { BdcDeploymentType } from '../../interfaces';
import { EOL } from 'os';
export class DeployClusterWizardModel extends Model {
constructor(public deploymentTarget: BdcDeploymentType) {
super();
}
public adAuthSupported: boolean = false;
public get hadrEnabled(): boolean {
return this.getBooleanValue(VariableNames.EnableHADR_VariableName);
}
public set hadrEnabled(value: boolean) {
this.setPropertyValue(VariableNames.EnableHADR_VariableName, value);
}
public get authenticationMode(): string | undefined {
return this.getStringValue(VariableNames.AuthenticationMode_VariableName);
}
public set authenticationMode(value: string | undefined) {
this.setPropertyValue(VariableNames.AuthenticationMode_VariableName, value);
}
public getStorageSettingValue(propertyName: string, defaultValuePropertyName: string): string | undefined {
const value = this.getStringValue(propertyName);
return (value === undefined || value === '') ? this.getStringValue(defaultValuePropertyName) : value;
}
private setStorageSettingValue(propertyName: string, defaultValuePropertyName: string): void {
const value = this.getStringValue(propertyName);
if (value === undefined || value === '') {
this.setPropertyValue(propertyName, this.getStringValue(defaultValuePropertyName));
}
}
private setStorageSettingValues(): void {
this.setStorageSettingValue(VariableNames.DataPoolDataStorageClassName_VariableName, VariableNames.ControllerDataStorageClassName_VariableName);
this.setStorageSettingValue(VariableNames.DataPoolDataStorageSize_VariableName, VariableNames.ControllerDataStorageSize_VariableName);
this.setStorageSettingValue(VariableNames.DataPoolLogsStorageClassName_VariableName, VariableNames.ControllerLogsStorageClassName_VariableName);
this.setStorageSettingValue(VariableNames.DataPoolLogsStorageSize_VariableName, VariableNames.ControllerLogsStorageSize_VariableName);
this.setStorageSettingValue(VariableNames.HDFSDataStorageClassName_VariableName, VariableNames.ControllerDataStorageClassName_VariableName);
this.setStorageSettingValue(VariableNames.HDFSDataStorageSize_VariableName, VariableNames.ControllerDataStorageSize_VariableName);
this.setStorageSettingValue(VariableNames.HDFSLogsStorageClassName_VariableName, VariableNames.ControllerLogsStorageClassName_VariableName);
this.setStorageSettingValue(VariableNames.HDFSLogsStorageSize_VariableName, VariableNames.ControllerLogsStorageSize_VariableName);
this.setStorageSettingValue(VariableNames.SQLServerDataStorageClassName_VariableName, VariableNames.ControllerDataStorageClassName_VariableName);
this.setStorageSettingValue(VariableNames.SQLServerDataStorageSize_VariableName, VariableNames.ControllerDataStorageSize_VariableName);
this.setStorageSettingValue(VariableNames.SQLServerLogsStorageClassName_VariableName, VariableNames.ControllerLogsStorageClassName_VariableName);
this.setStorageSettingValue(VariableNames.SQLServerLogsStorageSize_VariableName, VariableNames.ControllerLogsStorageSize_VariableName);
}
public setEnvironmentVariables(): void {
this.setStorageSettingValues();
}
public selectedProfile: BigDataClusterDeploymentProfile | undefined;
public createTargetProfile(): BigDataClusterDeploymentProfile {
// create a copy of the source files to avoid changing the source profile values
const sourceBdcJson = Object.assign({}, this.selectedProfile!.bdcConfig);
const sourceControlJson = Object.assign({}, this.selectedProfile!.controlConfig);
const targetDeploymentProfile = new BigDataClusterDeploymentProfile('', sourceBdcJson, sourceControlJson);
// cluster name
targetDeploymentProfile.clusterName = this.getStringValue(VariableNames.ClusterName_VariableName)!;
// storage settings
targetDeploymentProfile.controllerDataStorageClass = this.getStringValue(VariableNames.ControllerDataStorageClassName_VariableName)!;
targetDeploymentProfile.controllerDataStorageSize = this.getIntegerValue(VariableNames.ControllerDataStorageSize_VariableName)!;
targetDeploymentProfile.controllerLogsStorageClass = this.getStringValue(VariableNames.ControllerLogsStorageClassName_VariableName)!;
targetDeploymentProfile.controllerLogsStorageSize = this.getIntegerValue(VariableNames.ControllerLogsStorageSize_VariableName)!;
targetDeploymentProfile.setResourceStorage(DataResource,
this.getStorageSettingValue(VariableNames.DataPoolDataStorageClassName_VariableName, VariableNames.ControllerDataStorageClassName_VariableName)!,
Number.parseInt(this.getStorageSettingValue(VariableNames.DataPoolDataStorageSize_VariableName, VariableNames.ControllerDataStorageSize_VariableName)!),
this.getStorageSettingValue(VariableNames.DataPoolLogsStorageClassName_VariableName, VariableNames.ControllerLogsStorageClassName_VariableName)!,
Number.parseInt(this.getStorageSettingValue(VariableNames.DataPoolLogsStorageSize_VariableName, VariableNames.ControllerLogsStorageSize_VariableName)!)
);
targetDeploymentProfile.setResourceStorage(SqlServerMasterResource,
this.getStorageSettingValue(VariableNames.SQLServerDNSName_VariableName, VariableNames.ControllerDataStorageClassName_VariableName)!,
Number.parseInt(this.getStorageSettingValue(VariableNames.SQLServerDataStorageSize_VariableName, VariableNames.ControllerDataStorageSize_VariableName)!),
this.getStorageSettingValue(VariableNames.SQLServerLogsStorageClassName_VariableName, VariableNames.ControllerLogsStorageClassName_VariableName)!,
Number.parseInt(this.getStorageSettingValue(VariableNames.SQLServerLogsStorageSize_VariableName, VariableNames.ControllerLogsStorageSize_VariableName)!)
);
targetDeploymentProfile.setResourceStorage(HdfsResource,
this.getStorageSettingValue(VariableNames.HDFSDataStorageClassName_VariableName, VariableNames.ControllerDataStorageClassName_VariableName)!,
Number.parseInt(this.getStorageSettingValue(VariableNames.HDFSDataStorageSize_VariableName, VariableNames.ControllerDataStorageSize_VariableName)!),
this.getStorageSettingValue(VariableNames.HDFSLogsStorageClassName_VariableName, VariableNames.ControllerLogsStorageClassName_VariableName)!,
Number.parseInt(this.getStorageSettingValue(VariableNames.HDFSLogsStorageSize_VariableName, VariableNames.ControllerLogsStorageSize_VariableName)!)
);
// scale settings
targetDeploymentProfile.dataReplicas = this.getIntegerValue(VariableNames.DataPoolScale_VariableName);
targetDeploymentProfile.computeReplicas = this.getIntegerValue(VariableNames.ComputePoolScale_VariableName);
targetDeploymentProfile.hdfsReplicas = this.getIntegerValue(VariableNames.HDFSPoolScale_VariableName);
targetDeploymentProfile.sqlServerReplicas = this.getIntegerValue(VariableNames.SQLServerScale_VariableName);
targetDeploymentProfile.hdfsNameNodeReplicas = this.getIntegerValue(VariableNames.HDFSNameNodeScale_VariableName);
targetDeploymentProfile.sparkHeadReplicas = this.getIntegerValue(VariableNames.SparkHeadScale_VariableName);
targetDeploymentProfile.zooKeeperReplicas = this.getIntegerValue(VariableNames.ZooKeeperScale_VariableName);
const sparkScale = this.getIntegerValue(VariableNames.SparkPoolScale_VariableName);
if (sparkScale > 0) {
targetDeploymentProfile.addSparkResource(sparkScale);
}
targetDeploymentProfile.includeSpark = this.getBooleanValue(VariableNames.IncludeSpark_VariableName);
targetDeploymentProfile.hadrEnabled = this.getBooleanValue(VariableNames.EnableHADR_VariableName);
// port settings
targetDeploymentProfile.gatewayPort = this.getIntegerValue(VariableNames.GateWayPort_VariableName);
targetDeploymentProfile.sqlServerPort = this.getIntegerValue(VariableNames.SQLServerPort_VariableName);
targetDeploymentProfile.controllerPort = this.getIntegerValue(VariableNames.ControllerPort_VariableName);
targetDeploymentProfile.sqlServerReadableSecondaryPort = this.getIntegerValue(VariableNames.ReadableSecondaryPort_VariableName);
return targetDeploymentProfile;
}
public getCodeCellContentForNotebook(): string {
const profile = this.createTargetProfile();
const statements: string[] = [];
if (this.deploymentTarget === BdcDeploymentType.NewAKS) {
statements.push(`azure_subscription_id = '${this.getStringValue(VariableNames.SubscriptionId_VariableName, '')}'`);
statements.push(`azure_region = '${this.getStringValue(VariableNames.Region_VariableName)}'`);
statements.push(`azure_resource_group = '${this.getStringValue(VariableNames.ResourceGroup_VariableName)}'`);
statements.push(`azure_vm_size = '${this.getStringValue(VariableNames.VMSize_VariableName)}'`);
statements.push(`azure_vm_count = '${this.getStringValue(VariableNames.VMCount_VariableName)}'`);
statements.push(`aks_cluster_name = '${this.getStringValue(VariableNames.AksName_VariableName)}'`);
} else if (this.deploymentTarget === BdcDeploymentType.ExistingAKS || this.deploymentTarget === BdcDeploymentType.ExistingKubeAdm) {
statements.push(`mssql_kube_config_path = '${this.getStringValue(VariableNames.KubeConfigPath_VariableName)}'`);
statements.push(`mssql_cluster_context = '${this.getStringValue(VariableNames.ClusterContext_VariableName)}'`);
statements.push('os.environ["KUBECONFIG"] = mssql_kube_config_path');
}
statements.push(`mssql_cluster_name = '${this.getStringValue(VariableNames.ClusterName_VariableName)}'`);
statements.push(`mssql_controller_username = '${this.getStringValue(VariableNames.AdminUserName_VariableName)}'`);
statements.push(`bdc_json = '${profile.getBdcJson(false)}'`);
statements.push(`control_json = '${profile.getControlJson(false)}'`);
statements.push(`print('Variables have been set successfully.')`);
return statements.join(EOL);
}
}
export enum AuthenticationMode {
ActiveDirectory = 'ad',
Basic = 'basic'
}

View File

@@ -0,0 +1,107 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import * as azdata from 'azdata';
import * as vscode from 'vscode';
import * as nls from 'vscode-nls';
import { DeployClusterWizard } from '../deployClusterWizard';
import { SectionInfo, FieldType, LabelPosition } from '../../../interfaces';
import { WizardPageBase } from '../../wizardPageBase';
import { createSection, InputComponents, setModelValues, Validator } from '../../modelViewUtils';
import { SubscriptionId_VariableName, ResourceGroup_VariableName, Region_VariableName, AksName_VariableName, VMCount_VariableName, VMSize_VariableName } from '../constants';
const localize = nls.loadMessageBundle();
export class AzureSettingsPage extends WizardPageBase<DeployClusterWizard> {
private inputComponents: InputComponents = {};
constructor(wizard: DeployClusterWizard) {
super(localize('deployCluster.AzureSettingsPageTitle', "Azure settings"),
localize('deployCluster.AzureSettingsPageDescription', "Configure the settings to create an Azure Kubernetes Service cluster"), wizard);
}
public initialize(): void {
const self = this;
const azureSection: SectionInfo = {
title: '',
labelPosition: LabelPosition.Left,
fields: [
{
type: FieldType.Text,
label: localize('deployCluster.SubscriptionField', "Subscription id"),
required: false,
variableName: SubscriptionId_VariableName,
placeHolder: localize('deployCluster.SubscriptionPlaceholder', "Use my default Azure subscription"),
description: localize('deployCluster.SubscriptionDescription', "The default subscription will be used if you leave this field blank.")
}, {
type: FieldType.DateTimeText,
label: localize('deployCluster.ResourceGroupName', "New resource group name"),
required: true,
variableName: ResourceGroup_VariableName,
defaultValue: 'mssql-'
}, {
type: FieldType.Text,
label: localize('deployCluster.Region', "Region"),
required: true,
variableName: Region_VariableName,
defaultValue: 'eastus'
}, {
type: FieldType.DateTimeText,
label: localize('deployCluster.AksName', "AKS cluster name"),
required: true,
variableName: AksName_VariableName,
defaultValue: 'mssql-',
}, {
type: FieldType.Number,
label: localize('deployCluster.VMCount', "VM count"),
required: true,
variableName: VMCount_VariableName,
defaultValue: '5',
min: 1,
max: 999
}, {
type: FieldType.Text,
label: localize('deployCluster.VMSize', "VM size"),
required: true,
variableName: VMSize_VariableName,
defaultValue: 'Standard_E4s_v3'
}
]
};
this.pageObject.registerContent((view: azdata.ModelView) => {
const azureGroup = createSection({
sectionInfo: azureSection,
view: view,
onNewDisposableCreated: (disposable: vscode.Disposable): void => {
self.wizard.registerDisposable(disposable);
},
onNewInputComponentCreated: (name: string, component: azdata.InputBoxComponent | azdata.DropDownComponent | azdata.CheckBoxComponent): void => {
self.inputComponents[name] = component;
},
onNewValidatorCreated: (validator: Validator): void => {
self.validators.push(validator);
},
container: this.wizard.wizardObject
});
const formBuilder = view.modelBuilder.formContainer().withFormItems(
[{
title: '',
component: azureGroup
}],
{
horizontal: false,
componentWidth: '100%'
}
);
const form = formBuilder.withLayout({ width: '100%' }).component();
return view.initializeModel(form);
});
}
public onLeave(): void {
setModelValues(this.inputComponents, this.wizard.model);
}
}

View File

@@ -0,0 +1,251 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import * as azdata from 'azdata';
import * as vscode from 'vscode';
import * as nls from 'vscode-nls';
import { DeployClusterWizard } from '../deployClusterWizard';
import { SectionInfo, FieldType, LabelPosition } from '../../../interfaces';
import { createSection, InputComponents, setModelValues, Validator, isInputBoxEmpty, getInputBoxComponent, isValidSQLPassword, getInvalidSQLPasswordMessage, getPasswordMismatchMessage, MissingRequiredInformationErrorMessage } from '../../modelViewUtils';
import { WizardPageBase } from '../../wizardPageBase';
import * as VariableNames from '../constants';
import { EOL } from 'os';
import { AuthenticationMode } from '../deployClusterWizardModel';
const localize = nls.loadMessageBundle();
const ConfirmPasswordName = 'ConfirmPassword';
export class ClusterSettingsPage extends WizardPageBase<DeployClusterWizard> {
private inputComponents: InputComponents = {};
constructor(wizard: DeployClusterWizard) {
super(localize('deployCluster.ClusterSettingsPageTitle', "Cluster settings"),
localize('deployCluster.ClusterSettingsPageDescription', "Configure the SQL Server Big Data Cluster settings"), wizard);
}
public initialize(): void {
const self = this;
const basicSection: SectionInfo = {
labelPosition: LabelPosition.Left,
title: '',
fields: [
{
type: FieldType.Text,
label: localize('deployCluster.ClusterName', "Cluster name"),
required: true,
variableName: VariableNames.ClusterName_VariableName,
defaultValue: 'mssql-cluster',
useCustomValidator: true
}, {
type: FieldType.Text,
label: localize('deployCluster.ControllerUsername', "Controller username"),
required: true,
variableName: VariableNames.AdminUserName_VariableName,
defaultValue: 'admin',
useCustomValidator: true
}, {
type: FieldType.Password,
label: localize('deployCluster.AdminPassword', "Password"),
required: true,
variableName: VariableNames.AdminPassword_VariableName,
defaultValue: '',
useCustomValidator: true,
description: localize('deployCluster.AdminPasswordDescription', "You can also use this password to access SQL Server and gateway.")
}, {
type: FieldType.Password,
label: localize('deployCluster.ConfirmPassword', "Confirm password"),
required: true,
variableName: ConfirmPasswordName,
defaultValue: '',
useCustomValidator: true,
}, {
type: FieldType.Options,
label: localize('deployCluster.AuthenticationMode', "Authentication mode"),
required: true,
variableName: VariableNames.AuthenticationMode_VariableName,
defaultValue: AuthenticationMode.Basic,
options: [
{
name: AuthenticationMode.Basic,
displayName: localize('deployCluster.AuthenticationMode.Basic', "Basic")
},
{
name: AuthenticationMode.ActiveDirectory,
displayName: localize('deployCluster.AuthenticationMode.ActiveDirectory', "Active Directory")
}
]
}
]
};
const activeDirectorySection: SectionInfo = {
labelPosition: LabelPosition.Left,
title: localize('deployCluster.ActiveDirectorySettings', "Active Directory settings"),
fields: [
{
type: FieldType.Text,
label: localize('deployCluster.DistinguishedName', "Distinguished name"),
required: true,
variableName: VariableNames.DistinguishedName_VariableName,
useCustomValidator: true
}, {
type: FieldType.Text,
label: localize('deployCluster.AdminPrincipals', "Admin principals"),
required: true,
variableName: VariableNames.AdminPrincipals_VariableName,
useCustomValidator: true
}, {
type: FieldType.Text,
label: localize('deployCluster.UserPrincipals', "User principals"),
required: true,
variableName: VariableNames.UserPrincipals_VariableName,
useCustomValidator: true
}, {
type: FieldType.Text,
label: localize('deployCluster.UpstreamIPAddresses', "Upstream IP Addresses"),
required: true,
variableName: VariableNames.UpstreamIPAddresses_VariableName,
useCustomValidator: true
}, {
type: FieldType.Text,
label: localize('deployCluster.DNSName', "DNS name"),
required: true,
variableName: VariableNames.DnsName_VariableName,
useCustomValidator: true
}, {
type: FieldType.Text,
label: localize('deployCluster.Realm', "Realm"),
required: true,
variableName: VariableNames.Realm_VariableName,
useCustomValidator: true
}, {
type: FieldType.Text,
label: localize('deployCluster.AppOnwerPrincipals', "App owner principals"),
required: true,
variableName: VariableNames.AppOwnerPrincipals_VariableName,
useCustomValidator: true
}, {
type: FieldType.Text,
label: localize('deployCluster.AppReaderPrincipals', "App reader principals"),
required: true,
variableName: VariableNames.AppReaderPrincipals_VariableName,
useCustomValidator: true
}
]
};
this.pageObject.registerContent((view: azdata.ModelView) => {
const basicSettingsGroup = createSection({
view: view,
container: self.wizard.wizardObject,
sectionInfo: basicSection,
onNewDisposableCreated: (disposable: vscode.Disposable): void => {
self.wizard.registerDisposable(disposable);
},
onNewInputComponentCreated: (name: string, component: azdata.DropDownComponent | azdata.InputBoxComponent | azdata.CheckBoxComponent): void => {
self.inputComponents[name] = component;
},
onNewValidatorCreated: (validator: Validator): void => {
self.validators.push(validator);
}
});
const activeDirectorySettingsGroup = createSection({
view: view,
container: self.wizard.wizardObject,
sectionInfo: activeDirectorySection,
onNewDisposableCreated: (disposable: vscode.Disposable): void => {
self.wizard.registerDisposable(disposable);
},
onNewInputComponentCreated: (name: string, component: azdata.DropDownComponent | azdata.InputBoxComponent | azdata.CheckBoxComponent): void => {
self.inputComponents[name] = component;
},
onNewValidatorCreated: (validator: Validator): void => {
self.validators.push(validator);
}
});
const basicSettingsFormItem = { title: '', component: basicSettingsGroup };
const activeDirectoryFormItem = { title: '', component: activeDirectorySettingsGroup };
const authModeDropdown = <azdata.DropDownComponent>this.inputComponents[VariableNames.AuthenticationMode_VariableName];
const formBuilder = view.modelBuilder.formContainer().withFormItems(
[basicSettingsFormItem],
{
horizontal: false,
componentWidth: '100%'
}
);
this.wizard.registerDisposable(authModeDropdown.onValueChanged(() => {
const isBasicAuthMode = (<azdata.CategoryValue>authModeDropdown.value).name === 'basic';
if (isBasicAuthMode) {
formBuilder.removeFormItem(activeDirectoryFormItem);
} else {
formBuilder.insertFormItem(activeDirectoryFormItem);
}
}));
const form = formBuilder.withLayout({ width: '100%' }).component();
return view.initializeModel(form);
});
}
public onLeave() {
setModelValues(this.inputComponents, this.wizard.model);
this.wizard.wizardObject.registerNavigationValidator((pcInfo) => {
return true;
});
}
public onEnter() {
const authModeDropdown = <azdata.DropDownComponent>this.inputComponents[VariableNames.AuthenticationMode_VariableName];
if (authModeDropdown) {
authModeDropdown.enabled = this.wizard.model.adAuthSupported;
}
this.wizard.wizardObject.registerNavigationValidator((pcInfo) => {
this.wizard.wizardObject.message = { text: '' };
if (pcInfo.newPage > pcInfo.lastPage) {
const messages: string[] = [];
const authMode = typeof authModeDropdown.value === 'string' ? authModeDropdown.value : authModeDropdown.value!.name;
const requiredFieldsFilled: boolean = !isInputBoxEmpty(getInputBoxComponent(VariableNames.ClusterName_VariableName, this.inputComponents))
&& !isInputBoxEmpty(getInputBoxComponent(VariableNames.AdminUserName_VariableName, this.inputComponents))
&& !isInputBoxEmpty(getInputBoxComponent(VariableNames.AdminPassword_VariableName, this.inputComponents))
&& !isInputBoxEmpty(getInputBoxComponent(ConfirmPasswordName, this.inputComponents))
&& (!(authMode === AuthenticationMode.ActiveDirectory) || (
!isInputBoxEmpty(getInputBoxComponent(VariableNames.DistinguishedName_VariableName, this.inputComponents))
&& !isInputBoxEmpty(getInputBoxComponent(VariableNames.AdminPrincipals_VariableName, this.inputComponents))
&& !isInputBoxEmpty(getInputBoxComponent(VariableNames.UserPrincipals_VariableName, this.inputComponents))
&& !isInputBoxEmpty(getInputBoxComponent(VariableNames.UpstreamIPAddresses_VariableName, this.inputComponents))
&& !isInputBoxEmpty(getInputBoxComponent(VariableNames.DnsName_VariableName, this.inputComponents))
&& !isInputBoxEmpty(getInputBoxComponent(VariableNames.Realm_VariableName, this.inputComponents))
&& !isInputBoxEmpty(getInputBoxComponent(VariableNames.AppOwnerPrincipals_VariableName, this.inputComponents))
&& !isInputBoxEmpty(getInputBoxComponent(VariableNames.AppReaderPrincipals_VariableName, this.inputComponents))));
if (!requiredFieldsFilled) {
messages.push(MissingRequiredInformationErrorMessage);
}
if (!isInputBoxEmpty(getInputBoxComponent(VariableNames.AdminPassword_VariableName, this.inputComponents))
&& !isInputBoxEmpty(getInputBoxComponent(ConfirmPasswordName, this.inputComponents))) {
const password = getInputBoxComponent(VariableNames.AdminPassword_VariableName, this.inputComponents).value!;
const confirmPassword = getInputBoxComponent(ConfirmPasswordName, this.inputComponents).value!;
if (password !== confirmPassword) {
messages.push(getPasswordMismatchMessage(localize('deployCluster.AdminPasswordField', "Password")));
}
if (!isValidSQLPassword(password)) {
messages.push(getInvalidSQLPasswordMessage(localize('deployCluster.AdminPasswordField', "Password")));
}
}
if (messages.length > 0) {
this.wizard.wizardObject.message = {
text: messages.length === 1 ? messages[0] : localize('deployCluster.ValidationError', "There are some errors on this page, click 'Show Details' to view the errors."),
description: messages.length === 1 ? undefined : messages.join(EOL),
level: azdata.window.MessageLevel.Error
};
}
return messages.length === 0;
}
return true;
});
}
}

View File

@@ -0,0 +1,214 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import * as azdata from 'azdata';
import * as nls from 'vscode-nls';
import { DeployClusterWizard } from '../deployClusterWizard';
import { WizardPageBase } from '../../wizardPageBase';
import * as VariableNames from '../constants';
import { createFlexContainer } from '../../modelViewUtils';
import { BdcDeploymentType } from '../../../interfaces';
import { BigDataClusterDeploymentProfile } from '../../../services/bigDataClusterDeploymentProfile';
const localize = nls.loadMessageBundle();
export class DeploymentProfilePage extends WizardPageBase<DeployClusterWizard> {
private _cards: azdata.CardComponent[] = [];
private _cardContainer: azdata.FlexContainer | undefined;
private _loadingComponent: azdata.LoadingComponent | undefined;
private _view: azdata.ModelView | undefined;
constructor(wizard: DeployClusterWizard) {
super(localize('deployCluster.summaryPageTitle', "Deployment configuration template"),
localize('deployCluster.summaryPageDescription', "Select the target configuration template"), wizard);
}
public initialize(): void {
this.pageObject.registerContent((view: azdata.ModelView) => {
this._view = view;
this._cardContainer = view.modelBuilder.flexContainer().withLayout({ flexFlow: 'row', flexWrap: 'wrap' }).component();
const hintText = view.modelBuilder.text().withProperties<azdata.TextComponentProperties>({
value: localize('deployCluster.ProfileHintText', "Note: The settings of the deployment profile can be customized in later steps.")
}).component();
const container = createFlexContainer(view, [this._cardContainer, hintText], false);
this._loadingComponent = view.modelBuilder.loadingComponent().withItem(container).withProperties<azdata.LoadingComponentProperties>({
loading: true
}).component();
let formBuilder = view.modelBuilder.formContainer().withFormItems(
[
{
title: '',
component: this._loadingComponent
}
],
{
horizontal: false
}
).withLayout({ width: '100%', height: '100%' });
const form = formBuilder.withLayout({ width: '100%' }).component();
this.loadCards().then(() => {
this._loadingComponent!.loading = false;
}, (error) => {
this.wizard.wizardObject.message = {
level: azdata.window.MessageLevel.Error,
text: localize('deployCluster.loadProfileFailed', "Failed to load the deployment profiles: {0}", error.message)
};
this._loadingComponent!.loading = false;
});
return view.initializeModel(form);
});
}
private createProfileCard(profile: BigDataClusterDeploymentProfile, view: azdata.ModelView): azdata.CardComponent {
const descriptions: azdata.CardDescriptionItem[] = [{
label: localize('deployCluster.serviceLabel', "Service"),
value: localize('deployCluster.instancesLabel', "Instances"),
fontWeight: 'bold'
}, {
label: localize('deployCluster.masterPoolLabel', "SQL Server Master"),
value: profile.sqlServerReplicas.toString()
}, {
label: localize('deployCluster.computePoolLable', "Compute"),
value: profile.computeReplicas.toString()
}, {
label: localize('deployCluster.dataPoolLabel', "Data"),
value: profile.dataReplicas.toString()
}, {
label: localize('deployCluster.hdfsLabel', "HDFS + Spark"),
value: profile.hdfsReplicas.toString()
}, {
label: '' // line separator
}, {
label: localize('deployCluster.defaultDataStorage', "Data storage size (GB)"),
value: profile.controllerDataStorageSize.toString()
}, {
label: localize('deployCluster.defaultLogStorage', "Log storage size (GB)"),
value: profile.controllerLogsStorageSize.toString()
}, {
label: '' // line separator
}
];
if (profile.activeDirectorySupported) {
descriptions.push({
label: localize('deployCluster.activeDirectoryAuthentication', "Active Directory authentication"),
value: '✅'
});
} else {
descriptions.push({
label: localize('deployCluster.basicAuthentication', "Basic authentication"),
value: '✅'
});
}
if (profile.hadrEnabled) {
descriptions.push({
label: localize('deployCluster.hadr', "High Availability"),
value: '✅'
});
}
const card = view.modelBuilder.card().withProperties<azdata.CardProperties>({
cardType: azdata.CardType.VerticalButton,
label: profile.profileName,
descriptions: descriptions,
width: '240px',
height: '300px',
}).component();
this._cards.push(card);
this.wizard.registerDisposable(card.onCardSelectedChanged(() => {
if (card.selected) {
this.wizard.wizardObject.message = { text: '' };
this.setModelValuesByProfile(profile);
// clear the selected state of the previously selected card
this._cards.forEach(c => {
if (c !== card) {
c.selected = false;
}
});
} else {
// keep the selected state if no other card is selected
if (this._cards.filter(c => { return c !== card && c.selected; }).length === 0) {
card.selected = true;
}
}
}));
return card;
}
private setModelValuesByProfile(selectedProfile: BigDataClusterDeploymentProfile): void {
this.wizard.model.setPropertyValue(VariableNames.DeploymentProfile_VariableName, selectedProfile.profileName);
this.wizard.model.setPropertyValue(VariableNames.SparkPoolScale_VariableName, selectedProfile.sparkReplicas);
this.wizard.model.setPropertyValue(VariableNames.DataPoolScale_VariableName, selectedProfile.dataReplicas);
this.wizard.model.setPropertyValue(VariableNames.HDFSPoolScale_VariableName, selectedProfile.hdfsReplicas);
this.wizard.model.setPropertyValue(VariableNames.ComputePoolScale_VariableName, selectedProfile.computeReplicas);
this.wizard.model.setPropertyValue(VariableNames.HDFSNameNodeScale_VariableName, selectedProfile.hdfsNameNodeReplicas);
this.wizard.model.setPropertyValue(VariableNames.SQLServerScale_VariableName, selectedProfile.sqlServerReplicas);
this.wizard.model.setPropertyValue(VariableNames.SparkHeadScale_VariableName, selectedProfile.sparkHeadReplicas);
this.wizard.model.setPropertyValue(VariableNames.ZooKeeperScale_VariableName, selectedProfile.zooKeeperReplicas);
this.wizard.model.setPropertyValue(VariableNames.ControllerDataStorageSize_VariableName, selectedProfile.controllerDataStorageSize);
this.wizard.model.setPropertyValue(VariableNames.ControllerLogsStorageSize_VariableName, selectedProfile.controllerLogsStorageSize);
this.wizard.model.setPropertyValue(VariableNames.EnableHADR_VariableName, selectedProfile.hadrEnabled);
this.wizard.model.setPropertyValue(VariableNames.SQLServerPort_VariableName, selectedProfile.sqlServerPort);
this.wizard.model.setPropertyValue(VariableNames.GateWayPort_VariableName, selectedProfile.gatewayPort);
this.wizard.model.setPropertyValue(VariableNames.ControllerPort_VariableName, selectedProfile.controllerPort);
this.wizard.model.setPropertyValue(VariableNames.IncludeSpark_VariableName, selectedProfile.includeSpark);
this.wizard.model.setPropertyValue(VariableNames.ControllerDataStorageClassName_VariableName, selectedProfile.controllerDataStorageClass);
this.wizard.model.setPropertyValue(VariableNames.ControllerLogsStorageClassName_VariableName, selectedProfile.controllerLogsStorageClass);
this.wizard.model.setPropertyValue(VariableNames.ReadableSecondaryPort_VariableName, selectedProfile.sqlServerReadableSecondaryPort);
this.wizard.model.adAuthSupported = selectedProfile.activeDirectorySupported;
this.wizard.model.selectedProfile = selectedProfile;
}
private loadCards(): Promise<void> {
return this.wizard.azdataService.getDeploymentProfiles().then((profiles: BigDataClusterDeploymentProfile[]) => {
const defaultProfile: string = this.getDefaultProfile();
profiles.forEach(profile => {
const card = this.createProfileCard(profile, this._view!);
if (profile.profileName === defaultProfile) {
card.selected = true;
this.setModelValuesByProfile(profile);
}
this._cardContainer!.addItem(card, { flex: '0 0 auto' });
});
});
}
public onEnter() {
this.wizard.wizardObject.registerNavigationValidator((pcInfo) => {
this.wizard.wizardObject.message = { text: '' };
if (pcInfo.newPage > pcInfo.lastPage) {
const isValid = this.wizard.model.getStringValue(VariableNames.DeploymentProfile_VariableName) !== undefined;
if (!isValid) {
this.wizard.wizardObject.message = {
text: localize('deployCluster.ProfileNotSelectedError', "Please select a deployment profile."),
level: azdata.window.MessageLevel.Error
};
}
return isValid;
}
return true;
});
}
public onLeave() {
this.wizard.wizardObject.registerNavigationValidator((pcInfo) => {
return true;
});
}
private getDefaultProfile(): string {
switch (this.wizard.deploymentType) {
case BdcDeploymentType.NewAKS:
case BdcDeploymentType.ExistingAKS:
return 'aks-dev-test';
case BdcDeploymentType.ExistingKubeAdm:
return 'kubeadm-dev-test';
default:
throw new Error(`Unknown deployment type: ${this.wizard.deploymentType}`);
}
}
}

View File

@@ -0,0 +1,563 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import * as azdata from 'azdata';
import * as vscode from 'vscode';
import * as nls from 'vscode-nls';
import { DeployClusterWizard } from '../deployClusterWizard';
import { SectionInfo, FieldType } from '../../../interfaces';
import { Validator, InputComponents, createSection, createGroupContainer, createLabel, createFlexContainer, createTextInput, createNumberInput, setModelValues, getInputBoxComponent, getCheckboxComponent, isInputBoxEmpty, getDropdownComponent, MissingRequiredInformationErrorMessage } from '../../modelViewUtils';
import { WizardPageBase } from '../../wizardPageBase';
import * as VariableNames from '../constants';
import { AuthenticationMode } from '../deployClusterWizardModel';
const localize = nls.loadMessageBundle();
const PortInputWidth = '100px';
const inputWidth = '180px';
const labelWidth = '150px';
const spaceBetweenFields = '5px';
export class ServiceSettingsPage extends WizardPageBase<DeployClusterWizard> {
private inputComponents: InputComponents = {};
private endpointHeaderRow!: azdata.FlexContainer;
private dnsColumnHeader!: azdata.TextComponent;
private portColumnHeader!: azdata.TextComponent;
private controllerDNSInput!: azdata.InputBoxComponent;
private controllerPortInput!: azdata.InputBoxComponent;
private controllerEndpointRow!: azdata.FlexContainer;
private sqlServerDNSInput!: azdata.InputBoxComponent;
private sqlServerEndpointRow!: azdata.FlexContainer;
private sqlServerPortInput!: azdata.InputBoxComponent;
private gatewayDNSInput!: azdata.InputBoxComponent;
private gatewayPortInput!: azdata.InputBoxComponent;
private gatewayEndpointRow!: azdata.FlexContainer;
private readableSecondaryDNSInput!: azdata.InputBoxComponent;
private readableSecondaryPortInput!: azdata.InputBoxComponent;
private readableSecondaryEndpointRow!: azdata.FlexContainer;
private endpointNameColumnHeader!: azdata.TextComponent;
private controllerNameLabel!: azdata.TextComponent;
private SqlServerNameLabel!: azdata.TextComponent;
private gatewayNameLabel!: azdata.TextComponent;
private readableSecondaryNameLabel!: azdata.TextComponent;
constructor(wizard: DeployClusterWizard) {
super(localize('deployCluster.ServiceSettingsPageTitle', "Service settings"), '', wizard);
}
public initialize(): void {
const scaleSectionInfo: SectionInfo = {
title: localize('deployCluster.scaleSectionTitle', "Scale settings"),
labelWidth: labelWidth,
inputWidth: inputWidth,
spaceBetweenFields: spaceBetweenFields,
rows: [{
fields: [
{
type: FieldType.Number,
label: localize('deployCluster.ComputeText', "Compute"),
min: 1,
max: 100,
defaultValue: '1',
useCustomValidator: true,
required: true,
variableName: VariableNames.ComputePoolScale_VariableName,
}
]
}, {
fields: [{
type: FieldType.Number,
label: localize('deployCluster.DataText', "Data"),
min: 1,
max: 100,
defaultValue: '1',
useCustomValidator: true,
required: true,
variableName: VariableNames.DataPoolScale_VariableName,
}]
}, {
fields: [
{
type: FieldType.Number,
label: localize('deployCluster.HDFSText', "HDFS"),
min: 1,
max: 100,
defaultValue: '1',
useCustomValidator: true,
required: true,
variableName: VariableNames.HDFSPoolScale_VariableName
}, {
type: FieldType.Checkbox,
label: localize('deployCluster.includeSparkInHDFSPool', "Include Spark"),
defaultValue: 'true',
variableName: VariableNames.IncludeSpark_VariableName,
required: false
}
]
}, {
fields: [
{
type: FieldType.Number,
label: localize('deployCluster.SparkText', "Spark"),
min: 0,
max: 100,
defaultValue: '0',
useCustomValidator: true,
required: true,
variableName: VariableNames.SparkPoolScale_VariableName
}
]
}
]
};
const hadrSectionInfo: SectionInfo = {
title: localize('deployCluster.HadrSection', "High availability settings"),
labelWidth: labelWidth,
inputWidth: inputWidth,
spaceBetweenFields: spaceBetweenFields,
rows: [{
fields: [
{
type: FieldType.Options,
label: localize('deployCluster.MasterSqlText', "SQL Server Master"),
options: ['1', '3', '4', '5', '6', '7', '8', '9'],
defaultValue: '1',
required: true,
variableName: VariableNames.SQLServerScale_VariableName,
}, {
type: FieldType.Checkbox,
label: localize('deployCluster.EnableHADR', "Enable Availability Groups"),
defaultValue: 'false',
variableName: VariableNames.EnableHADR_VariableName,
required: false
}
]
}, {
fields: [
{
type: FieldType.Number,
label: localize('deployCluster.HDFSNameNodeText', "HDFS name node"),
min: 1,
max: 100,
defaultValue: '1',
useCustomValidator: true,
required: true,
variableName: VariableNames.HDFSNameNodeScale_VariableName
}
]
}, {
fields: [
{
type: FieldType.Number,
label: localize('deployCluster.SparkHeadText', "SparkHead"),
min: 0,
max: 100,
defaultValue: '1',
useCustomValidator: true,
required: true,
variableName: VariableNames.SparkHeadScale_VariableName
}
]
}, {
fields: [
{
type: FieldType.Number,
label: localize('deployCluster.ZooKeeperText', "ZooKeeper"),
min: 0,
max: 100,
defaultValue: '1',
useCustomValidator: true,
required: true,
variableName: VariableNames.ZooKeeperScale_VariableName
}
]
}
]
};
const hintTextForStorageFields = localize('deployCluster.storageFieldTooltip', "Use controller settings");
const storageSectionInfo: SectionInfo = {
title: '',
labelWidth: '0px',
inputWidth: inputWidth,
spaceBetweenFields: spaceBetweenFields,
rows: [{
fields: [
{
type: FieldType.ReadonlyText,
label: '',
required: false,
defaultValue: localize('deployCluster.DataStorageClassName', "Storage class for data"),
variableName: '',
labelWidth: labelWidth
}, {
type: FieldType.ReadonlyText,
label: '',
required: false,
defaultValue: localize('deployCluster.DataClaimSize', "Claim size for data (GB)"),
variableName: ''
}, {
type: FieldType.ReadonlyText,
label: '',
required: false,
defaultValue: localize('deployCluster.LogStorageClassName', "Storage class for logs"),
variableName: '',
}, {
type: FieldType.ReadonlyText,
label: '',
required: false,
defaultValue: localize('deployCluster.LogsClaimSize', "Claim size for logs (GB)"),
variableName: ''
}
]
},
{
fields: [
{
type: FieldType.Text,
label: localize('deployCluster.ControllerText', "Controller"),
useCustomValidator: true,
variableName: VariableNames.ControllerDataStorageClassName_VariableName,
required: true,
description: localize('deployCluster.AdvancedStorageDescription', "By default Controller storage settings will be applied to other services as well, you can expand the advanced storage settings to configure storage for other services."),
labelWidth: labelWidth
}, {
type: FieldType.Number,
label: '',
useCustomValidator: true,
min: 1,
variableName: VariableNames.ControllerDataStorageSize_VariableName,
}, {
type: FieldType.Text,
label: '',
useCustomValidator: true,
min: 1,
variableName: VariableNames.ControllerLogsStorageClassName_VariableName,
}, {
type: FieldType.Number,
label: '',
useCustomValidator: true,
min: 1,
variableName: VariableNames.ControllerLogsStorageSize_VariableName,
}
]
}
]
};
const advancedStorageSectionInfo: SectionInfo = {
title: localize('deployCluster.AdvancedStorageSectionTitle', "Advanced storage settings"),
labelWidth: '0px',
inputWidth: inputWidth,
spaceBetweenFields: spaceBetweenFields,
collapsible: true,
collapsed: true,
rows: [{
fields: [
{
type: FieldType.Text,
label: localize('deployCluster.HDFSText', "HDFS"),
required: false,
variableName: VariableNames.HDFSDataStorageClassName_VariableName,
placeHolder: hintTextForStorageFields,
labelWidth: labelWidth
}, {
type: FieldType.Number,
label: '',
required: false,
min: 1,
variableName: VariableNames.HDFSDataStorageSize_VariableName,
placeHolder: hintTextForStorageFields
}, {
type: FieldType.Text,
label: '',
required: false,
variableName: VariableNames.HDFSLogsStorageClassName_VariableName,
placeHolder: hintTextForStorageFields
}, {
type: FieldType.Number,
label: '',
required: false,
min: 1,
variableName: VariableNames.HDFSLogsStorageSize_VariableName,
placeHolder: hintTextForStorageFields
}
]
}, {
fields: [
{
type: FieldType.Text,
label: localize('deployCluster.DataText', "Data"),
required: false,
variableName: VariableNames.DataPoolDataStorageClassName_VariableName,
labelWidth: labelWidth,
placeHolder: hintTextForStorageFields
}, {
type: FieldType.Number,
label: '',
required: false,
min: 1,
variableName: VariableNames.DataPoolDataStorageSize_VariableName,
placeHolder: hintTextForStorageFields
}, {
type: FieldType.Text,
label: '',
required: false,
variableName: VariableNames.DataPoolLogsStorageClassName_VariableName,
placeHolder: hintTextForStorageFields
}, {
type: FieldType.Number,
label: '',
required: false,
min: 1,
variableName: VariableNames.DataPoolLogsStorageSize_VariableName,
placeHolder: hintTextForStorageFields
}
]
}, {
fields: [
{
type: FieldType.Text,
label: localize('deployCluster.MasterSqlText', "SQL Server Master"),
required: false,
variableName: VariableNames.SQLServerDataStorageClassName_VariableName,
labelWidth: labelWidth,
placeHolder: hintTextForStorageFields
}, {
type: FieldType.Number,
label: '',
required: false,
min: 1,
variableName: VariableNames.SQLServerDataStorageSize_VariableName,
placeHolder: hintTextForStorageFields
}, {
type: FieldType.Text,
label: '',
required: false,
variableName: VariableNames.SQLServerLogsStorageClassName_VariableName,
placeHolder: hintTextForStorageFields
}, {
type: FieldType.Number,
label: '',
required: false,
min: 1,
variableName: VariableNames.SQLServerLogsStorageSize_VariableName,
placeHolder: hintTextForStorageFields
}
]
}]
};
this.pageObject.registerContent((view: azdata.ModelView) => {
const createSectionFunc = (sectionInfo: SectionInfo): azdata.GroupContainer => {
return createSection({
view: view,
container: this.wizard.wizardObject,
sectionInfo: sectionInfo,
onNewDisposableCreated: (disposable: vscode.Disposable): void => {
this.wizard.registerDisposable(disposable);
},
onNewInputComponentCreated: (name: string, component: azdata.DropDownComponent | azdata.InputBoxComponent | azdata.CheckBoxComponent): void => {
this.inputComponents[name] = component;
},
onNewValidatorCreated: (validator: Validator): void => {
}
});
};
const scaleSection = createSectionFunc(scaleSectionInfo);
const hadrSection = createSectionFunc(hadrSectionInfo);
const endpointSection = this.createEndpointSection(view);
const storageSection = createSectionFunc(storageSectionInfo);
const advancedStorageSection = createSectionFunc(advancedStorageSectionInfo);
const storageContainer = createGroupContainer(view, [storageSection, advancedStorageSection], {
header: localize('deployCluster.StorageSectionTitle', "Storage settings"),
collapsible: true
});
this.setSQLServerMasterFieldEventHandler();
const form = view.modelBuilder.formContainer().withFormItems([
{
title: '',
component: scaleSection
}, {
title: '',
component: hadrSection
}, {
title: '',
component: endpointSection
}, {
title: '',
component: storageContainer
}
]).withLayout({ width: '100%' }).component();
return view.initializeModel(form);
});
}
private createEndpointSection(view: azdata.ModelView): azdata.GroupContainer {
this.endpointNameColumnHeader = createLabel(view, { text: '', width: labelWidth });
this.dnsColumnHeader = createLabel(view, { text: localize('deployCluster.DNSNameHeader', "DNS name"), width: inputWidth });
this.portColumnHeader = createLabel(view, { text: localize('deployCluster.PortHeader', "Port"), width: PortInputWidth });
this.endpointHeaderRow = createFlexContainer(view, [this.endpointNameColumnHeader, this.dnsColumnHeader, this.portColumnHeader]);
this.controllerNameLabel = createLabel(view, { text: localize('deployCluster.ControllerText', "Controller"), width: labelWidth, required: true });
this.controllerDNSInput = createTextInput(view, { ariaLabel: localize('deployCluster.ControllerDNSName', "Controller DNS name"), required: false, width: inputWidth });
this.controllerPortInput = createNumberInput(view, { ariaLabel: localize('deployCluster.ControllerPortName', "Controller port"), required: true, width: PortInputWidth, min: 1 });
this.controllerEndpointRow = createFlexContainer(view, [this.controllerNameLabel, this.controllerDNSInput, this.controllerPortInput]);
this.inputComponents[VariableNames.ControllerDNSName_VariableName] = this.controllerDNSInput;
this.inputComponents[VariableNames.ControllerPort_VariableName] = this.controllerPortInput;
this.SqlServerNameLabel = createLabel(view, { text: localize('deployCluster.MasterSqlText', "SQL Server Master"), width: labelWidth, required: true });
this.sqlServerDNSInput = createTextInput(view, { ariaLabel: localize('deployCluster.MasterSQLServerDNSName', "SQL Server Master DNS name"), required: false, width: inputWidth });
this.sqlServerPortInput = createNumberInput(view, { ariaLabel: localize('deployCluster.MasterSQLServerPortName', "SQL Server Master port"), required: true, width: PortInputWidth, min: 1 });
this.sqlServerEndpointRow = createFlexContainer(view, [this.SqlServerNameLabel, this.sqlServerDNSInput, this.sqlServerPortInput]);
this.inputComponents[VariableNames.SQLServerDNSName_VariableName] = this.sqlServerDNSInput;
this.inputComponents[VariableNames.SQLServerPort_VariableName] = this.sqlServerPortInput;
this.gatewayNameLabel = createLabel(view, { text: localize('deployCluster.GatewayText', "Gateway"), width: labelWidth, required: true });
this.gatewayDNSInput = createTextInput(view, { ariaLabel: localize('deployCluster.GatewayDNSName', "Gateway DNS name"), required: false, width: inputWidth });
this.gatewayPortInput = createNumberInput(view, { ariaLabel: localize('deployCluster.GatewayPortName', "Gateway port"), required: true, width: PortInputWidth, min: 1 });
this.gatewayEndpointRow = createFlexContainer(view, [this.gatewayNameLabel, this.gatewayDNSInput, this.gatewayPortInput]);
this.inputComponents[VariableNames.GatewayDNSName_VariableName] = this.gatewayDNSInput;
this.inputComponents[VariableNames.GateWayPort_VariableName] = this.gatewayPortInput;
this.readableSecondaryNameLabel = createLabel(view, { text: localize('deployCluster.ReadableSecondaryText', "Readable secondary"), width: labelWidth, required: true });
this.readableSecondaryDNSInput = createTextInput(view, { ariaLabel: localize('deployCluster.ReadableSecondaryDNSName', "Readable secondary DNS name"), required: false, width: inputWidth });
this.readableSecondaryPortInput = createNumberInput(view, { ariaLabel: localize('deployCluster.ReadableSecondaryPortName', "Readable secondary port"), required: false, width: PortInputWidth, min: 1 });
this.readableSecondaryEndpointRow = createFlexContainer(view, [this.readableSecondaryNameLabel, this.readableSecondaryDNSInput, this.readableSecondaryPortInput]);
this.inputComponents[VariableNames.ReadableSecondaryDNSName_VariableName] = this.readableSecondaryDNSInput;
this.inputComponents[VariableNames.ReadableSecondaryPort_VariableName] = this.readableSecondaryPortInput;
return createGroupContainer(view, [this.endpointHeaderRow, this.controllerEndpointRow, this.sqlServerEndpointRow, this.gatewayEndpointRow, this.readableSecondaryEndpointRow], {
header: localize('deployCluster.EndpointSettings', "Endpoint settings"),
collapsible: true
});
}
public onEnter(): void {
this.setDropdownValue(VariableNames.SQLServerScale_VariableName);
this.setCheckboxValue(VariableNames.EnableHADR_VariableName);
this.setInputBoxValue(VariableNames.ComputePoolScale_VariableName);
this.setInputBoxValue(VariableNames.DataPoolScale_VariableName);
this.setInputBoxValue(VariableNames.HDFSPoolScale_VariableName);
this.setInputBoxValue(VariableNames.HDFSNameNodeScale_VariableName);
this.setInputBoxValue(VariableNames.SparkPoolScale_VariableName);
this.setInputBoxValue(VariableNames.SparkHeadScale_VariableName);
this.setInputBoxValue(VariableNames.ZooKeeperScale_VariableName);
this.setCheckboxValue(VariableNames.IncludeSpark_VariableName);
this.setEnableHadrCheckboxState(this.wizard.model.getIntegerValue(VariableNames.SQLServerScale_VariableName));
this.setInputBoxValue(VariableNames.ControllerPort_VariableName);
this.setInputBoxValue(VariableNames.SQLServerPort_VariableName);
this.setInputBoxValue(VariableNames.GateWayPort_VariableName);
this.setInputBoxValue(VariableNames.ReadableSecondaryPort_VariableName);
this.setInputBoxValue(VariableNames.ControllerDataStorageClassName_VariableName);
this.setInputBoxValue(VariableNames.ControllerDataStorageSize_VariableName);
this.setInputBoxValue(VariableNames.ControllerLogsStorageClassName_VariableName);
this.setInputBoxValue(VariableNames.ControllerLogsStorageSize_VariableName);
this.endpointHeaderRow.clearItems();
if (this.wizard.model.authenticationMode === AuthenticationMode.ActiveDirectory) {
this.endpointHeaderRow.addItems([this.endpointNameColumnHeader, this.dnsColumnHeader, this.portColumnHeader]);
}
this.loadEndpointRow(this.controllerEndpointRow, this.controllerNameLabel, this.controllerDNSInput, this.controllerPortInput);
this.loadEndpointRow(this.gatewayEndpointRow, this.gatewayNameLabel, this.gatewayDNSInput, this.gatewayPortInput);
this.loadEndpointRow(this.sqlServerEndpointRow, this.SqlServerNameLabel, this.sqlServerDNSInput, this.sqlServerPortInput);
this.updateReadableSecondaryEndpointComponents(this.wizard.model.hadrEnabled);
this.wizard.wizardObject.registerNavigationValidator((pcInfo) => {
this.wizard.wizardObject.message = { text: '' };
if (pcInfo.newPage > pcInfo.lastPage) {
const isValid: boolean = !isInputBoxEmpty(getInputBoxComponent(VariableNames.ComputePoolScale_VariableName, this.inputComponents))
&& !isInputBoxEmpty(getInputBoxComponent(VariableNames.DataPoolScale_VariableName, this.inputComponents))
&& !isInputBoxEmpty(getInputBoxComponent(VariableNames.HDFSNameNodeScale_VariableName, this.inputComponents))
&& !isInputBoxEmpty(getInputBoxComponent(VariableNames.HDFSPoolScale_VariableName, this.inputComponents))
&& !isInputBoxEmpty(getInputBoxComponent(VariableNames.SparkPoolScale_VariableName, this.inputComponents))
&& !isInputBoxEmpty(getInputBoxComponent(VariableNames.SparkHeadScale_VariableName, this.inputComponents))
&& !isInputBoxEmpty(getInputBoxComponent(VariableNames.ZooKeeperScale_VariableName, this.inputComponents))
&& !isInputBoxEmpty(getInputBoxComponent(VariableNames.ControllerDataStorageClassName_VariableName, this.inputComponents))
&& !isInputBoxEmpty(getInputBoxComponent(VariableNames.ControllerDataStorageSize_VariableName, this.inputComponents))
&& !isInputBoxEmpty(getInputBoxComponent(VariableNames.ControllerLogsStorageClassName_VariableName, this.inputComponents))
&& !isInputBoxEmpty(getInputBoxComponent(VariableNames.ControllerLogsStorageSize_VariableName, this.inputComponents))
&& !isInputBoxEmpty(getInputBoxComponent(VariableNames.ControllerPort_VariableName, this.inputComponents))
&& !isInputBoxEmpty(getInputBoxComponent(VariableNames.SQLServerPort_VariableName, this.inputComponents))
&& !isInputBoxEmpty(getInputBoxComponent(VariableNames.GateWayPort_VariableName, this.inputComponents))
&& (!getCheckboxComponent(VariableNames.EnableHADR_VariableName, this.inputComponents).checked
|| !isInputBoxEmpty(this.readableSecondaryPortInput))
&& (this.wizard.model.authenticationMode !== AuthenticationMode.ActiveDirectory
|| (!isInputBoxEmpty(this.gatewayDNSInput)
&& !isInputBoxEmpty(this.controllerDNSInput)
&& !isInputBoxEmpty(this.sqlServerDNSInput)
&& !isInputBoxEmpty(this.readableSecondaryDNSInput)
));
if (!isValid) {
this.wizard.wizardObject.message = {
text: MissingRequiredInformationErrorMessage,
level: azdata.window.MessageLevel.Error
};
}
return isValid;
}
return true;
});
}
public onLeave(): void {
setModelValues(this.inputComponents, this.wizard.model);
this.wizard.wizardObject.registerNavigationValidator((pcInfo) => {
return true;
});
}
private setInputBoxValue(variableName: string): void {
getInputBoxComponent(variableName, this.inputComponents).value = this.wizard.model.getStringValue(variableName);
}
private setCheckboxValue(variableName: string): void {
getCheckboxComponent(variableName, this.inputComponents).checked = this.wizard.model.getBooleanValue(variableName);
}
private setDropdownValue(variableName: string): void {
getDropdownComponent(variableName, this.inputComponents).value = this.wizard.model.getStringValue(variableName);
}
private setSQLServerMasterFieldEventHandler() {
const sqlScaleDropdown = getDropdownComponent(VariableNames.SQLServerScale_VariableName, this.inputComponents);
const enableHadrCheckbox = getCheckboxComponent(VariableNames.EnableHADR_VariableName, this.inputComponents);
this.wizard.registerDisposable(sqlScaleDropdown.onValueChanged(() => {
const selectedValue = typeof sqlScaleDropdown.value === 'string' ? sqlScaleDropdown.value : sqlScaleDropdown.value!.name;
this.setEnableHadrCheckboxState(Number.parseInt(selectedValue));
}));
this.wizard.registerDisposable(enableHadrCheckbox.onChanged(() => {
this.updateReadableSecondaryEndpointComponents(enableHadrCheckbox.checked);
}));
}
private setEnableHadrCheckboxState(sqlInstances: number) {
// 1. it is ok to enable HADR when there is only 1 replica
// 2. if there are multiple replicas, the hadr.enabled switch must be set to true.
const enableHadrCheckbox = getCheckboxComponent(VariableNames.EnableHADR_VariableName, this.inputComponents);
const hadrEnabled = sqlInstances === 1 ? enableHadrCheckbox.checked : true;
if (sqlInstances === 1) {
enableHadrCheckbox.enabled = true;
} else {
enableHadrCheckbox.enabled = false;
}
enableHadrCheckbox.checked = hadrEnabled;
this.updateReadableSecondaryEndpointComponents(hadrEnabled);
}
private updateReadableSecondaryEndpointComponents(hadrEnabled: boolean) {
this.readableSecondaryEndpointRow.clearItems();
if (hadrEnabled) {
this.loadEndpointRow(this.readableSecondaryEndpointRow, this.readableSecondaryNameLabel, this.readableSecondaryDNSInput, this.readableSecondaryPortInput);
}
}
private loadEndpointRow(row: azdata.FlexContainer, label: azdata.TextComponent, dnsInput: azdata.InputBoxComponent, portInput: azdata.InputBoxComponent): void {
row.clearItems();
const itemLayout: azdata.FlexItemLayout = { CSSStyles: { 'margin-right': '20px' } };
row.addItem(label);
if (this.wizard.model.authenticationMode === AuthenticationMode.ActiveDirectory) {
row.addItem(dnsInput, itemLayout);
}
row.addItem(portInput);
}
}

View File

@@ -0,0 +1,415 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import * as azdata from 'azdata';
import * as nls from 'vscode-nls';
import * as vscode from 'vscode';
import { DeployClusterWizard } from '../deployClusterWizard';
import { SectionInfo, FieldType, LabelPosition, FontStyle, BdcDeploymentType } from '../../../interfaces';
import { createSection, createGroupContainer, createFlexContainer, createLabel } from '../../modelViewUtils';
import { WizardPageBase } from '../../wizardPageBase';
import * as VariableNames from '../constants';
import * as os from 'os';
import { join } from 'path';
import * as fs from 'fs';
import { AuthenticationMode } from '../deployClusterWizardModel';
import { BigDataClusterDeploymentProfile } from '../../../services/bigDataClusterDeploymentProfile';
const localize = nls.loadMessageBundle();
export class SummaryPage extends WizardPageBase<DeployClusterWizard> {
private formItems: azdata.FormComponent[] = [];
private form!: azdata.FormBuilder;
private view!: azdata.ModelView;
private targetDeploymentProfile!: BigDataClusterDeploymentProfile;
constructor(wizard: DeployClusterWizard) {
super(localize('deployCluster.summaryPageTitle', "Summary"), '', wizard);
}
public initialize(): void {
this.pageObject.registerContent((view: azdata.ModelView) => {
this.view = view;
const deploymentJsonSection = createGroupContainer(view, [
view.modelBuilder.flexContainer().withItems([
this.createSaveJsonButton(localize('deployCluster.SaveBdcJson', "Save bdc.json"), 'bdc.json', () => { return this.targetDeploymentProfile.getBdcJson(); }),
this.createSaveJsonButton(localize('deployCluster.SaveControlJson', "Save control.json"), 'control.json', () => { return this.targetDeploymentProfile.getControlJson(); })
], {
CSSStyles: { 'margin-right': '10px' }
}).withLayout({ flexFlow: 'row', alignItems: 'center' }).component()
], {
header: localize('deployCluster.DeploymentJSON', "Deployment JSON files"),
collapsible: true
});
this.form = view.modelBuilder.formContainer().withFormItems([
{
title: '',
component: deploymentJsonSection
}
]);
return view.initializeModel(this.form!.withLayout({ width: '100%' }).component());
});
}
public onEnter() {
this.targetDeploymentProfile = this.wizard.model.createTargetProfile();
this.formItems.forEach(item => {
this.form!.removeFormItem(item);
});
this.formItems = [];
const deploymentTargetSectionInfo: SectionInfo = {
labelPosition: LabelPosition.Left,
labelWidth: '150px',
inputWidth: '200px',
title: localize('deployCluster.DeploymentTarget', "Deployment target"),
rows: [
{
fields: [
{
type: FieldType.ReadonlyText,
label: localize('deployCluster.Kubeconfig', "Kube config"),
defaultValue: this.wizard.model.getStringValue(VariableNames.KubeConfigPath_VariableName),
fontStyle: FontStyle.Italic
},
{
type: FieldType.ReadonlyText,
label: localize('deployCluster.ClusterContext', "Cluster context"),
defaultValue: this.wizard.model.getStringValue(VariableNames.ClusterContext_VariableName),
fontStyle: FontStyle.Italic
}]
}
]
};
const clusterSectionInfo: SectionInfo = {
labelPosition: LabelPosition.Left,
labelWidth: '150px',
inputWidth: '200px',
title: localize('deployCluster.ClusterSettings', "Cluster settings"),
rows: [
{
fields: [
{
type: FieldType.ReadonlyText,
label: localize('deployCluster.DeploymentProfile', "Deployment profile"),
defaultValue: this.wizard.model.getStringValue(VariableNames.DeploymentProfile_VariableName),
fontStyle: FontStyle.Italic
},
{
type: FieldType.ReadonlyText,
label: localize('deployCluster.ClusterName', "Cluster name"),
defaultValue: this.wizard.model.getStringValue(VariableNames.ClusterName_VariableName),
fontStyle: FontStyle.Italic
}]
}, {
fields: [
{
type: FieldType.ReadonlyText,
label: localize('deployCluster.ControllerUsername', "Controller username"),
defaultValue: this.wizard.model.getStringValue(VariableNames.AdminUserName_VariableName),
fontStyle: FontStyle.Italic
}, {
type: FieldType.ReadonlyText,
label: localize('deployCluster.AuthenticationMode', "Authentication mode"),
defaultValue: this.wizard.model.authenticationMode === AuthenticationMode.ActiveDirectory ?
localize('deployCluster.AuthenticationMode.ActiveDirectory', "Active Directory") :
localize('deployCluster.AuthenticationMode.Basic', "Basic"),
fontStyle: FontStyle.Italic
}
]
}
]
};
const azureSectionInfo: SectionInfo = {
labelPosition: LabelPosition.Left,
labelWidth: '150px',
inputWidth: '200px',
title: localize('deployCluster.AzureSettings', "Azure settings"),
rows: [{
fields: [
{
type: FieldType.ReadonlyText,
label: localize('deployCluster.SubscriptionId', "Subscription id"),
defaultValue: this.wizard.model.getStringValue(VariableNames.SubscriptionId_VariableName) || localize('deployCluster.DefaultSubscription', "Default Azure Subscription"),
fontStyle: FontStyle.Italic
}, {
type: FieldType.ReadonlyText,
label: localize('deployCluster.ResourceGroup', "Resource group"),
defaultValue: this.wizard.model.getStringValue(VariableNames.ResourceGroup_VariableName),
fontStyle: FontStyle.Italic
}
]
}, {
fields: [
{
type: FieldType.ReadonlyText,
label: localize('deployCluster.Region', "Region"),
defaultValue: this.wizard.model.getStringValue(VariableNames.DeploymentProfile_VariableName),
fontStyle: FontStyle.Italic
}, {
type: FieldType.ReadonlyText,
label: localize('deployCluster.AksClusterName', "AKS cluster name"),
defaultValue: this.wizard.model.getStringValue(VariableNames.AksName_VariableName),
fontStyle: FontStyle.Italic
}
]
}, {
fields: [
{
type: FieldType.ReadonlyText,
label: localize('deployCluster.VMSize', "VM size"),
defaultValue: this.wizard.model.getStringValue(VariableNames.VMSize_VariableName),
fontStyle: FontStyle.Italic
}, {
type: FieldType.ReadonlyText,
label: localize('deployCluster.VMCount', "VM count"),
defaultValue: this.wizard.model.getStringValue(VariableNames.VMCount_VariableName),
fontStyle: FontStyle.Italic
}
]
}
]
};
const scaleSectionInfo: SectionInfo = {
labelPosition: LabelPosition.Left,
labelWidth: '150px',
inputWidth: '200px',
title: localize('deployCluster.ScaleSettings', "Scale settings"),
rows: [
{
fields: [{
type: FieldType.ReadonlyText,
label: localize('deployCluster.ComputeText', "Compute"),
defaultValue: this.wizard.model.getStringValue(VariableNames.ComputePoolScale_VariableName),
fontStyle: FontStyle.Italic
}, {
type: FieldType.ReadonlyText,
label: localize('deployCluster.DataText', "Data"),
defaultValue: this.wizard.model.getStringValue(VariableNames.DataPoolScale_VariableName),
fontStyle: FontStyle.Italic
}
]
}, {
fields: [
{
type: FieldType.ReadonlyText,
label: localize('deployCluster.HDFSText', "HDFS"),
defaultValue: `${this.wizard.model.getStringValue(VariableNames.HDFSPoolScale_VariableName)} ${this.wizard.model.getBooleanValue(VariableNames.IncludeSpark_VariableName) ? localize('deployCluster.WithSpark', "(Spark included)") : ''}`,
fontStyle: FontStyle.Italic
}, {
type: FieldType.ReadonlyText,
label: localize('deployCluster.SparkText', "Spark"),
defaultValue: this.wizard.model.getStringValue(VariableNames.SparkPoolScale_VariableName),
fontStyle: FontStyle.Italic
}
]
}
]
};
const hadrSectionInfo: SectionInfo = {
labelPosition: LabelPosition.Left,
labelWidth: '150px',
inputWidth: '200px',
title: localize('deployCluster.HadrSection', "High availability settings"),
rows: [
{
fields: [
{
type: FieldType.ReadonlyText,
label: localize('deployCluster.SqlServerText', "SQL Server Master"),
defaultValue: `${this.wizard.model.getStringValue(VariableNames.SQLServerScale_VariableName)} ${this.wizard.model.hadrEnabled ? localize('deployCluster.WithHADR', "(Availability Groups Enabled)") : ''}`,
fontStyle: FontStyle.Italic
}, {
type: FieldType.ReadonlyText,
label: localize('deployCluster.HDFSNameNodeText', "HDFS name node"),
defaultValue: this.wizard.model.getStringValue(VariableNames.HDFSNameNodeScale_VariableName),
fontStyle: FontStyle.Italic
}
]
}, {
fields: [
{
type: FieldType.ReadonlyText,
label: localize('deployCluster.ZooKeeperText', "ZooKeeper"),
defaultValue: this.wizard.model.getStringValue(VariableNames.ZooKeeperScale_VariableName),
fontStyle: FontStyle.Italic
}, {
type: FieldType.ReadonlyText,
label: localize('deployCluster.SparkHeadText', "SparkHead"),
defaultValue: this.wizard.model.getStringValue(VariableNames.SparkHeadScale_VariableName),
fontStyle: FontStyle.Italic
}
]
}
]
};
const createSectionFunc = (sectionInfo: SectionInfo): azdata.FormComponent => {
return {
title: '',
component: createSection({
container: this.wizard.wizardObject,
sectionInfo: sectionInfo,
view: this.view,
onNewDisposableCreated: () => { },
onNewInputComponentCreated: () => { },
onNewValidatorCreated: () => { }
})
};
};
if (this.wizard.deploymentType === BdcDeploymentType.ExistingAKS || this.wizard.deploymentType === BdcDeploymentType.ExistingKubeAdm) {
const deploymentTargetSection = createSectionFunc(deploymentTargetSectionInfo);
this.formItems.push(deploymentTargetSection);
}
const clusterSection = createSectionFunc(clusterSectionInfo);
const scaleSection = createSectionFunc(scaleSectionInfo);
const hadrSection = createSectionFunc(hadrSectionInfo);
const endpointSection = {
title: '',
component: this.createEndpointSection()
};
const storageSection = {
title: '',
component: this.createStorageSection()
};
if (this.wizard.model.getStringValue(VariableNames.AksName_VariableName)) {
const azureSection = createSectionFunc(azureSectionInfo);
this.formItems.push(azureSection);
}
this.formItems.push(clusterSection, scaleSection, hadrSection, endpointSection, storageSection);
this.form.addFormItems(this.formItems);
}
private getStorageSettingValue(propertyName: string, defaultValuePropertyName: string): string | undefined {
const value = this.wizard.model.getStringValue(propertyName);
return (value === undefined || value === '') ? this.wizard.model.getStringValue(defaultValuePropertyName) : value;
}
private createStorageSection(): azdata.GroupContainer {
const serviceNameColumn: azdata.TableColumn = {
value: ' ',
width: 150
};
const dataStorageClassColumn: azdata.TableColumn = {
value: localize('deployCluster.DataStorageClassName', "Storage class for data"),
width: 180
};
const dataStorageSizeColumn: azdata.TableColumn = {
value: localize('deployCluster.DataClaimSize', "Claim size for data (GB)"),
width: 180
};
const logStorageClassColumn: azdata.TableColumn = {
value: localize('deployCluster.LogStorageClassName', "Storage class for logs"),
width: 180
};
const logStorageSizeColumn: azdata.TableColumn = {
value: localize('deployCluster.LogsClaimSize', "Claim size for logs (GB)"),
width: 180
};
const storageTable = this.view.modelBuilder.table().withProperties<azdata.TableComponentProperties>({
data: [
[
localize('deployCluster.ControllerText', "Controller"),
this.wizard.model.getStringValue(VariableNames.ControllerDataStorageClassName_VariableName),
this.wizard.model.getStringValue(VariableNames.ControllerDataStorageSize_VariableName),
this.wizard.model.getStringValue(VariableNames.ControllerLogsStorageClassName_VariableName),
this.wizard.model.getStringValue(VariableNames.ControllerLogsStorageSize_VariableName)],
[
localize('deployCluster.HDFSText', "HDFS"),
this.getStorageSettingValue(VariableNames.HDFSDataStorageClassName_VariableName, VariableNames.ControllerDataStorageClassName_VariableName),
this.getStorageSettingValue(VariableNames.HDFSDataStorageSize_VariableName, VariableNames.ControllerDataStorageSize_VariableName),
this.getStorageSettingValue(VariableNames.HDFSLogsStorageClassName_VariableName, VariableNames.ControllerLogsStorageClassName_VariableName),
this.getStorageSettingValue(VariableNames.HDFSLogsStorageSize_VariableName, VariableNames.ControllerLogsStorageSize_VariableName)
], [
localize('deployCluster.DataText', "Data"),
this.getStorageSettingValue(VariableNames.DataPoolDataStorageClassName_VariableName, VariableNames.ControllerDataStorageClassName_VariableName),
this.getStorageSettingValue(VariableNames.DataPoolDataStorageSize_VariableName, VariableNames.ControllerDataStorageSize_VariableName),
this.getStorageSettingValue(VariableNames.DataPoolLogsStorageClassName_VariableName, VariableNames.ControllerLogsStorageClassName_VariableName),
this.getStorageSettingValue(VariableNames.DataPoolLogsStorageSize_VariableName, VariableNames.ControllerLogsStorageSize_VariableName)
], [
localize('deployCluster.MasterSqlText', "SQL Server Master"),
this.getStorageSettingValue(VariableNames.SQLServerDataStorageClassName_VariableName, VariableNames.ControllerDataStorageClassName_VariableName),
this.getStorageSettingValue(VariableNames.SQLServerDataStorageSize_VariableName, VariableNames.ControllerDataStorageSize_VariableName),
this.getStorageSettingValue(VariableNames.SQLServerLogsStorageClassName_VariableName, VariableNames.ControllerLogsStorageClassName_VariableName),
this.getStorageSettingValue(VariableNames.SQLServerLogsStorageSize_VariableName, VariableNames.ControllerLogsStorageSize_VariableName)
]
],
columns: [serviceNameColumn, dataStorageClassColumn, dataStorageSizeColumn, logStorageClassColumn, logStorageSizeColumn],
width: '1000px',
height: '140px'
}).component();
return createGroupContainer(this.view, [storageTable], {
header: localize('deployCluster.StorageSettings', "Storage settings"),
collapsible: true
});
}
private createEndpointSection(): azdata.GroupContainer {
const endpointRows = [
this.createEndpointRow(localize('deployCluster.ControllerText', "Controller"), VariableNames.ControllerDNSName_VariableName, VariableNames.ControllerPort_VariableName),
this.createEndpointRow(localize('deployCluster.SqlServerText', "SQL Server Master"), VariableNames.SQLServerDNSName_VariableName, VariableNames.SQLServerPort_VariableName),
this.createEndpointRow(localize('deployCluster.GatewayText', "Gateway"), VariableNames.GatewayDNSName_VariableName, VariableNames.GateWayPort_VariableName)
];
if (this.wizard.model.hadrEnabled) {
endpointRows.push(
this.createEndpointRow(localize('deployCluster.ReadableSecondaryText', "Readable secondary"), VariableNames.ReadableSecondaryDNSName_VariableName, VariableNames.ReadableSecondaryPort_VariableName)
);
}
return createGroupContainer(this.view, endpointRows, {
header: localize('deployCluster.EndpointSettings', "Endpoint settings"),
collapsible: true
});
}
private createEndpointRow(name: string, dnsVariableName: string, portVariableName: string): azdata.FlexContainer {
const items = [];
items.push(createLabel(this.view, { text: name, width: '150px' }));
if (this.wizard.model.authenticationMode === AuthenticationMode.ActiveDirectory) {
items.push(createLabel(this.view, { text: this.wizard.model.getStringValue(dnsVariableName)!, width: '200px', fontStyle: FontStyle.Italic }));
}
items.push(createLabel(this.view, { text: this.wizard.model.getStringValue(portVariableName)!, width: '100px', fontStyle: FontStyle.Italic }));
return createFlexContainer(this.view, items);
}
private createSaveJsonButton(label: string, fileName: string, getContent: () => string): azdata.ButtonComponent {
const button = this.view.modelBuilder.button().withProperties<azdata.ButtonProperties>({
title: label,
label: fileName,
ariaLabel: label,
width: '150px'
}).component();
this.wizard.registerDisposable(button.onDidClick(() => {
vscode.window.showSaveDialog({
defaultUri: vscode.Uri.file(join(os.homedir(), fileName)),
filters: {
'JSON': ['json']
}
}).then((path) => {
if (path) {
fs.promises.writeFile(path.fsPath, getContent()).then(() => {
this.wizard.wizardObject.message = {
text: localize('deployCluster.SaveJsonFileMessage', "File saved: {0}", path.fsPath),
level: azdata.window.MessageLevel.Information
};
}).catch((error) => {
this.wizard.wizardObject.message = {
text: error.message,
level: azdata.window.MessageLevel.Error
};
});
}
});
}));
return button;
}
}

View File

@@ -0,0 +1,176 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import * as azdata from 'azdata';
import * as os from 'os';
import * as vscode from 'vscode';
import * as nls from 'vscode-nls';
import { DeployClusterWizard } from '../deployClusterWizard';
import { WizardPageBase } from '../../wizardPageBase';
import { KubeClusterContext } from '../../../services/kubeService';
import { ClusterContext_VariableName, KubeConfigPath_VariableName } from '../constants';
const localize = nls.loadMessageBundle();
const ClusterRadioButtonGroupName = 'ClusterRadioGroup';
export class TargetClusterContextPage extends WizardPageBase<DeployClusterWizard> {
private existingClusterControl: azdata.FlexContainer | undefined;
private clusterContextsLabel: azdata.TextComponent | undefined;
private errorLoadingClustersLabel: azdata.TextComponent | undefined;
private clusterContextList: azdata.DivContainer | undefined;
private clusterContextLoadingComponent: azdata.LoadingComponent | undefined;
private configFileInput: azdata.InputBoxComponent | undefined;
private browseFileButton: azdata.ButtonComponent | undefined;
private loadDefaultKubeConfigFile: boolean = true;
private view: azdata.ModelView | undefined;
constructor(wizard: DeployClusterWizard) {
super(localize('deployCluster.TargetClusterContextPageTitle', "Target cluster context"),
localize('deployCluster.TargetClusterContextPageDescription', "Select the kube config file and then select a cluster context from the list"), wizard);
}
public initialize(): void {
this.pageObject.registerContent((view: azdata.ModelView) => {
this.view = view;
this.initExistingClusterControl();
let formBuilder = view.modelBuilder.formContainer().withFormItems(
[
{
component: this.existingClusterControl!,
title: ''
}
],
{
horizontal: false
}
).withLayout({ width: '100%', height: '100%' });
const form = formBuilder.withLayout({ width: '100%' }).component();
return view.initializeModel(form);
});
}
public onEnter() {
if (this.loadDefaultKubeConfigFile) {
let defaultKubeConfigPath = this.wizard.kubeService.getDefautConfigPath();
this.loadClusterContexts(defaultKubeConfigPath);
this.loadDefaultKubeConfigFile = false;
}
this.wizard.wizardObject.registerNavigationValidator((e) => {
if (e.lastPage > e.newPage) {
this.wizard.wizardObject.message = { text: '' };
return true;
}
let clusterSelected = this.wizard.model.getStringValue(ClusterContext_VariableName) !== undefined;
if (!clusterSelected) {
this.wizard.wizardObject.message = {
text: localize('deployCluster.ClusterContextNotSelectedMessage', 'Please select a cluster context.'),
level: azdata.window.MessageLevel.Error
};
}
return clusterSelected;
});
}
public onLeave() {
this.wizard.wizardObject.registerNavigationValidator((e) => {
return true;
});
}
private initExistingClusterControl(): void {
let self = this;
const labelWidth = '150px';
let configFileLabel = this.view!.modelBuilder.text().withProperties({ value: localize('deployCluster.kubeConfigFileLabelText', 'Kube config file path') }).component();
configFileLabel.width = labelWidth;
this.configFileInput = this.view!.modelBuilder.inputBox().withProperties({ width: '300px' }).component();
this.configFileInput.enabled = false;
this.browseFileButton = this.view!.modelBuilder.button().withProperties({ label: localize('deployCluster.browseText', 'Browse'), width: '100px' }).component();
let configFileContainer = this.view!.modelBuilder.flexContainer()
.withLayout({ flexFlow: 'row', alignItems: 'baseline' })
.withItems([configFileLabel, this.configFileInput, this.browseFileButton], { CSSStyles: { 'margin-right': '10px' } }).component();
this.clusterContextsLabel = this.view!.modelBuilder.text().withProperties({ value: localize('deployCluster.clusterContextsLabelText', 'Cluster Contexts') }).component();
this.clusterContextsLabel.width = labelWidth;
this.errorLoadingClustersLabel = this.view!.modelBuilder.text().withProperties({ value: localize('deployCluster.errorLoadingClustersText', 'No cluster information is found in the config file or an error ocurred while loading the config file') }).component();
this.clusterContextList = this.view!.modelBuilder.divContainer().component();
this.clusterContextLoadingComponent = this.view!.modelBuilder.loadingComponent().withItem(this.clusterContextList).component();
this.existingClusterControl = this.view!.modelBuilder.divContainer().withProperties<azdata.DivContainerProperties>({ clickable: false }).component();
let clusterContextContainer = this.view!.modelBuilder.flexContainer().withLayout({ flexFlow: 'row', alignItems: 'start' }).component();
clusterContextContainer.addItem(this.clusterContextsLabel, { flex: '0 0 auto' });
clusterContextContainer.addItem(this.clusterContextLoadingComponent, { flex: '0 0 auto', CSSStyles: { 'width': '400px', 'margin-left': '10px', 'margin-top': '10px' } });
this.existingClusterControl.addItem(configFileContainer, { CSSStyles: { 'margin-top': '0px' } });
this.existingClusterControl.addItem(clusterContextContainer, {
CSSStyles: { 'margin- top': '10px' }
});
this.wizard.registerDisposable(this.browseFileButton.onDidClick(async () => {
let fileUris = await vscode.window.showOpenDialog(
{
canSelectFiles: true,
canSelectFolders: false,
canSelectMany: false,
defaultUri: vscode.Uri.file(os.homedir()),
openLabel: localize('deployCluster.selectKubeConfigFileText', 'Select'),
filters: {
'Config Files': ['*'],
}
}
);
if (!fileUris || fileUris.length === 0) {
return;
}
self.clusterContextList!.clearItems();
let fileUri = fileUris[0];
self.loadClusterContexts(fileUri.fsPath);
}));
}
private async loadClusterContexts(configPath: string): Promise<void> {
this.clusterContextLoadingComponent!.loading = true;
this.wizard.model.setPropertyValue(ClusterContext_VariableName, undefined);
this.wizard.wizardObject.message = { text: '' };
let self = this;
this.configFileInput!.value = configPath;
let clusterContexts: KubeClusterContext[] = [];
try {
clusterContexts = await this.wizard.kubeService.getClusterContexts(configPath);
} catch (error) {
this.wizard.wizardObject.message = {
text: localize('deployCluster.ConfigParseError', "Failed to load the config file"),
description: error.message || error, level: azdata.window.MessageLevel.Error
};
}
if (clusterContexts.length !== 0) {
self.wizard.model.setPropertyValue(KubeConfigPath_VariableName, configPath);
let options = clusterContexts.map(clusterContext => {
let option = this.view!.modelBuilder.radioButton().withProperties<azdata.RadioButtonProperties>({
label: clusterContext.name,
checked: clusterContext.isCurrentContext,
name: ClusterRadioButtonGroupName
}).component();
if (clusterContext.isCurrentContext) {
self.wizard.model.setPropertyValue(ClusterContext_VariableName, clusterContext.name);
self.wizard.wizardObject.message = { text: '' };
}
this.wizard.registerDisposable(option.onDidClick(() => {
self.wizard.model.setPropertyValue(ClusterContext_VariableName, clusterContext.name);
self.wizard.wizardObject.message = { text: '' };
}));
return option;
});
self.clusterContextList!.addItems(options);
} else {
self.clusterContextList!.addItem(this.errorLoadingClustersLabel!);
}
this.clusterContextLoadingComponent!.loading = false;
}
}