From d959cb59675b89091a73a88ff6cd56c9b6118e2c Mon Sep 17 00:00:00 2001 From: azfuncgh Date: Thu, 12 Oct 2023 16:03:08 -0500 Subject: [PATCH 1/7] Updating patch version --- build/common.props | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build/common.props b/build/common.props index bbf48b9159..afa45088b8 100644 --- a/build/common.props +++ b/build/common.props @@ -5,7 +5,7 @@ latest 4 27 - 4 + 5 0 From 959162a4e826520e927ed6d82418c7ecabf8c22a Mon Sep 17 00:00:00 2001 From: Shyju Krishnankutty Date: Mon, 2 Oct 2023 12:53:21 -0700 Subject: [PATCH 2/7] Sending command line args with functions- prefix to prevent conflicts (#9514) * Sending a second copy of command line args with "FUNCTIONS_" prefix * Cleanup * Sending a second copy of command line args with "FUNCTIONS_" prefix * switched to functions- prefix * Removed duplicate port from functions_uri * Switching to kebab case for the new args * Update test * Update native placeholder package to handle new args * Update release notes. --- src/WebJobs.Script/WebJobs.Script.csproj | 2 +- src/WebJobs.Script/Workers/Rpc/RpcWorkerContext.cs | 5 ++++- .../Workers/DefaultWorkerProcessFactoryTests.cs | 2 +- 3 files changed, 6 insertions(+), 3 deletions(-) diff --git a/src/WebJobs.Script/WebJobs.Script.csproj b/src/WebJobs.Script/WebJobs.Script.csproj index 413bd27b75..e491970c15 100644 --- a/src/WebJobs.Script/WebJobs.Script.csproj +++ b/src/WebJobs.Script/WebJobs.Script.csproj @@ -47,7 +47,7 @@ - + diff --git a/src/WebJobs.Script/Workers/Rpc/RpcWorkerContext.cs b/src/WebJobs.Script/Workers/Rpc/RpcWorkerContext.cs index 50dd7d54ee..e39ce55adb 100644 --- a/src/WebJobs.Script/Workers/Rpc/RpcWorkerContext.cs +++ b/src/WebJobs.Script/Workers/Rpc/RpcWorkerContext.cs @@ -44,7 +44,10 @@ public RpcWorkerContext(string requestId, public override string GetFormattedArguments() { - return $" --host {ServerUri.Host} --port {ServerUri.Port} --workerId {WorkerId} --requestId {RequestId} --grpcMaxMessageLength {MaxMessageLength}"; + // Adding a second copy of the commandline arguments with the "functions-" prefix to prevent any conflicts caused by the existing generic names. + // Language workers are advised to use the "functions-" prefix ones and if not present fallback to existing ones. + + return $" --host {ServerUri.Host} --port {ServerUri.Port} --workerId {WorkerId} --requestId {RequestId} --grpcMaxMessageLength {MaxMessageLength} --functions-uri {ServerUri.AbsoluteUri} --functions-worker-id {WorkerId} --functions-request-id {RequestId} --functions-grpc-max-message-length {MaxMessageLength}"; } } } diff --git a/test/WebJobs.Script.Tests/Workers/DefaultWorkerProcessFactoryTests.cs b/test/WebJobs.Script.Tests/Workers/DefaultWorkerProcessFactoryTests.cs index 2286612603..5952481759 100644 --- a/test/WebJobs.Script.Tests/Workers/DefaultWorkerProcessFactoryTests.cs +++ b/test/WebJobs.Script.Tests/Workers/DefaultWorkerProcessFactoryTests.cs @@ -82,7 +82,7 @@ public void DefaultWorkerProcessFactory_Returns_ExpectedProcess(WorkerContext wo } if (workerContext is RpcWorkerContext) { - Assert.Equal(" httpvalue1 TestVal httpvalue2 --host localhost --port 80 --workerId testWorkerId --requestId testId --grpcMaxMessageLength 2147483647", childProcess.StartInfo.Arguments); + Assert.Equal(" httpvalue1 TestVal httpvalue2 --host localhost --port 80 --workerId testWorkerId --requestId testId --grpcMaxMessageLength 2147483647 --functions-uri http://localhost/ --functions-worker-id testWorkerId --functions-request-id testId --functions-grpc-max-message-length 2147483647", childProcess.StartInfo.Arguments); } else { From ea966b9d4734045954027a1a3d4a019c8308cb77 Mon Sep 17 00:00:00 2001 From: Shyju Krishnankutty Date: Fri, 29 Sep 2023 08:46:19 -0700 Subject: [PATCH 3/7] Limit dotnet-isolated specialization to 64 bit host process (#9553) * Skipping native placeholder specialization if host is not 64 bit process * formatting linting fix * Stylecop fix. * fix indentation * Adding E2E test. * Improving log message. * Switch from LogDebug to LogInformation * Fix tests to reflect logging changes * Logging with EventId * Logging using an event name * Updated release notes after rebase merge --- src/WebJobs.Script/Environment/IEnvironment.cs | 7 ++++++- .../Environment/SystemEnvironment.cs | 2 ++ .../ExtensionBundle/ExtensionBundleManager.cs | 6 +++--- src/WebJobs.Script/ScriptConstants.cs | 1 + .../Rpc/WebHostRpcWorkerChannelManager.cs | 9 +++++++++ .../WebHostEndToEnd/SpecializationE2ETests.cs | 18 ++++++++++++++++++ .../TestEnvironment.cs | 16 +++++++++++++++- 7 files changed, 54 insertions(+), 5 deletions(-) diff --git a/src/WebJobs.Script/Environment/IEnvironment.cs b/src/WebJobs.Script/Environment/IEnvironment.cs index 2889e3b7e2..e502b8effd 100644 --- a/src/WebJobs.Script/Environment/IEnvironment.cs +++ b/src/WebJobs.Script/Environment/IEnvironment.cs @@ -12,6 +12,11 @@ namespace Microsoft.Azure.WebJobs.Script /// public interface IEnvironment { + /// + /// Gets a value indicating whether the current process is a 64-bit process. + /// + public bool Is64BitProcess { get; } + /// /// Returns the value of an environment variable for the current . /// @@ -20,7 +25,7 @@ public interface IEnvironment string GetEnvironmentVariable(string name); /// - /// Creates, modifies, or deletes an environment variable stored in the current + /// Creates, modifies, or deletes an environment variable stored in the current . /// /// The environment variable name. /// The value to assign to the variable. diff --git a/src/WebJobs.Script/Environment/SystemEnvironment.cs b/src/WebJobs.Script/Environment/SystemEnvironment.cs index ed3e862225..d8ac86e36a 100644 --- a/src/WebJobs.Script/Environment/SystemEnvironment.cs +++ b/src/WebJobs.Script/Environment/SystemEnvironment.cs @@ -17,6 +17,8 @@ private SystemEnvironment() public static SystemEnvironment Instance => _instance.Value; + public bool Is64BitProcess => Environment.Is64BitProcess; + private static SystemEnvironment CreateInstance() { return new SystemEnvironment(); diff --git a/src/WebJobs.Script/ExtensionBundle/ExtensionBundleManager.cs b/src/WebJobs.Script/ExtensionBundle/ExtensionBundleManager.cs index afd3563a3a..3c88df31f3 100644 --- a/src/WebJobs.Script/ExtensionBundle/ExtensionBundleManager.cs +++ b/src/WebJobs.Script/ExtensionBundle/ExtensionBundleManager.cs @@ -71,7 +71,7 @@ public bool IsLegacyExtensionBundle() /// /// Attempts to locate the extension bundle inside the probing paths and download paths. If the extension bundle is not found then it will download the extension bundle. /// - /// Path of the extension bundle + /// Path of the extension bundle. public async Task GetExtensionBundlePath() { using (var httpClient = new HttpClient()) @@ -83,8 +83,8 @@ public async Task GetExtensionBundlePath() /// /// Attempts to locate the extension bundle inside the probing paths and download paths. If the extension bundle is not found then it will download the extension bundle. /// - /// HttpClient used to download the extension bundle - /// Path of the extension bundle + /// HttpClient used to download the extension bundle. + /// Path of the extension bundle. public async Task GetExtensionBundlePath(HttpClient httpClient) { return await GetBundle(httpClient); diff --git a/src/WebJobs.Script/ScriptConstants.cs b/src/WebJobs.Script/ScriptConstants.cs index 75b2275166..b5d982a4e8 100644 --- a/src/WebJobs.Script/ScriptConstants.cs +++ b/src/WebJobs.Script/ScriptConstants.cs @@ -88,6 +88,7 @@ public static class ScriptConstants public const string DefaultMasterKeyName = "master"; public const string DefaultFunctionKeyName = "default"; public const string ColdStartEventName = "ColdStart"; + public const string PlaceholderMissDueToBitnessEventName = "PlaceholderMissDueToBitness"; public const string FunctionsUserAgent = "AzureFunctionsRuntime"; public const string HttpScaleUserAgent = "HttpScaleManager"; diff --git a/src/WebJobs.Script/Workers/Rpc/WebHostRpcWorkerChannelManager.cs b/src/WebJobs.Script/Workers/Rpc/WebHostRpcWorkerChannelManager.cs index af2863f7cc..c02c23354a 100644 --- a/src/WebJobs.Script/Workers/Rpc/WebHostRpcWorkerChannelManager.cs +++ b/src/WebJobs.Script/Workers/Rpc/WebHostRpcWorkerChannelManager.cs @@ -181,6 +181,15 @@ private bool UsePlaceholderChannel(IRpcWorkerChannel channel) return false; } + // We support specialization of dotnet-isolated only on 64bit host process. + if (!_environment.Is64BitProcess) + { + _logger.LogInformation(new EventId(421, ScriptConstants.PlaceholderMissDueToBitnessEventName), + "This app is configured as 32-bit and therefore does not leverage all performance optimizations. See https://aka.ms/azure-functions/dotnet/placeholders for more information."); + + return false; + } + // Do not specialize if the placeholder is 6.0 but the site is 7.0 (for example). var currentWorkerRuntimeVersion = _environment.GetEnvironmentVariable(RpcWorkerConstants.FunctionWorkerRuntimeVersionSettingName); channel.WorkerProcess.Process.StartInfo.Environment.TryGetValue(RpcWorkerConstants.FunctionWorkerRuntimeVersionSettingName, out string placeholderWorkerRuntimeVersion); diff --git a/test/WebJobs.Script.Tests.Integration/WebHostEndToEnd/SpecializationE2ETests.cs b/test/WebJobs.Script.Tests.Integration/WebHostEndToEnd/SpecializationE2ETests.cs index ceb4160c8c..27e343fc73 100644 --- a/test/WebJobs.Script.Tests.Integration/WebHostEndToEnd/SpecializationE2ETests.cs +++ b/test/WebJobs.Script.Tests.Integration/WebHostEndToEnd/SpecializationE2ETests.cs @@ -895,6 +895,24 @@ public async Task DotNetIsolated_PlaceholderMiss_EnvVar() Assert.Contains("Shutting down placeholder worker. Worker is not compatible for runtime: dotnet-isolated", log); } + [Fact] + public async Task DotNetIsolated_PlaceholderMiss_Not64Bit() + { + _environment.SetProcessBitness(is64Bitness: false); + + // We only specialize when host process is 64 bit process. + await DotNetIsolatedPlaceholderMiss(() => + { + _environment.SetEnvironmentVariable(EnvironmentSettingNames.AzureWebsiteUsePlaceholderDotNetIsolated, "1"); + _environment.SetEnvironmentVariable(RpcWorkerConstants.FunctionWorkerRuntimeVersionSettingName, "6.0"); + }); + + var log = _loggerProvider.GetLog(); + Assert.Contains("UsePlaceholderDotNetIsolated: True", log); + Assert.Contains("This app is configured as 32-bit and therefore does not leverage all performance optimizations. See https://aka.ms/azure-functions/dotnet/placeholders for more information.", log); + Assert.Contains("Shutting down placeholder worker. Worker is not compatible for runtime: dotnet-isolated", log); + } + [Fact] public async Task DotNetIsolated_PlaceholderMiss_DotNetVer() { diff --git a/test/WebJobs.Script.Tests.Shared/TestEnvironment.cs b/test/WebJobs.Script.Tests.Shared/TestEnvironment.cs index 1bd73bea94..d8af0c5f26 100644 --- a/test/WebJobs.Script.Tests.Shared/TestEnvironment.cs +++ b/test/WebJobs.Script.Tests.Shared/TestEnvironment.cs @@ -12,17 +12,26 @@ namespace Microsoft.Azure.WebJobs.Script.Tests public class TestEnvironment : IEnvironment { private readonly IDictionary _variables; + private bool _is64BitProcess; public TestEnvironment() - : this(new Dictionary()) + : this(new Dictionary()) { } public TestEnvironment(IDictionary variables) + : this(variables, is64BitProcess: true) + { + } + + public TestEnvironment(IDictionary variables, bool is64BitProcess) { _variables = variables ?? throw new ArgumentNullException(nameof(variables)); + _is64BitProcess = is64BitProcess; } + public bool Is64BitProcess => _is64BitProcess; + public void Clear() { _variables.Clear(); @@ -54,5 +63,10 @@ public static TestEnvironment FromEnvironmentVariables() return new TestEnvironment(variables); } + + public void SetProcessBitness(bool is64Bitness) + { + _is64BitProcess = is64Bitness; + } } } From 2525628b1a82dbea2e0be092071d73b734e39122 Mon Sep 17 00:00:00 2001 From: Shyju Krishnankutty Date: Thu, 12 Oct 2023 11:46:26 -0700 Subject: [PATCH 4/7] Handling env reload response from native placeholder for failure case. (#9602) * Handling env reload response from native placeholder for failure case. * Almost working except one test needs cleanup * Cleanup * Cleanup * Fixing Worker.Extensions.Http package version to align with Http.AspNetCore package. * Logging error as it is received from the worker. * DotNetIsolatedNativeHost package to 1.0.2 * Inlining a variable (Reverting to previous version) --- .../Channel/GrpcWorkerChannel.cs | 27 ++++-- .../StatusResultExtensions.cs | 13 +-- src/WebJobs.Script/WebJobs.Script.csproj | 2 +- .../Workers/Rpc/IRpcWorkerChannel.cs | 2 +- .../Rpc/WebHostRpcWorkerChannelManager.cs | 6 +- test/DotNetIsolated60/DotNetIsolated60.csproj | 10 ++- test/DotNetIsolated60/DotNetIsolated60.sln | 6 ++ test/DotNetIsolated60/Program.cs | 6 +- .../DotNetIsolatedUnsupportedWorker.csproj | 29 +++++++ .../HttpRequestDataFunction.cs | 30 +++++++ .../HttpRequestFunction.cs | 23 ++++++ .../Program.cs | 23 ++++++ .../QueueFunction.cs | 21 +++++ .../DotNetIsolatedUnsupportedWorker/host.json | 12 +++ test/EmptyScriptRoot/host.json | 7 ++ .../WebHostEndToEnd/SpecializationE2ETests.cs | 82 ++++++++++++++----- .../Workers/Rpc/TestRpcWorkerChannel.cs | 4 +- 17 files changed, 257 insertions(+), 46 deletions(-) create mode 100644 test/DotNetIsolatedUnsupportedWorker/DotNetIsolatedUnsupportedWorker.csproj create mode 100644 test/DotNetIsolatedUnsupportedWorker/HttpRequestDataFunction.cs create mode 100644 test/DotNetIsolatedUnsupportedWorker/HttpRequestFunction.cs create mode 100644 test/DotNetIsolatedUnsupportedWorker/Program.cs create mode 100644 test/DotNetIsolatedUnsupportedWorker/QueueFunction.cs create mode 100644 test/DotNetIsolatedUnsupportedWorker/host.json create mode 100644 test/EmptyScriptRoot/host.json diff --git a/src/WebJobs.Script.Grpc/Channel/GrpcWorkerChannel.cs b/src/WebJobs.Script.Grpc/Channel/GrpcWorkerChannel.cs index f9a77c497e..9366a3b83e 100644 --- a/src/WebJobs.Script.Grpc/Channel/GrpcWorkerChannel.cs +++ b/src/WebJobs.Script.Grpc/Channel/GrpcWorkerChannel.cs @@ -374,12 +374,18 @@ internal void FunctionEnvironmentReloadResponse(FunctionEnvironmentReloadRespons ApplyCapabilities(res.Capabilities, res.CapabilitiesUpdateStrategy.ToGrpcCapabilitiesUpdateStrategy()); - if (res.Result.IsFailure(out Exception reloadEnvironmentVariablesException)) + if (res.Result.IsFailure(IsUserCodeExceptionCapabilityEnabled(), out var reloadEnvironmentVariablesException)) { - _workerChannelLogger.LogError(reloadEnvironmentVariablesException, "Failed to reload environment variables"); - _reloadTask.SetException(reloadEnvironmentVariablesException); + if (res.Result.Exception is not null && reloadEnvironmentVariablesException is not null) + { + _workerChannelLogger.LogWarning(reloadEnvironmentVariablesException, reloadEnvironmentVariablesException.Message); + } + _reloadTask.SetResult(false); + } + else + { + _reloadTask.SetResult(true); } - _reloadTask.SetResult(true); latencyEvent.Dispose(); } @@ -414,6 +420,15 @@ internal void WorkerInitResponse(GrpcEvent initEvent) _workerInitTask.TrySetResult(true); } + private bool IsUserCodeExceptionCapabilityEnabled() + { + var enableUserCodeExceptionCapability = string.Equals( + _workerCapabilities.GetCapabilityState(RpcWorkerConstants.EnableUserCodeException), bool.TrueString, + StringComparison.OrdinalIgnoreCase); + + return enableUserCodeExceptionCapability; + } + private void LogWorkerMetadata(WorkerMetadata workerMetadata) { if (workerMetadata == null) @@ -546,7 +561,7 @@ internal FunctionLoadRequestCollection GetFunctionLoadRequestCollection(IEnumera return functionLoadRequestCollection; } - public Task SendFunctionEnvironmentReloadRequest() + public Task SendFunctionEnvironmentReloadRequest() { _functionsIndexingTask = new TaskCompletionSource>(TaskCreationOptions.RunContinuationsAsynchronously); _functionMetadataRequestSent = false; @@ -1576,4 +1591,4 @@ private void OnTimeout() } } } -} +} \ No newline at end of file diff --git a/src/WebJobs.Script.Grpc/MessageExtensions/StatusResultExtensions.cs b/src/WebJobs.Script.Grpc/MessageExtensions/StatusResultExtensions.cs index b555e6c192..077a0185e3 100644 --- a/src/WebJobs.Script.Grpc/MessageExtensions/StatusResultExtensions.cs +++ b/src/WebJobs.Script.Grpc/MessageExtensions/StatusResultExtensions.cs @@ -3,20 +3,18 @@ using System; using System.Threading.Tasks; -using Grpc.Core; -using Microsoft.Azure.WebJobs.Script.Config; using Microsoft.Azure.WebJobs.Script.Grpc.Messages; namespace Microsoft.Azure.WebJobs.Script.Grpc { internal static class StatusResultExtensions { - public static bool IsFailure(this StatusResult statusResult, out Exception exception) + public static bool IsFailure(this StatusResult statusResult, bool enableUserCodeExceptionCapability, out Exception exception) { switch (statusResult.Status) { case StatusResult.Types.Status.Failure: - exception = GetRpcException(statusResult); + exception = GetRpcException(statusResult, enableUserCodeExceptionCapability); return true; case StatusResult.Types.Status.Cancelled: @@ -29,6 +27,11 @@ public static bool IsFailure(this StatusResult statusResult, out Exception excep } } + public static bool IsFailure(this StatusResult statusResult, out Exception exception) + { + return IsFailure(statusResult, false, out exception); + } + /// /// This method is only hit on the invocation code path. /// enableUserCodeExceptionCapability = feature flag exposed as a capability that is set by the worker. @@ -68,4 +71,4 @@ public static Workers.Rpc.RpcException GetRpcException(StatusResult statusResult return new Workers.Rpc.RpcException(status, string.Empty, string.Empty); } } -} +} \ No newline at end of file diff --git a/src/WebJobs.Script/WebJobs.Script.csproj b/src/WebJobs.Script/WebJobs.Script.csproj index e491970c15..bc2626083d 100644 --- a/src/WebJobs.Script/WebJobs.Script.csproj +++ b/src/WebJobs.Script/WebJobs.Script.csproj @@ -47,7 +47,7 @@ - + diff --git a/src/WebJobs.Script/Workers/Rpc/IRpcWorkerChannel.cs b/src/WebJobs.Script/Workers/Rpc/IRpcWorkerChannel.cs index df9bde5278..d9d3e6eb9c 100644 --- a/src/WebJobs.Script/Workers/Rpc/IRpcWorkerChannel.cs +++ b/src/WebJobs.Script/Workers/Rpc/IRpcWorkerChannel.cs @@ -22,7 +22,7 @@ public interface IRpcWorkerChannel : IWorkerChannel void SendFunctionLoadRequests(ManagedDependencyOptions managedDependencyOptions, TimeSpan? functionTimeout); - Task SendFunctionEnvironmentReloadRequest(); + Task SendFunctionEnvironmentReloadRequest(); void SendWorkerWarmupRequest(); diff --git a/src/WebJobs.Script/Workers/Rpc/WebHostRpcWorkerChannelManager.cs b/src/WebJobs.Script/Workers/Rpc/WebHostRpcWorkerChannelManager.cs index c02c23354a..8e0ce86d50 100644 --- a/src/WebJobs.Script/Workers/Rpc/WebHostRpcWorkerChannelManager.cs +++ b/src/WebJobs.Script/Workers/Rpc/WebHostRpcWorkerChannelManager.cs @@ -123,12 +123,14 @@ public async Task SpecializeAsync() if (_workerRuntime != null && rpcWorkerChannel != null) { + bool envReloadRequestResultSuccessful = false; if (UsePlaceholderChannel(rpcWorkerChannel)) { _logger.LogDebug("Loading environment variables for runtime: {runtime}", _workerRuntime); - await rpcWorkerChannel.SendFunctionEnvironmentReloadRequest(); + envReloadRequestResultSuccessful = await rpcWorkerChannel.SendFunctionEnvironmentReloadRequest(); } - else + + if (envReloadRequestResultSuccessful == false) { _logger.LogDebug("Shutting down placeholder worker. Worker is not compatible for runtime: {runtime}", _workerRuntime); // If we need to allow file edits, we should shutdown the webhost channel on specialization. diff --git a/test/DotNetIsolated60/DotNetIsolated60.csproj b/test/DotNetIsolated60/DotNetIsolated60.csproj index 8e602a3cd7..f6a818fb17 100644 --- a/test/DotNetIsolated60/DotNetIsolated60.csproj +++ b/test/DotNetIsolated60/DotNetIsolated60.csproj @@ -6,13 +6,15 @@ enable enable True + True + True - - - + + + - + diff --git a/test/DotNetIsolated60/DotNetIsolated60.sln b/test/DotNetIsolated60/DotNetIsolated60.sln index 1f839fdf73..286c56b973 100644 --- a/test/DotNetIsolated60/DotNetIsolated60.sln +++ b/test/DotNetIsolated60/DotNetIsolated60.sln @@ -5,6 +5,8 @@ VisualStudioVersion = 17.5.33627.172 MinimumVisualStudioVersion = 10.0.40219.1 Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "DotNetIsolated60", "DotNetIsolated60.csproj", "{1DA92227-F28E-408D-96B1-20C72571E4AE}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "DotNetIsolatedUnsupportedWorker", "..\DotNetIsolatedUnsupportedWorker\DotNetIsolatedUnsupportedWorker.csproj", "{3F15B936-6365-447E-9EC6-4E996B30C55F}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -15,6 +17,10 @@ Global {1DA92227-F28E-408D-96B1-20C72571E4AE}.Debug|Any CPU.Build.0 = Debug|Any CPU {1DA92227-F28E-408D-96B1-20C72571E4AE}.Release|Any CPU.ActiveCfg = Release|Any CPU {1DA92227-F28E-408D-96B1-20C72571E4AE}.Release|Any CPU.Build.0 = Release|Any CPU + {3F15B936-6365-447E-9EC6-4E996B30C55F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {3F15B936-6365-447E-9EC6-4E996B30C55F}.Debug|Any CPU.Build.0 = Debug|Any CPU + {3F15B936-6365-447E-9EC6-4E996B30C55F}.Release|Any CPU.ActiveCfg = Release|Any CPU + {3F15B936-6365-447E-9EC6-4E996B30C55F}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/test/DotNetIsolated60/Program.cs b/test/DotNetIsolated60/Program.cs index 2d2691d7d4..29dd670851 100644 --- a/test/DotNetIsolated60/Program.cs +++ b/test/DotNetIsolated60/Program.cs @@ -11,14 +11,12 @@ if (useProxy) { hostBuilder - .ConfigureFunctionsWebApplication() - .ConfigureGeneratedFunctionMetadataProvider(); + .ConfigureFunctionsWebApplication(); } else { hostBuilder - .ConfigureFunctionsWorkerDefaults() - .ConfigureGeneratedFunctionMetadataProvider(); + .ConfigureFunctionsWorkerDefaults(); } var host = hostBuilder.Build(); diff --git a/test/DotNetIsolatedUnsupportedWorker/DotNetIsolatedUnsupportedWorker.csproj b/test/DotNetIsolatedUnsupportedWorker/DotNetIsolatedUnsupportedWorker.csproj new file mode 100644 index 0000000000..81c3461eaf --- /dev/null +++ b/test/DotNetIsolatedUnsupportedWorker/DotNetIsolatedUnsupportedWorker.csproj @@ -0,0 +1,29 @@ + + + net6.0 + v4 + Exe + enable + enable + True + + + + + + + + + + + PreserveNewest + + + PreserveNewest + Never + + + + + + \ No newline at end of file diff --git a/test/DotNetIsolatedUnsupportedWorker/HttpRequestDataFunction.cs b/test/DotNetIsolatedUnsupportedWorker/HttpRequestDataFunction.cs new file mode 100644 index 0000000000..f494114440 --- /dev/null +++ b/test/DotNetIsolatedUnsupportedWorker/HttpRequestDataFunction.cs @@ -0,0 +1,30 @@ +using System.Net; +using Microsoft.Azure.Functions.Worker; +using Microsoft.Azure.Functions.Worker.Http; +using Microsoft.Extensions.Logging; + +namespace DotNetIsolatedUnsupportedWorker +{ + public class HttpRequestDataFunction + { + private readonly ILogger _logger; + + public HttpRequestDataFunction(ILoggerFactory loggerFactory) + { + _logger = loggerFactory.CreateLogger(); + } + + [Function("HttpRequestDataFunction")] + public HttpResponseData Run([HttpTrigger(AuthorizationLevel.Anonymous, "get", "post")] HttpRequestData req) + { + _logger.LogInformation("C# HTTP trigger function processed a request."); + + var response = req.CreateResponse(HttpStatusCode.OK); + response.Headers.Add("Content-Type", "text/plain; charset=utf-8"); + + response.WriteString("Welcome to Azure Functions!"); + + return response; + } + } +} diff --git a/test/DotNetIsolatedUnsupportedWorker/HttpRequestFunction.cs b/test/DotNetIsolatedUnsupportedWorker/HttpRequestFunction.cs new file mode 100644 index 0000000000..ba6df7f15b --- /dev/null +++ b/test/DotNetIsolatedUnsupportedWorker/HttpRequestFunction.cs @@ -0,0 +1,23 @@ +using Microsoft.AspNetCore.Http; +using Microsoft.Azure.Functions.Worker; +using Microsoft.Extensions.Logging; + +namespace DotNetIsolatedUnsupportedWorker +{ + public class HttpRequestFunction + { + private readonly ILogger _logger; + + public HttpRequestFunction(ILogger logger) + { + _logger = logger; + } + + [Function(nameof(HttpRequestFunction))] + public Task Run([HttpTrigger(AuthorizationLevel.Anonymous, "get", "post")] HttpRequest req) + { + _logger.LogInformation("C# HTTP trigger function processed a request."); + return req.HttpContext.Response.WriteAsync("Welcome to Azure Functions!"); + } + } +} diff --git a/test/DotNetIsolatedUnsupportedWorker/Program.cs b/test/DotNetIsolatedUnsupportedWorker/Program.cs new file mode 100644 index 0000000000..29dd670851 --- /dev/null +++ b/test/DotNetIsolatedUnsupportedWorker/Program.cs @@ -0,0 +1,23 @@ +using Microsoft.Azure.Functions.Worker; +using Microsoft.Extensions.Hosting; + +//Debugger.Launch(); + +// Tests can set an env var that will swap this to use the proxy +bool useProxy = Environment.GetEnvironmentVariable("UseProxyInTest")?.Contains("1") ?? false; + +var hostBuilder = new HostBuilder(); + +if (useProxy) +{ + hostBuilder + .ConfigureFunctionsWebApplication(); +} +else +{ + hostBuilder + .ConfigureFunctionsWorkerDefaults(); +} + +var host = hostBuilder.Build(); +host.Run(); diff --git a/test/DotNetIsolatedUnsupportedWorker/QueueFunction.cs b/test/DotNetIsolatedUnsupportedWorker/QueueFunction.cs new file mode 100644 index 0000000000..69dd183369 --- /dev/null +++ b/test/DotNetIsolatedUnsupportedWorker/QueueFunction.cs @@ -0,0 +1,21 @@ +using Microsoft.Azure.Functions.Worker; +using Microsoft.Extensions.Logging; + +namespace DotNetIsolatedUnsupportedWorker +{ + public class QueueFunction + { + private readonly ILogger _logger; + + public QueueFunction(ILoggerFactory loggerFactory) + { + _logger = loggerFactory.CreateLogger(); + } + + [Function("QueueFunction")] + public void Run([QueueTrigger("myqueue-items", Connection = "AzureWebJobsStorage")] string myQueueItem) + { + _logger.LogInformation($"C# Queue trigger function processed: {myQueueItem}"); + } + } +} diff --git a/test/DotNetIsolatedUnsupportedWorker/host.json b/test/DotNetIsolatedUnsupportedWorker/host.json new file mode 100644 index 0000000000..ee5cf5f83f --- /dev/null +++ b/test/DotNetIsolatedUnsupportedWorker/host.json @@ -0,0 +1,12 @@ +{ + "version": "2.0", + "logging": { + "applicationInsights": { + "samplingSettings": { + "isEnabled": true, + "excludedTypes": "Request" + }, + "enableLiveMetricsFilters": true + } + } +} \ No newline at end of file diff --git a/test/EmptyScriptRoot/host.json b/test/EmptyScriptRoot/host.json new file mode 100644 index 0000000000..55d16424d6 --- /dev/null +++ b/test/EmptyScriptRoot/host.json @@ -0,0 +1,7 @@ +{ + "version": "2.0", + "extensionBundle": { + "id": "Microsoft.Azure.Functions.ExtensionBundle", + "version": "[4.*, 5.0.0)" + } +} \ No newline at end of file diff --git a/test/WebJobs.Script.Tests.Integration/WebHostEndToEnd/SpecializationE2ETests.cs b/test/WebJobs.Script.Tests.Integration/WebHostEndToEnd/SpecializationE2ETests.cs index 27e343fc73..79ca16577d 100644 --- a/test/WebJobs.Script.Tests.Integration/WebHostEndToEnd/SpecializationE2ETests.cs +++ b/test/WebJobs.Script.Tests.Integration/WebHostEndToEnd/SpecializationE2ETests.cs @@ -51,6 +51,8 @@ public class SpecializationE2ETests private static readonly string _scriptRootConfigPath = ConfigurationPath.Combine(ConfigurationSectionNames.WebHost, nameof(ScriptApplicationHostOptions.ScriptPath)); private static readonly string _dotnetIsolated60Path = Path.GetFullPath(@"..\..\..\..\DotNetIsolated60\bin\Debug\net6.0"); + private static readonly string _dotnetIsolatedUnsuppportedPath = Path.GetFullPath(@"..\..\..\..\DotNetIsolatedUnsupportedWorker\bin\Debug\net6.0"); + private static readonly string _dotnetIsolatedEmptyScriptRoot = Path.GetFullPath(@"..\..\..\..\EmptyScriptRoot"); private const string _specializedScriptRoot = @"TestScripts\CSharp"; @@ -799,7 +801,7 @@ public async Task Specialization_JobHostInternalStorageOptionsUpdatesWithActiveH [Fact] public async Task DotNetIsolated_PlaceholderHit() { - var builder = InitializeDotNetIsolatedPlaceholderBuilder("HttpRequestDataFunction"); + var builder = InitializeDotNetIsolatedPlaceholderBuilder(_dotnetIsolated60Path, "HttpRequestDataFunction"); using var testServer = new TestServer(builder); @@ -839,7 +841,7 @@ public async Task DotNetIsolated_PlaceholderHit_WithProxies() { // This test ensures that capabilities are correctly applied in EnvironmentReload during // specialization - var builder = InitializeDotNetIsolatedPlaceholderBuilder("HttpRequestFunction"); + var builder = InitializeDotNetIsolatedPlaceholderBuilder(_dotnetIsolated60Path, "HttpRequestFunction"); using var testServer = new TestServer(builder); @@ -888,7 +890,7 @@ public async Task DotNetIsolated_PlaceholderHit_WithProxies() public async Task DotNetIsolated_PlaceholderMiss_EnvVar() { // Placeholder miss if the WEBSITE_USE_PLACEHOLDER_DOTNETISOLATED env var is not set - await DotNetIsolatedPlaceholderMiss(); + await DotNetIsolatedPlaceholderMiss(_dotnetIsolated60Path); var log = _loggerProvider.GetLog(); Assert.Contains("UsePlaceholderDotNetIsolated: False", log); @@ -901,7 +903,7 @@ public async Task DotNetIsolated_PlaceholderMiss_Not64Bit() _environment.SetProcessBitness(is64Bitness: false); // We only specialize when host process is 64 bit process. - await DotNetIsolatedPlaceholderMiss(() => + await DotNetIsolatedPlaceholderMiss(_dotnetIsolated60Path, () => { _environment.SetEnvironmentVariable(EnvironmentSettingNames.AzureWebsiteUsePlaceholderDotNetIsolated, "1"); _environment.SetEnvironmentVariable(RpcWorkerConstants.FunctionWorkerRuntimeVersionSettingName, "6.0"); @@ -918,7 +920,7 @@ public async Task DotNetIsolated_PlaceholderMiss_DotNetVer() { // Even with placeholders enabled via the WEBSITE_USE_PLACEHOLDER_DOTNETISOLATED env var, // if the dotnet version does not match, we should not use the placeholder - await DotNetIsolatedPlaceholderMiss(() => + await DotNetIsolatedPlaceholderMiss(_dotnetIsolated60Path, () => { _environment.SetEnvironmentVariable(EnvironmentSettingNames.AzureWebsiteUsePlaceholderDotNetIsolated, "1"); _environment.SetEnvironmentVariable(RpcWorkerConstants.FunctionWorkerRuntimeVersionSettingName, "7.0"); @@ -930,6 +932,36 @@ await DotNetIsolatedPlaceholderMiss(() => Assert.Contains("Shutting down placeholder worker. Worker is not compatible for runtime: dotnet-isolated", log); } + [Fact] + public async Task DotNetIsolated_PlaceholderMiss_UnsupportedWorkerPackage() + { + await DotNetIsolatedPlaceholderMiss(_dotnetIsolatedUnsuppportedPath, () => + { + _environment.SetEnvironmentVariable(EnvironmentSettingNames.AzureWebsiteUsePlaceholderDotNetIsolated, "1"); + _environment.SetEnvironmentVariable(RpcWorkerConstants.FunctionWorkerRuntimeVersionSettingName, "6.0"); + }); + + var log = _loggerProvider.GetLog(); + Assert.Contains("UsePlaceholderDotNetIsolated: True", log); + Assert.Contains("Placeholder runtime version: '6.0'. Site runtime version: '6.0'. Match: True", log); + Assert.Contains("Shutting down placeholder worker. Worker is not compatible for runtime: dotnet-isolated", log); + } + + [Fact] + public async Task DotNetIsolated_PlaceholderMiss_EmptyScriptRoot() + { + await DotNetIsolatedPlaceholderMiss(_dotnetIsolatedEmptyScriptRoot, () => + { + _environment.SetEnvironmentVariable(EnvironmentSettingNames.AzureWebsiteUsePlaceholderDotNetIsolated, "1"); + _environment.SetEnvironmentVariable(RpcWorkerConstants.FunctionWorkerRuntimeVersionSettingName, "6.0"); + }); + + var log = _loggerProvider.GetLog(); + Assert.Contains("UsePlaceholderDotNetIsolated: True", log); + Assert.Contains("Placeholder runtime version: '6.0'. Site runtime version: '6.0'. Match: True", log); + Assert.Contains("Shutting down placeholder worker. Worker is not compatible for runtime: dotnet-isolated", log); + } + [Fact] // Fix for https://github.com/Azure/azure-functions-host/issues/9288 public async Task SpecializedSite_StopsHostBeforeWorker() @@ -943,7 +975,7 @@ public async Task SpecializedSite_StopsHostBeforeWorker() await queue.CreateIfNotExistsAsync(); await queue.ClearAsync(); - var builder = InitializeDotNetIsolatedPlaceholderBuilder("HttpRequestDataFunction", "QueueFunction"); + var builder = InitializeDotNetIsolatedPlaceholderBuilder(_dotnetIsolated60Path, "HttpRequestDataFunction", "QueueFunction"); using var testServer = new TestServer(builder); @@ -1005,9 +1037,9 @@ await TestHelpers.Await(() => Assert.Empty(completedLogs.Where(p => p.Level == LogLevel.Error)); } - private async Task DotNetIsolatedPlaceholderMiss(Action additionalSpecializedSetup = null) + private async Task DotNetIsolatedPlaceholderMiss(string scriptRootPath, Action additionalSpecializedSetup = null) { - var builder = InitializeDotNetIsolatedPlaceholderBuilder("HttpRequestDataFunction"); + var builder = InitializeDotNetIsolatedPlaceholderBuilder(scriptRootPath, "HttpRequestDataFunction"); // remove WEBSITE_USE_PLACEHOLDER_DOTNETISOLATED _environment.SetEnvironmentVariable(EnvironmentSettingNames.AzureWebsiteUsePlaceholderDotNetIsolated, null); @@ -1033,18 +1065,26 @@ private async Task DotNetIsolatedPlaceholderMiss(Action additionalSpecializedSet additionalSpecializedSetup?.Invoke(); response = await client.GetAsync("api/HttpRequestDataFunction"); - response.EnsureSuccessStatusCode(); + if (scriptRootPath == _dotnetIsolatedEmptyScriptRoot) + { + Assert.Equal(HttpStatusCode.NotFound, response.StatusCode); + } + else + { + response.EnsureSuccessStatusCode(); - // Placeholder miss; new channel should be started using the deployed worker directly - var specializedChannel = await webChannelManager.GetChannels("dotnet-isolated").Single().Value.Task; - Assert.Contains("dotnet.exe", specializedChannel.WorkerProcess.Process.StartInfo.FileName); - Assert.Contains("DotNetIsolated60", specializedChannel.WorkerProcess.Process.StartInfo.Arguments); - runningProcess = Process.GetProcessById(specializedChannel.WorkerProcess.Id); - Assert.Contains(runningProcess.ProcessName, "dotnet"); - - // Ensure other process is gone. - Assert.DoesNotContain(Process.GetProcesses(), p => p.ProcessName.Contains("FunctionsNetHost")); - Assert.Throws(() => placeholderChannel.WorkerProcess.Process.Id); + var expectedProcessName = scriptRootPath == _dotnetIsolated60Path ? "DotNetIsolated60" : "DotNetIsolatedUnsupported"; + // Placeholder miss; new channel should be started using the deployed worker directly + var specializedChannel = await webChannelManager.GetChannels("dotnet-isolated").Single().Value.Task; + Assert.Contains("dotnet.exe", specializedChannel.WorkerProcess.Process.StartInfo.FileName); + Assert.Contains(expectedProcessName, specializedChannel.WorkerProcess.Process.StartInfo.Arguments); + runningProcess = Process.GetProcessById(specializedChannel.WorkerProcess.Id); + Assert.Contains(runningProcess.ProcessName, "dotnet"); + + // Ensure other process is gone. + Assert.DoesNotContain(Process.GetProcesses(), p => p.ProcessName.Contains("FunctionsNetHost")); + Assert.Throws(() => placeholderChannel.WorkerProcess.Process.Id); + } } private static void BuildDotnetIsolated60() @@ -1053,7 +1093,7 @@ private static void BuildDotnetIsolated60() p.WaitForExit(); } - private IWebHostBuilder InitializeDotNetIsolatedPlaceholderBuilder(params string[] functions) + private IWebHostBuilder InitializeDotNetIsolatedPlaceholderBuilder(string scriptRootPath, params string[] functions) { BuildDotnetIsolated60(); @@ -1068,7 +1108,7 @@ private IWebHostBuilder InitializeDotNetIsolatedPlaceholderBuilder(params string { config.AddInMemoryCollection(new Dictionary { - { _scriptRootConfigPath, _dotnetIsolated60Path }, + { _scriptRootConfigPath, scriptRootPath }, }); }); diff --git a/test/WebJobs.Script.Tests/Workers/Rpc/TestRpcWorkerChannel.cs b/test/WebJobs.Script.Tests/Workers/Rpc/TestRpcWorkerChannel.cs index c65e15d7fa..c7b6145565 100644 --- a/test/WebJobs.Script.Tests/Workers/Rpc/TestRpcWorkerChannel.cs +++ b/test/WebJobs.Script.Tests/Workers/Rpc/TestRpcWorkerChannel.cs @@ -74,10 +74,10 @@ public void SendFunctionLoadRequests(ManagedDependencyOptions managedDependencie _testLogger.LogInformation("RegisterFunctions called"); } - public Task SendFunctionEnvironmentReloadRequest() + public Task SendFunctionEnvironmentReloadRequest() { _testLogger.LogInformation("SendFunctionEnvironmentReloadRequest called"); - return Task.CompletedTask; + return Task.FromResult(true); } public void SendInvocationRequest(ScriptInvocationContext context) From ed05b419f860fd73d6d36a4b32bb0279e3df379b Mon Sep 17 00:00:00 2001 From: azfuncgh Date: Thu, 12 Oct 2023 16:06:29 -0500 Subject: [PATCH 5/7] Updating release notes for 4.27.5 --- release_notes.md | 15 +++------------ 1 file changed, 3 insertions(+), 12 deletions(-) diff --git a/release_notes.md b/release_notes.md index 60f1211c12..e051c95d77 100644 --- a/release_notes.md +++ b/release_notes.md @@ -4,17 +4,8 @@ - My change description (#PR) --> - Update Python Worker Version to [4.20.0](https://github.com/Azure/azure-functions-python-worker/releases/tag/4.20.0) -- Update Java Worker Version to [2.13.0](https://github.com/Azure/azure-functions-java-worker/releases/tag/2.13.0) -- Update WebJobsScriptHostService to remove hardcoded sleep during application shut down (#9520) -- Update PowerShell 7.2 Worker Version to [4.0.2974](https://github.com/Azure/azure-functions-powershell-worker/releases/tag/v4.0.2974) -- Update PowerShell 7.0 Worker Version to [4.0.2973](https://github.com/Azure/azure-functions-powershell-worker/releases/tag/v4.0.2973) -- Add support for standalone executable (ie: `dotnet build --standalone`) for out-of-proc workers in Linux Consumption. (#9550) -- Bug fix: Do not restart worker channels or JobHost when an API request is made to get or update the function metadata (unless the config was changed) (#9510) - - This fixes a bug where requests to 'admin/functions' lead to a "Did not find initialized workers" error when - worker indexing is enabled. -- Bug fix: If there are no channels created and the host is running, restart the JobHost instead of shutting down worker channels (#9510) - - This fixes a bug with worker indexing where we are shutting down worker channels and creating a new channel that never - gets properly initialized as the invocation buffers are not created - this leads to a "Did not find initialized workers" error. -- Check if a blob container or table exists before trying to create it (#9555) +- Increased maximum HTTP request content size to 210000000 Bytes (~200MB) +- Update Node.js Worker Version to [3.8.1](https://github.com/Azure/azure-functions-nodejs-worker/releases/tag/v3.8.1) +- Update PowerShell 7.4 Worker Version to [4.0.2930](https://github.com/Azure/azure-functions-powershell-worker/releases/tag/v4.0.2930) - Limit dotnet-isolated specialization to 64 bit host process (#9548) - Sending command line arguments to language workers with `functions-` prefix to prevent conflicts (#9514) From 377632eaf2e0c480f729e39565b11a7e70c13bcc Mon Sep 17 00:00:00 2001 From: Francisco Gamino Date: Tue, 12 Sep 2023 19:53:26 -0700 Subject: [PATCH 6/7] Update PowerShell Language Workers 7.0 (to version 4.0.2973), 7.2 (to version 4.0.2974), and 7.4 (to version 4.0.2975) (#9528) * Upgrade PowerShell language worker 7.4 to 4.0.2975 * Upgrade PowerShell language worker 7.2 to 4.0.2974 * Upgrade PowerShell language worker 7.0 to 4.0.2973 * Update release notes --- release_notes.md | 5 ++++- src/WebJobs.Script/WebJobs.Script.csproj | 6 +++--- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/release_notes.md b/release_notes.md index e051c95d77..507682dcec 100644 --- a/release_notes.md +++ b/release_notes.md @@ -6,6 +6,9 @@ - Update Python Worker Version to [4.20.0](https://github.com/Azure/azure-functions-python-worker/releases/tag/4.20.0) - Increased maximum HTTP request content size to 210000000 Bytes (~200MB) - Update Node.js Worker Version to [3.8.1](https://github.com/Azure/azure-functions-nodejs-worker/releases/tag/v3.8.1) -- Update PowerShell 7.4 Worker Version to [4.0.2930](https://github.com/Azure/azure-functions-powershell-worker/releases/tag/v4.0.2930) +- Update PowerShell 7.4 Worker Version to [4.0.2975](https://github.com/Azure/azure-functions-powershell-worker/releases/tag/v4.0.2975) +- Update PowerShell 7.2 Worker Version to [4.0.2974](https://github.com/Azure/azure-functions-powershell-worker/releases/tag/v4.0.2974) +- Update PowerShell 7.0 Worker Version to [4.0.2973](https://github.com/Azure/azure-functions-powershell-worker/releases/tag/v4.0.2973) - Limit dotnet-isolated specialization to 64 bit host process (#9548) - Sending command line arguments to language workers with `functions-` prefix to prevent conflicts (#9514) +- Update WebJobsScriptHostService to remove hardcoded sleep during application shut down (#9520) diff --git a/src/WebJobs.Script/WebJobs.Script.csproj b/src/WebJobs.Script/WebJobs.Script.csproj index bc2626083d..0ecab0a513 100644 --- a/src/WebJobs.Script/WebJobs.Script.csproj +++ b/src/WebJobs.Script/WebJobs.Script.csproj @@ -59,9 +59,9 @@ - - - + + + From f07a1177700ab8e687f16cbaa2b2bc1d6b5ee160 Mon Sep 17 00:00:00 2001 From: Shreyas Gopalakrishna <11889130+shreyas-gopalakrishna@users.noreply.github.com> Date: Mon, 18 Sep 2023 17:10:14 -0500 Subject: [PATCH 7/7] Update Java Worker Version to 2.13.0 (#9544) * Update Java Worker Version to 2.13.0 * Update release_notes.md for Java worker --------- Co-authored-by: AzureFunctionsJava --- release_notes.md | 1 + src/WebJobs.Script/WebJobs.Script.csproj | 2 +- .../WebJobs.Script.Tests.Integration.csproj | 2 +- 3 files changed, 3 insertions(+), 2 deletions(-) diff --git a/release_notes.md b/release_notes.md index 507682dcec..b39d1d8dd7 100644 --- a/release_notes.md +++ b/release_notes.md @@ -4,6 +4,7 @@ - My change description (#PR) --> - Update Python Worker Version to [4.20.0](https://github.com/Azure/azure-functions-python-worker/releases/tag/4.20.0) +- Update Java Worker Version to [2.13.0](https://github.com/Azure/azure-functions-java-worker/releases/tag/2.13.0) - Increased maximum HTTP request content size to 210000000 Bytes (~200MB) - Update Node.js Worker Version to [3.8.1](https://github.com/Azure/azure-functions-nodejs-worker/releases/tag/v3.8.1) - Update PowerShell 7.4 Worker Version to [4.0.2975](https://github.com/Azure/azure-functions-powershell-worker/releases/tag/v4.0.2975) diff --git a/src/WebJobs.Script/WebJobs.Script.csproj b/src/WebJobs.Script/WebJobs.Script.csproj index 0ecab0a513..7c00814776 100644 --- a/src/WebJobs.Script/WebJobs.Script.csproj +++ b/src/WebJobs.Script/WebJobs.Script.csproj @@ -57,7 +57,7 @@ - + diff --git a/test/WebJobs.Script.Tests.Integration/WebJobs.Script.Tests.Integration.csproj b/test/WebJobs.Script.Tests.Integration/WebJobs.Script.Tests.Integration.csproj index 72548c0bce..48052e5676 100644 --- a/test/WebJobs.Script.Tests.Integration/WebJobs.Script.Tests.Integration.csproj +++ b/test/WebJobs.Script.Tests.Integration/WebJobs.Script.Tests.Integration.csproj @@ -44,7 +44,7 @@ - +