Skip to content

Commit 1658352

Browse files
authored
Shutoff Managed Dependency upgrades after 7.2 EOL (#1091)
* Backport of "Shut off MD module upgrades after 7.2 EOL" (#1084) * 7.2 specific changes
1 parent 19046d6 commit 1658352

File tree

6 files changed

+102
-12
lines changed

6 files changed

+102
-12
lines changed

create_new_worker_instructions.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
## Instructions for Upgrading the PowerShell Language Worker to a New PowerShell SDK Minor Version (7.6+)
2+
Once a new PowerShell SDK version is released on [GiHub](https://github.com/PowerShell/PowerShell/releases), follow these steps to upgrade the PowerShell SDK reference used by the language worker:
3+
4+
- Update the solution targets as needed for whatever .NET version is targeted by the new PowerShell
5+
- Follow instructions in upgrade_ps_sdk_instructions.md to update loosely linked dependencies in project files
6+
- Update the Managed Dependency shutoff date in src/DependencyManagement/WorkerEnvironment.cs

src/DependencyManagement/BackgroundDependencySnapshotMaintainer.cs

Lines changed: 23 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,8 @@ internal class BackgroundDependencySnapshotMaintainer : IBackgroundDependencySna
2020
private TimeSpan MaxBackgroundUpgradePeriod { get; } =
2121
PowerShellWorkerConfiguration.GetTimeSpan("MDMaxBackgroundUpgradePeriod") ?? TimeSpan.FromDays(7);
2222

23+
private Func<bool> _getShouldPerformManagedDependencyUpgrades;
24+
2325
private readonly IDependencyManagerStorage _storage;
2426
private readonly IDependencySnapshotInstaller _installer;
2527
private readonly IDependencySnapshotPurger _purger;
@@ -29,11 +31,14 @@ internal class BackgroundDependencySnapshotMaintainer : IBackgroundDependencySna
2931
public BackgroundDependencySnapshotMaintainer(
3032
IDependencyManagerStorage storage,
3133
IDependencySnapshotInstaller installer,
32-
IDependencySnapshotPurger purger)
34+
IDependencySnapshotPurger purger,
35+
Func<bool> getShouldPerformManagedDependencyUpgrades)
3336
{
3437
_storage = storage ?? throw new ArgumentNullException(nameof(storage));
3538
_installer = installer ?? throw new ArgumentNullException(nameof(installer));
3639
_purger = purger ?? throw new ArgumentNullException(nameof(purger));
40+
_getShouldPerformManagedDependencyUpgrades = getShouldPerformManagedDependencyUpgrades;
41+
3742
}
3843

3944
public void Start(string currentSnapshotPath, DependencyManifestEntry[] dependencyManifest, ILogger logger)
@@ -56,6 +61,23 @@ public string InstallAndPurgeSnapshots(Func<PowerShell> pwshFactory, ILogger log
5661
{
5762
try
5863
{
64+
if (!_getShouldPerformManagedDependencyUpgrades())
65+
{
66+
logger.Log(
67+
isUserOnlyLog: false,
68+
RpcLog.Types.Level.Warning,
69+
PowerShellWorkerStrings.AutomaticUpgradesAreDisabled);
70+
71+
// Shutdown the timer that calls this method after the EOL date
72+
if (_installAndPurgeTimer is not null)
73+
{
74+
_installAndPurgeTimer.Dispose();
75+
_installAndPurgeTimer = null;
76+
}
77+
78+
return null;
79+
}
80+
5981
// Purge before installing a new snapshot, as we may be able to free some space.
6082
_purger.Purge(logger);
6183

src/DependencyManagement/DependencyManager.cs

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,9 @@ internal class DependencyManager : IDisposable
4141

4242
private Task _dependencyInstallationTask;
4343

44+
private bool EnableAutomaticUpgrades { get; } =
45+
PowerShellWorkerConfiguration.GetBoolean("MDEnableAutomaticUpgrades") ?? false;
46+
4447
#endregion
4548

4649
public DependencyManager(
@@ -67,7 +70,8 @@ public DependencyManager(
6770
maintainer ?? new BackgroundDependencySnapshotMaintainer(
6871
_storage,
6972
_installer,
70-
new DependencySnapshotPurger(_storage));
73+
new DependencySnapshotPurger(_storage),
74+
ShouldEnableManagedDpendencyUpgrades);
7175
_currentSnapshotContentLogger =
7276
currentSnapshotContentLogger ?? new BackgroundDependencySnapshotContentLogger(snapshotContentLogger);
7377
}
@@ -126,6 +130,18 @@ internal string Initialize(ILogger logger)
126130
}
127131
}
128132

133+
/// <summary>
134+
/// Determines whether the function app should enable automatic upgrades for managed dependencies
135+
/// </summary>
136+
/// <returns>
137+
/// True if Managed Dependencies should be upgraded (SDK is not past it's deprecation date OR user has configured this behavior via MDEnableAutomaticUpgrades env var
138+
/// False if Managed Dependencies should not be upgraded
139+
/// </returns>
140+
private bool ShouldEnableManagedDpendencyUpgrades()
141+
{
142+
return !WorkerEnvironment.IsPowerShellSDKDeprecated() || EnableAutomaticUpgrades;
143+
}
144+
129145
/// <summary>
130146
/// Start dependency installation if needed.
131147
/// firstPowerShell is the first PowerShell instance created in this process (which this is important for local debugging),

src/DependencyManagement/WorkerEnvironment.cs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,8 @@ internal static class WorkerEnvironment
1616
private const string ContainerName = "CONTAINER_NAME";
1717
private const string LegionServiceHost = "LEGION_SERVICE_HOST";
1818

19+
private static readonly DateTime PowerShellSDKDeprecationDate = new DateTime(2024, 11, 8);
20+
1921
public static bool IsAppService()
2022
{
2123
return !string.IsNullOrEmpty(Environment.GetEnvironmentVariable(AzureWebsiteInstanceId));
@@ -32,5 +34,10 @@ public static bool IsLinuxConsumptionOnLegion()
3234
!string.IsNullOrEmpty(Environment.GetEnvironmentVariable(ContainerName)) &&
3335
!string.IsNullOrEmpty(Environment.GetEnvironmentVariable(LegionServiceHost));
3436
}
37+
38+
public static bool IsPowerShellSDKDeprecated()
39+
{
40+
return DateTime.Now > PowerShellSDKDeprecationDate;
41+
}
3542
}
3643
}

src/resources/PowerShellWorkerStrings.resx

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -352,6 +352,9 @@
352352
<data name="DependencySnapshotDoesNotContainAcceptableModuleVersions" xml:space="preserve">
353353
<value>Dependency snapshot '{0}' does not contain acceptable module versions.</value>
354354
</data>
355+
<data name="WorkerInitCompleted" xml:space="preserve">
356+
<value>Worker init request completed in {0} ms.</value>
357+
</data>
355358
<data name="FoundExternalDurableSdkInSession" xml:space="preserve">
356359
<value>Found external Durable Functions SDK in session: Name='{0}', Version='{1}', Path='{2}'.</value>
357360
</data>
@@ -361,9 +364,6 @@
361364
<data name="UnableToInitializeOrchestrator" xml:space="preserve">
362365
<value>Unable to initialize orchestrator function due to presence of other bindings. Total number of bindings found is '{0}'. Orchestrator Functions should never use any input or output bindings other than the orchestration trigger itself. See: aka.ms/df-bindings</value>
363366
</data>
364-
<data name="WorkerInitCompleted" xml:space="preserve">
365-
<value>Worker init request completed in {0} ms.</value>
366-
</data>
367367
<data name="UnexpectedResultCount" xml:space="preserve">
368368
<value>Operation '{0}' expected '{1}' result(s) but received '{2}'.</value>
369369
</data>
@@ -388,4 +388,7 @@
388388
<data name="InvalidOpenTelemetryContext" xml:space="preserve">
389389
<value>The app is configured to use OpenTelemetry but the TraceContext passed from host was null. </value>
390390
</data>
391+
<data name="AutomaticUpgradesAreDisabled" xml:space="preserve">
392+
<value>Automatic upgrades are disabled in PowerShell 7.2 function apps. This warning should not be emitted until PowerShell 7.2's End of Life date. For more information, please see https://azure.microsoft.com/en-us/updates/v2/powershell72-azure-functions-retirement</value>
393+
</data>
391394
</root>

test/Unit/DependencyManagement/BackgroundDependencySnapshotMaintainerTests.cs

Lines changed: 43 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -13,8 +13,8 @@ namespace Microsoft.Azure.Functions.PowerShellWorker.Test.DependencyManagement
1313

1414
using Microsoft.Azure.Functions.PowerShellWorker.DependencyManagement;
1515
using Microsoft.Azure.Functions.PowerShellWorker.Utility;
16-
1716
using LogLevel = Microsoft.Azure.WebJobs.Script.Grpc.Messages.RpcLog.Types.Level;
17+
using Microsoft.Azure.WebJobs.Script.Grpc.Messages;
1818

1919
public class BackgroundDependencySnapshotMaintainerTests
2020
{
@@ -30,7 +30,7 @@ public class BackgroundDependencySnapshotMaintainerTests
3030
[Fact]
3131
public void SetsCurrentlyUsedSnapshotOnPurger()
3232
{
33-
using (var maintainer = CreateMaintainerWithMocks())
33+
using (var maintainer = CreateMaintainerWithMocks(true))
3434
{
3535
maintainer.Start("current snapshot", _dependencyManifest, _mockLogger.Object);
3636
}
@@ -56,7 +56,7 @@ public void InstallsSnapshotIfNoRecentlyInstalledSnapshotFound()
5656
It.IsAny<ILogger>()));
5757

5858
using (var dummyPowerShell = PowerShell.Create())
59-
using (var maintainer = CreateMaintainerWithMocks(_minBackgroundUpgradePeriod))
59+
using (var maintainer = CreateMaintainerWithMocks(true, _minBackgroundUpgradePeriod))
6060
{
6161
maintainer.Start("current snapshot", _dependencyManifest, _mockLogger.Object);
6262

@@ -73,14 +73,49 @@ public void InstallsSnapshotIfNoRecentlyInstalledSnapshotFound()
7373
}
7474
}
7575

