Skip to content

Commit

Permalink
Merge pull request #1619 from naveedaz/dev
Browse files Browse the repository at this point in the history
Add functionality to clone all deployment slots for a source web app
  • Loading branch information
Hovsep committed Jan 11, 2016
2 parents bd8cecb + aa025bb commit 95c6d74
Show file tree
Hide file tree
Showing 11 changed files with 11,396 additions and 14 deletions.
3 changes: 2 additions & 1 deletion ChangeLog.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,8 @@
* New-AzureRmWebAppSSLBinding
* Get-AzureRmWebAppSSLBinding
* Remove-AzureRmWebAppSSLBinding
* Azure Websites: Added AseName and AseResourceGroupName parameters in New-AzureRmWebApp and New-AzureRmAppServicePlan cmdlet
* Added AseName and AseResourceGroupName parameters in New-AzureRmWebApp and New-AzureRmAppServicePlan cmdlet
* Added support for cloning all deployment slots associated with source website
* Azure Stream Analytics: Added new cmdlet support for Functions.
* New-AzureRmStreamAnalyticsFunction
* Get-AzureRmStreamAnalyticsFunction
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -207,7 +207,7 @@ private void WriteError(string error)
}
}

private DeploymentExtended ProvisionDeploymentStatus(string resourceGroup, string deploymentName, Deployment deployment)
public DeploymentExtended ProvisionDeploymentStatus(string resourceGroup, string deploymentName, Deployment deployment)
{
operations = new List<DeploymentOperation>();

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -231,6 +231,9 @@
<None Include="SessionRecords\Microsoft.Azure.Commands.Websites.Test.ScenarioTests.WebAppTests\TestCloneNewWebApp.json">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Include="SessionRecords\Microsoft.Azure.Commands.Websites.Test.ScenarioTests.WebAppTests\TestCloneNewWebAppAndDeploymentSlots.json">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Include="SessionRecords\Microsoft.Azure.Commands.Websites.Test.ScenarioTests.WebAppTests\TestCloneNewWebAppWithNewTrafficManager.json">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,13 @@ public void TestCloneNewWebApp()
WebsitesController.NewInstance.RunPsTest("Test-CloneNewWebApp");
}

[Fact]
[Trait(Category.AcceptanceType, Category.CheckIn)]
public void TestCloneNewWebAppAndDeploymentSlots()
{
WebsitesController.NewInstance.RunPsTest("Test-CloneNewWebAppAndDeploymentSlots");
}

[Fact]
[Trait(Category.AcceptanceType, Category.CheckIn)]
public void TestCloneNewWebAppWithNewTrafficManager()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -297,6 +297,112 @@ function Test-CloneNewWebApp
}
}

