Skip to content

Conversation

@NickCao
Copy link
Collaborator

@NickCao NickCao commented Mar 11, 2025

See jumpstarter-dev/jumpstarter-protocol#17

Summary by CodeRabbit

  • Tests
    • Added a new end-to-end testing job in the workflow to enhance testing capabilities.
  • New Features
    • Enhanced lease data conversion processes to strengthen interoperability with Protobuf.
    • Introduced new methods for managing leases and exporters in the client service.
    • Added authentication methods for clients and exporters.
  • Refactor
    • Streamlined service architecture by separating client-related operations into a dedicated service.
    • Updated existing functions to utilize a more flexible parsing approach for identifiers.
  • Chores
    • Removed obsolete helper functions related to identifier parsing.

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Mar 11, 2025

Walkthrough

This pull request introduces several enhancements across different parts of the system. A new GitHub Actions job (e2e-tests-28d6b1cc3b49ab9ae176918ab9709a2e2522c97e) is added for end-to-end testing with specific parameters. Protobuf conversion functions are introduced for the Lease and Exporter structures, and the gRPC client-service responsibilities are refactored into a new ClientService struct. Additionally, identifier parsing functionality is generalized with new helper functions. These changes enhance code modularity and interoperability with Protobuf without altering existing legacy flows.

Changes

File(s) Change Summary
.github/workflows/e2e.yaml Added a new job e2e-tests-28d6b1cc3b49ab9ae176918ab9709a2e2522c97e running on ubuntu-latest using jumpstarter-dev/jumpstarter-e2e@11a5ce6 with specific input parameters.
api/v1alpha1/lease_helpers.go Introduced LeaseFromProtobuf and ToProtobuf functions to convert between Protobuf Lease representations and the internal Lease structure, with new imports.
api/v1alpha1/exporter_helpers.go Added ToProtobuf methods for Exporter and ExporterList to convert to Protobuf representations.
internal/service/controller_service.go Removed UnimplementedClientServiceServer from ControllerService; created a new ClientService struct implementing gRPC methods (e.g., GetExporter, ListExporters) with updated registration.
internal/service/auth/auth.go Introduced Auth struct and methods for client and exporter authentication, including AuthClient and AuthExporter.
internal/service/client/v1/client_service.go Added ClientService struct with methods for managing leases and exporters (e.g., GetLease, ListLeases, CreateLease, etc.).
internal/service/utils/identifier.go Introduced functions for parsing and un-parsing identifiers related to Kubernetes namespaces and objects, including ParseNamespaceIdentifier and others.
internal/service/helpers.go File deleted; previously contained functions for parsing and un-parsing identifiers for namespaces and exporters.

Possibly related PRs

  • Fix e2e test #78: The changes in the main PR introduce a new job in the GitHub Actions workflow for end-to-end tests, while the retrieved PR modifies the existing end-to-end testing workflow by removing inputs and changing the action used.

Suggested reviewers

  • mangelajo

Poem

Hoppin' through code in the moonlight so bright,
I found new tests and structures taking flight.
In workflows and gRPC, I dance with delight,
With Protobuf and helpers, everything's just right.
Bugs, beware — this rabbit brings improvements tonight! 🐰✨


📜 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 614246b and 903367e.

📒 Files selected for processing (1)
  • api/v1alpha1/lease_helpers.go (1 hunks)
⏰ Context from checks skipped due to timeout of 90000ms (2)
  • GitHub Check: e2e-tests-28d6b1cc3b49ab9ae176918ab9709a2e2522c97e
  • GitHub Check: e2e-tests
🔇 Additional comments (5)
api/v1alpha1/lease_helpers.go (5)

8-13: Well-organized imports for Protobuf integration

The new imports properly support the Protobuf conversion functionality being added. Using clear namespacing with the import aliases helps maintain code readability.


23-44: Good implementation of Protobuf deserialization

The LeaseFromProtobuf function properly handles the conversion from a Protobuf Lease to the internal representation, with appropriate error handling for the selector parsing. The type conversions are handled correctly.


81-84: LGTM on exporter identifier handling

The exporter identifier is correctly parsed using the utility function, maintaining consistent identifier formatting throughout the system.


90-99: Clean implementation of LeaseList conversion

