Skip to content

Commit 7789dd4

Browse files
committed
adding e2e tests for ServiceBus connection exhaustion
1 parent ebe3825 commit 7789dd4

File tree

7 files changed

+151
-5
lines changed

7 files changed

+151
-5
lines changed

test/WebJobs.Script.Tests.E2E/Constants.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,5 +17,6 @@ public class Constants
1717
public const string TargetSitePublishingUserSettingName = "AzureWebJobsTargetSitePublishingUser";
1818
public const string TargetSitePublishingPasswordSettingName = "AzureWebJobsTargetSitePublishingPassword";
1919
public const string RuntimeExtensionPackageUrlSettingName = "AzureWebjobsRuntimePrivateExtensionPackageUrl";
20+
public const string ServiceBusKey = "AzureWebJobsServiceBus";
2021
}
2122
}

test/WebJobs.Script.Tests.E2E/FunctionAppFixture.cs

Lines changed: 44 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -8,9 +8,12 @@
88
using System.IO.Compression;
99
using System.Linq;
1010
using System.Net.Http;
11+
using System.Text;
1112
using System.Threading.Tasks;
1213
using Microsoft.ApplicationInsights;
1314
using Microsoft.ApplicationInsights.DataContracts;
15+
using Newtonsoft.Json;
16+
using Newtonsoft.Json.Linq;
1417
using Xunit;
1518

1619
namespace WebJobs.Script.EndToEndTests
@@ -50,6 +53,8 @@ private async Task InitializeSite()
5053

5154
await StopSite();
5255

56+
await AddSettings();
57+
5358
_kuduClient = new KuduClient($"https://{Settings.SiteName}.scm.azurewebsites.net", Settings.SitePublishingUser, Settings.SitePublishingPassword);
5459

5560
await UpdateRuntime();
@@ -95,6 +100,13 @@ private async Task InitializeFunctionKeys()
95100
}
96101
}
97102

103+
private async Task AddSettings()
104+
{
105+
Trace.WriteLine("Updating app settings...");
106+
Telemetry.TrackEvent("SettingsUpdate");
107+
await AddAppSetting(Constants.ServiceBusKey, Environment.GetEnvironmentVariable(Constants.ServiceBusKey));
108+
}
109+
98110
private async Task UpdateSiteContents()
99111
{
100112
Trace.WriteLine("Updating site contents...");
@@ -145,23 +157,51 @@ public async Task StartSite()
145157
await IssueSiteCommand($"/subscriptions/{Settings.SiteSubscriptionId}/resourceGroups/{Settings.SiteResourceGroup}/providers/Microsoft.Web/sites/{Settings.SiteName}/start?api-version=2015-08-01");
146158
}
147159

148-
private async Task IssueSiteCommand(string commandUri)
160+
public async Task AddAppSetting(string name, string value)
161+
{
162+
string updateUri = $"/subscriptions/{Settings.SiteSubscriptionId}/resourceGroups/{Settings.SiteResourceGroup}/providers/Microsoft.Web/sites/{Settings.SiteName}/config/appSettings";
163+
string listUri = $"{updateUri}/list";
164+
165+
// We need to roundtrip these settings
166+
JObject settings = await IssueSiteCommand($"{listUri}?api-version=2015-08-01", delayInMs: 0);
167+
168+
// add or overwrite the existing setting
169+
settings["properties"][name] = value;
170+
171+
HttpRequestMessage request = new HttpRequestMessage(HttpMethod.Put, $"{updateUri}?api-version=2015-08-01")
172+
{
173+
Content = new StringContent(settings.ToString(Formatting.None), Encoding.UTF8, "application/json")
174+
};
175+
176+
// Site is stopped; no need to wait.
177+
await IssueSiteCommand(request, delayInMs: 0);
178+
}
179+
180+
private async Task<JObject> IssueSiteCommand(string commandUri, int delayInMs = 5000)
181+
{
182+
return await IssueSiteCommand(new HttpRequestMessage(HttpMethod.Post, commandUri), delayInMs);
183+
}
184+
185+
private async Task<JObject> IssueSiteCommand(HttpRequestMessage request, int delayInMs = 5000)
149186
{
150187
string token = await ArmAuthenticationHelpers.AcquireTokenBySPN(Settings.SiteTenantId, Settings.SiteApplicationId, Settings.SiteClientSecret);
151188

189+
JObject responseContent = null;
152190
using (var client = new HttpClient())
153191
{
154192
client.DefaultRequestHeaders.Add("Authorization", $"Bearer {token}");
155193
client.BaseAddress = new Uri("https://management.azure.com/");
156194

157-
158-
using (var response = await client.PostAsync(commandUri, null))
195+
using (var response = await client.SendAsync(request))
159196
{
160197
response.EnsureSuccessStatusCode();
198+
responseContent = await response.Content.ReadAsAsync<JObject>();
161199
}
162200
}
163201

164-
await Task.Delay(5000);
202+
await Task.Delay(delayInMs);
203+
204+
return responseContent;
165205
}
166206

167207
protected virtual void Dispose(bool disposing)
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
{
2+
"bindings": [
3+
{
4+
"authLevel": "function",
5+
"name": "req",
6+
"type": "httpTrigger",
7+
"direction": "in"
8+
},
9+
{
10+
"name": "$return",
11+
"type": "http",
12+
"direction": "out"
13+
},
14+
{
15+
"type": "serviceBus",
16+
"name": "outputSbMsg",
17+
"queueName": "node",
18+
"connection": "AzureWebJobsServiceBus",
19+
"accessRights_": "Manage",
20+
"direction": "out"
21+
}
22+
],
23+
"disabled": false
24+
}
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
module.exports = function (context, req) {
2+
var counters = JSON.parse(process.env.WEBSITE_COUNTERS_APP);
3+
var connections = {
4+
connections: counters.connections,
5+
connectionLimit: counters.connectionLimit
6+
};
7+
context.bindings.outputSbMsg = "test";
8+
context.done(null, connections);
9+
};

test/WebJobs.Script.Tests.E2E/GeneralEndToEndTests.cs

Lines changed: 60 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,8 @@
1010
using System.Threading.Tasks;
1111
using Microsoft.ApplicationInsights;
1212
using Microsoft.ApplicationInsights.DataContracts;
13+
using Microsoft.ServiceBus;
14+
using Microsoft.ServiceBus.Messaging;
1315
using Newtonsoft.Json.Linq;
1416
using Xunit;
1517

@@ -136,14 +138,71 @@ public async Task Invocation_Logs_AreReturned()
136138
{
137139
break;
138140
}
139-
141+
140142
await Task.Delay(3000);
141143
}
142144

143145
_fixture.Assert.True(resultToken != null);
144146
}
145147
}
146148

149+
[Fact]
150+
[TestTrace]
151+
public async Task ServiceBus_Node_DoesNotExhaustConnections()
152+
{
153+
var connectionString = Environment.GetEnvironmentVariable("AzureWebJobsServiceBus");
154+
NamespaceManager manager = NamespaceManager.CreateFromConnectionString(connectionString);
155+
156+
// Start with an empty queue
157+
await manager.DeleteQueueAsync("node");
158+
159+
// Pre-create the queue as we can end up with 409s if a bunch of requests
160+
// try to create the queue at once
161+
await manager.CreateQueueAsync("node");
162+
163+
int i = 0, j = 0, lastConnectionCount = 0, lastConnectionLimit = 0;
164+
165+
using (var client = CreateClient())
166+
{
167+
// make this longer as we'll start seeing long timeouts from Service Bus upon failure.
168+
client.Timeout = TimeSpan.FromMinutes(5);
169+
170+
// max connections in dynamic is currently 300
171+
for (i = 0; i < 25; i++)
172+
{
173+
List<Task<HttpResponseMessage>> requestTasks = new List<Task<HttpResponseMessage>>();
174+
175+
for (j = 0; j < 25; j++)
176+
{
177+
requestTasks.Add(client.GetAsync($"api/ServiceBusNode?code={_fixture.FunctionDefaultKey}"));
178+
}
179+
180+
await Task.WhenAll(requestTasks);
181+
182+
foreach (var requestTask in requestTasks)
183+
{
184+
HttpResponseMessage response = await requestTask;
185+
JObject result = await response.Content.ReadAsAsync<JObject>();
186+
187+
if (response.IsSuccessStatusCode)
188+
{
189+
// store these off for error details
190+
lastConnectionCount = (int)result["connections"];
191+
lastConnectionLimit = (int)result["connectionLimit"];
192+
193+
// make sure we have the correct limit
194+
Assert.Equal(300, lastConnectionLimit);
195+
}
196+
197+
Assert.True(response.IsSuccessStatusCode, $"Error: {response.StatusCode}, Last successful response: Connections: {lastConnectionCount}, ConnectionLimit: {lastConnectionLimit}");
198+
}
199+
}
200+
}
201+
202+
QueueDescription queueDescription = manager.GetQueue("node");
203+
Assert.Equal(i * j, queueDescription.MessageCountDetails.ActiveMessageCount);
204+
}
205+
147206
// Assumes we have a valid function name.
148207
// Function names are case-insensitive, case-preserving.
149208
// Table storage is case-sensitive. So need to normalize case to use as table keys.