<#
.SYNOPSIS
Tests clone a website.
#>
function Test-CloneNewWebAppAndDeploymentSlots
{
# Setup
$rgname = Get-ResourceGroupName
$appname = Get-WebsiteName
$slot1name = "staging"
$slot2name = "testing"
$location = Get-Location
$planName = Get-WebHostPlanName
$tier = "Premium"
$apiversion = "2015-08-01"
$resourceType = "Microsoft.Web/sites"

# Destination setup
$destPlanName = Get-WebHostPlanName
$destLocation = Get-SecondaryLocation
$destAppName = Get-WebsiteName

try
{
#Setup
New-AzureRmResourceGroup -Name $rgname -Location $location
$serverFarm = New-AzureRmAppServicePlan -ResourceGroupName $rgname -Name $planName -Location $location -Tier $tier

# Create new web app
$webapp = New-AzureRmWebApp -ResourceGroupName $rgname -Name $appname -Location $location -AppServicePlan $planName

# Assert
Assert-AreEqual $appname $webapp.Name
Assert-AreEqual $serverFarm.Id $webapp.ServerFarmId

# Get new web app
$webapp = Get-AzureRmWebApp -ResourceGroupName $rgname -Name $appname

# Assert
Assert-AreEqual $appname $webapp.Name
Assert-AreEqual $serverFarm.Id $webapp.ServerFarmId

# Create deployment slot 1
$slot1 = New-AzureRmWebAppSlot -ResourceGroupName $rgname -Name $appname -Slot $slot1name -AppServicePlan $planName
$appWithSlotName = "$appname/$slot1name"

# Assert
Assert-AreEqual $appWithSlotName $slot1.Name
Assert-AreEqual $serverFarm.Id $slot1.ServerFarmId

# Create deployment slot 2
$slot2 = New-AzureRmWebAppSlot -ResourceGroupName $rgname -Name $appname -Slot $slot2name -AppServicePlan $planName
$appWithSlotName = "$appname/$slot2name"

# Assert
Assert-AreEqual $appWithSlotName $slot2.Name
Assert-AreEqual $serverFarm.Id $slot2.ServerFarmId

# Create new server Farm
$serverFarm2 = New-AzureRmAppServicePlan -ResourceGroupName $rgname -Name $destPlanName -Location $destLocation -Tier $tier

# Clone web app
$webapp2 = New-AzureRmWebApp -ResourceGroupName $rgname -Name $destAppName -Location $destLocation -AppServicePlan $destPlanName -SourceWebApp $webapp -IncludeSourceWebAppSlots

# Assert
Assert-AreEqual $destAppName $webapp2.Name

# Get new web app
$webapp2 = Get-AzureRmWebApp -ResourceGroupName $rgname -Name $destAppName

# Assert
Assert-AreEqual $destAppName $webapp2.Name

# Get new web app slot1
$slot1 = Get-AzureRmWebAppSlot -ResourceGroupName $rgname -Name $destAppName -Slot $slot1name

$appWithSlotName = "$destAppName/$slot1name"

# Assert
Assert-AreEqual $appWithSlotName $slot1.Name
Assert-AreEqual $serverFarm2.Id $slot1.ServerFarmId

# Get new web app slot1
$slot2 = Get-AzureRmWebAppSlot -ResourceGroupName $rgname -Name $destAppName -Slot $slot2name
$appWithSlotName = "$destAppName/$slot2name"

# Assert
Assert-AreEqual $appWithSlotName $slot2.Name
Assert-AreEqual $serverFarm2.Id $slot2.ServerFarmId
}
finally
{
# Cleanup
Remove-AzureRmWebAppSlot -ResourceGroupName $rgname -Name $appname -Slot $slot1name -Force
Remove-AzureRmWebAppSlot -ResourceGroupName $rgname -Name $appname -Slot $slot2name -Force
Remove-AzureRmWebApp -ResourceGroupName $rgname -Name $appname -Force
Remove-AzureRmAppServicePlan -ResourceGroupName $rgname -Name $planName -Force

Remove-AzureRmWebAppSlot -ResourceGroupName $rgname -Name $destAppName -Slot $slot1name -Force
Remove-AzureRmWebAppSlot -ResourceGroupName $rgname -Name $destAppName -Slot $slot2name -Force
Remove-AzureRmWebApp -ResourceGroupName $rgname -Name $destAppName -Force
Remove-AzureRmAppServicePlan -ResourceGroupName $rgname -Name $destPlanName -Force
Remove-AzureRmResourceGroup -Name $rgname -Force
}
}

<#
.SYNOPSIS
Tests clone a website.
Expand Down

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,12 @@
using System.Collections.Generic;
using System.Linq;
using System.Management.Automation;
using Microsoft.Azure.Commands.Resources.Models;
using Microsoft.Azure.Commands.WebApps.Models;
using Microsoft.Azure.Commands.WebApps.Models.WebApp;
using Microsoft.Azure.Commands.WebApps.Utilities;
using Microsoft.Azure.Management.Resources;
using Microsoft.Azure.Management.Resources.Models;
using Microsoft.Azure.Management.WebSites.Models;
using Microsoft.PowerShell;
using Microsoft.WindowsAzure.Commands.Common;
Expand Down Expand Up @@ -80,6 +85,10 @@ public class NewAzureWebAppCmdlet : WebAppBaseClientCmdLet
[ValidateNotNullOrEmpty]
public string AseResourceGroupName { get; set; }

[Parameter(Position = 10, Mandatory = false, HelpMessage = "Clones slots associated with source web app")]
[ValidateNotNullOrEmpty]
public SwitchParameter IncludeSourceWebAppSlots { get; set; }

public override void ExecuteCmdlet()
{
CloningInfo cloningInfo = null;
Expand All @@ -97,7 +106,56 @@ public override void ExecuteCmdlet()
};
}

var cloneWebAppSlots = false;
string[] slotNames = null;
string srcResourceGroupName = null;
string srcwebAppName = null;
string srcSlotName = null;
if (IncludeSourceWebAppSlots.IsPresent)
{
CmdletHelpers.TryParseWebAppMetadataFromResourceId(SourceWebApp.Id, out srcResourceGroupName,
out srcwebAppName, out srcSlotName);
var slots = WebsitesClient.ListWebApps(srcResourceGroupName, srcwebAppName);
if (slots != null && slots.Any())
{
slotNames = slots.Select(s => s.Name.Replace(srcwebAppName + "/", string.Empty)).ToArray();
cloneWebAppSlots = true;
}
}

if (cloneWebAppSlots)
{
WriteVerboseWithTimestamp("Cloning source web app '{0}' to destination web app {1}", srcwebAppName, Name);
}

WriteObject(WebsitesClient.CreateWebApp(ResourceGroupName, Name, null, Location, AppServicePlan, cloningInfo, AseName, AseResourceGroupName));

if (cloneWebAppSlots)
{
WriteVerboseWithTimestamp("Cloning all deployment slots of source web app '{0}' to destination web app {1}", srcwebAppName, Name);
CloneSlots(slotNames);
}
}

