From f9cb740e8d3d54f1b4ab61fe8d7aed684db820f5 Mon Sep 17 00:00:00 2001 From: Paul Higinbotham Date: Tue, 6 Apr 2021 12:32:16 -0700 Subject: [PATCH 01/22] Fix SSH remoting connection hang with misconfigured endpoint --- .../fanin/OutOfProcTransportManager.cs | 114 +++++++++++++----- .../resources/RemotingErrorIdStrings.resx | 3 + 2 files changed, 87 insertions(+), 30 deletions(-) diff --git a/src/System.Management.Automation/engine/remoting/fanin/OutOfProcTransportManager.cs b/src/System.Management.Automation/engine/remoting/fanin/OutOfProcTransportManager.cs index b8f6da13c17..be3aefd84db 100644 --- a/src/System.Management.Automation/engine/remoting/fanin/OutOfProcTransportManager.cs +++ b/src/System.Management.Automation/engine/remoting/fanin/OutOfProcTransportManager.cs @@ -635,30 +635,7 @@ internal override void Dispose(bool isDisposing) { _cmdTransportManagers.Clear(); _closeTimeOutTimer.Dispose(); - - // Stop session processing thread. - try - { - _sessionMessageQueue.CompleteAdding(); - } - catch (ObjectDisposedException) - { - // Object already disposed. - } - - _sessionMessageQueue.Dispose(); - - // Stop command processing thread. - try - { - _commandMessageQueue.CompleteAdding(); - } - catch (ObjectDisposedException) - { - // Object already disposed. - } - - _commandMessageQueue.Dispose(); + DisposeMessageQueue(); } } @@ -1055,6 +1032,37 @@ internal void OnCloseTimeOutTimerElapsed(object source) } #endregion + + #region Protected Methods + + protected void DisposeMessageQueue() + { + // Stop session processing thread. + try + { + _sessionMessageQueue.CompleteAdding(); + } + catch (ObjectDisposedException) + { + // Object already disposed. + } + + _sessionMessageQueue.Dispose(); + + // Stop command processing thread. + try + { + _commandMessageQueue.CompleteAdding(); + } + catch (ObjectDisposedException) + { + // Object already disposed. + } + + _commandMessageQueue.Dispose(); + } + + #endregion } internal class OutOfProcessClientSessionTransportManager : OutOfProcessClientSessionTransportManagerBase @@ -1584,6 +1592,7 @@ internal sealed class SSHClientSessionTransportManager : OutOfProcessClientSessi private StreamReader _stdOutReader; private StreamReader _stdErrReader; private bool _connectionEstablished; + private Timer _sshProcessTimer; private const string _threadName = "SSHTransport Reader Thread"; @@ -1641,6 +1650,43 @@ internal override void CreateAsync() // Create reader thread and send first PSRP message. StartReaderThread(_stdOutReader); + + // Monitor SSH process until connection is established. + _sshProcessTimer = new Timer( + callback: (_) => + { + if (_connectionEstablished) + { + // Monitor no longer needed. + _sshProcessTimer?.Change(Timeout.Infinite, Timeout.Infinite); + return; + } + + bool sshTerminated = false; + try + { + // Detect if SSH client process terminates prematurely. + using (var sshProcess = System.Diagnostics.Process.GetProcessById(_sshProcessId)) + { + sshTerminated = sshProcess == null || sshProcess.Handle == IntPtr.Zero || sshProcess.HasExited; + } + } + catch + { + sshTerminated = true; + } + + if (sshTerminated) + { + _sshProcessTimer?.Change(Timeout.Infinite, Timeout.Infinite); + _sshProcessId = 0; + HandleSSHError( + new PSRemotingTransportException(RemotingErrorIdStrings.SSHClientCannotConnect)); + } + }, + state: null, + dueTime: 100, // 100 mSec + period: 100); // Repeat indefinitely } internal override void CloseAsync() @@ -1660,14 +1706,20 @@ internal override void CloseAsync() private void CloseConnection() { + // Ensure message queue is disposed. + base.DisposeMessageQueue(); + + var sshProcessTimer = Interlocked.Exchange(ref _sshProcessTimer, null); + sshProcessTimer?.Dispose(); + var stdInWriter = Interlocked.Exchange(ref _stdInWriter, null); - if (stdInWriter != null) { stdInWriter.Dispose(); } + stdInWriter?.Dispose(); var stdOutReader = Interlocked.Exchange(ref _stdOutReader, null); - if (stdOutReader != null) { stdOutReader.Dispose(); } + stdOutReader?.Dispose(); var stdErrReader = Interlocked.Exchange(ref _stdErrReader, null); - if (stdErrReader != null) { stdErrReader.Dispose(); } + stdErrReader?.Dispose(); // The CloseConnection() method can be called multiple times from multiple places. // Set the _sshProcessId to zero here so that we go through the work of finding @@ -1677,10 +1729,12 @@ private void CloseConnection() { try { - var sshProcess = System.Diagnostics.Process.GetProcessById(sshProcessId); - if ((sshProcess != null) && (sshProcess.Handle != IntPtr.Zero) && !sshProcess.HasExited) + using (var sshProcess = System.Diagnostics.Process.GetProcessById(sshProcessId)) { - sshProcess.Kill(); + if ((sshProcess != null) && (sshProcess.Handle != IntPtr.Zero) && !sshProcess.HasExited) + { + sshProcess.Kill(); + } } } catch (ArgumentException) { } diff --git a/src/System.Management.Automation/resources/RemotingErrorIdStrings.resx b/src/System.Management.Automation/resources/RemotingErrorIdStrings.resx index b4f9dc40a89..9af92df73df 100644 --- a/src/System.Management.Automation/resources/RemotingErrorIdStrings.resx +++ b/src/System.Management.Automation/resources/RemotingErrorIdStrings.resx @@ -1625,6 +1625,9 @@ All WinRM sessions connected to PowerShell session configurations, such as Micro The SSH client session has ended with error message: {0} + + The SSH client cannot connect to the target. This may be because the target endpoint 'sshd_config' file is misconfigured. + The provided SSHConnection hashtable is missing the required ComputerName or HostName parameter. From 25a3ac767e25991552a3b2ea4df0c37dce30e23b Mon Sep 17 00:00:00 2001 From: Paul Higinbotham Date: Fri, 9 Apr 2021 11:07:14 -0700 Subject: [PATCH 02/22] Add support for SSH connection attempt timeout --- .../remoting/commands/InvokeCommandCommand.cs | 24 +++ .../remoting/commands/PSRemotingCmdlet.cs | 37 ++-- .../remoting/commands/PushRunspaceCommand.cs | 2 +- .../remoting/commands/newrunspacecommand.cs | 6 +- .../remoting/common/RunspaceConnectionInfo.cs | 79 +++++--- .../fanin/OutOfProcTransportManager.cs | 34 ++-- .../resources/RemotingErrorIdStrings.resx | 7 +- test/SSHRemoting/SSHRemoting.Basic.Tests.ps1 | 172 ++++++++++++++++-- 8 files changed, 287 insertions(+), 74 deletions(-) diff --git a/src/System.Management.Automation/engine/remoting/commands/InvokeCommandCommand.cs b/src/System.Management.Automation/engine/remoting/commands/InvokeCommandCommand.cs index b60f3131f24..c86f7903a1a 100644 --- a/src/System.Management.Automation/engine/remoting/commands/InvokeCommandCommand.cs +++ b/src/System.Management.Automation/engine/remoting/commands/InvokeCommandCommand.cs @@ -737,6 +737,30 @@ public override string KeyFilePath set { base.KeyFilePath = value; } } + /// + /// Gets and sets a value for the SSH subsystem to use for the remote connection. + /// + [Parameter(ParameterSetName = InvokeCommandCommand.SSHHostParameterSet)] + [Parameter(ParameterSetName = InvokeCommandCommand.FilePathSSHHostParameterSet)] + public override string Subsystem + { + get { return base.Subsystem; } + + set { base.Subsystem = value; } + } + + /// + /// Gets and sets a value in milliseconds that limits the time allowed for an SSH connection to be established. + /// + [Parameter(ParameterSetName = InvokeCommandCommand.SSHHostParameterSet)] + [Parameter(ParameterSetName = InvokeCommandCommand.FilePathSSHHostParameterSet)] + public override int ConnectingTimeout + { + get { return base.ConnectingTimeout; } + + set { base.ConnectingTimeout = value; } + } + /// /// This parameter specifies that SSH is used to establish the remote /// connection and act as the remoting transport. By default WinRM is used diff --git a/src/System.Management.Automation/engine/remoting/commands/PSRemotingCmdlet.cs b/src/System.Management.Automation/engine/remoting/commands/PSRemotingCmdlet.cs index 9fafd34eec4..9a6082d1012 100644 --- a/src/System.Management.Automation/engine/remoting/commands/PSRemotingCmdlet.cs +++ b/src/System.Management.Automation/engine/remoting/commands/PSRemotingCmdlet.cs @@ -285,6 +285,7 @@ internal struct SSHConnection public string KeyFilePath; public int Port; public string Subsystem; + public int ConnectingTimeout; } /// @@ -761,6 +762,19 @@ public virtual string KeyFilePath set; } + /// + /// Gets and sets a value for the SSH subsystem to use for the remote connection. + /// + [Parameter(ValueFromPipelineByPropertyName = true, + ParameterSetName = PSRemotingBaseCmdlet.SSHHostParameterSet)] + public virtual string Subsystem { get; set; } + + /// + /// Gets and sets a value in milliseconds that limits the time allowed for an SSH connection to be established. + /// + [Parameter(ParameterSetName = PSRemotingBaseCmdlet.SSHHostParameterSet)] + public virtual int ConnectingTimeout { get; set; } = -1; // No timeout by default + /// /// This parameter specifies that SSH is used to establish the remote /// connection and act as the remoting transport. By default WinRM is used @@ -789,13 +803,6 @@ public virtual Hashtable[] SSHConnection set; } - /// - /// This parameter specifies the SSH subsystem to use for the remote connection. - /// - [Parameter(ValueFromPipelineByPropertyName = true, - ParameterSetName = InvokeCommandCommand.SSHHostParameterSet)] - public virtual string Subsystem { get; set; } - #endregion #endregion Properties @@ -856,6 +863,7 @@ internal static void ValidateSpecifiedAuthentication(PSCredential credential, st private const string IdentityFilePathAlias = "IdentityFilePath"; private const string PortParameter = "Port"; private const string SubsystemParameter = "Subsystem"; + private const string ConnectingTimeoutParameter = "ConnectingTimeout"; #endregion @@ -902,7 +910,7 @@ protected void ParseSshHostName(string hostname, out string host, out string use /// Array of SSHConnection objects. internal SSHConnection[] ParseSSHConnectionHashTable() { - List connections = new List(); + var connections = new List(); foreach (var item in this.SSHConnection) { if (item.ContainsKey(ComputerNameParameter) && item.ContainsKey(HostNameAlias)) @@ -915,7 +923,7 @@ internal SSHConnection[] ParseSSHConnectionHashTable() throw new PSArgumentException(RemotingErrorIdStrings.SSHConnectionDuplicateKeyPath); } - SSHConnection connectionInfo = new SSHConnection(); + var connectionInfo = new SSHConnection(); foreach (var key in item.Keys) { string paramName = key as string; @@ -955,6 +963,10 @@ internal SSHConnection[] ParseSSHConnectionHashTable() { connectionInfo.Subsystem = GetSSHConnectionStringParameter(item[paramName]); } + else if (paramName.Equals(ConnectingTimeoutParameter, StringComparison.OrdinalIgnoreCase)) + { + connectionInfo.ConnectingTimeout = GetSSHConnectionIntParameter(item[paramName]); + } else { throw new PSArgumentException( @@ -1448,9 +1460,9 @@ protected void CreateHelpersForSpecifiedSSHComputerNames() { ParseSshHostName(computerName, out string host, out string userName, out int port); - var sshConnectionInfo = new SSHConnectionInfo(userName, host, this.KeyFilePath, port, this.Subsystem); + var sshConnectionInfo = new SSHConnectionInfo(userName, host, KeyFilePath, port, Subsystem, ConnectingTimeout); var typeTable = TypeTable.LoadDefaultTypeFiles(); - var remoteRunspace = RunspaceFactory.CreateRunspace(sshConnectionInfo, this.Host, typeTable) as RemoteRunspace; + var remoteRunspace = RunspaceFactory.CreateRunspace(sshConnectionInfo, Host, typeTable) as RemoteRunspace; var pipeline = CreatePipeline(remoteRunspace); var operation = new ExecutionCmdletHelperComputerName(remoteRunspace, pipeline); @@ -1471,7 +1483,8 @@ protected void CreateHelpersForSpecifiedSSHHashComputerNames() sshConnection.ComputerName, sshConnection.KeyFilePath, sshConnection.Port, - sshConnection.Subsystem); + sshConnection.Subsystem, + sshConnection.ConnectingTimeout); var typeTable = TypeTable.LoadDefaultTypeFiles(); var remoteRunspace = RunspaceFactory.CreateRunspace(sshConnectionInfo, this.Host, typeTable) as RemoteRunspace; var pipeline = CreatePipeline(remoteRunspace); diff --git a/src/System.Management.Automation/engine/remoting/commands/PushRunspaceCommand.cs b/src/System.Management.Automation/engine/remoting/commands/PushRunspaceCommand.cs index cbc5f82742b..996b043d77e 100644 --- a/src/System.Management.Automation/engine/remoting/commands/PushRunspaceCommand.cs +++ b/src/System.Management.Automation/engine/remoting/commands/PushRunspaceCommand.cs @@ -1262,7 +1262,7 @@ private RemoteRunspace GetRunspaceForContainerSession() private RemoteRunspace GetRunspaceForSSHSession() { ParseSshHostName(HostName, out string host, out string userName, out int port); - var sshConnectionInfo = new SSHConnectionInfo(userName, host, this.KeyFilePath, port, this.Subsystem); + var sshConnectionInfo = new SSHConnectionInfo(userName, host, KeyFilePath, port, Subsystem, ConnectingTimeout); var typeTable = TypeTable.LoadDefaultTypeFiles(); // Use the class _tempRunspace field while the runspace is being opened so that StopProcessing can be handled at that time. diff --git a/src/System.Management.Automation/engine/remoting/commands/newrunspacecommand.cs b/src/System.Management.Automation/engine/remoting/commands/newrunspacecommand.cs index 625bb86fd64..d4a0a3e7508 100644 --- a/src/System.Management.Automation/engine/remoting/commands/newrunspacecommand.cs +++ b/src/System.Management.Automation/engine/remoting/commands/newrunspacecommand.cs @@ -1092,7 +1092,8 @@ private List CreateRunspacesForSSHHostParameterSet() host, this.KeyFilePath, port, - Subsystem); + Subsystem, + ConnectingTimeout); var typeTable = TypeTable.LoadDefaultTypeFiles(); string rsName = GetRunspaceName(index, out int rsIdUnused); index++; @@ -1118,7 +1119,8 @@ private List CreateRunspacesForSSHHostHashParameterSet() sshConnection.ComputerName, sshConnection.KeyFilePath, sshConnection.Port, - sshConnection.Subsystem); + sshConnection.Subsystem, + sshConnection.ConnectingTimeout); var typeTable = TypeTable.LoadDefaultTypeFiles(); string rsName = GetRunspaceName(index, out int rsIdUnused); index++; diff --git a/src/System.Management.Automation/engine/remoting/common/RunspaceConnectionInfo.cs b/src/System.Management.Automation/engine/remoting/common/RunspaceConnectionInfo.cs index ea8a9c1a9d0..b0a88eebe9b 100644 --- a/src/System.Management.Automation/engine/remoting/common/RunspaceConnectionInfo.cs +++ b/src/System.Management.Automation/engine/remoting/common/RunspaceConnectionInfo.cs @@ -1907,6 +1907,16 @@ internal override BaseClientSessionTransportManager CreateClientSessionTransport /// public sealed class SSHConnectionInfo : RunspaceConnectionInfo { + #region Constants + + /// + /// Default value for subsystem. + /// + private const string DefaultSubsystem = "powershell"; + private const int DefaultConnectingTimeoutTime = -1; // Never time out + + #endregion + #region Properties /// @@ -1945,6 +1955,16 @@ private string Subsystem set; } + /// + /// Gets or sets a time in milliseconds after which a connection attempt is terminated. + /// Default value (-1) never times out and a connection attempt waits indefinitely. + /// + public int ConnectingTimeout + { + get; + set; + } + #endregion #region Constructors @@ -1968,11 +1988,12 @@ public SSHConnectionInfo( { if (computerName == null) { throw new PSArgumentNullException(nameof(computerName)); } - this.UserName = userName; - this.ComputerName = computerName; - this.KeyFilePath = keyFilePath; - this.Port = 0; - this.Subsystem = DefaultSubsystem; + UserName = userName; + ComputerName = computerName; + KeyFilePath = keyFilePath; + Port = 0; + Subsystem = DefaultSubsystem; + ConnectingTimeout = DefaultConnectingTimeoutTime; } /// @@ -1989,8 +2010,7 @@ public SSHConnectionInfo( int port) : this(userName, computerName, keyFilePath) { ValidatePortInRange(port); - - this.Port = port; + Port = port; } /// @@ -2006,12 +2026,29 @@ public SSHConnectionInfo( string computerName, string keyFilePath, int port, - string subsystem) : this(userName, computerName, keyFilePath) + string subsystem) : this(userName, computerName, keyFilePath, port) { - ValidatePortInRange(port); + Subsystem = (string.IsNullOrEmpty(subsystem)) ? DefaultSubsystem : subsystem; + } - this.Port = port; - this.Subsystem = (string.IsNullOrEmpty(subsystem)) ? DefaultSubsystem : subsystem; + /// + /// Constructor. + /// + /// User Name. + /// Computer Name. + /// Key File Path. + /// Port number for connection (default 22). + /// Subsystem to use (default 'powershell'). + /// Timeout time for terminating connection attempt. + public SSHConnectionInfo( + string userName, + string computerName, + string keyFilePath, + int port, + string subsystem, + int connectingTimeout) : this(userName, computerName, keyFilePath, port, subsystem) + { + ConnectingTimeout = connectingTimeout; } #endregion @@ -2064,11 +2101,12 @@ public override string CertificateThumbprint internal override RunspaceConnectionInfo InternalCopy() { SSHConnectionInfo newCopy = new SSHConnectionInfo(); - newCopy.ComputerName = this.ComputerName; - newCopy.UserName = this.UserName; - newCopy.KeyFilePath = this.KeyFilePath; - newCopy.Port = this.Port; - newCopy.Subsystem = this.Subsystem; + newCopy.ComputerName = ComputerName; + newCopy.UserName = UserName; + newCopy.KeyFilePath = KeyFilePath; + newCopy.Port = Port; + newCopy.Subsystem = Subsystem; + newCopy.ConnectingTimeout = ConnectingTimeout; return newCopy; } @@ -2187,15 +2225,6 @@ internal int StartSSHProcess( #endregion - #region Constants - - /// - /// Default value for subsystem. - /// - private const string DefaultSubsystem = "powershell"; - - #endregion - #region SSH Process Creation #if UNIX diff --git a/src/System.Management.Automation/engine/remoting/fanin/OutOfProcTransportManager.cs b/src/System.Management.Automation/engine/remoting/fanin/OutOfProcTransportManager.cs index be3aefd84db..e698d831b6e 100644 --- a/src/System.Management.Automation/engine/remoting/fanin/OutOfProcTransportManager.cs +++ b/src/System.Management.Automation/engine/remoting/fanin/OutOfProcTransportManager.cs @@ -1592,7 +1592,7 @@ internal sealed class SSHClientSessionTransportManager : OutOfProcessClientSessi private StreamReader _stdOutReader; private StreamReader _stdErrReader; private bool _connectionEstablished; - private Timer _sshProcessTimer; + private Timer _connectionTimer; private const string _threadName = "SSHTransport Reader Thread"; @@ -1651,21 +1651,24 @@ internal override void CreateAsync() // Create reader thread and send first PSRP message. StartReaderThread(_stdOutReader); - // Monitor SSH process until connection is established. - _sshProcessTimer = new Timer( - callback: (_) => + if (_connectionInfo.ConnectingTimeout < 0) + { + return; + } + + // Start connection timeout timer if requested. + _connectionTimer = new Timer( + callback: (_) => { if (_connectionEstablished) { - // Monitor no longer needed. - _sshProcessTimer?.Change(Timeout.Infinite, Timeout.Infinite); return; } + // Detect if SSH client process terminates prematurely. bool sshTerminated = false; try { - // Detect if SSH client process terminates prematurely. using (var sshProcess = System.Diagnostics.Process.GetProcessById(_sshProcessId)) { sshTerminated = sshProcess == null || sshProcess.Handle == IntPtr.Zero || sshProcess.HasExited; @@ -1676,17 +1679,18 @@ internal override void CreateAsync() sshTerminated = true; } + var errorMessage = StringUtil.Format(RemotingErrorIdStrings.SSHClientConnectTimeout, _connectionInfo.ConnectingTimeout / 1000); if (sshTerminated) { - _sshProcessTimer?.Change(Timeout.Infinite, Timeout.Infinite); - _sshProcessId = 0; - HandleSSHError( - new PSRemotingTransportException(RemotingErrorIdStrings.SSHClientCannotConnect)); + errorMessage += "\n" + RemotingErrorIdStrings.SSHClientConnectProcessTerminated; } + + HandleSSHError( + new PSRemotingTransportException(errorMessage)); }, state: null, - dueTime: 100, // 100 mSec - period: 100); // Repeat indefinitely + dueTime: _connectionInfo.ConnectingTimeout, + period: Timeout.Infinite); } internal override void CloseAsync() @@ -1709,8 +1713,8 @@ private void CloseConnection() // Ensure message queue is disposed. base.DisposeMessageQueue(); - var sshProcessTimer = Interlocked.Exchange(ref _sshProcessTimer, null); - sshProcessTimer?.Dispose(); + var connectionTimer = Interlocked.Exchange(ref _connectionTimer, null); + connectionTimer?.Dispose(); var stdInWriter = Interlocked.Exchange(ref _stdInWriter, null); stdInWriter?.Dispose(); diff --git a/src/System.Management.Automation/resources/RemotingErrorIdStrings.resx b/src/System.Management.Automation/resources/RemotingErrorIdStrings.resx index 9af92df73df..664033e20bd 100644 --- a/src/System.Management.Automation/resources/RemotingErrorIdStrings.resx +++ b/src/System.Management.Automation/resources/RemotingErrorIdStrings.resx @@ -1625,8 +1625,11 @@ All WinRM sessions connected to PowerShell session configurations, such as Micro The SSH client session has ended with error message: {0} - - The SSH client cannot connect to the target. This may be because the target endpoint 'sshd_config' file is misconfigured. + + SSH connection attempt failed after time out: {0} seconds. + + + SSH client process terminated before connection could be established. The provided SSHConnection hashtable is missing the required ComputerName or HostName parameter. diff --git a/test/SSHRemoting/SSHRemoting.Basic.Tests.ps1 b/test/SSHRemoting/SSHRemoting.Basic.Tests.ps1 index 40fa0ec1027..c6dfa286ab5 100644 --- a/test/SSHRemoting/SSHRemoting.Basic.Tests.ps1 +++ b/test/SSHRemoting/SSHRemoting.Basic.Tests.ps1 @@ -6,6 +6,107 @@ Describe "SSHRemoting Basic Tests" -tags CI { # SSH remoting is set up to automatically authenticate current user via SSH keys # All tests connect back to localhost machine + $script:TestConnectingTimeout = 5000 # Milliseconds + + function CheckSSHDService + { + if ($IsWindows) + { + Write-Verbose -Verbose "Restarting Windows SSHD service..." + Restart-Service sshd + Write-Verbose -Verbose "SSHD service status: $(Get-Service sshd | Out-String)" + } + else + { + Write-Verbose -Verbose "Restarting Linux SSHD service..." + sudo service ssh restart + $status = sudo service ssh status + Write-Verbose -Verbose "SSHD service status: $status" + } + } + + function TryNewPSSession + { + param( + [string[]] $HostName, + [string[]] $Name, + [int] $Port, + [string] $UserName, + [string] $KeyFilePath, + [string] $Subsystem + ) + + # Try creating a new SSH connection + $timeout = $script:TestConnectingTimeout + $connectionError = $null + $session = $null + $count = 0 + while (($null -eq $session) -and ($count++ -lt 2)) + { + $session = New-PSSession @PSBoundParameters -ConnectingTimeout $timeout -ErrorVariable connectionError -ErrorAction SilentlyContinue + if ($null -eq $session) + { + Write-Verbose -Verbose "SSH New-PSSession remoting connect failed after $($timeout/1000) second wait." + + if ($count -eq 1) + { + # Try restarting sshd service + CheckSSHDService + } + } + } + + if ($null -eq $session) + { + $message = "New-PSSession unable to connect to SSH remoting endpoint after two attempts. Error: $($connectionError.Exception.Message)" + throw [System.Management.Automation.PSInvalidOperationException]::new($message) + } + + Write-Verbose -Verbose "SSH New-PSSession remoting connect succeeded." + Write-Output $session + } + + function TryNewPSSessionHash + { + param ( + [hashtable[]] $SSHConnection, + [string[]] $Name + ) + + foreach ($connect in $SSHConnection) + { + $connect.Add('ConnectingTimeout', $script:TestConnectingTimeout) + } + + # Try creating a new SSH connection + $connectionError = $null + $session = $null + $count = 0 + while (($null -eq $session) -and ($count++ -lt 2)) + { + $session = New-PSSession @PSBoundParameters -ErrorVariable connectionError -ErrorAction SilentlyContinue + if ($null -eq $session) + { + Write-Verbose -Verbose "SSH New-PSSession remoting connect failed after $($timeout/1000) second wait." + + if ($count -eq 1) + { + # Try restarting sshd service + CheckSSHDService + } + } + } + + if ($null -eq $session) + { + $message = "New-PSSession unable to connect to SSH remoting endpoint after two attempts. Error: $($connectionError.Exception.Message)" + throw [System.Management.Automation.PSInvalidOperationException]::new($message) + } + + Write-Verbose -Verbose "SSH New-PSSession remoting connect succeeded." + Write-Output $session + } + function VerifySession { param ( [System.Management.Automation.Runspaces.PSSession] $session @@ -28,37 +129,32 @@ Describe "SSHRemoting Basic Tests" -tags CI { } It "Verifies new connection with implicit current User" { - $script:session = New-PSSession -HostName localhost -ErrorVariable err - $err | Should -HaveCount 0 + $script:session = TryNewPSSession -HostName localhost VerifySession $script:session } It "Verifies new connection with explicit User parameter" { - $script:session = New-PSSession -HostName localhost -UserName (whoami) -ErrorVariable err - $err | Should -HaveCount 0 + $script:session = TryNewPSSession -HostName localhost -UserName (whoami) VerifySession $script:session } It "Verifies explicit Name parameter" { $sessionName = 'TestSessionNameA' - $script:session = New-PSSession -HostName localhost -Name $sessionName -ErrorVariable err - $err | Should -HaveCount 0 + $script:session = TryNewPSSession -HostName localhost -Name $sessionName VerifySession $script:session $script:session.Name | Should -BeExactly $sessionName } It "Verifies explicit Port parameter" { $portNum = 22 - $script:session = New-PSSession -HostName localhost -Port $portNum -ErrorVariable err - $err | Should -HaveCount 0 + $script:session = TryNewPSSession -HostName localhost -Port $portNum VerifySession $script:session } It "Verifies explicit Subsystem parameter" { $portNum = 22 $subSystem = 'powershell' - $script:session = New-PSSession -HostName localhost -Port $portNum -SubSystem $subSystem -ErrorVariable err - $err | Should -HaveCount 0 + $script:session = TryNewPSSession -HostName localhost -Port $portNum -SubSystem $subSystem VerifySession $script:session } @@ -66,8 +162,7 @@ Describe "SSHRemoting Basic Tests" -tags CI { $keyFilePath = "$HOME/.ssh/id_rsa" $portNum = 22 $subSystem = 'powershell' - $script:session = New-PSSession -HostName localhost -Port $portNum -SubSystem $subSystem -KeyFilePath $keyFilePath -ErrorVariable err - $err | Should -HaveCount 0 + $script:session = TryNewPSSession -HostName localhost -Port $portNum -SubSystem $subSystem -KeyFilePath $keyFilePath VerifySession $script:session } @@ -85,8 +180,7 @@ Describe "SSHRemoting Basic Tests" -tags CI { KeyFilePath = "$HOME/.ssh/id_rsa" Subsystem = 'powershell' }) - $script:sessions = New-PSSession -SSHConnection $sshConnection -Name 'Connection1','Connection2' -ErrorVariable err - $err | Should -HaveCount 0 + $script:sessions = TryNewPSSessionHash -SSHConnection $sshConnection -Name 'Connection1','Connection2' $script:sessions | Should -HaveCount 2 $script:sessions[0].Name | Should -BeLike 'Connection*' $script:sessions[1].Name | Should -BeLike 'Connection*' @@ -95,6 +189,52 @@ Describe "SSHRemoting Basic Tests" -tags CI { } } + function TryCreateRunspace + { + param ( + [string] $UserName, + [string] $ComputerName, + [string] $KeyFilePath, + [int] $Port, + [string] $Subsystem + ) + + $timeout = $script:TestConnectingTimeout + $connectionError = $null + $count = 0 + $rs = $null + $ci = [System.Management.Automation.Runspaces.SSHConnectionInfo]::new($UserName, $ComputerName, $KeyFilePath, $Port, $Subsystem, $timeout) + while (($null -eq $rs) -and ($count++ -lt 2)) + { + try + { + $rs = [runspacefactory]::CreateRunspace($host, $ci) + $null = $rs.Open() + } + catch + { + $connectionError = $_ + $rs = $null + Write-Verbose -Verbose "SSH Runspace Open remoting connect failed after $($timeout/1000) second wait." + + if ($count -eq 1) + { + # Try restarting sshd service + CheckSSHDService + } + } + } + + if ($null -eq $rs) + { + $message = "Runspace open unable to connect to SSH remoting endpoint after three attempts. Error: $($connectionError.Message)" + throw [System.Management.Automation.PSInvalidOperationException]::new($message) + } + + Write-Verbose -Verbose "SSH Runspace Open remoting connect succeeded." + Write-Output $rs + } + function VerifyRunspace { param ( [runspace] $rs @@ -178,9 +318,7 @@ Describe "SSHRemoting Basic Tests" -tags CI { $SubSystem ) - $ci = [System.Management.Automation.Runspaces.SSHConnectionInfo]::new($UserName, $ComputerName, $KeyFilePath, $Port, $Subsystem) - $script:rs = [runspacefactory]::CreateRunspace($host, $ci) - $script:rs.Open() + $script:rs = TryCreateRunspace -UserName $UserName -ComputerName $ComputerName -KeyFilePath $KeyFilePath -Port $Port -Subsystem $Subsystem VerifyRunspace $script:rs } } From bf1a6a8b8cdcb04e77bd65b2144fcd3be16410f8 Mon Sep 17 00:00:00 2001 From: Paul Higinbotham Date: Fri, 9 Apr 2021 11:17:24 -0700 Subject: [PATCH 03/22] Fix CodeFactor issues --- .../engine/remoting/commands/PSRemotingCmdlet.cs | 4 ++-- .../engine/remoting/common/RunspaceConnectionInfo.cs | 8 ++++---- .../engine/remoting/fanin/OutOfProcTransportManager.cs | 2 +- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/src/System.Management.Automation/engine/remoting/commands/PSRemotingCmdlet.cs b/src/System.Management.Automation/engine/remoting/commands/PSRemotingCmdlet.cs index 9a6082d1012..840b6c301d7 100644 --- a/src/System.Management.Automation/engine/remoting/commands/PSRemotingCmdlet.cs +++ b/src/System.Management.Automation/engine/remoting/commands/PSRemotingCmdlet.cs @@ -763,14 +763,14 @@ public virtual string KeyFilePath } /// - /// Gets and sets a value for the SSH subsystem to use for the remote connection. + /// Gets or sets a value for the SSH subsystem to use for the remote connection. /// [Parameter(ValueFromPipelineByPropertyName = true, ParameterSetName = PSRemotingBaseCmdlet.SSHHostParameterSet)] public virtual string Subsystem { get; set; } /// - /// Gets and sets a value in milliseconds that limits the time allowed for an SSH connection to be established. + /// Gets or sets a value in milliseconds that limits the time allowed for an SSH connection to be established. /// [Parameter(ParameterSetName = PSRemotingBaseCmdlet.SSHHostParameterSet)] public virtual int ConnectingTimeout { get; set; } = -1; // No timeout by default diff --git a/src/System.Management.Automation/engine/remoting/common/RunspaceConnectionInfo.cs b/src/System.Management.Automation/engine/remoting/common/RunspaceConnectionInfo.cs index b0a88eebe9b..77c4592dba7 100644 --- a/src/System.Management.Automation/engine/remoting/common/RunspaceConnectionInfo.cs +++ b/src/System.Management.Automation/engine/remoting/common/RunspaceConnectionInfo.cs @@ -2032,11 +2032,11 @@ public SSHConnectionInfo( } /// - /// Constructor. + /// Initializes a new instance of SSHConnectionInfo. /// - /// User Name. - /// Computer Name. - /// Key File Path. + /// Name of user. + /// Name of computer. + /// Path of key file. /// Port number for connection (default 22). /// Subsystem to use (default 'powershell'). /// Timeout time for terminating connection attempt. diff --git a/src/System.Management.Automation/engine/remoting/fanin/OutOfProcTransportManager.cs b/src/System.Management.Automation/engine/remoting/fanin/OutOfProcTransportManager.cs index e698d831b6e..5b7f3b31c0b 100644 --- a/src/System.Management.Automation/engine/remoting/fanin/OutOfProcTransportManager.cs +++ b/src/System.Management.Automation/engine/remoting/fanin/OutOfProcTransportManager.cs @@ -1711,7 +1711,7 @@ internal override void CloseAsync() private void CloseConnection() { // Ensure message queue is disposed. - base.DisposeMessageQueue(); + DisposeMessageQueue(); var connectionTimer = Interlocked.Exchange(ref _connectionTimer, null); connectionTimer?.Dispose(); From 95c9942dcb66b612dfdaf6c0223d7fed7b16d226 Mon Sep 17 00:00:00 2001 From: Paul Higinbotham Date: Fri, 9 Apr 2021 12:26:12 -0700 Subject: [PATCH 04/22] Add Write-Verbose for debugging --- test/SSHRemoting/SSHRemoting.Basic.Tests.ps1 | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/test/SSHRemoting/SSHRemoting.Basic.Tests.ps1 b/test/SSHRemoting/SSHRemoting.Basic.Tests.ps1 index c6dfa286ab5..ede6ce3ccf3 100644 --- a/test/SSHRemoting/SSHRemoting.Basic.Tests.ps1 +++ b/test/SSHRemoting/SSHRemoting.Basic.Tests.ps1 @@ -240,18 +240,22 @@ Describe "SSHRemoting Basic Tests" -tags CI { [runspace] $rs ) + Write-Verbose -Verbose "VerifyRunspace called for runspace: $($rs.Id)" + $rs.RunspaceStateInfo.State | Should -BeExactly 'Opened' $rs.RunspaceAvailability | Should -BeExactly 'Available' $rs.RunspaceIsRemote | Should -BeTrue $ps = [powershell]::Create() try { + Write-Verbose -Verbose "VerifyRunspace: Invoking PSSenderInfo" $ps.Runspace = $rs $psRemoteVersion = $ps.AddScript('$PSSenderInfo.ApplicationArguments.PSVersionTable.PSVersion').Invoke() $psRemoteVersion.Major | Should -BeExactly $PSVersionTable.PSVersion.Major $psRemoteVersion.Minor | Should -BeExactly $PSVersionTable.PSVersion.Minor $ps.Commands.Clear() + Write-Verbose -Verbose "VerifyRunspace: Invoking whoami" $ps.AddScript('whoami').Invoke() | Should -BeExactly $(whoami) } finally From a4c521eabd2036d1cb744ef4d8a9c7147da59a8e Mon Sep 17 00:00:00 2001 From: Paul Higinbotham Date: Fri, 9 Apr 2021 12:39:00 -0700 Subject: [PATCH 05/22] Add more debug info --- test/SSHRemoting/SSHRemoting.Basic.Tests.ps1 | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/test/SSHRemoting/SSHRemoting.Basic.Tests.ps1 b/test/SSHRemoting/SSHRemoting.Basic.Tests.ps1 index ede6ce3ccf3..4e2d15b94da 100644 --- a/test/SSHRemoting/SSHRemoting.Basic.Tests.ps1 +++ b/test/SSHRemoting/SSHRemoting.Basic.Tests.ps1 @@ -112,13 +112,18 @@ Describe "SSHRemoting Basic Tests" -tags CI { [System.Management.Automation.Runspaces.PSSession] $session ) + Write-Verbose -Verbose "VerifySession called for session: $($session.Id)" + $session.State | Should -BeExactly 'Opened' $session.ComputerName | Should -BeExactly 'localhost' $session.Transport | Should -BeExactly 'SSH' + Write-Verbose -Verbose "Invoking whoami" Invoke-Command -Session $session -ScriptBlock { whoami } | Should -BeExactly $(whoami) + Write-Verbose -Verbose "Invoking PSSenderInfo" $psRemoteVersion = Invoke-Command -Session $session -ScriptBlock { $PSSenderInfo.ApplicationArguments.PSVersionTable.PSVersion } $psRemoteVersion.Major | Should -BeExactly $PSVersionTable.PSVersion.Major $psRemoteVersion.Minor | Should -BeExactly $PSVersionTable.PSVersion.Minor + Write-Verbose -Verbose "VerifySession complete" } Context "New-PSSession Tests" { @@ -257,6 +262,7 @@ Describe "SSHRemoting Basic Tests" -tags CI { $ps.Commands.Clear() Write-Verbose -Verbose "VerifyRunspace: Invoking whoami" $ps.AddScript('whoami').Invoke() | Should -BeExactly $(whoami) + Write-Verbose -Verbose "VerifyRunspace complete" } finally { From 50dc46417b8308d7dedcf62bdda796df99875d3e Mon Sep 17 00:00:00 2001 From: Paul Higinbotham Date: Fri, 9 Apr 2021 13:12:25 -0700 Subject: [PATCH 06/22] [Feature] From 853598cf53bc76f9a7e9150aa427ef183815cb1a Mon Sep 17 00:00:00 2001 From: Paul Higinbotham Date: Fri, 9 Apr 2021 13:41:58 -0700 Subject: [PATCH 07/22] More debug output --- test/SSHRemoting/SSHRemoting.Basic.Tests.ps1 | 27 +++++++++++++++++++- 1 file changed, 26 insertions(+), 1 deletion(-) diff --git a/test/SSHRemoting/SSHRemoting.Basic.Tests.ps1 b/test/SSHRemoting/SSHRemoting.Basic.Tests.ps1 index 4e2d15b94da..bc0b664a799 100644 --- a/test/SSHRemoting/SSHRemoting.Basic.Tests.ps1 +++ b/test/SSHRemoting/SSHRemoting.Basic.Tests.ps1 @@ -36,6 +36,8 @@ Describe "SSHRemoting Basic Tests" -tags CI { [string] $Subsystem ) + Write-Verbose -Verbose "Starting TryNewPSSession ..." + # Try creating a new SSH connection $timeout = $script:TestConnectingTimeout $connectionError = $null @@ -73,6 +75,8 @@ Describe "SSHRemoting Basic Tests" -tags CI { [string[]] $Name ) + Write-Verbose -Verbose "Starting TryNewPSSessionHash ..." + foreach ($connect in $SSHConnection) { $connect.Add('ConnectingTimeout', $script:TestConnectingTimeout) @@ -129,49 +133,64 @@ Describe "SSHRemoting Basic Tests" -tags CI { Context "New-PSSession Tests" { AfterEach { + Write-Verbose -Verbose "Starting New-PSSession AfterEach" if ($script:session -ne $null) { Remove-PSSession -Session $script:session } if ($script:sessions -ne $null) { Remove-PSSession -Session $script:sessions } + Write-Verbose -Verbose "AfterEach complete" } It "Verifies new connection with implicit current User" { + Write-Verbose -Verbose "It Starting: Verifies new connection with implicit current User" $script:session = TryNewPSSession -HostName localhost VerifySession $script:session + Write-Verbose -Verbose "It Complete" } It "Verifies new connection with explicit User parameter" { + Write-Verbose -Verbose "It Starting: Verifies new connection with explicit User parameter" $script:session = TryNewPSSession -HostName localhost -UserName (whoami) VerifySession $script:session + Write-Verbose -Verbose "It Complete" } It "Verifies explicit Name parameter" { + Write-Verbose -Verbose "It Starting: Verifies explicit Name parameter" $sessionName = 'TestSessionNameA' $script:session = TryNewPSSession -HostName localhost -Name $sessionName VerifySession $script:session $script:session.Name | Should -BeExactly $sessionName + Write-Verbose -Verbose "It Complete" } It "Verifies explicit Port parameter" { + Write-Verbose -Verbose "It Starting: Verifies explicit Port parameter" $portNum = 22 $script:session = TryNewPSSession -HostName localhost -Port $portNum VerifySession $script:session + Write-Verbose -Verbose "It Complete" } It "Verifies explicit Subsystem parameter" { + Write-Verbose -Verbose "It Starting: Verifies explicit Subsystem parameter" $portNum = 22 $subSystem = 'powershell' $script:session = TryNewPSSession -HostName localhost -Port $portNum -SubSystem $subSystem VerifySession $script:session + Write-Verbose -Verbose "It Complete" } It "Verifies explicit KeyFilePath parameter" { + Write-Verbose -Verbose "It Starting: Verifies explicit KeyFilePath parameter" $keyFilePath = "$HOME/.ssh/id_rsa" $portNum = 22 $subSystem = 'powershell' $script:session = TryNewPSSession -HostName localhost -Port $portNum -SubSystem $subSystem -KeyFilePath $keyFilePath VerifySession $script:session + Write-Verbose -Verbose "It Complete" } It "Verifies SSHConnection hash table parameters" { + Write-Verbose -Verbose "It Starting: Verifies SSHConnection hash table parameters" $sshConnection = @( @{ HostName = 'localhost' @@ -191,6 +210,7 @@ Describe "SSHRemoting Basic Tests" -tags CI { $script:sessions[1].Name | Should -BeLike 'Connection*' VerifySession $script:sessions[0] VerifySession $script:sessions[1] + Write-Verbose -Verbose "It Complete" } } @@ -204,6 +224,8 @@ Describe "SSHRemoting Basic Tests" -tags CI { [string] $Subsystem ) + Write-Verbose -Verbose "Starting TryCreateRunspace ..." + $timeout = $script:TestConnectingTimeout $connectionError = $null $count = 0 @@ -325,11 +347,14 @@ Describe "SSHRemoting Basic Tests" -tags CI { $ComputerName, $KeyFilePath, $Port, - $SubSystem + $SubSystem, + $TestName ) + Write-Verbose -Verbose "It Starting: $TestName" $script:rs = TryCreateRunspace -UserName $UserName -ComputerName $ComputerName -KeyFilePath $KeyFilePath -Port $Port -Subsystem $Subsystem VerifyRunspace $script:rs + Write-Verbose -Verbose "It Complete" } } } From 33f5e505caca7a7d4b67053fe4331b60ddc3b2c4 Mon Sep 17 00:00:00 2001 From: Paul Higinbotham Date: Fri, 9 Apr 2021 13:55:32 -0700 Subject: [PATCH 08/22] [Feature] From 64af989111933fe17e8ef435f0db478010b1dcec Mon Sep 17 00:00:00 2001 From: Paul Higinbotham Date: Mon, 12 Apr 2021 09:18:48 -0700 Subject: [PATCH 09/22] Ensure all It blocks have Should --- test/SSHRemoting/SSHRemoting.Basic.Tests.ps1 | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/test/SSHRemoting/SSHRemoting.Basic.Tests.ps1 b/test/SSHRemoting/SSHRemoting.Basic.Tests.ps1 index bc0b664a799..dc9a8e67cdb 100644 --- a/test/SSHRemoting/SSHRemoting.Basic.Tests.ps1 +++ b/test/SSHRemoting/SSHRemoting.Basic.Tests.ps1 @@ -142,6 +142,7 @@ Describe "SSHRemoting Basic Tests" -tags CI { It "Verifies new connection with implicit current User" { Write-Verbose -Verbose "It Starting: Verifies new connection with implicit current User" $script:session = TryNewPSSession -HostName localhost + $script:session | Should -Not -BeNullOrEmpty VerifySession $script:session Write-Verbose -Verbose "It Complete" } @@ -149,6 +150,7 @@ Describe "SSHRemoting Basic Tests" -tags CI { It "Verifies new connection with explicit User parameter" { Write-Verbose -Verbose "It Starting: Verifies new connection with explicit User parameter" $script:session = TryNewPSSession -HostName localhost -UserName (whoami) + $script:session | Should -Not -BeNullOrEmpty VerifySession $script:session Write-Verbose -Verbose "It Complete" } @@ -157,6 +159,7 @@ Describe "SSHRemoting Basic Tests" -tags CI { Write-Verbose -Verbose "It Starting: Verifies explicit Name parameter" $sessionName = 'TestSessionNameA' $script:session = TryNewPSSession -HostName localhost -Name $sessionName + $script:session | Should -Not -BeNullOrEmpty VerifySession $script:session $script:session.Name | Should -BeExactly $sessionName Write-Verbose -Verbose "It Complete" @@ -166,6 +169,7 @@ Describe "SSHRemoting Basic Tests" -tags CI { Write-Verbose -Verbose "It Starting: Verifies explicit Port parameter" $portNum = 22 $script:session = TryNewPSSession -HostName localhost -Port $portNum + $script:session | Should -Not -BeNullOrEmpty VerifySession $script:session Write-Verbose -Verbose "It Complete" } @@ -175,6 +179,7 @@ Describe "SSHRemoting Basic Tests" -tags CI { $portNum = 22 $subSystem = 'powershell' $script:session = TryNewPSSession -HostName localhost -Port $portNum -SubSystem $subSystem + $script:session | Should -Not -BeNullOrEmpty VerifySession $script:session Write-Verbose -Verbose "It Complete" } @@ -185,6 +190,7 @@ Describe "SSHRemoting Basic Tests" -tags CI { $portNum = 22 $subSystem = 'powershell' $script:session = TryNewPSSession -HostName localhost -Port $portNum -SubSystem $subSystem -KeyFilePath $keyFilePath + $script:session | Should -Not -BeNullOrEmpty VerifySession $script:session Write-Verbose -Verbose "It Complete" } @@ -205,6 +211,7 @@ Describe "SSHRemoting Basic Tests" -tags CI { Subsystem = 'powershell' }) $script:sessions = TryNewPSSessionHash -SSHConnection $sshConnection -Name 'Connection1','Connection2' + $script:session | Should -Not -BeNullOrEmpty $script:sessions | Should -HaveCount 2 $script:sessions[0].Name | Should -BeLike 'Connection*' $script:sessions[1].Name | Should -BeLike 'Connection*' @@ -353,6 +360,7 @@ Describe "SSHRemoting Basic Tests" -tags CI { Write-Verbose -Verbose "It Starting: $TestName" $script:rs = TryCreateRunspace -UserName $UserName -ComputerName $ComputerName -KeyFilePath $KeyFilePath -Port $Port -Subsystem $Subsystem + $script:rs | Should -Not -BeNullOrEmpty VerifyRunspace $script:rs Write-Verbose -Verbose "It Complete" } From d05e51af0c969d17512c6dfce492c6dbca53d59d Mon Sep 17 00:00:00 2001 From: Paul Higinbotham Date: Mon, 12 Apr 2021 09:42:13 -0700 Subject: [PATCH 10/22] Try removing AfterEach --- test/SSHRemoting/SSHRemoting.Basic.Tests.ps1 | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/test/SSHRemoting/SSHRemoting.Basic.Tests.ps1 b/test/SSHRemoting/SSHRemoting.Basic.Tests.ps1 index dc9a8e67cdb..19c290d52ac 100644 --- a/test/SSHRemoting/SSHRemoting.Basic.Tests.ps1 +++ b/test/SSHRemoting/SSHRemoting.Basic.Tests.ps1 @@ -132,12 +132,14 @@ Describe "SSHRemoting Basic Tests" -tags CI { Context "New-PSSession Tests" { + <# AfterEach { Write-Verbose -Verbose "Starting New-PSSession AfterEach" if ($script:session -ne $null) { Remove-PSSession -Session $script:session } if ($script:sessions -ne $null) { Remove-PSSession -Session $script:sessions } Write-Verbose -Verbose "AfterEach complete" } + #> It "Verifies new connection with implicit current User" { Write-Verbose -Verbose "It Starting: Verifies new connection with implicit current User" @@ -301,9 +303,13 @@ Describe "SSHRemoting Basic Tests" -tags CI { Context "SSH Remoting API Tests" { + <# AfterEach { + Write-Verbose -Verbose "Starting Runspace close AfterEach" if ($script:rs -ne $null) { $script:rs.Dispose() } + Write-Verbose -Verbose "AfterEach complete" } + #> $testCases = @( @{ From 3332b2ff9671ac64db2ccff406af7f8e209a307c Mon Sep 17 00:00:00 2001 From: Paul Higinbotham Date: Mon, 12 Apr 2021 10:40:21 -0700 Subject: [PATCH 11/22] [Feature] From 3d82ad64d216970105615f287ded8f195254d381 Mon Sep 17 00:00:00 2001 From: Paul Higinbotham Date: Mon, 12 Apr 2021 10:47:32 -0700 Subject: [PATCH 12/22] [Feature] From 0079c52c2530459281c736dc3d6a942bdce26bbf Mon Sep 17 00:00:00 2001 From: Paul Higinbotham Date: Mon, 12 Apr 2021 11:05:15 -0700 Subject: [PATCH 13/22] Debug1 --- test/SSHRemoting/SSHRemoting.Basic.Tests.ps1 | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/test/SSHRemoting/SSHRemoting.Basic.Tests.ps1 b/test/SSHRemoting/SSHRemoting.Basic.Tests.ps1 index 19c290d52ac..5d4bfd3c094 100644 --- a/test/SSHRemoting/SSHRemoting.Basic.Tests.ps1 +++ b/test/SSHRemoting/SSHRemoting.Basic.Tests.ps1 @@ -37,6 +37,7 @@ Describe "SSHRemoting Basic Tests" -tags CI { ) Write-Verbose -Verbose "Starting TryNewPSSession ..." + return $null # Try creating a new SSH connection $timeout = $script:TestConnectingTimeout @@ -76,6 +77,7 @@ Describe "SSHRemoting Basic Tests" -tags CI { ) Write-Verbose -Verbose "Starting TryNewPSSessionHash ..." + return $null foreach ($connect in $SSHConnection) { @@ -116,6 +118,11 @@ Describe "SSHRemoting Basic Tests" -tags CI { [System.Management.Automation.Runspaces.PSSession] $session ) + if ($null -eq $session) + { + return + } + Write-Verbose -Verbose "VerifySession called for session: $($session.Id)" $session.State | Should -BeExactly 'Opened' @@ -234,6 +241,7 @@ Describe "SSHRemoting Basic Tests" -tags CI { ) Write-Verbose -Verbose "Starting TryCreateRunspace ..." + return $null $timeout = $script:TestConnectingTimeout $connectionError = $null @@ -276,6 +284,11 @@ Describe "SSHRemoting Basic Tests" -tags CI { [runspace] $rs ) + if ($null -eq $rs) + { + return + } + Write-Verbose -Verbose "VerifyRunspace called for runspace: $($rs.Id)" $rs.RunspaceStateInfo.State | Should -BeExactly 'Opened' From a770916f3fa474a95b93c9efe056dfe058434277 Mon Sep 17 00:00:00 2001 From: Paul Higinbotham Date: Mon, 12 Apr 2021 11:38:36 -0700 Subject: [PATCH 14/22] Debug2 --- test/SSHRemoting/SSHRemoting.Basic.Tests.ps1 | 25 +++++++------------- 1 file changed, 9 insertions(+), 16 deletions(-) diff --git a/test/SSHRemoting/SSHRemoting.Basic.Tests.ps1 b/test/SSHRemoting/SSHRemoting.Basic.Tests.ps1 index 5d4bfd3c094..3a629398de0 100644 --- a/test/SSHRemoting/SSHRemoting.Basic.Tests.ps1 +++ b/test/SSHRemoting/SSHRemoting.Basic.Tests.ps1 @@ -37,7 +37,6 @@ Describe "SSHRemoting Basic Tests" -tags CI { ) Write-Verbose -Verbose "Starting TryNewPSSession ..." - return $null # Try creating a new SSH connection $timeout = $script:TestConnectingTimeout @@ -77,7 +76,6 @@ Describe "SSHRemoting Basic Tests" -tags CI { ) Write-Verbose -Verbose "Starting TryNewPSSessionHash ..." - return $null foreach ($connect in $SSHConnection) { @@ -139,20 +137,18 @@ Describe "SSHRemoting Basic Tests" -tags CI { Context "New-PSSession Tests" { - <# AfterEach { Write-Verbose -Verbose "Starting New-PSSession AfterEach" if ($script:session -ne $null) { Remove-PSSession -Session $script:session } if ($script:sessions -ne $null) { Remove-PSSession -Session $script:sessions } Write-Verbose -Verbose "AfterEach complete" } - #> It "Verifies new connection with implicit current User" { Write-Verbose -Verbose "It Starting: Verifies new connection with implicit current User" $script:session = TryNewPSSession -HostName localhost $script:session | Should -Not -BeNullOrEmpty - VerifySession $script:session + #VerifySession $script:session Write-Verbose -Verbose "It Complete" } @@ -160,7 +156,7 @@ Describe "SSHRemoting Basic Tests" -tags CI { Write-Verbose -Verbose "It Starting: Verifies new connection with explicit User parameter" $script:session = TryNewPSSession -HostName localhost -UserName (whoami) $script:session | Should -Not -BeNullOrEmpty - VerifySession $script:session + #VerifySession $script:session Write-Verbose -Verbose "It Complete" } @@ -169,7 +165,7 @@ Describe "SSHRemoting Basic Tests" -tags CI { $sessionName = 'TestSessionNameA' $script:session = TryNewPSSession -HostName localhost -Name $sessionName $script:session | Should -Not -BeNullOrEmpty - VerifySession $script:session + #VerifySession $script:session $script:session.Name | Should -BeExactly $sessionName Write-Verbose -Verbose "It Complete" } @@ -179,7 +175,7 @@ Describe "SSHRemoting Basic Tests" -tags CI { $portNum = 22 $script:session = TryNewPSSession -HostName localhost -Port $portNum $script:session | Should -Not -BeNullOrEmpty - VerifySession $script:session + #VerifySession $script:session Write-Verbose -Verbose "It Complete" } @@ -189,7 +185,7 @@ Describe "SSHRemoting Basic Tests" -tags CI { $subSystem = 'powershell' $script:session = TryNewPSSession -HostName localhost -Port $portNum -SubSystem $subSystem $script:session | Should -Not -BeNullOrEmpty - VerifySession $script:session + #VerifySession $script:session Write-Verbose -Verbose "It Complete" } @@ -200,7 +196,7 @@ Describe "SSHRemoting Basic Tests" -tags CI { $subSystem = 'powershell' $script:session = TryNewPSSession -HostName localhost -Port $portNum -SubSystem $subSystem -KeyFilePath $keyFilePath $script:session | Should -Not -BeNullOrEmpty - VerifySession $script:session + #VerifySession $script:session Write-Verbose -Verbose "It Complete" } @@ -224,8 +220,8 @@ Describe "SSHRemoting Basic Tests" -tags CI { $script:sessions | Should -HaveCount 2 $script:sessions[0].Name | Should -BeLike 'Connection*' $script:sessions[1].Name | Should -BeLike 'Connection*' - VerifySession $script:sessions[0] - VerifySession $script:sessions[1] + #VerifySession $script:sessions[0] + #VerifySession $script:sessions[1] Write-Verbose -Verbose "It Complete" } } @@ -241,7 +237,6 @@ Describe "SSHRemoting Basic Tests" -tags CI { ) Write-Verbose -Verbose "Starting TryCreateRunspace ..." - return $null $timeout = $script:TestConnectingTimeout $connectionError = $null @@ -316,13 +311,11 @@ Describe "SSHRemoting Basic Tests" -tags CI { Context "SSH Remoting API Tests" { - <# AfterEach { Write-Verbose -Verbose "Starting Runspace close AfterEach" if ($script:rs -ne $null) { $script:rs.Dispose() } Write-Verbose -Verbose "AfterEach complete" } - #> $testCases = @( @{ @@ -380,7 +373,7 @@ Describe "SSHRemoting Basic Tests" -tags CI { Write-Verbose -Verbose "It Starting: $TestName" $script:rs = TryCreateRunspace -UserName $UserName -ComputerName $ComputerName -KeyFilePath $KeyFilePath -Port $Port -Subsystem $Subsystem $script:rs | Should -Not -BeNullOrEmpty - VerifyRunspace $script:rs + #VerifyRunspace $script:rs Write-Verbose -Verbose "It Complete" } } From 9b51d70fbf20ba239481e2d3a9fe0beebe07a37e Mon Sep 17 00:00:00 2001 From: Paul Higinbotham Date: Mon, 12 Apr 2021 13:15:35 -0700 Subject: [PATCH 15/22] Debug3 --- test/SSHRemoting/SSHRemoting.Basic.Tests.ps1 | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/test/SSHRemoting/SSHRemoting.Basic.Tests.ps1 b/test/SSHRemoting/SSHRemoting.Basic.Tests.ps1 index 3a629398de0..b3c7535d717 100644 --- a/test/SSHRemoting/SSHRemoting.Basic.Tests.ps1 +++ b/test/SSHRemoting/SSHRemoting.Basic.Tests.ps1 @@ -148,7 +148,7 @@ Describe "SSHRemoting Basic Tests" -tags CI { Write-Verbose -Verbose "It Starting: Verifies new connection with implicit current User" $script:session = TryNewPSSession -HostName localhost $script:session | Should -Not -BeNullOrEmpty - #VerifySession $script:session + VerifySession $script:session Write-Verbose -Verbose "It Complete" } @@ -156,7 +156,7 @@ Describe "SSHRemoting Basic Tests" -tags CI { Write-Verbose -Verbose "It Starting: Verifies new connection with explicit User parameter" $script:session = TryNewPSSession -HostName localhost -UserName (whoami) $script:session | Should -Not -BeNullOrEmpty - #VerifySession $script:session + VerifySession $script:session Write-Verbose -Verbose "It Complete" } @@ -165,7 +165,7 @@ Describe "SSHRemoting Basic Tests" -tags CI { $sessionName = 'TestSessionNameA' $script:session = TryNewPSSession -HostName localhost -Name $sessionName $script:session | Should -Not -BeNullOrEmpty - #VerifySession $script:session + VerifySession $script:session $script:session.Name | Should -BeExactly $sessionName Write-Verbose -Verbose "It Complete" } @@ -175,7 +175,7 @@ Describe "SSHRemoting Basic Tests" -tags CI { $portNum = 22 $script:session = TryNewPSSession -HostName localhost -Port $portNum $script:session | Should -Not -BeNullOrEmpty - #VerifySession $script:session + VerifySession $script:session Write-Verbose -Verbose "It Complete" } @@ -185,7 +185,7 @@ Describe "SSHRemoting Basic Tests" -tags CI { $subSystem = 'powershell' $script:session = TryNewPSSession -HostName localhost -Port $portNum -SubSystem $subSystem $script:session | Should -Not -BeNullOrEmpty - #VerifySession $script:session + VerifySession $script:session Write-Verbose -Verbose "It Complete" } @@ -196,7 +196,7 @@ Describe "SSHRemoting Basic Tests" -tags CI { $subSystem = 'powershell' $script:session = TryNewPSSession -HostName localhost -Port $portNum -SubSystem $subSystem -KeyFilePath $keyFilePath $script:session | Should -Not -BeNullOrEmpty - #VerifySession $script:session + VerifySession $script:session Write-Verbose -Verbose "It Complete" } @@ -220,8 +220,8 @@ Describe "SSHRemoting Basic Tests" -tags CI { $script:sessions | Should -HaveCount 2 $script:sessions[0].Name | Should -BeLike 'Connection*' $script:sessions[1].Name | Should -BeLike 'Connection*' - #VerifySession $script:sessions[0] - #VerifySession $script:sessions[1] + VerifySession $script:sessions[0] + VerifySession $script:sessions[1] Write-Verbose -Verbose "It Complete" } } From 3a47c803b1041ac33cde77a93e2bcff9ca041acd Mon Sep 17 00:00:00 2001 From: Paul Higinbotham Date: Mon, 12 Apr 2021 13:34:12 -0700 Subject: [PATCH 16/22] Debug4 --- .../engine/remoting/fanin/OutOfProcTransportManager.cs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/System.Management.Automation/engine/remoting/fanin/OutOfProcTransportManager.cs b/src/System.Management.Automation/engine/remoting/fanin/OutOfProcTransportManager.cs index 5b7f3b31c0b..61d87f76618 100644 --- a/src/System.Management.Automation/engine/remoting/fanin/OutOfProcTransportManager.cs +++ b/src/System.Management.Automation/engine/remoting/fanin/OutOfProcTransportManager.cs @@ -585,13 +585,13 @@ internal override void CloseAsync() try { - // send Close signal to the server and let it die gracefully. - stdInWriter.WriteLine(OutOfProcessUtils.CreateClosePacket(Guid.Empty)); - // start the timer..so client can fail deterministically _closeTimeOutTimer.Change(60 * 1000, Timeout.Infinite); + + // send Close signal to the server and let it die gracefully. + stdInWriter.WriteLine(OutOfProcessUtils.CreateClosePacket(Guid.Empty)); } - catch (IOException) + catch { // Cannot communicate with server. Allow client to complete close operation. shouldRaiseCloseCompleted = true; From 70dfcd4e3476f6098a0df8e1d890d4b006727956 Mon Sep 17 00:00:00 2001 From: Paul Higinbotham Date: Mon, 12 Apr 2021 14:05:07 -0700 Subject: [PATCH 17/22] Debug5 --- .../engine/remoting/fanin/OutOfProcTransportManager.cs | 6 +++--- test/SSHRemoting/SSHRemoting.Basic.Tests.ps1 | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/System.Management.Automation/engine/remoting/fanin/OutOfProcTransportManager.cs b/src/System.Management.Automation/engine/remoting/fanin/OutOfProcTransportManager.cs index 61d87f76618..7fe2aba3b8d 100644 --- a/src/System.Management.Automation/engine/remoting/fanin/OutOfProcTransportManager.cs +++ b/src/System.Management.Automation/engine/remoting/fanin/OutOfProcTransportManager.cs @@ -585,11 +585,11 @@ internal override void CloseAsync() try { - // start the timer..so client can fail deterministically - _closeTimeOutTimer.Change(60 * 1000, Timeout.Infinite); - // send Close signal to the server and let it die gracefully. stdInWriter.WriteLine(OutOfProcessUtils.CreateClosePacket(Guid.Empty)); + + // start the timer..so client can fail deterministically + _closeTimeOutTimer.Change(60 * 1000, Timeout.Infinite); } catch { diff --git a/test/SSHRemoting/SSHRemoting.Basic.Tests.ps1 b/test/SSHRemoting/SSHRemoting.Basic.Tests.ps1 index b3c7535d717..71407d4e297 100644 --- a/test/SSHRemoting/SSHRemoting.Basic.Tests.ps1 +++ b/test/SSHRemoting/SSHRemoting.Basic.Tests.ps1 @@ -373,7 +373,7 @@ Describe "SSHRemoting Basic Tests" -tags CI { Write-Verbose -Verbose "It Starting: $TestName" $script:rs = TryCreateRunspace -UserName $UserName -ComputerName $ComputerName -KeyFilePath $KeyFilePath -Port $Port -Subsystem $Subsystem $script:rs | Should -Not -BeNullOrEmpty - #VerifyRunspace $script:rs + VerifyRunspace $script:rs Write-Verbose -Verbose "It Complete" } } From 0dfde2b8e062ed800a9fd34a40315bbfeb72fc54 Mon Sep 17 00:00:00 2001 From: Paul Higinbotham Date: Mon, 12 Apr 2021 14:48:52 -0700 Subject: [PATCH 18/22] Final change --- test/SSHRemoting/SSHRemoting.Basic.Tests.ps1 | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/test/SSHRemoting/SSHRemoting.Basic.Tests.ps1 b/test/SSHRemoting/SSHRemoting.Basic.Tests.ps1 index 71407d4e297..e5c7ac3d743 100644 --- a/test/SSHRemoting/SSHRemoting.Basic.Tests.ps1 +++ b/test/SSHRemoting/SSHRemoting.Basic.Tests.ps1 @@ -8,7 +8,7 @@ Describe "SSHRemoting Basic Tests" -tags CI { $script:TestConnectingTimeout = 5000 # Milliseconds - function CheckSSHDService + function RestartSSHDService { if ($IsWindows) { @@ -48,12 +48,12 @@ Describe "SSHRemoting Basic Tests" -tags CI { $session = New-PSSession @PSBoundParameters -ConnectingTimeout $timeout -ErrorVariable connectionError -ErrorAction SilentlyContinue if ($null -eq $session) { - Write-Verbose -Verbose "SSH New-PSSession remoting connect failed after $($timeout/1000) second wait." + Write-Verbose -Verbose "SSH New-PSSession remoting connect failed." if ($count -eq 1) { # Try restarting sshd service - CheckSSHDService + RestartSSHDService } } } @@ -91,12 +91,12 @@ Describe "SSHRemoting Basic Tests" -tags CI { $session = New-PSSession @PSBoundParameters -ErrorVariable connectionError -ErrorAction SilentlyContinue if ($null -eq $session) { - Write-Verbose -Verbose "SSH New-PSSession remoting connect failed after $($timeout/1000) second wait." + Write-Verbose -Verbose "SSH New-PSSession remoting connect failed." if ($count -eq 1) { # Try restarting sshd service - CheckSSHDService + RestartSSHDService } } } @@ -216,7 +216,6 @@ Describe "SSHRemoting Basic Tests" -tags CI { Subsystem = 'powershell' }) $script:sessions = TryNewPSSessionHash -SSHConnection $sshConnection -Name 'Connection1','Connection2' - $script:session | Should -Not -BeNullOrEmpty $script:sessions | Should -HaveCount 2 $script:sessions[0].Name | Should -BeLike 'Connection*' $script:sessions[1].Name | Should -BeLike 'Connection*' @@ -254,19 +253,19 @@ Describe "SSHRemoting Basic Tests" -tags CI { { $connectionError = $_ $rs = $null - Write-Verbose -Verbose "SSH Runspace Open remoting connect failed after $($timeout/1000) second wait." + Write-Verbose -Verbose "SSH Runspace Open remoting connect failed." if ($count -eq 1) { # Try restarting sshd service - CheckSSHDService + RestartSSHDService } } } if ($null -eq $rs) { - $message = "Runspace open unable to connect to SSH remoting endpoint after three attempts. Error: $($connectionError.Message)" + $message = "Runspace open unable to connect to SSH remoting endpoint after two attempts. Error: $($connectionError.Message)" throw [System.Management.Automation.PSInvalidOperationException]::new($message) } From 430b3a29bf623a525230ca2d3504538059b4441c Mon Sep 17 00:00:00 2001 From: Paul Higinbotham Date: Mon, 12 Apr 2021 16:17:24 -0700 Subject: [PATCH 19/22] Modify close logic to catch specific exceptions --- .../engine/remoting/fanin/OutOfProcTransportManager.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/System.Management.Automation/engine/remoting/fanin/OutOfProcTransportManager.cs b/src/System.Management.Automation/engine/remoting/fanin/OutOfProcTransportManager.cs index 7fe2aba3b8d..a5478bfd3b5 100644 --- a/src/System.Management.Automation/engine/remoting/fanin/OutOfProcTransportManager.cs +++ b/src/System.Management.Automation/engine/remoting/fanin/OutOfProcTransportManager.cs @@ -591,8 +591,9 @@ internal override void CloseAsync() // start the timer..so client can fail deterministically _closeTimeOutTimer.Change(60 * 1000, Timeout.Infinite); } - catch + catch (Exception ex) when (ex is IOException || ex is ObjectDisposedException) { + // Cannot communicate with server. Allow client to complete close operation. shouldRaiseCloseCompleted = true; } From 922962d2fdeeb6e01a0b6ad4f56dad3eb8873e8f Mon Sep 17 00:00:00 2001 From: Paul Higinbotham Date: Tue, 13 Apr 2021 09:19:43 -0700 Subject: [PATCH 20/22] Implement review comments --- .../engine/remoting/commands/PSRemotingCmdlet.cs | 7 ++++--- .../engine/remoting/common/RunspaceConnectionInfo.cs | 8 ++++++-- .../engine/remoting/fanin/OutOfProcTransportManager.cs | 3 +-- .../resources/RemotingErrorIdStrings.resx | 3 ++- 4 files changed, 13 insertions(+), 8 deletions(-) diff --git a/src/System.Management.Automation/engine/remoting/commands/PSRemotingCmdlet.cs b/src/System.Management.Automation/engine/remoting/commands/PSRemotingCmdlet.cs index 840b6c301d7..62bc698bda6 100644 --- a/src/System.Management.Automation/engine/remoting/commands/PSRemotingCmdlet.cs +++ b/src/System.Management.Automation/engine/remoting/commands/PSRemotingCmdlet.cs @@ -771,9 +771,10 @@ public virtual string KeyFilePath /// /// Gets or sets a value in milliseconds that limits the time allowed for an SSH connection to be established. + /// Default timeout value is infinite. /// [Parameter(ParameterSetName = PSRemotingBaseCmdlet.SSHHostParameterSet)] - public virtual int ConnectingTimeout { get; set; } = -1; // No timeout by default + public virtual int ConnectingTimeout { get; set; } = -1; /// /// This parameter specifies that SSH is used to establish the remote @@ -910,7 +911,7 @@ protected void ParseSshHostName(string hostname, out string host, out string use /// Array of SSHConnection objects. internal SSHConnection[] ParseSSHConnectionHashTable() { - var connections = new List(); + List connections = new(); foreach (var item in this.SSHConnection) { if (item.ContainsKey(ComputerNameParameter) && item.ContainsKey(HostNameAlias)) @@ -923,7 +924,7 @@ internal SSHConnection[] ParseSSHConnectionHashTable() throw new PSArgumentException(RemotingErrorIdStrings.SSHConnectionDuplicateKeyPath); } - var connectionInfo = new SSHConnection(); + SSHConnection connectionInfo = new(); foreach (var key in item.Keys) { string paramName = key as string; diff --git a/src/System.Management.Automation/engine/remoting/common/RunspaceConnectionInfo.cs b/src/System.Management.Automation/engine/remoting/common/RunspaceConnectionInfo.cs index 77c4592dba7..dba6a557e6f 100644 --- a/src/System.Management.Automation/engine/remoting/common/RunspaceConnectionInfo.cs +++ b/src/System.Management.Automation/engine/remoting/common/RunspaceConnectionInfo.cs @@ -1913,7 +1913,11 @@ public sealed class SSHConnectionInfo : RunspaceConnectionInfo /// Default value for subsystem. /// private const string DefaultSubsystem = "powershell"; - private const int DefaultConnectingTimeoutTime = -1; // Never time out + + /// + /// Default value is infinite timeout. + /// + private const int DefaultConnectingTimeoutTime = -1; #endregion @@ -2028,7 +2032,7 @@ public SSHConnectionInfo( int port, string subsystem) : this(userName, computerName, keyFilePath, port) { - Subsystem = (string.IsNullOrEmpty(subsystem)) ? DefaultSubsystem : subsystem; + Subsystem = string.IsNullOrEmpty(subsystem) ? DefaultSubsystem : subsystem; } /// diff --git a/src/System.Management.Automation/engine/remoting/fanin/OutOfProcTransportManager.cs b/src/System.Management.Automation/engine/remoting/fanin/OutOfProcTransportManager.cs index a5478bfd3b5..3272a6363b1 100644 --- a/src/System.Management.Automation/engine/remoting/fanin/OutOfProcTransportManager.cs +++ b/src/System.Management.Automation/engine/remoting/fanin/OutOfProcTransportManager.cs @@ -593,7 +593,6 @@ internal override void CloseAsync() } catch (Exception ex) when (ex is IOException || ex is ObjectDisposedException) { - // Cannot communicate with server. Allow client to complete close operation. shouldRaiseCloseCompleted = true; } @@ -1683,7 +1682,7 @@ internal override void CreateAsync() var errorMessage = StringUtil.Format(RemotingErrorIdStrings.SSHClientConnectTimeout, _connectionInfo.ConnectingTimeout / 1000); if (sshTerminated) { - errorMessage += "\n" + RemotingErrorIdStrings.SSHClientConnectProcessTerminated; + errorMessage += RemotingErrorIdStrings.SSHClientConnectProcessTerminated; } HandleSSHError( diff --git a/src/System.Management.Automation/resources/RemotingErrorIdStrings.resx b/src/System.Management.Automation/resources/RemotingErrorIdStrings.resx index 664033e20bd..441228c9c44 100644 --- a/src/System.Management.Automation/resources/RemotingErrorIdStrings.resx +++ b/src/System.Management.Automation/resources/RemotingErrorIdStrings.resx @@ -1629,7 +1629,8 @@ All WinRM sessions connected to PowerShell session configurations, such as Micro SSH connection attempt failed after time out: {0} seconds. - SSH client process terminated before connection could be established. + +SSH client process terminated before connection could be established. The provided SSHConnection hashtable is missing the required ComputerName or HostName parameter. From bd137f207737220b29d43412b2bdfa1a4589ce6e Mon Sep 17 00:00:00 2001 From: Paul Higinbotham Date: Tue, 13 Apr 2021 10:11:29 -0700 Subject: [PATCH 21/22] Resolve more comment change requests --- .../engine/remoting/commands/PSRemotingCmdlet.cs | 3 ++- .../engine/remoting/common/RunspaceConnectionInfo.cs | 2 +- .../engine/remoting/fanin/OutOfProcTransportManager.cs | 2 ++ 3 files changed, 5 insertions(+), 2 deletions(-) diff --git a/src/System.Management.Automation/engine/remoting/commands/PSRemotingCmdlet.cs b/src/System.Management.Automation/engine/remoting/commands/PSRemotingCmdlet.cs index 62bc698bda6..40be7ec2503 100644 --- a/src/System.Management.Automation/engine/remoting/commands/PSRemotingCmdlet.cs +++ b/src/System.Management.Automation/engine/remoting/commands/PSRemotingCmdlet.cs @@ -15,6 +15,7 @@ using System.Management.Automation.Remoting; using System.Management.Automation.Remoting.Client; using System.Management.Automation.Runspaces; +using System.Threading; using Dbg = System.Management.Automation.Diagnostics; @@ -774,7 +775,7 @@ public virtual string KeyFilePath /// Default timeout value is infinite. /// [Parameter(ParameterSetName = PSRemotingBaseCmdlet.SSHHostParameterSet)] - public virtual int ConnectingTimeout { get; set; } = -1; + public virtual int ConnectingTimeout { get; set; } = Timeout.Infinite; /// /// This parameter specifies that SSH is used to establish the remote diff --git a/src/System.Management.Automation/engine/remoting/common/RunspaceConnectionInfo.cs b/src/System.Management.Automation/engine/remoting/common/RunspaceConnectionInfo.cs index dba6a557e6f..a16e7820bd1 100644 --- a/src/System.Management.Automation/engine/remoting/common/RunspaceConnectionInfo.cs +++ b/src/System.Management.Automation/engine/remoting/common/RunspaceConnectionInfo.cs @@ -1917,7 +1917,7 @@ public sealed class SSHConnectionInfo : RunspaceConnectionInfo /// /// Default value is infinite timeout. /// - private const int DefaultConnectingTimeoutTime = -1; + private const int DefaultConnectingTimeoutTime = Timeout.Infinite; #endregion diff --git a/src/System.Management.Automation/engine/remoting/fanin/OutOfProcTransportManager.cs b/src/System.Management.Automation/engine/remoting/fanin/OutOfProcTransportManager.cs index 3272a6363b1..ff206938879 100644 --- a/src/System.Management.Automation/engine/remoting/fanin/OutOfProcTransportManager.cs +++ b/src/System.Management.Automation/engine/remoting/fanin/OutOfProcTransportManager.cs @@ -1657,6 +1657,7 @@ internal override void CreateAsync() } // Start connection timeout timer if requested. + // Timer callback occurs only once after timeout time. _connectionTimer = new Timer( callback: (_) => { @@ -1685,6 +1686,7 @@ internal override void CreateAsync() errorMessage += RemotingErrorIdStrings.SSHClientConnectProcessTerminated; } + // Report error and terminate connection attempt. HandleSSHError( new PSRemotingTransportException(errorMessage)); }, From 082b50a3c0bc2ac3cff04c54a1ecbcbedd46e567 Mon Sep 17 00:00:00 2001 From: Paul Higinbotham Date: Tue, 13 Apr 2021 10:21:15 -0700 Subject: [PATCH 22/22] Previously neglected to commit test change --- test/SSHRemoting/SSHRemoting.Basic.Tests.ps1 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/SSHRemoting/SSHRemoting.Basic.Tests.ps1 b/test/SSHRemoting/SSHRemoting.Basic.Tests.ps1 index e5c7ac3d743..7383cc2b8a1 100644 --- a/test/SSHRemoting/SSHRemoting.Basic.Tests.ps1 +++ b/test/SSHRemoting/SSHRemoting.Basic.Tests.ps1 @@ -18,7 +18,7 @@ Describe "SSHRemoting Basic Tests" -tags CI { } else { - Write-Verbose -Verbose "Restarting Linux SSHD service..." + Write-Verbose -Verbose "Restarting Unix SSHD service..." sudo service ssh restart $status = sudo service ssh status Write-Verbose -Verbose "SSHD service status: $status"