diff --git a/release_notes.md b/release_notes.md index a8e2e23051..cc25a7f00c 100644 --- a/release_notes.md +++ b/release_notes.md @@ -24,4 +24,5 @@ - `Microsoft.Azure.WebJobs` updated to 3.0.41 - `Microsoft.Azure.WebJobs.Host.Storage` updated to 5.0.1 - `Microsoft.Extensions.Azure` updated to 1.7.1 - - `Azure.Storage.Blobs` updated to 12.19.1 \ No newline at end of file + - `Azure.Storage.Blobs` updated to 12.19.1 +- Enforcing Extension Bundle version [4.12.0](https://github.com/Azure/azure-functions-extension-bundles/releases/tag/4.12.0) as min version for Flex consumption (#9478) \ No newline at end of file diff --git a/src/WebJobs.Script/DependencyInjection/ScriptStartupTypeLocator.cs b/src/WebJobs.Script/DependencyInjection/ScriptStartupTypeLocator.cs index f7939b8fac..e7a6f94687 100644 --- a/src/WebJobs.Script/DependencyInjection/ScriptStartupTypeLocator.cs +++ b/src/WebJobs.Script/DependencyInjection/ScriptStartupTypeLocator.cs @@ -39,7 +39,7 @@ public class ScriptStartupTypeLocator : IWebJobsStartupTypeLocator private readonly Lazy> _startupTypes; private readonly IOptionsMonitor _languageWorkerOptions; - private static readonly ExtensionRequirementsInfo _extensionRequirements = DependencyHelper.GetExtensionRequirements(); + private static ExtensionRequirementsInfo _extensionRequirements; private static string[] _builtinExtensionAssemblies = GetBuiltinExtensionAssemblies(); public ScriptStartupTypeLocator(string rootScriptPath, ILogger logger, IExtensionBundleManager extensionBundleManager, @@ -52,6 +52,7 @@ public ScriptStartupTypeLocator(string rootScriptPath, ILogger>(() => GetExtensionsStartupTypesAsync().ConfigureAwait(false).GetAwaiter().GetResult()); _languageWorkerOptions = languageWorkerOptions; + _extensionRequirements = DependencyHelper.GetExtensionRequirements(SystemEnvironment.Instance); } private static string[] GetBuiltinExtensionAssemblies() diff --git a/src/WebJobs.Script/Description/DotNet/DependencyHelper.cs b/src/WebJobs.Script/Description/DotNet/DependencyHelper.cs index 3f9ed5e932..b5e2ede26e 100644 --- a/src/WebJobs.Script/Description/DotNet/DependencyHelper.cs +++ b/src/WebJobs.Script/Description/DotNet/DependencyHelper.cs @@ -91,9 +91,19 @@ internal static Dictionary GetRuntimeAssemblies(s .ToDictionary(a => a.Name, StringComparer.OrdinalIgnoreCase); } - internal static ExtensionRequirementsInfo GetExtensionRequirements() + internal static ExtensionRequirementsInfo GetExtensionRequirements(IEnvironment environment) { - string requirementsJson = GetResourceFileContents("extensionrequirements.json"); + string requirementsJson = string.Empty; + + if (environment.IsFlexConsumptionSku()) + { + requirementsJson = GetResourceFileContents("extensionrequirements_FlexConsumption.json"); + } + else + { + requirementsJson = GetResourceFileContents("extensionrequirements.json"); + } + JObject requirements = JObject.Parse(requirementsJson); var bundleRequirements = requirements["bundles"] @@ -184,7 +194,7 @@ public static List GetRuntimeFallbacks(string rid) /// bool if string in was in proper assembly representation format. public static bool IsAssemblyReferenceFormat(string assemblyFormatString) { - return assemblyFormatString != null && assemblyFormatString.StartsWith(AssemblyNamePrefix); + return assemblyFormatString != null && assemblyFormatString.StartsWith(AssemblyNamePrefix); } /// diff --git a/src/WebJobs.Script/WebJobs.Script.csproj b/src/WebJobs.Script/WebJobs.Script.csproj index 3333c6e579..fcc0d462ad 100644 --- a/src/WebJobs.Script/WebJobs.Script.csproj +++ b/src/WebJobs.Script/WebJobs.Script.csproj @@ -27,6 +27,9 @@ Never + + Never + diff --git a/src/WebJobs.Script/extensionrequirements_FlexConsumption.json b/src/WebJobs.Script/extensionrequirements_FlexConsumption.json new file mode 100644 index 0000000000..65d6fe295c --- /dev/null +++ b/src/WebJobs.Script/extensionrequirements_FlexConsumption.json @@ -0,0 +1,131 @@ +{ + "bundles": [ + { + "id": "Microsoft.Azure.Functions.ExtensionBundle", + "minimumVersion": "4.12.0" + } + ], + "types": [ + { + "name": "NetheriteProviderStartup", + "assemblyName": "DurableTask.Netherite.AzureFunctions", + "minimumAssemblyVersion": "1.0.0.0", + "minimumAssemblyFileVersion": "1.4.0.4129", + "packageName": "Microsoft.Azure.DurableTask.Netherite.AzureFunctions", + "minimumPackageVersion": "1.4.0" + }, + { + "name": "SqlDurabilityProviderStartup", + "assemblyName": "DurableTask.SqlServer.AzureFunctions", + "minimumAssemblyVersion": "1.2.0.0", + "packageName": "Microsoft.DurableTask.SqlServer.AzureFunctions", + "minimumPackageVersion": "1.2.0" + }, + { + "name": "CosmosDBWebJobsStartup", + "assemblyName": "Microsoft.Azure.WebJobs.Extensions.CosmosDB", + "minimumAssemblyVersion": "4.4.0.0", + "packageName": "Microsoft.Azure.WebJobs.Extensions.CosmosDB", + "minimumPackageVersion": "4.4.0" + }, + { + "name": "DurableTaskWebJobsStartup", + "assemblyName": "Microsoft.Azure.WebJobs.Extensions.DurableTask", + "minimumAssemblyVersion": "2.0.0.0", + "MinimumAssemblyFileVersion": "2.12.0", + "packageName": "Microsoft.Azure.WebJobs.Extensions.DurableTask", + "minimumPackageVersion": "2.12.0" + }, + { + "name": "EventGridWebJobsStartup", + "assemblyName": "Microsoft.Azure.WebJobs.Extensions.EventGrid", + "minimumAssemblyVersion": "3.3.0.0", + "packageName": "Microsoft.Azure.WebJobs.Extensions.EventGrid", + "minimumPackageVersion": "3.3.0" + }, + { + "name": "EventHubsWebJobsStartup", + "assemblyName": "Microsoft.Azure.WebJobs.Extensions.EventHubs", + "minimumAssemblyVersion": "5.5.0.0", + "packageName": "Microsoft.Azure.WebJobs.Extensions.EventHubs", + "minimumPackageVersion": "5.5.0" + }, + { + "name": "KafkaWebJobsStartup", + "assemblyName": "Microsoft.Azure.WebJobs.Extensions.Kafka", + "minimumAssemblyVersion": "3.9.0.0", + "packageName": "Microsoft.Azure.WebJobs.Extensions.Kafka", + "minimumPackageVersion": "3.9.0" + }, + { + "name": "RabbitMQWebJobsStartup", + "assemblyName": "Microsoft.Azure.WebJobs.Extensions.RabbitMQ", + "minimumAssemblyVersion": "2.0.3.0", + "packageName": "Microsoft.Azure.WebJobs.Extensions.RabbitMQ", + "minimumPackageVersion": "2.0.3" + }, + { + "name": "SendGridWebJobsStartup", + "assemblyName": "Microsoft.Azure.WebJobs.Extensions.SendGrid", + "minimumAssemblyVersion": "3.0.3.0", + "packageName": "Microsoft.Azure.WebJobs.Extensions.SendGrid", + "minimumPackageVersion": "3.0.3" + }, + { + "name": "ServiceBusWebJobsStartup", + "assemblyName": "Microsoft.Azure.WebJobs.Extensions.ServiceBus", + "minimumAssemblyVersion": "5.12.0.0", + "packageName": "Microsoft.Azure.WebJobs.Extensions.ServiceBus", + "minimumPackageVersion": "5.12.0" + }, + { + "name": "SignalRWebJobsStartup", + "assemblyName": "Microsoft.Azure.WebJobs.Extensions.SignalRService", + "minimumAssemblyVersion": "1.11.2.0", + "packageName": "Microsoft.Azure.WebJobs.Extensions.SignalRService", + "minimumPackageVersion": "1.11.2" + }, + { + "name": "SqlBindingStartup", + "assemblyName": "Microsoft.Azure.WebJobs.Extensions.Sql", + "minimumAssemblyVersion": "3.0.461.0", + "packageName": "Microsoft.Azure.WebJobs.Extensions.Sql", + "minimumPackageVersion": "3.0.461" + }, + { + "name": "AzureStorageBlobsWebJobsStartup", + "assemblyName": "Microsoft.Azure.WebJobs.Extensions.Storage.Blobs", + "minimumAssemblyVersion": "5.2.1.0", + "packageName": "Microsoft.Azure.WebJobs.Extensions.Storage.Blobs", + "minimumPackageVersion": "5.2.1" + }, + { + "name": "AzureStorageQueuesWebJobsStartup", + "assemblyName": "Microsoft.Azure.WebJobs.Extensions.Storage.Queues", + "minimumAssemblyVersion": "5.2.0.0", + "packageName": "Microsoft.Azure.WebJobs.Extensions.Storage.Queues", + "minimumPackageVersion": "5.2.0" + }, + { + "name": "AzureTablesWebJobsStartup", + "assemblyName": "Microsoft.Azure.WebJobs.Extensions.Tables", + "minimumAssemblyVersion": "1.2.0.0", + "packageName": "Microsoft.Azure.WebJobs.Extensions.Tables", + "minimumPackageVersion": "1.2.0" + }, + { + "name": "TwilioWebJobsStartup", + "assemblyName": "Microsoft.Azure.WebJobs.Extensions.Twilio", + "minimumAssemblyVersion": "3.0.2.0", + "packageName": "Microsoft.Azure.WebJobs.Extensions.Twilio", + "minimumPackageVersion": "3.0.2" + }, + { + "name": "WebPubSubWebJobsStartup", + "assemblyName": "Microsoft.Azure.WebJobs.Extensions.WebPubSub", + "minimumAssemblyVersion": "1.7.0.0", + "packageName": "Microsoft.Azure.WebJobs.Extensions.WebPubSub", + "minimumPackageVersion": "1.7.0" + } + ] +} \ No newline at end of file diff --git a/test/WebJobs.Script.Tests/ScriptStartupTypeDiscovererTests.cs b/test/WebJobs.Script.Tests/ScriptStartupTypeDiscovererTests.cs index 28a688f173..a3f49c4f4d 100644 --- a/test/WebJobs.Script.Tests/ScriptStartupTypeDiscovererTests.cs +++ b/test/WebJobs.Script.Tests/ScriptStartupTypeDiscovererTests.cs @@ -7,6 +7,7 @@ using System.IO; using System.Linq; using System.Threading.Tasks; +using Google.Protobuf.Compiler; using Microsoft.Azure.WebJobs.Extensions; using Microsoft.Azure.WebJobs.Extensions.Http; using Microsoft.Azure.WebJobs.Extensions.Storage; @@ -602,8 +603,10 @@ void CopyToBin(string path) } } - [Fact] - public async Task GetExtensionsStartupTypes_RejectsBundleBelowMinimumVersion() + [Theory] + [InlineData(true, "4.12.0", "4.9.0")] + [InlineData(false, "2.6.1", "2.1.0")] + public async Task GetExtensionsStartupTypes_RejectsBundleBelowMinimumVersion(bool isFlexConsumption, string expectedBundleVersion, string actualBundleVersion) { using (var directory = GetTempDirectory()) { @@ -614,12 +617,16 @@ public async Task GetExtensionsStartupTypes_RejectsBundleBelowMinimumVersion() var testLogger = factory.CreateLogger(); var binPath = Path.Combine(directory.Path, "bin"); + if (isFlexConsumption) + { + Environment.SetEnvironmentVariable(EnvironmentSettingNames.AzureWebsiteSku, ScriptConstants.FlexConsumptionSku); + } var mockExtensionBundleManager = new Mock(); mockExtensionBundleManager.Setup(e => e.IsExtensionBundleConfigured()).Returns(true); mockExtensionBundleManager.Setup(e => e.GetExtensionBundleBinPathAsync()).Returns(Task.FromResult(binPath)); mockExtensionBundleManager.Setup(e => e.IsLegacyExtensionBundle()).Returns(false); - mockExtensionBundleManager.Setup(e => e.GetExtensionBundleDetails()).Returns(Task.FromResult(GetV2BundleDetails("2.1.0"))); + mockExtensionBundleManager.Setup(e => e.GetExtensionBundleDetails()).Returns(Task.FromResult(GetV2BundleDetails(actualBundleVersion))); var languageWorkerOptions = new TestOptionsMonitor(new LanguageWorkerOptions()); var mockFunctionMetadataManager = GetTestFunctionMetadataManager(languageWorkerOptions); @@ -628,14 +635,17 @@ public async Task GetExtensionsStartupTypes_RejectsBundleBelowMinimumVersion() // Act var exception = await Assert.ThrowsAsync(async () => await discoverer.GetExtensionsStartupTypesAsync()); var traces = testLoggerProvider.GetAllLogMessages(); + Environment.SetEnvironmentVariable(EnvironmentSettingNames.AzureWebsiteSku, null); // Assert - Assert.True(traces.Any(m => string.Equals(m.FormattedMessage, $"Referenced bundle Microsoft.Azure.Functions.ExtensionBundle of version 2.1.0 does not meet the required minimum version of 2.6.1. Update your extension bundle reference in host.json to reference 2.6.1 or later."))); + Assert.True(traces.Any(m => string.Equals(m.FormattedMessage, $"Referenced bundle Microsoft.Azure.Functions.ExtensionBundle of version {actualBundleVersion} does not meet the required minimum version of {expectedBundleVersion}. Update your extension bundle reference in host.json to reference {expectedBundleVersion} or later."))); } } - [Fact] - public async Task GetExtensionsStartupTypes_RejectsExtensionsBelowMinimumVersion() + [Theory] + [InlineData(true)] + [InlineData(false)] + public async Task GetExtensionsStartupTypes_RejectsExtensionsBelowMinimumVersion(bool isFlexConsumption) { var mockExtensionBundleManager = new Mock(); TestMetricsLogger testMetricsLogger = new TestMetricsLogger(); @@ -644,6 +654,11 @@ public async Task GetExtensionsStartupTypes_RejectsExtensionsBelowMinimumVersion using (var directory = new TempDirectory()) { var binPath = Path.Combine(directory.Path, "bin"); + if (isFlexConsumption) + { + Environment.SetEnvironmentVariable(EnvironmentSettingNames.AzureWebsiteSku, ScriptConstants.FlexConsumptionSku); + } + Directory.CreateDirectory(binPath); void CopyToBin(string path) @@ -671,11 +686,20 @@ void CopyToBin(string path) var traces = testLoggerProvider.GetAllLogMessages(); // Assert - - var storageTrace = traces.FirstOrDefault(m => m.FormattedMessage.StartsWith("ExtensionStartupType AzureStorageWebJobsStartup")); - Assert.NotNull(storageTrace); - Assert.Equal("ExtensionStartupType AzureStorageWebJobsStartup from assembly 'Microsoft.Azure.WebJobs.Extensions.Storage, Version=3.0.10.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35' does not meet the required minimum version of 4.0.4.0. Update your NuGet package reference for Microsoft.Azure.WebJobs.Extensions.Storage to 4.0.4 or later.", - storageTrace.FormattedMessage); + try + { + string exceptionType = isFlexConsumption ? "DurableTaskWebJobsStartup" : "AzureStorageWebJobsStartup"; + var trace = traces.FirstOrDefault(m => m.FormattedMessage.StartsWith($"ExtensionStartupType {exceptionType}")); + Assert.NotNull(trace); + string message = isFlexConsumption + ? "ExtensionStartupType DurableTaskWebJobsStartup from assembly 'Microsoft.Azure.WebJobs.Extensions.DurableTask, Version=2.0.0.0, Culture=neutral, PublicKeyToken=014045d636e89289' does not meet the required minimum version of 2.12.0. Update your NuGet package reference for Microsoft.Azure.WebJobs.Extensions.DurableTask to 2.12.0 or later." + : "ExtensionStartupType AzureStorageWebJobsStartup from assembly 'Microsoft.Azure.WebJobs.Extensions.Storage, Version=3.0.10.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35' does not meet the required minimum version of 4.0.4.0. Update your NuGet package reference for Microsoft.Azure.WebJobs.Extensions.Storage to 4.0.4 or later."; + Assert.Equal(message, trace.FormattedMessage); + } + finally + { + Environment.SetEnvironmentVariable(EnvironmentSettingNames.AzureWebsiteSku, null); + } } }