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(resize): delegate RWX filesystem resize to share-manager #3267

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

Conversation

james-munson
Copy link
Contributor

@james-munson james-munson commented Nov 11, 2024

Which issue(s) this PR fixes:

Issue longhorn/longhorn#9736 aka longhorn/longhorn#8118

What this PR does / why we need it:

Special notes for your reviewer:

Additional documentation or context

Copy link

coderabbitai bot commented Nov 11, 2024

Walkthrough

The changes in this pull request introduce enhancements to the Longhorn CSI plugin, specifically for handling ReadWriteMany (RWX) volume expansion. A new environment variable POD_NAMESPACE is added to the longhorn-manager container configuration. Additionally, the NodeServer now includes a method NodeExpandSharedVolume to facilitate online filesystem resizing for RWX volumes, while the existing NodeExpandVolume method is modified to call this new method. Furthermore, a new method FilesystemResize is added to the ShareManagerClient struct to support the resizing operation.

Changes

File Change Summary
csi/deployment.go Added environment variable POD_NAMESPACE to PluginDeployment structure in NewPluginDeployment.
csi/node_server.go Added method NodeExpandSharedVolume for online expansion of RWX volumes; modified NodeExpandVolume to call it. Enhanced error handling and logging. Updated import statements for Kubernetes client libraries.
engineapi/share_manager.go Added method FilesystemResize to ShareManagerClient struct to perform filesystem resize operations.

Assessment against linked issues

