Add advanced options to backup service (#405)

* Add advanced options for Backup

* Add backup encryption strings

* Add test for backup advanced option

* Add strings to SR

* Add verify backup restore option

* Addressed PR comments

* Add MIT license header
This commit is contained in:
Kate Shin
2017-07-12 13:09:07 -07:00
committed by GitHub
parent 64e671ca2a
commit 414949d129
11 changed files with 450 additions and 125 deletions

View File

@@ -13,6 +13,7 @@ using Microsoft.SqlServer.Management.Sdk.Sfc;
using Microsoft.SqlServer.Management.Smo;
using Microsoft.SqlTools.ServiceLayer.Admin;
using Microsoft.SqlTools.ServiceLayer.DisasterRecovery.Contracts;
using System.Globalization;
namespace Microsoft.SqlTools.ServiceLayer.DisasterRecovery
{
@@ -137,13 +138,14 @@ namespace Microsoft.SqlTools.ServiceLayer.DisasterRecovery
/// </summary>
/// <param name="databaseName"></param>
/// <returns></returns>
public BackupConfigInfo GetBackupConfigInfo(string databaseName)
public BackupConfigInfo CreateBackupConfigInfo(string databaseName)
{
BackupConfigInfo databaseInfo = new BackupConfigInfo();
databaseInfo.RecoveryModel = this.GetRecoveryModel(databaseName);
databaseInfo.DefaultBackupFolder = this.GetDefaultBackupFolder();
databaseInfo.LatestBackups = this.GetLatestBackupLocations(databaseName);
return databaseInfo;
BackupConfigInfo configInfo = new BackupConfigInfo();
configInfo.RecoveryModel = GetRecoveryModel(databaseName);
configInfo.DefaultBackupFolder = GetDefaultBackupFolder();
configInfo.LatestBackups = GetLatestBackupLocations(databaseName);
configInfo.BackupEncryptors = GetBackupEncryptors();
return configInfo;
}
/// <summary>
@@ -226,17 +228,78 @@ namespace Microsoft.SqlTools.ServiceLayer.DisasterRecovery
}
this.backup.CopyOnly = this.backupInfo.IsCopyOnly;
this.backup.FormatMedia = this.backupInfo.FormatMedia;
this.backup.Initialize = this.backupInfo.Initialize;
this.backup.SkipTapeHeader = this.backupInfo.SkipTapeHeader;
this.backup.Checksum = this.backupInfo.Checksum;
this.backup.ContinueAfterError = this.backupInfo.ContinueAfterError;
//TODO: This should be changed to get user inputs
this.backup.FormatMedia = false;
this.backup.Initialize = false;
this.backup.SkipTapeHeader = true;
this.backup.Checksum = false;
this.backup.ContinueAfterError = false;
this.backup.LogTruncation = BackupTruncateLogType.Truncate;
if (!string.IsNullOrEmpty(this.backupInfo.MediaName))
{
this.backup.MediaName = this.backupInfo.MediaName;
}
if (!string.IsNullOrEmpty(this.backupInfo.MediaDescription))
{
this.backup.MediaDescription = this.backupInfo.MediaDescription;
}
if (this.backupInfo.TailLogBackup
&& !this.backupRestoreUtil.IsHADRDatabase(this.backupInfo.DatabaseName)
&& !this.backupRestoreUtil.IsMirroringEnabled(this.backupInfo.DatabaseName))
{
this.backup.NoRecovery = true;
}
if (this.backupInfo.LogTruncation)
{
this.backup.LogTruncation = BackupTruncateLogType.Truncate;
}
else
{
this.backup.LogTruncation = BackupTruncateLogType.NoTruncate;
}
if (!string.IsNullOrEmpty(this.backupInfo.BackupSetDescription))
{
this.backup.BackupSetDescription = this.backupInfo.BackupSetDescription;
}
if (this.backupInfo.RetainDays >= 0)
{
this.backup.RetainDays = this.backupInfo.RetainDays;
}
else
{
this.backup.ExpirationDate = this.backupInfo.ExpirationDate;
}
this.backup.CompressionOption = (BackupCompressionOptions)this.backupInfo.CompressionOption;
if (!string.IsNullOrEmpty(this.backupInfo.EncryptorName))
{
this.backup.EncryptionOption = new BackupEncryptionOptions((BackupEncryptionAlgorithm)this.backupInfo.EncryptionAlgorithm,
(BackupEncryptorType)this.backupInfo.EncryptorType,
this.backupInfo.EncryptorName);
}
// Execute backup
this.backup.SqlBackup(this.dataContainer.Server);
// Verify backup if required
if (this.backupInfo.VerifyBackupRequired)
{
Restore restore = new Restore();
restore.Devices.AddRange(this.backup.Devices);
restore.Database = this.backup.Database;
string errorMessage = null;
restore.SqlVerifyLatest(this.dataContainer.Server, out errorMessage);
if (errorMessage != null)
{
throw new Exception(errorMessage);
}
}
}
/// <summary>
@@ -277,11 +340,34 @@ namespace Microsoft.SqlTools.ServiceLayer.DisasterRecovery
}
/// <summary>
/// Return true if backup to URL is supported in the current SQL Server version
/// Returns the certificates and asymmetric keys from master for encryption
/// </summary>
private bool BackupToUrlSupported()
public List<BackupEncryptor> GetBackupEncryptors()
{
return BackupRestoreBase.IsBackupUrlDeviceSupported(this.dataContainer.Server.PingSqlServerVersion(this.dataContainer.ServerName));
List<BackupEncryptor> encryptors = new List<BackupEncryptor>();
if (this.dataContainer.Server.Databases.Contains("master"))
{
CertificateCollection certificates = this.dataContainer.Server.Databases["master"].Certificates;
DateTime currentUtcDateTime = DateTime.UtcNow;
foreach (Certificate item in certificates)
{
if ((item.Name.StartsWith("##", StringComparison.InvariantCulture) && item.Name.EndsWith("##", StringComparison.InvariantCulture)) ||
DateTime.Compare(item.ExpirationDate, currentUtcDateTime) < 0)
{
continue;
}
encryptors.Add(new BackupEncryptor((int)BackupEncryptorType.ServerCertificate, item.Name));
}
AsymmetricKeyCollection keys = this.dataContainer.Server.Databases["master"].AsymmetricKeys;
foreach (AsymmetricKey item in keys)
{
if (item.KeyEncryptionAlgorithm == AsymmetricKeyEncryptionAlgorithm.CryptographicProviderDefined)
{
encryptors.Add(new BackupEncryptor((int)BackupEncryptorType.ServerAsymmetricKey, item.Name));
}
}
}
return encryptors;
}
#endregion

