Smartura/sql migrate tde improvements (#23631)

* Automatically migrate certs on last page of wizard

* Updated TDE configuration dialog wording

* Added ConfigDialogSetting to track user selection

* Added numberOfDbsWithTde telemetry prop

* Migrate certs button moved back to target page

* Enable next button on cert migration success

* Reset TDE migration result if target changes

* Addressed PR feedback

* Added TDE navigation validator

* Fixed typo
This commit is contained in:
Steven Marturano 2023-07-12 15:32:28 -04:00 коммит произвёл GitHub
Родитель 8d1a7265ee
Коммит b595115c8a
Не найден ключ, соответствующий данной подписи
Идентификатор ключа GPG: 4AEE18F83AFDEB23
8 изменённых файлов: 210 добавлений и 124 удалений

Просмотреть файл

@ -1507,12 +1507,12 @@ export function RESULTS_INFO_BOX_STATUS(state: string | undefined, errors?: stri
export const TDE_WIZARD_TITLE = localize('sql.migration.tde.wizard.title', "Encrypted database selected.");
export const TDE_WIZARD_DESCRIPTION = localize('sql.migration.tde.wizard.description', "To migrate an encrypted database successfully you need to provide access to the encryption certificates or migrate certificates manually before proceeding with the migration. {0}.");
export const TDE_WIZARD_MIGRATION_CAPTION = localize('sql.migration.tde.wizard.optionscaption', "Certificate migration");
export const TDE_WIZARD_MIGRATION_OPTION_ADS = localize('sql.migration.tde.wizard.optionads', "Export my certificates and private key to the target.");
export const TDE_WIZARD_MIGRATION_OPTION_ADS = localize('sql.migration.tde.wizard.optionads', "Export my certificates and private keys to the target.");
export const TDE_WIZARD_MIGRATION_OPTION_ADS_CONFIRM = localize('sql.migration.tde.wizard.optionadsconfirm', "I give consent to use my credentials for accessing the certificates.");
export const TDE_WIZARD_MIGRATION_OPTION_MANUAL = localize('sql.migration.tde.wizard.optionmanual', "I don't want Azure Data Studio to export the certificates.");
export const TDE_WIZARD_MIGRATION_OPTION_MANUAL = localize('sql.migration.tde.wizard.optionmanual', "I have already migrated my certificates and private keys to the target.");
export const TDE_BUTTON_CAPTION = localize('sql.migration.tde.button.caption', "Edit");
export const TDE_WIZARD_MSG_MANUAL = localize('sql.migration.tde.msg.manual', "You have chosen to manually migrate certificates.");
export const TDE_WIZARD_MSG_TDE = localize('sql.migration.tde.msg.tde', "You have given access to Azure Data Studio to migrate the encryption certificates and database.");
export const TDE_WIZARD_MSG_MANUAL = localize('sql.migration.tde.msg.manual', "You have not given Azure Data Studio access to migrate the encryption certificates.");
export const TDE_WIZARD_MSG_TDE = localize('sql.migration.tde.msg.tde', "You have given Azure Data Studio access to migrate the encryption certificates and database.");
export const TDE_WIZARD_MSG_EMPTY = localize('sql.migration.tde.msg.empty', "No encrypted database selected.");
export function TDE_MIGRATION_ERROR(message: string): string {
@ -1532,7 +1532,7 @@ export function TDE_WIZARD_DATABASES_SELECTED(encryptedCount: number, totalCount
}
export const TDE_WIZARD_MIGRATION_OPTION_MANUAL_WARNING = localize('sql.migration.tde.wizard.optionmanual.warning', "You must migrate the certificates before proceeding with the migration otherwise the migration will fail. {0}.");
export const TDE_WIZARD_MIGRATION_OPTION_MANUAL_WARNING = localize('sql.migration.tde.wizard.optionmanual.warning', "Certificates must be migrated before proceeding with the database migration otherwise a failure will occur. {0} about manually migrating TDE certificates.");
export const TDE_WIZARD_ADS_CERTS_INFO = localize('sql.migration.network.share.header.text', "Please enter a location where the SQL Server will export the certificates. Also verify that SQL Server service has write access to this path and the current user should have administrator privileges on the computer where this network path is.");
@ -1553,7 +1553,7 @@ export const TDE_MIGRATEDIALOG_TITLE = localize('sql.migration.validation.dialog
export const TDE_MIGRATE_DONE_BUTTON = localize('sql.migration.tde.migrate.done.button', "Done");
export const TDE_MIGRATE_HEADING = localize('sql.migration.tde.migrate.heading', "Migrating the certificates from the following databases:");
export const TDE_MIGRATE_REQUIRED = localize('sql.migration.tde.migrate.required', "TDE certificate migration must be successful before continuing.");
export const TDE_MIGRATE_COLUMN_DATABASES = localize('sql.migration.tde.migrate.column.databases', "Databases");
export const TDE_MIGRATE_COLUMN_STATUS = localize('sql.migration.tde.migrate.column.status', "Status");
export const TDE_MIGRATE_RETRY_VALIDATION = localize('sql.migration.tde.migrate.start.validation', "Retry migration");

Просмотреть файл

@ -9,7 +9,7 @@ import { MigrationStateModel } from '../../models/stateMachine';
import * as constants from '../../constants/strings';
import * as styles from '../../constants/styles';
import * as utils from '../../api/utils';
import { SKURecommendationPage } from '../../wizard/skuRecommendationPage';
import { ConfigDialogSetting } from '../../models/tdeModels'
export class TdeConfigurationDialog {
@ -24,8 +24,7 @@ export class TdeConfigurationDialog {
private _networkPathText!: azdata.InputBoxComponent;
private _onClosed: () => void;
constructor(public skuRecommendationPage: SKURecommendationPage, public wizard: azdata.window.Wizard, public migrationStateModel: MigrationStateModel,
onClosed: () => void) {
constructor(public migrationStateModel: MigrationStateModel, onClosed: () => void) {
this._onClosed = onClosed;
}
@ -90,7 +89,6 @@ export class TdeConfigurationDialog {
return container;
}
private createMethodsContainer(_view: azdata.ModelView): azdata.FlexContainer {
const chooseMethodText = _view.modelBuilder.text().withProps({
value: constants.TDE_WIZARD_MIGRATION_CAPTION,
@ -123,7 +121,7 @@ export class TdeConfigurationDialog {
.withProps({
name: buttonGroup,
label: constants.TDE_WIZARD_MIGRATION_OPTION_ADS,
checked: this.migrationStateModel.tdeMigrationConfig.isTdeMigrationMethodAds(),
checked: this.migrationStateModel.tdeMigrationConfig.getAppliedConfigDialogSetting() === ConfigDialogSetting.ExportCertificates,
CSSStyles: {
...styles.BODY_CSS
},
@ -131,7 +129,7 @@ export class TdeConfigurationDialog {
this._disposables.push(
adsMethodButton.onDidChangeCheckedState(async checked => {
if (checked) {
this.migrationStateModel.tdeMigrationConfig.setTdeMigrationMethod(true);
this.migrationStateModel.tdeMigrationConfig.setPendingTdeMigrationMethod(ConfigDialogSetting.ExportCertificates);
await this.updateUI();
}
}));
@ -156,14 +154,14 @@ export class TdeConfigurationDialog {
.withProps({
name: buttonGroup,
label: constants.TDE_WIZARD_MIGRATION_OPTION_MANUAL,
checked: this.migrationStateModel.tdeMigrationConfig.isTdeMigrationMethodManual(),
checked: this.migrationStateModel.tdeMigrationConfig.getAppliedConfigDialogSetting() === ConfigDialogSetting.DoNotExport,
CSSStyles: { ...styles.BODY_CSS }
}).component();
this._disposables.push(
manualMethodButton.onDidChangeCheckedState(async checked => {
if (checked) {
this.migrationStateModel.tdeMigrationConfig.setTdeMigrationMethod(false);
this.migrationStateModel.tdeMigrationConfig.setPendingTdeMigrationMethod(ConfigDialogSetting.DoNotExport);
await this.updateUI();
}
}));
@ -193,7 +191,8 @@ export class TdeConfigurationDialog {
private createAdsConfirmationContainer(_view: azdata.ModelView): azdata.FlexContainer {
const container = _view.modelBuilder.flexContainer().withProps({
CSSStyles: {
'flex-direction': 'column'
'flex-direction': 'column',
'display': 'none'
}
}).component();
@ -226,11 +225,15 @@ export class TdeConfigurationDialog {
required: true,
CSSStyles: { ...styles.BODY_CSS, 'margin-top': '-1em', 'margin-left': '45px' }
}).component();
this._disposables.push(
this._networkPathText.onTextChanged(async networkPath => {
this.migrationStateModel.tdeMigrationConfig.setPendingNetworkPath(networkPath);
}));
this._adsConfirmationCheckBox = _view.modelBuilder.checkBox()
.withProps({
label: constants.TDE_WIZARD_MIGRATION_OPTION_ADS_CONFIRM,
checked: this.migrationStateModel.tdeMigrationConfig.isTdeMigrationMethodAdsConfirmed(),
checked: this.migrationStateModel.tdeMigrationConfig.getAppliedExportCertUserConsent(),
CSSStyles: {
...styles.BODY_CSS,
'margin': '10px 0 14px 15px'
@ -238,9 +241,7 @@ export class TdeConfigurationDialog {
}).component();
this._disposables.push(
this._adsConfirmationCheckBox.onChanged(async checked => {
this.migrationStateModel.tdeMigrationConfig.setAdsConfirmation(
checked,
this._networkPathText.value ?? '');
this.migrationStateModel.tdeMigrationConfig.setPendingExportCertUserConsent(checked);
await this.updateUI();
}));
@ -256,7 +257,8 @@ export class TdeConfigurationDialog {
private createManualWarningContainer(_view: azdata.ModelView): azdata.FlexContainer {
const container = _view.modelBuilder.flexContainer().withProps({
CSSStyles: {
'flex-direction': 'column'
'flex-direction': 'column',
'display': 'none'
}
}).component();
@ -283,18 +285,15 @@ export class TdeConfigurationDialog {
}
private async updateUI(): Promise<void> {
const useAds = this.migrationStateModel.tdeMigrationConfig.isTdeMigrationMethodAds();
let exportCertsUsingAds = this.migrationStateModel.tdeMigrationConfig.getPendingConfigDialogSetting() === ConfigDialogSetting.ExportCertificates;
this._networkPathText.value = this.migrationStateModel.tdeMigrationConfig.getPendingNetworkPath();
this._networkPathText.required = exportCertsUsingAds;
this._adsConfirmationCheckBox.checked = this.migrationStateModel.tdeMigrationConfig.getPendingExportCertUserConsent();
this._networkPathText.value = this.migrationStateModel.tdeMigrationConfig._networkPath;
this._adsConfirmationCheckBox.checked = this.migrationStateModel.tdeMigrationConfig.isTdeMigrationMethodAdsConfirmed();
await utils.updateControlDisplay(this._adsMethodConfirmationContainer, useAds);
await utils.updateControlDisplay(this._adsMethodConfirmationContainer, exportCertsUsingAds);
await utils.updateControlDisplay(this._manualMethodWarningContainer, this.migrationStateModel.tdeMigrationConfig.getPendingConfigDialogSetting() === ConfigDialogSetting.DoNotExport);
const useManual = this.migrationStateModel.tdeMigrationConfig.isTdeMigrationMethodManual();
await utils.updateControlDisplay(this._manualMethodWarningContainer, useManual);
this.dialog!.okButton.enabled = this.migrationStateModel.tdeMigrationConfig.isTdeMigrationMethodSet();
this._networkPathText.required = useAds;
this.dialog!.okButton.enabled = this.migrationStateModel.tdeMigrationConfig.isAnyChangeReadyToBeApplied()
}
public async openDialog(dialogName?: string,) {
@ -309,15 +308,12 @@ export class TdeConfigurationDialog {
'narrow');
this.dialog.okButton.label = constants.APPLY;
this.dialog.okButton.enabled = false;
this._disposables.push(
this.dialog.okButton.onClick(
(eventArgs) => {
() => {
this._isOpen = false;
this.migrationStateModel.tdeMigrationConfig.setConfigurationCompleted();
if (this.migrationStateModel.tdeMigrationConfig.shouldAdsMigrateCertificates()) {
this.migrationStateModel.tdeMigrationConfig._networkPath = this._networkPathText.value ?? '';
}
this.migrationStateModel.tdeMigrationConfig.applyConfigDialogSetting();
this._onClosed();
})
);
@ -325,6 +321,7 @@ export class TdeConfigurationDialog {
this._disposables.push(
this.dialog.cancelButton.onClick(
() => {
this.migrationStateModel.tdeMigrationConfig.cancelConfigDialogSetting();
this._isOpen = false;
this._onClosed();
}));

Просмотреть файл

@ -6,7 +6,6 @@
import * as azdata from 'azdata';
import * as vscode from 'vscode';
import * as constants from '../../constants/strings';
import * as utils from '../../api/utils';
import { logError, TelemetryAction, TelemetryViews, sendSqlMigrationActionEvent, getTelemetryProps } from '../../telemetry';
import { EOL } from 'os';
import { MigrationStateModel, OperationResult } from '../../models/stateMachine';
@ -55,10 +54,13 @@ export class TdeMigrationDialog {
};
private _valdiationErrors: string[] = [];
private _completedDatabasesCount: number = 0;
private _certMigrationEventEmitter;
constructor(
model: MigrationStateModel) {
model: MigrationStateModel,
certMigrationEventEmitter: vscode.EventEmitter<TdeMigrationResult>) {
this._model = model;
this._certMigrationEventEmitter = certMigrationEventEmitter;
}
public async openDialog(): Promise<void> {
@ -214,8 +216,6 @@ export class TdeMigrationDialog {
});
}
private async _loadMigrationResults(): Promise<void> {
const tdeMigrationResult = this._model.tdeMigrationConfig.lastTdeMigrationResult();
this._progressReportText.value = '';
@ -258,6 +258,7 @@ export class TdeMigrationDialog {
}
private async _retryTdeMigration(): Promise<void> {
this._model.tdeMigrationConfig.resetTdeMigrationResult();
const tdeMigrationResult = this._model.tdeMigrationConfig.lastTdeMigrationResult();
tdeMigrationResult.dbList = this._model.tdeMigrationConfig.getTdeEnabledDatabases().map<TdeMigrationDbState>(
db => ({
@ -318,25 +319,28 @@ export class TdeMigrationDialog {
};
this._model.tdeMigrationConfig.setTdeMigrationResult(this._tdeMigrationResult); // Set value on success.
this._certMigrationEventEmitter.fire(this._tdeMigrationResult);
sendSqlMigrationActionEvent(
TelemetryViews.TdeMigrationDialog,
TelemetryAction.TdeMigrationSuccess,
{
...getTelemetryProps(this._model)
...getTelemetryProps(this._model),
'numberOfDbsWithTde': this._model.tdeMigrationConfig.getTdeEnabledDatabasesCount().toString()
},
{}
);
}
else {
this._dialog!.okButton.enabled = false;
this._certMigrationEventEmitter.fire({ state: TdeMigrationState.Failed, dbList: [] });
sendSqlMigrationActionEvent(
TelemetryViews.TdeMigrationDialog,
TelemetryAction.TdeMigrationFailures,
{
...getTelemetryProps(this._model),
'runningAsAdmin': (await utils.isAdmin()).toString()
'numberOfDbsWithTde': this._model.tdeMigrationConfig.getTdeEnabledDatabasesCount().toString()
},
{}
);
@ -431,8 +435,6 @@ export class TdeMigrationDialog {
.component();
}
private async _updateTableFromOperationResult(operationResult: OperationResult<TdeMigrationDbResult[]>): Promise<void> {
let anyRowUpdated = false;

Просмотреть файл

@ -972,7 +972,7 @@ export class MigrationStateModel implements Model, vscode.Disposable {
this._targetSubscription?.id,
this._resourceGroup?.name,
this._targetServerInstance.name,
this.tdeMigrationConfig._networkPath,
this.tdeMigrationConfig.getAppliedNetworkPath(),
accessToken,
reportUpdate);

Просмотреть файл

@ -18,6 +18,12 @@ export enum TdeDatabaseMigrationState {
Canceled = 'Canceled',
}
export enum ConfigDialogSetting {
NoSelection = 'NoSelection',
ExportCertificates = 'ExportCertificates',
DoNotExport = 'DoNotExport',
}
export interface TdeMigrationDbState {
name: string;
dbState: TdeDatabaseMigrationState;
@ -29,17 +35,24 @@ export interface TdeMigrationResult {
state: TdeMigrationState;
}
export interface TdeMigrationDbResult {
name: string;
success: boolean;
message: string;
}
export class TdeMigrationModel {
private _exportUsingADS?: boolean | undefined;
private _adsExportConfirmation: boolean;
// Settings for which the user has clicked the apply button
private _appliedConfigDialogSetting: ConfigDialogSetting;
private _appliedExportCertUserConsent: boolean;
private _appliedNetworkPath: string;
// Settings that are pending but user has not clicked the apply button
private _pendingConfigDialogSetting: ConfigDialogSetting;
private _pendingExportCertUserConsent: boolean;
private _pendingNetworkPath: string;
private _configurationCompleted: boolean;
private _shownBefore: boolean;
private _encryptedDbs: string[];
@ -49,18 +62,21 @@ export class TdeMigrationModel {
dbList: []
};
public _networkPath: string;
constructor(
) {
this._exportUsingADS = true;
this._adsExportConfirmation = false;
this._configurationCompleted = false;
this._shownBefore = false;
this._encryptedDbs = [];
this._networkPath = '';
this._appliedConfigDialogSetting = ConfigDialogSetting.NoSelection;
this._pendingConfigDialogSetting = ConfigDialogSetting.NoSelection;
this._appliedNetworkPath = '';
this._pendingNetworkPath = '';
this._appliedExportCertUserConsent = false;
this._pendingExportCertUserConsent = false;
this._tdeMigrationCompleted = false;
this._tdeMigrationCompleted = this._tdeMigrationCompleted;
}
// If the configuration dialog was shown already.
@ -95,50 +111,44 @@ export class TdeMigrationModel {
this._shownBefore = false; // Reset the tde dialog showing status when databases change
}
// Sets the certificate migration method
public setTdeMigrationMethod(useAds: boolean): void {
if (useAds) {
this._exportUsingADS = true;
} else {
this._exportUsingADS = false;
this._adsExportConfirmation = false;
// User has clicked "Apply", applies setting
public applyConfigDialogSetting() {
if (!this.isAnyChangeReadyToBeApplied()) {
return;
}
this._tdeMigrationCompleted = false;
this._appliedConfigDialogSetting = this._pendingConfigDialogSetting;
this._appliedExportCertUserConsent = this._pendingExportCertUserConsent;
this._appliedNetworkPath = this._pendingNetworkPath;
if (this._appliedConfigDialogSetting !== ConfigDialogSetting.ExportCertificates) {
this._appliedExportCertUserConsent = false;
this._pendingExportCertUserConsent = false;
this._appliedNetworkPath = "";
this._pendingNetworkPath = "";
}
this._configurationCompleted = true;
}
// When a migration configuration was configured and accepted on the configuration blade.
public setConfigurationCompleted(): void {
this._configurationCompleted = true;
// User has clicked "Cancel", reverts settings to last applied
public cancelConfigDialogSetting() {
this._pendingConfigDialogSetting = this._appliedConfigDialogSetting;
this._pendingExportCertUserConsent = this._appliedExportCertUserConsent;
this._pendingNetworkPath = this._appliedNetworkPath;
}
// Sets the certificate migration method
public setPendingTdeMigrationMethod(config: ConfigDialogSetting): void {
this._pendingConfigDialogSetting = config;
this._tdeMigrationCompleted = false;
}
// When ADS is configured to do the certificates migration
public shouldAdsMigrateCertificates(): boolean {
return this.hasTdeEnabledDatabases() && this._configurationCompleted && this.isTdeMigrationMethodAdsConfirmed();
}
// When any valid method is properly set.
public isTdeMigrationMethodSet(): boolean {
return this.isTdeMigrationMethodAdsConfirmed() || this.isTdeMigrationMethodManual();
}
// When Ads is selected as method. may still need confirmation.
public isTdeMigrationMethodAds(): boolean {
return this._exportUsingADS === true;
}
// When ads migration method is confirmed
public isTdeMigrationMethodAdsConfirmed(): boolean {
return this.isTdeMigrationMethodAds() && this._adsExportConfirmation === true;
}
// When manual method is selected
public isTdeMigrationMethodManual(): boolean {
return this._exportUsingADS === false;
}
// When manual method is selected
public tdeMigrationCompleted(): boolean {
return this._tdeMigrationCompleted;
return this.hasTdeEnabledDatabases() &&
this._configurationCompleted &&
(this.getAppliedConfigDialogSetting() === ConfigDialogSetting.ExportCertificates);
}
// Get the value for the lastest tde migration result
@ -160,14 +170,47 @@ export class TdeMigrationModel {
};
}
// When the confirmation is set, for ADS certificate migration method
public setAdsConfirmation(status: boolean, networkPath: string): void {
if (status && this.isTdeMigrationMethodAds()) {
this._adsExportConfirmation = true;
this._networkPath = networkPath;
} else {
this._adsExportConfirmation = false;
public isAnyChangeReadyToBeApplied() {
if (this._pendingConfigDialogSetting === ConfigDialogSetting.NoSelection) {
return false;
}
if (this._pendingConfigDialogSetting === ConfigDialogSetting.ExportCertificates) {
return this._pendingExportCertUserConsent;
}
return true;
}
public getPendingConfigDialogSetting() {
return this._pendingConfigDialogSetting;
}
public getAppliedConfigDialogSetting() {
return this._appliedConfigDialogSetting;
}
public getPendingNetworkPath() {
return this._pendingNetworkPath;
}
public setPendingNetworkPath(pendingNetworkPath: string) {
this._pendingNetworkPath = pendingNetworkPath;
}
public getAppliedNetworkPath() {
return this._appliedNetworkPath;
}
public getAppliedExportCertUserConsent() {
return this._appliedExportCertUserConsent;
}
public getPendingExportCertUserConsent() {
return this._pendingExportCertUserConsent;
}
public setPendingExportCertUserConsent(pendingExportCertUserConsent: boolean) {
this._pendingExportCertUserConsent = pendingExportCertUserConsent;
}
}

Просмотреть файл

@ -82,7 +82,8 @@ export enum TelemetryAction {
TdeMigrationFailures = 'TdeMigrationFailures',
TdeMigrationClientException = 'TdeMigrationClientException',
TdeConfigurationUseADS = 'TdeConfigurationUseADS',
TdeConfigurationIgnoreADS = 'TdeConfigurationIgnoreADS'
TdeConfigurationAlreadyMigrated = 'TdeConfigurationAlreadyMigrated',
TdeConfigurationCancelled = 'TdeConfigurationCancelled'
}
export function logError(telemetryView: TelemetryViews, err: string, error: any): void {

Просмотреть файл

@ -23,6 +23,7 @@ import { logError, TelemetryViews, TelemetryAction, sendSqlMigrationActionEvent,
import { TdeConfigurationDialog } from '../dialog/tdeConfiguration/tdeConfigurationDialog';
import { TdeMigrationModel } from '../models/tdeModels';
import { getSourceConnectionProfile } from '../api/sqlUtils';
import { ConfigDialogSetting } from '../models/tdeModels'
export interface Product {
type: MigrationTargetType;
@ -382,7 +383,7 @@ export class SKURecommendationPage extends MigrationWizardPage {
'margin': '0',
}
}).component();
this._tdeConfigurationDialog = new TdeConfigurationDialog(this, this.wizard, this.migrationStateModel, () => this._onTdeConfigClosed());
this._tdeConfigurationDialog = new TdeConfigurationDialog(this.migrationStateModel, () => this._onTdeConfigClosed());
this._disposables.push(this._tdeEditButton.onDidClick(
async (e) => await this._tdeConfigurationDialog.openDialog()));
@ -845,16 +846,32 @@ export class SKURecommendationPage extends MigrationWizardPage {
}
private _onTdeConfigClosed(): Thenable<void> {
const tdeMsg = (this.migrationStateModel.tdeMigrationConfig.isTdeMigrationMethodAdsConfirmed()) ? constants.TDE_WIZARD_MSG_TDE : constants.TDE_WIZARD_MSG_MANUAL;
const tdeMsg = (this.migrationStateModel.tdeMigrationConfig.getAppliedConfigDialogSetting() === ConfigDialogSetting.ExportCertificates) ? constants.TDE_WIZARD_MSG_TDE : constants.TDE_WIZARD_MSG_MANUAL;
this._tdedatabaseSelectedHelperText.value = constants.TDE_MSG_DATABASES_SELECTED(this.migrationStateModel.tdeMigrationConfig.getTdeEnabledDatabasesCount(), tdeMsg);
const tdeTelemetryAction = (this.migrationStateModel.tdeMigrationConfig.isTdeMigrationMethodAdsConfirmed()) ? TelemetryAction.TdeConfigurationUseADS : TelemetryAction.TdeConfigurationIgnoreADS;
let tdeTelemetryAction: TelemetryAction;
switch (this.migrationStateModel.tdeMigrationConfig.getAppliedConfigDialogSetting()) {
case ConfigDialogSetting.ExportCertificates:
tdeTelemetryAction = TelemetryAction.TdeConfigurationUseADS;
break;
case ConfigDialogSetting.DoNotExport:
tdeTelemetryAction = TelemetryAction.TdeConfigurationAlreadyMigrated;
break;
case ConfigDialogSetting.NoSelection:
tdeTelemetryAction = TelemetryAction.TdeConfigurationCancelled;
break;
default:
tdeTelemetryAction = TelemetryAction.TdeConfigurationCancelled;
break;
}
sendSqlMigrationActionEvent(
TelemetryViews.TdeConfigurationDialog,
tdeTelemetryAction,
{
...getTelemetryProps(this.migrationStateModel)
...getTelemetryProps(this.migrationStateModel),
'numberOfDbsWithTde': this.migrationStateModel.tdeMigrationConfig.getTdeEnabledDatabasesCount().toString()
},
{}
);

Просмотреть файл

@ -17,8 +17,9 @@ import { azureResource } from 'azurecore';
import { AzureSqlDatabaseServer, getVMInstanceView, SqlVMServer } from '../api/azure';
import { collectTargetDatabaseInfo, TargetDatabaseInfo } from '../api/sqlUtils';
import { MigrationLocalStorage, MigrationServiceContext } from '../models/migrationLocalStorage';
import { TdeMigrationDialog } from '../dialog/tdeConfiguration/tdeMigrationDialog';
import { ValidationErrorCodes } from '../constants/helper';
import { TdeMigrationDialog } from '../dialog/tdeConfiguration/tdeMigrationDialog';
import { TdeMigrationResult, TdeMigrationState } from '../models/tdeModels';
const TDE_MIGRATION_BUTTON_INDEX = 1;
@ -45,6 +46,7 @@ export class TargetSelectionPage extends MigrationWizardPage {
private _connectionResultsInfoBox!: azdata.InfoBoxComponent;
private _migrationTargetPlatform!: utils.MigrationTargetType;
private _serviceContext!: MigrationServiceContext;
private _certMigrationEventEmitter: vscode.EventEmitter<TdeMigrationResult>;
constructor(
wizard: azdata.window.Wizard,
@ -53,6 +55,10 @@ export class TargetSelectionPage extends MigrationWizardPage {
wizard,
azdata.window.createWizardPage(constants.AZURE_SQL_TARGET_PAGE_TITLE),
migrationStateModel);
this._certMigrationEventEmitter = new vscode.EventEmitter();
this._certMigrationEventEmitter.event(() => this._updateNextButton());
}
protected async registerContent(view: azdata.ModelView): Promise<void> {
@ -82,25 +88,20 @@ export class TargetSelectionPage extends MigrationWizardPage {
d => { try { d.dispose(); } catch { } });
}));
if (this.migrationStateModel.resumeAssessment) {
await this.populateAzureAccountsDropdown();
}
this._disposables.push(
this.wizard.customButtons[TDE_MIGRATION_BUTTON_INDEX].onClick(
async e => await this._startTdeMigration()));
if (this.migrationStateModel.resumeAssessment) {
await this.populateAzureAccountsDropdown();
}
await this._view.initializeModel(form);
}
private async _startTdeMigration(): Promise<void> {
const dialog = new TdeMigrationDialog(this.migrationStateModel);
await dialog.openDialog();
}
public async onPageEnter(pageChangeInfo: azdata.window.WizardPageChangeInfo): Promise<void> {
this.wizard.customButtons[TDE_MIGRATION_BUTTON_INDEX].hidden = !this.migrationStateModel.tdeMigrationConfig.shouldAdsMigrateCertificates();
this._updateNextButton();
this._updateTdeMigrationButtonStatus();
if (pageChangeInfo.newPage < pageChangeInfo.lastPage) {
@ -163,6 +164,10 @@ export class TargetSelectionPage extends MigrationWizardPage {
if (targetMi && targetMi.properties?.state.toLowerCase() !== 'Ready'.toLowerCase()) {
errors.push(constants.MI_NOT_READY_ERROR(targetMi.name, targetMi.properties?.state));
}
if (this.migrationStateModel.tdeMigrationConfig.shouldAdsMigrateCertificates() &&
this.migrationStateModel.tdeMigrationConfig.lastTdeMigrationResult().state !== TdeMigrationState.Succeeded) {
errors.push(constants.TDE_MIGRATE_REQUIRED);
}
break;
case MigrationTargetType.SQLVM:
const targetVm = this.migrationStateModel._targetServerInstance as SqlVMServer;
@ -281,7 +286,6 @@ export class TargetSelectionPage extends MigrationWizardPage {
public async onPageLeave(pageChangeInfo: azdata.window.WizardPageChangeInfo): Promise<void> {
this.wizard.registerNavigationValidator(pageChangeInfo => true);
this.wizard.message = { text: '' };
this.wizard.customButtons[TDE_MIGRATION_BUTTON_INDEX].hidden = true;
}
@ -759,7 +763,6 @@ export class TargetSelectionPage extends MigrationWizardPage {
}
this.migrationStateModel.tdeMigrationConfig.resetTdeMigrationResult();
break;
case MigrationTargetType.SQLDB:
const sqlDatabaseServer = this.migrationStateModel._targetSqlDatabaseServers?.find(
@ -792,6 +795,8 @@ export class TargetSelectionPage extends MigrationWizardPage {
}
}
this._resetTdeMigrationResult();
this.migrationStateModel._sqlMigrationServices = undefined!;
if (isSqlDbTarget) {
await this._resetTargetMapping();
@ -1046,7 +1051,6 @@ export class TargetSelectionPage extends MigrationWizardPage {
this.migrationStateModel._targetManagedInstances,
this.migrationStateModel._location,
this.migrationStateModel._resourceGroup);
this._updateTdeMigrationButtonStatus();
break;
case MigrationTargetType.SQLVM:
@ -1075,12 +1079,6 @@ export class TargetSelectionPage extends MigrationWizardPage {
}
}
private _updateTdeMigrationButtonStatus() {
this.wizard.customButtons[TDE_MIGRATION_BUTTON_INDEX].enabled =
this.migrationStateModel.tdeMigrationConfig.shouldAdsMigrateCertificates() &&
this.migrationStateModel._targetManagedInstances.length > 0;
}
private async _populateResourceMappingTable(targetDatabases: TargetDatabaseInfo[]): Promise<void> {
// populate target database list
const databaseValues = this._getTargetDatabaseDropdownValues(
@ -1218,4 +1216,32 @@ export class TargetSelectionPage extends MigrationWizardPage {
targetDatabaseCollation.length > 0 &&
sourceDatabaseCollation.toLocaleLowerCase() === targetDatabaseCollation.toLocaleLowerCase();
}
private _updateNextButton() {
if (this.migrationStateModel.tdeMigrationConfig.shouldAdsMigrateCertificates() &&
this.migrationStateModel.tdeMigrationConfig.lastTdeMigrationResult().state !== TdeMigrationState.Succeeded) {
this.wizard.pages[this.wizard.currentPage + 1].enabled = false;
this.wizard.nextButton.enabled = false;
} else {
this.wizard.pages[this.wizard.currentPage + 1].enabled = true;
this.wizard.nextButton.enabled = true;
}
}
private _updateTdeMigrationButtonStatus() {
this.wizard.customButtons[TDE_MIGRATION_BUTTON_INDEX].enabled =
this.migrationStateModel.tdeMigrationConfig.shouldAdsMigrateCertificates() &&
this.migrationStateModel._targetManagedInstances.length > 0;
}
private _resetTdeMigrationResult() {
this.migrationStateModel.tdeMigrationConfig.resetTdeMigrationResult();
this._updateNextButton();
}
private async _startTdeMigration(): Promise<void> {
this._resetTdeMigrationResult();
const dialog = new TdeMigrationDialog(this.migrationStateModel, this._certMigrationEventEmitter);
await dialog.openDialog();
}
}