test/WebJobs.Script.Tests.E2E/WebJobs.Script.Tests.E2E.csproj

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,9 @@
4343
<Private>True</Private>
4444
</Reference>
4545
<Reference Include="Microsoft.CSharp" />
46+
<Reference Include="Microsoft.ServiceBus, Version=3.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
47+
<HintPath>..\..\packages\WindowsAzure.ServiceBus.4.1.0\lib\net45-full\Microsoft.ServiceBus.dll</HintPath>
48+
</Reference>
4649
<Reference Include="Newtonsoft.Json, Version=9.0.0.0, Culture=neutral, PublicKeyToken=30ad4fe6b2a6aeed, processorArchitecture=MSIL">
4750
<HintPath>..\..\packages\Newtonsoft.Json.9.0.1\lib\net45\Newtonsoft.Json.dll</HintPath>
4851
<Private>True</Private>
@@ -56,6 +59,9 @@
5659
<HintPath>..\..\packages\Microsoft.AspNet.WebApi.Client.5.2.3\lib\net45\System.Net.Http.Formatting.dll</HintPath>
5760
<Private>True</Private>
5861
</Reference>
62+
<Reference Include="System.Runtime.Serialization" />
63+
<Reference Include="System.ServiceModel" />
64+
<Reference Include="System.Xml" />
5965
<Reference Include="xunit.abstractions, Version=2.0.0.0, Culture=neutral, PublicKeyToken=8d05b1bb7a6fdb6c, processorArchitecture=MSIL">
6066
<HintPath>..\..\packages\xunit.abstractions.2.0.1\lib\net35\xunit.abstractions.dll</HintPath>
6167
<Private>True</Private>
@@ -100,6 +106,12 @@
100106
</ItemGroup>
101107
<ItemGroup>
102108
<None Include="app.config" />
109+
<None Include="Functions\wwwroot\ServiceBusNode\function.json">
110+
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
111+
</None>
112+
<Content Include="Functions\wwwroot\ServiceBusNode\index.js">
113+
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
114+
</Content>
103115
<None Include="Functions\wwwroot\GetInvocationId\function.json">
104116
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
105117
</None>

test/WebJobs.Script.Tests.E2E/packages.config

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
<package id="Microsoft.ApplicationInsights" version="2.2.0" targetFramework="net461" />
44
<package id="Microsoft.AspNet.WebApi.Client" version="5.2.3" targetFramework="net461" />
55
<package id="Newtonsoft.Json" version="9.0.1" targetFramework="net461" />
6+
<package id="WindowsAzure.ServiceBus" version="4.1.0" targetFramework="net461" />
67
<package id="xunit" version="2.2.0" targetFramework="net461" />
78
<package id="xunit.abstractions" version="2.0.1" targetFramework="net461" />
89
<package id="xunit.assert" version="2.2.0" targetFramework="net461" />

0 commit comments

Comments
 (0)