Skip to content

Commit f9c7827

Browse files
authored
Setting up workerConfig with ConfigureOptionsWithChangeTokenSource (#9264)
1 parent 711e7b2 commit f9c7827

File tree

11 files changed

+93
-37
lines changed

11 files changed

+93
-37
lines changed

src/WebJobs.Script.WebHost/WebHostServiceCollectionExtensions.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -204,7 +204,7 @@ public static void AddWebJobsScriptHost(this IServiceCollection services, IConfi
204204
services.AddSingleton<IOptionsChangeTokenSource<ScriptApplicationHostOptions>, ScriptApplicationHostOptionsChangeTokenSource>();
205205

206206
services.ConfigureOptions<StandbyOptionsSetup>();
207-
services.ConfigureOptions<LanguageWorkerOptionsSetup>();
207+
services.ConfigureOptionsWithChangeTokenSource<LanguageWorkerOptions, LanguageWorkerOptionsSetup, SpecializationChangeTokenSource<LanguageWorkerOptions>>();
208208
services.ConfigureOptionsWithChangeTokenSource<AppServiceOptions, AppServiceOptionsSetup, SpecializationChangeTokenSource<AppServiceOptions>>();
209209
services.ConfigureOptionsWithChangeTokenSource<HttpBodyControlOptions, HttpBodyControlOptionsSetup, SpecializationChangeTokenSource<HttpBodyControlOptions>>();
210210
services.ConfigureOptions<FunctionsHostingConfigOptionsSetup>();

src/WebJobs.Script/DependencyInjection/ScriptStartupTypeLocator.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -89,7 +89,7 @@ public async Task<IEnumerable<Type>> GetExtensionsStartupTypesAsync()
8989
ExtensionBundleDetails bundleDetails = await _extensionBundleManager.GetExtensionBundleDetails();
9090
ValidateBundleRequirements(bundleDetails);
9191

92-
var functionMetadataCollection = _functionMetadataManager.GetFunctionMetadata(forceRefresh: true, includeCustomProviders: false);
92+
var functionMetadataCollection = _functionMetadataManager.GetFunctionMetadata(forceRefresh: true, includeCustomProviders: false, workerConfigs: workerConfigs);
9393
bindingsSet = new HashSet<string>(StringComparer.OrdinalIgnoreCase);
9494

9595
// Generate a Hashset of all the binding types used in the function app

src/WebJobs.Script/Host/FunctionMetadataManager.cs

Lines changed: 15 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -30,17 +30,15 @@ public class FunctionMetadataManager : IFunctionMetadataManager
3030
private bool _servicesReset = false;
3131
private ILogger _logger;
3232
private IOptions<ScriptJobHostOptions> _scriptOptions;
33-
private IOptionsMonitor<LanguageWorkerOptions> _languageWorkerOptions;
3433
private ImmutableArray<FunctionMetadata> _functionMetadataArray;
3534
private Dictionary<string, ICollection<string>> _functionErrors = new Dictionary<string, ICollection<string>>();
3635
private ConcurrentDictionary<string, FunctionMetadata> _functionMetadataMap = new ConcurrentDictionary<string, FunctionMetadata>(StringComparer.OrdinalIgnoreCase);
3736

3837
public FunctionMetadataManager(IOptions<ScriptJobHostOptions> scriptOptions, IFunctionMetadataProvider functionMetadataProvider,
3938
IOptions<HttpWorkerOptions> httpWorkerOptions, IScriptHostManager scriptHostManager, ILoggerFactory loggerFactory,
40-
IOptionsMonitor<LanguageWorkerOptions> languageWorkerOptions, IEnvironment environment)
39+
IEnvironment environment)
4140
{
4241
_scriptOptions = scriptOptions;
43-
_languageWorkerOptions = languageWorkerOptions;
4442
_serviceProvider = scriptHostManager as IServiceProvider;
4543
_functionMetadataProvider = functionMetadataProvider;
4644
_loggerFactory = loggerFactory;
@@ -84,11 +82,11 @@ public bool TryGetFunctionMetadata(string functionName, out FunctionMetadata fun
8482
/// <param name="applyAllowList">Apply functions allow list filter.</param>
8583
/// <param name="includeCustomProviders">Include any metadata provided by IFunctionProvider when loading the metadata</param>
8684
/// <returns> An Immmutable array of FunctionMetadata.</returns>
87-
public ImmutableArray<FunctionMetadata> GetFunctionMetadata(bool forceRefresh, bool applyAllowList = true, bool includeCustomProviders = true)
85+
public ImmutableArray<FunctionMetadata> GetFunctionMetadata(bool forceRefresh, bool applyAllowList = true, bool includeCustomProviders = true, IList<RpcWorkerConfig> workerConfigs = null)
8886
{
8987
if (forceRefresh || _servicesReset || _functionMetadataArray.IsDefaultOrEmpty)
9088
{
91-
_functionMetadataArray = LoadFunctionMetadata(forceRefresh, includeCustomProviders);
89+
_functionMetadataArray = LoadFunctionMetadata(forceRefresh, includeCustomProviders, workerConfigs: workerConfigs);
9290
_logger.FunctionMetadataManagerFunctionsLoaded(ApplyAllowList(_functionMetadataArray).Count());
9391
_servicesReset = false;
9492
}
@@ -114,26 +112,35 @@ private void InitializeServices()
114112

115113
_isHttpWorker = _serviceProvider.GetService<IOptions<HttpWorkerOptions>>()?.Value?.Description != null;
116114
_scriptOptions = _serviceProvider.GetService<IOptions<ScriptJobHostOptions>>();
117-
_languageWorkerOptions = _serviceProvider.GetService<IOptionsMonitor<LanguageWorkerOptions>>();
118115

119116
// Resetting the logger switches the logger scope to Script Host level,
120117
// also making the logs available to Application Insights
121118
_logger = _serviceProvider?.GetService<ILoggerFactory>().CreateLogger(LogCategories.Startup);
122119
_servicesReset = true;
123120
}
124121

122+
/// <summary>
123+
/// This is the worker configuration created in the jobhost scope during placeholder initialization
124+
/// This is used as a fallback incase the config is not passed down from previous method call.
125+
/// </summary>
126+
private IList<RpcWorkerConfig> GetFallbackWorkerConfig()
127+
{
128+
return _serviceProvider.GetService<IOptionsMonitor<LanguageWorkerOptions>>().CurrentValue.WorkerConfigs;
129+
}
130+
125131
/// <summary>
126132
/// Read all functions and populate function metadata.
127133
/// </summary>
128-
internal ImmutableArray<FunctionMetadata> LoadFunctionMetadata(bool forceRefresh = false, bool includeCustomProviders = true, IFunctionInvocationDispatcher dispatcher = null)
134+
internal ImmutableArray<FunctionMetadata> LoadFunctionMetadata(bool forceRefresh = false, bool includeCustomProviders = true, IFunctionInvocationDispatcher dispatcher = null, IList<RpcWorkerConfig> workerConfigs = null)
129135
{
136+
workerConfigs ??= GetFallbackWorkerConfig();
137+
130138
_functionMetadataMap.Clear();
131139

132140
ICollection<string> functionsAllowList = _scriptOptions?.Value?.Functions;
133141
_logger.FunctionMetadataManagerLoadingFunctionsMetadata();
134142

135143
ImmutableArray<FunctionMetadata> immutableFunctionMetadata;
136-
var workerConfigs = _languageWorkerOptions.CurrentValue.WorkerConfigs;
137144

138145
immutableFunctionMetadata = _functionMetadataProvider.GetFunctionMetadataAsync(workerConfigs, _environment, forceRefresh).GetAwaiter().GetResult();
139146

src/WebJobs.Script/Host/IFunctionMetadataManager.cs

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

4+
using System.Collections.Generic;
45
using System.Collections.Immutable;
56
using Microsoft.Azure.WebJobs.Script.Description;
67
using Microsoft.Azure.WebJobs.Script.Workers;
8+
using Microsoft.Azure.WebJobs.Script.Workers.Rpc;
79

810
namespace Microsoft.Azure.WebJobs.Script
911
{
1012
public interface IFunctionMetadataManager
1113
{
1214
ImmutableDictionary<string, ImmutableArray<string>> Errors { get; }
1315

14-
ImmutableArray<FunctionMetadata> GetFunctionMetadata(bool forceRefresh = false, bool applyAllowlist = true, bool includeCustomProviders = true);
16+
ImmutableArray<FunctionMetadata> GetFunctionMetadata(bool forceRefresh = false, bool applyAllowlist = true, bool includeCustomProviders = true, IList<RpcWorkerConfig> workerConfigs = null);
1517

1618
bool TryGetFunctionMetadata(string functionName, out FunctionMetadata functionMetadata, bool forceRefresh = false);
1719
}

src/WebJobs.Script/Host/ScriptHost.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -367,7 +367,7 @@ private IEnumerable<FunctionMetadata> GetFunctionsMetadata()
367367
{
368368
IEnumerable<FunctionMetadata> functionMetadata;
369369

370-
functionMetadata = _functionMetadataManager.GetFunctionMetadata(forceRefresh: false);
370+
functionMetadata = _functionMetadataManager.GetFunctionMetadata(forceRefresh: false, workerConfigs: _languageWorkerOptions.CurrentValue.WorkerConfigs);
371371
_workerRuntime ??= Utility.GetWorkerRuntime(functionMetadata);
372372
foreach (var error in _functionMetadataManager.Errors)
373373
{

test/WebJobs.Script.Tests.Integration/Host/StandbyManager/StandbyManagerE2ETests_Linux.cs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -125,9 +125,9 @@ public async Task StandbyModeE2E_LinuxContainer()
125125
Assert.Equal(1, logLines.Count(p => p.Contains("Validating host assignment context")));
126126
Assert.Equal(1, logLines.Count(p => p.Contains("Starting Assignment")));
127127
Assert.Equal(1, logLines.Count(p => p.Contains("Applying 3 app setting(s)")));
128-
Assert.Equal(1, logLines.Count(p => p.Contains("Skipping WorkerConfig for language: python")));
129-
Assert.Equal(1, logLines.Count(p => p.Contains("Skipping WorkerConfig for language: powershell")));
130-
Assert.Equal(1, logLines.Count(p => p.Contains("Skipping WorkerConfig for language: java")));
128+
Assert.Equal(2, logLines.Count(p => p.Contains("Skipping WorkerConfig for language: python")));
129+
Assert.Equal(2, logLines.Count(p => p.Contains("Skipping WorkerConfig for language: powershell")));
130+
Assert.Equal(2, logLines.Count(p => p.Contains("Skipping WorkerConfig for language: java")));
131131
Assert.Equal(1, logLines.Count(p => p.Contains($"Extracting files to '{_expectedScriptPath}'")));
132132
Assert.Equal(1, logLines.Count(p => p.Contains("Zip extraction complete")));
133133
Assert.Equal(1, logLines.Count(p => p.Contains("Triggering specialization")));

test/WebJobs.Script.Tests.Integration/TestFunctionHost.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -480,7 +480,7 @@ private FunctionMetadataManager GetMetadataManager(IOptionsMonitor<ScriptApplica
480480
var metadataProvider = new HostFunctionMetadataProvider(optionsMonitor, NullLogger<HostFunctionMetadataProvider>.Instance, new TestMetricsLogger());
481481
var defaultProvider = new FunctionMetadataProvider(NullLogger<FunctionMetadataProvider>.Instance, null, metadataProvider, new OptionsWrapper<FunctionsHostingConfigOptions>(new FunctionsHostingConfigOptions()), SystemEnvironment.Instance);
482482
var metadataManager = new FunctionMetadataManager(managerServiceProvider.GetService<IOptions<ScriptJobHostOptions>>(), defaultProvider,
483-
managerServiceProvider.GetService<IOptions<HttpWorkerOptions>>(), manager, factory, new TestOptionsMonitor<LanguageWorkerOptions>(workerOptions), environment);
483+
managerServiceProvider.GetService<IOptions<HttpWorkerOptions>>(), manager, factory, environment);
484484

485485
return metadataManager;
486486
}

test/WebJobs.Script.Tests.Integration/WebHostEndToEnd/SpecializationE2ETests.cs

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -408,6 +408,51 @@ public async Task Specialization_UsePlaceholderWorkerforReadOnlyFileSystem()
408408
Assert.Equal(processId, newProcessId);
409409
}
410410

411+
[Fact]
412+
public async Task Specialization_RestartWorkerWithWorkerArguments()
413+
{
414+
_environment.SetEnvironmentVariable(RpcWorkerConstants.FunctionWorkerRuntimeSettingName, "node");
415+
_environment.SetEnvironmentVariable(EnvironmentSettingNames.AzureWebJobsFeatureFlags, ScriptConstants.FeatureFlagEnableWorkerIndexing);
416+
417+
var builder = CreateStandbyHostBuilder("HttpTriggerNoAuth");
418+
string isFileSystemReadOnly = ConfigurationPath.Combine(ConfigurationSectionNames.WebHost, nameof(ScriptApplicationHostOptions.IsFileSystemReadOnly));
419+
420+
builder.ConfigureAppConfiguration(config =>
421+
{
422+
config.AddInMemoryCollection(new Dictionary<string, string>
423+
{
424+
{ _scriptRootConfigPath, Path.GetFullPath(@"TestScripts\NodeWithBundles") }
425+
});
426+
});
427+
428+
using var testServer = new TestServer(builder);
429+
430+
var client = testServer.CreateClient();
431+
432+
var response = await client.GetAsync("api/warmup");
433+
response.EnsureSuccessStatusCode();
434+
435+
var webChannelManager = testServer.Services.GetService<IWebHostRpcWorkerChannelManager>();
436+
var channel = await webChannelManager.GetChannels("node").Single().Value.Task;
437+
var processId = channel.WorkerProcess.Process.Id;
438+
Assert.DoesNotContain("--max-old-space-size=1272", channel.WorkerProcess.Process.StartInfo.Arguments);
439+
440+
// Use an actual env var here as it will be refreshed in config after specialization
441+
using var envVars = new TestScopedEnvironmentVariable("languageWorkers:node:arguments", "--max-old-space-size=1272");
442+
_environment.SetEnvironmentVariable(EnvironmentSettingNames.AzureWebsiteRunFromPackage, "1");
443+
_environment.SetEnvironmentVariable(EnvironmentSettingNames.AzureWebsiteRunFromPackage, "1");
444+
_environment.SetEnvironmentVariable(EnvironmentSettingNames.AzureWebsiteContainerReady, "1");
445+
_environment.SetEnvironmentVariable(EnvironmentSettingNames.AzureWebsitePlaceholderMode, "0");
446+
447+
response = await client.GetAsync("api/HttpTriggerNoAuth");
448+
response.EnsureSuccessStatusCode();
449+
450+
channel = await webChannelManager.GetChannels("node").Single().Value.Task;
451+
var newProcessId = channel.WorkerProcess.Process.Id;
452+
Assert.Contains("--max-old-space-size=1272", channel.WorkerProcess.Process.StartInfo.Arguments);
453+
Assert.NotEqual(processId, newProcessId);
454+
}
455+
411456
[Fact]
412457
public async Task Specialization_GCMode()
413458
{

test/WebJobs.Script.Tests.Shared/TestFunctionMetadataManager.cs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
using Microsoft.Azure.WebJobs.Script.Grpc;
1010
using Microsoft.Azure.WebJobs.Script.Workers.Http;
1111
using Microsoft.Azure.WebJobs.Script.Workers.Rpc;
12+
using Microsoft.Extensions.DependencyInjection;
1213
using Microsoft.Extensions.Logging;
1314
using Microsoft.Extensions.Options;
1415
using Moq;
@@ -51,7 +52,7 @@ public static FunctionMetadataManager GetFunctionMetadataManager(IOptions<Script
5152
var source = new TestChangeTokenSource<ScriptApplicationHostOptions>();
5253
var changeTokens = new[] { source };
5354
var optionsMonitor = new OptionsMonitor<ScriptApplicationHostOptions>(factory, changeTokens, factory);
54-
return new FunctionMetadataManager(jobHostOptions, functionMetadataProvider, httpOptions, managerMock.Object, loggerFactory, languageWorkerOptions, SystemEnvironment.Instance);
55+
return new FunctionMetadataManager(jobHostOptions, functionMetadataProvider, httpOptions, managerMock.Object, loggerFactory, SystemEnvironment.Instance);
5556
}
5657
}
5758
}

test/WebJobs.Script.Tests/Description/FunctionInvokerBaseTests.cs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
using Microsoft.Azure.WebJobs.Script.Eventing;
1313
using Microsoft.Azure.WebJobs.Script.WebHost.Diagnostics;
1414
using Microsoft.Azure.WebJobs.Script.Workers;
15+
using Microsoft.Azure.WebJobs.Script.Workers.Rpc;
1516
using Microsoft.Extensions.DependencyInjection;
1617
using Microsoft.Extensions.Hosting;
1718
using Microsoft.Extensions.Logging;
@@ -254,7 +255,7 @@ public MockMetadataManager(ICollection<FunctionMetadata> functions)
254255
public ImmutableDictionary<string, ImmutableArray<string>> Errors =>
255256
ImmutableDictionary<string, ImmutableArray<string>>.Empty;
256257

257-
public ImmutableArray<FunctionMetadata> GetFunctionMetadata(bool forceRefresh = false, bool applyAllowlist = true, bool includeCustomProviders = true)
258+
public ImmutableArray<FunctionMetadata> GetFunctionMetadata(bool forceRefresh = false, bool applyAllowlist = true, bool includeCustomProviders = true, IList<RpcWorkerConfig> workerConfigs = null)
258259
{
259260
return _functions.ToImmutableArray();
260261
}

0 commit comments

Comments
 (0)