Skip to content
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
13 changes: 13 additions & 0 deletions src/DependencyManagement/BackgroundDependencySnapshotMaintainer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,9 @@ internal class BackgroundDependencySnapshotMaintainer : IBackgroundDependencySna
private TimeSpan MaxBackgroundUpgradePeriod { get; } =
PowerShellWorkerConfiguration.GetTimeSpan("MDMaxBackgroundUpgradePeriod") ?? TimeSpan.FromDays(7);

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

private readonly IDependencyManagerStorage _storage;
private readonly IDependencySnapshotInstaller _installer;
private readonly IDependencySnapshotPurger _purger;
Expand All @@ -42,6 +45,16 @@ public void Start(string currentSnapshotPath, DependencyManifestEntry[] dependen

_purger.SetCurrentlyUsedSnapshot(currentSnapshotPath, logger);

if (!EnableAutomaticUpgrades)
{
logger.Log(
isUserOnlyLog: false,
RpcLog.Types.Level.Warning,
PowerShellWorkerStrings.AutomaticUpgradesAreDisabled);

return;
}

_installAndPurgeTimer = new Timer(
_ => InstallAndPurgeSnapshots(PowerShell.Create, logger),
state: null,
Expand Down
13 changes: 13 additions & 0 deletions 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 Down Expand Up @@ -199,6 +202,16 @@ internal Exception InstallFunctionAppDependencies(PowerShell firstPwsh, Func<Pow
RpcLog.Types.Level.Trace,
PowerShellWorkerStrings.AcceptableFunctionAppDependenciesAlreadyInstalled);

if (!EnableAutomaticUpgrades)
{
logger.Log(
isUserOnlyLog: false,
RpcLog.Types.Level.Warning,
PowerShellWorkerStrings.AutomaticUpgradesAreDisabled);

return null;
}

// Background installation: can't use the firstPwsh runspace because it belongs
// to the pool used to run functions code, so create a new runspace.
_nextSnapshotPath = _backgroundSnapshotMaintainer.InstallAndPurgeSnapshots(pwshFactory, logger);
Expand Down
3 changes: 3 additions & 0 deletions src/resources/PowerShellWorkerStrings.resx
Original file line number Diff line number Diff line change
Expand Up @@ -352,4 +352,7 @@
<data name="DependencySnapshotDoesNotContainAcceptableModuleVersions" xml:space="preserve">
<value>Dependency snapshot '{0}' does not contain acceptable module versions.</value>
</data>
<data name="AutomaticUpgradesAreDisabled" xml:space="preserve">
<value>Automatic upgrades are disabled in PowerShell 7.0 function apps. To enable this functionality back, please migrate your function app to PowerShell 7.2. For more details, see https://aka.ms/functions-powershell-7.0-to-7.2.</value>
</data>
</root>
61 changes: 35 additions & 26 deletions test/Unit/DependencyManagement/DependencyManagerTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -163,40 +163,49 @@ public void StartDependencyInstallationIfNeeded_InstallsSnapshotInForeground_Whe
[Fact]
public void StartDependencyInstallationIfNeeded_InvokesBackgroundMaintainer_WhenAcceptableDependenciesAlreadyInstalled()
{
_mockInstalledDependenciesLocator.Setup(_ => _.GetPathWithAcceptableDependencyVersionsInstalled())
.Returns("AlreadyInstalled");
_mockStorage.Setup(_ => _.GetDependencies()).Returns(GetAnyNonEmptyDependencyManifestEntries());
try
{
Environment.SetEnvironmentVariable("MDEnableAutomaticUpgrades", "true");

var firstPowerShellRunspace = PowerShell.Create();
Func<PowerShell> powerShellFactory = PowerShell.Create;
_mockInstalledDependenciesLocator.Setup(_ => _.GetPathWithAcceptableDependencyVersionsInstalled())
.Returns("AlreadyInstalled");
_mockStorage.Setup(_ => _.GetDependencies()).Returns(GetAnyNonEmptyDependencyManifestEntries());

_mockStorage.Setup(_ => _.SnapshotExists("AlreadyInstalled")).Returns(true);
var firstPowerShellRunspace = PowerShell.Create();
Func<PowerShell> powerShellFactory = PowerShell.Create;

_mockBackgroundDependencySnapshotMaintainer.Setup(
_ => _.InstallAndPurgeSnapshots(It.IsAny<Func<PowerShell>>(), It.IsAny<ILogger>()))
.Returns("NewSnapshot");
_mockStorage.Setup(_ => _.SnapshotExists("AlreadyInstalled")).Returns(true);

using (var dependencyManager = CreateDependencyManagerWithMocks())
{
dependencyManager.Initialize(_mockLogger.Object);
dependencyManager.StartDependencyInstallationIfNeeded(firstPowerShellRunspace, powerShellFactory, _mockLogger.Object);
var hadToWait = dependencyManager.WaitForDependenciesAvailability(() => _mockLogger.Object);
_mockBackgroundDependencySnapshotMaintainer.Setup(
_ => _.InstallAndPurgeSnapshots(It.IsAny<Func<PowerShell>>(), It.IsAny<ILogger>()))
.Returns("NewSnapshot");

Assert.False(hadToWait);
Assert.Equal("NewSnapshot", dependencyManager.WaitForBackgroundDependencyInstallationTaskCompletion());
using (var dependencyManager = CreateDependencyManagerWithMocks())
{
dependencyManager.Initialize(_mockLogger.Object);
dependencyManager.StartDependencyInstallationIfNeeded(firstPowerShellRunspace, powerShellFactory, _mockLogger.Object);
var hadToWait = dependencyManager.WaitForDependenciesAvailability(() => _mockLogger.Object);

_mockBackgroundDependencySnapshotMaintainer.Verify(
_ => _.InstallAndPurgeSnapshots(powerShellFactory, _mockLogger.Object),
Assert.False(hadToWait);
Assert.Equal("NewSnapshot", dependencyManager.WaitForBackgroundDependencyInstallationTaskCompletion());

_mockBackgroundDependencySnapshotMaintainer.Verify(
_ => _.InstallAndPurgeSnapshots(powerShellFactory, _mockLogger.Object),
Times.Once);
}

_mockLogger.Verify(
_ => _.Log(
false,
LogLevel.Trace,
It.Is<string>(message => message.Contains(PowerShellWorkerStrings.AcceptableFunctionAppDependenciesAlreadyInstalled)),
It.IsAny<Exception>()),
Times.Once);
}

_mockLogger.Verify(
_ => _.Log(
false,
LogLevel.Trace,
It.Is<string>(message => message.Contains(PowerShellWorkerStrings.AcceptableFunctionAppDependenciesAlreadyInstalled)),
It.IsAny<Exception>()),
Times.Once);
finally
{
Environment.SetEnvironmentVariable("MDEnableAutomaticUpgrades", null);
}
}

[Fact]
Expand Down
4 changes: 2 additions & 2 deletions tools/helper.psm1
Original file line number Diff line number Diff line change
Expand Up @@ -74,9 +74,9 @@ function Install-Dotnet {
$version = "$majorMinorVersion.$($DotnetSDKVersionRequirements[$majorMinorVersion].DefaultPatch)"
Write-Log "Installing dotnet SDK version $version" -Warning
if ($IsWindowsEnv) {
& .\$installScript -Channel $Channel -Version $Version
& .\$installScript -Channel $Channel -Version $Version -InstallDir "$env:ProgramFiles/dotnet"
} else {
bash ./$installScript -c $Channel -v $Version
bash ./$installScript -c $Channel -v $Version --install-dir /usr/share/dotnet
}
}

Expand Down