Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(v2 upgrade): support engine live upgrade #716

Open
wants to merge 2 commits into
base: master
Choose a base branch
from

Conversation

derekbit
Copy link
Member

Signed-off-by: Derek Su [email protected]#### Which issue(s) this PR fixes:

Issue longhorn/longhorn#9104

What this PR does / why we need it:

Special notes for your reviewer:

Additional documentation or context

@derekbit derekbit self-assigned this Nov 19, 2024
Copy link

coderabbitai bot commented Nov 19, 2024

Walkthrough

The changes introduce two new fields, StandbyTargetPortStart and StandbyTargetPortEnd, to the InstanceStatus struct in pkg/api/instance.go. These fields are populated in the RPCToInstanceStatus function. Additionally, the pkg/instance/instance.go file has been modified to enhance instance management, including updates to the engineResponseToInstanceResponse function and changes to the InstanceCreate, InstanceSuspend, InstanceResume, InstanceSwitchOverTarget, and InstanceDeleteTarget methods, with a focus on error handling and response structure.

Changes

File Change Summary
pkg/api/instance.go Added StandbyTargetPortStart and StandbyTargetPortEnd fields to InstanceStatus struct; updated RPCToInstanceStatus function to populate these fields.
pkg/instance/instance.go Repositioned emptypb import; updated engineResponseToInstanceResponse to include new fields; modified logic in InstanceCreate, InstanceSuspend, InstanceResume, InstanceSwitchOverTarget, and InstanceDeleteTarget methods to enhance error handling.

Assessment against linked issues

