From 04b665c9c684c4fda17adee2ba3ae965944ab571 Mon Sep 17 00:00:00 2001 From: Shreyas Gopalakrishna Date: Wed, 20 Apr 2022 14:45:18 -0500 Subject: [PATCH 1/9] Conditional based profiles --- .../Workers/Profiles/EnvironmentCondition.cs | 62 ++++++++++++ .../Workers/Profiles/HostPropertyCondition.cs | 92 ++++++++++++++++++ .../Profiles/IWorkerProfileCondition.cs | 10 ++ .../IWorkerProfileConditionFactory.cs | 12 +++ .../Profiles/WorkerDescriptionProfile.cs | 94 +++++++++++++++++++ .../Profiles/WorkerProfileConditionFactory.cs | 36 +++++++ .../Rpc/Configuration/RpcWorkerConfig.cs | 2 + .../Configuration/RpcWorkerConfigFactory.cs | 54 +++++++++++ .../Rpc/WebHostRpcWorkerChannelManager.cs | 12 +++ src/WebJobs.Script/Workers/WorkerConstants.cs | 5 + 10 files changed, 379 insertions(+) create mode 100644 src/WebJobs.Script/Workers/Profiles/EnvironmentCondition.cs create mode 100644 src/WebJobs.Script/Workers/Profiles/HostPropertyCondition.cs create mode 100644 src/WebJobs.Script/Workers/Profiles/IWorkerProfileCondition.cs create mode 100644 src/WebJobs.Script/Workers/Profiles/IWorkerProfileConditionFactory.cs create mode 100644 src/WebJobs.Script/Workers/Profiles/WorkerDescriptionProfile.cs create mode 100644 src/WebJobs.Script/Workers/Profiles/WorkerProfileConditionFactory.cs diff --git a/src/WebJobs.Script/Workers/Profiles/EnvironmentCondition.cs b/src/WebJobs.Script/Workers/Profiles/EnvironmentCondition.cs new file mode 100644 index 0000000000..242f91ba2e --- /dev/null +++ b/src/WebJobs.Script/Workers/Profiles/EnvironmentCondition.cs @@ -0,0 +1,62 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System; +using System.ComponentModel.DataAnnotations; +using System.Text.RegularExpressions; +using Microsoft.Extensions.Logging; + +namespace Microsoft.Azure.WebJobs.Script.Workers +{ + public class EnvironmentCondition : IWorkerProfileCondition + { + private readonly ILogger _logger; + private readonly IEnvironment _environment; + + public EnvironmentCondition(ILogger logger, IEnvironment environment, string name, string expression) + { + _logger = logger ?? throw new ArgumentNullException(nameof(logger)); + _environment = environment ?? throw new ArgumentNullException(nameof(environment)); + Name = name; + Expression = expression; + Validate(); + } + + public string Name { get; set; } + + public string Expression { get; set; } + + public bool Evaluate() + { + string value = _environment.GetEnvironmentVariable(Name); + if (string.IsNullOrEmpty(value)) + { + return false; + } + _logger.LogDebug($"Evaluating EnvironmentCondition with value: {value} and expression {Expression}"); + return Regex.IsMatch(value, Expression); + } + + public void Validate() + { + if (string.IsNullOrEmpty(Name)) + { + throw new ValidationException($"EnvironmentCondition {nameof(Name)} cannot be empty."); + } + + if (string.IsNullOrEmpty(Expression)) + { + throw new ValidationException($"EnvironmentCondition {nameof(Expression)} cannot be empty."); + } + + try + { + new Regex(Expression); + } + catch + { + throw new ValidationException($"EnvironmentCondition {nameof(Expression)} must be a valid regular expression."); + } + } + } +} \ No newline at end of file diff --git a/src/WebJobs.Script/Workers/Profiles/HostPropertyCondition.cs b/src/WebJobs.Script/Workers/Profiles/HostPropertyCondition.cs new file mode 100644 index 0000000000..12a3c370a8 --- /dev/null +++ b/src/WebJobs.Script/Workers/Profiles/HostPropertyCondition.cs @@ -0,0 +1,92 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System; +using System.ComponentModel.DataAnnotations; +using System.Linq; +using System.Text.RegularExpressions; +using Microsoft.Azure.WebJobs.Script.Config; +using Microsoft.Extensions.Logging; + +namespace Microsoft.Azure.WebJobs.Script.Workers +{ + public class HostPropertyCondition : IWorkerProfileCondition + { + private readonly ILogger _logger; + private readonly ISystemRuntimeInformation _systemRuntimeInformation; + + public HostPropertyCondition(ILogger logger, ISystemRuntimeInformation systemRuntimeInformation, string name, string expression) + { + _logger = logger ?? throw new ArgumentNullException(nameof(logger)); + _systemRuntimeInformation = systemRuntimeInformation ?? throw new ArgumentNullException(nameof(systemRuntimeInformation)); + Name = name; + Expression = expression; + Validate(); + } + + public enum ConditionHostPropertyName + { + Sku, + Platform, + HostVersion + } + + public string Name { get; set; } + + public string Expression { get; set; } + + public bool Evaluate() + { + string value = string.Empty; + Enum.TryParse(Name, out ConditionHostPropertyName hostPropertyName); + switch (hostPropertyName) + { + case ConditionHostPropertyName.Sku: + value = ScriptSettingsManager.Instance.GetSetting(EnvironmentSettingNames.AzureWebsiteSku); + break; + case ConditionHostPropertyName.Platform: + value = _systemRuntimeInformation.GetOSPlatform().ToString(); + break; + case ConditionHostPropertyName.HostVersion: + value = ScriptHost.Version; + break; + default: + break; + } + + if (string.IsNullOrEmpty(value)) + { + return false; + } + _logger.LogDebug($"Evaluating HostPropertyCondition with value: {value} and expression {Expression}"); + return Regex.IsMatch(value, Expression); + } + + public void Validate() + { + if (string.IsNullOrEmpty(Name)) + { + throw new ValidationException($"HostPropertyCondition {nameof(Name)} cannot be empty."); + } + + if (!Enum.GetNames(typeof(ConditionHostPropertyName)).Any(x => x.ToLower().Contains(Name))) + { + throw new ValidationException($"HostPropertyCondition {nameof(Name)} is not a valid host property name."); + } + + if (string.IsNullOrEmpty(Expression)) + { + throw new ValidationException($"HostPropertyCondition {nameof(Expression)} cannot be empty."); + } + + try + { + new Regex(Expression); + } + catch + { + throw new ValidationException($"HostPropertyCondition {nameof(Expression)} must be a valid regular expression."); + } + } + } +} \ No newline at end of file diff --git a/src/WebJobs.Script/Workers/Profiles/IWorkerProfileCondition.cs b/src/WebJobs.Script/Workers/Profiles/IWorkerProfileCondition.cs new file mode 100644 index 0000000000..e8987eeb15 --- /dev/null +++ b/src/WebJobs.Script/Workers/Profiles/IWorkerProfileCondition.cs @@ -0,0 +1,10 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the MIT License. See License.txt in the project root for license information. + +namespace Microsoft.Azure.WebJobs.Script.Workers +{ + public interface IWorkerProfileCondition + { + bool Evaluate(); + } +} \ No newline at end of file diff --git a/src/WebJobs.Script/Workers/Profiles/IWorkerProfileConditionFactory.cs b/src/WebJobs.Script/Workers/Profiles/IWorkerProfileConditionFactory.cs new file mode 100644 index 0000000000..8e7e91e861 --- /dev/null +++ b/src/WebJobs.Script/Workers/Profiles/IWorkerProfileConditionFactory.cs @@ -0,0 +1,12 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System.Collections.Generic; + +namespace Microsoft.Azure.WebJobs.Script.Workers +{ + internal interface IWorkerProfileConditionFactory + { + IWorkerProfileCondition CreateWorkerProfileCondition(IDictionary properties); + } +} diff --git a/src/WebJobs.Script/Workers/Profiles/WorkerDescriptionProfile.cs b/src/WebJobs.Script/Workers/Profiles/WorkerDescriptionProfile.cs new file mode 100644 index 0000000000..0a34e7e6d4 --- /dev/null +++ b/src/WebJobs.Script/Workers/Profiles/WorkerDescriptionProfile.cs @@ -0,0 +1,94 @@ +// 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.ComponentModel.DataAnnotations; +using Microsoft.Azure.WebJobs.Script.Workers.Rpc; + +namespace Microsoft.Azure.WebJobs.Script.Workers +{ + public class WorkerDescriptionProfile + { + public WorkerDescriptionProfile(string name, List conditions, RpcWorkerDescription profileDescription) + { + Name = name; + Conditions = conditions; + ProfileDescription = profileDescription; + ProfileLoaded = false; + Validate(); + } + + /// + /// Gets or sets the name of the profile. + /// + public string Name { get; set; } + + /// + /// Gets or sets the conditions which must be met for the profile to be used. + /// + public List Conditions { get; set; } + + /// + /// Gets or sets the worker description for the profile + /// + public RpcWorkerDescription ProfileDescription { get; set; } + + public bool ProfileLoaded { get; set; } + + public void Validate() + { + if (string.IsNullOrEmpty(Name)) + { + throw new ValidationException($"WorkerDescriptionProfile {nameof(Name)} cannot be empty"); + } + + if (Conditions == null || Conditions.Count < 1) + { + throw new ValidationException($"WorkerDescriptionProfile {nameof(Conditions)} cannot be empty"); + } + + if (ProfileDescription == null) + { + throw new ValidationException($"WorkerDescriptionProfile {nameof(ProfileDescription)} cannot be null"); + } + } + + public bool EvaluateConditions() + { + foreach (var condition in Conditions) + { + if (!condition.Evaluate()) + { + return false; + } + } + return true; + } + + /// + /// Overrides the worker description parameters to that available in the profile + /// + public RpcWorkerDescription ApplyProfile(RpcWorkerDescription defaultWorkerDescription) + { + ProfileLoaded = true; + defaultWorkerDescription.Arguments = UseProfileOrDefault(ProfileDescription.Arguments, defaultWorkerDescription.Arguments); + defaultWorkerDescription.DefaultExecutablePath = UseProfileOrDefault(ProfileDescription.DefaultExecutablePath, defaultWorkerDescription.DefaultExecutablePath); + defaultWorkerDescription.DefaultWorkerPath = UseProfileOrDefault(ProfileDescription.DefaultWorkerPath, defaultWorkerDescription.DefaultWorkerPath); + defaultWorkerDescription.Extensions = UseProfileOrDefault(ProfileDescription.Extensions, defaultWorkerDescription.Extensions) as List; + defaultWorkerDescription.Language = UseProfileOrDefault(ProfileDescription.Language, defaultWorkerDescription.Language); + defaultWorkerDescription.WorkerDirectory = UseProfileOrDefault(ProfileDescription.WorkerDirectory, defaultWorkerDescription.WorkerDirectory); + return defaultWorkerDescription; + } + + private string UseProfileOrDefault(string profileParameter, string defaultParameter) + { + return string.IsNullOrEmpty(profileParameter) ? defaultParameter : profileParameter; + } + + private IList UseProfileOrDefault(IList profileParameter, IList defaultParameter) + { + return profileParameter.Count > 0 ? profileParameter : defaultParameter; + } + } +} \ No newline at end of file diff --git a/src/WebJobs.Script/Workers/Profiles/WorkerProfileConditionFactory.cs b/src/WebJobs.Script/Workers/Profiles/WorkerProfileConditionFactory.cs new file mode 100644 index 0000000000..44f2a0fbb6 --- /dev/null +++ b/src/WebJobs.Script/Workers/Profiles/WorkerProfileConditionFactory.cs @@ -0,0 +1,36 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System; +using System.Collections.Generic; +using Microsoft.Extensions.Logging; + +namespace Microsoft.Azure.WebJobs.Script.Workers +{ + internal class WorkerProfileConditionFactory : IWorkerProfileConditionFactory + { + private readonly ILogger _logger; + private readonly ISystemRuntimeInformation _systemRuntimeInformation; + private readonly IEnvironment _environment; + + public WorkerProfileConditionFactory(ILogger logger, ISystemRuntimeInformation systemRuntimeInfo, IEnvironment environment) + { + _logger = logger ?? throw new ArgumentNullException(nameof(logger)); + _environment = environment ?? throw new ArgumentNullException(nameof(environment)); + _systemRuntimeInformation = systemRuntimeInfo ?? throw new ArgumentNullException(nameof(systemRuntimeInfo)); + } + + public IWorkerProfileCondition CreateWorkerProfileCondition(IDictionary properties) + { + switch (properties[WorkerConstants.WorkerDescriptionProfileConditionType]) + { + case WorkerConstants.WorkerDescriptionProfileHostPropertyCondition: + return new HostPropertyCondition(_logger, _systemRuntimeInformation, (string)properties["name"], (string)properties["expression"]); + case WorkerConstants.WorkerDescriptionProfileEnvironmentCondition: + return new EnvironmentCondition(_logger, _environment, (string)properties["name"], (string)properties["expression"]); + default: + throw new ArgumentException(nameof(WorkerConstants.WorkerDescriptionProfileConditionType)); + } + } + } +} diff --git a/src/WebJobs.Script/Workers/Rpc/Configuration/RpcWorkerConfig.cs b/src/WebJobs.Script/Workers/Rpc/Configuration/RpcWorkerConfig.cs index 6d5a62219d..9c1f8ea307 100644 --- a/src/WebJobs.Script/Workers/Rpc/Configuration/RpcWorkerConfig.cs +++ b/src/WebJobs.Script/Workers/Rpc/Configuration/RpcWorkerConfig.cs @@ -10,5 +10,7 @@ public class RpcWorkerConfig public WorkerProcessArguments Arguments { get; set; } public WorkerProcessCountOptions CountOptions { get; set; } + + public List Profiles { get; set; } } } diff --git a/src/WebJobs.Script/Workers/Rpc/Configuration/RpcWorkerConfigFactory.cs b/src/WebJobs.Script/Workers/Rpc/Configuration/RpcWorkerConfigFactory.cs index 80bf16f5f0..9ee2d99204 100644 --- a/src/WebJobs.Script/Workers/Rpc/Configuration/RpcWorkerConfigFactory.cs +++ b/src/WebJobs.Script/Workers/Rpc/Configuration/RpcWorkerConfigFactory.cs @@ -9,6 +9,7 @@ using Microsoft.Azure.WebJobs.Script.Diagnostics; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.Logging; +using Newtonsoft.Json; using Newtonsoft.Json.Linq; namespace Microsoft.Azure.WebJobs.Script.Workers.Rpc @@ -118,6 +119,13 @@ internal void AddProvider(string workerDir) RpcWorkerDescription workerDescription = workerConfig.Property(WorkerConstants.WorkerDescription).Value.ToObject(); workerDescription.WorkerDirectory = workerDir; + //Read the profiles from worker description and load the profile for which the conditions match + List workerDescriptionProfiles = ReadWorkerDescriptionProfiles(workerConfig); + if (workerDescriptionProfiles.Count > 0) + { + workerDescription = LoadWorkerDescriptionFromProfiles(workerDescriptionProfiles, workerDescription); + } + // Check if any appsettings are provided for that langauge var languageSection = _config.GetSection($"{RpcWorkerConstants.LanguageWorkersSectionName}:{workerDescription.Language}"); workerDescription.Arguments = workerDescription.Arguments ?? new List(); @@ -146,6 +154,7 @@ internal void AddProvider(string workerDir) Description = workerDescription, Arguments = arguments, CountOptions = workerProcessCount, + Profiles = workerDescriptionProfiles, }; _workerDescriptionDictionary[workerDescription.Language] = rpcWorkerConfig; ReadLanguageWorkerFile(arguments.WorkerPath); @@ -159,6 +168,51 @@ internal void AddProvider(string workerDir) } } + private List ReadWorkerDescriptionProfiles(JObject workerConfig) + { + JArray profiles = workerConfig.GetValue(WorkerConstants.WorkerDescriptionProfiles) as JArray; + if (profiles != null) + { + var descriptionProfiles = new List(profiles.Count); + var workerProfileConditionFactory = new WorkerProfileConditionFactory(_logger, _systemRuntimeInformation, _environment); + try + { + foreach (JObject profile in profiles) + { + var conditions = profile.GetValue(WorkerConstants.WorkerDescriptionProfileConditions) as JArray; + var profileConditions = new List(conditions.Count); + + foreach (JObject condition in conditions) + { + var properties = JsonConvert.DeserializeObject>(condition.ToString()); + IWorkerProfileCondition workerProfileCondition = workerProfileConditionFactory.CreateWorkerProfileCondition(properties); + profileConditions.Add(workerProfileCondition); + } + descriptionProfiles.Add(new WorkerDescriptionProfile((string)profile[WorkerConstants.WorkerDescriptionProfileName], profileConditions, profile.Property(WorkerConstants.WorkerDescription).Value.ToObject())); + } + } + catch + { + throw new FormatException("Failed to parse profiles in worker config."); + } + return descriptionProfiles; + } + return new List(0); + } + + private RpcWorkerDescription LoadWorkerDescriptionFromProfiles(List workerDescriptionProfiles, RpcWorkerDescription defaultWorkerDescription) + { + foreach (var profile in workerDescriptionProfiles) + { + if (profile.EvaluateConditions()) + { + _logger?.LogInformation($"Worker initialized with {profile.Name} profile from worker config."); + return profile.ApplyProfile(defaultWorkerDescription); + } + } + return defaultWorkerDescription; + } + internal WorkerProcessCountOptions GetWorkerProcessCount(JObject workerConfig) { WorkerProcessCountOptions workerProcessCount = workerConfig.Property(WorkerConstants.ProcessCount)?.Value.ToObject(); diff --git a/src/WebJobs.Script/Workers/Rpc/WebHostRpcWorkerChannelManager.cs b/src/WebJobs.Script/Workers/Rpc/WebHostRpcWorkerChannelManager.cs index 9be687b637..d0cc4adc5d 100644 --- a/src/WebJobs.Script/Workers/Rpc/WebHostRpcWorkerChannelManager.cs +++ b/src/WebJobs.Script/Workers/Rpc/WebHostRpcWorkerChannelManager.cs @@ -141,6 +141,18 @@ private bool UsePlaceholderChannel(string workerRuntime) return false; } + // If a profile evaluates to true and was not previously loaded, restart worker process + RpcWorkerConfig workerConfig = _lanuageworkerOptions.CurrentValue.WorkerConfigs.First(c => c.Description.Language == workerRuntime); + if (workerConfig != null && workerConfig.Profiles != null) + { + foreach (var profile in workerConfig.Profiles) + { + if (profile.EvaluateConditions() && !profile.ProfileLoaded) + { + return false; + } + } + } // Special case: node and PowerShell apps must be read-only to use the placeholder mode channel // Also cannot use placeholder worker that is targeting ~3 but has backwards compatibility with V2 enabled // TODO: Remove special casing when resolving https://github.com/Azure/azure-functions-host/issues/4534 diff --git a/src/WebJobs.Script/Workers/WorkerConstants.cs b/src/WebJobs.Script/Workers/WorkerConstants.cs index 854798e84e..3371ca4565 100644 --- a/src/WebJobs.Script/Workers/WorkerConstants.cs +++ b/src/WebJobs.Script/Workers/WorkerConstants.cs @@ -29,6 +29,11 @@ public static class WorkerConstants // Profiles public const string WorkerDescriptionProfiles = "profiles"; + public const string WorkerDescriptionProfileName = "name"; + public const string WorkerDescriptionProfileConditions = "conditions"; + public const string WorkerDescriptionProfileConditionType = "type"; + public const string WorkerDescriptionProfileEnvironmentCondition = "environment"; + public const string WorkerDescriptionProfileHostPropertyCondition = "hostProperty"; public const string WorkerDescriptionAppServiceEnvProfileName = "appServiceEnvironment"; // Logs From c4e9e9398661b7d1b698f59b76ed12dbf5abd61b Mon Sep 17 00:00:00 2001 From: Fabio Cavalcante Date: Thu, 21 Apr 2022 17:38:12 -0700 Subject: [PATCH 2/9] WIP: Example refactoring using registered services --- .../WebScriptHostBuilderExtension.cs | 2 + .../ScriptHostBuilderExtensions.cs | 3 ++ .../Workers/Profiles/EnvironmentCondition.cs | 27 +++++++++---- .../Workers/Profiles/HostPropertyCondition.cs | 29 ++++++++++---- .../IWorkerProfileConditionFactory.cs | 12 ------ .../IWorkerProfileConditionManager.cs | 12 ++++++ .../IWorkerProfileConditionProvider.cs | 14 +++++++ .../Profiles/SystemConditionProvider.cs | 35 ++++++++++++++++ .../WorkerProfileConditionDescriptor.cs | 35 ++++++++++++++++ .../Profiles/WorkerProfileConditionFactory.cs | 36 ----------------- .../Profiles/WorkerProfileConditionManager.cs | 39 ++++++++++++++++++ .../Profiles/WorkerProfileDescriptor.cs | 17 ++++++++ .../LanguageWorkerOptionsSetup.cs | 35 +++++++++++----- .../Rpc/Configuration/RpcWorkerConfig.cs | 2 + .../Configuration/RpcWorkerConfigFactory.cs | 40 +++++++++++++------ .../WebJobs.Script.Tests.Shared/TestLogger.cs | 6 +++ .../LanguageWorkerOptionsSetupTests.cs | 7 +++- .../Rpc/RpcWorkerConfigFactoryTests.cs | 22 +++++----- .../Workers/Rpc/RpcWorkerConfigTests.cs | 7 +++- 19 files changed, 282 insertions(+), 98 deletions(-) delete mode 100644 src/WebJobs.Script/Workers/Profiles/IWorkerProfileConditionFactory.cs create mode 100644 src/WebJobs.Script/Workers/Profiles/IWorkerProfileConditionManager.cs create mode 100644 src/WebJobs.Script/Workers/Profiles/IWorkerProfileConditionProvider.cs create mode 100644 src/WebJobs.Script/Workers/Profiles/SystemConditionProvider.cs create mode 100644 src/WebJobs.Script/Workers/Profiles/WorkerProfileConditionDescriptor.cs delete mode 100644 src/WebJobs.Script/Workers/Profiles/WorkerProfileConditionFactory.cs create mode 100644 src/WebJobs.Script/Workers/Profiles/WorkerProfileConditionManager.cs create mode 100644 src/WebJobs.Script/Workers/Profiles/WorkerProfileDescriptor.cs diff --git a/src/WebJobs.Script.WebHost/WebScriptHostBuilderExtension.cs b/src/WebJobs.Script.WebHost/WebScriptHostBuilderExtension.cs index edfba542ac..a771d0b92f 100644 --- a/src/WebJobs.Script.WebHost/WebScriptHostBuilderExtension.cs +++ b/src/WebJobs.Script.WebHost/WebScriptHostBuilderExtension.cs @@ -152,6 +152,8 @@ public static IHostBuilder AddWebScriptHost(this IHostBuilder builder, IServiceP services.AddSingleton(); services.TryAddEnumerable(ServiceDescriptor.Singleton(p => p.GetService())); + // Worker/OOP services + ConfigureRegisteredBuilders(services, rootServiceProvider); }); diff --git a/src/WebJobs.Script/ScriptHostBuilderExtensions.cs b/src/WebJobs.Script/ScriptHostBuilderExtensions.cs index 4fcd27b685..54b3306ba4 100644 --- a/src/WebJobs.Script/ScriptHostBuilderExtensions.cs +++ b/src/WebJobs.Script/ScriptHostBuilderExtensions.cs @@ -27,6 +27,7 @@ using Microsoft.Azure.WebJobs.Script.Scale; using Microsoft.Azure.WebJobs.Script.Workers; using Microsoft.Azure.WebJobs.Script.Workers.Http; +using Microsoft.Azure.WebJobs.Script.Workers.Profiles; using Microsoft.Azure.WebJobs.Script.Workers.Rpc; using Microsoft.Extensions.Azure; using Microsoft.Extensions.Configuration; @@ -334,6 +335,8 @@ public static void AddCommonServices(IServiceCollection services) services.TryAddSingleton(); services.AddSingleton(); services.AddSingleton(); + services.AddSingleton(); + services.AddSingleton(); services.TryAddSingleton(); services.TryAddSingleton(); services.TryAddSingleton(); diff --git a/src/WebJobs.Script/Workers/Profiles/EnvironmentCondition.cs b/src/WebJobs.Script/Workers/Profiles/EnvironmentCondition.cs index 242f91ba2e..5f9ee14e61 100644 --- a/src/WebJobs.Script/Workers/Profiles/EnvironmentCondition.cs +++ b/src/WebJobs.Script/Workers/Profiles/EnvironmentCondition.cs @@ -4,6 +4,7 @@ using System; using System.ComponentModel.DataAnnotations; using System.Text.RegularExpressions; +using Microsoft.Azure.WebJobs.Script.Workers.Profiles; using Microsoft.Extensions.Logging; namespace Microsoft.Azure.WebJobs.Script.Workers @@ -12,19 +13,29 @@ public class EnvironmentCondition : IWorkerProfileCondition { private readonly ILogger _logger; private readonly IEnvironment _environment; + private readonly string _name; + private readonly string _expression; + private Regex _regex; - public EnvironmentCondition(ILogger logger, IEnvironment environment, string name, string expression) + internal EnvironmentCondition(ILogger logger, IEnvironment environment, WorkerProfileConditionDescriptor descriptor) { + if (descriptor is null) + { + throw new ArgumentNullException(nameof(descriptor)); + } + _logger = logger ?? throw new ArgumentNullException(nameof(logger)); _environment = environment ?? throw new ArgumentNullException(nameof(environment)); - Name = name; - Expression = expression; + + descriptor.Properties.TryGetValue("name", out _name); + descriptor.Properties.TryGetValue("expression", out _expression); + Validate(); } - public string Name { get; set; } + public string Name => _name; - public string Expression { get; set; } + public string Expression => _expression; public bool Evaluate() { @@ -33,8 +44,10 @@ public bool Evaluate() { return false; } + _logger.LogDebug($"Evaluating EnvironmentCondition with value: {value} and expression {Expression}"); - return Regex.IsMatch(value, Expression); + + return _regex.IsMatch(value); } public void Validate() @@ -51,7 +64,7 @@ public void Validate() try { - new Regex(Expression); + _regex = new Regex(Expression); } catch { diff --git a/src/WebJobs.Script/Workers/Profiles/HostPropertyCondition.cs b/src/WebJobs.Script/Workers/Profiles/HostPropertyCondition.cs index 12a3c370a8..8c3e356dc2 100644 --- a/src/WebJobs.Script/Workers/Profiles/HostPropertyCondition.cs +++ b/src/WebJobs.Script/Workers/Profiles/HostPropertyCondition.cs @@ -6,21 +6,32 @@ using System.Linq; using System.Text.RegularExpressions; using Microsoft.Azure.WebJobs.Script.Config; +using Microsoft.Azure.WebJobs.Script.Workers.Profiles; using Microsoft.Extensions.Logging; namespace Microsoft.Azure.WebJobs.Script.Workers { - public class HostPropertyCondition : IWorkerProfileCondition + internal class HostPropertyCondition : IWorkerProfileCondition { private readonly ILogger _logger; private readonly ISystemRuntimeInformation _systemRuntimeInformation; + private readonly string _name; + private readonly string _expression; + private Regex _regex; - public HostPropertyCondition(ILogger logger, ISystemRuntimeInformation systemRuntimeInformation, string name, string expression) + public HostPropertyCondition(ILogger logger, ISystemRuntimeInformation systemRuntimeInformation, WorkerProfileConditionDescriptor descriptor) { + if (descriptor is null) + { + throw new ArgumentNullException(nameof(descriptor)); + } + _logger = logger ?? throw new ArgumentNullException(nameof(logger)); _systemRuntimeInformation = systemRuntimeInformation ?? throw new ArgumentNullException(nameof(systemRuntimeInformation)); - Name = name; - Expression = expression; + + descriptor.Properties.TryGetValue("name", out _name); + descriptor.Properties.TryGetValue("expression", out _expression); + Validate(); } @@ -31,9 +42,9 @@ public enum ConditionHostPropertyName HostVersion } - public string Name { get; set; } + public string Name => _name; - public string Expression { get; set; } + public string Expression => _expression; public bool Evaluate() { @@ -58,8 +69,10 @@ public bool Evaluate() { return false; } + _logger.LogDebug($"Evaluating HostPropertyCondition with value: {value} and expression {Expression}"); - return Regex.IsMatch(value, Expression); + + return _regex.IsMatch(value); } public void Validate() @@ -81,7 +94,7 @@ public void Validate() try { - new Regex(Expression); + _regex = new Regex(Expression); } catch { diff --git a/src/WebJobs.Script/Workers/Profiles/IWorkerProfileConditionFactory.cs b/src/WebJobs.Script/Workers/Profiles/IWorkerProfileConditionFactory.cs deleted file mode 100644 index 8e7e91e861..0000000000 --- a/src/WebJobs.Script/Workers/Profiles/IWorkerProfileConditionFactory.cs +++ /dev/null @@ -1,12 +0,0 @@ -// Copyright (c) .NET Foundation. All rights reserved. -// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. - -using System.Collections.Generic; - -namespace Microsoft.Azure.WebJobs.Script.Workers -{ - internal interface IWorkerProfileConditionFactory - { - IWorkerProfileCondition CreateWorkerProfileCondition(IDictionary properties); - } -} diff --git a/src/WebJobs.Script/Workers/Profiles/IWorkerProfileConditionManager.cs b/src/WebJobs.Script/Workers/Profiles/IWorkerProfileConditionManager.cs new file mode 100644 index 0000000000..ec853c1e2a --- /dev/null +++ b/src/WebJobs.Script/Workers/Profiles/IWorkerProfileConditionManager.cs @@ -0,0 +1,12 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using Microsoft.Azure.WebJobs.Script.Workers.Profiles; + +namespace Microsoft.Azure.WebJobs.Script.Workers +{ + internal interface IWorkerProfileConditionManager + { + bool TryCreateWorkerProfileCondition(WorkerProfileConditionDescriptor conditionDescriptor, out IWorkerProfileCondition condition); + } +} diff --git a/src/WebJobs.Script/Workers/Profiles/IWorkerProfileConditionProvider.cs b/src/WebJobs.Script/Workers/Profiles/IWorkerProfileConditionProvider.cs new file mode 100644 index 0000000000..351f4ba8fe --- /dev/null +++ b/src/WebJobs.Script/Workers/Profiles/IWorkerProfileConditionProvider.cs @@ -0,0 +1,14 @@ +// 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.Text; + +namespace Microsoft.Azure.WebJobs.Script.Workers.Profiles +{ + internal interface IWorkerProfileConditionProvider + { + bool TryCreateCondition(WorkerProfileConditionDescriptor descriptor, out IWorkerProfileCondition condition); + } +} diff --git a/src/WebJobs.Script/Workers/Profiles/SystemConditionProvider.cs b/src/WebJobs.Script/Workers/Profiles/SystemConditionProvider.cs new file mode 100644 index 0000000000..c0398c8eb0 --- /dev/null +++ b/src/WebJobs.Script/Workers/Profiles/SystemConditionProvider.cs @@ -0,0 +1,35 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System; +using Microsoft.Azure.WebJobs.Script.Workers.Profiles; +using Microsoft.Extensions.Logging; + +namespace Microsoft.Azure.WebJobs.Script.Workers +{ + internal sealed class SystemConditionProvider : IWorkerProfileConditionProvider + { + private readonly ILogger _logger; + private readonly IEnvironment _environment; + private readonly ISystemRuntimeInformation _systemRuntimeInformation; + + public SystemConditionProvider(ILogger logger, ISystemRuntimeInformation systemRuntimeInfo, IEnvironment environment) + { + _logger = logger ?? throw new ArgumentNullException(nameof(logger)); + _environment = environment ?? throw new ArgumentNullException(nameof(environment)); + _systemRuntimeInformation = systemRuntimeInfo ?? throw new ArgumentNullException(nameof(systemRuntimeInfo)); + } + + public bool TryCreateCondition(WorkerProfileConditionDescriptor descriptor, out IWorkerProfileCondition condition) + { + condition = descriptor.Type switch + { + WorkerConstants.WorkerDescriptionProfileHostPropertyCondition => new HostPropertyCondition(_logger, _systemRuntimeInformation, descriptor), + WorkerConstants.WorkerDescriptionProfileEnvironmentCondition => new EnvironmentCondition(_logger, _environment, descriptor), + _ => null + }; + + return condition != null; + } + } +} diff --git a/src/WebJobs.Script/Workers/Profiles/WorkerProfileConditionDescriptor.cs b/src/WebJobs.Script/Workers/Profiles/WorkerProfileConditionDescriptor.cs new file mode 100644 index 0000000000..1da57ab7e3 --- /dev/null +++ b/src/WebJobs.Script/Workers/Profiles/WorkerProfileConditionDescriptor.cs @@ -0,0 +1,35 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// 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; + +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; + + 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; + } + } + } +} diff --git a/src/WebJobs.Script/Workers/Profiles/WorkerProfileConditionFactory.cs b/src/WebJobs.Script/Workers/Profiles/WorkerProfileConditionFactory.cs deleted file mode 100644 index 44f2a0fbb6..0000000000 --- a/src/WebJobs.Script/Workers/Profiles/WorkerProfileConditionFactory.cs +++ /dev/null @@ -1,36 +0,0 @@ -// Copyright (c) .NET Foundation. All rights reserved. -// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. - -using System; -using System.Collections.Generic; -using Microsoft.Extensions.Logging; - -namespace Microsoft.Azure.WebJobs.Script.Workers -{ - internal class WorkerProfileConditionFactory : IWorkerProfileConditionFactory - { - private readonly ILogger _logger; - private readonly ISystemRuntimeInformation _systemRuntimeInformation; - private readonly IEnvironment _environment; - - public WorkerProfileConditionFactory(ILogger logger, ISystemRuntimeInformation systemRuntimeInfo, IEnvironment environment) - { - _logger = logger ?? throw new ArgumentNullException(nameof(logger)); - _environment = environment ?? throw new ArgumentNullException(nameof(environment)); - _systemRuntimeInformation = systemRuntimeInfo ?? throw new ArgumentNullException(nameof(systemRuntimeInfo)); - } - - public IWorkerProfileCondition CreateWorkerProfileCondition(IDictionary properties) - { - switch (properties[WorkerConstants.WorkerDescriptionProfileConditionType]) - { - case WorkerConstants.WorkerDescriptionProfileHostPropertyCondition: - return new HostPropertyCondition(_logger, _systemRuntimeInformation, (string)properties["name"], (string)properties["expression"]); - case WorkerConstants.WorkerDescriptionProfileEnvironmentCondition: - return new EnvironmentCondition(_logger, _environment, (string)properties["name"], (string)properties["expression"]); - default: - throw new ArgumentException(nameof(WorkerConstants.WorkerDescriptionProfileConditionType)); - } - } - } -} diff --git a/src/WebJobs.Script/Workers/Profiles/WorkerProfileConditionManager.cs b/src/WebJobs.Script/Workers/Profiles/WorkerProfileConditionManager.cs new file mode 100644 index 0000000000..2aaf022470 --- /dev/null +++ b/src/WebJobs.Script/Workers/Profiles/WorkerProfileConditionManager.cs @@ -0,0 +1,39 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System; +using System.Collections.Generic; +using Microsoft.Azure.WebJobs.Script.Workers.Profiles; +using Microsoft.Extensions.Logging; + +namespace Microsoft.Azure.WebJobs.Script.Workers +{ + internal class WorkerProfileConditionManager : IWorkerProfileConditionManager + { + private readonly ILogger _logger; + private readonly IEnumerable _conditionProviders; + + public WorkerProfileConditionManager(ILogger logger, + IEnumerable conditionProviders) + { + _logger = logger ?? throw new ArgumentNullException(nameof(logger)); + _conditionProviders = conditionProviders ?? throw new ArgumentNullException(nameof(conditionProviders)); + } + + public bool TryCreateWorkerProfileCondition(WorkerProfileConditionDescriptor conditionDescriptor, out IWorkerProfileCondition condition) + { + foreach (var provider in _conditionProviders) + { + if (provider.TryCreateCondition(conditionDescriptor, out condition)) + { + return true; + } + } + + _logger.LogInformation("Unable to create profile condition for condition type '{conditionType}'", conditionDescriptor.Type); + + condition = null; + return false; + } + } +} diff --git a/src/WebJobs.Script/Workers/Profiles/WorkerProfileDescriptor.cs b/src/WebJobs.Script/Workers/Profiles/WorkerProfileDescriptor.cs new file mode 100644 index 0000000000..26611fab56 --- /dev/null +++ b/src/WebJobs.Script/Workers/Profiles/WorkerProfileDescriptor.cs @@ -0,0 +1,17 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the MIT License. See License.txt in the project root for license information. + +using System.Collections.Generic; +using Microsoft.Azure.WebJobs.Script.Workers.Rpc; + +namespace Microsoft.Azure.WebJobs.Script.Workers.Profiles +{ + public sealed class WorkerProfileDescriptor + { + public string Name { get; set; } + + public RpcWorkerDescription Description { get; set; } + + public IList Conditions { get; set; } + } +} diff --git a/src/WebJobs.Script/Workers/Rpc/Configuration/LanguageWorkerOptionsSetup.cs b/src/WebJobs.Script/Workers/Rpc/Configuration/LanguageWorkerOptionsSetup.cs index f987204c2e..d4ff25e418 100644 --- a/src/WebJobs.Script/Workers/Rpc/Configuration/LanguageWorkerOptionsSetup.cs +++ b/src/WebJobs.Script/Workers/Rpc/Configuration/LanguageWorkerOptionsSetup.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; using System.Collections.Generic; using Microsoft.Azure.WebJobs.Script.Diagnostics; using Microsoft.Extensions.Configuration; @@ -11,17 +12,29 @@ namespace Microsoft.Azure.WebJobs.Script.Workers.Rpc { internal class LanguageWorkerOptionsSetup : IConfigureOptions { - private IConfiguration _configuration; - private ILogger _logger; - private IEnvironment _environment; - private IMetricsLogger _metricsLogger; + private readonly IWorkerProfileConditionManager _profileConditionManager; + private readonly IConfiguration _configuration; + private readonly ILogger _logger; + private readonly IEnvironment _environment; + private readonly IMetricsLogger _metricsLogger; - public LanguageWorkerOptionsSetup(IConfiguration configuration, ILoggerFactory loggerFactory, IEnvironment environment, IMetricsLogger metricsLogger) + public LanguageWorkerOptionsSetup(IConfiguration configuration, + ILoggerFactory loggerFactory, + IEnvironment environment, + IWorkerProfileConditionManager profileConditionManager, + IMetricsLogger metricsLogger) { - _configuration = configuration; + if (loggerFactory is null) + { + throw new ArgumentNullException(nameof(loggerFactory)); + } + + _profileConditionManager = profileConditionManager ?? throw new System.ArgumentNullException(nameof(profileConditionManager)); + _configuration = configuration ?? throw new ArgumentNullException(nameof(configuration)); + _environment = environment ?? throw new ArgumentNullException(nameof(environment)); + _metricsLogger = metricsLogger ?? throw new ArgumentNullException(nameof(metricsLogger)); + _logger = loggerFactory.CreateLogger("Host.LanguageWorkerConfig"); - _environment = environment; - _metricsLogger = metricsLogger; } public void Configure(LanguageWorkerOptions options) @@ -33,8 +46,10 @@ public void Configure(LanguageWorkerOptions options) options.WorkerConfigs = new List(); return; } - ISystemRuntimeInformation systemRuntimeInfo = new SystemRuntimeInformation(); - var configFactory = new RpcWorkerConfigFactory(_configuration, _logger, systemRuntimeInfo, _environment, _metricsLogger); + + var configFactory = new RpcWorkerConfigFactory(_configuration, _logger, SystemRuntimeInformation.Instance, + _profileConditionManager, _environment, _metricsLogger); + options.WorkerConfigs = configFactory.GetConfigs(); } } diff --git a/src/WebJobs.Script/Workers/Rpc/Configuration/RpcWorkerConfig.cs b/src/WebJobs.Script/Workers/Rpc/Configuration/RpcWorkerConfig.cs index 9c1f8ea307..a4fab27280 100644 --- a/src/WebJobs.Script/Workers/Rpc/Configuration/RpcWorkerConfig.cs +++ b/src/WebJobs.Script/Workers/Rpc/Configuration/RpcWorkerConfig.cs @@ -1,6 +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.Collections.Generic; + namespace Microsoft.Azure.WebJobs.Script.Workers.Rpc { public class RpcWorkerConfig diff --git a/src/WebJobs.Script/Workers/Rpc/Configuration/RpcWorkerConfigFactory.cs b/src/WebJobs.Script/Workers/Rpc/Configuration/RpcWorkerConfigFactory.cs index 9ee2d99204..39428a026b 100644 --- a/src/WebJobs.Script/Workers/Rpc/Configuration/RpcWorkerConfigFactory.cs +++ b/src/WebJobs.Script/Workers/Rpc/Configuration/RpcWorkerConfigFactory.cs @@ -7,6 +7,7 @@ using System.Linq; 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; @@ -20,17 +21,24 @@ internal class RpcWorkerConfigFactory private readonly IConfiguration _config; private readonly ILogger _logger; private readonly ISystemRuntimeInformation _systemRuntimeInformation; + private readonly IWorkerProfileConditionManager _profileConditionManager; private readonly IMetricsLogger _metricsLogger; private readonly string _workerRuntime; private readonly IEnvironment _environment; private Dictionary _workerDescriptionDictionary = new Dictionary(); - public RpcWorkerConfigFactory(IConfiguration config, ILogger logger, ISystemRuntimeInformation systemRuntimeInfo, IEnvironment environment, IMetricsLogger metricsLogger) + public RpcWorkerConfigFactory(IConfiguration config, + ILogger logger, + ISystemRuntimeInformation systemRuntimeInfo, + IWorkerProfileConditionManager profileConditionManager, + IEnvironment environment, + IMetricsLogger metricsLogger) { _config = config ?? throw new ArgumentNullException(nameof(config)); _logger = logger ?? throw new ArgumentNullException(nameof(logger)); _systemRuntimeInformation = systemRuntimeInfo ?? throw new ArgumentNullException(nameof(systemRuntimeInfo)); + _profileConditionManager = profileConditionManager ?? throw new ArgumentNullException(nameof(profileConditionManager)); _environment = environment ?? throw new ArgumentNullException(nameof(environment)); _metricsLogger = metricsLogger; _workerRuntime = _environment.GetEnvironmentVariable(RpcWorkerConstants.FunctionWorkerRuntimeSettingName); @@ -170,33 +178,39 @@ internal void AddProvider(string workerDir) private List ReadWorkerDescriptionProfiles(JObject workerConfig) { - JArray profiles = workerConfig.GetValue(WorkerConstants.WorkerDescriptionProfiles) as JArray; - if (profiles != null) + var profiles = workerConfig.GetValue(WorkerConstants.WorkerDescriptionProfiles).ToObject>(); + + if (profiles != null && profiles.Count > 0) { var descriptionProfiles = new List(profiles.Count); - var workerProfileConditionFactory = new WorkerProfileConditionFactory(_logger, _systemRuntimeInformation, _environment); try { - foreach (JObject profile in profiles) + foreach (var profile in profiles) { - var conditions = profile.GetValue(WorkerConstants.WorkerDescriptionProfileConditions) as JArray; - var profileConditions = new List(conditions.Count); + var profileConditions = new List(profile.Conditions.Count); - foreach (JObject condition in conditions) + foreach (var descriptor in profile.Conditions) { - var properties = JsonConvert.DeserializeObject>(condition.ToString()); - IWorkerProfileCondition workerProfileCondition = workerProfileConditionFactory.CreateWorkerProfileCondition(properties); - profileConditions.Add(workerProfileCondition); + if (!_profileConditionManager.TryCreateWorkerProfileCondition(descriptor, out IWorkerProfileCondition condition)) + { + // TODO: Handle this failure here... this should likely translate into an initialization error as we don't + // want to run the worker if we can't evaluate the profile conditions + } + + profileConditions.Add(condition); } - descriptionProfiles.Add(new WorkerDescriptionProfile((string)profile[WorkerConstants.WorkerDescriptionProfileName], profileConditions, profile.Property(WorkerConstants.WorkerDescription).Value.ToObject())); + + descriptionProfiles.Add(new (profile.Name, profileConditions, profile.Description)); } } - catch + catch (Exception) { throw new FormatException("Failed to parse profiles in worker config."); } + return descriptionProfiles; } + return new List(0); } diff --git a/test/WebJobs.Script.Tests.Shared/TestLogger.cs b/test/WebJobs.Script.Tests.Shared/TestLogger.cs index dbb6669f03..45ac491d4b 100644 --- a/test/WebJobs.Script.Tests.Shared/TestLogger.cs +++ b/test/WebJobs.Script.Tests.Shared/TestLogger.cs @@ -9,6 +9,12 @@ namespace Microsoft.Azure.WebJobs.Script.Tests { + [DebuggerDisplay("{DebuggerDisplay,nq}")] + public class TestLogger : TestLogger, ILogger + { + public TestLogger() : base(typeof(T).Name) { } + } + [DebuggerDisplay("{DebuggerDisplay,nq}")] public class TestLogger : ILogger { diff --git a/test/WebJobs.Script.Tests/Configuration/LanguageWorkerOptionsSetupTests.cs b/test/WebJobs.Script.Tests/Configuration/LanguageWorkerOptionsSetupTests.cs index 14a6aecce9..7a4921a469 100644 --- a/test/WebJobs.Script.Tests/Configuration/LanguageWorkerOptionsSetupTests.cs +++ b/test/WebJobs.Script.Tests/Configuration/LanguageWorkerOptionsSetupTests.cs @@ -2,6 +2,7 @@ // Licensed under the MIT License. See License.txt in the project root for license information. using System; +using Microsoft.Azure.WebJobs.Script.Workers; using Microsoft.Azure.WebJobs.Script.Workers.Rpc; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.Logging.Abstractions; @@ -18,16 +19,20 @@ public class LanguageWorkerOptionsSetupTests [InlineData("node")] public void LanguageWorkerOptions_Expected_ListOfConfigs(string workerRuntime) { + var runtimeInfo = new TestSystemRuntimeInformation(); var testEnvironment = new TestEnvironment(); var configurationBuilder = new ConfigurationBuilder() .Add(new ScriptEnvironmentVariablesConfigurationSource()); + var systemConditionProvider = new SystemConditionProvider(new TestLogger(), runtimeInfo, testEnvironment); + var profileConditionManager = new WorkerProfileConditionManager(new TestLogger(), new[] { systemConditionProvider }); + var configuration = configurationBuilder.Build(); if (!string.IsNullOrEmpty(workerRuntime)) { testEnvironment.SetEnvironmentVariable(RpcWorkerConstants.FunctionWorkerRuntimeSettingName, workerRuntime); } - LanguageWorkerOptionsSetup setup = new LanguageWorkerOptionsSetup(configuration, NullLoggerFactory.Instance, testEnvironment, new TestMetricsLogger()); + LanguageWorkerOptionsSetup setup = new LanguageWorkerOptionsSetup(configuration, NullLoggerFactory.Instance, testEnvironment, profileConditionManager, new TestMetricsLogger()); LanguageWorkerOptions options = new LanguageWorkerOptions(); setup.Configure(options); if (string.IsNullOrEmpty(workerRuntime)) diff --git a/test/WebJobs.Script.Tests/Workers/Rpc/RpcWorkerConfigFactoryTests.cs b/test/WebJobs.Script.Tests/Workers/Rpc/RpcWorkerConfigFactoryTests.cs index baea4e1087..b9c1db7e7a 100644 --- a/test/WebJobs.Script.Tests/Workers/Rpc/RpcWorkerConfigFactoryTests.cs +++ b/test/WebJobs.Script.Tests/Workers/Rpc/RpcWorkerConfigFactoryTests.cs @@ -17,12 +17,16 @@ namespace Microsoft.Azure.WebJobs.Script.Tests.Workers.Rpc { public class RpcWorkerConfigFactoryTests : IDisposable { + private readonly WorkerProfileConditionManager _profileConditionManager; private TestSystemRuntimeInformation _testSysRuntimeInfo = new TestSystemRuntimeInformation(); private TestEnvironment _testEnvironment; public RpcWorkerConfigFactoryTests() { _testEnvironment = new TestEnvironment(); + + var systemConditionProvider = new SystemConditionProvider(new TestLogger(), _testSysRuntimeInfo, _testEnvironment); + _profileConditionManager = new WorkerProfileConditionManager(new TestLogger(), new[] { systemConditionProvider }); } public void Dispose() @@ -36,7 +40,7 @@ public void DefaultLanguageWorkersDir() var expectedWorkersDir = Path.Combine(Path.GetDirectoryName(new Uri(typeof(RpcWorkerConfigFactory).Assembly.Location).LocalPath), RpcWorkerConstants.DefaultWorkersDirectoryName); var config = new ConfigurationBuilder().Build(); var testLogger = new TestLogger("test"); - var configFactory = new RpcWorkerConfigFactory(config, testLogger, _testSysRuntimeInfo, _testEnvironment, new TestMetricsLogger()); + var configFactory = new RpcWorkerConfigFactory(config, testLogger, _testSysRuntimeInfo, _profileConditionManager, _testEnvironment, new TestMetricsLogger()); Assert.Equal(expectedWorkersDir, configFactory.WorkersDirPath); } @@ -69,7 +73,7 @@ public void LanguageWorker_WorkersDir_Set() }) .Build(); var testLogger = new TestLogger("test"); - var configFactory = new RpcWorkerConfigFactory(config, testLogger, _testSysRuntimeInfo, _testEnvironment, new TestMetricsLogger()); + var configFactory = new RpcWorkerConfigFactory(config, testLogger, _testSysRuntimeInfo, _profileConditionManager, _testEnvironment, new TestMetricsLogger()); Assert.Equal(expectedWorkersDir, configFactory.WorkersDirPath); } @@ -85,7 +89,7 @@ public void LanguageWorker_WorkersDir_NotSet() var config = configBuilder.Build(); var scriptSettingsManager = new ScriptSettingsManager(config); var testLogger = new TestLogger("test"); - var configFactory = new RpcWorkerConfigFactory(config, testLogger, _testSysRuntimeInfo, _testEnvironment, new TestMetricsLogger()); + var configFactory = new RpcWorkerConfigFactory(config, testLogger, _testSysRuntimeInfo, _profileConditionManager, _testEnvironment, new TestMetricsLogger()); Assert.Equal(expectedWorkersDir, configFactory.WorkersDirPath); } @@ -96,7 +100,7 @@ public void JavaPath_FromEnvVars() var config = configBuilder.Build(); var scriptSettingsManager = new ScriptSettingsManager(config); var testLogger = new TestLogger("test"); - var configFactory = new RpcWorkerConfigFactory(config, testLogger, _testSysRuntimeInfo, _testEnvironment, new TestMetricsLogger()); + var configFactory = new RpcWorkerConfigFactory(config, testLogger, _testSysRuntimeInfo, _profileConditionManager, _testEnvironment, new TestMetricsLogger()); var workerConfigs = configFactory.GetConfigs(); var javaPath = workerConfigs.Where(c => c.Description.Language.Equals("java", StringComparison.OrdinalIgnoreCase)).FirstOrDefault().Description.DefaultExecutablePath; Assert.DoesNotContain(@"%JAVA_HOME%", javaPath); @@ -117,7 +121,7 @@ public void DefaultWorkerConfigs_Overrides_DefaultWorkerRuntimeVersion_AppSettin var testLogger = new TestLogger("test"); using (var variables = new TestScopedSettings(scriptSettingsManager, testEnvVariables)) { - var configFactory = new RpcWorkerConfigFactory(config, testLogger, _testSysRuntimeInfo, _testEnvironment, new TestMetricsLogger()); + var configFactory = new RpcWorkerConfigFactory(config, testLogger, _testSysRuntimeInfo, _profileConditionManager, _testEnvironment, new TestMetricsLogger()); var workerConfigs = configFactory.GetConfigs(); var pythonWorkerConfig = workerConfigs.Where(w => w.Description.Language.Equals("python", StringComparison.OrdinalIgnoreCase)).FirstOrDefault(); var powershellWorkerConfig = workerConfigs.Where(w => w.Description.Language.Equals("powershell", StringComparison.OrdinalIgnoreCase)).FirstOrDefault(); @@ -139,7 +143,7 @@ public void DefaultWorkerConfigs_Overrides_VersionAppSetting() var config = configBuilder.Build(); var scriptSettingsManager = new ScriptSettingsManager(config); var testLogger = new TestLogger("test"); - var configFactory = new RpcWorkerConfigFactory(config, testLogger, _testSysRuntimeInfo, testEnvironment, new TestMetricsLogger()); + var configFactory = new RpcWorkerConfigFactory(config, testLogger, _testSysRuntimeInfo, _profileConditionManager, testEnvironment, new TestMetricsLogger()); var workerConfigs = configFactory.GetConfigs(); var powershellWorkerConfig = workerConfigs.Where(w => w.Description.Language.Equals("powershell", StringComparison.OrdinalIgnoreCase)).FirstOrDefault(); Assert.Equal(1, workerConfigs.Count); @@ -166,7 +170,7 @@ public void ShouldAddProvider_Returns_Expected(string workerLanguage, string wor } var config = new ConfigurationBuilder().Build(); var testLogger = new TestLogger("test"); - RpcWorkerConfigFactory rpcWorkerConfigFactory = new RpcWorkerConfigFactory(config, testLogger, _testSysRuntimeInfo, _testEnvironment, new TestMetricsLogger()); + RpcWorkerConfigFactory rpcWorkerConfigFactory = new RpcWorkerConfigFactory(config, testLogger, _testSysRuntimeInfo, _profileConditionManager, _testEnvironment, new TestMetricsLogger()); _testEnvironment.SetEnvironmentVariable(RpcWorkerConstants.FunctionWorkerRuntimeSettingName, workerRuntime); Assert.Equal(expectedResult, rpcWorkerConfigFactory.ShouldAddWorkerConfig(workerLanguage)); } @@ -196,7 +200,7 @@ public void GetWorkerProcessCount_Tests(bool defaultWorkerConfig, bool setProces } var config = new ConfigurationBuilder().Build(); var testLogger = new TestLogger("test"); - RpcWorkerConfigFactory rpcWorkerConfigFactory = new RpcWorkerConfigFactory(config, testLogger, _testSysRuntimeInfo, _testEnvironment, new TestMetricsLogger()); + RpcWorkerConfigFactory rpcWorkerConfigFactory = new RpcWorkerConfigFactory(config, testLogger, _testSysRuntimeInfo, _profileConditionManager, _testEnvironment, new TestMetricsLogger()); var result = rpcWorkerConfigFactory.GetWorkerProcessCount(workerConfig); if (defaultWorkerConfig) { @@ -240,7 +244,7 @@ public void GetWorkerProcessCount_ThrowsException_Tests() var config = new ConfigurationBuilder().Build(); var testLogger = new TestLogger("test"); - RpcWorkerConfigFactory rpcWorkerConfigFactory = new RpcWorkerConfigFactory(config, testLogger, _testSysRuntimeInfo, _testEnvironment, new TestMetricsLogger()); + RpcWorkerConfigFactory rpcWorkerConfigFactory = new RpcWorkerConfigFactory(config, testLogger, _testSysRuntimeInfo, _profileConditionManager, _testEnvironment, new TestMetricsLogger()); var resultEx1 = Assert.Throws(() => rpcWorkerConfigFactory.GetWorkerProcessCount(workerConfig)); Assert.Contains("ProcessCount must be greater than 0", resultEx1.Message); diff --git a/test/WebJobs.Script.Tests/Workers/Rpc/RpcWorkerConfigTests.cs b/test/WebJobs.Script.Tests/Workers/Rpc/RpcWorkerConfigTests.cs index ef0df03469..35c325ab9c 100644 --- a/test/WebJobs.Script.Tests/Workers/Rpc/RpcWorkerConfigTests.cs +++ b/test/WebJobs.Script.Tests/Workers/Rpc/RpcWorkerConfigTests.cs @@ -24,13 +24,16 @@ public class RpcWorkerConfigTests : IDisposable private static string customRootPath = Path.Combine(Path.GetTempPath(), Guid.NewGuid().ToString()); private static string testLanguagePath = Path.Combine(Path.GetTempPath(), Guid.NewGuid().ToString()); private static string testLanguage = "testLanguage"; - private readonly TestSystemRuntimeInformation _testSysRuntimeInfo = new TestSystemRuntimeInformation(); private readonly TestEnvironment _testEnvironment; + private readonly WorkerProfileConditionManager _profileConditionManager; public RpcWorkerConfigTests() { _testEnvironment = new TestEnvironment(); + + var systemConditionProvider = new SystemConditionProvider(new TestLogger(), _testSysRuntimeInfo, _testEnvironment); + _profileConditionManager = new WorkerProfileConditionManager(new TestLogger(), new[] { systemConditionProvider }); } public static IEnumerable InvalidWorkerDescriptions @@ -630,7 +633,7 @@ private IEnumerable TestReadWorkerProviderFromConfig(IEnumerabl var scriptHostOptions = new ScriptJobHostOptions(); var scriptSettingsManager = new ScriptSettingsManager(config); - var configFactory = new RpcWorkerConfigFactory(config, testLogger, _testSysRuntimeInfo, _testEnvironment, testMetricsLogger); + var configFactory = new RpcWorkerConfigFactory(config, testLogger, _testSysRuntimeInfo, _profileConditionManager, _testEnvironment, testMetricsLogger); if (appSvcEnv) { var testEnvVariables = new Dictionary From 5a144d85cd28409c5ff9e06c038b0af9888d0e61 Mon Sep 17 00:00:00 2001 From: Shreyas Gopalakrishna Date: Thu, 5 May 2022 14:00:55 -0500 Subject: [PATCH 3/9] Use the Profile manager to store, load and evaluate profiles --- .../WebScriptHostBuilderExtension.cs | 4 +- .../ScriptHostBuilderExtensions.cs | 2 +- .../Workers/Profiles/EnvironmentCondition.cs | 4 +- ...eConditionManager.cs => FalseCondition.cs} | 13 ++- .../Workers/Profiles/HostPropertyCondition.cs | 4 +- .../Workers/Profiles/IWorkerProfileManager.cs | 20 +++++ .../Profiles/WorkerDescriptionProfile.cs | 5 +- .../Profiles/WorkerProfileConditionManager.cs | 39 --------- .../Workers/Profiles/WorkerProfileManager.cs | 86 +++++++++++++++++++ .../LanguageWorkerOptionsSetup.cs | 4 +- .../Rpc/Configuration/RpcWorkerConfig.cs | 2 - .../Configuration/RpcWorkerConfigFactory.cs | 65 ++++++-------- .../Rpc/WebHostRpcWorkerChannelManager.cs | 18 ++-- src/WebJobs.Script/Workers/WorkerConstants.cs | 2 + .../LanguageWorkerOptionsSetupTests.cs | 2 +- .../Rpc/RpcWorkerConfigFactoryTests.cs | 22 ++--- .../Workers/Rpc/RpcWorkerConfigTests.cs | 6 +- .../WebHostRpcWorkerChannelManagerTests.cs | 26 +++--- 18 files changed, 192 insertions(+), 132 deletions(-) rename src/WebJobs.Script/Workers/Profiles/{IWorkerProfileConditionManager.cs => FalseCondition.cs} (51%) create mode 100644 src/WebJobs.Script/Workers/Profiles/IWorkerProfileManager.cs delete mode 100644 src/WebJobs.Script/Workers/Profiles/WorkerProfileConditionManager.cs create mode 100644 src/WebJobs.Script/Workers/Profiles/WorkerProfileManager.cs diff --git a/src/WebJobs.Script.WebHost/WebScriptHostBuilderExtension.cs b/src/WebJobs.Script.WebHost/WebScriptHostBuilderExtension.cs index a771d0b92f..36a81202fe 100644 --- a/src/WebJobs.Script.WebHost/WebScriptHostBuilderExtension.cs +++ b/src/WebJobs.Script.WebHost/WebScriptHostBuilderExtension.cs @@ -22,6 +22,8 @@ using Microsoft.Azure.WebJobs.Script.WebHost.Management; using Microsoft.Azure.WebJobs.Script.WebHost.Middleware; using Microsoft.Azure.WebJobs.Script.WebHost.Storage; +using Microsoft.Azure.WebJobs.Script.Workers; +using Microsoft.Azure.WebJobs.Script.Workers.Profiles; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.DependencyInjection.Extensions; using Microsoft.Extensions.Hosting; @@ -152,8 +154,6 @@ public static IHostBuilder AddWebScriptHost(this IHostBuilder builder, IServiceP services.AddSingleton(); services.TryAddEnumerable(ServiceDescriptor.Singleton(p => p.GetService())); - // Worker/OOP services - ConfigureRegisteredBuilders(services, rootServiceProvider); }); diff --git a/src/WebJobs.Script/ScriptHostBuilderExtensions.cs b/src/WebJobs.Script/ScriptHostBuilderExtensions.cs index 54b3306ba4..d2f2651c7d 100644 --- a/src/WebJobs.Script/ScriptHostBuilderExtensions.cs +++ b/src/WebJobs.Script/ScriptHostBuilderExtensions.cs @@ -335,7 +335,7 @@ public static void AddCommonServices(IServiceCollection services) services.TryAddSingleton(); services.AddSingleton(); services.AddSingleton(); - services.AddSingleton(); + services.AddSingleton(); services.AddSingleton(); services.TryAddSingleton(); services.TryAddSingleton(); diff --git a/src/WebJobs.Script/Workers/Profiles/EnvironmentCondition.cs b/src/WebJobs.Script/Workers/Profiles/EnvironmentCondition.cs index 5f9ee14e61..7428b7b1f8 100644 --- a/src/WebJobs.Script/Workers/Profiles/EnvironmentCondition.cs +++ b/src/WebJobs.Script/Workers/Profiles/EnvironmentCondition.cs @@ -27,8 +27,8 @@ internal EnvironmentCondition(ILogger logger, IEnvironment environment, WorkerPr _logger = logger ?? throw new ArgumentNullException(nameof(logger)); _environment = environment ?? throw new ArgumentNullException(nameof(environment)); - descriptor.Properties.TryGetValue("name", out _name); - descriptor.Properties.TryGetValue("expression", out _expression); + descriptor.Properties.TryGetValue(WorkerConstants.WorkerDescriptionProfileConditionName, out _name); + descriptor.Properties.TryGetValue(WorkerConstants.WorkerDescriptionProfileConditionExpression, out _expression); Validate(); } diff --git a/src/WebJobs.Script/Workers/Profiles/IWorkerProfileConditionManager.cs b/src/WebJobs.Script/Workers/Profiles/FalseCondition.cs similarity index 51% rename from src/WebJobs.Script/Workers/Profiles/IWorkerProfileConditionManager.cs rename to src/WebJobs.Script/Workers/Profiles/FalseCondition.cs index ec853c1e2a..5f1a274815 100644 --- a/src/WebJobs.Script/Workers/Profiles/IWorkerProfileConditionManager.cs +++ b/src/WebJobs.Script/Workers/Profiles/FalseCondition.cs @@ -1,12 +1,19 @@ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. +using System; +using System.ComponentModel.DataAnnotations; +using System.Text.RegularExpressions; using Microsoft.Azure.WebJobs.Script.Workers.Profiles; +using Microsoft.Extensions.Logging; namespace Microsoft.Azure.WebJobs.Script.Workers { - internal interface IWorkerProfileConditionManager + public class FalseCondition : IWorkerProfileCondition { - bool TryCreateWorkerProfileCondition(WorkerProfileConditionDescriptor conditionDescriptor, out IWorkerProfileCondition condition); + public bool Evaluate() + { + return false; + } } -} +} \ No newline at end of file diff --git a/src/WebJobs.Script/Workers/Profiles/HostPropertyCondition.cs b/src/WebJobs.Script/Workers/Profiles/HostPropertyCondition.cs index 8c3e356dc2..70e0fb4a92 100644 --- a/src/WebJobs.Script/Workers/Profiles/HostPropertyCondition.cs +++ b/src/WebJobs.Script/Workers/Profiles/HostPropertyCondition.cs @@ -29,8 +29,8 @@ public HostPropertyCondition(ILogger logger, ISystemRuntimeInformation systemRun _logger = logger ?? throw new ArgumentNullException(nameof(logger)); _systemRuntimeInformation = systemRuntimeInformation ?? throw new ArgumentNullException(nameof(systemRuntimeInformation)); - descriptor.Properties.TryGetValue("name", out _name); - descriptor.Properties.TryGetValue("expression", out _expression); + descriptor.Properties.TryGetValue(WorkerConstants.WorkerDescriptionProfileConditionName, out _name); + descriptor.Properties.TryGetValue(WorkerConstants.WorkerDescriptionProfileConditionExpression, out _expression); Validate(); } diff --git a/src/WebJobs.Script/Workers/Profiles/IWorkerProfileManager.cs b/src/WebJobs.Script/Workers/Profiles/IWorkerProfileManager.cs new file mode 100644 index 0000000000..afc9520697 --- /dev/null +++ b/src/WebJobs.Script/Workers/Profiles/IWorkerProfileManager.cs @@ -0,0 +1,20 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System.Collections.Generic; +using Microsoft.Azure.WebJobs.Script.Workers.Profiles; +using Microsoft.Azure.WebJobs.Script.Workers.Rpc; + +namespace Microsoft.Azure.WebJobs.Script.Workers +{ + public interface IWorkerProfileManager + { + bool TryCreateWorkerProfileCondition(WorkerProfileConditionDescriptor conditionDescriptor, out IWorkerProfileCondition condition); + + void SaveWorkerDescriptionProfiles(List workerDescriptionProfiles, string language); + + void LoadWorkerDescriptionFromProfiles(RpcWorkerDescription defaultWorkerDescription, out RpcWorkerDescription workerDescription); + + bool IsCorrectProfileLoaded(string workerRuntime); + } +} diff --git a/src/WebJobs.Script/Workers/Profiles/WorkerDescriptionProfile.cs b/src/WebJobs.Script/Workers/Profiles/WorkerDescriptionProfile.cs index 0a34e7e6d4..3fa5039f90 100644 --- a/src/WebJobs.Script/Workers/Profiles/WorkerDescriptionProfile.cs +++ b/src/WebJobs.Script/Workers/Profiles/WorkerDescriptionProfile.cs @@ -15,7 +15,7 @@ public WorkerDescriptionProfile(string name, List condi Name = name; Conditions = conditions; ProfileDescription = profileDescription; - ProfileLoaded = false; + ProfileId = Guid.NewGuid().ToString(); Validate(); } @@ -34,7 +34,7 @@ public WorkerDescriptionProfile(string name, List condi /// public RpcWorkerDescription ProfileDescription { get; set; } - public bool ProfileLoaded { get; set; } + public string ProfileId { get; } public void Validate() { @@ -71,7 +71,6 @@ public bool EvaluateConditions() /// public RpcWorkerDescription ApplyProfile(RpcWorkerDescription defaultWorkerDescription) { - ProfileLoaded = true; defaultWorkerDescription.Arguments = UseProfileOrDefault(ProfileDescription.Arguments, defaultWorkerDescription.Arguments); defaultWorkerDescription.DefaultExecutablePath = UseProfileOrDefault(ProfileDescription.DefaultExecutablePath, defaultWorkerDescription.DefaultExecutablePath); defaultWorkerDescription.DefaultWorkerPath = UseProfileOrDefault(ProfileDescription.DefaultWorkerPath, defaultWorkerDescription.DefaultWorkerPath); diff --git a/src/WebJobs.Script/Workers/Profiles/WorkerProfileConditionManager.cs b/src/WebJobs.Script/Workers/Profiles/WorkerProfileConditionManager.cs deleted file mode 100644 index 2aaf022470..0000000000 --- a/src/WebJobs.Script/Workers/Profiles/WorkerProfileConditionManager.cs +++ /dev/null @@ -1,39 +0,0 @@ -// Copyright (c) .NET Foundation. All rights reserved. -// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. - -using System; -using System.Collections.Generic; -using Microsoft.Azure.WebJobs.Script.Workers.Profiles; -using Microsoft.Extensions.Logging; - -namespace Microsoft.Azure.WebJobs.Script.Workers -{ - internal class WorkerProfileConditionManager : IWorkerProfileConditionManager - { - private readonly ILogger _logger; - private readonly IEnumerable _conditionProviders; - - public WorkerProfileConditionManager(ILogger logger, - IEnumerable conditionProviders) - { - _logger = logger ?? throw new ArgumentNullException(nameof(logger)); - _conditionProviders = conditionProviders ?? throw new ArgumentNullException(nameof(conditionProviders)); - } - - public bool TryCreateWorkerProfileCondition(WorkerProfileConditionDescriptor conditionDescriptor, out IWorkerProfileCondition condition) - { - foreach (var provider in _conditionProviders) - { - if (provider.TryCreateCondition(conditionDescriptor, out condition)) - { - return true; - } - } - - _logger.LogInformation("Unable to create profile condition for condition type '{conditionType}'", conditionDescriptor.Type); - - condition = null; - return false; - } - } -} diff --git a/src/WebJobs.Script/Workers/Profiles/WorkerProfileManager.cs b/src/WebJobs.Script/Workers/Profiles/WorkerProfileManager.cs new file mode 100644 index 0000000000..4161e984f9 --- /dev/null +++ b/src/WebJobs.Script/Workers/Profiles/WorkerProfileManager.cs @@ -0,0 +1,86 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System; +using System.Collections.Generic; +using Microsoft.Azure.WebJobs.Script.Workers.Profiles; +using Microsoft.Azure.WebJobs.Script.Workers.Rpc; +using Microsoft.Extensions.Logging; + +namespace Microsoft.Azure.WebJobs.Script.Workers +{ + internal class WorkerProfileManager : IWorkerProfileManager + { + private readonly ILogger _logger; + private readonly IEnumerable _conditionProviders; + private Dictionary> _profiles = new Dictionary>(); + private string _activeProfile; + + public WorkerProfileManager(ILogger logger, + IEnumerable conditionProviders) + { + _logger = logger ?? throw new ArgumentNullException(nameof(logger)); + _conditionProviders = conditionProviders ?? throw new ArgumentNullException(nameof(conditionProviders)); + _activeProfile = string.Empty; + } + + public void SaveWorkerDescriptionProfiles(List workerDescriptionProfiles, string language) + { + _profiles.Add(language, workerDescriptionProfiles); + } + + private bool GetEvaluatedProfile(string language, out WorkerDescriptionProfile evaluatedProfile) + { + if (_profiles.TryGetValue(language, out List profiles)) + { + foreach (var profile in profiles) + { + if (profile.EvaluateConditions()) + { + evaluatedProfile = profile; + return true; + } + } + } + evaluatedProfile = null; + return false; + } + + public void LoadWorkerDescriptionFromProfiles(RpcWorkerDescription defaultWorkerDescription, out RpcWorkerDescription workerDescription) + { + if (GetEvaluatedProfile(defaultWorkerDescription.Language, out WorkerDescriptionProfile profile)) + { + _logger?.LogInformation($"Worker initialized with profile - {profile.Name}, Profile ID {profile.ProfileId} from worker config."); + _activeProfile = profile.ProfileId; + workerDescription = profile.ApplyProfile(defaultWorkerDescription); + } + workerDescription = defaultWorkerDescription; + } + + public bool TryCreateWorkerProfileCondition(WorkerProfileConditionDescriptor conditionDescriptor, out IWorkerProfileCondition condition) + { + foreach (var provider in _conditionProviders) + { + if (provider.TryCreateCondition(conditionDescriptor, out condition)) + { + return true; + } + } + + _logger.LogInformation("Unable to create profile condition for condition type '{conditionType}'", conditionDescriptor.Type); + + condition = null; + return false; + } + + public bool IsCorrectProfileLoaded(string workerRuntime) + { + var profileId = string.Empty; + if (GetEvaluatedProfile(workerRuntime, out WorkerDescriptionProfile profile)) + { + profileId = profile.ProfileId; + } + return _activeProfile.Equals(profileId); + } + } +} diff --git a/src/WebJobs.Script/Workers/Rpc/Configuration/LanguageWorkerOptionsSetup.cs b/src/WebJobs.Script/Workers/Rpc/Configuration/LanguageWorkerOptionsSetup.cs index d4ff25e418..6586b7e25d 100644 --- a/src/WebJobs.Script/Workers/Rpc/Configuration/LanguageWorkerOptionsSetup.cs +++ b/src/WebJobs.Script/Workers/Rpc/Configuration/LanguageWorkerOptionsSetup.cs @@ -12,7 +12,7 @@ namespace Microsoft.Azure.WebJobs.Script.Workers.Rpc { internal class LanguageWorkerOptionsSetup : IConfigureOptions { - private readonly IWorkerProfileConditionManager _profileConditionManager; + private readonly IWorkerProfileManager _profileConditionManager; private readonly IConfiguration _configuration; private readonly ILogger _logger; private readonly IEnvironment _environment; @@ -21,7 +21,7 @@ internal class LanguageWorkerOptionsSetup : IConfigureOptions Profiles { get; set; } } } diff --git a/src/WebJobs.Script/Workers/Rpc/Configuration/RpcWorkerConfigFactory.cs b/src/WebJobs.Script/Workers/Rpc/Configuration/RpcWorkerConfigFactory.cs index 39428a026b..924cfcbc9b 100644 --- a/src/WebJobs.Script/Workers/Rpc/Configuration/RpcWorkerConfigFactory.cs +++ b/src/WebJobs.Script/Workers/Rpc/Configuration/RpcWorkerConfigFactory.cs @@ -21,7 +21,7 @@ internal class RpcWorkerConfigFactory private readonly IConfiguration _config; private readonly ILogger _logger; private readonly ISystemRuntimeInformation _systemRuntimeInformation; - private readonly IWorkerProfileConditionManager _profileConditionManager; + private readonly IWorkerProfileManager _profileManager; private readonly IMetricsLogger _metricsLogger; private readonly string _workerRuntime; private readonly IEnvironment _environment; @@ -31,14 +31,14 @@ internal class RpcWorkerConfigFactory public RpcWorkerConfigFactory(IConfiguration config, ILogger logger, ISystemRuntimeInformation systemRuntimeInfo, - IWorkerProfileConditionManager profileConditionManager, + IWorkerProfileManager profileManager, IEnvironment environment, IMetricsLogger metricsLogger) { _config = config ?? throw new ArgumentNullException(nameof(config)); _logger = logger ?? throw new ArgumentNullException(nameof(logger)); _systemRuntimeInformation = systemRuntimeInfo ?? throw new ArgumentNullException(nameof(systemRuntimeInfo)); - _profileConditionManager = profileConditionManager ?? throw new ArgumentNullException(nameof(profileConditionManager)); + _profileManager = profileManager ?? throw new ArgumentNullException(nameof(profileManager)); _environment = environment ?? throw new ArgumentNullException(nameof(environment)); _metricsLogger = metricsLogger; _workerRuntime = _environment.GetEnvironmentVariable(RpcWorkerConstants.FunctionWorkerRuntimeSettingName); @@ -131,7 +131,8 @@ internal void AddProvider(string workerDir) List workerDescriptionProfiles = ReadWorkerDescriptionProfiles(workerConfig); if (workerDescriptionProfiles.Count > 0) { - workerDescription = LoadWorkerDescriptionFromProfiles(workerDescriptionProfiles, workerDescription); + _profileManager.SaveWorkerDescriptionProfiles(workerDescriptionProfiles, workerDescription.Language); + _profileManager.LoadWorkerDescriptionFromProfiles(workerDescription, out workerDescription); } // Check if any appsettings are provided for that langauge @@ -162,7 +163,6 @@ internal void AddProvider(string workerDir) Description = workerDescription, Arguments = arguments, CountOptions = workerProcessCount, - Profiles = workerDescriptionProfiles, }; _workerDescriptionDictionary[workerDescription.Language] = rpcWorkerConfig; ReadLanguageWorkerFile(arguments.WorkerPath); @@ -180,51 +180,38 @@ private List ReadWorkerDescriptionProfiles(JObject wor { var profiles = workerConfig.GetValue(WorkerConstants.WorkerDescriptionProfiles).ToObject>(); - if (profiles != null && profiles.Count > 0) + if (profiles == null || profiles.Count <= 0) { - var descriptionProfiles = new List(profiles.Count); - try + return new List(0); + } + + var descriptionProfiles = new List(profiles.Count); + try + { + foreach (var profile in profiles) { - foreach (var profile in profiles) - { - var profileConditions = new List(profile.Conditions.Count); + var profileConditions = new List(profile.Conditions.Count); - foreach (var descriptor in profile.Conditions) + foreach (var descriptor in profile.Conditions) + { + if (!_profileManager.TryCreateWorkerProfileCondition(descriptor, out IWorkerProfileCondition condition)) { - if (!_profileConditionManager.TryCreateWorkerProfileCondition(descriptor, out IWorkerProfileCondition condition)) - { - // TODO: Handle this failure here... this should likely translate into an initialization error as we don't - // want to run the worker if we can't evaluate the profile conditions - } - - profileConditions.Add(condition); + // Failed to resolve condition. This profile will be disabled using a mock false condition + _logger?.LogInformation($"Profile {profile.Name} is disabled. Cannout resolve the profile condition {descriptor.Type}"); + condition = new FalseCondition(); } - descriptionProfiles.Add(new (profile.Name, profileConditions, profile.Description)); + profileConditions.Add(condition); } - } - catch (Exception) - { - throw new FormatException("Failed to parse profiles in worker config."); - } - return descriptionProfiles; + descriptionProfiles.Add(new (profile.Name, profileConditions, profile.Description)); + } } - - return new List(0); - } - - private RpcWorkerDescription LoadWorkerDescriptionFromProfiles(List workerDescriptionProfiles, RpcWorkerDescription defaultWorkerDescription) - { - foreach (var profile in workerDescriptionProfiles) + catch (Exception) { - if (profile.EvaluateConditions()) - { - _logger?.LogInformation($"Worker initialized with {profile.Name} profile from worker config."); - return profile.ApplyProfile(defaultWorkerDescription); - } + throw new FormatException("Failed to parse profiles in worker config."); } - return defaultWorkerDescription; + return descriptionProfiles; } internal WorkerProcessCountOptions GetWorkerProcessCount(JObject workerConfig) diff --git a/src/WebJobs.Script/Workers/Rpc/WebHostRpcWorkerChannelManager.cs b/src/WebJobs.Script/Workers/Rpc/WebHostRpcWorkerChannelManager.cs index d0cc4adc5d..22315edc3c 100644 --- a/src/WebJobs.Script/Workers/Rpc/WebHostRpcWorkerChannelManager.cs +++ b/src/WebJobs.Script/Workers/Rpc/WebHostRpcWorkerChannelManager.cs @@ -26,13 +26,14 @@ public class WebHostRpcWorkerChannelManager : IWebHostRpcWorkerChannelManager private readonly ILoggerFactory _loggerFactory = null; private readonly IRpcWorkerChannelFactory _rpcWorkerChannelFactory; private readonly IMetricsLogger _metricsLogger; + private readonly IWorkerProfileManager _profileManager; private string _workerRuntime; private Action _shutdownStandbyWorkerChannels; private IConfiguration _config; private ConcurrentDictionary>> _workerChannels = new ConcurrentDictionary>>(StringComparer.OrdinalIgnoreCase); - public WebHostRpcWorkerChannelManager(IScriptEventManager eventManager, IEnvironment environment, ILoggerFactory loggerFactory, IRpcWorkerChannelFactory rpcWorkerChannelFactory, IOptionsMonitor applicationHostOptions, IMetricsLogger metricsLogger, IOptionsMonitor languageWorkerOptions, IConfiguration config) + public WebHostRpcWorkerChannelManager(IScriptEventManager eventManager, IEnvironment environment, ILoggerFactory loggerFactory, IRpcWorkerChannelFactory rpcWorkerChannelFactory, IOptionsMonitor applicationHostOptions, IMetricsLogger metricsLogger, IOptionsMonitor languageWorkerOptions, IConfiguration config, IWorkerProfileManager profileManager) { _environment = environment ?? throw new ArgumentNullException(nameof(environment)); _eventManager = eventManager; @@ -43,6 +44,7 @@ public WebHostRpcWorkerChannelManager(IScriptEventManager eventManager, IEnviron _applicationHostOptions = applicationHostOptions; _lanuageworkerOptions = languageWorkerOptions; _config = config ?? throw new ArgumentNullException(nameof(config)); + _profileManager = profileManager ?? throw new ArgumentNullException(nameof(profileManager)); _shutdownStandbyWorkerChannels = ScheduleShutdownStandbyChannels; _shutdownStandbyWorkerChannels = _shutdownStandbyWorkerChannels.Debounce(milliseconds: 5000); @@ -141,18 +143,12 @@ private bool UsePlaceholderChannel(string workerRuntime) return false; } - // If a profile evaluates to true and was not previously loaded, restart worker process - RpcWorkerConfig workerConfig = _lanuageworkerOptions.CurrentValue.WorkerConfigs.First(c => c.Description.Language == workerRuntime); - if (workerConfig != null && workerConfig.Profiles != null) + // If a profile evaluates to true and was not previously loaded, restart worker process + if (!_profileManager.IsCorrectProfileLoaded(workerRuntime)) { - foreach (var profile in workerConfig.Profiles) - { - if (profile.EvaluateConditions() && !profile.ProfileLoaded) - { - return false; - } - } + return false; } + // Special case: node and PowerShell apps must be read-only to use the placeholder mode channel // Also cannot use placeholder worker that is targeting ~3 but has backwards compatibility with V2 enabled // TODO: Remove special casing when resolving https://github.com/Azure/azure-functions-host/issues/4534 diff --git a/src/WebJobs.Script/Workers/WorkerConstants.cs b/src/WebJobs.Script/Workers/WorkerConstants.cs index 3371ca4565..79c4a3b6a5 100644 --- a/src/WebJobs.Script/Workers/WorkerConstants.cs +++ b/src/WebJobs.Script/Workers/WorkerConstants.cs @@ -34,6 +34,8 @@ public static class WorkerConstants public const string WorkerDescriptionProfileConditionType = "type"; public const string WorkerDescriptionProfileEnvironmentCondition = "environment"; public const string WorkerDescriptionProfileHostPropertyCondition = "hostProperty"; + public const string WorkerDescriptionProfileConditionName = "name"; + public const string WorkerDescriptionProfileConditionExpression = "expression"; public const string WorkerDescriptionAppServiceEnvProfileName = "appServiceEnvironment"; // Logs diff --git a/test/WebJobs.Script.Tests/Configuration/LanguageWorkerOptionsSetupTests.cs b/test/WebJobs.Script.Tests/Configuration/LanguageWorkerOptionsSetupTests.cs index 7a4921a469..ae7cd93ab8 100644 --- a/test/WebJobs.Script.Tests/Configuration/LanguageWorkerOptionsSetupTests.cs +++ b/test/WebJobs.Script.Tests/Configuration/LanguageWorkerOptionsSetupTests.cs @@ -25,7 +25,7 @@ public void LanguageWorkerOptions_Expected_ListOfConfigs(string workerRuntime) .Add(new ScriptEnvironmentVariablesConfigurationSource()); var systemConditionProvider = new SystemConditionProvider(new TestLogger(), runtimeInfo, testEnvironment); - var profileConditionManager = new WorkerProfileConditionManager(new TestLogger(), new[] { systemConditionProvider }); + var profileConditionManager = new WorkerProfileManager(new TestLogger(), new[] { systemConditionProvider }); var configuration = configurationBuilder.Build(); if (!string.IsNullOrEmpty(workerRuntime)) diff --git a/test/WebJobs.Script.Tests/Workers/Rpc/RpcWorkerConfigFactoryTests.cs b/test/WebJobs.Script.Tests/Workers/Rpc/RpcWorkerConfigFactoryTests.cs index b9c1db7e7a..2eb7a73175 100644 --- a/test/WebJobs.Script.Tests/Workers/Rpc/RpcWorkerConfigFactoryTests.cs +++ b/test/WebJobs.Script.Tests/Workers/Rpc/RpcWorkerConfigFactoryTests.cs @@ -17,7 +17,7 @@ namespace Microsoft.Azure.WebJobs.Script.Tests.Workers.Rpc { public class RpcWorkerConfigFactoryTests : IDisposable { - private readonly WorkerProfileConditionManager _profileConditionManager; + private readonly WorkerProfileManager _profileManager; private TestSystemRuntimeInformation _testSysRuntimeInfo = new TestSystemRuntimeInformation(); private TestEnvironment _testEnvironment; @@ -26,7 +26,7 @@ public RpcWorkerConfigFactoryTests() _testEnvironment = new TestEnvironment(); var systemConditionProvider = new SystemConditionProvider(new TestLogger(), _testSysRuntimeInfo, _testEnvironment); - _profileConditionManager = new WorkerProfileConditionManager(new TestLogger(), new[] { systemConditionProvider }); + _profileManager = new WorkerProfileManager(new TestLogger(), new[] { systemConditionProvider }); } public void Dispose() @@ -40,7 +40,7 @@ public void DefaultLanguageWorkersDir() var expectedWorkersDir = Path.Combine(Path.GetDirectoryName(new Uri(typeof(RpcWorkerConfigFactory).Assembly.Location).LocalPath), RpcWorkerConstants.DefaultWorkersDirectoryName); var config = new ConfigurationBuilder().Build(); var testLogger = new TestLogger("test"); - var configFactory = new RpcWorkerConfigFactory(config, testLogger, _testSysRuntimeInfo, _profileConditionManager, _testEnvironment, new TestMetricsLogger()); + var configFactory = new RpcWorkerConfigFactory(config, testLogger, _testSysRuntimeInfo, _profileManager, _testEnvironment, new TestMetricsLogger()); Assert.Equal(expectedWorkersDir, configFactory.WorkersDirPath); } @@ -73,7 +73,7 @@ public void LanguageWorker_WorkersDir_Set() }) .Build(); var testLogger = new TestLogger("test"); - var configFactory = new RpcWorkerConfigFactory(config, testLogger, _testSysRuntimeInfo, _profileConditionManager, _testEnvironment, new TestMetricsLogger()); + var configFactory = new RpcWorkerConfigFactory(config, testLogger, _testSysRuntimeInfo, _profileManager, _testEnvironment, new TestMetricsLogger()); Assert.Equal(expectedWorkersDir, configFactory.WorkersDirPath); } @@ -89,7 +89,7 @@ public void LanguageWorker_WorkersDir_NotSet() var config = configBuilder.Build(); var scriptSettingsManager = new ScriptSettingsManager(config); var testLogger = new TestLogger("test"); - var configFactory = new RpcWorkerConfigFactory(config, testLogger, _testSysRuntimeInfo, _profileConditionManager, _testEnvironment, new TestMetricsLogger()); + var configFactory = new RpcWorkerConfigFactory(config, testLogger, _testSysRuntimeInfo, _profileManager, _testEnvironment, new TestMetricsLogger()); Assert.Equal(expectedWorkersDir, configFactory.WorkersDirPath); } @@ -100,7 +100,7 @@ public void JavaPath_FromEnvVars() var config = configBuilder.Build(); var scriptSettingsManager = new ScriptSettingsManager(config); var testLogger = new TestLogger("test"); - var configFactory = new RpcWorkerConfigFactory(config, testLogger, _testSysRuntimeInfo, _profileConditionManager, _testEnvironment, new TestMetricsLogger()); + var configFactory = new RpcWorkerConfigFactory(config, testLogger, _testSysRuntimeInfo, _profileManager, _testEnvironment, new TestMetricsLogger()); var workerConfigs = configFactory.GetConfigs(); var javaPath = workerConfigs.Where(c => c.Description.Language.Equals("java", StringComparison.OrdinalIgnoreCase)).FirstOrDefault().Description.DefaultExecutablePath; Assert.DoesNotContain(@"%JAVA_HOME%", javaPath); @@ -121,7 +121,7 @@ public void DefaultWorkerConfigs_Overrides_DefaultWorkerRuntimeVersion_AppSettin var testLogger = new TestLogger("test"); using (var variables = new TestScopedSettings(scriptSettingsManager, testEnvVariables)) { - var configFactory = new RpcWorkerConfigFactory(config, testLogger, _testSysRuntimeInfo, _profileConditionManager, _testEnvironment, new TestMetricsLogger()); + var configFactory = new RpcWorkerConfigFactory(config, testLogger, _testSysRuntimeInfo, _profileManager, _testEnvironment, new TestMetricsLogger()); var workerConfigs = configFactory.GetConfigs(); var pythonWorkerConfig = workerConfigs.Where(w => w.Description.Language.Equals("python", StringComparison.OrdinalIgnoreCase)).FirstOrDefault(); var powershellWorkerConfig = workerConfigs.Where(w => w.Description.Language.Equals("powershell", StringComparison.OrdinalIgnoreCase)).FirstOrDefault(); @@ -143,7 +143,7 @@ public void DefaultWorkerConfigs_Overrides_VersionAppSetting() var config = configBuilder.Build(); var scriptSettingsManager = new ScriptSettingsManager(config); var testLogger = new TestLogger("test"); - var configFactory = new RpcWorkerConfigFactory(config, testLogger, _testSysRuntimeInfo, _profileConditionManager, testEnvironment, new TestMetricsLogger()); + var configFactory = new RpcWorkerConfigFactory(config, testLogger, _testSysRuntimeInfo, _profileManager, testEnvironment, new TestMetricsLogger()); var workerConfigs = configFactory.GetConfigs(); var powershellWorkerConfig = workerConfigs.Where(w => w.Description.Language.Equals("powershell", StringComparison.OrdinalIgnoreCase)).FirstOrDefault(); Assert.Equal(1, workerConfigs.Count); @@ -170,7 +170,7 @@ public void ShouldAddProvider_Returns_Expected(string workerLanguage, string wor } var config = new ConfigurationBuilder().Build(); var testLogger = new TestLogger("test"); - RpcWorkerConfigFactory rpcWorkerConfigFactory = new RpcWorkerConfigFactory(config, testLogger, _testSysRuntimeInfo, _profileConditionManager, _testEnvironment, new TestMetricsLogger()); + RpcWorkerConfigFactory rpcWorkerConfigFactory = new RpcWorkerConfigFactory(config, testLogger, _testSysRuntimeInfo, _profileManager, _testEnvironment, new TestMetricsLogger()); _testEnvironment.SetEnvironmentVariable(RpcWorkerConstants.FunctionWorkerRuntimeSettingName, workerRuntime); Assert.Equal(expectedResult, rpcWorkerConfigFactory.ShouldAddWorkerConfig(workerLanguage)); } @@ -200,7 +200,7 @@ public void GetWorkerProcessCount_Tests(bool defaultWorkerConfig, bool setProces } var config = new ConfigurationBuilder().Build(); var testLogger = new TestLogger("test"); - RpcWorkerConfigFactory rpcWorkerConfigFactory = new RpcWorkerConfigFactory(config, testLogger, _testSysRuntimeInfo, _profileConditionManager, _testEnvironment, new TestMetricsLogger()); + RpcWorkerConfigFactory rpcWorkerConfigFactory = new RpcWorkerConfigFactory(config, testLogger, _testSysRuntimeInfo, _profileManager, _testEnvironment, new TestMetricsLogger()); var result = rpcWorkerConfigFactory.GetWorkerProcessCount(workerConfig); if (defaultWorkerConfig) { @@ -244,7 +244,7 @@ public void GetWorkerProcessCount_ThrowsException_Tests() var config = new ConfigurationBuilder().Build(); var testLogger = new TestLogger("test"); - RpcWorkerConfigFactory rpcWorkerConfigFactory = new RpcWorkerConfigFactory(config, testLogger, _testSysRuntimeInfo, _profileConditionManager, _testEnvironment, new TestMetricsLogger()); + RpcWorkerConfigFactory rpcWorkerConfigFactory = new RpcWorkerConfigFactory(config, testLogger, _testSysRuntimeInfo, _profileManager, _testEnvironment, new TestMetricsLogger()); var resultEx1 = Assert.Throws(() => rpcWorkerConfigFactory.GetWorkerProcessCount(workerConfig)); Assert.Contains("ProcessCount must be greater than 0", resultEx1.Message); diff --git a/test/WebJobs.Script.Tests/Workers/Rpc/RpcWorkerConfigTests.cs b/test/WebJobs.Script.Tests/Workers/Rpc/RpcWorkerConfigTests.cs index 35c325ab9c..49e8991dd5 100644 --- a/test/WebJobs.Script.Tests/Workers/Rpc/RpcWorkerConfigTests.cs +++ b/test/WebJobs.Script.Tests/Workers/Rpc/RpcWorkerConfigTests.cs @@ -26,14 +26,14 @@ public class RpcWorkerConfigTests : IDisposable private static string testLanguage = "testLanguage"; private readonly TestSystemRuntimeInformation _testSysRuntimeInfo = new TestSystemRuntimeInformation(); private readonly TestEnvironment _testEnvironment; - private readonly WorkerProfileConditionManager _profileConditionManager; + private readonly WorkerProfileManager _profileManager; public RpcWorkerConfigTests() { _testEnvironment = new TestEnvironment(); var systemConditionProvider = new SystemConditionProvider(new TestLogger(), _testSysRuntimeInfo, _testEnvironment); - _profileConditionManager = new WorkerProfileConditionManager(new TestLogger(), new[] { systemConditionProvider }); + _profileManager = new WorkerProfileManager(new TestLogger(), new[] { systemConditionProvider }); } public static IEnumerable InvalidWorkerDescriptions @@ -633,7 +633,7 @@ private IEnumerable TestReadWorkerProviderFromConfig(IEnumerabl var scriptHostOptions = new ScriptJobHostOptions(); var scriptSettingsManager = new ScriptSettingsManager(config); - var configFactory = new RpcWorkerConfigFactory(config, testLogger, _testSysRuntimeInfo, _profileConditionManager, _testEnvironment, testMetricsLogger); + var configFactory = new RpcWorkerConfigFactory(config, testLogger, _testSysRuntimeInfo, _profileManager, _testEnvironment, testMetricsLogger); if (appSvcEnv) { var testEnvVariables = new Dictionary diff --git a/test/WebJobs.Script.Tests/Workers/Rpc/WebHostRpcWorkerChannelManagerTests.cs b/test/WebJobs.Script.Tests/Workers/Rpc/WebHostRpcWorkerChannelManagerTests.cs index af9ee8f0a7..0ac14c3f06 100644 --- a/test/WebJobs.Script.Tests/Workers/Rpc/WebHostRpcWorkerChannelManagerTests.cs +++ b/test/WebJobs.Script.Tests/Workers/Rpc/WebHostRpcWorkerChannelManagerTests.cs @@ -34,6 +34,8 @@ public class WebHostRpcWorkerChannelManagerTests private readonly Mock _rpcWorkerProcess; private readonly TestLogger _testLogger; private readonly IConfiguration _emptyConfig; + private readonly TestSystemRuntimeInformation _testSysRuntimeInfo = new TestSystemRuntimeInformation(); + private readonly WorkerProfileManager _profileManager; private WebHostRpcWorkerChannelManager _rpcWorkerChannelManager; @@ -71,7 +73,9 @@ public WebHostRpcWorkerChannelManagerTests() _testLogger = new TestLogger("WebHostLanguageWorkerChannelManagerTests"); _rpcWorkerChannelFactory = new TestRpcWorkerChannelFactory(_eventManager, _testLogger, _scriptRootPath); _emptyConfig = new ConfigurationBuilder().Build(); - _rpcWorkerChannelManager = new WebHostRpcWorkerChannelManager(_eventManager, _testEnvironment, _loggerFactory, _rpcWorkerChannelFactory, _optionsMonitor, new TestMetricsLogger(), _workerOptionsMonitor, _emptyConfig); + var systemConditionProvider = new SystemConditionProvider(new TestLogger(), _testSysRuntimeInfo, _testEnvironment); + _profileManager = new WorkerProfileManager(new TestLogger(), new[] { systemConditionProvider }); + _rpcWorkerChannelManager = new WebHostRpcWorkerChannelManager(_eventManager, _testEnvironment, _loggerFactory, _rpcWorkerChannelFactory, _optionsMonitor, new TestMetricsLogger(), _workerOptionsMonitor, _emptyConfig, _profileManager); } [Fact] @@ -92,7 +96,7 @@ public async Task ShutdownStandByChannels_Succeeds() { var testMetricsLogger = new TestMetricsLogger(); _testEnvironment.SetEnvironmentVariable(RpcWorkerConstants.FunctionWorkerRuntimeSettingName, RpcWorkerConstants.JavaLanguageWorkerName); - _rpcWorkerChannelManager = new WebHostRpcWorkerChannelManager(_eventManager, _testEnvironment, _loggerFactory, _rpcWorkerChannelFactory, _optionsMonitor, testMetricsLogger, _workerOptionsMonitor, _emptyConfig); + _rpcWorkerChannelManager = new WebHostRpcWorkerChannelManager(_eventManager, _testEnvironment, _loggerFactory, _rpcWorkerChannelFactory, _optionsMonitor, testMetricsLogger, _workerOptionsMonitor, _emptyConfig, _profileManager); IRpcWorkerChannel javaWorkerChannel = CreateTestChannel(RpcWorkerConstants.JavaLanguageWorkerName); @@ -113,7 +117,7 @@ public async Task ShutdownStandByChannels_WorkerRuntinmeDotNet_Succeeds() { var testMetricsLogger = new TestMetricsLogger(); _testEnvironment.SetEnvironmentVariable(RpcWorkerConstants.FunctionWorkerRuntimeSettingName, RpcWorkerConstants.DotNetLanguageWorkerName); - _rpcWorkerChannelManager = new WebHostRpcWorkerChannelManager(_eventManager, _testEnvironment, _loggerFactory, _rpcWorkerChannelFactory, _optionsMonitor, testMetricsLogger, _workerOptionsMonitor, _emptyConfig); + _rpcWorkerChannelManager = new WebHostRpcWorkerChannelManager(_eventManager, _testEnvironment, _loggerFactory, _rpcWorkerChannelFactory, _optionsMonitor, testMetricsLogger, _workerOptionsMonitor, _emptyConfig, _profileManager); IRpcWorkerChannel javaWorkerChannel = CreateTestChannel(RpcWorkerConstants.JavaLanguageWorkerName); IRpcWorkerChannel nodeWorkerChannel = CreateTestChannel(RpcWorkerConstants.NodeLanguageWorkerName); @@ -152,7 +156,7 @@ public async Task ShutdownStandyChannels_WorkerRuntime_Node_Set() { var testMetricsLogger = new TestMetricsLogger(); _testEnvironment.SetEnvironmentVariable(RpcWorkerConstants.FunctionWorkerRuntimeSettingName, RpcWorkerConstants.NodeLanguageWorkerName); - _rpcWorkerChannelManager = new WebHostRpcWorkerChannelManager(_eventManager, _testEnvironment, _loggerFactory, _rpcWorkerChannelFactory, _optionsMonitor, testMetricsLogger, _workerOptionsMonitor, _emptyConfig); + _rpcWorkerChannelManager = new WebHostRpcWorkerChannelManager(_eventManager, _testEnvironment, _loggerFactory, _rpcWorkerChannelFactory, _optionsMonitor, testMetricsLogger, _workerOptionsMonitor, _emptyConfig, _profileManager); IRpcWorkerChannel javaWorkerChannel = CreateTestChannel(RpcWorkerConstants.JavaLanguageWorkerName); @@ -175,7 +179,7 @@ public async Task SpecializeAsync_ReadOnly_KeepsProcessAlive(string runtime, str _testEnvironment.SetEnvironmentVariable(RpcWorkerConstants.FunctionWorkerRuntimeSettingName, runtime); _optionsMonitor.CurrentValue.IsFileSystemReadOnly = true; - _rpcWorkerChannelManager = new WebHostRpcWorkerChannelManager(_eventManager, _testEnvironment, _loggerFactory, _rpcWorkerChannelFactory, _optionsMonitor, testMetricsLogger, _workerOptionsMonitor, _emptyConfig); + _rpcWorkerChannelManager = new WebHostRpcWorkerChannelManager(_eventManager, _testEnvironment, _loggerFactory, _rpcWorkerChannelFactory, _optionsMonitor, testMetricsLogger, _workerOptionsMonitor, _emptyConfig, _profileManager); IRpcWorkerChannel workerChannel = CreateTestChannel(languageWorkerName); @@ -204,7 +208,7 @@ public async Task SpecializeAsync_Java_KeepsProcessAlive() _testEnvironment.SetEnvironmentVariable(RpcWorkerConstants.FunctionWorkerRuntimeSettingName, RpcWorkerConstants.JavaLanguageWorkerName); _testEnvironment.SetEnvironmentVariable(EnvironmentSettingNames.AzureWebsiteZipDeployment, "0"); - _rpcWorkerChannelManager = new WebHostRpcWorkerChannelManager(_eventManager, _testEnvironment, _loggerFactory, _rpcWorkerChannelFactory, _optionsMonitor, testMetricsLogger, _workerOptionsMonitor, _emptyConfig); + _rpcWorkerChannelManager = new WebHostRpcWorkerChannelManager(_eventManager, _testEnvironment, _loggerFactory, _rpcWorkerChannelFactory, _optionsMonitor, testMetricsLogger, _workerOptionsMonitor, _emptyConfig, _profileManager); IRpcWorkerChannel javaWorkerChannel = CreateTestChannel(RpcWorkerConstants.JavaLanguageWorkerName); @@ -234,7 +238,7 @@ public async Task SpecializeAsync_Java_ReadOnly_KeepsProcessAlive() _testEnvironment.SetEnvironmentVariable(RpcWorkerConstants.FunctionWorkerRuntimeSettingName, RpcWorkerConstants.JavaLanguageWorkerName); _testEnvironment.SetEnvironmentVariable(EnvironmentSettingNames.AzureWebsiteZipDeployment, "1"); - _rpcWorkerChannelManager = new WebHostRpcWorkerChannelManager(_eventManager, _testEnvironment, _loggerFactory, _rpcWorkerChannelFactory, _optionsMonitor, testMetricsLogger, _workerOptionsMonitor, _emptyConfig); + _rpcWorkerChannelManager = new WebHostRpcWorkerChannelManager(_eventManager, _testEnvironment, _loggerFactory, _rpcWorkerChannelFactory, _optionsMonitor, testMetricsLogger, _workerOptionsMonitor, _emptyConfig, _profileManager); IRpcWorkerChannel javaWorkerChannel = CreateTestChannel(RpcWorkerConstants.JavaLanguageWorkerName); @@ -268,7 +272,7 @@ public async Task SpecializeAsync_NotReadOnly_KillsProcess(string languageWorker // This is an invalid setting configuration, but just to show that run from zip is NOT set _testEnvironment.SetEnvironmentVariable(EnvironmentSettingNames.AzureWebsiteZipDeployment, "0"); - _rpcWorkerChannelManager = new WebHostRpcWorkerChannelManager(_eventManager, _testEnvironment, _loggerFactory, _rpcWorkerChannelFactory, _optionsMonitor, testMetricsLogger, _workerOptionsMonitor, _emptyConfig); + _rpcWorkerChannelManager = new WebHostRpcWorkerChannelManager(_eventManager, _testEnvironment, _loggerFactory, _rpcWorkerChannelFactory, _optionsMonitor, testMetricsLogger, _workerOptionsMonitor, _emptyConfig, _profileManager); IRpcWorkerChannel workerChannel = CreateTestChannel(languageWorkerName); @@ -301,7 +305,7 @@ public async Task SpecializeAsync_LanguageWorkerArguments_KillsProcess(string la [$"{RpcWorkerConstants.LanguageWorkersSectionName}:{languageWorkerName}:{WorkerConstants.WorkerDescriptionArguments}"] = argument }) .Build(); - _rpcWorkerChannelManager = new WebHostRpcWorkerChannelManager(_eventManager, _testEnvironment, _loggerFactory, _rpcWorkerChannelFactory, _optionsMonitor, testMetricsLogger, _workerOptionsMonitor, config); + _rpcWorkerChannelManager = new WebHostRpcWorkerChannelManager(_eventManager, _testEnvironment, _loggerFactory, _rpcWorkerChannelFactory, _optionsMonitor, testMetricsLogger, _workerOptionsMonitor, config, _profileManager); IRpcWorkerChannel workerChannel = CreateTestChannel(languageWorkerName); @@ -327,7 +331,7 @@ public async Task SpecializeAsync_Node_V2CompatibilityWithV3Extension_KillsProce _testEnvironment.SetEnvironmentVariable(EnvironmentSettingNames.FunctionsV2CompatibilityModeKey, "true"); _testEnvironment.SetEnvironmentVariable(EnvironmentSettingNames.FunctionsExtensionVersion, "~3"); - _rpcWorkerChannelManager = new WebHostRpcWorkerChannelManager(_eventManager, _testEnvironment, _loggerFactory, _rpcWorkerChannelFactory, _optionsMonitor, testMetricsLogger, _workerOptionsMonitor, _emptyConfig); + _rpcWorkerChannelManager = new WebHostRpcWorkerChannelManager(_eventManager, _testEnvironment, _loggerFactory, _rpcWorkerChannelFactory, _optionsMonitor, testMetricsLogger, _workerOptionsMonitor, _emptyConfig, _profileManager); IRpcWorkerChannel workerChannel = CreateTestChannel(languageWorkerName); @@ -402,7 +406,7 @@ await TestHelpers.Await(() => public async Task InitializeLanguageWorkerChannel_ThrowsOnProcessStartup() { var rpcWorkerChannelFactory = new TestRpcWorkerChannelFactory(_eventManager, null, _scriptRootPath, throwOnProcessStartUp: true); - var rpcWorkerChannelManager = new WebHostRpcWorkerChannelManager(_eventManager, _testEnvironment, _loggerFactory, rpcWorkerChannelFactory, _optionsMonitor, new TestMetricsLogger(), _workerOptionsMonitor, _emptyConfig); + var rpcWorkerChannelManager = new WebHostRpcWorkerChannelManager(_eventManager, _testEnvironment, _loggerFactory, rpcWorkerChannelFactory, _optionsMonitor, new TestMetricsLogger(), _workerOptionsMonitor, _emptyConfig, _profileManager); var rpcWorkerChannel = await rpcWorkerChannelManager.InitializeLanguageWorkerChannel("test", _scriptRootPath); var ex = await Assert.ThrowsAsync(async () => await rpcWorkerChannelManager.GetChannelAsync("test")); Assert.Contains("Process startup failed", ex.InnerException.Message); From da7546d254b4ec6ff0c994f4c49b909e3eb22788 Mon Sep 17 00:00:00 2001 From: Shreyas Gopalakrishna Date: Fri, 20 May 2022 01:42:21 -0500 Subject: [PATCH 4/9] Addressing review comments --- .../Workers/Profiles/EnvironmentCondition.cs | 8 +++-- .../Workers/Profiles/FalseCondition.cs | 8 ++--- .../Workers/Profiles/HostPropertyCondition.cs | 32 ++++++++----------- .../Profiles/IWorkerProfileCondition.cs | 4 +++ .../IWorkerProfileConditionProvider.cs | 8 ++--- .../Workers/Profiles/IWorkerProfileManager.cs | 13 ++++++++ .../Profiles/SystemConditionProvider.cs | 1 + .../Profiles/WorkerDescriptionProfile.cs | 16 +++++----- .../Workers/Profiles/WorkerProfileManager.cs | 10 ++++-- .../Rpc/Configuration/RpcWorkerConfig.cs | 2 -- .../Configuration/RpcWorkerConfigFactory.cs | 16 ++++++---- .../Rpc/WebHostRpcWorkerChannelManager.cs | 9 +++++- 12 files changed, 78 insertions(+), 49 deletions(-) diff --git a/src/WebJobs.Script/Workers/Profiles/EnvironmentCondition.cs b/src/WebJobs.Script/Workers/Profiles/EnvironmentCondition.cs index 7428b7b1f8..04c2e3ccdc 100644 --- a/src/WebJobs.Script/Workers/Profiles/EnvironmentCondition.cs +++ b/src/WebJobs.Script/Workers/Profiles/EnvironmentCondition.cs @@ -9,6 +9,7 @@ namespace Microsoft.Azure.WebJobs.Script.Workers { + // Environment condition checks if environment variables match the expected output public class EnvironmentCondition : IWorkerProfileCondition { private readonly ILogger _logger; @@ -37,20 +38,23 @@ internal EnvironmentCondition(ILogger logger, IEnvironment environment, WorkerPr public string Expression => _expression; + /// public bool Evaluate() { string value = _environment.GetEnvironmentVariable(Name); + if (string.IsNullOrEmpty(value)) { return false; } - _logger.LogDebug($"Evaluating EnvironmentCondition with value: {value} and expression {Expression}"); + _logger.LogDebug($"Evaluating EnvironmentCondition with value '{value}' and expression '{Expression}'"); return _regex.IsMatch(value); } - public void Validate() + // Validates if condition parametrs meet expected values, fail if they don't + internal void Validate() { if (string.IsNullOrEmpty(Name)) { diff --git a/src/WebJobs.Script/Workers/Profiles/FalseCondition.cs b/src/WebJobs.Script/Workers/Profiles/FalseCondition.cs index 5f1a274815..de5bcf782c 100644 --- a/src/WebJobs.Script/Workers/Profiles/FalseCondition.cs +++ b/src/WebJobs.Script/Workers/Profiles/FalseCondition.cs @@ -1,14 +1,10 @@ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. -using System; -using System.ComponentModel.DataAnnotations; -using System.Text.RegularExpressions; -using Microsoft.Azure.WebJobs.Script.Workers.Profiles; -using Microsoft.Extensions.Logging; - namespace Microsoft.Azure.WebJobs.Script.Workers { + // The false condition always evalues to false. + // This condition is used to disable a profile when condition providers fail to resolve conditions. public class FalseCondition : IWorkerProfileCondition { public bool Evaluate() diff --git a/src/WebJobs.Script/Workers/Profiles/HostPropertyCondition.cs b/src/WebJobs.Script/Workers/Profiles/HostPropertyCondition.cs index 70e0fb4a92..3396905c4f 100644 --- a/src/WebJobs.Script/Workers/Profiles/HostPropertyCondition.cs +++ b/src/WebJobs.Script/Workers/Profiles/HostPropertyCondition.cs @@ -11,6 +11,7 @@ namespace Microsoft.Azure.WebJobs.Script.Workers { + // HostPropertycondition checks if host match the expected output for properties such as Sku, Platform, HostVersion internal class HostPropertyCondition : IWorkerProfileCondition { private readonly ILogger _logger; @@ -35,7 +36,7 @@ public HostPropertyCondition(ILogger logger, ISystemRuntimeInformation systemRun Validate(); } - public enum ConditionHostPropertyName + public enum HostProperty { Sku, Platform, @@ -46,24 +47,18 @@ public enum ConditionHostPropertyName public string Expression => _expression; + /// public bool Evaluate() { - string value = string.Empty; - Enum.TryParse(Name, out ConditionHostPropertyName hostPropertyName); - switch (hostPropertyName) + Enum.TryParse(Name, out HostProperty hostPropertyName); + + string value = hostPropertyName switch { - case ConditionHostPropertyName.Sku: - value = ScriptSettingsManager.Instance.GetSetting(EnvironmentSettingNames.AzureWebsiteSku); - break; - case ConditionHostPropertyName.Platform: - value = _systemRuntimeInformation.GetOSPlatform().ToString(); - break; - case ConditionHostPropertyName.HostVersion: - value = ScriptHost.Version; - break; - default: - break; - } + HostProperty.Sku => ScriptSettingsManager.Instance.GetSetting(EnvironmentSettingNames.AzureWebsiteSku), + HostProperty.Platform => _systemRuntimeInformation.GetOSPlatform().ToString(), + HostProperty.HostVersion => ScriptHost.Version, + _ => null + }; if (string.IsNullOrEmpty(value)) { @@ -75,14 +70,15 @@ public bool Evaluate() return _regex.IsMatch(value); } - public void Validate() + // Validates if condition parametrs meet expected values, fail if they don't + internal void Validate() { if (string.IsNullOrEmpty(Name)) { throw new ValidationException($"HostPropertyCondition {nameof(Name)} cannot be empty."); } - if (!Enum.GetNames(typeof(ConditionHostPropertyName)).Any(x => x.ToLower().Contains(Name))) + if (!Enum.GetNames(typeof(HostProperty)).Any(x => x.ToLower().Contains(Name))) { throw new ValidationException($"HostPropertyCondition {nameof(Name)} is not a valid host property name."); } diff --git a/src/WebJobs.Script/Workers/Profiles/IWorkerProfileCondition.cs b/src/WebJobs.Script/Workers/Profiles/IWorkerProfileCondition.cs index e8987eeb15..2444d6a021 100644 --- a/src/WebJobs.Script/Workers/Profiles/IWorkerProfileCondition.cs +++ b/src/WebJobs.Script/Workers/Profiles/IWorkerProfileCondition.cs @@ -3,8 +3,12 @@ namespace Microsoft.Azure.WebJobs.Script.Workers { + // Interface for different types of conditions public interface IWorkerProfileCondition { + /// + /// Check if different condition type meet their criteria + /// bool Evaluate(); } } \ No newline at end of file diff --git a/src/WebJobs.Script/Workers/Profiles/IWorkerProfileConditionProvider.cs b/src/WebJobs.Script/Workers/Profiles/IWorkerProfileConditionProvider.cs index 351f4ba8fe..6dcbca8f8e 100644 --- a/src/WebJobs.Script/Workers/Profiles/IWorkerProfileConditionProvider.cs +++ b/src/WebJobs.Script/Workers/Profiles/IWorkerProfileConditionProvider.cs @@ -1,14 +1,14 @@ // 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.Text; - namespace Microsoft.Azure.WebJobs.Script.Workers.Profiles { + // Manage differnt conditions internal interface IWorkerProfileConditionProvider { + /// + /// Factory method to create a profile condition + /// bool TryCreateCondition(WorkerProfileConditionDescriptor descriptor, out IWorkerProfileCondition condition); } } diff --git a/src/WebJobs.Script/Workers/Profiles/IWorkerProfileManager.cs b/src/WebJobs.Script/Workers/Profiles/IWorkerProfileManager.cs index afc9520697..f69628cd12 100644 --- a/src/WebJobs.Script/Workers/Profiles/IWorkerProfileManager.cs +++ b/src/WebJobs.Script/Workers/Profiles/IWorkerProfileManager.cs @@ -7,14 +7,27 @@ namespace Microsoft.Azure.WebJobs.Script.Workers { + // Regulate profile operations through the profile manager public interface IWorkerProfileManager { + /// + /// Creates profile condition using different condition descriptor properties + /// bool TryCreateWorkerProfileCondition(WorkerProfileConditionDescriptor conditionDescriptor, out IWorkerProfileCondition condition); + /// + /// Save different profiles for a given worker runtime language + /// void SaveWorkerDescriptionProfiles(List workerDescriptionProfiles, string language); + /// + /// Load a profile that meets it's conditions + /// void LoadWorkerDescriptionFromProfiles(RpcWorkerDescription defaultWorkerDescription, out RpcWorkerDescription workerDescription); + /// + /// Verify if the current profile's conditions have changed + /// bool IsCorrectProfileLoaded(string workerRuntime); } } diff --git a/src/WebJobs.Script/Workers/Profiles/SystemConditionProvider.cs b/src/WebJobs.Script/Workers/Profiles/SystemConditionProvider.cs index c0398c8eb0..d2f7be2ca7 100644 --- a/src/WebJobs.Script/Workers/Profiles/SystemConditionProvider.cs +++ b/src/WebJobs.Script/Workers/Profiles/SystemConditionProvider.cs @@ -20,6 +20,7 @@ public SystemConditionProvider(ILogger logger, ISystemR _systemRuntimeInformation = systemRuntimeInfo ?? throw new ArgumentNullException(nameof(systemRuntimeInfo)); } + /// public bool TryCreateCondition(WorkerProfileConditionDescriptor descriptor, out IWorkerProfileCondition condition) { condition = descriptor.Type switch diff --git a/src/WebJobs.Script/Workers/Profiles/WorkerDescriptionProfile.cs b/src/WebJobs.Script/Workers/Profiles/WorkerDescriptionProfile.cs index 3fa5039f90..659c11f82a 100644 --- a/src/WebJobs.Script/Workers/Profiles/WorkerDescriptionProfile.cs +++ b/src/WebJobs.Script/Workers/Profiles/WorkerDescriptionProfile.cs @@ -69,15 +69,15 @@ public bool EvaluateConditions() /// /// Overrides the worker description parameters to that available in the profile /// - public RpcWorkerDescription ApplyProfile(RpcWorkerDescription defaultWorkerDescription) + public RpcWorkerDescription ApplyProfile(RpcWorkerDescription workerDescription) { - defaultWorkerDescription.Arguments = UseProfileOrDefault(ProfileDescription.Arguments, defaultWorkerDescription.Arguments); - defaultWorkerDescription.DefaultExecutablePath = UseProfileOrDefault(ProfileDescription.DefaultExecutablePath, defaultWorkerDescription.DefaultExecutablePath); - defaultWorkerDescription.DefaultWorkerPath = UseProfileOrDefault(ProfileDescription.DefaultWorkerPath, defaultWorkerDescription.DefaultWorkerPath); - defaultWorkerDescription.Extensions = UseProfileOrDefault(ProfileDescription.Extensions, defaultWorkerDescription.Extensions) as List; - defaultWorkerDescription.Language = UseProfileOrDefault(ProfileDescription.Language, defaultWorkerDescription.Language); - defaultWorkerDescription.WorkerDirectory = UseProfileOrDefault(ProfileDescription.WorkerDirectory, defaultWorkerDescription.WorkerDirectory); - return defaultWorkerDescription; + workerDescription.Arguments = UseProfileOrDefault(ProfileDescription.Arguments, workerDescription.Arguments); + workerDescription.DefaultExecutablePath = UseProfileOrDefault(ProfileDescription.DefaultExecutablePath, workerDescription.DefaultExecutablePath); + workerDescription.DefaultWorkerPath = UseProfileOrDefault(ProfileDescription.DefaultWorkerPath, workerDescription.DefaultWorkerPath); + workerDescription.Extensions = UseProfileOrDefault(ProfileDescription.Extensions, workerDescription.Extensions) as List; + workerDescription.Language = UseProfileOrDefault(ProfileDescription.Language, workerDescription.Language); + workerDescription.WorkerDirectory = UseProfileOrDefault(ProfileDescription.WorkerDirectory, workerDescription.WorkerDirectory); + return workerDescription; } private string UseProfileOrDefault(string profileParameter, string defaultParameter) diff --git a/src/WebJobs.Script/Workers/Profiles/WorkerProfileManager.cs b/src/WebJobs.Script/Workers/Profiles/WorkerProfileManager.cs index 4161e984f9..410ff1284c 100644 --- a/src/WebJobs.Script/Workers/Profiles/WorkerProfileManager.cs +++ b/src/WebJobs.Script/Workers/Profiles/WorkerProfileManager.cs @@ -9,6 +9,7 @@ namespace Microsoft.Azure.WebJobs.Script.Workers { + // The default profile manager that manages profiles from language workers internal class WorkerProfileManager : IWorkerProfileManager { private readonly ILogger _logger; @@ -16,19 +17,20 @@ internal class WorkerProfileManager : IWorkerProfileManager private Dictionary> _profiles = new Dictionary>(); private string _activeProfile; - public WorkerProfileManager(ILogger logger, - IEnumerable conditionProviders) + public WorkerProfileManager(ILogger logger, IEnumerable conditionProviders) { _logger = logger ?? throw new ArgumentNullException(nameof(logger)); _conditionProviders = conditionProviders ?? throw new ArgumentNullException(nameof(conditionProviders)); _activeProfile = string.Empty; } + /// public void SaveWorkerDescriptionProfiles(List workerDescriptionProfiles, string language) { _profiles.Add(language, workerDescriptionProfiles); } + // Evaluate profile conditions for a language private bool GetEvaluatedProfile(string language, out WorkerDescriptionProfile evaluatedProfile) { if (_profiles.TryGetValue(language, out List profiles)) @@ -46,6 +48,7 @@ private bool GetEvaluatedProfile(string language, out WorkerDescriptionProfile e return false; } + /// public void LoadWorkerDescriptionFromProfiles(RpcWorkerDescription defaultWorkerDescription, out RpcWorkerDescription workerDescription) { if (GetEvaluatedProfile(defaultWorkerDescription.Language, out WorkerDescriptionProfile profile)) @@ -53,10 +56,12 @@ public void LoadWorkerDescriptionFromProfiles(RpcWorkerDescription defaultWorker _logger?.LogInformation($"Worker initialized with profile - {profile.Name}, Profile ID {profile.ProfileId} from worker config."); _activeProfile = profile.ProfileId; workerDescription = profile.ApplyProfile(defaultWorkerDescription); + return; } workerDescription = defaultWorkerDescription; } + /// public bool TryCreateWorkerProfileCondition(WorkerProfileConditionDescriptor conditionDescriptor, out IWorkerProfileCondition condition) { foreach (var provider in _conditionProviders) @@ -73,6 +78,7 @@ public bool TryCreateWorkerProfileCondition(WorkerProfileConditionDescriptor con return false; } + /// public bool IsCorrectProfileLoaded(string workerRuntime) { var profileId = string.Empty; diff --git a/src/WebJobs.Script/Workers/Rpc/Configuration/RpcWorkerConfig.cs b/src/WebJobs.Script/Workers/Rpc/Configuration/RpcWorkerConfig.cs index d04aacae3c..6d5a62219d 100644 --- a/src/WebJobs.Script/Workers/Rpc/Configuration/RpcWorkerConfig.cs +++ b/src/WebJobs.Script/Workers/Rpc/Configuration/RpcWorkerConfig.cs @@ -1,8 +1,6 @@ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the MIT License. See License.txt in the project root for license information. -using System.Collections.Generic; - namespace Microsoft.Azure.WebJobs.Script.Workers.Rpc { public class RpcWorkerConfig diff --git a/src/WebJobs.Script/Workers/Rpc/Configuration/RpcWorkerConfigFactory.cs b/src/WebJobs.Script/Workers/Rpc/Configuration/RpcWorkerConfigFactory.cs index 924cfcbc9b..88651684fa 100644 --- a/src/WebJobs.Script/Workers/Rpc/Configuration/RpcWorkerConfigFactory.cs +++ b/src/WebJobs.Script/Workers/Rpc/Configuration/RpcWorkerConfigFactory.cs @@ -128,11 +128,15 @@ internal void AddProvider(string workerDir) workerDescription.WorkerDirectory = workerDir; //Read the profiles from worker description and load the profile for which the conditions match - List workerDescriptionProfiles = ReadWorkerDescriptionProfiles(workerConfig); - if (workerDescriptionProfiles.Count > 0) + var profilesJToken = workerConfig.GetValue(WorkerConstants.WorkerDescriptionProfiles); + if (profilesJToken != null) { - _profileManager.SaveWorkerDescriptionProfiles(workerDescriptionProfiles, workerDescription.Language); - _profileManager.LoadWorkerDescriptionFromProfiles(workerDescription, out workerDescription); + List workerDescriptionProfiles = ReadWorkerDescriptionProfiles(profilesJToken); + if (workerDescriptionProfiles.Count > 0) + { + _profileManager.SaveWorkerDescriptionProfiles(workerDescriptionProfiles, workerDescription.Language); + _profileManager.LoadWorkerDescriptionFromProfiles(workerDescription, out workerDescription); + } } // Check if any appsettings are provided for that langauge @@ -176,9 +180,9 @@ internal void AddProvider(string workerDir) } } - private List ReadWorkerDescriptionProfiles(JObject workerConfig) + private List ReadWorkerDescriptionProfiles(JToken profilesJToken) { - var profiles = workerConfig.GetValue(WorkerConstants.WorkerDescriptionProfiles).ToObject>(); + var profiles = profilesJToken.ToObject>(); if (profiles == null || profiles.Count <= 0) { diff --git a/src/WebJobs.Script/Workers/Rpc/WebHostRpcWorkerChannelManager.cs b/src/WebJobs.Script/Workers/Rpc/WebHostRpcWorkerChannelManager.cs index 22315edc3c..1ea3e036c5 100644 --- a/src/WebJobs.Script/Workers/Rpc/WebHostRpcWorkerChannelManager.cs +++ b/src/WebJobs.Script/Workers/Rpc/WebHostRpcWorkerChannelManager.cs @@ -33,7 +33,14 @@ public class WebHostRpcWorkerChannelManager : IWebHostRpcWorkerChannelManager private ConcurrentDictionary>> _workerChannels = new ConcurrentDictionary>>(StringComparer.OrdinalIgnoreCase); - public WebHostRpcWorkerChannelManager(IScriptEventManager eventManager, IEnvironment environment, ILoggerFactory loggerFactory, IRpcWorkerChannelFactory rpcWorkerChannelFactory, IOptionsMonitor applicationHostOptions, IMetricsLogger metricsLogger, IOptionsMonitor languageWorkerOptions, IConfiguration config, IWorkerProfileManager profileManager) + public WebHostRpcWorkerChannelManager(IScriptEventManager eventManager, + IEnvironment environment, + ILoggerFactory loggerFactory, + IRpcWorkerChannelFactory rpcWorkerChannelFactory, + IOptionsMonitor applicationHostOptions, + IMetricsLogger metricsLogger, IOptionsMonitor languageWorkerOptions, + IConfiguration config, + IWorkerProfileManager profileManager) { _environment = environment ?? throw new ArgumentNullException(nameof(environment)); _eventManager = eventManager; From f6fc193f2e90fefb7f2ac51a033ac901159d2175 Mon Sep 17 00:00:00 2001 From: Shreyas Gopalakrishna Date: Tue, 24 May 2022 23:44:41 -0500 Subject: [PATCH 5/9] Adding unit tests for profiles --- .../WebScriptHostBuilderExtension.cs | 2 - .../Workers/Profiles/EnvironmentCondition.cs | 2 +- .../Workers/Profiles/HostPropertyCondition.cs | 8 +- .../Profiles/SystemConditionProvider.cs | 4 +- .../Profiles/WorkerDescriptionProfile.cs | 2 +- .../WorkerProfileConditionDescriptor.cs | 1 + .../Configuration/RpcWorkerConfigFactory.cs | 6 +- .../Profiles/EnvironmentConditionTests.cs | 74 +++++++++++ .../Workers/Profiles/FalseConditionTests.cs | 17 +++ .../Profiles/HostPropertyConditionTests.cs | 73 +++++++++++ .../Workers/Profiles/ProfilesTestUtilities.cs | 50 ++++++++ .../Profiles/WorkerDescriptionProfileTests.cs | 120 ++++++++++++++++++ .../WorkerProfileConditionDescriptorTests.cs | 32 +++++ .../Rpc/RpcWorkerConfigTestUtilities.cs | 9 +- 14 files changed, 385 insertions(+), 15 deletions(-) create mode 100644 test/WebJobs.Script.Tests/Workers/Profiles/EnvironmentConditionTests.cs create mode 100644 test/WebJobs.Script.Tests/Workers/Profiles/FalseConditionTests.cs create mode 100644 test/WebJobs.Script.Tests/Workers/Profiles/HostPropertyConditionTests.cs create mode 100644 test/WebJobs.Script.Tests/Workers/Profiles/ProfilesTestUtilities.cs create mode 100644 test/WebJobs.Script.Tests/Workers/Profiles/WorkerDescriptionProfileTests.cs create mode 100644 test/WebJobs.Script.Tests/Workers/Profiles/WorkerProfileConditionDescriptorTests.cs diff --git a/src/WebJobs.Script.WebHost/WebScriptHostBuilderExtension.cs b/src/WebJobs.Script.WebHost/WebScriptHostBuilderExtension.cs index 36a81202fe..edfba542ac 100644 --- a/src/WebJobs.Script.WebHost/WebScriptHostBuilderExtension.cs +++ b/src/WebJobs.Script.WebHost/WebScriptHostBuilderExtension.cs @@ -22,8 +22,6 @@ using Microsoft.Azure.WebJobs.Script.WebHost.Management; using Microsoft.Azure.WebJobs.Script.WebHost.Middleware; using Microsoft.Azure.WebJobs.Script.WebHost.Storage; -using Microsoft.Azure.WebJobs.Script.Workers; -using Microsoft.Azure.WebJobs.Script.Workers.Profiles; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.DependencyInjection.Extensions; using Microsoft.Extensions.Hosting; diff --git a/src/WebJobs.Script/Workers/Profiles/EnvironmentCondition.cs b/src/WebJobs.Script/Workers/Profiles/EnvironmentCondition.cs index 04c2e3ccdc..2790fa18cc 100644 --- a/src/WebJobs.Script/Workers/Profiles/EnvironmentCondition.cs +++ b/src/WebJobs.Script/Workers/Profiles/EnvironmentCondition.cs @@ -54,7 +54,7 @@ public bool Evaluate() } // Validates if condition parametrs meet expected values, fail if they don't - internal void Validate() + private void Validate() { if (string.IsNullOrEmpty(Name)) { diff --git a/src/WebJobs.Script/Workers/Profiles/HostPropertyCondition.cs b/src/WebJobs.Script/Workers/Profiles/HostPropertyCondition.cs index 3396905c4f..4548b84af4 100644 --- a/src/WebJobs.Script/Workers/Profiles/HostPropertyCondition.cs +++ b/src/WebJobs.Script/Workers/Profiles/HostPropertyCondition.cs @@ -12,7 +12,7 @@ namespace Microsoft.Azure.WebJobs.Script.Workers { // HostPropertycondition checks if host match the expected output for properties such as Sku, Platform, HostVersion - internal class HostPropertyCondition : IWorkerProfileCondition + public class HostPropertyCondition : IWorkerProfileCondition { private readonly ILogger _logger; private readonly ISystemRuntimeInformation _systemRuntimeInformation; @@ -50,7 +50,7 @@ public enum HostProperty /// public bool Evaluate() { - Enum.TryParse(Name, out HostProperty hostPropertyName); + var hostPropertyName = Enum.Parse(typeof(HostProperty), Name, true); string value = hostPropertyName switch { @@ -71,14 +71,14 @@ public bool Evaluate() } // Validates if condition parametrs meet expected values, fail if they don't - internal void Validate() + private void Validate() { if (string.IsNullOrEmpty(Name)) { throw new ValidationException($"HostPropertyCondition {nameof(Name)} cannot be empty."); } - if (!Enum.GetNames(typeof(HostProperty)).Any(x => x.ToLower().Contains(Name))) + if (!Enum.GetNames(typeof(HostProperty)).Any(x => x.ToLower().Contains(Name.ToLower()))) { throw new ValidationException($"HostPropertyCondition {nameof(Name)} is not a valid host property name."); } diff --git a/src/WebJobs.Script/Workers/Profiles/SystemConditionProvider.cs b/src/WebJobs.Script/Workers/Profiles/SystemConditionProvider.cs index d2f7be2ca7..cc33690bde 100644 --- a/src/WebJobs.Script/Workers/Profiles/SystemConditionProvider.cs +++ b/src/WebJobs.Script/Workers/Profiles/SystemConditionProvider.cs @@ -9,11 +9,11 @@ namespace Microsoft.Azure.WebJobs.Script.Workers { internal sealed class SystemConditionProvider : IWorkerProfileConditionProvider { - private readonly ILogger _logger; + private readonly ILogger _logger; private readonly IEnvironment _environment; private readonly ISystemRuntimeInformation _systemRuntimeInformation; - public SystemConditionProvider(ILogger logger, ISystemRuntimeInformation systemRuntimeInfo, IEnvironment environment) + public SystemConditionProvider(ILogger logger, ISystemRuntimeInformation systemRuntimeInfo, IEnvironment environment) { _logger = logger ?? throw new ArgumentNullException(nameof(logger)); _environment = environment ?? throw new ArgumentNullException(nameof(environment)); diff --git a/src/WebJobs.Script/Workers/Profiles/WorkerDescriptionProfile.cs b/src/WebJobs.Script/Workers/Profiles/WorkerDescriptionProfile.cs index 659c11f82a..ae9b55d9b5 100644 --- a/src/WebJobs.Script/Workers/Profiles/WorkerDescriptionProfile.cs +++ b/src/WebJobs.Script/Workers/Profiles/WorkerDescriptionProfile.cs @@ -36,7 +36,7 @@ public WorkerDescriptionProfile(string name, List condi public string ProfileId { get; } - public void Validate() + private void Validate() { if (string.IsNullOrEmpty(Name)) { diff --git a/src/WebJobs.Script/Workers/Profiles/WorkerProfileConditionDescriptor.cs b/src/WebJobs.Script/Workers/Profiles/WorkerProfileConditionDescriptor.cs index 1da57ab7e3..0204b8fca1 100644 --- a/src/WebJobs.Script/Workers/Profiles/WorkerProfileConditionDescriptor.cs +++ b/src/WebJobs.Script/Workers/Profiles/WorkerProfileConditionDescriptor.cs @@ -17,6 +17,7 @@ public sealed class WorkerProfileConditionDescriptor private IDictionary _properties; + [JsonProperty(Required = Required.Always)] public string Type { get; set; } public IDictionary Properties diff --git a/src/WebJobs.Script/Workers/Rpc/Configuration/RpcWorkerConfigFactory.cs b/src/WebJobs.Script/Workers/Rpc/Configuration/RpcWorkerConfigFactory.cs index 88651684fa..4a878c0c91 100644 --- a/src/WebJobs.Script/Workers/Rpc/Configuration/RpcWorkerConfigFactory.cs +++ b/src/WebJobs.Script/Workers/Rpc/Configuration/RpcWorkerConfigFactory.cs @@ -128,10 +128,10 @@ internal void AddProvider(string workerDir) workerDescription.WorkerDirectory = workerDir; //Read the profiles from worker description and load the profile for which the conditions match - var profilesJToken = workerConfig.GetValue(WorkerConstants.WorkerDescriptionProfiles); - if (profilesJToken != null) + JToken profiles = workerConfig.GetValue(WorkerConstants.WorkerDescriptionProfiles); + if (profiles != null) { - List workerDescriptionProfiles = ReadWorkerDescriptionProfiles(profilesJToken); + List workerDescriptionProfiles = ReadWorkerDescriptionProfiles(profiles); if (workerDescriptionProfiles.Count > 0) { _profileManager.SaveWorkerDescriptionProfiles(workerDescriptionProfiles, workerDescription.Language); diff --git a/test/WebJobs.Script.Tests/Workers/Profiles/EnvironmentConditionTests.cs b/test/WebJobs.Script.Tests/Workers/Profiles/EnvironmentConditionTests.cs new file mode 100644 index 0000000000..5e2d1fd114 --- /dev/null +++ b/test/WebJobs.Script.Tests/Workers/Profiles/EnvironmentConditionTests.cs @@ -0,0 +1,74 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System; +using System.ComponentModel.DataAnnotations; +using Microsoft.Azure.WebJobs.Script.Tests; +using Xunit; + +namespace Microsoft.Azure.WebJobs.Script.Workers.Profiles +{ + public class EnvironmentConditionTests + { + private TestEnvironment _testEnvironment = new TestEnvironment(); + + [Theory] + [InlineData(null, null)] + [InlineData("", "")] + [InlineData("", null)] + [InlineData(null, "")] + [InlineData("APPLICATIONINSIGHTS_ENABLE_AGENT", null)] + [InlineData("APPLICATIONINSIGHTS_ENABLE_AGENT", "")] + [InlineData(null, "true")] + [InlineData("", "true")] + public void EnvironmentConditionTest_ThrowsValidationException(string name, string expression) + { + var testLogger = new TestLogger("test"); + var descriptor = new WorkerProfileConditionDescriptor(); + descriptor.Type = WorkerConstants.WorkerDescriptionProfileEnvironmentCondition; + + descriptor.Properties[WorkerConstants.WorkerDescriptionProfileConditionName] = name; + descriptor.Properties[WorkerConstants.WorkerDescriptionProfileConditionExpression] = expression; + + Assert.Throws(() => new EnvironmentCondition(testLogger, _testEnvironment, descriptor)); + } + + [Theory] + [InlineData("APPLICATIONINSIGHTS_ENABLE_AGENT", "true", "true")] + [InlineData("APPLICATIONINSIGHTS_ENABLE_AGENT", "^((?!true).)*$", "false")] + public void EnvironmentConditionTest_EvaluateTrue(string name, string testExpression, string environmentSetting) + { + _testEnvironment.SetEnvironmentVariable(name, environmentSetting); + + var testLogger = new TestLogger("test"); + + var descriptor = new WorkerProfileConditionDescriptor(); + descriptor.Type = WorkerConstants.WorkerDescriptionProfileEnvironmentCondition; + descriptor.Properties[WorkerConstants.WorkerDescriptionProfileConditionName] = name; + descriptor.Properties[WorkerConstants.WorkerDescriptionProfileConditionExpression] = testExpression; + + var environmentCondition = new EnvironmentCondition(testLogger, _testEnvironment, descriptor); + + Assert.True(environmentCondition.Evaluate()); + } + + [Theory] + [InlineData("APPLICATIONINSIGHTS_ENABLE_AGENT", "true", "false")] + [InlineData("APPLICATIONINSIGHTS_ENABLE_AGENT", "^((?!true).)*$", "true")] + public void EnvironmentConditionTest_EvaluateFalse(string name, string testExpression, string environmentSetting) + { + _testEnvironment.SetEnvironmentVariable(name, environmentSetting); + + var testLogger = new TestLogger("test"); + + var descriptor = new WorkerProfileConditionDescriptor(); + descriptor.Type = WorkerConstants.WorkerDescriptionProfileEnvironmentCondition; + descriptor.Properties[WorkerConstants.WorkerDescriptionProfileConditionName] = name; + descriptor.Properties[WorkerConstants.WorkerDescriptionProfileConditionExpression] = testExpression; + + var environmentCondition = new EnvironmentCondition(testLogger, _testEnvironment, descriptor); + + Assert.False(environmentCondition.Evaluate(), "Expression evaluates to false"); + } + } +} \ No newline at end of file diff --git a/test/WebJobs.Script.Tests/Workers/Profiles/FalseConditionTests.cs b/test/WebJobs.Script.Tests/Workers/Profiles/FalseConditionTests.cs new file mode 100644 index 0000000000..896f513773 --- /dev/null +++ b/test/WebJobs.Script.Tests/Workers/Profiles/FalseConditionTests.cs @@ -0,0 +1,17 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using Xunit; + +namespace Microsoft.Azure.WebJobs.Script.Workers.Profiles +{ + public class FalseConditionTests + { + [Fact] + public void FalseCondition_EvaluesFalse() + { + var falseCondition = new FalseCondition(); + Assert.False(falseCondition.Evaluate(), "False condition must always return false"); + } + } +} \ No newline at end of file diff --git a/test/WebJobs.Script.Tests/Workers/Profiles/HostPropertyConditionTests.cs b/test/WebJobs.Script.Tests/Workers/Profiles/HostPropertyConditionTests.cs new file mode 100644 index 0000000000..c7764a98a8 --- /dev/null +++ b/test/WebJobs.Script.Tests/Workers/Profiles/HostPropertyConditionTests.cs @@ -0,0 +1,73 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System.ComponentModel.DataAnnotations; +using Microsoft.Azure.WebJobs.Script.Tests; +using Xunit; + +namespace Microsoft.Azure.WebJobs.Script.Workers.Profiles +{ + public class HostPropertyConditionTests + { + private TestSystemRuntimeInformation _testSystemRuntimeInfo = new TestSystemRuntimeInformation(); + + [Theory] + [InlineData(null, null)] + [InlineData("", "")] + [InlineData("", null)] + [InlineData(null, "")] + [InlineData("sku", null)] + [InlineData("Platform", "")] + [InlineData("HostVersion", null)] + [InlineData("APPLICATIONINSIGHTS_ENABLE_AGENT", "")] + [InlineData(null, "true")] + [InlineData("", "true")] + public void HostPropertyConditionTest_ThrowsValidationException(string name, string expression) + { + var testLogger = new TestLogger("test"); + var descriptor = new WorkerProfileConditionDescriptor(); + descriptor.Type = WorkerConstants.WorkerDescriptionProfileHostPropertyCondition; + + descriptor.Properties[WorkerConstants.WorkerDescriptionProfileConditionName] = name; + descriptor.Properties[WorkerConstants.WorkerDescriptionProfileConditionExpression] = expression; + + Assert.Throws(() => new HostPropertyCondition(testLogger, _testSystemRuntimeInfo, descriptor)); + } + + [Theory] + //[InlineData("sku", "Dynamic")] TODO: Add test case + [InlineData("platForm", "LINUX")] + [InlineData("HostVersion", "4.*")] + public void HostPropertyConditionTest_EvaluateTrue(string name, string testExpression) + { + var testLogger = new TestLogger("test"); + + var descriptor = new WorkerProfileConditionDescriptor(); + descriptor.Type = WorkerConstants.WorkerDescriptionProfileHostPropertyCondition; + descriptor.Properties[WorkerConstants.WorkerDescriptionProfileConditionName] = name; + descriptor.Properties[WorkerConstants.WorkerDescriptionProfileConditionExpression] = testExpression; + + var hostPropertyCondition = new HostPropertyCondition(testLogger, _testSystemRuntimeInfo, descriptor); + + Assert.True(hostPropertyCondition.Evaluate()); + } + + [Theory] + //[InlineData("sku", "Dynamic")] TODO: Add test case + [InlineData("platForm", "Windows")] + [InlineData("HostVersion", "3.*")] + public void HostPropertyConditionTest_EvaluateFalse(string name, string testExpression) + { + var testLogger = new TestLogger("test"); + + var descriptor = new WorkerProfileConditionDescriptor(); + descriptor.Type = WorkerConstants.WorkerDescriptionProfileHostPropertyCondition; + descriptor.Properties[WorkerConstants.WorkerDescriptionProfileConditionName] = name; + descriptor.Properties[WorkerConstants.WorkerDescriptionProfileConditionExpression] = testExpression; + + var hostPropertyCondition = new HostPropertyCondition(testLogger, _testSystemRuntimeInfo, descriptor); + + Assert.False(hostPropertyCondition.Evaluate(), "Expression evaluates to false"); + } + } +} \ No newline at end of file diff --git a/test/WebJobs.Script.Tests/Workers/Profiles/ProfilesTestUtilities.cs b/test/WebJobs.Script.Tests/Workers/Profiles/ProfilesTestUtilities.cs new file mode 100644 index 0000000000..a754626095 --- /dev/null +++ b/test/WebJobs.Script.Tests/Workers/Profiles/ProfilesTestUtilities.cs @@ -0,0 +1,50 @@ +// 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 Microsoft.Azure.WebJobs.Script.Tests.Workers.Rpc; +using Microsoft.Azure.WebJobs.Script.Workers; +using Microsoft.Azure.WebJobs.Script.Workers.Profiles; +using Newtonsoft.Json.Linq; + +namespace Microsoft.Azure.WebJobs.Script.Tests.Workers.Profiles +{ + public class ProfilesTestUtilities + { + public static JObject GetTestWorkerProfileCondition(string type = "hostproperty", string name = "hostversion", string expression = "4.*") + { + var condition = new JObject(); + condition[WorkerConstants.WorkerDescriptionProfileConditionType] = type; + condition[WorkerConstants.WorkerDescriptionProfileConditionName] = name; + condition[WorkerConstants.WorkerDescriptionProfileConditionExpression] = expression; + return condition; + } + + public static WorkerProfileConditionDescriptor GetTestWorkerProfileConditionDescriptor(string type, string name, string expression) + { + var condition = GetTestWorkerProfileCondition(type, name, expression); + return condition.ToObject(); + } + + public static EnvironmentCondition GetTestEnvironmentCondition(TestLogger logger, TestEnvironment testEnvironment, string name, string expression) + { + var descriptor = new WorkerProfileConditionDescriptor(); + descriptor.Type = WorkerConstants.WorkerDescriptionProfileEnvironmentCondition; + descriptor.Properties[WorkerConstants.WorkerDescriptionProfileConditionName] = name; + descriptor.Properties[WorkerConstants.WorkerDescriptionProfileConditionExpression] = expression; + + return new EnvironmentCondition(logger, testEnvironment, descriptor); + } + + public static HostPropertyCondition GetTestHostPropertyCondition(TestLogger logger, TestSystemRuntimeInformation testSystemRuntimeInfo, string name, string expression) + { + var descriptor = new WorkerProfileConditionDescriptor(); + descriptor.Type = WorkerConstants.WorkerDescriptionProfileHostPropertyCondition; + descriptor.Properties[WorkerConstants.WorkerDescriptionProfileConditionName] = name; + descriptor.Properties[WorkerConstants.WorkerDescriptionProfileConditionExpression] = expression; + + return new HostPropertyCondition(logger, testSystemRuntimeInfo, descriptor); + } + } +} diff --git a/test/WebJobs.Script.Tests/Workers/Profiles/WorkerDescriptionProfileTests.cs b/test/WebJobs.Script.Tests/Workers/Profiles/WorkerDescriptionProfileTests.cs new file mode 100644 index 0000000000..078148138c --- /dev/null +++ b/test/WebJobs.Script.Tests/Workers/Profiles/WorkerDescriptionProfileTests.cs @@ -0,0 +1,120 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System.Collections.Generic; +using System.ComponentModel.DataAnnotations; +using Microsoft.Azure.WebJobs.Script.Tests; +using Microsoft.Azure.WebJobs.Script.Tests.Workers.Profiles; +using Microsoft.Azure.WebJobs.Script.Tests.Workers.Rpc; +using Microsoft.Azure.WebJobs.Script.Workers.Rpc; +using Xunit; + +namespace Microsoft.Azure.WebJobs.Script.Workers.Profiles +{ + public class WorkerDescriptionProfileTests + { + private static TestEnvironment _testEnvironment = new TestEnvironment(); + private static TestSystemRuntimeInformation _testSystemRuntimeInfo = new TestSystemRuntimeInformation(); + private static TestLogger _testLogger = new TestLogger("test"); + + private static string[] argumentList = new string[] { "-TestArg=1" }; + + [Theory] + [MemberData(nameof(WorkerDescriptionProfileExceptionData))] + public void WorkerDescriptionProfile_ThrowsValidationException(string name, List coditions, RpcWorkerDescription workerDescription) + { + Assert.Throws(() => new WorkerDescriptionProfile(name, coditions, workerDescription)); + } + + public static IEnumerable WorkerDescriptionProfileExceptionData() + { + var description = RpcWorkerConfigTestUtilities.GetTestDefaultWorkerDescription("java", argumentList); + + var validConditionsList = new List(); + validConditionsList.Add(ProfilesTestUtilities.GetTestEnvironmentCondition(_testLogger, _testEnvironment, "APPLICATIONINSIGHTS_ENABLE_AGENT", "true")); + validConditionsList.Add(ProfilesTestUtilities.GetTestHostPropertyCondition(_testLogger, _testSystemRuntimeInfo, "hostversion", "4.*")); + + yield return new object[] { null, validConditionsList, description }; + yield return new object[] { string.Empty, validConditionsList, description }; + yield return new object[] { "profileName", new List(0), description }; + yield return new object[] { "profileName", null, description }; + yield return new object[] { "profileName", new List(1), description }; + } + + [Theory] + [MemberData(nameof(WorkerDescriptionProfileData))] + public void WorkerDescriptionProfile_ApplyProfile(string name, List coditions, RpcWorkerDescription workerDescription) + { + _testEnvironment.SetEnvironmentVariable("APPLICATIONINSIGHTS_ENABLE_AGENT", "true"); + var defaultDescription = RpcWorkerConfigTestUtilities.GetTestDefaultWorkerDescription("java", new string[] { "-DefaultArgs" }); + + var workerDescriptionProfile = new WorkerDescriptionProfile(name, coditions, workerDescription); + defaultDescription = workerDescriptionProfile.ApplyProfile(defaultDescription); + + Assert.Equal(defaultDescription.Arguments[0], argumentList[0]); + Assert.NotEqual(defaultDescription.Arguments[0], "-DefaultArgs"); + } + + public static IEnumerable WorkerDescriptionProfileData() + { + var description = RpcWorkerConfigTestUtilities.GetTestDefaultWorkerDescription("java", argumentList); + + var validConditionsList = new List(); + validConditionsList.Add(ProfilesTestUtilities.GetTestEnvironmentCondition(_testLogger, _testEnvironment, "APPLICATIONINSIGHTS_ENABLE_AGENT", "true")); + yield return new object[] { "profileName", validConditionsList, description }; + + validConditionsList.Add(ProfilesTestUtilities.GetTestHostPropertyCondition(_testLogger, _testSystemRuntimeInfo, "hostversion", "4.*")); + yield return new object[] { "profileName", validConditionsList, description }; + } + + [Theory] + [MemberData(nameof(WorkerDescriptionProfileInvalidData))] + public void WorkerDescriptionProfile_DoNotApplyProfile(string name, List coditions, RpcWorkerDescription workerDescription) + { + _testEnvironment.SetEnvironmentVariable("APPLICATIONINSIGHTS_ENABLE_AGENT", "false"); + var defaultDescription = RpcWorkerConfigTestUtilities.GetTestDefaultWorkerDescription("java", new string[] { "-DefaultArgs" }); + + var workerDescriptionProfile = new WorkerDescriptionProfile(name, coditions, workerDescription); + defaultDescription = workerDescriptionProfile.ApplyProfile(defaultDescription); + + Assert.NotNull(defaultDescription.Arguments[0]); + Assert.Equal(defaultDescription.Arguments[0], "-DefaultArgs"); + } + + public static IEnumerable WorkerDescriptionProfileInvalidData() + { + var description = RpcWorkerConfigTestUtilities.GetTestDefaultWorkerDescription("java", new string[] { }); + + var validConditionsList = new List(); + validConditionsList.Add(ProfilesTestUtilities.GetTestEnvironmentCondition(_testLogger, _testEnvironment, "APPLICATIONINSIGHTS_ENABLE_AGENT", "true")); + yield return new object[] { "profileName", validConditionsList, description }; + + validConditionsList.Add(ProfilesTestUtilities.GetTestHostPropertyCondition(_testLogger, _testSystemRuntimeInfo, "hostversion", "3.*")); + yield return new object[] { "profileName", validConditionsList, description }; + } + + [Fact] + public void WorkerDescriptionProfile_EvaluateConditions() + { + var description = RpcWorkerConfigTestUtilities.GetTestDefaultWorkerDescription("java", argumentList); + + var conditions = new List(); + conditions.Add(ProfilesTestUtilities.GetTestEnvironmentCondition(_testLogger, _testEnvironment, "APPLICATIONINSIGHTS_ENABLE_AGENT", "true")); + + var workerDescriptionProfile = new WorkerDescriptionProfile("profileName", conditions, description); + Assert.False(workerDescriptionProfile.EvaluateConditions()); + + _testEnvironment.SetEnvironmentVariable("APPLICATIONINSIGHTS_ENABLE_AGENT", "true"); + Assert.True(workerDescriptionProfile.EvaluateConditions()); + + conditions.Add(ProfilesTestUtilities.GetTestHostPropertyCondition(_testLogger, _testSystemRuntimeInfo, "hostversion", "4.*")); + workerDescriptionProfile = new WorkerDescriptionProfile("profileName", conditions, description); + Assert.True(workerDescriptionProfile.EvaluateConditions()); + + conditions.Add(ProfilesTestUtilities.GetTestHostPropertyCondition(_testLogger, _testSystemRuntimeInfo, "platform", "windows")); + workerDescriptionProfile = new WorkerDescriptionProfile("profileName", conditions, description); + + Assert.False(workerDescriptionProfile.EvaluateConditions()); + } + } +} \ No newline at end of file diff --git a/test/WebJobs.Script.Tests/Workers/Profiles/WorkerProfileConditionDescriptorTests.cs b/test/WebJobs.Script.Tests/Workers/Profiles/WorkerProfileConditionDescriptorTests.cs new file mode 100644 index 0000000000..b0ea4e5fad --- /dev/null +++ b/test/WebJobs.Script.Tests/Workers/Profiles/WorkerProfileConditionDescriptorTests.cs @@ -0,0 +1,32 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using Newtonsoft.Json; +using Newtonsoft.Json.Linq; +using Xunit; + +namespace Microsoft.Azure.WebJobs.Script.Workers.Profiles +{ + public class WorkerProfileConditionDescriptorTests + { + [Fact] + public void ConditionDescriptor_ConvertsJObject() + { + var conditionJObject = new JObject(); + conditionJObject[WorkerConstants.WorkerDescriptionProfileConditionName] = WorkerConstants.WorkerDescriptionProfileConditionName; + conditionJObject[WorkerConstants.WorkerDescriptionProfileConditionExpression] = WorkerConstants.WorkerDescriptionProfileConditionExpression; + + Assert.Throws(() => conditionJObject.ToObject()); + + 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); + + Assert.Equal(WorkerConstants.WorkerDescriptionProfileConditionName, conditionDescriptorName); + Assert.Equal(WorkerConstants.WorkerDescriptionProfileConditionExpression, conditionDescriptorExpression); + Assert.Equal(WorkerConstants.WorkerDescriptionProfileEnvironmentCondition, conditionDescriptor.Type); + } + } +} \ No newline at end of file diff --git a/test/WebJobs.Script.Tests/Workers/Rpc/RpcWorkerConfigTestUtilities.cs b/test/WebJobs.Script.Tests/Workers/Rpc/RpcWorkerConfigTestUtilities.cs index 1f84c605a8..8d7f746036 100644 --- a/test/WebJobs.Script.Tests/Workers/Rpc/RpcWorkerConfigTestUtilities.cs +++ b/test/WebJobs.Script.Tests/Workers/Rpc/RpcWorkerConfigTestUtilities.cs @@ -4,6 +4,7 @@ using System.Collections.Generic; using System.IO; using System.Linq; +using Microsoft.Azure.WebJobs.Script.Tests.Workers.Profiles; using Microsoft.Azure.WebJobs.Script.Workers; using Microsoft.Azure.WebJobs.Script.Workers.Rpc; using Newtonsoft.Json.Linq; @@ -31,8 +32,12 @@ public static JObject GetTestWorkerConfig(string language, string[] arguments, b DefaultExecutablePath = "myFooPath", }; - JObject profiles = new JObject(); - profiles[profileName] = JObject.FromObject(appSvcDescription); + JArray profiles = new JArray(); + JObject profile = new JObject(); + profile[WorkerConstants.WorkerDescriptionProfileName] = "profileName"; + profile[WorkerConstants.WorkerDescriptionProfileConditions] = ProfilesTestUtilities.GetTestWorkerProfileCondition(); + profile[WorkerConstants.WorkerDescription] = JObject.FromObject(appSvcDescription); + profiles.Add(profile); config[WorkerConstants.WorkerDescriptionProfiles] = profiles; } From 567780b445daceeb24fc6cc8f9f3598614b30f78 Mon Sep 17 00:00:00 2001 From: Shreyas Gopalakrishna Date: Tue, 7 Jun 2022 22:57:56 -0500 Subject: [PATCH 6/9] Fixed dependency injection and review comments --- src/WebJobs.Script/ScriptHostBuilderExtensions.cs | 6 +++++- .../Workers/Profiles/EnvironmentCondition.cs | 5 ++++- src/WebJobs.Script/Workers/Profiles/FalseCondition.cs | 6 ++++-- .../Workers/Profiles/HostPropertyCondition.cs | 5 ++++- .../Workers/Profiles/IWorkerProfileCondition.cs | 4 +++- .../Workers/Profiles/IWorkerProfileConditionProvider.cs | 4 +++- .../Workers/Profiles/IWorkerProfileManager.cs | 4 +++- .../Workers/Profiles/WorkerDescriptionProfile.cs | 3 +++ ...tionProvider.cs => WorkerProfileConditionProvider.cs} | 8 ++++---- .../Workers/Profiles/WorkerProfileManager.cs | 9 ++++++--- .../Workers/Rpc/Configuration/RpcWorkerConfigFactory.cs | 2 +- src/WebJobs.Script/Workers/WorkerConstants.cs | 8 ++++---- .../Configuration/LanguageWorkerOptionsSetupTests.cs | 4 ++-- .../Workers/Rpc/RpcWorkerConfigFactoryTests.cs | 4 ++-- .../Workers/Rpc/RpcWorkerConfigTests.cs | 4 ++-- .../Workers/Rpc/WebHostRpcWorkerChannelManagerTests.cs | 4 ++-- 16 files changed, 52 insertions(+), 28 deletions(-) rename src/WebJobs.Script/Workers/Profiles/{SystemConditionProvider.cs => WorkerProfileConditionProvider.cs} (75%) diff --git a/src/WebJobs.Script/ScriptHostBuilderExtensions.cs b/src/WebJobs.Script/ScriptHostBuilderExtensions.cs index d2f2651c7d..fa42063ec8 100644 --- a/src/WebJobs.Script/ScriptHostBuilderExtensions.cs +++ b/src/WebJobs.Script/ScriptHostBuilderExtensions.cs @@ -292,6 +292,10 @@ public static IHostBuilder AddScriptHostCore(this IHostBuilder builder, ScriptAp services.AddSingleton(); + services.AddSingleton(); + + services.AddSingleton(); + if (!applicationHostOptions.HasParentScope) { AddCommonServices(services); @@ -336,7 +340,7 @@ public static void AddCommonServices(IServiceCollection services) services.AddSingleton(); services.AddSingleton(); services.AddSingleton(); - services.AddSingleton(); + services.AddSingleton(); services.TryAddSingleton(); services.TryAddSingleton(); services.TryAddSingleton(); diff --git a/src/WebJobs.Script/Workers/Profiles/EnvironmentCondition.cs b/src/WebJobs.Script/Workers/Profiles/EnvironmentCondition.cs index 2790fa18cc..963de9d41d 100644 --- a/src/WebJobs.Script/Workers/Profiles/EnvironmentCondition.cs +++ b/src/WebJobs.Script/Workers/Profiles/EnvironmentCondition.cs @@ -9,7 +9,10 @@ namespace Microsoft.Azure.WebJobs.Script.Workers { - // Environment condition checks if environment variables match the expected output + /// + /// An implementation of an that checks if + /// environment variables match the expected output + /// public class EnvironmentCondition : IWorkerProfileCondition { private readonly ILogger _logger; diff --git a/src/WebJobs.Script/Workers/Profiles/FalseCondition.cs b/src/WebJobs.Script/Workers/Profiles/FalseCondition.cs index de5bcf782c..10f875d566 100644 --- a/src/WebJobs.Script/Workers/Profiles/FalseCondition.cs +++ b/src/WebJobs.Script/Workers/Profiles/FalseCondition.cs @@ -3,8 +3,10 @@ namespace Microsoft.Azure.WebJobs.Script.Workers { - // The false condition always evalues to false. - // This condition is used to disable a profile when condition providers fail to resolve conditions. + /// + /// An implementation of an that always evaluates to false. + /// This condition is used to disable a profile when condition providers fail to resolve conditions. + /// public class FalseCondition : IWorkerProfileCondition { public bool Evaluate() diff --git a/src/WebJobs.Script/Workers/Profiles/HostPropertyCondition.cs b/src/WebJobs.Script/Workers/Profiles/HostPropertyCondition.cs index 4548b84af4..e309703eae 100644 --- a/src/WebJobs.Script/Workers/Profiles/HostPropertyCondition.cs +++ b/src/WebJobs.Script/Workers/Profiles/HostPropertyCondition.cs @@ -11,7 +11,10 @@ namespace Microsoft.Azure.WebJobs.Script.Workers { - // HostPropertycondition checks if host match the expected output for properties such as Sku, Platform, HostVersion + /// + /// An implementation of an that checks if different host properties + /// such as Sku, Platform, HostVersion match the expected output + /// public class HostPropertyCondition : IWorkerProfileCondition { private readonly ILogger _logger; diff --git a/src/WebJobs.Script/Workers/Profiles/IWorkerProfileCondition.cs b/src/WebJobs.Script/Workers/Profiles/IWorkerProfileCondition.cs index 2444d6a021..1d4c52dba2 100644 --- a/src/WebJobs.Script/Workers/Profiles/IWorkerProfileCondition.cs +++ b/src/WebJobs.Script/Workers/Profiles/IWorkerProfileCondition.cs @@ -3,7 +3,9 @@ namespace Microsoft.Azure.WebJobs.Script.Workers { - // Interface for different types of conditions + /// + /// Interface for different types of profile conditions + /// public interface IWorkerProfileCondition { /// diff --git a/src/WebJobs.Script/Workers/Profiles/IWorkerProfileConditionProvider.cs b/src/WebJobs.Script/Workers/Profiles/IWorkerProfileConditionProvider.cs index 6dcbca8f8e..cae7b39d8a 100644 --- a/src/WebJobs.Script/Workers/Profiles/IWorkerProfileConditionProvider.cs +++ b/src/WebJobs.Script/Workers/Profiles/IWorkerProfileConditionProvider.cs @@ -3,7 +3,9 @@ namespace Microsoft.Azure.WebJobs.Script.Workers.Profiles { - // Manage differnt conditions + /// + /// Interface to manage different profile condition providers + /// internal interface IWorkerProfileConditionProvider { /// diff --git a/src/WebJobs.Script/Workers/Profiles/IWorkerProfileManager.cs b/src/WebJobs.Script/Workers/Profiles/IWorkerProfileManager.cs index f69628cd12..a3982648cd 100644 --- a/src/WebJobs.Script/Workers/Profiles/IWorkerProfileManager.cs +++ b/src/WebJobs.Script/Workers/Profiles/IWorkerProfileManager.cs @@ -7,7 +7,9 @@ namespace Microsoft.Azure.WebJobs.Script.Workers { - // Regulate profile operations through the profile manager + /// + /// Interface to regulate profile operations through the profile manager + /// public interface IWorkerProfileManager { /// diff --git a/src/WebJobs.Script/Workers/Profiles/WorkerDescriptionProfile.cs b/src/WebJobs.Script/Workers/Profiles/WorkerDescriptionProfile.cs index ae9b55d9b5..a5f3942deb 100644 --- a/src/WebJobs.Script/Workers/Profiles/WorkerDescriptionProfile.cs +++ b/src/WebJobs.Script/Workers/Profiles/WorkerDescriptionProfile.cs @@ -8,6 +8,9 @@ namespace Microsoft.Azure.WebJobs.Script.Workers { + /// + /// Class that holds data of a profile + /// public class WorkerDescriptionProfile { public WorkerDescriptionProfile(string name, List conditions, RpcWorkerDescription profileDescription) diff --git a/src/WebJobs.Script/Workers/Profiles/SystemConditionProvider.cs b/src/WebJobs.Script/Workers/Profiles/WorkerProfileConditionProvider.cs similarity index 75% rename from src/WebJobs.Script/Workers/Profiles/SystemConditionProvider.cs rename to src/WebJobs.Script/Workers/Profiles/WorkerProfileConditionProvider.cs index cc33690bde..b71876691b 100644 --- a/src/WebJobs.Script/Workers/Profiles/SystemConditionProvider.cs +++ b/src/WebJobs.Script/Workers/Profiles/WorkerProfileConditionProvider.cs @@ -7,17 +7,17 @@ namespace Microsoft.Azure.WebJobs.Script.Workers { - internal sealed class SystemConditionProvider : IWorkerProfileConditionProvider + internal sealed class WorkerProfileConditionProvider : IWorkerProfileConditionProvider { - private readonly ILogger _logger; + private readonly ILogger _logger; private readonly IEnvironment _environment; private readonly ISystemRuntimeInformation _systemRuntimeInformation; - public SystemConditionProvider(ILogger logger, ISystemRuntimeInformation systemRuntimeInfo, IEnvironment environment) + public WorkerProfileConditionProvider(ILogger logger, ISystemRuntimeInformation systemRuntimeInfo, IEnvironment environment) { _logger = logger ?? throw new ArgumentNullException(nameof(logger)); _environment = environment ?? throw new ArgumentNullException(nameof(environment)); - _systemRuntimeInformation = systemRuntimeInfo ?? throw new ArgumentNullException(nameof(systemRuntimeInfo)); + _systemRuntimeInformation = systemRuntimeInfo ?? SystemRuntimeInformation.Instance; } /// diff --git a/src/WebJobs.Script/Workers/Profiles/WorkerProfileManager.cs b/src/WebJobs.Script/Workers/Profiles/WorkerProfileManager.cs index 410ff1284c..89475c72de 100644 --- a/src/WebJobs.Script/Workers/Profiles/WorkerProfileManager.cs +++ b/src/WebJobs.Script/Workers/Profiles/WorkerProfileManager.cs @@ -9,18 +9,21 @@ namespace Microsoft.Azure.WebJobs.Script.Workers { - // The default profile manager that manages profiles from language workers + /// + /// The default profile manager that manages profiles from language workers + /// internal class WorkerProfileManager : IWorkerProfileManager { private readonly ILogger _logger; private readonly IEnumerable _conditionProviders; - private Dictionary> _profiles = new Dictionary>(); + private Dictionary> _profiles; private string _activeProfile; - public WorkerProfileManager(ILogger logger, IEnumerable conditionProviders) + public WorkerProfileManager(ILogger logger, IEnumerable conditionProviders) { _logger = logger ?? throw new ArgumentNullException(nameof(logger)); _conditionProviders = conditionProviders ?? throw new ArgumentNullException(nameof(conditionProviders)); + _profiles = new Dictionary>(); _activeProfile = string.Empty; } diff --git a/src/WebJobs.Script/Workers/Rpc/Configuration/RpcWorkerConfigFactory.cs b/src/WebJobs.Script/Workers/Rpc/Configuration/RpcWorkerConfigFactory.cs index 4a878c0c91..d18929a583 100644 --- a/src/WebJobs.Script/Workers/Rpc/Configuration/RpcWorkerConfigFactory.cs +++ b/src/WebJobs.Script/Workers/Rpc/Configuration/RpcWorkerConfigFactory.cs @@ -201,7 +201,7 @@ private List ReadWorkerDescriptionProfiles(JToken prof if (!_profileManager.TryCreateWorkerProfileCondition(descriptor, out IWorkerProfileCondition condition)) { // Failed to resolve condition. This profile will be disabled using a mock false condition - _logger?.LogInformation($"Profile {profile.Name} is disabled. Cannout resolve the profile condition {descriptor.Type}"); + _logger?.LogInformation($"Profile {profile.Name} is disabled. Cannot resolve the profile condition {descriptor.Type}"); condition = new FalseCondition(); } diff --git a/src/WebJobs.Script/Workers/WorkerConstants.cs b/src/WebJobs.Script/Workers/WorkerConstants.cs index 79c4a3b6a5..e0071fa604 100644 --- a/src/WebJobs.Script/Workers/WorkerConstants.cs +++ b/src/WebJobs.Script/Workers/WorkerConstants.cs @@ -29,13 +29,13 @@ public static class WorkerConstants // Profiles public const string WorkerDescriptionProfiles = "profiles"; - public const string WorkerDescriptionProfileName = "name"; + public const string WorkerDescriptionProfileName = "profileName"; public const string WorkerDescriptionProfileConditions = "conditions"; - public const string WorkerDescriptionProfileConditionType = "type"; + public const string WorkerDescriptionProfileConditionType = "conditionType"; public const string WorkerDescriptionProfileEnvironmentCondition = "environment"; public const string WorkerDescriptionProfileHostPropertyCondition = "hostProperty"; - public const string WorkerDescriptionProfileConditionName = "name"; - public const string WorkerDescriptionProfileConditionExpression = "expression"; + public const string WorkerDescriptionProfileConditionName = "conditionName"; + public const string WorkerDescriptionProfileConditionExpression = "conditionExpression"; public const string WorkerDescriptionAppServiceEnvProfileName = "appServiceEnvironment"; // Logs diff --git a/test/WebJobs.Script.Tests/Configuration/LanguageWorkerOptionsSetupTests.cs b/test/WebJobs.Script.Tests/Configuration/LanguageWorkerOptionsSetupTests.cs index ae7cd93ab8..dc0259fd2c 100644 --- a/test/WebJobs.Script.Tests/Configuration/LanguageWorkerOptionsSetupTests.cs +++ b/test/WebJobs.Script.Tests/Configuration/LanguageWorkerOptionsSetupTests.cs @@ -24,8 +24,8 @@ public void LanguageWorkerOptions_Expected_ListOfConfigs(string workerRuntime) var configurationBuilder = new ConfigurationBuilder() .Add(new ScriptEnvironmentVariablesConfigurationSource()); - var systemConditionProvider = new SystemConditionProvider(new TestLogger(), runtimeInfo, testEnvironment); - var profileConditionManager = new WorkerProfileManager(new TestLogger(), new[] { systemConditionProvider }); + var profileConditionProvider = new WorkerProfileConditionProvider(new TestLogger(), runtimeInfo, testEnvironment); + var profileConditionManager = new WorkerProfileManager(new TestLogger(), new[] { profileConditionProvider }); var configuration = configurationBuilder.Build(); if (!string.IsNullOrEmpty(workerRuntime)) diff --git a/test/WebJobs.Script.Tests/Workers/Rpc/RpcWorkerConfigFactoryTests.cs b/test/WebJobs.Script.Tests/Workers/Rpc/RpcWorkerConfigFactoryTests.cs index 2eb7a73175..2634968758 100644 --- a/test/WebJobs.Script.Tests/Workers/Rpc/RpcWorkerConfigFactoryTests.cs +++ b/test/WebJobs.Script.Tests/Workers/Rpc/RpcWorkerConfigFactoryTests.cs @@ -25,8 +25,8 @@ public RpcWorkerConfigFactoryTests() { _testEnvironment = new TestEnvironment(); - var systemConditionProvider = new SystemConditionProvider(new TestLogger(), _testSysRuntimeInfo, _testEnvironment); - _profileManager = new WorkerProfileManager(new TestLogger(), new[] { systemConditionProvider }); + var profileConditionProvider = new WorkerProfileConditionProvider(new TestLogger(), _testSysRuntimeInfo, _testEnvironment); + _profileManager = new WorkerProfileManager(new TestLogger(), new[] { profileConditionProvider }); } public void Dispose() diff --git a/test/WebJobs.Script.Tests/Workers/Rpc/RpcWorkerConfigTests.cs b/test/WebJobs.Script.Tests/Workers/Rpc/RpcWorkerConfigTests.cs index 49e8991dd5..11a10f47d6 100644 --- a/test/WebJobs.Script.Tests/Workers/Rpc/RpcWorkerConfigTests.cs +++ b/test/WebJobs.Script.Tests/Workers/Rpc/RpcWorkerConfigTests.cs @@ -32,8 +32,8 @@ public RpcWorkerConfigTests() { _testEnvironment = new TestEnvironment(); - var systemConditionProvider = new SystemConditionProvider(new TestLogger(), _testSysRuntimeInfo, _testEnvironment); - _profileManager = new WorkerProfileManager(new TestLogger(), new[] { systemConditionProvider }); + var profileConditionProvider = new WorkerProfileConditionProvider(new TestLogger(), _testSysRuntimeInfo, _testEnvironment); + _profileManager = new WorkerProfileManager(new TestLogger(), new[] { profileConditionProvider }); } public static IEnumerable InvalidWorkerDescriptions diff --git a/test/WebJobs.Script.Tests/Workers/Rpc/WebHostRpcWorkerChannelManagerTests.cs b/test/WebJobs.Script.Tests/Workers/Rpc/WebHostRpcWorkerChannelManagerTests.cs index 0ac14c3f06..4799c8aad0 100644 --- a/test/WebJobs.Script.Tests/Workers/Rpc/WebHostRpcWorkerChannelManagerTests.cs +++ b/test/WebJobs.Script.Tests/Workers/Rpc/WebHostRpcWorkerChannelManagerTests.cs @@ -73,8 +73,8 @@ public WebHostRpcWorkerChannelManagerTests() _testLogger = new TestLogger("WebHostLanguageWorkerChannelManagerTests"); _rpcWorkerChannelFactory = new TestRpcWorkerChannelFactory(_eventManager, _testLogger, _scriptRootPath); _emptyConfig = new ConfigurationBuilder().Build(); - var systemConditionProvider = new SystemConditionProvider(new TestLogger(), _testSysRuntimeInfo, _testEnvironment); - _profileManager = new WorkerProfileManager(new TestLogger(), new[] { systemConditionProvider }); + var profileConditionProvider = new WorkerProfileConditionProvider(new TestLogger(), _testSysRuntimeInfo, _testEnvironment); + _profileManager = new WorkerProfileManager(new TestLogger(), new[] { profileConditionProvider }); _rpcWorkerChannelManager = new WebHostRpcWorkerChannelManager(_eventManager, _testEnvironment, _loggerFactory, _rpcWorkerChannelFactory, _optionsMonitor, new TestMetricsLogger(), _workerOptionsMonitor, _emptyConfig, _profileManager); } From 7a8cde13933336861d58c236a27b66f2a317ac9d Mon Sep 17 00:00:00 2001 From: Shreyas Gopalakrishna Date: Thu, 9 Jun 2022 00:36:25 -0500 Subject: [PATCH 7/9] Fixing unit tests --- .../Workers/Profiles/WorkerDescriptionProfile.cs | 2 +- .../Profiles/WorkerProfileConditionDescriptor.cs | 2 +- .../Workers/Profiles/WorkerProfileDescriptor.cs | 2 +- .../Rpc/Configuration/RpcWorkerConfigFactory.cs | 4 ++-- .../Workers/Profiles/HostPropertyConditionTests.cs | 2 +- .../Workers/Profiles/ProfilesTestUtilities.cs | 2 +- .../Workers/Rpc/RpcWorkerConfigTestUtilities.cs | 14 ++++++++++---- .../Workers/Rpc/RpcWorkerConfigTests.cs | 8 ++++---- 8 files changed, 21 insertions(+), 15 deletions(-) diff --git a/src/WebJobs.Script/Workers/Profiles/WorkerDescriptionProfile.cs b/src/WebJobs.Script/Workers/Profiles/WorkerDescriptionProfile.cs index a5f3942deb..d81d2ad81a 100644 --- a/src/WebJobs.Script/Workers/Profiles/WorkerDescriptionProfile.cs +++ b/src/WebJobs.Script/Workers/Profiles/WorkerDescriptionProfile.cs @@ -90,7 +90,7 @@ private string UseProfileOrDefault(string profileParameter, string defaultParame private IList UseProfileOrDefault(IList profileParameter, IList defaultParameter) { - return profileParameter.Count > 0 ? profileParameter : defaultParameter; + return profileParameter != null && profileParameter.Count > 0 ? profileParameter : defaultParameter; } } } \ No newline at end of file diff --git a/src/WebJobs.Script/Workers/Profiles/WorkerProfileConditionDescriptor.cs b/src/WebJobs.Script/Workers/Profiles/WorkerProfileConditionDescriptor.cs index 0204b8fca1..29bf5022ea 100644 --- a/src/WebJobs.Script/Workers/Profiles/WorkerProfileConditionDescriptor.cs +++ b/src/WebJobs.Script/Workers/Profiles/WorkerProfileConditionDescriptor.cs @@ -17,7 +17,7 @@ public sealed class WorkerProfileConditionDescriptor private IDictionary _properties; - [JsonProperty(Required = Required.Always)] + [JsonProperty(Required = Required.Always, PropertyName = WorkerConstants.WorkerDescriptionProfileConditionType)] public string Type { get; set; } public IDictionary Properties diff --git a/src/WebJobs.Script/Workers/Profiles/WorkerProfileDescriptor.cs b/src/WebJobs.Script/Workers/Profiles/WorkerProfileDescriptor.cs index 26611fab56..7bbadb6c45 100644 --- a/src/WebJobs.Script/Workers/Profiles/WorkerProfileDescriptor.cs +++ b/src/WebJobs.Script/Workers/Profiles/WorkerProfileDescriptor.cs @@ -8,7 +8,7 @@ namespace Microsoft.Azure.WebJobs.Script.Workers.Profiles { public sealed class WorkerProfileDescriptor { - public string Name { get; set; } + public string ProfileName { get; set; } public RpcWorkerDescription Description { get; set; } diff --git a/src/WebJobs.Script/Workers/Rpc/Configuration/RpcWorkerConfigFactory.cs b/src/WebJobs.Script/Workers/Rpc/Configuration/RpcWorkerConfigFactory.cs index d18929a583..9d0ddedf9a 100644 --- a/src/WebJobs.Script/Workers/Rpc/Configuration/RpcWorkerConfigFactory.cs +++ b/src/WebJobs.Script/Workers/Rpc/Configuration/RpcWorkerConfigFactory.cs @@ -201,14 +201,14 @@ private List ReadWorkerDescriptionProfiles(JToken prof if (!_profileManager.TryCreateWorkerProfileCondition(descriptor, out IWorkerProfileCondition condition)) { // Failed to resolve condition. This profile will be disabled using a mock false condition - _logger?.LogInformation($"Profile {profile.Name} is disabled. Cannot resolve the profile condition {descriptor.Type}"); + _logger?.LogInformation($"Profile {profile.ProfileName} is disabled. Cannot resolve the profile condition {descriptor.Type}"); condition = new FalseCondition(); } profileConditions.Add(condition); } - descriptionProfiles.Add(new (profile.Name, profileConditions, profile.Description)); + descriptionProfiles.Add(new (profile.ProfileName, profileConditions, profile.Description)); } } catch (Exception) diff --git a/test/WebJobs.Script.Tests/Workers/Profiles/HostPropertyConditionTests.cs b/test/WebJobs.Script.Tests/Workers/Profiles/HostPropertyConditionTests.cs index c7764a98a8..17d9ee1964 100644 --- a/test/WebJobs.Script.Tests/Workers/Profiles/HostPropertyConditionTests.cs +++ b/test/WebJobs.Script.Tests/Workers/Profiles/HostPropertyConditionTests.cs @@ -55,7 +55,7 @@ public void HostPropertyConditionTest_EvaluateTrue(string name, string testExpre [Theory] //[InlineData("sku", "Dynamic")] TODO: Add test case [InlineData("platForm", "Windows")] - [InlineData("HostVersion", "3.*")] + [InlineData("HostVersion", "-1")] public void HostPropertyConditionTest_EvaluateFalse(string name, string testExpression) { var testLogger = new TestLogger("test"); diff --git a/test/WebJobs.Script.Tests/Workers/Profiles/ProfilesTestUtilities.cs b/test/WebJobs.Script.Tests/Workers/Profiles/ProfilesTestUtilities.cs index a754626095..4298632d5e 100644 --- a/test/WebJobs.Script.Tests/Workers/Profiles/ProfilesTestUtilities.cs +++ b/test/WebJobs.Script.Tests/Workers/Profiles/ProfilesTestUtilities.cs @@ -12,7 +12,7 @@ namespace Microsoft.Azure.WebJobs.Script.Tests.Workers.Profiles { public class ProfilesTestUtilities { - public static JObject GetTestWorkerProfileCondition(string type = "hostproperty", string name = "hostversion", string expression = "4.*") + public static JObject GetTestWorkerProfileCondition(string type = WorkerConstants.WorkerDescriptionProfileHostPropertyCondition, string name = "hostVersion", string expression = "4.*") { var condition = new JObject(); condition[WorkerConstants.WorkerDescriptionProfileConditionType] = type; diff --git a/test/WebJobs.Script.Tests/Workers/Rpc/RpcWorkerConfigTestUtilities.cs b/test/WebJobs.Script.Tests/Workers/Rpc/RpcWorkerConfigTestUtilities.cs index 8d7f746036..acde14225e 100644 --- a/test/WebJobs.Script.Tests/Workers/Rpc/RpcWorkerConfigTestUtilities.cs +++ b/test/WebJobs.Script.Tests/Workers/Rpc/RpcWorkerConfigTestUtilities.cs @@ -18,7 +18,7 @@ public class RpcWorkerConfigTestUtilities public const string HttpWorkerExe = "httpServer.exe"; public const string TestDefaultExecutablePath = "testWorkerPath"; - public static JObject GetTestWorkerConfig(string language, string[] arguments, bool invalid, string profileName, bool emptyWorkerPath = false) + public static JObject GetTestWorkerConfig(string language, string[] arguments, bool invalid, string profileName, bool invalidProfile, bool emptyWorkerPath = false) { WorkerDescription description = GetTestDefaultWorkerDescription(language, arguments); @@ -32,10 +32,16 @@ public static JObject GetTestWorkerConfig(string language, string[] arguments, b DefaultExecutablePath = "myFooPath", }; - JArray profiles = new JArray(); - JObject profile = new JObject(); + var profiles = new JArray(); + var profile = new JObject(); + var conditions = new JArray(); profile[WorkerConstants.WorkerDescriptionProfileName] = "profileName"; - profile[WorkerConstants.WorkerDescriptionProfileConditions] = ProfilesTestUtilities.GetTestWorkerProfileCondition(); + if (invalidProfile) + { + conditions.Add(ProfilesTestUtilities.GetTestWorkerProfileCondition(WorkerConstants.WorkerDescriptionProfileHostPropertyCondition, "hostVersion", "-1")); + } + conditions.Add(ProfilesTestUtilities.GetTestWorkerProfileCondition()); + profile[WorkerConstants.WorkerDescriptionProfileConditions] = conditions; profile[WorkerConstants.WorkerDescription] = JObject.FromObject(appSvcDescription); profiles.Add(profile); config[WorkerConstants.WorkerDescriptionProfiles] = profiles; diff --git a/test/WebJobs.Script.Tests/Workers/Rpc/RpcWorkerConfigTests.cs b/test/WebJobs.Script.Tests/Workers/Rpc/RpcWorkerConfigTests.cs index 11a10f47d6..250805137d 100644 --- a/test/WebJobs.Script.Tests/Workers/Rpc/RpcWorkerConfigTests.cs +++ b/test/WebJobs.Script.Tests/Workers/Rpc/RpcWorkerConfigTests.cs @@ -115,7 +115,7 @@ public void ReadWorkerProviderFromConfig_ArgumentsFromSettings() [Fact] public void ReadWorkerProviderFromConfig_EmptyWorkerPath() { - var configs = new List() { MakeTestConfig(testLanguage, new string[0], false, string.Empty, true) }; + var configs = new List() { MakeTestConfig(testLanguage, new string[0], false, string.Empty, false, true) }; TestMetricsLogger testMetricsLogger = new TestMetricsLogger(); var workerConfigs = TestReadWorkerProviderFromConfig(configs, new TestLogger(testLanguage), testMetricsLogger); @@ -205,7 +205,7 @@ public void ReadWorkerProviderFromConfig_InvalidWorker() public void ReadWorkerProviderFromConfig_AddProfile_ReturnsDefaultDescription() { var expectedArguments = new string[] { "-v", "verbose" }; - var configs = new List() { MakeTestConfig(testLanguage, expectedArguments, false, "TestProfile") }; + var configs = new List() { MakeTestConfig(testLanguage, expectedArguments, false, "TestProfile", true) }; var testLogger = new TestLogger(testLanguage); TestMetricsLogger testMetricsLogger = new TestMetricsLogger(); @@ -672,9 +672,9 @@ private static IConfigurationRoot TestConfigBuilder(string workerPathSection, Di return config; } - private static TestRpcWorkerConfig MakeTestConfig(string language, string[] arguments, bool invalid = false, string addAppSvcProfile = "", bool emptyWorkerPath = false) + private static TestRpcWorkerConfig MakeTestConfig(string language, string[] arguments, bool invalid = false, string addAppSvcProfile = "", bool invalidProfile = false, bool emptyWorkerPath = false) { - string json = RpcWorkerConfigTestUtilities.GetTestWorkerConfig(language, arguments, invalid, addAppSvcProfile, emptyWorkerPath).ToString(); + string json = RpcWorkerConfigTestUtilities.GetTestWorkerConfig(language, arguments, invalid, addAppSvcProfile, invalidProfile, emptyWorkerPath).ToString(); return new TestRpcWorkerConfig() { Json = json, From 76710afe0fc1ef64b5cd327ea3b130f4cecbbff5 Mon Sep 17 00:00:00 2001 From: Shreyas Gopalakrishna Date: Fri, 10 Jun 2022 13:04:21 -0500 Subject: [PATCH 8/9] Updating WorkerProfileConditionProvider and tests --- .../Workers/Profiles/WorkerProfileConditionProvider.cs | 6 ++---- src/WebJobs.Script/Workers/Profiles/WorkerProfileManager.cs | 2 +- .../Configuration/LanguageWorkerOptionsSetupTests.cs | 2 +- .../Workers/Rpc/RpcWorkerConfigFactoryTests.cs | 2 +- .../Workers/Rpc/RpcWorkerConfigTests.cs | 2 +- .../Workers/Rpc/WebHostRpcWorkerChannelManagerTests.cs | 2 +- 6 files changed, 7 insertions(+), 9 deletions(-) diff --git a/src/WebJobs.Script/Workers/Profiles/WorkerProfileConditionProvider.cs b/src/WebJobs.Script/Workers/Profiles/WorkerProfileConditionProvider.cs index b71876691b..b7f9c4247d 100644 --- a/src/WebJobs.Script/Workers/Profiles/WorkerProfileConditionProvider.cs +++ b/src/WebJobs.Script/Workers/Profiles/WorkerProfileConditionProvider.cs @@ -11,13 +11,11 @@ internal sealed class WorkerProfileConditionProvider : IWorkerProfileConditionPr { private readonly ILogger _logger; private readonly IEnvironment _environment; - private readonly ISystemRuntimeInformation _systemRuntimeInformation; - public WorkerProfileConditionProvider(ILogger logger, ISystemRuntimeInformation systemRuntimeInfo, IEnvironment environment) + public WorkerProfileConditionProvider(ILogger logger, IEnvironment environment) { _logger = logger ?? throw new ArgumentNullException(nameof(logger)); _environment = environment ?? throw new ArgumentNullException(nameof(environment)); - _systemRuntimeInformation = systemRuntimeInfo ?? SystemRuntimeInformation.Instance; } /// @@ -25,7 +23,7 @@ public bool TryCreateCondition(WorkerProfileConditionDescriptor descriptor, out { condition = descriptor.Type switch { - WorkerConstants.WorkerDescriptionProfileHostPropertyCondition => new HostPropertyCondition(_logger, _systemRuntimeInformation, descriptor), + WorkerConstants.WorkerDescriptionProfileHostPropertyCondition => new HostPropertyCondition(_logger, SystemRuntimeInformation.Instance, descriptor), WorkerConstants.WorkerDescriptionProfileEnvironmentCondition => new EnvironmentCondition(_logger, _environment, descriptor), _ => null }; diff --git a/src/WebJobs.Script/Workers/Profiles/WorkerProfileManager.cs b/src/WebJobs.Script/Workers/Profiles/WorkerProfileManager.cs index 89475c72de..ea5b2e9339 100644 --- a/src/WebJobs.Script/Workers/Profiles/WorkerProfileManager.cs +++ b/src/WebJobs.Script/Workers/Profiles/WorkerProfileManager.cs @@ -30,7 +30,7 @@ public WorkerProfileManager(ILogger logger, IEnumerable public void SaveWorkerDescriptionProfiles(List workerDescriptionProfiles, string language) { - _profiles.Add(language, workerDescriptionProfiles); + _profiles[language] = workerDescriptionProfiles; } // Evaluate profile conditions for a language diff --git a/test/WebJobs.Script.Tests/Configuration/LanguageWorkerOptionsSetupTests.cs b/test/WebJobs.Script.Tests/Configuration/LanguageWorkerOptionsSetupTests.cs index dc0259fd2c..6eb52a8f8e 100644 --- a/test/WebJobs.Script.Tests/Configuration/LanguageWorkerOptionsSetupTests.cs +++ b/test/WebJobs.Script.Tests/Configuration/LanguageWorkerOptionsSetupTests.cs @@ -24,7 +24,7 @@ public void LanguageWorkerOptions_Expected_ListOfConfigs(string workerRuntime) var configurationBuilder = new ConfigurationBuilder() .Add(new ScriptEnvironmentVariablesConfigurationSource()); - var profileConditionProvider = new WorkerProfileConditionProvider(new TestLogger(), runtimeInfo, testEnvironment); + var profileConditionProvider = new WorkerProfileConditionProvider(new TestLogger(), testEnvironment); var profileConditionManager = new WorkerProfileManager(new TestLogger(), new[] { profileConditionProvider }); var configuration = configurationBuilder.Build(); diff --git a/test/WebJobs.Script.Tests/Workers/Rpc/RpcWorkerConfigFactoryTests.cs b/test/WebJobs.Script.Tests/Workers/Rpc/RpcWorkerConfigFactoryTests.cs index 2634968758..ac7040d5cf 100644 --- a/test/WebJobs.Script.Tests/Workers/Rpc/RpcWorkerConfigFactoryTests.cs +++ b/test/WebJobs.Script.Tests/Workers/Rpc/RpcWorkerConfigFactoryTests.cs @@ -25,7 +25,7 @@ public RpcWorkerConfigFactoryTests() { _testEnvironment = new TestEnvironment(); - var profileConditionProvider = new WorkerProfileConditionProvider(new TestLogger(), _testSysRuntimeInfo, _testEnvironment); + var profileConditionProvider = new WorkerProfileConditionProvider(new TestLogger(), _testEnvironment); _profileManager = new WorkerProfileManager(new TestLogger(), new[] { profileConditionProvider }); } diff --git a/test/WebJobs.Script.Tests/Workers/Rpc/RpcWorkerConfigTests.cs b/test/WebJobs.Script.Tests/Workers/Rpc/RpcWorkerConfigTests.cs index 250805137d..89b6c57296 100644 --- a/test/WebJobs.Script.Tests/Workers/Rpc/RpcWorkerConfigTests.cs +++ b/test/WebJobs.Script.Tests/Workers/Rpc/RpcWorkerConfigTests.cs @@ -32,7 +32,7 @@ public RpcWorkerConfigTests() { _testEnvironment = new TestEnvironment(); - var profileConditionProvider = new WorkerProfileConditionProvider(new TestLogger(), _testSysRuntimeInfo, _testEnvironment); + var profileConditionProvider = new WorkerProfileConditionProvider(new TestLogger(), _testEnvironment); _profileManager = new WorkerProfileManager(new TestLogger(), new[] { profileConditionProvider }); } diff --git a/test/WebJobs.Script.Tests/Workers/Rpc/WebHostRpcWorkerChannelManagerTests.cs b/test/WebJobs.Script.Tests/Workers/Rpc/WebHostRpcWorkerChannelManagerTests.cs index 4799c8aad0..37947855d3 100644 --- a/test/WebJobs.Script.Tests/Workers/Rpc/WebHostRpcWorkerChannelManagerTests.cs +++ b/test/WebJobs.Script.Tests/Workers/Rpc/WebHostRpcWorkerChannelManagerTests.cs @@ -73,7 +73,7 @@ public WebHostRpcWorkerChannelManagerTests() _testLogger = new TestLogger("WebHostLanguageWorkerChannelManagerTests"); _rpcWorkerChannelFactory = new TestRpcWorkerChannelFactory(_eventManager, _testLogger, _scriptRootPath); _emptyConfig = new ConfigurationBuilder().Build(); - var profileConditionProvider = new WorkerProfileConditionProvider(new TestLogger(), _testSysRuntimeInfo, _testEnvironment); + var profileConditionProvider = new WorkerProfileConditionProvider(new TestLogger(), _testEnvironment); _profileManager = new WorkerProfileManager(new TestLogger(), new[] { profileConditionProvider }); _rpcWorkerChannelManager = new WebHostRpcWorkerChannelManager(_eventManager, _testEnvironment, _loggerFactory, _rpcWorkerChannelFactory, _optionsMonitor, new TestMetricsLogger(), _workerOptionsMonitor, _emptyConfig, _profileManager); } From 6273049cef881158a1a5c0395d9f199bae8cf526 Mon Sep 17 00:00:00 2001 From: Shreyas Gopalakrishna Date: Thu, 16 Jun 2022 13:33:08 -0500 Subject: [PATCH 9/9] Rename SetWorkerDescriptionProfiles --- src/WebJobs.Script/Workers/Profiles/IWorkerProfileManager.cs | 4 ++-- src/WebJobs.Script/Workers/Profiles/WorkerProfileManager.cs | 2 +- .../Workers/Rpc/Configuration/RpcWorkerConfigFactory.cs | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/WebJobs.Script/Workers/Profiles/IWorkerProfileManager.cs b/src/WebJobs.Script/Workers/Profiles/IWorkerProfileManager.cs index a3982648cd..d61b7b83aa 100644 --- a/src/WebJobs.Script/Workers/Profiles/IWorkerProfileManager.cs +++ b/src/WebJobs.Script/Workers/Profiles/IWorkerProfileManager.cs @@ -18,9 +18,9 @@ public interface IWorkerProfileManager bool TryCreateWorkerProfileCondition(WorkerProfileConditionDescriptor conditionDescriptor, out IWorkerProfileCondition condition); /// - /// Save different profiles for a given worker runtime language + /// Set the different profiles for a given worker runtime language /// - void SaveWorkerDescriptionProfiles(List workerDescriptionProfiles, string language); + void SetWorkerDescriptionProfiles(List workerDescriptionProfiles, string language); /// /// Load a profile that meets it's conditions diff --git a/src/WebJobs.Script/Workers/Profiles/WorkerProfileManager.cs b/src/WebJobs.Script/Workers/Profiles/WorkerProfileManager.cs index ea5b2e9339..669ac71116 100644 --- a/src/WebJobs.Script/Workers/Profiles/WorkerProfileManager.cs +++ b/src/WebJobs.Script/Workers/Profiles/WorkerProfileManager.cs @@ -28,7 +28,7 @@ public WorkerProfileManager(ILogger logger, IEnumerable - public void SaveWorkerDescriptionProfiles(List workerDescriptionProfiles, string language) + public void SetWorkerDescriptionProfiles(List workerDescriptionProfiles, string language) { _profiles[language] = workerDescriptionProfiles; } diff --git a/src/WebJobs.Script/Workers/Rpc/Configuration/RpcWorkerConfigFactory.cs b/src/WebJobs.Script/Workers/Rpc/Configuration/RpcWorkerConfigFactory.cs index 9d0ddedf9a..c22498304c 100644 --- a/src/WebJobs.Script/Workers/Rpc/Configuration/RpcWorkerConfigFactory.cs +++ b/src/WebJobs.Script/Workers/Rpc/Configuration/RpcWorkerConfigFactory.cs @@ -134,7 +134,7 @@ internal void AddProvider(string workerDir) List workerDescriptionProfiles = ReadWorkerDescriptionProfiles(profiles); if (workerDescriptionProfiles.Count > 0) { - _profileManager.SaveWorkerDescriptionProfiles(workerDescriptionProfiles, workerDescription.Language); + _profileManager.SetWorkerDescriptionProfiles(workerDescriptionProfiles, workerDescription.Language); _profileManager.LoadWorkerDescriptionFromProfiles(workerDescription, out workerDescription); } }