Integrate generate script with task service (#426)

* Support generate script for backup

* change

* update task service data contract for Generate Script

* more changes

* update test

* add comments

* Add missing files

* update stub backup operation for testing

* pr comments

* remove empty space

* Fix tests

* Add unit/integration tests and isCancelable to TaskInfo

* address pr comments

* pr comments - fix tests

* fix minor issue

* fix minor issues

* remove unused variable
This commit is contained in:
Kate Shin
2017-08-09 19:59:40 -07:00
committed by GitHub
parent 6696b7e72f
commit cd870e6f15
26 changed files with 845 additions and 386 deletions

View File

@@ -53,7 +53,8 @@ CREATE CERTIFICATE {1} WITH SUBJECT = 'Backup Encryption Certificate'; ";
OwnerUri = liveConnection.ConnectionInfo.OwnerUri
};
await DisasterRecoveryService.HandleBackupConfigInfoRequest(dbParams, requestContext.Object);
DisasterRecoveryService service = new DisasterRecoveryService();
await service.HandleBackupConfigInfoRequest(dbParams, requestContext.Object);
requestContext.Verify(x => x.SendResult(It.Is<BackupConfigInfoResponse>
(p => p.BackupConfigInfo.RecoveryModel != string.Empty
@@ -63,115 +64,106 @@ CREATE CERTIFICATE {1} WITH SUBJECT = 'Backup Encryption Certificate'; ";
testDb.Cleanup();
}
/// Test is failing in code coverage runs. Reenable when stable.
///[Fact]
/// <summary>
/// Create simple backup test
/// </summary>
[Fact]
public void CreateBackupTest()
{
DisasterRecoveryService service = new DisasterRecoveryService();
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,
string backupPath = GetDefaultBackupPath(service, databaseName, helper.DataContainer, sqlConn);
BackupInfo backupInfo = CreateDefaultBackupInfo(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
};
new List<string>() { backupPath },
new Dictionary<string, int>() { { backupPath, (int)DeviceType.File } });
BackupOperation backupOperation = CreateBackupOperation(service, liveConnection.ConnectionInfo.OwnerUri, backupInfo, helper.DataContainer, sqlConn);
// 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);
}
service.PerformBackup(backupOperation);
// Clean up the database
VerifyAndCleanBackup(backupPath);
testDb.Cleanup();
}
[Fact]
public void ScriptBackupTest()
{
DisasterRecoveryService service = new DisasterRecoveryService();
string databaseName = "testbackup_" + new Random().Next(10000000, 99999999);
SqlTestDb testDb = SqlTestDb.CreateNew(TestServerType.OnPrem, false, databaseName);
var liveConnection = LiveConnectionHelper.InitLiveConnectionInfo(databaseName);
DatabaseTaskHelper helper = AdminService.CreateDatabaseTaskHelper(liveConnection.ConnectionInfo, databaseExists: true);
SqlConnection sqlConn = DisasterRecoveryService.GetSqlConnection(liveConnection.ConnectionInfo);
string backupPath = GetDefaultBackupPath(service, databaseName, helper.DataContainer, sqlConn);
BackupInfo backupInfo = CreateDefaultBackupInfo(databaseName,
BackupType.Full,
new List<string>() { backupPath },
new Dictionary<string, int>() { { backupPath, (int)DeviceType.File } });
BackupOperation backupOperation = CreateBackupOperation(service, liveConnection.ConnectionInfo.OwnerUri, backupInfo, helper.DataContainer, sqlConn);
// Generate script for backup
service.ScriptBackup(backupOperation);
string script = backupOperation.ScriptContent;
Assert.True(!string.IsNullOrEmpty(script));
// Execute the script
testDb.RunQuery(script);
VerifyAndCleanBackup(backupPath);
testDb.Cleanup();
}
/// <summary>
/// Test creating backup with advanced options set.
/// </summary>
[Fact]
public void CreateBackupWithAdvancedOptionsTest()
{
DisasterRecoveryService service = new DisasterRecoveryService();
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);
string backupPath = GetDefaultBackupPath(service, databaseName, helper.DataContainer, sqlConn);
// 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");
string certificateName = CreateCertificate(testDb);
string cleanupCertificateQuery = string.Format(CleanupCertificateQueryFormat, certificateName);
BackupInfo backupInfo = CreateBackupInfo(databaseName,
BackupInfo backupInfo = CreateDefaultBackupInfo(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.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
};
BackupOperation backupOperation = CreateBackupOperation(service, liveConnection.ConnectionInfo.OwnerUri, backupInfo, helper.DataContainer, sqlConn);
// 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));
service.PerformBackup(backupOperation);
// Remove the backup file
Console.WriteLine("Remove backup file..");
if (File.Exists(backupPath))
{
File.Delete(backupPath);
}
Console.WriteLine("Verify the backup file exists and remove..");
VerifyAndCleanBackup(backupPath);
// Delete certificate and master key
Console.WriteLine("Remove certificate and master key..");
@@ -182,7 +174,76 @@ CREATE CERTIFICATE {1} WITH SUBJECT = 'Backup Encryption Certificate'; ";
testDb.Cleanup();
}
private BackupInfo CreateBackupInfo(string databaseName, BackupType backupType, List<string> backupPathList, Dictionary<string, int> backupPathDevices)
/// <summary>
/// Test creating backup with advanced options set.
/// </summary>
[Fact]
public void ScriptBackupWithAdvancedOptionsTest()
{
DisasterRecoveryService service = new DisasterRecoveryService();
string databaseName = "testbackup_" + new Random().Next(10000000, 99999999);
SqlTestDb testDb = SqlTestDb.CreateNew(TestServerType.OnPrem, false, databaseName);
var liveConnection = LiveConnectionHelper.InitLiveConnectionInfo(databaseName);
DatabaseTaskHelper helper = AdminService.CreateDatabaseTaskHelper(liveConnection.ConnectionInfo, databaseExists: true);
SqlConnection sqlConn = DisasterRecoveryService.GetSqlConnection(liveConnection.ConnectionInfo);
string backupPath = GetDefaultBackupPath(service, databaseName, helper.DataContainer, sqlConn);
string certificateName = CreateCertificate(testDb);
string cleanupCertificateQuery = string.Format(CleanupCertificateQueryFormat, certificateName);
BackupInfo backupInfo = CreateDefaultBackupInfo(databaseName,
BackupType.Full,
new List<string>() { backupPath },
new Dictionary<string, int>() { { backupPath, (int)DeviceType.File } });
backupInfo.FormatMedia = true;
backupInfo.SkipTapeHeader = true;
backupInfo.Initialize = true;
backupInfo.MediaName = "backup test media";
backupInfo.MediaDescription = "backup test";
backupInfo.EncryptionAlgorithm = (int)BackupEncryptionAlgorithm.Aes128;
backupInfo.EncryptorType = (int)BackupEncryptorType.ServerCertificate;
backupInfo.EncryptorName = certificateName;
BackupOperation backupOperation = CreateBackupOperation(service, liveConnection.ConnectionInfo.OwnerUri, backupInfo, helper.DataContainer, sqlConn);
// Backup the database
Console.WriteLine("Generate script for backup operation..");
service.ScriptBackup(backupOperation);
string script = backupOperation.ScriptContent;
// Run the script
Console.WriteLine("Execute the script..");
testDb.RunQuery(script);
// Remove the backup file
Console.WriteLine("Verify the backup file exists and remove..");
VerifyAndCleanBackup(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();
}
#region private methods
private string CreateCertificate(SqlTestDb testDb)
{
string certificateName = "backupcertificate" + new Random().Next(10000000, 99999999);
string masterkeyPassword = Guid.NewGuid().ToString();
string createCertificateQuery = string.Format(CreateCertificateQueryFormat, masterkeyPassword, certificateName);
// create master key and certificate
Console.WriteLine("Create master key and certificate..");
testDb.RunQuery(createCertificateQuery);
return certificateName;
}
private BackupInfo CreateDefaultBackupInfo(string databaseName, BackupType backupType, List<string> backupPathList, Dictionary<string, int> backupPathDevices)
{
BackupInfo backupInfo = new BackupInfo();
backupInfo.BackupComponent = (int)BackupComponent.Database;
@@ -196,5 +257,36 @@ CREATE CERTIFICATE {1} WITH SUBJECT = 'Backup Encryption Certificate'; ";
backupInfo.SelectedFiles = "";
return backupInfo;
}
private string GetDefaultBackupPath(DisasterRecoveryService service, string databaseName, CDataContainer dataContainer, SqlConnection sqlConn)
{
BackupConfigInfo backupConfigInfo = service.GetBackupConfigInfo(dataContainer, sqlConn, sqlConn.Database);
return Path.Combine(backupConfigInfo.DefaultBackupFolder, databaseName + ".bak");
}
private BackupOperation CreateBackupOperation(DisasterRecoveryService service, string uri, BackupInfo backupInfo, CDataContainer dataContainer, SqlConnection sqlConn)
{
var backupParams = new BackupParams
{
OwnerUri = uri,
BackupInfo = backupInfo,
};
return service.CreateBackupOperation(dataContainer, sqlConn, backupParams.BackupInfo);
}
private void VerifyAndCleanBackup(string backupPath)
{
// Verify it created backup
Assert.True(File.Exists(backupPath));
// Remove the backup file
if (File.Exists(backupPath))
{
File.Delete(backupPath);
}
}
#endregion
}
}

View File

@@ -664,11 +664,12 @@ namespace Microsoft.SqlTools.ServiceLayer.IntegrationTests.DisasterRecovery
var backupParams = new BackupParams
{
OwnerUri = liveConnection.ConnectionInfo.OwnerUri,
BackupInfo = backupInfo
BackupInfo = backupInfo,
IsScripting = false
};
// Backup the database
BackupOperation backupOperation = DisasterRecoveryService.Instance.SetBackupInput(helper.DataContainer, sqlConn, backupParams.BackupInfo);
BackupOperation backupOperation = DisasterRecoveryService.Instance.CreateBackupOperation(helper.DataContainer, sqlConn, backupParams.BackupInfo);
DisasterRecoveryService.Instance.PerformBackup(backupOperation);
// Clean up the database

View File

@@ -7,6 +7,7 @@ using Microsoft.SqlServer.Management.Smo;
using Microsoft.SqlTools.ServiceLayer.Admin;
using Microsoft.SqlTools.ServiceLayer.DisasterRecovery;
using Microsoft.SqlTools.ServiceLayer.DisasterRecovery.Contracts;
using Microsoft.SqlTools.ServiceLayer.TaskServices;
using System;
using System.Data.SqlClient;
using System.Threading;
@@ -25,6 +26,7 @@ namespace Microsoft.SqlTools.ServiceLayer.UnitTests.DisasterRecovery
this.BackupSemaphore = new SemaphoreSlim(0, 1);
}
public string ScriptContent { get; set; }
/// <summary>
/// Initialize
@@ -56,7 +58,7 @@ namespace Microsoft.SqlTools.ServiceLayer.UnitTests.DisasterRecovery
/// <summary>
/// Execute backup
/// </summary>
public void PerformBackup()
public void Execute(TaskExecutionMode mode)
{
this.BackupSemaphore.Wait(TimeSpan.FromSeconds(5));
}
@@ -64,7 +66,7 @@ namespace Microsoft.SqlTools.ServiceLayer.UnitTests.DisasterRecovery
/// <summary>
/// Cancel backup
/// </summary>
public void CancelBackup()
public void Cancel()
{
}
}

View File

@@ -25,7 +25,7 @@ namespace Microsoft.SqlTools.ServiceLayer.UnitTests.DisasterRecovery
DisasterRecoveryService service = new DisasterRecoveryService();
var mockBackupOperation = new Mock<IBackupOperation>();
TaskMetadata taskMetaData = this.CreateTaskMetaData(mockBackupOperation.Object);
SqlTask sqlTask = manager.CreateTask(taskMetaData, service.BackupTaskAsync);
SqlTask sqlTask = manager.CreateTask(taskMetaData, service.PerformBackupTaskAsync, service.CancelBackupTaskAsync);
Assert.NotNull(sqlTask);
Task taskToVerify = sqlTask.RunAsync().ContinueWith(Task =>
{
@@ -35,7 +35,32 @@ namespace Microsoft.SqlTools.ServiceLayer.UnitTests.DisasterRecovery
await taskToVerify;
}
}
/// <summary>
/// Generate script for backup task
/// </summary>
/// <returns></returns>
[Fact]
public async Task VerifyScriptBackupTask()
{
using (SqlTaskManager manager = new SqlTaskManager())
{
DisasterRecoveryService service = new DisasterRecoveryService();
var mockBackupOperation = new Mock<IBackupOperation>();
TaskMetadata taskMetaData = this.CreateTaskMetaData(mockBackupOperation.Object);
taskMetaData.TaskExecutionMode = TaskExecutionMode.Script;
SqlTask sqlTask = manager.CreateTask(taskMetaData, service.PerformBackupTaskAsync);
Assert.NotNull(sqlTask);
Task taskToVerify = sqlTask.RunAsync().ContinueWith(Task =>
{
Assert.Equal(SqlTaskStatus.Succeeded, sqlTask.TaskStatus);
});
await taskToVerify;
}
}
/// <summary>
/// Create and run multiple backup tasks
/// </summary>
@@ -49,8 +74,8 @@ namespace Microsoft.SqlTools.ServiceLayer.UnitTests.DisasterRecovery
var mockBackupOperation = new Mock<IBackupOperation>();
TaskMetadata taskMetaData = this.CreateTaskMetaData(mockBackupOperation.Object);
SqlTask sqlTask = manager.CreateTask(taskMetaData, service.BackupTaskAsync);
SqlTask sqlTask2 = manager.CreateTask(taskMetaData, service.BackupTaskAsync);
SqlTask sqlTask = manager.CreateTask(taskMetaData, service.PerformBackupTaskAsync, service.CancelBackupTaskAsync);
SqlTask sqlTask2 = manager.CreateTask(taskMetaData, service.PerformBackupTaskAsync, service.CancelBackupTaskAsync);
Assert.NotNull(sqlTask);
Assert.NotNull(sqlTask2);
@@ -77,10 +102,10 @@ namespace Microsoft.SqlTools.ServiceLayer.UnitTests.DisasterRecovery
{
using (SqlTaskManager manager = new SqlTaskManager())
{
IBackupOperation backupOperation = new BackupOperationStub();
IBackupOperation backupOperation = new BackupOperationStub();
DisasterRecoveryService service = new DisasterRecoveryService();
TaskMetadata taskMetaData = this.CreateTaskMetaData(backupOperation);
SqlTask sqlTask = manager.CreateTask(taskMetaData, service.BackupTaskAsync);
SqlTask sqlTask = manager.CreateTask(taskMetaData, service.PerformBackupTaskAsync, service.CancelBackupTaskAsync);
Assert.NotNull(sqlTask);
Task taskToVerify = sqlTask.RunAsync().ContinueWith(Task =>
{
@@ -110,8 +135,8 @@ namespace Microsoft.SqlTools.ServiceLayer.UnitTests.DisasterRecovery
TaskMetadata taskMetaData = this.CreateTaskMetaData(backupOperation);
TaskMetadata taskMetaData2 = this.CreateTaskMetaData(backupOperation2);
SqlTask sqlTask = manager.CreateTask(taskMetaData, service.BackupTaskAsync);
SqlTask sqlTask2 = manager.CreateTask(taskMetaData2, service.BackupTaskAsync);
SqlTask sqlTask = manager.CreateTask(taskMetaData, service.PerformBackupTaskAsync, service.CancelBackupTaskAsync);
SqlTask sqlTask2 = manager.CreateTask(taskMetaData2, service.PerformBackupTaskAsync, service.CancelBackupTaskAsync);
Assert.NotNull(sqlTask);
Assert.NotNull(sqlTask2);
@@ -149,11 +174,11 @@ namespace Microsoft.SqlTools.ServiceLayer.UnitTests.DisasterRecovery
DisasterRecoveryService service = new DisasterRecoveryService();
IBackupOperation backupOperation = new BackupOperationStub();
TaskMetadata taskMetaData = this.CreateTaskMetaData(backupOperation);
SqlTask sqlTask = manager.CreateTask(taskMetaData, service.BackupTaskAsync);
SqlTask sqlTask = manager.CreateTask(taskMetaData, service.PerformBackupTaskAsync, service.CancelBackupTaskAsync);
var mockBackupOperation = new Mock<IBackupOperation>();
TaskMetadata taskMetaData2 = this.CreateTaskMetaData(mockBackupOperation.Object);
SqlTask sqlTask2 = manager.CreateTask(taskMetaData2, service.BackupTaskAsync);
SqlTask sqlTask2 = manager.CreateTask(taskMetaData, service.PerformBackupTaskAsync, service.CancelBackupTaskAsync);
Assert.NotNull(sqlTask);
Assert.NotNull(sqlTask2);

View File

@@ -23,6 +23,8 @@ namespace Microsoft.SqlTools.ServiceLayer.UnitTests.TaskServices
Failed = true;
}
public TaskScript TaskScript { get; set; }
public TaskResult TaskResult { get; set; }
public bool IsStopped { get; set; }
@@ -62,5 +64,21 @@ namespace Microsoft.SqlTools.ServiceLayer.UnitTests.TaskServices
};
});
}
public async Task<TaskResult> FunctionToScript(SqlTask sqlTask)
{
return await Task.Factory.StartNew(() =>
{
sqlTask.AddMessage("start scripting", SqlTaskStatus.InProgress, true);
TaskScript = sqlTask.AddScript(SqlTaskStatus.Succeeded, "script generated!");
sqlTask.AddMessage("done", SqlTaskStatus.Succeeded);
return new TaskResult
{
TaskStatus = SqlTaskStatus.Succeeded,
};
});
}
}
}

View File

@@ -147,5 +147,26 @@ namespace Microsoft.SqlTools.ServiceLayer.UnitTests.TaskServices
operation.FailTheOperation();
await taskToVerify;
}
[Fact]
public async Task RunScriptShouldReturnScriptContent()
{
SqlTaskStatus expectedStatus = SqlTaskStatus.Succeeded;
DatabaseOperationStub operation = new DatabaseOperationStub();
operation.TaskResult = new TaskResult
{
TaskStatus = expectedStatus
};
SqlTask sqlTask = new SqlTask(new TaskMetadata(), operation.FunctionToScript, null);
Assert.Equal(sqlTask.TaskStatus, SqlTaskStatus.NotStarted);
Task taskToVerify = sqlTask.RunAsync().ContinueWith(task => {
Assert.Equal(sqlTask.TaskStatus, expectedStatus);
Assert.Equal(sqlTask.IsCompleted, true);
Assert.NotNull(operation.TaskScript);
Assert.True(!string.IsNullOrEmpty(operation.TaskScript.Script));
});
await taskToVerify;
}
}
}

View File

@@ -86,5 +86,40 @@ namespace Microsoft.SqlTools.ServiceLayer.UnitTests.TaskServices
}
}
[Fact]
public async Task VerifyScriptTask()
{
using (SqlTaskManager manager = new SqlTaskManager())
{
DatabaseOperationStub operation = new DatabaseOperationStub();
operation.TaskResult = new TaskResult
{
TaskStatus = SqlTaskStatus.Succeeded
};
SqlTask sqlTask = manager.CreateTask(taskMetaData, operation.FunctionToScript);
bool scriptAddedEventRaised = false;
string script = null;
sqlTask.ScriptAdded += (object sender, TaskEventArgs<TaskScript> e) =>
{
scriptAddedEventRaised = true;
script = e.TaskData.Script;
};
Assert.NotNull(sqlTask);
Task taskToVerify = sqlTask.RunAsync().ContinueWith(task =>
{
Assert.True(scriptAddedEventRaised);
Assert.True(!string.IsNullOrEmpty(script));
Assert.True(manager.HasCompletedTasks());
manager.RemoveCompletedTask(sqlTask);
});
operation.Stop();
await taskToVerify;
}
}
}
}