Skip to content
This repository was archived by the owner on Jan 23, 2026. It is now read-only.

Commit 09da837

Browse files
authored
Merge pull request #785 from jumpstarter-dev/backport-750-to-release-0.7
[Backport release-0.7] Add properties to SSHWrapperClient
2 parents bd07a7b + 3070fd0 commit 09da837

File tree

2 files changed

+47
-10
lines changed

2 files changed

+47
-10
lines changed

packages/jumpstarter-driver-ssh/jumpstarter_driver_ssh/client.py

Lines changed: 32 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -94,13 +94,33 @@ def stream(self, method="connect"):
9494
async def stream_async(self, method):
9595
return await self.tcp.stream_async(method)
9696

97+
@property
98+
def command(self) -> str:
99+
"""Get the base SSH command"""
100+
return self.call("get_ssh_command")
101+
102+
@property
103+
def identity(self) -> str | None:
104+
"""
105+
Get the SSH identity (private key) as a string.
106+
107+
Returns:
108+
The SSH identity key content, or None if not configured.
109+
110+
Raises:
111+
ConfigurationError: If `ssh_identity_file` is configured on the
112+
driver but cannot be read.
113+
"""
114+
return self.call("get_ssh_identity")
115+
116+
@property
117+
def username(self) -> str:
118+
"""Get the default SSH username"""
119+
return self.call("get_default_username")
120+
97121
def run(self, options: SSHCommandRunOptions, args) -> SSHCommandRunResult:
98122
"""Run SSH command with the given parameters and arguments"""
99123
# Get SSH command and default username from driver
100-
ssh_command = self.call("get_ssh_command")
101-
default_username = self.call("get_default_username")
102-
ssh_identity = self.call("get_ssh_identity")
103-
104124
if options.direct:
105125
# Use direct TCP address
106126
try:
@@ -111,7 +131,7 @@ def run(self, options: SSHCommandRunOptions, args) -> SSHCommandRunResult:
111131
if not host or not port:
112132
raise ValueError(f"Invalid address format: {address}")
113133
self.logger.debug("Using direct TCP connection for SSH - host: %s, port: %s", host, port)
114-
return self._run_ssh_local(host, port, ssh_command, options, default_username, ssh_identity, args)
134+
return self._run_ssh_local(host, port, options, args)
115135
except (DriverMethodNotImplemented, ValueError) as e:
116136
self.logger.error("Direct address connection failed (%s), falling back to SSH port forwarding", e)
117137
return self.run(SSHCommandRunOptions(
@@ -127,11 +147,12 @@ def run(self, options: SSHCommandRunOptions, args) -> SSHCommandRunResult:
127147
) as addr:
128148
host, port = addr
129149
self.logger.debug("SSH port forward established - host: %s, port: %s", host, port)
130-
return self._run_ssh_local(host, port, ssh_command, options, default_username, ssh_identity, args)
150+
return self._run_ssh_local(host, port, options, args)
131151

132-
def _run_ssh_local(self, host, port, ssh_command, options, default_username, ssh_identity, args):
152+
def _run_ssh_local(self, host, port, options, args):
133153
"""Run SSH command with the given host, port, and arguments"""
134154
# Create temporary identity file if needed
155+
ssh_identity = self.identity
135156
identity_file = None
136157
temp_file = None
137158
if ssh_identity:
@@ -154,7 +175,7 @@ def _run_ssh_local(self, host, port, ssh_command, options, default_username, ssh
154175

155176
try:
156177
# Build SSH command arguments
157-
ssh_args = self._build_ssh_command_args(ssh_command, port, default_username, identity_file, args)
178+
ssh_args = self._build_ssh_command_args(port, identity_file, args)
158179

159180
# Separate SSH options from command arguments
160181
ssh_options, command_args = self._separate_ssh_options_and_command_args(args)
@@ -173,10 +194,11 @@ def _run_ssh_local(self, host, port, ssh_command, options, default_username, ssh
173194
except Exception as e:
174195
self.logger.warning("Failed to clean up temporary identity file %s: %s", identity_file, str(e))
175196

176-
def _build_ssh_command_args(self, ssh_command, port, default_username, identity_file, args):
197+
def _build_ssh_command_args(self, port, identity_file, args):
177198
"""Build initial SSH command arguments"""
178199
# Split the SSH command into individual arguments
179-
ssh_args = shlex.split(ssh_command)
200+
ssh_args = shlex.split(self.command)
201+
default_username = self.username
180202

181203
# Add identity file if provided
182204
if identity_file:

packages/jumpstarter-driver-ssh/jumpstarter_driver_ssh/driver_test.py

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -692,3 +692,18 @@ def test_ssh_identity_temp_file_cleanup_error():
692692

693693
assert result.return_code == 0
694694
assert result.stdout == "some stdout"
695+
696+
697+
def test_ssh_client_properties():
698+
"""Test that the client properties correctly reflect the driver configuration"""
699+
instance = SSHWrapper(
700+
children={"tcp": TcpNetwork(host="127.0.0.1", port=22)},
701+
default_username="testuser",
702+
ssh_identity=TEST_SSH_KEY,
703+
ssh_command="my-ssh-command",
704+
)
705+
706+
with serve(instance) as client:
707+
assert client.username == "testuser"
708+
assert client.identity == TEST_SSH_KEY
709+
assert client.command == "my-ssh-command"

0 commit comments

Comments
 (0)