Skip to content

Commit

Permalink
Adding min version requirements for flex consumption
Browse files Browse the repository at this point in the history
  • Loading branch information
soninaren committed Jul 15, 2024
1 parent 7fd5ca6 commit 2875402
Show file tree
Hide file tree
Showing 6 changed files with 186 additions and 16 deletions.
3 changes: 2 additions & 1 deletion release_notes.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
- `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)
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ public class ScriptStartupTypeLocator : IWebJobsStartupTypeLocator
private readonly Lazy<IEnumerable<Type>> _startupTypes;
private readonly IOptionsMonitor<LanguageWorkerOptions> _languageWorkerOptions;

private static readonly ExtensionRequirementsInfo _extensionRequirements = DependencyHelper.GetExtensionRequirements();
private static ExtensionRequirementsInfo _extensionRequirements;
private static string[] _builtinExtensionAssemblies = GetBuiltinExtensionAssemblies();

public ScriptStartupTypeLocator(string rootScriptPath, ILogger<ScriptStartupTypeLocator> logger, IExtensionBundleManager extensionBundleManager,
Expand All @@ -52,6 +52,7 @@ public ScriptStartupTypeLocator(string rootScriptPath, ILogger<ScriptStartupType
_metricsLogger = metricsLogger;
_startupTypes = new Lazy<IEnumerable<Type>>(() => GetExtensionsStartupTypesAsync().ConfigureAwait(false).GetAwaiter().GetResult());
_languageWorkerOptions = languageWorkerOptions;
_extensionRequirements = DependencyHelper.GetExtensionRequirements(SystemEnvironment.Instance);
}

private static string[] GetBuiltinExtensionAssemblies()
Expand Down
16 changes: 13 additions & 3 deletions src/WebJobs.Script/Description/DotNet/DependencyHelper.cs
Original file line number Diff line number Diff line change
Expand Up @@ -91,9 +91,19 @@ internal static Dictionary<string, ScriptRuntimeAssembly> 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"]
Expand Down Expand Up @@ -184,7 +194,7 @@ public static List<string> GetRuntimeFallbacks(string rid)
/// <returns> bool if string in was in proper assembly representation format. </returns>
public static bool IsAssemblyReferenceFormat(string assemblyFormatString)
{
return assemblyFormatString != null && assemblyFormatString.StartsWith(AssemblyNamePrefix);
return assemblyFormatString != null && assemblyFormatString.StartsWith(AssemblyNamePrefix);
}

/// <summary>
Expand Down
3 changes: 3 additions & 0 deletions src/WebJobs.Script/WebJobs.Script.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,9 @@
<EmbeddedResource Include="extensionrequirements.json">
<CopyToOutputDirectory>Never</CopyToOutputDirectory>
</EmbeddedResource>
<EmbeddedResource Include="extensionrequirements_FlexConsumption.json">
<CopyToOutputDirectory>Never</CopyToOutputDirectory>
</EmbeddedResource>
<EmbeddedResource Include="FileProvisioning\PowerShell\profile.ps1" />
<EmbeddedResource Include="FileProvisioning\PowerShell\requirements.psd1" />
<EmbeddedResource Include="runtimeassemblies-relaxed.json">
Expand Down
131 changes: 131 additions & 0 deletions src/WebJobs.Script/extensionrequirements_FlexConsumption.json
Original file line number Diff line number Diff line change
@@ -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"
}
]
}
46 changes: 35 additions & 11 deletions test/WebJobs.Script.Tests/ScriptStartupTypeDiscovererTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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())
{
Expand All @@ -614,12 +617,16 @@ public async Task GetExtensionsStartupTypes_RejectsBundleBelowMinimumVersion()
var testLogger = factory.CreateLogger<ScriptStartupTypeLocator>();

var binPath = Path.Combine(directory.Path, "bin");
if (isFlexConsumption)
{
Environment.SetEnvironmentVariable(EnvironmentSettingNames.AzureWebsiteSku, ScriptConstants.FlexConsumptionSku);
}

var mockExtensionBundleManager = new Mock<IExtensionBundleManager>();
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<LanguageWorkerOptions>(new LanguageWorkerOptions());
var mockFunctionMetadataManager = GetTestFunctionMetadataManager(languageWorkerOptions);
Expand All @@ -628,14 +635,17 @@ public async Task GetExtensionsStartupTypes_RejectsBundleBelowMinimumVersion()
// Act
var exception = await Assert.ThrowsAsync<HostInitializationException>(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<IExtensionBundleManager>();
TestMetricsLogger testMetricsLogger = new TestMetricsLogger();
Expand All @@ -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)
Expand Down Expand Up @@ -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);
}
}
}

Expand Down

0 comments on commit 2875402

Please sign in to comment.