Skip to content

Commit d53ee48

Browse files
Francisco-Gaminoazfuncghsatvuandystaples
authoredMar 14, 2025··
Cherry-pick changes to release/in-proc/4.38 from patch/4.38.200: Update PS workers for patch (in-proc) (#10925), Platform release channel bundles resolution fix and additional logging (#10921), [in-proc backport] Support release channel settings in extension bundles resolution (#10915) (#10927)
* Update version to 4.38.200 * Clear release notes * [in-proc backport] Support release channel settings in extension bundles resolution (#10915) * Platform release channel bundles resolution fix and additional logging (#10921) * Update PS workers for patch (in-proc) (#10925) --------- Co-authored-by: Azure Functions Release <57925570+azfuncgh@users.noreply.github.com> Co-authored-by: sarah <35204912+satvu@users.noreply.github.com> Co-authored-by: andystaples <77818326+andystaples@users.noreply.github.com>
1 parent 333b57f commit d53ee48

File tree

7 files changed

+194
-29
lines changed

7 files changed

+194
-29
lines changed
 

‎eng/build/Workers.Powershell.props

+1-1
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
</ItemGroup>
88

99
<ItemGroup>
10-
<PackageReference Include="Microsoft.Azure.Functions.PowerShellWorker.PS7.4" Version="4.0.4175" />
10+
<PackageReference Include="Microsoft.Azure.Functions.PowerShellWorker.PS7.4" Version="4.0.4206" />
1111
</ItemGroup>
1212

1313
<Target Name="RemovePowershellWorkerRuntimes" BeforeTargets="AssignTargetPaths" Condition="$(RuntimeIdentifier.StartsWith(win))">

‎release_notes.md

+4-9
Original file line numberDiff line numberDiff line change
@@ -3,12 +3,7 @@
33
<!-- Please add your release notes in the following format:
44
- My change description (#PR)
55
-->
6-
- Update Java Worker Version to [2.18.0](https://github.com/Azure/azure-functions-java-worker/releases/tag/2.18.0)
7-
- Updated Microsoft.Extensions.DependencyModel to 6.0.2 and 8.0.2 for .NET 6 and .NET 8, respectively. (#10661)
8-
- Update the `DefaultHttpProxyService` to better handle client disconnect scenarios (#10688)
9-
- Replaced `InvalidOperationException` with `HttpForwardingException` when there is a ForwarderError
10-
- [In-proc] Codeql : Fix to remove exception details from the response (#10751)
11-
- Update PowerShell 7.4 worker to [4.0.4134](https://github.com/Azure/azure-functions-powershell-worker/releases/tag/v4.0.4134)
12-
- Update Python Worker Version to [4.35.0](https://github.com/Azure/azure-functions-python-worker/releases/tag/4.35.0)
13-
- Update domain for CDN URI
14-
- Update PowerShell worker to 4.0.4175 (sets defaultRuntimeVersion to 7.4 in worker.config.json)
6+
7+
- Add support for the release channel setting `WEBSITE_PlatformReleaseChannel` and use this value in extension bundles resolution.
8+
- Bug fix for platform release channel bundles resolution casing issue and additional logging (#10921)
9+
- Update PowerShell worker to [4.0.4206](https://github.com/Azure/azure-functions-powershell-worker/releases/tag/v4.0.4206)

‎src/Directory.Version.props

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
<Project>
22
<PropertyGroup>
3-
<VersionPrefix>4.$(MinorVersionPrefix)38.100</VersionPrefix>
3+
<VersionPrefix>4.$(MinorVersionPrefix)38.200</VersionPrefix>
44
<UpdateBuildNumber>true</UpdateBuildNumber>
55
</PropertyGroup>
66
</Project>

‎src/WebJobs.Script/Environment/EnvironmentSettingNames.cs

+3
Original file line numberDiff line numberDiff line change
@@ -141,6 +141,9 @@ public static class EnvironmentSettingNames
141141
public const string AntaresPlatformVersionWindows = "WEBSITE_PLATFORM_VERSION";
142142
public const string AntaresPlatformVersionLinux = "PLATFORM_VERSION";
143143

144+
// Antares Release Channel / RU Feed Settings
145+
public const string AntaresPlatformReleaseChannel = "WEBSITE_PlatformReleaseChannel";
146+
144147
// Machine identifier
145148
public const string AntaresComputerName = "COMPUTERNAME";
146149

‎src/WebJobs.Script/ExtensionBundle/ExtensionBundleManager.cs

+51-6
Original file line numberDiff line numberDiff line change
@@ -5,10 +5,8 @@
55
using System.Collections.Generic;
66
using System.IO;
77
using System.IO.Compression;
8-
using System.IO.Pipes;
98
using System.Linq;
109
using System.Net.Http;
11-
using System.Resources;
1210
using System.Threading.Tasks;
1311
using Microsoft.Azure.WebJobs.Script.Config;
1412
using Microsoft.Azure.WebJobs.Script.Configuration;
@@ -27,15 +25,17 @@ public class ExtensionBundleManager : IExtensionBundleManager
2725
private readonly FunctionsHostingConfigOptions _configOption;
2826
private readonly ILogger _logger;
2927
private readonly string _cdnUri;
28+
private readonly string _platformReleaseChannel;
3029
private string _extensionBundleVersion;
3130

3231
public ExtensionBundleManager(ExtensionBundleOptions options, IEnvironment environment, ILoggerFactory loggerFactory, FunctionsHostingConfigOptions configOption)
3332
{
3433
_environment = environment ?? throw new ArgumentNullException(nameof(environment));
3534
_logger = loggerFactory.CreateLogger<ExtensionBundleManager>() ?? throw new ArgumentNullException(nameof(loggerFactory));
36-
_cdnUri = _environment.GetEnvironmentVariable(EnvironmentSettingNames.ExtensionBundleSourceUri) ?? ScriptConstants.ExtensionBundleDefaultSourceUri;
3735
_options = options ?? throw new ArgumentNullException(nameof(options));
3836
_configOption = configOption ?? throw new ArgumentNullException(nameof(configOption));
37+
_cdnUri = _environment.GetEnvironmentVariable(EnvironmentSettingNames.ExtensionBundleSourceUri) ?? ScriptConstants.ExtensionBundleDefaultSourceUri;
38+
_platformReleaseChannel = _environment.GetEnvironmentVariable(EnvironmentSettingNames.AntaresPlatformReleaseChannel) ?? ScriptConstants.LatestPlatformChannelNameUpper;
3939
}
4040

4141
public async Task<ExtensionBundleDetails> GetExtensionBundleDetails()
@@ -250,7 +250,7 @@ private async Task<string> GetLatestMatchingBundleVersionAsync(HttpClient httpCl
250250
return matchingBundleVersion;
251251
}
252252

253-
internal static string FindBestVersionMatch(VersionRange versionRange, IEnumerable<string> versions, string bundleId, FunctionsHostingConfigOptions configOption)
253+
internal string FindBestVersionMatch(VersionRange versionRange, IEnumerable<string> versions, string bundleId, FunctionsHostingConfigOptions configOption)
254254
{
255255
var bundleVersions = versions.Select(p =>
256256
{
@@ -261,9 +261,9 @@ internal static string FindBestVersionMatch(VersionRange versionRange, IEnumerab
261261
version = versionRange.Satisfies(version) ? version : null;
262262
}
263263
return version;
264-
}).Where(v => v != null);
264+
}).Where(v => v != null).OrderByDescending(version => version.Version).ToList();
265265

266-
var matchingVersion = bundleVersions.OrderByDescending(version => version.Version).FirstOrDefault();
266+
var matchingVersion = ResolvePlatformReleaseChannelVersion(bundleVersions);
267267

268268
if (bundleId != ScriptConstants.DefaultExtensionBundleId)
269269
{
@@ -293,6 +293,51 @@ internal static string FindBestVersionMatch(VersionRange versionRange, IEnumerab
293293
return matchingVersion?.ToString();
294294
}
295295

296+
private NuGetVersion ResolvePlatformReleaseChannelVersion(IList<NuGetVersion> orderedByDescBundles) => _platformReleaseChannel.ToUpper() switch
297+
{
298+
ScriptConstants.StandardPlatformChannelNameUpper or ScriptConstants.ExtendedPlatformChannelNameUpper => GetStandardOrExtendedBundleVersion(orderedByDescBundles),
299+
ScriptConstants.LatestPlatformChannelNameUpper or "" => GetLatestBundleVersion(orderedByDescBundles),
300+
_ => HandleUnknownPlatformReleaseChannelName(orderedByDescBundles)
301+
};
302+
303+
// Standard: Resolves to the version prior to the latest(n-1), if that version is available.
304+
// Extended: Resolves to the version two releases prior to the latest(n-2), if that version is available.
305+
// However, Functions and Rapid Update should treat Standard and Extended the same, resolving to n-1.
306+
private NuGetVersion GetStandardOrExtendedBundleVersion(IList<NuGetVersion> orderedByDescBundlesList)
307+
{
308+
var latest = orderedByDescBundlesList.FirstOrDefault();
309+
310+
if (orderedByDescBundlesList.Count > 1)
311+
{
312+
var previous = orderedByDescBundlesList[1];
313+
_logger.LogInformation("Applying platform release channel configuration {platformReleaseChannelName}. Previous bundle version {previous} will be used instead of latest version {latest}.", _platformReleaseChannel, previous, latest);
314+
315+
// These channels should resolve to the version prior to latest. This list is in descending order, which makes latest [0], and prior-to-latest [1].
316+
return previous;
317+
}
318+
319+
// keep the latest version, log a notice
320+
_logger.LogWarning("Unable to apply platform release channel configuration {platformReleaseChannelName}. Only one matching bundle version is available. {latestBundleVersion} will be used", _platformReleaseChannel, latest);
321+
return latest;
322+
}
323+
324+
private NuGetVersion GetLatestBundleVersion(IList<NuGetVersion> orderedByDescBundlesList)
325+
{
326+
var latest = orderedByDescBundlesList.FirstOrDefault();
327+
if (string.Equals(_platformReleaseChannel.ToUpper(), ScriptConstants.LatestPlatformChannelNameUpper))
328+
{
329+
_logger.LogInformation("Applying platform release channel configuration {platformReleaseChannelName}. Bundle version {latest} will be used", _platformReleaseChannel, latest);
330+
}
331+
return latest;
332+
}
333+
334+
private NuGetVersion HandleUnknownPlatformReleaseChannelName(IList<NuGetVersion> orderedByDescBundlesList)
335+
{
336+
var latest = GetLatestBundleVersion(orderedByDescBundlesList);
337+
_logger.LogWarning("Unknown platform release channel name {platformReleaseChannelName}. The latest bundle version, {latestBundleVersion}, will be used.", _platformReleaseChannel, latest);
338+
return latest;
339+
}
340+
296341
public async Task<string> GetExtensionBundleBinPathAsync()
297342
{
298343
string bundlePath = await GetExtensionBundlePath();

‎src/WebJobs.Script/ScriptConstants.cs

+5
Original file line numberDiff line numberDiff line change
@@ -214,6 +214,11 @@ public static class ScriptConstants
214214
public const string LogicAppDefaultExtensionBundleVersion = "[1.*, 2.0.0)";
215215
public const string DefaultExtensionBundleVersion = "[4.*, 5.0.0)";
216216

217+
// Antares Platform Channel Names, used in extension bundle resolution
218+
public const string LatestPlatformChannelNameUpper = "LATEST";
219+
public const string StandardPlatformChannelNameUpper = "STANDARD";
220+
public const string ExtendedPlatformChannelNameUpper = "EXTENDED";
221+
217222
public const string AzureMonitorTraceCategory = "FunctionAppLogs";
218223

219224
public const string KubernetesManagedAppName = "K8SE_APP_NAME";

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

+129-12
Original file line numberDiff line numberDiff line change
@@ -9,20 +9,14 @@
99
using System.Linq;
1010
using System.Net;
1111
using System.Net.Http;
12-
using System.Reflection;
1312
using System.Runtime.InteropServices;
14-
using System.Text;
1513
using System.Threading;
1614
using System.Threading.Tasks;
1715
using Microsoft.Azure.WebJobs.Script.Config;
1816
using Microsoft.Azure.WebJobs.Script.Configuration;
1917
using Microsoft.Azure.WebJobs.Script.ExtensionBundle;
20-
using Microsoft.Extensions.Configuration;
2118
using Microsoft.Extensions.Logging;
22-
using Microsoft.Extensions.Logging.Abstractions;
23-
using Microsoft.Extensions.Options;
2419
using Moq;
25-
using Newtonsoft.Json;
2620
using NuGet.Versioning;
2721
using Xunit;
2822
using static Microsoft.Azure.WebJobs.Script.EnvironmentSettingNames;
@@ -407,22 +401,125 @@ public void LimitMaxVersion(string versionRange, string hostConfigVersion, strin
407401
}
408402
}
409403

410-
var resolvedVersion = ExtensionBundleManager.FindBestVersionMatch(range, new List<string>()
411-
{ "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" },
412-
ScriptConstants.DefaultExtensionBundleId, hostingConfiguration);
404+
var options = GetTestExtensionBundleOptions(BundleId, versionRange);
405+
var manager = GetExtensionBundleManager(options, GetTestAppServiceEnvironment());
406+
407+
var resolvedVersion = manager.FindBestVersionMatch(range, GetLargeVersionsList(), ScriptConstants.DefaultExtensionBundleId, hostingConfiguration);
408+
409+
Assert.Equal(expectedVersion, resolvedVersion);
410+
}
411+
412+
[Theory]
413+
[InlineData(ScriptConstants.LatestPlatformChannelNameUpper, "[4.*, 5.0.0)", "4.2.0", "4.2.0")]
414+
[InlineData(ScriptConstants.StandardPlatformChannelNameUpper, "[4.*, 5.0.0)", "4.2.0", "4.2.0")]
415+
[InlineData(ScriptConstants.ExtendedPlatformChannelNameUpper, "[4.*, 5.0.0)", "4.2.0", "4.2.0")]
416+
[InlineData(ScriptConstants.StandardPlatformChannelNameUpper, "[4.*, 5.0.0)", "4.3.0", "4.2.0")]
417+
[InlineData(ScriptConstants.ExtendedPlatformChannelNameUpper, "[4.*, 5.0.0)", "4.3.0", "4.2.0")]
418+
[InlineData(ScriptConstants.StandardPlatformChannelNameUpper, "[4.*, 5.0.0)", "4.1.0", "4.1.0")]
419+
[InlineData(ScriptConstants.ExtendedPlatformChannelNameUpper, "[4.*, 5.0.0)", "4.1.0", "4.1.0")]
420+
[InlineData(ScriptConstants.LatestPlatformChannelNameUpper, "[4.*, 5.0.0)", null, "4.3.0")]
421+
[InlineData("latest", "[4.*, 5.0.0)", null, "4.3.0")]
422+
[InlineData(ScriptConstants.StandardPlatformChannelNameUpper, "[4.*, 5.0.0)", null, "4.2.0")]
423+
[InlineData("standard", "[4.*, 5.0.0)", null, "4.2.0")]
424+
[InlineData(ScriptConstants.ExtendedPlatformChannelNameUpper, "[4.*, 5.0.0)", null, "4.2.0")]
425+
[InlineData("extended", "[4.*, 5.0.0)", null, "4.2.0")]
426+
public void WhenPlatformReleaseChannelSet_ExpectedVersionChosen(string platformReleaseChannelName, string versionRange, string hostConfigMaxVersion, string expectedVersion)
427+
{
428+
var range = VersionRange.Parse(versionRange);
429+
var hostingConfiguration = new FunctionsHostingConfigOptions();
430+
431+
if (!string.IsNullOrEmpty(hostConfigMaxVersion))
432+
{
433+
if (range.MinVersion.Major == 3)
434+
{
435+
hostingConfiguration.Features.Add(ScriptConstants.MaximumBundleV3Version, hostConfigMaxVersion);
436+
}
437+
438+
if (range.MinVersion.Major == 4)
439+
{
440+
hostingConfiguration.Features.Add(ScriptConstants.MaximumBundleV4Version, hostConfigMaxVersion);
441+
}
442+
}
443+
444+
var options = GetTestExtensionBundleOptions(BundleId, versionRange);
445+
var testEnvironment = GetTestAppServiceEnvironment(platformReleaseChannelName);
446+
var manager = GetExtensionBundleManager(options, testEnvironment);
447+
448+
var versions = GetLargeVersionsList();
449+
450+
var resolvedVersion = manager.FindBestVersionMatch(range, versions, ScriptConstants.DefaultExtensionBundleId, hostingConfiguration);
413451

414452
Assert.Equal(expectedVersion, resolvedVersion);
415453
}
416454

417-
private ExtensionBundleManager GetExtensionBundleManager(ExtensionBundleOptions bundleOptions, TestEnvironment environment = null)
455+
[Theory]
456+
[InlineData(ScriptConstants.ExtendedPlatformChannelNameUpper)]
457+
[InlineData(ScriptConstants.StandardPlatformChannelNameUpper)]
458+
public void StandardExtendedReleaseChannel_OneBundleVersionOnDisk_Handled(string platformReleaseChannelName)
459+
{
460+
// these release channels take the version prior to the latest version
461+
// however, if there is only one bundle version available on disk, that bundle should be chosen and information logged
462+
463+
var versions = new List<string>() { "4.20.0" }; // only one bundle version available on disk
464+
var versionRange = "[4.*, 5.0.0)";
465+
var expected = "4.20.0";
466+
467+
var loggedString = $"Unable to apply platform release channel configuration {platformReleaseChannelName}. Only one matching bundle version is available. {expected} will be used";
468+
var mockLogger = GetVerifiableMockLogger(loggedString, LogLevel.Warning);
469+
var mockLoggerFactory = new Mock<ILoggerFactory>();
470+
mockLoggerFactory.Setup(f => f.CreateLogger(It.IsAny<string>())).Returns(() => mockLogger.Object);
471+
472+
var options = GetTestExtensionBundleOptions(BundleId, versionRange);
473+
var testEnvironment = GetTestAppServiceEnvironment(platformReleaseChannelName);
474+
var manager = GetExtensionBundleManager(options, testEnvironment, mockLoggerFactory);
475+
476+
var resolvedVersion = manager.FindBestVersionMatch(VersionRange.Parse(versionRange), versions, ScriptConstants.DefaultExtensionBundleId, new FunctionsHostingConfigOptions());
477+
478+
Assert.Equal(expected, resolvedVersion);
479+
mockLogger.Verify();
480+
}
481+
482+
[Fact]
483+
public void UnknownReleaseChannel_ExpectedVersionChosen()
484+
{
485+
var versions = new List<string>() { "4.20.0" };
486+
var versionRange = "[4.*, 5.0.0)";
487+
var expected = "4.20.0";
488+
489+
var incorrectChannelName = "someIncorrectReleaseChannelName";
490+
var loggedString = $"Unknown platform release channel name {incorrectChannelName}. The latest bundle version, {expected}, will be used.";
491+
var mockLogger = GetVerifiableMockLogger(loggedString, LogLevel.Warning);
492+
var mockLoggerFactory = new Mock<ILoggerFactory>();
493+
mockLoggerFactory.Setup(f => f.CreateLogger(It.IsAny<string>())).Returns(() => mockLogger.Object);
494+
495+
var options = GetTestExtensionBundleOptions(BundleId, versionRange);
496+
var testEnvironment = GetTestAppServiceEnvironment(incorrectChannelName);
497+
var manager = GetExtensionBundleManager(options, testEnvironment, mockLoggerFactory);
498+
499+
var resolvedVersion = manager.FindBestVersionMatch(VersionRange.Parse(versionRange), versions, ScriptConstants.DefaultExtensionBundleId, new FunctionsHostingConfigOptions());
500+
501+
Assert.Equal(expected, resolvedVersion); // unknown release channel should default to latest and log information
502+
mockLogger.Verify();
503+
}
504+
505+
private ExtensionBundleManager GetExtensionBundleManager(ExtensionBundleOptions bundleOptions, TestEnvironment environment = null, Mock<ILoggerFactory> mockLoggerFactory = null)
418506
{
419507
environment = environment ?? new TestEnvironment();
420-
return new ExtensionBundleManager(bundleOptions, environment, MockNullLoggerFactory.CreateLoggerFactory(), new FunctionsHostingConfigOptions());
508+
509+
if (mockLoggerFactory is null)
510+
{
511+
return new ExtensionBundleManager(bundleOptions, environment, MockNullLoggerFactory.CreateLoggerFactory(), new FunctionsHostingConfigOptions());
512+
}
513+
else
514+
{
515+
return new ExtensionBundleManager(bundleOptions, environment, mockLoggerFactory.Object, new FunctionsHostingConfigOptions());
516+
}
421517
}
422518

423-
private TestEnvironment GetTestAppServiceEnvironment()
519+
private TestEnvironment GetTestAppServiceEnvironment(string platformReleaseChannel = null)
424520
{
425521
var environment = new TestEnvironment();
522+
environment.SetEnvironmentVariable(AntaresPlatformReleaseChannel, platformReleaseChannel);
426523
environment.SetEnvironmentVariable(AzureWebsiteInstanceId, Guid.NewGuid().ToString("N"));
427524
string downloadPath = string.Empty;
428525
if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
@@ -473,6 +570,26 @@ public void Dispose()
473570
FileUtility.Instance = null;
474571
}
475572

573+
private IList<string> GetLargeVersionsList()
574+
{
575+
return new List<string>()
576+
{ "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" };
577+
}
578+
579+
private Mock<ILogger> GetVerifiableMockLogger(string stringToVerify, LogLevel logLevel)
580+
{
581+
var mockLogger = new Mock<ILogger>();
582+
mockLogger
583+
.Setup(x => x.Log(
584+
logLevel,
585+
It.IsAny<EventId>(),
586+
It.Is<It.IsAnyType>((v, t) => v.ToString().Contains(stringToVerify)),
587+
It.IsAny<Exception>(),
588+
It.IsAny<Func<It.IsAnyType, Exception, string>>()))
589+
.Verifiable();
590+
return mockLogger;
591+
}
592+
476593
private class MockHttpHandler : HttpClientHandler
477594
{
478595
private readonly string _version;

0 commit comments

Comments
 (0)
Please sign in to comment.