The ToProtobuf method for LeaseList is simple and effective, properly iterating through each lease and handling pagination with the NextPageToken.


46-88:

❓ Verification inconclusive

Well-structured ToProtobuf implementation

The implementation is thorough and handles all the necessary field conversions between the internal and Protobuf representations.

The TODOs on lines 66 and 69 indicate future work for lease renewal and scheduled leases. Consider creating tickets to track these features if they don't already exist.


🏁 Script executed:

#!/bin/bash
# Check if there are existing tickets/issues for the TODOs

echo "Searching for issues related to lease renewal and scheduled leases..."
gh issue list --repo jumpstarter-dev/jumpstarter-controller --search "lease renewal OR scheduled leases" --limit 5

Length of output: 380


Action: Confirm tracking of pending TODO items

The conversion logic in the ToProtobuf function is well-structured and correctly handles the field conversions. However, I ran a search for existing issues related to "lease renewal" or "scheduled leases" and found only an unrelated ticket concerning lease failures. This suggests that dedicated tickets for the TODO items still need to be created if they are not already present. Please verify your backlog and consider filing new tickets for:

  • Implementing lease renewal (line 66)
  • Implementing scheduled leases (line 69)
✨ Finishing Touches
  • 📝 Generate Docstrings

🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Generate unit testing code for this file.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query. Examples:
    • @coderabbitai generate unit testing code for this file.
    • @coderabbitai modularize this function.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read src/utils.ts and generate unit testing code.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.
    • @coderabbitai help me debug CodeRabbit configuration file.

Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments.

CodeRabbit Commands (Invoked using PR comments)

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai full review to do a full review from scratch and review all the files again.
  • @coderabbitai summary to regenerate the summary of the PR.
  • @coderabbitai generate docstrings to generate docstrings for this PR.
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @coderabbitai configuration to show the current CodeRabbit configuration for the repository.
  • @coderabbitai help to get help.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

CodeRabbit Configuration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Documentation and Community

  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

@NickCao NickCao marked this pull request as ready for review March 12, 2025 13:50
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

🔭 Outside diff range comments (2)
internal/service/controller_service.go (2)

653-676: ⚠️ Potential issue

Enforce namespace checks for security.
Currently, this method allows any authenticated client to retrieve any Exporter simply by passing in an identifier (including the Exporter’s namespace). Consider verifying that the requested Exporter’s namespace actually matches the requesting client’s namespace (or otherwise enforce authorization explicitly).

