deploy BDC wizard improvement for CU1 (#7756)

* unified admin user account (#7485)

* azdata changes

* spaces

* error message

* comments

* support AD authentication for bdc deployment (#7518)

* enable ad authentication

* remove export for internal interface

* add comments

* more changes after testing

* update notebooks

* escape slash

* more comments

* Update deploy-bdc-aks.ipynb

* Update deploy-bdc-existing-aks.ipynb

* Update deploy-bdc-existing-kubeadm.ipynb

* AD changes and review feedback (#7618)

* enable ad authentication

* remove export for internal interface

* add comments

* more changes after testing

* update notebooks

* escape slash

* more comments

* Update deploy-bdc-aks.ipynb

* Update deploy-bdc-existing-aks.ipynb

* Update deploy-bdc-existing-kubeadm.ipynb

* address comments from scenario review (#7546)

* support AD authentication for bdc deployment (#7518)

* enable ad authentication

* remove export for internal interface

* add comments

* more changes after testing

* update notebooks

* escape slash

* more comments

* Update deploy-bdc-aks.ipynb

* Update deploy-bdc-existing-aks.ipynb

* Update deploy-bdc-existing-kubeadm.ipynb

* scenario review feedbacks

* more fixes

* adjust the display order of resource types

* different way to implement left side buttons

* revert unwanted changes

* rename variable

* more fixes for the scenario review feedback (#7589)

* fix more issues

* add help links

* model view readonly text with links

* fix size string

* address comments

* update notebooks

* text update

* address the feedback of 2nd round of deploy BDC wizard review (#7646)

* 2nd review meeting comments

* fix the unit test failure

* recent changes in azdata

* notebook background execution with azdata (#7741)

* notebook background execution with azdata

* prompt to open notebook in case of failure

* fix path quote issue

* better temp file handling

* expose docker settings (#7751)

* add docker settings

* new icon for container image
This commit is contained in:
Alan Ren
2019-10-16 20:41:15 -07:00
committed by GitHub
parent 5d4da455bd
commit 2ab7a47353
40 changed files with 2019 additions and 730 deletions

View File

@@ -8,17 +8,20 @@ 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 OrganizationalUnitDistinguishedName_VariableName = 'AZDATA_NB_VAR_BDC_AD_OUDN';
export const ClusterAdmins_VariableName = 'AZDATA_NB_VAR_BDC_AD_CLUSTER_ADMINS';
export const ClusterUsers_VariableName = 'AZDATA_NB_VAR_BDC_AD_CLUSTER_USERS';
export const DomainDNSIPAddresses_VariableName = 'AZDATA_NB_VAR_BDC_AD_UPSTREAM_IPADDRESSES';
export const DomainControllerFQDNs_VariableName = 'AZDATA_NB_VAR_BDC_AD_DC_FQDNs';
export const DomainDNSName_VariableName = 'AZDATA_NB_VAR_BDC_AD_DOMAIN_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 DomainServiceAccountUserName_VariableName = 'AZDATA_NB_VAR_BDC_AD_DOMAIN_SVC_USERNAME';
export const DomainServiceAccountPassword_VariableName = 'AZDATA_NB_VAR_BDC_AD_DOMAIN_SVC_PASSWORD';
export const AppOwners_VariableName = 'AZDATA_NB_VAR_AD_BDC_APP_OWNERS';
export const AppReaders_VariableName = 'AZDATA_NB_VAR_AD_BDC_APP_READERS';
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 Location_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';
@@ -57,4 +60,12 @@ 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';
export const ServiceProxyDNSName_VariableName = 'AZDATA_NB_VAR_BDC_SERVICEPROXY_DNS';
export const ServiceProxyPort_VariableName = 'AZDATA_NB_VAR_BDC_SERVICEPROXY_PORT';
export const AppServiceProxyDNSName_VariableName = 'AZDATA_NB_VAR_BDC_APPSERVICEPROXY_DNS';
export const AppServiceProxyPort_VariableName = 'AZDATA_NB_VAR_BDC_APPSERVICEPROXY_PORT';
export const DockerRepository_VariableName = 'AZDATA_NB_VAR_BDC_REPOSITORY';
export const DockerRegistry_VariableName = 'AZDATA_NB_VAR_BDC_REGISTRY';
export const DockerImageTag_VariableName = 'AZDATA_NB_VAR_BDC_DOCKER_IMAGE_TAG';
export const DockerUsername_VariableName = 'AZDATA_NB_VAR_BDC_DOCKER_USERNAME';
export const DockerPassword_VariableName = 'AZDATA_NB_VAR_BDC_DOCKER_PASSWORD';

View File

@@ -15,14 +15,20 @@ 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 { IAzdataService, BdcEndpoint } from '../../services/azdataService';
import { DeploymentProfilePage } from './pages/deploymentProfilePage';
import { INotebookService } from '../../services/notebookService';
import { DeployClusterWizardModel } from './deployClusterWizardModel';
import { getErrorMessage, getDateTimeString } from '../../utils';
import { DeployClusterWizardModel, AuthenticationMode } from './deployClusterWizardModel';
import * as VariableNames from './constants';
import * as os from 'os';
import { join } from 'path';
import * as fs from 'fs';
const localize = nls.loadMessageBundle();
export class DeployClusterWizard extends WizardBase<DeployClusterWizard, DeployClusterWizardModel> {
private _saveConfigButton: azdata.window.Button;
private _scriptToNotebookButton: azdata.window.Button;
public get kubeService(): IKubeService {
return this._kubeService;
@@ -36,8 +42,24 @@ export class DeployClusterWizard extends WizardBase<DeployClusterWizard, DeployC
return this._notebookService;
}
public get saveConfigButton(): azdata.window.Button {
return this._saveConfigButton;
}
public get scriptToNotebookButton(): azdata.window.Button {
return this._scriptToNotebookButton;
}
constructor(private wizardInfo: WizardInfo, private _kubeService: IKubeService, private _azdataService: IAzdataService, private _notebookService: INotebookService) {
super(DeployClusterWizard.getTitle(wizardInfo.type), new DeployClusterWizardModel(wizardInfo.type));
this._saveConfigButton = azdata.window.createButton(localize('deployCluster.SaveConfigFiles', "Save config files"), 'left');
this._saveConfigButton.hidden = true;
this._scriptToNotebookButton = azdata.window.createButton(localize('deployCluster.ScriptToNotebook', 'Script to Notebook'), 'left');
this._scriptToNotebookButton.hidden = true;
this.addButton(this._saveConfigButton);
this.addButton(this._scriptToNotebookButton);
this.registerDisposable(this._saveConfigButton.onClick(() => this.saveConfigFiles()));
this.registerDisposable(this._scriptToNotebookButton.onClick(() => this.scriptToNotebook()));
}
public get deploymentType(): BdcDeploymentType {
@@ -47,23 +69,79 @@ export class DeployClusterWizard extends WizardBase<DeployClusterWizard, DeployC
protected initialize(): void {
this.setPages(this.getPages());
this.wizardObject.generateScriptButton.hidden = true;
this.wizardObject.doneButton.label = localize('deployCluster.ScriptToNotebook', 'Script to Notebook');
this.wizardObject.doneButton.label = localize('deployCluster.Deploy', "Deploy");
}
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({
const taskName = localize('resourceDeployment.DeployBDCTask', "Deploy SQL Server Big Data Cluster \"{0}\"", this.model.getStringValue(VariableNames.ClusterName_VariableName));
azdata.tasks.startBackgroundOperation({
displayName: taskName,
description: taskName,
isCancelable: false,
operation: async op => {
op.updateStatus(azdata.TaskStatus.InProgress);
const env: NodeJS.ProcessEnv = {};
this.setEnvironmentVariables(env);
const notebook = await this.notebookService.getNotebook(this.wizardInfo.azdata_notebook);
notebook.cells.splice(3, 0, {
cell_type: 'code',
source: this.model.getCodeCellContentForNotebook()
}, 7);
});
}, (error) => {
vscode.window.showErrorMessage(error);
source: this.model.getCodeCellContentForNotebook(),
metadata: {},
execution_count: 0,
outputs: []
});
const result = await this.notebookService.executeNotebook(notebook, env);
if (result.succeeded) {
op.updateStatus(azdata.TaskStatus.Succeeded);
const connectToMasterSql = localize('resourceDeployment.ConnectToMasterSQLServer', "Connect to Master SQL Server");
const selectedOption = await vscode.window.showInformationMessage(localize('resourceDeployment.DeploymentSucceeded', "Successfully deployed SQL Server Big Data Cluster: {0}",
this.model.getStringValue(VariableNames.ClusterName_VariableName)),
connectToMasterSql);
if (selectedOption === connectToMasterSql) {
let endpoints: BdcEndpoint[];
try {
endpoints = await this.azdataService.getEndpoints(this.model.getStringValue(VariableNames.ClusterName_VariableName)!,
this.model.getStringValue(VariableNames.AdminUserName_VariableName)!,
this.model.getStringValue(VariableNames.AdminPassword_VariableName)!);
} catch (error) {
vscode.window.showErrorMessage(localize('resourceDeployment.ErroRetrievingEndpoints', "Failed to retrieve the endpoint list. {0}{1}", os.EOL, getErrorMessage(error)));
return;
}
const sqlEndpoint = endpoints.find(endpoint => endpoint.name === 'sql-server-master');
if (sqlEndpoint) {
vscode.commands.executeCommand('azdata.connect', {
serverName: sqlEndpoint.endpoint,
providerName: 'MSSQL',
authenticationType: 'SqlLogin',
userName: this.model.getStringValue(VariableNames.AdminUserName_VariableName)!,
password: this.model.getStringValue(VariableNames.AdminPassword_VariableName)!
});
} else {
vscode.window.showErrorMessage(localize('resourceDeployment.NoSQLEndpointFound', "Master SQL Server endpoint is not found."));
}
}
} else {
op.updateStatus(azdata.TaskStatus.Failed, result.errorMessage);
if (result.outputNotebook) {
const viewErrorDetail = localize('resourceDeployment.ViewErrorDetail', "View error detail");
const selectedOption = await vscode.window.showErrorMessage(localize('resourceDeployment.DeployFailed', "Failed to deploy SQL Server Big Data Cluster \"{0}\".",
this.model.getStringValue(VariableNames.ClusterName_VariableName)),
viewErrorDetail);
if (selectedOption === viewErrorDetail) {
try {
this.notebookService.launchNotebookWithContent(`deploy-${getDateTimeString()}`, result.outputNotebook);
} catch (error) {
vscode.window.showErrorMessage(localize('resourceDeployment.FailedToOpenNotebook', "An error occured launching the output notebook. {1}{2}.", os.EOL, getErrorMessage(error)));
}
}
} else {
vscode.window.showErrorMessage(localize('resourceDeployment.DeployFailedNoOutputNotebook', "Failed to deploy SQL Server Big Data Cluster and no output notebook was generated."));
}
}
}
});
}
@@ -100,6 +178,58 @@ export class DeployClusterWizard extends WizardBase<DeployClusterWizard, DeployC
return pages;
}
private async saveConfigFiles(): Promise<void> {
const options: vscode.OpenDialogOptions = {
defaultUri: vscode.Uri.file(os.homedir()),
canSelectFiles: false,
canSelectFolders: true,
canSelectMany: false,
openLabel: localize('deployCluster.SelectConfigFileFolder', "Save config files")
};
const pathArray = await vscode.window.showOpenDialog(options);
if (pathArray && pathArray[0]) {
const targetFolder = pathArray[0].fsPath;
try {
const profile = this.model.createTargetProfile();
await fs.promises.writeFile(join(targetFolder, 'bdc.json'), profile.getBdcJson());
await fs.promises.writeFile(join(targetFolder, 'control.json'), profile.getControlJson());
this.wizardObject.message = {
text: localize('deployCluster.SaveConfigFileSucceeded', "Config files saved to {0}", targetFolder),
level: azdata.window.MessageLevel.Information
};
}
catch (error) {
this.wizardObject.message = {
text: error.message,
level: azdata.window.MessageLevel.Error
};
}
}
}
private scriptToNotebook(): void {
this.setEnvironmentVariables(process.env);
this.notebookService.launchNotebook(this.wizardInfo.notebook).then((notebook: azdata.nb.NotebookEditor) => {
notebook.edit((editBuilder: azdata.nb.NotebookEditorEdit) => {
// 5 is the position after the 'Set variables' cell in the deployment notebooks
editBuilder.insertCell({
cell_type: 'code',
source: this.model.getCodeCellContentForNotebook()
}, 5);
});
}, (error) => {
vscode.window.showErrorMessage(error);
});
}
private setEnvironmentVariables(env: NodeJS.ProcessEnv): void {
env[VariableNames.AdminPassword_VariableName] = this.model.getStringValue(VariableNames.AdminPassword_VariableName);
env[VariableNames.DockerPassword_VariableName] = this.model.getStringValue(VariableNames.DockerPassword_VariableName);
if (this.model.authenticationMode === AuthenticationMode.ActiveDirectory) {
env[VariableNames.DomainServiceAccountPassword_VariableName] = this.model.getStringValue(VariableNames.DomainServiceAccountPassword_VariableName);
}
}
static getTitle(type: BdcDeploymentType): string {
switch (type) {
case BdcDeploymentType.NewAKS:

View File

@@ -15,14 +15,6 @@ export class DeployClusterWizardModel extends Model {
}
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);
}
@@ -71,6 +63,13 @@ export class DeployClusterWizardModel extends Model {
const sourceBdcJson = Object.assign({}, this.selectedProfile!.bdcConfig);
const sourceControlJson = Object.assign({}, this.selectedProfile!.controlConfig);
const targetDeploymentProfile = new BigDataClusterDeploymentProfile('', sourceBdcJson, sourceControlJson);
// docker settings
targetDeploymentProfile.controlConfig.spec.docker = {
registry: this.getStringValue(VariableNames.DockerRegistry_VariableName),
repository: this.getStringValue(VariableNames.DockerRepository_VariableName),
imageTag: this.getStringValue(VariableNames.DockerImageTag_VariableName),
imagePullPolicy: 'Always'
};
// cluster name
targetDeploymentProfile.clusterName = this.getStringValue(VariableNames.ClusterName_VariableName)!;
// storage settings
@@ -111,23 +110,37 @@ export class DeployClusterWizardModel extends Model {
}
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);
// endpoint settings
targetDeploymentProfile.setGatewayEndpoint(this.getIntegerValue(VariableNames.GateWayPort_VariableName), this.getStringValue(VariableNames.GatewayDNSName_VariableName));
targetDeploymentProfile.setSqlServerEndpoint(this.getIntegerValue(VariableNames.SQLServerPort_VariableName), this.getStringValue(VariableNames.SQLServerDNSName_VariableName));
targetDeploymentProfile.setControllerEndpoint(this.getIntegerValue(VariableNames.ControllerPort_VariableName), this.getStringValue(VariableNames.ControllerDNSName_VariableName));
targetDeploymentProfile.setSqlServerReadableSecondaryEndpoint(this.getIntegerValue(VariableNames.ReadableSecondaryPort_VariableName), this.getStringValue(VariableNames.ReadableSecondaryDNSName_VariableName));
targetDeploymentProfile.setServiceProxyEndpoint(this.getIntegerValue(VariableNames.ServiceProxyPort_VariableName), this.getStringValue(VariableNames.ServiceProxyDNSName_VariableName));
targetDeploymentProfile.setAppServiceProxyEndpoint(this.getIntegerValue(VariableNames.AppServiceProxyPort_VariableName), this.getStringValue(VariableNames.AppServiceProxyDNSName_VariableName));
targetDeploymentProfile.setAuthenticationMode(this.authenticationMode!);
if (this.authenticationMode === AuthenticationMode.ActiveDirectory) {
targetDeploymentProfile.setActiveDirectorySettings({
organizationalUnit: this.getStringValue(VariableNames.OrganizationalUnitDistinguishedName_VariableName)!,
domainControllerFQDNs: this.getStringValue(VariableNames.DomainControllerFQDNs_VariableName)!,
domainDNSName: this.getStringValue(VariableNames.DomainDNSName_VariableName)!,
dnsIPAddresses: this.getStringValue(VariableNames.DomainDNSIPAddresses_VariableName)!,
clusterAdmins: this.getStringValue(VariableNames.ClusterAdmins_VariableName)!,
clusterUsers: this.getStringValue(VariableNames.ClusterUsers_VariableName)!,
appOwners: this.getStringValue(VariableNames.AppOwners_VariableName),
appReaders: this.getStringValue(VariableNames.AppReaders_VariableName)
});
}
return targetDeploymentProfile;
}
public getCodeCellContentForNotebook(): string {
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_region = '${this.getStringValue(VariableNames.Location_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)}'`);
@@ -137,12 +150,20 @@ export class DeployClusterWizardModel extends Model {
statements.push(`mssql_cluster_context = '${this.getStringValue(VariableNames.ClusterContext_VariableName)}'`);
statements.push('os.environ["KUBECONFIG"] = mssql_kube_config_path');
}
if (this.authenticationMode === AuthenticationMode.ActiveDirectory) {
statements.push(`mssql_domain_service_account_username = '${this.escapeForNotebookCodeCell(this.getStringValue(VariableNames.DomainServiceAccountUserName_VariableName)!)}'`);
}
statements.push(`mssql_cluster_name = '${this.getStringValue(VariableNames.ClusterName_VariableName)}'`);
statements.push(`mssql_controller_username = '${this.getStringValue(VariableNames.AdminUserName_VariableName)}'`);
statements.push(`mssql_username = '${this.getStringValue(VariableNames.AdminUserName_VariableName)}'`);
statements.push(`mssql_auth_mode = '${this.authenticationMode}'`);
statements.push(`bdc_json = '${profile.getBdcJson(false)}'`);
statements.push(`control_json = '${profile.getControlJson(false)}'`);
if (this.getStringValue(VariableNames.DockerUsername_VariableName) && this.getStringValue(VariableNames.DockerPassword_VariableName)) {
statements.push(`os.environ["DOCKER_USERNAME"] = '${this.getStringValue(VariableNames.DockerUsername_VariableName)}'`);
statements.push(`os.environ["DOCKER_PASSWORD"] = os.environ["${VariableNames.DockerPassword_VariableName}"]`);
}
statements.push(`print('Variables have been set successfully.')`);
return statements.join(EOL);
return statements.map(line => line + EOL);
}
private escapeForNotebookCodeCell(original: string): string {

View File

@@ -9,8 +9,8 @@ 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';
import { createSection, InputComponents, setModelValues, Validator, getDropdownComponent, MissingRequiredInformationErrorMessage } from '../../modelViewUtils';
import { SubscriptionId_VariableName, ResourceGroup_VariableName, Location_VariableName, AksName_VariableName, VMCount_VariableName, VMSize_VariableName } from '../constants';
const localize = nls.loadMessageBundle();
export class AzureSettingsPage extends WizardPageBase<DeployClusterWizard> {
@@ -26,8 +26,9 @@ export class AzureSettingsPage extends WizardPageBase<DeployClusterWizard> {
const azureSection: SectionInfo = {
title: '',
labelPosition: LabelPosition.Left,
fields: [
{
spaceBetweenFields: '5px',
rows: [{
fields: [{
type: FieldType.Text,
label: localize('deployCluster.SubscriptionField', "Subscription id"),
required: false,
@@ -35,42 +36,100 @@ export class AzureSettingsPage extends WizardPageBase<DeployClusterWizard> {
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.ReadonlyText,
label: '',
labelWidth: '0px',
defaultValue: localize('deployCluster.SubscriptionHelpText', "{0}"),
links: [
{
text: localize('deployCluster.SubscriptionHelpLink', "View available Azure subscriptions"),
url: 'https://portal.azure.com/#blade/Microsoft_Azure_Billing/SubscriptionsBlade'
}
]
}]
}, {
fields: [{
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"),
}]
}, {
fields: [{
type: FieldType.Options,
label: localize('deployCluster.Location', "Location"),
required: true,
variableName: Region_VariableName,
defaultValue: 'eastus'
variableName: Location_VariableName,
defaultValue: 'eastus',
editable: true,
// The options are not localized because this is an editable dropdown,
// It would cause confusion to user about what value to type in, if they type in the localized value, we don't know how to process.
options: [
'centralus',
'eastus',
'eastus2',
'northcentralus',
'southcentralus',
'westus',
'westus2',
'canadacentral',
'canadaeast'
]
}, {
type: FieldType.ReadonlyText,
label: '',
labelWidth: '0px',
defaultValue: localize('deployCluster.LocationHelpText', "{0}"),
links: [
{
text: localize('deployCluster.AzureLocationHelpLink', "View available Azure locations"),
url: 'https://azure.microsoft.com/global-infrastructure/services/?products=kubernetes-service'
}
]
}]
}, {
fields: [{
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
}, {
}]
}, {
fields: [
{
type: FieldType.Number,
label: localize('deployCluster.VMCount', "VM count"),
required: true,
variableName: VMCount_VariableName,
defaultValue: '5',
min: 1,
max: 999
}
]
}, {
fields: [{
type: FieldType.Text,
label: localize('deployCluster.VMSize', "VM size"),
required: true,
variableName: VMSize_VariableName,
defaultValue: 'Standard_E4s_v3'
}
]
defaultValue: 'Standard_E8s_v3'
}, {
type: FieldType.ReadonlyText,
label: '',
labelWidth: '0px',
defaultValue: localize('deployCluster.VMSizeHelpText', "{0}"),
links: [
{
text: localize('deployCluster.VMSizeHelpLink', "View available VM sizes"),
url: 'https://docs.microsoft.com/azure/virtual-machines/linux/sizes'
}
]
}]
}]
};
this.pageObject.registerContent((view: azdata.ModelView) => {
const azureGroup = createSection({
sectionInfo: azureSection,
view: view,
@@ -101,7 +160,28 @@ export class AzureSettingsPage extends WizardPageBase<DeployClusterWizard> {
});
}
public onEnter(): void {
this.wizard.wizardObject.registerNavigationValidator((pcInfo) => {
this.wizard.wizardObject.message = { text: '' };
if (pcInfo.newPage > pcInfo.lastPage) {
const location = getDropdownComponent(Location_VariableName, this.inputComponents).value;
if (!location) {
this.wizard.wizardObject.message = {
text: MissingRequiredInformationErrorMessage,
level: azdata.window.MessageLevel.Error
};
}
return !!location;
} else {
return true;
}
});
}
public onLeave(): void {
this.wizard.wizardObject.registerNavigationValidator((pcInfo) => {
return true;
});
setModelValues(this.inputComponents, this.wizard.model);
}
}

View File

@@ -18,6 +18,8 @@ const localize = nls.loadMessageBundle();
const ConfirmPasswordName = 'ConfirmPassword';
export class ClusterSettingsPage extends WizardPageBase<DeployClusterWizard> {
private inputComponents: InputComponents = {};
private activeDirectorySection!: azdata.FormComponent;
private formBuilder!: azdata.FormBuilder;
constructor(wizard: DeployClusterWizard) {
super(localize('deployCluster.ClusterSettingsPageTitle', "Cluster settings"),
@@ -39,11 +41,12 @@ export class ClusterSettingsPage extends WizardPageBase<DeployClusterWizard> {
useCustomValidator: true
}, {
type: FieldType.Text,
label: localize('deployCluster.ControllerUsername', "Controller username"),
label: localize('deployCluster.AdminUsername', "Admin username"),
required: true,
variableName: VariableNames.AdminUserName_VariableName,
defaultValue: 'admin',
useCustomValidator: true
useCustomValidator: true,
description: localize('deployCluster.AdminUsernameDescription', "This username will be used for controller and SQL Server. Username for the gateway will be root.")
}, {
type: FieldType.Password,
label: localize('deployCluster.AdminPassword', "Password"),
@@ -51,7 +54,7 @@ export class ClusterSettingsPage extends WizardPageBase<DeployClusterWizard> {
variableName: VariableNames.AdminPassword_VariableName,
defaultValue: '',
useCustomValidator: true,
description: localize('deployCluster.AdminPasswordDescription', "You can also use this password to access SQL Server and gateway.")
description: localize('deployCluster.AdminPasswordDescription', "This password can be used to access the controller, SQL Server and gateway.")
}, {
type: FieldType.Password,
label: localize('deployCluster.ConfirmPassword', "Confirm password"),
@@ -80,58 +83,118 @@ export class ClusterSettingsPage extends WizardPageBase<DeployClusterWizard> {
]
};
const dockerSection: SectionInfo = {
labelPosition: LabelPosition.Left,
collapsed: true,
collapsible: true,
title: localize('deployCluster.DockerSettings', "Docker settings"),
fields: [
{
type: FieldType.Text,
label: localize('deployCluster.DockerRegistry', "Registry"),
required: true,
variableName: VariableNames.DockerRegistry_VariableName
}, {
type: FieldType.Text,
label: localize('deployCluster.DockerRepository', "Repository"),
required: true,
variableName: VariableNames.DockerRepository_VariableName
}, {
type: FieldType.Text,
label: localize('deployCluster.DockerImageTag', "Image tag"),
required: true,
variableName: VariableNames.DockerImageTag_VariableName
}, {
type: FieldType.Text,
label: localize('deployCluster.DockerUsername', "Username"),
required: false,
variableName: VariableNames.DockerUsername_VariableName
}, {
type: FieldType.Text,
label: localize('deployCluster.DockerPassword', "Password"),
required: false,
variableName: VariableNames.DockerPassword_VariableName
}
]
};
const activeDirectorySection: SectionInfo = {
labelPosition: LabelPosition.Left,
title: localize('deployCluster.ActiveDirectorySettings', "Active Directory settings"),
fields: [
{
type: FieldType.Text,
label: localize('deployCluster.DistinguishedName', "Distinguished name"),
label: localize('deployCluster.OuDistinguishedName', "Organizational unit"),
required: true,
variableName: VariableNames.DistinguishedName_VariableName,
variableName: VariableNames.OrganizationalUnitDistinguishedName_VariableName,
useCustomValidator: true,
description: localize('deployCluster.OuDistinguishedNameDescription', "Distinguished name for the organizational unit. For example: OU=bdc,DC=contoso,DC=com.")
}, {
type: FieldType.Text,
label: localize('deployCluster.DomainControllerFQDNs', "Domain controller FQDNs"),
required: true,
variableName: VariableNames.DomainControllerFQDNs_VariableName,
useCustomValidator: true,
placeHolder: localize('deployCluster.DomainControllerFQDNsPlaceHolder', "Use comma to separate the values."),
description: localize('deployCluster.DomainControllerFQDNDescription', "Fully qualified domain names for the domain controller. For example: DC1.CONTOSO.COM. Use comma to separate multiple FQDNs.")
}, {
type: FieldType.Text,
label: localize('deployCluster.DomainDNSIPAddresses', "Domain DNS IP addresses"),
required: true,
variableName: VariableNames.DomainDNSIPAddresses_VariableName,
useCustomValidator: true,
placeHolder: localize('deployCluster.DomainDNSIPAddressesPlaceHolder', "Use comma to separate the values."),
description: localize('deployCluster.DomainDNSIPAddressesDescription', "Domain DNS servers' IP Addresses. Use comma to separate multiple IP addresses.")
}, {
type: FieldType.Text,
label: localize('deployCluster.DomainDNSName', "Domain DNS name"),
required: true,
variableName: VariableNames.DomainDNSName_VariableName,
useCustomValidator: true
}, {
type: FieldType.Text,
label: localize('deployCluster.AdminPrincipals', "Admin principals"),
label: localize('deployCluster.ClusterAdmins', "Cluster admin group"),
required: true,
variableName: VariableNames.AdminPrincipals_VariableName,
variableName: VariableNames.ClusterAdmins_VariableName,
useCustomValidator: true,
description: localize('deployCluster.ClusterAdminsDescription', "The Active Directory group for cluster admin.")
}, {
type: FieldType.Text,
label: localize('deployCluster.ClusterUsers', "Cluster users"),
required: true,
variableName: VariableNames.ClusterUsers_VariableName,
useCustomValidator: true,
placeHolder: localize('deployCluster.ClusterUsersPlaceHolder', "Use comma to separate the values."),
description: localize('deployCluster.ClusterUsersDescription', "The Active Directory users/groups with cluster users role. Use comma to separate multiple users/groups.")
}, {
type: FieldType.Text,
label: localize('deployCluster.DomainServiceAccountUserName', "Service account username"),
required: true,
variableName: VariableNames.DomainServiceAccountUserName_VariableName,
useCustomValidator: true,
description: localize('deployCluster.DomainServiceAccountUserNameDescription', "Domain service account for Big Data Cluster")
}, {
type: FieldType.Password,
label: localize('deployCluster.DomainServiceAccountPassword', "Service account password"),
required: true,
variableName: VariableNames.DomainServiceAccountPassword_VariableName,
useCustomValidator: true
}, {
type: FieldType.Text,
label: localize('deployCluster.UserPrincipals', "User principals"),
required: true,
variableName: VariableNames.UserPrincipals_VariableName,
useCustomValidator: true
label: localize('deployCluster.AppOwers', "App owners"),
required: false,
variableName: VariableNames.AppOwners_VariableName,
useCustomValidator: true,
placeHolder: localize('deployCluster.AppOwnersPlaceHolder', "Use comma to separate the values."),
description: localize('deployCluster.AppOwnersDescription', "The Active Directory users or groups with app owners role. Use comma to separate multiple users/groups.")
}, {
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
label: localize('deployCluster.AppReaders', "App readers"),
required: false,
variableName: VariableNames.AppReaders_VariableName,
useCustomValidator: true,
placeHolder: localize('deployCluster.AppReadersPlaceHolder', "Use comma to separate the values."),
description: localize('deployCluster.AppReadersDescription', "The Active Directory users or groups of app readers. Use comma as separator them if there are multiple users/groups.")
}
]
};
@@ -164,11 +227,26 @@ export class ClusterSettingsPage extends WizardPageBase<DeployClusterWizard> {
self.validators.push(validator);
}
});
const dockerSettingsGroup = createSection({
view: view,
container: self.wizard.wizardObject,
sectionInfo: dockerSection,
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 dockerSettingsFormItem = { title: '', component: dockerSettingsGroup };
this.activeDirectorySection = { title: '', component: activeDirectorySettingsGroup };
const authModeDropdown = <azdata.DropDownComponent>this.inputComponents[VariableNames.AuthenticationMode_VariableName];
const formBuilder = view.modelBuilder.formContainer().withFormItems(
[basicSettingsFormItem],
this.formBuilder = view.modelBuilder.formContainer().withFormItems(
[basicSettingsFormItem, dockerSettingsFormItem],
{
horizontal: false,
componentWidth: '100%'
@@ -176,76 +254,99 @@ export class ClusterSettingsPage extends WizardPageBase<DeployClusterWizard> {
);
this.wizard.registerDisposable(authModeDropdown.onValueChanged(() => {
const isBasicAuthMode = (<azdata.CategoryValue>authModeDropdown.value).name === 'basic';
if (isBasicAuthMode) {
formBuilder.removeFormItem(activeDirectoryFormItem);
this.formBuilder.removeFormItem(this.activeDirectorySection);
} else {
formBuilder.insertFormItem(activeDirectoryFormItem);
this.formBuilder.insertFormItem(this.activeDirectorySection);
}
}));
const form = formBuilder.withLayout({ width: '100%' }).component();
const form = this.formBuilder.withLayout({ width: '100%' }).component();
return view.initializeModel(form);
});
}
public onLeave() {
setModelValues(this.inputComponents, this.wizard.model);
if (this.wizard.model.authenticationMode === AuthenticationMode.ActiveDirectory) {
const variableDNSPrefixMapping: { [s: string]: string } = {};
variableDNSPrefixMapping[VariableNames.AppServiceProxyDNSName_VariableName] = 'bdc-appproxy';
variableDNSPrefixMapping[VariableNames.ControllerDNSName_VariableName] = 'bdc-control';
variableDNSPrefixMapping[VariableNames.GatewayDNSName_VariableName] = 'bdc-gateway';
variableDNSPrefixMapping[VariableNames.ReadableSecondaryDNSName_VariableName] = 'bdc-sqlread';
variableDNSPrefixMapping[VariableNames.SQLServerDNSName_VariableName] = 'bdc-sql';
variableDNSPrefixMapping[VariableNames.ServiceProxyDNSName_VariableName] = 'bdc-proxy';
Object.keys(variableDNSPrefixMapping).forEach((variableName: string) => {
if (!this.wizard.model.getStringValue(variableName)) {
this.wizard.model.setPropertyValue(variableName, `${variableDNSPrefixMapping[variableName]}.${this.wizard.model.getStringValue(VariableNames.DomainDNSName_VariableName)}`);
}
});
}
this.wizard.wizardObject.registerNavigationValidator((pcInfo) => {
return true;
});
}
public onEnter() {
getInputBoxComponent(VariableNames.DockerRegistry_VariableName, this.inputComponents).value = this.wizard.model.getStringValue(VariableNames.DockerRegistry_VariableName);
getInputBoxComponent(VariableNames.DockerRepository_VariableName, this.inputComponents).value = this.wizard.model.getStringValue(VariableNames.DockerRepository_VariableName);
getInputBoxComponent(VariableNames.DockerImageTag_VariableName, this.inputComponents).value = this.wizard.model.getStringValue(VariableNames.DockerImageTag_VariableName);
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;
const adAuthSelected = (<azdata.CategoryValue>authModeDropdown.value).name === 'ad';
if (!this.wizard.model.adAuthSupported && adAuthSelected) {
this.formBuilder.removeFormItem(this.activeDirectorySection);
authModeDropdown.value = {
name: AuthenticationMode.Basic,
displayName: localize('deployCluster.AuthenticationMode.Basic', "Basic")
};
}
return true;
});
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.OrganizationalUnitDistinguishedName_VariableName, this.inputComponents))
&& !isInputBoxEmpty(getInputBoxComponent(VariableNames.DomainControllerFQDNs_VariableName, this.inputComponents))
&& !isInputBoxEmpty(getInputBoxComponent(VariableNames.ClusterAdmins_VariableName, this.inputComponents))
&& !isInputBoxEmpty(getInputBoxComponent(VariableNames.ClusterUsers_VariableName, this.inputComponents))
&& !isInputBoxEmpty(getInputBoxComponent(VariableNames.DomainDNSIPAddresses_VariableName, this.inputComponents))
&& !isInputBoxEmpty(getInputBoxComponent(VariableNames.DomainDNSName_VariableName, this.inputComponents))));
if (!requiredFieldsFilled) {
messages.push(MissingRequiredInformationErrorMessage);
}
if (!isInputBoxEmpty(getInputBoxComponent(VariableNames.AdminUserName_VariableName, this.inputComponents))
&& !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, getInputBoxComponent(VariableNames.AdminUserName_VariableName, this.inputComponents).value!)) {
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

@@ -81,31 +81,36 @@ export class DeploymentProfilePage extends WizardPageBase<DeployClusterWizard> {
}, {
label: '' // line separator
}, {
label: localize('deployCluster.defaultDataStorage', "Data storage size (GB)"),
label: localize('deployCluster.storageSize', "Storage size"),
value: localize('deployCluster.gbPerInstance', "GB per Instance"),
fontWeight: 'bold'
}, {
label: localize('deployCluster.defaultDataStorage', "Data storage"),
value: profile.controllerDataStorageSize.toString()
}, {
label: localize('deployCluster.defaultLogStorage', "Log storage size (GB)"),
label: localize('deployCluster.defaultLogStorage', "Log storage"),
value: profile.controllerLogsStorageSize.toString()
}, {
label: '' // line separator
}
];
}, {
label: localize('deployCluster.features', "Features"),
value: '',
fontWeight: 'bold'
}, {
label: localize('deployCluster.basicAuthentication', "Basic authentication"),
value: ''
}];
if (profile.activeDirectorySupported) {
descriptions.push({
label: localize('deployCluster.activeDirectoryAuthentication', "Active Directory authentication"),
value: ''
});
} else {
descriptions.push({
label: localize('deployCluster.basicAuthentication', "Basic authentication"),
value: '✅'
value: ''
});
}
if (profile.hadrEnabled) {
if (profile.sqlServerReplicas > 1) {
descriptions.push({
label: localize('deployCluster.hadr', "High Availability"),
value: ''
value: ''
});
}
@@ -114,7 +119,7 @@ export class DeploymentProfilePage extends WizardPageBase<DeployClusterWizard> {
label: profile.profileName,
descriptions: descriptions,
width: '240px',
height: '300px',
height: '320px',
}).component();
this._cards.push(card);
this.wizard.registerDisposable(card.onCardSelectedChanged(() => {
@@ -150,20 +155,24 @@ export class DeploymentProfilePage extends WizardPageBase<DeployClusterWizard> {
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.ServiceProxyPort_VariableName, selectedProfile.serviceProxyPort);
this.wizard.model.setPropertyValue(VariableNames.AppServiceProxyPort_VariableName, selectedProfile.appServiceProxyPort);
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.setPropertyValue(VariableNames.DockerRegistry_VariableName, selectedProfile.registry);
this.wizard.model.setPropertyValue(VariableNames.DockerRepository_VariableName, selectedProfile.repository);
this.wizard.model.setPropertyValue(VariableNames.DockerImageTag_VariableName, selectedProfile.imageTag);
this.wizard.model.adAuthSupported = selectedProfile.activeDirectorySupported;
this.wizard.model.selectedProfile = selectedProfile;
}
private loadCards(): Promise<void> {
return this.wizard.azdataService.getDeploymentProfiles().then((profiles: BigDataClusterDeploymentProfile[]) => {
return this.wizard.azdataService.getDeploymentProfiles(this.wizard.deploymentType).then((profiles: BigDataClusterDeploymentProfile[]) => {
const defaultProfile: string = this.getDefaultProfile();
profiles.forEach(profile => {

View File

@@ -13,9 +13,9 @@ import * as VariableNames from '../constants';
import { AuthenticationMode } from '../deployClusterWizardModel';
const localize = nls.loadMessageBundle();
const PortInputWidth = '100px';
const NumberInputWidth = '100px';
const inputWidth = '180px';
const labelWidth = '150px';
const labelWidth = '200px';
const spaceBetweenFields = '5px';
export class ServiceSettingsPage extends WizardPageBase<DeployClusterWizard> {
@@ -32,6 +32,12 @@ export class ServiceSettingsPage extends WizardPageBase<DeployClusterWizard> {
private gatewayDNSInput!: azdata.InputBoxComponent;
private gatewayPortInput!: azdata.InputBoxComponent;
private gatewayEndpointRow!: azdata.FlexContainer;
private serviceProxyDNSInput!: azdata.InputBoxComponent;
private serviceProxyPortInput!: azdata.InputBoxComponent;
private serviceProxyEndpointRow!: azdata.FlexContainer;
private appServiceProxyDNSInput!: azdata.InputBoxComponent;
private appServiceProxyPortInput!: azdata.InputBoxComponent;
private appServiceProxyEndpointRow!: azdata.FlexContainer;
private readableSecondaryDNSInput!: azdata.InputBoxComponent;
private readableSecondaryPortInput!: azdata.InputBoxComponent;
private readableSecondaryEndpointRow!: azdata.FlexContainer;
@@ -39,7 +45,10 @@ export class ServiceSettingsPage extends WizardPageBase<DeployClusterWizard> {
private controllerNameLabel!: azdata.TextComponent;
private SqlServerNameLabel!: azdata.TextComponent;
private gatewayNameLabel!: azdata.TextComponent;
private serviceProxyNameLabel!: azdata.TextComponent;
private appServiceProxyNameLabel!: azdata.TextComponent;
private readableSecondaryNameLabel!: azdata.TextComponent;
private endpointSection!: azdata.GroupContainer;
constructor(wizard: DeployClusterWizard) {
super(localize('deployCluster.ServiceSettingsPageTitle', "Service settings"), '', wizard);
@@ -48,37 +57,51 @@ export class ServiceSettingsPage extends WizardPageBase<DeployClusterWizard> {
const scaleSectionInfo: SectionInfo = {
title: localize('deployCluster.scaleSectionTitle', "Scale settings"),
labelWidth: labelWidth,
inputWidth: inputWidth,
spaceBetweenFields: spaceBetweenFields,
inputWidth: NumberInputWidth,
spaceBetweenFields: '40px',
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.Options,
label: localize('deployCluster.MasterSqlServerInstances', "SQL Server master instances"),
options: ['1', '3', '4', '5', '6', '7', '8', '9'],
defaultValue: '1',
required: true,
variableName: VariableNames.SQLServerScale_VariableName,
}, {
type: FieldType.Number,
label: localize('deployCluster.ComputePoolInstances', "Compute pool instances"),
min: 1,
max: 100,
defaultValue: '1',
useCustomValidator: true,
required: true,
variableName: VariableNames.ComputePoolScale_VariableName,
}]
}, {
fields: [{
type: FieldType.Number,
label: localize('deployCluster.DataText', "Data"),
label: localize('deployCluster.DataPoolInstances', "Data pool instances"),
min: 1,
max: 100,
defaultValue: '1',
useCustomValidator: true,
required: true,
variableName: VariableNames.DataPoolScale_VariableName,
}, {
type: FieldType.Number,
label: localize('deployCluster.SparkPoolInstances', "Spark pool instances"),
min: 0,
max: 100,
defaultValue: '0',
useCustomValidator: true,
required: true,
variableName: VariableNames.SparkPoolScale_VariableName
}]
}, {
fields: [
{
type: FieldType.Number,
label: localize('deployCluster.HDFSText', "HDFS"),
label: localize('deployCluster.StoragePoolInstances', "Storage pool (HDFS) instances"),
min: 1,
max: 100,
defaultValue: '1',
@@ -87,90 +110,12 @@ export class ServiceSettingsPage extends WizardPageBase<DeployClusterWizard> {
variableName: VariableNames.HDFSPoolScale_VariableName
}, {
type: FieldType.Checkbox,
label: localize('deployCluster.includeSparkInHDFSPool', "Include Spark"),
label: localize('deployCluster.IncludeSparkInStoragePool', "Include Spark in storage pool"),
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
}
]
}
]
};
@@ -255,7 +200,7 @@ export class ServiceSettingsPage extends WizardPageBase<DeployClusterWizard> {
fields: [
{
type: FieldType.Text,
label: localize('deployCluster.HDFSText', "HDFS"),
label: localize('deployCluster.StoragePool', "Storage pool (HDFS)"),
required: false,
variableName: VariableNames.HDFSDataStorageClassName_VariableName,
placeHolder: hintTextForStorageFields,
@@ -286,7 +231,7 @@ export class ServiceSettingsPage extends WizardPageBase<DeployClusterWizard> {
fields: [
{
type: FieldType.Text,
label: localize('deployCluster.DataText', "Data"),
label: localize('deployCluster.DataPool', "Data pool"),
required: false,
variableName: VariableNames.DataPoolDataStorageClassName_VariableName,
labelWidth: labelWidth,
@@ -364,25 +309,22 @@ export class ServiceSettingsPage extends WizardPageBase<DeployClusterWizard> {
});
};
const scaleSection = createSectionFunc(scaleSectionInfo);
const hadrSection = createSectionFunc(hadrSectionInfo);
const endpointSection = this.createEndpointSection(view);
this.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();
this.handleSparkSettingEvents();
const form = view.modelBuilder.formContainer().withFormItems([
{
title: '',
component: scaleSection
}, {
title: '',
component: hadrSection
}, {
title: '',
component: endpointSection
component: this.endpointSection
}, {
title: '',
component: storageContainer
@@ -392,86 +334,120 @@ export class ServiceSettingsPage extends WizardPageBase<DeployClusterWizard> {
});
}
private handleSparkSettingEvents(): void {
const sparkInstanceInput = getInputBoxComponent(VariableNames.SparkPoolScale_VariableName, this.inputComponents);
const includeSparkCheckbox = getCheckboxComponent(VariableNames.IncludeSpark_VariableName, this.inputComponents);
this.wizard.registerDisposable(includeSparkCheckbox.onChanged(() => {
if (!includeSparkCheckbox.checked && !(sparkInstanceInput.value && Number.parseInt(sparkInstanceInput.value) > 0)) {
sparkInstanceInput.value = '1';
}
}));
}
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.portColumnHeader = createLabel(view, { text: localize('deployCluster.PortHeader', "Port"), width: NumberInputWidth });
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.controllerPortInput = createNumberInput(view, { ariaLabel: localize('deployCluster.ControllerPortName', "Controller port"), required: true, width: NumberInputWidth, 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.sqlServerPortInput = createNumberInput(view, { ariaLabel: localize('deployCluster.MasterSQLServerPortName', "SQL Server Master port"), required: true, width: NumberInputWidth, 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.gatewayPortInput = createNumberInput(view, { ariaLabel: localize('deployCluster.GatewayPortName', "Gateway port"), required: true, width: NumberInputWidth, 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.serviceProxyNameLabel = createLabel(view, { text: localize('deployCluster.ServiceProxyText', "Management proxy"), width: labelWidth, required: true });
this.serviceProxyDNSInput = createTextInput(view, { ariaLabel: localize('deployCluster.ServiceProxyDNSName', "Management proxy DNS name"), required: false, width: inputWidth });
this.serviceProxyPortInput = createNumberInput(view, { ariaLabel: localize('deployCluster.ServiceProxyPortName', "Management proxy port"), required: true, width: NumberInputWidth, min: 1 });
this.serviceProxyEndpointRow = createFlexContainer(view, [this.serviceProxyNameLabel, this.serviceProxyDNSInput, this.serviceProxyPortInput]);
this.inputComponents[VariableNames.ServiceProxyDNSName_VariableName] = this.serviceProxyDNSInput;
this.inputComponents[VariableNames.ServiceProxyPort_VariableName] = this.serviceProxyPortInput;
this.appServiceProxyNameLabel = createLabel(view, { text: localize('deployCluster.AppServiceProxyText', "Application proxy"), width: labelWidth, required: true });
this.appServiceProxyDNSInput = createTextInput(view, { ariaLabel: localize('deployCluster.AppServiceProxyDNSName', "Application proxy DNS name"), required: false, width: inputWidth });
this.appServiceProxyPortInput = createNumberInput(view, { ariaLabel: localize('deployCluster.AppServiceProxyPortName', "Application proxy port"), required: true, width: NumberInputWidth, min: 1 });
this.appServiceProxyEndpointRow = createFlexContainer(view, [this.appServiceProxyNameLabel, this.appServiceProxyDNSInput, this.appServiceProxyPortInput]);
this.inputComponents[VariableNames.AppServiceProxyDNSName_VariableName] = this.appServiceProxyDNSInput;
this.inputComponents[VariableNames.AppServiceProxyPort_VariableName] = this.appServiceProxyPortInput;
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.readableSecondaryPortInput = createNumberInput(view, { ariaLabel: localize('deployCluster.ReadableSecondaryPortName', "Readable secondary port"), required: false, width: NumberInputWidth, 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], {
return createGroupContainer(view, [this.endpointHeaderRow, this.controllerEndpointRow, this.sqlServerEndpointRow, this.gatewayEndpointRow, this.serviceProxyEndpointRow, this.appServiceProxyEndpointRow, 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.ServiceProxyPort_VariableName);
this.setInputBoxValue(VariableNames.AppServiceProxyPort_VariableName);
this.setInputBoxValue(VariableNames.ReadableSecondaryPort_VariableName);
this.setInputBoxValue(VariableNames.GatewayDNSName_VariableName);
this.setInputBoxValue(VariableNames.AppServiceProxyDNSName_VariableName);
this.setInputBoxValue(VariableNames.SQLServerDNSName_VariableName);
this.setInputBoxValue(VariableNames.ReadableSecondaryDNSName_VariableName);
this.setInputBoxValue(VariableNames.ServiceProxyDNSName_VariableName);
this.setInputBoxValue(VariableNames.ControllerDNSName_VariableName);
this.setInputBoxValue(VariableNames.ControllerDataStorageClassName_VariableName);
this.setInputBoxValue(VariableNames.ControllerDataStorageSize_VariableName);
this.setInputBoxValue(VariableNames.ControllerLogsStorageClassName_VariableName);
this.setInputBoxValue(VariableNames.ControllerLogsStorageSize_VariableName);
this.endpointHeaderRow.clearItems();
this.endpointSection.collapsed = this.wizard.model.authenticationMode !== AuthenticationMode.ActiveDirectory;
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.loadEndpointRow(this.appServiceProxyEndpointRow, this.appServiceProxyNameLabel, this.appServiceProxyDNSInput, this.appServiceProxyPortInput);
this.loadEndpointRow(this.serviceProxyEndpointRow, this.serviceProxyNameLabel, this.serviceProxyDNSInput, this.serviceProxyPortInput);
const sqlServerScaleDropdown = getDropdownComponent(VariableNames.SQLServerScale_VariableName, this.inputComponents);
const sqlServerScale = this.wizard.model.getIntegerValue(VariableNames.SQLServerScale_VariableName);
if (sqlServerScale > 1) {
sqlServerScaleDropdown.values = ['3', '4', '5', '6', '7', '8', '9'];
this.loadEndpointRow(this.readableSecondaryEndpointRow, this.readableSecondaryNameLabel, this.readableSecondaryDNSInput, this.readableSecondaryPortInput);
} else {
this.readableSecondaryEndpointRow.clearItems();
sqlServerScaleDropdown.values = ['1'];
}
sqlServerScaleDropdown.value = sqlServerScale.toString();
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))
const allInputFilled: 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))
@@ -479,21 +455,34 @@ export class ServiceSettingsPage extends WizardPageBase<DeployClusterWizard> {
&& !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))
&& !isInputBoxEmpty(getInputBoxComponent(VariableNames.AppServiceProxyPort_VariableName, this.inputComponents))
&& !isInputBoxEmpty(getInputBoxComponent(VariableNames.ServiceProxyPort_VariableName, this.inputComponents))
&& (getDropdownComponent(VariableNames.SQLServerScale_VariableName, this.inputComponents).value === '1'
|| (!isInputBoxEmpty(this.readableSecondaryPortInput)
&& (this.wizard.model.authenticationMode !== AuthenticationMode.ActiveDirectory || !isInputBoxEmpty(this.readableSecondaryDNSInput))))
&& (this.wizard.model.authenticationMode !== AuthenticationMode.ActiveDirectory
|| (!isInputBoxEmpty(this.gatewayDNSInput)
&& !isInputBoxEmpty(this.controllerDNSInput)
&& !isInputBoxEmpty(this.sqlServerDNSInput)
&& !isInputBoxEmpty(this.readableSecondaryDNSInput)
&& !isInputBoxEmpty(this.appServiceProxyDNSInput)
&& !isInputBoxEmpty(this.serviceProxyDNSInput)
));
if (!isValid) {
const sparkEnabled = Number.parseInt(getInputBoxComponent(VariableNames.SparkPoolScale_VariableName, this.inputComponents).value!) !== 0
|| getCheckboxComponent(VariableNames.IncludeSpark_VariableName, this.inputComponents).checked!;
let errorMessage: string | undefined;
if (!allInputFilled) {
errorMessage = MissingRequiredInformationErrorMessage;
} else if (!sparkEnabled) {
errorMessage = localize('deployCluster.SparkMustBeIncluded', "Invalid Spark configuration, you must check the 'Include Spark' checkbox or set the 'Spark pool instances' to at least 1.");
}
if (errorMessage) {
this.wizard.wizardObject.message = {
text: MissingRequiredInformationErrorMessage,
text: errorMessage,
level: azdata.window.MessageLevel.Error
};
}
return isValid;
return allInputFilled && sparkEnabled;
}
return true;
});
@@ -514,43 +503,6 @@ export class ServiceSettingsPage extends WizardPageBase<DeployClusterWizard> {
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' } };

View File

@@ -4,24 +4,18 @@
*--------------------------------------------------------------------------------------------*/
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 { SectionInfo, FieldType, LabelPosition, BdcDeploymentType, FontWeight } 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);
@@ -30,30 +24,20 @@ export class SummaryPage extends WizardPageBase<DeployClusterWizard> {
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
}
]);
this.form = view.modelBuilder.formContainer();
return view.initializeModel(this.form!.withLayout({ width: '100%' }).component());
});
}
public onEnter() {
this.targetDeploymentProfile = this.wizard.model.createTargetProfile();
if (this.wizard.model.deploymentTarget === BdcDeploymentType.NewAKS) {
this.wizard.wizardObject.message = {
level: azdata.window.MessageLevel.Information,
text: localize('resourceDeployment.NewAKSBrowserWindowPrompt', "A browser window for logging to Azure will be opened during the SQL Server Big Data Cluster deployment.")
};
}
this.wizard.saveConfigButton.hidden = false;
this.wizard.scriptToNotebookButton.hidden = false;
this.formItems.forEach(item => {
this.form!.removeFormItem(item);
});
@@ -71,13 +55,13 @@ export class SummaryPage extends WizardPageBase<DeployClusterWizard> {
type: FieldType.ReadonlyText,
label: localize('deployCluster.Kubeconfig', "Kube config"),
defaultValue: this.wizard.model.getStringValue(VariableNames.KubeConfigPath_VariableName),
fontStyle: FontStyle.Italic
labelFontWeight: FontWeight.Bold
},
{
type: FieldType.ReadonlyText,
label: localize('deployCluster.ClusterContext', "Cluster context"),
defaultValue: this.wizard.model.getStringValue(VariableNames.ClusterContext_VariableName),
fontStyle: FontStyle.Italic
labelFontWeight: FontWeight.Bold
}]
}
]
@@ -96,13 +80,13 @@ export class SummaryPage extends WizardPageBase<DeployClusterWizard> {
type: FieldType.ReadonlyText,
label: localize('deployCluster.DeploymentProfile', "Deployment profile"),
defaultValue: this.wizard.model.getStringValue(VariableNames.DeploymentProfile_VariableName),
fontStyle: FontStyle.Italic
labelFontWeight: FontWeight.Bold
},
{
type: FieldType.ReadonlyText,
label: localize('deployCluster.ClusterName', "Cluster name"),
defaultValue: this.wizard.model.getStringValue(VariableNames.ClusterName_VariableName),
fontStyle: FontStyle.Italic
labelFontWeight: FontWeight.Bold
}]
}, {
fields: [
@@ -110,20 +94,92 @@ export class SummaryPage extends WizardPageBase<DeployClusterWizard> {
type: FieldType.ReadonlyText,
label: localize('deployCluster.ControllerUsername', "Controller username"),
defaultValue: this.wizard.model.getStringValue(VariableNames.AdminUserName_VariableName),
fontStyle: FontStyle.Italic
labelFontWeight: FontWeight.Bold
}, {
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
labelFontWeight: FontWeight.Bold
}
]
}
]
};
if (this.wizard.model.authenticationMode === AuthenticationMode.ActiveDirectory) {
clusterSectionInfo.rows!.push({
fields: [
{
type: FieldType.ReadonlyText,
label: localize('deployCluster.OuDistinguishedName', "Organizational unit"),
defaultValue: this.wizard.model.getStringValue(VariableNames.OrganizationalUnitDistinguishedName_VariableName),
labelFontWeight: FontWeight.Bold
},
{
type: FieldType.ReadonlyText,
label: localize('deployCluster.DomainControllerFQDNs', "Domain controller FQDNs"),
defaultValue: this.wizard.model.getStringValue(VariableNames.DomainControllerFQDNs_VariableName),
labelFontWeight: FontWeight.Bold
}]
});
clusterSectionInfo.rows!.push({
fields: [
{
type: FieldType.ReadonlyText,
label: localize('deployCluster.DomainDNSIPAddresses', "Domain DNS IP addresses"),
defaultValue: this.wizard.model.getStringValue(VariableNames.DomainDNSIPAddresses_VariableName),
labelFontWeight: FontWeight.Bold
},
{
type: FieldType.ReadonlyText,
label: localize('deployCluster.DomainDNSName', "Domain DNS name"),
defaultValue: this.wizard.model.getStringValue(VariableNames.DomainDNSName_VariableName),
labelFontWeight: FontWeight.Bold
}]
});
clusterSectionInfo.rows!.push({
fields: [
{
type: FieldType.ReadonlyText,
label: localize('deployCluster.ClusterAdmins', "Cluster admin group"),
defaultValue: this.wizard.model.getStringValue(VariableNames.ClusterAdmins_VariableName),
labelFontWeight: FontWeight.Bold
},
{
type: FieldType.ReadonlyText,
label: localize('deployCluster.ClusterUsers', "Cluster users"),
defaultValue: this.wizard.model.getStringValue(VariableNames.ClusterUsers_VariableName),
labelFontWeight: FontWeight.Bold
}]
});
clusterSectionInfo.rows!.push({
fields: [
{
type: FieldType.ReadonlyText,
label: localize('deployCluster.AppOwers', "App owners"),
defaultValue: this.wizard.model.getStringValue(VariableNames.AppOwners_VariableName),
labelFontWeight: FontWeight.Bold
},
{
type: FieldType.ReadonlyText,
label: localize('deployCluster.AppReaders', "App readers"),
defaultValue: this.wizard.model.getStringValue(VariableNames.AppReaders_VariableName),
labelFontWeight: FontWeight.Bold
}]
});
clusterSectionInfo.rows!.push({
fields: [
{
type: FieldType.ReadonlyText,
label: localize('deployCluster.DomainServiceAccountUserName', "Service account username"),
defaultValue: this.wizard.model.getStringValue(VariableNames.DomainServiceAccountUserName_VariableName),
labelFontWeight: FontWeight.Bold
}]
});
}
const azureSectionInfo: SectionInfo = {
labelPosition: LabelPosition.Left,
labelWidth: '150px',
@@ -135,26 +191,26 @@ export class SummaryPage extends WizardPageBase<DeployClusterWizard> {
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
labelFontWeight: FontWeight.Bold
}, {
type: FieldType.ReadonlyText,
label: localize('deployCluster.ResourceGroup', "Resource group"),
defaultValue: this.wizard.model.getStringValue(VariableNames.ResourceGroup_VariableName),
fontStyle: FontStyle.Italic
labelFontWeight: FontWeight.Bold
}
]
}, {
fields: [
{
type: FieldType.ReadonlyText,
label: localize('deployCluster.Region', "Region"),
defaultValue: this.wizard.model.getStringValue(VariableNames.Region_VariableName),
fontStyle: FontStyle.Italic
label: localize('deployCluster.Location', "Location"),
defaultValue: this.wizard.model.getStringValue(VariableNames.Location_VariableName),
labelFontWeight: FontWeight.Bold
}, {
type: FieldType.ReadonlyText,
label: localize('deployCluster.AksClusterName', "AKS cluster name"),
defaultValue: this.wizard.model.getStringValue(VariableNames.AksName_VariableName),
fontStyle: FontStyle.Italic
labelFontWeight: FontWeight.Bold
}
]
}, {
@@ -163,12 +219,12 @@ export class SummaryPage extends WizardPageBase<DeployClusterWizard> {
type: FieldType.ReadonlyText,
label: localize('deployCluster.VMSize', "VM size"),
defaultValue: this.wizard.model.getStringValue(VariableNames.VMSize_VariableName),
fontStyle: FontStyle.Italic
labelFontWeight: FontWeight.Bold
}, {
type: FieldType.ReadonlyText,
label: localize('deployCluster.VMCount', "VM count"),
defaultValue: this.wizard.model.getStringValue(VariableNames.VMCount_VariableName),
fontStyle: FontStyle.Italic
labelFontWeight: FontWeight.Bold
}
]
}
@@ -184,68 +240,34 @@ export class SummaryPage extends WizardPageBase<DeployClusterWizard> {
{
fields: [{
type: FieldType.ReadonlyText,
label: localize('deployCluster.ComputeText', "Compute"),
defaultValue: this.wizard.model.getStringValue(VariableNames.ComputePoolScale_VariableName),
fontStyle: FontStyle.Italic
label: localize('deployCluster.MasterSqlServerInstances', "SQL Server master instances"),
defaultValue: this.wizard.model.getStringValue(VariableNames.SQLServerScale_VariableName),
labelFontWeight: FontWeight.Bold
}, {
type: FieldType.ReadonlyText,
label: localize('deployCluster.DataText', "Data"),
label: localize('deployCluster.ComputePoolInstances', "Compute pool instances"),
defaultValue: this.wizard.model.getStringValue(VariableNames.ComputePoolScale_VariableName),
labelFontWeight: FontWeight.Bold
}]
}, {
fields: [{
type: FieldType.ReadonlyText,
label: localize('deployCluster.DataPoolInstances', "Data pool instances"),
defaultValue: this.wizard.model.getStringValue(VariableNames.DataPoolScale_VariableName),
fontStyle: FontStyle.Italic
}
]
labelFontWeight: FontWeight.Bold
}, {
type: FieldType.ReadonlyText,
label: localize('deployCluster.SparkPoolInstances', "Spark pool instances"),
defaultValue: this.wizard.model.getStringValue(VariableNames.SparkPoolScale_VariableName),
labelFontWeight: FontWeight.Bold
}]
}, {
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
}
]
fields: [{
type: FieldType.ReadonlyText,
label: localize('deployCluster.StoragePoolInstances', "Storage pool (HDFS) instances"),
defaultValue: `${this.wizard.model.getStringValue(VariableNames.HDFSPoolScale_VariableName)} ${this.wizard.model.getBooleanValue(VariableNames.IncludeSpark_VariableName) ? localize('deployCluster.WithSpark', "(Spark included)") : ''}`,
labelFontWeight: FontWeight.Bold
}]
}
]
};
@@ -271,7 +293,6 @@ export class SummaryPage extends WizardPageBase<DeployClusterWizard> {
const clusterSection = createSectionFunc(clusterSectionInfo);
const scaleSection = createSectionFunc(scaleSectionInfo);
const hadrSection = createSectionFunc(hadrSectionInfo);
const endpointSection = {
title: '',
component: this.createEndpointSection()
@@ -285,10 +306,16 @@ export class SummaryPage extends WizardPageBase<DeployClusterWizard> {
this.formItems.push(azureSection);
}
this.formItems.push(clusterSection, scaleSection, hadrSection, endpointSection, storageSection);
this.formItems.push(clusterSection, scaleSection, endpointSection, storageSection);
this.form.addFormItems(this.formItems);
}
public onLeave() {
this.wizard.saveConfigButton.hidden = true;
this.wizard.scriptToNotebookButton.hidden = true;
this.wizard.wizardObject.message = { text: '' };
}
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;
@@ -324,7 +351,7 @@ export class SummaryPage extends WizardPageBase<DeployClusterWizard> {
this.wizard.model.getStringValue(VariableNames.ControllerLogsStorageClassName_VariableName),
this.wizard.model.getStringValue(VariableNames.ControllerLogsStorageSize_VariableName)],
[
localize('deployCluster.HDFSText', "HDFS"),
localize('deployCluster.StoragePool', "Storage pool (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),
@@ -357,10 +384,12 @@ export class SummaryPage extends WizardPageBase<DeployClusterWizard> {
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)
this.createEndpointRow(localize('deployCluster.GatewayText', "Gateway"), VariableNames.GatewayDNSName_VariableName, VariableNames.GateWayPort_VariableName),
this.createEndpointRow(localize('deployCluster.AppServiceProxyText', "Application proxy"), VariableNames.AppServiceProxyDNSName_VariableName, VariableNames.AppServiceProxyPort_VariableName),
this.createEndpointRow(localize('deployCluster.ServiceProxyText', "Management proxy"), VariableNames.ServiceProxyDNSName_VariableName, VariableNames.ServiceProxyPort_VariableName)
];
if (this.wizard.model.hadrEnabled) {
if (this.wizard.model.getIntegerValue(VariableNames.SQLServerScale_VariableName) > 1) {
endpointRows.push(
this.createEndpointRow(localize('deployCluster.ReadableSecondaryText', "Readable secondary"), VariableNames.ReadableSecondaryDNSName_VariableName, VariableNames.ReadableSecondaryPort_VariableName)
);
@@ -373,43 +402,15 @@ export class SummaryPage extends WizardPageBase<DeployClusterWizard> {
private createEndpointRow(name: string, dnsVariableName: string, portVariableName: string): azdata.FlexContainer {
const items = [];
items.push(createLabel(this.view, { text: name, width: '150px' }));
items.push(createLabel(this.view, { text: name, width: '150px', fontWeight: FontWeight.Bold }));
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(dnsVariableName)!, width: '200px'
}));
}
items.push(createLabel(this.view, { text: this.wizard.model.getStringValue(portVariableName)!, width: '100px', fontStyle: FontStyle.Italic }));
items.push(createLabel(this.view, {
text: this.wizard.model.getStringValue(portVariableName)!, width: '100px'
}));
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;
}
}