Skip to content

Commit

Permalink
Skip validation of FUNCTIONS_WORKER_RUNTIME with function metadata …
Browse files Browse the repository at this point in the history
…in placeholder mode (#10459)

* Excluding WarmUp function during placeholder mode when validating FWR with Language in funciton metadata.

* Simplified the change based on PR feedback.

* Cancelled another change which is needed due to simplification.

* Adding a check in `ValidateAndLogRuntimeMismatch` to ensure it is called only during non placeholder mode code path. If called form placeholder mode, this method will throw now.

* Checking error log level in tests.
  • Loading branch information
kshyju authored Sep 9, 2024
1 parent 6bccacf commit 48505fd
Show file tree
Hide file tree
Showing 4 changed files with 42 additions and 4 deletions.
3 changes: 2 additions & 1 deletion release_notes.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,4 +8,5 @@
- Update application insights agent version to 3.5.4
- Includes fixes from 2.16.0
- Migrated Scale Metrics to use `Azure.Data.Tables` SDK (#10276)
- Added support for Identity-based connections
- Added support for Identity-based connections
- Skip validation of `FUNCTIONS_WORKER_RUNTIME` with funciton metadata in placeholder mode. (#10459)
9 changes: 7 additions & 2 deletions src/WebJobs.Script/Host/ScriptHost.cs
Original file line number Diff line number Diff line change
Expand Up @@ -776,8 +776,13 @@ private void TrySetDirectType(FunctionMetadata metadata)
// Ensure customer deployed application payload matches with the worker runtime configured for the function app and log a warning if not.
// If a customer has "dotnet-isolated" worker runtime configured for the function app, and then they deploy an in-proc app payload, this will warn/error
// If there is a mismatch, the method will return false, else true.
private static bool ValidateAndLogRuntimeMismatch(IEnumerable<FunctionMetadata> functionMetadata, string workerRuntime, IOptions<FunctionsHostingConfigOptions> hostingConfigOptions, ILogger logger)
private bool ValidateAndLogRuntimeMismatch(IEnumerable<FunctionMetadata> functionMetadata, string workerRuntime, IOptions<FunctionsHostingConfigOptions> hostingConfigOptions, ILogger logger)
{
if (_environment.IsPlaceholderModeEnabled())
{
throw new InvalidOperationException($"Validation of '{EnvironmentSettingNames.FunctionWorkerRuntime}' with deployed payload metadata should not occur in placeholder mode.");
}

if (functionMetadata != null && functionMetadata.Any() && !Utility.ContainsAnyFunctionMatchingWorkerRuntime(functionMetadata, workerRuntime))
{
var languages = string.Join(", ", functionMetadata.Select(f => f.Language).Distinct()).Replace(DotNetScriptTypes.DotNetAssembly, RpcWorkerConstants.DotNetLanguageWorkerName);
Expand All @@ -804,7 +809,7 @@ internal async Task<Collection<FunctionDescriptor>> GetFunctionDescriptorsAsync(
{
bool throwOnWorkerRuntimeAndPayloadMetadataMismatch = true;
// this dotnet isolated specific logic is temporary to ensure in-proc payload compatibility with "dotnet-isolated" as the FUNCTIONS_WORKER_RUNTIME value.
if (string.Equals(workerRuntime, RpcWorkerConstants.DotNetIsolatedLanguageWorkerName, StringComparison.OrdinalIgnoreCase))
if (string.Equals(workerRuntime, RpcWorkerConstants.DotNetIsolatedLanguageWorkerName, StringComparison.OrdinalIgnoreCase) && !_environment.IsPlaceholderModeEnabled())
{
bool payloadMatchesWorkerRuntime = ValidateAndLogRuntimeMismatch(functions, workerRuntime, _hostingConfigOptions, _logger);
if (!payloadMatchesWorkerRuntime)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,38 @@ public async Task ZipPackageFailure_DetectedOnSpecialization()
host.Dispose();
}


[Fact]
public async Task StandbyModeE2E_DotnetIsolated_WarmupSucceeds()
{
_settings.Add(EnvironmentSettingNames.AzureWebsiteInstanceId, Guid.NewGuid().ToString());

var environment = new TestEnvironment(_settings);
environment.SetEnvironmentVariable(RpcWorkerConstants.FunctionWorkerRuntimeSettingName, RpcWorkerConstants.DotNetIsolatedLanguageWorkerName);

await InitializeTestHostAsync("Windows", environment);

await VerifyWarmupSucceeds();

await TestHelpers.Await(() =>
{
// wait for the trace indicating that the host has started in placeholder mode.
var logs = _loggerProvider.GetAllLogMessages().Where(p => p.FormattedMessage != null).Select(p => p.FormattedMessage).ToArray();
return logs.Contains("Job host started") && logs.Contains("Host state changed from Initialized to Running.");
}, userMessageCallback: () => string.Join(Environment.NewLine, _loggerProvider.GetAllLogMessages().Select(p => $"[{p.Timestamp.ToString("HH:mm:ss.fff")}] {p.FormattedMessage}")));

var logLines = _loggerProvider.GetAllLogMessages().Where(p => p.FormattedMessage != null).Select(p => p.FormattedMessage).ToArray();

Assert.Single(logLines.Where(l => l.EndsWith("[FunctionsNetHost] Starting FunctionsNetHost")));
Assert.Equal(1, logLines.Count(p => p.Contains("Creating StandbyMode placeholder function directory")));
Assert.Equal(1, logLines.Count(p => p.Contains("StandbyMode placeholder function directory created")));
Assert.Equal(1, logLines.Count(p => p.Contains("Host is in standby mode")));

// Ensure no warning logs are present.
var warningLogEntries = _loggerProvider.GetAllLogMessages().Where(a => a.Level == Microsoft.Extensions.Logging.LogLevel.Warning);
Assert.True(!warningLogEntries.Any(), $"Warnings found in logs: {string.Join(Environment.NewLine, warningLogEntries.Select(e => e.FormattedMessage))}");
}

[Theory]
[InlineData(true)]
[InlineData(false)]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@ public async Task InProcAppsWorkWithDotnetIsolatedAsFunctionWorkerRuntimeValue()
"which does not match the worker runtime metadata found in the deployed function app artifacts. " +
"The deployed artifacts are for 'dotnet'. See https://aka.ms/functions-invalid-worker-runtime " +
"for more information. The application will continue to run, but may throw an exception in the future.";
Assert.Single(fixture.Host.GetScriptHostLogMessages(), p => p.FormattedMessage != null && p.FormattedMessage.EndsWith(expectedLogEntry));
Assert.Single(fixture.Host.GetScriptHostLogMessages().Where(a => a.Level == Microsoft.Extensions.Logging.LogLevel.Warning), p => p.FormattedMessage != null && p.FormattedMessage.EndsWith(expectedLogEntry));
}
finally
{
Expand Down

0 comments on commit 48505fd

Please sign in to comment.