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();
                }
            }
        }
Пример #3
0
 public ScriptRouteHandler(ILoggerFactory loggerFactory, IScriptJobHost scriptHost, IEnvironment environment, bool isProxy)
 {
     _scriptHost    = scriptHost;
     _loggerFactory = loggerFactory;
     _environment   = environment;
     _isProxy       = isProxy;
 }
Пример #4
0
 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."));
        }
Пример #6
0
        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));
        }
Пример #7
0
 public HttpInitializationService(IOptions <HttpOptions> httpOptions, IWebJobsRouter router, ILoggerFactory loggerFactory, IScriptJobHost host, IEnvironment environment)
 {
     _httpOptions   = httpOptions;
     _router        = router;
     _loggerFactory = loggerFactory;
     _host          = host;
     _environment   = environment;
 }
Пример #8
0
 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."));
         }
     }
 }
Пример #9
0
 /// <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));
     }));
 }
Пример #10
0
        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);
        }
Пример #11
0
        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();
                }
            }
        }
Пример #12
0
        /// <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);
        }
Пример #13
0
        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));
        }
Пример #14
0
 /// <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());
        }
Пример #16
0
 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());
        }
Пример #18
0
 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)));
 }
Пример #20
0
        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());
        }
Пример #22
0
        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());
        }
Пример #23
0
        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());
        }