mirror of
https://github.com/ckaczor/azuredatastudio.git
synced 2026-02-17 02:51:36 -05:00
Link Database Earliest and Latest Point in time with DryRun (#17506)
This commit is contained in:
committed by
GitHub
parent
b48f392ab2
commit
08d3803453
@@ -1189,7 +1189,7 @@
|
|||||||
"description": "%arc.sql.retention.days.description%",
|
"description": "%arc.sql.retention.days.description%",
|
||||||
"variableName": "AZDATA_NB_VAR_SQL_RETENTION_DAYS",
|
"variableName": "AZDATA_NB_VAR_SQL_RETENTION_DAYS",
|
||||||
"type": "number",
|
"type": "number",
|
||||||
"min": 1,
|
"min": 0,
|
||||||
"max": 35,
|
"max": 35,
|
||||||
"required": false
|
"required": false
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -151,7 +151,7 @@
|
|||||||
"arc.sql.license.type.label": "I already have a SQL Server License",
|
"arc.sql.license.type.label": "I already have a SQL Server License",
|
||||||
"arc.sql.license.type.description": "Apply the Azure Hybrid Benefit if you already own a SQL Server License",
|
"arc.sql.license.type.description": "Apply the Azure Hybrid Benefit if you already own a SQL Server License",
|
||||||
"arc.sql.pitr.description": "Point in time restore",
|
"arc.sql.pitr.description": "Point in time restore",
|
||||||
"arc.sql.retention.days.label": "PITR retention (days)",
|
"arc.sql.retention.days.label": "Point in time retention (days)",
|
||||||
"arc.sql.retention.days.description": "Specify how long you want to keep your point-in-time backups.",
|
"arc.sql.retention.days.description": "Specify how long you want to keep your point-in-time backups.",
|
||||||
"arc.sql.dev.use.description": "Check the box to indicate this instance will be used for development or testing purposes only. This instance will not be billed.",
|
"arc.sql.dev.use.description": "Check the box to indicate this instance will be used for development or testing purposes only. This instance will not be billed.",
|
||||||
"arc.postgres.storage-class.backups.description": "The storage class to be used for backup persistent volumes",
|
"arc.postgres.storage-class.backups.description": "The storage class to be used for backup persistent volumes",
|
||||||
|
|||||||
@@ -363,3 +363,11 @@ export function debounce(delay: number): Function {
|
|||||||
};
|
};
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function getTimeStamp(dateTime: string | undefined): number {
|
||||||
|
return dateTime ? (new Date(dateTime)).getTime() : 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function checkISOTimeString(dateTime: string): boolean {
|
||||||
|
return /\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}.\d.*Z/.test(dateTime);
|
||||||
|
}
|
||||||
|
|||||||
@@ -60,18 +60,19 @@ export const type = localize('arc.type', "Type");
|
|||||||
export const status = localize('arc.status', "Status");
|
export const status = localize('arc.status', "Status");
|
||||||
export const database = localize('arc.database', "Database");
|
export const database = localize('arc.database', "Database");
|
||||||
export const sourceDatabase = localize('arc.sourceDatabase', "Source database");
|
export const sourceDatabase = localize('arc.sourceDatabase', "Source database");
|
||||||
export const earliestPitrRestorePoint = localize('arc.earliestPitrRestorePoint', "Earliest PITR restore point");
|
export const earliestPitrRestorePoint = localize('arc.earliestPitrRestorePoint', "Earliest point in time");
|
||||||
export const latestpitrRestorePoint = localize('arc.latestpitrRestorePoint', "Latest PITR restore point");
|
export const latestpitrRestorePoint = localize('arc.latestpitrRestorePoint', "Latest point in time");
|
||||||
export const pitr = localize('arc.pitr', "Point-in-time restore (PITR)");
|
export const pitr = localize('arc.pitr', "Point in time restore");
|
||||||
export const projectDetails = localize('arc.projectDetails', "Project Details");
|
export const projectDetails = localize('arc.projectDetails', "Project Details");
|
||||||
export const projectDetailsText = localize('arc.projectDetailsText', "Select the subscription to manage deployed resources. Use resource groups like folders to organize and manage all your resources.");
|
export const projectDetailsText = localize('arc.projectDetailsText', "Select the subscription to manage deployed resources. Use resource groups like folders to organize and manage all your resources.");
|
||||||
export const sourceDetails = localize('arc.sourceDetails', "Source Details");
|
export const sourceDetails = localize('arc.sourceDetails', "Source Details");
|
||||||
export const sourceDetailsText = localize('arc.sourceDetailsText', "Select a backup source and provide details. Additional settings will be defaulted where possible based on the selected backup.");
|
export const sourceDetailsText = localize('arc.sourceDetailsText', "Select a backup source and provide details. Additional settings will be defaulted where possible based on the selected database.");
|
||||||
export const databaseDetails = localize('arc.databaseDetails', "Database Details");
|
export const databaseDetails = localize('arc.databaseDetails', "Destination Details");
|
||||||
export const databaseDetailsText = localize('arc.databaseDetailsText', "Enter the required settings for this database, including a name and a target managed instance. By default, the source instance is selected.");
|
export const restorePointDetails = localize('arc.restorePointDetails', "Restore Point Details");
|
||||||
|
export const databaseDetailsText = localize('arc.databaseDetailsText', "Enter the required settings for target database name and SQL managed instance. By default, the source managed instance is selected.");
|
||||||
export const restore = localize('arc.restore', "Restore");
|
export const restore = localize('arc.restore', "Restore");
|
||||||
export const instance = localize('arc.instance', "Instance");
|
export const instance = localize('arc.instance', "Instance");
|
||||||
export const restorePoint = localize('arc.restorePoint', "Restore point (UTC)");
|
export const restorePoint = localize('arc.restorePoint', "Restore point (UTC), in a time format: 'YYYY-MM-DDTHH:MM:SSZ");
|
||||||
export const restoreDatabase = localize('arc.restoreDatabase', "Restore Database");
|
export const restoreDatabase = localize('arc.restoreDatabase', "Restore Database");
|
||||||
export const miaaAdmin = localize('arc.miaaAdmin', "Managed instance admin");
|
export const miaaAdmin = localize('arc.miaaAdmin', "Managed instance admin");
|
||||||
export const extensionName = localize('arc.extensionName', "Extension name");
|
export const extensionName = localize('arc.extensionName', "Extension name");
|
||||||
@@ -79,6 +80,7 @@ export const extensionsDescription = localize('arc.extensionsDescription', "Post
|
|||||||
export const extensionsFunction = localize('arc.extensionsFunction', "Some extensions must be loaded into PostgreSQL at startup time before they can be used. These preloaded extensions can be viewed and edited below.");
|
export const extensionsFunction = localize('arc.extensionsFunction', "Some extensions must be loaded into PostgreSQL at startup time before they can be used. These preloaded extensions can be viewed and edited below.");
|
||||||
export function extensionsAddFunction(extensions: string): string { return localize('arc.extensionsAddFunction', "Some extensions must be loaded into PostgreSQL at startup time before they can be used. To edit, type in comma separated list of valid extensions: ({0}).", extensions); }
|
export function extensionsAddFunction(extensions: string): string { return localize('arc.extensionsAddFunction', "Some extensions must be loaded into PostgreSQL at startup time before they can be used. To edit, type in comma separated list of valid extensions: ({0}).", extensions); }
|
||||||
export function extensionsAddErrorrMessage(extensions: string): string { return localize('arc.extensionsAddErrorrMessage', "Value should be either of the following: ({0}).", extensions); }
|
export function extensionsAddErrorrMessage(extensions: string): string { return localize('arc.extensionsAddErrorrMessage', "Value should be either of the following: ({0}).", extensions); }
|
||||||
|
export function restorePointErrorMessage(earliestPoint: string, latestPoint: string) { return localize('arc.restorePointErrorrMessage', "Provide time in correct format and within range: {0} to {1}", earliestPoint, latestPoint); }
|
||||||
export const extensionsLearnMore = localize('arc.extensionsLearnMore', "Learn more about PostgreSQL extensions.");
|
export const extensionsLearnMore = localize('arc.extensionsLearnMore', "Learn more about PostgreSQL extensions.");
|
||||||
export const extensionsTableLoading = localize('arc.extensionsTableLoading', "Table of preloaded extensions are loading.");
|
export const extensionsTableLoading = localize('arc.extensionsTableLoading', "Table of preloaded extensions are loading.");
|
||||||
export const extensionsTableLabel = localize('arc.extensionsTableLabel', "Table of preloaded extensions.");
|
export const extensionsTableLabel = localize('arc.extensionsTableLabel', "Table of preloaded extensions.");
|
||||||
@@ -192,7 +194,8 @@ export const postgresComputeAndStorageDescriptionPartOne = localize('arc.postgre
|
|||||||
export const miaaComputeAndStorageDescriptionPartOne = localize('arc.miaaComputeAndStorageDescriptionPartOne', "You can scale your Azure SQL managed instance - Azure Arc by");
|
export const miaaComputeAndStorageDescriptionPartOne = localize('arc.miaaComputeAndStorageDescriptionPartOne', "You can scale your Azure SQL managed instance - Azure Arc by");
|
||||||
export const miaaBackupsDatabasesDescription = localize('arc.miaaBackupsDatabasesDescription', "Databases with available backups are displayed below. Restore databases to this instance or any other instance within the same custom location.");
|
export const miaaBackupsDatabasesDescription = localize('arc.miaaBackupsDatabasesDescription', "Databases with available backups are displayed below. Restore databases to this instance or any other instance within the same custom location.");
|
||||||
export const pitrInfo = localize('arc.pitrInfo', "Specify how long you want to keep your point-in-time backups. Customize this for backup availability.");
|
export const pitrInfo = localize('arc.pitrInfo', "Specify how long you want to keep your point-in-time backups. Customize this for backup availability.");
|
||||||
export const restoreInfo = localize('arc.restoreInfo', "Restore a database to an Azure Arc enabled SQL Managed Instance of your choice.");
|
export const restoreInfo = localize('arc.restoreInfo', "Restore a database to an Azure Arc enabled SQL Managed Instance.");
|
||||||
|
export const restorePointText = localize('arc.restorePointText', "Enter a restore point in the specified time format within given range of earliest and latest restore time.");
|
||||||
export const postgresComputeAndStorageDescriptionPartTwo = localize('arc.postgres.computeAndStorageDescriptionPartTwo', "PostgreSQL Hyperscale server group by");
|
export const postgresComputeAndStorageDescriptionPartTwo = localize('arc.postgres.computeAndStorageDescriptionPartTwo', "PostgreSQL Hyperscale server group by");
|
||||||
export const computeAndStorageDescriptionPartThree = localize('arc.computeAndStorageDescriptionPartThree', "without downtime and by");
|
export const computeAndStorageDescriptionPartThree = localize('arc.computeAndStorageDescriptionPartThree', "without downtime and by");
|
||||||
export const computeAndStorageDescriptionPartFour = localize('arc.computeAndStorageDescriptionPartFour', "Before doing so, you need to ensure");
|
export const computeAndStorageDescriptionPartFour = localize('arc.computeAndStorageDescriptionPartFour', "Before doing so, you need to ensure");
|
||||||
@@ -289,6 +292,7 @@ export const couldNotFindControllerRegistration = localize('arc.couldNotFindCont
|
|||||||
export const dropMultipleExtensions = localize('arc.dropMultipleExtensions', "Currently dropping another extension, try again once that is completed.");
|
export const dropMultipleExtensions = localize('arc.dropMultipleExtensions', "Currently dropping another extension, try again once that is completed.");
|
||||||
export function updateExtensionsFailed(error: any): string { return localize('arc.updateExtensionsFailed', "Editing extensions failed. {0}", getErrorMessage(error)); }
|
export function updateExtensionsFailed(error: any): string { return localize('arc.updateExtensionsFailed', "Editing extensions failed. {0}", getErrorMessage(error)); }
|
||||||
export function refreshFailed(error: any): string { return localize('arc.refreshFailed', "Refresh failed. {0}", getErrorMessage(error)); }
|
export function refreshFailed(error: any): string { return localize('arc.refreshFailed', "Refresh failed. {0}", getErrorMessage(error)); }
|
||||||
|
export function restoreTimeWindowUpdateFailed(error: any): string { return localize('arc.restoreTimeWindowUpdateFailed', "Point in time restore time window update failed. {0}", getErrorMessage(error)); }
|
||||||
export function resetFailed(error: any): string { return localize('arc.resetFailed', "Reset failed. {0}", getErrorMessage(error)); }
|
export function resetFailed(error: any): string { return localize('arc.resetFailed', "Reset failed. {0}", getErrorMessage(error)); }
|
||||||
export function openDashboardFailed(error: any): string { return localize('arc.openDashboardFailed', "Error opening dashboard. {0}", getErrorMessage(error)); }
|
export function openDashboardFailed(error: any): string { return localize('arc.openDashboardFailed', "Error opening dashboard. {0}", getErrorMessage(error)); }
|
||||||
export function instanceDeletionFailed(name: string, error: any): string { return localize('arc.instanceDeletionFailed', "Failed to delete instance {0}. {1}", name, getErrorMessage(error)); }
|
export function instanceDeletionFailed(name: string, error: any): string { return localize('arc.instanceDeletionFailed', "Failed to delete instance {0}. {1}", name, getErrorMessage(error)); }
|
||||||
|
|||||||
@@ -9,14 +9,14 @@ import * as azExt from 'az-ext';
|
|||||||
import * as vscode from 'vscode';
|
import * as vscode from 'vscode';
|
||||||
import { UserCancelledError } from '../common/api';
|
import { UserCancelledError } from '../common/api';
|
||||||
import { Deferred } from '../common/promise';
|
import { Deferred } from '../common/promise';
|
||||||
import { parseIpAndPort } from '../common/utils';
|
import { getTimeStamp, parseIpAndPort } from '../common/utils';
|
||||||
import * as loc from '../localizedConstants';
|
import * as loc from '../localizedConstants';
|
||||||
import { ConnectToMiaaSqlDialog } from '../ui/dialogs/connectMiaaDialog';
|
import { ConnectToMiaaSqlDialog } from '../ui/dialogs/connectMiaaDialog';
|
||||||
import { AzureArcTreeDataProvider } from '../ui/tree/azureArcTreeDataProvider';
|
import { AzureArcTreeDataProvider } from '../ui/tree/azureArcTreeDataProvider';
|
||||||
import { ControllerModel, Registration } from './controllerModel';
|
import { ControllerModel, Registration } from './controllerModel';
|
||||||
import { ResourceModel } from './resourceModel';
|
import { ResourceModel } from './resourceModel';
|
||||||
|
|
||||||
export type DatabaseModel = { name: string, status: string, lastBackup: string };
|
export type DatabaseModel = { name: string, status: string, earliestBackup: string, lastBackup: string };
|
||||||
export type RPModel = { recoveryPointObjective: string, retentionDays: string };
|
export type RPModel = { recoveryPointObjective: string, retentionDays: string };
|
||||||
export type PITRModel = {
|
export type PITRModel = {
|
||||||
instanceName: string,
|
instanceName: string,
|
||||||
@@ -27,7 +27,9 @@ export type PITRModel = {
|
|||||||
restorePoint: string,
|
restorePoint: string,
|
||||||
earliestPitr: string,
|
earliestPitr: string,
|
||||||
latestPitr: string,
|
latestPitr: string,
|
||||||
|
destDbName: string
|
||||||
};
|
};
|
||||||
|
|
||||||
export const systemDbs = ['master', 'msdb', 'tempdb', 'model'];
|
export const systemDbs = ['master', 'msdb', 'tempdb', 'model'];
|
||||||
export class MiaaModel extends ResourceModel {
|
export class MiaaModel extends ResourceModel {
|
||||||
|
|
||||||
@@ -44,12 +46,20 @@ export class MiaaModel extends ResourceModel {
|
|||||||
recoveryPointObjective: '',
|
recoveryPointObjective: '',
|
||||||
retentionDays: ''
|
retentionDays: ''
|
||||||
};
|
};
|
||||||
|
private _databaseTimeWindow: Map<string, string[]>;
|
||||||
|
|
||||||
private _refreshPromise: Deferred<void> | undefined = undefined;
|
private _refreshPromise: Deferred<void> | undefined = undefined;
|
||||||
|
private _pitrArgs = {
|
||||||
|
destName: '',
|
||||||
|
managedInstance: '',
|
||||||
|
time: '',
|
||||||
|
noWait: true,
|
||||||
|
dryRun: false
|
||||||
|
};
|
||||||
constructor(_controllerModel: ControllerModel, private _miaaInfo: MiaaResourceInfo, registration: Registration, private _treeDataProvider: AzureArcTreeDataProvider) {
|
constructor(_controllerModel: ControllerModel, private _miaaInfo: MiaaResourceInfo, registration: Registration, private _treeDataProvider: AzureArcTreeDataProvider) {
|
||||||
super(_controllerModel, _miaaInfo, registration);
|
super(_controllerModel, _miaaInfo, registration);
|
||||||
this._azApi = <azExt.IExtension>vscode.extensions.getExtension(azExt.extension.name)?.exports;
|
this._azApi = <azExt.IExtension>vscode.extensions.getExtension(azExt.extension.name)?.exports;
|
||||||
|
this._databaseTimeWindow = new Map<string, string[]>();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -92,6 +102,7 @@ export class MiaaModel extends ResourceModel {
|
|||||||
this.configLastUpdated = new Date();
|
this.configLastUpdated = new Date();
|
||||||
this.rpSettings.retentionDays = this._config?.spec?.backup?.retentionPeriodInDays?.toString() ?? '';
|
this.rpSettings.retentionDays = this._config?.spec?.backup?.retentionPeriodInDays?.toString() ?? '';
|
||||||
this._onConfigUpdated.fire(this._config);
|
this._onConfigUpdated.fire(this._config);
|
||||||
|
this._onDatabasesUpdated.fire(this._databases);
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
// If an error occurs show a message so the user knows something failed but still
|
// If an error occurs show a message so the user knows something failed but still
|
||||||
// fire the event so callers can know to update (e.g. so dashboards don't show the
|
// fire the event so callers can know to update (e.g. so dashboards don't show the
|
||||||
@@ -138,6 +149,12 @@ export class MiaaModel extends ResourceModel {
|
|||||||
throw error;
|
throw error;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the list of databases and adds backup earliest and latest point in time
|
||||||
|
* information, this could be used as an upper and lower time limit for restoring
|
||||||
|
* backup.
|
||||||
|
*/
|
||||||
public async getDatabases(promptForConnection: boolean = true): Promise<void> {
|
public async getDatabases(promptForConnection: boolean = true): Promise<void> {
|
||||||
if (!this._connectionProfile) {
|
if (!this._connectionProfile) {
|
||||||
await this.getConnectionProfile(promptForConnection);
|
await this.getConnectionProfile(promptForConnection);
|
||||||
@@ -158,10 +175,21 @@ export class MiaaModel extends ResourceModel {
|
|||||||
if (!databases) {
|
if (!databases) {
|
||||||
throw new Error('Could not fetch databases');
|
throw new Error('Could not fetch databases');
|
||||||
}
|
}
|
||||||
if (databases.length > 0 && typeof (databases[0]) === 'object') {
|
else {
|
||||||
this._databases = (<azdata.DatabaseInfo[]>databases).map(db => { return { name: db.options['name'], status: db.options['state'], lastBackup: db.options['lastBackup'] }; });
|
if (databases.length > 0 && typeof (databases[0]) === 'object') {
|
||||||
} else {
|
for (let i in databases) {
|
||||||
this._databases = (<string[]>databases).map(db => { return { name: db, status: '-', lastBackup: '' }; });
|
const di: azdata.DatabaseInfo = <azdata.DatabaseInfo>databases[i];
|
||||||
|
const name = di.options['name'];
|
||||||
|
await this.executeDryRun(di.options['name']);
|
||||||
|
const dm: DatabaseModel = {
|
||||||
|
name: name, status: di.options['state'], earliestBackup: this._databaseTimeWindow.get(name)?.[0] ?? '',
|
||||||
|
lastBackup: this._databaseTimeWindow.get(name)?.[1] ?? ''
|
||||||
|
};
|
||||||
|
this._databases[i] = dm;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
this._databases = (<string[]>databases).map(db => { return { name: db, status: '-', earliestBackup: '', lastBackup: '' }; });
|
||||||
|
}
|
||||||
}
|
}
|
||||||
this.databasesLastUpdated = new Date();
|
this.databasesLastUpdated = new Date();
|
||||||
this._onDatabasesUpdated.fire(this._databases);
|
this._onDatabasesUpdated.fire(this._databases);
|
||||||
@@ -206,4 +234,33 @@ export class MiaaModel extends ResourceModel {
|
|||||||
await this._treeDataProvider.saveControllers();
|
await this._treeDataProvider.saveControllers();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected async executeDryRun(dbName: string): Promise<void> {
|
||||||
|
// Allow next dry Run to be executed only after 5(300000 ms ) minutes from current time as the log backups are
|
||||||
|
// generated only at 5 minutes interval
|
||||||
|
if ((systemDbs.indexOf(dbName) === -1) && (Date.now() - getTimeStamp(this._databaseTimeWindow.get(dbName)?.[1]) >= 300000)) {
|
||||||
|
try {
|
||||||
|
//Execute dryRun for earliestTime and save latest time as well so there is one call to az cli
|
||||||
|
this._pitrArgs.destName = dbName + '-' + Date.now().toString();
|
||||||
|
this._pitrArgs.managedInstance = this.info.name;
|
||||||
|
this._pitrArgs.time = new Date().toISOString();
|
||||||
|
this._pitrArgs.noWait = false;
|
||||||
|
this._pitrArgs.dryRun = true;
|
||||||
|
const result = await this._azApi.az.sql.midbarc.restore(
|
||||||
|
dbName, this._pitrArgs, this.controllerModel.info.namespace, this.controllerModel.azAdditionalEnvVars);
|
||||||
|
const restoreResult = result.stdout;
|
||||||
|
if (restoreResult) {
|
||||||
|
const earliestTime = restoreResult['earliestRestoreTime'];
|
||||||
|
const latestTime = restoreResult['latestRestoreTime'];
|
||||||
|
console.log(loc.earliestPitrRestorePoint + '-' + dbName + ':' + earliestTime);
|
||||||
|
console.log(loc.latestpitrRestorePoint + '-' + dbName + ':' + latestTime);
|
||||||
|
this._databaseTimeWindow.set(dbName, [earliestTime, latestTime]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (err) {
|
||||||
|
console.log(loc.pitr + ' ' + loc.failed + ':' + err);
|
||||||
|
this._databaseTimeWindow.set(dbName, ['', '']);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -20,7 +20,6 @@ export class MiaaBackupsPage extends DashboardPage {
|
|||||||
this._azApi = vscode.extensions.getExtension(azExt.extension.name)?.exports;
|
this._azApi = vscode.extensions.getExtension(azExt.extension.name)?.exports;
|
||||||
this.disposables.push(
|
this.disposables.push(
|
||||||
this._miaaModel.onDatabasesUpdated(() => this.eventuallyRunOnInitialized(() => this.handleDatabasesUpdated())),
|
this._miaaModel.onDatabasesUpdated(() => this.eventuallyRunOnInitialized(() => this.handleDatabasesUpdated())),
|
||||||
this._miaaModel.onConfigUpdated(() => this.eventuallyRunOnInitialized(() => this.handleDatabasesUpdated()))
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
private _databasesContainer!: azdata.DivContainer;
|
private _databasesContainer!: azdata.DivContainer;
|
||||||
@@ -32,16 +31,17 @@ export class MiaaBackupsPage extends DashboardPage {
|
|||||||
private _databasesMessage!: azdata.TextComponent;
|
private _databasesMessage!: azdata.TextComponent;
|
||||||
private readonly _azApi: azExt.IExtension;
|
private readonly _azApi: azExt.IExtension;
|
||||||
|
|
||||||
public saveArgs: RPModel = {
|
private _saveArgs: RPModel = {
|
||||||
recoveryPointObjective: '',
|
recoveryPointObjective: '',
|
||||||
retentionDays: ''
|
retentionDays: ''
|
||||||
};
|
};
|
||||||
|
|
||||||
public pitrArgs = {
|
private _pitrArgs = {
|
||||||
destName: '',
|
destName: '',
|
||||||
managedInstance: '',
|
managedInstance: '',
|
||||||
time: '',
|
time: '',
|
||||||
noWait: true
|
noWait: true,
|
||||||
|
dryRun: false
|
||||||
};
|
};
|
||||||
|
|
||||||
public get title(): string {
|
public get title(): string {
|
||||||
@@ -66,7 +66,7 @@ export class MiaaBackupsPage extends DashboardPage {
|
|||||||
.component();
|
.component();
|
||||||
const content = this.modelView.modelBuilder.divContainer().component();
|
const content = this.modelView.modelBuilder.divContainer().component();
|
||||||
this._databasesContainer = this.modelView.modelBuilder.divContainer().component();
|
this._databasesContainer = this.modelView.modelBuilder.divContainer().component();
|
||||||
root.addItem(content, { CSSStyles: { 'margin': '20px' } });
|
root.addItem(content, { CSSStyles: { 'margin': '5px' } });
|
||||||
const databaseTitle = this.modelView.modelBuilder.text().withProps({
|
const databaseTitle = this.modelView.modelBuilder.text().withProps({
|
||||||
value: loc.databases,
|
value: loc.databases,
|
||||||
CSSStyles: { ...cssStyles.title },
|
CSSStyles: { ...cssStyles.title },
|
||||||
@@ -130,11 +130,19 @@ export class MiaaBackupsPage extends DashboardPage {
|
|||||||
headerCssStyles: cssStyles.tableHeader,
|
headerCssStyles: cssStyles.tableHeader,
|
||||||
rowCssStyles: cssStyles.tableRow
|
rowCssStyles: cssStyles.tableRow
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
displayName: loc.earliestPitrRestorePoint,
|
||||||
|
valueType: azdata.DeclarativeDataType.string,
|
||||||
|
isReadOnly: true,
|
||||||
|
width: '30%',
|
||||||
|
headerCssStyles: cssStyles.tableHeader,
|
||||||
|
rowCssStyles: cssStyles.tableRow
|
||||||
|
},
|
||||||
{
|
{
|
||||||
displayName: loc.latestpitrRestorePoint,
|
displayName: loc.latestpitrRestorePoint,
|
||||||
valueType: azdata.DeclarativeDataType.string,
|
valueType: azdata.DeclarativeDataType.string,
|
||||||
isReadOnly: true,
|
isReadOnly: true,
|
||||||
width: '50%',
|
width: '30%',
|
||||||
headerCssStyles: cssStyles.tableHeader,
|
headerCssStyles: cssStyles.tableHeader,
|
||||||
rowCssStyles: cssStyles.tableRow
|
rowCssStyles: cssStyles.tableRow
|
||||||
},
|
},
|
||||||
@@ -142,7 +150,7 @@ export class MiaaBackupsPage extends DashboardPage {
|
|||||||
displayName: loc.restore,
|
displayName: loc.restore,
|
||||||
valueType: azdata.DeclarativeDataType.component,
|
valueType: azdata.DeclarativeDataType.component,
|
||||||
isReadOnly: true,
|
isReadOnly: true,
|
||||||
width: '20%',
|
width: '10%',
|
||||||
headerCssStyles: cssStyles.tableHeader,
|
headerCssStyles: cssStyles.tableHeader,
|
||||||
rowCssStyles: cssStyles.tableRow,
|
rowCssStyles: cssStyles.tableRow,
|
||||||
}
|
}
|
||||||
@@ -198,13 +206,13 @@ export class MiaaBackupsPage extends DashboardPage {
|
|||||||
this._configureRetentionPolicyButton.onDidClick(async () => {
|
this._configureRetentionPolicyButton.onDidClick(async () => {
|
||||||
const retentionPolicySqlDialog = new ConfigureRPOSqlDialog(this._miaaModel);
|
const retentionPolicySqlDialog = new ConfigureRPOSqlDialog(this._miaaModel);
|
||||||
this.refreshRD();
|
this.refreshRD();
|
||||||
retentionPolicySqlDialog.showDialog(loc.configureRP, this.saveArgs.retentionDays);
|
retentionPolicySqlDialog.showDialog(loc.configureRP, this._saveArgs.retentionDays);
|
||||||
|
|
||||||
let rpArg = await retentionPolicySqlDialog.waitForClose();
|
let rpArg = await retentionPolicySqlDialog.waitForClose();
|
||||||
if (rpArg) {
|
if (rpArg) {
|
||||||
try {
|
try {
|
||||||
this._configureRetentionPolicyButton.enabled = false;
|
this._configureRetentionPolicyButton.enabled = false;
|
||||||
this.saveArgs.retentionDays = rpArg.retentionDays;
|
this._saveArgs.retentionDays = rpArg.retentionDays;
|
||||||
await vscode.window.withProgress(
|
await vscode.window.withProgress(
|
||||||
{
|
{
|
||||||
location: vscode.ProgressLocation.Notification,
|
location: vscode.ProgressLocation.Notification,
|
||||||
@@ -213,7 +221,7 @@ export class MiaaBackupsPage extends DashboardPage {
|
|||||||
},
|
},
|
||||||
async (_progress, _token): Promise<void> => {
|
async (_progress, _token): Promise<void> => {
|
||||||
await this._azApi.az.sql.miarc.edit(
|
await this._azApi.az.sql.miarc.edit(
|
||||||
this._miaaModel.info.name, this.saveArgs, this._miaaModel.controllerModel.info.namespace, this._miaaModel.controllerModel.azAdditionalEnvVars);
|
this._miaaModel.info.name, this._saveArgs, this._miaaModel.controllerModel.info.namespace, this._miaaModel.controllerModel.azAdditionalEnvVars);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
await this._miaaModel.refresh();
|
await this._miaaModel.refresh();
|
||||||
@@ -244,13 +252,16 @@ export class MiaaBackupsPage extends DashboardPage {
|
|||||||
// If we were able to get the databases it means we have a good connection so update the username too
|
// If we were able to get the databases it means we have a good connection so update the username too
|
||||||
let databaseDisplay = this._miaaModel.databases.map(d => [
|
let databaseDisplay = this._miaaModel.databases.map(d => [
|
||||||
d.name,
|
d.name,
|
||||||
|
d.earliestBackup,
|
||||||
d.lastBackup,
|
d.lastBackup,
|
||||||
this.createRestoreButton(d)]);
|
this.createRestoreButton(d)]);
|
||||||
|
|
||||||
let databasesValues = databaseDisplay.map(d => {
|
let databasesValues = databaseDisplay.map(d => {
|
||||||
return d.map((value): azdata.DeclarativeTableCellValue => {
|
return d.map((value): azdata.DeclarativeTableCellValue => {
|
||||||
return { value: value };
|
return { value: value };
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
this._databasesTable.setDataValues(databasesValues);
|
this._databasesTable.setDataValues(databasesValues);
|
||||||
|
|
||||||
this._databasesTableLoading.loading = false;
|
this._databasesTableLoading.loading = false;
|
||||||
@@ -271,7 +282,7 @@ export class MiaaBackupsPage extends DashboardPage {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private refreshRD(): void {
|
private refreshRD(): void {
|
||||||
this.saveArgs.retentionDays = this._miaaModel.config?.spec?.backup?.retentionPeriodInDays.toString() ?? '';
|
this._saveArgs.retentionDays = this._miaaModel.config?.spec?.backup?.retentionPeriodInDays.toString() ?? '';
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create restore button for every database entry in the database table
|
// Create restore button for every database entry in the database table
|
||||||
@@ -292,9 +303,9 @@ export class MiaaBackupsPage extends DashboardPage {
|
|||||||
if (args) {
|
if (args) {
|
||||||
try {
|
try {
|
||||||
restoreButton.enabled = false;
|
restoreButton.enabled = false;
|
||||||
this.pitrArgs.destName = args.dbName;
|
this._pitrArgs.destName = args.destDbName;
|
||||||
this.pitrArgs.managedInstance = args.instanceName;
|
this._pitrArgs.managedInstance = args.instanceName;
|
||||||
this.pitrArgs.time = `"${args.restorePoint}"`;
|
this._pitrArgs.time = `"${args.restorePoint}"`;
|
||||||
await vscode.window.withProgress(
|
await vscode.window.withProgress(
|
||||||
{
|
{
|
||||||
location: vscode.ProgressLocation.Notification,
|
location: vscode.ProgressLocation.Notification,
|
||||||
@@ -303,7 +314,7 @@ export class MiaaBackupsPage extends DashboardPage {
|
|||||||
},
|
},
|
||||||
async (_progress, _token): Promise<void> => {
|
async (_progress, _token): Promise<void> => {
|
||||||
await this._azApi.az.sql.midbarc.restore(
|
await this._azApi.az.sql.midbarc.restore(
|
||||||
db.name, this.pitrArgs, this._miaaModel.controllerModel.info.namespace, this._miaaModel.controllerModel.azAdditionalEnvVars);
|
db.name, this._pitrArgs, this._miaaModel.controllerModel.info.namespace, this._miaaModel.controllerModel.azAdditionalEnvVars);
|
||||||
try {
|
try {
|
||||||
await this._miaaModel.refresh();
|
await this._miaaModel.refresh();
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
@@ -320,4 +331,6 @@ export class MiaaBackupsPage extends DashboardPage {
|
|||||||
}));
|
}));
|
||||||
return restoreButton;
|
return restoreButton;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -32,7 +32,7 @@ export class ConfigureRPOSqlDialog extends InitializingComponent {
|
|||||||
this.retentionDaysInputBox = this.modelBuilder.inputBox()
|
this.retentionDaysInputBox = this.modelBuilder.inputBox()
|
||||||
.withProps({
|
.withProps({
|
||||||
readOnly: false,
|
readOnly: false,
|
||||||
min: 1,
|
min: 0,
|
||||||
max: 35,
|
max: 35,
|
||||||
inputType: 'number',
|
inputType: 'number',
|
||||||
ariaLabel: loc.retentionDays,
|
ariaLabel: loc.retentionDays,
|
||||||
|
|||||||
@@ -5,6 +5,7 @@
|
|||||||
|
|
||||||
import * as azdata from 'azdata';
|
import * as azdata from 'azdata';
|
||||||
import { Deferred } from '../../common/promise';
|
import { Deferred } from '../../common/promise';
|
||||||
|
import { getTimeStamp, checkISOTimeString } from '../../common/utils';
|
||||||
import * as loc from '../../localizedConstants';
|
import * as loc from '../../localizedConstants';
|
||||||
import * as vscode from 'vscode';
|
import * as vscode from 'vscode';
|
||||||
import { cssStyles } from '../../constants';
|
import { cssStyles } from '../../constants';
|
||||||
@@ -24,6 +25,7 @@ export class RestoreSqlDialog extends InitializingComponent {
|
|||||||
restorePoint: '-',
|
restorePoint: '-',
|
||||||
earliestPitr: '-',
|
earliestPitr: '-',
|
||||||
latestPitr: '-',
|
latestPitr: '-',
|
||||||
|
destDbName: '-',
|
||||||
};
|
};
|
||||||
|
|
||||||
private earliestRestorePointInputBox!: azdata.InputBoxComponent;
|
private earliestRestorePointInputBox!: azdata.InputBoxComponent;
|
||||||
@@ -36,10 +38,11 @@ export class RestoreSqlDialog extends InitializingComponent {
|
|||||||
private instanceInputBox!: azdata.InputBoxComponent;
|
private instanceInputBox!: azdata.InputBoxComponent;
|
||||||
protected _completionPromise = new Deferred<PITRModel | undefined>();
|
protected _completionPromise = new Deferred<PITRModel | undefined>();
|
||||||
private _azurecoreApi: azurecore.IExtension;
|
private _azurecoreApi: azurecore.IExtension;
|
||||||
|
protected disposables: vscode.Disposable[] = [];
|
||||||
constructor(protected _miaaModel: MiaaModel, protected _controllerModel: ControllerModel, protected _database: DatabaseModel) {
|
constructor(protected _miaaModel: MiaaModel, protected _controllerModel: ControllerModel, protected _database: DatabaseModel) {
|
||||||
super();
|
super();
|
||||||
this._azurecoreApi = vscode.extensions.getExtension(azurecore.extension.name)?.exports;
|
this._azurecoreApi = vscode.extensions.getExtension(azurecore.extension.name)?.exports;
|
||||||
|
this.refreshPitrSettings();
|
||||||
}
|
}
|
||||||
|
|
||||||
public showDialog(dialogTitle: string): azdata.window.Dialog {
|
public showDialog(dialogTitle: string): azdata.window.Dialog {
|
||||||
@@ -50,14 +53,15 @@ export class RestoreSqlDialog extends InitializingComponent {
|
|||||||
this.refreshPitrSettings();
|
this.refreshPitrSettings();
|
||||||
const pitrTitle = this.modelBuilder.text().withProps({
|
const pitrTitle = this.modelBuilder.text().withProps({
|
||||||
value: loc.pitr,
|
value: loc.pitr,
|
||||||
CSSStyles: { ...cssStyles.title }
|
CSSStyles: { ...cssStyles.title, 'margin-block-start': '0px', 'margin-block-end': '0px', 'max-width': 'auto' },
|
||||||
}).component();
|
}).component();
|
||||||
const projectDetailsTitle = this.modelBuilder.text().withProps({
|
const projectDetailsTitle = this.modelBuilder.text().withProps({
|
||||||
value: loc.projectDetails,
|
value: loc.projectDetails,
|
||||||
CSSStyles: { ...cssStyles.title }
|
CSSStyles: { ...cssStyles.title, 'margin-block-start': '0px', 'margin-block-end': '0px', 'max-width': 'auto' }
|
||||||
}).component();
|
}).component();
|
||||||
const projectDetailsTextLabel = this.modelBuilder.text().withProps({
|
const projectDetailsTextLabel = this.modelBuilder.text().withProps({
|
||||||
value: loc.projectDetailsText,
|
value: loc.projectDetailsText,
|
||||||
|
CSSStyles: { ...cssStyles.text, 'margin-block-start': '0px', 'margin-block-end': '0px', 'max-width': 'auto' }
|
||||||
}).component();
|
}).component();
|
||||||
this.subscriptionInputBox = this.modelBuilder.inputBox()
|
this.subscriptionInputBox = this.modelBuilder.inputBox()
|
||||||
.withProps({
|
.withProps({
|
||||||
@@ -74,10 +78,11 @@ export class RestoreSqlDialog extends InitializingComponent {
|
|||||||
|
|
||||||
const sourceDetailsTitle = this.modelBuilder.text().withProps({
|
const sourceDetailsTitle = this.modelBuilder.text().withProps({
|
||||||
value: loc.sourceDetails,
|
value: loc.sourceDetails,
|
||||||
CSSStyles: { ...cssStyles.title }
|
CSSStyles: { ...cssStyles.title, 'margin-block-end': '0px', 'max-width': 'auto' }
|
||||||
}).component();
|
}).component();
|
||||||
const sourceDetailsTextLabel = this.modelBuilder.text().withProps({
|
const sourceDetailsTextLabel = this.modelBuilder.text().withProps({
|
||||||
value: loc.sourceDetailsText,
|
value: loc.sourceDetailsText,
|
||||||
|
CSSStyles: { ...cssStyles.text, 'margin-block-start': '0px', 'margin-block-end': '0px', 'max-width': 'auto' }
|
||||||
}).component();
|
}).component();
|
||||||
this.sourceDbInputBox = this.modelBuilder.inputBox()
|
this.sourceDbInputBox = this.modelBuilder.inputBox()
|
||||||
.withProps({
|
.withProps({
|
||||||
@@ -85,12 +90,15 @@ export class RestoreSqlDialog extends InitializingComponent {
|
|||||||
ariaLabel: loc.sourceDatabase,
|
ariaLabel: loc.sourceDatabase,
|
||||||
value: this._database.name
|
value: this._database.name
|
||||||
}).component();
|
}).component();
|
||||||
|
const restoreDetailsTextLabel = this.modelBuilder.text().withProps({
|
||||||
|
value: loc.restorePointText,
|
||||||
|
CSSStyles: { ...cssStyles.text, 'margin-block-start': '0px', 'margin-block-end': '0px', 'max-width': 'auto' }
|
||||||
|
}).component();
|
||||||
this.earliestRestorePointInputBox = this.modelBuilder.inputBox()
|
this.earliestRestorePointInputBox = this.modelBuilder.inputBox()
|
||||||
.withProps({
|
.withProps({
|
||||||
enabled: false,
|
enabled: false,
|
||||||
ariaLabel: loc.earliestPitrRestorePoint,
|
ariaLabel: loc.earliestPitrRestorePoint,
|
||||||
value: ''
|
value: this._database.earliestBackup
|
||||||
}).component();
|
}).component();
|
||||||
|
|
||||||
this.latestRestorePointInputBox = this.modelBuilder.inputBox()
|
this.latestRestorePointInputBox = this.modelBuilder.inputBox()
|
||||||
@@ -104,14 +112,39 @@ export class RestoreSqlDialog extends InitializingComponent {
|
|||||||
.withProps({
|
.withProps({
|
||||||
readOnly: false,
|
readOnly: false,
|
||||||
ariaLabel: loc.restorePoint,
|
ariaLabel: loc.restorePoint,
|
||||||
value: ''
|
value: '',
|
||||||
|
validationErrorMessage: loc.restorePointErrorMessage(this.earliestRestorePointInputBox.value ?? loc.earliestPitrRestorePoint, this.latestRestorePointInputBox.value ?? loc.latestpitrRestorePoint),
|
||||||
|
}).withValidation(async () => {
|
||||||
|
try {
|
||||||
|
if (!checkISOTimeString(this.restorePointInputBox.value ?? '')) { return false; }
|
||||||
|
if (this.earliestRestorePointInputBox.value) {
|
||||||
|
if ((getTimeStamp(this.restorePointInputBox.value) >= getTimeStamp(this.earliestRestorePointInputBox.value)
|
||||||
|
&& getTimeStamp(this.restorePointInputBox.value) <= getTimeStamp(this.latestRestorePointInputBox.value))) {
|
||||||
|
this.pitrSettings.restorePoint = this.restorePointInputBox.value ?? '';
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (err) {
|
||||||
|
throw err;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
}).component();
|
}).component();
|
||||||
const databaseDetailsTitle = this.modelBuilder.text().withProps({
|
const pitrDetailsTitle = this.modelBuilder.text().withProps({
|
||||||
|
value: loc.restorePointDetails,
|
||||||
|
CSSStyles: { ...cssStyles.title, 'margin-block-end': '0px', 'max-width': 'auto' }
|
||||||
|
}).component();
|
||||||
|
const destinationDetailsTitle = this.modelBuilder.text().withProps({
|
||||||
value: loc.databaseDetails,
|
value: loc.databaseDetails,
|
||||||
CSSStyles: { ...cssStyles.title }
|
CSSStyles: { ...cssStyles.title, 'margin-block-end': '0px', 'max-width': 'auto' }
|
||||||
}).component();
|
}).component();
|
||||||
const databaseDetailsTextLabel = this.modelBuilder.text().withProps({
|
const databaseDetailsTextLabel = this.modelBuilder.text().withProps({
|
||||||
value: loc.databaseDetailsText,
|
value: loc.databaseDetailsText,
|
||||||
|
CSSStyles: { ...cssStyles.text, 'margin-block-start': '0px', 'margin-block-end': '0px', 'max-width': 'auto' }
|
||||||
}).component();
|
}).component();
|
||||||
this.databaseNameInputBox = this.modelBuilder.inputBox()
|
this.databaseNameInputBox = this.modelBuilder.inputBox()
|
||||||
.withProps({
|
.withProps({
|
||||||
@@ -119,7 +152,10 @@ export class RestoreSqlDialog extends InitializingComponent {
|
|||||||
ariaLabel: loc.databaseName,
|
ariaLabel: loc.databaseName,
|
||||||
value: ''
|
value: ''
|
||||||
}).component();
|
}).component();
|
||||||
|
this.disposables.push(
|
||||||
|
this.databaseNameInputBox.onTextChanged(() => {
|
||||||
|
this.pitrSettings.destDbName = this.databaseNameInputBox.value ?? '';
|
||||||
|
}));
|
||||||
this.instanceInputBox = this.modelBuilder.inputBox()
|
this.instanceInputBox = this.modelBuilder.inputBox()
|
||||||
.withProps({
|
.withProps({
|
||||||
enabled: false,
|
enabled: false,
|
||||||
@@ -128,7 +164,7 @@ export class RestoreSqlDialog extends InitializingComponent {
|
|||||||
}).component();
|
}).component();
|
||||||
const info = this.modelBuilder.text().withProps({
|
const info = this.modelBuilder.text().withProps({
|
||||||
value: loc.restoreInfo,
|
value: loc.restoreInfo,
|
||||||
CSSStyles: { ...cssStyles.text, 'margin-block-start': '0px', 'margin-block-end': '0px' }
|
CSSStyles: { ...cssStyles.text, 'margin-block-start': '0px', 'max-width': 'auto' }
|
||||||
}).component();
|
}).component();
|
||||||
|
|
||||||
const link = this.modelBuilder.hyperlink().withProps({
|
const link = this.modelBuilder.hyperlink().withProps({
|
||||||
@@ -178,22 +214,7 @@ export class RestoreSqlDialog extends InitializingComponent {
|
|||||||
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
component: this.earliestRestorePointInputBox,
|
component: destinationDetailsTitle,
|
||||||
title: loc.earliestPitrRestorePoint,
|
|
||||||
|
|
||||||
},
|
|
||||||
{
|
|
||||||
component: this.latestRestorePointInputBox,
|
|
||||||
title: loc.latestpitrRestorePoint,
|
|
||||||
|
|
||||||
},
|
|
||||||
{
|
|
||||||
component: this.restorePointInputBox,
|
|
||||||
title: loc.restorePoint,
|
|
||||||
required: true
|
|
||||||
},
|
|
||||||
{
|
|
||||||
component: databaseDetailsTitle,
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
component: databaseDetailsTextLabel,
|
component: databaseDetailsTextLabel,
|
||||||
@@ -208,6 +229,27 @@ export class RestoreSqlDialog extends InitializingComponent {
|
|||||||
title: loc.instance,
|
title: loc.instance,
|
||||||
|
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
component: pitrDetailsTitle
|
||||||
|
},
|
||||||
|
{
|
||||||
|
component: restoreDetailsTextLabel,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
component: this.earliestRestorePointInputBox,
|
||||||
|
title: loc.earliestPitrRestorePoint,
|
||||||
|
|
||||||
|
},
|
||||||
|
{
|
||||||
|
component: this.latestRestorePointInputBox,
|
||||||
|
title: loc.latestpitrRestorePoint,
|
||||||
|
|
||||||
|
},
|
||||||
|
{
|
||||||
|
component: this.restorePointInputBox,
|
||||||
|
title: loc.restorePoint,
|
||||||
|
required: true
|
||||||
|
},
|
||||||
],
|
],
|
||||||
title: ''
|
title: ''
|
||||||
}]).withLayout({ width: '100%' }).component();
|
}]).withLayout({ width: '100%' }).component();
|
||||||
@@ -269,4 +311,9 @@ export class RestoreSqlDialog extends InitializingComponent {
|
|||||||
this.pitrSettings.restorePoint = this._database.lastBackup;
|
this.pitrSettings.restorePoint = this._database.lastBackup;
|
||||||
this.pitrSettings.earliestPitr = '';
|
this.pitrSettings.earliestPitr = '';
|
||||||
}
|
}
|
||||||
|
public updatePitrTimeWindow(earliestPitr: string, latestPitr: string): void {
|
||||||
|
this.earliestRestorePointInputBox.value = earliestPitr;
|
||||||
|
this.latestRestorePointInputBox.value = latestPitr;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -140,6 +140,7 @@ export function getAzApi(localAzDiscovered: Promise<IAzTool | undefined>, azTool
|
|||||||
managedInstance?: string,
|
managedInstance?: string,
|
||||||
time?: string,
|
time?: string,
|
||||||
noWait?: boolean,
|
noWait?: boolean,
|
||||||
|
dryRun?: boolean
|
||||||
},
|
},
|
||||||
namespace: string, additionalEnvVars?: azExt.AdditionalEnvVars) => {
|
namespace: string, additionalEnvVars?: azExt.AdditionalEnvVars) => {
|
||||||
await localAzDiscovered;
|
await localAzDiscovered;
|
||||||
|
|||||||
@@ -185,16 +185,18 @@ export class AzTool implements azExt.IAzApi {
|
|||||||
managedInstance?: string,
|
managedInstance?: string,
|
||||||
time?: string,
|
time?: string,
|
||||||
noWait?: boolean,
|
noWait?: boolean,
|
||||||
|
dryRun?: boolean
|
||||||
},
|
},
|
||||||
namespace: string,
|
namespace: string,
|
||||||
additionalEnvVars?: azExt.AdditionalEnvVars
|
additionalEnvVars?: azExt.AdditionalEnvVars
|
||||||
): Promise<azExt.AzOutput<void>> => {
|
): Promise<azExt.AzOutput<azExt.SqlMiDbRestoreResult>> => {
|
||||||
const argsArray = ['sql', 'midb-arc', 'restore', '--name', name, '--k8s-namespace', namespace, '--use-k8s'];
|
const argsArray = ['sql', 'midb-arc', 'restore', '--name', name, '--k8s-namespace', namespace, '--use-k8s'];
|
||||||
if (args.destName) { argsArray.push('--dest-name', args.destName); }
|
if (args.destName) { argsArray.push('--dest-name', args.destName); }
|
||||||
if (args.managedInstance) { argsArray.push('--managed-instance', args.managedInstance); }
|
if (args.managedInstance) { argsArray.push('--managed-instance', args.managedInstance); }
|
||||||
if (args.time) { argsArray.push('--time', args.time); }
|
if (args.time) { argsArray.push('--time', args.time); }
|
||||||
if (args.noWait) { argsArray.push('--no-wait'); }
|
if (args.noWait) { argsArray.push('--no-wait'); }
|
||||||
return this.executeCommand<void>(argsArray, additionalEnvVars);
|
if (args.dryRun) { argsArray.push('--dry-run'); }
|
||||||
|
return this.executeCommand<azExt.SqlMiDbRestoreResult>(argsArray, additionalEnvVars);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|||||||
14
extensions/azcli/src/typings/az-ext.d.ts
vendored
14
extensions/azcli/src/typings/az-ext.d.ts
vendored
@@ -173,6 +173,17 @@ declare module 'az-ext' {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface SqlMiDbRestoreResult {
|
||||||
|
destDatabase: string, //testDbToRestore
|
||||||
|
earliestRestoreTime: string, // "2020-08-19T20:25:11Z"
|
||||||
|
latestRestoreTime: string, //"2020-08-19T20:25:11Z"
|
||||||
|
message: string, //Dry run for restore operation succeeded.
|
||||||
|
observedGeneration: number, //1
|
||||||
|
restorePoint: string, // "2020-08-19T20:25:11Z"
|
||||||
|
sourceDatabase: string, //testDb
|
||||||
|
state: string //Completed
|
||||||
|
}
|
||||||
|
|
||||||
export interface PostgresServerShowResult {
|
export interface PostgresServerShowResult {
|
||||||
apiVersion: string, // "arcdata.microsoft.com/v1alpha1"
|
apiVersion: string, // "arcdata.microsoft.com/v1alpha1"
|
||||||
kind: string, // "postgresql"
|
kind: string, // "postgresql"
|
||||||
@@ -336,10 +347,11 @@ declare module 'az-ext' {
|
|||||||
managedInstance?: string, //sqlmi1
|
managedInstance?: string, //sqlmi1
|
||||||
time?: string, //2021-10-12T11:16:30.000Z
|
time?: string, //2021-10-12T11:16:30.000Z
|
||||||
noWait?: boolean, //true
|
noWait?: boolean, //true
|
||||||
|
dryRun?: boolean, //true
|
||||||
},
|
},
|
||||||
namespace?: string,
|
namespace?: string,
|
||||||
additionalEnvVars?: AdditionalEnvVars
|
additionalEnvVars?: AdditionalEnvVars
|
||||||
): Promise<AzOutput<void>>
|
): Promise<AzOutput<SqlMiDbRestoreResult>>
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
getPath(): Promise<string>,
|
getPath(): Promise<string>,
|
||||||
|
|||||||
Reference in New Issue
Block a user