Skip to content

Commit 7430899

Browse files
SatishRanjanpragnagopa
authored andcommitted
Add ManagedDependency options to host.json (#4050)
1 parent af72576 commit 7430899

12 files changed

+197
-14
lines changed

src/WebJobs.Script/Config/ConfigurationSectionNames.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,5 +12,6 @@ public static class ConfigurationSectionNames
1212
public const string HealthMonitor = "healthMonitor";
1313
public const string HostIdPath = WebHost + ":hostid";
1414
public const string ExtensionBundle = "extensionBundle";
15+
public const string ManagedDependency = "managedDependency";
1516
}
1617
}

src/WebJobs.Script/Config/HostJsonFileConfigurationSource.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,7 @@ private class HostJsonFileConfigurationProvider : ConfigurationProvider
4545
private static readonly string[] WellKnownHostJsonProperties = new[]
4646
{
4747
"version", "functionTimeout", "functions", "http", "watchDirectories", "queues", "serviceBus",
48-
"eventHub", "singleton", "logging", "aggregator", "healthMonitor", "extensionBundle"
48+
"eventHub", "singleton", "logging", "aggregator", "healthMonitor", "extensionBundle", "managedDependencies"
4949
};
5050

5151
private readonly HostJsonFileConfigurationSource _configurationSource;
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
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.Collections.Generic;
5+
using Microsoft.Azure.WebJobs.Script.Grpc.Messages;
6+
using Newtonsoft.Json;
7+
8+
namespace Microsoft.Azure.WebJobs.Script.ManagedDependencies
9+
{
10+
public class ManagedDependencyOptions
11+
{
12+
public bool Enabled { get; set; }
13+
}
14+
}
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
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;
7+
using System.Text;
8+
using Microsoft.Azure.WebJobs.Script.Configuration;
9+
using Microsoft.Extensions.Configuration;
10+
using Microsoft.Extensions.Options;
11+
12+
namespace Microsoft.Azure.WebJobs.Script.ManagedDependencies
13+
{
14+
internal class ManagedDependencyOptionsSetup : IConfigureOptions<ManagedDependencyOptions>
15+
{
16+
private readonly IConfiguration _configuration;
17+
18+
public ManagedDependencyOptionsSetup(IConfiguration configuration)
19+
{
20+
_configuration = configuration;
21+
}
22+
23+
public void Configure(ManagedDependencyOptions options)
24+
{
25+
IConfigurationSection jobHostSection = _configuration.GetSection(ConfigurationSectionNames.JobHost);
26+
var managedDependencySection = jobHostSection.GetSection(ConfigurationSectionNames.ManagedDependency);
27+
managedDependencySection.Bind(options);
28+
}
29+
}
30+
}

src/WebJobs.Script/Rpc/FunctionRegistration/FunctionDispatcher.cs

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
using Microsoft.Azure.WebJobs.Script.Description;
1111
using Microsoft.Azure.WebJobs.Script.Diagnostics;
1212
using Microsoft.Azure.WebJobs.Script.Eventing;
13+
using Microsoft.Azure.WebJobs.Script.ManagedDependencies;
1314
using Microsoft.Extensions.Logging;
1415
using Microsoft.Extensions.Options;
1516

@@ -30,13 +31,15 @@ internal class FunctionDispatcher : IFunctionDispatcher
3031
private IList<IDisposable> _workerStateSubscriptions = new List<IDisposable>();
3132
private ScriptJobHostOptions _scriptOptions;
3233
private bool disposedValue = false;
34+
private IOptions<ManagedDependencyOptions> _managedDependencyOptions;
3335

3436
public FunctionDispatcher(IOptions<ScriptJobHostOptions> scriptHostOptions,
3537
IMetricsLogger metricsLogger,
3638
IScriptEventManager eventManager,
3739
ILoggerFactory loggerFactory,
3840
IOptions<LanguageWorkerOptions> languageWorkerOptions,
39-
ILanguageWorkerChannelManager languageWorkerChannelManager)
41+
ILanguageWorkerChannelManager languageWorkerChannelManager,
42+
IOptions<ManagedDependencyOptions> managedDependencyOptions)
4043
{
4144
_metricsLogger = metricsLogger;
4245
_scriptOptions = scriptHostOptions.Value;
@@ -47,6 +50,7 @@ public FunctionDispatcher(IOptions<ScriptJobHostOptions> scriptHostOptions,
4750

4851
_workerErrorSubscription = _eventManager.OfType<WorkerErrorEvent>()
4952
.Subscribe(WorkerError);
53+
_managedDependencyOptions = managedDependencyOptions;
5054
}
5155

5256
public IDictionary<string, LanguageWorkerState> LanguageWorkerChannelStates => _workerStates;
@@ -59,7 +63,7 @@ internal CreateChannel ChannelFactory
5963
{
6064
_channelFactory = (language, registrations, attemptCount) =>
6165
{
62-
var languageWorkerChannel = _languageWorkerChannelManager.CreateLanguageWorkerChannel(Guid.NewGuid().ToString(), _scriptOptions.RootScriptPath, language, registrations, _metricsLogger, attemptCount, false);
66+
var languageWorkerChannel = _languageWorkerChannelManager.CreateLanguageWorkerChannel(Guid.NewGuid().ToString(), _scriptOptions.RootScriptPath, language, registrations, _metricsLogger, attemptCount, false, _managedDependencyOptions);
6367
languageWorkerChannel.StartWorkerProcess();
6468
return languageWorkerChannel;
6569
};

src/WebJobs.Script/Rpc/ILanguageWorkerChannelManager.cs

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,8 @@
66
using System.Threading.Tasks;
77
using Microsoft.Azure.WebJobs.Host;
88
using Microsoft.Azure.WebJobs.Script.Diagnostics;
9-
9+
using Microsoft.Azure.WebJobs.Script.ManagedDependencies;
10+
using Microsoft.Extensions.Options;
1011
using FunctionMetadata = Microsoft.Azure.WebJobs.Script.Description.FunctionMetadata;
1112

1213
namespace Microsoft.Azure.WebJobs.Script.Rpc
@@ -25,6 +26,6 @@ public interface ILanguageWorkerChannelManager
2526

2627
void ShutdownChannels();
2728

28-
ILanguageWorkerChannel CreateLanguageWorkerChannel(string workerId, string scriptRootPath, string language, IObservable<FunctionRegistrationContext> functionRegistrations, IMetricsLogger metricsLogger, int attemptCount, bool isWebhostChannel);
29+
ILanguageWorkerChannel CreateLanguageWorkerChannel(string workerId, string scriptRootPath, string language, IObservable<FunctionRegistrationContext> functionRegistrations, IMetricsLogger metricsLogger, int attemptCount, bool isWebhostChannel, IOptions<ManagedDependencyOptions> managedDependencyOptions);
2930
}
3031
}

src/WebJobs.Script/Rpc/LanguageWorkerChannel.cs

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,14 +9,18 @@
99
using System.IO;
1010
using System.Linq;
1111
using System.Reactive.Linq;
12+
using System.Runtime.InteropServices;
1213
using System.Threading.Tasks.Dataflow;
1314
using Microsoft.Azure.WebJobs.Logging;
1415
using Microsoft.Azure.WebJobs.Script.Description;
1516
using Microsoft.Azure.WebJobs.Script.Diagnostics;
1617
using Microsoft.Azure.WebJobs.Script.Eventing;
1718
using Microsoft.Azure.WebJobs.Script.Eventing.Rpc;
1819
using Microsoft.Azure.WebJobs.Script.Grpc.Messages;
20+
using Microsoft.Azure.WebJobs.Script.ManagedDependencies;
1921
using Microsoft.Extensions.Logging;
22+
using Microsoft.Extensions.Options;
23+
using Newtonsoft.Json;
2024

2125
using FunctionMetadata = Microsoft.Azure.WebJobs.Script.Description.FunctionMetadata;
2226
using MsgType = Microsoft.Azure.WebJobs.Script.Grpc.Messages.StreamingMessage.ContentOneofCase;
@@ -52,6 +56,7 @@ internal class LanguageWorkerChannel : ILanguageWorkerChannel
5256
private IDisposable _startSubscription;
5357
private IDisposable _startLatencyMetric;
5458
private Uri _serverUri;
59+
private IOptions<ManagedDependencyOptions> _managedDependencyOptions;
5560

5661
internal LanguageWorkerChannel()
5762
{
@@ -71,7 +76,8 @@ internal LanguageWorkerChannel(
7176
IMetricsLogger metricsLogger,
7277
int attemptCount,
7378
ILanguageWorkerConsoleLogSource consoleLogSource,
74-
bool isWebHostChannel = false)
79+
bool isWebHostChannel = false,
80+
IOptions<ManagedDependencyOptions> managedDependencyOptions = null)
7581
{
7682
_workerId = workerId;
7783
_functionRegistrations = functionRegistrations;
@@ -104,6 +110,7 @@ internal LanguageWorkerChannel(
104110
.Subscribe((msg) => InvokeResponse(msg.Message.InvocationResponse)));
105111

106112
_startLatencyMetric = metricsLogger?.LatencyEvent(string.Format(MetricEventNames.WorkerInitializeLatency, workerConfig.Language, attemptCount));
113+
_managedDependencyOptions = managedDependencyOptions;
107114
}
108115

109116
public string Id => _workerId;
@@ -343,6 +350,12 @@ internal void SendFunctionLoadRequest(FunctionRegistrationContext context)
343350
}
344351
};
345352

353+
if (_managedDependencyOptions?.Value != null)
354+
{
355+
_workerChannelLogger?.LogInformation($"Adding dependency download request to {_workerConfig.Language} language worker");
356+
request.ManagedDependencyEnabled = _managedDependencyOptions.Value.Enabled;
357+
}
358+
346359
foreach (var binding in metadata.Bindings)
347360
{
348361
BindingInfo bindingInfo = binding.ToBindingInfo();
@@ -363,6 +376,12 @@ internal void LoadResponse(FunctionLoadResponse loadResponse)
363376
//Cache function load errors to replay error messages on invoking failed functions
364377
_functionLoadErrors[loadResponse.FunctionId] = ex;
365378
}
379+
380+
if (loadResponse.IsDependencyDownloaded)
381+
{
382+
_workerChannelLogger?.LogInformation($"Managed dependency successfully downloaded by the {_workerConfig.Language} language worker");
383+
}
384+
366385
var inputBuffer = _functionInputBuffers[loadResponse.FunctionId];
367386
// link the invocation inputs to the invoke call
368387
var invokeBlock = new ActionBlock<ScriptInvocationContext>(ctx => SendInvocationRequest(ctx));

src/WebJobs.Script/Rpc/LanguageWorkerChannelManager.cs

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
using Microsoft.Azure.WebJobs.Script.Abstractions;
1010
using Microsoft.Azure.WebJobs.Script.Diagnostics;
1111
using Microsoft.Azure.WebJobs.Script.Eventing;
12+
using Microsoft.Azure.WebJobs.Script.ManagedDependencies;
1213
using Microsoft.Azure.WebJobs.Script.Properties;
1314
using Microsoft.Extensions.Logging;
1415
using Microsoft.Extensions.Options;
@@ -64,7 +65,7 @@ public LanguageWorkerChannelManager(IScriptEventManager eventManager, IEnvironme
6465
.Subscribe(AddOrUpdateWorkerChannels);
6566
}
6667

67-
public ILanguageWorkerChannel CreateLanguageWorkerChannel(string workerId, string scriptRootPath, string language, IObservable<FunctionRegistrationContext> functionRegistrations, IMetricsLogger metricsLogger, int attemptCount, bool isWebhostChannel = false)
68+
public ILanguageWorkerChannel CreateLanguageWorkerChannel(string workerId, string scriptRootPath, string language, IObservable<FunctionRegistrationContext> functionRegistrations, IMetricsLogger metricsLogger, int attemptCount, bool isWebhostChannel = false, IOptions<ManagedDependencyOptions> managedDependencyOptions = null)
6869
{
6970
var languageWorkerConfig = _workerConfigs.Where(c => c.Language.Equals(language, StringComparison.OrdinalIgnoreCase)).FirstOrDefault();
7071
if (languageWorkerConfig == null)
@@ -84,7 +85,8 @@ public ILanguageWorkerChannel CreateLanguageWorkerChannel(string workerId, strin
8485
metricsLogger,
8586
attemptCount,
8687
_consoleLogSource,
87-
isWebhostChannel);
88+
isWebhostChannel,
89+
managedDependencyOptions);
8890
}
8991

9092
public async Task InitializeChannelAsync(string runtime)
@@ -99,7 +101,7 @@ private async Task InitializeLanguageWorkerChannel(string language, string scrip
99101
{
100102
string workerId = Guid.NewGuid().ToString();
101103
_logger.LogInformation("Creating language worker channel for runtime:{runtime}", language);
102-
ILanguageWorkerChannel languageWorkerChannel = CreateLanguageWorkerChannel(workerId, scriptRootPath, language, null, null, 0, true);
104+
ILanguageWorkerChannel languageWorkerChannel = CreateLanguageWorkerChannel(workerId, scriptRootPath, language, null, null, 0, true, null);
103105
languageWorkerChannel.StartWorkerProcess();
104106
IObservable<RpcWebHostChannelReadyEvent> rpcChannelReadyEvent = _eventManager.OfType<RpcWebHostChannelReadyEvent>()
105107
.Where(msg => msg.Language == language).Timeout(workerInitTimeout);

src/WebJobs.Script/ScriptHostBuilderExtensions.cs

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
using Microsoft.Azure.WebJobs.Script.Extensibility;
2222
using Microsoft.Azure.WebJobs.Script.Grpc;
2323
using Microsoft.Azure.WebJobs.Script.Grpc.Messages;
24+
using Microsoft.Azure.WebJobs.Script.ManagedDependencies;
2425
using Microsoft.Azure.WebJobs.Script.Rpc;
2526
using Microsoft.Azure.WebJobs.Script.Scale;
2627
using Microsoft.Extensions.Configuration;
@@ -58,7 +59,6 @@ public static IHostBuilder AddScriptHost(this IHostBuilder builder, ScriptApplic
5859
loggerFactory = loggerFactory ?? NullLoggerFactory.Instance;
5960

6061
builder.SetAzureFunctionsConfigurationRoot();
61-
6262
// Host configuration
6363
builder.ConfigureLogging((context, loggingBuilder) =>
6464
{
@@ -89,7 +89,6 @@ public static IHostBuilder AddScriptHost(this IHostBuilder builder, ScriptApplic
8989
public static IHostBuilder AddScriptHostCore(this IHostBuilder builder, ScriptApplicationHostOptions applicationHostOptions, Action<IWebJobsBuilder> configureWebJobs = null)
9090
{
9191
var skipHostInitialization = builder.Properties.ContainsKey(ScriptConstants.SkipHostInitializationKey);
92-
9392
builder.ConfigureWebJobs(webJobsBuilder =>
9493
{
9594
// Built in binding registrations
@@ -144,6 +143,7 @@ public static IHostBuilder AddScriptHostCore(this IHostBuilder builder, ScriptAp
144143
// TODO: pgopa only add this to WebHostServiceCollection
145144
services.ConfigureOptions<LanguageWorkerOptionsSetup>();
146145
services.ConfigureOptions<ExtensionBundleOptionsSetup>();
146+
services.ConfigureOptions<ManagedDependencyOptionsSetup>();
147147
services.AddOptions<FunctionResultAggregatorOptions>()
148148
.Configure<IConfiguration>((o, c) =>
149149
{
@@ -185,7 +185,6 @@ public static void AddCommonServices(IServiceCollection services)
185185
services.AddSingleton<IRpcServer, GrpcServer>();
186186
services.TryAddSingleton<ILanguageWorkerConsoleLogSource, LanguageWorkerConsoleLogSource>();
187187
services.TryAddSingleton<ILanguageWorkerChannelManager, LanguageWorkerChannelManager>();
188-
189188
services.TryAddSingleton<IDebugManager, DebugManager>();
190189
services.TryAddSingleton<IDebugStateProvider, DebugStateProvider>();
191190
services.TryAddSingleton<IEnvironment>(SystemEnvironment.Instance);
Lines changed: 113 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,113 @@
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.IO;
6+
using System.Linq;
7+
using Microsoft.Azure.WebJobs.Script.Configuration;
8+
using Microsoft.Azure.WebJobs.Script.ManagedDependencies;
9+
using Microsoft.Extensions.Configuration;
10+
using Microsoft.Extensions.Logging;
11+
using Microsoft.Extensions.Options;
12+
using Microsoft.WebJobs.Script.Tests;
13+
using Xunit;
14+
15+
namespace Microsoft.Azure.WebJobs.Script.Tests.ManagedDependencies
16+
{
17+
public class ManagedDependencyOptionsSetupTest
18+
{
19+
private readonly ScriptApplicationHostOptions _options;
20+
private readonly string _hostJsonFilePath;
21+
private readonly TestLoggerProvider _loggerProvider;
22+
23+
public ManagedDependencyOptionsSetupTest()
24+
{
25+
_loggerProvider = new TestLoggerProvider();
26+
27+
var scriptPath = Path.Combine(Path.GetTempPath(), "managed_dependency_test");
28+
if (!Directory.Exists(scriptPath))
29+
{
30+
Directory.CreateDirectory(scriptPath);
31+
}
32+
33+
_hostJsonFilePath = Path.Combine(scriptPath, "host.json");
34+
if (File.Exists(_hostJsonFilePath))
35+
{
36+
File.Delete(_hostJsonFilePath);
37+
}
38+
39+
_options = new ScriptApplicationHostOptions
40+
{
41+
ScriptPath = scriptPath
42+
};
43+
}
44+
45+
[Theory]
46+
[InlineData(@"{
47+
'version': '2.0'
48+
}")]
49+
[InlineData(@"{
50+
'version': '2.0',
51+
'managedDependency':{}
52+
}")]
53+
public void Test_ManagedDependencyOptionsSetup_Empty_Or_No_ManagedDependencies_InHostJson(string hostJson)
54+
{
55+
File.WriteAllText(_hostJsonFilePath, hostJson);
56+
Assert.True(File.Exists(_hostJsonFilePath));
57+
var managedDependencyOptions = new ManagedDependencyOptions();
58+
var configuration = BuildHostJsonConfiguration();
59+
ManagedDependencyOptionsSetup managedDependencyOptionsSetup = new ManagedDependencyOptionsSetup(configuration);
60+
managedDependencyOptionsSetup.Configure(managedDependencyOptions);
61+
Assert.True(managedDependencyOptions.Enabled == false);
62+
}
63+
64+
[Theory]
65+
[InlineData(@"{
66+
'version': '2.0',
67+
'managedDependency':
68+
{
69+
'enabled': true
70+
}
71+
}")]
72+
[InlineData(@"{
73+
'version': '2.0',
74+
'managedDependency':
75+
{
76+
'enabled': false
77+
}
78+
}")]
79+
public void Test_ManagedDependencyOptionsSetup_Valid_ManagedDependencies_InHostFile(string hostJson)
80+
{
81+
File.WriteAllText(_hostJsonFilePath, hostJson);
82+
Assert.True(File.Exists(_hostJsonFilePath));
83+
84+
var managedDependencyOptions = new ManagedDependencyOptions();
85+
var configuration = BuildHostJsonConfiguration();
86+
ManagedDependencyOptionsSetup managedDependencyOptionsSetup = new ManagedDependencyOptionsSetup(configuration);
87+
managedDependencyOptionsSetup.Configure(managedDependencyOptions);
88+
if (managedDependencyOptions.Enabled)
89+
{
90+
Assert.True(true);
91+
}
92+
else
93+
{
94+
Assert.True(managedDependencyOptions.Enabled == false);
95+
}
96+
}
97+
98+
private IConfiguration BuildHostJsonConfiguration(IEnvironment environment = null)
99+
{
100+
environment = environment ?? new TestEnvironment();
101+
102+
var loggerFactory = new LoggerFactory();
103+
loggerFactory.AddProvider(_loggerProvider);
104+
105+
var configSource = new HostJsonFileConfigurationSource(_options, environment, loggerFactory);
106+
107+
var configurationBuilder = new ConfigurationBuilder()
108+
.Add(configSource);
109+
110+
return configurationBuilder.Build();
111+
}
112+
}
113+
}

0 commit comments

Comments
 (0)