A minimal fix might involve fetching the client object (e.g., jclient, err := s.controller.authenticateClient(ctx)) and checking:

 func (s *clientService) GetExporter(
    ctx context.Context,
    req *cpb.GetExporterRequest,
 ) (*cpb.Exporter, error) {
-   _, err := s.controller.authenticateClient(ctx)
+   jclient, err := s.controller.authenticateClient(ctx)
    if err != nil {
        return nil, err
    }

    namespace, name, err := ParseExporterIdentifier(req.Name)
    if err != nil {
        return nil, err
    }

+   // Ensure the Exporter is in the same namespace as the client
+   if namespace != jclient.Namespace {
+       return nil, fmt.Errorf("GetExporter permission denied")
+   }

    var exporter jumpstarterdevv1alpha1.Exporter
    ...

678-719: ⚠️ Potential issue

Restrict listing scope to the client's namespace.
As written, this method can list exporters in any namespace. If that’s intentional, ensure robust authorization measures. Otherwise, validate that namespace == jclient.Namespace to prevent cross-namespace listing.

 func (s *clientService) ListExporters(
    ctx context.Context,
    req *cpb.ListExportersRequest,
 ) (*cpb.ListExportersResponse, error) {
-   _, err := s.controller.authenticateClient(ctx)
+   jclient, err := s.controller.authenticateClient(ctx)
    if err != nil {
        return nil, err
    }

    namespace, err := ParseNamespaceIdentifier(req.Parent)
    if err != nil {
        return nil, err
    }

+   if namespace != jclient.Namespace {
+       return nil, fmt.Errorf("ListExporters permission denied")
+   }

    selector, err := labels.Parse(req.Filter)
    ...
🧹 Nitpick comments (6)
.github/workflows/e2e.yaml (1)

10-15: Pin the action version for better stability.

Using @main might introduce unexpected breakage if the referenced action changes upstream. Consider pinning to a specific version or commit SHA.

Example diff:

-      - uses: jumpstarter-dev/jumpstarter-e2e@main
+      - uses: jumpstarter-dev/jumpstarter-e2e@v1.0.0
internal/service/helpers.go (1)

31-31: Consider adding a more descriptive doc comment.

Adding a short comment explaining parameter expectations (e.g., 'kind' must be either exporters, leases, etc.) would aid readability and clarity.

+// ParseIdentifier parses an identifier of the form "namespaces/<ns>/<kind>/<name>".
// Returns namespace and name if valid, or an error otherwise.
 func ParseIdentifier(identifier string, kind string) (string, string, error) {
api/v1alpha1/lease_helpers.go (2)

21-53: Consider validating duration and operator constraints.

If a zero or nil req.Duration is unexpected, consider validating or defaulting. Similarly, guard against unknown or invalid exp.Operator values to prevent downstream issues.

 if req.Duration == nil {
+  // Optionally return an error or enforce a default duration:
+  // return nil // or handle a default
 }
 for _, exp := range req.Selector.MatchExpressions {
+  // Optionally verify that exp.Operator is in an allowed set:
+  // allowedOps := map[string]bool{"In": true, "NotIn": true, ...}
+  // if !allowedOps[exp.Operator] {
+  //   return nil // or handle an error
+  // }
 }

55-106: Follow up on the remaining TODOs.

The TODO comments for lease renewal and scheduled leases might warrant dedicated tracking to ensure they get implemented. Let me know if you need additional help or if I should open a new issue to highlight these tasks.

internal/service/controller_service.go (2)

73-76: Struct design looks good, but be mindful of potential circular references.
Embedding cpb.UnimplementedClientServiceServer is correct for implementing the client service. However, referencing ControllerService from clientService and vice versa can, in some cases, lead to circular dependencies or confusion. Ensure it’s well-documented to avoid maintainability issues.


847-879: Clarify “Delete” vs. “Release” semantics.
This method sets lease.Spec.Release = true instead of deleting the resource. That’s fine if it’s intentionally a “finalize and release” operation, but the name may confuse users who anticipate a hard deletion. Consider renaming or adding doc comments to clarify behavior.

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 155220e and ab7bd1d.

⛔ Files ignored due to path filters (4)
  • internal/protocol/jumpstarter/client/v1/client.pb.go is excluded by !**/*.pb.go
  • internal/protocol/jumpstarter/client/v1/client_grpc.pb.go is excluded by !**/*.pb.go
  • internal/protocol/jumpstarter/v1/client.pb.go is excluded by !**/*.pb.go
  • internal/protocol/jumpstarter/v1/client_grpc.pb.go is excluded by !**/*.pb.go
📒 Files selected for processing (4)
  • .github/workflows/e2e.yaml (1 hunks)
  • api/v1alpha1/lease_helpers.go (1 hunks)
  • internal/service/controller_service.go (8 hunks)
  • internal/service/helpers.go (1 hunks)
🔇 Additional comments (10)
internal/service/helpers.go (3)

24-25: Clean consolidation of parsing logic.

Delegating the parsing to ParseIdentifier makes the code more DRY and maintainable.


27-28: Consistent use of ParseIdentifier for leases.

This approach ensures uniform validation across different identifier types.


39-40: Logic and error message align well.

This validation step ensures the correct kind segment. No issues identified.

api/v1alpha1/lease_helpers.go (2)

8-11: New protobuf imports look correct.

These imports properly support the introduced Protobuf conversion logic.


16-17: Pointer utilities correctly imported.

Using k8s.io/utils/ptr for pointer convenience is a neat approach.

internal/service/controller_service.go (5)

33-33: Good introduction of the cpb import.
This import provides the Protobuf definitions for the new client-oriented gRPC service.


42-42: Use of emptypb is appropriate.
Importing emptypb from google.golang.org/protobuf/types/known/emptypb is standard practice for methods returning empty payloads in gRPC.


777-807: Creation flow looks correct.
The method enforces namespace ownership and sets the client reference properly. The use of uuid.NewV7() for lease naming is a secure and collision-resistant approach.


809-845: Updating the lease is handled well.
The permission checks and patching logic ensure the client can only update a lease it owns. This is consistent with the rest of the design.


916-916: Registering the new client service is correct.
Leveraging a separate service struct for client methods helps keep the system modular and avoids polluting ControllerService with client-specific gRPC endpoints.

@NickCao NickCao marked this pull request as draft March 13, 2025 14:48
@NickCao NickCao marked this pull request as ready for review March 13, 2025 16:51
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

🧹 Nitpick comments (6)
internal/service/utils/identifier.go (1)

36-71: Valid object identifier parsing
Checking for the correct number of segments, validating "namespaces" as the first segment, and matching the resource kind are all handled cleanly. You might consider refactoring the literal "namespaces" into a constant to avoid typos and centralize the logic, for instance:

+const resourceNamespaceSegment = "namespaces"

 func ParseObjectIdentifier(identifier string, kind string) (key *kclient.ObjectKey, err error) {
   segments := strings.Split(identifier, "/")

-  if segments[0] != "namespaces" {
+  if segments[0] != resourceNamespaceSegment {
     return nil, status.Errorf(
       codes.InvalidArgument,
       ...
internal/service/auth/auth.go (1)

37-55: Client authentication
Using the OIDC VerifyClientObjectToken followed by a namespace check is logically sound. Consider whether returning codes.Unauthenticated (instead of PermissionDenied) might be appropriate if the token is invalid.

api/v1alpha1/lease_helpers.go (2)

23-44: Consider validating the lease duration.

Currently, LeaseFromProtobuf simply converts and stores req.Duration. If you anticipate invalid (e.g. negative or zero) durations, you could validate them here to avoid unexpected runtime behaviors.

 if err != nil {
   return nil, err
 }
 
+// Optionally ensure the duration is positive
+if req.Duration.AsDuration() <= 0 {
+  return nil, fmt.Errorf("lease duration must be positive")
+}

 return &Lease{

46-88: Acknowledge the TODO for extended lease management.

The EffectiveDuration currently defaults to the same value as Duration. Make sure to address your // TODO: implement lease renewal to adjust EffectiveDuration if lease-renewal functionality is required.

Do you want help drafting the lease renewal logic?

internal/service/controller_service.go (1)

683-686: Registration of the new ClientService.

Replacing the direct client-service logic with clientsvcv1.NewClientService makes the architecture more modular. Just confirm that the auth.NewAuth(...) call handles concurrency or ephemeral contexts properly—generally it appears fine.

 cpb.RegisterClientServiceServer(
   server,
-  clientsvcv1.NewClientService(s.Client, *auth.NewAuth(s.Client, s.Authn, s.Authz, s.Attr)),
+  clientsvcv1.NewClientService(
+    s.Client,
+    *auth.NewAuth(s.Client, s.Authn, s.Authz, s.Attr),
+  ),
 )
internal/service/client/v1/client_service.go (1)

192-229: UpdateLease flow.

Correctly enforces an ownership check (permission denied if not the same client). Only updates the lease’s Spec.Duration from the new request. Consider whether additional fields should also be updatable.

// If more lease fields need updating beyond duration, handle them here:
jlease.Spec.Duration = desired.Spec.Duration
+// e.g. jlease.Spec.Selector = desired.Spec.Selector?
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between f35e455 and fd74fc9.

⛔ Files ignored due to path filters (4)
  • internal/protocol/jumpstarter/client/v1/client.pb.go is excluded by !**/*.pb.go
  • internal/protocol/jumpstarter/client/v1/client_grpc.pb.go is excluded by !**/*.pb.go
  • internal/protocol/jumpstarter/v1/client.pb.go is excluded by !**/*.pb.go
  • internal/protocol/jumpstarter/v1/client_grpc.pb.go is excluded by !**/*.pb.go
📒 Files selected for processing (8)
  • .github/workflows/e2e.yaml (1 hunks)
  • api/v1alpha1/exporter_helpers.go (2 hunks)
  • api/v1alpha1/lease_helpers.go (1 hunks)
  • internal/service/auth/auth.go (1 hunks)
  • internal/service/client/v1/client_service.go (1 hunks)
  • internal/service/controller_service.go (2 hunks)
  • internal/service/helpers.go (0 hunks)
  • internal/service/utils/identifier.go (1 hunks)
💤 Files with no reviewable changes (1)
  • internal/service/helpers.go
🚧 Files skipped from review as they are similar to previous changes (1)
  • .github/workflows/e2e.yaml
🧰 Additional context used
🧠 Learnings (1)
internal/service/controller_service.go (1)
Learnt from: NickCao
PR: jumpstarter-dev/jumpstarter-controller#115
File: internal/service/controller_service.go:740-775
Timestamp: 2025-03-13T14:39:31.845Z
Learning: Clients are allowed to see each other's leases in the Jumpstarter controller. The ListLeases function intentionally does not filter by client ownership.
🔇 Additional comments (21)
internal/service/utils/identifier.go (4)

12-34: Well-structured error handling for namespace identifier
The function correctly enforces a two-segment rule with "namespaces" as the first. This yields clear, precise gRPC errors when inputs are invalid.


73-75: Consistent string formatting
Building the identifier string mirrors the parsing logic, ensuring consistency between parse/unparse.


77-83: Exporter identifier convenience methods
Delegating to the generic Parse/UnparseObjectIdentifier with "exporters" fosters clean, DRY code.


85-91: Lease identifier convenience methods
Similarly, these methods neatly wrap the parse/unparse logic for "leases", maintaining consistency with naming conventions.

api/v1alpha1/exporter_helpers.go (2)

25-30: Protobuf exporter conversion
Using utils.UnparseExporterIdentifier ensures a consistent Name format in the Protobuf output, and labels are passed through directly.


32-41: ExporterList to Protobuf
This method elegantly maps each item to Protobuf via ToProtobuf(), and includes NextPageToken to support paging.

internal/service/auth/auth.go (3)

16-21: Auth struct fields
The combination of client, authenticator, authorizer, and context attributes covers the dependencies needed for token checks and permission evaluation.


23-35: Auth constructor
Clear dependency injection ensures future testability and maintainability, bundling all required components in a unified struct.


57-75: Exporter authentication
This mirrors AuthClient, providing consistent OIDC verification and namespace validation for exporters.

api/v1alpha1/lease_helpers.go (2)

8-20: Imports look consistent with new Protobuf helpers.

All newly introduced imports cleanly integrate with the existing module references. No concerns about unused or conflicting imports.


90-99: Conversion from LeaseList to a gRPC response is solid.

This maintains pagination support via NextPageToken. Looks clean and straightforward.

internal/service/controller_service.go (1)

33-36: Imports for expanded client service integration.

The newly added imports for cpb, auth, and clientsvcv1 are in line with the restructuring to delegate lease management responsibilities to the new client service.

internal/service/client/v1/client_service.go (9)

1-15: License and package declaration.

All licensing headers and package statements are in place. No issues here.


19-34: Imports and global references.

The import set is well-organized. The usage of controller.MatchingActiveLeases and utils for identifier parsing is aligned with code conventions found elsewhere in this repository.


36-47: NewClientService constructor is straightforward.

Dependency injection for both Client and Auth is clear. Ensure AuthClient checks are robust for multi-tenant scenarios.


49-69: GetExporter method.

Retrieves exporter by namespace and name with no ownership checks. This is consistent with the accepted policy that “Clients can see each other’s resources.” The usage of AuthClient is correct for verifying at least the namespace access.


71-101: ListExporters method.

Applying parent-namespace verification and label filtering aligns with existing patterns. Page size and token support is well-handled.


103-120: GetLease: read-only lease retrieval for any client.

Consistent with the policy that clients can view each other’s leases. If future requirements change, consider adding explicit ownership checks.


122-157: ListLeases method.

Retrieves all leases matching the label filter in the given namespace, with no explicit client ownership filter. This matches the “clients may see each other’s leases” guideline.


159-190: CreateLease flow.

Generates a unique name via uuid.NewV7() and relies on LeaseFromProtobuf. It sets Spec.ClientRef to the authenticated client. Logic looks correct.


231-260: DeleteLease with a “release” patch.

Enforces ownership checks, then sets Spec.Release = true. This approach is consistent across the codebase.

@mangelajo mangelajo merged commit aa44e76 into main Mar 14, 2025
7 checks passed
@NickCao NickCao deleted the client-svc-lease branch March 17, 2025 14:20
@mangelajo mangelajo added this to the 0.6.0 milestone May 8, 2025
@NickCao NickCao mentioned this pull request May 13, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants