Skip to content

Shut off MD module upgrades after 7.4 EOL #1084

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 7 commits into from
Sep 25, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions create_new_worker_instructions.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
## Instructions for Upgrading the PowerShell Language Worker to a New PowerShell SDK Minor Version (7.6+)
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:

- Update the solution targets as needed for whatever .NET version is targeted by the new PowerShell
- Follow instructions in upgrade_ps_sdk_instructions.md to update loosely linked dependencies in project files
- Update the Managed Dependency shutoff date in src/DependencyManagement/WorkerEnvironment.cs
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@ internal class BackgroundDependencySnapshotMaintainer : IBackgroundDependencySna
private TimeSpan MaxBackgroundUpgradePeriod { get; } =
PowerShellWorkerConfiguration.GetTimeSpan("MDMaxBackgroundUpgradePeriod") ?? TimeSpan.FromDays(7);

private Func<bool> _getShouldPerformManagedDependencyUpgrades;

private readonly IDependencyManagerStorage _storage;
private readonly IDependencySnapshotInstaller _installer;
private readonly IDependencySnapshotPurger _purger;
Expand All @@ -29,11 +31,14 @@ internal class BackgroundDependencySnapshotMaintainer : IBackgroundDependencySna
public BackgroundDependencySnapshotMaintainer(
IDependencyManagerStorage storage,
IDependencySnapshotInstaller installer,
IDependencySnapshotPurger purger)
IDependencySnapshotPurger purger,
Func<bool> getShouldPerformManagedDependencyUpgrades)
{
_storage = storage ?? throw new ArgumentNullException(nameof(storage));
_installer = installer ?? throw new ArgumentNullException(nameof(installer));
_purger = purger ?? throw new ArgumentNullException(nameof(purger));
_getShouldPerformManagedDependencyUpgrades = getShouldPerformManagedDependencyUpgrades;

}