View File

@@ -54,6 +54,33 @@ namespace Microsoft.SqlTools.ServiceLayer.DisasterRecovery
StandBy
}
/// <summary>
/// Class for backup encryptor
/// </summary>
public class BackupEncryptor
{
/// <summary>
/// Ctor
/// </summary>
/// <param name="encryptorType"></param>
/// <param name="encryptorName"></param>
public BackupEncryptor(int encryptorType, string encryptorName)
{
this.EncryptorType = encryptorType;
this.EncryptorName = encryptorName;
}
/// <summary>
/// Encryptor type - certificate or asymetric key
/// </summary>
public int EncryptorType { get; set; }
/// <summary>
/// Encryptor name
/// </summary>
public string EncryptorName { get; set; }
}
/// <summary>
/// Restore item source
/// </summary>

View File

@@ -13,11 +13,34 @@ namespace Microsoft.SqlTools.ServiceLayer.DisasterRecovery.Contracts
/// </summary>
public class BackupConfigInfo
{
/// <summary>
/// Gets or sets default database info
/// </summary>
public DatabaseInfo DatabaseInfo { get; set; }
/// <summary>
/// Gets or sets recovery model of a database
/// </summary>
public string RecoveryModel { get; set; }
/// <summary>
/// Gets or sets the latest backup set of a database
/// </summary>
public List<RestoreItemSource> LatestBackups { get; set; }
/// <summary>
/// Gets or sets the default backup folder
/// </summary>
public string DefaultBackupFolder { get; set; }
/// <summary>
/// Gets or sets backup encryptors
/// </summary>
public List<BackupEncryptor> BackupEncryptors { get; set; }
/// <summary>
/// Ctor
/// </summary>
public BackupConfigInfo()
{
}

View File

@@ -3,6 +3,7 @@
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
//
using System;
using System.Collections.Generic;
namespace Microsoft.SqlTools.ServiceLayer.DisasterRecovery.Contracts
@@ -13,7 +14,7 @@ namespace Microsoft.SqlTools.ServiceLayer.DisasterRecovery.Contracts
/// Name of the datbase to perfom backup
/// </summary>
public string DatabaseName { get; set; }
/// <summary>
/// Component to backup - Database or Files
/// </summary>
@@ -48,15 +49,104 @@ namespace Microsoft.SqlTools.ServiceLayer.DisasterRecovery.Contracts
/// List of {key: backup path, value: device type}
/// </summary>
public Dictionary<string, int> BackupPathDevices { get; set; }
/// <summary>
/// List of selected backup paths
/// </summary>
public List<string> BackupPathList { get; set; }
/// <summary>
/// Indicates if the backup should be copy-only
/// </summary>
public bool IsCopyOnly { get; set; }
/// <summary>
/// Gets or sets a Boolean property value that determines whether a media is formatted as the first step of the backup operation.
/// </summary>
public bool FormatMedia { get; set; }
/// <summary>
/// Gets or sets a Boolean property value that determines whether the devices associated with a backup operation are initialized as part of the backup operation.
/// </summary>
public bool Initialize { get; set; }
/// <summary>
/// Gets or sets Boolean property that determines whether the tape header is read.
/// </summary>
public bool SkipTapeHeader { get; set; }
/// <summary>
/// Gets or sets the name used to identify a particular media set.
/// </summary>
public string MediaName { get; set; }
/// <summary>
/// Gets or sets a textual description of the medium that contains a backup set.
/// </summary>
public string MediaDescription { get; set; }
/// <summary>
/// Gets or sets a Boolean property value that determines whether a checksum value is calculated during backup or restore operations.
/// </summary>
public bool Checksum { get; set; }
/// <summary>
/// Gets or sets a Boolean property value that determines whether the backup or restore continues after a checksum error occurs.
/// </summary>
public bool ContinueAfterError { get; set; }
/// <summary>
/// Gets or sets a Boolean property value that determines whether to truncate the database log.
/// </summary>
public bool LogTruncation { get; set; }
/// <summary>
/// Gets or sets a Boolean property value that determines whether to backup the tail of the log
/// </summary>
public bool TailLogBackup { get; set; }
/// <summary>
/// Gets or sets a textual description for a particular backup set.
/// </summary>
public string BackupSetDescription { get; set; }
/// <summary>
/// Gets or sets the number of days that must elapse before a backup set can be overwritten.
/// </summary>
public int RetainDays { get; set; }
/// <summary>
/// Gets or sets the date and time when the backup set expires and the backup data is no longer considered relevant.
/// </summary>
public DateTime ExpirationDate { get; set; }
/// <summary>
/// Gets or sets the backup compression option.
/// This should be converted to BackupCompressionOptions when setting it to Backup object.
/// </summary>
public int CompressionOption { get; set; }
/// <summary>
/// Gets or sets a Boolean property that determines whether verify is required.
/// </summary>
public bool VerifyBackupRequired { get; set; }
/// <summary>
/// Specifies the algorithm type used for backup encryption.
/// This should be converted to BackupEncryptionAlgorithm when creating BackupEncryptionOptions object.
/// </summary>
public int EncryptionAlgorithm { get; set; }
/// <summary>
/// Specifies the encryptor type used to encrypt an encryption key.
/// This should be converted to BackupEncryptorType when creating BackupEncryptionOptions object.
/// </summary>
public int EncryptorType { get; set; }
/// <summary>
/// Gets or sets the name of the encryptor.
/// </summary>
public string EncryptorName { get; set; }
}
}

View File

@@ -281,7 +281,7 @@ namespace Microsoft.SqlTools.ServiceLayer.DisasterRecovery
{
BackupOperation backupOperation = new BackupOperation();
backupOperation.Initialize(dataContainer, sqlConnection);
return backupOperation.GetBackupConfigInfo(databaseName);
return backupOperation.CreateBackupConfigInfo(databaseName);
}
internal BackupOperation SetBackupInput(CDataContainer dataContainer, SqlConnection sqlConnection, BackupInfo input)

View File

@@ -26,7 +26,7 @@ namespace Microsoft.SqlTools.ServiceLayer.DisasterRecovery
/// </summary>
/// <param name="databaseName"></param>
/// <returns></returns>
BackupConfigInfo GetBackupConfigInfo(string databaseName);
BackupConfigInfo CreateBackupConfigInfo(string databaseName);
/// <summary>
/// Set backup input properties

View File

@@ -812,7 +812,6 @@ Backup_TaskName = Backup Database
Task_InProgress = In progress
Task_Completed = Completed
###########################################################################
# Restore
ConflictWithNoRecovery = Specifying this option when restoring a backup with the NORECOVERY option is not permitted.

View File

@@ -0,0 +1,200 @@
//
// Copyright (c) Microsoft. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
//
using System;
using System.Collections.Generic;
using System.Data.SqlClient;
using System.IO;
using System.Threading.Tasks;
using Microsoft.SqlServer.Management.Smo;
using Microsoft.SqlTools.Hosting.Protocol;
using Microsoft.SqlTools.ServiceLayer.Admin;
using Microsoft.SqlTools.ServiceLayer.Admin.Contracts;
using Microsoft.SqlTools.ServiceLayer.DisasterRecovery;
using Microsoft.SqlTools.ServiceLayer.DisasterRecovery.Contracts;
using Microsoft.SqlTools.ServiceLayer.IntegrationTests.Utility;
using Microsoft.SqlTools.ServiceLayer.Test.Common;
using Moq;
using Xunit;
namespace Microsoft.SqlTools.ServiceLayer.IntegrationTests.DisasterRecovery
{
public class BackupServiceTests
{
// Query format to create master key and certificate for backup encryption
private const string CreateCertificateQueryFormat = @"USE master;
IF NOT EXISTS(SELECT * FROM sys.symmetric_keys WHERE symmetric_key_id = 101)
CREATE MASTER KEY ENCRYPTION BY PASSWORD = '{0}';
IF NOT EXISTS(SELECT * FROM sys.certificates WHERE name = '{1}')
CREATE CERTIFICATE {1} WITH SUBJECT = 'Backup Encryption Certificate'; ";
// Query format to clean up master key and certificate
private const string CleanupCertificateQueryFormat = @"USE master; DROP CERTIFICATE {0}; DROP MASTER KEY";
/// <summary>
/// Get backup configuration info
/// </summary>
/// Test is failing in code coverage runs. Reenable when stable.
///[Fact]
public async void GetBackupConfigInfoTest()
{
string databaseName = "testbackup_" + new Random().Next(10000000, 99999999);
SqlTestDb testDb = SqlTestDb.CreateNew(TestServerType.OnPrem, false, databaseName);
var liveConnection = LiveConnectionHelper.InitLiveConnectionInfo(databaseName);
var requestContext = new Mock<RequestContext<BackupConfigInfoResponse>>();
requestContext.Setup(x => x.SendResult(It.IsAny<BackupConfigInfoResponse>()))
.Returns(Task.FromResult(new object()));
var dbParams = new DefaultDatabaseInfoParams
{
OwnerUri = liveConnection.ConnectionInfo.OwnerUri
};
await DisasterRecoveryService.HandleBackupConfigInfoRequest(dbParams, requestContext.Object);
requestContext.Verify(x => x.SendResult(It.Is<BackupConfigInfoResponse>
(p => p.BackupConfigInfo.RecoveryModel != string.Empty
&& p.BackupConfigInfo.DefaultBackupFolder != string.Empty
&& p.BackupConfigInfo.DatabaseInfo != null)));
testDb.Cleanup();
}
/// Test is failing in code coverage runs. Reenable when stable.
///[Fact]
public void CreateBackupTest()
{
string databaseName = "testbackup_" + new Random().Next(10000000, 99999999);
SqlTestDb testDb = SqlTestDb.CreateNew(TestServerType.OnPrem, false, databaseName);
var liveConnection = LiveConnectionHelper.InitLiveConnectionInfo(databaseName);
// Initialize backup service
DatabaseTaskHelper helper = AdminService.CreateDatabaseTaskHelper(liveConnection.ConnectionInfo, databaseExists: true);
SqlConnection sqlConn = DisasterRecoveryService.GetSqlConnection(liveConnection.ConnectionInfo);
// Get default backup path
BackupConfigInfo backupConfigInfo = DisasterRecoveryService.Instance.GetBackupConfigInfo(helper.DataContainer, sqlConn, sqlConn.Database);
string backupPath = Path.Combine(backupConfigInfo.DefaultBackupFolder, databaseName + ".bak");
BackupInfo backupInfo = CreateBackupInfo(databaseName,
BackupType.Full,
new List<string>(){ backupPath },
new Dictionary<string, int>(){{ backupPath, (int)DeviceType.File }});
var backupParams = new BackupParams
{
OwnerUri = liveConnection.ConnectionInfo.OwnerUri,
BackupInfo = backupInfo
};
// Backup the database
BackupOperation backupOperation = DisasterRecoveryService.Instance.SetBackupInput(helper.DataContainer, sqlConn, backupParams.BackupInfo);
DisasterRecoveryService.Instance.PerformBackup(backupOperation);
// Remove the backup file
if (File.Exists(backupPath))
{
File.Delete(backupPath);
}
// Clean up the database
testDb.Cleanup();
}
/// <summary>
/// Test creating backup with advanced options set.
/// </summary>
[Fact]
public void CreateBackupWithAdvancedOptionsTest()
{
string databaseName = "testbackup_" + new Random().Next(10000000, 99999999);
SqlTestDb testDb = SqlTestDb.CreateNew(TestServerType.OnPrem, false, databaseName);
string certificateName = "backupcertificate" + new Random().Next(10000000, 99999999);
string masterkeyPassword = Guid.NewGuid().ToString();
string createCertificateQuery = string.Format(CreateCertificateQueryFormat, masterkeyPassword, certificateName);
string cleanupCertificateQuery = string.Format(CleanupCertificateQueryFormat, certificateName);
// create master key and certificate
Console.WriteLine("Create master key and certificate..");
testDb.RunQuery(createCertificateQuery);
var liveConnection = LiveConnectionHelper.InitLiveConnectionInfo(databaseName);
// Initialize backup service
DatabaseTaskHelper helper = AdminService.CreateDatabaseTaskHelper(liveConnection.ConnectionInfo, databaseExists: true);
SqlConnection sqlConn = DisasterRecoveryService.GetSqlConnection(liveConnection.ConnectionInfo);
// Get default backup path
Console.WriteLine("Get default backup path..");
BackupConfigInfo backupConfigInfo = DisasterRecoveryService.Instance.GetBackupConfigInfo(helper.DataContainer, sqlConn, sqlConn.Database);
string backupPath = Path.Combine(backupConfigInfo.DefaultBackupFolder, databaseName + ".bak");
BackupInfo backupInfo = CreateBackupInfo(databaseName,
BackupType.Full,
new List<string>(){ backupPath },
new Dictionary<string, int>(){{ backupPath, (int)DeviceType.File }});
// Set advanced options
backupInfo.ContinueAfterError = true;
backupInfo.FormatMedia = true;
backupInfo.SkipTapeHeader = true;
backupInfo.Initialize = true;
backupInfo.MediaName = "backup test media";
backupInfo.MediaDescription = "backup test";
backupInfo.RetainDays = 90;
backupInfo.CompressionOption = (int)BackupCompressionOptions.On;
// Set encryption
backupInfo.EncryptionAlgorithm = (int)BackupEncryptionAlgorithm.Aes128;
backupInfo.EncryptorType = (int)BackupEncryptorType.ServerCertificate;
backupInfo.EncryptorName = certificateName;
var backupParams = new BackupParams
{
OwnerUri = liveConnection.ConnectionInfo.OwnerUri,
BackupInfo = backupInfo
};
// Backup the database
Console.WriteLine("Perform backup operation..");
BackupOperation backupOperation = DisasterRecoveryService.Instance.SetBackupInput(helper.DataContainer, sqlConn, backupParams.BackupInfo);
DisasterRecoveryService.Instance.PerformBackup(backupOperation);
// Verify backup file is created
Assert.True(File.Exists(backupPath));
// Remove the backup file
Console.WriteLine("Remove backup file..");
if (File.Exists(backupPath))
{
File.Delete(backupPath);
}
// Delete certificate and master key
Console.WriteLine("Remove certificate and master key..");
testDb.RunQuery(cleanupCertificateQuery);
// Clean up the database
Console.WriteLine("Clean up database..");
testDb.Cleanup();
}
private BackupInfo CreateBackupInfo(string databaseName, BackupType backupType, List<string> backupPathList, Dictionary<string, int> backupPathDevices)
{
BackupInfo backupInfo = new BackupInfo();
backupInfo.BackupComponent = (int)BackupComponent.Database;
backupInfo.BackupDeviceType = (int)BackupDeviceType.Disk;
backupInfo.BackupPathDevices = backupPathDevices;
backupInfo.BackupPathList = backupPathList;
backupInfo.BackupsetName = "default_backup";
backupInfo.BackupType = (int)backupType;
backupInfo.DatabaseName = databaseName;
backupInfo.SelectedFileGroup = null;
backupInfo.SelectedFiles = "";
return backupInfo;
}
}
}

View File

@@ -1,98 +0,0 @@
using System;
using System.Collections.Generic;
using System.Data.SqlClient;
using System.IO;
using System.Threading.Tasks;
using Microsoft.SqlServer.Management.Smo;
using Microsoft.SqlTools.Hosting.Protocol;
using Microsoft.SqlTools.ServiceLayer.Admin;
using Microsoft.SqlTools.ServiceLayer.Admin.Contracts;
using Microsoft.SqlTools.ServiceLayer.DisasterRecovery;
using Microsoft.SqlTools.ServiceLayer.DisasterRecovery.Contracts;
using Microsoft.SqlTools.ServiceLayer.IntegrationTests.Utility;
using Microsoft.SqlTools.ServiceLayer.Test.Common;
using Moq;
using Xunit;
namespace Microsoft.SqlTools.ServiceLayer.IntegrationTests.DisasterRecovery
{
public class BackupTests
{
/// <summary>
/// Get backup configuration info
/// </summary>
/// Test is failing in code coverage runs. Reenable when stable.
/// [Fact]
public async void GetBackupConfigInfoTest()
{
string databaseName = "testbackup_" + new Random().Next(10000000, 99999999);
SqlTestDb testDb = SqlTestDb.CreateNew(TestServerType.OnPrem, false, databaseName);
var liveConnection = LiveConnectionHelper.InitLiveConnectionInfo(databaseName);
var requestContext = new Mock<RequestContext<BackupConfigInfoResponse>>();
requestContext.Setup(x => x.SendResult(It.IsAny<BackupConfigInfoResponse>()))
.Returns(Task.FromResult(new object()));
var dbParams = new DefaultDatabaseInfoParams
{
OwnerUri = liveConnection.ConnectionInfo.OwnerUri
};
await DisasterRecoveryService.HandleBackupConfigInfoRequest(dbParams, requestContext.Object);
requestContext.Verify(x => x.SendResult(It.Is<BackupConfigInfoResponse>
(p => p.BackupConfigInfo.RecoveryModel != string.Empty
&& p.BackupConfigInfo.DefaultBackupFolder != string.Empty
&& p.BackupConfigInfo.DatabaseInfo != null)));
testDb.Cleanup();
}
/// Test is failing in code coverage runs. Reenable when stable.
///[Fact]
public void CreateBackupTest()
{
string databaseName = "testbackup_" + new Random().Next(10000000, 99999999);
SqlTestDb testDb = SqlTestDb.CreateNew(TestServerType.OnPrem, false, databaseName);
var liveConnection = LiveConnectionHelper.InitLiveConnectionInfo(databaseName);
// Initialize backup service
DatabaseTaskHelper helper = AdminService.CreateDatabaseTaskHelper(liveConnection.ConnectionInfo, databaseExists: true);
SqlConnection sqlConn = DisasterRecoveryService.GetSqlConnection(liveConnection.ConnectionInfo);
// Get default backup path
BackupConfigInfo backupConfigInfo = DisasterRecoveryService.Instance.GetBackupConfigInfo(helper.DataContainer, sqlConn, sqlConn.Database);
string backupPath = backupConfigInfo.DefaultBackupFolder + "\\" + databaseName + ".bak";
var backupInfo = new BackupInfo();
backupInfo.BackupComponent = (int)BackupComponent.Database;
backupInfo.BackupDeviceType = (int)BackupDeviceType.Disk;
backupInfo.BackupPathDevices = new Dictionary<string, int>() { { backupPath, (int)DeviceType.File } };
backupInfo.BackupPathList = new List<string>(new string[] { backupPath });
backupInfo.BackupsetName = "default_backup";
backupInfo.BackupType = (int)BackupType.Full;
backupInfo.DatabaseName = databaseName;
backupInfo.SelectedFileGroup = null;
backupInfo.SelectedFiles = "";
var backupParams = new BackupParams
{
OwnerUri = liveConnection.ConnectionInfo.OwnerUri,
BackupInfo = backupInfo
};
// Backup the database
BackupOperation backupOperation = DisasterRecoveryService.Instance.SetBackupInput(helper.DataContainer, sqlConn, backupParams.BackupInfo);
DisasterRecoveryService.Instance.PerformBackup(backupOperation);
// Remove the backup file
if (File.Exists(backupPath))
{
File.Delete(backupPath);
}
// Clean up the database
testDb.Cleanup();
}
}
}

