Skip to content

Add Data Sync PowerShell Cmdlets to Azure.SQL #4037

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

Merged
merged 28 commits into from
Jun 22, 2017

Conversation

fhljys
Copy link
Member

@fhljys fhljys commented May 26, 2017

Description

Azure SQL Data Sync is a new feature with a new set of REST API on the existing Azure SQL RP (Microsoft.Sql namespace). Data Sync feature is mainly used in two scenarios: one is sync schema between two Azure SQL databases, and the other is sync schema between an Azure SQL Database and an on-premises SQL server database. Currently, the REST API is in public review. And this PR is to add PowerShell Cmdlets for data sync feature.


This checklist is used to make sure that common guidelines for a pull request are followed. You can find a more complete discussion of PowerShell cmdlet best practices here.

General Guidelines

  • [Y] Title of the pull request is clear and informative.
  • [Y] There are a small number of commits, each of which have an informative message. This means that previously merged commits do not appear in the history of the PR. For more information on cleaning up the commits in your PR, see this page.
  • [Y] The pull request does not introduce breaking changes (unless a major version change occurs in the assembly and module).

Testing Guidelines

  • [Y] Pull request includes test coverage for the included changes.
  • [Y] PowerShell scripts used in tests should do any necessary setup as part of the test or suite setup, and should not use hard-coded values for locations or existing resources.

Cmdlet Signature Guidelines

  • [Y] New cmdlets that make changes or have side effects should implement ShouldProcess and have SupportShouldProcess=true specified in the cmdlet attribute. You can find more information on ShouldProcess here.
  • [Y] Cmdlet specifies OutputType attribute if any output is produced - if the cmdlet produces no output, it should implement a PassThru parameter.

Cmdlet Parameter Guidelines

  • [Y] Parameter types should not expose types from the management library - complex parameter types should be defined in the module.
  • [Y] Complex parameter types are discouraged - a parameter type should be simple types as often as possible. If complex types are used, they should be shallow and easily creatable from a constructor or another cmdlet.
  • [Y] Cmdlet parameter sets should be mutually exclusive - each parameter set must have at least one mandatory parameter not in other parameter sets.

@msftclas
Copy link

@fhljys,
Thanks for your contribution as a Microsoft full-time employee or intern. You do not need to sign a CLA.
Thanks,
Microsoft Pull Request Bot

@azuresdkci
Copy link

Can one of the admins verify this patch?

@fhljys
Copy link
Member Author

fhljys commented May 30, 2017

@akromm Hi Adam, could you please help review this one? Thanks a lot!

Copy link
Contributor

@jaredmoo jaredmoo left a comment

Choose a reason for hiding this comment

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

Minor comments, mainly looks good

function Get-SqlSyncGroupTestEnvironmentParameters ($testSuffix)
{
return @{
intervalInSeconds = 300;
Copy link
Contributor

Choose a reason for hiding this comment

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

nit: bad indentation here due to tabs

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 will fix this. Thanks.

#>
function Get-SqlSyncMemberTestEnvironmentParameters ($testSuffix)
{
return @{
Copy link
Contributor

Choose a reason for hiding this comment

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

nit: bad indentation here due to tabs

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 will fix this.

/// <summary>
/// Gets or sets the name of the server on which syncDB is hosted
/// </summary>
[Parameter(Mandatory = true,
Copy link
Contributor

Choose a reason for hiding this comment

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

Could you make SyncDatabaseServerName and SyncDatabaseResourceGroupName non-mandatory, use the value of ServerName and ResourceGroupName by default? That way you don't have to specify it if sync agent server and sync db server are the same.

Copy link
Member Author

Choose a reason for hiding this comment

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

OK

&& MyInvocation.BoundParameters.ContainsKey("SyncDatabaseServerName")
&& MyInvocation.BoundParameters.ContainsKey("SyncDatabaseName"))
{
this.syncDatabaseId = string.Format("resourceGroups/{0}/providers/Microsoft.Sql/servers/{1}/databases/{2}",
Copy link
Contributor

Choose a reason for hiding this comment

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

Where does "/subscriptions/{id}/" prefix get added? can you comment?

Copy link
Member Author

Choose a reason for hiding this comment

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

It was added in AzureSqlDataSyncCommunicator. I will add a comment in the code.

/// Gets or sets the frequency (in seconds) of doing data synchronization.
/// </summary>
[Parameter(Mandatory = false, HelpMessage = "The frequency (in seconds) of doing data synchronization. Default is -1, which means the auto synchronization is not enabled.")]
public int? IntervalInSeconds { get; set; }
Copy link
Contributor

Choose a reason for hiding this comment

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

Nice, perhaps consider adding IntervalInMinutes, IntervalInHours as different parameter sets so that user doesn't need to do math

Copy link
Member Author

Choose a reason for hiding this comment

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

Thank you for your suggestions. This will be considered in future improvement.

Copy link
Member

Choose a reason for hiding this comment

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

Any reason not to use a TimeSpan interval?

Copy link
Member Author

Choose a reason for hiding this comment

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

Its unit is seconds. It is more like a frequency than a time span.

}
catch (Newtonsoft.Json.JsonReaderException)
{
throw new PSArgumentException("The schema file is empty or invalid!", "SchemaFile");
Copy link
Contributor

Choose a reason for hiding this comment

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

Can you provide a link to a page that describes the expected schema?

Copy link
Member Author

Choose a reason for hiding this comment

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

The official doc is not published yet. Could we add this in next sprint?

Copy link
Contributor

Choose a reason for hiding this comment

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

sure.

/// Gets or sets the frequency (in seconds) of doing data synchronization.
/// </summary>
[Parameter(Mandatory = false, HelpMessage = "The frequency (in seconds) of doing data synchronization. Default is -1, which means the auto synchronization is not enabled.")]
public int? IntervalInSeconds { get; set; }
Copy link
Contributor

Choose a reason for hiding this comment

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

I don't think this needs to be nullable.

Copy link
Member Author

@fhljys fhljys May 30, 2017

Choose a reason for hiding this comment

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

I will change it to int.

[Parameter(Mandatory = true,
HelpMessage = "The database used to store sync related metadata.")]
[ValidateNotNullOrEmpty]
public string SyncDatabaseName { get; set; }
Copy link
Contributor

Choose a reason for hiding this comment

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

Can you sort all of these so that the required parameters appear first in the list.

Copy link
Member Author

Choose a reason for hiding this comment

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

OK. Thanks.

[Parameter(Mandatory = true,
ParameterSetName = AzureSqlSet,
HelpMessage = "The Azure SQL Server Name of the member database.")]
public string MemberServerName { get; set; }
Copy link
Contributor

Choose a reason for hiding this comment

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

please sort such that required parameters appear before optional

Copy link
Member Author

Choose a reason for hiding this comment

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

ok

/// </summary>
public override void ExecuteCmdlet()
{
if (!Force.IsPresent && !ShouldProcess(
Copy link
Contributor

Choose a reason for hiding this comment

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

Copy link
Member Author

Choose a reason for hiding this comment

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

Fixed.

if (!Force.IsPresent && !ShouldProcess(
string.Format(CultureInfo.InvariantCulture, Microsoft.Azure.Commands.Sql.Properties.Resources.RemoveAzureSqlSyncMemberDescription, this.SyncMemberName, this.SyncGroupName),
string.Format(CultureInfo.InvariantCulture, Microsoft.Azure.Commands.Sql.Properties.Resources.RemoveAzureSqlSyncMemberWarning, this.SyncMemberName, this.SyncGroupName),
Microsoft.Azure.Commands.Sql.Properties.Resources.ShouldProcessCaption))
Copy link
Contributor

Choose a reason for hiding this comment

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

Copy link
Member Author

Choose a reason for hiding this comment

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

Fixed

/// Gets or sets the frequency (in seconds) of doing data synchronization
/// </summary>
[Parameter(Mandatory = false, HelpMessage = "The frequency (in seconds) of doing data synchronization. Default is -1, which means the auto synchronization is not enabled.")]
public int? IntervalInSeconds { get; set; }
Copy link
Contributor

Choose a reason for hiding this comment

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

This doesn't need to be nullable.

Copy link
Member Author

Choose a reason for hiding this comment

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

Ok.

[Parameter(Mandatory = true, ValueFromPipelineByPropertyName = true,
HelpMessage = "The database type.")]
[ValidateSet("AzureSqlDatabase", IgnoreCase = true)]
public string DatabaseType { get; set; }
Copy link
Contributor

Choose a reason for hiding this comment

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

This parameter seems unnecessary. It is marked as mandatory and it can only have 1 value. Furthermore it is not used anywhere.

Copy link
Member Author

Choose a reason for hiding this comment

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

Ok. I will delete it.

/// <summary>
/// Gets or sets the frequency (in seconds) time of doing data synchronization
/// </summary>
public int? IntervalInSeconds { get; set; }
Copy link
Contributor

Choose a reason for hiding this comment

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

Should this be nullable? What does it mean if this is null?

Additionally consider changing this to a TimeSpan object.

Copy link
Member Author

@fhljys fhljys May 31, 2017

Choose a reason for hiding this comment

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

The service layer will use the default value when this is null. It is the business logic requirement of service layer. I have changed Power Shell parameters to 'int' instead of 'int?'. Will keep it as it here.

Copy link
Member

Choose a reason for hiding this comment

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

If there is a server default this is appropriate. In PowerShell, you can make this parameter optional and check BoundParameters to see if the user provided it. Limiting the number of values that the user has to provide is a good thing

Tables = syncGroupSchemaTable
};
}

Copy link
Contributor

Choose a reason for hiding this comment

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

Nit: remove blank link.

Copy link
Member Author

Choose a reason for hiding this comment

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

OK. Thanks.

@fhljys
Copy link
Member Author

fhljys commented May 31, 2017

@akromm Hi Adam, I have updated this code to resolve your comments. Please help me review it again.

akromm-zz
akromm-zz previously approved these changes May 31, 2017
@fhljys
Copy link
Member Author

fhljys commented Jun 2, 2017

@akromm Hi Adam, I pushed a commit to improve the help doc xml, which dismissed your signoff accidently. Could you please help me review this again. Very sorry for the trouble. Thanks a lot.

akromm-zz
akromm-zz previously approved these changes Jun 2, 2017
@fhljys
Copy link
Member Author

fhljys commented Jun 18, 2017

@markcowl Please help me review the changes. Thanks.

/// </summary>
public override void ExecuteCmdlet()
{
if (!Force.IsPresent && !ShouldProcess(
Copy link
Member

Choose a reason for hiding this comment

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

This is not the correct way to implement ShouldProcess. This should be implemented as

{
  if (Force || ShouldContinue)
  { 
    <execute action>

You can get this behavior using ConfirmAction(bool, string, string, Action) as well

{
return string.Format("{0}.{1}", this.ServerName, this.DatabaseName);
if (!Force.IsPresent && !ShouldProcess(
Copy link
Member

Choose a reason for hiding this comment

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

same comment

@@ -15507,6 +15507,14 @@ ResourceGroup01 Server01 DBAs 40b79501-b343-44ed-9ce7-da4c8cc7353b</d
<maml:uri /></dev:type>
<dev:defaultValue>False</dev:defaultValue>
</command:parameter>
<command:parameter required="false" variableLength="true" globbing="false" pipelineInput="False" position="named" aliases="none"><maml:name>Force</maml:name>
Copy link
Member

Choose a reason for hiding this comment

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

this file should not be included in the repo

@fhljys fhljys assigned markcowl and unassigned fhljys Jun 19, 2017
@fhljys
Copy link
Member Author

fhljys commented Jun 19, 2017

@markcowl Hi Mark, please help review this one. Thank you very much!

@markcowl
Copy link
Member

@fhljys
Copy link
Member Author

fhljys commented Jun 21, 2017

@markcowl @cormacpayne @azuresdkci Hi Mark and Team, the on demand run finished successfully. Is there anything need I do to finish this PR? Thanks a lot.

@markcowl markcowl merged commit b87bc4e into Azure:preview Jun 22, 2017
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

8 participants