public void Start(string currentSnapshotPath, DependencyManifestEntry[] dependencyManifest, ILogger logger)
Expand All @@ -56,6 +61,23 @@ public string InstallAndPurgeSnapshots(Func<PowerShell> pwshFactory, ILogger log
{
try
{
if (!_getShouldPerformManagedDependencyUpgrades())
{
logger.Log(
isUserOnlyLog: false,
RpcLog.Types.Level.Warning,
PowerShellWorkerStrings.AutomaticUpgradesAreDisabled);

// Shutdown the timer that calls this method after the EOL date
if (_installAndPurgeTimer is not null)
{
_installAndPurgeTimer.Dispose();
_installAndPurgeTimer = null;
}

return null;
}

// Purge before installing a new snapshot, as we may be able to free some space.
_purger.Purge(logger);

Expand Down
18 changes: 17 additions & 1 deletion src/DependencyManagement/DependencyManager.cs
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,9 @@ internal class DependencyManager : IDisposable

private Task _dependencyInstallationTask;

private bool EnableAutomaticUpgrades { get; } =
PowerShellWorkerConfiguration.GetBoolean("MDEnableAutomaticUpgrades") ?? false;

#endregion

public DependencyManager(
Expand All @@ -67,7 +70,8 @@ public DependencyManager(
maintainer ?? new BackgroundDependencySnapshotMaintainer(
_storage,
_installer,
new DependencySnapshotPurger(_storage));
new DependencySnapshotPurger(_storage),
ShouldEnableManagedDpendencyUpgrades);
_currentSnapshotContentLogger =
currentSnapshotContentLogger ?? new BackgroundDependencySnapshotContentLogger(snapshotContentLogger);
}
Expand Down Expand Up @@ -126,6 +130,18 @@ internal string Initialize(ILogger logger)
}
}

/// <summary>
/// Determines whether the function app should enable automatic upgrades for managed dependencies
/// </summary>
/// <returns>
/// 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
/// False if Managed Dependencies should not be upgraded
/// </returns>
private bool ShouldEnableManagedDpendencyUpgrades()
{
return !WorkerEnvironment.IsPowerShellSDKDeprecated() || EnableAutomaticUpgrades;
}

/// <summary>
/// Start dependency installation if needed.
/// firstPowerShell is the first PowerShell instance created in this process (which this is important for local debugging),
Expand Down
7 changes: 7 additions & 0 deletions src/DependencyManagement/WorkerEnvironment.cs
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@ internal static class WorkerEnvironment
private const string ContainerName = "CONTAINER_NAME";
private const string LegionServiceHost = "LEGION_SERVICE_HOST";

private static readonly DateTime PowerShellSDKDeprecationDate = new DateTime(2026, 11, 10);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We'll need to remember to keep updating this in the future. Perhaps we should add a reminder item to https://github.com/Azure/azure-functions-powershell-worker/blob/dev/upgrade_ps_sdk_instructions.md

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added create_new_worker_instructions.md to document steps that must be taken when updating to a new PS 7 minor version (like 7.6).
This file includes a note to update the Managed Dependencies shutoff date. It is likely we will find many new checklist items for this file as we start the 7.6 worker next year.


public static bool IsAppService()
{
return !string.IsNullOrEmpty(Environment.GetEnvironmentVariable(AzureWebsiteInstanceId));
Expand All @@ -32,5 +34,10 @@ public static bool IsLinuxConsumptionOnLegion()
!string.IsNullOrEmpty(Environment.GetEnvironmentVariable(ContainerName)) &&
!string.IsNullOrEmpty(Environment.GetEnvironmentVariable(LegionServiceHost));
}

public static bool IsPowerShellSDKDeprecated()
{
return DateTime.Now > PowerShellSDKDeprecationDate;
}
}
}
9 changes: 6 additions & 3 deletions src/resources/PowerShellWorkerStrings.resx
Original file line number Diff line number Diff line change
Expand Up @@ -352,6 +352,9 @@
<data name="DependencySnapshotDoesNotContainAcceptableModuleVersions" xml:space="preserve">
<value>Dependency snapshot '{0}' does not contain acceptable module versions.</value>
</data>
<data name="WorkerInitCompleted" xml:space="preserve">
<value>Worker init request completed in {0} ms.</value>
</data>
<data name="FoundExternalDurableSdkInSession" xml:space="preserve">
<value>Found external Durable Functions SDK in session: Name='{0}', Version='{1}', Path='{2}'.</value>
</data>
Expand All @@ -361,9 +364,6 @@
<data name="UnableToInitializeOrchestrator" xml:space="preserve">
<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>
</data>
<data name="WorkerInitCompleted" xml:space="preserve">
<value>Worker init request completed in {0} ms.</value>
</data>
<data name="UnexpectedResultCount" xml:space="preserve">
<value>Operation '{0}' expected '{1}' result(s) but received '{2}'.</value>
</data>
Expand All @@ -388,4 +388,7 @@
<data name="InvalidOpenTelemetryContext" xml:space="preserve">
<value>The app is configured to use OpenTelemetry but the TraceContext passed from host was null. </value>
</data>
<data name="AutomaticUpgradesAreDisabled" xml:space="preserve">
<value>Automatic upgrades are disabled in PowerShell 7.4 function apps. This warning should not be emitted until PowerShell 7.4's End of Life date, at which time, more guidance will be available regarding how to upgrade your function app to the latest version. </value>
</data>
</root>
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,8 @@ namespace Microsoft.Azure.Functions.PowerShellWorker.Test.DependencyManagement

using Microsoft.Azure.Functions.PowerShellWorker.DependencyManagement;
using Microsoft.Azure.Functions.PowerShellWorker.Utility;

using LogLevel = Microsoft.Azure.WebJobs.Script.Grpc.Messages.RpcLog.Types.Level;
using Microsoft.Azure.WebJobs.Script.Grpc.Messages;

public class BackgroundDependencySnapshotMaintainerTests
{
Expand All @@ -30,7 +30,7 @@ public class BackgroundDependencySnapshotMaintainerTests
[Fact]
public void SetsCurrentlyUsedSnapshotOnPurger()
{
using (var maintainer = CreateMaintainerWithMocks())
using (var maintainer = CreateMaintainerWithMocks(true))
{
maintainer.Start("current snapshot", _dependencyManifest, _mockLogger.Object);
}
Expand All @@ -56,7 +56,7 @@ public void InstallsSnapshotIfNoRecentlyInstalledSnapshotFound()
It.IsAny<ILogger>()));

