diff --git a/Packages.props b/Packages.props index b3acecce87..a69bc1bea7 100644 --- a/Packages.props +++ b/Packages.props @@ -26,12 +26,12 @@ - + - + diff --git a/bin/nuget/Microsoft.SqlServer.Migration.Assessment.1.0.20240321.1.nupkg b/bin/nuget/Microsoft.SqlServer.Migration.Assessment.1.0.20240410.1.nupkg similarity index 78% rename from bin/nuget/Microsoft.SqlServer.Migration.Assessment.1.0.20240321.1.nupkg rename to bin/nuget/Microsoft.SqlServer.Migration.Assessment.1.0.20240410.1.nupkg index eecc9c7917..b0a0d352c1 100644 Binary files a/bin/nuget/Microsoft.SqlServer.Migration.Assessment.1.0.20240321.1.nupkg and b/bin/nuget/Microsoft.SqlServer.Migration.Assessment.1.0.20240410.1.nupkg differ diff --git a/bin/nuget/Microsoft.SqlServer.SqlTargetProvisioning.1.0.20240321.1.nupkg b/bin/nuget/Microsoft.SqlServer.SqlTargetProvisioning.1.0.20240410.1.nupkg similarity index 80% rename from bin/nuget/Microsoft.SqlServer.SqlTargetProvisioning.1.0.20240321.1.nupkg rename to bin/nuget/Microsoft.SqlServer.SqlTargetProvisioning.1.0.20240410.1.nupkg index b349a81c7f..25ed89a6fd 100644 Binary files a/bin/nuget/Microsoft.SqlServer.SqlTargetProvisioning.1.0.20240321.1.nupkg and b/bin/nuget/Microsoft.SqlServer.SqlTargetProvisioning.1.0.20240410.1.nupkg differ diff --git a/src/Microsoft.SqlTools.Migration/Contracts/GetArmTemplateRequest.cs b/src/Microsoft.SqlTools.Migration/Contracts/GetArmTemplateRequest.cs index 0077d29bc1..24816362a0 100644 --- a/src/Microsoft.SqlTools.Migration/Contracts/GetArmTemplateRequest.cs +++ b/src/Microsoft.SqlTools.Migration/Contracts/GetArmTemplateRequest.cs @@ -3,6 +3,7 @@ // Licensed under the MIT license. See LICENSE file in the project root for full license information. // +using System.Collections.Generic; using Microsoft.SqlTools.Hosting.Protocol.Contracts; namespace Microsoft.SqlTools.Migration.Contracts @@ -10,7 +11,7 @@ namespace Microsoft.SqlTools.Migration.Contracts public class GetArmTemplateRequest { public static readonly - RequestType Type = - RequestType.Create("migration/getarmtemplate"); + RequestType> Type = + RequestType>.Create("migration/getarmtemplate"); } } diff --git a/src/Microsoft.SqlTools.Migration/MigrationService.cs b/src/Microsoft.SqlTools.Migration/MigrationService.cs index 1ac1d643dd..874329b8ae 100644 --- a/src/Microsoft.SqlTools.Migration/MigrationService.cs +++ b/src/Microsoft.SqlTools.Migration/MigrationService.cs @@ -607,21 +607,24 @@ internal async Task HandleEstablishUserMapping( /// internal async Task HandleGetArmTemplateRequest( string skuRecommendationReportFilePath, - RequestContext requestContext) + RequestContext> requestContext) { try { ProvisioningScriptServiceProvider provider = new ProvisioningScriptServiceProvider(); List recommendations = ExtractSkuRecommendationReportAction.ExtractSkuRecommendationsFromReport(skuRecommendationReportFilePath); - SqlArmTemplate template = provider.GenerateProvisioningScript(recommendations); - - string jsonOutput = JsonConvert.SerializeObject( - template, - Formatting.Indented, - new JsonSerializerSettings { NullValueHandling = NullValueHandling.Ignore, Culture = CultureInfo.InvariantCulture } - ); - - await requestContext.SendResult(jsonOutput); + List templateList = provider.GenerateProvisioningScript(recommendations); + List armTemplates = new List(); + foreach (SqlArmTemplate template in templateList) + { + string jsonOutput = JsonConvert.SerializeObject( + template, + Formatting.Indented, + new JsonSerializerSettings { NullValueHandling = NullValueHandling.Ignore, Culture = CultureInfo.InvariantCulture } + ); + armTemplates.Add(jsonOutput); + } + await requestContext.SendResult(armTemplates); } catch (Exception e) { diff --git a/test/Microsoft.SqlTools.Migration.IntegrationTests/Migration/MigrationServiceTests.cs b/test/Microsoft.SqlTools.Migration.IntegrationTests/Migration/MigrationServiceTests.cs index 149ad39906..b76f131319 100644 --- a/test/Microsoft.SqlTools.Migration.IntegrationTests/Migration/MigrationServiceTests.cs +++ b/test/Microsoft.SqlTools.Migration.IntegrationTests/Migration/MigrationServiceTests.cs @@ -22,6 +22,8 @@ using Assert_ = Microsoft.VisualStudio.TestTools.UnitTesting.Assert; using Microsoft.SqlServer.Migration.SkuRecommendation.TargetProvisioning.Contracts; using Microsoft.SqlServer.Migration.TargetProvisioning; +using Microsoft.SqlServer.Migration.SqlTargetProvisioning.Constants; + namespace Microsoft.SqlTools.Migration.IntegrationTests.Migration { @@ -163,13 +165,59 @@ public void GenerateProvisioningScript_DatabaseRecommendation_ReturnsDBArmTempla var result = provisioningScriptserviceProvider.GenerateProvisioningScript(recs); // Assert - Assert_.IsInstanceOfType(result, typeof(SqlArmTemplate)); - Assert.AreEqual(result.resources.Count, 2); - Assert.AreEqual(result.resources.Where(res => res.type == "Microsoft.Sql/servers").Count(), 1); - Assert.AreEqual(result.resources.Where(res => res.type == "Microsoft.Sql/servers/databases").Count(), 1); - Assert.AreEqual(result.parameters.Where(par => par.Key == "Server collation").FirstOrDefault().Value.defaultValue, "Latin1_General_CI_AI"); - Assert.AreEqual(result.parameters.Where(par => par.Key == "Collation for database testdb").FirstOrDefault().Value.defaultValue, "Latin1_General_CI_AI"); - Assert.AreEqual(result.parameters.Count, 9); + Assert_.IsInstanceOfType(result[0], typeof(SqlArmTemplate)); + Assert.AreEqual(result[0].resources.Count, 2); + Assert.AreEqual(result[0].resources.Where(res => res.type == "Microsoft.Sql/servers").Count(), 1); + Assert.AreEqual(result[0].resources.Where(res => res.type == "Microsoft.Sql/servers/databases").Count(), 1); + Assert.AreEqual(result[0].parameters.Where(par => par.Key == "Server collation").FirstOrDefault().Value.defaultValue, "Latin1_General_CI_AI"); + Assert.AreEqual(result[0].parameters.Where(par => par.Key == "Collation for database testdb").FirstOrDefault().Value.defaultValue, "Latin1_General_CI_AI"); + Assert.AreEqual(result[0].parameters.Count, 9); + } + + [Test] + public void GenerateProvisioningScript_DatabaseRecommendationLargeNumber_ReturnsDBArmTemplateList() + { + // Arrange + List recs = new List(); + for (int i = 0; i < 125; i++) + { + var rec = new SkuRecommendationResult + { + SqlInstanceName = "TestServer", + DatabaseName = "TestDb" + i, + ServerCollation = serverLevelCollation, + DatabaseCollation = databaseLevelCollations["TestDb"], + TargetSku = new AzureSqlPaaSSku( + new AzureSqlSkuPaaSCategory( + AzureSqlTargetPlatform.AzureSqlDatabase, + AzureSqlPurchasingModel.vCore, + AzureSqlPaaSServiceTier.GeneralPurpose, + ComputeTier.Provisioned, + AzureSqlPaaSHardwareType.Gen5), + 2, + i) + { + } + }; + + recs.Add(rec); + } + + // Act + var result = provisioningScriptserviceProvider.GenerateProvisioningScript(recs); + + // Assert + Assert_.AreEqual(result.Count, 1 + recs.Count / ArmConstants.AzureSqlDbProvisioningBatchSize); + foreach (var res in result) + { + Assert_.IsInstanceOfType(res, typeof(SqlArmTemplate)); + Assert.IsTrue(res.resources.Count <= 51); + Assert.AreEqual(res.resources.Where(res => res.type == "Microsoft.Sql/servers").Count(), 1); + Assert.IsTrue(res.resources.Where(res => res.type == "Microsoft.Sql/servers/databases").Count() <= 50); + Assert.AreEqual(res.parameters.Where(par => par.Key == "Server collation").FirstOrDefault().Value.defaultValue, "Latin1_General_CI_AI"); + Assert.AreEqual(res.parameters.Where(par => par.Key.StartsWith("Collation for database testdb")).FirstOrDefault().Value.defaultValue, "Latin1_General_CI_AI"); + Assert.IsTrue(res.parameters.Count <= 256); + } } [Test] @@ -200,14 +248,14 @@ public void GenerateProvisioningScript_ManagedInstanceRecommendation_ReturnsMIAr var result = provisioningScriptserviceProvider.GenerateProvisioningScript(recs); // Assert - Assert_.IsInstanceOfType(result, typeof(SqlArmTemplate)); - Assert.AreEqual(result.resources.Count, 4); - Assert.AreEqual(result.resources.Where(res => res.type == "Microsoft.Network/networkSecurityGroups").Count(), 1); - Assert.AreEqual(result.resources.Where(res => res.type == "Microsoft.Network/routeTables").Count(), 1); - Assert.AreEqual(result.resources.Where(res => res.type == "Microsoft.Network/virtualNetworks").Count(), 1); - Assert.AreEqual(result.resources.Where(res => res.type == "Microsoft.Sql/managedInstances").Count(), 1); - Assert.AreEqual(result.parameters.Where(par => par.Key == "Server collation").FirstOrDefault().Value.defaultValue, "Latin1_General_CI_AI"); - Assert.AreEqual(result.parameters.Count, 11); + Assert_.IsInstanceOfType(result[0], typeof(SqlArmTemplate)); + Assert.AreEqual(result[0].resources.Count, 4); + Assert.AreEqual(result[0].resources.Where(res => res.type == "Microsoft.Network/networkSecurityGroups").Count(), 1); + Assert.AreEqual(result[0].resources.Where(res => res.type == "Microsoft.Network/routeTables").Count(), 1); + Assert.AreEqual(result[0].resources.Where(res => res.type == "Microsoft.Network/virtualNetworks").Count(), 1); + Assert.AreEqual(result[0].resources.Where(res => res.type == "Microsoft.Sql/managedInstances").Count(), 1); + Assert.AreEqual(result[0].parameters.Where(par => par.Key == "Server collation").FirstOrDefault().Value.defaultValue, "Latin1_General_CI_AI"); + Assert.AreEqual(result[0].parameters.Count, 11); } [Test] @@ -268,16 +316,16 @@ public void GenerateProvisioningScript_VirtualMachineRecommendation_ReturnsVMArm var result = provisioningScriptserviceProvider.GenerateProvisioningScript(recs); // Assert - Assert_.IsInstanceOfType(result, typeof(SqlArmTemplate)); - Assert.AreEqual(result.resources.Count, 6); - Assert.AreEqual(result.resources.Where(res => res.type == "Microsoft.Network/networkSecurityGroups").Count(), 1); - Assert.AreEqual(result.resources.Where(res => res.type == "Microsoft.SqlVirtualMachine/SqlVirtualMachines").Count(), 1); - Assert.AreEqual(result.resources.Where(res => res.type == "Microsoft.Network/virtualNetworks").Count(), 1); - Assert.AreEqual(result.resources.Where(res => res.type == "Microsoft.Compute/virtualMachines").Count(), 1); - Assert.AreEqual(result.resources.Where(res => res.type == "Microsoft.Network/publicIpAddresses").Count(), 1); - Assert.AreEqual(result.resources.Where(res => res.type == "Microsoft.Network/networkInterfaces").Count(), 1); - Assert.AreEqual(result.parameters.Where(par => par.Key == "Server collation").FirstOrDefault().Value.defaultValue, "Latin1_General_CI_AI"); - Assert.AreEqual(result.parameters.Count, 14); + Assert_.IsInstanceOfType(result[0], typeof(SqlArmTemplate)); + Assert.AreEqual(result[0].resources.Count, 7); + Assert.AreEqual(result[0].resources.Where(res => res.type == "Microsoft.Network/networkSecurityGroups").Count(), 1); + Assert.AreEqual(result[0].resources.Where(res => res.type == "Microsoft.SqlVirtualMachine/SqlVirtualMachines").Count(), 1); + Assert.AreEqual(result[0].resources.Where(res => res.type == "Microsoft.Network/virtualNetworks").Count(), 1); + Assert.AreEqual(result[0].resources.Where(res => res.type == "Microsoft.Compute/virtualMachines").Count(), 1); + Assert.AreEqual(result[0].resources.Where(res => res.type == "Microsoft.Network/publicIpAddresses").Count(), 1); + Assert.AreEqual(result[0].resources.Where(res => res.type == "Microsoft.Network/networkInterfaces").Count(), 1); + Assert.AreEqual(result[0].parameters.Where(par => par.Key == "Server collation").FirstOrDefault().Value.defaultValue, "Latin1_General_CI_AI"); + Assert.AreEqual(result[0].parameters.Count, 21); // Add additional assertions based on the expected outcome for the virtual machine case } @@ -309,6 +357,48 @@ public void GenerateProvisioningScript_InvalidTargetPlatform_ThrowsArgumentExcep Microsoft.VisualStudio.TestTools.UnitTesting.Assert.ThrowsException(() => provisioningScriptserviceProvider.GenerateProvisioningScript(recs)); } + [Test] + public void GenerateProvisioningScript_ManagedInstanceRecommendation_NextGenGP_ReturnsMIArmTemplate() + { + // Arrange + var recs = new List + { + new SkuRecommendationResult + { + SqlInstanceName = "TestServer", + ServerCollation = serverLevelCollation, + TargetSku = new AzureSqlPaaSSku( + new AzureSqlSkuPaaSCategory( + AzureSqlTargetPlatform.AzureSqlManagedInstance, + AzureSqlPurchasingModel.vCore, + AzureSqlPaaSServiceTier.NextGenGeneralPurpose, + ComputeTier.Provisioned, + AzureSqlPaaSHardwareType.Gen5), + 4, + 32, + maxStorageIops: 300) + { + } + } + }; + + // Act + var result = provisioningScriptserviceProvider.GenerateProvisioningScript(recs); + + // Assert + Assert_.IsInstanceOfType(result[0], typeof(SqlArmTemplate)); + Assert.AreEqual(result[0].resources.Count, 4); + Assert.AreEqual(result[0].resources.Where(res => res.type == "Microsoft.Network/networkSecurityGroups").Count(), 1); + Assert.AreEqual(result[0].resources.Where(res => res.type == "Microsoft.Network/routeTables").Count(), 1); + Assert.AreEqual(result[0].resources.Where(res => res.type == "Microsoft.Network/virtualNetworks").Count(), 1); + Assert.AreEqual(result[0].resources.Where(res => res.type == "Microsoft.Sql/managedInstances").Count(), 1); + Assert.AreEqual(result[0].parameters.Where(par => par.Key == "Server collation").FirstOrDefault().Value.defaultValue, "Latin1_General_CI_AI"); + Assert.AreEqual(result[0].parameters.Where(par => par.Key == "Server collation").FirstOrDefault().Value.defaultValue, "Latin1_General_CI_AI"); + Assert.AreEqual(result[0].parameters.Count, 12); + int maxStorageIOPS = result[0].parameters.Where(par => par.Key == "Max storage IOPS").Select(par => Convert.ToInt32(par.Value.defaultValue)).FirstOrDefault(); + Assert.AreEqual(maxStorageIOPS, 300); + } + } } \ No newline at end of file