Adds Port parameter for SSH PSSessions#3499
Conversation
| } | ||
| catch | ||
| { | ||
| $_.FullyQualifiedErrorId | Should Match "PSArgumentNullException" |
There was a problem hiding this comment.
Please use ShouldBeErrorId
Sample https://github.com/PowerShell/PowerShell/blob/master/test/powershell/Modules/Microsoft.PowerShell.Core/Job.Tests.ps1#L28
| catch | ||
| { | ||
| $_.FullyQualifiedErrorId | Should Be "PSArgumentNullException" | ||
| $_.FullyQualifiedErrorId | Should Match "PSArgumentNullException" |
There was a problem hiding this comment.
Please use ShouldBeErrorId
| $expectedFileNotFoundExecption = $_.Exception.InnerException.InnerException | ||
| } | ||
|
|
||
| ($expectedFileNotFoundExecption.GetType().FullName) | Should Be "System.IO.FileNotFoundException" |
There was a problem hiding this comment.
Your code may raise null reference exception.
Why do you not use BeOfType?
| $expectedArgumentException = $_.Exception.InnerException | ||
| } | ||
|
|
||
| ($expectedArgumentException.GetType().FullName) | Should Be "System.ArgumentException" |
| $rs = [runspacefactory]::CreateRunspace($sshConnectionInfo) | ||
| $rs.Open() | ||
|
|
||
| throw "SSHConnectionInfo did not throw expected ArgumentException exception" |
There was a problem hiding this comment.
The same about throw "No Exception!"
| $rs.Open() | ||
|
|
||
| throw "No Exception!" | ||
| throw "SSHConnectionInfo did not throw expected FileNotFoundException exception" |
There was a problem hiding this comment.
Please use throw "No Exception!" - it is our documented template
There was a problem hiding this comment.
Merge fail on my behalf! Will fix.
|
Fail merge from master on my behalf with tests, will push a fix later |
|
Reverted SSHRemotingAPI.Tests.ps1 to master, added back in new test, and made requested changes to existing tests |
| string computerName, | ||
| string keyFilePath) | ||
| string keyFilePath, | ||
| int port) |
There was a problem hiding this comment.
We shouldn't change a public API.
Preferred way is to add new constructor.
| $expectedFileNotFoundException = $_.Exception.InnerException.InnerException | ||
| } | ||
|
|
||
| $expectedFileNotFoundException | Should BeOfType "System.IO.FileNotFoundException" |
There was a problem hiding this comment.
I believe that there is no need to examine the properties, we can get an undesirable exception only when a method is called (ex., $prop.GetType()).
Here we can make the test more graceful and use ShouldBeErrorId too:
$exc = {
$sshConnectionInfo = [System.Management.Automation.Runspaces.SSHConnectionInfo]::new(
"UserName",
"localhost",
"NoValidKeyFilePath",
22)
$rs = [runspacefactory]::CreateRunspace($sshConnectionInfo)
$rs.Open()
} | ShouldBeErrorId "PSRemotingDataStructureException"
$exc.Exception.InnerException.InnerException | Should BeOfType System.IO.FileNotFoundExceptionAlso the tests skip a cleanup code. Please add something like:
AfterEach {
if ($rs -ne $null) { $rs.Dispose() }
}There was a problem hiding this comment.
It would appear that ShouldBeErrorId doesn't return the thrown exception, so we would be unable to process $exc further here (ShouldBeErrorId will return result of Should Be - bool).
Based on this, would you prefer for ShouldBeErrorId to be implemented here to check for PSRemotingDataStructureException, or continue to inspect the nested inner exception?
There was a problem hiding this comment.
Cleanup code added
|
I haven't had a chance to look at this yet. But will review by EOD Wednesday (4/12). |
| /// to override the policy setting | ||
| /// </remarks> | ||
| [Parameter(ParameterSetName = PSRemotingBaseCmdlet.ComputerNameParameterSet)] | ||
| [Parameter(ParameterSetName = PSRemotingBaseCmdlet.SSHHostParameterSet)] |
There was a problem hiding this comment.
I am a little worried about sharing parameters between WinRM and SSH parameter sets. My worry is that PowerShell parameter resolution might resolve to the wrong parameter set for positional parameters (e.g., New-PSSession localhost -Port 8080). Please verify that parameter set resolution does not change (i.e. causes a breaking change to existing behavior).
There was a problem hiding this comment.
If this turns out to be a problem we can think about adding a new SSH specific parameter set port parameter (SSHPort), but hopefully we won't have to.
In reply to: 111206290 [](ancestors = 111206290)
There was a problem hiding this comment.
Just tested this, confirmed to default to ComputerNameParameterSet:
PS C:\Users\Lee\Documents\PowerShell> New-PSSession localhost -Port 5985
Id Name ComputerName ComputerType State ConfigurationName Availability
-- ---- ------------ ------------ ----- ----------------- ------------
11 WinRM11 localhost RemoteMachine Opened Microsoft.PowerShell Available
Not sure whether DefaultParameterSetName would help here to remove any uncertainty?
There was a problem hiding this comment.
It looks like we are Ok. I assume that this works and doesn't result in a "cannot resolve parameter set" error?
New-PSSession -HostName localhost -Port 22Also please check this again when you implement Invoke-Command SSH parameter sets
Invoke-Command -HostName localhost -Port 22 -ScriptBlock { "Hello" }
Invoke-Command localhost { "Hello" } -Port 5895I think this will also be Ok but it does have more complex parameter sets.
There was a problem hiding this comment.
Parameter set looks to be resolving correctly:
PS C:\Users\Lee\Projects\PowerShell> New-PSSession -HostName localhost -Port 22
Lee@DESKTOP-0I6KRV7@localhost's password:
There was a problem hiding this comment.
I've confirmed this to be resolving correctly following implementation for Invoke-Command:
PS C:\Users\Lee\Projects\PowerShell> Invoke-Command -HostName localhost -Port 22 -ScriptBlock { "Hello" }
Lee@DESKTOP-0I6KRV7@localhost's password:
PS C:\Users\Lee\Projects\PowerShell> Invoke-Command localhost { "Hello" } -Port 5985
Hello
There was a problem hiding this comment.
Great news. I am looking forward to the next commit.
| { | ||
| connectionInfo.KeyFilePath = paramValue; | ||
| } | ||
| else if (paramName.Equals(PortParameter, StringComparison.OrdinalIgnoreCase)) |
There was a problem hiding this comment.
I think the Port parameter in the hash table should be an integer and not a string. It would be more natural than enclosing the port value in quotes: @{ HostName = 'targetMachine'; UserName = 'UserA'; Port = 8080 }. This would mean updating this parse method to special case the Port parameter.
|
|
||
| foreach (string computerName in ResolvedComputerNames) | ||
| { | ||
| var sshConnectionInfo = new SSHConnectionInfo(this.UserName, computerName, this.KeyFilePath); |
There was a problem hiding this comment.
These creation helper functions are used by Invoke-Command, and this cmdlet will also have to be updated to support the new "Port" parameter. All cmdlets that currently support SSH parameter sets are New-PSSession, Enter-PSSession, Invoke-Command. You covered the first two but missed the last one.
| PSRemotingErrorInvariants.FormatResourceString( | ||
| RemotingErrorIdStrings.PortIsOutOfRange, port); | ||
| ArgumentException e = new ArgumentException(message); | ||
| throw e; |
There was a problem hiding this comment.
Since this is duplicate code from WSManConnectionInfo, can we put this in a helper method in the base RunspaceConnectionInfo class?
| string userName, | ||
| string computerName, | ||
| string keyFilePath, | ||
| int port) |
There was a problem hiding this comment.
Please use the original constructor so as not to duplicate code:
public SSHConnectionInfo(string userName, string computerName, string keyFilePath, int port) : this(userName, computerName, keyFilePath)
{
if ((port < MinPort) || (port > MaxPort))
{
...
}
this.Port = (port != 0) port : DefaultPort;
}
PaulHigin
left a comment
There was a problem hiding this comment.
Please see my comments for requested changes.
|
I'll action this tomorrow evening :) |
…er constructor overload to use original constructor
|
All requested changes made :) |
|
|
||
| string paramValue = item[paramName] as string; | ||
| if (string.IsNullOrEmpty(paramValue)) | ||
| if (paramName.Equals(PortParameter, StringComparison.OrdinalIgnoreCase)) |
There was a problem hiding this comment.
I didn't like how it was done in code below. We should use the previous style of if-else if.
We could even use C# 7.0 patterns.
/cc @daxian-dbw Are we ready to use the patterns?
There was a problem hiding this comment.
Are you referring to the ParseSSHConnectionHashTable() @iSazonov?
There was a problem hiding this comment.
I means that you add extra if-else level with the port parameter.
There was a problem hiding this comment.
Ahh, I did this as we now need to handle InvalidSSHConnectionParameter for value types (integer), wereas previously we would check whether paramValue (as string) was null.
The only reason I added the extra level of if-else was to handle the integer/string values differently, although I do agree that it isn't ideal :(
I'll take another look at this later.
There was a problem hiding this comment.
I agree with @iSazonov, it would be nice to keep the if-else-if blocks. You can create a helper method to get a string or int parameter and throw exception a needed.
private static string GetStringParameter(object param)
{
var paramValue = param as string;
if (string.IsNullOrEmpty(paramValue)) { throw new PSArgumentException("..."); }
return paramValue;
}
private static int GetIntParameter(object param)
{
int? paramValue = param as int?;
if (paramValue == null) { throw new PSArgumentException("..."); }
return paramValue.Value;
}
if (paramName.Equals(PortParameter, StringComparison.OridinalIgnoreCase))
{
connectionInfo.Port = GetIntParameter(item[paramName]);
}
else if ...
We don't need the Convert.ToInt32() and I believe the integer is only un-boxed once.
There was a problem hiding this comment.
I've just added a commit to address this - much cleaner :)
… methods for retrieving SSHConnection hashtable values
|
|
||
| return paramValue.Value; | ||
| } | ||
|
|
There was a problem hiding this comment.
What about C# 7.0 patterns:
private static string GetSSHConnectionStringParameter(object param)
{
if (param is string paramValue && string.IsNullOrEmpty(paramValue))
{
return paramValue;
}
throw new PSArgumentException(RemotingErrorIdStrings.InvalidSSHConnectionParameter);
}
private static int GetSSHConnectionIntParameter(object param)
{
if (param is int paramValue)
{
return paramValue;
}
throw new PSArgumentException(RemotingErrorIdStrings.InvalidSSHConnectionParameter);
}There was a problem hiding this comment.
I like it, but is this supported in are targeted runtimes?
There was a problem hiding this comment.
I can not still build locally after move to Core2.0. Maybe @daxian-dbw confirm.
There was a problem hiding this comment.
I am able to build with Core 2.0 and this pattern builds successfully. So I think we are good to use it.
There was a problem hiding this comment.
C#7 is supported by the latest dotnet-cli and VS2017, so we are good here.
There was a problem hiding this comment.
I've added another commit to add C# 7 pattern goodness, sorry for the delay!
|
@PaulHigin Have all of your concerns been addressed? If not, please call out any remaining issues. |
|
@lee303 - Thanks for the contribution. I'll merge it once CI passes |
Ref #3475
This pull request adds support for the Port parameter when establishing a new SSH PSSession.
Examples:
New-PSSession -HostName "localhost" -Port 8080New-PSSession -SSHConnection @{"ComputerName"="localhost";"Port"="8080"}