Skip to content

Commit

Permalink
Add extensions config from host.json to sync trigger payload (#7386)
Browse files Browse the repository at this point in the history
  • Loading branch information
TsuyoshiUshio authored and pragnagopa committed May 21, 2021
1 parent 70fa587 commit 55faf4e
Show file tree
Hide file tree
Showing 3 changed files with 154 additions and 2 deletions.
44 changes: 42 additions & 2 deletions src/WebJobs.Script.WebHost/Management/FunctionsSyncManager.cs
Original file line number Diff line number Diff line change
Expand Up @@ -304,6 +304,17 @@ public async Task<SyncTriggersPayload> GetSyncTriggersPayload()
var functionDetails = await WebFunctionsManager.GetFunctionMetadataResponse(listableFunctions, hostOptions, _hostNameProvider);
result.Add("functions", new JArray(functionDetails.Select(p => JObject.FromObject(p))));

// TEMP: refactor this code to properly add extensions in all scenario(#7394)
// Add the host.json extensions to the payload
if (_environment.IsKubernetesManagedHosting())
{
JObject extensionsPayload = await GetHostJsonExtensionsAsync(_applicationHostOptions, _logger);
if (extensionsPayload != null)
{
result.Add("extensions", extensionsPayload);
}
}

// Add functions secrets to the payload
// Only secret types we own/control can we cache directly
// Encryption is handled by Antares before storage
Expand Down Expand Up @@ -346,7 +357,8 @@ public async Task<SyncTriggersPayload> GetSyncTriggersPayload()
}

string json = JsonConvert.SerializeObject(result);
if (json.Length > ScriptConstants.MaxTriggersStringLength)

if (json.Length > ScriptConstants.MaxTriggersStringLength && !_environment.IsKubernetesManagedHosting())
{
// The settriggers call to the FE enforces a max request size
// limit. If we're over limit, revert to the minimal triggers
Expand All @@ -366,6 +378,34 @@ public async Task<SyncTriggersPayload> GetSyncTriggersPayload()
};
}

internal static async Task<JObject> GetHostJsonExtensionsAsync(IOptionsMonitor<ScriptApplicationHostOptions> applicationHostOptions, ILogger logger)
{
var hostOptions = applicationHostOptions.CurrentValue.ToHostOptions();
string hostJsonPath = Path.Combine(hostOptions.RootScriptPath, ScriptConstants.HostMetadataFileName);
if (FileUtility.FileExists(hostJsonPath))
{
try
{
var hostJson = JObject.Parse(await FileUtility.ReadAsync(hostJsonPath));
if (hostJson.TryGetValue("extensions", out JToken token))
{
return (JObject)token;
}
else
{
return null;
}
}
catch (JsonException ex)
{
logger.LogWarning($"Unable to parse host configuration file '{hostJsonPath}'. : {ex}");
return null;
}
}

return null;
}

internal async Task<IEnumerable<JObject>> GetFunctionTriggers(IEnumerable<FunctionMetadata> functionsMetadata, ScriptJobHostOptions hostOptions)
{
var triggers = (await functionsMetadata
Expand Down Expand Up @@ -659,4 +699,4 @@ public bool HasValues()
}
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -269,6 +269,7 @@ private void VerifyResultWithCacheOn(string connection = DefaultTestConnection,
Assert.Equal("bbb", (string)function1Secrets["secrets"]["TestFunctionKey2"]);

var logs = _loggerProvider.GetAllLogMessages().Where(m => m.Category.Equals(SyncManagerLogCategory)).ToList();

var log = logs[0];
int startIdx = log.FormattedMessage.IndexOf("Content=") + 8;
int endIdx = log.FormattedMessage.LastIndexOf(')');
Expand Down Expand Up @@ -400,6 +401,7 @@ public async Task TrySyncTriggers_BackgroundSync_SetTriggersFailure_HashNotUpdat

// verify log statements
var logMessages = _loggerProvider.GetAllLogMessages().Where(m => m.Category.Equals(SyncManagerLogCategory)).Select(p => p.FormattedMessage).ToArray();

Assert.True(logMessages[0].Contains("Content="));
Assert.Equal(expectedErrorMessage, logMessages[1]);
}
Expand Down
110 changes: 110 additions & 0 deletions test/WebJobs.Script.Tests/Managment/FunctionsSyncManagerTests.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the MIT License. See License.txt in the project root for license information.

using System;
using System.Collections.Generic;
using System.IO;
using System.Text;
using System.Threading.Tasks;
using Microsoft.Azure.WebJobs.Script.WebHost.Management;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
using Moq;
using Xunit;

namespace Microsoft.Azure.WebJobs.Script.Tests.Managment
{
public class FunctionsSyncManagerTests
{
private const string HostJsonWithExtensions = @"
{
""version"": ""2.0"",
""logging"": {
""applicationInsights"": {
""samplingSettings"": {
""isEnabled"": true,
""excludedTypes"": ""Request""
}
}
},
""extensionBundle"": {
""id"": ""Microsoft.Azure.Functions.ExtensionBundle"",
""version"": ""[2.*, 3.0.0)""
},
""extensions"": {
""queues"": {
""maxPollingInterval"": ""00:00:30"",
""visibilityTimeout"": ""00:01:00"",
""batchSize"": 16,
""maxDequeueCount"": 5,
""newBatchThreshold"": 8,
""messageEncoding"": ""base64""
}
}
}
";

private const string HostJsonWithoutExtensions = @"{}";

private const string HostJsonWithWrongExtensions = @"
{
""version"": ""2.0"",
""extensions"": {
""queues"":
""maxPollingInterval"": ""00:00:30"",
""visibilityTimeout"": ""00:01:00"",
""batchSize"": 16,
""maxDequeueCount"": 5,
""newBatchThreshold"": 8,
""messageEncoding"": ""base64""
}
}
}
";

[Theory]
[InlineData(HostJsonWithExtensions, false, false, "00:00:30")]
[InlineData(HostJsonWithoutExtensions, true, false, "")]
[InlineData(HostJsonWithExtensions, true, true, "")]
[InlineData(HostJsonWithWrongExtensions, true, false, "")]
public async Task GetHostJsonExtensionsAsyncTest(string hostJsonContents, bool isNull, bool skipWriteFile, string value)
{
string scriptPath = string.Empty;
try
{
var logger = new Mock<ILogger>();
var monitor = new Mock<IOptionsMonitor<ScriptApplicationHostOptions>>();
var options = new ScriptApplicationHostOptions();
scriptPath = GetTempDirectory();
var hostJsonPath = Path.Combine(scriptPath, "host.json");
if (!skipWriteFile)
{
await FileUtility.WriteAsync(hostJsonPath, hostJsonContents);
}

options.ScriptPath = scriptPath;
monitor.Setup(x => x.CurrentValue).Returns(options);
var json = await FunctionsSyncManager.GetHostJsonExtensionsAsync(monitor.Object, logger.Object);
if (isNull)
{
Assert.Null(json);
}
else
{
Assert.Equal(value, json["queues"]?["maxPollingInterval"]);
}
}
finally
{
await FileUtility.DeleteDirectoryAsync(scriptPath, true);
}
}

private string GetTempDirectory()
{
var temp = Path.Combine(Path.GetTempPath(), Path.GetRandomFileName());
Directory.CreateDirectory(temp);
return temp;
}
}
}

0 comments on commit 55faf4e

Please sign in to comment.