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

feat(driver-ssh): add --user selection to j ssh#769

Closed
aesteve-rh wants to merge 1 commit intojumpstarter-dev:mainfrom
aesteve-rh:user-arg-ssh
Closed

feat(driver-ssh): add --user selection to j ssh#769
aesteve-rh wants to merge 1 commit intojumpstarter-dev:mainfrom
aesteve-rh:user-arg-ssh

Conversation

@aesteve-rh
Copy link
Contributor

@aesteve-rh aesteve-rh commented Dec 4, 2025

Allow j ssh -u someuser to be able to select the user with which to connect to the board.

Summary by CodeRabbit

  • New Features

    • Added CLI option (-u/--user) to specify an SSH user; the explicit user now overrides the configured default and is preserved across direct and fallback SSH execution paths.
  • Tests

    • Added tests verifying the explicit user overrides defaults both for direct SSH and when falling back to port-forwarding execution.

✏️ Tip: You can customize this high-level summary in your review settings.

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Dec 4, 2025

Walkthrough

A new optional CLI flag for SSH user (-u/--user) is added and threaded from the CLI handler through SSHWrapperClient.run into _run_ssh_local and _build_ssh_command_args. The provided user, when present, is preferred over the driver's default username during SSH command construction and preserved across fallback paths.

Changes

Cohort / File(s) Summary
SSH client — user parameter threading
packages/jumpstarter-driver-ssh/jumpstarter_driver_ssh/client.py
Added optional user parameter to the CLI ssh handler and threaded it through run(...), _run_ssh_local(...), and _build_ssh_command_args(...). Updated logic to prefer the explicit user over default_username and pass user across direct and fallback execution paths. Type hints updated accordingly.
Tests — explicit user behavior
packages/jumpstarter-driver-ssh/jumpstarter_driver_ssh/driver_test.py
Added test_ssh_command_with_explicit_user_parameter and test_ssh_command_with_explicit_user_parameter_fallback to verify that an explicit user override is applied in both direct and fallback SSH execution flows and that the default username is not used when override is provided.

Estimated code review effort

🎯 2 (Simple) | ⏱️ ~12 minutes

  • Changes are localized parameter threading and small conditional logic.
  • Tests cover direct and fallback flows.
  • Areas to check:
    • All call sites updated to accept and forward the optional user.
    • _build_ssh_command_args prioritization of explicit user over default_username.
    • CLI wiring passes the user into run.

Possibly related PRs

  • jumpstarter-dev/jumpstarter#748: Modifies the SSH client call path and threading of additional parameters through SSHWrapperClient.run and related helpers.

Suggested reviewers

  • mangelajo

Poem

🐰 I hopped in with a tiny new flag,

-u in paw, no username drag.
Through run and local, I carried it true,
Commands now greet hosts with the name you choose.
A little hop, a tidy thread — hooray, SSH is through! 🐇✨

Pre-merge checks and finishing touches

✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly and concisely describes the main change: adding a --user selection option to the j ssh command, which aligns perfectly with the PR objectives and code changes.
Docstring Coverage ✅ Passed Docstring coverage is 90.00% which is sufficient. The required threshold is 80.00%.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment

📜 Recent review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 7069788 and a5fa019.

📒 Files selected for processing (2)
  • packages/jumpstarter-driver-ssh/jumpstarter_driver_ssh/client.py (6 hunks)
  • packages/jumpstarter-driver-ssh/jumpstarter_driver_ssh/driver_test.py (1 hunks)
🧰 Additional context used
📓 Path-based instructions (3)
packages/jumpstarter-driver-*/jumpstarter_driver_*/client.py

📄 CodeRabbit inference engine (.cursor/rules/creating-new-drivers.mdc)

Driver client CLIs should either implement known classes that provide a CLI interface, or implement their own CLI interface. Use CompositeClient from jumpstarter_driver_composite.client for composite drivers with child drivers.

Files:

  • packages/jumpstarter-driver-ssh/jumpstarter_driver_ssh/client.py
