@@ -188,22 +188,44 @@ def test_each_jumpbox_prerequisite_is_checked_on_its_own(script):
188188 assert "command -v rsync" not in boot
189189
190190
191- def test_ssh_to_the_jumpbox_never_falls_back_to_a_password (script ):
192- """Found on a live deploy (six retries, all stalled at the jumpbox).
191+ # Every shipped example carries a jumpbox, so each produces a deploy script.
192+ JUMPBOX_EXAMPLES = ["minimal.json" , "parallel-chains.json" , "peered.json" , "redstack.json" ]
193+
194+
195+ @pytest .mark .parametrize ("name" , JUMPBOX_EXAMPLES )
196+ @pytest .mark .parametrize ("provider" , ["gcp" , "aws" ])
197+ def test_ssh_to_the_jumpbox_is_non_interactive_everywhere (provider , name ):
198+ """Found on a live AWS deploy (six retries, all stalled at the jumpbox).
193199
194200 A fresh box answers port 22 before cloud-init has written the operator user's
195201 authorized_keys, so the key is briefly refused. Without BatchMode, ssh then
196202 prompts for a password: the wait-for-ssh probe stalls on that prompt instead of
197203 retrying, and the first heredoc step dies with `Permission denied, please try
198- again`. Every operator-side ssh and scp that targets the jumpbox must set
199- BatchMode=yes so a not-yet-ready key fails fast and the retry loop rides out the
200- cloud-init race, the job that loop was written to do.
204+ again`.
205+
206+ The jumpbox SSH path is shared, the provider only swaps the preflight and the
207+ DNS reminder, never the connection, so the guarantee is asserted across both
208+ providers and every jumpbox-bearing blueprint: every operator-side ssh goes
209+ through $SSH, which sets BatchMode, every scp sets it too, and nothing runs a
210+ bare `ssh` that could fall back to a password. A not-yet-ready key then fails
211+ fast and the retry loop rides out the cloud-init race, the job it was written for.
201212 """
202- code = _code (script )
203- ssh_def = [ln for ln in code .splitlines () if ln .strip ().startswith ("SSH=" )]
204- assert ssh_def and all ("BatchMode=yes" in ln for ln in ssh_def ), ssh_def
205- scp_lines = [ln for ln in code .splitlines () if ln .strip ().startswith ("scp " )]
206- assert scp_lines and all ("BatchMode=yes" in ln for ln in scp_lines ), scp_lines
213+ script = generate_deploy_script (example (name ), Registry (), provider = provider )
214+ assert script , (name , provider , "a jumpbox topology must produce a deploy script" )
215+ # Operator-side only: the BOOT/RUNNER heredocs run locally on the jumpbox, where
216+ # a prompt cannot happen and is not the failure this guards.
217+ lines = _code (script , local_only = True ).splitlines ()
218+
219+ ssh_def = [ln for ln in lines if ln .strip ().startswith ("SSH=" )]
220+ assert ssh_def and all ("BatchMode=yes" in ln for ln in ssh_def ), (name , provider , ssh_def )
221+
222+ scp_lines = [ln for ln in lines if ln .strip ().startswith ("scp " )]
223+ assert scp_lines and all ("BatchMode=yes" in ln for ln in scp_lines ), (name , provider , scp_lines )
224+
225+ # No executed ssh bypasses $SSH. The SSH= assignment and the echoed "watch it"
226+ # hints do not start with "ssh ", so only a bare executed ssh would trip this.
227+ bare = [ln for ln in lines if ln .strip ().startswith ("ssh " )]
228+ assert not bare , (name , provider , bare )
207229
208230
209231def test_pip_is_invoked_as_a_python_module (script ):
0 commit comments