-
Notifications
You must be signed in to change notification settings - Fork 225
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
Get-SqlDscPreferredModule: Optionally specify which version of the SQL module is imported #1966
Get-SqlDscPreferredModule: Optionally specify which version of the SQL module is imported #1966
Conversation
Codecov Report
@@ Coverage Diff @@
## main #1966 +/- ##
====================================
- Coverage 92% 92% -1%
====================================
Files 92 93 +1
Lines 7829 7832 +3
====================================
+ Hits 7204 7206 +2
- Misses 625 626 +1
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Awesome work on this, and impressive work on the unit tests! Just minor comments.
Reviewed 6 of 6 files at r1, all commit messages.
Reviewable status: all files reviewed, 11 unresolved discussions (waiting on @YaroBear)
source/Public/Get-SqlDscPreferredModule.ps1
line 96 at r1 (raw file):
$availableModules = Get-Module -Name $Name -ListAvailable | ForEach-Object {
We should use named parameters.
Suggestion:
ForEach-Object -Process {
source/Public/Get-SqlDscPreferredModule.ps1
line 126 at r1 (raw file):
{ $preferredModules = $availableModules | Where-Object { $_.PSModuleInfo.Name -eq $preferredModuleName}
We should use named parameters.
Suggestion:
Where-Object -FilterScript { $_.PSModuleInfo.Name -eq $preferredModuleName}
source/Public/Get-SqlDscPreferredModule.ps1
line 126 at r1 (raw file):
{ $preferredModules = $availableModules | Where-Object { $_.PSModuleInfo.Name -eq $preferredModuleName}
Couldn't we filter the preferred module from the result from Get-Module
above directly, before calculating version? It would remove this foreach-loop. 🤔 What do you think`
source/Public/Get-SqlDscPreferredModule.ps1
line 134 at r1 (raw file):
# Get the version specified in $env:SMODefaultModuleVersion if available $availableModule = $preferredModules | Where-Object { $_.CalculatedVersion -eq $env:SMODefaultModuleVersion} |
We should use named parameters.
Suggestion:
Where-Object -FIlterScript { $_.CalculatedVersion -eq $env:SMODefaultModuleVersion} |
source/Public/Get-SqlDscPreferredModule.ps1
line 135 at r1 (raw file):
$availableModule = $preferredModules | Where-Object { $_.CalculatedVersion -eq $env:SMODefaultModuleVersion} | Select-Object -First 1
We should indent these two lines of the pipeline one indent when they are on separate rows (which is good to not make the line too long).
Code quote:
Where-Object { $_.CalculatedVersion -eq $env:SMODefaultModuleVersion} |
Select-Object -First 1
source/Public/Get-SqlDscPreferredModule.ps1
line 139 at r1 (raw file):
Write-Verbose -Message ($script:localizedData.PreferredModule_ModuleVersionFound -f $availableModule.PSModuleInfo.Name, $availableModule.CalculatedVersion) } else {
We should have open brace on a separate line.
Code quote:
else {
source/Public/Get-SqlDscPreferredModule.ps1
line 143 at r1 (raw file):
$availableModule = $preferredModules | Sort-Object -Property 'CalculatedVersion' -Descending | Select-Object -First 1
We should indent these two lines of the pipeline one indent when they are on separate rows (which is good to not make the line too long).
Code quote:
Sort-Object -Property 'CalculatedVersion' -Descending |
Select-Object -First 1
source/Public/Get-SqlDscPreferredModule.ps1
line 145 at r1 (raw file):
Select-Object -First 1 Write-Verbose -Message ($script:localizedData.PreferredModule_ModuleFound -f $availableModule.PSModuleInfo.Name)
Maybe we could use the string PreferredModule_ModuleVersionFound
here too, to output the version even if the user did not specify a specific version. It would simplify the code with just one Write-Verbose
before the break
and the output is more detailed. 🤔 What do you think?
Code quote:
PreferredModule_ModuleFound
source/Public/Import-SqlDscPreferredModule.ps1
line 83 at r1 (raw file):
if ($PSBoundParameters.ContainsKey('Name')) { $removeModule += Get-Module $Name
We should use named parameters.
Suggestion:
Get-Module -Name $Name
source/Public/Import-SqlDscPreferredModule.ps1
line 97 at r1 (raw file):
} Remove-Module $removeModule -Force -ErrorAction 'SilentlyContinue'
We should use named parameters.
Suggestion:
Remove-Module -Name $removeModule -Force -ErrorAction 'SilentlyContinue'
source/Public/Import-SqlDscPreferredModule.ps1
line 114 at r1 (raw file):
return }
What if the wrong version is already imported, what shall happen then? I think we need to throw an exception because then the wrong SMO assemblies has been loaded into the session and that can mess things up if we would just try to import and newer or older version.
Code quote:
<#
Check if the preferred module is already loaded into the session.
#>
$loadedModuleName = (Get-Module -Name $availableModule.Name | Select-Object -First 1).Name
if ($loadedModuleName)
{
Write-Verbose -Message ($script:localizedData.PreferredModule_AlreadyImported -f $loadedModuleName)
return
}
I merged another PR so you will need to rebase this one. |
Also, check the HQRM tests failing (style errors): https://dev.azure.com/dsccommunity/SqlServerDsc/_build/results?buildId=8129&view=logs&j=9150365c-bae3-55fa-031b-59bba4aba4d9&t=dc12ef69-6f79-52f3-2e04-c23de96e6308&l=565 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Reviewable status: all files reviewed, 11 unresolved discussions (waiting on @johlju)
source/Public/Get-SqlDscPreferredModule.ps1
line 126 at r1 (raw file):
Previously, johlju (Johan Ljunggren) wrote…
Couldn't we filter the preferred module from the result from
Get-Module
above directly, before calculating version? It would remove this foreach-loop. 🤔 What do you think`
We are already filtering by $name on line 95: $availableModules = Get-Module -Name $Name
The foreach-loop serves to find the preferred module in the order defined by the $Name
variable.
For example: if $Name = @('sqlserver', 'sqlps')
, in the foreach-loop, if a sqlserver module is found, it will set $availableModule = SqlServer
and break from the loop.
If sqlserver module is not found, then it will look for sqlps.
source/Public/Import-SqlDscPreferredModule.ps1
line 114 at r1 (raw file):
Previously, johlju (Johan Ljunggren) wrote…
What if the wrong version is already imported, what shall happen then? I think we need to throw an exception because then the wrong SMO assemblies has been loaded into the session and that can mess things up if we would just try to import and newer or older version.
Hm, Get-SqlDscPreferredModule probably needs to return not just the PSModuleInfo object, but also the CalculatedVersion property in order to check this accurately for SQLPS (without duplicating logic)
Do you recommend a statement terminating error, i.e $PSCmdlet.ThrowTerminatingError
?
Reviewable.io seems to be down currently...
Actually, I think I'll need to reuse the logic either way. Maybe a private function would be better? Get-SMOModuleCalculatedVersion can be reused in both Get-SqlDscPreferredModule and Import-SqlDscPreferredModule |
…able module version selection via SMODefaultModuleVersion env var
…wrong version of smo module was loaded; tests
7604999
to
472ef04
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Reviewable status: 1 of 8 files reviewed, 11 unresolved discussions (waiting on @johlju)
source/Public/Get-SqlDscPreferredModule.ps1
line 96 at r1 (raw file):
Previously, johlju (Johan Ljunggren) wrote…
We should use named parameters.
Done.
source/Public/Get-SqlDscPreferredModule.ps1
line 126 at r1 (raw file):
Previously, johlju (Johan Ljunggren) wrote…
We should use named parameters.
Done.
source/Public/Get-SqlDscPreferredModule.ps1
line 134 at r1 (raw file):
Previously, johlju (Johan Ljunggren) wrote…
We should use named parameters.
Done.
source/Public/Get-SqlDscPreferredModule.ps1
line 135 at r1 (raw file):
Previously, johlju (Johan Ljunggren) wrote…
We should indent these two lines of the pipeline one indent when they are on separate rows (which is good to not make the line too long).
Done.
source/Public/Get-SqlDscPreferredModule.ps1
line 139 at r1 (raw file):
Previously, johlju (Johan Ljunggren) wrote…
We should have open brace on a separate line.
Done.
source/Public/Get-SqlDscPreferredModule.ps1
line 143 at r1 (raw file):
Previously, johlju (Johan Ljunggren) wrote…
We should indent these two lines of the pipeline one indent when they are on separate rows (which is good to not make the line too long).
Done.
source/Public/Get-SqlDscPreferredModule.ps1
line 145 at r1 (raw file):
Previously, johlju (Johan Ljunggren) wrote…
Maybe we could use the string
PreferredModule_ModuleVersionFound
here too, to output the version even if the user did not specify a specific version. It would simplify the code with just oneWrite-Verbose
before thebreak
and the output is more detailed. 🤔 What do you think?
Done.
source/Public/Import-SqlDscPreferredModule.ps1
line 83 at r1 (raw file):
Previously, johlju (Johan Ljunggren) wrote…
We should use named parameters.
Done.
source/Public/Import-SqlDscPreferredModule.ps1
line 97 at r1 (raw file):
Previously, johlju (Johan Ljunggren) wrote…
We should use named parameters.
Done.
Run tests\QA\ScriptAnalyzer.Tests.ps1 to run all ScriptAnalyzer tests
Need to wrap tests for unexported/private functions with |
I will continue review tomorrow I hope. Looks like you answered all questions yourself? 🙂 |
Yep, I think so! Thanks! |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Great thought moving the calculation of module version to a private function! Just a few more comments.
Reviewed 4 of 7 files at r2, 3 of 3 files at r3, all commit messages.
Reviewable status: all files reviewed, 2 unresolved discussions (waiting on @YaroBear)
source/Public/Import-SqlDscPreferredModule.ps1
line 114 at r1 (raw file):
Previously, YaroBear (Yaroslav Berejnoi) wrote…
Hm, Get-SqlDscPreferredModule probably needs to return not just the PSModuleInfo object, but also the CalculatedVersion property in order to check this accurately for SQLPS (without duplicating logic)
Do you recommend a statement terminating error, i.e
$PSCmdlet.ThrowTerminatingError
?
My bad, it seems there was a bug prior to this change that should have thrown, see previous comment.
source/Public/Import-SqlDscPreferredModule.ps1
line 73 at r3 (raw file):
} $availableModule = Get-SqlDscPreferredModule @getSqlDscPreferredModuleParameters
This should be called with -ErrorAction 'Stop'
so that it throws if the wrong version is found, or if no module is found at all. This seems like this was a bug prior to this change.
source/Public/Import-SqlDscPreferredModule.ps1
line 123 at r3 (raw file):
) ) }
No sure we need this error since Get-SqlDscPreferredModule should throw an exception prior to this (see previous comment). 🤔 Do you agree?
Code quote:
$loadedModuleCalculatedVersion = $loadedModule | Get-SMOModuleCalculatedVersion
$availableModuleCalculatedVersion = $availableModule | Get-SMOModuleCalculatedVersion
if ($env:SMODefaultModuleVersion -and $loadedModuleCalculatedVersion -ne $availableModuleCalculatedVersion)
{
$PSCmdlet.ThrowTerminatingError(
[System.Management.Automation.ErrorRecord]::new(
($script:localizedData.PreferredModule_WrongModuleVersionLoaded -f $loadedModule.Name, $loadedModule.Version, $availableModule.Version),
'ISDPM0001', # cspell: disable-line
[System.Management.Automation.ErrorCategory]::ObjectNotFound,
'PreferredModule'
)
)
}
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Reviewable status: all files reviewed, 2 unresolved discussions (waiting on @johlju)
source/Public/Import-SqlDscPreferredModule.ps1
line 114 at r1 (raw file):
Previously, johlju (Johan Ljunggren) wrote…
My bad, it seems there was a bug prior to this change that should have thrown, see previous comment.
Got it! Thanks. Will make the update
source/Public/Import-SqlDscPreferredModule.ps1
line 73 at r3 (raw file):
Previously, johlju (Johan Ljunggren) wrote…
This should be called with
-ErrorAction 'Stop'
so that it throws if the wrong version is found, or if no module is found at all. This seems like this was a bug prior to this change.
ah right, that will make Write-Error in Get-SqlDscPreferredModule a terminating error. Got it!
source/Public/Import-SqlDscPreferredModule.ps1
line 123 at r3 (raw file):
Previously, johlju (Johan Ljunggren) wrote…
No sure we need this error since Get-SqlDscPreferredModule should throw an exception prior to this (see previous comment). 🤔 Do you agree?
Agreed!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks!
Reviewable status: all files reviewed, 2 unresolved discussions (waiting on @johlju)
source/Public/Import-SqlDscPreferredModule.ps1
line 73 at r3 (raw file):
Previously, YaroBear (Yaroslav Berejnoi) wrote…
ah right, that will make Write-Error in Get-SqlDscPreferredModule a terminating error. Got it!
Just checking, instead of the if/else we should now wrap Get-SqlDscPreferredModule
in a try/catch with the $PSCmdlet.ThrowTerminating error in the catch body?
Previously, YaroBear (Yaroslav Berejnoi) wrote…
There are already and error thrown for us in the Get-command (the command Write-Error) so by adding ErrorAction to our call here we make sure that when the Write-Error is executed we get a terminating error. So no need for a try/catch here and no need for adding any kind of extra throw in the import-command . The logic for checking for the wrong version is in the get function and it is that function that throws an error. A user can igonore the error in the Get-command by adding Hope that is more clear what I meant. 😊 |
@johlju Were you able to check my last commit? Now that the Get command has an Are we getting rid of $PSCmdlet.ThrowTerminatingError in the Import command entirely now so that it's a script terminating error? Sorry for confusion, just double checking 😅 |
No worries, glad you discussion and double checking so we get it correct. After testing running the code I do like your change so let's keep it! 🙂 But not sure what you mean by script terminating error? Can you explain what you meant by "script terminating error" so I understand what you meant for the future? 🙂 If we call PS> Get-SqlDscPreferredModule -ErrorAction stop
Get-SqlDscPreferredModule : No preferred PowerShell module was found.
At line:1 char:1
+ Get-SqlDscPreferredModule -ErrorAction stop
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : ObjectNotFound: (SqlServer, SQLPS:String) [Write-Error], WriteErrorException
+ FullyQualifiedErrorId : GSDPM0001,Get-SqlDscPreferredModule Running PS> Import-SqlDscPreferredModule
Import-SqlDscPreferredModule : Failed to find a dependent module. Unable to run SQL Server commands or use SQL Server types. Please install one of the preferred SMO modules or the SQLPS module, then try to import SqlServerDsc again.
At line:1 char:1
+ Import-SqlDscPreferredModule
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : ObjectNotFound: (PreferredModule:String) [Import-SqlDscPreferredModule], Exception
+ FullyQualifiedErrorId : ISDPM0001,Import-SqlDscPreferredModule The only difference (except for the error message and command name) is the category information were it says |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Reviewed 4 of 4 files at r4, all commit messages.
Reviewable status: complete! all files reviewed, all discussions resolved (waiting on @YaroBear)
Awesome work on this @YaroBear. I kicked off the failed tests again (they fail because the free Microsoft-hosted runners are to slow at times). Will merge once they passes. |
Awesome, thanks for working with me and having the great discussions! I wasn't aware of the difference between I got the information from this stackoverflow. So it might not be accurate as it's not in the official PowerShell docs:
|
I learned something new today too. But that article does not mention |
This portion of CONTRIBUTING.md specifies:
Although we are not setting So I think we have to have the try/catch block on Example code:
function Invoke-WriteError {
[CmdletBinding()]
param (
)
Write-Error 'An error occured'
}
function Invoke-ThrowTerminatingError() {
[CmdletBinding()]
param (
)
$PScmdlet.ThrowTerminatingError(
[System.Management.Automation.ErrorRecord]::new(
('Command terminating error'),
'ISDPM0001',
[System.Management.Automation.ErrorCategory]::InvalidOperation,
'CommandTerminatingError'
))
}
function Invoke-WriteErrorWithErrorActionStop() {
Invoke-WriteError -ErrorAction 'Stop'
Write-Host "This line should not be reached"
}
function Invoke-ThrowTerminatingErrorAndLog() {
Invoke-ThrowTerminatingError
Write-Host "This line should be reached"
} |
I did not know that But if the above function 'Invoke-ThrowTerminatingErrorAndLog` is made like this: function Invoke-ThrowTerminatingErrorAndLog {
[CmdletBinding()]
param (
)
Invoke-ThrowTerminatingError
Write-Host "This line should be reached"
} Then called with
But that requires that the user actually knows to pass in |
Testing this with a pipeline this is how I would expected it to work function Invoke-PipelineThrow
{
[CmdletBinding()]
param
(
[Parameter(Mandatory = $true, ValueFromPipeline = $true)]
[System.String]
$MyStringValue
)
process
{
Write-Host ("Start PROCESS for value '{0}'" -f $MyStringValue)
if ($MyStringValue -eq 'ThrowWriteError')
{
Write-Error -Message ("Non-terminating error for value '{0}'" -f $MyStringValue)
}
if ($MyStringValue -eq 'ThrowTerminatingError')
{
$PScmdlet.ThrowTerminatingError(
[System.Management.Automation.ErrorRecord]::new(
("Terminating error for value '{0}'" -f $MyStringValue),
'UniqueID',
[System.Management.Automation.ErrorCategory]::InvalidOperation,
$MyStringValue
))
}
Write-Host ("Ran all of PROCESS for value '{0}'" -f $MyStringValue)
}
end
{
Write-Host "Reached END"
}
} Running this we see that PS > 'FirstValue', 'SecondValue', 'ThrowWriteError', 'ThrowTerminatingError', 'LastValue' | Invoke-PipelineThrow
Start PROCESS for value 'FirstValue'
Ran all of PROCESS for value 'FirstValue'
Start PROCESS for value 'SecondValue'
Ran all of PROCESS for value 'SecondValue'
Start PROCESS for value 'ThrowWriteError'
Invoke-PipelineThrow: Non-terminating error for value 'ThrowWriteError'
Ran all of PROCESS for value 'ThrowWriteError'
Start PROCESS for value 'ThrowTerminatingError'
Invoke-PipelineThrow: Terminating error for value 'ThrowTerminatingError' Even if we pass in PS > 'FirstValue', 'SecondValue', 'ThrowWriteError', 'ThrowTerminatingError', 'LastValue' | Invoke-PipelineThrow -ErrorAction 'SilentlyContinue'
Start PROCESS for value 'FirstValue'
Ran all of PROCESS for value 'FirstValue'
Start PROCESS for value 'SecondValue'
Ran all of PROCESS for value 'SecondValue'
Start PROCESS for value 'ThrowWriteError'
Ran all of PROCESS for value 'ThrowWriteError'
Start PROCESS for value 'ThrowTerminatingError'
Invoke-PipelineThrow: Terminating error for value 'ThrowTerminatingError' If we pass in PS > 'FirstValue', 'SecondValue', 'ThrowWriteError', 'ThrowTerminatingError', 'LastValue' | Invoke-PipelineThrow -ErrorAction 'Stop'
Start PROCESS for value 'FirstValue'
Ran all of PROCESS for value 'FirstValue'
Start PROCESS for value 'SecondValue'
Ran all of PROCESS for value 'SecondValue'
Start PROCESS for value 'ThrowWriteError'
Invoke-PipelineThrow: Non-terminating error for value 'ThrowWriteError' Non of these will reach So I guess one must use |
Pull Request (PR) description
Optionally set the
SMODefaultModuleVersion
environment variable for your preferred module.For example:
This Pull Request (PR) fixes the following issues
Get-SqlDscPreferredModule
: Should be possible to specify which version of the SqlServer module is imported #1965Task list
file CHANGELOG.md. Entry should say what was changed and how that
affects users (if applicable), and reference the issue being resolved
(if applicable).
This change is