diff --git a/source/Calamari.AzureAppService.Tests/AppServiceBehaviourFixture.cs b/source/Calamari.AzureAppService.Tests/AppServiceBehaviourFixture.cs index 3607c295c..8ad65cf69 100644 --- a/source/Calamari.AzureAppService.Tests/AppServiceBehaviourFixture.cs +++ b/source/Calamari.AzureAppService.Tests/AppServiceBehaviourFixture.cs @@ -5,14 +5,12 @@ using System.Linq; using System.Reflection; using System.Text; -using System.Threading; using System.Threading.Tasks; using System.Xml; using System.Xml.Linq; using Azure; using Azure.ResourceManager.AppService; using Azure.ResourceManager.AppService.Models; -using Azure.ResourceManager.Resources; using Azure.ResourceManager.Storage; using Azure.ResourceManager.Storage.Models; using Calamari.AzureAppService.Azure; @@ -32,14 +30,11 @@ public class AppServiceBehaviorFixture [TestFixture] public class WhenUsingAWindowsDotNetAppService : AppServiceIntegrationTest { - private AppServicePlanResource appServicePlanResource; - - protected override async Task ConfigureTestResources(ResourceGroupResource resourceGroup) + public override async Task SetUp() { - var (appServicePlan, webSite) = await CreateAppServicePlanAndWebApp(resourceGroup); - - appServicePlanResource = appServicePlan; - WebSiteResource = webSite; + await base.SetUp(); + + WebSiteResource = await CreateWebApp(WindowsAppServicePlanResource); } [Test] @@ -55,7 +50,7 @@ await CommandTestBuilder.CreateAsync() }) .Execute(); - await AssertContent(WebSiteResource.Data.DefaultHostName, $"Hello {greeting}"); + await AssertContent(WebSiteResource.Data.DefaultHostName, $"Hello {Greeting}"); } [Test] @@ -72,7 +67,7 @@ await CommandTestBuilder.CreateAsync() }) .Execute(); - await AssertContent(WebSiteResource.Data.DefaultHostName, $"Hello {greeting}"); + await AssertContent(WebSiteResource.Data.DefaultHostName, $"Hello {Greeting}"); } [Test] @@ -95,14 +90,14 @@ await CommandTestBuilder.CreateAsync() }) .Execute(); - await AssertContent(WebSiteResource.Data.DefaultHostName, $"Hello {greeting}"); + await AssertContent(WebSiteResource.Data.DefaultHostName, $"Hello {Greeting}"); } [Test] public async Task CanDeployWebAppZip_ToDeploymentSlot() { const string slotName = "stage"; - greeting = "stage"; + Greeting = "stage"; (string packagePath, string packageName, string packageVersion) packageinfo; @@ -130,7 +125,7 @@ await CommandTestBuilder.CreateAsync() }) .Execute(); - await AssertContent(slotResource.Data.DefaultHostName, $"Hello {greeting}"); + await AssertContent(slotResource.Data.DefaultHostName, $"Hello {Greeting}"); } [Test] @@ -147,7 +142,7 @@ await CommandTestBuilder.CreateAsync() .Execute(); //await new AzureAppServiceBehaviour(new InMemoryLog()).Execute(runningContext); - await AssertContent(WebSiteResource.Data.DefaultHostName, $"Hello {greeting}"); + await AssertContent(WebSiteResource.Data.DefaultHostName, $"Hello {Greeting}"); } [Test] @@ -171,7 +166,7 @@ await CommandTestBuilder.CreateAsync() .Execute(); //await new AzureAppServiceBehaviour(new InMemoryLog()).Execute(runningContext); - await AssertContent(WebSiteResource.Data.DefaultHostName, $"Hello {greeting}"); + await AssertContent(WebSiteResource.Data.DefaultHostName, $"Hello {Greeting}"); } [Test] @@ -181,10 +176,10 @@ public async Task CanDeployWarPackage() // Need java installed on the test runner (MJH 2022-05-06: is this actually true? I don't see why we'd need java on the test runner) var javaSite = await ResourceGroupResource.GetWebSites() .CreateOrUpdateAsync(WaitUntil.Completed, - $"{ResourceGroupName}-java", + $"{WebSiteResource.Data.Name}-java", new WebSiteData(ResourceGroupResource.Data.Location) { - AppServicePlanId = appServicePlanResource.Data.Id, + AppServicePlanId = WindowsAppServicePlanResource.Data.Id, SiteConfig = new SiteConfigProperties { JavaVersion = "1.8", @@ -203,7 +198,7 @@ public async Task CanDeployWarPackage() packageinfo.packagePath = Path.Combine(assemblyFileInfo.Directory.FullName, "sample.1.0.0.war"); packageinfo.packageVersion = "1.0.0"; packageinfo.packageName = "sample"; - greeting = "java"; + Greeting = "java"; await CommandTestBuilder.CreateAsync() .WithArrange(context => @@ -214,11 +209,11 @@ await CommandTestBuilder.CreateAsync() context.Variables[PackageVariables.SubstituteInFilesTargets] = "test.jsp"; }) .Execute(); - + await DoWithRetries(3, async () => { - await AssertContent(javaSite.Value.Data.DefaultHostName, $"Hello! {greeting}", "test.jsp"); + await AssertContent(javaSite.Value.Data.DefaultHostName, $"Hello! {Greeting}", "test.jsp"); }, secondsBetweenRetries: 10); } @@ -289,11 +284,10 @@ private static (string packagePath, string packageName, string packageVersion) P return packageinfo; } - async Task<(string packagePath, string packageName, string packageVersion)> PrepareNugetPackage() { (string packagePath, string packageName, string packageVersion) packageinfo; - greeting = "nuget"; + Greeting = "nuget"; var tempPath = TemporaryDirectory.Create(); new DirectoryInfo(tempPath.DirectoryPath).CreateSubdirectory("AzureZipDeployPackage"); @@ -327,7 +321,6 @@ await Task.Run(() => File.WriteAllText( return packageinfo; } - private static (string packagePath, string packageName, string packageVersion) PrepareFunctionAppZipPackage() { (string packagePath, string packageName, string packageVersion) packageInfo; @@ -345,35 +338,30 @@ private static (string packagePath, string packageName, string packageVersion) P private void AddVariables(CommandTestBuilderContext context) { AddAzureVariables(context); - context.Variables.Add("Greeting", greeting); + context.Variables.Add("Greeting", Greeting); context.Variables.Add(KnownVariables.Package.EnabledFeatures, KnownVariables.Features.SubstituteInFiles); context.Variables.Add(PackageVariables.SubstituteInFilesTargets, "index.html"); context.Variables.Add(SpecialVariables.Action.Azure.DeploymentType, "ZipDeploy"); - + var settings = BuildAppSettingsJson(new[] { ("WEBSITES_CONTAINER_START_TIME_LIMIT", "460", false), ("WEBSITE_SCM_ALWAYS_ON_ENABLED", "true", false) }); - - context.Variables[SpecialVariables.Action.Azure.AppSettings] = settings.json; - context.Variables[SpecialVariables.Action.Azure.AsyncZipDeploymentTimeout] = "3"; + + context.Variables[SpecialVariables.Action.Azure.AppSettings] = settings.json; + context.Variables[SpecialVariables.Action.Azure.AsyncZipDeploymentTimeout] = "3"; } } [TestFixture] public class WhenUsingALinuxAppService : AppServiceIntegrationTest - { - // For some reason we are having issues creating these linux resources on Standard in EastUS - protected override string DefaultResourceGroupLocation => RandomAzureRegion.GetRandomRegionWithExclusions("eastus"); - - static readonly CancellationTokenSource CancellationTokenSource = new CancellationTokenSource(); - readonly CancellationToken cancellationToken = CancellationTokenSource.Token; - - protected override async Task ConfigureTestResources(ResourceGroupResource resourceGroup) + { + public override async Task SetUp() { + await base.SetUp(); + var storageAccountName = ResourceGroupName.Replace("-", "").Substring(0, 20); - var storageAccountResponse = await ResourceGroupResource .GetStorageAccounts() .CreateOrUpdateAsync(WaitUntil.Completed, @@ -384,54 +372,33 @@ protected override async Task ConfigureTestResources(ResourceGroupResource resou ResourceGroupResource.Data.Location) ); - var keys = await storageAccountResponse - .Value + var storageAccountResource = storageAccountResponse.Value; + + var keys = await storageAccountResource .GetKeysAsync() .ToListAsync(); - var linuxAppServicePlan = await resourceGroup.GetAppServicePlans() - .CreateOrUpdateAsync(WaitUntil.Completed, - $"{resourceGroup.Data.Name}-linux-asp", - new AppServicePlanData(resourceGroup.Data.Location) - { - Sku = new AppServiceSkuDescription - { - Name = "P1V3", - Tier = "PremiumV3" - }, - Kind = "linux", - IsReserved = true - }); - - await linuxAppServicePlan.WaitForCompletionAsync(cancellationToken); - - var linuxWebSiteResponse = await resourceGroup.GetWebSites() - .CreateOrUpdateAsync(WaitUntil.Completed, - $"{resourceGroup.Data.Name}-linux", - new WebSiteData(resourceGroup.Data.Location) - { - AppServicePlanId = linuxAppServicePlan.Value.Id, - Kind = "functionapp,linux", - IsReserved = true, - SiteConfig = new SiteConfigProperties - { - IsAlwaysOn = true, - LinuxFxVersion = "DOTNET|6.0", - Use32BitWorkerProcess = true, - AppSettings = new List - { - new AppServiceNameValuePair { Name = "FUNCTIONS_WORKER_RUNTIME", Value = "dotnet" }, - new AppServiceNameValuePair { Name = "FUNCTIONS_EXTENSION_VERSION", Value = "~4" }, - new AppServiceNameValuePair { Name = "AzureWebJobsStorage", Value = $"DefaultEndpointsProtocol=https;AccountName={storageAccountName};AccountKey={keys.First().Value};EndpointSuffix=core.windows.net" }, - new AppServiceNameValuePair { Name = "WEBSITES_CONTAINER_START_TIME_LIMIT", Value = "460" }, - new AppServiceNameValuePair { Name = "WEBSITE_SCM_ALWAYS_ON_ENABLED", Value = "true"} - } - } - }); - - await linuxWebSiteResponse.WaitForCompletionAsync(cancellationToken); - - WebSiteResource = linuxWebSiteResponse.Value; + WebSiteResource = await CreateWebApp(LinuxAppServicePlanResource, + new WebSiteData(ResourceGroupResource.Data.Location) + { + AppServicePlanId = LinuxAppServicePlanResource.Id, + Kind = "functionapp,linux", + IsReserved = true, + SiteConfig = new SiteConfigProperties + { + IsAlwaysOn = true, + LinuxFxVersion = "DOTNET|6.0", + Use32BitWorkerProcess = true, + AppSettings = new List + { + new AppServiceNameValuePair { Name = "FUNCTIONS_WORKER_RUNTIME", Value = "dotnet" }, + new AppServiceNameValuePair { Name = "FUNCTIONS_EXTENSION_VERSION", Value = "~4" }, + new AppServiceNameValuePair { Name = "AzureWebJobsStorage", Value = $"DefaultEndpointsProtocol=https;AccountName={storageAccountName};AccountKey={keys.First().Value};EndpointSuffix=core.windows.net" }, + new AppServiceNameValuePair { Name = "WEBSITES_CONTAINER_START_TIME_LIMIT", Value = "460" }, + new AppServiceNameValuePair { Name = "WEBSITE_SCM_ALWAYS_ON_ENABLED", Value = "true" } + } + } + }); } [Test] @@ -454,8 +421,8 @@ await DoWithRetries(2, async () => { await AssertContent(WebSiteResource.Data.DefaultHostName, - rootPath: $"api/HttpExample?name={greeting}", - actualText: $"Hello, {greeting}"); + rootPath: $"api/HttpExample?name={Greeting}", + actualText: $"Hello, {Greeting}"); }, secondsBetweenRetries: 10); } @@ -484,13 +451,12 @@ await DoWithRetries(2, async () => { await AssertContent(WebSiteResource.Data.DefaultHostName, - rootPath: $"api/HttpExample?name={greeting}", - actualText: $"Hello, {greeting}"); + rootPath: $"api/HttpExample?name={Greeting}", + actualText: $"Hello, {Greeting}"); }, secondsBetweenRetries: 10); } - [Test] public async Task CanDeployZip_ToLinuxFunctionApp_WithAsyncDeploymentAndPolling() { @@ -518,8 +484,8 @@ await DoWithRetries(2, async () => { await AssertContent(WebSiteResource.Data.DefaultHostName, - rootPath: $"api/HttpExample?name={greeting}", - actualText: $"Hello, {greeting}"); + rootPath: $"api/HttpExample?name={Greeting}", + actualText: $"Hello, {Greeting}"); }, secondsBetweenRetries: 10); } @@ -549,14 +515,14 @@ private void AddVariables(CommandTestBuilderContext context) { AddAzureVariables(context); context.Variables.Add(SpecialVariables.Action.Azure.DeploymentType, "ZipDeploy"); - + var settings = BuildAppSettingsJson(new[] { ("WEBSITES_CONTAINER_START_TIME_LIMIT", "460", false), ("WEBSITE_SCM_ALWAYS_ON_ENABLED", "true", false) }); - - context.Variables[SpecialVariables.Action.Azure.AppSettings] = settings.json; + + context.Variables[SpecialVariables.Action.Azure.AppSettings] = settings.json; } } } diff --git a/source/Calamari.AzureAppService.Tests/AppServiceIntegrationTest.cs b/source/Calamari.AzureAppService.Tests/AppServiceIntegrationTest.cs index bcad94362..a06f98b07 100644 --- a/source/Calamari.AzureAppService.Tests/AppServiceIntegrationTest.cs +++ b/source/Calamari.AzureAppService.Tests/AppServiceIntegrationTest.cs @@ -1,7 +1,7 @@ using System; using System.Collections.Generic; +using System.Diagnostics; using System.Linq; -using System.Net; using System.Net.Http; using System.Threading; using System.Threading.Tasks; @@ -9,7 +9,6 @@ using Azure.Core; using Azure.ResourceManager; using Azure.ResourceManager.AppService; -using Azure.ResourceManager.AppService.Models; using Azure.ResourceManager.Resources; using Calamari.Azure; using Calamari.AzureAppService.Azure; @@ -30,22 +29,23 @@ public abstract class AppServiceIntegrationTest protected string ClientSecret { get; private set; } protected string TenantId { get; private set; } protected string SubscriptionId { get; private set; } - protected string ResourceGroupName { get; private set; } - protected string ResourceGroupLocation { get; private set; } - protected string greeting = "Calamari"; + protected string Greeting = "Calamari"; + protected ArmClient ArmClient { get; private set; } - - protected SubscriptionResource SubscriptionResource { get; private set; } protected ResourceGroupResource ResourceGroupResource { get; private set; } - protected WebSiteResource WebSiteResource { get; private protected set; } + protected string ResourceGroupName => ResourceGroupResource.Data.Name; + protected AppServicePlanResource LinuxAppServicePlanResource { get; private set; } - private readonly HttpClient client = new HttpClient(); + protected AppServicePlanResource WindowsAppServicePlanResource { get; private set; } + protected AppServicePlanResource WindowsContainerAppServicePlanResource { get; private set; } + protected WebSiteResource WebSiteResource { get; private protected set; } - protected virtual string DefaultResourceGroupLocation => RandomAzureRegion.GetRandomRegionWithExclusions(); + readonly HttpClient client = new HttpClient(); static readonly CancellationTokenSource CancellationTokenSource = new CancellationTokenSource(); - readonly CancellationToken cancellationToken = CancellationTokenSource.Token; + protected readonly CancellationToken CancellationToken = CancellationTokenSource.Token; + SubscriptionResource subscriptionResource; [OneTimeSetUp] public async Task Setup() @@ -55,15 +55,10 @@ public async Task Setup() var activeDirectoryEndpointBaseUri = Environment.GetEnvironmentVariable(AccountVariables.ActiveDirectoryEndPoint) ?? DefaultVariables.ActiveDirectoryEndpoint; - ResourceGroupName = $"{DateTime.UtcNow:yyyyMMdd}-{Guid.NewGuid():N}"; - - ClientId = await ExternalVariables.Get(ExternalVariable.AzureSubscriptionClientId, cancellationToken); - ClientSecret = await ExternalVariables.Get(ExternalVariable.AzureSubscriptionPassword, cancellationToken); - TenantId = await ExternalVariables.Get(ExternalVariable.AzureSubscriptionTenantId, cancellationToken); - SubscriptionId = await ExternalVariables.Get(ExternalVariable.AzureSubscriptionId, cancellationToken); - ResourceGroupLocation = Environment.GetEnvironmentVariable("AZURE_NEW_RESOURCE_REGION") ?? DefaultResourceGroupLocation; - - TestContext.Progress.WriteLine($"Resource group location: {ResourceGroupLocation}"); + ClientId = await ExternalVariables.Get(ExternalVariable.AzureSubscriptionClientId, CancellationToken); + ClientSecret = await ExternalVariables.Get(ExternalVariable.AzureSubscriptionPassword, CancellationToken); + TenantId = await ExternalVariables.Get(ExternalVariable.AzureSubscriptionTenantId, CancellationToken); + SubscriptionId = await ExternalVariables.Get(ExternalVariable.AzureSubscriptionId, CancellationToken); var servicePrincipalAccount = new AzureServicePrincipalAccount(SubscriptionId, ClientId, @@ -83,12 +78,28 @@ public async Task Setup() }); //create the resource group - SubscriptionResource = ArmClient.GetSubscriptionResource(SubscriptionResource.CreateResourceIdentifier(SubscriptionId)); - var response = await SubscriptionResource + subscriptionResource = ArmClient.GetSubscriptionResource(SubscriptionResource.CreateResourceIdentifier(SubscriptionId)); + ResourceGroupResource staticResourceGroupResource = await subscriptionResource.GetResourceGroupAsync("calamari-testing-static-rg", CancellationToken); + + WindowsAppServicePlanResource = await staticResourceGroupResource.GetAppServicePlanAsync("calamari-testing-static-win-plan", CancellationToken); + LinuxAppServicePlanResource = await staticResourceGroupResource.GetAppServicePlanAsync("calamari-testing-static-linux-plan", CancellationToken); + WindowsContainerAppServicePlanResource = await staticResourceGroupResource.GetAppServicePlanAsync("calamari-testing-static-container-win-plan", CancellationToken); + LinuxAppServicePlanResource = await staticResourceGroupResource.GetAppServicePlanAsync("calamari-testing-static-linux-plan", CancellationToken); + } + + [SetUp] + public virtual async Task SetUp() + { + var sw = Stopwatch.StartNew(); + var name = $"{DateTime.UtcNow:yyyyMMdd}-{Guid.NewGuid():N}"; + + TestContext.WriteLine($"Creating resource group '{name}'"); + + var response = await subscriptionResource .GetResourceGroups() .CreateOrUpdateAsync(WaitUntil.Completed, - ResourceGroupName, - new ResourceGroupData(new AzureLocation(ResourceGroupLocation)) + name, + new ResourceGroupData(AzureLocation.AustraliaEast) { Tags = { @@ -97,27 +108,33 @@ public async Task Setup() // We keep them for 14 days just in case we need to do debugging/investigation ["LifetimeInDays"] = "14" } - }); + }, + CancellationToken); ResourceGroupResource = response.Value; - await ConfigureTestResources(ResourceGroupResource); + sw.Stop(); + TestContext.WriteLine($"Created resource group '{name}' in {sw.Elapsed:g}"); } - protected abstract Task ConfigureTestResources(ResourceGroupResource resourceGroup); - - [OneTimeTearDown] - public virtual async Task Cleanup() + [TearDown] + public virtual async Task TearDown() { - await ArmClient.GetResourceGroupResource(ResourceGroupResource.CreateResourceIdentifier(SubscriptionId, ResourceGroupName)) - .DeleteAsync(WaitUntil.Started); + TestContext.WriteLine($"Deleting web app '{WebSiteResource.Data.Name}' (with no waiting)"); + + //we explicitly delete the website so we can set deleteEmptyServerFarm to be false (otherwise cleaning up the resource group _sometimes_ deletes the app service plan) + await RetryPolicies.TestsTransientArmOperationsErrorsPolicy.ExecuteAsync(async ct => await WebSiteResource.DeleteAsync(WaitUntil.Started, deleteEmptyServerFarm: false, cancellationToken: ct), CancellationToken); + + TestContext.WriteLine($"Deleting resource group '{ResourceGroupResource.Data.Name}' (with no waiting)"); + //delete the rest of the resources + await RetryPolicies.TestsTransientArmOperationsErrorsPolicy.ExecuteAsync(async ct => await ResourceGroupResource.DeleteAsync(WaitUntil.Started, cancellationToken: ct), CancellationToken); } protected async Task AssertContent(string hostName, string actualText, string rootPath = null) { - var response = await RetryPolicies.TestsTransientHttpErrorsPolicy.ExecuteAsync(async context => + var response = await RetryPolicies.TestsTransientHttpErrorsPolicy.ExecuteAsync(async (context, ct) => { - var r = await client.GetAsync($"https://{hostName}/{rootPath}"); + var r = await client.GetAsync($"https://{hostName}/{rootPath}", CancellationToken); if (!r.IsSuccessStatusCode) { var messageContent = await r.Content.ReadAsStringAsync(); @@ -127,7 +144,8 @@ protected async Task AssertContent(string hostName, string actualText, string ro r.EnsureSuccessStatusCode(); return r; }, - contextData: new Dictionary()); + contextData: new Dictionary(), + cancellationToken: CancellationToken); var result = await response.Content.ReadAsStringAsync(); result.Should().Contain(actualText); @@ -167,34 +185,25 @@ protected void AddAzureVariables(VariableDictionary variables) variables.Add(SpecialVariables.Action.Azure.WebAppName, WebSiteResource.Data.Name); } - protected async Task<(AppServicePlanResource, WebSiteResource)> CreateAppServicePlanAndWebApp( - ResourceGroupResource resourceGroup, - AppServicePlanData appServicePlanData = null, - WebSiteData webSiteData = null) + protected async Task CreateWebApp(AppServicePlanResource appServicePlanResource, WebSiteData webSiteData = null) { - appServicePlanData ??= new AppServicePlanData(resourceGroup.Data.Location) - { - Sku = new AppServiceSkuDescription - { - Name = "P1V3", - Tier = "PremiumV3" - } - }; - - var servicePlanResponse = await resourceGroup.GetAppServicePlans() - .CreateOrUpdateAsync(WaitUntil.Completed, - resourceGroup.Data.Name, - appServicePlanData); - - webSiteData ??= new WebSiteData(resourceGroup.Data.Location); - webSiteData.AppServicePlanId = servicePlanResponse.Value.Id; - - var webSiteResponse = await resourceGroup.GetWebSites() - .CreateOrUpdateAsync(WaitUntil.Completed, - resourceGroup.Data.Name, - webSiteData); - - return (servicePlanResponse.Value, webSiteResponse.Value); + var sw = Stopwatch.StartNew(); + + webSiteData ??= new WebSiteData(ResourceGroupResource.Data.Location); + webSiteData.AppServicePlanId = appServicePlanResource.Id; + + TestContext.WriteLine($"Creating web app '{ResourceGroupName}'"); + var webSiteResponse = await ResourceGroupResource.GetWebSites() + .CreateOrUpdateAsync(WaitUntil.Completed, + ResourceGroupName, + webSiteData, + CancellationToken); + + + sw.Stop(); + TestContext.WriteLine($"Created web app '{ResourceGroupName}' in {sw.Elapsed:g}"); + + return webSiteResponse.Value; } protected (string json, IEnumerable setting) BuildAppSettingsJson(IEnumerable<(string name, string value, bool isSlotSetting)> settings) diff --git a/source/Calamari.AzureAppService.Tests/AppServiceSettingsBehaviourFixture.cs b/source/Calamari.AzureAppService.Tests/AppServiceSettingsBehaviourFixture.cs index 1477888ec..4e12d629f 100644 --- a/source/Calamari.AzureAppService.Tests/AppServiceSettingsBehaviourFixture.cs +++ b/source/Calamari.AzureAppService.Tests/AppServiceSettingsBehaviourFixture.cs @@ -23,11 +23,12 @@ public class AppServiceSettingsBehaviorFixture : AppServiceIntegrationTest AppServiceConfigurationDictionary existingSettings; ConnectionStringDictionary existingConnectionStrings; - protected override async Task ConfigureTestResources(ResourceGroupResource resourceGroup) + public override async Task SetUp() { - var (_, webSiteResource) = await CreateAppServicePlanAndWebApp(resourceGroup); - WebSiteResource = webSiteResource; - + await base.SetUp(); + + WebSiteResource = await CreateWebApp(WindowsAppServicePlanResource); + existingSettings = new AppServiceConfigurationDictionary { Properties = diff --git a/source/Calamari.AzureAppService.Tests/AzureAppServiceDeployContainerBehaviourFixture.cs b/source/Calamari.AzureAppService.Tests/AzureAppServiceDeployContainerBehaviourFixture.cs index b5c8ad900..8c5097261 100644 --- a/source/Calamari.AzureAppService.Tests/AzureAppServiceDeployContainerBehaviourFixture.cs +++ b/source/Calamari.AzureAppService.Tests/AzureAppServiceDeployContainerBehaviourFixture.cs @@ -6,7 +6,6 @@ using Azure; using Azure.ResourceManager.AppService; using Azure.ResourceManager.AppService.Models; -using Azure.ResourceManager.Resources; using Calamari.AzureAppService.Azure; using Calamari.AzureAppService.Behaviors; using Calamari.Common.Commands; @@ -33,39 +32,25 @@ public class WhenUsingAWindowsAppService : AppServiceIntegrationTest CalamariVariables newVariables; readonly HttpClient client = new HttpClient(); - //We are having capacity issues in EastUS and WestUS2 - protected override string DefaultResourceGroupLocation => RandomAzureRegion.GetRandomRegionWithExclusions("eastus", "westus2"); - - protected override async Task ConfigureTestResources(ResourceGroupResource resourceGroup) + public override async Task SetUp() { - var (_, webSite) = await CreateAppServicePlanAndWebApp(resourceGroup, - new AppServicePlanData(resourceGroup.Data.Location) - { - IsXenon = true, - IsHyperV = true, - Sku = new AppServiceSkuDescription - { - Name = "P1V3", - Tier = "PremiumV3" - } - }, - webSiteData: new WebSiteData(resourceGroup.Data.Location) - { - SiteConfig = new SiteConfigProperties - { - WindowsFxVersion = "DOCKER|mcr.microsoft.com/dotnet/samples:aspnetapp", - IsAlwaysOn = true, - AppSettings = new List - { - new AppServiceNameValuePair { Name = "DOCKER_REGISTRY_SERVER_URL", Value = "https://index.docker.io" }, - new AppServiceNameValuePair { Name = "WEBSITES_ENABLE_APP_SERVICE_STORAGE", Value = "false" }, - new AppServiceNameValuePair { Name = "WEBSITES_CONTAINER_START_TIME_LIMIT", Value = "460" } - } - } - }); - - WebSiteResource = webSite; - + await base.SetUp(); + + WebSiteResource = await CreateWebApp(WindowsContainerAppServicePlanResource, + new WebSiteData(ResourceGroupResource.Data.Location) + { + SiteConfig = new SiteConfigProperties + { + WindowsFxVersion = "DOCKER|mcr.microsoft.com/dotnet/samples:aspnetapp", + IsAlwaysOn = true, + AppSettings = new List + { + new AppServiceNameValuePair { Name = "DOCKER_REGISTRY_SERVER_URL", Value = "https://index.docker.io" }, + new AppServiceNameValuePair { Name = "WEBSITES_ENABLE_APP_SERVICE_STORAGE", Value = "false" }, + new AppServiceNameValuePair { Name = "WEBSITES_CONTAINER_START_TIME_LIMIT", Value = "460" } + } + } + }); await AssertSetupSuccessAsync(); } @@ -150,9 +135,9 @@ async Task AssertDeploySuccessAsync(AzureTargetSite targetSite) var imageName = newVariables.Get(SpecialVariables.Action.Package.PackageId); var registryUrl = newVariables.Get(SpecialVariables.Action.Package.Registry); var imageVersion = newVariables.Get(SpecialVariables.Action.Package.PackageVersion) ?? "latest"; - - var config = await WebSiteResource.GetWebSiteConfig().GetAsync(); - Assert.AreEqual($@"DOCKER|{imageName}:{imageVersion}", config.Value.Data.WindowsFxVersion); + + var config = await ArmClient.GetSiteConfigDataAsync(targetSite); + Assert.AreEqual($"DOCKER|{imageName}:{imageVersion}", config.WindowsFxVersion); var appSettings = await ArmClient.GetAppSettingsListAsync(targetSite); Assert.AreEqual("https://" + registryUrl, appSettings.FirstOrDefault(app => app.Name == "DOCKER_REGISTRY_SERVER_URL")?.Value); @@ -176,39 +161,26 @@ public class WhenUsingALinuxAppService : AppServiceIntegrationTest CalamariVariables newVariables; readonly HttpClient client = new HttpClient(); - // For some reason we are having issues creating these linux resources on Standard in EastUS - protected override string DefaultResourceGroupLocation => RandomAzureRegion.GetRandomRegionWithExclusions("eastus"); - - protected override async Task ConfigureTestResources(ResourceGroupResource resourceGroup) + public override async Task SetUp() { - var (_, webSite) = await CreateAppServicePlanAndWebApp(resourceGroup, - new AppServicePlanData(resourceGroup.Data.Location) - { - Kind = "linux", - IsReserved = true, - Sku = new AppServiceSkuDescription - { - Name = "P1V3", - Tier = "PremiumV3" - } - }, - new WebSiteData(resourceGroup.Data.Location) - { - Kind = "app,linux,container", - SiteConfig = new SiteConfigProperties - { - LinuxFxVersion = "DOCKER|mcr.microsoft.com/dotnet/samples:aspnetapp", - IsAlwaysOn = true, - AppSettings = new List - { - new AppServiceNameValuePair { Name = "DOCKER_REGISTRY_SERVER_URL", Value = "https://index.docker.io" }, - new AppServiceNameValuePair { Name = "WEBSITES_ENABLE_APP_SERVICE_STORAGE", Value = "false" }, - new AppServiceNameValuePair { Name = "WEBSITES_CONTAINER_START_TIME_LIMIT", Value = "460" } - } - } - }); - - WebSiteResource = webSite; + await base.SetUp(); + + WebSiteResource = await CreateWebApp(LinuxAppServicePlanResource, + new WebSiteData(ResourceGroupResource.Data.Location) + { + Kind = "app,linux,container", + SiteConfig = new SiteConfigProperties + { + LinuxFxVersion = "DOCKER|mcr.microsoft.com/dotnet/samples:aspnetapp", + IsAlwaysOn = true, + AppSettings = new List + { + new AppServiceNameValuePair { Name = "DOCKER_REGISTRY_SERVER_URL", Value = "https://index.docker.io" }, + new AppServiceNameValuePair { Name = "WEBSITES_ENABLE_APP_SERVICE_STORAGE", Value = "false" }, + new AppServiceNameValuePair { Name = "WEBSITES_CONTAINER_START_TIME_LIMIT", Value = "460" } + } + } + }); await AssertSetupSuccessAsync(); } @@ -295,8 +267,8 @@ async Task AssertDeploySuccessAsync(AzureTargetSite targetSite) var registryUrl = newVariables.Get(SpecialVariables.Action.Package.Registry); var imageVersion = newVariables.Get(SpecialVariables.Action.Package.PackageVersion) ?? "latest"; - var config = await WebSiteResource.GetWebSiteConfig().GetAsync(); - Assert.AreEqual($@"DOCKER|{imageName}:{imageVersion}", config.Value.Data.LinuxFxVersion); + var config = await ArmClient.GetSiteConfigDataAsync(targetSite); + Assert.AreEqual($"DOCKER|{imageName}:{imageVersion}", config.LinuxFxVersion); var appSettings = await ArmClient.GetAppSettingsListAsync(targetSite); Assert.AreEqual("https://" + registryUrl, appSettings.FirstOrDefault(app => app.Name == "DOCKER_REGISTRY_SERVER_URL")?.Value); diff --git a/source/Calamari.AzureAppService.Tests/AzureWebAppHealthCheckActionHandlerFixture.cs b/source/Calamari.AzureAppService.Tests/AzureWebAppHealthCheckActionHandlerFixture.cs index 79303072f..40d47e8bc 100644 --- a/source/Calamari.AzureAppService.Tests/AzureWebAppHealthCheckActionHandlerFixture.cs +++ b/source/Calamari.AzureAppService.Tests/AzureWebAppHealthCheckActionHandlerFixture.cs @@ -22,34 +22,26 @@ class AzureWebAppHealthCheckActionHandlerFixture : AppServiceIntegrationTest readonly IWebProxy originalProxy = WebRequest.DefaultWebProxy; readonly string originalProxyHost = Environment.GetEnvironmentVariable(EnvironmentVariables.TentacleProxyHost); readonly string originalProxyPort = Environment.GetEnvironmentVariable(EnvironmentVariables.TentacleProxyPort); - - protected override async Task ConfigureTestResources(ResourceGroupResource resourceGroup) + + public override async Task SetUp() { - var (_, webSiteResource) = await CreateAppServicePlanAndWebApp(resourceGroup, - new AppServicePlanData(resourceGroup.Data.Location) - { - Sku = new AppServiceSkuDescription - { - Name = "B1", - Tier = "Basic" - } - }, - new WebSiteData(resourceGroup.Data.Location) - { - SiteConfig = new SiteConfigProperties - { - NetFrameworkVersion = "v6.0" - } - }); - WebSiteResource = webSiteResource; + await base.SetUp(); + + WebSiteResource = await CreateWebApp(WindowsAppServicePlanResource, + new WebSiteData(ResourceGroupResource.Data.Location) + { + SiteConfig = new SiteConfigProperties + { + NetFrameworkVersion = "v6.0" + } + }); } - public override async Task Cleanup() + [OneTimeTearDown] + public void Cleanup() { RestoreLocalEnvironmentProxySettings(); RestoreCiEnvironmentProxySettings(); - - await base.Cleanup(); } [Test] diff --git a/source/Calamari.AzureAppService.Tests/TargetDiscoveryBehaviourIntegrationTestFixture.cs b/source/Calamari.AzureAppService.Tests/TargetDiscoveryBehaviourIntegrationTestFixture.cs index 41018801d..198f3f48c 100644 --- a/source/Calamari.AzureAppService.Tests/TargetDiscoveryBehaviourIntegrationTestFixture.cs +++ b/source/Calamari.AzureAppService.Tests/TargetDiscoveryBehaviourIntegrationTestFixture.cs @@ -1,5 +1,4 @@ -using Azure.ResourceManager.Resources; -using Calamari.AzureAppService.Behaviors; +using Calamari.AzureAppService.Behaviors; using Calamari.Common.Commands; using Calamari.Common.Features.Discovery; using Calamari.Common.Plumbing.Variables; @@ -12,49 +11,27 @@ using System.Threading.Tasks; using Azure; using Azure.ResourceManager.AppService; -using Azure.ResourceManager.AppService.Models; using Calamari.Common.Plumbing.Extensions; -using Polly.Retry; namespace Calamari.AzureAppService.Tests { [TestFixture] public class TargetDiscoveryBehaviourIntegrationTestFixture : AppServiceIntegrationTest { - private readonly string appName = Guid.NewGuid().ToString(); - private readonly List slotNames = new List { "blue", "green" }; - private static readonly string Type = "Azure"; - private static readonly string AuthenticationMethod = "ServicePrincipal"; - private static readonly string AccountId = "Accounts-1"; - private static readonly string Role = "my-azure-app-role"; - private static readonly string EnvironmentName = "dev"; - private RetryPolicy retryPolicy; - - private AppServicePlanResource appServicePlanResource; - - protected override async Task ConfigureTestResources(ResourceGroupResource resourceGroup) - { - var response = await resourceGroup.GetAppServicePlans() - .CreateOrUpdateAsync(WaitUntil.Completed, - ResourceGroupName, - new AppServicePlanData(resourceGroup.Data.Location) - { - Sku = new AppServiceSkuDescription - { - Name = "P1V3", - Tier = "PremiumV3" - } - }); - - appServicePlanResource = response.Value; - } - - [SetUp] - public async Task CreateOrResetWebAppAndSlots() + readonly List slotNames = new List { "blue", "green" }; + const string Type = "Azure"; + const string AuthenticationMethod = "ServicePrincipal"; + const string AccountId = "Accounts-1"; + const string Role = "my-azure-app-role"; + const string EnvironmentName = "dev"; + + public override async Task SetUp() { + await base.SetUp(); + // Call update on the web app and each slot without and tags // to reset it for each test. - WebSiteResource = await CreateOrUpdateTestWebApp(); + WebSiteResource = await CreateWebApp(WindowsAppServicePlanResource); await CreateOrUpdateTestWebAppSlots(WebSiteResource); } @@ -75,7 +52,7 @@ public async Task Execute_WebAppWithMatchingTags_CreatesCorrectTargets() { TargetTags.RoleTagName, Role }, }; - await CreateOrUpdateTestWebApp(tags); + await UpdateTestWebApp(tags); var cancellationTokenSource = new CancellationTokenSource(TimeSpan.FromMinutes(15)); await Eventually.ShouldEventually(async () => @@ -85,7 +62,7 @@ await Eventually.ShouldEventually(async () => // Assert var serviceMessageToCreateWebAppTarget = TargetDiscoveryHelpers.CreateWebAppTargetCreationServiceMessage(ResourceGroupName, - appName, + WebSiteResource.Data.Name, AccountId, Role, null, @@ -114,14 +91,14 @@ public async Task Execute_WebAppWithNonMatchingTags_CreatesNoTargets() { TargetTags.RoleTagName, Role }, }; - await CreateOrUpdateTestWebApp(tags); + await UpdateTestWebApp(tags); // Act await sut.Execute(context); // Assert var serviceMessageToCreateWebAppTarget = TargetDiscoveryHelpers.CreateWebAppTargetCreationServiceMessage(ResourceGroupName, - appName, + WebSiteResource.Data.Name, AccountId, "a-different-role", null, @@ -155,7 +132,7 @@ await Eventually.ShouldEventually(async () => await sut.Execute(context); var serviceMessageToCreateWebAppTarget = TargetDiscoveryHelpers.CreateWebAppTargetCreationServiceMessage(ResourceGroupName, - appName, + WebSiteResource.Data.Name, AccountId, Role, null, @@ -166,7 +143,7 @@ await Eventually.ShouldEventually(async () => foreach (var slotName in slotNames) { var serviceMessageToCreateTargetForSlot = TargetDiscoveryHelpers.CreateWebAppTargetCreationServiceMessage(ResourceGroupName, - appName, + WebSiteResource.Data.Name, AccountId, Role, null, @@ -195,8 +172,8 @@ public async Task Execute_MultipleWebAppSlotsWithTags_WebAppWithTags_CreatesCorr { TargetTags.RoleTagName, Role }, }; - var webSiteResource =await CreateOrUpdateTestWebApp(tags); - await CreateOrUpdateTestWebAppSlots(webSiteResource,tags); + var webSiteResource = await UpdateTestWebApp(tags); + await CreateOrUpdateTestWebAppSlots(webSiteResource, tags); var cancellationTokenSource = new CancellationTokenSource(TimeSpan.FromMinutes(15)); await Eventually.ShouldEventually(async () => @@ -205,7 +182,7 @@ await Eventually.ShouldEventually(async () => await sut.Execute(context); var serviceMessageToCreateWebAppTarget = TargetDiscoveryHelpers.CreateWebAppTargetCreationServiceMessage(ResourceGroupName, - appName, + WebSiteResource.Data.Name, AccountId, Role, null, @@ -216,7 +193,7 @@ await Eventually.ShouldEventually(async () => foreach (var slotName in slotNames) { var serviceMessageToCreateTargetForSlot = TargetDiscoveryHelpers.CreateWebAppTargetCreationServiceMessage(ResourceGroupName, - appName, + WebSiteResource.Data.Name, AccountId, Role, null, @@ -249,7 +226,7 @@ public async Task Execute_MultipleWebAppSlotsWithPartialTags_WebAppWithPartialTa { TargetTags.RoleTagName, Role }, }; - var webSiteResource = await CreateOrUpdateTestWebApp(webAppTags); + var webSiteResource = await UpdateTestWebApp(webAppTags); await CreateOrUpdateTestWebAppSlots(webSiteResource, slotTags); var cancellationTokenSource = new CancellationTokenSource(TimeSpan.FromMinutes(15)); @@ -260,7 +237,7 @@ await Eventually.ShouldEventually(async () => var serviceMessageToCreateWebAppTarget = TargetDiscoveryHelpers.CreateWebAppTargetCreationServiceMessage(ResourceGroupName, - appName, + WebSiteResource.Data.Name, AccountId, Role, null, @@ -274,7 +251,7 @@ await Eventually.ShouldEventually(async () => { var serviceMessageToCreateTargetForSlot = TargetDiscoveryHelpers.CreateWebAppTargetCreationServiceMessage(ResourceGroupName, - appName, + WebSiteResource.Data.Name, AccountId, Role, null, @@ -288,25 +265,20 @@ await Eventually.ShouldEventually(async () => cancellationTokenSource.Token); } - private async Task CreateOrUpdateTestWebApp(IDictionary tags = null) + async Task UpdateTestWebApp(IDictionary tags = null) { - var data = new WebSiteData(ResourceGroupResource.Data.Location) - { - AppServicePlanId = appServicePlanResource.Id - }; - if (tags != null) - data.Tags.AddRange(tags); + WebSiteResource.Data.Tags.AddRange(tags); var response = await ResourceGroupResource.GetWebSites() - .CreateOrUpdateAsync(WaitUntil.Completed, - appName, - data); + .CreateOrUpdateAsync(WaitUntil.Completed, + WebSiteResource.Data.Name, + WebSiteResource.Data); return response.Value; } - private async Task CreateOrUpdateTestWebAppSlots(WebSiteResource webSiteResource, Dictionary tags = null) + async Task CreateOrUpdateTestWebAppSlots(WebSiteResource webSiteResource, Dictionary tags = null) { var webSiteData = webSiteResource.Data; @@ -314,14 +286,14 @@ private async Task CreateOrUpdateTestWebAppSlots(WebSiteResource webSiteResource webSiteData.Tags.AddRange(tags); var slotTasks = new List(); - + foreach (var slotName in slotNames) { var task = webSiteResource.GetWebSiteSlots() - .CreateOrUpdateAsync(WaitUntil.Completed, - slotName, - webSiteData - ); + .CreateOrUpdateAsync(WaitUntil.Completed, + slotName, + webSiteData + ); slotTasks.Add(task); } @@ -333,7 +305,7 @@ private void CreateVariables(RunningDeployment context) string targetDiscoveryContext = $@"{{ ""scope"": {{ ""spaceName"": ""default"", - ""environmentName"": ""{EnvironmentName}"", + ""EnvironmentName"": ""{EnvironmentName}"", ""projectName"": ""my-test-project"", ""tenantName"": null, ""roles"": [""{Role}""] diff --git a/source/Calamari.AzureAppService/RetryPolicies.cs b/source/Calamari.AzureAppService/RetryPolicies.cs index 6afc43581..1b8ef59bc 100644 --- a/source/Calamari.AzureAppService/RetryPolicies.cs +++ b/source/Calamari.AzureAppService/RetryPolicies.cs @@ -2,6 +2,8 @@ using System.Net; using System.Net.Http; using System.Net.Sockets; +using Azure; +using Azure.ResourceManager; using Calamari.Common.Plumbing.Logging; using Polly; using Polly.Retry; @@ -31,9 +33,15 @@ public static class ContextKeys public static AsyncRetryPolicy TestsTransientHttpErrorsPolicy { get; } = Policy.Handle() .Or() .OrResult(r => (int)r.StatusCode >= 500 || r.StatusCode == HttpStatusCode.RequestTimeout) + .Or() .WaitAndRetryAsync(4, retryAttempt => TimeSpan.FromSeconds(Math.Pow(3.5, retryAttempt)) + TimeSpan.FromMilliseconds(Jitterer.Next(0, 10000))); + public static AsyncRetryPolicy TestsTransientArmOperationsErrorsPolicy { get; } = Policy + .Handle() + .WaitAndRetryAsync(4, + retryAttempt => TimeSpan.FromSeconds(Math.Pow(3.5, retryAttempt)) + TimeSpan.FromMilliseconds(Jitterer.Next(0, 10000))); + public static AsyncRetryPolicy AsynchronousZipDeploymentOperationPolicy { get; } = Policy.HandleResult(r => r.StatusCode == HttpStatusCode.Accepted) .WaitAndRetryForeverAsync((_1, ctx) => TimeSpan.FromSeconds(4), (response, timeout, ctx) =>