Skip to content

Commit a615d5a

Browse files
author
Matt Mason
committed
Handle listener errors
1 parent 8c27056 commit a615d5a

File tree

10 files changed

+127
-9
lines changed

10 files changed

+127
-9
lines changed

src/WebJobs.Script/Host/ScriptHost.cs

Lines changed: 9 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
using Microsoft.Azure.WebJobs.Extensions.BotFramework.Bindings;
2121
using Microsoft.Azure.WebJobs.Host;
2222
using Microsoft.Azure.WebJobs.Host.Indexers;
23+
using Microsoft.Azure.WebJobs.Host.Listeners;
2324
using Microsoft.Azure.WebJobs.Script.Binding;
2425
using Microsoft.Azure.WebJobs.Script.Config;
2526
using Microsoft.Azure.WebJobs.Script.Description;
@@ -1087,19 +1088,19 @@ private void HandleHostError(Exception exception)
10871088
FunctionInvocationException invocationException = exception as FunctionInvocationException;
10881089
NotifyInvoker(invocationException.MethodName, invocationException);
10891090
}
1090-
else if (exception is FunctionIndexingException)
1091+
else if (exception is FunctionIndexingException || exception is FunctionListenerException)
10911092
{
1092-
// For all startup time indexing errors, we accumulate them per function
1093-
FunctionIndexingException indexingException = exception as FunctionIndexingException;
1094-
string formattedError = Utility.FlattenException(indexingException);
1095-
AddFunctionError(FunctionErrors, indexingException.MethodName, formattedError);
1093+
// For all startup time indexing/listener errors, we accumulate them per function
1094+
FunctionException functionException = exception as FunctionException;
1095+
string formattedError = Utility.FlattenException(functionException);
1096+
AddFunctionError(FunctionErrors, functionException.MethodName, formattedError);
10961097

10971098
// Also notify the invoker so the error can also be written to the function
10981099
// log file
1099-
NotifyInvoker(indexingException.MethodName, indexingException);
1100+
NotifyInvoker(functionException.MethodName, functionException);
11001101

1101-
// Mark the error as handled so indexing will continue
1102-
indexingException.Handled = true;
1102+
// Mark the error as handled so execution will continue with this function disabled
1103+
functionException.Handled = true;
11031104
}
11041105
else
11051106
{

src/WebJobs.Script/azurefunctions/functions.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -107,7 +107,7 @@ function getEntryPoint(f, context) {
107107
f = f[context._entryPoint];
108108
delete context._entryPoint;
109109
}
110-
else if (Object.keys(f).length == 1) {
110+
else if (Object.keys(f).length === 1) {
111111
// a single named function was exported
112112
var name = Object.keys(f)[0];
113113
f = f[name];

test/WebJobs.Script.Tests.Integration/EndToEndTestFixture.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,7 @@ protected EndToEndTestFixture(string rootPath, string testId)
5151
// Reset the timer logs first, since one of the tests will
5252
// be checking them
5353
TestHelpers.ClearFunctionLogs("TimerTrigger");
54+
TestHelpers.ClearFunctionLogs("ListenerStartupException");
5455
Host = ScriptHost.Create(ScriptHostEnvironmentMock.Object, config, _settingsManager);
5556
Host.Start();
5657
}
Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
// Copyright (c) .NET Foundation. All rights reserved.
2+
// Licensed under the MIT License. See License.txt in the project root for license information.
3+
4+
using System.Collections.Generic;
5+
using System.Threading.Tasks;
6+
using Xunit;
7+
8+
namespace Microsoft.Azure.WebJobs.Script.Tests
9+
{
10+
public class ListenerFailureTests : EndToEndTestsBase<ListenerFailureTests.TestFixture>
11+
{
12+
public ListenerFailureTests(TestFixture fixture) : base(fixture)
13+
{
14+
}
15+
16+
[Fact]
17+
public async Task ListenerError_LogsAndDoesNotStopHost()
18+
{
19+
IList<string> logs = null;
20+
21+
await TestHelpers.Await(() =>
22+
{
23+
logs = TestHelpers.GetFunctionLogsAsync("ListenerStartupException", false).Result;
24+
return logs.Count > 0;
25+
});
26+
27+
// assert that listener error was captured
28+
Assert.Contains(logs, (log) => log.Contains("The listener for function 'Functions.ListenerStartupException' was unable to start."));
29+
30+
TestHelpers.ClearFunctionLogs("TimerTrigger");
31+
await TestHelpers.Await(() =>
32+
{
33+
logs = TestHelpers.GetFunctionLogsAsync("TimerTrigger", false).Result;
34+
return logs.Count > 0;
35+
});
36+
37+
// assert that timer function is still running
38+
Assert.Contains(logs, (log) => log.Contains("Timer function ran!"));
39+
40+
// assert that Stop does not throw error
41+
Fixture.Host.Stop();
42+
}
43+
44+
public class TestFixture : EndToEndTestFixture
45+
{
46+
public TestFixture() : base(@"TestScripts\ListenerExceptions", "listeners")
47+
{
48+
}
49+
50+
public override void Dispose()
51+
{
52+
// host should already be stopped from test
53+
try
54+
{
55+
Host.Stop();
56+
}
57+
catch
58+
{
59+
}
60+
Host.Dispose();
61+
ServiceBusQueueClient.Close();
62+
}
63+
}
64+
}
65+
}
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
{
2+
"bindings": [
3+
{
4+
"type": "serviceBusTrigger",
5+
"name": "inMessage",
6+
"direction": "in",
7+
"queueName": "samples-input-nonexistent",
8+
"accessRights": "listen"
9+
}
10+
]
11+
}
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
var util = require('util');
2+
3+
module.exports = function (context, inMessage) {
4+
// we don't expect this to execute, as the queue cannot be created with listen rights
5+
}
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
{
2+
"bindings": [
3+
{
4+
"type": "timerTrigger",
5+
"name": "timerInfo",
6+
"direction": "in",
7+
"runOnStartup": true,
8+
"schedule": "*/2 * * * * *"
9+
}
10+
]
11+
}
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
module.exports = function (context, timerInfo) {
2+
var timeStamp = new Date().toISOString();
3+
context.log('Timer function ran! ', timeStamp);
4+
context.done();
5+
}
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
{
2+
"id": "function-listener-failues"
3+
}

test/WebJobs.Script.Tests.Integration/WebJobs.Script.Tests.Integration.csproj

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -443,12 +443,19 @@
443443
<Compile Include="Host\ScriptHostManagerTests.cs" />
444444
<Compile Include="Host\StandbyModeTests.cs" />
445445
<Compile Include="Host\WebScriptHostManagerTests.cs" />
446+
<Compile Include="ListenerFailureTests.cs" />
446447
<Compile Include="WindowsBatchEndToEndTests.cs" />
447448
</ItemGroup>
448449
<ItemGroup Condition="'$(OS)'!='Unix'">
449450
<Compile Include="PowerShellEndToEndTests.cs" />
450451
</ItemGroup>
451452
<ItemGroup>
453+
<Content Include="TestScripts\ListenerExceptions\ListenerStartupException\index.js">
454+
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
455+
</Content>
456+
<Content Include="TestScripts\ListenerExceptions\TimerTrigger\index.js">
457+
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
458+
</Content>
452459
<Content Include="TestScripts\Node\ApiHubTableEntityIn\index.js">
453460
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
454461
</Content>
@@ -566,6 +573,15 @@
566573
<None Include="TestScripts\DotNet\host.json">
567574
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
568575
</None>
576+
<Content Include="TestScripts\ListenerExceptions\host.json">
577+
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
578+
</Content>
579+
<Content Include="TestScripts\ListenerExceptions\ListenerStartupException\function.json">
580+
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
581+
</Content>
582+
<Content Include="TestScripts\ListenerExceptions\TimerTrigger\function.json">
583+
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
584+
</Content>
569585
<None Include="TestScripts\Node\HttpTriggerShared\function.json">
570586
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
571587
</None>

0 commit comments

Comments
 (0)