[cinder-csi-plugin] Wait for volume availability before attach#3124
[cinder-csi-plugin] Wait for volume availability before attach#3124hemna wants to merge 1 commit into
Conversation
|
[APPROVALNOTIFIER] This PR is NOT APPROVED This pull-request has been approved by: The full list of commands accepted by this bot can be found here. DetailsNeeds approval from an approver in each of these files:Approvers can indicate their approval by writing |
|
Hi @hemna. Thanks for your PR. I'm waiting for a kubernetes member to verify that this patch is reasonable to test. If it is, they should reply with Regular contributors should join the org to skip this step. Once the patch is verified, the new status will be reflected by the I understand the commands that are listed here. DetailsInstructions for interacting with me using PR comments are available here. If you have questions or suggestions related to my behavior, please file an issue against the kubernetes-sigs/prow repository. |
4e4519a to
35031dc
Compare
35031dc to
4396d1c
Compare
|
/ok-to-test |
|
/retest |
|
/retest openstack-cloud-csi-cinder-sanity-test |
|
The CI failure seems unrelated to my patch. k8s is killing the CI job. |
|
/retest |
1 similar comment
|
/retest |
kayrus
left a comment
There was a problem hiding this comment.
thanks for the PR, see my notes
|
|
||
| // volumeReadyPollInterval is the interval at which the volume status is | ||
| // polled when waiting for a volume to become available before attaching. | ||
| volumeReadyPollInterval = 3 * time.Second |
There was a problem hiding this comment.
does it make sense to make this variable configurable?
There was a problem hiding this comment.
added volume-status-poll-delay (in seconds) to BlockStorageOpts, defaulting to 3 seconds via VolumeStatusPollInterval().
| // context with a deadline (e.g., gRPC request context) and wants the wait | ||
| // to be bounded by that deadline rather than a fixed number of steps. | ||
| // The interval parameter controls how often the volume status is polled. | ||
| func (os *OpenStack) WaitVolumeTargetStatusWithContext(ctx context.Context, volumeID string, tStatus []string, interval time.Duration) error { |
There was a problem hiding this comment.
this method can be extended to return volume object, or even better, adjust the GetInstanceByID method to wait for the volume status. Currently the logic calls WaitVolumeTargetStatusWithContext, then GetInstanceByID, which does not make sense.
There was a problem hiding this comment.
Adjusted to only available is accepted for non-multiattach volumes. If a non-multiattach volume is already in-use, it fails immediately rather than waiting. Multiattach volumes accept both available and in-use.
| // "in-use" for multiattach) state. Without this wait, the driver would | ||
| // immediately hit a 409 Conflict if CreateVolume has just been called and | ||
| // the volume is still being provisioned on the backend. | ||
| targetStatus := []string{openstack.VolumeAvailableStatus, openstack.VolumeInUseStatus} |
There was a problem hiding this comment.
these target statuses should depend on multiattach feature turned on or off. in some cases, when cinder has issues and the volume is already in use, the CSI plugin will try to attach an in-use volume and get 409.
d77b6c3 to
2516855
Compare
| return false, fmt.Errorf("volume %s is already in-use and does not support multiattach", volumeID) | ||
| } | ||
|
|
||
| for _, eState := range openstack.VolumeErrorStates { |
There was a problem hiding this comment.
it makes sense to use the https://pkg.go.dev/slices#Contains
| if vol.Multiattach && vol.Status == openstack.VolumeInUseStatus { | ||
| return true, nil | ||
| } | ||
|
|
||
| // If a non-multiattach volume is already in-use, it cannot be | ||
| // attached again - fail immediately rather than waiting. | ||
| if !vol.Multiattach && vol.Status == openstack.VolumeInUseStatus { | ||
| return false, fmt.Errorf("volume %s is already in-use and does not support multiattach", volumeID) | ||
| } |
There was a problem hiding this comment.
I think this approach would be cleaner
| if vol.Multiattach && vol.Status == openstack.VolumeInUseStatus { | |
| return true, nil | |
| } | |
| // If a non-multiattach volume is already in-use, it cannot be | |
| // attached again - fail immediately rather than waiting. | |
| if !vol.Multiattach && vol.Status == openstack.VolumeInUseStatus { | |
| return false, fmt.Errorf("volume %s is already in-use and does not support multiattach", volumeID) | |
| } | |
| if vol.Status == openstack.VolumeInUseStatus { | |
| if vol.Multiattach { | |
| return true, nil | |
| } | |
| // If a non-multiattach volume is already in-use, it cannot be | |
| // attached again - fail immediately rather than waiting | |
| return false, fmt.Errorf("volume %s is already in-use and does not support multiattach", volumeID) | |
| } |
|
|
||
| _ = volume // volume is available for future use (e.g. multiattach validation) | ||
|
|
||
| _, err = cloud.GetInstanceByID(ctx, instanceID) |
There was a problem hiding this comment.
volumes.Get API call still happens here in GetInstanceByID after the waitForVolumeAttachable. Does it make sense to merge these two methods?
| } | ||
| } | ||
| for _, eState := range volumeErrorStates { | ||
| for _, eState := range VolumeErrorStates { |
There was a problem hiding this comment.
it makes sense to use the https://pkg.go.dev/slices#Contains
| RescanOnResize bool `gcfg:"rescan-on-resize"` | ||
| IgnoreVolumeAZ bool `gcfg:"ignore-volume-az"` | ||
| IgnoreVolumeMicroversion bool `gcfg:"ignore-volume-microversion"` | ||
| VolumeStatusPollDelay int `gcfg:"volume-status-poll-delay"` |
There was a problem hiding this comment.
the documentation must be updated accordingly with a supported time format example
2516855 to
075b611
Compare
The actual redundant |
ControllerPublishVolume now waits for the volume to reach a state valid for attachment before calling the Cinder attachment API. Previously, if the CO called ControllerPublishVolume immediately after CreateVolume, the volume could still be in 'creating' state on the backend. This caused Cinder to reject the attachment with a 409 Conflict, forcing the CO to retry blindly. The new waitForVolumeAttachable helper: - Polls the volume status using wait.PollUntilContextCancel, bounded by the gRPC context deadline (no fixed step count). - Returns immediately if the volume is already 'available'. - For multiattach volumes, also accepts 'in-use' as a valid state. - For non-multiattach volumes, fails immediately if the volume is already 'in-use' (cannot attach again). - Fails immediately if the volume enters an error state. - Returns the volume object, eliminating the redundant GetVolume call. - Returns FAILED_PRECONDITION on timeout, signaling the CO to retry with exponential backoff. The poll interval defaults to 3 seconds and is configurable via the 'volume-status-poll-delay' option in the [BlockStorage] config section.
075b611 to
f413b34
Compare
What this PR does / why we need it:
ControllerPublishVolumenow waits for the volume to reachavailableorin-usestatus before calling the Cinder attachment API.Previously, if the CO called
ControllerPublishVolumeimmediately afterCreateVolume, the volume could still be increatingstate on the backend. This caused Cinder to reject the attachment with a 409 Conflict:This forced the CO (external-attacher) to retry blindly, generating unnecessary API calls to Cinder and delaying volume attachment. The issue is most pronounced with storage backends where volume creation is asynchronous and takes several seconds (e.g., when volumes are backed by network-attached storage).
How the fix works:
A new
WaitVolumeTargetStatusWithContextmethod useswait.PollUntilContextCancelwhich:availableorin-useIf the context expires before the volume is ready, the driver returns
FAILED_PRECONDITIONwhich tells the CO to retry with exponential backoff.Which issue this PR fixes(if applicable):
fixes #
Special notes for reviewers:
The
volumeReadyPollIntervalis set to 3 seconds, which balances responsiveness against API load on the Cinder service. The total wait time is bounded by the gRPC context deadline (typically 15-30s depending on the external-attacher configuration), not a fixed backoff.The existing
WaitVolumeTargetStatus(used inControllerExpandVolume) is left unchanged to avoid disrupting existing behavior.Release note: