diff --git a/src/DependencyManagement/DependencyManager.cs b/src/DependencyManagement/DependencyManager.cs index 41075b82..fda9407b 100644 --- a/src/DependencyManagement/DependencyManager.cs +++ b/src/DependencyManagement/DependencyManager.cs @@ -48,12 +48,13 @@ public DependencyManager( IInstalledDependenciesLocator installedDependenciesLocator = null, IDependencySnapshotInstaller installer = null, INewerDependencySnapshotDetector newerSnapshotDetector = null, - IBackgroundDependencySnapshotMaintainer maintainer = null) + IBackgroundDependencySnapshotMaintainer maintainer = null, + ILogger logger = null) { _storage = storage ?? new DependencyManagerStorage(GetFunctionAppRootPath(requestMetadataDirectory)); _installedDependenciesLocator = installedDependenciesLocator ?? new InstalledDependenciesLocator(_storage); _installer = installer ?? new DependencySnapshotInstaller( - moduleProvider ?? new PowerShellGalleryModuleProvider(), + moduleProvider ?? new PowerShellGalleryModuleProvider(logger), _storage, new PowerShellModuleSnapshotComparer(), new PowerShellModuleSnapshotLogger()); diff --git a/src/DependencyManagement/PowerShellGalleryModuleProvider.cs b/src/DependencyManagement/PowerShellGalleryModuleProvider.cs index feb581d6..1e442067 100644 --- a/src/DependencyManagement/PowerShellGalleryModuleProvider.cs +++ b/src/DependencyManagement/PowerShellGalleryModuleProvider.cs @@ -11,13 +11,17 @@ namespace Microsoft.Azure.Functions.PowerShellWorker.DependencyManagement using Microsoft.Azure.Functions.PowerShellWorker.PowerShell; using Microsoft.Azure.Functions.PowerShellWorker.Utility; + using LogLevel = Microsoft.Azure.WebJobs.Script.Grpc.Messages.RpcLog.Types.Level; internal class PowerShellGalleryModuleProvider : IModuleProvider { + private readonly ILogger _logger; + private readonly IPowerShellGallerySearchInvoker _searchInvoker; - public PowerShellGalleryModuleProvider(IPowerShellGallerySearchInvoker searchInvoker = null) + public PowerShellGalleryModuleProvider(ILogger logger, IPowerShellGallerySearchInvoker searchInvoker = null) { + _logger = logger ?? throw new ArgumentNullException(nameof(logger)); _searchInvoker = searchInvoker ?? new PowerShellGallerySearchInvoker(); } @@ -87,8 +91,18 @@ public void SaveModule(PowerShell pwsh, string moduleName, string version, strin .AddParameter("AllowPrerelease", Utils.BoxedTrue) .AddParameter("Path", path) .AddParameter("Force", Utils.BoxedTrue) - .AddParameter("ErrorAction", "Stop") - .InvokeAndClearCommands(); + .AddParameter("ErrorAction", "Stop"); + + try + { + pwsh.Invoke(); + } + finally + { + LogSaveModuleErrorsAndWarnings(pwsh, moduleName, version); + pwsh.Streams.ClearStreams(); + pwsh.Commands.Clear(); + } } /// @@ -104,6 +118,21 @@ public void Cleanup(PowerShell pwsh) .InvokeAndClearCommands(); } + private void LogSaveModuleErrorsAndWarnings(PowerShell pwsh, string moduleName, string version) + { + var prefix = $"Save-Module('{moduleName}', '{version}'): "; + + foreach (var item in pwsh.Streams.Error) + { + _logger.Log(isUserOnlyLog: false, LogLevel.Error, $"{prefix}{item.Exception.Message}"); + } + + foreach (var item in pwsh.Streams.Warning) + { + _logger.Log(isUserOnlyLog: false, LogLevel.Warning, $"{prefix}{item.Message}"); + } + } + private static Version GetLatestVersion( XmlNode root, XmlNamespaceManager namespaceManager, string expectedVersionStart, Version latestVersion) { diff --git a/src/RequestProcessor.cs b/src/RequestProcessor.cs index 3f4b51b2..e0492626 100644 --- a/src/RequestProcessor.cs +++ b/src/RequestProcessor.cs @@ -190,7 +190,7 @@ internal StreamingMessage ProcessFunctionLoadRequest(StreamingMessage request) var rpcLogger = new RpcLogger(_msgStream); rpcLogger.SetContext(request.RequestId, null); - _dependencyManager = new DependencyManager(request.FunctionLoadRequest.Metadata.Directory); + _dependencyManager = new DependencyManager(request.FunctionLoadRequest.Metadata.Directory, logger: rpcLogger); var managedDependenciesPath = _dependencyManager.Initialize(request, rpcLogger); // Setup the FunctionApp root path and module path. diff --git a/test/Unit/DependencyManagement/DependencyManagementTests.cs b/test/Unit/DependencyManagement/DependencyManagementTests.cs index 80f1a22b..e176ed79 100644 --- a/test/Unit/DependencyManagement/DependencyManagementTests.cs +++ b/test/Unit/DependencyManagement/DependencyManagementTests.cs @@ -102,7 +102,7 @@ public void TestManagedDependencyBasicRequirements() var functionLoadRequest = GetFuncLoadRequest(functionFolderPath, true); // Create DependencyManager and process the requirements.psd1 file at the function app root. - using (var dependencyManager = new DependencyManager(functionLoadRequest.Metadata.Directory)) + using (var dependencyManager = new DependencyManager(functionLoadRequest.Metadata.Directory, logger: _testLogger)) { var currentDependenciesPath = dependencyManager.Initialize(_testLogger); @@ -132,7 +132,7 @@ public void TestManagedDependencyEmptyHashtableRequirement() var functionLoadRequest = GetFuncLoadRequest(functionFolderPath, true); // Create DependencyManager and process the requirements.psd1 file at the function app root. - using (var dependencyManager = new DependencyManager(functionLoadRequest.Metadata.Directory)) + using (var dependencyManager = new DependencyManager(functionLoadRequest.Metadata.Directory, logger: _testLogger)) { var currentDependenciesPath = dependencyManager.Initialize(_testLogger); @@ -153,7 +153,7 @@ public void TestManagedDependencyNoHashtableRequirementShouldThrow() var functionFolderPath = Path.Combine(_dependencyManagementDirectory, requirementsDirectoryName, "FunctionDirectory"); var functionLoadRequest = GetFuncLoadRequest(functionFolderPath, true); - using (var dependencyManager = new DependencyManager(functionLoadRequest.Metadata.Directory)) + using (var dependencyManager = new DependencyManager(functionLoadRequest.Metadata.Directory, logger: _testLogger)) { // Trying to set the functionApp dependencies should throw since requirements.psd1 is not a hash table. var exception = Assert.Throws( @@ -172,7 +172,7 @@ public void TestManagedDependencyInvalidRequirementsFormatShouldThrow() var functionFolderPath = Path.Combine(_dependencyManagementDirectory, requirementsDirectoryName, "FunctionDirectory"); var functionLoadRequest = GetFuncLoadRequest(functionFolderPath, true); - using (var dependencyManager = new DependencyManager(functionLoadRequest.Metadata.Directory)) + using (var dependencyManager = new DependencyManager(functionLoadRequest.Metadata.Directory, logger: _testLogger)) { // Trying to set the functionApp dependencies should throw since the module version // in requirements.psd1 is not in a valid format. @@ -193,7 +193,7 @@ public void TestManagedDependencyNoRequirementsFileShouldThrow() var functionFolderPath = Path.Combine(_dependencyManagementDirectory, requirementsDirectoryName, "FunctionDirectory"); var functionLoadRequest = GetFuncLoadRequest(functionFolderPath, true); - using (var dependencyManager = new DependencyManager(functionLoadRequest.Metadata.Directory)) + using (var dependencyManager = new DependencyManager(functionLoadRequest.Metadata.Directory, logger: _testLogger)) { // Trying to set the functionApp dependencies should throw since no // requirements.psd1 is found at the function app root. diff --git a/test/Unit/DependencyManagement/PowerShellGalleryModuleProviderTests.cs b/test/Unit/DependencyManagement/PowerShellGalleryModuleProviderTests.cs index d72a0406..a8633e51 100644 --- a/test/Unit/DependencyManagement/PowerShellGalleryModuleProviderTests.cs +++ b/test/Unit/DependencyManagement/PowerShellGalleryModuleProviderTests.cs @@ -7,18 +7,27 @@ namespace Microsoft.Azure.Functions.PowerShellWorker.Test.DependencyManagement using Xunit; using PowerShellWorker.DependencyManagement; + using PowerShellWorker.Utility; public class PowerShellGalleryModuleProviderTests { + private readonly Mock _mockLogger = new Mock(); + private readonly Mock _mockSearchInvoker = new Mock(MockBehavior.Strict); + private PowerShellGalleryModuleProvider _moduleProvider; + + public PowerShellGalleryModuleProviderTests() + { + _moduleProvider = new PowerShellGalleryModuleProvider(_mockLogger.Object, _mockSearchInvoker.Object); + } + [Fact] public void ReturnsNullIfSearchInvokerReturnsNull() { _mockSearchInvoker.Setup(_ => _.Invoke(It.IsAny())).Returns(default(Stream)); - var moduleProvider = new PowerShellGalleryModuleProvider(_mockSearchInvoker.Object); - var actualVersion = moduleProvider.GetLatestPublishedModuleVersion("ModuleName", "1"); + var actualVersion = _moduleProvider.GetLatestPublishedModuleVersion("ModuleName", "1"); Assert.Null(actualVersion); } @@ -43,8 +52,7 @@ public void ReturnsSingleVersion() using (var responseStream = new MemoryStream(Encoding.UTF8.GetBytes(ResponseText))) { _mockSearchInvoker.Setup(_ => _.Invoke(It.IsAny())).Returns(responseStream); - var moduleProvider = new PowerShellGalleryModuleProvider(_mockSearchInvoker.Object); - var actualVersion = moduleProvider.GetLatestPublishedModuleVersion("ModuleName", "1"); + var actualVersion = _moduleProvider.GetLatestPublishedModuleVersion("ModuleName", "1"); Assert.Equal("1.2.3.4", actualVersion); } } @@ -82,8 +90,7 @@ public void FindsLatestVersionRegardlessOfResponseOrder() using (var responseStream = new MemoryStream(Encoding.UTF8.GetBytes(ResponseText))) { _mockSearchInvoker.Setup(_ => _.Invoke(It.IsAny())).Returns(responseStream); - var moduleProvider = new PowerShellGalleryModuleProvider(_mockSearchInvoker.Object); - var actualVersion = moduleProvider.GetLatestPublishedModuleVersion("ModuleName", "1"); + var actualVersion = _moduleProvider.GetLatestPublishedModuleVersion("ModuleName", "1"); Assert.Equal("1.2.3.6", actualVersion); } } @@ -121,8 +128,7 @@ public void IgnoresPrereleaseVersions() using (var responseStream = new MemoryStream(Encoding.UTF8.GetBytes(ResponseText))) { _mockSearchInvoker.Setup(_ => _.Invoke(It.IsAny())).Returns(responseStream); - var moduleProvider = new PowerShellGalleryModuleProvider(_mockSearchInvoker.Object); - var actualVersion = moduleProvider.GetLatestPublishedModuleVersion("ModuleName", "1"); + var actualVersion = _moduleProvider.GetLatestPublishedModuleVersion("ModuleName", "1"); Assert.Equal("1.2.3.5", actualVersion); } } @@ -166,8 +172,7 @@ public void IgnoresNotMatchingMajorVersions() using (var responseStream = new MemoryStream(Encoding.UTF8.GetBytes(ResponseText))) { _mockSearchInvoker.Setup(_ => _.Invoke(It.IsAny())).Returns(responseStream); - var moduleProvider = new PowerShellGalleryModuleProvider(_mockSearchInvoker.Object); - var actualVersion = moduleProvider.GetLatestPublishedModuleVersion("ModuleName", "1"); + var actualVersion = _moduleProvider.GetLatestPublishedModuleVersion("ModuleName", "1"); Assert.Equal("1.2.3.6", actualVersion); } } @@ -207,8 +212,7 @@ public void ComparesVersionsCorrectly(string lowerVersion, string higherVersion) using (var responseStream = new MemoryStream(Encoding.UTF8.GetBytes(responseText))) { _mockSearchInvoker.Setup(_ => _.Invoke(It.IsAny())).Returns(responseStream); - var moduleProvider = new PowerShellGalleryModuleProvider(_mockSearchInvoker.Object); - var actualVersion = moduleProvider.GetLatestPublishedModuleVersion("ModuleName", "0"); + var actualVersion = _moduleProvider.GetLatestPublishedModuleVersion("ModuleName", "0"); Assert.Equal(higherVersion, actualVersion); } } @@ -228,8 +232,7 @@ public void ReturnsNullIfNoVersionFound() using (var responseStream = new MemoryStream(Encoding.UTF8.GetBytes(ResponseText))) { _mockSearchInvoker.Setup(_ => _.Invoke(It.IsAny())).Returns(responseStream); - var moduleProvider = new PowerShellGalleryModuleProvider(_mockSearchInvoker.Object); - var actualVersion = moduleProvider.GetLatestPublishedModuleVersion("ModuleName", "1"); + var actualVersion = _moduleProvider.GetLatestPublishedModuleVersion("ModuleName", "1"); Assert.Null(actualVersion); } } @@ -315,8 +318,7 @@ public void FindsLatestVersionAcrossMultiplePages() _mockSearchInvoker.Setup(_ => _.Invoke(new Uri("https://NextLink2"))) .Returns(responseStream3); - var moduleProvider = new PowerShellGalleryModuleProvider(_mockSearchInvoker.Object); - var actualVersion = moduleProvider.GetLatestPublishedModuleVersion("ModuleName", "1"); + var actualVersion = _moduleProvider.GetLatestPublishedModuleVersion("ModuleName", "1"); Assert.Equal("1.2.3.6", actualVersion); } }