Skip to content

Commit 025b68b

Browse files
authored
Send function app directory at specialization (#5256)
* Send function app directory at specialization * Updated subtree from https://github.com/azure/azure-functions-language-worker-protobuf. Tag: v1.3.6-protofile. Commit: 66534f0 * update proto name and add tests * added test to be fixed later * disable flaky test
1 parent ffa3fb8 commit 025b68b

File tree

9 files changed

+99
-16
lines changed

9 files changed

+99
-16
lines changed

src/WebJobs.Script.Grpc/azure-functions-language-worker-protobuf/src/proto/FunctionRpc.proto

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -189,6 +189,8 @@ message WorkerStatusResponse {
189189
message FunctionEnvironmentReloadRequest {
190190
// Environment variables from the current process
191191
map<string, string> environment_variables = 1;
192+
// Current directory of function app
193+
string function_app_directory = 2;
192194
}
193195

194196
message FunctionEnvironmentReloadResponse {

src/WebJobs.Script.WebHost/Standby/StandbyManager.cs

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -107,13 +107,15 @@ public async Task SpecializeHostCoreAsync()
107107
// user dependencies
108108
FunctionAssemblyLoadContext.ResetSharedContext();
109109

110+
// Signals change of JobHost options from placeholder mode
111+
// (ex: ScriptPath is updated)
112+
NotifyChange();
113+
110114
using (_metricsLogger.LatencyEvent(MetricEventNames.SpecializationLanguageWorkerChannelManagerSpecialize))
111115
{
112116
await _rpcWorkerChannelManager.SpecializeAsync();
113117
}
114118

115-
NotifyChange();
116-
117119
using (_metricsLogger.LatencyEvent(MetricEventNames.SpecializationRestartHost))
118120
{
119121
await _scriptHostManager.RestartHostAsync();

src/WebJobs.Script/WebJobs.Script.csproj

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@
3939
</PackageReference>
4040
<PackageReference Include="Microsoft.Azure.AppService.Proxy.Client" Version="2.0.8100001-0126c21e" />
4141
<PackageReference Include="Microsoft.Azure.Functions.JavaWorker" Version="1.5.2-SNAPSHOT" />
42-
<PackageReference Include="Microsoft.Azure.Functions.NodeJsWorker" Version="1.1.1" />
42+
<PackageReference Include="Microsoft.Azure.Functions.NodeJsWorker" Version="1.2.0" />
4343
<PackageReference Include="Microsoft.Azure.Functions.PowerShellWorker" Version="1.0.197" />
4444
<PackageReference Include="Microsoft.Azure.WebJobs" Version="3.0.15" />
4545
<PackageReference Include="Microsoft.Azure.WebJobs.Extensions" Version="3.0.5" />

src/WebJobs.Script/Workers/Rpc/RpcWorkerChannel.cs

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -27,10 +27,10 @@ namespace Microsoft.Azure.WebJobs.Script.Workers.Rpc
2727
internal class RpcWorkerChannel : IRpcWorkerChannel, IDisposable
2828
{
2929
private readonly TimeSpan workerInitTimeout = TimeSpan.FromSeconds(30);
30-
private readonly string _rootScriptPath;
3130
private readonly IScriptEventManager _eventManager;
3231
private readonly RpcWorkerConfig _workerConfig;
3332
private readonly string _runtime;
33+
private readonly IOptionsMonitor<ScriptApplicationHostOptions> _applicationHostOptions;
3434

3535
private IDisposable _functionLoadRequestResponseEvent;
3636
private bool _disposed;
@@ -58,23 +58,23 @@ internal class RpcWorkerChannel : IRpcWorkerChannel, IDisposable
5858

5959
internal RpcWorkerChannel(
6060
string workerId,
61-
string rootScriptPath,
6261
IScriptEventManager eventManager,
6362
RpcWorkerConfig workerConfig,
6463
IWorkerProcess rpcWorkerProcess,
6564
ILogger logger,
6665
IMetricsLogger metricsLogger,
6766
int attemptCount,
67+
IOptionsMonitor<ScriptApplicationHostOptions> applicationHostOptions,
6868
IOptions<ManagedDependencyOptions> managedDependencyOptions = null)
6969
{
7070
_workerId = workerId;
71-
_rootScriptPath = rootScriptPath;
7271
_eventManager = eventManager;
7372
_workerConfig = workerConfig;
7473
_runtime = workerConfig.Description.Language;
7574
_rpcWorkerProcess = rpcWorkerProcess;
7675
_workerChannelLogger = logger;
7776
_metricsLogger = metricsLogger;
77+
_applicationHostOptions = applicationHostOptions;
7878

7979
_workerCapabilities = new Capabilities(_workerChannelLogger);
8080

@@ -230,6 +230,9 @@ internal FunctionEnvironmentReloadRequest GetFunctionEnvironmentReloadRequest(ID
230230
request.EnvironmentVariables.Add(entry.Key.ToString(), entry.Value.ToString());
231231
}
232232
}
233+
234+
request.FunctionAppDirectory = _applicationHostOptions.CurrentValue.ScriptPath;
235+
233236
return request;
234237
}
235238

src/WebJobs.Script/Workers/Rpc/RpcWorkerChannelFactory.cs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ public class RpcWorkerChannelFactory : IRpcWorkerChannelFactory
2020
private readonly IRpcWorkerProcessFactory _rpcWorkerProcessFactory = null;
2121
private readonly IScriptEventManager _eventManager = null;
2222
private readonly IEnumerable<RpcWorkerConfig> _workerConfigs = null;
23+
private readonly IOptionsMonitor<ScriptApplicationHostOptions> _applicationHostOptions = null;
2324

2425
public RpcWorkerChannelFactory(IScriptEventManager eventManager, IEnvironment environment, IRpcServer rpcServer, ILoggerFactory loggerFactory, IOptions<LanguageWorkerOptions> languageWorkerOptions,
2526
IOptionsMonitor<ScriptApplicationHostOptions> applicationHostOptions, IRpcWorkerProcessFactory rpcWorkerProcessManager)
@@ -28,6 +29,7 @@ public RpcWorkerChannelFactory(IScriptEventManager eventManager, IEnvironment en
2829
_loggerFactory = loggerFactory;
2930
_workerConfigs = languageWorkerOptions.Value.WorkerConfigs;
3031
_rpcWorkerProcessFactory = rpcWorkerProcessManager;
32+
_applicationHostOptions = applicationHostOptions;
3133
}
3234

3335
public IRpcWorkerChannel Create(string scriptRootPath, string runtime, IMetricsLogger metricsLogger, int attemptCount, IOptions<ManagedDependencyOptions> managedDependencyOptions = null)
@@ -42,13 +44,13 @@ public IRpcWorkerChannel Create(string scriptRootPath, string runtime, IMetricsL
4244
IWorkerProcess rpcWorkerProcess = _rpcWorkerProcessFactory.Create(workerId, runtime, scriptRootPath);
4345
return new RpcWorkerChannel(
4446
workerId,
45-
scriptRootPath,
4647
_eventManager,
4748
languageWorkerConfig,
4849
rpcWorkerProcess,
4950
workerLogger,
5051
metricsLogger,
5152
attemptCount,
53+
_applicationHostOptions,
5254
managedDependencyOptions);
5355
}
5456
}

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

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -97,6 +97,13 @@ public async Task StandbyModeE2E_JavaTemplateSite()
9797
await Verify_StandbyModeE2E_Java();
9898
}
9999

100+
[Fact(Skip = "https://github.com/Azure/azure-functions-host/issues/4230")]
101+
public async Task StandbyModeE2E_Node()
102+
{
103+
_settings.Add(EnvironmentSettingNames.AzureWebsiteInstanceId, Guid.NewGuid().ToString());
104+
await Verify_StandbyModeE2E_Node();
105+
}
106+
100107
private async Task Verify_StandbyModeE2E_Java()
101108
{
102109
var environment = new TestEnvironment(_settings);
@@ -137,5 +144,47 @@ await TestHelpers.Await(() =>
137144
var result = javaProcessesBefore.Where(pId1 => !javaProcessesAfter.Any(pId2 => pId2 == pId1));
138145
Assert.Equal(0, result.Count());
139146
}
147+
148+
private async Task Verify_StandbyModeE2E_Node()
149+
{
150+
var environment = new TestEnvironment(_settings);
151+
await InitializeTestHostAsync("Windows_Node", environment);
152+
153+
await VerifyWarmupSucceeds();
154+
await VerifyWarmupSucceeds(restart: true);
155+
156+
// Get java process Id before specialization
157+
IEnumerable<int> nodeProcessesBefore = Process.GetProcessesByName("node").Select(p => p.Id);
158+
159+
// now specialize the host
160+
environment.SetEnvironmentVariable(EnvironmentSettingNames.AzureWebsitePlaceholderMode, "0");
161+
environment.SetEnvironmentVariable(EnvironmentSettingNames.AzureWebsiteContainerReady, "1");
162+
environment.SetEnvironmentVariable(RpcWorkerConstants.FunctionWorkerRuntimeSettingName, "node");
163+
environment.SetEnvironmentVariable(EnvironmentSettingNames.AzureWebJobsScriptRoot, "/");
164+
165+
Assert.False(environment.IsPlaceholderModeEnabled());
166+
Assert.True(environment.IsContainerReady());
167+
168+
// give time for the specialization to happen
169+
string[] logLines = null;
170+
await TestHelpers.Await(() =>
171+
{
172+
// wait for the trace indicating that the host has been specialized
173+
logLines = _loggerProvider.GetAllLogMessages().Where(p => p.FormattedMessage != null).Select(p => p.FormattedMessage).ToArray();
174+
return logLines.Contains("Generating 0 job function(s)") && logLines.Contains("Stopping JobHost");
175+
}, timeout: 60 * 1000, userMessageCallback: () => string.Join(Environment.NewLine, _loggerProvider.GetAllLogMessages().Select(p => $"[{p.Timestamp.ToString("HH:mm:ss.fff")}] {p.FormattedMessage}")));
176+
177+
IEnumerable<int> nodeProcessesAfter = Process.GetProcessesByName("node").Select(p => p.Id);
178+
179+
// Verify number of java processes before and after specialization are the same.
180+
Assert.Equal(nodeProcessesBefore.Count(), nodeProcessesAfter.Count());
181+
182+
//Verify atleast one java process is running
183+
Assert.True(nodeProcessesAfter.Count() >= 1);
184+
185+
// Verify Java same java process is used after host restart
186+
var result = nodeProcessesBefore.Where(pId1 => !nodeProcessesAfter.Any(pId2 => pId2 == pId1));
187+
Assert.Equal(0, result.Count());
188+
}
140189
}
141190
}

test/WebJobs.Script.Tests.Integration/Management/InstanceManagerTests.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -464,7 +464,7 @@ public async Task SpecializeMSISidecar_Fails()
464464
p => Assert.StartsWith("Specialize MSI sidecar call failed. StatusCode=BadRequest", p));
465465
}
466466

467-
[Fact]
467+
[Fact(Skip = "https://github.com/Azure/azure-functions-host/issues/5261")]
468468
public async Task Mounts_Valid_BYOS_Accounts()
469469
{
470470
_environment.SetEnvironmentVariable(EnvironmentSettingNames.AzureWebsitePlaceholderMode, "1");

test/WebJobs.Script.Tests.Integration/WebJobs.Script.Tests.Integration.csproj

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@
3838
<PackageReference Include="Microsoft.AspNetCore.TestHost" Version="2.2.0" />
3939
<PackageReference Include="Microsoft.Azure.DocumentDB.Core" Version="2.3.0" />
4040
<PackageReference Include="Microsoft.Azure.EventHubs" Version="2.1.0" />
41-
<PackageReference Include="Microsoft.Azure.Functions.NodeJsWorker" Version="1.1.1" />
41+
<PackageReference Include="Microsoft.Azure.Functions.NodeJsWorker" Version="1.2.0" />
4242
<PackageReference Include="Microsoft.Azure.WebJobs.Extensions.Storage" Version="3.0.10-11660" />
4343
<PackageReference Include="Microsoft.Azure.Functions.JavaWorker" Version="1.5.2-SNAPSHOT" />
4444
<PackageReference Include="Microsoft.Azure.Mobile.Client" Version="4.0.2" />

test/WebJobs.Script.Tests/Workers/Rpc/RpcWorkerChannelTests.cs

Lines changed: 32 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
using Microsoft.Azure.WebJobs.Script.Workers;
1515
using Microsoft.Azure.WebJobs.Script.Workers.Rpc;
1616
using Microsoft.Extensions.Logging;
17+
using Microsoft.Extensions.Options;
1718
using Moq;
1819
using Xunit;
1920

@@ -38,6 +39,7 @@ public class RpcWorkerChannelTests
3839
private readonly TestLogger _logger;
3940
private readonly IEnumerable<FunctionMetadata> _functions = new List<FunctionMetadata>();
4041
private readonly RpcWorkerConfig _testWorkerConfig;
42+
private readonly IOptionsMonitor<ScriptApplicationHostOptions> _hostOptionsMonitor;
4143
private RpcWorkerChannel _workerChannel;
4244

4345
public RpcWorkerChannelTests()
@@ -47,15 +49,25 @@ public RpcWorkerChannelTests()
4749
_testWorkerConfig = TestHelpers.GetTestWorkerConfigs().FirstOrDefault();
4850
_mockrpcWorkerProcess.Setup(m => m.StartProcessAsync()).Returns(Task.CompletedTask);
4951

52+
var hostOptions = new ScriptApplicationHostOptions
53+
{
54+
IsSelfHost = true,
55+
ScriptPath = _scriptRootPath,
56+
LogPath = Environment.CurrentDirectory, // not tested
57+
SecretsPath = Environment.CurrentDirectory, // not tested
58+
HasParentScope = true
59+
};
60+
_hostOptionsMonitor = TestHelpers.CreateOptionsMonitor(hostOptions);
61+
5062
_workerChannel = new RpcWorkerChannel(
5163
_workerId,
52-
_scriptRootPath,
5364
_eventManager,
5465
_testWorkerConfig,
5566
_mockrpcWorkerProcess.Object,
5667
_logger,
5768
_metricsLogger,
58-
0);
69+
0,
70+
_hostOptionsMonitor);
5971
}
6072

6173
[Fact]
@@ -98,13 +110,13 @@ public async Task StartWorkerProcessAsync_WorkerProcess_Throws()
98110

99111
_workerChannel = new RpcWorkerChannel(
100112
_workerId,
101-
_scriptRootPath,
102113
_eventManager,
103114
_testWorkerConfig,
104115
mockrpcWorkerProcessThatThrows.Object,
105116
_logger,
106117
_metricsLogger,
107-
0);
118+
0,
119+
_hostOptionsMonitor);
108120
await Assert.ThrowsAsync<FileNotFoundException>(async () => await _workerChannel.StartWorkerProcessAsync());
109121
}
110122

@@ -154,13 +166,13 @@ public async Task Drain_Verify()
154166
Guid invocationId = Guid.NewGuid();
155167
RpcWorkerChannel channel = new RpcWorkerChannel(
156168
_workerId,
157-
_scriptRootPath,
158169
_eventManager,
159170
_testWorkerConfig,
160171
_mockrpcWorkerProcess.Object,
161172
_logger,
162173
_metricsLogger,
163-
0);
174+
0,
175+
_hostOptionsMonitor);
164176
channel.SetupFunctionInvocationBuffers(GetTestFunctionsList("node"));
165177
ScriptInvocationContext scriptInvocationContext = GetTestScriptInvocationContext(invocationId, resultSource);
166178
channel.SendInvocationRequest(scriptInvocationContext);
@@ -232,7 +244,7 @@ public async Task SendSendFunctionEnvironmentReloadRequest_ThrowsTimeout()
232244
}
233245

234246
[Fact]
235-
public void SendSendFunctionEnvironmentReloadRequest_SanitizedEnvironmentVariables()
247+
public void SendFunctionEnvironmentReloadRequest_SanitizedEnvironmentVariables()
236248
{
237249
var environmentVariables = new Dictionary<string, string>()
238250
{
@@ -248,6 +260,19 @@ public void SendSendFunctionEnvironmentReloadRequest_SanitizedEnvironmentVariabl
248260
Assert.True(envReloadRequest.EnvironmentVariables["TestValid"] == "TestValue");
249261
}
250262

263+
[Fact]
264+
public void SendFunctionEnvironmentReloadRequest_WithDirectory()
265+
{
266+
var environmentVariables = new Dictionary<string, string>()
267+
{
268+
{ "TestValid", "TestValue" }
269+
};
270+
271+
FunctionEnvironmentReloadRequest envReloadRequest = _workerChannel.GetFunctionEnvironmentReloadRequest(environmentVariables);
272+
Assert.True(envReloadRequest.EnvironmentVariables["TestValid"] == "TestValue");
273+
Assert.True(envReloadRequest.FunctionAppDirectory == _scriptRootPath);
274+
}
275+
251276
[Fact]
252277
public void ReceivesInboundEvent_InvocationResponse()
253278
{

0 commit comments

Comments
 (0)