packages/jumpstarter-driver-**/jumpstarter_driver_**/*.py

📄 CodeRabbit inference engine (.cursor/rules/creating-new-drivers.mdc)

Driver implementations should follow existing code style validated with make lint (fix with make lint-fix), perform static type checking with make ty-pkg-${package_name}, add comprehensive tests, and verify all tests pass with make test-pkg-${package_name} or make test

Files:

  • packages/jumpstarter-driver-ssh/jumpstarter_driver_ssh/client.py
  • packages/jumpstarter-driver-ssh/jumpstarter_driver_ssh/driver_test.py
**/*.py

📄 CodeRabbit inference engine (.cursor/rules/project-structure.mdc)

Ruff should be used for code formatting and linting, excluding jumpstarter-protocol package

Files:

  • packages/jumpstarter-driver-ssh/jumpstarter_driver_ssh/client.py
  • packages/jumpstarter-driver-ssh/jumpstarter_driver_ssh/driver_test.py
🧠 Learnings (5)
📓 Common learnings
Learnt from: mangelajo
Repo: jumpstarter-dev/jumpstarter PR: 647
File: packages/jumpstarter-driver-ssh/jumpstarter_driver_ssh/client.py:0-0
Timestamp: 2025-09-25T05:52:27.157Z
Learning: In the SSHWrapper driver's client.py, hostname replacement should only occur for "userhostname" format arguments that get preprocessed into "-l user hostname". Regular hostname arguments (like "device" in SSH commands) should be passed through unchanged to allow the wrapper to handle connections internally.
📚 Learning: 2025-09-25T05:52:27.157Z
Learnt from: mangelajo
Repo: jumpstarter-dev/jumpstarter PR: 647
File: packages/jumpstarter-driver-ssh/jumpstarter_driver_ssh/client.py:0-0
Timestamp: 2025-09-25T05:52:27.157Z
Learning: In the SSHWrapper driver's client.py, hostname replacement should only occur for "userhostname" format arguments that get preprocessed into "-l user hostname". Regular hostname arguments (like "device" in SSH commands) should be passed through unchanged to allow the wrapper to handle connections internally.

Applied to files:

  • packages/jumpstarter-driver-ssh/jumpstarter_driver_ssh/client.py
📚 Learning: 2025-11-27T09:58:41.875Z
Learnt from: CR
Repo: jumpstarter-dev/jumpstarter PR: 0
File: .cursor/rules/creating-new-drivers.mdc:0-0
Timestamp: 2025-11-27T09:58:41.875Z
Learning: Applies to packages/jumpstarter-driver-*/jumpstarter_driver_*/client.py : Driver client CLIs should either implement known classes that provide a CLI interface, or implement their own CLI interface. Use `CompositeClient` from `jumpstarter_driver_composite.client` for composite drivers with child drivers.

Applied to files:

  • packages/jumpstarter-driver-ssh/jumpstarter_driver_ssh/client.py
📚 Learning: 2025-11-27T09:58:55.346Z
Learnt from: CR
Repo: jumpstarter-dev/jumpstarter PR: 0
File: .cursor/rules/project-structure.mdc:0-0
Timestamp: 2025-11-27T09:58:55.346Z
Learning: Applies to packages/jumpstarter-driver-*/jumpstarter_driver_*/ : Driver packages must implement a `client.py` file containing the client implementation

Applied to files:

  • packages/jumpstarter-driver-ssh/jumpstarter_driver_ssh/client.py
📚 Learning: 2025-11-27T09:58:41.875Z
Learnt from: CR
Repo: jumpstarter-dev/jumpstarter PR: 0
File: .cursor/rules/creating-new-drivers.mdc:0-0
Timestamp: 2025-11-27T09:58:41.875Z
Learning: Applies to packages/jumpstarter-driver-**/jumpstarter_driver_**/*.py : Driver implementations should follow existing code style validated with `make lint` (fix with `make lint-fix`), perform static type checking with `make ty-pkg-${package_name}`, add comprehensive tests, and verify all tests pass with `make test-pkg-${package_name}` or `make test`

Applied to files:

  • packages/jumpstarter-driver-ssh/jumpstarter_driver_ssh/driver_test.py
🧬 Code graph analysis (2)
packages/jumpstarter-driver-ssh/jumpstarter_driver_ssh/client.py (1)
packages/jumpstarter/jumpstarter/client/core.py (1)
  • DriverMethodNotImplemented (39-42)
packages/jumpstarter-driver-ssh/jumpstarter_driver_ssh/driver_test.py (3)
packages/jumpstarter-driver-ssh/jumpstarter_driver_ssh/driver.py (2)
  • SSHWrapper (9-50)
  • client (28-29)
packages/jumpstarter-driver-network/jumpstarter_driver_network/driver.py (1)
  • TcpNetwork (88-121)
packages/jumpstarter-driver-ssh/jumpstarter_driver_ssh/client.py (3)
  • run (123-159)
  • SSHCommandRunOptions (33-47)
  • SSHCommandRunResult (17-29)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (9)
  • GitHub Check: check-warnings
  • GitHub Check: build
  • GitHub Check: Redirect rules - jumpstarter-docs
  • GitHub Check: Header rules - jumpstarter-docs
  • GitHub Check: Pages changed - jumpstarter-docs
  • GitHub Check: pytest-matrix (ubuntu-24.04, 3.11)
  • GitHub Check: pytest-matrix (ubuntu-24.04, 3.13)
  • GitHub Check: pytest-matrix (ubuntu-24.04, 3.12)
  • GitHub Check: e2e
🔇 Additional comments (7)
packages/jumpstarter-driver-ssh/jumpstarter_driver_ssh/client.py (5)

65-65: LGTM! Well-formed CLI option.

The Click option follows standard conventions with both short (-u) and long (--user) forms and clear help text.


67-76: LGTM! Proper parameter threading.

The user parameter is correctly captured from the Click option and properly forwarded to self.run() with explicit keyword argument syntax.


123-159: LGTM! Comprehensive parameter threading with excellent fallback handling.

The user parameter is:

  • Properly typed (str | None = None)
  • Well-documented in the docstring
  • Consistently threaded through all execution paths: direct (line 143), port-forward (line 159), and crucially preserved during the fallback from direct to port-forward (line 150)

The fallback preservation ensures that if a user explicitly specifies --user, it will be respected even when the direct connection fails and the system falls back to SSH port forwarding.


161-187: LGTM! Proper signature and forwarding.

The _run_ssh_local() method signature is correctly updated with the optional user parameter, and it's properly forwarded to _build_ssh_command_args() at line 187.


206-232: LGTM! Correct username resolution with proper precedence.

The implementation correctly resolves the username with the following precedence:

  1. Explicit -l flag in SSH args (checked at lines 223-226, highest priority)
  2. --user CLI option (via user parameter)
  3. default_username from driver config (via self.username, lowest priority)

The short-circuit evaluation in user or self.username (line 210) efficiently avoids unnecessary driver calls when an explicit user is provided. The existing logic that checks for -l in args ensures compatibility with manual username specification.

packages/jumpstarter-driver-ssh/jumpstarter_driver_ssh/driver_test.py (2)

108-138: LGTM! Comprehensive test for explicit user override.

This test thoroughly validates that the user parameter correctly overrides the default_username:

  • Verifies overrideuser is present in the SSH command
  • Confirms testuser (the default) is not present
  • Validates the -l flag is positioned correctly with the override value
  • Checks command structure integrity (hostname and command args preserved)

140-161: LGTM! Excellent test for fallback scenario.

This test validates a critical path: when the direct connection fails and the system falls back to SSH port forwarding, the explicit user parameter is preserved. This ensures that users' intent (specifying --user overrideuser) is respected even through error recovery paths.

The test correctly:

  • Simulates direct connection failure by mocking tcp.address() to raise ValueError
  • Verifies that after fallback, overrideuser is still used
  • Confirms the default testuser is not present

This is particularly important for the user experience, as it would be confusing if the --user flag was silently ignored during fallback.


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@netlify
Copy link

netlify bot commented Dec 4, 2025

Deploy Preview for jumpstarter-docs ready!

Name Link
🔨 Latest commit a5fa019
🔍 Latest deploy log https://app.netlify.com/projects/jumpstarter-docs/deploys/693159b88b002d0008da3ae5
😎 Deploy Preview https://deploy-preview-769--jumpstarter-docs.netlify.app
📱 Preview on mobile
Toggle QR Code...

QR Code

Use your smartphone camera to open QR code link.

To edit notification comments on pull requests, go to your Netlify project configuration.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 0

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
packages/jumpstarter-driver-ssh/jumpstarter_driver_ssh/client.py (1)

136-142: Critical: Missing user parameter in fallback path.

When the direct connection fails and falls back to SSH port forwarding, the user parameter is not passed to the recursive run() call. This causes the explicit user override to be lost during fallback.

Apply this diff to fix the issue:

                 return self.run(SSHCommandRunOptions(
                     direct=False,
                     capture_output=options.capture_output,
                     capture_as_text=options.capture_as_text,
-                ), args)
+                ), args, user=user)
🧹 Nitpick comments (1)
packages/jumpstarter-driver-ssh/jumpstarter_driver_ssh/driver_test.py (1)

108-138: LGTM: Test comprehensively validates the user parameter feature.

The test correctly verifies that the explicit user parameter overrides the default username and is properly passed through to the SSH command.

Consider adding a test case for the fallback scenario where direct=True but the direct connection fails and falls back to port forwarding. This would ensure the user parameter is preserved during the fallback (which currently has a bug in the implementation).

Example test structure:

def test_ssh_command_with_explicit_user_parameter_fallback():
    """Test that user parameter is preserved during direct-to-portforward fallback."""
    instance = SSHWrapper(
        children={"tcp": TcpNetwork(host="127.0.0.1", port=22)},
        default_username="testuser",
    )
    
    with serve(instance) as client:
        with patch("subprocess.run") as mock_run:
            mock_run.return_value = MagicMock(returncode=0, stdout="some stdout", stderr="")
            
            # Mock tcp.address() to fail, triggering fallback
            with patch.object(client.tcp, 'address', side_effect=ValueError("Connection failed")):
                result = client.run(SSHCommandRunOptions(direct=True), ["hostname"], user="overrideuser")
                
                # Verify that overrideuser is still used after fallback
                call_args = mock_run.call_args[0][0]
                assert "-l" in call_args
                assert "overrideuser" in call_args
                assert "testuser" not in call_args
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 5300757 and 7069788.

📒 Files selected for processing (2)
  • packages/jumpstarter-driver-ssh/jumpstarter_driver_ssh/client.py (6 hunks)
  • packages/jumpstarter-driver-ssh/jumpstarter_driver_ssh/driver_test.py (1 hunks)
🧰 Additional context used
📓 Path-based instructions (3)
packages/jumpstarter-driver-**/jumpstarter_driver_**/*.py

📄 CodeRabbit inference engine (.cursor/rules/creating-new-drivers.mdc)

Driver implementations should follow existing code style validated with make lint (fix with make lint-fix), perform static type checking with make ty-pkg-${package_name}, add comprehensive tests, and verify all tests pass with make test-pkg-${package_name} or make test

Files:

  • packages/jumpstarter-driver-ssh/jumpstarter_driver_ssh/driver_test.py
  • packages/jumpstarter-driver-ssh/jumpstarter_driver_ssh/client.py
**/*.py

📄 CodeRabbit inference engine (.cursor/rules/project-structure.mdc)

Ruff should be used for code formatting and linting, excluding jumpstarter-protocol package

Files:

  • packages/jumpstarter-driver-ssh/jumpstarter_driver_ssh/driver_test.py
  • packages/jumpstarter-driver-ssh/jumpstarter_driver_ssh/client.py
packages/jumpstarter-driver-*/jumpstarter_driver_*/client.py

📄 CodeRabbit inference engine (.cursor/rules/creating-new-drivers.mdc)

Driver client CLIs should either implement known classes that provide a CLI interface, or implement their own CLI interface. Use CompositeClient from jumpstarter_driver_composite.client for composite drivers with child drivers.

Files:

  • packages/jumpstarter-driver-ssh/jumpstarter_driver_ssh/client.py
🧠 Learnings (2)
📚 Learning: 2025-09-25T05:52:27.157Z
Learnt from: mangelajo
Repo: jumpstarter-dev/jumpstarter PR: 647
File: packages/jumpstarter-driver-ssh/jumpstarter_driver_ssh/client.py:0-0
Timestamp: 2025-09-25T05:52:27.157Z
Learning: In the SSHWrapper driver's client.py, hostname replacement should only occur for "userhostname" format arguments that get preprocessed into "-l user hostname". Regular hostname arguments (like "device" in SSH commands) should be passed through unchanged to allow the wrapper to handle connections internally.

Applied to files:

  • packages/jumpstarter-driver-ssh/jumpstarter_driver_ssh/client.py
📚 Learning: 2025-11-27T09:58:41.875Z
Learnt from: CR
Repo: jumpstarter-dev/jumpstarter PR: 0
File: .cursor/rules/creating-new-drivers.mdc:0-0
Timestamp: 2025-11-27T09:58:41.875Z
Learning: Applies to packages/jumpstarter-driver-*/jumpstarter_driver_*/client.py : Driver client CLIs should either implement known classes that provide a CLI interface, or implement their own CLI interface. Use `CompositeClient` from `jumpstarter_driver_composite.client` for composite drivers with child drivers.

Applied to files:

  • packages/jumpstarter-driver-ssh/jumpstarter_driver_ssh/client.py
🧬 Code graph analysis (1)
packages/jumpstarter-driver-ssh/jumpstarter_driver_ssh/driver_test.py (2)
packages/jumpstarter-driver-ssh/jumpstarter_driver_ssh/driver.py (2)
  • SSHWrapper (9-50)
  • client (28-29)
packages/jumpstarter-driver-ssh/jumpstarter_driver_ssh/client.py (3)
  • run (122-151)
  • SSHCommandRunOptions (33-47)
  • SSHCommandRunResult (17-29)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (9)
  • GitHub Check: Redirect rules - jumpstarter-docs
  • GitHub Check: Header rules - jumpstarter-docs
  • GitHub Check: Pages changed - jumpstarter-docs
  • GitHub Check: pytest-matrix (ubuntu-24.04, 3.13)
  • GitHub Check: pytest-matrix (macos-15, 3.12)
  • GitHub Check: pytest-matrix (ubuntu-24.04, 3.12)
  • GitHub Check: pytest-matrix (macos-15, 3.11)
  • GitHub Check: pytest-matrix (ubuntu-24.04, 3.11)
  • GitHub Check: e2e
🔇 Additional comments (3)
packages/jumpstarter-driver-ssh/jumpstarter_driver_ssh/client.py (3)

65-75: LGTM: CLI option properly integrated.

The --user option is correctly added to the CLI command and properly threaded through to the run method.


153-179: LGTM: User parameter correctly threaded through.

The _run_ssh_local method signature is properly updated and the user parameter is correctly passed to _build_ssh_command_args.


198-224: LGTM: Username selection logic is correct.

The _build_ssh_command_args method correctly prioritizes the explicit user parameter over the driver's default username, while still respecting any -l flags provided in the arguments.

Allow `j ssh -u someuser` to be able to select the user with
which to connect to the board.

Signed-off-by: Albert Esteve <aesteve@redhat.com>
@aesteve-rh aesteve-rh closed this Dec 4, 2025
@aesteve-rh aesteve-rh deleted the user-arg-ssh branch December 4, 2025 10:32
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant