Skip to content

Commit d251aef

Browse files
authored
capping the bundle version to 3.19.0 and 4.2.0 (#9217)
1 parent 8129be7 commit d251aef

File tree

6 files changed

+85
-16
lines changed

6 files changed

+85
-16
lines changed

src/WebJobs.Script/Config/FunctionsHostingConfigOptions.cs

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,28 @@ public bool FunctionsWorkerDynamicConcurrencyEnabled
3131
}
3232
}
3333

34+
/// <summary>
35+
/// Gets the highest version of extension bundle v3 supported
36+
/// </summary>
37+
public string MaximumSupportedBundleV3Version
38+
{
39+
get
40+
{
41+
return GetFeature(ScriptConstants.MaximumSupportedBundleV3Version) ?? "3.19.0";
42+
}
43+
}
44+
45+
/// <summary>
46+
/// Gets the highest version of extension bundle v4 supported
47+
/// </summary>
48+
public string MaximumSupportedBundleV4Version
49+
{
50+
get
51+
{
52+
return GetFeature(ScriptConstants.MaximumSupportedBundleV4Version) ?? "4.2.0";
53+
}
54+
}
55+
3456
/// <summary>
3557
/// Gets feature by name.
3658
/// </summary>

src/WebJobs.Script/ExtensionBundle/ExtensionBundleManager.cs

Lines changed: 32 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -10,10 +10,12 @@
1010
using System.Net.Http;
1111
using System.Resources;
1212
using System.Threading.Tasks;
13+
using Microsoft.Azure.WebJobs.Script.Config;
1314
using Microsoft.Azure.WebJobs.Script.Configuration;
1415
using Microsoft.Azure.WebJobs.Script.Diagnostics.Extensions;
1516
using Microsoft.Azure.WebJobs.Script.Models;
1617
using Microsoft.Extensions.Logging;
18+
using Microsoft.WindowsAzure.Storage.Shared.Protocol;
1719
using Newtonsoft.Json;
1820
using NuGet.Versioning;
1921

@@ -23,16 +25,18 @@ public class ExtensionBundleManager : IExtensionBundleManager
2325
{
2426
private readonly IEnvironment _environment;
2527
private readonly ExtensionBundleOptions _options;
28+
private readonly FunctionsHostingConfigOptions _configOption;
2629
private readonly ILogger _logger;
2730
private readonly string _cdnUri;
2831
private string _extensionBundleVersion;
2932

30-
public ExtensionBundleManager(ExtensionBundleOptions options, IEnvironment environment, ILoggerFactory loggerFactory)
33+
public ExtensionBundleManager(ExtensionBundleOptions options, IEnvironment environment, ILoggerFactory loggerFactory, FunctionsHostingConfigOptions configOption)
3134
{
3235
_environment = environment ?? throw new ArgumentNullException(nameof(environment));
3336
_logger = loggerFactory.CreateLogger<ExtensionBundleManager>() ?? throw new ArgumentNullException(nameof(loggerFactory));
3437
_cdnUri = _environment.GetEnvironmentVariable(EnvironmentSettingNames.ExtensionBundleSourceUri) ?? ScriptConstants.ExtensionBundleDefaultSourceUri;
3538
_options = options ?? throw new ArgumentNullException(nameof(options));
39+
_configOption = configOption ?? throw new ArgumentNullException(nameof(configOption));
3640
}
3741

3842
public async Task<ExtensionBundleDetails> GetExtensionBundleDetails()
@@ -128,7 +132,7 @@ internal bool TryLocateExtensionBundle(out string bundlePath)
128132
if (FileUtility.DirectoryExists(path))
129133
{
130134
var bundleDirectories = FileUtility.EnumerateDirectories(path);
131-
string version = FindBestVersionMatch(_options.Version, bundleDirectories);
135+
string version = FindBestVersionMatch(_options.Version, bundleDirectories, _options.Id, _configOption);
132136

133137
if (!string.IsNullOrEmpty(version))
134138
{
@@ -236,7 +240,8 @@ private async Task<string> GetLatestMatchingBundleVersionAsync(HttpClient httpCl
236240

237241
var content = await response.Content.ReadAsStringAsync();
238242
var bundleVersions = JsonConvert.DeserializeObject<IEnumerable<string>>(content);
239-
var matchingBundleVersion = FindBestVersionMatch(_options.Version, bundleVersions);
243+
244+
var matchingBundleVersion = FindBestVersionMatch(_options.Version, bundleVersions, _options.Id, _configOption);
240245

241246
if (string.IsNullOrEmpty(matchingBundleVersion))
242247
{
@@ -246,7 +251,7 @@ private async Task<string> GetLatestMatchingBundleVersionAsync(HttpClient httpCl
246251
return matchingBundleVersion;
247252
}
248253

249-
private static string FindBestVersionMatch(VersionRange versionRange, IEnumerable<string> versions)
254+
internal static string FindBestVersionMatch(VersionRange versionRange, IEnumerable<string> versions, string bundleId, FunctionsHostingConfigOptions configOption)
250255
{
251256
var bundleVersions = versions.Select(p =>
252257
{
@@ -259,7 +264,29 @@ private static string FindBestVersionMatch(VersionRange versionRange, IEnumerabl
259264
return version;
260265
}).Where(v => v != null);
261266

262-
return bundleVersions.OrderByDescending(version => version.Version).FirstOrDefault()?.ToString();
267+
var matchingVersion = bundleVersions.OrderByDescending(version => version.Version).FirstOrDefault();
268+
269+
if (bundleId != ScriptConstants.DefaultExtensionBundleId)
270+
{
271+
return matchingVersion?.ToString();
272+
}
273+
274+
var maximumBundleV3Version = NuGetVersion.Parse(configOption.MaximumSupportedBundleV3Version);
275+
matchingVersion = matchingVersion?.Major == 3 && matchingVersion > maximumBundleV3Version
276+
? maximumBundleV3Version
277+
: matchingVersion;
278+
279+
var maximumBundleV4Version = NuGetVersion.Parse(configOption.MaximumSupportedBundleV4Version);
280+
matchingVersion = matchingVersion?.Major == 4 && matchingVersion > maximumBundleV4Version
281+
? maximumBundleV4Version
282+
: matchingVersion;
283+
284+
if (matchingVersion?.Major > 4)
285+
{
286+
throw new HostInitializationException($"Referenced bundle {bundleId} of version {matchingVersion} does not meet the requirements for maximum supported version for extension bundle. Update your extension bundle reference in host.json to reference bundle version {maximumBundleV4Version} or lower.");
287+
}
288+
289+
return matchingVersion?.ToString();
263290
}
264291

265292
public async Task<string> GetExtensionBundleBinPathAsync()

src/WebJobs.Script/ScriptConstants.cs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -212,5 +212,7 @@ public static class ScriptConstants
212212
public static readonly ImmutableArray<string> SystemLogCategoryPrefixes = ImmutableArray.Create("Microsoft.Azure.WebJobs.", "Function.", "Worker.", "Host.");
213213

214214
public static readonly string FunctionsHostingConfigSectionName = "FunctionsHostingConfig";
215+
public static readonly string MaximumSupportedBundleV3Version = "FunctionRuntimeV3MaxBundleV3Version";
216+
public static readonly string MaximumSupportedBundleV4Version = "FunctionRuntimeV3MaxBundleV4Version";
215217
}
216218
}

src/WebJobs.Script/ScriptHostBuilderExtensions.cs

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -100,6 +100,7 @@ public static IHostBuilder AddScriptHost(this IHostBuilder builder,
100100
{
101101
configBuilder.Add(new HostJsonFileConfigurationSource(applicationOptions, SystemEnvironment.Instance, loggerFactory, metricsLogger));
102102
}
103+
configBuilder.Add(new FunctionsHostingConfigSource(SystemEnvironment.Instance));
103104
});
104105

105106
// WebJobs configuration
@@ -111,7 +112,11 @@ public static IHostBuilder AddScriptHost(this IHostBuilder builder,
111112
// Pre-build configuration here to load bundles and to store for later validation.
112113
var config = configBuilder.Build();
113114
var extensionBundleOptions = GetExtensionBundleOptions(config);
114-
var bundleManager = new ExtensionBundleManager(extensionBundleOptions, SystemEnvironment.Instance, loggerFactory);
115+
FunctionsHostingConfigOptions configOption = new FunctionsHostingConfigOptions();
116+
var optionsSetup = new FunctionsHostingConfigOptionsSetup(config);
117+
optionsSetup.Configure(configOption);
118+
119+
var bundleManager = new ExtensionBundleManager(extensionBundleOptions, SystemEnvironment.Instance, loggerFactory, configOption);
115120
var metadataServiceManager = applicationOptions.RootServiceProvider.GetService<IFunctionMetadataManager>();
116121
var languageWorkerOptions = applicationOptions.RootServiceProvider.GetService<IOptions<LanguageWorkerOptions>>();
117122

test/CSharpPrecompiledTestProjects/WebJobsStartupTests/Function1.cs

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -93,23 +93,23 @@ private static bool ValidateConfig(IConfiguration _config)
9393
{
9494
if (_config is ConfigurationRoot root)
9595
{
96-
if (root.Providers.Count() != 7)
96+
if (root.Providers.Count() != 8)
9797
{
9898
return false;
9999
}
100100

101101
int i = 0;
102102

103103
return
104-
root.Providers.ElementAt(i++) is ChainedConfigurationProvider &&
105-
root.Providers.ElementAt(i++) is MemoryConfigurationProvider &&
106-
root.Providers.ElementAt(i++).GetType().Name.StartsWith("HostJsonFile") &&
107-
root.Providers.ElementAt(i++) is JsonConfigurationProvider &&
108-
root.Providers.ElementAt(i++) is EnvironmentVariablesConfigurationProvider &&
109-
root.Providers.ElementAt(i++) is MemoryConfigurationProvider && // From Startup.cs
110-
root.Providers.ElementAt(i++) is JsonConfigurationProvider; // From test settings; Always runs last in tests.
104+
root.Providers.ElementAt(i++) is ChainedConfigurationProvider &&
105+
root.Providers.ElementAt(i++) is MemoryConfigurationProvider &&
106+
root.Providers.ElementAt(i++).GetType().Name.StartsWith("HostJsonFile") &&
107+
root.Providers.ElementAt(i++).GetType().Name.StartsWith("FunctionsHostingConfigProvider") &&
108+
root.Providers.ElementAt(i++) is JsonConfigurationProvider &&
109+
root.Providers.ElementAt(i++) is EnvironmentVariablesConfigurationProvider &&
110+
root.Providers.ElementAt(i++) is MemoryConfigurationProvider && // From Startup.cs
111+
root.Providers.ElementAt(i++) is JsonConfigurationProvider; // From test settings; Always runs last in tests.
111112
}
112-
113113
return false;
114114
}
115115
}

test/WebJobs.Script.Tests/ExtensionBundle/ExtensionBundleManagerTests.cs

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
using System.Text;
1515
using System.Threading;
1616
using System.Threading.Tasks;
17+
using Microsoft.Azure.WebJobs.Script.Config;
1718
using Microsoft.Azure.WebJobs.Script.Configuration;
1819
using Microsoft.Azure.WebJobs.Script.ExtensionBundle;
1920
using Microsoft.Extensions.Configuration;
@@ -384,10 +385,22 @@ public async Task GetExtensionBundle_CannotReachZipEndpoint_ReturnsFalseAsync()
384385
Assert.Null(await manager.GetExtensionBundlePath(httpClient));
385386
}
386387

388+
[Theory]
389+
[InlineData("[3.*, 4.0.0)", "3.19.0")]
390+
[InlineData("[4.*, 5.0.0)", "4.2.0")]
391+
public void LimitMaxVersion(string versionRange, string version)
392+
{
393+
var range = VersionRange.Parse(versionRange);
394+
var resolvedVersion = ExtensionBundleManager.FindBestVersionMatch(range, new List<string>()
395+
{ "3.7.0", "3.10.0", "3.11.0", "3.15.0", "3.14.0", "2.16.0", "3.13.0", "3.12.0", "3.9.1", "2.12.1", "2.18.0", "3.16.0", "2.19.0", "3.17.0", "4.0.2", "2.20.0", "3.18.0", "4.1.0", "4.2.0", "2.21.0", "3.19.0", "3.19.2", "4.3.0", "3.20.0" },
396+
ScriptConstants.DefaultExtensionBundleId, new FunctionsHostingConfigOptions());
397+
Assert.Equal(version, resolvedVersion);
398+
}
399+
387400
private ExtensionBundleManager GetExtensionBundleManager(ExtensionBundleOptions bundleOptions, TestEnvironment environment = null)
388401
{
389402
environment = environment ?? new TestEnvironment();
390-
return new ExtensionBundleManager(bundleOptions, environment, MockNullLoggerFactory.CreateLoggerFactory());
403+
return new ExtensionBundleManager(bundleOptions, environment, MockNullLoggerFactory.CreateLoggerFactory(), new FunctionsHostingConfigOptions());
391404
}
392405

393406
private TestEnvironment GetTestAppServiceEnvironment()

0 commit comments

Comments
 (0)