using (var dummyPowerShell = PowerShell.Create())
using (var maintainer = CreateMaintainerWithMocks(_minBackgroundUpgradePeriod))
using (var maintainer = CreateMaintainerWithMocks(true, _minBackgroundUpgradePeriod))
{
maintainer.Start("current snapshot", _dependencyManifest, _mockLogger.Object);

Expand All @@ -73,14 +73,49 @@ public void InstallsSnapshotIfNoRecentlyInstalledSnapshotFound()
}
}

[Fact]
public void DoesNothingIfManagedDependenciesUpgradesAreDisabled()
{
_mockStorage.Setup(_ => _.GetInstalledAndInstallingSnapshots()).Returns(new[] { "older snapshot" });
_mockStorage.Setup(_ => _.GetSnapshotCreationTimeUtc("older snapshot"))
.Returns(DateTime.UtcNow - _minBackgroundUpgradePeriod - TimeSpan.FromSeconds(1));

_mockStorage.Setup(_ => _.CreateNewSnapshotPath()).Returns("new snapshot path");

_mockInstaller.Setup(
_ => _.InstallSnapshot(
It.IsAny<DependencyManifestEntry[]>(),
It.IsAny<string>(),
It.IsAny<PowerShell>(),
It.IsAny<DependencySnapshotInstallationMode>(),
It.IsAny<ILogger>()));

using (var dummyPowerShell = PowerShell.Create())
using (var maintainer = CreateMaintainerWithMocks(false, _minBackgroundUpgradePeriod))
{
maintainer.Start("current snapshot", _dependencyManifest, _mockLogger.Object);

// ReSharper disable once AccessToDisposedClosure
var installedSnapshotPath = maintainer.InstallAndPurgeSnapshots(() => dummyPowerShell, _mockLogger.Object);
Assert.Equal(null, installedSnapshotPath);

// ReSharper disable once AccessToDisposedClosure
_mockInstaller.Verify(
_ => _.InstallSnapshot(_dependencyManifest, "new snapshot path", dummyPowerShell, DependencySnapshotInstallationMode.Optional, _mockLogger.Object),
Times.Never);

_mockLogger.Verify(_ => _.Log(false, LogLevel.Warning, PowerShellWorkerStrings.AutomaticUpgradesAreDisabled, null), Times.Once);
}
}

[Fact]
public void DoesNotInstallSnapshotIfRecentlyInstalledSnapshotFound()
{
_mockStorage.Setup(_ => _.GetInstalledAndInstallingSnapshots()).Returns(new[] { "older snapshot" });
_mockStorage.Setup(_ => _.GetSnapshotCreationTimeUtc("older snapshot"))
.Returns(DateTime.UtcNow - _minBackgroundUpgradePeriod + TimeSpan.FromSeconds(1));

using (var maintainer = CreateMaintainerWithMocks(_minBackgroundUpgradePeriod))
using (var maintainer = CreateMaintainerWithMocks(true, _minBackgroundUpgradePeriod))
{
maintainer.Start("current snapshot", _dependencyManifest, _mockLogger.Object);

Expand Down Expand Up @@ -112,7 +147,7 @@ public void LogsWarningIfCannotInstallSnapshot()
.Throws(injectedException);

using (var dummyPowerShell = PowerShell.Create())
using (var maintainer = CreateMaintainerWithMocks(_minBackgroundUpgradePeriod))
using (var maintainer = CreateMaintainerWithMocks(true, _minBackgroundUpgradePeriod))
{
maintainer.Start("current snapshot", _dependencyManifest, _mockLogger.Object);

Expand All @@ -129,12 +164,13 @@ public void LogsWarningIfCannotInstallSnapshot()
Times.Once);
}

private BackgroundDependencySnapshotMaintainer CreateMaintainerWithMocks(TimeSpan? minBackgroundUpgradePeriod = null)
private BackgroundDependencySnapshotMaintainer CreateMaintainerWithMocks(bool shouldPerformManagedDependenciesUpgrades, TimeSpan? minBackgroundUpgradePeriod = null)
{
var maintainer = new BackgroundDependencySnapshotMaintainer(
_mockStorage.Object,
_mockInstaller.Object,
_mockPurger.Object);
_mockPurger.Object,
() => { return shouldPerformManagedDependenciesUpgrades; });

if (minBackgroundUpgradePeriod != null)
{
Expand Down