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

fix: add data engine option to restore backing image api #3392

Open
wants to merge 1 commit into
base: master
Choose a base branch
from

Conversation

ChanYiLin
Copy link
Contributor

@ChanYiLin ChanYiLin commented Dec 23, 2024

ref:

Fix:

cc @derekbit @mantissahz @houhoucoop

Copy link

coderabbitai bot commented Dec 23, 2024

Walkthrough

This pull request introduces modifications to the backing image backup and restore functionality across multiple files. The changes primarily focus on adding support for a new DataEngine parameter in the backing image restore process. The modifications span the API, client, controller, and manager layers, updating method signatures, input structures, and restoration logic to accommodate the data engine specification during backing image operations.

Changes

File Change Summary
api/backupbackingimage.go Updated BackupBackingImageRestore and BackupBackingImageCreate methods to handle new DataEngine parameter and use new naming function.
api/model.go Added DataEngine field to BackingImageRestoreInput struct.
client/generated_backing_image_restore_input.go Added DataEngine field with JSON and YAML tags.
controller/system_backup_controller.go Added conditional check to skip backup for data engine version 2 backing images.
manager/backupbackingimage.go Updated RestoreBackupBackingImage method signature to include dataEngine parameter.
manager/volume.go Modified restoreBackingImage method to handle dataEngine parameter and validate data engine type.

Assessment against linked issues

Objective Addressed Explanation
Resolve backup backing image creation issue [#10023]
Support data engine specification in backing image restore

Possibly related PRs

Suggested reviewers

  • ChanYiLin
  • mantissahz

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 generate docstrings to generate docstrings for this PR. (Beta)
  • @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: 2

🧹 Nitpick comments (3)
manager/volume.go (2)

1200-1202: Fallback to DataEngineTypeV1 is correct.

Providing a default (v1) if no engine is specified helps maintain backward compatibility.


1204-1207: Ensure further data engine type validation.

The explicit check for v1 or v2 is sufficient. In the future, consider a single source of truth for permissible engine types if more types are introduced.

controller/system_backup_controller.go (1)

990-993: Skipping data engine v2 backing images.

A single comment clarifies the unimplemented backup logic for v2. If upcoming releases will support v2 backups, consider adding a future-proof structure or TODO next to this code.

📜 Review details

Configuration used: .coderabbit.yaml
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between fb9c6ca and ee091b3.

📒 Files selected for processing (6)
  • api/backupbackingimage.go (3 hunks)
  • api/model.go (1 hunks)
  • client/generated_backing_image_restore_input.go (1 hunks)
  • controller/system_backup_controller.go (1 hunks)
  • manager/backupbackingimage.go (2 hunks)
  • manager/volume.go (4 hunks)
🧰 Additional context used
📓 Learnings (1)
manager/volume.go (1)
Learnt from: derekbit
PR: longhorn/longhorn-manager#3295
File: controller/backing_image_data_source_controller.go:404-407
Timestamp: 2024-12-13T07:06:57.033Z
Learning: In the `syncBackingImage` function in `controller/backing_image_data_source_controller.go`, when adding a new entry to `bi.Spec.DiskFileSpecMap`, the `DataEngine` is set to `DataEngineTypeV1` because the backing image data source always prepares the backing image on the v1 disk.
🪛 GitHub Check: CodeFactor
manager/volume.go

[notice] 1192-1260: manager/volume.go#L1192-L1260
Complex Method

🔇 Additional comments (9)
manager/volume.go (4)

1192-1192: New function signature for restoreBackingImage looks good.

The added dataEngine parameter is consistent with the feature requirement. No immediate syntax or logic issues are spotted at the function declaration level.

🧰 Tools
🪛 GitHub Check: CodeFactor

[notice] 1192-1260: manager/volume.go#L1192-L1260
Complex Method


1219-1221: Logically consistent mismatch check.

Rejecting a pre-existing backing image with a different data engine prevents inconsistent states. Ensure the error message covers potential user confusion about partial restore attempts.


1242-1242: Setting the backing image data engine type.

Assigning the data engine for the newly restored backing image is correct. This aligns with the added logic earlier, ensuring the correct metadata is stored.


161-161: Verify backing image existence before restore.

Here, a restore is initiated unconditionally. Although the function internally handles empty or non-existent backing images, a lightweight existence check for spec.BackingImage beforehand could improve clarity and reduce function calls.

api/backupbackingimage.go (2)

75-77: Streamlined naming pattern for backup backing images.

Using types.GetBackupBackingImageNameFromBIName standardizes naming across the system. This also helps avoid potential collisions.


60-60: Properly forwarding the DataEngine parameter.

Including 'input.DataEngine' ensures that the new engine type is passed along for restoration. Verify that the API surfaces any erroneous or empty dataEngine input clearly to the user.

✅ Verification successful

Let me gather information about how the DataEngine parameter is handled in the codebase.


Let me check how the DataEngine parameter is validated and handled in the RestoreBackupBackingImage method.


Let me check the manager's implementation of RestoreBackupBackingImage and related validation code.


Let me check for any validation of DataEngine in the backing image and volume code, as well as the complete implementation of RestoreBackupBackingImage.


Based on the code analysis, I can now generate the final response:

DataEngine parameter is properly handled with default fallback and validation.

The implementation shows proper handling of the DataEngine parameter:

  • In CreateBackingImage, empty DataEngine defaults to "v1"
  • IsDataEngineV1/V2 functions validate the engine type
  • RestoreBackupBackingImage forwards the parameter to restoreBackingImage for actual restoration
🏁 Scripts executed

The following scripts were executed for the analysis:

Error: No shell scripts found


Script:

#!/bin/bash
# Check the implementation of RestoreBackupBackingImage method
ast-grep --pattern 'func ($_, $_) RestoreBackupBackingImage($$$) {
  $$$
}'