private void CloneSlots(string[] slotNames)
{
var hostingEnvironmentProfile = WebsitesClient.CreateHostingEnvironmentProfile(ResourceGroupName, AseResourceGroupName, AseName);
var template = DeploymentTemplateHelper.CreateSlotCloneDeploymentTemplate(Location, AppServicePlan, Name, SourceWebApp.Id,
slotNames, hostingEnvironmentProfile, WebsitesClient.WrappedWebsitesClient.ApiVersion);

var deployment = new Management.Resources.Models.Deployment
{
Properties = new DeploymentProperties
{
Mode = DeploymentMode.Incremental,
Template = template
}
};

var deploymentName = string.Format("CloneSlotsFor{0}", Name);
ResourcesClient.ResourceManagementClient.Deployments.CreateOrUpdate(ResourceGroupName, deploymentName, deployment);
var result = ResourcesClient.ProvisionDeploymentStatus(ResourceGroupName, deploymentName, deployment);
WriteObject(result.ToPSResourceGroupDeployment(ResourceGroupName));
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -172,6 +172,7 @@
<Compile Include="Cmdlets\AppServicePlans\NewAzureAppServicePlan.cs" />
<Compile Include="Cmdlets\AppServicePlans\RemoveAppServicePlan.cs" />
<Compile Include="Models.WebApp\AppServicePlanBaseCmdlet.cs" />
<Compile Include="Models.WebApp\DeploymentTemplate.cs" />
<Compile Include="Models.WebApp\WebAppBaseCmdlet.cs" />
<Compile Include="Models.WebApp\WebAppBaseClient.cs" />
<Compile Include="Models.WebApp\WebAppSlotBaseCmdlet.cs" />
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,133 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Microsoft.Azure.Management.WebSites.Models;
using Newtonsoft.Json;
using Newtonsoft.Json.Schema;
using Newtonsoft.Json.Serialization;

namespace Microsoft.Azure.Commands.WebApps.Models.WebApp
{
internal class DeploymentTemplate
{
[JsonProperty("$schema")]
public string Schema { get; set; }

public string ContentVersion { get; set; }

public Dictionary<string, ParameterType> Parameters { get; set; }

public Dictionary<string, object> Variables { get; set; }

public WebAppResource[] Resources { get; set; }

}

internal class ParameterType
{
public string Type { get; set; }

public string AllowedValues { get; set; }
}

internal class WebAppResource
{
public string Name { get; set; }

public string ApiVersion { get; set; }

public string Type { get; set; }

public string Location { get; set; }

public string[] DependsOn { get; set; }

public CopyFunction Copy { get; set; }

public WebAppProperties Properties { get; set; }
}

internal class CopyFunction
{
public string Name { get; set; }

public string Count { get; set; }
}

internal class WebAppProperties
{
public string ServerFarmId { get; set; }

public CloningInfo CloningInfo { get; set; }

public HostingEnvironmentProfile HostingEnvironmentProfile { get; set; }
}


internal static class DeploymentTemplateHelper
{
private const string WebAppSlotName = "[concat(variables('webAppName'), '/', variables('slotNames')[copyIndex()])]";
private const string SourceWebAppSlotId = "[concat(variables('sourceWebAppId'), '/slots/', variables('slotNames')[copyIndex()])]";
private const string WebAppSlotResourceType = "Microsoft.Web/sites/slots";
private const string WebAppSlotCount = "[length(variables('slotNames'))]";
private const string ContentVersion = "1.0.0.0";

internal static string ToJsonString(this DeploymentTemplate template)
{
var serializationSettings = new JsonSerializerSettings
{
NullValueHandling = NullValueHandling.Ignore,
ContractResolver = new CamelCasePropertyNamesContractResolver()
};

var serializer = JsonSerializer.Create(serializationSettings);
var textWriter = new StringWriter();
serializer.Serialize(textWriter, template);
return textWriter.ToString();
}

internal static string CreateSlotCloneDeploymentTemplate(string location, string serverFarmId, string destinationWebAppName, string sourceWebAppId, string[] slotNames, HostingEnvironmentProfile hostingProfile, string apiVersion)
{
var template = new DeploymentTemplate
{
ContentVersion = ContentVersion,
Schema = "http://schema.management.azure.com/schemas/2015-01-01/deploymentTemplate.json#",
Variables = new Dictionary<string, object>
{
{ "slotNames", slotNames },
{ "webAppName", destinationWebAppName },
{ "sourceWebAppId", sourceWebAppId }
},
Resources = new WebAppResource[]
{
new WebAppResource
{
Type = WebAppSlotResourceType,
ApiVersion = apiVersion,
Location = location,
Name = WebAppSlotName,
Properties = new WebAppProperties
{
CloningInfo = new CloningInfo
{
SourceWebAppId = SourceWebAppSlotId
},
ServerFarmId = serverFarmId,
HostingEnvironmentProfile = hostingProfile
},
Copy = new CopyFunction
{
Name = "SlotCopy",
Count = WebAppSlotCount
}
}
}
};

return template.ToJsonString();
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,18 @@ internal static bool ShouldUseDeploymentSlot(string webSiteName, string slotName
return result;
}

internal static HostingEnvironmentProfile CreateHostingEnvironmentProfile(string subscriptionId, string resourceGroupName, string aseResourceGroupName, string aseName)
{
var rg = string.IsNullOrEmpty(aseResourceGroupName) ? resourceGroupName : aseResourceGroupName;
var aseResourceId = CmdletHelpers.GetApplicationServiceEnvironmentResourceId(subscriptionId, rg, aseName);
return new HostingEnvironmentProfile
{
Id = aseResourceId,
Type = CmdletHelpers.ApplicationServiceEnvironmentResourcesName,
Name = aseName
};
}

internal static string BuildMetricFilter(DateTime? startTime, DateTime? endTime, string timeGrain, IReadOnlyList<string> metricNames)
{
var dateTimeFormat = "yyyy-MM-ddTHH:mm:ssZ";
Expand Down
Loading

0 comments on commit 95c6d74

Please sign in to comment.