Skip to content

Conversation

@bennyz
Copy link
Member

@bennyz bennyz commented Dec 2, 2025

depends on jumpstarter-dev/jumpstarter-controller#199

Summary by CodeRabbit

  • New Features
    • Added --to-client CLI option so users can transfer lease ownership to another client and update owner, duration, or begin time via the update_lease command.
  • Bug Fixes / Validation
    • Command now requires at least one of duration, begin-time, or --to-client to be specified and shows updated help text/documentation.

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

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Dec 2, 2025

📝 Walkthrough

Walkthrough

Adds lease-owner transfer support via a new CLI option --to-client (mapped to to_client), propagates the new client parameter through the config client to the gRPC ClientService.UpdateLease RPC, and updates validation to require at least one of duration, begin_time, or to_client.

Changes

Cohort / File(s) Summary
CLI layer
packages/jumpstarter-cli/jumpstarter_cli/update.py
Added --to-client (to_client) option to update_lease command; updated signature and help text; validation now requires at least one of --duration, --begin-time, or --to-client; constructs a client path using current namespace and passes it to config.update_lease.
Service layer
packages/jumpstarter/jumpstarter/client/grpc.py
ClientService.UpdateLease signature gains optional `client: str
Config client layer
packages/jumpstarter/jumpstarter/config/client.py
ClientConfigV1Alpha1.update_lease accepts optional `client: str

Sequence Diagram(s)

sequenceDiagram
  autonumber
  actor User
  participant CLI as Jumpstarter CLI
  participant Config as ClientConfigV1Alpha1
  participant Service as ClientService (gRPC)

  Note over CLI,Config: User runs update with --to-client (optional) and/or duration/begin_time
  User->>CLI: run update --name ... [--duration/--begin-time/--to-client]
  CLI->>Config: update_lease(name, duration?, begin_time?, client?=namespace/to-client)
  Config->>Service: UpdateLease(name, duration?, begin_time?, client?)
  alt client provided
    Note right of Service: service sets lease.client and adds "client" to field mask
  else client not provided
    Note right of Service: service updates only provided fields (duration/begin_time)
  end
  Service-->>Config: UpdateLease response
  Config-->>CLI: formatted response / status
  CLI-->>User: display result
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Suggested reviewers

  • mangelajo
  • kirkbrauer

Poem

🐰 I hopped through code, with ears held high,
A lease now skitters to another ally,
CLI, config, and gRPC in a gentle dance,
Handoffs made smooth with a tiny advance —
Hooray, new owner! 🥕✨

Pre-merge checks and finishing touches

❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 25.00% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'cli: support lease transfer' accurately and concisely summarizes the main change—adding CLI support for transferring leases between clients.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment

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.

@bennyz bennyz requested a review from mangelajo December 2, 2025 15:59
@netlify
Copy link

netlify bot commented Dec 2, 2025

Deploy Preview for jumpstarter-docs ready!

Name Link
🔨 Latest commit 8ba9b40
🔍 Latest deploy log https://app.netlify.com/projects/jumpstarter-docs/deploys/694bb884da2e5a00086eda5e
😎 Deploy Preview https://deploy-preview-765--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.

@mangelajo mangelajo added usability user-requested This label means, that actual users are requesting the feature and find it necessary/useful. labels Dec 3, 2025
@bennyz bennyz marked this pull request as ready for review December 17, 2025 08:40
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: 1

📜 Review details

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 882775c and 09b7515.

📒 Files selected for processing (3)
  • packages/jumpstarter-cli/jumpstarter_cli/update.py (1 hunks)
  • packages/jumpstarter/jumpstarter/client/grpc.py (2 hunks)
  • packages/jumpstarter/jumpstarter/config/client.py (1 hunks)
🧰 Additional context used
📓 Path-based instructions (1)
**/*.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/jumpstarter/config/client.py
  • packages/jumpstarter/jumpstarter/client/grpc.py
  • packages/jumpstarter-cli/jumpstarter_cli/update.py
🧠 Learnings (3)
📚 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/jumpstarter/config/client.py
  • packages/jumpstarter/jumpstarter/client/grpc.py
  • packages/jumpstarter-cli/jumpstarter_cli/update.py
📚 Learning: 2025-10-14T17:43:07.788Z
Learnt from: michalskrivanek
Repo: jumpstarter-dev/jumpstarter PR: 704
File: packages/jumpstarter/jumpstarter/client/grpc.py:100-107
Timestamp: 2025-10-14T17:43:07.788Z
Learning: In the Jumpstarter client lease model (packages/jumpstarter/jumpstarter/client/grpc.py), `effective_duration` represents the elapsed time for an active lease so far, not the total duration. To calculate expected release time, use `effective_begin_time + duration` (where `duration` is the configured/requested duration), not `effective_begin_time + effective_duration`.

Applied to files:

  • packages/jumpstarter/jumpstarter/config/client.py
  • packages/jumpstarter/jumpstarter/client/grpc.py
  • packages/jumpstarter-cli/jumpstarter_cli/update.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-cli/jumpstarter_cli/update.py
🧬 Code graph analysis (2)
packages/jumpstarter/jumpstarter/config/client.py (1)
packages/jumpstarter/jumpstarter/client/grpc.py (2)
  • ClientService (316-462)
  • UpdateLease (411-454)
packages/jumpstarter-cli/jumpstarter_cli/update.py (1)
packages/jumpstarter/jumpstarter/config/client.py (2)
  • update_lease (244-252)
  • lease (135-143)
⏰ 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). (7)
  • GitHub Check: pytest-matrix (ubuntu-24.04, 3.11)
  • GitHub Check: pytest-matrix (macos-15, 3.12)
  • GitHub Check: pytest-matrix (ubuntu-24.04, 3.12)
  • GitHub Check: pytest-matrix (ubuntu-24.04, 3.13)
  • GitHub Check: pytest-matrix (macos-15, 3.13)
  • GitHub Check: pytest-matrix (macos-15, 3.11)
  • GitHub Check: e2e
🔇 Additional comments (7)
packages/jumpstarter/jumpstarter/client/grpc.py (1)

417-417: LGTM: Parameter addition follows existing pattern.

The client parameter is added consistently with the existing optional parameters (duration and begin_time), maintaining a clean and predictable API.

packages/jumpstarter/jumpstarter/config/client.py (1)

244-252: LGTM: Clean pass-through implementation.

The update_lease method correctly forwards the client parameter to the underlying gRPC service, maintaining consistency with the existing parameter handling pattern.

packages/jumpstarter-cli/jumpstarter_cli/update.py (5)

25-30: LGTM: Well-documented CLI option.

The --client option is properly defined with clear help text that specifies the expected identifier format. This helps users understand how to use the lease transfer feature correctly.


33-35: LGTM: Function signature updated correctly.

The function signature properly includes the new client parameter with appropriate type annotation.


39-44: LGTM: Comprehensive documentation.

The docstring is well-written and provides clear guidance on:

  • What fields can be updated
  • The requirement for at least one parameter
  • The expected format for client identifiers with a concrete example

48-49: LGTM: Validation logic correctly updated.

The validation ensures at least one of the three parameters is provided, and the error message clearly communicates the requirement to users.


51-51: LGTM: Parameter correctly forwarded.

The client parameter is properly passed through to the underlying config.update_lease call.

Comment on lines +437 to +442
if client is not None:
lease_pb.client = client
update_fields.append("client")

if not update_fields:
raise ValueError("At least one of duration or begin_time must be provided")
raise ValueError("At least one of duration, begin_time, or client must be provided")
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Search for client identifier format validation patterns

# Search for parse_client_identifier usage 
echo "=== Searching for parse_client_identifier usage ===" 
rg -n "parse_client_identifier" --type py -C 3

# Search for validation patterns with client identifiers
echo -e "\n=== Searching for namespaces.*clients pattern ===" 
rg -n "namespaces.*clients" --type py -C 2

# Search for client field validation in grpc file
echo -e "\n=== Searching for client validation in grpc.py ===" 
rg -n "client.*=" packages/jumpstarter/jumpstarter/client/grpc.py -C 1

# Search for CLI help text
echo -e "\n=== Checking CLI help text ===" 
rg -n "namespaces.*client" packages/jumpstarter-cli/jumpstarter_cli/update.py -C 2

Repository: jumpstarter-dev/jumpstarter

Length of output: 3850


🏁 Script executed:

#!/bin/bash
# Find parse_identifier function implementation

echo "=== Searching for parse_identifier function definition ===" 
rg -n "def parse_identifier" packages/jumpstarter/jumpstarter/client/grpc.py -A 10

# Also check what happens when parse_identifier is called with invalid format
echo -e "\n=== Searching for error handling around client parsing ===" 
rg -n "parse_client_identifier\|ValueError\|InvalidArgument" packages/jumpstarter/jumpstarter/client/grpc.py -B 2 -A 2

Repository: jumpstarter-dev/jumpstarter

Length of output: 801


🏁 Script executed:

#!/bin/bash
# Check CLI validation for client parameter

echo "=== Checking CLI update.py for validation ===" 
cat packages/jumpstarter-cli/jumpstarter_cli/update.py | head -100

# Check if there's any validation of the client parameter before calling grpc
echo -e "\n=== Searching for client parameter validation in update.py ===" 
rg -n "parse_client\|ValueError\|client.*format" packages/jumpstarter-cli/jumpstarter_cli/update.py -C 2

Repository: jumpstarter-dev/jumpstarter

Length of output: 1881


🏁 Script executed:

#!/bin/bash
# Find the config.update_lease implementation

echo "=== Searching for config.update_lease implementation ===" 
rg -n "def update_lease" packages/jumpstarter-cli/ -A 5

# Also check the flow from CLI to grpc
echo -e "\n=== Checking where parse_client_identifier is used ===" 
rg -n "parse_client_identifier" packages/jumpstarter/jumpstarter/client/grpc.py -B 3 -A 3

Repository: jumpstarter-dev/jumpstarter

Length of output: 1175


Add client identifier format validation before sending to server.

The client parameter is passed directly to the gRPC request without format validation. While parse_identifier() already exists in this file to validate the format (namespaces/{namespace}/clients/{client}), it's currently only used to parse server responses. Consider either validating the client parameter using parse_identifier(client, "clients") before the gRPC call, or confirm that the server-side validation in the dependent controller PR (jumpstarter-controller#199) comprehensively validates both the format and authorization for lease transfers.

🤖 Prompt for AI Agents
In packages/jumpstarter/jumpstarter/client/grpc.py around lines 437-442, the
client value is assigned to the gRPC request without validating its format; call
parse_identifier(client, "clients") before setting lease_pb.client and appending
"client" to update_fields, and if parse_identifier raises/returns an invalid
result then raise a ValueError with a clear message (e.g., "client must be a
valid identifier: namespaces/{namespace}/clients/{client}") so only validated
client identifiers are sent to the server.

Copy link
Member

@mangelajo mangelajo left a comment

Choose a reason for hiding this comment

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

Just one comment! :)

"--client",
type=str,
default=None,
help="Transfer lease to a different client (format: namespaces/{namespace}/clients/{client})",
Copy link
Member

Choose a reason for hiding this comment

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

We don't support cross namespace on the server anymore, it was removed after a few calls on the community meeting, and to make it straight forward with the operator.

We should probably just take "{client}"

Copy link
Member Author

Choose a reason for hiding this comment

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

right right, i knew that 😅

client: str | None = None,
):
lease_pb = client_pb2.Lease(
name="namespaces/{}/leases/{}".format(self.namespace, name),
Copy link
Member

Choose a reason for hiding this comment

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

we should probably construct it as we do here :)

depends on jumpstarter-dev/jumpstarter-controller#199

Signed-off-by: Benny Zlotnik <bzlotnik@redhat.com>
Signed-off-by: Benny Zlotnik <bzlotnik@redhat.com>
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: 2

♻️ Duplicate comments (1)
packages/jumpstarter/jumpstarter/client/grpc.py (1)

437-439: Client identifier passed without format validation.

The client value is assigned directly to lease_pb.client without validating its format. Given that parse_client_identifier() exists (lines 67-68) to validate the namespaces/{namespace}/clients/{client} format, consider validating before sending to the server to fail fast with a clear error message.

However, per the past review discussion, the server-side validation in the controller PR (jumpstarter-controller#199) may handle this. If server validation is comprehensive, this is acceptable—but a client-side check would improve UX by providing immediate feedback.

📜 Review details

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 09b7515 and 8ba9b40.

📒 Files selected for processing (3)
  • packages/jumpstarter-cli/jumpstarter_cli/update.py
  • packages/jumpstarter/jumpstarter/client/grpc.py
  • packages/jumpstarter/jumpstarter/config/client.py
🧰 Additional context used
📓 Path-based instructions (1)
**/*.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/jumpstarter/config/client.py
  • packages/jumpstarter/jumpstarter/client/grpc.py
  • packages/jumpstarter-cli/jumpstarter_cli/update.py
🧠 Learnings (3)
📚 Learning: 2025-10-14T17:43:07.788Z
Learnt from: michalskrivanek
Repo: jumpstarter-dev/jumpstarter PR: 704
File: packages/jumpstarter/jumpstarter/client/grpc.py:100-107
Timestamp: 2025-10-14T17:43:07.788Z
Learning: In the Jumpstarter client lease model (packages/jumpstarter/jumpstarter/client/grpc.py), `effective_duration` represents the elapsed time for an active lease so far, not the total duration. To calculate expected release time, use `effective_begin_time + duration` (where `duration` is the configured/requested duration), not `effective_begin_time + effective_duration`.

Applied to files:

  • packages/jumpstarter/jumpstarter/config/client.py
  • packages/jumpstarter/jumpstarter/client/grpc.py
  • packages/jumpstarter-cli/jumpstarter_cli/update.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/jumpstarter/config/client.py
  • packages/jumpstarter/jumpstarter/client/grpc.py
📚 Learning: 2025-09-15T08:19:27.412Z
Learnt from: mangelajo
Repo: jumpstarter-dev/jumpstarter PR: 610
File: packages/jumpstarter-driver-flashers/jumpstarter_driver_flashers/client.py:666-691
Timestamp: 2025-09-15T08:19:27.412Z
Learning: Bearer tokens in the jumpstarter flasher client should be validated for format, length, and character safety similar to HTTP header validation using _validate_bearer_token method.

Applied to files:

  • packages/jumpstarter/jumpstarter/client/grpc.py
🧬 Code graph analysis (2)
packages/jumpstarter/jumpstarter/config/client.py (1)
packages/jumpstarter/jumpstarter/client/grpc.py (2)
  • ClientService (316-462)
  • UpdateLease (411-454)
packages/jumpstarter-cli/jumpstarter_cli/update.py (1)
packages/jumpstarter/jumpstarter/config/client.py (2)
  • update_lease (244-252)
  • lease (135-143)
⏰ 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). (11)
  • GitHub Check: Redirect rules - jumpstarter-docs
  • GitHub Check: Header rules - jumpstarter-docs
  • GitHub Check: Pages changed - jumpstarter-docs
  • GitHub Check: build
  • GitHub Check: pytest-matrix (macos-15, 3.13)
  • GitHub Check: pytest-matrix (ubuntu-24.04, 3.11)
  • GitHub Check: pytest-matrix (macos-15, 3.12)
  • GitHub Check: pytest-matrix (macos-15, 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 (3)
packages/jumpstarter/jumpstarter/client/grpc.py (1)

411-442: LGTM on the UpdateLease signature and logic.

The conditional handling of the client parameter follows the same pattern as duration and begin_time, and the updated validation message is consistent.

packages/jumpstarter/jumpstarter/config/client.py (1)

244-252: LGTM!

The client parameter is correctly added to the method signature and passed through to the underlying ClientService.UpdateLease call, maintaining consistency with the existing parameter handling pattern.

packages/jumpstarter-cli/jumpstarter_cli/update.py (1)

55-57: LGTM on client path construction.

The client_path is correctly constructed using the current namespace from config.metadata.namespace, ensuring leases can only be transferred within the same namespace. This aligns with the past review discussion about removing cross-namespace support.

Comment on lines +44 to +47
Update the duration, begin time, and/or owner of an existing lease.
At least one of --duration, --begin-time, or --to_client must be specified.
To transfer a lease to another client in the same namespace, use the --to_client option.
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Docstring uses underscore instead of hyphen for option name.

The docstring references --to_client (underscore) but the CLI option is --to-client (hyphen).

🔎 Proposed fix
-    At least one of --duration, --begin-time, or --to_client must be specified.
+    At least one of --duration, --begin-time, or --to-client must be specified.

-    To transfer a lease to another client in the same namespace, use the --to_client option.
+    To transfer a lease to another client in the same namespace, use the --to-client option.
📝 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
Update the duration, begin time, and/or owner of an existing lease.
At least one of --duration, --begin-time, or --to_client must be specified.
To transfer a lease to another client in the same namespace, use the --to_client option.
Update the duration, begin time, and/or owner of an existing lease.
At least one of --duration, --begin-time, or --to-client must be specified.
To transfer a lease to another client in the same namespace, use the --to-client option.
🤖 Prompt for AI Agents
In packages/jumpstarter-cli/jumpstarter_cli/update.py around lines 44 to 47, the
docstring incorrectly references the CLI option as --to_client (underscore);
update the docstring text to use the correct option name --to-client (hyphen) so
it matches the actual CLI flag, and scan nearby docstring lines in the same
function to ensure no other option names use underscores.

Comment on lines +52 to +53
if duration is None and begin_time is None and to_client is None:
raise click.UsageError("At least one of --duration, --begin-time, or --client must be specified")
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Error message references incorrect option name.

The error message says --client but the actual CLI option is --to-client. This inconsistency would confuse users.

🔎 Proposed fix
     if duration is None and begin_time is None and to_client is None:
-        raise click.UsageError("At least one of --duration, --begin-time, or --client must be specified")
+        raise click.UsageError("At least one of --duration, --begin-time, or --to-client must be specified")
🤖 Prompt for AI Agents
In packages/jumpstarter-cli/jumpstarter_cli/update.py around lines 52-53 the
UsageError message references the wrong CLI option name `--client`; change the
error text to reference the actual option `--to-client` (e.g., "At least one of
--duration, --begin-time, or --to-client must be specified") so the message
matches the implemented flag and avoids user confusion.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

usability user-requested This label means, that actual users are requesting the feature and find it necessary/useful.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants