Skip to content
This repository has been archived by the owner on May 2, 2023. It is now read-only.

Commit

Permalink
Ensure AppService Deployment Targets' health check respects proxy set…
Browse files Browse the repository at this point in the history
…tings (#35)

* Ensure HealthCheck respects configured proxy settings
* Extract integration test base class to its own file
* Pull common variable setup down to base class
* Add test to ensure Health Check respects proxy settings
* Ensure Proxy test works on both CI and locally
* Fixes another small difference in testing between CI and local
* More tweaks to test for cross-OS assertion
* Fix up refactor in AppServiceBehaviourFixture
* Simplify Proxy test code
  • Loading branch information
mjhilton authored Jun 15, 2022
1 parent f038c60 commit 07785f5
Show file tree
Hide file tree
Showing 7 changed files with 302 additions and 129 deletions.
126 changes: 8 additions & 118 deletions source/Calamari.Tests/AppServiceBehaviorFixture.cs
Original file line number Diff line number Diff line change
@@ -1,16 +1,12 @@
using System;
using System.Collections.Generic;
using System.Collections.Generic;
using System.IO;
using System.IO.Compression;
using System.Linq;
using System.Net.Http;
using System.Reflection;
using System.Text;
using System.Threading.Tasks;
using System.Xml;
using System.Xml.Linq;
using Azure.Identity;
using Azure.ResourceManager.Resources;
using Azure.ResourceManager.Resources.Models;
using Calamari.Azure;
using Calamari.Common.Plumbing.FileSystem;
Expand Down Expand Up @@ -281,12 +277,7 @@ private static (string packagePath, string packageName, string packageVersion) P

private void AddVariables(CommandTestBuilderContext context)
{
context.Variables.Add(AccountVariables.ClientId, clientId);
context.Variables.Add(AccountVariables.Password, clientSecret);
context.Variables.Add(AccountVariables.TenantId, tenantId);
context.Variables.Add(AccountVariables.SubscriptionId, subscriptionId);
context.Variables.Add("Octopus.Action.Azure.ResourceGroupName", resourceGroupName);
context.Variables.Add("Octopus.Action.Azure.WebAppName", site.Name);
AddAzureVariables(context);
context.Variables.Add("Greeting", greeting);
context.Variables.Add(KnownVariables.Package.EnabledFeatures, KnownVariables.Features.SubstituteInFiles);
context.Variables.Add(PackageVariables.SubstituteInFilesTargets, "index.html");
Expand All @@ -297,9 +288,6 @@ private void AddVariables(CommandTestBuilderContext context)
[TestFixture]
public class WhenUsingALinuxAppService : AppServiceIntegrationTest
{
private string linuxServicePlanName;
private string functionAppSiteName;

protected override async Task ConfigureTestResources(ResourceGroup resourceGroup)
{
var storageClient = new StorageManagementClient(new TokenCredentials(authToken))
Expand Down Expand Up @@ -329,7 +317,7 @@ protected override async Task ConfigureTestResources(ResourceGroup resourceGroup
}
);

var functionAppSite = await webMgmtClient.WebApps.BeginCreateOrUpdateAsync(resourceGroupName,
site = await webMgmtClient.WebApps.BeginCreateOrUpdateAsync(resourceGroupName,
$"{resourceGroupName}-linux",
new Site(resourceGroupLocation)
{
Expand All @@ -350,9 +338,6 @@ protected override async Task ConfigureTestResources(ResourceGroup resourceGroup
}
}
);

linuxServicePlanName = linuxSvcPlan.Name;
functionAppSiteName = functionAppSite.Name;
}

[Test]
Expand All @@ -371,7 +356,7 @@ await CommandTestBuilder.CreateAsync<DeployAzureAppServiceCommand, Program>().Wi
// Assert
await DoWithRetries(10, async () =>
{
await AssertContent($"{functionAppSiteName}.azurewebsites.net",
await AssertContent($"{site.Name}.azurewebsites.net",
rootPath: $"api/HttpExample?name={greeting}",
actualText: $"Hello, {greeting}");
},
Expand All @@ -382,9 +367,9 @@ await AssertContent($"{functionAppSiteName}.azurewebsites.net",
public async Task CanDeployZip_ToLinuxFunctionApp_WithRunFromPackageFlag()
{
// Arrange
var settings = await webMgmtClient.WebApps.ListApplicationSettingsAsync(resourceGroupName, functionAppSiteName);
var settings = await webMgmtClient.WebApps.ListApplicationSettingsAsync(resourceGroupName, site.Name);
settings.Properties["WEBSITE_RUN_FROM_PACKAGE"] = "1";
await webMgmtClient.WebApps.UpdateApplicationSettingsAsync(resourceGroupName, functionAppSiteName, settings);
await webMgmtClient.WebApps.UpdateApplicationSettingsAsync(resourceGroupName, site.Name, settings);

var packageInfo = PrepareZipPackage();

Expand All @@ -398,7 +383,7 @@ await CommandTestBuilder.CreateAsync<DeployAzureAppServiceCommand, Program>().Wi
// Assert
await DoWithRetries(10, async () =>
{
await AssertContent($"{functionAppSiteName}.azurewebsites.net",
await AssertContent($"{site.Name}.azurewebsites.net",
rootPath: $"api/HttpExample?name={greeting}",
actualText: $"Hello, {greeting}");
},
Expand Down Expand Up @@ -428,104 +413,9 @@ private static (string packagePath, string packageName, string packageVersion) P

private void AddVariables(CommandTestBuilderContext context)
{
context.Variables.Add(AccountVariables.ClientId, clientId);
context.Variables.Add(AccountVariables.Password, clientSecret);
context.Variables.Add(AccountVariables.TenantId, tenantId);
context.Variables.Add(AccountVariables.SubscriptionId, subscriptionId);
context.Variables.Add("Octopus.Action.Azure.ResourceGroupName", resourceGroupName);
context.Variables.Add("Octopus.Action.Azure.WebAppName", functionAppSiteName);
AddAzureVariables(context);
context.Variables.Add(SpecialVariables.Action.Azure.DeploymentType, "ZipDeploy");
}
}
}

public abstract class AppServiceIntegrationTest
{
protected string clientId;
protected string clientSecret;
protected string tenantId;
protected string subscriptionId;
protected string resourceGroupName;
protected string resourceGroupLocation;
protected string greeting = "Calamari";
protected string authToken;
protected WebSiteManagementClient webMgmtClient;
protected Site site;

private ResourceGroupsOperations resourceGroupClient;
private readonly HttpClient client = new HttpClient();

[OneTimeSetUp]
public async Task Setup()
{
var resourceManagementEndpointBaseUri =
Environment.GetEnvironmentVariable(AccountVariables.ResourceManagementEndPoint) ??
DefaultVariables.ResourceManagementEndpoint;
var activeDirectoryEndpointBaseUri =
Environment.GetEnvironmentVariable(AccountVariables.ActiveDirectoryEndPoint) ??
DefaultVariables.ActiveDirectoryEndpoint;

resourceGroupName = Guid.NewGuid().ToString();

clientId = ExternalVariables.Get(ExternalVariable.AzureSubscriptionClientId);
clientSecret = ExternalVariables.Get(ExternalVariable.AzureSubscriptionPassword);
tenantId = ExternalVariables.Get(ExternalVariable.AzureSubscriptionTenantId);
subscriptionId = ExternalVariables.Get(ExternalVariable.AzureSubscriptionId);
resourceGroupLocation = Environment.GetEnvironmentVariable("AZURE_NEW_RESOURCE_REGION") ?? "eastus";

authToken = await Auth.GetAuthTokenAsync(activeDirectoryEndpointBaseUri, resourceManagementEndpointBaseUri,
tenantId, clientId, clientSecret);

var resourcesClient = new ResourcesManagementClient(subscriptionId,
new ClientSecretCredential(tenantId, clientId, clientSecret));

resourceGroupClient = resourcesClient.ResourceGroups;

var resourceGroup = new ResourceGroup(resourceGroupLocation);
resourceGroup = await resourceGroupClient.CreateOrUpdateAsync(resourceGroupName, resourceGroup);

webMgmtClient = new WebSiteManagementClient(new TokenCredentials(authToken))
{
SubscriptionId = subscriptionId,
HttpClient = { BaseAddress = new Uri(DefaultVariables.ResourceManagementEndpoint) },
};

await ConfigureTestResources(resourceGroup);
}

protected abstract Task ConfigureTestResources(ResourceGroup resourceGroup);

[OneTimeTearDown]
public async Task Cleanup()
{
if (resourceGroupClient != null)
await resourceGroupClient.StartDeleteAsync(resourceGroupName);
}

protected async Task AssertContent(string hostName, string actualText, string rootPath = null)
{
var result = await client.GetStringAsync($"https://{hostName}/{rootPath}");

result.Should().Contain(actualText);
}

protected static async Task DoWithRetries(int retries, Func<Task> action, int secondsBetweenRetries)
{
foreach (var retry in Enumerable.Range(1, retries))
{
try
{
await action();
break;
}
catch
{
if (retry == retries)
throw;

await Task.Delay(secondsBetweenRetries * 1000);
}
}
}
}
}
117 changes: 117 additions & 0 deletions source/Calamari.Tests/AppServiceIntegrationTest.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,117 @@
using System;
using System.Linq;
using System.Net.Http;
using System.Threading.Tasks;
using Azure.Identity;
using Azure.ResourceManager.Resources;
using Azure.ResourceManager.Resources.Models;
using Calamari.Azure;
using Calamari.Tests.Shared;
using FluentAssertions;
using Microsoft.Azure.Management.WebSites;
using Microsoft.Azure.Management.WebSites.Models;
using Microsoft.Rest;
using NUnit.Framework;

namespace Calamari.AzureAppService.Tests
{
public abstract class AppServiceIntegrationTest
{
protected string clientId;
protected string clientSecret;
protected string tenantId;
protected string subscriptionId;
protected string resourceGroupName;
protected string resourceGroupLocation;
protected string greeting = "Calamari";
protected string authToken;
protected WebSiteManagementClient webMgmtClient;
protected Site site;

private ResourceGroupsOperations resourceGroupClient;
private readonly HttpClient client = new HttpClient();

[OneTimeSetUp]
public async Task Setup()
{
var resourceManagementEndpointBaseUri =
Environment.GetEnvironmentVariable(AccountVariables.ResourceManagementEndPoint) ??
DefaultVariables.ResourceManagementEndpoint;
var activeDirectoryEndpointBaseUri =
Environment.GetEnvironmentVariable(AccountVariables.ActiveDirectoryEndPoint) ??
DefaultVariables.ActiveDirectoryEndpoint;

resourceGroupName = Guid.NewGuid().ToString();

clientId = ExternalVariables.Get(ExternalVariable.AzureSubscriptionClientId);
clientSecret = ExternalVariables.Get(ExternalVariable.AzureSubscriptionPassword);
tenantId = ExternalVariables.Get(ExternalVariable.AzureSubscriptionTenantId);
subscriptionId = ExternalVariables.Get(ExternalVariable.AzureSubscriptionId);
resourceGroupLocation = Environment.GetEnvironmentVariable("AZURE_NEW_RESOURCE_REGION") ?? "eastus";

authToken = await Auth.GetAuthTokenAsync(activeDirectoryEndpointBaseUri, resourceManagementEndpointBaseUri,
tenantId, clientId, clientSecret);

var resourcesClient = new ResourcesManagementClient(subscriptionId,
new ClientSecretCredential(tenantId, clientId, clientSecret));

resourceGroupClient = resourcesClient.ResourceGroups;

var resourceGroup = new ResourceGroup(resourceGroupLocation);
resourceGroup = await resourceGroupClient.CreateOrUpdateAsync(resourceGroupName, resourceGroup);

webMgmtClient = new WebSiteManagementClient(new TokenCredentials(authToken))
{
SubscriptionId = subscriptionId,
HttpClient = { BaseAddress = new Uri(DefaultVariables.ResourceManagementEndpoint) },
};

await ConfigureTestResources(resourceGroup);
}

protected abstract Task ConfigureTestResources(ResourceGroup resourceGroup);

[OneTimeTearDown]
public async Task Cleanup()
{
if (resourceGroupClient != null)
await resourceGroupClient.StartDeleteAsync(resourceGroupName);
}

protected async Task AssertContent(string hostName, string actualText, string rootPath = null)
{
var result = await client.GetStringAsync($"https://{hostName}/{rootPath}");

result.Should().Contain(actualText);
}

protected static async Task DoWithRetries(int retries, Func<Task> action, int secondsBetweenRetries)
{
foreach (var retry in Enumerable.Range(1, retries))
{
try
{
await action();
break;
}
catch
{
if (retry == retries)
throw;

await Task.Delay(secondsBetweenRetries * 1000);
}
}
}

protected void AddAzureVariables(CommandTestBuilderContext context)
{
context.Variables.Add(AccountVariables.ClientId, clientId);
context.Variables.Add(AccountVariables.Password, clientSecret);
context.Variables.Add(AccountVariables.TenantId, tenantId);
context.Variables.Add(AccountVariables.SubscriptionId, subscriptionId);
context.Variables.Add("Octopus.Action.Azure.ResourceGroupName", resourceGroupName);
context.Variables.Add("Octopus.Action.Azure.WebAppName", site.Name);
}
}
}
22 changes: 22 additions & 0 deletions source/Calamari/Azure/AzureClient.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
using System.Net;
using System.Net.Http;
using Azure.Core.Pipeline;
using Azure.Identity;
using Azure.ResourceManager;
using Microsoft.Azure.Management.Fluent;
using Microsoft.Azure.Management.ResourceManager.Fluent;

Expand All @@ -23,5 +26,24 @@ public static IAzure CreateAzureClient(this ServicePrincipalAccount servicePrinc
.Authenticate(credentials)
.WithSubscription(servicePrincipal.SubscriptionNumber);
}

/// <summary>
/// Creates an ArmClient for the new Azure SDK, which replaces the older fluent libraries.
/// We should migrate to this SDK once it stabilises.
/// </summary>
/// <param name="servicePrincipal">Service Principal Account to use when connecting to Azure</param>
/// <returns></returns>
public static ArmClient CreateArmClient(this ServicePrincipalAccount servicePrincipal)
{
var environment = new AzureKnownEnvironment(servicePrincipal.AzureEnvironment).AsAzureArmEnvironment();

var httpClientTransport = new HttpClientTransport(new HttpClientHandler { Proxy = WebRequest.DefaultWebProxy });

var tokenCredentialOptions = new TokenCredentialOptions { Transport = httpClientTransport };
var credential = new ClientSecretCredential(servicePrincipal.TenantId, servicePrincipal.ClientId, servicePrincipal.Password, tokenCredentialOptions);

var armClientOptions = new ArmClientOptions() { Transport = httpClientTransport, Environment = environment };
return new ArmClient(credential, defaultSubscriptionId: servicePrincipal.SubscriptionNumber, armClientOptions);
}
}
}
18 changes: 15 additions & 3 deletions source/Calamari/Azure/AzureKnownEnvironment.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using System;
using Azure.ResourceManager;
using Microsoft.Azure.Management.ResourceManager.Fluent;

namespace Calamari.Azure
Expand All @@ -15,11 +16,11 @@ public AzureKnownEnvironment(string environment)
if (string.IsNullOrEmpty(environment) || environment == "AzureCloud") // This environment name is defined in Sashimi.Azure.Accounts.AzureEnvironmentsListAction
Value = Global.Value; // We interpret it as the normal Azure environment for historical reasons)

azureEnvironment = AzureEnvironment.FromName(Value) ??
azureSdkEnvironment = AzureEnvironment.FromName(Value) ??
throw new InvalidOperationException($"Unknown environment name {Value}");
}

private readonly AzureEnvironment azureEnvironment;
private readonly AzureEnvironment azureSdkEnvironment;
public string Value { get; }

public static readonly AzureKnownEnvironment Global = new AzureKnownEnvironment("AzureGlobalCloud");
Expand All @@ -29,7 +30,18 @@ public AzureKnownEnvironment(string environment)

public AzureEnvironment AsAzureSDKEnvironment()
{
return azureEnvironment;
return azureSdkEnvironment;
}

public ArmEnvironment AsAzureArmEnvironment() => ToArmEnvironment(Value);

private static ArmEnvironment ToArmEnvironment(string name) => name switch
{
"AzureGlobalCloud" => ArmEnvironment.AzurePublicCloud,
"AzureChinaCloud" => ArmEnvironment.AzureChina,
"AzureGermanCloud" => ArmEnvironment.AzureGermany,
"AzureUSGovernment" => ArmEnvironment.AzureGovernment,
_ => throw new InvalidOperationException($"ARM Environment {name} is not a known Azure Environment name.")
};
}
}
2 changes: 2 additions & 0 deletions source/Calamari/Calamari.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@
<TargetFramework>net5.0</TargetFramework>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Azure.Identity" Version="1.2.3" />
<PackageReference Include="Azure.ResourceManager.AppService" Version="1.0.0-beta.2" />
<PackageReference Include="Calamari.Common" Version="21.1.0" />
<PackageReference Include="Microsoft.Azure.Management.AppService.Fluent" Version="1.37.1" />
<PackageReference Include="Microsoft.Azure.Management.Fluent" Version="1.37.1" />
Expand Down
Loading

0 comments on commit 07785f5

Please sign in to comment.