Objective Addressed Explanation
Support online expansion of RWX PVCs (Issue #9736)
Ensure filesystem resizing functionality is implemented (Issue #9736)
Improve error handling during volume expansion (Issue #9736)

Possibly related PRs


📜 Recent review details

Configuration used: .coderabbit.yaml
Review profile: CHILL

📥 Commits

Reviewing files that changed from the base of the PR and between 077ce60 and 1dcd787.

⛔ Files ignored due to path filters (6)
  • go.mod is excluded by !go.mod
  • go.sum is excluded by !**/*.sum, !go.sum
  • vendor/github.com/longhorn/longhorn-share-manager/pkg/client/share_manager_client.go is excluded by !vendor/**
  • vendor/github.com/longhorn/types/pkg/generated/smrpc/smrpc.pb.go is excluded by !**/*.pb.go, !**/generated/**, !vendor/**
  • vendor/github.com/longhorn/types/pkg/generated/smrpc/smrpc_grpc.pb.go is excluded by !**/*.pb.go, !**/generated/**, !vendor/**
  • vendor/modules.txt is excluded by !vendor/**
📒 Files selected for processing (3)
  • csi/deployment.go (1 hunks)
  • csi/node_server.go (3 hunks)
  • engineapi/share_manager.go (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (2)
  • csi/deployment.go
  • engineapi/share_manager.go
🔇 Additional comments (3)
csi/node_server.go (3)

698-751: LGTM! Well-structured implementation with good error handling.

The implementation is clean and follows good practices:

  • Proper error handling and wrapping
  • Resource cleanup with defer client.Close()
  • Clear comments explaining the implementation decisions

793-804: LGTM! Clean integration of RWX volume expansion support.

The changes properly integrate RWX volume expansion by:

  • Validating volume access mode
  • Delegating to NodeExpandSharedVolume for RWX volumes
  • Maintaining existing behavior for other volume types

698-751: Verify potential race conditions in concurrent expansion requests.

Since multiple nodes can trigger expansion simultaneously, let's verify the handling of concurrent requests.

✅ Verification successful

Concurrent expansion requests are safely handled by design

The current implementation is safe for concurrent expansion requests because:

  1. The FilesystemResize operation is idempotent by nature - multiple resize attempts on an already resized filesystem are harmless
  2. The comment in the code explicitly acknowledges this: "Each node with a workload pod will send an RPC request. The first will win, and the others are no-ops"
  3. The filesystem resize operation is atomic at the filesystem level, making concurrent calls safe
🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Check for potential race conditions in volume expansion

# Look for any error handling or synchronization mechanisms around FilesystemResize
rg -A 5 "FilesystemResize" 

# Check for any existing issue reports about concurrent expansion
gh issue list --search "RWX concurrent expansion"

Length of output: 11257


Script:

#!/bin/bash
# Let's check the share manager server implementation to understand how concurrent FilesystemResize requests are handled

# Search for the server-side implementation of FilesystemResize
ast-grep --pattern 'func $_(ctx context.Context, _ *emptypb.Empty) (*emptypb.Empty, error) {
  $$$
  FilesystemResize
  $$$
}'

# Look for any locking mechanisms or synchronization in the share manager
rg -A 5 "sync\.Mutex|sync\.RWMutex" vendor/github.com/longhorn/longhorn-share-manager/

Length of output: 217


Script:

#!/bin/bash
# Let's check the share manager implementation in a different way

# Look for the share manager service implementation
rg -g "!vendor/*" -g "!*.pb.go" -A 10 "type.*ShareManager.*Service.*struct"

# Check for any filesystem resize implementation
rg -g "!vendor/*" -g "!*.pb.go" -A 10 "func.*[Ff]ilesystem[Rr]esize"

# Look for any resize-related locks or synchronization
rg -g "!vendor/*" "sync\.(Mutex|RWMutex).*(resize|expand|filesystem)"

Length of output: 773


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.

@james-munson james-munson marked this pull request as draft November 11, 2024 21:16
@james-munson james-munson force-pushed the 9736-rwx-resize branch 3 times, most recently from 8a2ae97 to 077ce60 Compare November 15, 2024 00:29
@james-munson james-munson marked this pull request as ready for review November 15, 2024 00:29
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: 1

🧹 Outside diff range and nitpick comments (3)
engineapi/share_manager.go (1)

42-44: Consider enhancing error handling and documentation

While the implementation is functionally correct, consider these improvements:

  1. Wrap the error with context for better debugging
  2. Add method documentation explaining its purpose and usage

Consider applying this enhancement:

+// FilesystemResize triggers an online filesystem resize operation through the share-manager.
+// This is specifically used for expanding ReadWriteMany (RWX) volumes.
 func (c *ShareManagerClient) FilesystemResize() error {
-	return c.grpcClient.FilesystemResize()
+	if err := c.grpcClient.FilesystemResize(); err != nil {
+		return errors.Wrap(err, "failed to resize filesystem via share-manager")
+	}
+	return nil
 }
csi/node_server.go (2)

708-722: Consider reusing Kubernetes clients to enhance performance

In the NodeExpandSharedVolume function, Kubernetes clients (kubeClient and lhClient) are created on each invocation. This can introduce unnecessary overhead. Consider initializing these clients once during the NodeServer initialization and storing them in the NodeServer struct for reuse.


723-733: Use context.Background() instead of context.TODO()

When making API calls to Kubernetes resources, it's preferable to use context.Background() as the base context in production code.

Apply this diff:

- sm, err := lhClient.LonghornV1beta2().ShareManagers(lhNamespace).Get(context.TODO(), volumeName, metav1.GetOptions{})
+ sm, err := lhClient.LonghornV1beta2().ShareManagers(lhNamespace).Get(context.Background(), volumeName, metav1.GetOptions{})

- pod, err := kubeClient.CoreV1().Pods(lhNamespace).Get(context.TODO(), podName, metav1.GetOptions{})
+ pod, err := kubeClient.CoreV1().Pods(lhNamespace).Get(context.Background(), podName, metav1.GetOptions{})
📜 Review details

Configuration used: .coderabbit.yaml
Review profile: CHILL

📥 Commits

Reviewing files that changed from the base of the PR and between 470b53c and 077ce60.

⛔ Files ignored due to path filters (6)
  • go.mod is excluded by !go.mod
  • go.sum is excluded by !**/*.sum, !go.sum
  • vendor/github.com/longhorn/longhorn-share-manager/pkg/client/share_manager_client.go is excluded by !vendor/**
  • vendor/github.com/longhorn/types/pkg/generated/smrpc/smrpc.pb.go is excluded by !**/*.pb.go, !**/generated/**, !vendor/**
  • vendor/github.com/longhorn/types/pkg/generated/smrpc/smrpc_grpc.pb.go is excluded by !**/*.pb.go, !**/generated/**, !vendor/**
  • vendor/modules.txt is excluded by !vendor/**
📒 Files selected for processing (3)
  • csi/deployment.go (1 hunks)
  • csi/node_server.go (3 hunks)
  • engineapi/share_manager.go (1 hunks)
🔇 Additional comments (2)
engineapi/share_manager.go (1)

42-44: Implementation aligns with PR objectives

The new FilesystemResize method correctly delegates the resize operation to the share-manager via gRPC, which addresses the core issue of RWX PVC filesystem resize failures.

csi/deployment.go (1)

421-428: LGTM! The POD_NAMESPACE environment variable is correctly implemented.

The addition of the POD_NAMESPACE environment variable is well-structured and necessary for enabling the CSI plugin to interact with share-manager pods during RWX volume resize operations.

Let's verify the integration with share-manager:

✅ Verification successful

The POD_NAMESPACE environment variable is correctly implemented and used for RWX volume resizing

The verification results confirm that:

  1. The POD_NAMESPACE environment variable is used in NodeExpandSharedVolume to get the namespace for share-manager pod communication
  2. The implementation correctly uses the environment variable via types.EnvPodNamespace
  3. The share-manager integration is properly implemented with pod naming conventions and namespace-aware operations
🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Verify that share-manager pods are deployed in the same namespace
# and the CSI plugin can access them for resize operations.

# Check if share-manager pods exist in the same namespace as CSI plugin
rg -A 5 "share-manager.*Pod" 

# Verify the usage of POD_NAMESPACE in resize-related code
ast-grep --pattern 'NodeExpandSharedVolume($$$) {
  $$$
  POD_NAMESPACE
  $$$
}'

Length of output: 113


Script:

#!/bin/bash
# Let's try a different approach to verify the integration

# Check for share-manager related code
rg -g '*.go' -B 2 -A 2 'share-manager'

# Look for NodeExpandSharedVolume implementation
rg -g '*.go' -B 2 -A 5 'NodeExpandSharedVolume'

# Search for POD_NAMESPACE usage in the codebase
rg -g '*.go' 'POD_NAMESPACE'

# Check for share-manager pod related code
rg -g '*.go' 'ShareManagerPod'

Length of output: 17106

csi/node_server.go Show resolved Hide resolved
Copy link

mergify bot commented Nov 17, 2024

This pull request is now in conflict. Could you fix it @james-munson? 🙏

@james-munson james-munson self-assigned this Nov 20, 2024
Copy link
Contributor

@PhanLe1010 PhanLe1010 left a comment

Choose a reason for hiding this comment

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

I have some concerns:

  1. As we mentioned in the sync up, we should double check network policy for the new connection from Longhorn CSI plugin to share manager
  2. With this design, we can only finish resizing when the RWX PVC is attached, right? Because only when RWX PVC is attached and used by workload then we have share manager pod. What about offline RWX PVC expansion. Do you have any idea?

Comment on lines +703 to +737
lhNamespace := os.Getenv(types.EnvPodNamespace)
if lhNamespace == "" {
return fmt.Errorf("failed to detect pod namespace, environment variable %v is missing", types.EnvPodNamespace)
}

config, err := rest.InClusterConfig()
if err != nil {
return errors.Wrap(err, "failed to get client config")
}

kubeClient, err := clientset.NewForConfig(config)
if err != nil {
return errors.Wrap(err, "failed to get k8s client")
}

lhClient, err := lhclientset.NewForConfig(config)
if err != nil {
return errors.Wrap(err, "failed to get longhorn clientset")
}

sm, err := lhClient.LonghornV1beta2().ShareManagers(lhNamespace).Get(context.TODO(), volumeName, metav1.GetOptions{})
if err != nil {
return errors.Wrap(err, "failed to get ShareManager CR")
}

podName := types.GetShareManagerPodNameFromShareManagerName(sm.Name)
pod, err := kubeClient.CoreV1().Pods(lhNamespace).Get(context.TODO(), podName, metav1.GetOptions{})
if err != nil {
return errors.Wrap(err, "failed to get ShareManager pod")
}

client, err := engineapi.NewShareManagerClient(sm, pod)
if err != nil {
return errors.Wrapf(err, "failed to launch gRPC client for share manager before resizing volume %v", volumeName)
}
Copy link
Contributor

Choose a reason for hiding this comment

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

I think we can get IP of share manager service by accessing volume.ShareEndpoint without having to look up the share manager pod?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Looks like it is not the same IP address, though.

Copy link
Contributor

Choose a reason for hiding this comment

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

Yes, volume.ShareEndpoint is the IP of the service which should eventually forward to the pod

Comment on lines +421 to +428
{
Name: "POD_NAMESPACE",
ValueFrom: &corev1.EnvVarSource{
FieldRef: &corev1.ObjectFieldSelector{
FieldPath: "metadata.namespace",
},
},
},
Copy link
Contributor

Choose a reason for hiding this comment

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

Maybe we don't need to add namespace if we don't need to look up the share manager pod as mentioned below

@james-munson
Copy link
Contributor Author

I have some concerns:

1. As we mentioned in the sync up, we should double check network policy for the new connection from Longhorn CSI plugin to share manager

2. With this design, we can only finish resizing when the RWX PVC is attached, right? Because only when RWX PVC is attached and used by workload then we have share manager pod. What about offline RWX PVC expansion. Do you have any idea?

Checking on the first. My test cluster has no network policies, except for a cattle-fleet-system one that allows all in its namespace.

For the second, offline expansion works like it formerly did. The resize will happen when the share-manager pod is started and mounts the volume. That's why the code for resize was already there, https://github.com/longhorn/longhorn-share-manager/blob/5b2d717e3ca35a727fbaa628af478af4da535fcf/pkg/server/share_manager.go#L143

@PhanLe1010
Copy link
Contributor

Checking on the first. My test cluster has no network policies, except for a cattle-fleet-system one that allows all in its namespace.

Thank you

For the second, offline expansion works like it formerly did. The resize will happen when the share-manager pod is started and mounts the volume. That's why the code for resize was already there, https://github.com/longhorn/longhorn-share-manager/blob/5b2d717e3ca35a727fbaa628af478af4da535fcf/pkg/server/share_manager.go#L143

So in the meantime, the PVC will stuck in pending expansion until the workload start? Is it same behavior as RWO PVC?
Could we add a test case for this scenario? Thank you

@james-munson
Copy link
Contributor Author

So in the meantime, the PVC will stuck in pending expansion until the workload start? Is it same behavior as RWO PVC?
Could we add a test case for this scenario?

As we resolved, there is a "pending expansion" in either case, because the filesystem grow is accomplished in NodeStageVolume. However, there is no lingering Condition about it.

There already is an automated test case for offline expansion, since that was the only way to do it automatically. There's just not one for online, since it had a manual step.

@james-munson
Copy link
Contributor Author

For the point about network policy, we agreed that it does not look to be necessary for this functionality. Better to keep things simple.

Copy link

mergify bot commented Nov 23, 2024

This pull request is now in conflict. Could you fix it @james-munson? 🙏

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