Skip to content

Commit 23404de

Browse files
authored
Use preserved requirements.psd1 for snapshot comparison (#670) (#676)
1 parent 5b1786c commit 23404de

11 files changed

+121
-30
lines changed

src/DependencyManagement/DependencyManager.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,7 @@ public DependencyManager(
5555
ILogger logger = null)
5656
{
5757
_storage = storage ?? new DependencyManagerStorage(GetFunctionAppRootPath(requestMetadataDirectory));
58-
_installedDependenciesLocator = installedDependenciesLocator ?? new InstalledDependenciesLocator(_storage);
58+
_installedDependenciesLocator = installedDependenciesLocator ?? new InstalledDependenciesLocator(_storage, logger);
5959
var snapshotContentLogger = new PowerShellModuleSnapshotLogger();
6060
_installer = installer ?? new DependencySnapshotInstaller(
6161
moduleProvider ?? new PowerShellGalleryModuleProvider(logger),

src/DependencyManagement/DependencyManagerStorage.cs

Lines changed: 30 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,8 +24,7 @@ public DependencyManagerStorage(string functionAppRootPath)
2424

2525
public IEnumerable<DependencyManifestEntry> GetDependencies()
2626
{
27-
var dependencyManifest = new DependencyManifest(_functionAppRootPath);
28-
return dependencyManifest.GetEntries();
27+
return GetAppDependencyManifest().GetEntries();
2928
}
3029

3130
public bool SnapshotExists(string path)
@@ -127,6 +126,30 @@ public DateTime GetSnapshotAccessTimeUtc(string path)
127126
return heartbeatLastWrite >= snapshotCreation ? heartbeatLastWrite : snapshotCreation;
128127
}
129128

129+
public void PreserveDependencyManifest(string path)
130+
{
131+
var source = GetAppDependencyManifest().GetPath();
132+
var destination = Path.Join(path, Path.GetFileName(source));
133+
File.Copy(source, destination, overwrite: true);
134+
}
135+
136+
public bool IsEquivalentDependencyManifest(string path)
137+
{
138+
var source = GetAppDependencyManifest().GetPath();
139+
if (!File.Exists(source))
140+
{
141+
return false;
142+
}
143+
144+
var destination = Path.Join(path, Path.GetFileName(source));
145+
if (!File.Exists(destination))
146+
{
147+
return false;
148+
}
149+
150+
return File.ReadAllText(source) == File.ReadAllText(destination);
151+
}
152+
130153
private IEnumerable<string> GetInstalledSnapshots()
131154
{
132155
if (!Directory.Exists(_managedDependenciesRootPath))
@@ -138,5 +161,10 @@ private IEnumerable<string> GetInstalledSnapshots()
138161
_managedDependenciesRootPath,
139162
DependencySnapshotFolderNameTools.InstalledPattern);
140163
}
164+
165+
private DependencyManifest GetAppDependencyManifest()
166+
{
167+
return new DependencyManifest(_functionAppRootPath);
168+
}
141169
}
142170
}

src/DependencyManagement/DependencyManifest.cs

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,11 @@ public DependencyManifest(string functionAppRootPath, int maxDependencyEntries =
3232
_maxDependencyEntries = maxDependencyEntries;
3333
}
3434

