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

Commit 07785f5

Browse files
authored
Ensure AppService Deployment Targets' health check respects proxy settings (#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
1 parent f038c60 commit 07785f5

7 files changed

+302
-129
lines changed

source/Calamari.Tests/AppServiceBehaviorFixture.cs

+8-118
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,12 @@
1-
using System;
2-
using System.Collections.Generic;
1+
using System.Collections.Generic;
32
using System.IO;
43
using System.IO.Compression;
54
using System.Linq;
6-
using System.Net.Http;
75
using System.Reflection;
86
using System.Text;
97
using System.Threading.Tasks;
108
using System.Xml;
119
using System.Xml.Linq;
12-
using Azure.Identity;
13-
using Azure.ResourceManager.Resources;
1410
using Azure.ResourceManager.Resources.Models;
1511
using Calamari.Azure;
1612
using Calamari.Common.Plumbing.FileSystem;
@@ -281,12 +277,7 @@ private static (string packagePath, string packageName, string packageVersion) P
281277

282278
private void AddVariables(CommandTestBuilderContext context)
283279
{
284-
context.Variables.Add(AccountVariables.ClientId, clientId);
285-
context.Variables.Add(AccountVariables.Password, clientSecret);
286-
context.Variables.Add(AccountVariables.TenantId, tenantId);
287-
context.Variables.Add(AccountVariables.SubscriptionId, subscriptionId);
288-
context.Variables.Add("Octopus.Action.Azure.ResourceGroupName", resourceGroupName);
289-
context.Variables.Add("Octopus.Action.Azure.WebAppName", site.Name);
280+
AddAzureVariables(context);
290281
context.Variables.Add("Greeting", greeting);
291282
context.Variables.Add(KnownVariables.Package.EnabledFeatures, KnownVariables.Features.SubstituteInFiles);
292283
context.Variables.Add(PackageVariables.SubstituteInFilesTargets, "index.html");
@@ -297,9 +288,6 @@ private void AddVariables(CommandTestBuilderContext context)
297288
[TestFixture]
298289
public class WhenUsingALinuxAppService : AppServiceIntegrationTest
299290
{
300-
private string linuxServicePlanName;
301-
private string functionAppSiteName;
302-
303291
protected override async Task ConfigureTestResources(ResourceGroup resourceGroup)
304292
{
305293
var storageClient = new StorageManagementClient(new TokenCredentials(authToken))
@@ -329,7 +317,7 @@ protected override async Task ConfigureTestResources(ResourceGroup resourceGroup
329317
}
330318
);
331319

332-
var functionAppSite = await webMgmtClient.WebApps.BeginCreateOrUpdateAsync(resourceGroupName,
320+
site = await webMgmtClient.WebApps.BeginCreateOrUpdateAsync(resourceGroupName,
333321
$"{resourceGroupName}-linux",
334322
new Site(resourceGroupLocation)
335323
{
@@ -350,9 +338,6 @@ protected override async Task ConfigureTestResources(ResourceGroup resourceGroup
350338
}
351339
}
352340
);
353-
354-
linuxServicePlanName = linuxSvcPlan.Name;
355-
functionAppSiteName = functionAppSite.Name;
356341
}
357342

358343
[Test]
@@ -371,7 +356,7 @@ await CommandTestBuilder.CreateAsync<DeployAzureAppServiceCommand, Program>().Wi
371356
// Assert
372357
await DoWithRetries(10, async () =>
373358
{
374-
await AssertContent($"{functionAppSiteName}.azurewebsites.net",
359+
await AssertContent($"{site.Name}.azurewebsites.net",
375360
rootPath: $"api/HttpExample?name={greeting}",
376361
actualText: $"Hello, {greeting}");
377362
},
@@ -382,9 +367,9 @@ await AssertContent($"{functionAppSiteName}.azurewebsites.net",
382367
public async Task CanDeployZip_ToLinuxFunctionApp_WithRunFromPackageFlag()
383368
{
384369
// Arrange
385-
var settings = await webMgmtClient.WebApps.ListApplicationSettingsAsync(resourceGroupName, functionAppSiteName);
370+
var settings = await webMgmtClient.WebApps.ListApplicationSettingsAsync(resourceGroupName, site.Name);
386371
settings.Properties["WEBSITE_RUN_FROM_PACKAGE"] = "1";
387-
await webMgmtClient.WebApps.UpdateApplicationSettingsAsync(resourceGroupName, functionAppSiteName, settings);
372+
await webMgmtClient.WebApps.UpdateApplicationSettingsAsync(resourceGroupName, site.Name, settings);
388373

389374
var packageInfo = PrepareZipPackage();
390375

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

429414
private void AddVariables(CommandTestBuilderContext context)
430415
{
431-
context.Variables.Add(AccountVariables.ClientId, clientId);
432-
context.Variables.Add(AccountVariables.Password, clientSecret);
433-
context.Variables.Add(AccountVariables.TenantId, tenantId);
434-
context.Variables.Add(AccountVariables.SubscriptionId, subscriptionId);
435-
context.Variables.Add("Octopus.Action.Azure.ResourceGroupName", resourceGroupName);
436-
context.Variables.Add("Octopus.Action.Azure.WebAppName", functionAppSiteName);
416+
AddAzureVariables(context);
437417
context.Variables.Add(SpecialVariables.Action.Azure.DeploymentType, "ZipDeploy");
438418
}
439419
}
440420
}
441-
442-
public abstract class AppServiceIntegrationTest
443-
{
444-
protected string clientId;
445-
protected string clientSecret;
446-
protected string tenantId;
447-
protected string subscriptionId;
448-
protected string resourceGroupName;
449-
protected string resourceGroupLocation;
450-
protected string greeting = "Calamari";
451-
protected string authToken;
452-
protected WebSiteManagementClient webMgmtClient;
453-
protected Site site;
454-
455-
private ResourceGroupsOperations resourceGroupClient;
456-
private readonly HttpClient client = new HttpClient();
457-
458-
[OneTimeSetUp]
459-
public async Task Setup()
460-
{
461-
var resourceManagementEndpointBaseUri =
462-
Environment.GetEnvironmentVariable(AccountVariables.ResourceManagementEndPoint) ??
463-
DefaultVariables.ResourceManagementEndpoint;
464-
var activeDirectoryEndpointBaseUri =
465-
Environment.GetEnvironmentVariable(AccountVariables.ActiveDirectoryEndPoint) ??
466-
DefaultVariables.ActiveDirectoryEndpoint;
467-
468-
resourceGroupName = Guid.NewGuid().ToString();
469-
470-
clientId = ExternalVariables.Get(ExternalVariable.AzureSubscriptionClientId);
471-
clientSecret = ExternalVariables.Get(ExternalVariable.AzureSubscriptionPassword);
472-
tenantId = ExternalVariables.Get(ExternalVariable.AzureSubscriptionTenantId);
473-
subscriptionId = ExternalVariables.Get(ExternalVariable.AzureSubscriptionId);
474-
resourceGroupLocation = Environment.GetEnvironmentVariable("AZURE_NEW_RESOURCE_REGION") ?? "eastus";
475-
476-
authToken = await Auth.GetAuthTokenAsync(activeDirectoryEndpointBaseUri, resourceManagementEndpointBaseUri,
477-
tenantId, clientId, clientSecret);
478-
479-
var resourcesClient = new ResourcesManagementClient(subscriptionId,
480-
new ClientSecretCredential(tenantId, clientId, clientSecret));
481-
482-
resourceGroupClient = resourcesClient.ResourceGroups;
483-
484-
var resourceGroup = new ResourceGroup(resourceGroupLocation);
485-
resourceGroup = await resourceGroupClient.CreateOrUpdateAsync(resourceGroupName, resourceGroup);
486-
487-
webMgmtClient = new WebSiteManagementClient(new TokenCredentials(authToken))
488-
{
489-
SubscriptionId = subscriptionId,
490-
HttpClient = { BaseAddress = new Uri(DefaultVariables.ResourceManagementEndpoint) },
491-
};
492-
493-
await ConfigureTestResources(resourceGroup);
494-
}
495-
496-
protected abstract Task ConfigureTestResources(ResourceGroup resourceGroup);
497-
498-
[OneTimeTearDown]
499-
public async Task Cleanup()
500-
{
501-
if (resourceGroupClient != null)
502-
await resourceGroupClient.StartDeleteAsync(resourceGroupName);
503-
}
504-
505-
protected async Task AssertContent(string hostName, string actualText, string rootPath = null)
506-
{
507-
var result = await client.GetStringAsync($"https://{hostName}/{rootPath}");
508-
509-
result.Should().Contain(actualText);
510-
}
511-
512-
protected static async Task DoWithRetries(int retries, Func<Task> action, int secondsBetweenRetries)
513-
{
514-
foreach (var retry in Enumerable.Range(1, retries))
515-
{
516-
try
517-
{
518-
await action();
519-
break;
520-
}
521-
catch
522-
{
523-
if (retry == retries)
524-
throw;
525-
526-
await Task.Delay(secondsBetweenRetries * 1000);
527-
}
528-
}
529-
}
530-
}
531421
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,117 @@
1+
using System;
2+
using System.Linq;
3+
using System.Net.Http;
4+
using System.Threading.Tasks;
5+
using Azure.Identity;
6+
using Azure.ResourceManager.Resources;
7+
using Azure.ResourceManager.Resources.Models;
8+
using Calamari.Azure;
9+
using Calamari.Tests.Shared;
10+
using FluentAssertions;
11+
using Microsoft.Azure.Management.WebSites;
12+
using Microsoft.Azure.Management.WebSites.Models;
13+
using Microsoft.Rest;
14+
using NUnit.Framework;
15+
16+
namespace Calamari.AzureAppService.Tests
17+
{
18+
public abstract class AppServiceIntegrationTest
19+
{
20+
protected string clientId;
21+
protected string clientSecret;
22+
protected string tenantId;
23+
protected string subscriptionId;
24+
protected string resourceGroupName;
25+
protected string resourceGroupLocation;
26+
protected string greeting = "Calamari";
27+
protected string authToken;
28+
protected WebSiteManagementClient webMgmtClient;
29+
protected Site site;
30+
31+
private ResourceGroupsOperations resourceGroupClient;
32+
private readonly HttpClient client = new HttpClient();
33+
34+
[OneTimeSetUp]
35+
public async Task Setup()
36+
{
37+
var resourceManagementEndpointBaseUri =
38+
Environment.GetEnvironmentVariable(AccountVariables.ResourceManagementEndPoint) ??
39+
DefaultVariables.ResourceManagementEndpoint;
40+
var activeDirectoryEndpointBaseUri =
41+
Environment.GetEnvironmentVariable(AccountVariables.ActiveDirectoryEndPoint) ??
42+
DefaultVariables.ActiveDirectoryEndpoint;
43+
44+
resourceGroupName = Guid.NewGuid().ToString();
45+
46+
clientId = ExternalVariables.Get(ExternalVariable.AzureSubscriptionClientId);
47+
clientSecret = ExternalVariables.Get(ExternalVariable.AzureSubscriptionPassword);
48+
tenantId = ExternalVariables.Get(ExternalVariable.AzureSubscriptionTenantId);
49+
subscriptionId = ExternalVariables.Get(ExternalVariable.AzureSubscriptionId);
50+
resourceGroupLocation = Environment.GetEnvironmentVariable("AZURE_NEW_RESOURCE_REGION") ?? "eastus";
51+
52+
authToken = await Auth.GetAuthTokenAsync(activeDirectoryEndpointBaseUri, resourceManagementEndpointBaseUri,
53+
tenantId, clientId, clientSecret);
54+
55+
var resourcesClient = new ResourcesManagementClient(subscriptionId,
56+
new ClientSecretCredential(tenantId, clientId, clientSecret));
57+
58+
resourceGroupClient = resourcesClient.ResourceGroups;
59+
60+
var resourceGroup = new ResourceGroup(resourceGroupLocation);
61+
resourceGroup = await resourceGroupClient.CreateOrUpdateAsync(resourceGroupName, resourceGroup);
62+
63+
webMgmtClient = new WebSiteManagementClient(new TokenCredentials(authToken))
64+
{
65+
SubscriptionId = subscriptionId,
66+
HttpClient = { BaseAddress = new Uri(DefaultVariables.ResourceManagementEndpoint) },
67+
};
68+
69+
await ConfigureTestResources(resourceGroup);
70+
}
71+
72+
protected abstract Task ConfigureTestResources(ResourceGroup resourceGroup);
73+
74+
[OneTimeTearDown]
75+
public async Task Cleanup()
76+
{
77+
if (resourceGroupClient != null)
78+
await resourceGroupClient.StartDeleteAsync(resourceGroupName);
79+
}
80+
81+
protected async Task AssertContent(string hostName, string actualText, string rootPath = null)
82+
{
83+
var result = await client.GetStringAsync($"https://{hostName}/{rootPath}");
84+
85+
result.Should().Contain(actualText);
86+
}
87+
88+
protected static async Task DoWithRetries(int retries, Func<Task> action, int secondsBetweenRetries)
89+
{
90+
foreach (var retry in Enumerable.Range(1, retries))
91+
{
92+
try
93+
{
94+
await action();
95+
break;
96+
}
97+
catch
98+
{
99+
if (retry == retries)
100+
throw;
101+
102+
await Task.Delay(secondsBetweenRetries * 1000);
103+
}
104+
}
105+
}
106+
107+
protected void AddAzureVariables(CommandTestBuilderContext context)
108+
{
109+
context.Variables.Add(AccountVariables.ClientId, clientId);
110+
context.Variables.Add(AccountVariables.Password, clientSecret);
111+
context.Variables.Add(AccountVariables.TenantId, tenantId);
112+
context.Variables.Add(AccountVariables.SubscriptionId, subscriptionId);
113+
context.Variables.Add("Octopus.Action.Azure.ResourceGroupName", resourceGroupName);
114+
context.Variables.Add("Octopus.Action.Azure.WebAppName", site.Name);
115+
}
116+
}
117+
}

source/Calamari/Azure/AzureClient.cs

+22
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,8 @@
11
using System.Net;
22
using System.Net.Http;
3+
using Azure.Core.Pipeline;
4+
using Azure.Identity;
5+
using Azure.ResourceManager;
36
using Microsoft.Azure.Management.Fluent;
47
using Microsoft.Azure.Management.ResourceManager.Fluent;
58

@@ -23,5 +26,24 @@ public static IAzure CreateAzureClient(this ServicePrincipalAccount servicePrinc
2326
.Authenticate(credentials)
2427
.WithSubscription(servicePrincipal.SubscriptionNumber);
2528
}
29+
30+
/// <summary>
31+
/// Creates an ArmClient for the new Azure SDK, which replaces the older fluent libraries.
32+
/// We should migrate to this SDK once it stabilises.
33+
/// </summary>
34+
/// <param name="servicePrincipal">Service Principal Account to use when connecting to Azure</param>
35+
/// <returns></returns>
36+
public static ArmClient CreateArmClient(this ServicePrincipalAccount servicePrincipal)
37+
{
38+
var environment = new AzureKnownEnvironment(servicePrincipal.AzureEnvironment).AsAzureArmEnvironment();
39+
40+
var httpClientTransport = new HttpClientTransport(new HttpClientHandler { Proxy = WebRequest.DefaultWebProxy });
41+
42+
var tokenCredentialOptions = new TokenCredentialOptions { Transport = httpClientTransport };
43+
var credential = new ClientSecretCredential(servicePrincipal.TenantId, servicePrincipal.ClientId, servicePrincipal.Password, tokenCredentialOptions);
44+
45+
var armClientOptions = new ArmClientOptions() { Transport = httpClientTransport, Environment = environment };
46+
return new ArmClient(credential, defaultSubscriptionId: servicePrincipal.SubscriptionNumber, armClientOptions);
47+
}
2648
}
2749
}

source/Calamari/Azure/AzureKnownEnvironment.cs

+15-3
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
using System;
2+
using Azure.ResourceManager;
23
using Microsoft.Azure.Management.ResourceManager.Fluent;
34

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

18-
azureEnvironment = AzureEnvironment.FromName(Value) ??
19+
azureSdkEnvironment = AzureEnvironment.FromName(Value) ??
1920
throw new InvalidOperationException($"Unknown environment name {Value}");
2021
}
2122

22-
private readonly AzureEnvironment azureEnvironment;
23+
private readonly AzureEnvironment azureSdkEnvironment;
2324
public string Value { get; }
2425

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

3031
public AzureEnvironment AsAzureSDKEnvironment()
3132
{
32-
return azureEnvironment;
33+
return azureSdkEnvironment;
3334
}
35+
36+
public ArmEnvironment AsAzureArmEnvironment() => ToArmEnvironment(Value);
37+
38+
private static ArmEnvironment ToArmEnvironment(string name) => name switch
39+
{
40+
"AzureGlobalCloud" => ArmEnvironment.AzurePublicCloud,
41+
"AzureChinaCloud" => ArmEnvironment.AzureChina,
42+
"AzureGermanCloud" => ArmEnvironment.AzureGermany,
43+
"AzureUSGovernment" => ArmEnvironment.AzureGovernment,
44+
_ => throw new InvalidOperationException($"ARM Environment {name} is not a known Azure Environment name.")
45+
};
3446
}
3547
}

source/Calamari/Calamari.csproj

+2
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,8 @@
1616
<TargetFramework>net5.0</TargetFramework>
1717
</PropertyGroup>
1818
<ItemGroup>
19+
<PackageReference Include="Azure.Identity" Version="1.2.3" />
20+
<PackageReference Include="Azure.ResourceManager.AppService" Version="1.0.0-beta.2" />
1921
<PackageReference Include="Calamari.Common" Version="21.1.0" />
2022
<PackageReference Include="Microsoft.Azure.Management.AppService.Fluent" Version="1.37.1" />
2123
<PackageReference Include="Microsoft.Azure.Management.Fluent" Version="1.37.1" />

0 commit comments

Comments
 (0)