Skip to content

Commit

Permalink
Reserving "runtime" route
Browse files Browse the repository at this point in the history
  • Loading branch information
mathewc committed Sep 17, 2018
1 parent b490a2c commit cd0af65
Show file tree
Hide file tree
Showing 3 changed files with 81 additions and 68 deletions.
5 changes: 3 additions & 2 deletions src/WebJobs.Script.WebHost/Controllers/FunctionsController.cs
Original file line number Diff line number Diff line change
Expand Up @@ -121,14 +121,15 @@ public IActionResult Invoke(string name, [FromBody] FunctionInvocation invocatio
[HttpGet]
[Route("admin/functions/{name}/status")]
[Authorize(Policy = PolicyNames.AdminAuthLevel)]
public async Task<IActionResult> GetFunctionStatus(string name, [FromServices] IScriptJobHost scriptHost)
public async Task<IActionResult> GetFunctionStatus(string name, [FromServices] IScriptJobHost scriptHost = null)
{
FunctionStatus status = new FunctionStatus();

// first see if the function has any errors
// if the host is not running or is offline
// there will be no error info
if (scriptHost.FunctionErrors.TryGetValue(name, out ICollection<string> functionErrors))
if (scriptHost != null &&
scriptHost.FunctionErrors.TryGetValue(name, out ICollection<string> functionErrors))
{
status.Errors = functionErrors;
}
Expand Down
3 changes: 2 additions & 1 deletion src/WebJobs.Script/Host/ScriptHost.cs
Original file line number Diff line number Diff line change
Expand Up @@ -764,7 +764,8 @@ internal static void ValidateHttpFunction(string functionName, HttpTriggerAttrib

// disallow custom routes in our own reserved route space
string httpRoute = httpTrigger.Route.Trim('/').ToLowerInvariant();
if (httpRoute.StartsWith("admin"))
if (httpRoute.StartsWith("admin") ||
httpRoute.StartsWith("runtime"))
{
throw new InvalidOperationException("The specified route conflicts with one or more built in routes.");
}
Expand Down
141 changes: 76 additions & 65 deletions test/WebJobs.Script.Tests/ScriptHostTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -927,67 +927,6 @@ public async Task InitializeRpcService_Throws()
Assert.Equal("Failed to start Grpc Service. Check if your app is hitting connection limits.", ex.Message);
}

#if WEBROUTING
[Fact]
public void HttpRoutesConflict_ReturnsExpectedResult()
{
var first = new HttpTriggerAttribute
{
Route = "foo/bar/baz"
};
var second = new HttpTriggerAttribute
{
Route = "foo/bar"
};
Assert.False(ScriptHost.HttpRoutesConflict(first, second));
Assert.False(ScriptHost.HttpRoutesConflict(second, first));

first = new HttpTriggerAttribute
{
Route = "foo/bar/baz"
};
second = new HttpTriggerAttribute
{
Route = "foo/bar/baz"
};
Assert.True(ScriptHost.HttpRoutesConflict(first, second));
Assert.True(ScriptHost.HttpRoutesConflict(second, first));

// no conflict since methods do not intersect
first = new HttpTriggerAttribute(AuthorizationLevel.Function, "get", "head")
{
Route = "foo/bar/baz"
};
second = new HttpTriggerAttribute(AuthorizationLevel.Function, "post", "put")
{
Route = "foo/bar/baz"
};
Assert.False(ScriptHost.HttpRoutesConflict(first, second));
Assert.False(ScriptHost.HttpRoutesConflict(second, first));

first = new HttpTriggerAttribute(AuthorizationLevel.Function, "get", "head")
{
Route = "foo/bar/baz"
};
second = new HttpTriggerAttribute
{
Route = "foo/bar/baz"
};
Assert.True(ScriptHost.HttpRoutesConflict(first, second));
Assert.True(ScriptHost.HttpRoutesConflict(second, first));

first = new HttpTriggerAttribute(AuthorizationLevel.Function, "get", "head", "put", "post")
{
Route = "foo/bar/baz"
};
second = new HttpTriggerAttribute(AuthorizationLevel.Function, "put")
{
Route = "foo/bar/baz"
};
Assert.True(ScriptHost.HttpRoutesConflict(first, second));
Assert.True(ScriptHost.HttpRoutesConflict(second, first));
}

[Fact]
public void ValidateFunction_ValidatesHttpRoutes()
{
Expand Down Expand Up @@ -1056,16 +995,88 @@ public void ValidateFunction_ValidatesHttpRoutes()
});
Assert.Equal("The specified route conflicts with one or more built in routes.", ex.Message);

// verify that empty route is defaulted to function name
// try to add a route under reserved runtime route
function = new Mock<FunctionDescriptor>(MockBehavior.Strict, "test6", null, metadata, null, null, null, null);
attribute = new HttpTriggerAttribute
{
Route = "runtime/foo/bar"
};
function.Setup(p => p.GetTriggerAttributeOrNull<HttpTriggerAttribute>()).Returns(() => attribute);
ex = Assert.Throws<InvalidOperationException>(() =>
{
ScriptHost.ValidateFunction(function.Object, httpFunctions);
});
Assert.Equal("The specified route conflicts with one or more built in routes.", ex.Message);

// verify that empty route is defaulted to function name
function = new Mock<FunctionDescriptor>(MockBehavior.Strict, "test7", null, metadata, null, null, null, null);
attribute = new HttpTriggerAttribute();
function.Setup(p => p.GetTriggerAttributeOrNull<HttpTriggerAttribute>()).Returns(() => attribute);
ScriptHost.ValidateFunction(function.Object, httpFunctions);
Assert.Equal(4, httpFunctions.Count);
Assert.True(httpFunctions.ContainsKey("test6"));
Assert.Equal("test6", attribute.Route);
Assert.True(httpFunctions.ContainsKey("test7"));
Assert.Equal("test7", attribute.Route);
}

[Fact]
public void HttpRoutesConflict_ReturnsExpectedResult()
{
var first = new HttpTriggerAttribute
{
Route = "foo/bar/baz"
};
var second = new HttpTriggerAttribute
{
Route = "foo/bar"
};
Assert.False(ScriptHost.HttpRoutesConflict(first, second));
Assert.False(ScriptHost.HttpRoutesConflict(second, first));

first = new HttpTriggerAttribute
{
Route = "foo/bar/baz"
};
second = new HttpTriggerAttribute
{
Route = "foo/bar/baz"
};
Assert.True(ScriptHost.HttpRoutesConflict(first, second));
Assert.True(ScriptHost.HttpRoutesConflict(second, first));

// no conflict since methods do not intersect
first = new HttpTriggerAttribute(AuthorizationLevel.Function, "get", "head")
{
Route = "foo/bar/baz"
};
second = new HttpTriggerAttribute(AuthorizationLevel.Function, "post", "put")
{
Route = "foo/bar/baz"
};
Assert.False(ScriptHost.HttpRoutesConflict(first, second));
Assert.False(ScriptHost.HttpRoutesConflict(second, first));

first = new HttpTriggerAttribute(AuthorizationLevel.Function, "get", "head")
{
Route = "foo/bar/baz"
};
second = new HttpTriggerAttribute
{
Route = "foo/bar/baz"
};
Assert.True(ScriptHost.HttpRoutesConflict(first, second));
Assert.True(ScriptHost.HttpRoutesConflict(second, first));

first = new HttpTriggerAttribute(AuthorizationLevel.Function, "get", "head", "put", "post")
{
Route = "foo/bar/baz"
};
second = new HttpTriggerAttribute(AuthorizationLevel.Function, "put")
{
Route = "foo/bar/baz"
};
Assert.True(ScriptHost.HttpRoutesConflict(first, second));
Assert.True(ScriptHost.HttpRoutesConflict(second, first));
}
#endif

[Fact]
public void ValidateFunction_ThrowsOnDuplicateName()
Expand Down

0 comments on commit cd0af65

Please sign in to comment.