Files
azuredatastudio/extensions/resource-deployment/src/ui/deployClusterWizard/pages/clusterSettingsPage.ts
Alan Ren 2ab7a47353 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
2019-10-16 20:41:15 -07:00

353 lines
17 KiB
TypeScript

/*---------------------------------------------------------------------------------------------
* 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 = {};
private activeDirectorySection!: azdata.FormComponent;
private formBuilder!: azdata.FormBuilder;
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.AdminUsername', "Admin username"),
required: true,
variableName: VariableNames.AdminUserName_VariableName,
defaultValue: 'admin',
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"),
required: true,
variableName: VariableNames.AdminPassword_VariableName,
defaultValue: '',
useCustomValidator: true,
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"),
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 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.OuDistinguishedName', "Organizational unit"),
required: true,
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.ClusterAdmins', "Cluster admin group"),
required: true,
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.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.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.")
}
]
};
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 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 dockerSettingsFormItem = { title: '', component: dockerSettingsGroup };
this.activeDirectorySection = { title: '', component: activeDirectorySettingsGroup };
const authModeDropdown = <azdata.DropDownComponent>this.inputComponents[VariableNames.AuthenticationMode_VariableName];
this.formBuilder = view.modelBuilder.formContainer().withFormItems(
[basicSettingsFormItem, dockerSettingsFormItem],
{
horizontal: false,
componentWidth: '100%'
}
);
this.wizard.registerDisposable(authModeDropdown.onValueChanged(() => {
const isBasicAuthMode = (<azdata.CategoryValue>authModeDropdown.value).name === 'basic';
if (isBasicAuthMode) {
this.formBuilder.removeFormItem(this.activeDirectorySection);
} else {
this.formBuilder.insertFormItem(this.activeDirectorySection);
}
}));
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;
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")
};
}
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;
});
}
}
}