Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 10 additions & 4 deletions pysqa/base/remote.py
Original file line number Diff line number Diff line change
Expand Up @@ -93,9 +93,12 @@ def __init__(
)
self._ssh_host = config["ssh_host"]
self._ssh_username = config["ssh_username"]
self._ssh_known_hosts = os.path.abspath(
os.path.expanduser(config["known_hosts"])
)
if "known_hosts" in config:
self._ssh_known_hosts = os.path.abspath(
os.path.expanduser(config["known_hosts"])
)
else:
self._ssh_known_hosts = ""
self._ssh_ask_for_password: Union[bool, str] = False
self._ssh_key = (
os.path.abspath(os.path.expanduser(config["ssh_key"]))
Expand Down Expand Up @@ -346,7 +349,10 @@ def _open_ssh_connection(self) -> paramiko.SSHClient:
paramiko.SSHClient: The SSH connection object.
"""
ssh = paramiko.SSHClient()
ssh.load_host_keys(self._ssh_known_hosts)
if len(self._ssh_known_hosts) > 0:
ssh.load_host_keys(self._ssh_known_hosts)
else:
ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
if (
self._ssh_key is not None
and self._ssh_key_passphrase is not None
Expand Down
1 change: 0 additions & 1 deletion tests/config/remote_rebex/queue.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ queue_type: REMOTE
queue_primary: remote
ssh_host: test.rebex.net
ssh_username: demo
known_hosts: ~/.ssh/known_hosts
ssh_password: password
ssh_remote_config_dir: /
ssh_remote_path: /
Expand Down
14 changes: 14 additions & 0 deletions tests/config/remote_rebex_hosts/queue.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
queue_type: REMOTE
queue_primary: remote
ssh_host: test.rebex.net
ssh_username: demo
known_hosts: ~/.ssh/known_hosts
ssh_password: password
ssh_remote_config_dir: /
ssh_remote_path: /
ssh_local_path: /home/localuser/projects/
ssh_continous_connection: False
ssh_port: 22
python_executable: python3
queues:
remote: {cores_max: 100, cores_min: 10, run_time_max: 259200}
27 changes: 22 additions & 5 deletions tests/test_remote.py
Original file line number Diff line number Diff line change
Expand Up @@ -151,36 +151,53 @@ def test_remote_command_continous_connection(self):
output = remote._adapter._execute_remote_command(command="pwd")
self.assertEqual(output, "/\n")

def test_remote_command_individual_connections_hosts(self):
path = os.path.dirname(os.path.abspath(__file__))
remote = QueueAdapter(directory=os.path.join(path, "config/remote_rebex_hosts"))
remote._adapter._ssh_remote_path = path
remote._adapter._open_ssh_connection()
output = remote._adapter._execute_remote_command(command="pwd")
self.assertEqual(output, "/\n")

Comment on lines +154 to +161
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Avoid opening a redundant SSH connection in the individual-connections host test.

When _ssh_continous_connection is False, _execute_remote_command opens and closes its own connection. Calling _open_ssh_connection beforehand (Line 158) creates an unused connection and may leak resources.

Apply this diff:

 def test_remote_command_individual_connections_hosts(self):
     path = os.path.dirname(os.path.abspath(__file__))
     remote = QueueAdapter(directory=os.path.join(path, "config/remote_rebex_hosts"))
     remote._adapter._ssh_remote_path = path
-    remote._adapter._open_ssh_connection()
     output = remote._adapter._execute_remote_command(command="pwd")
     self.assertEqual(output, "/\n")

Optionally, make the same change in test_remote_command_individual_connections above (Lines 141–143) for consistency.

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
def test_remote_command_individual_connections_hosts(self):
path = os.path.dirname(os.path.abspath(__file__))
remote = QueueAdapter(directory=os.path.join(path, "config/remote_rebex_hosts"))
remote._adapter._ssh_remote_path = path
remote._adapter._open_ssh_connection()
output = remote._adapter._execute_remote_command(command="pwd")
self.assertEqual(output, "/\n")
def test_remote_command_individual_connections_hosts(self):
path = os.path.dirname(os.path.abspath(__file__))
remote = QueueAdapter(directory=os.path.join(path, "config/remote_rebex_hosts"))
remote._adapter._ssh_remote_path = path
output = remote._adapter._execute_remote_command(command="pwd")
self.assertEqual(output, "/\n")
🤖 Prompt for AI Agents
In tests/test_remote.py around lines 154 to 161, the test calls
remote._adapter._open_ssh_connection() before executing the command which is
redundant when _ssh_continous_connection is False and causes a leaked unused
connection; remove the explicit call to _open_ssh_connection() (line 158) so
_execute_remote_command manages its own connect/disconnect, and also apply the
same removal to the similar test at lines ~141–143 for consistency.

def test_remote_command_continous_connection_hosts(self):
path = os.path.dirname(os.path.abspath(__file__))
remote = QueueAdapter(directory=os.path.join(path, "config/remote_rebex_hosts"))
remote._adapter._ssh_remote_path = path
remote._adapter._ssh_continous_connection = True
remote._adapter._open_ssh_connection()
output = remote._adapter._execute_remote_command(command="pwd")
self.assertEqual(output, "/\n")

def test_submit_job(self):
path = os.path.dirname(os.path.abspath(__file__))
remote = QueueAdapter(directory=os.path.join(path, "config/remote_rebex"))
remote = QueueAdapter(directory=os.path.join(path, "config/remote_rebex_hosts"))
remote._adapter._ssh_remote_path = path
output = remote._adapter.submit_job(working_directory=os.path.join(path, "config/empty"), command="echo 1")
self.assertEqual(output, 1)

def test_transferfile_individual_connections(self):
path = os.path.dirname(os.path.abspath(__file__))
remote = QueueAdapter(directory=os.path.join(path, "config/remote_rebex"))
remote = QueueAdapter(directory=os.path.join(path, "config/remote_rebex_hosts"))
remote._adapter._ssh_remote_path = path
self.assertIsNone(remote._adapter.transfer_file(file="readme.txt", transfer_back=True))

def test_transferfile_continous_connection(self):
path = os.path.dirname(os.path.abspath(__file__))
remote = QueueAdapter(directory=os.path.join(path, "config/remote_rebex"))
remote = QueueAdapter(directory=os.path.join(path, "config/remote_rebex_hosts"))
remote._adapter._ssh_remote_path = path
remote._adapter._ssh_continous_connection = True
self.assertIsNone(remote._adapter.transfer_file(file="readme.txt", transfer_back=True))

def test_get_transport(self):
path = os.path.dirname(os.path.abspath(__file__))
remote = QueueAdapter(directory=os.path.join(path, "config/remote_rebex"))
remote = QueueAdapter(directory=os.path.join(path, "config/remote_rebex_hosts"))
self.assertIsNotNone(get_transport(remote._adapter._open_ssh_connection()))
with self.assertRaises(ValueError):
get_transport(ssh=FakeSSH())

def test_get_job_from_remote(self):
path = os.path.dirname(os.path.abspath(__file__))
remote = QueueAdapter(directory=os.path.join(path, "config/remote_rebex"))
remote = QueueAdapter(directory=os.path.join(path, "config/remote_rebex_hosts"))
remote._adapter._ssh_remote_path = path
remote._adapter._ssh_local_path = path
remote._adapter._ssh_delete_file_on_remote = True
Expand Down
6 changes: 3 additions & 3 deletions tests/test_remote_auth.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,21 +19,21 @@
class TestRemoteQueueAdapterAuth(unittest.TestCase):
def test_password_auth(self):
path = os.path.dirname(os.path.abspath(__file__))
remote = QueueAdapter(directory=os.path.join(path, "config/remote_rebex"))
remote = QueueAdapter(directory=os.path.join(path, "config/remote_rebex_hosts"))
remote._adapter._ssh_ask_for_password = False
remote._adapter._ssh_key = None
self.assertIsNotNone(remote._adapter._open_ssh_connection())

def test_key_auth(self):
path = os.path.dirname(os.path.abspath(__file__))
remote = QueueAdapter(directory=os.path.join(path, "config/remote_rebex"))
remote = QueueAdapter(directory=os.path.join(path, "config/remote_rebex_hosts"))
remote._adapter._ssh_password = None
with self.assertRaises(ValueError):
remote._adapter._open_ssh_connection()

def test_two_factor_auth(self):
path = os.path.dirname(os.path.abspath(__file__))
remote = QueueAdapter(directory=os.path.join(path, "config/remote_rebex"))
remote = QueueAdapter(directory=os.path.join(path, "config/remote_rebex_hosts"))
remote._adapter._ssh_two_factor_authentication = True
with self.assertRaises(paramiko.ssh_exception.AuthenticationException):
remote._adapter._open_ssh_connection()
Loading