View File

@@ -40,7 +40,7 @@ namespace Microsoft.SqlTools.ServiceLayer.UnitTests.DisasterRecovery
/// </summary>
/// <param name="databaseName"></param>
/// <returns></returns>
public BackupConfigInfo GetBackupConfigInfo(string databaseName)
public BackupConfigInfo CreateBackupConfigInfo(string databaseName)
{
return null;
}

View File

@@ -99,8 +99,7 @@ namespace Microsoft.SqlTools.ServiceLayer.UnitTests.DisasterRecovery
/// Cancel multiple backup tasks
/// </summary>
/// <returns></returns>
/// Test is failing unreliably in AppVeyor runs so disabling for.
///[Fact]
[Fact]
public async Task VerifyCancelMultipleBackupTasks()
{
using (SqlTaskManager manager = new SqlTaskManager())
@@ -142,8 +141,7 @@ namespace Microsoft.SqlTools.ServiceLayer.UnitTests.DisasterRecovery
/// Create two backup tasks and cancel one task
/// </summary>
/// <returns></returns>
/// Test is failing in AppVeyor unreliabily..disabling for now. please reenalbe when test is stable in AppVeyor builds.
/// [Fact]
[Fact]
public async Task VerifyCombinationRunAndCancelBackupTasks()
{
using (SqlTaskManager manager = new SqlTaskManager())