From 8caaaaa3464fdb87111eb59cc7f78f8e2b5ef0a5 Mon Sep 17 00:00:00 2001 From: Steve Lee Date: Wed, 20 Sep 2017 08:38:09 -0700 Subject: [PATCH 1/3] [feature] added tests for remote import-module fixed issue with remotely importing module where it was checking the version filter incorrectly against the proxy module - the filters are applied when importing the module remotely - after proxy module is generated it always has a module version of 1.0, so the filters will always fail when importing the proxy locally --- .../engine/Modules/ImportModuleCommand.cs | 4 + .../Import-Module.Tests.ps1 | 8 +- .../RemoteImportModule.Tests.ps1 | 111 ++++++++++++++++++ 3 files changed, 122 insertions(+), 1 deletion(-) create mode 100644 test/powershell/Modules/Microsoft.PowerShell.Core/RemoteImportModule.Tests.ps1 diff --git a/src/System.Management.Automation/engine/Modules/ImportModuleCommand.cs b/src/System.Management.Automation/engine/Modules/ImportModuleCommand.cs index dbd058cab1b..0c21fab4cff 100644 --- a/src/System.Management.Automation/engine/Modules/ImportModuleCommand.cs +++ b/src/System.Management.Automation/engine/Modules/ImportModuleCommand.cs @@ -989,6 +989,10 @@ private PSModuleInfo ImportModule_RemotelyViaPsrpSession_SinglePreimportedModule try { this.ArgumentList = new object[] { psSession }; + // since we already applied these filters remotely, we don't want to apply it locally where the generated module is always 1.0 + BaseMinimumVersion = null; + BaseMaximumVersion = null; + BaseRequiredVersion = null; ImportModule_LocallyViaName(importModuleOptions, wildcardEscapedPsd1Path); } finally diff --git a/test/powershell/Modules/Microsoft.PowerShell.Core/Import-Module.Tests.ps1 b/test/powershell/Modules/Microsoft.PowerShell.Core/Import-Module.Tests.ps1 index a1c4e134920..5e2d92c8c80 100644 --- a/test/powershell/Modules/Microsoft.PowerShell.Core/Import-Module.Tests.ps1 +++ b/test/powershell/Modules/Microsoft.PowerShell.Core/Import-Module.Tests.ps1 @@ -21,10 +21,16 @@ { Import-Module -ModuleInfo $a } | Should Not Throw (Get-Module -Name $moduleName).Name | Should Be $moduleName } + + It "should be able to load an already loaded module" { + Import-Module $moduleName + { $script:module = Import-Module $moduleName -PassThru } | Should Not Throw + Get-Module -Name $moduleName | Should Be $script:module + } } Describe "Import-Module with ScriptsToProcess" -Tags "CI" { - + BeforeAll { $moduleRootPath = Join-Path $TestDrive 'TestModules' New-Item $moduleRootPath -ItemType Directory -Force | Out-Null diff --git a/test/powershell/Modules/Microsoft.PowerShell.Core/RemoteImportModule.Tests.ps1 b/test/powershell/Modules/Microsoft.PowerShell.Core/RemoteImportModule.Tests.ps1 new file mode 100644 index 00000000000..bc75cfd8b21 --- /dev/null +++ b/test/powershell/Modules/Microsoft.PowerShell.Core/RemoteImportModule.Tests.ps1 @@ -0,0 +1,111 @@ +Describe "Remote import-module tests" -Tags 'Feature','RequireAdminOnWindows' { + + BeforeAll { + $originalDefaultParameterValues = $PSDefaultParameterValues.Clone() + $modulePath = "$testdrive\Modules\TestImport" + if (!$IsWindows) { + $PSDefaultParameterValues["it:skip"] = $true + } else { + $pssession = New-RemoteSession + Invoke-Command -Session $pssession -ScriptBlock { $env:PSModulePath += ";${using:testdrive}" } + # pending https://github.com/PowerShell/PowerShell/issues/4819 + # $cimsession = New-RemoteSession -CimSession + $null = New-Item -ItemType Directory -Path $modulePath + Set-Content -Path $modulePath\testimport.psm1 -Value "function test-hello { 'world' }" + New-ModuleManifest -Path $modulePath\testimport.psd1 -ModuleVersion 1.2.3 -RootModule testimport.psm1 -FunctionsToExport "test-hello" ` + -HelpInfoUri "https://help" -Guid (New-Guid) + } + } + + AfterAll { + $global:PSDefaultParameterValues = $originalDefaultParameterValues + if ($IsWindows) { + $pssession | Remove-PSSession -ErrorAction SilentlyContinue + } + } + + BeforeEach { + Remove-Module TestImport -Force -ErrorAction SilentlyContinue + } + + It "Import-Module can be called as an API with '' = ''" -TestCases @( + @{parameter = "Global" ; value = $true}, + @{parameter = "Global" ; value = $false}, + @{parameter = "Prefix" ; value = "Hello"}, + @{parameter = "Name" ; value = "foo","bar"}, + @{parameter = "FullyQualifiedName" ; value = @{ModuleName='foo';RequiredVersion='0.0'},@{ModuleName='bar';RequiredVersion='1.1'}}, + @{parameter = "Assembly" ; script = { [System.AppDomain]::CurrentDomain.GetAssemblies() | Select-Object -First 2 }} + @{parameter = "Function" ; value = "foo","bar"}, + @{parameter = "Cmdlet" ; value = "foo","bar"}, + @{parameter = "Variable" ; value = "foo","bar"}, + @{parameter = "Alias" ; value = "foo","bar"}, + @{parameter = "Force" ; value = $true}, + @{parameter = "Force" ; value = $false}, + @{parameter = "PassThru" ; value = $true}, + @{parameter = "PassThru" ; value = $false}, + @{parameter = "AsCustomObject" ; value = $true}, + @{parameter = "AsCustomObject" ; value = $false}, + @{parameter = "MinimumVersion" ; value = "1.2.3"}, + @{parameter = "MaximumVersion" ; value = "3.2.1"}, + @{parameter = "RequiredVersion" ; value = "1.1.1"}, + @{parameter = "ArgumentList" ; value = "hello","world"}, + @{parameter = "DisableNameChecking"; value = $true}, + @{parameter = "DisableNameChecking"; value = $false}, + @{parameter = "NoClobber" ; value = $true}, + @{parameter = "NoClobber" ; value = $false}, + @{parameter = "Scope" ; value = "Local"}, + @{parameter = "Scope" ; value = "Global"}, + @{parameter = "PSSession" ; value = $pssession}, + # @{parameter = "CimSession" ; value = $cimsession}, + @{parameter = "CimResourceUri" ; value = "http://foo/"}, + @{parameter = "CimNamespace" ; value = "foo"} + ) { + param($parameter, $value, $script) + + $importModuleCommand = [Microsoft.PowerShell.Commands.ImportModuleCommand]::new() + if ($script -ne $null) { + $value = & $script + } + $importModuleCommand.$parameter = $value + if ($parameter -eq "FullyQualifiedName") { + $importModuleCommand.FullyQualifiedName.Count | Should BeExactly 2 + $importModuleCommand.FullyQualifiedName | Should BeOfType "Microsoft.PowerShell.Commands.ModuleSpecification" + $importModuleCommand.FullyQualifiedName[0].Name | Should Be "foo" + $importModuleCommand.FullyQualifiedName[0].RequiredVersion | Should Be "0.0" + $importModuleCommand.FullyQualifiedName[1].Name | Should Be "bar" + $importModuleCommand.FullyQualifiedName[1].RequiredVersion | Should Be "1.1" + } else { + $importModuleCommand.$parameter | Should BeExactly $value + } + } + + It "Import-Module can import over remote session: " -TestCases @( + @{ test = "pssession" ; parameters = @{Name="TestImport";PSSession=$pssession}}, +# @{ test = "cimsession" ; parameters = @{Name="TestImport";CimSession=$cimsession}}, + @{ test = "minimumversion" ; parameters = @{Name="TestImport";PSSession=$pssession;MinimumVersion="1.0";Force=$true}}, + @{ test = "requiredversion" ; parameters = @{Name="TestImport";PSSession=$pssession;RequiredVersion="1.2.3"}}, + @{ test = "maxiumversion" ; parameters = @{Name="TestImport";PSSession=$pssession;MaximumVersion="2.0"}}, + @{ test = "invalid miniumversion" ; parameters = @{Name="TestImport";PSSession=$pssession;MinimumVersion="2.0"}; + errorid = "Modules_ModuleWithVersionNotFound,Microsoft.PowerShell.Commands.ImportModuleCommand,Microsoft.PowerShell.Commands.ImportModuleCommand"}, + @{ test = "invalid maximumversion" ; parameters = @{Name="TestImport";PSSession=$pssession;MaximumVersion="1.0"}; + errorid = "Modules_ModuleWithVersionNotFound,Microsoft.PowerShell.Commands.ImportModuleCommand,Microsoft.PowerShell.Commands.ImportModuleCommand"}, + @{ test = "fullyqualifiedname" ; parameters = @{FullyQualifiedName=@{Modulename="TestImport"; RequiredVersion="1.2.3"};PSSession=$pssession}} + ) { + param ($test, $parameters, $errorid) + + Invoke-Command -Session $pssession -ScriptBlock { $env:PSModulePath += ";$(Split-Path $using:modulePath)"} + Get-Module TestImport | Should BeNullOrEmpty + if ($errorid) { + { Import-Module @parameters -ErrorAction Stop } | ShouldBeErrorId $errorid + } else { + Import-Module @parameters + $module = Get-Module TestImport + $module.Name | Should BeExactly "TestImport" + + # generated proxy module always uses 1.0 + $module.Version | Should BeExactly "1.0" + + test-hello | Should BeExactly "world" + } + } +} From 4f289821146803cc4aee98f81215f0347adf8ec8 Mon Sep 17 00:00:00 2001 From: "Steve Lee [MSFT]" Date: Fri, 22 Sep 2017 16:06:29 -0700 Subject: [PATCH 2/3] address PR feedback to store and revert the changed parameter values --- .../engine/Modules/ImportModuleCommand.cs | 20 +++++++++++++++++-- 1 file changed, 18 insertions(+), 2 deletions(-) diff --git a/src/System.Management.Automation/engine/Modules/ImportModuleCommand.cs b/src/System.Management.Automation/engine/Modules/ImportModuleCommand.cs index 0c21fab4cff..f87b1f043fb 100644 --- a/src/System.Management.Automation/engine/Modules/ImportModuleCommand.cs +++ b/src/System.Management.Automation/engine/Modules/ImportModuleCommand.cs @@ -989,11 +989,27 @@ private PSModuleInfo ImportModule_RemotelyViaPsrpSession_SinglePreimportedModule try { this.ArgumentList = new object[] { psSession }; - // since we already applied these filters remotely, we don't want to apply it locally where the generated module is always 1.0 + + // The correct module version has already been imported from the remote session and created locally. + // The locally created module always has a version of 1.0 regardless of the actual module version + // imported from the remote session, and version checking is no longer needed and will not work while + // importing this created local module. + Version originalBaseMinimumVersion = BaseMinimumVersion; + Version originalBaseMaximumVersion = BaseMaximumVersion; + Version originalBaseRequiredVersion = BaseRequiredVersion; BaseMinimumVersion = null; BaseMaximumVersion = null; BaseRequiredVersion = null; - ImportModule_LocallyViaName(importModuleOptions, wildcardEscapedPsd1Path); + try + { + ImportModule_LocallyViaName(importModuleOptions, wildcardEscapedPsd1Path); + } + finally + { + BaseMinimumVersion = originalBaseMinimumVersion; + BaseMaximumVersion = originalBaseMaximumVersion; + BaseRequiredVersion = originalBaseRequiredVersion; + } } finally { From c6c2f96a2e7ef3f81dade8614f4b32a2297f221e Mon Sep 17 00:00:00 2001 From: Steve Lee Date: Mon, 25 Sep 2017 14:39:11 -0700 Subject: [PATCH 3/3] [feature] address PR feedback from Paul --- .../engine/Modules/ImportModuleCommand.cs | 20 +++++++------------ 1 file changed, 7 insertions(+), 13 deletions(-) diff --git a/src/System.Management.Automation/engine/Modules/ImportModuleCommand.cs b/src/System.Management.Automation/engine/Modules/ImportModuleCommand.cs index f87b1f043fb..c4fdf96de92 100644 --- a/src/System.Management.Automation/engine/Modules/ImportModuleCommand.cs +++ b/src/System.Management.Automation/engine/Modules/ImportModuleCommand.cs @@ -986,6 +986,9 @@ private PSModuleInfo ImportModule_RemotelyViaPsrpSession_SinglePreimportedModule // import the proxy module just as any other local module // object[] oldArgumentList = this.ArgumentList; + Version originalBaseMinimumVersion = BaseMinimumVersion; + Version originalBaseMaximumVersion = BaseMaximumVersion; + Version originalBaseRequiredVersion = BaseRequiredVersion; try { this.ArgumentList = new object[] { psSession }; @@ -994,26 +997,17 @@ private PSModuleInfo ImportModule_RemotelyViaPsrpSession_SinglePreimportedModule // The locally created module always has a version of 1.0 regardless of the actual module version // imported from the remote session, and version checking is no longer needed and will not work while // importing this created local module. - Version originalBaseMinimumVersion = BaseMinimumVersion; - Version originalBaseMaximumVersion = BaseMaximumVersion; - Version originalBaseRequiredVersion = BaseRequiredVersion; BaseMinimumVersion = null; BaseMaximumVersion = null; BaseRequiredVersion = null; - try - { - ImportModule_LocallyViaName(importModuleOptions, wildcardEscapedPsd1Path); - } - finally - { - BaseMinimumVersion = originalBaseMinimumVersion; - BaseMaximumVersion = originalBaseMaximumVersion; - BaseRequiredVersion = originalBaseRequiredVersion; - } + ImportModule_LocallyViaName(importModuleOptions, wildcardEscapedPsd1Path); } finally { this.ArgumentList = oldArgumentList; + BaseMinimumVersion = originalBaseMinimumVersion; + BaseMaximumVersion = originalBaseMaximumVersion; + BaseRequiredVersion = originalBaseRequiredVersion; } //