76+
[Fact]
77+
public void DoesNothingIfManagedDependenciesUpgradesAreDisabled()
78+
{
79+
_mockStorage.Setup(_ => _.GetInstalledAndInstallingSnapshots()).Returns(new[] { "older snapshot" });
80+
_mockStorage.Setup(_ => _.GetSnapshotCreationTimeUtc("older snapshot"))
81+
.Returns(DateTime.UtcNow - _minBackgroundUpgradePeriod - TimeSpan.FromSeconds(1));
82+
83+
_mockStorage.Setup(_ => _.CreateNewSnapshotPath()).Returns("new snapshot path");
84+
85+
_mockInstaller.Setup(
86+
_ => _.InstallSnapshot(
87+
It.IsAny<DependencyManifestEntry[]>(),
88+
It.IsAny<string>(),
89+
It.IsAny<PowerShell>(),
90+
It.IsAny<DependencySnapshotInstallationMode>(),
91+
It.IsAny<ILogger>()));
92+
93+
using (var dummyPowerShell = PowerShell.Create())
94+
using (var maintainer = CreateMaintainerWithMocks(false, _minBackgroundUpgradePeriod))
95+
{
96+
maintainer.Start("current snapshot", _dependencyManifest, _mockLogger.Object);
97+
98+
// ReSharper disable once AccessToDisposedClosure
99+
var installedSnapshotPath = maintainer.InstallAndPurgeSnapshots(() => dummyPowerShell, _mockLogger.Object);
100+
Assert.Equal(null, installedSnapshotPath);
101+
102+
// ReSharper disable once AccessToDisposedClosure
103+
_mockInstaller.Verify(
104+
_ => _.InstallSnapshot(_dependencyManifest, "new snapshot path", dummyPowerShell, DependencySnapshotInstallationMode.Optional, _mockLogger.Object),
105+
Times.Never);
106+
107+
_mockLogger.Verify(_ => _.Log(false, LogLevel.Warning, PowerShellWorkerStrings.AutomaticUpgradesAreDisabled, null), Times.Once);
108+
}
109+
}
110+
76111
[Fact]
77112
public void DoesNotInstallSnapshotIfRecentlyInstalledSnapshotFound()
78113
{
79114
_mockStorage.Setup(_ => _.GetInstalledAndInstallingSnapshots()).Returns(new[] { "older snapshot" });
80115
_mockStorage.Setup(_ => _.GetSnapshotCreationTimeUtc("older snapshot"))
81116
.Returns(DateTime.UtcNow - _minBackgroundUpgradePeriod + TimeSpan.FromSeconds(1));
82117

83-
using (var maintainer = CreateMaintainerWithMocks(_minBackgroundUpgradePeriod))
118+
using (var maintainer = CreateMaintainerWithMocks(true, _minBackgroundUpgradePeriod))
84119
{
85120
maintainer.Start("current snapshot", _dependencyManifest, _mockLogger.Object);
86121

@@ -112,7 +147,7 @@ public void LogsWarningIfCannotInstallSnapshot()
112147
.Throws(injectedException);
113148

114149
using (var dummyPowerShell = PowerShell.Create())
115-
using (var maintainer = CreateMaintainerWithMocks(_minBackgroundUpgradePeriod))
150+
using (var maintainer = CreateMaintainerWithMocks(true, _minBackgroundUpgradePeriod))
116151
{
117152
maintainer.Start("current snapshot", _dependencyManifest, _mockLogger.Object);
118153

@@ -129,12 +164,13 @@ public void LogsWarningIfCannotInstallSnapshot()
129164
Times.Once);
130165
}
131166

132-
private BackgroundDependencySnapshotMaintainer CreateMaintainerWithMocks(TimeSpan? minBackgroundUpgradePeriod = null)
167+
private BackgroundDependencySnapshotMaintainer CreateMaintainerWithMocks(bool shouldPerformManagedDependenciesUpgrades, TimeSpan? minBackgroundUpgradePeriod = null)
133168
{
134169
var maintainer = new BackgroundDependencySnapshotMaintainer(
135170
_mockStorage.Object,
136171
_mockInstaller.Object,
137-
_mockPurger.Object);
172+
_mockPurger.Object,
173+
() => { return shouldPerformManagedDependenciesUpgrades; });
138174

139175
if (minBackgroundUpgradePeriod != null)
140176
{

0 commit comments

Comments
 (0)