11import base64
22import logging
3+ import os
4+ import platform
35import shlex
46import subprocess
57
@@ -63,7 +65,7 @@ def _ellide_log_lines(log):
6365 return "\n {}" .format ("\n " .join (reduced_message ))
6466
6567def _ssh (hostname_or_ip , cmd , check , simple_output , suppress_fingerprint_warnings ,
66- background , decode , options ) -> Union [SSHResult , SSHCommandFailed , str , bytes , None ]:
68+ background , decode , options , multiplexing ) -> Union [SSHResult , SSHCommandFailed , str , bytes , None ]:
6769 opts = list (options )
6870 opts .append ('-o "BatchMode yes"' )
6971 opts .append ('-o "PubkeyAcceptedAlgorithms +ssh-rsa"' )
@@ -74,6 +76,18 @@ def _ssh(hostname_or_ip, cmd, check, simple_output, suppress_fingerprint_warning
7476 opts .append ('-o "StrictHostKeyChecking no"' )
7577 opts .append ('-o "LogLevel ERROR"' )
7678 opts .append ('-o "UserKnownHostsFile /dev/null"' )
79+ # ssh multiplexing is not always well supported on windows, so we disable it on that platform.
80+ # It could work with git bash—we might want to check that instead.
81+ # We use the pid in the control path to avoid a race condition on the master socket creation
82+ # when running the tests in parallel. The socket is removed by the ssh client olding the master
83+ # connection when it reaches the timeout.
84+ if multiplexing and platform .system () != "Windows" :
85+ opts .append ('-o "ControlMaster auto"' )
86+ opts .append (f'-o "ControlPath ~/.ssh/control-{ os .getpid ()} :%h:%p:%r"' )
87+ opts .append ('-o "ControlPersist 10m"' )
88+ opts .append ('-o "ServerAliveInterval 10s"' )
89+ else :
90+ opts .append ('-o "ControlMaster no"' )
7791
7892 if isinstance (cmd , str ):
7993 command = cmd
@@ -143,47 +157,47 @@ def _ssh(hostname_or_ip, cmd, check, simple_output, suppress_fingerprint_warning
143157def ssh (hostname_or_ip : HostAddress , cmd : Union [str , List [str ]], * , check : bool = True ,
144158 simple_output : Literal [True ] = True ,
145159 suppress_fingerprint_warnings : bool = True , background : Literal [False ] = False ,
146- decode : Literal [True ] = True , options : List [str ] = []) -> str :
160+ decode : Literal [True ] = True , options : List [str ] = [], multiplexing = True ) -> str :
147161 ...
148162@overload
149163def ssh (hostname_or_ip : HostAddress , cmd : Union [str , List [str ]], * , check : bool = True ,
150164 simple_output : Literal [True ] = True ,
151165 suppress_fingerprint_warnings : bool = True , background : Literal [False ] = False ,
152- decode : Literal [False ], options : List [str ] = []) -> bytes :
166+ decode : Literal [False ], options : List [str ] = [], multiplexing = True ) -> bytes :
153167 ...
154168@overload
155169def ssh (hostname_or_ip : HostAddress , cmd : Union [str , List [str ]], * , check : bool = True ,
156170 simple_output : Literal [False ],
157171 suppress_fingerprint_warnings : bool = True , background : Literal [False ] = False ,
158- decode : bool = True , options : List [str ] = []) -> SSHResult :
172+ decode : bool = True , options : List [str ] = [], multiplexing = True ) -> SSHResult :
159173 ...
160174@overload
161175def ssh (hostname_or_ip : HostAddress , cmd : Union [str , List [str ]], * , check : bool = True ,
162176 simple_output : Literal [False ],
163177 suppress_fingerprint_warnings : bool = True , background : Literal [True ],
164- decode : bool = True , options : List [str ] = []) -> None :
178+ decode : bool = True , options : List [str ] = [], multiplexing = True ) -> None :
165179 ...
166180@overload
167181def ssh (hostname_or_ip : HostAddress , cmd : Union [str , List [str ]], * , check = True ,
168182 simple_output : bool = True ,
169183 suppress_fingerprint_warnings = True , background : bool = False ,
170- decode : bool = True , options : List [str ] = []) \
184+ decode : bool = True , options : List [str ] = [], multiplexing = True ) \
171185 -> Union [str , bytes , SSHResult , None ]:
172186 ...
173187def ssh (hostname_or_ip , cmd , * , check = True , simple_output = True ,
174188 suppress_fingerprint_warnings = True ,
175- background = False , decode = True , options = []):
189+ background = False , decode = True , options = [], multiplexing = True ):
176190 result_or_exc = _ssh (hostname_or_ip , cmd , check , simple_output , suppress_fingerprint_warnings ,
177- background , decode , options )
191+ background , decode , options , multiplexing )
178192 if isinstance (result_or_exc , SSHCommandFailed ):
179193 raise result_or_exc
180194 else :
181195 return result_or_exc
182196
183197def ssh_with_result (hostname_or_ip , cmd , suppress_fingerprint_warnings = True ,
184- background = False , decode = True , options = []) -> SSHResult :
198+ background = False , decode = True , options = [], multiplexing = True ) -> SSHResult :
185199 result_or_exc = _ssh (hostname_or_ip , cmd , False , False , suppress_fingerprint_warnings ,
186- background , decode , options )
200+ background , decode , options , multiplexing )
187201 if isinstance (result_or_exc , SSHCommandFailed ):
188202 raise result_or_exc
189203 elif isinstance (result_or_exc , SSHResult ):
0 commit comments