diff --git a/src/WebJobs.Script.Grpc/Channel/GrpcWorkerChannel.cs b/src/WebJobs.Script.Grpc/Channel/GrpcWorkerChannel.cs index 8b832fe7a5..a06d6c1164 100644 --- a/src/WebJobs.Script.Grpc/Channel/GrpcWorkerChannel.cs +++ b/src/WebJobs.Script.Grpc/Channel/GrpcWorkerChannel.cs @@ -545,11 +545,16 @@ internal Task> SendFunctionMetadataRequest() return _functionsIndexingTask.Task; } - // parse metadata response into RawFunctionMetadata objects for WorkerFunctionMetadataProvider to further parse and validate + // parse metadata response into RawFunctionMetadata objects for AggregateFunctionMetadataProvider to further parse and validate internal void ProcessFunctionMetadataResponses(FunctionMetadataResponse functionMetadataResponse) { _workerChannelLogger.LogDebug("Received the worker function metadata response from worker {worker_id}", _workerId); + if (functionMetadataResponse.Result.IsFailure(out Exception metadataResponseEx)) + { + _workerChannelLogger?.LogError(metadataResponseEx, "Worker failed to index functions"); + } + var functions = new List(); if (functionMetadataResponse.UseDefaultMetadataIndexing == false) diff --git a/src/WebJobs.Script/Host/AggregateFunctionMetadataProvider.cs b/src/WebJobs.Script/Host/AggregateFunctionMetadataProvider.cs index 5a0eb16de6..8951cbe736 100644 --- a/src/WebJobs.Script/Host/AggregateFunctionMetadataProvider.cs +++ b/src/WebJobs.Script/Host/AggregateFunctionMetadataProvider.cs @@ -81,7 +81,7 @@ public async Task> GetFunctionMetadataAsync(IEn await _dispatcher.FinishInitialization(functions); // Validate if the app has functions in legacy format and add in logs to inform about the mixed app - _ = Task.Delay(TimeSpan.FromMinutes(1)).ContinueWith(t => ValidateFunctionAppFormat(_scriptOptions.Value.RootScriptPath, _logger)); + _ = Task.Delay(TimeSpan.FromMinutes(1)).ContinueWith(t => ValidateFunctionAppFormat(_scriptOptions.Value.RootScriptPath, _logger, environment)); } else { @@ -93,7 +93,7 @@ public async Task> GetFunctionMetadataAsync(IEn return _functions; } - internal static void ValidateFunctionAppFormat(string scriptPath, ILogger logger, IFileSystem fileSystem = null) + internal static void ValidateFunctionAppFormat(string scriptPath, ILogger logger, IEnvironment environment, IFileSystem fileSystem = null) { fileSystem = fileSystem ?? FileUtility.Instance; bool mixedApp = false; @@ -114,7 +114,16 @@ internal static void ValidateFunctionAppFormat(string scriptPath, ILogger logger if (mixedApp) { - logger.Log(LogLevel.Information, $"Detected mixed function app. Some functions may not be indexed - {legacyFormatFunctions}"); + string logMessage = $"Detected mixed function app. Some functions may not be indexed - {legacyFormatFunctions}"; + + if (environment.IsCoreTools()) + { + logger.Log(LogLevel.Warning, logMessage + " Refer to the documentation to filter warning - https://docs.microsoft.com/en-us/azure/azure-functions/configure-monitoring?tabs=v2"); + } + else + { + logger.Log(LogLevel.Information, logMessage); + } } } } diff --git a/src/WebJobs.Script/Utility.cs b/src/WebJobs.Script/Utility.cs index f4cf407507..7a00c2de93 100644 --- a/src/WebJobs.Script/Utility.cs +++ b/src/WebJobs.Script/Utility.cs @@ -906,17 +906,21 @@ public static void ValidateRetryOptions(RetryOptions public static bool CanWorkerIndex(IEnumerable workerConfigs, IEnvironment environment) { + if (!FeatureFlags.IsEnabled(ScriptConstants.FeatureFlagEnableWorkerIndexing, environment)) + { + return false; + } + var workerRuntime = environment.GetEnvironmentVariable(EnvironmentSettingNames.FunctionWorkerRuntime); if (workerConfigs != null) { var workerConfig = workerConfigs.Where(c => c.Description != null && c.Description.Language != null && c.Description.Language.Equals(workerRuntime, StringComparison.InvariantCultureIgnoreCase)).FirstOrDefault(); // if feature flag is enabled and workerConfig.WorkerIndexing == true, then return true - if (FeatureFlags.IsEnabled(ScriptConstants.FeatureFlagEnableWorkerIndexing, environment) - && (workerConfig != null + if (workerConfig != null && workerConfig.Description != null && workerConfig.Description.WorkerIndexing != null - && workerConfig.Description.WorkerIndexing.Equals("true", StringComparison.OrdinalIgnoreCase))) + && workerConfig.Description.WorkerIndexing.Equals("true", StringComparison.OrdinalIgnoreCase)) { return true; } diff --git a/test/WebJobs.Script.Tests/AggregateFunctionMetadataProviderTests.cs b/test/WebJobs.Script.Tests/AggregateFunctionMetadataProviderTests.cs index f8570c1524..94e30f3964 100644 --- a/test/WebJobs.Script.Tests/AggregateFunctionMetadataProviderTests.cs +++ b/test/WebJobs.Script.Tests/AggregateFunctionMetadataProviderTests.cs @@ -222,7 +222,8 @@ public void ValidateFunctionAppFormat_InputMixedApp() { _logger.ClearLogMessages(); string scriptPath = Path.Combine(Environment.CurrentDirectory, @"..", "..", "..", "..", "..", "sample", "node"); - AggregateFunctionMetadataProvider.ValidateFunctionAppFormat(scriptPath, _logger); + var environment = SystemEnvironment.Instance; + AggregateFunctionMetadataProvider.ValidateFunctionAppFormat(scriptPath, _logger, environment); var traces = _logger.GetLogMessages(); var functionLoadLogs = traces.Where(m => m.FormattedMessage.Contains("Detected mixed function app. Some functions may not be indexed")); Assert.True(functionLoadLogs.Any()); diff --git a/test/WebJobs.Script.Tests/Workers/Rpc/GrpcWorkerChannelTests.cs b/test/WebJobs.Script.Tests/Workers/Rpc/GrpcWorkerChannelTests.cs index 65ce8bc6c2..5afea03823 100644 --- a/test/WebJobs.Script.Tests/Workers/Rpc/GrpcWorkerChannelTests.cs +++ b/test/WebJobs.Script.Tests/Workers/Rpc/GrpcWorkerChannelTests.cs @@ -588,6 +588,15 @@ public void ReceivesInboundEvent_Failed_FunctionMetadataResponse() Assert.True(traces.Any(m => string.Equals(m.FormattedMessage, $"Worker failed to index function {functionId}"))); } + [Fact] + public void ReceivesInboundEvent_Failed_OverallFunctionMetadataResponse() + { + var functions = _workerChannel.GetFunctionMetadata(); + _testFunctionRpcService.PublishWorkerMetadataResponse("TestFunctionId1", null, null, false, false, false); + var traces = _logger.GetLogMessages(); + Assert.True(traces.Any(m => string.Equals(m.FormattedMessage, $"Worker failed to index functions"))); + } + [Fact] public void FunctionLoadRequest_IsExpected() { diff --git a/test/WebJobs.Script.Tests/Workers/Rpc/TestFunctionRpcService.cs b/test/WebJobs.Script.Tests/Workers/Rpc/TestFunctionRpcService.cs index 7ddf91527b..86d8e481e1 100644 --- a/test/WebJobs.Script.Tests/Workers/Rpc/TestFunctionRpcService.cs +++ b/test/WebJobs.Script.Tests/Workers/Rpc/TestFunctionRpcService.cs @@ -200,7 +200,7 @@ public void PublishStartStreamEvent(string workerId) _eventManager.Publish(new InboundGrpcEvent(_workerId, responseMessage)); } - public void PublishWorkerMetadataResponse(string workerId, string functionId, IEnumerable functionMetadata, bool successful, bool useDefaultMetadataIndexing = false) + public void PublishWorkerMetadataResponse(string workerId, string functionId, IEnumerable functionMetadata, bool successful, bool useDefaultMetadataIndexing = false, bool overallStatus = true) { StatusResult statusResult = new StatusResult(); if (successful) @@ -215,19 +215,27 @@ public void PublishWorkerMetadataResponse(string workerId, string functionId, IE FunctionMetadataResponse overallResponse = new FunctionMetadataResponse(); overallResponse.UseDefaultMetadataIndexing = useDefaultMetadataIndexing; - foreach (FunctionMetadata response in functionMetadata) + if (functionMetadata != null) { - RpcFunctionMetadata indexingResponse = new RpcFunctionMetadata() + foreach (FunctionMetadata response in functionMetadata) { - Name = response.Name, - Language = response.Language, - Status = statusResult, - FunctionId = functionId - }; - - overallResponse.FunctionMetadataResults.Add(indexingResponse); + RpcFunctionMetadata indexingResponse = new RpcFunctionMetadata() + { + Name = response.Name, + Language = response.Language, + Status = statusResult, + FunctionId = functionId + }; + + overallResponse.FunctionMetadataResults.Add(indexingResponse); + } } + overallResponse.Result = new StatusResult() + { + Status = overallStatus == true ? StatusResult.Types.Status.Success : StatusResult.Types.Status.Failure + }; + StreamingMessage responseMessage = new StreamingMessage() { FunctionMetadataResponse = overallResponse