public ScriptRouteHandler(ILoggerFactory loggerFactory, IScriptJobHost scriptHost, IEnvironment environment, bool isWarmup = false) { _scriptHost = scriptHost; _loggerFactory = loggerFactory; _environment = environment; _isWarmup = isWarmup; }
private void DisplayHttpFunctionsInfo(IScriptJobHost scriptHost, HttpOptions httpOptions, Uri baseUri) { if (scriptHost != null) { var httpFunctions = scriptHost.Functions.Where(f => f.Metadata.IsHttpFunction()); if (httpFunctions.Any()) { ColoredConsole .WriteLine() .WriteLine(Yellow("Http Functions:")) .WriteLine(); } foreach (var function in httpFunctions) { var binding = function.Metadata.Bindings.FirstOrDefault(b => b.Type != null && b.Type.Equals("httpTrigger", StringComparison.OrdinalIgnoreCase)); var httpRoute = binding?.Raw?.GetValue("route", StringComparison.OrdinalIgnoreCase)?.ToString(); httpRoute = httpRoute ?? function.Name; string hostRoutePrefix = ""; if (!function.Metadata.IsProxy) { hostRoutePrefix = httpOptions.RoutePrefix ?? "api/"; hostRoutePrefix = string.IsNullOrEmpty(hostRoutePrefix) || hostRoutePrefix.EndsWith("/") ? hostRoutePrefix : $"{hostRoutePrefix}/"; } var url = $"{baseUri.ToString().Replace("0.0.0.0", "localhost")}{hostRoutePrefix}{httpRoute}"; ColoredConsole .WriteLine($"\t{Yellow($"{function.Name}:")} {Green(url)}") .WriteLine(); } } }
public ScriptRouteHandler(ILoggerFactory loggerFactory, IScriptJobHost scriptHost, IEnvironment environment, bool isProxy) { _scriptHost = scriptHost; _loggerFactory = loggerFactory; _environment = environment; _isProxy = isProxy; }
public FunctionExecutionFeature(IScriptJobHost host, FunctionDescriptor descriptor, IEnvironment environment, ILoggerFactory loggerFactory) { _host = host ?? throw new ArgumentNullException(nameof(host)); _descriptor = descriptor; _environment = environment; _logger = loggerFactory.CreateLogger(ScriptConstants.LogCategoryHostMetrics); }
public async Task <IActionResult> Warmup([FromServices] IScriptHostManager scriptHostManager) { // Endpoint only for Windows Elastic Premium or Linux App Service plans if (!(_environment.IsLinuxAppService() || _environment.IsWindowsElasticPremium())) { return(BadRequest("This API is not available for the current hosting plan")); } if (Interlocked.CompareExchange(ref _warmupExecuted, 1, 0) != 0) { return(Ok()); } if (scriptHostManager is IServiceProvider serviceProvider) { IScriptJobHost jobHost = serviceProvider.GetService <IScriptJobHost>(); if (jobHost == null) { _logger.LogError($"No active host available."); return(StatusCode(503)); } await jobHost.TryInvokeWarmupAsync(); return(Ok()); } return(BadRequest("This API is not supported by the current hosting environment.")); }
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 != null && scriptHost.FunctionErrors.TryGetValue(name, out ICollection <string> functionErrors)) { status.Errors = functionErrors; } else { // if we don't have any errors registered, make sure the function exists // before returning empty errors var result = await _functionsManager.GetFunctionsMetadata(includeProxies : true); var function = result.FirstOrDefault(p => p.Name.ToLowerInvariant() == name.ToLowerInvariant()); if (function == null) { return(NotFound()); } } return(Ok(status)); }
public HttpInitializationService(IOptions <HttpOptions> httpOptions, IWebJobsRouter router, ILoggerFactory loggerFactory, IScriptJobHost host, IEnvironment environment) { _httpOptions = httpOptions; _router = router; _loggerFactory = loggerFactory; _host = host; _environment = environment; }
private void DisplayDisabledFunctions(IScriptJobHost scriptHost) { if (scriptHost != null) { foreach (var function in scriptHost.Functions.Where(f => f.Metadata.IsDisabled)) { ColoredConsole.WriteLine(WarningColor($"Function {function.Name} is disabled.")); } } }
/// <summary> /// Lookup a warmup function /// </summary> /// <returns>Warmup function or null if not found</returns> public static FunctionDescriptor GetWarmupFunctionOrNull(this IScriptJobHost scriptJobHost) { return(scriptJobHost.Functions.FirstOrDefault(f => { return IsFunctionNameMatch(f.Name, WarmupFunctionName) && f.Metadata .InputBindings .Any(b => b.IsTrigger && b.Type.Equals(WarmupTriggerName, StringComparison.OrdinalIgnoreCase)); })); }
private async Task <(IHost, IScriptJobHost, IScriptHostManager)> CreateAndStartWebScriptHost() { var functions = new Collection <string> { "TimeoutToken" }; var options = new ScriptJobHostOptions() { RootScriptPath = $@"TestScripts\CSharp", FileLoggingMode = FileLoggingMode.Always, Functions = functions, FunctionTimeout = TimeSpan.FromSeconds(3) }; var mockEventManager = new Mock <IScriptEventManager>(); var mockRouter = new Mock <IWebJobsRouter>(); var host = new HostBuilder() .ConfigureDefaultTestWebScriptHost() .ConfigureServices(s => { s.AddSingleton <IWebJobsRouter>(mockRouter.Object); s.AddSingleton <ISecretManagerProvider>(new TestSecretManagerProvider()); s.AddSingleton <IScriptEventManager>(mockEventManager.Object); s.AddSingleton <IOptions <ScriptJobHostOptions> >(new OptionsWrapper <ScriptJobHostOptions>(options)); s.AddSingleton <IOptions <ScriptApplicationHostOptions> >(new OptionsWrapper <ScriptApplicationHostOptions>(new ScriptApplicationHostOptions { SecretsPath = _secretsDirectory.Path })); s.AddSingleton <ILoggerFactory>(NullLoggerFactory.Instance); s.AddSingleton(ScriptSettingsManager.Instance); }) .Build(); await host.StartAsync(); var manager = host.Services.GetService <IScriptHostManager>(); await TestHelpers.Await(() => manager.State == ScriptHostState.Running, userMessageCallback : () => "Expected host to be running"); IScriptJobHost scriptHost = host.GetScriptHost(); return(host, scriptHost, manager); }
private void DisplayHttpFunctionsInfo(IScriptJobHost scriptHost, HttpOptions httpOptions, Uri baseUri) { if (scriptHost != null) { var httpFunctions = scriptHost.Functions.Where(f => f.Metadata.IsHttpFunction() && !f.Metadata.IsDisabled()); if (httpFunctions.Any()) { ColoredConsole .WriteLine() .WriteLine(DarkYellow("Http Functions:")) .WriteLine(); } foreach (var function in httpFunctions) { var binding = function.Metadata.Bindings.FirstOrDefault(b => b.Type != null && b.Type.Equals("httpTrigger", StringComparison.OrdinalIgnoreCase)); var httpRoute = binding?.Raw?.GetValue("route", StringComparison.OrdinalIgnoreCase)?.ToString(); httpRoute = httpRoute ?? function.Name; string[] methods = null; var methodsRaw = binding?.Raw?.GetValue("methods", StringComparison.OrdinalIgnoreCase)?.ToString(); if (string.IsNullOrEmpty(methodsRaw) == false) { methods = methodsRaw.Split(','); } string hostRoutePrefix = ""; if (!function.Metadata.IsProxy()) { hostRoutePrefix = httpOptions.RoutePrefix ?? "api/"; hostRoutePrefix = string.IsNullOrEmpty(hostRoutePrefix) || hostRoutePrefix.EndsWith("/") ? hostRoutePrefix : $"{hostRoutePrefix}/"; } var functionMethods = methods != null ? $"{CleanAndFormatHttpMethods(string.Join(",", methods))}" : null; var url = $"{baseUri.ToString().Replace("0.0.0.0", "localhost")}{hostRoutePrefix}{httpRoute}"; ColoredConsole .WriteLine($"\t{HttpFunctionNameColor($"{function.Name}:")} {HttpFunctionUrlColor(functionMethods)} {HttpFunctionUrlColor(url)}") .WriteLine(); } } }
/// <summary> /// Try to invoke a warmup function if available /// </summary> /// <returns> /// A task that represents the asynchronous operation. /// The task results true if a warmup function was invoked, false otherwise. /// </returns> public static async Task <bool> TryInvokeWarmupAsync(this IScriptJobHost scriptJobHost) { var warmupFunction = scriptJobHost.GetWarmupFunctionOrNull(); if (warmupFunction != null) { ParameterDescriptor inputParameter = warmupFunction.Parameters.First(p => p.IsTrigger); var arguments = new Dictionary <string, object>() { { inputParameter.Name, new WarmupContext() } }; await scriptJobHost.CallAsync(warmupFunction.Name, arguments); return(true); } return(false); }
public IActionResult GetFunctionStatus(string name, [FromServices] IScriptJobHost scriptHost) { FunctionStatus status = new FunctionStatus(); // first see if the function has any errors if (scriptHost.FunctionErrors.TryGetValue(name, out ICollection <string> functionErrors)) { status.Errors = functionErrors; } else { // if we don't have any errors registered, make sure the function exists // before returning empty errors FunctionDescriptor function = scriptHost.Functions.FirstOrDefault(p => p.Name.ToLowerInvariant() == name.ToLowerInvariant()); if (function == null) { return(NotFound()); } } return(Ok(status)); }
/// <summary> /// Lookup a function by name /// </summary> /// <param name="name">name of function</param> /// <returns>function or null if not found</returns> public static FunctionDescriptor GetFunctionOrNull(this IScriptJobHost scriptJobHost, string name) { return(scriptJobHost.Functions.FirstOrDefault(f => IsFunctionNameMatch(f.Name, name))); }
public IActionResult Invoke(string name, [FromBody] FunctionInvocation invocation, [FromServices] IScriptJobHost scriptHost) { if (invocation == null) { return(BadRequest()); } FunctionDescriptor function = scriptHost.GetFunctionOrNull(name); if (function == null) { return(NotFound()); } ParameterDescriptor inputParameter = function.Parameters.First(p => p.IsTrigger); Dictionary <string, object> arguments = new Dictionary <string, object>() { { inputParameter.Name, invocation.Input } }; Task.Run(() => scriptHost.CallAsync(function.Name, arguments)); return(Accepted()); }
public void InitializeHttpFunctionRoutes(IScriptJobHost host) { // noop }
public void InitializeHttpFunctionRoutes(IScriptJobHost host) { var routesLogBuilder = new StringBuilder(); routesLogBuilder.AppendLine("Initializing function HTTP routes"); _router.ClearRoutes(); // TODO: FACAVAL Instantiation of the ScriptRouteHandler should be cleaned up WebJobsRouteBuilder routesBuilder = _router.CreateBuilder(new ScriptRouteHandler(_loggerFactory, host, _environment, false), _httpOptions.Value.RoutePrefix); // Proxies do not honor the route prefix defined in host.json WebJobsRouteBuilder proxiesRoutesBuilder = _router.CreateBuilder(new ScriptRouteHandler(_loggerFactory, host, _environment, true), routePrefix: null); foreach (var function in host.Functions) { var httpTrigger = function.GetTriggerAttributeOrNull <HttpTriggerAttribute>(); if (httpTrigger != null) { var constraints = new RouteValueDictionary(); if (httpTrigger.Methods != null) { constraints.Add("httpMethod", new HttpMethodRouteConstraint(httpTrigger.Methods)); } string route = httpTrigger.Route; if (string.IsNullOrEmpty(route) && !function.Metadata.IsProxy) { route = function.Name; } WebJobsRouteBuilder builder = function.Metadata.IsProxy ? proxiesRoutesBuilder : routesBuilder; builder.MapFunctionRoute(function.Metadata.Name, route, constraints, function.Metadata.Name); LogRouteMap(routesLogBuilder, function.Metadata.Name, route, httpTrigger.Methods, function.Metadata.IsProxy, _httpOptions.Value.RoutePrefix); } } IRouter proxyRouter = null; IRouter functionRouter = null; if (routesBuilder.Count == 0 && proxiesRoutesBuilder.Count == 0) { routesLogBuilder.AppendLine("No HTTP routes mapped"); } else { if (proxiesRoutesBuilder.Count > 0) { proxyRouter = proxiesRoutesBuilder.Build(); } if (routesBuilder.Count > 0) { functionRouter = routesBuilder.Build(); } } _router.AddFunctionRoutes(functionRouter, proxyRouter); ILogger logger = _loggerFactory.CreateLogger <WebScriptHostHttpRoutesManager>(); logger.LogInformation(routesLogBuilder.ToString()); }
internal ProxyFunctionExecutor(IScriptJobHost scriptHost) { _scriptHost = scriptHost; }
/// <summary> /// Lookup a function by name /// </summary> /// <param name="name">name of function</param> /// <returns>function or null if not found</returns> public static FunctionDescriptor GetFunctionOrNull(this IScriptJobHost scriptJobHost, string name) { return(scriptJobHost.Functions.FirstOrDefault(p => string.Equals(p.Name, name, StringComparison.OrdinalIgnoreCase))); }
public async Task <IActionResult> Warmup([FromServices] IScriptJobHost scriptHost) { await scriptHost.TryInvokeWarmupAsync(); return(Ok()); }
public void InitializeHttpFunctionRoutes(IScriptJobHost host) { var routesLogBuilder = new StringBuilder(); routesLogBuilder.AppendLine("Initializing function HTTP routes"); _router.ClearRoutes(); // TODO: FACAVAL Instantiation of the ScriptRouteHandler should be cleaned up WebJobsRouteBuilder routesBuilder = _router.CreateBuilder(new ScriptRouteHandler(_loggerFactory, host, _environment, false), _httpOptions.Value.RoutePrefix); // Proxies do not honor the route prefix defined in host.json WebJobsRouteBuilder proxiesRoutesBuilder = _router.CreateBuilder(new ScriptRouteHandler(_loggerFactory, host, _environment, true), routePrefix: null); WebJobsRouteBuilder warmupRouteBuilder = null; if (!_environment.IsLinuxConsumption() && !_environment.IsWindowsConsumption()) { warmupRouteBuilder = _router.CreateBuilder(new ScriptRouteHandler(_loggerFactory, host, _environment, isProxy: false, isWarmup: true), routePrefix: "admin"); } foreach (var function in host.Functions) { var httpTrigger = function.HttpTriggerAttribute; if (httpTrigger != null) { var constraints = new RouteValueDictionary(); if (httpTrigger.Methods != null) { constraints.Add("httpMethod", new HttpMethodRouteConstraint(httpTrigger.Methods)); } string route = httpTrigger.Route; bool isProxy = function.Metadata.IsProxy(); if (string.IsNullOrEmpty(route) && !isProxy) { route = function.Name; } WebJobsRouteBuilder builder = isProxy ? proxiesRoutesBuilder : routesBuilder; builder.MapFunctionRoute(function.Metadata.Name, route, constraints, function.Metadata.Name); LogRouteMap(routesLogBuilder, function.Metadata.Name, route, httpTrigger.Methods, isProxy, _httpOptions.Value.RoutePrefix); } else if (warmupRouteBuilder != null && function.IsWarmupFunction()) { warmupRouteBuilder.MapFunctionRoute(function.Metadata.Name, "warmup", function.Metadata.Name); } } IRouter proxyRouter = null; IRouter functionRouter = null; if (routesBuilder.Count == 0 && proxiesRoutesBuilder.Count == 0) { routesLogBuilder.AppendLine("No HTTP routes mapped"); } else { if (proxiesRoutesBuilder.Count > 0) { proxyRouter = proxiesRoutesBuilder.Build(); } if (routesBuilder.Count > 0) { functionRouter = routesBuilder.Build(); } } _router.AddFunctionRoutes(functionRouter, proxyRouter); if (warmupRouteBuilder != null) { // Adding the default admin/warmup route when no warmup function is present if (warmupRouteBuilder.Count == 0) { warmupRouteBuilder.MapFunctionRoute(string.Empty, "warmup", string.Empty); } IRouter warmupRouter = warmupRouteBuilder.Build(); _router.AddFunctionRoutes(warmupRouter, null); } ILogger logger = _loggerFactory.CreateLogger <WebScriptHostHttpRoutesManager>(); logger.LogInformation(routesLogBuilder.ToString()); }
public IActionResult Invoke(string name, [FromBody] FunctionInvocation invocation, [FromServices] IScriptJobHost scriptHost) { if (invocation == null) { return(BadRequest()); } FunctionDescriptor function = scriptHost.GetFunctionOrNull(name); if (function == null) { return(NotFound()); } ParameterDescriptor inputParameter = function.Parameters.First(p => p.IsTrigger); Dictionary <string, object> arguments = new Dictionary <string, object>() { { inputParameter.Name, invocation.Input } }; using (System.Threading.ExecutionContext.SuppressFlow()) { Task.Run(async() => { IDictionary <string, object> loggerScope = new Dictionary <string, object> { { "MS_IgnoreActivity", null } }; using (_logger.BeginScope(loggerScope)) { await scriptHost.CallAsync(function.Name, arguments); } }); } return(Accepted()); }
public IActionResult Invoke(string name, [FromBody] FunctionInvocation invocation, [FromServices] IScriptJobHost scriptHost) { if (invocation == null) { return(BadRequest()); } FunctionDescriptor function = scriptHost.GetFunctionOrNull(name); if (function == null) { return(NotFound()); } ParameterDescriptor inputParameter = function.Parameters.First(p => p.IsTrigger); Dictionary <string, object> arguments = new Dictionary <string, object>() { { inputParameter.Name, invocation.Input } }; // LiveLogs session id is used to show only contextual logs in the "Code + Test" experience. string sessionId = this.Request?.Headers[ScriptConstants.LiveLogsSessionAIKey]; using (System.Threading.ExecutionContext.SuppressFlow()) { Task.Run(async() => { IDictionary <string, object> loggerScope = new Dictionary <string, object> { { "MS_IgnoreActivity", null } }; using (_logger.BeginScope(loggerScope)) { if (!string.IsNullOrWhiteSpace(sessionId)) { // Current activity is null due to SuppressFlow. Start a new activity and set baggage so that it's included in the custom dimensions. Activity activity = new Activity($"{nameof(FunctionsController)}.Invoke"); activity?.Start(); activity?.AddBaggage(ScriptConstants.LiveLogsSessionAIKey, sessionId); try { await scriptHost.CallAsync(function.Name, arguments); } finally { activity?.Stop(); } } else { await scriptHost.CallAsync(function.Name, arguments); } } }); } return(Accepted()); }