diff --git a/release_notes.md b/release_notes.md index c6aeec4e8b..53ff50f8af 100644 --- a/release_notes.md +++ b/release_notes.md @@ -1,8 +1,9 @@ -### Release notes - - -- Improved memory metrics reporting using CGroup data for Linux consumption (#10968) -- Fixing GrpcWorkerChannel concurrency bug (#10998) -- Avoid circular dependency when resolving LinuxContainerLegionMetricsPublisher. (#10991) +### Release notes + + +- Improved memory metrics reporting using CGroup data for Linux consumption (#10968) +- Memory allocation optimizations in `RpcWorkerConfigFactory.AddProviders` (#10959) +- Fixing GrpcWorkerChannel concurrency bug (#10998) +- Avoid circular dependency when resolving LinuxContainerLegionMetricsPublisher. (#10991) diff --git a/src/WebJobs.Script/Workers/Profiles/Conditions/EnvironmentCondition.cs b/src/WebJobs.Script/Workers/Profiles/Conditions/EnvironmentCondition.cs index 9e9fdc121f..5e60682d6e 100644 --- a/src/WebJobs.Script/Workers/Profiles/Conditions/EnvironmentCondition.cs +++ b/src/WebJobs.Script/Workers/Profiles/Conditions/EnvironmentCondition.cs @@ -30,8 +30,15 @@ internal EnvironmentCondition(ILogger logger, IEnvironment environment, WorkerPr _logger = logger ?? throw new ArgumentNullException(nameof(logger)); _environment = environment ?? throw new ArgumentNullException(nameof(environment)); - descriptor.Properties.TryGetValue(WorkerConstants.WorkerDescriptionProfileConditionName, out _name); - descriptor.Properties.TryGetValue(WorkerConstants.WorkerDescriptionProfileConditionExpression, out _expression); + if (descriptor.Properties.TryGetValue(WorkerConstants.WorkerDescriptionProfileConditionName, out var conditionNameElement)) + { + _name = conditionNameElement.GetString(); + } + + if (descriptor.Properties.TryGetValue(WorkerConstants.WorkerDescriptionProfileConditionExpression, out var conditionExpressionElement)) + { + _expression = conditionExpressionElement.GetString(); + } Validate(); } diff --git a/src/WebJobs.Script/Workers/Profiles/Conditions/HostPropertyCondition.cs b/src/WebJobs.Script/Workers/Profiles/Conditions/HostPropertyCondition.cs index be12a081b3..bdac80ee78 100644 --- a/src/WebJobs.Script/Workers/Profiles/Conditions/HostPropertyCondition.cs +++ b/src/WebJobs.Script/Workers/Profiles/Conditions/HostPropertyCondition.cs @@ -32,8 +32,15 @@ public HostPropertyCondition(ILogger logger, ISystemRuntimeInformation systemRun _logger = logger ?? throw new ArgumentNullException(nameof(logger)); _systemRuntimeInformation = systemRuntimeInformation ?? throw new ArgumentNullException(nameof(systemRuntimeInformation)); - descriptor.Properties.TryGetValue(WorkerConstants.WorkerDescriptionProfileConditionName, out _name); - descriptor.Properties.TryGetValue(WorkerConstants.WorkerDescriptionProfileConditionExpression, out _expression); + if (descriptor.Properties.TryGetValue(WorkerConstants.WorkerDescriptionProfileConditionName, out var conditionNameElement)) + { + _name = conditionNameElement.GetString(); + } + + if (descriptor.Properties.TryGetValue(WorkerConstants.WorkerDescriptionProfileConditionExpression, out var conditionExpressionElement)) + { + _expression = conditionExpressionElement.GetString(); + } Validate(); } diff --git a/src/WebJobs.Script/Workers/Profiles/WorkerProfileConditionDescriptor.cs b/src/WebJobs.Script/Workers/Profiles/WorkerProfileConditionDescriptor.cs index 29bf5022ea..400ba164bf 100644 --- a/src/WebJobs.Script/Workers/Profiles/WorkerProfileConditionDescriptor.cs +++ b/src/WebJobs.Script/Workers/Profiles/WorkerProfileConditionDescriptor.cs @@ -2,35 +2,18 @@ // Licensed under the MIT License. See License.txt in the project root for license information. using System.Collections.Generic; -using System.Linq; -using Newtonsoft.Json; -using Newtonsoft.Json.Linq; +using System.Text.Json; +using System.Text.Json.Serialization; namespace Microsoft.Azure.WebJobs.Script.Workers.Profiles { public sealed class WorkerProfileConditionDescriptor { - [JsonExtensionData] -#pragma warning disable CS0649 // The value is assigned by the serializer - private IDictionary _extensionData; -#pragma warning restore CS0649 - - private IDictionary _properties; - - [JsonProperty(Required = Required.Always, PropertyName = WorkerConstants.WorkerDescriptionProfileConditionType)] + [JsonRequired] + [JsonPropertyName(WorkerConstants.WorkerDescriptionProfileConditionType)] public string Type { get; set; } - public IDictionary Properties - { - get - { - if (_properties == null) - { - _properties = _extensionData?.ToDictionary(kv => kv.Key, kv => kv.Value.ToString()) ?? new Dictionary(); - } - - return _properties; - } - } + [JsonExtensionData] + public Dictionary Properties { get; set; } = new Dictionary(); } } diff --git a/src/WebJobs.Script/Workers/Rpc/Configuration/RpcWorkerConfigFactory.cs b/src/WebJobs.Script/Workers/Rpc/Configuration/RpcWorkerConfigFactory.cs index a4edaf88c7..bb05581073 100644 --- a/src/WebJobs.Script/Workers/Rpc/Configuration/RpcWorkerConfigFactory.cs +++ b/src/WebJobs.Script/Workers/Rpc/Configuration/RpcWorkerConfigFactory.cs @@ -5,12 +5,12 @@ using System.Collections.Generic; using System.IO; using System.Linq; +using System.Text.Json; using System.Text.RegularExpressions; using Microsoft.Azure.WebJobs.Script.Diagnostics; using Microsoft.Azure.WebJobs.Script.Workers.Profiles; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.Logging; -using Newtonsoft.Json.Linq; namespace Microsoft.Azure.WebJobs.Script.Workers.Rpc { @@ -24,6 +24,10 @@ internal class RpcWorkerConfigFactory private readonly IMetricsLogger _metricsLogger; private readonly string _workerRuntime; private readonly IEnvironment _environment; + private readonly JsonSerializerOptions _jsonSerializerOptions = new() + { + PropertyNameCaseInsensitive = true + }; private Dictionary _workerDescriptionDictionary = new Dictionary(); @@ -116,6 +120,18 @@ internal void AddProvider(string workerDir) { try { + // After specialization, load worker config only for the specified runtime unless it's a multi-language app. + if (!string.IsNullOrWhiteSpace(_workerRuntime) && !_environment.IsPlaceholderModeEnabled() && !_environment.IsMultiLanguageRuntimeEnvironment()) + { + string workerRuntime = Path.GetFileName(workerDir); + // Only skip worker directories that don't match the current runtime. + // Do not skip non-worker directories like the function app payload directory + if (!workerRuntime.Equals(_workerRuntime, StringComparison.OrdinalIgnoreCase) && workerDir.StartsWith(WorkersDirPath)) + { + return; + } + } + string workerConfigPath = Path.Combine(workerDir, RpcWorkerConstants.WorkerConfigFileName); if (!File.Exists(workerConfigPath)) @@ -126,15 +142,13 @@ internal void AddProvider(string workerDir) _logger.LogDebug("Found worker config: {workerConfigPath}", workerConfigPath); - // Parse worker config file - string json = File.ReadAllText(workerConfigPath); - JObject workerConfig = JObject.Parse(json); - RpcWorkerDescription workerDescription = workerConfig.Property(WorkerConstants.WorkerDescription).Value.ToObject(); + var workerConfig = GetWorkerConfigJsonElement(workerConfigPath); + var workerDescriptionElement = workerConfig.GetProperty(WorkerConstants.WorkerDescription); + var workerDescription = workerDescriptionElement.Deserialize(_jsonSerializerOptions); workerDescription.WorkerDirectory = workerDir; - //Read the profiles from worker description and load the profile for which the conditions match - JToken profiles = workerConfig.GetValue(WorkerConstants.WorkerDescriptionProfiles); - if (profiles != null) + // Read the profiles from worker description and load the profile for which the conditions match + if (workerConfig.TryGetProperty(WorkerConstants.WorkerDescriptionProfiles, out var profiles)) { List workerDescriptionProfiles = ReadWorkerDescriptionProfiles(profiles); if (workerDescriptionProfiles.Count > 0) @@ -146,7 +160,7 @@ internal void AddProvider(string workerDir) // Check if any app settings are provided for that language var languageSection = _config.GetSection($"{RpcWorkerConstants.LanguageWorkersSectionName}:{workerDescription.Language}"); - workerDescription.Arguments = workerDescription.Arguments ?? new List(); + workerDescription.Arguments ??= new List(); GetWorkerDescriptionFromAppSettings(workerDescription, languageSection); AddArgumentsFromAppSettings(workerDescription, languageSection); @@ -197,9 +211,24 @@ internal void AddProvider(string workerDir) } } - private List ReadWorkerDescriptionProfiles(JToken profilesJToken) + private static JsonElement GetWorkerConfigJsonElement(string workerConfigPath) { - var profiles = profilesJToken.ToObject>(); + ReadOnlySpan jsonSpan = File.ReadAllBytes(workerConfigPath); + + if (jsonSpan.StartsWith([0xEF, 0xBB, 0xBF])) + { + jsonSpan = jsonSpan[3..]; // Skip UTF-8 Byte Order Mark (BOM) if present at the beginning of the file. + } + + var reader = new Utf8JsonReader(jsonSpan, isFinalBlock: true, state: default); + using var doc = JsonDocument.ParseValue(ref reader); + + return doc.RootElement.Clone(); + } + + private List ReadWorkerDescriptionProfiles(JsonElement profilesElement) + { + var profiles = profilesElement.Deserialize>(_jsonSerializerOptions); if (profiles == null || profiles.Count <= 0) { @@ -237,11 +266,16 @@ private List ReadWorkerDescriptionProfiles(JToken prof return descriptionProfiles; } - internal WorkerProcessCountOptions GetWorkerProcessCount(JObject workerConfig) + internal WorkerProcessCountOptions GetWorkerProcessCount(JsonElement workerConfig) { - WorkerProcessCountOptions workerProcessCount = workerConfig.Property(WorkerConstants.ProcessCount)?.Value.ToObject(); + WorkerProcessCountOptions workerProcessCount = null; + + if (workerConfig.TryGetProperty(WorkerConstants.ProcessCount, out var processCountElement)) + { + workerProcessCount = processCountElement.Deserialize(); + } - workerProcessCount = workerProcessCount ?? new WorkerProcessCountOptions(); + workerProcessCount ??= new WorkerProcessCountOptions(); if (workerProcessCount.SetProcessCountToNumberOfCpuCores) { diff --git a/test/WebJobs.Script.Tests/Workers/Profiles/EnvironmentConditionTests.cs b/test/WebJobs.Script.Tests/Workers/Profiles/EnvironmentConditionTests.cs index ca6431a113..4d3eb2ab58 100644 --- a/test/WebJobs.Script.Tests/Workers/Profiles/EnvironmentConditionTests.cs +++ b/test/WebJobs.Script.Tests/Workers/Profiles/EnvironmentConditionTests.cs @@ -1,8 +1,8 @@ // 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.ComponentModel.DataAnnotations; +using System.Text.Json; using Microsoft.Azure.WebJobs.Script.Workers; using Microsoft.Azure.WebJobs.Script.Workers.Profiles; using Xunit; @@ -28,8 +28,8 @@ public void EnvironmentConditionTest_ThrowsValidationException(string name, stri var descriptor = new WorkerProfileConditionDescriptor(); descriptor.Type = WorkerConstants.WorkerDescriptionProfileEnvironmentCondition; - descriptor.Properties[WorkerConstants.WorkerDescriptionProfileConditionName] = name; - descriptor.Properties[WorkerConstants.WorkerDescriptionProfileConditionExpression] = expression; + descriptor.Properties[WorkerConstants.WorkerDescriptionProfileConditionName] = JsonSerializer.SerializeToElement(name); + descriptor.Properties[WorkerConstants.WorkerDescriptionProfileConditionExpression] = JsonSerializer.SerializeToElement(expression); Assert.Throws(() => new EnvironmentCondition(testLogger, _testEnvironment, descriptor)); } @@ -45,8 +45,8 @@ public void EnvironmentConditionTest_EvaluateTrue(string name, string testExpres var descriptor = new WorkerProfileConditionDescriptor(); descriptor.Type = WorkerConstants.WorkerDescriptionProfileEnvironmentCondition; - descriptor.Properties[WorkerConstants.WorkerDescriptionProfileConditionName] = name; - descriptor.Properties[WorkerConstants.WorkerDescriptionProfileConditionExpression] = testExpression; + descriptor.Properties[WorkerConstants.WorkerDescriptionProfileConditionName] = JsonSerializer.SerializeToElement(name); + descriptor.Properties[WorkerConstants.WorkerDescriptionProfileConditionExpression] = JsonSerializer.SerializeToElement(testExpression); var environmentCondition = new EnvironmentCondition(testLogger, _testEnvironment, descriptor); @@ -64,8 +64,8 @@ public void EnvironmentConditionTest_EvaluateFalse(string name, string testExpre var descriptor = new WorkerProfileConditionDescriptor(); descriptor.Type = WorkerConstants.WorkerDescriptionProfileEnvironmentCondition; - descriptor.Properties[WorkerConstants.WorkerDescriptionProfileConditionName] = name; - descriptor.Properties[WorkerConstants.WorkerDescriptionProfileConditionExpression] = testExpression; + descriptor.Properties[WorkerConstants.WorkerDescriptionProfileConditionName] = JsonSerializer.SerializeToElement(name); + descriptor.Properties[WorkerConstants.WorkerDescriptionProfileConditionExpression] = JsonSerializer.SerializeToElement(testExpression); var environmentCondition = new EnvironmentCondition(testLogger, _testEnvironment, descriptor); diff --git a/test/WebJobs.Script.Tests/Workers/Profiles/HostPropertyConditionTests.cs b/test/WebJobs.Script.Tests/Workers/Profiles/HostPropertyConditionTests.cs index 5d33e10f2f..15e3f28fa7 100644 --- a/test/WebJobs.Script.Tests/Workers/Profiles/HostPropertyConditionTests.cs +++ b/test/WebJobs.Script.Tests/Workers/Profiles/HostPropertyConditionTests.cs @@ -2,6 +2,8 @@ // Licensed under the MIT License. See License.txt in the project root for license information. using System.ComponentModel.DataAnnotations; +using System.Linq.Expressions; +using System.Text.Json; using Microsoft.Azure.WebJobs.Script.Workers; using Microsoft.Azure.WebJobs.Script.Workers.Profiles; using Xunit; @@ -29,8 +31,8 @@ public void HostPropertyConditionTest_ThrowsValidationException(string name, str var descriptor = new WorkerProfileConditionDescriptor(); descriptor.Type = WorkerConstants.WorkerDescriptionProfileHostPropertyCondition; - descriptor.Properties[WorkerConstants.WorkerDescriptionProfileConditionName] = name; - descriptor.Properties[WorkerConstants.WorkerDescriptionProfileConditionExpression] = expression; + descriptor.Properties[WorkerConstants.WorkerDescriptionProfileConditionName] = JsonSerializer.SerializeToElement(name); + descriptor.Properties[WorkerConstants.WorkerDescriptionProfileConditionExpression] = JsonSerializer.SerializeToElement(expression); Assert.Throws(() => new HostPropertyCondition(testLogger, _testSystemRuntimeInfo, descriptor)); } @@ -45,8 +47,8 @@ public void HostPropertyConditionTest_EvaluateTrue(string name, string testExpre var descriptor = new WorkerProfileConditionDescriptor(); descriptor.Type = WorkerConstants.WorkerDescriptionProfileHostPropertyCondition; - descriptor.Properties[WorkerConstants.WorkerDescriptionProfileConditionName] = name; - descriptor.Properties[WorkerConstants.WorkerDescriptionProfileConditionExpression] = testExpression; + descriptor.Properties[WorkerConstants.WorkerDescriptionProfileConditionName] = JsonSerializer.SerializeToElement(name); + descriptor.Properties[WorkerConstants.WorkerDescriptionProfileConditionExpression] = JsonSerializer.SerializeToElement(testExpression); var hostPropertyCondition = new HostPropertyCondition(testLogger, _testSystemRuntimeInfo, descriptor); @@ -63,8 +65,8 @@ public void HostPropertyConditionTest_EvaluateFalse(string name, string testExpr var descriptor = new WorkerProfileConditionDescriptor(); descriptor.Type = WorkerConstants.WorkerDescriptionProfileHostPropertyCondition; - descriptor.Properties[WorkerConstants.WorkerDescriptionProfileConditionName] = name; - descriptor.Properties[WorkerConstants.WorkerDescriptionProfileConditionExpression] = testExpression; + descriptor.Properties[WorkerConstants.WorkerDescriptionProfileConditionName] = JsonSerializer.SerializeToElement(name); + descriptor.Properties[WorkerConstants.WorkerDescriptionProfileConditionExpression] = JsonSerializer.SerializeToElement(testExpression); var hostPropertyCondition = new HostPropertyCondition(testLogger, _testSystemRuntimeInfo, descriptor); diff --git a/test/WebJobs.Script.Tests/Workers/Profiles/ProfilesTestUtilities.cs b/test/WebJobs.Script.Tests/Workers/Profiles/ProfilesTestUtilities.cs index a25b790705..eb81f1efa7 100644 --- a/test/WebJobs.Script.Tests/Workers/Profiles/ProfilesTestUtilities.cs +++ b/test/WebJobs.Script.Tests/Workers/Profiles/ProfilesTestUtilities.cs @@ -1,6 +1,7 @@ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the MIT License. See License.txt in the project root for license information. +using System.Text.Json; using Microsoft.Azure.WebJobs.Script.Workers; using Microsoft.Azure.WebJobs.Script.Workers.Profiles; using Microsoft.Extensions.Logging; @@ -29,8 +30,8 @@ public static EnvironmentCondition GetTestEnvironmentCondition(ILogger logger, T { var descriptor = new WorkerProfileConditionDescriptor(); descriptor.Type = WorkerConstants.WorkerDescriptionProfileEnvironmentCondition; - descriptor.Properties[WorkerConstants.WorkerDescriptionProfileConditionName] = name; - descriptor.Properties[WorkerConstants.WorkerDescriptionProfileConditionExpression] = expression; + descriptor.Properties[WorkerConstants.WorkerDescriptionProfileConditionName] = JsonSerializer.SerializeToElement(name); + descriptor.Properties[WorkerConstants.WorkerDescriptionProfileConditionExpression] = JsonSerializer.SerializeToElement(expression); return new EnvironmentCondition(logger, testEnvironment, descriptor); } @@ -39,8 +40,8 @@ public static HostPropertyCondition GetTestHostPropertyCondition(ILogger logger, { var descriptor = new WorkerProfileConditionDescriptor(); descriptor.Type = WorkerConstants.WorkerDescriptionProfileHostPropertyCondition; - descriptor.Properties[WorkerConstants.WorkerDescriptionProfileConditionName] = name; - descriptor.Properties[WorkerConstants.WorkerDescriptionProfileConditionExpression] = expression; + descriptor.Properties[WorkerConstants.WorkerDescriptionProfileConditionName] = JsonSerializer.SerializeToElement(name); + descriptor.Properties[WorkerConstants.WorkerDescriptionProfileConditionExpression] = JsonSerializer.SerializeToElement(expression); return new HostPropertyCondition(logger, testSystemRuntimeInfo, descriptor); } diff --git a/test/WebJobs.Script.Tests/Workers/Profiles/WorkerProfileConditionDescriptorTests.cs b/test/WebJobs.Script.Tests/Workers/Profiles/WorkerProfileConditionDescriptorTests.cs index 0abe39e31d..58a886d8f0 100644 --- a/test/WebJobs.Script.Tests/Workers/Profiles/WorkerProfileConditionDescriptorTests.cs +++ b/test/WebJobs.Script.Tests/Workers/Profiles/WorkerProfileConditionDescriptorTests.cs @@ -1,10 +1,10 @@ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the MIT License. See License.txt in the project root for license information. +using System.Text.Json; +using System.Text.Json.Nodes; using Microsoft.Azure.WebJobs.Script.Workers; using Microsoft.Azure.WebJobs.Script.Workers.Profiles; -using Newtonsoft.Json; -using Newtonsoft.Json.Linq; using Xunit; namespace Microsoft.Azure.WebJobs.Script.Tests.Workers.Profiles @@ -14,20 +14,22 @@ public class WorkerProfileConditionDescriptorTests [Fact] public void ConditionDescriptor_ConvertsJObject() { - var conditionJObject = new JObject(); - conditionJObject[WorkerConstants.WorkerDescriptionProfileConditionName] = WorkerConstants.WorkerDescriptionProfileConditionName; - conditionJObject[WorkerConstants.WorkerDescriptionProfileConditionExpression] = WorkerConstants.WorkerDescriptionProfileConditionExpression; + var conditionJObject = new JsonObject + { + [WorkerConstants.WorkerDescriptionProfileConditionName] = WorkerConstants.WorkerDescriptionProfileConditionName, + [WorkerConstants.WorkerDescriptionProfileConditionExpression] = WorkerConstants.WorkerDescriptionProfileConditionExpression + }; - Assert.Throws(() => conditionJObject.ToObject()); + Assert.Throws(() => conditionJObject.Deserialize()); conditionJObject[WorkerConstants.WorkerDescriptionProfileConditionType] = WorkerConstants.WorkerDescriptionProfileEnvironmentCondition; - var conditionDescriptor = conditionJObject.ToObject(); - conditionDescriptor.Properties.TryGetValue(WorkerConstants.WorkerDescriptionProfileConditionName, out string conditionDescriptorName); - conditionDescriptor.Properties.TryGetValue(WorkerConstants.WorkerDescriptionProfileConditionExpression, out string conditionDescriptorExpression); + var conditionDescriptor = conditionJObject.Deserialize(); + conditionDescriptor.Properties.TryGetValue(WorkerConstants.WorkerDescriptionProfileConditionName, out var conditionDescriptorName); + conditionDescriptor.Properties.TryGetValue(WorkerConstants.WorkerDescriptionProfileConditionExpression, out var conditionDescriptorExpression); - Assert.Equal(WorkerConstants.WorkerDescriptionProfileConditionName, conditionDescriptorName); - Assert.Equal(WorkerConstants.WorkerDescriptionProfileConditionExpression, conditionDescriptorExpression); + Assert.Equal(WorkerConstants.WorkerDescriptionProfileConditionName, conditionDescriptorName.GetString()); + Assert.Equal(WorkerConstants.WorkerDescriptionProfileConditionExpression, conditionDescriptorExpression.GetString()); Assert.Equal(WorkerConstants.WorkerDescriptionProfileEnvironmentCondition, conditionDescriptor.Type); } } diff --git a/test/WebJobs.Script.Tests/Workers/Profiles/WorkerProfileManagerTests.cs b/test/WebJobs.Script.Tests/Workers/Profiles/WorkerProfileManagerTests.cs index cf7ca3dbef..2cf8c283fd 100644 --- a/test/WebJobs.Script.Tests/Workers/Profiles/WorkerProfileManagerTests.cs +++ b/test/WebJobs.Script.Tests/Workers/Profiles/WorkerProfileManagerTests.cs @@ -2,6 +2,8 @@ // Licensed under the MIT License. See License.txt in the project root for license information. using System.Collections.Generic; +using System.Text.Json; +using System.Text.Json.Nodes; using Microsoft.Azure.WebJobs.Script.Tests.Workers.Rpc; using Microsoft.Azure.WebJobs.Script.Workers; using Microsoft.Azure.WebJobs.Script.Workers.Profiles; @@ -53,11 +55,14 @@ public void LoadWorkerDescriptionFromProfiles_NoConditionsMet_ReturnsDefaultDesc [Fact] public void TryCreateWorkerProfileCondition_ValidCondition_ReturnsTrue() { - var conditionJObject = new JObject(); - conditionJObject[WorkerConstants.WorkerDescriptionProfileConditionType] = "environment"; - conditionJObject[WorkerConstants.WorkerDescriptionProfileConditionName] = "PROFILE_TEST_ENVIRONMENT_VARIABLE"; - conditionJObject[WorkerConstants.WorkerDescriptionProfileConditionExpression] = "true"; - var conditionDescriptor = conditionJObject.ToObject(); + var conditionJObject = new JsonObject + { + [WorkerConstants.WorkerDescriptionProfileConditionType] = "environment", + [WorkerConstants.WorkerDescriptionProfileConditionName] = "PROFILE_TEST_ENVIRONMENT_VARIABLE", + [WorkerConstants.WorkerDescriptionProfileConditionExpression] = "true" + }; + + WorkerProfileConditionDescriptor conditionDescriptor = conditionJObject.Deserialize(); WorkerProfileManager profileManager = new(_testLogger, _testEnvironment); var result = profileManager.TryCreateWorkerProfileCondition(conditionDescriptor, out var condition); diff --git a/test/WebJobs.Script.Tests/Workers/Rpc/RpcWorkerConfigFactoryTests.cs b/test/WebJobs.Script.Tests/Workers/Rpc/RpcWorkerConfigFactoryTests.cs index 7d5f50486a..983f16bf87 100644 --- a/test/WebJobs.Script.Tests/Workers/Rpc/RpcWorkerConfigFactoryTests.cs +++ b/test/WebJobs.Script.Tests/Workers/Rpc/RpcWorkerConfigFactoryTests.cs @@ -5,12 +5,12 @@ using System.Collections.Generic; using System.IO; using System.Linq; +using System.Text.Json; using Microsoft.Azure.WebJobs.Script.Config; using Microsoft.Azure.WebJobs.Script.Workers; using Microsoft.Azure.WebJobs.Script.Workers.Profiles; using Microsoft.Azure.WebJobs.Script.Workers.Rpc; using Microsoft.Extensions.Configuration; -using Newtonsoft.Json.Linq; using Xunit; namespace Microsoft.Azure.WebJobs.Script.Tests.Workers.Rpc @@ -115,7 +115,7 @@ public void WorkerDescription_Skipped_When_Profile_Disables_Worker() var configFactory = new RpcWorkerConfigFactory(config, testLogger, _testSysRuntimeInfo, _testEnvironment, new TestMetricsLogger(), _testWorkerProfileManager); var workerConfigs = configFactory.GetConfigs(); - + var errors = testLogger.GetLogMessages().Where(m => m.Exception != null).ToList(); Assert.False(testLogger.GetLogMessages().Any(m => m.Exception != null), "There should not be an exception logged while executing GetConfigs method."); Assert.Equal(1, workerConfigs.Count); Assert.EndsWith("worker1\\1.bat", workerConfigs[0].Description.DefaultWorkerPath); @@ -217,18 +217,30 @@ public void ShouldAddProvider_Returns_Expected(string workerLanguage, string wor [InlineData(false, true, true, 4, 8, "00:00:05")] public void GetWorkerProcessCount_Tests(bool defaultWorkerConfig, bool setProcessCountToNumberOfCpuCores, bool setWorkerCountInEnv, int minProcessCount, int maxProcessCount, string processStartupInterval) { - JObject processCount = new JObject(); - processCount["ProcessCount"] = minProcessCount; - processCount["MaxProcessCount"] = maxProcessCount; - processCount["ProcessStartupInterval"] = processStartupInterval; - processCount["SetProcessCountToNumberOfCpuCores"] = setProcessCountToNumberOfCpuCores; - - JObject workerConfig = new JObject(); - if (!defaultWorkerConfig) + using var stream = new MemoryStream(); + using (var writer = new Utf8JsonWriter(stream)) { - workerConfig[WorkerConstants.ProcessCount] = processCount; + writer.WriteStartObject(); + + if (!defaultWorkerConfig) + { + writer.WritePropertyName(WorkerConstants.ProcessCount); + writer.WriteStartObject(); + + writer.WriteNumber("ProcessCount", minProcessCount); + writer.WriteNumber("MaxProcessCount", maxProcessCount); + writer.WriteString("ProcessStartupInterval", processStartupInterval); + writer.WriteBoolean("SetProcessCountToNumberOfCpuCores", setProcessCountToNumberOfCpuCores); + + writer.WriteEndObject(); + } + + writer.WriteEndObject(); } + using var jsonDocument = JsonDocument.Parse(stream.ToArray()); + JsonElement workerConfig = jsonDocument.RootElement; + if (setWorkerCountInEnv) { _testEnvironment.SetEnvironmentVariable(RpcWorkerConstants.FunctionsWorkerProcessCountSettingName, "7"); @@ -273,14 +285,7 @@ public void GetWorkerProcessCount_Tests(bool defaultWorkerConfig, bool setProces [Fact] public void GetWorkerProcessCount_ThrowsException_Tests() { - JObject processCount = new JObject(); - processCount["ProcessCount"] = -4; - processCount["MaxProcessCount"] = 10; - processCount["ProcessStartupInterval"] = "00:10:00"; - processCount["SetProcessCountToNumberOfCpuCores"] = false; - - JObject workerConfig = new JObject(); - workerConfig[WorkerConstants.ProcessCount] = processCount; + JsonElement workerConfig = CreateWorkerConfig(-4, 10, "00:10:00", false); var config = new ConfigurationBuilder().Build(); var testLogger = new TestLogger("test"); @@ -288,14 +293,35 @@ public void GetWorkerProcessCount_ThrowsException_Tests() var resultEx1 = Assert.Throws(() => rpcWorkerConfigFactory.GetWorkerProcessCount(workerConfig)); Assert.Contains("ProcessCount must be greater than 0", resultEx1.Message); - processCount["ProcessCount"] = 40; + workerConfig = CreateWorkerConfig(40, 10, "00:10:00", false); var resultEx2 = Assert.Throws(() => rpcWorkerConfigFactory.GetWorkerProcessCount(workerConfig)); Assert.Contains("ProcessCount must not be greater than MaxProcessCount", resultEx2.Message); - processCount["ProcessStartupInterval"] = "-800"; - processCount["ProcessCount"] = 10; - var resultEx3 = Assert.Throws(() => rpcWorkerConfigFactory.GetWorkerProcessCount(workerConfig)); - Assert.Contains("The TimeSpan must not be negative", resultEx3.Message); + workerConfig = CreateWorkerConfig(10, 10, "-800", false); + var resultEx3 = Assert.Throws(() => rpcWorkerConfigFactory.GetWorkerProcessCount(workerConfig)); + Assert.Contains("value could not be converted to System.TimeSpan", resultEx3.Message); + } + + private static JsonElement CreateWorkerConfig(int processCount, int maxProcessCount, string processStartupInterval, bool setProcessCountToCores) + { + using var stream = new MemoryStream(); + using (var writer = new Utf8JsonWriter(stream)) + { + writer.WriteStartObject(); + writer.WritePropertyName(WorkerConstants.ProcessCount); + + writer.WriteStartObject(); + writer.WriteNumber("ProcessCount", processCount); + writer.WriteNumber("MaxProcessCount", maxProcessCount); + writer.WriteString("ProcessStartupInterval", processStartupInterval); + writer.WriteBoolean("SetProcessCountToNumberOfCpuCores", setProcessCountToCores); + writer.WriteEndObject(); + + writer.WriteEndObject(); + } + + using var doc = JsonDocument.Parse(stream.ToArray()); + return doc.RootElement.Clone(); } } } \ No newline at end of file