35+
public string GetPath()
36+
{
37+
return Path.Combine(_functionAppRootPath, RequirementsPsd1FileName);
38+
}
39+
3540
public IEnumerable<DependencyManifestEntry> GetEntries()
3641
{
3742
var hashtable = ParsePowerShellDataFile();
@@ -93,7 +98,7 @@ private static DependencyManifestEntry CreateDependencyManifestEntry(string name
9398
private Hashtable ParsePowerShellDataFile()
9499
{
95100
// Path to requirements.psd1 file.
96-
var requirementsFilePath = Path.Join(_functionAppRootPath, RequirementsPsd1FileName);
101+
var requirementsFilePath = GetPath();
97102

98103
if (!File.Exists(requirementsFilePath))
99104
{

src/DependencyManagement/DependencySnapshotInstaller.cs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -108,7 +108,9 @@ private string CreateInstallingSnapshot(string path)
108108
{
109109
try
110110
{
111-
return _storage.CreateInstallingSnapshot(path);
111+
var installingPath = _storage.CreateInstallingSnapshot(path);
112+
_storage.PreserveDependencyManifest(installingPath);
113+
return installingPath;
112114
}
113115
catch (Exception e)
114116
{

src/DependencyManagement/IDependencyManagerStorage.cs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,5 +37,9 @@ internal interface IDependencyManagerStorage
3737
void SetSnapshotAccessTimeToUtcNow(string path);
3838

3939
DateTime GetSnapshotAccessTimeUtc(string path);
40+
41+
void PreserveDependencyManifest(string path);
42+
43+
bool IsEquivalentDependencyManifest(string path);
4044
}
4145
}

src/DependencyManagement/InstalledDependenciesLocator.cs

Lines changed: 26 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -7,28 +7,46 @@ namespace Microsoft.Azure.Functions.PowerShellWorker.DependencyManagement
77
{
88
using System;
99
using System.Linq;
10+
using Microsoft.Azure.Functions.PowerShellWorker.Utility;
11+
using static Microsoft.Azure.WebJobs.Script.Grpc.Messages.RpcLog.Types;
1012

1113
internal class InstalledDependenciesLocator : IInstalledDependenciesLocator
1214
{
1315
private readonly IDependencyManagerStorage _storage;
1416

15-
public InstalledDependenciesLocator(IDependencyManagerStorage storage)
17+
private readonly ILogger _logger;
18+
19+
public InstalledDependenciesLocator(IDependencyManagerStorage storage, ILogger logger)
1620
{
17-
_storage = storage;
21+
_storage = storage ?? throw new ArgumentNullException(nameof(storage));
22+
_logger = logger ?? throw new ArgumentNullException(nameof(logger));
1823
}
1924

2025
public string GetPathWithAcceptableDependencyVersionsInstalled()
2126
{
2227
var lastSnapshotPath = _storage.GetLatestInstalledSnapshot();
23-
if (lastSnapshotPath != null)
28+
if (lastSnapshotPath == null)
29+
{
30+
_logger.Log(isUserOnlyLog: false, Level.Information, string.Format(PowerShellWorkerStrings.NoInstalledDependencySnapshot, lastSnapshotPath));
31+
return null;
32+
}
33+
34+
_logger.Log(isUserOnlyLog: false, Level.Information, string.Format(PowerShellWorkerStrings.LastInstalledDependencySnapshotFound, lastSnapshotPath));
35+
36+
if (_storage.IsEquivalentDependencyManifest(lastSnapshotPath))
37+
{
38+
_logger.Log(isUserOnlyLog: false, Level.Information, string.Format(PowerShellWorkerStrings.EquivalentDependencySnapshotManifest, lastSnapshotPath));
39+
return lastSnapshotPath;
40+
}
41+
42+
var dependencies = _storage.GetDependencies();
43+
if (dependencies.All(entry => IsAcceptableVersionInstalled(lastSnapshotPath, entry)))
2444
{
25-
var dependencies = _storage.GetDependencies();
26-
if (dependencies.All(entry => IsAcceptableVersionInstalled(lastSnapshotPath, entry)))
27-
{
28-
return lastSnapshotPath;
29-
}
45+
_logger.Log(isUserOnlyLog: false, Level.Information, string.Format(PowerShellWorkerStrings.DependencySnapshotContainsAcceptableModuleVersions, lastSnapshotPath));
46+
return lastSnapshotPath;
3047
}
3148

49+
_logger.Log(isUserOnlyLog: false, Level.Information, string.Format(PowerShellWorkerStrings.DependencySnapshotDoesNotContainAcceptableModuleVersions, lastSnapshotPath));
3250
return null;
3351
}
3452

src/resources/PowerShellWorkerStrings.resx

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -337,4 +337,19 @@
337337
<data name="LogDependencySnapshotsInstalledAndSnapshotsToKeep" xml:space="preserve">
338338
<value>Number of dependency snapshots installed: '{0}'. Dependency snapshots to keep: '{1}'.</value>
339339
</data>
340+
<data name="NoInstalledDependencySnapshot" xml:space="preserve">
341+
<value>No installed dependency snapshot found.</value>
342+
</data>
343+
<data name="LastInstalledDependencySnapshotFound" xml:space="preserve">
344+
<value>Last installed dependency snapshot found: '{0}'.</value>
345+
</data>
346+
<data name="EquivalentDependencySnapshotManifest" xml:space="preserve">
347+
<value>Dependency snapshot '{0}' manifest is equivalent to the current manifest.</value>
348+
</data>
349+
<data name="DependencySnapshotContainsAcceptableModuleVersions" xml:space="preserve">
350+
<value>Dependency snapshot '{0}' contains acceptable module versions.</value>
351+
</data>
352+
<data name="DependencySnapshotDoesNotContainAcceptableModuleVersions" xml:space="preserve">
353+
<value>Dependency snapshot '{0}' does not contain acceptable module versions.</value>
354+
</data>
340355
</root>

test/Unit/DependencyManagement/DependencyManagementTests.cs

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -221,7 +221,7 @@ public void TestManagedDependencySuccessfulModuleDownload()
221221
var mockModuleProvider = new MockModuleProvider { SuccessfulDownload = true };
222222

223223
// Create DependencyManager and process the requirements.psd1 file at the function app root.
224-
using (var dependencyManager = new DependencyManager(functionLoadRequest.Metadata.Directory, mockModuleProvider))
224+
using (var dependencyManager = new DependencyManager(functionLoadRequest.Metadata.Directory, mockModuleProvider, logger: _testLogger))
225225
{
226226
dependencyManager.Initialize(_testLogger);
227227

@@ -268,7 +268,7 @@ public void TestManagedDependencySuccessfulModuleDownloadAfterTwoTries()
268268
var mockModuleProvider = new MockModuleProvider { ShouldNotThrowAfterCount = 2 };
269269

270270
// Create DependencyManager and process the requirements.psd1 file at the function app root.
271-
using (var dependencyManager = new DependencyManager(functionLoadRequest.Metadata.Directory, mockModuleProvider))
271+
using (var dependencyManager = new DependencyManager(functionLoadRequest.Metadata.Directory, mockModuleProvider, logger: _testLogger))
272272
{
273273
dependencyManager.Initialize(_testLogger);
274274

@@ -325,7 +325,7 @@ public void TestManagedDependencyRetryLogicMaxNumberOfTries()
325325
var functionLoadRequest = GetFuncLoadRequest(functionFolderPath, true);
326326

327327
// Create DependencyManager and process the requirements.psd1 file at the function app root.
328-
using (var dependencyManager = new DependencyManager(functionLoadRequest.Metadata.Directory, new MockModuleProvider()))
328+
using (var dependencyManager = new DependencyManager(functionLoadRequest.Metadata.Directory, new MockModuleProvider(), logger: _testLogger))
329329
{
330330
dependencyManager.Initialize(_testLogger);
331331

@@ -382,7 +382,8 @@ public void FunctionAppExecutionShouldStopIfNoPreviousDependenciesAreInstalled()
382382
// the PSGallery to retrieve the latest module version
383383
using (var dependencyManager = new DependencyManager(
384384
functionLoadRequest.Metadata.Directory,
385-
new MockModuleProvider { GetLatestModuleVersionThrows = true }))
385+
new MockModuleProvider { GetLatestModuleVersionThrows = true },
386+
logger: _testLogger))
386387
{
387388
dependencyManager.Initialize(_testLogger);
388389
dependencyManager.StartDependencyInstallationIfNeeded(PowerShell.Create(), PowerShell.Create, _testLogger);
@@ -417,7 +418,8 @@ public void FunctionAppExecutionShouldContinueIfPreviousDependenciesExist()
417418
// the PSGallery to retrive the latest module version
418419
using (var dependencyManager = new DependencyManager(
419420
functionLoadRequest.Metadata.Directory,
420-
new MockModuleProvider { GetLatestModuleVersionThrows = true }))
421+
new MockModuleProvider { GetLatestModuleVersionThrows = true },
422+
logger: _testLogger))
421423
{
422424
// Create a path to mimic an existing installation of the Az module
423425
AzModulePath = Path.Join(managedDependenciesFolderPath, "FakeDependenciesSnapshot", "Az");

test/Unit/DependencyManagement/DependencyManagerTests.cs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -313,7 +313,8 @@ private DependencyManager CreateDependencyManagerWithMocks()
313313
installer: _mockInstaller.Object,
314314
newerSnapshotDetector: _mockNewerDependencySnapshotDetector.Object,
315315
maintainer: _mockBackgroundDependencySnapshotMaintainer.Object,
316-
currentSnapshotContentLogger: _mockBackgroundDependencySnapshotContentLogger.Object);
316+
currentSnapshotContentLogger: _mockBackgroundDependencySnapshotContentLogger.Object,
317+
logger: _mockLogger.Object);
317318
}
318319
}
319320
}

test/Unit/DependencyManagement/DependencySnapshotInstallerTests.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ public DependencySnapshotInstallerTests()
3434
_targetPathInstalled = DependencySnapshotFolderNameTools.CreateUniqueName();
3535
_targetPathInstalling = DependencySnapshotFolderNameTools.ConvertInstalledToInstalling(_targetPathInstalled);
3636
_mockStorage.Setup(_ => _.CreateInstallingSnapshot(_targetPathInstalled)).Returns(_targetPathInstalling);
37+
_mockStorage.Setup(_ => _.PreserveDependencyManifest(_targetPathInstalling));
3738
_mockStorage.Setup(_ => _.PromoteInstallingSnapshotToInstalledAtomically(_targetPathInstalled));
3839
_mockStorage.Setup(_ => _.GetLatestInstalledSnapshot()).Returns(default(string));
3940
}

0 commit comments

Comments
 (0)