# Search for validation or error handling of DataEngine parameter
rg -A 5 "DataEngine.*error|error.*DataEngine" 

# Look for DataEngine type definition and valid values
rg "type.*DataEngine|DataEngine.*string"

Length of output: 49825


Script:

#!/bin/bash
# Look for RestoreBackupBackingImage implementation
ast-grep --pattern 'func ($_, $_) RestoreBackupBackingImage($$$) {
  $$$
}'

# Check for validation of DataEngine in backing image related code
rg -A 5 "RestoreBackupBackingImage.*DataEngine|DataEngine.*RestoreBackupBackingImage"

# Look for backing image restore input validation
rg -A 5 "BackingImageRestoreInput.*validate|validate.*BackingImageRestoreInput"

Length of output: 653


Script:

#!/bin/bash
# Look for RestoreBackupBackingImage implementation in manager
rg -A 10 "func.*RestoreBackupBackingImage" manager/

# Check for DataEngine validation in backing image related code
rg -A 5 "ValidateDataEngine|validateDataEngine" 

# Look for backing image restore validation in webhook
rg -A 10 "backupbackingimage.*validate" webhook/

Length of output: 847


Script:

#!/bin/bash
# Get the complete RestoreBackupBackingImage implementation
rg -A 30 "func.*RestoreBackupBackingImage" manager/

# Check for DataEngine validation in types
rg -A 5 "IsDataEngineV1|IsDataEngineV2|ValidateDataEngine" types/

# Look for backing image creation with DataEngine
rg -A 10 "CreateBackingImage.*DataEngine" manager/

Length of output: 3940

manager/backupbackingimage.go (1)

65-65: Reuses existing method restoreBackingImage.

Passing the new dataEngine parameter is seamless and aligns well with the updated function signature in volume.go.

api/model.go (1)

298-298: DataEngine field in BackingImageRestoreInput recognized.

The new DataEngine string field extends the restore request payload, aligning with the rest of the code changes. Consider validating or enumerating allowed engine types on the API level.

client/generated_backing_image_restore_input.go (1)

10-11: LGTM! Field addition follows proper patterns.

The new DataEngine field is correctly implemented with appropriate JSON/YAML tags and follows the existing naming conventions. The omitempty tag ensures backward compatibility with existing clients.

Let's verify the generator configuration to ensure this addition is properly reflected:

✅ Verification successful

Field addition is correctly synchronized with API model

The verification shows that the DataEngine field is properly defined in both:

  • api/model.go: Core API model definition
  • client/generated_backing_image_restore_input.go: Generated client code

The field definitions are consistent across both files with matching types and JSON tags, confirming this is a valid generated client implementation.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Verify the generator configuration and related schema definitions

# Look for generator configuration files
fd -t f -e yaml -e json 'schema|swagger|openapi' | while read -r file; do
  echo "=== Checking $file for DataEngine field definition ==="
  cat "$file"
done

# Look for related type definitions that might reference this
rg -A 5 -B 5 'type BackingImageRestoreInput struct' --type go

Length of output: 46686

@@ -41,7 +41,7 @@ func (m *VolumeManager) DeleteBackupBackingImage(name string) error {
return m.ds.DeleteBackupBackingImage(name)
}

func (m *VolumeManager) RestoreBackupBackingImage(name string, secret, secretNamespace string) error {
func (m *VolumeManager) RestoreBackupBackingImage(name string, secret, secretNamespace string, dataEngine string) error {
Copy link

Choose a reason for hiding this comment

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

💡 Codebase verification

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

Add validation for 'dataEngine' parameter

The code already has a guard for empty 'name' parameter, but lacks validation for 'dataEngine'. Looking at the codebase:

  • Empty name check exists: if name == "" { return fmt.Errorf("restore backing image name is not given") }
  • Other parts of the codebase validate dataEngine with messages like "invalid data engine %v"
  • Similar function in manager/volume.go validates dataEngine: if dataEngine == "" { ... }

Add validation for dataEngine parameter to fail fast:

if dataEngine == "" {
    return fmt.Errorf("data engine is not specified")
}
🔗 Analysis chain

Add guards around empty 'name' or 'dataEngine' parameters.

When name is empty, it fails fast. Similarly, consider quickly checking if 'dataEngine' has an invalid format rather than relying solely on the deeper restore logic.


Let me gather information about the parameter validation in the codebase by checking the implementation and related functions.


Let me check the implementation of RestoreBackupBackingImage to see how dataEngine is validated.

🏁 Scripts executed

The following scripts were executed for the analysis:

Error: No shell scripts found


Script:

#!/bin/bash
# Check the implementation of RestoreBackupBackingImage and its parameter validation
ast-grep --pattern 'func (m *VolumeManager) RestoreBackupBackingImage($$$) {
  $$$
}'

# Look for similar functions in the same file to understand validation patterns
rg -A 5 "func.*VolumeManager.*" manager/backupbackingimage.go

# Search for validation patterns across the codebase
rg "if.*name\s*==\s*\"\"" 
rg "if.*dataEngine\s*==\s*\"\"" 

Length of output: 7490


Script:

#!/bin/bash
# Get the full implementation of RestoreBackupBackingImage
rg -A 20 "func \(m \*VolumeManager\) RestoreBackupBackingImage" manager/backupbackingimage.go

# Check for dataEngine validation patterns in the codebase
rg "dataEngine.*validation" -A 5
rg "invalid.*dataEngine"

Length of output: 7521

Copy link
Member

@derekbit derekbit left a comment

Choose a reason for hiding this comment

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

In general, LGTM. Few comments need to be addressed.

Comment on lines 6 to 8
"github.com/gorilla/mux"
"github.com/longhorn/longhorn-manager/types"
"github.com/pkg/errors"
Copy link
Member

Choose a reason for hiding this comment

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

Reorg

@@ -41,7 +41,7 @@ func (m *VolumeManager) DeleteBackupBackingImage(name string) error {
return m.ds.DeleteBackupBackingImage(name)
}

func (m *VolumeManager) RestoreBackupBackingImage(name string, secret, secretNamespace string) error {
func (m *VolumeManager) RestoreBackupBackingImage(name string, secret, secretNamespace string, dataEngine string) error {
Copy link
Member

Choose a reason for hiding this comment

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

Suggested change
func (m *VolumeManager) RestoreBackupBackingImage(name string, secret, secretNamespace string, dataEngine string) error {
func (m *VolumeManager) RestoreBackupBackingImage(name string, secret, secretNamespace, dataEngine string) error {

@derekbit
Copy link
Member

@mergify backport v1.8.x

@derekbit derekbit force-pushed the LH6341_v2_backing_image_restore_api branch from ee091b3 to c5effab Compare December 23, 2024 12:45
Copy link

mergify bot commented Dec 23, 2024

backport v1.8.x

🟠 Waiting for conditions to match

  • merged [📌 backport requirement]

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

🧹 Nitpick comments (2)
manager/volume.go (2)

1192-1207: LGTM! Consider using a constant for the default data engine value.

The parameter validation and error handling look good. However, consider using string(longhorn.DataEngineTypeV1) directly in the default value assignment for consistency.

-		dataEngine = string(longhorn.DataEngineTypeV1)
+		dataEngine = string(longhorn.DataEngineTypeV1)
🧰 Tools
🪛 GitHub Check: CodeFactor

[notice] 1192-1260: manager/volume.go#L1192-L1260
Complex Method


Line range hint 1192-1260: Consider breaking down the complex method into smaller functions.

While the method is well-structured, it could benefit from being split into smaller, more focused functions to improve readability and maintainability:

  1. validateBackingImageSecret
  2. validateDataEngine
  3. checkExistingBackingImage
  4. restoreFromBackup

Example refactor for the first two functions:

func (m *VolumeManager) validateBackingImageSecret(secret, secretNamespace string) error {
    if secret != "" || secretNamespace != "" {
        if _, err := m.ds.GetSecretRO(secretNamespace, secret); err != nil {
            return errors.Wrapf(err, "failed to get secret %v in namespace %v", secret, secretNamespace)
        }
    }
    return nil
}

func (m *VolumeManager) validateDataEngine(dataEngine string) (string, error) {
    if dataEngine == "" {
        dataEngine = string(longhorn.DataEngineTypeV1)
    }
    
    if longhorn.DataEngineType(dataEngine) != longhorn.DataEngineTypeV1 && 
       longhorn.DataEngineType(dataEngine) != longhorn.DataEngineTypeV2 {
        return "", fmt.Errorf("invalid data engine type %v", dataEngine)
    }
    return dataEngine, nil
}
📜 Review details

Configuration used: .coderabbit.yaml
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between ee091b3 and c5effab.

📒 Files selected for processing (6)
  • api/backupbackingimage.go (3 hunks)
  • api/model.go (1 hunks)
  • client/generated_backing_image_restore_input.go (1 hunks)
  • controller/system_backup_controller.go (1 hunks)
  • manager/backupbackingimage.go (2 hunks)
  • manager/volume.go (4 hunks)
🚧 Files skipped from review as they are similar to previous changes (5)
  • client/generated_backing_image_restore_input.go
  • api/model.go
  • manager/backupbackingimage.go
  • controller/system_backup_controller.go
  • api/backupbackingimage.go
🧰 Additional context used
🪛 GitHub Check: CodeFactor
manager/volume.go

[notice] 1192-1260: manager/volume.go#L1192-L1260
Complex Method

🔇 Additional comments (2)
manager/volume.go (2)

1219-1221: LGTM! Good validation for data engine type compatibility.

The validation ensures that an existing backing image's data engine type matches the requested type, preventing potential inconsistencies.


1242-1242: LGTM! Data engine type correctly set in backing image spec.

The data engine type is properly propagated to the new backing image during restoration.

@derekbit derekbit force-pushed the LH6341_v2_backing_image_restore_api branch from c5effab to 97779ed Compare December 23, 2024 14:38
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

🧹 Nitpick comments (1)
manager/volume.go (1)

Line range hint 1192-1260: Consider breaking down the complex method

The restoreBackingImage method is flagged as complex by static analysis. Consider breaking it down into smaller, focused functions:

  1. validateBackingImageSecret
  2. validateAndDefaultDataEngine
  3. checkExistingBackingImage
  4. createRestoreBackingImage

Example refactor:

func (m *VolumeManager) restoreBackingImage(backupTargetName, biName, secret, secretNamespace, dataEngine string) error {
+    if err := m.validateBackingImageSecret(secret, secretNamespace); err != nil {
+        return err
+    }
+
+    dataEngine, err := m.validateAndDefaultDataEngine(dataEngine)
+    if err != nil {
+        return err
+    }
+
+    if biName == "" {
+        return nil
+    }
+
+    if err := m.checkExistingBackingImage(biName, dataEngine); err != nil {
+        return err
+    }
+
+    return m.createRestoreBackingImage(backupTargetName, biName, secret, secretNamespace, dataEngine)
}

+func (m *VolumeManager) validateBackingImageSecret(secret, secretNamespace string) error {
+    if secret != "" || secretNamespace != "" {
+        if _, err := m.ds.GetSecretRO(secretNamespace, secret); err != nil {
+            return errors.Wrapf(err, "failed to get secret %v in namespace %v", secret, secretNamespace)
+        }
+    }
+    return nil
}

+func (m *VolumeManager) validateAndDefaultDataEngine(dataEngine string) (string, error) {
+    if dataEngine == "" {
+        dataEngine = string(longhorn.DataEngineTypeV1)
+    }
+    if longhorn.DataEngineType(dataEngine) != longhorn.DataEngineTypeV1 && 
+       longhorn.DataEngineType(dataEngine) != longhorn.DataEngineTypeV2 {
+        return "", fmt.Errorf("invalid data engine type %v", dataEngine)
+    }
+    return dataEngine, nil
}
📜 Review details

Configuration used: .coderabbit.yaml
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between c5effab and 97779ed.

📒 Files selected for processing (6)
  • api/backupbackingimage.go (3 hunks)
  • api/model.go (1 hunks)
  • client/generated_backing_image_restore_input.go (1 hunks)
  • controller/system_backup_controller.go (1 hunks)
  • manager/backupbackingimage.go (2 hunks)
  • manager/volume.go (4 hunks)
🚧 Files skipped from review as they are similar to previous changes (5)
  • client/generated_backing_image_restore_input.go
  • controller/system_backup_controller.go
  • api/model.go
  • manager/backupbackingimage.go
  • api/backupbackingimage.go
🧰 Additional context used
📓 Learnings (1)
manager/volume.go (1)
Learnt from: derekbit
PR: longhorn/longhorn-manager#3295
File: controller/backing_image_data_source_controller.go:404-407
Timestamp: 2024-12-13T07:06:57.033Z
Learning: In the `syncBackingImage` function in `controller/backing_image_data_source_controller.go`, when adding a new entry to `bi.Spec.DiskFileSpecMap`, the `DataEngine` is set to `DataEngineTypeV1` because the backing image data source always prepares the backing image on the v1 disk.
🪛 GitHub Check: CodeFactor
manager/volume.go

[notice] 1192-1260: manager/volume.go#L1192-L1260
Complex Method

🔇 Additional comments (3)
manager/volume.go (3)

161-161: LGTM: Data engine parameter added correctly

The addition of the data engine parameter to the restoreBackingImage call is consistent with the PR objectives and properly converts the DataEngine type to string.


1200-1206: LGTM: Proper data engine validation with default fallback

The code correctly:

  1. Defaults to DataEngineTypeV1 when no engine is specified
  2. Validates that only V1 or V2 engines are allowed
  3. Provides clear error messaging

1219-1221: LGTM: Proper data engine compatibility check

The code correctly prevents restoring a backing image when there's a data engine type mismatch with an existing backing image.

@@ -71,8 +72,9 @@ func (s *Server) BackupBackingImageCreate(w http.ResponseWriter, req *http.Reque
}

backingImageName := mux.Vars(req)["name"]
if err := s.m.CreateBackupBackingImage(input.Name, backingImageName, input.BackupTargetName); err != nil {
return errors.Wrapf(err, "failed to create backup backing image '%s'", input.Name)
backupBackingImageName := types.GetBackupBackingImageNameFromBIName(backingImageName)
Copy link
Contributor

Choose a reason for hiding this comment

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

  • Can we check if input.Name is equal to backingImageName or an empty string, and then generate a random UUID name?
  • Or we won't allow users to customize the backup backing image name from the API when creating it.

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.

[BUG] Unable to backup backing image through Longhorn UI
3 participants