Objective Addressed Explanation
Support live upgrade for control plane (#9104) The changes do not explicitly address live upgrade functionality.

Suggested reviewers

  • DamiaSan
  • innobead

Warning

There were issues while running some tools. Please review the errors and either fix the tool’s configuration or disable the tool if it’s a critical failure.

🔧 golangci-lint (1.62.2)

level=warning msg="[runner] Can't run linter goanalysis_metalinter: buildir: failed to load package qcow: could not load export data: no export data for "github.com/longhorn/longhorn-engine/pkg/qcow""
level=error msg="Running error: can't run linter goanalysis_metalinter\nbuildir: failed to load package qcow: could not load export data: no export data for "github.com/longhorn/longhorn-engine/pkg/qcow""


📜 Recent review details

Configuration used: .coderabbit.yaml
Review profile: CHILL

📥 Commits

Reviewing files that changed from the base of the PR and between 5977643 and 36f5024.

⛔ Files ignored due to path filters (19)
  • go.mod is excluded by !go.mod
  • go.sum is excluded by !**/*.sum, !go.sum
  • vendor/github.com/longhorn/go-spdk-helper/pkg/types/types.go is excluded by !vendor/**
  • vendor/github.com/longhorn/longhorn-spdk-engine/pkg/api/types.go is excluded by !vendor/**
  • vendor/github.com/longhorn/longhorn-spdk-engine/pkg/client/client.go is excluded by !vendor/**
  • vendor/github.com/longhorn/longhorn-spdk-engine/pkg/client/types.go is excluded by !vendor/**
  • vendor/github.com/longhorn/longhorn-spdk-engine/pkg/spdk/backup.go is excluded by !vendor/**
  • vendor/github.com/longhorn/longhorn-spdk-engine/pkg/spdk/disk.go is excluded by !vendor/**
  • vendor/github.com/longhorn/longhorn-spdk-engine/pkg/spdk/disk/nvme/nvme.go is excluded by !vendor/**
  • vendor/github.com/longhorn/longhorn-spdk-engine/pkg/spdk/engine.go is excluded by !vendor/**
  • vendor/github.com/longhorn/longhorn-spdk-engine/pkg/spdk/replica.go is excluded by !vendor/**
  • vendor/github.com/longhorn/longhorn-spdk-engine/pkg/spdk/restore.go is excluded by !vendor/**
  • vendor/github.com/longhorn/longhorn-spdk-engine/pkg/spdk/server.go is excluded by !vendor/**
  • vendor/github.com/longhorn/longhorn-spdk-engine/pkg/spdk/types.go is excluded by !vendor/**
  • vendor/github.com/longhorn/longhorn-spdk-engine/pkg/spdk/util.go is excluded by !vendor/**
  • vendor/github.com/longhorn/longhorn-spdk-engine/pkg/util/block.go is excluded by !vendor/**
  • vendor/github.com/longhorn/types/pkg/generated/imrpc/instance.pb.go is excluded by !**/*.pb.go, !**/generated/**, !vendor/**
  • vendor/github.com/longhorn/types/pkg/generated/spdkrpc/spdk.pb.go is excluded by !**/*.pb.go, !**/generated/**, !vendor/**
  • vendor/modules.txt is excluded by !vendor/**
📒 Files selected for processing (2)
  • pkg/api/instance.go (1 hunks)
  • pkg/instance/instance.go (3 hunks)
🚧 Files skipped from review as they are similar to previous changes (2)
  • pkg/api/instance.go
  • pkg/instance/instance.go

Thank you for using CodeRabbit. We offer it for free to the OSS community and would appreciate your support in helping us grow. If you find it useful, would you consider giving us a shout-out on your favorite social media?

❤️ Share
🪧 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 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.

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.

Copy link

@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

🧹 Outside diff range and nitpick comments (4)
pkg/api/instance.go (2)

80-88: Consider adding port range validation.

While the mapping is correct, consider adding validation to ensure StandbyTargetPortStart is less than StandbyTargetPortEnd to prevent potential runtime issues.

Example validation:

 func RPCToInstanceStatus(obj *rpc.InstanceStatus) InstanceStatus {
+	if obj.StandbyTargetPortStart > obj.StandbyTargetPortEnd {
+		// Either swap the values or log a warning
+		obj.StandbyTargetPortStart, obj.StandbyTargetPortEnd = obj.StandbyTargetPortEnd, obj.StandbyTargetPortStart
+	}
 	return InstanceStatus{
 		State:                  obj.State,
 		ErrorMsg:               obj.ErrorMsg,

67-88: Consider documenting the live upgrade port management strategy.

The addition of standby target ports is a crucial part of the live upgrade infrastructure. Consider adding documentation that explains:

  • The lifecycle of standby ports during upgrades
  • Port allocation strategy and potential range conflicts
  • Coordination between active and standby ports

Add a comment block above the InstanceStatus struct explaining the role of standby ports in live upgrades:

+// InstanceStatus represents the current state of an instance including its port configurations.
+// During live upgrades, the instance can maintain both active ports (TargetPort*) and
+// standby ports (StandbyTargetPort*) to facilitate seamless transitions.
 type InstanceStatus struct {
pkg/instance/instance.go (2)

723-731: Consistent port assignments for standby target ports.

Setting both StandbyTargetPortStart and StandbyTargetPortEnd to e.StandbyTargetPort is acceptable if StandbyTargetPort represents a single port value. If a range of ports is intended, consider adjusting these fields to reflect the correct port range.


723-731: Initialize the 'Conditions' map appropriately.

The Conditions field is initialized with an empty map using make(map[string]bool). Ensure that this map is populated with relevant condition statuses where necessary, or consider removing it if it remains unused to improve clarity.

📜 Review details

Configuration used: .coderabbit.yaml
Review profile: CHILL

📥 Commits

Reviewing files that changed from the base of the PR and between ee26f75 and fc70fdf.

⛔ Files ignored due to path filters (9)
  • go.mod is excluded by !go.mod
  • go.sum is excluded by !**/*.sum, !go.sum
  • vendor/github.com/longhorn/longhorn-spdk-engine/pkg/api/types.go is excluded by !vendor/**
  • vendor/github.com/longhorn/longhorn-spdk-engine/pkg/client/client.go is excluded by !vendor/**
  • vendor/github.com/longhorn/longhorn-spdk-engine/pkg/spdk/engine.go is excluded by !vendor/**
  • vendor/github.com/longhorn/longhorn-spdk-engine/pkg/spdk/server.go is excluded by !vendor/**
  • vendor/github.com/longhorn/types/pkg/generated/imrpc/instance.pb.go is excluded by !**/*.pb.go, !**/generated/**, !vendor/**
  • vendor/github.com/longhorn/types/pkg/generated/spdkrpc/spdk.pb.go is excluded by !**/*.pb.go, !**/generated/**, !vendor/**
  • vendor/modules.txt is excluded by !vendor/**
📒 Files selected for processing (2)
  • pkg/api/instance.go (1 hunks)
  • pkg/instance/instance.go (3 hunks)
🔇 Additional comments (3)
pkg/api/instance.go (1)

67-75: LGTM! The new standby target port fields are well-structured.

The addition of StandbyTargetPortStart and StandbyTargetPortEnd fields follows consistent naming and typing patterns with existing port fields, supporting the live upgrade functionality.

Let's verify the port range usage across the codebase:

✅ Verification successful

Let me search for port validation logic to ensure proper range checks:


Let me search for port assignment and validation in the instance creation flow:


Based on the search results, I can now provide a final response:

Port range validation is properly handled in the codebase

The standby target port fields are well integrated with existing port management:

  • Port range validation exists in the SPDK replica code ensuring PortStart < PortEnd
  • Port allocation is handled through a dedicated allocator that manages ranges
  • Port cleanup and release is properly implemented when instances are deleted
  • The new standby target ports follow the same pattern as existing port fields
🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Check for proper port range validation and usage
# Expected: Find validation logic ensuring StandbyTargetPortStart < StandbyTargetPortEnd

# Search for port range validation logic
rg -t go "StandbyTargetPort(Start|End)" -A 5 -B 5

Length of output: 5868


Script:

#!/bin/bash
# Search for validation logic in instance-related files
rg -t go "func.*Instance.*Create" -A 20 -B 5

# Also check for any port validation functions
rg -t go "func.*(ValidatePort|CheckPort|PortRange)" -A 10

Length of output: 26667


Script:

#!/bin/bash
# Search for port-related logic in instance creation
rg -t go "Port.*Start.*End" -B 5 -A 5

# Look for port validation in the instance package
rg -t go "func.*Instance.*Port" -A 10

Length of output: 11909

pkg/instance/instance.go (2)

12-13: Addition of 'emptypb' import is appropriate.

The import of emptypb is required for methods returning *emptypb.Empty, such as InstanceSuspend and InstanceResume. This inclusion is necessary for handling empty protobuf messages.


155-155: Parameters passed to EngineCreate are correctly updated.

The inclusion of additional parameters in the EngineCreate function call aligns with the enhanced functionality for engine live upgrades. Ensure that req.Spec.PortCount, req.Spec.InitiatorAddress, req.Spec.TargetAddress, and req.Spec.SpdkInstanceSpec.SalvageRequested are correctly handled within the EngineCreate method.

Copy link

@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

🧹 Outside diff range and nitpick comments (8)
pkg/instance/instance.go (8)

723-731: Consistency in Port Assignments in engineResponseToInstanceResponse

In the engineResponseToInstanceResponse function, both PortStart and PortEnd are assigned the same value e.Port. Similarly, TargetPortStart and TargetPortEnd are assigned e.TargetPort, and StandbyTargetPortStart and StandbyTargetPortEnd are assigned e.StandbyTargetPort. If these fields are intended to represent a single port, this is acceptable. However, if port ranges are expected in the future, consider updating the implementation to support port ranges.


731-731: Initialization of Conditions Field

The Conditions field is initialized as an empty map with make(map[string]bool). If this field is expected to hold condition statuses, consider populating it with meaningful data or remove it if it's not needed.


Line range hint 846-851: Correct Error Code for Unsupported Operation in InstanceSuspend

In the InstanceSuspend method for V2DataEngineInstanceOps, when suspending a replica instance (which is not supported), the error code grpccodes.InvalidArgument is used. Since the operation is not implemented for replicas, it's more appropriate to use grpccodes.Unimplemented to indicate that the method is not supported for this instance type.

Apply this diff to correct the error code:

-     return nil, grpcstatus.Errorf(grpccodes.InvalidArgument, "suspend is not supported for instance type %v", req.Type)
+     return nil, grpcstatus.Errorf(grpccodes.Unimplemented, "suspend is not supported for instance type %v", req.Type)

Line range hint 871-876: Correct Error Code for Unsupported Operation in InstanceResume

Similarly, in the InstanceResume method for V2DataEngineInstanceOps, return grpccodes.Unimplemented when the resume operation is not supported for replicas, instead of grpccodes.InvalidArgument.

Apply this diff:

-     return nil, grpcstatus.Errorf(grpccodes.InvalidArgument, "resume is not supported for instance type %v", req.Type)
+     return nil, grpcstatus.Errorf(grpccodes.Unimplemented, "resume is not supported for instance type %v", req.Type)

Line range hint 896-901: Correct Error Code for Unsupported Operation in InstanceSwitchOverTarget

In the InstanceSwitchOverTarget method, use grpccodes.Unimplemented to indicate that target switch over is not supported for replicas.

Apply this diff:

-     return nil, grpcstatus.Errorf(grpccodes.InvalidArgument, "target switch over is not supported for instance type %v", req.Type)
+     return nil, grpcstatus.Errorf(grpccodes.Unimplemented, "target switch over is not supported for instance type %v", req.Type)

Line range hint 921-926: Correct Error Code for Unsupported Operation in InstanceDeleteTarget

In the InstanceDeleteTarget method, adjust the error code to grpccodes.Unimplemented when the operation is not supported for replicas.

Apply this diff:

-     return nil, grpcstatus.Errorf(grpccodes.InvalidArgument, "target deletion is not supported for instance type %v", req.Type)
+     return nil, grpcstatus.Errorf(grpccodes.Unimplemented, "target deletion is not supported for instance type %v", req.Type)

Line range hint 636-656: Consider Implementing InstanceReplace for V2 Data Engine

The InstanceReplace method for V2DataEngineInstanceOps returns an unimplemented error. If replacing instances is a required functionality for the V2 data engine, consider implementing this method to provide consistency across data engine versions.


Line range hint 676-684: Consider Implementing InstanceLog for V2 Data Engine

The InstanceLog method for V2DataEngineInstanceOps is currently unimplemented. Providing logging functionality is crucial for debugging and monitoring. Consider implementing this method to allow users to retrieve logs from instances managed by the V2 data engine.

📜 Review details

Configuration used: .coderabbit.yaml
Review profile: CHILL

📥 Commits

Reviewing files that changed from the base of the PR and between fc70fdf and 413cf47.

⛔ Files ignored due to path filters (9)
  • go.mod is excluded by !go.mod
  • go.sum is excluded by !**/*.sum, !go.sum
  • vendor/github.com/longhorn/longhorn-spdk-engine/pkg/api/types.go is excluded by !vendor/**
  • vendor/github.com/longhorn/longhorn-spdk-engine/pkg/client/client.go is excluded by !vendor/**
  • vendor/github.com/longhorn/longhorn-spdk-engine/pkg/spdk/engine.go is excluded by !vendor/**
  • vendor/github.com/longhorn/longhorn-spdk-engine/pkg/spdk/server.go is excluded by !vendor/**
  • vendor/github.com/longhorn/types/pkg/generated/imrpc/instance.pb.go is excluded by !**/*.pb.go, !**/generated/**, !vendor/**
  • vendor/github.com/longhorn/types/pkg/generated/spdkrpc/spdk.pb.go is excluded by !**/*.pb.go, !**/generated/**, !vendor/**
  • vendor/modules.txt is excluded by !vendor/**
📒 Files selected for processing (2)
  • pkg/api/instance.go (1 hunks)
  • pkg/instance/instance.go (3 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • pkg/api/instance.go
🔇 Additional comments (2)
pkg/instance/instance.go (2)

729-730: Verify Proper Handling of Standby Target Ports

Ensure that the new fields StandbyTargetPortStart and StandbyTargetPortEnd correctly represent the standby target ports. Verify that these fields are properly utilized throughout the system and that any consumers of this API are updated to handle these new fields appropriately.

Run the following script to find usages of the new standby target port fields:

✅ Verification successful

Let me gather more context about the usage and implementation of these fields.


Based on the search results, I can now provide a conclusive response about the standby target port fields.

Standby Target Port Fields Are Correctly Implemented

The implementation is correct and consistent with the codebase's behavior:

  • The original StandbyTargetPort field from the engine is properly mapped to both StandbyTargetPortStart and StandbyTargetPortEnd
  • This mapping maintains backward compatibility while adapting to the new port range structure
  • The implementation is consistent across the API layer (pkg/api/instance.go) and instance management (pkg/instance/instance.go)
  • The engine's implementation shows that standby target ports are properly managed, allocated, and released as single ports, which aligns with setting both start and end to the same value
🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Search for references to 'StandbyTargetPortStart' and 'StandbyTargetPortEnd'.

# Test: Find all occurrences of the new standby target port fields in the codebase.
rg 'StandbyTargetPort(Start|End)'

Length of output: 1541


Script:

#!/bin/bash
# Search for StandbyTargetPort to understand the original field and its usage
rg "StandbyTargetPort[^S][^t]" -A 2

# Also check the protobuf definition
fd "instance.proto" --exec cat {}

Length of output: 6492


155-155: Removal of UpgradeRequired Parameter in EngineCreate

In the InstanceCreate method for V2DataEngineInstanceOps, the UpgradeRequired parameter has been removed from the EngineCreate call. Ensure that this change is intentional and that the EngineCreate function no longer requires this parameter. Verify that this does not affect the upgrade functionality or introduce any unintended behavior.

Run the following script to check if the UpgradeRequired parameter is used elsewhere:

DamiaSan
DamiaSan previously approved these changes Nov 22, 2024
Copy link

mergify bot commented Nov 23, 2024

This pull request is now in conflict. Could you fix it @derekbit? 🙏

Copy link

@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 and nitpick comments (7)
pkg/api/instance.go (3)

66-72: Add field documentation for NvmeDevicePath struct

Consider adding documentation comments for each field to clarify their purpose and expected values, especially for fields like Trtype (valid values: rdma, tcp) and State (possible states).

 type NvmeDevicePath struct {
+	// Trtype specifies the NVMe-oF transport type (e.g., "rdma", "tcp")
 	Trtype  string `json:"trtype"`
+	// Traddr specifies the transport address of the NVMe target
 	Traddr  string `json:"traddr"`
+	// Trsvcid specifies the transport service ID or port number
 	Trsvcid string `json:"trsvcid"`
+	// SrcAddr specifies the source/initiator address for the connection
 	SrcAddr string `json:"src_addr"`
+	// State represents the current state of the NVMe device path
 	State   string `json:"state"`
 }

92-92: Fix typo in variable name

The variable name 'nvmeSubstem' should be 'nvmeSubsystem'.

-	nvmeSubstem := NvmeSubsystem{
+	nvmeSubsystem := NvmeSubsystem{

66-115: Architecture supports live upgrade requirements

The implementation provides a solid foundation for live upgrades through:

  1. NVMe path management for handling device connections
  2. Separate port ranges for active and standby instances
  3. State tracking for individual paths

Consider documenting the upgrade workflow and potential failure scenarios in the package documentation.

pkg/instance/instance.go (4)

726-726: Adjust log level for debugging information

The log statement logrus.Infof("Debug =====> pathName: %v, path: %v", pathName, path) is more suitable at the debug level rather than info level. This prevents verbose output in production environments.

Apply this diff to adjust the log level:

-logrus.Infof("Debug =====> pathName: %v, path: %v", pathName, path)
+logrus.Debugf("pathName: %v, path: %v", pathName, path)

Line range hint 1029-1032: Consistent error handling for unsupported operations

In InstanceReplace for V2DataEngineInstanceOps, the error returned is grpcstatus.Error(grpccodes.Unimplemented, "v2 data engine instance replace is not supported"). Ensure that all methods returning unimplemented errors use consistent messages and status codes for clarity.

Consider updating the error messages for consistency:

- return grpcstatus.Error(grpccodes.Unimplemented, "v2 data engine instance replace is not supported")
+ return grpcstatus.Errorf(grpccodes.Unimplemented, "%s is not supported for v2 data engine", "InstanceReplace")

Apply similar changes to other methods that return unimplemented errors.


Line range hint 1128-1129: Improve error messages for unimplemented methods

In InstanceLog for V2DataEngineInstanceOps, the error message is "v2 data engine instance log is not supported". For better clarity and consistency, consider specifying the method name in the error message.

Update the error message as follows:

- return grpcstatus.Error(grpccodes.Unimplemented, "v2 data engine instance log is not supported")
+ return grpcstatus.Errorf(grpccodes.Unimplemented, "%s is not supported for v2 data engine", "InstanceLog")

Line range hint 1620-1636: Consider implementing InstanceReplace for V2 data engine

Currently, InstanceReplace for V2DataEngineInstanceOps returns an unimplemented error. If InstanceReplace functionality is required for V2 data engines, consider implementing this method to support seamless instance replacement.

📜 Review details

Configuration used: .coderabbit.yaml
Review profile: CHILL

📥 Commits

Reviewing files that changed from the base of the PR and between 293e308 and 9db0036.

⛔ Files ignored due to path filters (7)
  • go.mod is excluded by !go.mod
  • go.sum is excluded by !**/*.sum, !go.sum
  • vendor/github.com/longhorn/longhorn-spdk-engine/pkg/api/types.go is excluded by !vendor/**
  • vendor/github.com/longhorn/longhorn-spdk-engine/pkg/spdk/engine.go is excluded by !vendor/**
  • vendor/github.com/longhorn/types/pkg/generated/imrpc/instance.pb.go is excluded by !**/*.pb.go, !**/generated/**, !vendor/**
  • vendor/github.com/longhorn/types/pkg/generated/spdkrpc/spdk.pb.go is excluded by !**/*.pb.go, !**/generated/**, !vendor/**
  • vendor/modules.txt is excluded by !vendor/**
📒 Files selected for processing (2)
  • pkg/api/instance.go (1 hunks)
  • pkg/instance/instance.go (4 hunks)
🔇 Additional comments (5)
pkg/api/instance.go (1)

86-88: Verify port range validation

The addition of standby target ports suggests support for live migration. Consider adding validation to ensure:

  1. Standby port ranges don't overlap with active target ports
  2. Port ranges are valid (start ≤ end)
pkg/instance/instance.go (4)

738-745: Verify the assignment of Port and TargetPort fields

The PortStart and PortEnd fields are both assigned e.Port, and similarly, TargetPortStart and TargetPortEnd are assigned e.TargetPort. Please verify that the engine uses single ports and that this assignment is intentional. If the engine can operate over a range of ports, consider adjusting the assignments accordingly.


744-745: Ensure correct assignment of StandbyTargetPort fields

The StandbyTargetPortStart and StandbyTargetPortEnd are both assigned e.StandbyTargetPort. Please confirm that this is intentional and that e.StandbyTargetPort represents a single port. If a range is intended, ensure that both start and end ports are correctly assigned.


Line range hint 1356-1357: Handle context cancellation appropriately in watchers

In the watchProcess, watchSPDKEngine, and watchSPDKReplica functions, ensure that the context cancellation is properly handled to prevent resource leaks. Specifically, verify that any goroutines or streams are terminated when the context is done.


155-155: Update function call to match new parameter requirements

The call to EngineCreate includes req.Spec.SpdkInstanceSpec.SalvageRequested as a new parameter. Confirm that this change aligns with the updated function signature of EngineCreate and that all necessary parameters are correctly passed.

Run the following script to verify the function signature:

✅ Verification successful

Based on the search results, I can now generate the final response:

The function call parameters match the implementation

The EngineCreate function call in pkg/instance/instance.go correctly matches the implementation in vendor/github.com/longhorn/longhorn-spdk-engine/pkg/client/client.go. The function signature is:

func (c *SPDKClient) EngineCreate(name, volumeName, frontend string, specSize uint64, replicaAddressMap map[string]string, portCount int32, initiatorAddress, targetAddress string, salvageRequested bool) (*api.Engine, error)

And the call is made with the corresponding parameters in the correct order:

engine, err := c.EngineCreate(
    req.Spec.Name,                              // name
    req.Spec.VolumeName,                        // volumeName
    req.Spec.SpdkInstanceSpec.Frontend,         // frontend
    req.Spec.SpdkInstanceSpec.Size,             // specSize
    req.Spec.SpdkInstanceSpec.ReplicaAddressMap,// replicaAddressMap
    req.Spec.PortCount,                         // portCount
    req.Spec.InitiatorAddress,                  // initiatorAddress
    req.Spec.TargetAddress,                     // targetAddress
    req.Spec.SpdkInstanceSpec.SalvageRequested  // salvageRequested
)
🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Check the definition of EngineCreate to ensure it matches the parameters used.

# Search for the EngineCreate function definition
rg -A 10 'func\s+EngineCreate' | grep -A 10 'func EngineCreate'

Length of output: 65


Script:

#!/bin/bash
# Let's try a broader search for EngineCreate without restricting to function definition
rg -A 5 "EngineCreate"

# Also search for any interface or type that might define EngineCreate
ast-grep --pattern 'interface { $$$ EngineCreate $$$ }'

# Additionally search for any struct method implementations
ast-grep --pattern 'func ($_ $_) EngineCreate'

Length of output: 24836

Comment on lines 717 to 725
for pathName, path := range e.NvmeSubsystem.Paths {
nvmeSubsystem.Paths[pathName] = &rpc.NvmeDevicePath{
Trtype: path.Trtype,
Traddr: path.Traddr,
Trsvcid: path.Trsvcid,
SrcAddr: path.SrcAddr,
State: path.State,
}

logrus.Infof("Debug =====> pathName: %v, path: %v", pathName, path)
}
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue

Add nil check to prevent potential nil pointer dereference

In the engineResponseToInstanceResponse function, there is a potential for a nil pointer dereference when accessing e.NvmeSubsystem.Paths. If e.NvmeSubsystem is nil, this will cause a runtime panic. It's advisable to check if e.NvmeSubsystem is not nil before iterating over e.NvmeSubsystem.Paths.

Apply this diff to add the nil check:

 nvmeSubsystem := &rpc.NvmeSubsystem{
 	Paths: make(map[string]*rpc.NvmeDevicePath),
 }
+if e.NvmeSubsystem != nil {
 	for pathName, path := range e.NvmeSubsystem.Paths {
 		nvmeSubsystem.Paths[pathName] = &rpc.NvmeDevicePath{
 			Trtype:  path.Trtype,
 			Traddr:  path.Traddr,
 			Trsvcid: path.Trsvcid,
 			SrcAddr: path.SrcAddr,
 			State:   path.State,
 		}
-		logrus.Infof("Debug =====> pathName: %v, path: %v", pathName, path)
+		logrus.Debugf("pathName: %v, path: %v", pathName, path)
 	}
+}
📝 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
for pathName, path := range e.NvmeSubsystem.Paths {
nvmeSubsystem.Paths[pathName] = &rpc.NvmeDevicePath{
Trtype: path.Trtype,
Traddr: path.Traddr,
Trsvcid: path.Trsvcid,
SrcAddr: path.SrcAddr,
State: path.State,
}
logrus.Infof("Debug =====> pathName: %v, path: %v", pathName, path)
}
nvmeSubsystem := &rpc.NvmeSubsystem{
Paths: make(map[string]*rpc.NvmeDevicePath),
}
if e.NvmeSubsystem != nil {
for pathName, path := range e.NvmeSubsystem.Paths {
nvmeSubsystem.Paths[pathName] = &rpc.NvmeDevicePath{
Trtype: path.Trtype,
Traddr: path.Traddr,
Trsvcid: path.Trsvcid,
SrcAddr: path.SrcAddr,
State: path.State,
}
logrus.Debugf("pathName: %v, path: %v", pathName, path)
}
}

Copy link

@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: 4

🧹 Outside diff range and nitpick comments (1)
pkg/api/instance.go (1)

66-76: Consider adding field validation and documentation

While the NVMe-related structs are well-structured, consider:

  1. Adding field validation for required fields (e.g., Trtype, Traddr)
  2. Including documentation comments to describe valid values and constraints
  3. Adding constants for known transport types and states
+// Known transport types for NVMe devices
+const (
+    TrtypeTCP  = "tcp"
+    TrtypeRDMA = "rdma"
+)
+
+// Known states for NVMe device paths
+const (
+    PathStateActive    = "active"
+    PathStateInactive  = "inactive"
+)
+
+// NvmeDevicePath represents the addressing and state information for an NVMe device
 type NvmeDevicePath struct {
+    // Trtype specifies the transport type (e.g., tcp, rdma)
     Trtype  string `json:"trtype"`
+    // Traddr specifies the transport address
     Traddr  string `json:"traddr"`
+    // Trsvcid specifies the transport service ID (typically the port)
     Trsvcid string `json:"trsvcid"`
+    // SrcAddr specifies the source address for the connection
     SrcAddr string `json:"src_addr"`
+    // State indicates the current state of the path
     State   string `json:"state"`
 }
📜 Review details

Configuration used: .coderabbit.yaml
Review profile: CHILL

📥 Commits

Reviewing files that changed from the base of the PR and between 9db0036 and a8e9e60.

⛔ Files ignored due to path filters (8)
  • go.mod is excluded by !go.mod
  • go.sum is excluded by !**/*.sum, !go.sum
  • vendor/github.com/longhorn/go-spdk-helper/pkg/nvme/initiator.go is excluded by !vendor/**
  • vendor/github.com/longhorn/longhorn-spdk-engine/pkg/api/types.go is excluded by !vendor/**
  • vendor/github.com/longhorn/longhorn-spdk-engine/pkg/spdk/engine.go is excluded by !vendor/**
  • vendor/github.com/longhorn/types/pkg/generated/imrpc/instance.pb.go is excluded by !**/*.pb.go, !**/generated/**, !vendor/**
  • vendor/github.com/longhorn/types/pkg/generated/spdkrpc/spdk.pb.go is excluded by !**/*.pb.go, !**/generated/**, !vendor/**
  • vendor/modules.txt is excluded by !vendor/**
📒 Files selected for processing (2)
  • pkg/api/instance.go (1 hunks)
  • pkg/instance/instance.go (4 hunks)
🔇 Additional comments (4)
pkg/api/instance.go (1)

79-115: Verify the usage of new fields for live upgrades

Let's verify how these new fields are used in the upgrade process.

✅ Verification successful

Implementation of standby ports and NVMe subsystem is consistent

The code changes are properly implemented across the codebase:

  • The standby target ports are consistently used in both the API layer (pkg/api/instance.go) and instance implementation (pkg/instance/instance.go)
  • NVMe subsystem implementation is complete with proper mapping between RPC and API types
  • The fields are properly propagated from the engine response to instance response

The implementation appears to be part of the infrastructure needed to support live upgrades, with standby ports allowing for seamless transition during the upgrade process.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Search for usage of standby target ports and NVMe subsystem
echo "Searching for standby target port usage..."
rg "StandbyTargetPort(Start|End)" -A 5

echo "Searching for NVMe subsystem usage in upgrade logic..."
rg "NvmeSubsystem.*upgrade" -A 5

Length of output: 3794


Script:

#!/bin/bash
# Let's search for more context about the usage of these fields

# Search for files containing upgrade-related code
echo "Searching for upgrade-related files..."
fd -e go | xargs rg -l "upgrade"

# Search for instance status usage in upgrade context
echo "Searching for instance status in upgrade context..."
rg "InstanceStatus.*upgrade" -B 5 -A 5

# Search for standby port usage in non-generated files
echo "Searching for standby port usage in implementation..."
rg "StandbyTargetPort" --type go --glob '!vendor/**' --glob '!**/generated/**' -B 5 -A 5

# Search for NVMe subsystem implementation
echo "Searching for NVMe subsystem implementation..."
rg "NvmeSubsystem" --type go --glob '!vendor/**' --glob '!**/generated/**' -B 5 -A 5

Length of output: 10523

pkg/instance/instance.go (3)

714-724: LGTM: Clean implementation of NVMe subsystem handling

The implementation correctly maps the NVMe subsystem paths while maintaining all necessary fields.


738-747: LGTM: Proper implementation of standby target ports for live upgrade support

The addition of StandbyTargetPortStart and StandbyTargetPortEnd fields, populated from the Engine's StandbyTargetPort, provides the necessary support for live upgrades. The implementation maintains consistency with the existing port handling pattern.

✅ Verification successful

LGTM: Standby target port implementation is properly integrated and validated

The verification confirms that the standby target port implementation is well-integrated across the codebase with proper:

  • Port allocation and release handling in releaseTargetAndStandbyTargetPort
  • Validation checks to prevent duplicate port assignments (StandbyTargetPort != e.TargetPort)
  • State management during target switchover
  • Consistent usage in RPC/API interfaces

The implementation maintains proper port lifecycle management and includes necessary validation checks, confirming it's production-ready for live upgrade support.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Verify the usage of standby target ports in the codebase
rg -A 5 "StandbyTargetPort"

# Check for any validation of port ranges
ast-grep --pattern 'if $_.StandbyTargetPort$_ {
  $$$
}'

Length of output: 13693


155-155: Verify the removal of UpgradeRequired parameter

The removal of UpgradeRequired from EngineCreate call suggests a change in upgrade handling strategy. Please confirm if this aligns with the new live upgrade implementation.

✅ Verification successful

Based on the search results, I can now generate the final response:

The removal of UpgradeRequired parameter is intentional

The UpgradeRequired field still exists in the protobuf definitions and request specs (pkg/client/instance.go and protobuf files), but its usage has been changed. The field is now only used for logging purposes before engine creation ("upgradeRequired": req.Spec.UpgradeRequired), indicating that the upgrade handling has been refactored to use a different mechanism, likely through the new NVMe subsystem handling and standby target port fields that were added as part of the live upgrade support.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Check for any remaining references to UpgradeRequired in engine creation
rg -A 5 "UpgradeRequired.*EngineCreate"

# Check for any migration code handling the parameter removal
rg "UpgradeRequired.*deprecated"

Length of output: 76


Script:

#!/bin/bash
# Let's search for any references to UpgradeRequired in the codebase
rg "UpgradeRequired" -A 3

# Also check for the EngineCreate function definition to understand its parameters
ast-grep --pattern 'func $_ EngineCreate($_) {
  $$$
}'

Length of output: 3318


Script:

#!/bin/bash
# Let's look at the git history of the specific line to understand the change
git log -L155,155:pkg/instance/instance.go

# Also check for any new upgrade-related functionality that might have replaced this
rg "upgrade" -g "*.go" --no-filename -A 3 | grep -v "vendor"

Length of output: 20064

Comment on lines 106 to 117
State: obj.State,
ErrorMsg: obj.ErrorMsg,
Conditions: obj.Conditions,
PortStart: obj.PortStart,
PortEnd: obj.PortEnd,
TargetPortStart: obj.TargetPortStart,
TargetPortEnd: obj.TargetPortEnd,
StandbyTargetPortStart: obj.StandbyTargetPortStart,
StandbyTargetPortEnd: obj.StandbyTargetPortEnd,
NvmeSubsystem: nvmeSubstem,
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Validate instance status before return

Add validation of the constructed InstanceStatus before returning it to ensure data consistency.

-    return InstanceStatus{
+    status := InstanceStatus{
         State:                  obj.State,
         ErrorMsg:               obj.ErrorMsg,
         Conditions:             obj.Conditions,
         PortStart:              obj.PortStart,
         PortEnd:                obj.PortEnd,
         TargetPortStart:        obj.TargetPortStart,
         TargetPortEnd:          obj.TargetPortEnd,
         StandbyTargetPortStart: obj.StandbyTargetPortStart,
         StandbyTargetPortEnd:   obj.StandbyTargetPortEnd,
         NvmeSubsystem:          nvmeSubstem,
     }
+    if err := status.Validate(); err != nil {
+        return InstanceStatus{
+            State:    "error",
+            ErrorMsg: fmt.Sprintf("invalid instance status: %v", err),
+        }
+    }
+    return status

Committable suggestion skipped: line range outside the PR's diff.

Comment on lines 92 to 105
nvmeSubstem := NvmeSubsystem{
Paths: map[string]NvmeDevicePath{},
}
for pathName, path := range obj.NvmeSubsystem.Paths {
nvmeSubstem.Paths[pathName] = NvmeDevicePath{
Trtype: path.Trtype,
Traddr: path.Traddr,
Trsvcid: path.Trsvcid,
SrcAddr: path.SrcAddr,
State: path.State,
}
}
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue

Add nil checks and validation for NVMe subsystem conversion

The current implementation could panic if obj.NvmeSubsystem is nil. The previous review comment about this issue is still valid.

Apply this fix:

 func RPCToInstanceStatus(obj *rpc.InstanceStatus) InstanceStatus {
     nvmeSubstem := NvmeSubsystem{
         Paths: map[string]NvmeDevicePath{},
     }
+    if obj == nil {
+        return InstanceStatus{
+            State:    "error",
+            ErrorMsg: "invalid instance status: nil object",
+        }
+    }
+    if obj.NvmeSubsystem == nil {
+        return InstanceStatus{
+            State:    obj.State,
+            ErrorMsg: obj.ErrorMsg,
+        }
+    }
     for pathName, path := range obj.NvmeSubsystem.Paths {
+        if path == nil {
+            continue
+        }
         nvmeSubstem.Paths[pathName] = NvmeDevicePath{
             Trtype:  path.Trtype,
             Traddr:  path.Traddr,
             Trsvcid: path.Trsvcid,
             SrcAddr: path.SrcAddr,
             State:   path.State,
         }
     }
📝 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
nvmeSubstem := NvmeSubsystem{
Paths: map[string]NvmeDevicePath{},
}
for pathName, path := range obj.NvmeSubsystem.Paths {
nvmeSubstem.Paths[pathName] = NvmeDevicePath{
Trtype: path.Trtype,
Traddr: path.Traddr,
Trsvcid: path.Trsvcid,
SrcAddr: path.SrcAddr,
State: path.State,
}
}
nvmeSubstem := NvmeSubsystem{
Paths: map[string]NvmeDevicePath{},
}
if obj == nil {
return InstanceStatus{
State: "error",
ErrorMsg: "invalid instance status: nil object",
}
}
if obj.NvmeSubsystem == nil {
return InstanceStatus{
State: obj.State,
ErrorMsg: obj.ErrorMsg,
}
}
for pathName, path := range obj.NvmeSubsystem.Paths {
if path == nil {
continue
}
nvmeSubstem.Paths[pathName] = NvmeDevicePath{
Trtype: path.Trtype,
Traddr: path.Traddr,
Trsvcid: path.Trsvcid,
SrcAddr: path.SrcAddr,
State: path.State,
}
}

State: path.State,
}

logrus.Infof("Debug =====> pathName: %v, path: %v", pathName, path)
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Use appropriate logging level for debug messages

The debug message is using logrus.Infof which is inappropriate for debug information.

-logrus.Infof("Debug =====> pathName: %v, path: %v", pathName, path)
+logrus.Debugf("pathName: %v, path: %v", pathName, path)
📝 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
logrus.Infof("Debug =====> pathName: %v, path: %v", pathName, path)
logrus.Debugf("pathName: %v, path: %v", pathName, path)

Copy link

@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 and nitpick comments (3)
pkg/api/instance.go (3)

74-76: Add documentation and helper methods for NvmeSubsystem

The struct needs documentation and helper methods for path management.

Add documentation and methods:

+// NvmeSubsystem represents a collection of NVMe device paths
type NvmeSubsystem struct {
    Paths map[string]NvmeDevicePath `json:"paths"`
}

+// AddPath adds a new device path to the subsystem
+func (s *NvmeSubsystem) AddPath(name string, path NvmeDevicePath) error {
+    if err := path.Validate(); err != nil {
+        return fmt.Errorf("invalid path: %v", err)
+    }
+    s.Paths[name] = path
+    return nil
+}

79-88: Add documentation for new fields

The new fields need documentation explaining their purpose and relationship:

  • How standby ports relate to active ports
  • The role of NvmeSubsystem in the instance status

Add field documentation:

 type InstanceStatus struct {
     State                  string          `json:"state"`
     ErrorMsg               string          `json:"errorMsg"`
     Conditions             map[string]bool `json:"conditions"`
     PortStart              int32           `json:"portStart"`
     PortEnd                int32           `json:"portEnd"`
     TargetPortStart        int32           `json:"targetPortStart"`
     TargetPortEnd          int32           `json:"targetPortEnd"`
+    // StandbyTargetPortStart is the starting port number for standby targets during live upgrade
     StandbyTargetPortStart int32           `json:"standbyTargetPortStart"`
+    // StandbyTargetPortEnd is the ending port number for standby targets during live upgrade
     StandbyTargetPortEnd   int32           `json:"standbyTargetPortEnd"`
+    // NvmeSubsystem contains the NVMe device paths configuration and state
     NvmeSubsystem          NvmeSubsystem   `json:"nvmeSubsystem"`
 }

79-117: Document live upgrade strategy

The code introduces support for live upgrades through standby ports and NVMe subsystems, but the strategy needs documentation:

  1. How the standby ports are used during upgrades
  2. The role of NVMe subsystem in maintaining availability
  3. The upgrade process and failure handling

Consider adding a package-level documentation file (doc.go) explaining:

  • The live upgrade architecture
  • Port allocation strategy
  • State transition flows
  • Failure recovery mechanisms
📜 Review details

Configuration used: .coderabbit.yaml
Review profile: CHILL

📥 Commits

Reviewing files that changed from the base of the PR and between a8e9e60 and 338bbab.

⛔ Files ignored due to path filters (8)
  • go.mod is excluded by !go.mod
  • go.sum is excluded by !**/*.sum, !go.sum
  • vendor/github.com/longhorn/go-spdk-helper/pkg/nvme/initiator.go is excluded by !vendor/**
  • vendor/github.com/longhorn/longhorn-spdk-engine/pkg/api/types.go is excluded by !vendor/**
  • vendor/github.com/longhorn/longhorn-spdk-engine/pkg/spdk/engine.go is excluded by !vendor/**
  • vendor/github.com/longhorn/types/pkg/generated/imrpc/instance.pb.go is excluded by !**/*.pb.go, !**/generated/**, !vendor/**
  • vendor/github.com/longhorn/types/pkg/generated/spdkrpc/spdk.pb.go is excluded by !**/*.pb.go, !**/generated/**, !vendor/**
  • vendor/modules.txt is excluded by !vendor/**
📒 Files selected for processing (2)
  • pkg/api/instance.go (1 hunks)
  • pkg/instance/instance.go (4 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • pkg/instance/instance.go

Comment on lines 66 to 72
type NvmeDevicePath struct {
Trtype string `json:"trtype"`
Traddr string `json:"traddr"`
Trsvcid string `json:"trsvcid"`
SrcAddr string `json:"src_addr"`
State string `json:"state"`
}
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Add documentation and validation for NvmeDevicePath

The struct needs documentation and field validation:

  1. Add godoc comments explaining the purpose and usage
  2. Consider adding validation for required fields
  3. Define constants for possible state values

Add documentation and constants:

+// NvmeDevicePath represents the configuration and state of an NVMe device path
+const (
+    NvmeDeviceStateActive  = "active"
+    NvmeDeviceStateStandby = "standby"
+    // Add other valid states
+)

type NvmeDevicePath struct {
    Trtype  string `json:"trtype"`  // Transport type (e.g., tcp, rdma)
    Traddr  string `json:"traddr"`  // Transport address
    Trsvcid string `json:"trsvcid"` // Transport service ID
    SrcAddr string `json:"src_addr"`// Source address
    State   string `json:"state"`   // Current state of the device path
}

+// Validate ensures all required fields are set and values are valid
+func (p *NvmeDevicePath) Validate() error {
+    if p.Trtype == "" {
+        return fmt.Errorf("transport type is required")
+    }
+    // Add other validation as needed
+    return nil
+}

Committable suggestion skipped: line range outside the PR's diff.

Comment on lines 92 to 105
nvmeSubstem := NvmeSubsystem{
Paths: map[string]NvmeDevicePath{},
}
if obj.NvmeSubsystem != nil {
for pathName, path := range obj.NvmeSubsystem.Paths {
nvmeSubstem.Paths[pathName] = NvmeDevicePath{
Trtype: path.Trtype,
Traddr: path.Traddr,
Trsvcid: path.Trsvcid,
SrcAddr: path.SrcAddr,
State: path.State,
}
}
}
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Add validation during NVMe path conversion

The path conversion needs validation to ensure data integrity:

  1. Validate individual paths during conversion
  2. Handle invalid states gracefully

Add validation during conversion:

 nvmeSubstem := NvmeSubsystem{
     Paths: map[string]NvmeDevicePath{},
 }
 if obj.NvmeSubsystem != nil {
     for pathName, path := range obj.NvmeSubsystem.Paths {
+        if path == nil {
+            continue
+        }
+        devicePath := NvmeDevicePath{
+            Trtype:  path.Trtype,
+            Traddr:  path.Traddr,
+            Trsvcid: path.Trsvcid,
+            SrcAddr: path.SrcAddr,
+            State:   path.State,
+        }
+        if err := devicePath.Validate(); err != nil {
+            // Log the error but continue processing other paths
+            continue
+        }
-        nvmeSubstem.Paths[pathName] = NvmeDevicePath{
-            Trtype:  path.Trtype,
-            Traddr:  path.Traddr,
-            Trsvcid: path.Trsvcid,
-            SrcAddr: path.SrcAddr,
-            State:   path.State,
-        }
+        nvmeSubstem.Paths[pathName] = devicePath
     }
 }

Committable suggestion skipped: line range outside the PR's diff.

Copy link

@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: 3

📜 Review details

Configuration used: .coderabbit.yaml
Review profile: CHILL

📥 Commits

Reviewing files that changed from the base of the PR and between 338bbab and f706647.

⛔ Files ignored due to path filters (8)
  • go.mod is excluded by !go.mod
  • go.sum is excluded by !**/*.sum, !go.sum
  • vendor/github.com/longhorn/go-spdk-helper/pkg/nvme/initiator.go is excluded by !vendor/**
  • vendor/github.com/longhorn/longhorn-spdk-engine/pkg/api/types.go is excluded by !vendor/**
  • vendor/github.com/longhorn/longhorn-spdk-engine/pkg/spdk/engine.go is excluded by !vendor/**
  • vendor/github.com/longhorn/types/pkg/generated/imrpc/instance.pb.go is excluded by !**/*.pb.go, !**/generated/**, !vendor/**
  • vendor/github.com/longhorn/types/pkg/generated/spdkrpc/spdk.pb.go is excluded by !**/*.pb.go, !**/generated/**, !vendor/**
  • vendor/modules.txt is excluded by !vendor/**
📒 Files selected for processing (2)
  • pkg/api/instance.go (1 hunks)
  • pkg/instance/instance.go (4 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • pkg/instance/instance.go
🔇 Additional comments (1)
pkg/api/instance.go (1)

79-117: Verify live upgrade functionality

Please verify the following aspects of live upgrade:

  1. Port allocation strategy during upgrade
  2. NVMe path state transitions
  3. Handling of in-flight I/O during upgrade
✅ Verification successful

Let me gather more specific information about the port allocation and state management in the context of upgrades.


Based on the gathered information, I can now generate the final response:

Live upgrade functionality appears properly implemented

The codebase shows robust handling of live upgrades through:

  • Proper port allocation and release mechanisms for both target and standby target ports
  • Clean port switchover during upgrades (StandbyTargetPort becomes TargetPort)
  • NVMe subsystem state tracking for paths and controllers
  • Device mapper reloading after target switchover

Key implementation details:

  • Port allocation is managed through a bitmap allocator ensuring no conflicts
  • Target switchover includes proper cleanup and state management
  • NVMe paths maintain state information for monitoring health
🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Verify live upgrade implementation details

# Check for upgrade-related logic
echo "Searching for upgrade handlers..."
rg -l 'upgrade|migration' --type go

# Check for port allocation logic
echo "Searching for port allocation..."
rg -l 'port.*allocation|allocate.*port' --type go

# Check for NVMe state transition logic
echo "Searching for NVMe state management..."
rg -l 'nvme.*state|state.*transition' --type go

# Look for upgrade tests
echo "Searching for upgrade tests..."
rg -l 'test.*upgrade|upgrade.*test' --type go

Length of output: 6631


Script:

#!/bin/bash
# Check process manager's port allocation implementation
echo "Checking process manager's port allocation..."
rg -A 10 'func.*[Aa]llocate.*[Pp]ort' pkg/process/process_manager.go

# Check instance state management
echo "Checking instance state management..."
rg -A 10 'State.*=' pkg/instance/instance.go

# Check SPDK engine's port and state handling
echo "Checking SPDK engine implementation..."
rg -A 10 'StandbyTargetPort|NvmeSubsystem' vendor/github.com/longhorn/longhorn-spdk-engine/pkg/spdk/engine.go

# Look for upgrade-related state transitions
echo "Checking state transitions..."
rg 'transition|upgrade' pkg/instance/instance.go

Length of output: 6531

Comment on lines 92 to 117
nvmeSubsystem := NvmeSubsystem{
Paths: map[string]NvmeDevicePath{},
}
if obj.NvmeSubsystem != nil {
for pathName, path := range obj.NvmeSubsystem.Paths {
nvmeSubsystem.Paths[pathName] = NvmeDevicePath{
Trtype: path.Trtype,
Traddr: path.Traddr,
Trsvcid: path.Trsvcid,
SrcAddr: path.SrcAddr,
State: path.State,
}
}
}

return InstanceStatus{
State: obj.State,
ErrorMsg: obj.ErrorMsg,
Conditions: obj.Conditions,
PortStart: obj.PortStart,
PortEnd: obj.PortEnd,
TargetPortStart: obj.TargetPortStart,
TargetPortEnd: obj.TargetPortEnd,
State: obj.State,
ErrorMsg: obj.ErrorMsg,
Conditions: obj.Conditions,
PortStart: obj.PortStart,
PortEnd: obj.PortEnd,
TargetPortStart: obj.TargetPortStart,
TargetPortEnd: obj.TargetPortEnd,
StandbyTargetPortStart: obj.StandbyTargetPortStart,
StandbyTargetPortEnd: obj.StandbyTargetPortEnd,
NvmeSubsystem: nvmeSubsystem,
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue

Add proper error handling and validation in conversion function

The RPCToInstanceStatus function needs better error handling and validation:

  1. Add nil checks for input parameters
  2. Validate NVMe paths during conversion
  3. Validate the final InstanceStatus before returning
-func RPCToInstanceStatus(obj *rpc.InstanceStatus) InstanceStatus {
+func RPCToInstanceStatus(obj *rpc.InstanceStatus) (InstanceStatus, error) {
+    if obj == nil {
+        return InstanceStatus{}, fmt.Errorf("nil instance status")
+    }
+
     nvmeSubsystem := NvmeSubsystem{
         Paths: map[string]NvmeDevicePath{},
     }
     if obj.NvmeSubsystem != nil {
         for pathName, path := range obj.NvmeSubsystem.Paths {
+            if path == nil {
+                continue
+            }
+            if !isValidTransportType(path.Trtype) {
+                return InstanceStatus{}, fmt.Errorf(
+                    "invalid transport type: %s", path.Trtype)
+            }
             nvmeSubsystem.Paths[pathName] = NvmeDevicePath{
                 Trtype:  path.Trtype,
                 Traddr:  path.Traddr,
                 Trsvcid: path.Trsvcid,
                 SrcAddr: path.SrcAddr,
                 State:   path.State,
             }
         }
     }

-    return InstanceStatus{
+    status := InstanceStatus{
         State:                  obj.State,
         ErrorMsg:               obj.ErrorMsg,
         Conditions:             obj.Conditions,
         PortStart:              obj.PortStart,
         PortEnd:                obj.PortEnd,
         TargetPortStart:        obj.TargetPortStart,
         TargetPortEnd:          obj.TargetPortEnd,
         StandbyTargetPortStart: obj.StandbyTargetPortStart,
         StandbyTargetPortEnd:   obj.StandbyTargetPortEnd,
         NvmeSubsystem:          nvmeSubsystem,
     }
+    
+    if err := status.Validate(); err != nil {
+        return InstanceStatus{}, fmt.Errorf(
+            "invalid instance status: %v", err)
+    }
+    
+    return status, nil
 }

This change will require updating all callers to handle the error return value.

Committable suggestion skipped: line range outside the PR's diff.

Comment on lines 66 to 76
type NvmeDevicePath struct {
Trtype string `json:"trtype"`
Traddr string `json:"traddr"`
Trsvcid string `json:"trsvcid"`
SrcAddr string `json:"src_addr"`
State string `json:"state"`
}

type NvmeSubsystem struct {
Paths map[string]NvmeDevicePath `json:"paths"`
}
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Add documentation and type safety for NVMe structures

The new NVMe-related structures need documentation and type safety improvements:

  1. Add package documentation explaining the NVMe subsystem concept
  2. Define string constants for valid states and transport types
  3. Add validation methods
+// NvmeTransportType represents the supported NVMe transport protocols
+type NvmeTransportType string
+
+const (
+    NvmeTransportTCP  NvmeTransportType = "tcp"
+    NvmeTransportRDMA NvmeTransportType = "rdma"
+)
+
+// NvmeDeviceState represents the possible states of an NVMe device path
+type NvmeDeviceState string
+
+const (
+    NvmeDeviceStateActive  NvmeDeviceState = "active"
+    NvmeDeviceStateStandby NvmeDeviceState = "standby"
+)
+
+// NvmeDevicePath represents a path configuration for NVMe-oF (NVMe over Fabrics)
 type NvmeDevicePath struct {
-    Trtype  string `json:"trtype"`
+    Trtype  NvmeTransportType `json:"trtype"`
     Traddr  string `json:"traddr"`
     Trsvcid string `json:"trsvcid"`
     SrcAddr string `json:"src_addr"`
-    State   string `json:"state"`
+    State   NvmeDeviceState `json:"state"`
 }

+// NvmeSubsystem represents an NVMe subsystem with multiple device paths
+// used for high availability and live migration
 type NvmeSubsystem struct {
     Paths map[string]NvmeDevicePath `json:"paths"`
 }
📝 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
type NvmeDevicePath struct {
Trtype string `json:"trtype"`
Traddr string `json:"traddr"`
Trsvcid string `json:"trsvcid"`
SrcAddr string `json:"src_addr"`
State string `json:"state"`
}
type NvmeSubsystem struct {
Paths map[string]NvmeDevicePath `json:"paths"`
}
// NvmeTransportType represents the supported NVMe transport protocols
type NvmeTransportType string
const (
NvmeTransportTCP NvmeTransportType = "tcp"
NvmeTransportRDMA NvmeTransportType = "rdma"
)
// NvmeDeviceState represents the possible states of an NVMe device path
type NvmeDeviceState string
const (
NvmeDeviceStateActive NvmeDeviceState = "active"
NvmeDeviceStateStandby NvmeDeviceState = "standby"
)
// NvmeDevicePath represents a path configuration for NVMe-oF (NVMe over Fabrics)
type NvmeDevicePath struct {
Trtype NvmeTransportType `json:"trtype"`
Traddr string `json:"traddr"`
Trsvcid string `json:"trsvcid"`
SrcAddr string `json:"src_addr"`
State NvmeDeviceState `json:"state"`
}
// NvmeSubsystem represents an NVMe subsystem with multiple device paths
// used for high availability and live migration
type NvmeSubsystem struct {
Paths map[string]NvmeDevicePath `json:"paths"`
}

Comment on lines 79 to 88
State string `json:"state"`
ErrorMsg string `json:"errorMsg"`
Conditions map[string]bool `json:"conditions"`
PortStart int32 `json:"portStart"`
PortEnd int32 `json:"portEnd"`
TargetPortStart int32 `json:"targetPortStart"`
TargetPortEnd int32 `json:"targetPortEnd"`
StandbyTargetPortStart int32 `json:"standbyTargetPortStart"`
StandbyTargetPortEnd int32 `json:"standbyTargetPortEnd"`
NvmeSubsystem NvmeSubsystem `json:"nvmeSubsystem"`
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Add validation for port ranges and document fields

The InstanceStatus struct needs validation and documentation for the new fields:

  1. Document the relationship between active and standby ports
  2. Add validation to prevent port range overlaps
  3. Consider using a dedicated type for port ranges
+// PortRange represents a range of network ports
+type PortRange struct {
+    Start int32 `json:"start"`
+    End   int32 `json:"end"`
+}
+
+// InstanceStatus represents the current state and configuration of an instance
 type InstanceStatus struct {
     State      string          `json:"state"`
     ErrorMsg   string          `json:"errorMsg"`
     Conditions map[string]bool `json:"conditions"`
-    PortStart              int32         `json:"portStart"`
-    PortEnd                int32         `json:"portEnd"`
-    TargetPortStart        int32         `json:"targetPortStart"`
-    TargetPortEnd          int32         `json:"targetPortEnd"`
-    StandbyTargetPortStart int32         `json:"standbyTargetPortStart"`
-    StandbyTargetPortEnd   int32         `json:"standbyTargetPortEnd"`
+    // Port range for general instance communication
+    Ports PortRange `json:"ports"`
+    // Port range for active NVMe target
+    TargetPorts PortRange `json:"targetPorts"`
+    // Port range for standby NVMe target used during live migration
+    StandbyTargetPorts PortRange `json:"standbyTargetPorts"`
     NvmeSubsystem NvmeSubsystem `json:"nvmeSubsystem"`
 }
+
+func (pr *PortRange) Validate() error {
+    if pr.Start > pr.End {
+        return fmt.Errorf("invalid port range: start(%d) > end(%d)", 
+            pr.Start, pr.End)
+    }
+    return nil
+}
+
+func (is *InstanceStatus) Validate() error {
+    if err := is.Ports.Validate(); err != nil {
+        return fmt.Errorf("invalid ports: %v", err)
+    }
+    if err := is.TargetPorts.Validate(); err != nil {
+        return fmt.Errorf("invalid target ports: %v", err)
+    }
+    if err := is.StandbyTargetPorts.Validate(); err != nil {
+        return fmt.Errorf("invalid standby target ports: %v", err)
+    }
+    // Check for overlaps between active and standby ports
+    if is.TargetPorts.Start <= is.StandbyTargetPorts.End && 
+       is.StandbyTargetPorts.Start <= is.TargetPorts.End {
+        return fmt.Errorf("target port ranges overlap")
+    }
+    return nil
+}
📝 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
State string `json:"state"`
ErrorMsg string `json:"errorMsg"`
Conditions map[string]bool `json:"conditions"`
PortStart int32 `json:"portStart"`
PortEnd int32 `json:"portEnd"`
TargetPortStart int32 `json:"targetPortStart"`
TargetPortEnd int32 `json:"targetPortEnd"`
StandbyTargetPortStart int32 `json:"standbyTargetPortStart"`
StandbyTargetPortEnd int32 `json:"standbyTargetPortEnd"`
NvmeSubsystem NvmeSubsystem `json:"nvmeSubsystem"`
// PortRange represents a range of network ports
type PortRange struct {
Start int32 `json:"start"`
End int32 `json:"end"`
}
// InstanceStatus represents the current state and configuration of an instance
type InstanceStatus struct {
State string `json:"state"`
ErrorMsg string `json:"errorMsg"`
Conditions map[string]bool `json:"conditions"`
// Port range for general instance communication
Ports PortRange `json:"ports"`
// Port range for active NVMe target
TargetPorts PortRange `json:"targetPorts"`
// Port range for standby NVMe target used during live migration
StandbyTargetPorts PortRange `json:"standbyTargetPorts"`
NvmeSubsystem NvmeSubsystem `json:"nvmeSubsystem"`
}
func (pr *PortRange) Validate() error {
if pr.Start > pr.End {
return fmt.Errorf("invalid port range: start(%d) > end(%d)",
pr.Start, pr.End)
}
return nil
}
func (is *InstanceStatus) Validate() error {
if err := is.Ports.Validate(); err != nil {
return fmt.Errorf("invalid ports: %v", err)
}
if err := is.TargetPorts.Validate(); err != nil {
return fmt.Errorf("invalid target ports: %v", err)
}
if err := is.StandbyTargetPorts.Validate(); err != nil {
return fmt.Errorf("invalid standby target ports: %v", err)
}
// Check for overlaps between active and standby ports
if is.TargetPorts.Start <= is.StandbyTargetPorts.End &&
is.StandbyTargetPorts.Start <= is.TargetPorts.End {
return fmt.Errorf("target port ranges overlap")
}
return nil
}

Copy link

@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

🧹 Outside diff range and nitpick comments (1)
pkg/util/util.go (1)

Line range hint 129-137: Consider enhancing error logging in IsSPDKTgtReady.

While the function works correctly, it could benefit from debug-level logging to help diagnose connectivity issues in production.

Consider adding debug logging:

 func IsSPDKTgtReady(timeout time.Duration) bool {
     for i := 0; i < int(timeout.Seconds()); i++ {
         conn, err := net.DialTimeout(helpertypes.DefaultJSONServerNetwork, helpertypes.DefaultUnixDomainSocketPath, 1*time.Second)
         if err == nil {
             conn.Close()
             return true
         }
+        logrus.Debugf("SPDK target not ready yet, attempt %d: %v", i+1, err)
         time.Sleep(time.Second)
     }
     return false
 }
📜 Review details

Configuration used: .coderabbit.yaml
Review profile: CHILL

📥 Commits

Reviewing files that changed from the base of the PR and between f706647 and 5977643.

⛔ Files ignored due to path filters (11)
  • go.mod is excluded by !go.mod
  • go.sum is excluded by !**/*.sum, !go.sum
  • vendor/github.com/longhorn/go-spdk-helper/pkg/nvme/initiator.go is excluded by !vendor/**
  • vendor/github.com/longhorn/go-spdk-helper/pkg/nvme/nvme.go is excluded by !vendor/**
  • vendor/github.com/longhorn/go-spdk-helper/pkg/nvme/nvmecli.go is excluded by !vendor/**
  • vendor/github.com/longhorn/go-spdk-helper/pkg/types/types.go is excluded by !vendor/**
  • vendor/github.com/longhorn/longhorn-spdk-engine/pkg/api/types.go is excluded by !vendor/**
  • vendor/github.com/longhorn/longhorn-spdk-engine/pkg/spdk/engine.go is excluded by !vendor/**
  • vendor/github.com/longhorn/types/pkg/generated/imrpc/instance.pb.go is excluded by !**/*.pb.go, !**/generated/**, !vendor/**
  • vendor/github.com/longhorn/types/pkg/generated/spdkrpc/spdk.pb.go is excluded by !**/*.pb.go, !**/generated/**, !vendor/**
  • vendor/modules.txt is excluded by !vendor/**
📒 Files selected for processing (3)
  • pkg/api/instance.go (1 hunks)
  • pkg/instance/instance.go (4 hunks)
  • pkg/util/util.go (2 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • pkg/instance/instance.go
🔇 Additional comments (5)
pkg/util/util.go (1)

20-20: LGTM! Clean import alias change.

The change from spdkhelpertypes to helpertypes is applied consistently across the codebase. The usage of the imported constants remains correct.

Also applies to: 131-131

pkg/api/instance.go (4)

92-117: Verify and update all callers of RPCToInstanceStatus

The signature change of RPCToInstanceStatus requires updates to all calling functions.


92-117: ⚠️ Potential issue

Add proper error handling and validation in conversion function

The RPCToInstanceStatus function needs better error handling to prevent panics and ensure data consistency:

  1. Add nil checks for input parameters
  2. Validate NVMe paths during conversion
  3. Validate the final status before returning
-func RPCToInstanceStatus(obj *rpc.InstanceStatus) InstanceStatus {
+func RPCToInstanceStatus(obj *rpc.InstanceStatus) (InstanceStatus, error) {
+    if obj == nil {
+        return InstanceStatus{}, fmt.Errorf("nil instance status")
+    }
+
     nvmeSubsystem := NvmeSubsystem{
         Paths: map[string]NvmeDevicePath{},
     }
     if obj.NvmeSubsystem != nil {
         for pathName, path := range obj.NvmeSubsystem.Paths {
+            if path == nil {
+                continue
+            }
             nvmeSubsystem.Paths[pathName] = NvmeDevicePath{
                 Trtype:  path.Trtype,
                 Traddr:  path.Traddr,
                 Trsvcid: path.Trsvcid,
                 SrcAddr: path.SrcAddr,
                 State:   path.State,
             }
+            if err := nvmeSubsystem.Paths[pathName].Validate(); err != nil {
+                return InstanceStatus{}, fmt.Errorf("invalid path %q: %v", pathName, err)
+            }
         }
     }

-    return InstanceStatus{
+    status := InstanceStatus{
         State:                  obj.State,
         ErrorMsg:               obj.ErrorMsg,
         Conditions:             obj.Conditions,
         PortStart:              obj.PortStart,
         PortEnd:                obj.PortEnd,
         TargetPortStart:        obj.TargetStart,
         TargetPortEnd:          obj.TargetEnd,
         StandbyTargetPortStart: obj.StandbyTargetPortStart,
         StandbyTargetPortEnd:   obj.StandbyTargetPortEnd,
         NvmeSubsystem:          nvmeSubsystem,
     }
+
+    if err := status.Validate(); err != nil {
+        return InstanceStatus{}, fmt.Errorf("invalid status: %v", err)
+    }
+
+    return status, nil
 }

Note: This change will require updating all callers to handle the error return value.

Likely invalid or redundant comment.


66-76: 🛠️ Refactor suggestion

Enhance type safety and add documentation for NVMe structures

The NVMe structures need improvements for better maintainability and reliability:

  1. Add type safety for transport types and states
  2. Add documentation for types and fields
  3. Add validation methods
+// TransportType represents the NVMe over Fabrics transport protocol
+type TransportType string

+const (
+    TransportTCP  TransportType = "tcp"
+    TransportRDMA TransportType = "rdma"
+)

+// DeviceState represents the operational state of an NVMe device path
+type DeviceState string

+const (
+    DeviceStateActive  DeviceState = "active"
+    DeviceStateStandby DeviceState = "standby"
+)

+// NvmeDevicePath represents a connection path to an NVMe over Fabrics target
 type NvmeDevicePath struct {
-    Trtype  string `json:"trtype"`
+    Trtype  TransportType `json:"trtype"`  // Transport protocol (tcp/rdma)
     Traddr  string        `json:"traddr"`  // Target address
     Trsvcid string        `json:"trsvcid"` // Target port
     SrcAddr string        `json:"src_addr"`// Source address
-    State   string        `json:"state"`
+    State   DeviceState   `json:"state"`   // Current device state
 }

+// Validate checks if the NvmeDevicePath fields are valid
+func (p *NvmeDevicePath) Validate() error {
+    if p.Trtype == "" {
+        return fmt.Errorf("transport type is required")
+    }
+    if p.Traddr == "" {
+        return fmt.Errorf("target address is required")
+    }
+    return nil
+}

+// NvmeSubsystem represents an NVMe subsystem containing multiple device paths
+// used for high availability and live migration support
 type NvmeSubsystem struct {
     Paths map[string]NvmeDevicePath `json:"paths"`
 }

+// Validate checks if the NvmeSubsystem configuration is valid
+func (s *NvmeSubsystem) Validate() error {
+    for name, path := range s.Paths {
+        if err := path.Validate(); err != nil {
+            return fmt.Errorf("invalid path %q: %v", name, err)
+        }
+    }
+    return nil
+}

Likely invalid or redundant comment.


79-88: 🛠️ Refactor suggestion

Add documentation and validation for InstanceStatus

The InstanceStatus struct needs documentation and validation for the new fields to prevent configuration errors:

  1. Document the purpose of standby target ports
  2. Add validation to prevent port range conflicts
  3. Consider using a dedicated type for port ranges
+// InstanceStatus represents the current state and configuration of an instance
+// including its NVMe subsystem and port allocations for live upgrades
 type InstanceStatus struct {
     State                  string          `json:"state"`     // Current operational state
     ErrorMsg               string          `json:"errorMsg"`  // Error message if any
     Conditions             map[string]bool `json:"conditions"`// Instance conditions
     PortStart              int32           `json:"portStart"` // Start of general port range
     PortEnd                int32           `json:"portEnd"`   // End of general port range
     TargetPortStart        int32           `json:"targetPortStart"`  // Start of active target ports
     TargetPortEnd          int32           `json:"targetPortEnd"`    // End of active target ports
     StandbyTargetPortStart int32           `json:"standbyTargetPortStart"`  // Start of standby target ports for live upgrade
     StandbyTargetPortEnd   int32           `json:"standbyTargetPortEnd"`    // End of standby target ports for live upgrade
     NvmeSubsystem          NvmeSubsystem   `json:"nvmeSubsystem"`    // NVMe subsystem configuration
 }

+// Validate checks if the InstanceStatus configuration is valid
+func (s *InstanceStatus) Validate() error {
+    if s.PortEnd < s.PortStart {
+        return fmt.Errorf("invalid port range: %d-%d", s.PortStart, s.PortEnd)
+    }
+    if s.TargetPortEnd < s.TargetPortStart {
+        return fmt.Errorf("invalid target port range: %d-%d", s.TargetPortStart, s.TargetPortEnd)
+    }
+    if s.StandbyTargetPortEnd < s.StandbyTargetPortStart {
+        return fmt.Errorf("invalid standby target port range: %d-%d", s.StandbyTargetPortStart, s.StandbyTargetPortEnd)
+    }
+    // Check for overlapping port ranges
+    if s.TargetPortStart <= s.StandbyTargetPortEnd && s.StandbyTargetPortStart <= s.TargetPortEnd {
+        return fmt.Errorf("target port ranges overlap: active(%d-%d) standby(%d-%d)",
+            s.TargetPortStart, s.TargetPortEnd, s.StandbyTargetPortStart, s.StandbyTargetPortEnd)
+    }
+    return s.NvmeSubsystem.Validate()
+}

Likely invalid or redundant comment.

Copy link

mergify bot commented Nov 30, 2024

This pull request is now in conflict. Could you fix it @derekbit? 🙏

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.

2 participants