diff --git a/WebJobs.Script.sln b/WebJobs.Script.sln index 0fe24f2ec4..a0bc400292 100644 --- a/WebJobs.Script.sln +++ b/WebJobs.Script.sln @@ -1,7 +1,7 @@  Microsoft Visual Studio Solution File, Format Version 12.00 # Visual Studio Version 17 -VisualStudioVersion = 17.0.31717.71 +VisualStudioVersion = 17.14.36623.8 d17.14 MinimumVisualStudioVersion = 15.0.0.0 Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{16351B76-87CA-4A8C-80A1-3DD83A0C4AA6}" EndProject diff --git a/src/WebJobs.Script.Grpc/Channel/GrpcWorkerChannelFactory.cs b/src/WebJobs.Script.Grpc/Channel/GrpcWorkerChannelFactory.cs index 1c0113967a..768abb5e29 100644 --- a/src/WebJobs.Script.Grpc/Channel/GrpcWorkerChannelFactory.cs +++ b/src/WebJobs.Script.Grpc/Channel/GrpcWorkerChannelFactory.cs @@ -1,4 +1,4 @@ -// Copyright (c) .NET Foundation. All rights reserved. +// Copyright (c) .NET Foundation. All rights reserved. // Licensed under the MIT License. See License.txt in the project root for license information. using System; @@ -17,7 +17,7 @@ namespace Microsoft.Azure.WebJobs.Script.Grpc { - public class GrpcWorkerChannelFactory : IRpcWorkerChannelFactory + internal class GrpcWorkerChannelFactory : IRpcWorkerChannelFactory { private readonly ILoggerFactory _loggerFactory = null; private readonly IRpcWorkerProcessFactory _rpcWorkerProcessFactory = null; diff --git a/src/WebJobs.Script.Grpc/Eventing/GrpcEvent.cs b/src/WebJobs.Script.Grpc/Eventing/GrpcEvent.cs index 465fda8f2a..3082965198 100644 --- a/src/WebJobs.Script.Grpc/Eventing/GrpcEvent.cs +++ b/src/WebJobs.Script.Grpc/Eventing/GrpcEvent.cs @@ -1,4 +1,4 @@ -// Copyright (c) .NET Foundation. All rights reserved. +// Copyright (c) .NET Foundation. All rights reserved. // Licensed under the MIT License. See License.txt in the project root for license information. using Microsoft.Azure.WebJobs.Script.Eventing; @@ -6,7 +6,7 @@ namespace Microsoft.Azure.WebJobs.Script.Grpc.Eventing { - public class GrpcEvent : ScriptEvent + internal class GrpcEvent : ScriptEvent { internal GrpcEvent(string workerId, StreamingMessage message, MessageOrigin origin = MessageOrigin.Host) : base(message.ContentCase.ToString(), EventSources.Rpc) diff --git a/src/WebJobs.Script.Grpc/Eventing/InboundGrpcEvent.cs b/src/WebJobs.Script.Grpc/Eventing/InboundGrpcEvent.cs index b6b148cde0..d5761208a0 100644 --- a/src/WebJobs.Script.Grpc/Eventing/InboundGrpcEvent.cs +++ b/src/WebJobs.Script.Grpc/Eventing/InboundGrpcEvent.cs @@ -1,11 +1,11 @@ -// Copyright (c) .NET Foundation. All rights reserved. +// Copyright (c) .NET Foundation. All rights reserved. // Licensed under the MIT License. See License.txt in the project root for license information. using Microsoft.Azure.WebJobs.Script.Grpc.Messages; namespace Microsoft.Azure.WebJobs.Script.Grpc.Eventing { - public class InboundGrpcEvent : GrpcEvent + internal class InboundGrpcEvent : GrpcEvent { public InboundGrpcEvent(string workerId, StreamingMessage message) : base(workerId, message, MessageOrigin.Worker) { diff --git a/src/WebJobs.Script.Grpc/Eventing/OutboundGrpcEvent.cs b/src/WebJobs.Script.Grpc/Eventing/OutboundGrpcEvent.cs index 07974ea687..4f5deec11e 100644 --- a/src/WebJobs.Script.Grpc/Eventing/OutboundGrpcEvent.cs +++ b/src/WebJobs.Script.Grpc/Eventing/OutboundGrpcEvent.cs @@ -1,11 +1,11 @@ -// Copyright (c) .NET Foundation. All rights reserved. +// Copyright (c) .NET Foundation. All rights reserved. // Licensed under the MIT License. See License.txt in the project root for license information. using Microsoft.Azure.WebJobs.Script.Grpc.Messages; namespace Microsoft.Azure.WebJobs.Script.Grpc.Eventing { - public class OutboundGrpcEvent : GrpcEvent + internal class OutboundGrpcEvent : GrpcEvent { public OutboundGrpcEvent(string workerId, StreamingMessage message) : base(workerId, message, MessageOrigin.Host) { diff --git a/src/WebJobs.Script/Workers/FunctionInvocationDispatcherFactory.cs b/src/WebJobs.Script.Grpc/FunctionInvocationDispatcherFactory.cs similarity index 91% rename from src/WebJobs.Script/Workers/FunctionInvocationDispatcherFactory.cs rename to src/WebJobs.Script.Grpc/FunctionInvocationDispatcherFactory.cs index 0b5dc643a4..96081513eb 100644 --- a/src/WebJobs.Script/Workers/FunctionInvocationDispatcherFactory.cs +++ b/src/WebJobs.Script.Grpc/FunctionInvocationDispatcherFactory.cs @@ -1,4 +1,4 @@ -// Copyright (c) .NET Foundation. All rights reserved. +// Copyright (c) .NET Foundation. All rights reserved. // Licensed under the MIT License. See License.txt in the project root for license information. using System; @@ -15,7 +15,7 @@ namespace Microsoft.Azure.WebJobs.Script.Workers { - internal class FunctionInvocationDispatcherFactory : IFunctionInvocationDispatcherFactory + internal class FunctionInvocationDispatcherFactory : IFunctionInvocationDispatcherFactory, IDisposable { private readonly IFunctionInvocationDispatcher _functionDispatcher; @@ -65,5 +65,11 @@ public FunctionInvocationDispatcherFactory(IOptions script } public IFunctionInvocationDispatcher GetFunctionDispatcher() => _functionDispatcher; + + public void Dispose() + { + // This is a "provider" rather than a "factory". We are responsible for disposing. + _functionDispatcher.Dispose(); + } } } diff --git a/src/WebJobs.Script/Workers/FunctionInvocationDispatcherShutdownManager.cs b/src/WebJobs.Script.Grpc/FunctionInvocationDispatcherShutdownManager.cs similarity index 94% rename from src/WebJobs.Script/Workers/FunctionInvocationDispatcherShutdownManager.cs rename to src/WebJobs.Script.Grpc/FunctionInvocationDispatcherShutdownManager.cs index b79dee4924..cf958b7232 100644 --- a/src/WebJobs.Script/Workers/FunctionInvocationDispatcherShutdownManager.cs +++ b/src/WebJobs.Script.Grpc/FunctionInvocationDispatcherShutdownManager.cs @@ -1,4 +1,4 @@ -// Copyright (c) .NET Foundation. All rights reserved. +// Copyright (c) .NET Foundation. All rights reserved. // Licensed under the MIT License. See License.txt in the project root for license information. using System.Threading; diff --git a/src/WebJobs.Script/Workers/FunctionInvocationDispatcherState.cs b/src/WebJobs.Script.Grpc/FunctionInvocationDispatcherState.cs similarity index 91% rename from src/WebJobs.Script/Workers/FunctionInvocationDispatcherState.cs rename to src/WebJobs.Script.Grpc/FunctionInvocationDispatcherState.cs index 9409e8c98d..46ebf5b3a5 100644 --- a/src/WebJobs.Script/Workers/FunctionInvocationDispatcherState.cs +++ b/src/WebJobs.Script.Grpc/FunctionInvocationDispatcherState.cs @@ -1,9 +1,9 @@ -// Copyright (c) .NET Foundation. All rights reserved. +// Copyright (c) .NET Foundation. All rights reserved. // Licensed under the MIT License. See License.txt in the project root for license information. namespace Microsoft.Azure.WebJobs.Script.Workers { - public enum FunctionInvocationDispatcherState + internal enum FunctionInvocationDispatcherState { /// /// The FunctionDispatcher has not yet been created diff --git a/src/WebJobs.Script.Grpc/Hosting/HttpScriptHostRecycleOptionsSetup.cs b/src/WebJobs.Script.Grpc/Hosting/HttpScriptHostRecycleOptionsSetup.cs new file mode 100644 index 0000000000..c600562f76 --- /dev/null +++ b/src/WebJobs.Script.Grpc/Hosting/HttpScriptHostRecycleOptionsSetup.cs @@ -0,0 +1,32 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the MIT License. See License.txt in the project root for license information. + +using System; +using Microsoft.Azure.WebJobs.Script.Workers.Http; +using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.Options; + +namespace Microsoft.Azure.WebJobs.Script.Rpc.Hosting +{ + internal sealed class HttpScriptHostRecycleOptionsSetup : IPostConfigureOptions + { + private readonly IOptions _httpWorkerOptions; + private readonly IConfiguration _configuration; + + public HttpScriptHostRecycleOptionsSetup( + IOptions httpWorkerOptions, IConfiguration configuration) + { + _httpWorkerOptions = httpWorkerOptions ?? throw new ArgumentNullException(nameof(httpWorkerOptions)); + _configuration = configuration ?? throw new ArgumentNullException(nameof(configuration)); + } + + public void PostConfigure(string name, ScriptHostRecycleOptions options) + { + // Enforcing sequential host restarts when a user-specified custom handler port is configured to prevent multiple processes from attempting to bind to the same port concurrently. + if (_httpWorkerOptions.Value.IsPortManuallySet) + { + options.SequentialHostRestartRequired = true; + } + } + } +} diff --git a/src/WebJobs.Script.Grpc/Hosting/RpcServiceCollectionExtensions.cs b/src/WebJobs.Script.Grpc/Hosting/RpcServiceCollectionExtensions.cs new file mode 100644 index 0000000000..6f66dd02be --- /dev/null +++ b/src/WebJobs.Script.Grpc/Hosting/RpcServiceCollectionExtensions.cs @@ -0,0 +1,95 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the MIT License. See License.txt in the project root for license information. + +using System; +using System.Runtime.InteropServices; +using Microsoft.Azure.WebJobs.Host.Scale; +using Microsoft.Azure.WebJobs.Script; +using Microsoft.Azure.WebJobs.Script.Config; +using Microsoft.Azure.WebJobs.Script.Description; +using Microsoft.Azure.WebJobs.Script.Host; +using Microsoft.Azure.WebJobs.Script.Rpc; +using Microsoft.Azure.WebJobs.Script.Rpc.Configuration; +using Microsoft.Azure.WebJobs.Script.Rpc.Hosting; +using Microsoft.Azure.WebJobs.Script.Scale; +using Microsoft.Azure.WebJobs.Script.WebHost; +using Microsoft.Azure.WebJobs.Script.Workers; +using Microsoft.Azure.WebJobs.Script.Workers.Http; +using Microsoft.Azure.WebJobs.Script.Workers.Rpc; +using Microsoft.Extensions.DependencyInjection.Extensions; +using Microsoft.Extensions.Hosting; +using Microsoft.Extensions.Options; + +namespace Microsoft.Extensions.DependencyInjection; + +public static class RpcServiceCollectionExtensions +{ + public static IServiceCollection AddRpcScriptHostServices(this IServiceCollection services) + { + ArgumentNullException.ThrowIfNull(services); + + // HTTP Worker + services.AddSingleton(); + services.AddSingleton(); + services.AddSingleton(); + services.TryAddEnumerable(ServiceDescriptor.Singleton()); + + //Worker Function Invocation dispatcher + services.AddSingleton(); + + // Rpc Worker + services.AddSingleton(); + services.AddSingleton(); + + services.AddSingleton(); + + // Configuration + services.AddSingleton, HttpScriptHostRecycleOptionsSetup>(); + services.ConfigureOptions(); + services.ConfigureOptions(); + services.ConfigureOptions(); + + services.TryAddEnumerable(ServiceDescriptor.Singleton()); + + services.AddSingleton(); + services.AddSingleton(); + services.AddSingleton(); + + return services; + } + + public static IServiceCollection AddCommonRpcServices(this IServiceCollection services) + { + ArgumentNullException.ThrowIfNull(services); + + // Add Language Worker Service + services.AddSingleton(); + + services.AddSingleton(); + services.TryAddSingleton(); + services.TryAddSingleton(); + services.AddSingleton(); + services.TryAddEnumerable(ServiceDescriptor.Singleton()); + services.AddSingleton(); + + services.AddManagedHostedService(); + + AddProcessRegistry(services); + + return services; + } + + private static void AddProcessRegistry(IServiceCollection services) + { + // W3WP already manages job objects + if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows) + && !ScriptSettingsManager.Instance.IsAppServiceEnvironment) + { + services.AddSingleton(); + } + else + { + services.AddSingleton(); + } + } +} diff --git a/src/WebJobs.Script/Workers/Http/Configuration/CustomHandlerType.cs b/src/WebJobs.Script.Grpc/Http/Configuration/CustomHandlerType.cs similarity index 68% rename from src/WebJobs.Script/Workers/Http/Configuration/CustomHandlerType.cs rename to src/WebJobs.Script.Grpc/Http/Configuration/CustomHandlerType.cs index aa882e652a..efe0b7bcb4 100644 --- a/src/WebJobs.Script/Workers/Http/Configuration/CustomHandlerType.cs +++ b/src/WebJobs.Script.Grpc/Http/Configuration/CustomHandlerType.cs @@ -1,9 +1,9 @@ -// Copyright (c) .NET Foundation. All rights reserved. +// Copyright (c) .NET Foundation. All rights reserved. // Licensed under the MIT License. See License.txt in the project root for license information. namespace Microsoft.Azure.WebJobs.Script.Workers.Http { - public enum CustomHandlerType + internal enum CustomHandlerType { Http = 0, None = 1 diff --git a/src/WebJobs.Script/Workers/Http/Configuration/HttpWorkerOptions.cs b/src/WebJobs.Script.Grpc/Http/Configuration/HttpWorkerOptions.cs similarity index 98% rename from src/WebJobs.Script/Workers/Http/Configuration/HttpWorkerOptions.cs rename to src/WebJobs.Script.Grpc/Http/Configuration/HttpWorkerOptions.cs index b44dab9460..6ec52cedc7 100644 --- a/src/WebJobs.Script/Workers/Http/Configuration/HttpWorkerOptions.cs +++ b/src/WebJobs.Script.Grpc/Http/Configuration/HttpWorkerOptions.cs @@ -8,7 +8,7 @@ namespace Microsoft.Azure.WebJobs.Script.Workers.Http { - public class HttpWorkerOptions : IOptionsFormatter + internal class HttpWorkerOptions : IOptionsFormatter { private int? _port; diff --git a/src/WebJobs.Script/Workers/Http/Configuration/HttpWorkerOptionsSetup.cs b/src/WebJobs.Script.Grpc/Http/Configuration/HttpWorkerOptionsSetup.cs similarity index 100% rename from src/WebJobs.Script/Workers/Http/Configuration/HttpWorkerOptionsSetup.cs rename to src/WebJobs.Script.Grpc/Http/Configuration/HttpWorkerOptionsSetup.cs diff --git a/src/WebJobs.Script.Grpc/Http/Configuration/RpcFunctionMetadataOptionsSetup.cs b/src/WebJobs.Script.Grpc/Http/Configuration/RpcFunctionMetadataOptionsSetup.cs new file mode 100644 index 0000000000..518e6e88ac --- /dev/null +++ b/src/WebJobs.Script.Grpc/Http/Configuration/RpcFunctionMetadataOptionsSetup.cs @@ -0,0 +1,24 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the MIT License. See License.txt in the project root for license information. + +using Microsoft.Azure.WebJobs.Script.Workers.Http; +using Microsoft.Extensions.Options; + +namespace Microsoft.Azure.WebJobs.Script.Rpc.Configuration; + +internal class RpcFunctionMetadataOptionsSetup : IConfigureOptions +{ + private readonly bool _isHttpWorker; + + public RpcFunctionMetadataOptionsSetup(IOptions httpOptions) + { + _isHttpWorker = httpOptions.Value.Description is not null; + } + + public void Configure(FunctionMetadataOptions options) + { + // certain validations do not apply to HTTP workers + options.SkipScriptFileValidation = _isHttpWorker; + options.SkipRuntimeValidation = _isHttpWorker; + } +} \ No newline at end of file diff --git a/src/WebJobs.Script.Grpc/Http/Configuration/RpcScriptHostRecycleOptionsSetup.cs b/src/WebJobs.Script.Grpc/Http/Configuration/RpcScriptHostRecycleOptionsSetup.cs new file mode 100644 index 0000000000..7b752b8e76 --- /dev/null +++ b/src/WebJobs.Script.Grpc/Http/Configuration/RpcScriptHostRecycleOptionsSetup.cs @@ -0,0 +1,25 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the MIT License. See License.txt in the project root for license information. + +using Microsoft.Azure.WebJobs.Script.Workers.Http; +using Microsoft.Extensions.Options; + +namespace Microsoft.Azure.WebJobs.Script.Rpc.Configuration; + +internal class RpcScriptHostRecycleOptionsSetup : IConfigureOptions +{ + private readonly IOptions _httpOptions; + + public RpcScriptHostRecycleOptionsSetup(IOptions httpOptions) + { + _httpOptions = httpOptions; + } + + public void Configure(ScriptHostRecycleOptions options) + { + if (_httpOptions.Value.IsPortManuallySet) + { + options.SequentialHostRestartRequired = true; + } + } +} diff --git a/src/WebJobs.Script/Workers/Http/CustomHandlerHttpOptions.cs b/src/WebJobs.Script.Grpc/Http/CustomHandlerHttpOptions.cs similarity index 90% rename from src/WebJobs.Script/Workers/Http/CustomHandlerHttpOptions.cs rename to src/WebJobs.Script.Grpc/Http/CustomHandlerHttpOptions.cs index e52acd5c7a..9457f61b7d 100644 --- a/src/WebJobs.Script/Workers/Http/CustomHandlerHttpOptions.cs +++ b/src/WebJobs.Script.Grpc/Http/CustomHandlerHttpOptions.cs @@ -1,13 +1,12 @@ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the MIT License. See License.txt in the project root for license information. -using System.Collections; using System.Collections.Generic; using Microsoft.Azure.WebJobs.Extensions.Http; namespace Microsoft.Azure.WebJobs.Script.Workers.Http { - public sealed class CustomHandlerHttpOptions + internal sealed class CustomHandlerHttpOptions { /// /// Gets or sets the default authorization level for custom handler HTTP worker routes. diff --git a/src/WebJobs.Script/Workers/Http/DefaultHttpWorkerService.cs b/src/WebJobs.Script.Grpc/Http/DefaultHttpWorkerService.cs similarity index 99% rename from src/WebJobs.Script/Workers/Http/DefaultHttpWorkerService.cs rename to src/WebJobs.Script.Grpc/Http/DefaultHttpWorkerService.cs index bb1f2a2ae9..3776636efa 100644 --- a/src/WebJobs.Script/Workers/Http/DefaultHttpWorkerService.cs +++ b/src/WebJobs.Script.Grpc/Http/DefaultHttpWorkerService.cs @@ -19,7 +19,7 @@ namespace Microsoft.Azure.WebJobs.Script.Workers.Http { - public class DefaultHttpWorkerService : IHttpWorkerService + internal class DefaultHttpWorkerService : IHttpWorkerService { private readonly HttpClient _httpClient; private readonly HttpWorkerOptions _httpWorkerOptions; diff --git a/src/WebJobs.Script/Description/Workers/Http/HttpFunctionDescriptorProvider.cs b/src/WebJobs.Script.Grpc/Http/HttpFunctionDescriptorProvider.cs similarity index 100% rename from src/WebJobs.Script/Description/Workers/Http/HttpFunctionDescriptorProvider.cs rename to src/WebJobs.Script.Grpc/Http/HttpFunctionDescriptorProvider.cs diff --git a/src/WebJobs.Script/Workers/Http/HttpOutputBindingResponse.cs b/src/WebJobs.Script.Grpc/Http/HttpOutputBindingResponse.cs similarity index 100% rename from src/WebJobs.Script/Workers/Http/HttpOutputBindingResponse.cs rename to src/WebJobs.Script.Grpc/Http/HttpOutputBindingResponse.cs diff --git a/src/WebJobs.Script/Workers/Http/HttpScriptInvocationContext.cs b/src/WebJobs.Script.Grpc/Http/HttpScriptInvocationContext.cs similarity index 79% rename from src/WebJobs.Script/Workers/Http/HttpScriptInvocationContext.cs rename to src/WebJobs.Script.Grpc/Http/HttpScriptInvocationContext.cs index aa7ff3431d..758c3795a6 100644 --- a/src/WebJobs.Script/Workers/Http/HttpScriptInvocationContext.cs +++ b/src/WebJobs.Script.Grpc/Http/HttpScriptInvocationContext.cs @@ -1,11 +1,11 @@ -// Copyright (c) .NET Foundation. All rights reserved. +// Copyright (c) .NET Foundation. All rights reserved. // Licensed under the MIT License. See License.txt in the project root for license information. using System.Collections.Generic; namespace Microsoft.Azure.WebJobs.Script.Workers.Http { - public class HttpScriptInvocationContext + internal class HttpScriptInvocationContext { public IDictionary Data { get; set; } = new Dictionary(); diff --git a/src/WebJobs.Script/Workers/MessageExtensions/HttpScriptInvocationContextExtensions.cs b/src/WebJobs.Script.Grpc/Http/HttpScriptInvocationContextExtensions.cs similarity index 86% rename from src/WebJobs.Script/Workers/MessageExtensions/HttpScriptInvocationContextExtensions.cs rename to src/WebJobs.Script.Grpc/Http/HttpScriptInvocationContextExtensions.cs index a2e0e42acc..384eb10836 100644 --- a/src/WebJobs.Script/Workers/MessageExtensions/HttpScriptInvocationContextExtensions.cs +++ b/src/WebJobs.Script.Grpc/Http/HttpScriptInvocationContextExtensions.cs @@ -1,4 +1,4 @@ -// Copyright (c) .NET Foundation. All rights reserved. +// Copyright (c) .NET Foundation. All rights reserved. // Licensed under the MIT License. See License.txt in the project root for license information. using System; @@ -8,7 +8,7 @@ namespace Microsoft.Azure.WebJobs.Script.Workers { - public static class HttpScriptInvocationContextExtensions + internal static class HttpScriptInvocationContextExtensions { public static HttpRequestMessage ToHttpRequestMessage(this HttpScriptInvocationContext context, string requestUri) { diff --git a/src/WebJobs.Script/Workers/Http/HttpScriptInvocationResult.cs b/src/WebJobs.Script.Grpc/Http/HttpScriptInvocationResult.cs similarity index 78% rename from src/WebJobs.Script/Workers/Http/HttpScriptInvocationResult.cs rename to src/WebJobs.Script.Grpc/Http/HttpScriptInvocationResult.cs index 81f0b63056..d334d70215 100644 --- a/src/WebJobs.Script/Workers/Http/HttpScriptInvocationResult.cs +++ b/src/WebJobs.Script.Grpc/Http/HttpScriptInvocationResult.cs @@ -1,11 +1,11 @@ -// Copyright (c) .NET Foundation. All rights reserved. +// Copyright (c) .NET Foundation. All rights reserved. // Licensed under the MIT License. See License.txt in the project root for license information. using System.Collections.Generic; namespace Microsoft.Azure.WebJobs.Script.Workers.Http { - public class HttpScriptInvocationResult + internal class HttpScriptInvocationResult { public object ReturnValue { get; set; } diff --git a/src/WebJobs.Script/Workers/Http/HttpScriptInvocationResultExtensions.cs b/src/WebJobs.Script.Grpc/Http/HttpScriptInvocationResultExtensions.cs similarity index 97% rename from src/WebJobs.Script/Workers/Http/HttpScriptInvocationResultExtensions.cs rename to src/WebJobs.Script.Grpc/Http/HttpScriptInvocationResultExtensions.cs index d71ce47bd7..9d3876ecf5 100644 --- a/src/WebJobs.Script/Workers/Http/HttpScriptInvocationResultExtensions.cs +++ b/src/WebJobs.Script.Grpc/Http/HttpScriptInvocationResultExtensions.cs @@ -1,4 +1,4 @@ -// Copyright (c) .NET Foundation. All rights reserved. +// Copyright (c) .NET Foundation. All rights reserved. // Licensed under the MIT License. See License.txt in the project root for license information. using System; @@ -10,7 +10,7 @@ namespace Microsoft.Azure.WebJobs.Script.Workers.Http { - public static class HttpScriptInvocationResultExtensions + internal static class HttpScriptInvocationResultExtensions { public static ScriptInvocationResult ToScriptInvocationResult(this HttpScriptInvocationResult httpScriptInvocationResult, ScriptInvocationContext scriptInvocationContext) { diff --git a/src/WebJobs.Script/Workers/Http/HttpWorkerChannel.cs b/src/WebJobs.Script.Grpc/Http/HttpWorkerChannel.cs similarity index 97% rename from src/WebJobs.Script/Workers/Http/HttpWorkerChannel.cs rename to src/WebJobs.Script.Grpc/Http/HttpWorkerChannel.cs index 39eec29396..81d34c19e7 100644 --- a/src/WebJobs.Script/Workers/Http/HttpWorkerChannel.cs +++ b/src/WebJobs.Script.Grpc/Http/HttpWorkerChannel.cs @@ -1,4 +1,4 @@ -// Copyright (c) .NET Foundation. All rights reserved. +// Copyright (c) .NET Foundation. All rights reserved. // Licensed under the MIT License. See License.txt in the project root for license information. using System; @@ -11,7 +11,7 @@ namespace Microsoft.Azure.WebJobs.Script.Workers { - public class HttpWorkerChannel : IHttpWorkerChannel, IDisposable + internal class HttpWorkerChannel : IHttpWorkerChannel, IDisposable { private readonly IScriptEventManager _eventManager; private bool _disposed; diff --git a/src/WebJobs.Script/Workers/Http/HttpWorkerChannelFactory.cs b/src/WebJobs.Script.Grpc/Http/HttpWorkerChannelFactory.cs similarity index 94% rename from src/WebJobs.Script/Workers/Http/HttpWorkerChannelFactory.cs rename to src/WebJobs.Script.Grpc/Http/HttpWorkerChannelFactory.cs index b23442c326..166542300d 100644 --- a/src/WebJobs.Script/Workers/Http/HttpWorkerChannelFactory.cs +++ b/src/WebJobs.Script.Grpc/Http/HttpWorkerChannelFactory.cs @@ -1,4 +1,4 @@ -// Copyright (c) .NET Foundation. All rights reserved. +// Copyright (c) .NET Foundation. All rights reserved. // Licensed under the MIT License. See License.txt in the project root for license information. using System; @@ -10,7 +10,7 @@ namespace Microsoft.Azure.WebJobs.Script.Workers { - public class HttpWorkerChannelFactory : IHttpWorkerChannelFactory + internal class HttpWorkerChannelFactory : IHttpWorkerChannelFactory { private readonly ILoggerFactory _loggerFactory = null; private readonly IHttpWorkerProcessFactory _httpWorkerProcessFactory = null; diff --git a/src/WebJobs.Script/Workers/Http/HttpWorkerConstants.cs b/src/WebJobs.Script.Grpc/Http/HttpWorkerConstants.cs similarity index 89% rename from src/WebJobs.Script/Workers/Http/HttpWorkerConstants.cs rename to src/WebJobs.Script.Grpc/Http/HttpWorkerConstants.cs index 3925dc32b1..3dc58c3635 100644 --- a/src/WebJobs.Script/Workers/Http/HttpWorkerConstants.cs +++ b/src/WebJobs.Script.Grpc/Http/HttpWorkerConstants.cs @@ -1,9 +1,9 @@ -// Copyright (c) .NET Foundation. All rights reserved. +// Copyright (c) .NET Foundation. All rights reserved. // Licensed under the MIT License. See License.txt in the project root for license information. namespace Microsoft.Azure.WebJobs.Script.Workers { - public static class HttpWorkerConstants + internal static class HttpWorkerConstants { // Headers public const string InvocationIdHeaderName = "X-Azure-Functions-InvocationId"; diff --git a/src/WebJobs.Script/Workers/Http/HttpWorkerContext.cs b/src/WebJobs.Script.Grpc/Http/HttpWorkerContext.cs similarity index 100% rename from src/WebJobs.Script/Workers/Http/HttpWorkerContext.cs rename to src/WebJobs.Script.Grpc/Http/HttpWorkerContext.cs diff --git a/src/WebJobs.Script/Workers/Http/HttpWorkerDescription.cs b/src/WebJobs.Script.Grpc/Http/HttpWorkerDescription.cs similarity index 95% rename from src/WebJobs.Script/Workers/Http/HttpWorkerDescription.cs rename to src/WebJobs.Script.Grpc/Http/HttpWorkerDescription.cs index 190433eea6..f277aa5d6b 100644 --- a/src/WebJobs.Script/Workers/Http/HttpWorkerDescription.cs +++ b/src/WebJobs.Script.Grpc/Http/HttpWorkerDescription.cs @@ -1,16 +1,15 @@ -// Copyright (c) .NET Foundation. All rights reserved. +// Copyright (c) .NET Foundation. All rights reserved. // Licensed under the MIT License. See License.txt in the project root for license information. using System; using System.Collections.Generic; using System.ComponentModel.DataAnnotations; using System.IO; -using System.Linq; using Microsoft.Extensions.Logging; namespace Microsoft.Azure.WebJobs.Script.Workers.Http { - public class HttpWorkerDescription : WorkerDescription + internal class HttpWorkerDescription : WorkerDescription { /// /// Gets or sets WorkingDirectory for the process: DefaultExecutablePath diff --git a/src/WebJobs.Script/Workers/Http/HttpWorkerFunctionProvider.cs b/src/WebJobs.Script.Grpc/Http/HttpWorkerFunctionProvider.cs similarity index 100% rename from src/WebJobs.Script/Workers/Http/HttpWorkerFunctionProvider.cs rename to src/WebJobs.Script.Grpc/Http/HttpWorkerFunctionProvider.cs diff --git a/src/WebJobs.Script/Workers/Http/HttpWorkerProcess.cs b/src/WebJobs.Script.Grpc/Http/HttpWorkerProcess.cs similarity index 100% rename from src/WebJobs.Script/Workers/Http/HttpWorkerProcess.cs rename to src/WebJobs.Script.Grpc/Http/HttpWorkerProcess.cs diff --git a/src/WebJobs.Script/Workers/Http/HttpWorkerProcessFactory.cs b/src/WebJobs.Script.Grpc/Http/HttpWorkerProcessFactory.cs similarity index 100% rename from src/WebJobs.Script/Workers/Http/HttpWorkerProcessFactory.cs rename to src/WebJobs.Script.Grpc/Http/HttpWorkerProcessFactory.cs diff --git a/src/WebJobs.Script/Workers/Http/HttpWorkerRoute.cs b/src/WebJobs.Script.Grpc/Http/HttpWorkerRoute.cs similarity index 93% rename from src/WebJobs.Script/Workers/Http/HttpWorkerRoute.cs rename to src/WebJobs.Script.Grpc/Http/HttpWorkerRoute.cs index 6daa318a06..178279373f 100644 --- a/src/WebJobs.Script/Workers/Http/HttpWorkerRoute.cs +++ b/src/WebJobs.Script.Grpc/Http/HttpWorkerRoute.cs @@ -8,7 +8,7 @@ namespace Microsoft.Azure.WebJobs.Script.Workers.Http /// /// Route mapping for a custom handler HTTP worker. /// - public sealed class HttpWorkerRoute + internal sealed class HttpWorkerRoute { /// /// Gets or sets route template. diff --git a/src/WebJobs.Script/Workers/Http/IHttpWorkerChannel.cs b/src/WebJobs.Script.Grpc/Http/IHttpWorkerChannel.cs similarity index 68% rename from src/WebJobs.Script/Workers/Http/IHttpWorkerChannel.cs rename to src/WebJobs.Script.Grpc/Http/IHttpWorkerChannel.cs index 5b0486ea55..1cc42ad6d7 100644 --- a/src/WebJobs.Script/Workers/Http/IHttpWorkerChannel.cs +++ b/src/WebJobs.Script.Grpc/Http/IHttpWorkerChannel.cs @@ -1,13 +1,12 @@ -// Copyright (c) .NET Foundation. All rights reserved. +// Copyright (c) .NET Foundation. All rights reserved. // Licensed under the MIT License. See License.txt in the project root for license information. -using System.Threading; using System.Threading.Tasks; using Microsoft.Azure.WebJobs.Script.Description; namespace Microsoft.Azure.WebJobs.Script.Workers { - public interface IHttpWorkerChannel : IWorkerChannel + internal interface IHttpWorkerChannel : IWorkerChannel { Task InvokeAsync(ScriptInvocationContext context); } diff --git a/src/WebJobs.Script/Workers/Http/IHttpWorkerChannelFactory.cs b/src/WebJobs.Script.Grpc/Http/IHttpWorkerChannelFactory.cs similarity index 75% rename from src/WebJobs.Script/Workers/Http/IHttpWorkerChannelFactory.cs rename to src/WebJobs.Script.Grpc/Http/IHttpWorkerChannelFactory.cs index c56a107107..ac8042044d 100644 --- a/src/WebJobs.Script/Workers/Http/IHttpWorkerChannelFactory.cs +++ b/src/WebJobs.Script.Grpc/Http/IHttpWorkerChannelFactory.cs @@ -1,11 +1,11 @@ -// Copyright (c) .NET Foundation. All rights reserved. +// Copyright (c) .NET Foundation. All rights reserved. // Licensed under the MIT License. See License.txt in the project root for license information. using Microsoft.Azure.WebJobs.Script.Diagnostics; namespace Microsoft.Azure.WebJobs.Script.Workers { - public interface IHttpWorkerChannelFactory + internal interface IHttpWorkerChannelFactory { IHttpWorkerChannel Create(string scriptRootPath, IMetricsLogger metricsLogger, int attemptCount); } diff --git a/src/WebJobs.Script/Workers/Http/IHttpWorkerProcessFactory.cs b/src/WebJobs.Script.Grpc/Http/IHttpWorkerProcessFactory.cs similarity index 67% rename from src/WebJobs.Script/Workers/Http/IHttpWorkerProcessFactory.cs rename to src/WebJobs.Script.Grpc/Http/IHttpWorkerProcessFactory.cs index 9107d0248f..f8b6cb03b6 100644 --- a/src/WebJobs.Script/Workers/Http/IHttpWorkerProcessFactory.cs +++ b/src/WebJobs.Script.Grpc/Http/IHttpWorkerProcessFactory.cs @@ -1,12 +1,11 @@ -// Copyright (c) .NET Foundation. All rights reserved. +// Copyright (c) .NET Foundation. All rights reserved. // Licensed under the MIT License. See License.txt in the project root for license information. using Microsoft.Azure.WebJobs.Script.Workers.Http; -using Microsoft.Azure.WebJobs.Script.Workers.Rpc; namespace Microsoft.Azure.WebJobs.Script.Workers { - public interface IHttpWorkerProcessFactory + internal interface IHttpWorkerProcessFactory { IWorkerProcess Create(string workerId, string scriptRootPath, HttpWorkerOptions httpWorkerOptions); } diff --git a/src/WebJobs.Script/Workers/Http/IHttpWorkerService.cs b/src/WebJobs.Script.Grpc/Http/IHttpWorkerService.cs similarity index 81% rename from src/WebJobs.Script/Workers/Http/IHttpWorkerService.cs rename to src/WebJobs.Script.Grpc/Http/IHttpWorkerService.cs index d85f0c20d0..d135105d5f 100644 --- a/src/WebJobs.Script/Workers/Http/IHttpWorkerService.cs +++ b/src/WebJobs.Script.Grpc/Http/IHttpWorkerService.cs @@ -1,4 +1,4 @@ -// Copyright (c) .NET Foundation. All rights reserved. +// Copyright (c) .NET Foundation. All rights reserved. // Licensed under the MIT License. See License.txt in the project root for license information. using System.Threading; @@ -7,7 +7,7 @@ namespace Microsoft.Azure.WebJobs.Script.Workers { - public interface IHttpWorkerService + internal interface IHttpWorkerService { Task InvokeAsync(ScriptInvocationContext scriptInvocationContext); diff --git a/src/WebJobs.Script.Grpc/Http/RpcScriptInvocationContextExtensions.cs b/src/WebJobs.Script.Grpc/Http/RpcScriptInvocationContextExtensions.cs new file mode 100644 index 0000000000..4274f7ba5e --- /dev/null +++ b/src/WebJobs.Script.Grpc/Http/RpcScriptInvocationContextExtensions.cs @@ -0,0 +1,98 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the MIT License. See License.txt in the project root for license information. + +using System; +using System.Threading.Tasks; +using Microsoft.AspNetCore.Http; +using Microsoft.Azure.WebJobs.Host; +using Microsoft.Azure.WebJobs.Script.Description; +using Microsoft.Azure.WebJobs.Script.Extensions; +using Microsoft.Azure.WebJobs.Script.Workers.Http; +using Newtonsoft.Json; +using Newtonsoft.Json.Linq; + +namespace Microsoft.Azure.WebJobs.Script.Workers +{ + internal static class RpcScriptInvocationContextExtensions + { + public static async Task ToHttpScriptInvocationContext(this ScriptInvocationContext scriptInvocationContext) + { + HttpScriptInvocationContext httpScriptInvocationContext = new HttpScriptInvocationContext(); + + // populate metadata + foreach (var bindingDataPair in scriptInvocationContext.BindingData) + { + if (bindingDataPair.Value != null) + { + if (bindingDataPair.Value is HttpRequest) + { + // no metadata for httpTrigger + continue; + } + if (bindingDataPair.Key.EndsWith("trigger", StringComparison.OrdinalIgnoreCase)) + { + // Data will include value of the trigger. Do not duplicate + continue; + } + httpScriptInvocationContext.Metadata[bindingDataPair.Key] = GetHttpScriptInvocationContextValue(bindingDataPair.Value); + } + } + + // populate input bindings + foreach (var input in scriptInvocationContext.Inputs) + { + if (input.Val is HttpRequest httpRequest) + { + httpScriptInvocationContext.Data[input.Name] = await httpRequest.GetRequestAsJObject(); + continue; + } + httpScriptInvocationContext.Data[input.Name] = GetHttpScriptInvocationContextValue(input.Val, input.Type); + } + + SetRetryContext(scriptInvocationContext, httpScriptInvocationContext); + return httpScriptInvocationContext; + } + + internal static object GetHttpScriptInvocationContextValue(object inputValue, DataType dataType = DataType.String) + { + if (inputValue is byte[] byteArray) + { + if (dataType == DataType.Binary) + { + return byteArray; + } + return Convert.ToBase64String(byteArray); + } + if (inputValue is DateTime) + { + return DateTime.Parse(inputValue.ToString()); + } + if (inputValue is DateTimeOffset) + { + return DateTimeOffset.Parse(inputValue.ToString()); + } + try + { + return JObject.FromObject(inputValue); + } + catch + { + } + return JsonConvert.SerializeObject(inputValue); + } + + internal static void SetRetryContext(ScriptInvocationContext scriptInvocationContext, HttpScriptInvocationContext httpScriptInvocationContext) + { + if (scriptInvocationContext.ExecutionContext.RetryContext != null) + { + var retryContext = scriptInvocationContext.ExecutionContext.RetryContext; + httpScriptInvocationContext.Metadata["RetryContext"] = new RetryContext() + { + MaxRetryCount = retryContext.MaxRetryCount, + RetryCount = retryContext.RetryCount, + Exception = retryContext.Exception, + }; + } + } + } +} \ No newline at end of file diff --git a/src/WebJobs.Script/Workers/Http/HttpFunctionInvocationDispatcher.cs b/src/WebJobs.Script.Grpc/HttpFunctionInvocationDispatcher.cs similarity index 100% rename from src/WebJobs.Script/Workers/Http/HttpFunctionInvocationDispatcher.cs rename to src/WebJobs.Script.Grpc/HttpFunctionInvocationDispatcher.cs diff --git a/src/WebJobs.Script/Workers/IFunctionInvocationDispatcher.cs b/src/WebJobs.Script.Grpc/IFunctionInvocationDispatcher.cs similarity index 87% rename from src/WebJobs.Script/Workers/IFunctionInvocationDispatcher.cs rename to src/WebJobs.Script.Grpc/IFunctionInvocationDispatcher.cs index b63e6f84c9..9ee6cc9feb 100644 --- a/src/WebJobs.Script/Workers/IFunctionInvocationDispatcher.cs +++ b/src/WebJobs.Script.Grpc/IFunctionInvocationDispatcher.cs @@ -1,4 +1,4 @@ -// Copyright (c) .NET Foundation. All rights reserved. +// Copyright (c) .NET Foundation. All rights reserved. // Licensed under the MIT License. See License.txt in the project root for license information. using System; @@ -9,7 +9,7 @@ namespace Microsoft.Azure.WebJobs.Script.Workers { - public interface IFunctionInvocationDispatcher : IDisposable + internal interface IFunctionInvocationDispatcher : IDisposable { FunctionInvocationDispatcherState State { get; } diff --git a/src/WebJobs.Script/Workers/IFunctionInvocationDispatcherFactory.cs b/src/WebJobs.Script.Grpc/IFunctionInvocationDispatcherFactory.cs similarity index 65% rename from src/WebJobs.Script/Workers/IFunctionInvocationDispatcherFactory.cs rename to src/WebJobs.Script.Grpc/IFunctionInvocationDispatcherFactory.cs index a1827b60b4..10eda29a5f 100644 --- a/src/WebJobs.Script/Workers/IFunctionInvocationDispatcherFactory.cs +++ b/src/WebJobs.Script.Grpc/IFunctionInvocationDispatcherFactory.cs @@ -1,10 +1,10 @@ -// Copyright (c) .NET Foundation. All rights reserved. +// Copyright (c) .NET Foundation. All rights reserved. // Licensed under the MIT License. See License.txt in the project root for license information. namespace Microsoft.Azure.WebJobs.Script.Workers { - public interface IFunctionInvocationDispatcherFactory + internal interface IFunctionInvocationDispatcherFactory { IFunctionInvocationDispatcher GetFunctionDispatcher(); } -} +} \ No newline at end of file diff --git a/src/WebJobs.Script/Workers/IWorkerChannel.cs b/src/WebJobs.Script.Grpc/IWorkerChannel.cs similarity index 78% rename from src/WebJobs.Script/Workers/IWorkerChannel.cs rename to src/WebJobs.Script.Grpc/IWorkerChannel.cs index 97002027fe..accb01cb2b 100644 --- a/src/WebJobs.Script/Workers/IWorkerChannel.cs +++ b/src/WebJobs.Script.Grpc/IWorkerChannel.cs @@ -1,13 +1,12 @@ -// Copyright (c) .NET Foundation. All rights reserved. +// Copyright (c) .NET Foundation. All rights reserved. // Licensed under the MIT License. See License.txt in the project root for license information. -using System.Diagnostics; using System.Threading; using System.Threading.Tasks; namespace Microsoft.Azure.WebJobs.Script.Workers { - public interface IWorkerChannel + internal interface IWorkerChannel { string Id { get; } diff --git a/src/WebJobs.Script/Description/Workers/Rpc/MultiLanguageFunctionDescriptorProvider.cs b/src/WebJobs.Script.Grpc/MultiLanguageFunctionDescriptorProvider.cs similarity index 100% rename from src/WebJobs.Script/Description/Workers/Rpc/MultiLanguageFunctionDescriptorProvider.cs rename to src/WebJobs.Script.Grpc/MultiLanguageFunctionDescriptorProvider.cs diff --git a/src/WebJobs.Script/Workers/ProcessManagement/DefaultWorkerProcessFactory.cs b/src/WebJobs.Script.Grpc/ProcessManagement/DefaultWorkerProcessFactory.cs similarity index 100% rename from src/WebJobs.Script/Workers/ProcessManagement/DefaultWorkerProcessFactory.cs rename to src/WebJobs.Script.Grpc/ProcessManagement/DefaultWorkerProcessFactory.cs diff --git a/src/WebJobs.Script/Workers/ProcessManagement/EmptyProcessRegistry.cs b/src/WebJobs.Script.Grpc/ProcessManagement/EmptyProcessRegistry.cs similarity index 100% rename from src/WebJobs.Script/Workers/ProcessManagement/EmptyProcessRegistry.cs rename to src/WebJobs.Script.Grpc/ProcessManagement/EmptyProcessRegistry.cs diff --git a/src/WebJobs.Script/Workers/ProcessManagement/IProcessRegistry.cs b/src/WebJobs.Script.Grpc/ProcessManagement/IProcessRegistry.cs similarity index 100% rename from src/WebJobs.Script/Workers/ProcessManagement/IProcessRegistry.cs rename to src/WebJobs.Script.Grpc/ProcessManagement/IProcessRegistry.cs diff --git a/src/WebJobs.Script/Workers/ProcessManagement/IWorkerProcess.cs b/src/WebJobs.Script.Grpc/ProcessManagement/IWorkerProcess.cs similarity index 72% rename from src/WebJobs.Script/Workers/ProcessManagement/IWorkerProcess.cs rename to src/WebJobs.Script.Grpc/ProcessManagement/IWorkerProcess.cs index dd5222e341..47f98787e7 100644 --- a/src/WebJobs.Script/Workers/ProcessManagement/IWorkerProcess.cs +++ b/src/WebJobs.Script.Grpc/ProcessManagement/IWorkerProcess.cs @@ -1,13 +1,12 @@ -// Copyright (c) .NET Foundation. All rights reserved. +// Copyright (c) .NET Foundation. All rights reserved. // Licensed under the MIT License. See License.txt in the project root for license information. using System.Diagnostics; using System.Threading.Tasks; -using Microsoft.Azure.WebJobs.Script.Scale; namespace Microsoft.Azure.WebJobs.Script.Workers { - public interface IWorkerProcess + internal interface IWorkerProcess { int Id { get; } diff --git a/src/WebJobs.Script/Workers/ProcessManagement/IWorkerProcessFactory.cs b/src/WebJobs.Script.Grpc/ProcessManagement/IWorkerProcessFactory.cs similarity index 100% rename from src/WebJobs.Script/Workers/ProcessManagement/IWorkerProcessFactory.cs rename to src/WebJobs.Script.Grpc/ProcessManagement/IWorkerProcessFactory.cs diff --git a/src/WebJobs.Script/Workers/ProcessManagement/JobObjectRegistry.cs b/src/WebJobs.Script.Grpc/ProcessManagement/JobObjectRegistry.cs similarity index 92% rename from src/WebJobs.Script/Workers/ProcessManagement/JobObjectRegistry.cs rename to src/WebJobs.Script.Grpc/ProcessManagement/JobObjectRegistry.cs index dffb2ce5cd..43ba5794d1 100644 --- a/src/WebJobs.Script/Workers/ProcessManagement/JobObjectRegistry.cs +++ b/src/WebJobs.Script.Grpc/ProcessManagement/JobObjectRegistry.cs @@ -1,8 +1,8 @@ -// Copyright (c) .NET Foundation. All rights reserved. +// Copyright (c) .NET Foundation. All rights reserved. // Licensed under the MIT License. See License.txt in the project root for license information. using System; -using System.Diagnostics; +using System.Diagnostics.CodeAnalysis; using System.Runtime.InteropServices; namespace Microsoft.Azure.WebJobs.Script.Workers @@ -90,7 +90,8 @@ public void Close() } } - public enum JobObjectInfoType + [SuppressMessage("StyleCop.CSharp.OrderingRules", "SA1201:Elements must appear in the correct order")] + internal enum JobObjectInfoType { AssociateCompletionPortInformation = 7, BasicLimitInformation = 2, @@ -126,8 +127,9 @@ internal struct JOBOBJECT_BASIC_LIMIT_INFORMATION public uint SchedulingClass; } + [SuppressMessage("StyleCop.CSharp.NamingRules", "SA1307:Accessible fields must begin with upper-case letter")] [StructLayout(LayoutKind.Sequential)] - public struct SECURITY_ATTRIBUTES + internal struct SECURITY_ATTRIBUTES { public uint nLength; public IntPtr lpSecurityDescriptor; diff --git a/src/WebJobs.Script/Workers/ProcessManagement/WorkerContext.cs b/src/WebJobs.Script.Grpc/ProcessManagement/WorkerContext.cs similarity index 87% rename from src/WebJobs.Script/Workers/ProcessManagement/WorkerContext.cs rename to src/WebJobs.Script.Grpc/ProcessManagement/WorkerContext.cs index eeec16a934..55e731ae18 100644 --- a/src/WebJobs.Script/Workers/ProcessManagement/WorkerContext.cs +++ b/src/WebJobs.Script.Grpc/ProcessManagement/WorkerContext.cs @@ -1,4 +1,4 @@ -// Copyright (c) .NET Foundation. All rights reserved. +// Copyright (c) .NET Foundation. All rights reserved. // Licensed under the MIT License. See License.txt in the project root for license information. using System.Collections.Generic; @@ -6,7 +6,7 @@ namespace Microsoft.Azure.WebJobs.Script.Workers { // Arguments to start a worker process - public abstract class WorkerContext + internal abstract class WorkerContext { public WorkerProcessArguments Arguments { get; set; } diff --git a/src/WebJobs.Script/Workers/ProcessManagement/WorkerProcess.cs b/src/WebJobs.Script.Grpc/ProcessManagement/WorkerProcess.cs similarity index 100% rename from src/WebJobs.Script/Workers/ProcessManagement/WorkerProcess.cs rename to src/WebJobs.Script.Grpc/ProcessManagement/WorkerProcess.cs diff --git a/src/WebJobs.Script/Workers/ProcessManagement/WorkerProcessExitException.cs b/src/WebJobs.Script.Grpc/ProcessManagement/WorkerProcessExitException.cs similarity index 100% rename from src/WebJobs.Script/Workers/ProcessManagement/WorkerProcessExitException.cs rename to src/WebJobs.Script.Grpc/ProcessManagement/WorkerProcessExitException.cs diff --git a/src/WebJobs.Script/Workers/ProcessManagement/WorkerProcessUtilities.cs b/src/WebJobs.Script.Grpc/ProcessManagement/WorkerProcessUtilities.cs similarity index 100% rename from src/WebJobs.Script/Workers/ProcessManagement/WorkerProcessUtilities.cs rename to src/WebJobs.Script.Grpc/ProcessManagement/WorkerProcessUtilities.cs diff --git a/src/WebJobs.Script/Workers/Rpc/FunctionRegistration/IRpcFunctionInvocationDispatcherLoadBalancer.cs b/src/WebJobs.Script.Grpc/Rpc/FunctionRegistration/IRpcFunctionInvocationDispatcherLoadBalancer.cs similarity index 100% rename from src/WebJobs.Script/Workers/Rpc/FunctionRegistration/IRpcFunctionInvocationDispatcherLoadBalancer.cs rename to src/WebJobs.Script.Grpc/Rpc/FunctionRegistration/IRpcFunctionInvocationDispatcherLoadBalancer.cs diff --git a/src/WebJobs.Script/Workers/Rpc/FunctionRegistration/RpcFunctionInvocationDispatcher.cs b/src/WebJobs.Script.Grpc/Rpc/FunctionRegistration/RpcFunctionInvocationDispatcher.cs similarity index 99% rename from src/WebJobs.Script/Workers/Rpc/FunctionRegistration/RpcFunctionInvocationDispatcher.cs rename to src/WebJobs.Script.Grpc/Rpc/FunctionRegistration/RpcFunctionInvocationDispatcher.cs index 6a416929b4..d04f13da30 100644 --- a/src/WebJobs.Script/Workers/Rpc/FunctionRegistration/RpcFunctionInvocationDispatcher.cs +++ b/src/WebJobs.Script.Grpc/Rpc/FunctionRegistration/RpcFunctionInvocationDispatcher.cs @@ -706,6 +706,9 @@ private void AddLogUserCategory(IEnumerable functions) public void PreShutdown() { + // It's important to prevent any new workers from starting on the orphaned host. The + // only way to guarantee this is to signal to the dispatcher that it's done with process + // creation before we begin a new host. _logger.LogDebug($"Preventing any new worker processes from starting during shutdown."); _processStartCancellationToken.Cancel(); if (_hostingConfigOptions.Value.ShutdownWebhostWorkerChannelsOnHostShutdown && !_scriptOptions.IsStandbyConfiguration) diff --git a/src/WebJobs.Script/Workers/Rpc/FunctionRegistration/RpcFunctionInvocationDispatcherLoadBalancer.cs b/src/WebJobs.Script.Grpc/Rpc/FunctionRegistration/RpcFunctionInvocationDispatcherLoadBalancer.cs similarity index 100% rename from src/WebJobs.Script/Workers/Rpc/FunctionRegistration/RpcFunctionInvocationDispatcherLoadBalancer.cs rename to src/WebJobs.Script.Grpc/Rpc/FunctionRegistration/RpcFunctionInvocationDispatcherLoadBalancer.cs diff --git a/src/WebJobs.Script/Workers/Rpc/IJobHostRpcWorkerChannelManager.cs b/src/WebJobs.Script.Grpc/Rpc/IJobHostRpcWorkerChannelManager.cs similarity index 91% rename from src/WebJobs.Script/Workers/Rpc/IJobHostRpcWorkerChannelManager.cs rename to src/WebJobs.Script.Grpc/Rpc/IJobHostRpcWorkerChannelManager.cs index 37f310ea62..e49a3e50f1 100644 --- a/src/WebJobs.Script/Workers/Rpc/IJobHostRpcWorkerChannelManager.cs +++ b/src/WebJobs.Script.Grpc/Rpc/IJobHostRpcWorkerChannelManager.cs @@ -1,4 +1,4 @@ -// Copyright (c) .NET Foundation. All rights reserved. +// Copyright (c) .NET Foundation. All rights reserved. // Licensed under the MIT License. See License.txt in the project root for license information. using System; diff --git a/src/WebJobs.Script/Workers/Rpc/IRpcServer.cs b/src/WebJobs.Script.Grpc/Rpc/IRpcServer.cs similarity index 87% rename from src/WebJobs.Script/Workers/Rpc/IRpcServer.cs rename to src/WebJobs.Script.Grpc/Rpc/IRpcServer.cs index 3a3f0e135a..696e7f1aa9 100644 --- a/src/WebJobs.Script/Workers/Rpc/IRpcServer.cs +++ b/src/WebJobs.Script.Grpc/Rpc/IRpcServer.cs @@ -1,14 +1,12 @@ -// Copyright (c) .NET Foundation. All rights reserved. +// Copyright (c) .NET Foundation. All rights reserved. // Licensed under the MIT License. See License.txt in the project root for license information. using System; -using System.Collections.Generic; -using System.Text; using System.Threading.Tasks; namespace Microsoft.Azure.WebJobs.Script.Workers.Rpc { - public interface IRpcServer + internal interface IRpcServer { /// /// Gets the address that the rpc server is listening on diff --git a/src/WebJobs.Script/Workers/Rpc/IRpcWorkerChannel.cs b/src/WebJobs.Script.Grpc/Rpc/IRpcWorkerChannel.cs similarity index 90% rename from src/WebJobs.Script/Workers/Rpc/IRpcWorkerChannel.cs rename to src/WebJobs.Script.Grpc/Rpc/IRpcWorkerChannel.cs index f187eae71f..1e6da2fb99 100644 --- a/src/WebJobs.Script/Workers/Rpc/IRpcWorkerChannel.cs +++ b/src/WebJobs.Script.Grpc/Rpc/IRpcWorkerChannel.cs @@ -1,4 +1,4 @@ -// Copyright (c) .NET Foundation. All rights reserved. +// Copyright (c) .NET Foundation. All rights reserved. // Licensed under the MIT License. See License.txt in the project root for license information. using System; @@ -10,7 +10,7 @@ namespace Microsoft.Azure.WebJobs.Script.Workers.Rpc { - public interface IRpcWorkerChannel : IWorkerChannel + internal interface IRpcWorkerChannel : IWorkerChannel { RpcWorkerConfig WorkerConfig { get; } diff --git a/src/WebJobs.Script/Description/Workers/Rpc/IRpcWorkerChannelDictionary.cs b/src/WebJobs.Script.Grpc/Rpc/IRpcWorkerChannelDictionary.cs similarity index 100% rename from src/WebJobs.Script/Description/Workers/Rpc/IRpcWorkerChannelDictionary.cs rename to src/WebJobs.Script.Grpc/Rpc/IRpcWorkerChannelDictionary.cs diff --git a/src/WebJobs.Script/Workers/Rpc/IRpcWorkerChannelFactory.cs b/src/WebJobs.Script.Grpc/Rpc/IRpcWorkerChannelFactory.cs similarity index 80% rename from src/WebJobs.Script/Workers/Rpc/IRpcWorkerChannelFactory.cs rename to src/WebJobs.Script.Grpc/Rpc/IRpcWorkerChannelFactory.cs index f7b840b4f0..9e72878434 100644 --- a/src/WebJobs.Script/Workers/Rpc/IRpcWorkerChannelFactory.cs +++ b/src/WebJobs.Script.Grpc/Rpc/IRpcWorkerChannelFactory.cs @@ -1,4 +1,4 @@ -// Copyright (c) .NET Foundation. All rights reserved. +// Copyright (c) .NET Foundation. All rights reserved. // Licensed under the MIT License. See License.txt in the project root for license information. using System.Collections.Generic; @@ -6,7 +6,7 @@ namespace Microsoft.Azure.WebJobs.Script.Workers.Rpc { - public interface IRpcWorkerChannelFactory + internal interface IRpcWorkerChannelFactory { IRpcWorkerChannel Create(string scriptRootPath, string language, IMetricsLogger metricsLogger, int attemptCount, IEnumerable workerConfigs); } diff --git a/src/WebJobs.Script/Workers/Rpc/IRpcWorkerProcessFactory.cs b/src/WebJobs.Script.Grpc/Rpc/IRpcWorkerProcessFactory.cs similarity index 73% rename from src/WebJobs.Script/Workers/Rpc/IRpcWorkerProcessFactory.cs rename to src/WebJobs.Script.Grpc/Rpc/IRpcWorkerProcessFactory.cs index b79b4a0070..02b7fc7712 100644 --- a/src/WebJobs.Script/Workers/Rpc/IRpcWorkerProcessFactory.cs +++ b/src/WebJobs.Script.Grpc/Rpc/IRpcWorkerProcessFactory.cs @@ -1,9 +1,9 @@ -// Copyright (c) .NET Foundation. All rights reserved. +// Copyright (c) .NET Foundation. All rights reserved. // Licensed under the MIT License. See License.txt in the project root for license information. namespace Microsoft.Azure.WebJobs.Script.Workers.Rpc { - public interface IRpcWorkerProcessFactory + internal interface IRpcWorkerProcessFactory { IWorkerProcess Create(string workerId, string runtime, string scriptRootPath, RpcWorkerConfig rpcWorkerConfig); } diff --git a/src/WebJobs.Script/Workers/Rpc/IWebHostRpcWorkerChannelManager.cs b/src/WebJobs.Script.Grpc/Rpc/IWebHostRpcWorkerChannelManager.cs similarity index 85% rename from src/WebJobs.Script/Workers/Rpc/IWebHostRpcWorkerChannelManager.cs rename to src/WebJobs.Script.Grpc/Rpc/IWebHostRpcWorkerChannelManager.cs index 9df04d3d62..5182ead215 100644 --- a/src/WebJobs.Script/Workers/Rpc/IWebHostRpcWorkerChannelManager.cs +++ b/src/WebJobs.Script.Grpc/Rpc/IWebHostRpcWorkerChannelManager.cs @@ -1,4 +1,4 @@ -// Copyright (c) .NET Foundation. All rights reserved. +// Copyright (c) .NET Foundation. All rights reserved. // Licensed under the MIT License. See License.txt in the project root for license information. using System; @@ -7,7 +7,7 @@ namespace Microsoft.Azure.WebJobs.Script.Workers.Rpc { - public interface IWebHostRpcWorkerChannelManager + internal interface IWebHostRpcWorkerChannelManager { Task InitializeChannelAsync(IEnumerable workerConfigs, string language); diff --git a/src/WebJobs.Script/Workers/Rpc/JobHostRpcWorkerChannelManager.cs b/src/WebJobs.Script.Grpc/Rpc/JobHostRpcWorkerChannelManager.cs similarity index 100% rename from src/WebJobs.Script/Workers/Rpc/JobHostRpcWorkerChannelManager.cs rename to src/WebJobs.Script.Grpc/Rpc/JobHostRpcWorkerChannelManager.cs diff --git a/src/WebJobs.Script/Workers/Rpc/RpcInitializationService.cs b/src/WebJobs.Script.Grpc/Rpc/RpcInitializationService.cs similarity index 98% rename from src/WebJobs.Script/Workers/Rpc/RpcInitializationService.cs rename to src/WebJobs.Script.Grpc/Rpc/RpcInitializationService.cs index fb204925fd..a9c44fa41f 100644 --- a/src/WebJobs.Script/Workers/Rpc/RpcInitializationService.cs +++ b/src/WebJobs.Script.Grpc/Rpc/RpcInitializationService.cs @@ -11,7 +11,7 @@ namespace Microsoft.Azure.WebJobs.Script.Workers.Rpc { - public class RpcInitializationService : IManagedHostedService + internal class RpcInitializationService : IManagedHostedService { private readonly IOptionsMonitor _applicationHostOptions; private readonly IEnvironment _environment; diff --git a/src/WebJobs.Script/Workers/Rpc/RpcWorkerChannelState.cs b/src/WebJobs.Script.Grpc/Rpc/RpcWorkerChannelState.cs similarity index 89% rename from src/WebJobs.Script/Workers/Rpc/RpcWorkerChannelState.cs rename to src/WebJobs.Script.Grpc/Rpc/RpcWorkerChannelState.cs index 0eb4a3ef88..e926460d81 100644 --- a/src/WebJobs.Script/Workers/Rpc/RpcWorkerChannelState.cs +++ b/src/WebJobs.Script.Grpc/Rpc/RpcWorkerChannelState.cs @@ -1,4 +1,4 @@ -// Copyright (c) .NET Foundation. All rights reserved. +// Copyright (c) .NET Foundation. All rights reserved. // Licensed under the MIT License. See License.txt in the project root for license information. using System; @@ -6,7 +6,7 @@ namespace Microsoft.Azure.WebJobs.Script.Workers.Rpc { [Flags] - public enum RpcWorkerChannelState + internal enum RpcWorkerChannelState { /// /// The Default state of LanguageWorkerChannel. diff --git a/src/WebJobs.Script/Workers/Rpc/RpcWorkerContext.cs b/src/WebJobs.Script.Grpc/Rpc/RpcWorkerContext.cs similarity index 100% rename from src/WebJobs.Script/Workers/Rpc/RpcWorkerContext.cs rename to src/WebJobs.Script.Grpc/Rpc/RpcWorkerContext.cs diff --git a/src/WebJobs.Script/Workers/Rpc/RpcWorkerProcess.cs b/src/WebJobs.Script.Grpc/Rpc/RpcWorkerProcess.cs similarity index 100% rename from src/WebJobs.Script/Workers/Rpc/RpcWorkerProcess.cs rename to src/WebJobs.Script.Grpc/Rpc/RpcWorkerProcess.cs diff --git a/src/WebJobs.Script/Workers/Rpc/RpcWorkerProcessFactory.cs b/src/WebJobs.Script.Grpc/Rpc/RpcWorkerProcessFactory.cs similarity index 100% rename from src/WebJobs.Script/Workers/Rpc/RpcWorkerProcessFactory.cs rename to src/WebJobs.Script.Grpc/Rpc/RpcWorkerProcessFactory.cs diff --git a/src/WebJobs.Script/Workers/Rpc/WebHostRpcWorkerChannelManager.cs b/src/WebJobs.Script.Grpc/Rpc/WebHostRpcWorkerChannelManager.cs similarity index 99% rename from src/WebJobs.Script/Workers/Rpc/WebHostRpcWorkerChannelManager.cs rename to src/WebJobs.Script.Grpc/Rpc/WebHostRpcWorkerChannelManager.cs index 95663b3ccb..7d47f903c0 100644 --- a/src/WebJobs.Script/Workers/Rpc/WebHostRpcWorkerChannelManager.cs +++ b/src/WebJobs.Script.Grpc/Rpc/WebHostRpcWorkerChannelManager.cs @@ -17,7 +17,7 @@ namespace Microsoft.Azure.WebJobs.Script.Workers.Rpc { - public class WebHostRpcWorkerChannelManager : IWebHostRpcWorkerChannelManager + internal class WebHostRpcWorkerChannelManager : IWebHostRpcWorkerChannelManager { private readonly ILogger _logger = null; private readonly IOptionsMonitor _applicationHostOptions = null; diff --git a/src/WebJobs.Script/Description/Workers/Rpc/RpcFunctionDescriptorProvider.cs b/src/WebJobs.Script.Grpc/RpcFunctionDescriptorProvider.cs similarity index 100% rename from src/WebJobs.Script/Description/Workers/Rpc/RpcFunctionDescriptorProvider.cs rename to src/WebJobs.Script.Grpc/RpcFunctionDescriptorProvider.cs diff --git a/src/WebJobs.Script.Grpc/RpcScriptHostLifecycleService.cs b/src/WebJobs.Script.Grpc/RpcScriptHostLifecycleService.cs new file mode 100644 index 0000000000..363663d05c --- /dev/null +++ b/src/WebJobs.Script.Grpc/RpcScriptHostLifecycleService.cs @@ -0,0 +1,32 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the MIT License. See License.txt in the project root for license information. + +using System.Collections.Generic; +using System.Threading; +using System.Threading.Tasks; +using Microsoft.Azure.WebJobs.Script.Description; +using Microsoft.Azure.WebJobs.Script.Host; +using Microsoft.Azure.WebJobs.Script.Workers; + +namespace Microsoft.Azure.WebJobs.Script.Rpc; + +internal sealed class RpcScriptHostLifecycleService : IScriptHostLifecycleService +{ + private readonly IFunctionInvocationDispatcher _dispatcher; + + public RpcScriptHostLifecycleService(IFunctionInvocationDispatcherFactory dispatcherFactory) + { + _dispatcher = dispatcherFactory.GetFunctionDispatcher(); + } + + public Task InitializedAsync(IEnumerable functions, CancellationToken cancellationToken) + { + return _dispatcher.InitializeAsync(functions, cancellationToken); + } + + public Task StoppingAsync(CancellationToken cancellationToken) + { + _dispatcher.PreShutdown(); + return Task.CompletedTask; + } +} diff --git a/src/WebJobs.Script.Grpc/RpcScriptHostWorkerManager.cs b/src/WebJobs.Script.Grpc/RpcScriptHostWorkerManager.cs new file mode 100644 index 0000000000..ebddc84088 --- /dev/null +++ b/src/WebJobs.Script.Grpc/RpcScriptHostWorkerManager.cs @@ -0,0 +1,89 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the MIT License. See License.txt in the project root for license information. + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using Microsoft.Azure.WebJobs.Script.Workers; +using Microsoft.Azure.WebJobs.Script.Workers.Rpc; + +namespace Microsoft.Azure.WebJobs.Script.Rpc; + +internal class RpcScriptHostWorkerManager : IScriptHostWorkerManager +{ + private readonly IFunctionInvocationDispatcher _dispatcher; + private readonly IJobHostRpcWorkerChannelManager _jobHostManager; + private readonly IWebHostRpcWorkerChannelManager _webHostManager; + + public RpcScriptHostWorkerManager( + IFunctionInvocationDispatcherFactory dispatcherFactory, + IJobHostRpcWorkerChannelManager jobHostManager, + IWebHostRpcWorkerChannelManager webHostManager) + { + _dispatcher = dispatcherFactory.GetFunctionDispatcher(); + _jobHostManager = jobHostManager; + _webHostManager = webHostManager; + } + + public WorkerManagerState State => + _dispatcher.State switch + { + FunctionInvocationDispatcherState.Default => WorkerManagerState.Default, + _ => WorkerManagerState.Initialized + }; + + public async Task> GetWorkerProcessInfoAsync(string workerRuntime) + { + List channels = _jobHostManager.GetChannels(workerRuntime).ToList(); + + var webhostChannelDictionary = _webHostManager.GetChannels(workerRuntime); + + List> webHostchannelTasks = new List>(); + if (webhostChannelDictionary is not null) + { + foreach (var pair in webhostChannelDictionary) + { + var workerChannel = pair.Value.Task; + webHostchannelTasks.Add(workerChannel); + } + } + + var webHostchannels = await Task.WhenAll(webHostchannelTasks); + channels = channels ?? new List(); + channels.AddRange(webHostchannels); + + var processes = new List(); + + foreach (var channel in channels) + { + var processInfo = new WorkerProcessInfo() + { + ProcessId = channel.WorkerProcess.Process.Id, + ProcessName = channel.WorkerProcess.Process.ProcessName, + DebugEngine = Utility.GetDebugEngineInfo(channel.WorkerConfig, workerRuntime), + }; + processes.Add(processInfo); + } + + return processes; + } + + public Task GetWorkerStatusesAsync() + { + // This is only called from one place (HostPerformanceManager) and the original contract was that + // GetWorkerStatusAsync() is not called if the dispatcher is not initialized. It appears that it is used + // to populate latency history internally and the result is never used directly, so don't return it. + if (_dispatcher.State != FunctionInvocationDispatcherState.Initialized) + { + return Task.CompletedTask; + } + + return _dispatcher.GetWorkerStatusesAsync(); + } + + public Task RestartWorkerWithInvocationIdAsync(string invocationId, Exception exception) + { + return _dispatcher.RestartWorkerWithInvocationIdAsync(invocationId, exception); + } +} diff --git a/src/WebJobs.Script.Grpc/RpcWebHostWorkerManager.cs b/src/WebJobs.Script.Grpc/RpcWebHostWorkerManager.cs new file mode 100644 index 0000000000..40c7e94cbc --- /dev/null +++ b/src/WebJobs.Script.Grpc/RpcWebHostWorkerManager.cs @@ -0,0 +1,28 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the MIT License. See License.txt in the project root for license information. + +using System.Threading.Tasks; +using Microsoft.Azure.WebJobs.Script.WebHost; +using Microsoft.Azure.WebJobs.Script.Workers.Rpc; + +namespace Microsoft.Azure.WebJobs.Script.Rpc; + +internal class RpcWebHostWorkerManager : IWebHostWorkerManager +{ + private readonly IWebHostRpcWorkerChannelManager _webHostChannelManager; + + public RpcWebHostWorkerManager(IWebHostRpcWorkerChannelManager webHostChannelManager) + { + _webHostChannelManager = webHostChannelManager; + } + + public Task SpecializeAsync() + { + return _webHostChannelManager.SpecializeAsync(); + } + + public Task WorkerWarmupAsync() + { + return _webHostChannelManager.WorkerWarmupAsync(); + } +} diff --git a/src/WebJobs.Script.Grpc/RpcWorkerFunctionDescriptorProviderFactory.cs b/src/WebJobs.Script.Grpc/RpcWorkerFunctionDescriptorProviderFactory.cs new file mode 100644 index 0000000000..fb8717fcac --- /dev/null +++ b/src/WebJobs.Script.Grpc/RpcWorkerFunctionDescriptorProviderFactory.cs @@ -0,0 +1,62 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the MIT License. See License.txt in the project root for license information. + +using System; +using System.Collections.Generic; +using System.Linq; +using Microsoft.AspNetCore.Hosting; +using Microsoft.Azure.WebJobs.Logging; +using Microsoft.Azure.WebJobs.Script.Extensibility; +using Microsoft.Azure.WebJobs.Script.Workers; +using Microsoft.Azure.WebJobs.Script.Workers.Http; +using Microsoft.Azure.WebJobs.Script.Workers.Rpc; +using Microsoft.Extensions.Logging; +using Microsoft.Extensions.Options; + +namespace Microsoft.Azure.WebJobs.Script.Description; + +internal class RpcWorkerFunctionDescriptorProviderFactory : IWorkerFunctionDescriptorProviderFactory +{ + private readonly IFunctionInvocationDispatcher _dispatcher; + private readonly IApplicationLifetime _applicationLifetime; + private readonly HttpWorkerOptions _httpWorkerOptions; + private readonly ScriptJobHostOptions _scriptHostOptions; + private readonly IOptionsMonitor _languageWorkerOptionsMonitor; + private readonly ILoggerFactory _loggerFactory; + private readonly ILogger _logger; + + public RpcWorkerFunctionDescriptorProviderFactory(IFunctionInvocationDispatcherFactory dispatcherFactory, IApplicationLifetime applicationLifetime, IOptions scriptHostOptions, + IOptions httpWorkerOptions, IOptionsMonitor languageWorkerOptionsMonitor, ILoggerFactory loggerFactory) + { + _dispatcher = dispatcherFactory.GetFunctionDispatcher(); + _applicationLifetime = applicationLifetime; + _httpWorkerOptions = httpWorkerOptions.Value; + _scriptHostOptions = scriptHostOptions.Value; + _languageWorkerOptionsMonitor = languageWorkerOptionsMonitor; + _loggerFactory = loggerFactory; + _logger = _loggerFactory.CreateLogger(LogCategories.Startup); + } + + public FunctionDescriptorProvider CreateMultiWorkerDescriptorProvider(ScriptHost host, ICollection bindingProviders) + { + var workerOptions = _languageWorkerOptionsMonitor.CurrentValue; + return new MultiLanguageFunctionDescriptorProvider(host, workerOptions.WorkerConfigs, _scriptHostOptions, bindingProviders, + _dispatcher, _loggerFactory, _applicationLifetime, workerOptions.WorkerConfigs.Max(wc => wc.CountOptions.InitializationTimeout)); + } + + public FunctionDescriptorProvider CreateWorkerDescriptorProvider(ScriptHost host, string workerRuntime, ICollection bindingProviders) + { + if (_httpWorkerOptions.Description is not null) + { + _logger.LogDebug(new EventId(414, "AddingDescriptorProviderForHttpWorker"), "Adding Function descriptor provider for HttpWorker."); + return new HttpFunctionDescriptorProvider(host, _scriptHostOptions, bindingProviders, _dispatcher, _loggerFactory, _applicationLifetime, _httpWorkerOptions.InitializationTimeout); + } + + var workerConfig = _languageWorkerOptionsMonitor.CurrentValue.WorkerConfigs?.FirstOrDefault(c => c.Description.Language.Equals(workerRuntime, StringComparison.OrdinalIgnoreCase)); + + // If there's no worker config, use the default (for legacy behavior; mostly for tests). + TimeSpan initializationTimeout = workerConfig?.CountOptions?.InitializationTimeout ?? WorkerProcessCountOptions.DefaultInitializationTimeout; + + return new RpcFunctionDescriptorProvider(host, workerRuntime, _scriptHostOptions, bindingProviders, _dispatcher, _loggerFactory, _applicationLifetime, initializationTimeout); + } +} \ No newline at end of file diff --git a/src/WebJobs.Script.Grpc/Server/AspNetCoreGrpcServer.cs b/src/WebJobs.Script.Grpc/Server/AspNetCoreGrpcServer.cs index 9e966b8dad..f11584a282 100644 --- a/src/WebJobs.Script.Grpc/Server/AspNetCoreGrpcServer.cs +++ b/src/WebJobs.Script.Grpc/Server/AspNetCoreGrpcServer.cs @@ -1,4 +1,4 @@ -// Copyright (c) .NET Foundation. All rights reserved. +// Copyright (c) .NET Foundation. All rights reserved. // Licensed under the MIT License. See License.txt in the project root for license information. using System; @@ -16,7 +16,7 @@ namespace Microsoft.Azure.WebJobs.Script.Grpc { - public class AspNetCoreGrpcServer : IRpcServer, IDisposable, IAsyncDisposable + internal class AspNetCoreGrpcServer : IRpcServer, IDisposable, IAsyncDisposable { private readonly IHostBuilder _grpcHostBuilder; private readonly ILogger _logger; diff --git a/src/WebJobs.Script/Scale/WorkerChannelThrottleProvider.cs b/src/WebJobs.Script.Grpc/WorkerChannelThrottleProvider.cs similarity index 90% rename from src/WebJobs.Script/Scale/WorkerChannelThrottleProvider.cs rename to src/WebJobs.Script.Grpc/WorkerChannelThrottleProvider.cs index 235104c616..dfed20ab6f 100644 --- a/src/WebJobs.Script/Scale/WorkerChannelThrottleProvider.cs +++ b/src/WebJobs.Script.Grpc/WorkerChannelThrottleProvider.cs @@ -1,4 +1,4 @@ -// Copyright (c) .NET Foundation. All rights reserved. +// Copyright (c) .NET Foundation. All rights reserved. // Licensed under the MIT License. See License.txt in the project root for license information. using System; @@ -12,7 +12,7 @@ namespace Microsoft.Azure.WebJobs.Script.Scale /// /// Throttle provider that monitors the health of OOP worker channels. /// - public class WorkerChannelThrottleProvider : IConcurrencyThrottleProvider + internal class WorkerChannelThrottleProvider : IConcurrencyThrottleProvider { private readonly IServiceProvider _serviceProvider; diff --git a/src/WebJobs.Script/Workers/WorkerConcurrencyManager.cs b/src/WebJobs.Script.Grpc/WorkerConcurrencyManager.cs similarity index 97% rename from src/WebJobs.Script/Workers/WorkerConcurrencyManager.cs rename to src/WebJobs.Script.Grpc/WorkerConcurrencyManager.cs index 287e7fa35a..eea827150e 100644 --- a/src/WebJobs.Script/Workers/WorkerConcurrencyManager.cs +++ b/src/WebJobs.Script.Grpc/WorkerConcurrencyManager.cs @@ -1,4 +1,4 @@ -// Copyright (c) .NET Foundation. All rights reserved. +// Copyright (c) .NET Foundation. All rights reserved. // Licensed under the MIT License. See License.txt in the project root for license information. using System; @@ -8,8 +8,8 @@ using System.Text; using System.Threading; using System.Threading.Tasks; +using Microsoft.Azure.AppService.Proxy.Common.Infra; using Microsoft.Azure.WebJobs.Host; -using Microsoft.Azure.WebJobs.Host.Scale; using Microsoft.Azure.WebJobs.Logging; using Microsoft.Azure.WebJobs.Script.Config; using Microsoft.Azure.WebJobs.Script.Diagnostics; @@ -147,7 +147,15 @@ internal async void OnTimer(object sender, System.Timers.ElapsedEventArgs e) // don't allow background exceptions to escape _logger.LogError(ex, "Error monitoring worker concurrency"); } - _timer.Start(); + + try + { + _timer.Start(); + } + catch (ObjectDisposedException) + { + // ignore this as disposal can happen while this method was running + } } private void Activate() diff --git a/src/WebJobs.Script/Description/Workers/WorkerFunctionDescriptorProvider.cs b/src/WebJobs.Script.Grpc/WorkerFunctionDescriptorProvider.cs similarity index 100% rename from src/WebJobs.Script/Description/Workers/WorkerFunctionDescriptorProvider.cs rename to src/WebJobs.Script.Grpc/WorkerFunctionDescriptorProvider.cs diff --git a/src/WebJobs.Script/Description/Workers/WorkerFunctionInvoker.cs b/src/WebJobs.Script.Grpc/WorkerFunctionInvoker.cs similarity index 100% rename from src/WebJobs.Script/Description/Workers/WorkerFunctionInvoker.cs rename to src/WebJobs.Script.Grpc/WorkerFunctionInvoker.cs diff --git a/src/WebJobs.Script/Host/WorkerFunctionMetadataProvider.cs b/src/WebJobs.Script.Grpc/WorkerFunctionMetadataProvider.cs similarity index 100% rename from src/WebJobs.Script/Host/WorkerFunctionMetadataProvider.cs rename to src/WebJobs.Script.Grpc/WorkerFunctionMetadataProvider.cs diff --git a/src/WebJobs.Script/WorkerUtilities.cs b/src/WebJobs.Script.Grpc/WorkerUtilities.cs similarity index 97% rename from src/WebJobs.Script/WorkerUtilities.cs rename to src/WebJobs.Script.Grpc/WorkerUtilities.cs index e1c4f14e01..2ef7040223 100644 --- a/src/WebJobs.Script/WorkerUtilities.cs +++ b/src/WebJobs.Script.Grpc/WorkerUtilities.cs @@ -6,7 +6,7 @@ namespace Microsoft.Azure.WebJobs.Script.Workers { - public static class WorkerUtilities + internal static class WorkerUtilities { public static int GetUnusedTcpPort() { diff --git a/src/WebJobs.Script.WebHost/Controllers/HostController.cs b/src/WebJobs.Script.WebHost/Controllers/HostController.cs index adc2c7ddec..6101f51484 100644 --- a/src/WebJobs.Script.WebHost/Controllers/HostController.cs +++ b/src/WebJobs.Script.WebHost/Controllers/HostController.cs @@ -5,7 +5,6 @@ using System.Collections.Generic; using System.Collections.ObjectModel; using System.Diagnostics; -using System.Linq; using System.Net.Http; using System.Threading; using System.Threading.Tasks; @@ -23,6 +22,7 @@ using Microsoft.Azure.WebJobs.Script.WebHost.Management; using Microsoft.Azure.WebJobs.Script.WebHost.Models; using Microsoft.Azure.WebJobs.Script.WebHost.Security.Authorization.Policies; +using Microsoft.Azure.WebJobs.Script.Workers; using Microsoft.Azure.WebJobs.Script.Workers.Rpc; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Logging; @@ -123,7 +123,7 @@ public async Task GetHostStatus([FromServices] IScriptHostManager [Authorize(Policy = PolicyNames.AdminAuthLevel)] public async Task GetWorkerProcesses([FromServices] IScriptHostManager scriptHostManager) { - if (!Utility.TryGetHostService(scriptHostManager, out IWebHostRpcWorkerChannelManager webHostLanguageWorkerChannelManager)) + if (!Utility.TryGetHostService(scriptHostManager, out IWebHostWorkerManager webHostWorkerManager)) { return StatusCode(StatusCodes.Status503ServiceUnavailable); } @@ -142,38 +142,21 @@ public async Task GetWorkerProcesses([FromServices] IScriptHostMa string workerRuntime = _environment.GetFunctionsWorkerRuntime(); - List channels = null; - if (Utility.TryGetHostService(scriptHostManager, out IJobHostRpcWorkerChannelManager jobHostLanguageWorkerChannelManager)) + IEnumerable workerProcesses = null; + if (Utility.TryGetHostService(scriptHostManager, out IScriptHostWorkerManager scriptHostWorkerManager)) { - channels = jobHostLanguageWorkerChannelManager.GetChannels(workerRuntime).ToList(); + workerProcesses = await scriptHostWorkerManager.GetWorkerProcessInfoAsync(workerRuntime); } - var webhostChannelDictionary = webHostLanguageWorkerChannelManager.GetChannels(workerRuntime); - - List> webHostchannelTasks = new List>(); - if (webhostChannelDictionary is not null) - { - foreach (var pair in webhostChannelDictionary) - { - var workerChannel = pair.Value.Task; - webHostchannelTasks.Add(workerChannel); - } - } - - var webHostchannels = await Task.WhenAll(webHostchannelTasks); - channels = channels ?? new List(); - channels.AddRange(webHostchannels); - - foreach (var channel in channels) + foreach (var workerProcess in workerProcesses) { - var processInfo = new FunctionProcesses.FunctionProcessInfo() + processes.Add(new FunctionProcesses.FunctionProcessInfo() { - ProcessId = channel.WorkerProcess.Process.Id, - ProcessName = channel.WorkerProcess.Process.ProcessName, - DebugEngine = Utility.GetDebugEngineInfo(channel.WorkerConfig, workerRuntime), + ProcessId = workerProcess.ProcessId, + ProcessName = workerProcess.ProcessName, + DebugEngine = workerProcess.DebugEngine, IsEligibleForOpenInBrowser = false - }; - processes.Add(processInfo); + }); } var functionProcesses = new FunctionProcesses() diff --git a/src/WebJobs.Script.WebHost/DependencyInjection/DependencyValidator/DependencyValidator.cs b/src/WebJobs.Script.WebHost/DependencyInjection/DependencyValidator/DependencyValidator.cs index e0038bd32d..54964a269f 100644 --- a/src/WebJobs.Script.WebHost/DependencyInjection/DependencyValidator/DependencyValidator.cs +++ b/src/WebJobs.Script.WebHost/DependencyInjection/DependencyValidator/DependencyValidator.cs @@ -1,4 +1,4 @@ -// Copyright (c) .NET Foundation. All rights reserved. +// Copyright (c) .NET Foundation. All rights reserved. // Licensed under the MIT License. See License.txt in the project root for license information. using System.Text; @@ -10,10 +10,8 @@ using Microsoft.Azure.WebJobs.Script.Eventing; using Microsoft.Azure.WebJobs.Script.FileProvisioning; using Microsoft.Azure.WebJobs.Script.Host; -using Microsoft.Azure.WebJobs.Script.Scale; using Microsoft.Azure.WebJobs.Script.WebHost.Diagnostics; using Microsoft.Azure.WebJobs.Script.Workers; -using Microsoft.Azure.WebJobs.Script.Workers.Rpc; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Hosting; using Microsoft.Extensions.Logging; @@ -47,8 +45,8 @@ private static ExpectedDependencyBuilder CreateExpectedDependencies() .ExpectFactory() .ExpectFactory() .Expect() - .Expect() - .Expect() + .OptionalExternal("Microsoft.Azure.WebJobs.Script.Workers.FunctionInvocationDispatcherShutdownManager", "Microsoft.Azure.WebJobs.Script.Grpc", null) + .OptionalExternal("Microsoft.Azure.WebJobs.Script.Workers.WorkerConcurrencyManager", "Microsoft.Azure.WebJobs.Script.Grpc", null) .Optional() // Conditionally registered. .Optional() // Used by powershell. .Optional() // Missing when host is offline. diff --git a/src/WebJobs.Script.WebHost/FileMonitoringService.cs b/src/WebJobs.Script.WebHost/FileMonitoringService.cs index 2449471fcc..96bc1bdac7 100644 --- a/src/WebJobs.Script.WebHost/FileMonitoringService.cs +++ b/src/WebJobs.Script.WebHost/FileMonitoringService.cs @@ -1,4 +1,4 @@ -// Copyright (c) .NET Foundation. All rights reserved. +// Copyright (c) .NET Foundation. All rights reserved. // Licensed under the MIT License. See License.txt in the project root for license information. using System; @@ -14,7 +14,6 @@ using Microsoft.Azure.WebJobs.Script.Eventing; using Microsoft.Azure.WebJobs.Script.Eventing.File; using Microsoft.Azure.WebJobs.Script.IO; -using Microsoft.Extensions.Hosting; using Microsoft.Extensions.Logging; using Microsoft.Extensions.Options; using IApplicationLifetime = Microsoft.AspNetCore.Hosting.IApplicationLifetime; @@ -178,7 +177,7 @@ private void HandleHostRestartEvent(HostRestartEvent restartEvent) /// /// Initializes the file and directory monitoring that does not need to happen as part of a Host startup - /// These watchers can be started after a delay to avoid startup performance issue + /// These watchers can be started after a delay to avoid startup performance issue. /// private void InitializeSecondaryFileWatchers() { @@ -246,7 +245,7 @@ private void StopFileWatchers() } /// - /// Whenever the debug sentinel file changes we update our debug timeout + /// Whenever the debug sentinel file changes we update our debug timeout. /// private void OnDebugModeFileChanged(object sender, FileSystemEventArgs e) { @@ -257,7 +256,7 @@ private void OnDebugModeFileChanged(object sender, FileSystemEventArgs e) } /// - /// Whenever the diagnostic sentinel file changes we update our debug timeout + /// Whenever the diagnostic sentinel file changes we update our debug timeout. /// private void OnDiagnosticModeFileChanged(object sender, FileSystemEventArgs e) { diff --git a/src/WebJobs.Script.WebHost/Middleware/HostWarmupMiddleware.cs b/src/WebJobs.Script.WebHost/Middleware/HostWarmupMiddleware.cs index 22ca847c17..58e643a778 100644 --- a/src/WebJobs.Script.WebHost/Middleware/HostWarmupMiddleware.cs +++ b/src/WebJobs.Script.WebHost/Middleware/HostWarmupMiddleware.cs @@ -1,4 +1,4 @@ -// Copyright (c) .NET Foundation. All rights reserved. +// Copyright (c) .NET Foundation. All rights reserved. // Licensed under the MIT License. See License.txt in the project root for license information. using System; @@ -9,7 +9,6 @@ using Microsoft.AspNetCore.Http; using Microsoft.Azure.WebJobs.Script.Config; using Microsoft.Azure.WebJobs.Script.Extensions; -using Microsoft.Azure.WebJobs.Script.Workers.Rpc; using Microsoft.Diagnostics.JitTrace; using Microsoft.Extensions.Logging; using Microsoft.Extensions.Options; @@ -19,7 +18,7 @@ namespace Microsoft.Azure.WebJobs.Script.WebHost.Middleware { public class HostWarmupMiddleware { - private readonly IWebHostRpcWorkerChannelManager _webHostRpcWorkerChannelManager; + private readonly IWebHostWorkerManager _workerManager; private readonly IOptions _hostingConfigOptions; private readonly RequestDelegate _next; private readonly IScriptWebHostEnvironment _webHostEnvironment; @@ -38,7 +37,7 @@ public HostWarmupMiddleware( IEnvironment environment, IScriptHostManager hostManager, ILogger logger, - IWebHostRpcWorkerChannelManager rpcWorkerChannelManager, + IWebHostWorkerManager workerManager, IOptions hostingConfigOptions) { _next = next; @@ -47,7 +46,7 @@ public HostWarmupMiddleware( _hostManager = hostManager; _logger = logger; _assemblyLocalPath = Path.GetDirectoryName(new Uri(typeof(HostWarmupMiddleware).Assembly.Location).LocalPath); - _webHostRpcWorkerChannelManager = rpcWorkerChannelManager ?? throw new ArgumentNullException(nameof(rpcWorkerChannelManager)); + _workerManager = workerManager ?? throw new ArgumentNullException(nameof(workerManager)); _hostingConfigOptions = hostingConfigOptions; } @@ -88,7 +87,7 @@ public async Task WarmupInvoke(HttpContext httpContext) private async Task WorkerWarmupAsync() { - await _webHostRpcWorkerChannelManager.WorkerWarmupAsync(); + await _workerManager.WorkerWarmupAsync(); } internal void ReadRuntimeAssemblyFiles() diff --git a/src/WebJobs.Script.WebHost/Standby/StandbyManager.cs b/src/WebJobs.Script.WebHost/Standby/StandbyManager.cs index 1e89eb6c26..f12c6540c4 100644 --- a/src/WebJobs.Script.WebHost/Standby/StandbyManager.cs +++ b/src/WebJobs.Script.WebHost/Standby/StandbyManager.cs @@ -11,7 +11,6 @@ using Microsoft.Azure.WebJobs.Script.Description; using Microsoft.Azure.WebJobs.Script.Diagnostics; using Microsoft.Azure.WebJobs.Script.WebHost.Properties; -using Microsoft.Azure.WebJobs.Script.Workers.Rpc; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.FileProviders; using Microsoft.Extensions.Logging; @@ -30,7 +29,7 @@ public class StandbyManager : IStandbyManager, IDisposable private readonly Lazy _specializationTask; private readonly IScriptWebHostEnvironment _webHostEnvironment; private readonly IEnvironment _environment; - private readonly IWebHostRpcWorkerChannelManager _rpcWorkerChannelManager; + private readonly IWebHostWorkerManager _workerManager; private readonly IConfigurationRoot _configuration; private readonly ILogger _logger; private readonly IMetricsLogger _metricsLogger; @@ -44,13 +43,13 @@ public class StandbyManager : IStandbyManager, IDisposable private static IChangeToken _standbyChangeToken = new CancellationChangeToken(_standbyCancellationTokenSource.Token); private static SemaphoreSlim _semaphore = new SemaphoreSlim(1, 1); - public StandbyManager(IScriptHostManager scriptHostManager, IWebHostRpcWorkerChannelManager rpcWorkerChannelManager, IConfiguration configuration, IScriptWebHostEnvironment webHostEnvironment, + public StandbyManager(IScriptHostManager scriptHostManager, IWebHostWorkerManager workerManager, IConfiguration configuration, IScriptWebHostEnvironment webHostEnvironment, IEnvironment environment, IOptionsMonitor options, ILogger logger, HostNameProvider hostNameProvider, IApplicationLifetime applicationLifetime, IMetricsLogger metricsLogger) - : this(scriptHostManager, rpcWorkerChannelManager, configuration, webHostEnvironment, environment, options, logger, hostNameProvider, applicationLifetime, TimeSpan.FromMilliseconds(50), metricsLogger) + : this(scriptHostManager, workerManager, configuration, webHostEnvironment, environment, options, logger, hostNameProvider, applicationLifetime, TimeSpan.FromMilliseconds(50), metricsLogger) { } - public StandbyManager(IScriptHostManager scriptHostManager, IWebHostRpcWorkerChannelManager rpcWorkerChannelManager, IConfiguration configuration, IScriptWebHostEnvironment webHostEnvironment, + public StandbyManager(IScriptHostManager scriptHostManager, IWebHostWorkerManager workerManager, IConfiguration configuration, IScriptWebHostEnvironment webHostEnvironment, IEnvironment environment, IOptionsMonitor options, ILogger logger, HostNameProvider hostNameProvider, IApplicationLifetime applicationLifetime, TimeSpan specializationTimerInterval, IMetricsLogger metricsLogger) { _scriptHostManager = scriptHostManager ?? throw new ArgumentNullException(nameof(scriptHostManager)); @@ -61,7 +60,7 @@ public StandbyManager(IScriptHostManager scriptHostManager, IWebHostRpcWorkerCha _webHostEnvironment = webHostEnvironment ?? throw new ArgumentNullException(nameof(webHostEnvironment)); _environment = environment ?? throw new ArgumentNullException(nameof(environment)); _configuration = configuration as IConfigurationRoot ?? throw new ArgumentNullException(nameof(configuration)); - _rpcWorkerChannelManager = rpcWorkerChannelManager ?? throw new ArgumentNullException(nameof(rpcWorkerChannelManager)); + _workerManager = workerManager ?? throw new ArgumentNullException(nameof(workerManager)); _hostNameProvider = hostNameProvider ?? throw new ArgumentNullException(nameof(hostNameProvider)); _changeTokenCallbackSubscription = ChangeToken.RegisterChangeCallback(_ => _logger.LogDebug($"{nameof(StandbyManager)}.{nameof(ChangeToken)} callback has fired."), null); _specializationTimerInterval = specializationTimerInterval; @@ -116,7 +115,7 @@ public async Task SpecializeHostCoreAsync() using (_metricsLogger.LatencyEvent(MetricEventNames.SpecializationLanguageWorkerChannelManagerSpecialize)) { - await _rpcWorkerChannelManager.SpecializeAsync(); + await _workerManager.SpecializeAsync(); } using (_metricsLogger.LatencyEvent(MetricEventNames.SpecializationRestartHost)) diff --git a/src/WebJobs.Script.WebHost/WebHostServiceCollectionExtensions.cs b/src/WebJobs.Script.WebHost/WebHostServiceCollectionExtensions.cs index c3bd43bf0f..4dc8b755bd 100644 --- a/src/WebJobs.Script.WebHost/WebHostServiceCollectionExtensions.cs +++ b/src/WebJobs.Script.WebHost/WebHostServiceCollectionExtensions.cs @@ -31,7 +31,6 @@ using Microsoft.Azure.WebJobs.Script.WebHost.Security.Authorization.Policies; using Microsoft.Azure.WebJobs.Script.WebHost.Standby; using Microsoft.Azure.WebJobs.Script.Workers.FunctionDataCache; -using Microsoft.Azure.WebJobs.Script.Workers.Profiles; using Microsoft.Azure.WebJobs.Script.Workers.Rpc; using Microsoft.Azure.WebJobs.Script.Workers.Rpc.Configuration; using Microsoft.Azure.WebJobs.Script.Workers.SharedMemoryDataTransfer; @@ -192,8 +191,8 @@ public static void AddWebJobsScriptHost(this IServiceCollection services, IConfi // Register common services with the WebHost // Language Worker Hosted Services need to be intialized before WebJobsScriptHostService ScriptHostBuilderExtensions.AddCommonServices(services); + services.AddCommonRpcServices(); - services.AddSingleton(); services.AddSingleton(); services.AddSingleton(); diff --git a/src/WebJobs.Script.WebHost/WebJobsScriptHostService.cs b/src/WebJobs.Script.WebHost/WebJobsScriptHostService.cs index 84b0056459..f6158a6c6e 100644 --- a/src/WebJobs.Script.WebHost/WebJobsScriptHostService.cs +++ b/src/WebJobs.Script.WebHost/WebJobsScriptHostService.cs @@ -20,7 +20,6 @@ using Microsoft.Azure.WebJobs.Logging; using Microsoft.Azure.WebJobs.Logging.ApplicationInsights; using Microsoft.Azure.WebJobs.Script.Config; -using Microsoft.Azure.WebJobs.Script.Configuration; using Microsoft.Azure.WebJobs.Script.Diagnostics; using Microsoft.Azure.WebJobs.Script.Eventing; using Microsoft.Azure.WebJobs.Script.Metrics; @@ -28,7 +27,6 @@ using Microsoft.Azure.WebJobs.Script.WebHost.Configuration; using Microsoft.Azure.WebJobs.Script.WebHost.Diagnostics; using Microsoft.Azure.WebJobs.Script.WebHost.Diagnostics.Extensions; -using Microsoft.Azure.WebJobs.Script.Workers; using Microsoft.Azure.WebJobs.Script.Workers.Rpc; using Microsoft.Azure.WebJobs.Script.Workers.Rpc.Configuration; using Microsoft.Extensions.Configuration; @@ -638,7 +636,7 @@ public async Task RestartHostAsync(string reason, CancellationToken cancellation } else { - NotifyHostStopping(previousHost); + await NotifyHostStoppingAsync(previousHost, cancellationToken); startTask = UnsynchronizedStartHostAsync(activeOperation); stopTask = Orphan(previousHost, cancellationToken); } @@ -673,14 +671,10 @@ public async Task RestartHostAsync(string reason, CancellationToken cancellation // Because we fire-and-forget the host disposal, we cannot be guaranteed when it will be stopped // or disposed. Use this method to explicitly stop any services in the host that may be // problematic to run side-by-side with the new host that is starting. - private static void NotifyHostStopping(IHost previousHost) + private static Task NotifyHostStoppingAsync(IHost previousHost, CancellationToken cancellationToken) { - // It's important to prevent any new workers from starting on the orphaned host. The - // only way to guarantee this is to signal to the dispatcher that it's done with process - // creation before we begin a new host. - var dispatcherFactory = previousHost?.Services?.GetService(); - IFunctionInvocationDispatcher dispatcher = dispatcherFactory?.GetFunctionDispatcher(); - dispatcher?.PreShutdown(); + var scriptHost = previousHost?.Services?.GetService(); + return scriptHost?.NotifyStoppingAsync(cancellationToken) ?? Task.CompletedTask; } internal bool ShouldEnforceSequentialRestart(IHost host = null) diff --git a/src/WebJobs.Script.WebHost/WebScriptHostBuilderExtension.cs b/src/WebJobs.Script.WebHost/WebScriptHostBuilderExtension.cs index d163a515e6..1b4278c875 100644 --- a/src/WebJobs.Script.WebHost/WebScriptHostBuilderExtension.cs +++ b/src/WebJobs.Script.WebHost/WebScriptHostBuilderExtension.cs @@ -3,7 +3,6 @@ using System; using System.Net.Http; -using System.Runtime.InteropServices; using Microsoft.AspNetCore.Mvc.ApplicationParts; using Microsoft.Azure.WebJobs.Host.Config; using Microsoft.Azure.WebJobs.Host.Executors; @@ -18,20 +17,17 @@ using Microsoft.Azure.WebJobs.Script.Description; using Microsoft.Azure.WebJobs.Script.Diagnostics; using Microsoft.Azure.WebJobs.Script.Middleware; -using Microsoft.Azure.WebJobs.Script.Scale; using Microsoft.Azure.WebJobs.Script.WebHost.Configuration; using Microsoft.Azure.WebJobs.Script.WebHost.DependencyInjection; using Microsoft.Azure.WebJobs.Script.WebHost.Diagnostics; using Microsoft.Azure.WebJobs.Script.WebHost.Management; using Microsoft.Azure.WebJobs.Script.WebHost.Middleware; using Microsoft.Azure.WebJobs.Script.WebHost.Storage; -using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.DependencyInjection.Extensions; using Microsoft.Extensions.Hosting; using Microsoft.Extensions.Logging; using Microsoft.Extensions.Options; -using static Microsoft.Azure.WebJobs.Script.Utility; namespace Microsoft.Azure.WebJobs.Script.WebHost { @@ -62,6 +58,8 @@ public static IHostBuilder AddWebScriptHost(this IHostBuilder builder, IServiceP }) .AddScriptHost(webHostOptions, configLoggerFactory, metricsLogger, webJobsBuilder => { + webJobsBuilder.Services.AddRpcScriptHostServices(); + // Adds necessary Azure-based services to the ScriptHost, which will use the host-provided IAzureBlobStorageProvider registered below. webJobsBuilder.AddAzureStorageCoreServices(); @@ -150,7 +148,6 @@ public static IHostBuilder AddWebScriptHost(this IHostBuilder builder, IServiceP } services.AddSingleton(); - services.TryAddEnumerable(ServiceDescriptor.Singleton()); // Make sure the registered IHostIdProvider is used IHostIdProvider provider = rootServiceProvider.GetService(); diff --git a/src/WebJobs.Script.WebHost/WebScriptHostExceptionHandler.cs b/src/WebJobs.Script.WebHost/WebScriptHostExceptionHandler.cs index 66a7073307..813a9430ff 100644 --- a/src/WebJobs.Script.WebHost/WebScriptHostExceptionHandler.cs +++ b/src/WebJobs.Script.WebHost/WebScriptHostExceptionHandler.cs @@ -16,12 +16,12 @@ public class WebScriptHostExceptionHandler : IWebJobsExceptionHandler { private readonly IApplicationLifetime _applicationLifetime; private readonly ILogger _logger; - private readonly IFunctionInvocationDispatcherFactory _functionInvocationDispatcherFactory; + private readonly IScriptHostWorkerManager _workerManager; - public WebScriptHostExceptionHandler(IApplicationLifetime applicationLifetime, ILogger logger, IFunctionInvocationDispatcherFactory functionInvocationDispatcherFactory) + public WebScriptHostExceptionHandler(IApplicationLifetime applicationLifetime, ILogger logger, IScriptHostWorkerManager workerManager) { _applicationLifetime = applicationLifetime ?? throw new ArgumentNullException(nameof(applicationLifetime)); - _functionInvocationDispatcherFactory = functionInvocationDispatcherFactory; + _workerManager = workerManager; _logger = logger ?? throw new ArgumentNullException(nameof(logger)); } @@ -49,13 +49,12 @@ public async Task OnTimeoutExceptionAsync(ExceptionDispatchInfo exceptionInfo, T // Task ignoreTask = _hostManager.StopAsync(); // Give the manager and all running tasks some time to shut down gracefully. // await Task.Delay(timeoutGracePeriod); - IFunctionInvocationDispatcher functionInvocationDispatcher = _functionInvocationDispatcherFactory.GetFunctionDispatcher(); - if (!functionInvocationDispatcher.State.Equals(FunctionInvocationDispatcherState.Default)) + if (!_workerManager.State.Equals(WorkerManagerState.Default)) { _logger.LogWarning($"A function timeout has occurred. Restarting worker process executing invocationId '{timeoutException.InstanceId}'.", exceptionInfo.SourceException); // If invocation id is not found in any of the workers => worker is already disposed. No action needed. - await functionInvocationDispatcher.RestartWorkerWithInvocationIdAsync(timeoutException.InstanceId.ToString(), timeoutException); + await _workerManager.RestartWorkerWithInvocationIdAsync(timeoutException.InstanceId.ToString(), timeoutException); _logger.LogWarning("Attempt to restart language worker process(es) completed.", exceptionInfo.SourceException); } diff --git a/src/WebJobs.Script/Config/FunctionMetadataOptions.cs b/src/WebJobs.Script/Config/FunctionMetadataOptions.cs new file mode 100644 index 0000000000..58b6f39e02 --- /dev/null +++ b/src/WebJobs.Script/Config/FunctionMetadataOptions.cs @@ -0,0 +1,19 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the MIT License. See License.txt in the project root for license information. + +namespace Microsoft.Azure.WebJobs.Script; + +public class FunctionMetadataOptions +{ + /// + /// Gets or sets a value indicating whether a script file is required for function metadata. Some workers + /// (such as HTTP workers) do not require a script file to be present. + /// + public bool SkipScriptFileValidation { get; set; } = false; + + /// + /// Gets or sets a value indicating whether to validate that the worker runtime matches the language + /// in the function metadata. Some workers (such as HTTP workers) do not require this validation. + /// + public bool SkipRuntimeValidation { get; set; } = false; +} diff --git a/src/WebJobs.Script/Config/ScriptHostRecycleOptionsSetup.cs b/src/WebJobs.Script/Config/ScriptHostRecycleOptionsSetup.cs index feeacc8f19..f95df68848 100644 --- a/src/WebJobs.Script/Config/ScriptHostRecycleOptionsSetup.cs +++ b/src/WebJobs.Script/Config/ScriptHostRecycleOptionsSetup.cs @@ -2,7 +2,6 @@ // Licensed under the MIT License. See License.txt in the project root for license information. using System; -using Microsoft.Azure.WebJobs.Script.Workers.Http; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.Options; @@ -10,25 +9,16 @@ namespace Microsoft.Azure.WebJobs.Script.Configuration { internal sealed class ScriptHostRecycleOptionsSetup : IConfigureOptions { - private readonly IOptions _httpWorkerOptions; private readonly IConfiguration _configuration; - public ScriptHostRecycleOptionsSetup( - IOptions httpWorkerOptions, IConfiguration configuration) + public ScriptHostRecycleOptionsSetup(IConfiguration configuration) { - _httpWorkerOptions = httpWorkerOptions ?? throw new ArgumentNullException(nameof(httpWorkerOptions)); _configuration = configuration ?? throw new ArgumentNullException(nameof(configuration)); } public void Configure(ScriptHostRecycleOptions options) { options.Configure(_configuration); - - // Enforcing sequential host restarts when a user-specified custom handler port is configured to prevent multiple processes from attempting to bind to the same port concurrently. - if (_httpWorkerOptions.Value.IsPortManuallySet) - { - options.SequentialHostRestartRequired = true; - } } } } diff --git a/src/WebJobs.Script/Config/ScriptJobHostOptions.cs b/src/WebJobs.Script/Config/ScriptJobHostOptions.cs index d0dac72fb0..f0295cdfd7 100644 --- a/src/WebJobs.Script/Config/ScriptJobHostOptions.cs +++ b/src/WebJobs.Script/Config/ScriptJobHostOptions.cs @@ -1,4 +1,4 @@ -// Copyright (c) .NET Foundation. All rights reserved. +// Copyright (c) .NET Foundation. All rights reserved. // Licensed under the MIT License. See License.txt in the project root for license information. using System; diff --git a/src/WebJobs.Script/Config/ScriptTelemetryProcessor.cs b/src/WebJobs.Script/Config/ScriptTelemetryProcessor.cs index b728618cb2..7a2fd8e9c4 100644 --- a/src/WebJobs.Script/Config/ScriptTelemetryProcessor.cs +++ b/src/WebJobs.Script/Config/ScriptTelemetryProcessor.cs @@ -1,4 +1,4 @@ -// Copyright (c) .NET Foundation. All rights reserved. +// Copyright (c) .NET Foundation. All rights reserved. // Licensed under the MIT License. See License.txt in the project root for license information. using System; @@ -47,23 +47,5 @@ private ITelemetry ToUserException(RpcException rpcException, ITelemetry origina return newET; } - - /// - /// Returns true if the feature flag for surfacing user code exceptions was set by the worker, - /// and false if not. - /// - /// The instance. - private bool EnableUserExceptionFeatureFlag(Exception ex) - { - try - { - string value = (string)ex.Data[RpcWorkerConstants.EnableUserCodeException]; - return bool.Parse(value); - } - catch - { - return false; - } - } } } diff --git a/src/WebJobs.Script/Description/IWorkerFunctionDescriptorProviderFactory.cs b/src/WebJobs.Script/Description/IWorkerFunctionDescriptorProviderFactory.cs new file mode 100644 index 0000000000..48454b3b36 --- /dev/null +++ b/src/WebJobs.Script/Description/IWorkerFunctionDescriptorProviderFactory.cs @@ -0,0 +1,17 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the MIT License. See License.txt in the project root for license information. + +using System.Collections.Generic; +using Microsoft.Azure.WebJobs.Script.Extensibility; + +namespace Microsoft.Azure.WebJobs.Script.Description; + +/// +/// Hooks for creating FunctionDescriptorProviders for external workers +/// +public interface IWorkerFunctionDescriptorProviderFactory +{ + FunctionDescriptorProvider CreateMultiWorkerDescriptorProvider(ScriptHost host, ICollection bindingProviders); + + FunctionDescriptorProvider CreateWorkerDescriptorProvider(ScriptHost host, string workerRuntime, ICollection bindingProviders); +} \ No newline at end of file diff --git a/src/WebJobs.Script/Diagnostics/Extensions/ScriptHostLoggerExtension.cs b/src/WebJobs.Script/Diagnostics/Extensions/ScriptHostLoggerExtension.cs index 38db52b371..d627144c50 100644 --- a/src/WebJobs.Script/Diagnostics/Extensions/ScriptHostLoggerExtension.cs +++ b/src/WebJobs.Script/Diagnostics/Extensions/ScriptHostLoggerExtension.cs @@ -1,4 +1,4 @@ -// Copyright (c) .NET Foundation. All rights reserved. +// Copyright (c) .NET Foundation. All rights reserved. // Licensed under the MIT License. See License.txt in the project root for license information. using System; @@ -88,12 +88,6 @@ internal static class ScriptHostLoggerExtension new EventId(413, nameof(ScriptHostStarted)), "Host started ({ms}ms)"); - private static readonly Action _addingDescriptorProviderForHttpWorker = - LoggerMessage.Define( - LogLevel.Debug, - new EventId(414, nameof(AddingDescriptorProviderForHttpWorker)), - "Adding Function descriptor provider for HttpWorker."); - private static readonly Action _stoppingScriptHost = LoggerMessage.Define( LogLevel.Debug, @@ -158,11 +152,6 @@ public static void AddingDescriptorProviderForLanguage(this ILogger logger, stri _addingDescriptorProviderForLanguage(logger, workerRuntime, null); } - public static void AddingDescriptorProviderForHttpWorker(this ILogger logger) - { - _addingDescriptorProviderForHttpWorker(logger, null); - } - public static void CreatingDescriptors(this ILogger logger) { _creatingDescriptors(logger, null); diff --git a/src/WebJobs.Script/Host/FunctionMetadataManager.cs b/src/WebJobs.Script/Host/FunctionMetadataManager.cs index cdc4e4c999..bb1cef0771 100644 --- a/src/WebJobs.Script/Host/FunctionMetadataManager.cs +++ b/src/WebJobs.Script/Host/FunctionMetadataManager.cs @@ -1,4 +1,4 @@ -// Copyright (c) .NET Foundation. All rights reserved. +// Copyright (c) .NET Foundation. All rights reserved. // Licensed under the MIT License. See License.txt in the project root for license information. using System; @@ -11,7 +11,6 @@ using Microsoft.Azure.WebJobs.Logging; using Microsoft.Azure.WebJobs.Script.Description; using Microsoft.Azure.WebJobs.Script.Diagnostics.Extensions; -using Microsoft.Azure.WebJobs.Script.Workers.Http; using Microsoft.Azure.WebJobs.Script.Workers.Rpc; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Logging; @@ -30,8 +29,8 @@ public sealed class FunctionMetadataManager : IFunctionMetadataManager, IDisposa private readonly IOptionsMonitor _languageOptions; private IDisposable _onChangeSubscription; private IOptions _scriptOptions; + private IOptions _metadataOptions; private ILogger _logger; - private bool _isHttpWorker; private bool _servicesReset = false; private ImmutableArray _functionMetadataArray; private Dictionary> _functionErrors = new Dictionary>(); @@ -40,17 +39,17 @@ public sealed class FunctionMetadataManager : IFunctionMetadataManager, IDisposa public FunctionMetadataManager( IOptions scriptOptions, IFunctionMetadataProvider functionMetadataProvider, - IOptions httpWorkerOptions, IScriptHostManager scriptHostManager, ILoggerFactory loggerFactory, IEnvironment environment, - IOptionsMonitor languageOptions) + IOptionsMonitor languageOptions, + IOptions metadataOptions) { _scriptOptions = scriptOptions; + _metadataOptions = metadataOptions; _serviceProvider = scriptHostManager as IServiceProvider; _functionMetadataProvider = functionMetadataProvider; _logger = loggerFactory.CreateLogger(LogCategories.Startup); - _isHttpWorker = httpWorkerOptions?.Value?.Description != null; _environment = environment; _languageOptions = languageOptions; @@ -125,8 +124,8 @@ private void InitializeServices() { _functionMetadataMap.Clear(); - _isHttpWorker = _serviceProvider.GetService>()?.Value?.Description != null; _scriptOptions = _serviceProvider.GetService>(); + _metadataOptions = _serviceProvider.GetService>(); // Resetting the logger switches the logger scope to Script Host level, // also making the logs available to Application Insights @@ -197,20 +196,14 @@ internal ImmutableArray LoadFunctionMetadata(bool forceRefresh internal bool IsScriptFileDetermined(FunctionMetadata functionMetadata) { - try - { - if (string.IsNullOrEmpty(functionMetadata.ScriptFile) && !_isHttpWorker && !functionMetadata.IsProxy() && _servicesReset) - { - throw new FunctionConfigurationException(FunctionConfigurationErrorMessage); - } - } - catch (FunctionConfigurationException exc) + if (string.IsNullOrEmpty(functionMetadata.ScriptFile) && !_metadataOptions.Value.SkipScriptFileValidation && !functionMetadata.IsProxy() && _servicesReset) { // for functions in error, log the error and don't // add to the functions collection - Utility.AddFunctionError(_functionErrors, functionMetadata.Name, exc.Message); + Utility.AddFunctionError(_functionErrors, functionMetadata.Name, FunctionConfigurationErrorMessage); return false; } + return true; } diff --git a/src/WebJobs.Script/Host/IScriptHostLifecycleService.cs b/src/WebJobs.Script/Host/IScriptHostLifecycleService.cs new file mode 100644 index 0000000000..b80c07309d --- /dev/null +++ b/src/WebJobs.Script/Host/IScriptHostLifecycleService.cs @@ -0,0 +1,30 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the MIT License. See License.txt in the project root for license information. + +using System.Collections.Generic; +using System.Threading; +using System.Threading.Tasks; +using Microsoft.Azure.WebJobs.Script.Description; + +namespace Microsoft.Azure.WebJobs.Script.Host; + +/// +/// Defines methods that can be run at specific points in the ScriptHost lifecycle. Similar to +/// , but specific to the ScriptHost. +/// +public interface IScriptHostLifecycleService +{ + /// + /// Triggered at the end of . + /// + /// Indicates that the start process has been aborted. + /// A that represents the asynchronous operation. + Task InitializedAsync(IEnumerable functions, CancellationToken cancellationToken); + + /// + /// Triggered before . + /// + /// Indicates that the stop process has been aborted. + /// A that represents the asynchronous operation. + Task StoppingAsync(CancellationToken cancellationToken); +} diff --git a/src/WebJobs.Script/Host/ScriptHost.cs b/src/WebJobs.Script/Host/ScriptHost.cs index 107bdaabb2..39ca8365a6 100644 --- a/src/WebJobs.Script/Host/ScriptHost.cs +++ b/src/WebJobs.Script/Host/ScriptHost.cs @@ -30,8 +30,7 @@ using Microsoft.Azure.WebJobs.Script.Eventing; using Microsoft.Azure.WebJobs.Script.Extensibility; using Microsoft.Azure.WebJobs.Script.ExtensionBundle; -using Microsoft.Azure.WebJobs.Script.Workers; -using Microsoft.Azure.WebJobs.Script.Workers.Http; +using Microsoft.Azure.WebJobs.Script.Host; using Microsoft.Azure.WebJobs.Script.Workers.Rpc; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.Logging; @@ -56,8 +55,6 @@ public class ScriptHost : JobHost, IScriptJobHost private readonly IMetricsLogger _metricsLogger = null; private readonly string _hostLogPath; private readonly IOptions _hostOptions; - private readonly bool _isHttpWorker; - private readonly HttpWorkerOptions _httpWorkerOptions; private readonly IConfiguration _configuration; private readonly ScriptTypeLocator _typeLocator; private readonly IDebugStateProvider _debugManager; @@ -69,11 +66,13 @@ public class ScriptHost : JobHost, IScriptJobHost private readonly IEnvironment _environment; private readonly IFunctionDataCache _functionDataCache; private readonly IOptions _hostingConfigOptions; + private readonly IWorkerFunctionDescriptorProviderFactory _descriptorProviderFactory; + private readonly IScriptHostLifecycleService _scriptHostLifecycleService; + private readonly IOptions _metadataOptions; private readonly IOptionsMonitor _languageWorkerOptions; private readonly ILogger _logger; private readonly IPrimaryHostStateProvider _primaryHostStateProvider; private readonly IList _eventSubscriptions = new List(); - private readonly IFunctionInvocationDispatcher _functionDispatcher; private static readonly int _processId = Process.GetCurrentProcess().Id; public static readonly string Version = GetAssemblyFileVersion(typeof(ScriptHost).Assembly); @@ -85,14 +84,12 @@ public class ScriptHost : JobHost, IScriptJobHost // Map from BindingType to the Assembly Qualified Type name for its IExtensionConfigProvider object. public ScriptHost(IOptions options, - IOptions httpWorkerOptions, IEnvironment environment, IJobHostContextFactory jobHostContextFactory, IConfiguration configuration, IDistributedLockManager distributedLockManager, IScriptEventManager eventManager, ILoggerFactory loggerFactory, - IFunctionInvocationDispatcherFactory functionDispatcherFactory, IFunctionMetadataManager functionMetadataManager, IFileLoggingStatusManager fileLoggingStatusManager, IMetricsLogger metricsLogger, @@ -110,6 +107,9 @@ public ScriptHost(IOptions options, IFunctionDataCache functionDataCache, IOptionsMonitor languageWorkerOptions, IOptions hostingConfigOptions, + IWorkerFunctionDescriptorProviderFactory descriptorProviderFactory, + IScriptHostLifecycleService scriptHostLifecycleService, + IOptions metadataOptions, ScriptSettingsManager settingsManager = null) : base(options, jobHostContextFactory) { @@ -126,13 +126,10 @@ public ScriptHost(IOptions options, _applicationLifetime = applicationLifetime; _hostIdProvider = hostIdProvider; _httpRoutesManager = httpRoutesManager; - _isHttpWorker = httpWorkerOptions.Value.Description != null; - _httpWorkerOptions = httpWorkerOptions.Value; ScriptOptions = scriptHostOptions.Value; _scriptHostManager = scriptHostManager; FunctionErrors = new Dictionary>(StringComparer.OrdinalIgnoreCase); EventManager = eventManager; - _functionDispatcher = functionDispatcherFactory.GetFunctionDispatcher(); _settingsManager = settingsManager ?? ScriptSettingsManager.Instance; ExtensionBundleManager = extensionBundleManager; @@ -158,6 +155,9 @@ public ScriptHost(IOptions options, _functionDataCache = functionDataCache; _hostingConfigOptions = hostingConfigOptions; + _descriptorProviderFactory = descriptorProviderFactory; + _scriptHostLifecycleService = scriptHostLifecycleService; + _metadataOptions = metadataOptions; } public event EventHandler HostInitializing; @@ -218,8 +218,6 @@ private set /// public virtual bool InDiagnosticMode => _debugManager.InDiagnosticMode; - internal IFunctionInvocationDispatcher FunctionDispatcher => _functionDispatcher; - /// /// Returns true if the specified name is the name of a known function, /// regardless of whether the function is in error. @@ -321,7 +319,8 @@ public async Task InitializeAsync(CancellationToken cancellationToken = default) await InitializeFunctionDescriptorsAsync(functionMetadataList, workerRuntime, cancellationToken); var filteredFunctionMetadata = functionMetadataList.Where(m => m.IsProxy() || !Utility.IsCodelessDotNetLanguageFunction(m)); - await _functionDispatcher.InitializeAsync(Utility.GetValidFunctions(filteredFunctionMetadata, Functions), cancellationToken); + + await _scriptHostLifecycleService.InitializedAsync(Utility.GetValidFunctions(filteredFunctionMetadata, Functions), cancellationToken); GenerateFunctions(); ScheduleFileSystemCleanup(); @@ -568,15 +567,8 @@ private void AddFunctionDescriptors(IEnumerable functionMetada { _logger.AddingDescriptorProviderForLanguage("All (Multi Language)"); - var workerOptions = _languageWorkerOptions.CurrentValue; - - _descriptorProviders.Add(new MultiLanguageFunctionDescriptorProvider(this, workerOptions.WorkerConfigs, ScriptOptions, _bindingProviders, - _functionDispatcher, _loggerFactory, _applicationLifetime, workerOptions.WorkerConfigs.Max(wc => wc.CountOptions.InitializationTimeout))); - } - else if (_isHttpWorker) - { - _logger.AddingDescriptorProviderForHttpWorker(); - _descriptorProviders.Add(new HttpFunctionDescriptorProvider(this, ScriptOptions, _bindingProviders, _functionDispatcher, _loggerFactory, _applicationLifetime, _httpWorkerOptions.InitializationTimeout)); + var descriptorProvider = _descriptorProviderFactory.CreateMultiWorkerDescriptorProvider(this, _bindingProviders); + _descriptorProviders.Add(descriptorProvider); } else if (string.Equals(workerRuntime, RpcWorkerConstants.DotNetLanguageWorkerName, StringComparison.OrdinalIgnoreCase)) { @@ -587,13 +579,9 @@ private void AddFunctionDescriptors(IEnumerable functionMetada { _logger.AddingDescriptorProviderForLanguage(workerRuntime); - var workerConfig = _languageWorkerOptions.CurrentValue.WorkerConfigs?.FirstOrDefault(c => c.Description.Language.Equals(workerRuntime, StringComparison.OrdinalIgnoreCase)); - - // If there's no worker config, use the default (for legacy behavior; mostly for tests). - TimeSpan initializationTimeout = workerConfig?.CountOptions?.InitializationTimeout ?? WorkerProcessCountOptions.DefaultInitializationTimeout; - - _descriptorProviders.Add(new RpcFunctionDescriptorProvider(this, workerRuntime, ScriptOptions, _bindingProviders, - _functionDispatcher, _loggerFactory, _applicationLifetime, initializationTimeout)); + // this handles both http and grpc workers + var descriptorProvider = _descriptorProviderFactory.CreateWorkerDescriptorProvider(this, workerRuntime, _bindingProviders); + _descriptorProviders.Add(descriptorProvider); } // Codeless functions run side by side with regular functions. @@ -818,7 +806,7 @@ internal async Task> GetFunctionDescriptorsAsync( var httpFunctions = new Dictionary(); - Utility.VerifyFunctionsMatchSpecifiedLanguage(functions, workerRuntime, _environment.IsPlaceholderModeEnabled(), _isHttpWorker, cancellationToken, throwOnMismatch: throwOnWorkerRuntimeAndPayloadMetadataMismatch); + Utility.VerifyFunctionsMatchSpecifiedLanguage(functions, workerRuntime, _environment.IsPlaceholderModeEnabled(), _metadataOptions.Value.SkipRuntimeValidation, cancellationToken, throwOnMismatch: throwOnWorkerRuntimeAndPayloadMetadataMismatch); var inProcIndexingSupported = _environment.IsPlaceholderModeEnabled() || (_environment.IsDotNetInProcSupported() @@ -1076,6 +1064,11 @@ protected override void OnHostStarted() _logger.ScriptHostStarted((long)_stopwatch.GetElapsedTime().TotalMilliseconds); } + internal Task NotifyStoppingAsync(CancellationToken cancellationToken) + { + return _scriptHostLifecycleService.StoppingAsync(cancellationToken); + } + protected override async Task StopAsyncCore(CancellationToken cancellationToken) { _logger.StoppingScriptHost(ScriptOptions.InstanceId); @@ -1094,8 +1087,6 @@ protected override void Dispose(bool disposing) subscription.Dispose(); } - _functionDispatcher?.Dispose(); - foreach (var function in Functions) { (function.Invoker as IDisposable)?.Dispose(); diff --git a/src/WebJobs.Script/Properties/AssemblyInfo.cs b/src/WebJobs.Script/Properties/AssemblyInfo.cs index 0a98ac11a2..1d15c7a120 100644 --- a/src/WebJobs.Script/Properties/AssemblyInfo.cs +++ b/src/WebJobs.Script/Properties/AssemblyInfo.cs @@ -1,8 +1,6 @@ -// Copyright (c) .NET Foundation. All rights reserved. +// Copyright (c) .NET Foundation. All rights reserved. // Licensed under the MIT License. See License.txt in the project root for license information. -using System.Reflection; -using System.Resources; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; diff --git a/src/WebJobs.Script/Workers/Rpc/Configuration/RpcWorkerConfig.cs b/src/WebJobs.Script/RpcWorkerConfig.cs similarity index 86% rename from src/WebJobs.Script/Workers/Rpc/Configuration/RpcWorkerConfig.cs rename to src/WebJobs.Script/RpcWorkerConfig.cs index 6d5a62219d..0f4f0cb197 100644 --- a/src/WebJobs.Script/Workers/Rpc/Configuration/RpcWorkerConfig.cs +++ b/src/WebJobs.Script/RpcWorkerConfig.cs @@ -1,4 +1,4 @@ -// Copyright (c) .NET Foundation. All rights reserved. +// Copyright (c) .NET Foundation. All rights reserved. // Licensed under the MIT License. See License.txt in the project root for license information. namespace Microsoft.Azure.WebJobs.Script.Workers.Rpc @@ -11,4 +11,4 @@ public class RpcWorkerConfig public WorkerProcessCountOptions CountOptions { get; set; } } -} +} \ No newline at end of file diff --git a/src/WebJobs.Script/Scale/HostPerformanceManager.cs b/src/WebJobs.Script/Scale/HostPerformanceManager.cs index 892c869617..4df26d4f06 100644 --- a/src/WebJobs.Script/Scale/HostPerformanceManager.cs +++ b/src/WebJobs.Script/Scale/HostPerformanceManager.cs @@ -1,4 +1,4 @@ -// Copyright (c) .NET Foundation. All rights reserved. +// Copyright (c) .NET Foundation. All rights reserved. // Licensed under the MIT License. See License.txt in the project root for license information. using System; @@ -106,13 +106,13 @@ public async Task TryHandleHealthPingAsync(HttpRequest request, I internal async Task ProcessThresholdsExceeded(ILogger logger = null) { - var dispatcher = GetDispatcherOrNull(); - if (dispatcher != null) + var workerManager = _serviceProvider.GetScriptHostServiceOrNull(); + if (workerManager != null) { // TEMP: This call pings all the OOP workers, to ensure we include any channel latency // in the upstream ping result. // Once the WorkerChannelThrottleProvider is fully implemented, this call can be removed. - await dispatcher.GetWorkerStatusesAsync(); + await workerManager.GetWorkerStatusesAsync(); } // ThrottleManager internally consults various throttle providers that check @@ -205,19 +205,5 @@ public void Dispose() { Dispose(true); } - - private IFunctionInvocationDispatcher GetDispatcherOrNull() - { - var dispatcherFactory = _serviceProvider.GetScriptHostServiceOrNull(); - if (dispatcherFactory != null) - { - var dispatcher = dispatcherFactory.GetFunctionDispatcher(); - if (dispatcher.State == FunctionInvocationDispatcherState.Initialized) - { - return dispatcher; - } - } - return null; - } } } diff --git a/src/WebJobs.Script/ScriptHostBuilderExtensions.cs b/src/WebJobs.Script/ScriptHostBuilderExtensions.cs index 2de1821f5e..390a8cc6a9 100644 --- a/src/WebJobs.Script/ScriptHostBuilderExtensions.cs +++ b/src/WebJobs.Script/ScriptHostBuilderExtensions.cs @@ -6,7 +6,6 @@ using System.ComponentModel; using System.Diagnostics.Tracing; using System.Net.Http; -using System.Runtime.InteropServices; using Microsoft.ApplicationInsights.Extensibility; using Microsoft.ApplicationInsights.WindowsServer.TelemetryChannel; using Microsoft.Azure.WebJobs.Host; @@ -33,7 +32,6 @@ using Microsoft.Azure.WebJobs.Script.Metrics; using Microsoft.Azure.WebJobs.Script.Scale; using Microsoft.Azure.WebJobs.Script.Workers; -using Microsoft.Azure.WebJobs.Script.Workers.Http; using Microsoft.Azure.WebJobs.Script.Workers.Profiles; using Microsoft.Azure.WebJobs.Script.Workers.Rpc; using Microsoft.Azure.WebJobs.Script.Workers.Rpc.Configuration; @@ -304,21 +302,9 @@ public static IHostBuilder AddScriptHostCore(this IHostBuilder builder, ScriptAp // Core WebJobs/Script Host services services.AddSingleton(); - // HTTP Worker - services.AddSingleton(); - services.AddSingleton(); - services.AddSingleton(); - // Rpc Worker - services.AddSingleton(); - services.AddSingleton(); - - //Worker Function Invocation dispatcher - services.AddSingleton(); services.AddSingleton(p => p.GetRequiredService()); services.AddSingleton(p => p.GetRequiredService()); services.TryAddEnumerable(ServiceDescriptor.Singleton()); - services.TryAddEnumerable(ServiceDescriptor.Singleton()); - services.AddSingleton(); services.AddSingleton(); services.AddSingleton(); @@ -339,7 +325,6 @@ public static IHostBuilder AddScriptHostCore(this IHostBuilder builder, ScriptAp services.ConfigureOptions(); services.ConfigureOptions(); services.AddOptions(); - services.ConfigureOptions(); services.ConfigureOptions(); services.ConfigureOptions(); services.AddOptions() @@ -373,8 +358,6 @@ public static IHostBuilder AddScriptHostCore(this IHostBuilder builder, ScriptAp services.AddSingleton(); } - services.TryAddEnumerable(ServiceDescriptor.Singleton()); - services.AddSingleton(); services.AddSingleton(); }); @@ -396,20 +379,13 @@ public static void AddCommonServices(IServiceCollection services) services.TryAddSingleton(); services.AddSingleton(); - // Add Language Worker Service - // Need to maintain the order: Add RpcInitializationService before core script host services - services.AddManagedHostedService(); services.TryAddSingleton(); - services.AddSingleton(); - services.AddSingleton(); - services.TryAddSingleton(); + services.TryAddSingleton(); services.TryAddSingleton(); services.TryAddSingleton(SystemEnvironment.Instance); services.TryAddSingleton(); services.ConfigureOptions(); - - AddProcessRegistry(services); } public static IHostBuilder SetAzureFunctionsEnvironment(this IHostBuilder builder) @@ -557,20 +533,6 @@ private static void RegisterFileProvisioningService(IHostBuilder builder) } } - private static void AddProcessRegistry(IServiceCollection services) - { - // W3WP already manages job objects - if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows) - && !ScriptSettingsManager.Instance.IsAppServiceEnvironment) - { - services.AddSingleton(); - } - else - { - services.AddSingleton(); - } - } - /// /// Gets and removes the specified value, if it exists and is of type T. /// Throws an InvalidOperationException if the key does not exist or is not of type T. diff --git a/src/WebJobs.Script/Utility.cs b/src/WebJobs.Script/Utility.cs index cacc8c0b5d..db9c5273b6 100644 --- a/src/WebJobs.Script/Utility.cs +++ b/src/WebJobs.Script/Utility.cs @@ -629,11 +629,11 @@ internal static bool TryReadFunctionConfig(string scriptDir, out string json, IF return true; } - internal static void VerifyFunctionsMatchSpecifiedLanguage(IEnumerable functions, string workerRuntime, bool isPlaceholderMode, bool isHttpWorker, CancellationToken cancellationToken, bool throwOnMismatch = true) + internal static void VerifyFunctionsMatchSpecifiedLanguage(IEnumerable functions, string workerRuntime, bool isPlaceholderMode, bool shouldSkipValidation, CancellationToken cancellationToken, bool throwOnMismatch = true) { cancellationToken.ThrowIfCancellationRequested(); - if (isPlaceholderMode || isHttpWorker) + if (isPlaceholderMode || shouldSkipValidation) { return; } diff --git a/src/WebJobs.Script/Workers/IScriptHostWorkerManager.cs b/src/WebJobs.Script/Workers/IScriptHostWorkerManager.cs new file mode 100644 index 0000000000..e7fd4f2545 --- /dev/null +++ b/src/WebJobs.Script/Workers/IScriptHostWorkerManager.cs @@ -0,0 +1,19 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the MIT License. See License.txt in the project root for license information. + +using System; +using System.Collections.Generic; +using System.Threading.Tasks; + +namespace Microsoft.Azure.WebJobs.Script.Workers; + +public interface IScriptHostWorkerManager +{ + WorkerManagerState State { get; } + + Task GetWorkerStatusesAsync(); + + Task RestartWorkerWithInvocationIdAsync(string invocationId, Exception exception); + + Task> GetWorkerProcessInfoAsync(string workerRuntime); +} \ No newline at end of file diff --git a/src/WebJobs.Script/Workers/IWebHostWorkerManager.cs b/src/WebJobs.Script/Workers/IWebHostWorkerManager.cs new file mode 100644 index 0000000000..ad5927598f --- /dev/null +++ b/src/WebJobs.Script/Workers/IWebHostWorkerManager.cs @@ -0,0 +1,14 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the MIT License. See License.txt in the project root for license information. + +using System.Threading.Tasks; + +namespace Microsoft.Azure.WebJobs.Script.WebHost +{ + public interface IWebHostWorkerManager + { + Task SpecializeAsync(); + + Task WorkerWarmupAsync(); + } +} \ No newline at end of file diff --git a/src/WebJobs.Script/Workers/MessageExtensions/ScriptInvocationContextExtensions.cs b/src/WebJobs.Script/Workers/MessageExtensions/ScriptInvocationContextExtensions.cs index 21ffe42dfb..f9ada2009d 100644 --- a/src/WebJobs.Script/Workers/MessageExtensions/ScriptInvocationContextExtensions.cs +++ b/src/WebJobs.Script/Workers/MessageExtensions/ScriptInvocationContextExtensions.cs @@ -1,4 +1,4 @@ -// Copyright (c) .NET Foundation. All rights reserved. +// Copyright (c) .NET Foundation. All rights reserved. // Licensed under the MIT License. See License.txt in the project root for license information. using System; @@ -7,7 +7,6 @@ using Microsoft.Azure.WebJobs.Host; using Microsoft.Azure.WebJobs.Script.Description; using Microsoft.Azure.WebJobs.Script.Extensions; -using Microsoft.Azure.WebJobs.Script.Workers.Http; using Newtonsoft.Json; using Newtonsoft.Json.Linq; @@ -15,44 +14,6 @@ namespace Microsoft.Azure.WebJobs.Script.Workers { internal static class ScriptInvocationContextExtensions { - public static async Task ToHttpScriptInvocationContext(this ScriptInvocationContext scriptInvocationContext) - { - HttpScriptInvocationContext httpScriptInvocationContext = new HttpScriptInvocationContext(); - - // populate metadata - foreach (var bindingDataPair in scriptInvocationContext.BindingData) - { - if (bindingDataPair.Value != null) - { - if (bindingDataPair.Value is HttpRequest) - { - // no metadata for httpTrigger - continue; - } - if (bindingDataPair.Key.EndsWith("trigger", StringComparison.OrdinalIgnoreCase)) - { - // Data will include value of the trigger. Do not duplicate - continue; - } - httpScriptInvocationContext.Metadata[bindingDataPair.Key] = GetHttpScriptInvocationContextValue(bindingDataPair.Value); - } - } - - // populate input bindings - foreach (var input in scriptInvocationContext.Inputs) - { - if (input.Val is HttpRequest httpRequest) - { - httpScriptInvocationContext.Data[input.Name] = await httpRequest.GetRequestAsJObject(); - continue; - } - httpScriptInvocationContext.Data[input.Name] = GetHttpScriptInvocationContextValue(input.Val, input.Type); - } - - SetRetryContext(scriptInvocationContext, httpScriptInvocationContext); - return httpScriptInvocationContext; - } - internal static object GetHttpScriptInvocationContextValue(object inputValue, DataType dataType = DataType.String) { if (inputValue is byte[] byteArray) @@ -81,20 +42,6 @@ internal static object GetHttpScriptInvocationContextValue(object inputValue, Da return JsonConvert.SerializeObject(inputValue); } - internal static void SetRetryContext(ScriptInvocationContext scriptInvocationContext, HttpScriptInvocationContext httpScriptInvocationContext) - { - if (scriptInvocationContext.ExecutionContext.RetryContext != null) - { - var retryContext = scriptInvocationContext.ExecutionContext.RetryContext; - httpScriptInvocationContext.Metadata["RetryContext"] = new RetryContext() - { - MaxRetryCount = retryContext.MaxRetryCount, - RetryCount = retryContext.RetryCount, - Exception = retryContext.Exception, - }; - } - } - internal static bool TryGetHttpRequest(this ScriptInvocationContext scriptInvocationContext, out HttpRequest request) { request = null; diff --git a/src/WebJobs.Script/Workers/ProcessManagement/WorkerDescription.cs b/src/WebJobs.Script/Workers/WorkerDescription.cs similarity index 100% rename from src/WebJobs.Script/Workers/ProcessManagement/WorkerDescription.cs rename to src/WebJobs.Script/Workers/WorkerDescription.cs diff --git a/src/WebJobs.Script/Workers/WorkerManagerState.cs b/src/WebJobs.Script/Workers/WorkerManagerState.cs new file mode 100644 index 0000000000..969c8b4632 --- /dev/null +++ b/src/WebJobs.Script/Workers/WorkerManagerState.cs @@ -0,0 +1,20 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the MIT License. See License.txt in the project root for license information. + +namespace Microsoft.Azure.WebJobs.Script.Workers; + +/// +/// Note: Only adding the bare minimum values for refactoring purposes. +/// +public enum WorkerManagerState +{ + /// + /// The WorkerManager has not yet been initialized. + /// + Default, + + /// + /// The WorkerManager has been initialized. + /// + Initialized +} diff --git a/src/WebJobs.Script/Workers/ProcessManagement/WorkerProcessArguments.cs b/src/WebJobs.Script/Workers/WorkerProcessArguments.cs similarity index 100% rename from src/WebJobs.Script/Workers/ProcessManagement/WorkerProcessArguments.cs rename to src/WebJobs.Script/Workers/WorkerProcessArguments.cs diff --git a/src/WebJobs.Script/Workers/ProcessManagement/WorkerProcessCountOptions.cs b/src/WebJobs.Script/Workers/WorkerProcessCountOptions.cs similarity index 100% rename from src/WebJobs.Script/Workers/ProcessManagement/WorkerProcessCountOptions.cs rename to src/WebJobs.Script/Workers/WorkerProcessCountOptions.cs diff --git a/src/WebJobs.Script/Workers/WorkerProcessInfo.cs b/src/WebJobs.Script/Workers/WorkerProcessInfo.cs new file mode 100644 index 0000000000..2b2d706850 --- /dev/null +++ b/src/WebJobs.Script/Workers/WorkerProcessInfo.cs @@ -0,0 +1,13 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the MIT License. See License.txt in the project root for license information. + +namespace Microsoft.Azure.WebJobs.Script.Workers; + +public class WorkerProcessInfo +{ + public int ProcessId { get; set; } + + public string ProcessName { get; set; } + + public string DebugEngine { get; set; } +} diff --git a/test/WebJobs.Script.Tests.Integration/Management/FunctionsSyncManagerTests.cs b/test/WebJobs.Script.Tests.Integration/Management/FunctionsSyncManagerTests.cs index 89c6fd61a4..988372e699 100644 --- a/test/WebJobs.Script.Tests.Integration/Management/FunctionsSyncManagerTests.cs +++ b/test/WebJobs.Script.Tests.Integration/Management/FunctionsSyncManagerTests.cs @@ -155,7 +155,7 @@ public FunctionsSyncManagerTests() var functionMetadataProvider = new HostFunctionMetadataProvider(optionsMonitor, NullLogger.Instance, new TestMetricsLogger(), SystemEnvironment.Instance); var defaultProvider = new FunctionMetadataProvider(NullLogger.Instance, null, functionMetadataProvider, new OptionsWrapper(new FunctionsHostingConfigOptions()), _mockEnvironment.Object); - var functionMetadataManager = TestFunctionMetadataManager.GetFunctionMetadataManager(new OptionsWrapper(jobHostOptions), defaultProvider, null, new OptionsWrapper(new HttpWorkerOptions()), loggerFactory, new TestOptionsMonitor(CreateLanguageWorkerConfigSettings())); + var functionMetadataManager = TestFunctionMetadataManager.GetFunctionMetadataManager(new OptionsWrapper(jobHostOptions), defaultProvider, null, loggerFactory, new TestOptionsMonitor(CreateLanguageWorkerConfigSettings())); _scriptHostManager = new TestScriptHostService(configuration); var azureBlobStorageProvider = TestHelpers.GetAzureBlobStorageProvider(configuration, scriptHostManager: _scriptHostManager); diff --git a/test/WebJobs.Script.Tests.Integration/Rpc/FunctionDispatcherEndToEndTests.cs b/test/WebJobs.Script.Tests.Integration/Rpc/FunctionDispatcherEndToEndTests.cs index 773923c918..0d768e2f7e 100644 --- a/test/WebJobs.Script.Tests.Integration/Rpc/FunctionDispatcherEndToEndTests.cs +++ b/test/WebJobs.Script.Tests.Integration/Rpc/FunctionDispatcherEndToEndTests.cs @@ -1,13 +1,13 @@ -// Copyright (c) .NET Foundation. All rights reserved. +// Copyright (c) .NET Foundation. All rights reserved. // Licensed under the MIT License. See License.txt in the project root for license information. -using System; -using System.Collections.Generic; using System.Diagnostics; using System.Linq; using System.Threading.Tasks; using Microsoft.Azure.WebJobs.Script.Grpc; +using Microsoft.Azure.WebJobs.Script.Workers; using Microsoft.Azure.WebJobs.Script.Workers.Rpc; +using Microsoft.Extensions.DependencyInjection; using Xunit; namespace Microsoft.Azure.WebJobs.Script.Tests @@ -68,7 +68,7 @@ private static void KillProcess(int oldProcId) private GrpcWorkerChannel GetCurrentJobHostWorkerChannel() { - RpcFunctionInvocationDispatcher fd = Fixture.JobHost.FunctionDispatcher as RpcFunctionInvocationDispatcher; + RpcFunctionInvocationDispatcher fd = Fixture.Host.Services.GetService().GetFunctionDispatcher() as RpcFunctionInvocationDispatcher; return (GrpcWorkerChannel)fd.JobHostLanguageWorkerChannelManager.GetChannels().FirstOrDefault(); } diff --git a/test/WebJobs.Script.Tests.Integration/Rpc/WorkerConcurrencyManagerEndToEndTests.cs b/test/WebJobs.Script.Tests.Integration/Rpc/WorkerConcurrencyManagerEndToEndTests.cs index 9647ca4820..a4769d6439 100644 --- a/test/WebJobs.Script.Tests.Integration/Rpc/WorkerConcurrencyManagerEndToEndTests.cs +++ b/test/WebJobs.Script.Tests.Integration/Rpc/WorkerConcurrencyManagerEndToEndTests.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Collections.Generic; using System.Linq; using System.Threading; @@ -6,7 +6,9 @@ using System.Threading.Tasks; using Microsoft.Azure.WebJobs.Script.Eventing; using Microsoft.Azure.WebJobs.Script.Grpc.Eventing; +using Microsoft.Azure.WebJobs.Script.Workers; using Microsoft.Azure.WebJobs.Script.Workers.Rpc; +using Microsoft.Extensions.DependencyInjection; using Xunit; namespace Microsoft.Azure.WebJobs.Script.Tests @@ -28,7 +30,8 @@ public async Task WorkerStatus_NewWorkerAdded() await TestHelpers.Await(async () => { - fd = Fixture.JobHost.FunctionDispatcher as RpcFunctionInvocationDispatcher; + IFunctionInvocationDispatcherFactory factory = Fixture.Host.Services.GetService(); + fd = factory.GetFunctionDispatcher() as RpcFunctionInvocationDispatcher; channels = await fd.GetInitializedWorkerChannelsAsync(); return channels.Count() == 2; }, pollingInterval: 1000, timeout: 120 * 1000); @@ -61,7 +64,7 @@ public void Publish(ScriptEvent scriptEvent) try { _scriptEventManager.Publish(scriptEvent); - } + } catch (ObjectDisposedException) { // Do no throw ObjectDisposedException diff --git a/test/WebJobs.Script.Tests.Integration/TestFunctionHost.cs b/test/WebJobs.Script.Tests.Integration/TestFunctionHost.cs index d98c74284b..83d36ad5db 100644 --- a/test/WebJobs.Script.Tests.Integration/TestFunctionHost.cs +++ b/test/WebJobs.Script.Tests.Integration/TestFunctionHost.cs @@ -1,4 +1,4 @@ -// Copyright (c) .NET Foundation. All rights reserved. +// Copyright (c) .NET Foundation. All rights reserved. // Licensed under the MIT License. See License.txt in the project root for license information. using System; @@ -24,7 +24,6 @@ using Microsoft.Azure.WebJobs.Script.WebHost.Middleware; using Microsoft.Azure.WebJobs.Script.WebHost.Models; using Microsoft.Azure.WebJobs.Script.WebHost.Security; -using Microsoft.Azure.WebJobs.Script.Workers.Http; using Microsoft.Azure.WebJobs.Script.Workers.Rpc; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.Configuration.EnvironmentVariables; @@ -507,12 +506,13 @@ private FunctionMetadataManager GetMetadataManager(IOptionsMonitor o.CurrentValue).Returns(workerOptions); mockOptions.Setup(o => o.OnChange(It.IsAny>())).Returns(Mock.Of()); + var metadataOptions = new OptionsWrapper(new FunctionMetadataOptions()); + var managerServiceProvider = manager as IServiceProvider; var metadataProvider = new HostFunctionMetadataProvider(optionsMonitor, NullLogger.Instance, new TestMetricsLogger(), SystemEnvironment.Instance); var defaultProvider = new FunctionMetadataProvider(NullLogger.Instance, null, metadataProvider, new OptionsWrapper(new FunctionsHostingConfigOptions()), SystemEnvironment.Instance); - var metadataManager = new FunctionMetadataManager(managerServiceProvider.GetService>(), defaultProvider, - managerServiceProvider.GetService>(), manager, factory, environment, mockOptions.Object); + var metadataManager = new FunctionMetadataManager(managerServiceProvider.GetService>(), defaultProvider, manager, factory, environment, mockOptions.Object, metadataOptions); return metadataManager; } diff --git a/test/WebJobs.Script.Tests.Integration/WebHostEndToEnd/SpecializationE2ETests.cs b/test/WebJobs.Script.Tests.Integration/WebHostEndToEnd/SpecializationE2ETests.cs index d7dfc4175c..b82186394e 100644 --- a/test/WebJobs.Script.Tests.Integration/WebHostEndToEnd/SpecializationE2ETests.cs +++ b/test/WebJobs.Script.Tests.Integration/WebHostEndToEnd/SpecializationE2ETests.cs @@ -1117,7 +1117,7 @@ public async Task DotNetIsolated_PlaceholderHit_WithProxies() { // This test ensures that capabilities are correctly applied in EnvironmentReload during // specialization - var builder = InitializeDotNetIsolatedPlaceholderBuilder(_dotnetIsolated60Path, _loggerProvider,"HttpRequestFunction"); + var builder = InitializeDotNetIsolatedPlaceholderBuilder(_dotnetIsolated60Path, _loggerProvider, "HttpRequestFunction"); using var testServer = new TestServer(builder); @@ -1499,7 +1499,7 @@ private IWebHostBuilder CreateStandbyHostBuilder(TestLoggerProvider loggerProvid private class InfiniteTimerStandbyManager : StandbyManager { - public InfiniteTimerStandbyManager(IScriptHostManager scriptHostManager, IWebHostRpcWorkerChannelManager rpcWorkerChannelManager, + public InfiniteTimerStandbyManager(IScriptHostManager scriptHostManager, IWebHostWorkerManager rpcWorkerChannelManager, IConfiguration configuration, IScriptWebHostEnvironment webHostEnvironment, IEnvironment environment, IOptionsMonitor options, ILogger logger, HostNameProvider hostNameProvider, IApplicationLifetime applicationLifetime) : base(scriptHostManager, rpcWorkerChannelManager, configuration, webHostEnvironment, environment, options, diff --git a/test/WebJobs.Script.Tests.Integration/WebHostEndToEnd/WebJobsStartupEndToEndTests.cs b/test/WebJobs.Script.Tests.Integration/WebHostEndToEnd/WebJobsStartupEndToEndTests.cs index 7f256abfb5..d30773e817 100644 --- a/test/WebJobs.Script.Tests.Integration/WebHostEndToEnd/WebJobsStartupEndToEndTests.cs +++ b/test/WebJobs.Script.Tests.Integration/WebHostEndToEnd/WebJobsStartupEndToEndTests.cs @@ -1,4 +1,5 @@ -using System.Collections.Generic; +using System; +using System.Collections.Generic; using System.Linq; using System.Net; using System.Threading.Tasks; @@ -148,10 +149,10 @@ private async Task RunStartupExceptionTest(string expectedErrorMessage) // Check that we continuously retry this (it will backoff). var logMessages = fixture.Host.GetWebHostLogMessages(); var buildingMessageCount = logMessages.Count(p => p.FormattedMessage != null && p.FormattedMessage.Contains("Building host: version spec: ~4, startup suppressed: 'False'")); - Assert.True(buildingMessageCount > 1, $"Expected more than one host restart. Actual: {buildingMessageCount}."); + Assert.True(buildingMessageCount > 1, $"Expected more than one host restart. Actual: {buildingMessageCount}.{Environment.NewLine}{fixture.Host.GetLog()}"); var diagnosticEventCount = logMessages.Count(p => p.Category == LogCategories.Startup && p.State?.Any(k => k.Key == ScriptConstants.DiagnosticEventKey) == true); - Assert.True(diagnosticEventCount > 1, $"Expected more than one diagnostic event. Actual: {diagnosticEventCount}."); + Assert.True(diagnosticEventCount > 1, $"Expected more than one diagnostic event. Actual: {diagnosticEventCount}.{Environment.NewLine}{fixture.Host.GetLog()}"); } finally { diff --git a/test/WebJobs.Script.Tests.Shared/Properties/AssemblyInfo.cs b/test/WebJobs.Script.Tests.Shared/Properties/AssemblyInfo.cs new file mode 100644 index 0000000000..fdc591406b --- /dev/null +++ b/test/WebJobs.Script.Tests.Shared/Properties/AssemblyInfo.cs @@ -0,0 +1,8 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the MIT License. See License.txt in the project root for license information. + +using System.Runtime.CompilerServices; + +[assembly: InternalsVisibleTo("Microsoft.Azure.WebJobs.Script.Tests")] +[assembly: InternalsVisibleTo("Microsoft.Azure.WebJobs.Script.Tests.Integration")] +[assembly: InternalsVisibleTo("DynamicProxyGenAssembly2")] \ No newline at end of file diff --git a/test/WebJobs.Script.Tests.Shared/TestFunctionMetadataManager.cs b/test/WebJobs.Script.Tests.Shared/TestFunctionMetadataManager.cs index 5df2124149..38ae971052 100644 --- a/test/WebJobs.Script.Tests.Shared/TestFunctionMetadataManager.cs +++ b/test/WebJobs.Script.Tests.Shared/TestFunctionMetadataManager.cs @@ -1,4 +1,4 @@ -// Copyright (c) .NET Foundation. All rights reserved. +// Copyright (c) .NET Foundation. All rights reserved. // Licensed under the MIT License. See License.txt in the project root for license information. using System; @@ -13,31 +13,32 @@ namespace Microsoft.Azure.WebJobs.Script.Tests { - public static class TestFunctionMetadataManager + internal static class TestFunctionMetadataManager { - public static FunctionMetadataManager GetFunctionMetadataManager(IOptions jobHostOptions, IFunctionMetadataProvider functionMetadataProvider, - IOptions httpOptions, ILoggerFactory loggerFactory, IOptionsMonitor languageWorkerOptions) + public static FunctionMetadataManager GetFunctionMetadataManager(IOptions jobHostOptions, IFunctionMetadataProvider functionMetadataProvider, ILoggerFactory loggerFactory, IOptionsMonitor languageWorkerOptions) { - return GetFunctionMetadataManager(jobHostOptions, functionMetadataProvider, new List(), httpOptions, loggerFactory, languageWorkerOptions); + return GetFunctionMetadataManager(jobHostOptions, functionMetadataProvider, new List(), loggerFactory, languageWorkerOptions); } public static FunctionMetadataManager GetFunctionMetadataManager(IOptions jobHostOptions, - IFunctionMetadataProvider functionMetadataProvider, IList functionProviders, IOptions httpOptions, + IFunctionMetadataProvider functionMetadataProvider, IList functionProviders, ILoggerFactory loggerFactory, IOptionsMonitor languageWorkerOptions) { var managerMock = new Mock(); - return GetFunctionMetadataManager(jobHostOptions, managerMock, functionMetadataProvider, functionProviders, httpOptions, loggerFactory, languageWorkerOptions); + return GetFunctionMetadataManager(jobHostOptions, managerMock, functionMetadataProvider, functionProviders, loggerFactory, languageWorkerOptions); } public static FunctionMetadataManager GetFunctionMetadataManager(IOptions jobHostOptions, Mock managerMock, - IFunctionMetadataProvider functionMetadataProvider, IList functionProviders, IOptions httpOptions, ILoggerFactory loggerFactory, IOptionsMonitor languageWorkerOptions) + IFunctionMetadataProvider functionMetadataProvider, IList functionProviders, ILoggerFactory loggerFactory, IOptionsMonitor languageWorkerOptions) { + var metadataOptions = new OptionsWrapper(new FunctionMetadataOptions()); + managerMock.As().Setup(m => m.GetService(typeof(IEnumerable))).Returns(functionProviders); managerMock.As().Setup(m => m.GetService(typeof(IOptions))).Returns(jobHostOptions); - managerMock.As().Setup(m => m.GetService(typeof(IOptions))).Returns(httpOptions); managerMock.As().Setup(m => m.GetService(typeof(IOptionsMonitor))).Returns(languageWorkerOptions); managerMock.As().Setup(m => m.GetService(typeof(ILoggerFactory))).Returns(loggerFactory); + managerMock.As().Setup(m => m.GetService(typeof(IOptions))).Returns(metadataOptions); var testData = new Dictionary(StringComparer.OrdinalIgnoreCase) { @@ -60,7 +61,7 @@ public static FunctionMetadataManager GetFunctionMetadataManager(IOptions