Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

generic way to support scriptable operations #438

Merged
merged 7 commits into from
Aug 18, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[![Travis CI](https://travis-ci.org/Microsoft/sqltoolsservice.svg?branch=dev)](https://travis-ci.org/Microsoft/sqltoolsservice)
[![AppVeyor](https://ci.appveyor.com/api/projects/status/github/Microsoft/sqltoolsservice?svg=true&retina=true&branch=dev)](https://ci.appveyor.com/project/kburtram/sqltoolsservice)
[![Coverage Status](https://coveralls.io/repos/github/Microsoft/sqltoolsservice/badge.svg?branch=dev)](https://coveralls.io/github/Microsoft/sqltoolsservice?branch=dev)
[![Coverage Status](https://coveralls.io/repos/github/Microsoft/sqltoolsservice/badge.svg?branch=dev)](https://coveralls.io/github/Microsoft/sqltoolsservice?branch=master)
Copy link
Contributor

@kevcunnane kevcunnane Aug 18, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice catch :) #WontFix


# Microsoft SQL Tools Service
The SQL Tools Service is an application that provides core functionality for various SQL Server tools. These features include the following:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ public class BackupOperation : IBackupOperation
/// this is used when the backup dialog is launched in the context of a backup device
/// The InitialBackupDestination will be loaded in LoadData
private string initialBackupDestination = string.Empty;

// Helps in populating the properties of an Azure blob given its URI
private class BlobProperties
{
Expand Down Expand Up @@ -163,6 +163,19 @@ public string ScriptContent
}
}

/// <summary>
/// The error occurred during backup operation
/// </summary>
public string ErrorMessage
{
get
{
return string.Empty;
Copy link
Member

@kburtram kburtram Aug 18, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

what's the point of this property? #WontFix

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I added the error message to IBackupOperation so that the task manager get the error of the operation but didn't integrate the new changes with backup


In reply to: 134057582 [](ancestors = 134057582)

}
}

public SqlTask SqlTask { get; set; }

/// <summary>
/// Execute backup
/// </summary>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
//

using System.Collections.Generic;
using Microsoft.SqlTools.ServiceLayer.TaskServices;
using Microsoft.SqlTools.ServiceLayer.Utility;
using Newtonsoft.Json.Linq;

Expand All @@ -12,7 +13,7 @@ namespace Microsoft.SqlTools.ServiceLayer.DisasterRecovery.Contracts
/// <summary>
/// Restore request parameters
/// </summary>
public class RestoreParams : GeneralRequestDetails
public class RestoreParams : GeneralRequestDetails, IScriptableRequestParams
{
/// <summary>
/// Restore session id. The parameter is optional and if passed, an existing plan will be used
Expand Down Expand Up @@ -140,6 +141,26 @@ internal IEnumerable<string> SelectedBackupSets
SetOptionValue(RestoreOptionsHelper.SelectedBackupSets, value);
}
}

/// <summary>
/// The executation mode for the operation. default is execution
/// </summary>
public TaskExecutionMode TaskExecutionMode { get; set; }

/// <summary>
/// Same as Target Database name. Used by task manager to create task info
/// </summary>
public string DatabaseName
{
get
{
return TargetDatabaseName;
Copy link
Member

@kburtram kburtram Aug 18, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why do we have a property that gets & sets another property? Should we remove this property or the other one? #WontFix

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I added the DatabaseName to the request base class assuming all requests will have a database name field. TargetDatabase is the same as DatabaseName if restore so that's why I'm implementing it this way


In reply to: 134058351 [](ancestors = 134058351)

}
set
{
TargetDatabaseName = value;
}
}
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -224,15 +224,10 @@ internal async Task HandleRestoreRequest(
if (restoreDataObject != null)
{
// create task metadata
TaskMetadata metadata = new TaskMetadata();
metadata.ServerName = connInfo.ConnectionDetails.ServerName;
metadata.DatabaseName = restoreParams.TargetDatabaseName;
metadata.Name = SR.RestoreTaskName;
metadata.IsCancelable = true;
metadata.Data = restoreDataObject;
TaskMetadata metadata = TaskMetadata.Create(restoreParams, SR.RestoreTaskName, restoreDataObject, ConnectionServiceInstance);
Copy link
Contributor

@kevcunnane kevcunnane Aug 18, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What happens to IsCancelable? Is the default True and you just override or don't? #Pending

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I removed it from here and if the cancel function is defined I assume it's cancelable


In reply to: 134042668 [](ancestors = 134042668)


// create restore task and perform
SqlTask sqlTask = SqlTaskManagerInstance.CreateAndRun(metadata, this.restoreDatabaseService.RestoreTaskAsync, restoreDatabaseService.CancelTaskAsync);
SqlTask sqlTask = SqlTaskManagerInstance.CreateAndRun<SqlTask>(metadata);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

how are we setting restore's CancelTask in SqlTask?

response.TaskId = sqlTask.TaskId.ToString();
}
else
Expand Down Expand Up @@ -285,8 +280,7 @@ internal async Task HandleBackupRequest(
TaskMetadata metadata = new TaskMetadata();
metadata.ServerName = connInfo.ConnectionDetails.ServerName;
metadata.DatabaseName = connInfo.ConnectionDetails.DatabaseName;
metadata.Data = backupOperation;
metadata.IsCancelable = true;
metadata.TaskOperation = backupOperation;

if (backupParams.IsScripting)
{
Expand Down Expand Up @@ -414,7 +408,7 @@ internal void ScriptBackup(BackupOperation backupOperation)
/// <returns></returns>
internal async Task<TaskResult> PerformBackupTaskAsync(SqlTask sqlTask)
{
IBackupOperation backupOperation = sqlTask.TaskMetadata.Data as IBackupOperation;
IBackupOperation backupOperation = sqlTask.TaskMetadata.TaskOperation as IBackupOperation;
TaskResult result = new TaskResult();

// Create a task to perform backup
Expand Down Expand Up @@ -463,7 +457,7 @@ await Task.Factory.StartNew(() =>
/// <returns></returns>
internal async Task<TaskResult> CancelBackupTaskAsync(SqlTask sqlTask)
{
IBackupOperation backupOperation = sqlTask.TaskMetadata.Data as IBackupOperation;
IBackupOperation backupOperation = sqlTask.TaskMetadata.TaskOperation as IBackupOperation;
TaskResult result = new TaskResult();

await Task.Factory.StartNew(() =>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,13 +7,11 @@
using System.Linq;
using System.Data.Common;
using System.Data.SqlClient;
using System.Threading.Tasks;
using Microsoft.SqlServer.Management.Common;
using Microsoft.SqlServer.Management.Smo;
using Microsoft.SqlTools.ServiceLayer.Connection;
using Microsoft.SqlTools.ServiceLayer.Connection.ReliableConnection;
using Microsoft.SqlTools.ServiceLayer.DisasterRecovery.Contracts;
using Microsoft.SqlTools.ServiceLayer.TaskServices;
using Microsoft.SqlTools.Utility;
using System.Collections.Concurrent;
using Microsoft.SqlTools.ServiceLayer.Utility;
Expand All @@ -28,107 +26,6 @@ public class RestoreDatabaseHelper
public const string LastBackupTaken = "lastBackupTaken";
private ConcurrentDictionary<string, RestoreDatabaseTaskDataObject> sessions = new ConcurrentDictionary<string, RestoreDatabaseTaskDataObject>();

/// <summary>
/// Create a backup task for execution and cancellation
/// </summary>
/// <param name="sqlTask"></param>
/// <returns></returns>
internal async Task<TaskResult> RestoreTaskAsync(SqlTask sqlTask)
{
sqlTask.AddMessage(SR.TaskInProgress, SqlTaskStatus.InProgress, true);
RestoreDatabaseTaskDataObject restoreDataObject = sqlTask.TaskMetadata.Data as RestoreDatabaseTaskDataObject;
TaskResult taskResult = null;

if (restoreDataObject != null)
{
// Create a task to perform backup
return await Task.Factory.StartNew(() =>
{
TaskResult result = new TaskResult();
try
{
if (restoreDataObject.IsValid)
{
ExecuteRestore(restoreDataObject, sqlTask);
result.TaskStatus = SqlTaskStatus.Succeeded;
}
else
{
result.TaskStatus = SqlTaskStatus.Failed;
if (restoreDataObject.ActiveException != null)
{
result.ErrorMessage = restoreDataObject.ActiveException.Message;
}
else
{
result.ErrorMessage = SR.RestoreNotSupported;
}
}
}
catch (Exception ex)
{
result.TaskStatus = SqlTaskStatus.Failed;
result.ErrorMessage = ex.Message;
if (ex.InnerException != null)
{
result.ErrorMessage += Environment.NewLine + ex.InnerException.Message;
}
if (restoreDataObject != null && restoreDataObject.ActiveException != null)
{
result.ErrorMessage += Environment.NewLine + restoreDataObject.ActiveException.Message;
}
}
return result;
});
}
else
{
taskResult = new TaskResult();
taskResult.TaskStatus = SqlTaskStatus.Failed;
}

return taskResult;
}



/// <summary>
/// Async task to cancel restore
/// </summary>
public async Task<TaskResult> CancelTaskAsync(SqlTask sqlTask)
{
RestoreDatabaseTaskDataObject restoreDataObject = sqlTask.TaskMetadata.Data as RestoreDatabaseTaskDataObject;
TaskResult taskResult = null;


if (restoreDataObject != null && restoreDataObject.IsValid)
{
// Create a task for backup cancellation request
return await Task.Factory.StartNew(() =>
{

foreach (Restore restore in restoreDataObject.RestorePlan.RestoreOperations)
{
restore.Abort();
}


return new TaskResult
{
TaskStatus = SqlTaskStatus.Canceled
};

});
}
else
{
taskResult = new TaskResult();
taskResult.TaskStatus = SqlTaskStatus.Failed;
}

return taskResult;
}

/// <summary>
/// Creates response which includes information about the server given to restore (default data location, db names with backupsets)
/// </summary>
Expand Down Expand Up @@ -166,7 +63,7 @@ public RestorePlanResponse CreateRestorePlanResponse(RestoreDatabaseTaskDataObje
{
if (restoreDataObject != null && restoreDataObject.IsValid)
{
UpdateRestorePlan(restoreDataObject);
restoreDataObject.UpdateRestoreTaskObject();

if (restoreDataObject != null && restoreDataObject.IsValid)
{
Expand Down Expand Up @@ -227,6 +124,7 @@ public RestorePlanResponse CreateRestorePlanResponse(RestoreDatabaseTaskDataObje
response.ErrorMessage += Environment.NewLine;
response.ErrorMessage += ex.InnerException.Message;
}
Logger.Write(LogLevel.Normal, $"Failed to create restore plan. error: { response.ErrorMessage}");
}
return response;

Expand Down Expand Up @@ -295,63 +193,11 @@ private RestoreDatabaseTaskDataObject CreateRestoreForNewSession(string ownerUri
return null;
}

/// <summary>
/// Create a restore data object that includes the plan to do the restore operation
/// </summary>
/// <param name="requestParam"></param>
/// <returns></returns>
private void UpdateRestorePlan(RestoreDatabaseTaskDataObject restoreDataObject)
{
bool shouldCreateNewPlan = restoreDataObject.ShouldCreateNewPlan();

if (!string.IsNullOrEmpty(restoreDataObject.RestoreParams.BackupFilePaths))
{
restoreDataObject.AddFiles(restoreDataObject.RestoreParams.BackupFilePaths);
}
restoreDataObject.RestorePlanner.ReadHeaderFromMedia = restoreDataObject.RestoreParams.ReadHeaderFromMedia;

RestoreOptionFactory.Instance.SetAndValidate(RestoreOptionsHelper.SourceDatabaseName, restoreDataObject);
RestoreOptionFactory.Instance.SetAndValidate(RestoreOptionsHelper.TargetDatabaseName, restoreDataObject);

if (shouldCreateNewPlan)
{
restoreDataObject.CreateNewRestorePlan();
}

restoreDataObject.UpdateRestorePlan();

}


private bool CanChangeTargetDatabase(RestoreDatabaseTaskDataObject restoreDataObject)
{
return DatabaseUtils.IsSystemDatabaseConnection(restoreDataObject.Server.ConnectionContext.DatabaseName);
}

/// <summary>
/// Executes the restore operation
/// </summary>
/// <param name="requestParam"></param>
public void ExecuteRestore(RestoreDatabaseTaskDataObject restoreDataObject, SqlTask sqlTask = null)
{
// Restore Plan should be already created and updated at this point
UpdateRestorePlan(restoreDataObject);

if (restoreDataObject != null && CanRestore(restoreDataObject))
{
try
{
restoreDataObject.SqlTask = sqlTask;
restoreDataObject.Execute();
}
catch(Exception ex)
{
throw ex;
}
}
else
{
throw new InvalidOperationException(SR.RestoreNotSupported);
}
}
}
}
Loading