Skip to content

Commit 3a8d307

Browse files
Refactor code to move the current logic to search for WorkerConfigs to a default worker configuration resolver (#11229)
1 parent f6cdf90 commit 3a8d307

21 files changed

+671
-86
lines changed

release_notes.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,4 +6,4 @@
66
- Add JitTrace Files for v4.1042
77
- Updating OTel related nuget packages (#11216)
88
- Moving to version 1.5.7 of Microsoft.Azure.AppService.Middleware.Functions (https://github.com/Azure/azure-functions-host/pull/11231)
9-
9+
- Refactor code to move the logic to search for WorkerConfigs to a default worker configuration resolver (#11219)

src/WebJobs.Script.WebHost/WebHostServiceCollectionExtensions.cs

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,9 @@
2929
using Microsoft.Azure.WebJobs.Script.WebHost.Security.Authorization.Policies;
3030
using Microsoft.Azure.WebJobs.Script.WebHost.Standby;
3131
using Microsoft.Azure.WebJobs.Script.Workers.FunctionDataCache;
32+
using Microsoft.Azure.WebJobs.Script.Workers.Profiles;
3233
using Microsoft.Azure.WebJobs.Script.Workers.Rpc;
34+
using Microsoft.Azure.WebJobs.Script.Workers.Rpc.Configuration;
3335
using Microsoft.Azure.WebJobs.Script.Workers.SharedMemoryDataTransfer;
3436
using Microsoft.Extensions.Azure;
3537
using Microsoft.Extensions.Configuration;
@@ -228,9 +230,11 @@ public static void AddWebJobsScriptHost(this IServiceCollection services, IConfi
228230
services.AddHostingConfigOptions(configuration);
229231
services.ConfigureOptions<ExtensionRequirementOptionsSetup>();
230232

231-
// Refresh LanguageWorkerOptions when HostBuiltChangeTokenSource is triggered.
233+
// Refresh WorkerConfigurationResolverOptions and LanguageWorkerOptions when HostBuiltChangeTokenSource is triggered.
234+
services.ConfigureOptionsWithChangeTokenSource<WorkerConfigurationResolverOptions, WorkerConfigurationResolverOptionsSetup, HostBuiltChangeTokenSource<WorkerConfigurationResolverOptions>>();
232235
services.ConfigureOptionsWithChangeTokenSource<LanguageWorkerOptions, LanguageWorkerOptionsSetup, HostBuiltChangeTokenSource<LanguageWorkerOptions>>();
233236

237+
services.AddSingleton<IWorkerConfigurationResolver, DefaultWorkerConfigurationResolver>();
234238
services.TryAddSingleton<IDependencyValidator, DependencyValidator>();
235239
services.TryAddSingleton<IJobHostMiddlewarePipeline>(s => DefaultMiddlewarePipeline.Empty);
236240

src/WebJobs.Script.WebHost/WebJobsScriptHostService.cs

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@
3030
using Microsoft.Azure.WebJobs.Script.WebHost.Diagnostics.Extensions;
3131
using Microsoft.Azure.WebJobs.Script.Workers;
3232
using Microsoft.Azure.WebJobs.Script.Workers.Rpc;
33+
using Microsoft.Azure.WebJobs.Script.Workers.Rpc.Configuration;
3334
using Microsoft.Extensions.Configuration;
3435
using Microsoft.Extensions.DependencyInjection;
3536
using Microsoft.Extensions.Hosting;
@@ -66,6 +67,7 @@ public class WebJobsScriptHostService : IHostedService, IScriptHostManager, ISer
6667
private readonly bool _originalStandbyModeValue;
6768
private readonly string _originalFunctionsWorkerRuntime;
6869
private readonly string _originalFunctionsWorkerRuntimeVersion;
70+
private readonly IOptionsChangeTokenSource<WorkerConfigurationResolverOptions> _workerConfigResolverOptionsChangeTokenSource;
6971
private readonly IOptionsChangeTokenSource<LanguageWorkerOptions> _languageWorkerOptionsChangeTokenSource;
7072

7173
// we're only using this dictionary's keys so it acts as a "ConcurrentHashSet"
@@ -89,7 +91,8 @@ public WebJobsScriptHostService(IOptionsMonitor<ScriptApplicationHostOptions> ap
8991
HostPerformanceManager hostPerformanceManager, IOptions<HostHealthMonitorOptions> healthMonitorOptions,
9092
IMetricsLogger metricsLogger, IApplicationLifetime applicationLifetime, IConfiguration config, IScriptEventManager eventManager, IHostMetrics hostMetrics,
9193
IOptions<FunctionsHostingConfigOptions> hostingConfigOptions,
92-
IOptionsChangeTokenSource<LanguageWorkerOptions> languageWorkerOptionsChangeTokenSource)
94+
IOptionsChangeTokenSource<LanguageWorkerOptions> languageWorkerOptionsChangeTokenSource,
95+
IOptionsChangeTokenSource<WorkerConfigurationResolverOptions> workerConfigResolverOptionsChangeTokenSource)
9396
{
9497
ArgumentNullException.ThrowIfNull(loggerFactory);
9598

@@ -100,6 +103,7 @@ public WebJobsScriptHostService(IOptionsMonitor<ScriptApplicationHostOptions> ap
100103
RegisterApplicationLifetimeEvents();
101104

102105
_metricsLogger = metricsLogger;
106+
_workerConfigResolverOptionsChangeTokenSource = workerConfigResolverOptionsChangeTokenSource ?? throw new ArgumentNullException(nameof(workerConfigResolverOptionsChangeTokenSource));
103107
_languageWorkerOptionsChangeTokenSource = languageWorkerOptionsChangeTokenSource ?? throw new ArgumentNullException(nameof(languageWorkerOptionsChangeTokenSource));
104108
_applicationHostOptions = applicationHostOptions ?? throw new ArgumentNullException(nameof(applicationHostOptions));
105109
_scriptWebHostEnvironment = scriptWebHostEnvironment ?? throw new ArgumentNullException(nameof(scriptWebHostEnvironment));
@@ -378,6 +382,11 @@ private async Task UnsynchronizedStartHostAsync(ScriptHostStartupOperation activ
378382
}
379383
}
380384

385+
if (_workerConfigResolverOptionsChangeTokenSource is HostBuiltChangeTokenSource<WorkerConfigurationResolverOptions> { } hostBuiltChangeTokenResolverOptions)
386+
{
387+
hostBuiltChangeTokenResolverOptions.TriggerChange();
388+
}
389+
381390
if (_languageWorkerOptionsChangeTokenSource is HostBuiltChangeTokenSource<LanguageWorkerOptions> { } hostBuiltChangeTokenSource)
382391
{
383392
hostBuiltChangeTokenSource.TriggerChange();

src/WebJobs.Script/Diagnostics/Extensions/LoggerExtension.cs

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -216,6 +216,11 @@ internal static class LoggerExtension
216216
new EventId(342, nameof(OutdatedExtensionBundle)),
217217
"Your current bundle version {currentVersion} has reached end of support on Aug 4, 2026. Upgrade to [{suggestedMinVersion}.*, {suggestedMaxVersion}.0.0). For more information, see https://aka.ms/FunctionsBundlesUpgrade");
218218

219+
private static readonly Action<ILogger, string, Exception> _defaultWorkersDirectoryPath =
220+
LoggerMessage.Define<string>(LogLevel.Debug,
221+
new EventId(343, nameof(DefaultWorkersDirectoryPath)),
222+
"Workers Directory set to: {workersDirPath}");
223+
219224
public static void PublishingMetrics(this ILogger logger, string metrics)
220225
{
221226
_publishingMetrics(logger, metrics, null);
@@ -418,6 +423,11 @@ public static void IncorrectAzureFunctionsFolderPath(this ILogger logger, string
418423
_incorrectAzureFunctionsFolderPath(logger, path, EnvironmentSettingNames.FunctionWorkerRuntime, null);
419424
}
420425

426+
public static void DefaultWorkersDirectoryPath(this ILogger logger, string workersDirPath)
427+
{
428+
_defaultWorkersDirectoryPath(logger, workersDirPath, null);
429+
}
430+
421431
public static void OutdatedExtensionBundle(this ILogger logger, string currentVersion, int suggestedMinVersion, int suggestedMaxVersion)
422432
{
423433
var currentTime = DateTime.UtcNow;

src/WebJobs.Script/ScriptConstants.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,7 @@ public static class ScriptConstants
6363
public const string LogCategoryHost = "Host";
6464
public const string LogCategoryFunction = "Function";
6565
public const string LogCategoryWorker = "Worker";
66+
public const string LogCategoryWorkerConfig = "Host.LanguageWorkerConfig";
6667

6768
public const string SkipHostJsonConfigurationKey = "MS_SkipHostJsonConfiguration";
6869
public const string SkipHostInitializationKey = "MS_SkipHostInitialization";

src/WebJobs.Script/ScriptHostBuilderExtensions.cs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@
3333
using Microsoft.Azure.WebJobs.Script.Workers.Http;
3434
using Microsoft.Azure.WebJobs.Script.Workers.Profiles;
3535
using Microsoft.Azure.WebJobs.Script.Workers.Rpc;
36+
using Microsoft.Azure.WebJobs.Script.Workers.Rpc.Configuration;
3637
using Microsoft.Extensions.Configuration;
3738
using Microsoft.Extensions.DependencyInjection;
3839
using Microsoft.Extensions.DependencyInjection.Extensions;
@@ -346,6 +347,7 @@ public static IHostBuilder AddScriptHostCore(this IHostBuilder builder, ScriptAp
346347
}
347348
else
348349
{
350+
services.ConfigureOptions<WorkerConfigurationResolverOptionsSetup>();
349351
services.ConfigureOptions<LanguageWorkerOptionsSetup>();
350352
AddCommonServices(services);
351353
}
Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
// Copyright (c) .NET Foundation. All rights reserved.
2+
// Licensed under the MIT License. See License.txt in the project root for license information.
3+
4+
using System;
5+
using System.Collections.Generic;
6+
using System.IO.Abstractions;
7+
using Microsoft.Azure.WebJobs.Script.Diagnostics.Extensions;
8+
using Microsoft.Extensions.Logging;
9+
using Microsoft.Extensions.Options;
10+
11+
namespace Microsoft.Azure.WebJobs.Script.Workers.Rpc.Configuration
12+
{
13+
// This class resolves worker configurations by scanning the "workers" directory within the Host for worker config files.
14+
internal sealed class DefaultWorkerConfigurationResolver : IWorkerConfigurationResolver
15+
{
16+
private readonly ILogger _logger;
17+
private readonly IOptionsMonitor<WorkerConfigurationResolverOptions> _workerConfigurationResolverOptions;
18+
private readonly IFileSystem _fileSystem;
19+
20+
public DefaultWorkerConfigurationResolver(ILoggerFactory loggerFactory,
21+
IFileSystem fileSystem,
22+
IOptionsMonitor<WorkerConfigurationResolverOptions> workerConfigurationResolverOptions)
23+
{
24+
ArgumentNullException.ThrowIfNull(loggerFactory);
25+
_logger = loggerFactory.CreateLogger(ScriptConstants.LogCategoryWorkerConfig);
26+
_workerConfigurationResolverOptions = workerConfigurationResolverOptions ?? throw new ArgumentNullException(nameof(workerConfigurationResolverOptions));
27+
_fileSystem = fileSystem ?? throw new ArgumentNullException(nameof(fileSystem));
28+
}
29+
30+
public WorkerConfigurationInfo GetConfigurationInfo()
31+
{
32+
var workersRootDirPath = _workerConfigurationResolverOptions.CurrentValue.WorkersRootDirPath;
33+
_logger.DefaultWorkersDirectoryPath(workersRootDirPath);
34+
35+
var workerConfigPaths = new List<string>();
36+
37+
foreach (var workerDir in _fileSystem.Directory.EnumerateDirectories(workersRootDirPath))
38+
{
39+
string workerConfigPath = _fileSystem.Path.Combine(workerDir, RpcWorkerConstants.WorkerConfigFileName);
40+
41+
if (_fileSystem.File.Exists(workerConfigPath))
42+
{
43+
workerConfigPaths.Add(workerDir);
44+
}
45+
}
46+
47+
return new WorkerConfigurationInfo(_workerConfigurationResolverOptions.CurrentValue.WorkersRootDirPath, workerConfigPaths);
48+
}
49+
}
50+
}

src/WebJobs.Script/Workers/Rpc/Configuration/LanguageWorkerOptionsSetup.cs

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
// Copyright (c) .NET Foundation. All rights reserved.
1+
// Copyright (c) .NET Foundation. All rights reserved.
22
// Licensed under the MIT License. See License.txt in the project root for license information.
33

44
using System;
@@ -21,13 +21,15 @@ internal class LanguageWorkerOptionsSetup : IConfigureOptions<LanguageWorkerOpti
2121
private readonly IMetricsLogger _metricsLogger;
2222
private readonly IWorkerProfileManager _workerProfileManager;
2323
private readonly IScriptHostManager _scriptHostManager;
24+
private readonly IWorkerConfigurationResolver _workerConfigurationResolver;
2425

2526
public LanguageWorkerOptionsSetup(IConfiguration configuration,
2627
ILoggerFactory loggerFactory,
2728
IEnvironment environment,
2829
IMetricsLogger metricsLogger,
2930
IWorkerProfileManager workerProfileManager,
30-
IScriptHostManager scriptHostManager)
31+
IScriptHostManager scriptHostManager,
32+
IWorkerConfigurationResolver workerConfigurationResolver)
3133
{
3234
if (loggerFactory is null)
3335
{
@@ -39,8 +41,9 @@ public LanguageWorkerOptionsSetup(IConfiguration configuration,
3941
_environment = environment ?? throw new ArgumentNullException(nameof(environment));
4042
_metricsLogger = metricsLogger ?? throw new ArgumentNullException(nameof(metricsLogger));
4143
_workerProfileManager = workerProfileManager ?? throw new ArgumentNullException(nameof(workerProfileManager));
44+
_workerConfigurationResolver = workerConfigurationResolver ?? throw new ArgumentNullException(nameof(workerConfigurationResolver));
4245

43-
_logger = loggerFactory.CreateLogger("Host.LanguageWorkerConfig");
46+
_logger = loggerFactory.CreateLogger(ScriptConstants.LogCategoryWorkerConfig);
4447
}
4548

4649
public void Configure(LanguageWorkerOptions options)
@@ -72,7 +75,7 @@ public void Configure(LanguageWorkerOptions options)
7275
}
7376
}
7477

75-
var configFactory = new RpcWorkerConfigFactory(configuration, _logger, SystemRuntimeInformation.Instance, _environment, _metricsLogger, _workerProfileManager);
78+
var configFactory = new RpcWorkerConfigFactory(configuration, _logger, SystemRuntimeInformation.Instance, _environment, _metricsLogger, _workerProfileManager, _workerConfigurationResolver);
7679
options.WorkerConfigs = configFactory.GetConfigs();
7780
}
7881
}

src/WebJobs.Script/Workers/Rpc/Configuration/RpcWorkerConfigFactory.cs

Lines changed: 18 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
// Copyright (c) .NET Foundation. All rights reserved.
1+
// Copyright (c) .NET Foundation. All rights reserved.
22
// Licensed under the MIT License. See License.txt in the project root for license information.
33

44
using System;
@@ -11,6 +11,7 @@
1111
using System.Threading.Tasks;
1212
using Microsoft.Azure.WebJobs.Script.Diagnostics;
1313
using Microsoft.Azure.WebJobs.Script.Workers.Profiles;
14+
using Microsoft.Azure.WebJobs.Script.Workers.Rpc.Configuration;
1415
using Microsoft.Extensions.Configuration;
1516
using Microsoft.Extensions.Logging;
1617

@@ -26,6 +27,7 @@ internal class RpcWorkerConfigFactory
2627
private readonly IMetricsLogger _metricsLogger;
2728
private readonly string _workerRuntime;
2829
private readonly IEnvironment _environment;
30+
private readonly IWorkerConfigurationResolver _workerConfigurationResolver;
2931
private readonly JsonSerializerOptions _jsonSerializerOptions = new()
3032
{
3133
PropertyNameCaseInsensitive = true
@@ -38,7 +40,8 @@ public RpcWorkerConfigFactory(IConfiguration config,
3840
ISystemRuntimeInformation systemRuntimeInfo,
3941
IEnvironment environment,
4042
IMetricsLogger metricsLogger,
41-
IWorkerProfileManager workerProfileManager)
43+
IWorkerProfileManager workerProfileManager,
44+
IWorkerConfigurationResolver workerConfigurationResolver)
4245
{
4346
_config = config ?? throw new ArgumentNullException(nameof(config));
4447
_logger = logger ?? throw new ArgumentNullException(nameof(logger));
@@ -47,18 +50,9 @@ public RpcWorkerConfigFactory(IConfiguration config,
4750
_metricsLogger = metricsLogger ?? throw new ArgumentNullException(nameof(metricsLogger));
4851
_profileManager = workerProfileManager ?? throw new ArgumentNullException(nameof(workerProfileManager));
4952
_workerRuntime = _environment.GetEnvironmentVariable(RpcWorkerConstants.FunctionWorkerRuntimeSettingName);
50-
51-
WorkersDirPath = GetDefaultWorkersDirectory(Directory.Exists);
52-
var workersDirectorySection = _config.GetSection($"{RpcWorkerConstants.LanguageWorkersSectionName}:{WorkerConstants.WorkersDirectorySectionName}");
53-
54-
if (!string.IsNullOrEmpty(workersDirectorySection.Value))
55-
{
56-
WorkersDirPath = workersDirectorySection.Value;
57-
}
53+
_workerConfigurationResolver = workerConfigurationResolver ?? throw new ArgumentNullException(nameof(workerConfigurationResolver));
5854
}
5955

60-
public string WorkersDirPath { get; }
61-
6256
public IList<RpcWorkerConfig> GetConfigs()
6357
{
6458
using (_metricsLogger.LatencyEvent(MetricEventNames.GetConfigs))
@@ -68,41 +62,25 @@ public IList<RpcWorkerConfig> GetConfigs()
6862
}
6963
}
7064

71-
internal static string GetDefaultWorkersDirectory(Func<string, bool> directoryExists)
72-
{
73-
#pragma warning disable SYSLIB0012 // Type or member is obsolete
74-
string assemblyLocalPath = Path.GetDirectoryName(new Uri(typeof(RpcWorkerConfigFactory).Assembly.CodeBase).LocalPath);
75-
#pragma warning restore SYSLIB0012 // Type or member is obsolete
76-
string workersDirPath = Path.Combine(assemblyLocalPath, RpcWorkerConstants.DefaultWorkersDirectoryName);
77-
if (!directoryExists(workersDirPath))
78-
{
79-
// Site Extension. Default to parent directory
80-
workersDirPath = Path.Combine(Directory.GetParent(assemblyLocalPath).FullName, RpcWorkerConstants.DefaultWorkersDirectoryName);
81-
}
82-
return workersDirPath;
83-
}
84-
8565
internal void BuildWorkerProviderDictionary()
8666
{
87-
AddProviders();
88-
AddProvidersFromAppSettings();
67+
var workerConfigurationInfo = _workerConfigurationResolver.GetConfigurationInfo();
68+
69+
AddProviders(workerConfigurationInfo);
70+
AddProvidersFromAppSettings(workerConfigurationInfo);
8971
}
9072

91-
internal void AddProviders()
73+
internal void AddProviders(WorkerConfigurationInfo workerConfigurationInfo)
9274
{
93-
_logger.LogDebug("Workers Directory set to: {WorkersDirPath}", WorkersDirPath);
75+
var workerConfigs = workerConfigurationInfo.WorkerConfigPaths;
9476

95-
foreach (var workerDir in Directory.EnumerateDirectories(WorkersDirPath))
77+
foreach (var workerConfig in workerConfigs)
9678
{
97-
string workerConfigPath = Path.Combine(workerDir, RpcWorkerConstants.WorkerConfigFileName);
98-
if (File.Exists(workerConfigPath))
99-
{
100-
AddProvider(workerDir);
101-
}
79+
AddProvider(workerConfig, workerConfigurationInfo.WorkersRootDirPath);
10280
}
10381
}
10482

105-
internal void AddProvidersFromAppSettings()
83+
internal void AddProvidersFromAppSettings(WorkerConfigurationInfo workerConfigurationInfo)
10684
{
10785
var languagesSection = _config.GetSection($"{RpcWorkerConstants.LanguageWorkersSectionName}");
10886
foreach (var languageSection in languagesSection.GetChildren())
@@ -111,12 +89,12 @@ internal void AddProvidersFromAppSettings()
11189
if (workerDirectorySection.Value != null)
11290
{
11391
_workerDescriptionDictionary.Remove(languageSection.Key);
114-
AddProvider(workerDirectorySection.Value);
92+
AddProvider(workerDirectorySection.Value, workerConfigurationInfo.WorkersRootDirPath);
11593
}
11694
}
11795
}
11896

119-
internal void AddProvider(string workerDir)
97+
internal void AddProvider(string workerDir, string workersRootDirPath)
12098
{
12199
using (_metricsLogger.LatencyEvent(string.Format(MetricEventNames.AddProvider, workerDir)))
122100
{
@@ -128,7 +106,7 @@ internal void AddProvider(string workerDir)
128106
string workerRuntime = Path.GetFileName(workerDir);
129107
// Only skip worker directories that don't match the current runtime.
130108
// Do not skip non-worker directories like the function app payload directory
131-
if (!workerRuntime.Equals(_workerRuntime, StringComparison.OrdinalIgnoreCase) && workerDir.StartsWith(WorkersDirPath))
109+
if (!workerRuntime.Equals(_workerRuntime, StringComparison.OrdinalIgnoreCase) && workerDir.StartsWith(workersRootDirPath))
132110
{
133111
return;
134112
}

0 commit comments

Comments
 (0)