Repository: kubernetes/kubernetes (language: go)

Issue:
Failure cluster [160f0676...]: Error verifying UnMountDeviceCallCount: Expected DeviceUnmount Call 1, got 0
### Failure cluster [160f06764dc2af72bd12](https://go.k8s.io/triage#160f06764dc2af72bd12)

##### Error text:
```
Failed;Failed;
=== RUN   Test_UncertainDeviceGlobalMounts/timed_out_operations_should_result_in_device_marked_as_uncertain_[Filesystem][
=== PAUSE Test_UncertainDeviceGlobalMounts/timed_out_operations_should_result_in_device_marked_as_uncertain_[Filesystem][
=== CONT  Test_UncertainDeviceGlobalMounts/timed_out_operations_should_result_in_device_marked_as_uncertain_[Filesystem][
E1008 21:58:23.121079   43496 reconstruct.go:54] "Cannot get volumes from disk, skip sync states for volume reconstruction" err="open fake-dir-3e017bc9e2cc0330461d202a228a4439: no such file or directory"
I1008 21:58:23.121129   43496 reconciler.go:26] "Reconciler: start to sync state"
I1008 21:58:23.121285   43496 reconciler_common.go:251] "operationExecutor.VerifyControllerAttachedVolume started for volume \"timeout-mount-device-volume\" (UniqueName: \"fake-plugin/timeout-mount-device-volume\") pod \"pod1\" (UID: \"pod1uid\") " pod="pod1"
I1008 21:58:23.169125   43496 operation_generator.go:1491] "Controller attach succeeded for volume \"timeout-mount-device-volume\" (UniqueName: \"fake-plugin/timeout-mount-device-volume\") pod \"pod1\" (UID: \"pod1uid\") device path: \"fake/path\"" pod="pod1"
I1008 21:58:23.264405   43496 operation_generator.go:538] "MountVolume.WaitForAttach entering for volume \"timeout-mount-device-volume\" (UniqueName: \"fake-plugin/timeout-mount-device-volume\") pod \"pod1\" (UID: \"pod1uid\") DevicePath \"fake/path\"" pod="pod1"
I1008 21:58:23.264673   43496 operation_generator
```    reconciler_test.go:1654: Error verifying UnMountDeviceCallCount: Expected DeviceUnmount Call 1, got 0
#### Recent failures:
[10/8/2024, 11:42:09 PM ci-kubernetes-unit](https://prow.k8s.io/view/gs/kubernetes-ci-logs/logs/ci-kubernetes-unit/1843768670367518720)

/kind failing-test
/kind flake
/sig storage



Discussion / hints:
This issue is currently awaiting triage.

If a SIG or subproject determines this is a relevant issue, they will accept it by applying the `triage/accepted` label and provide further guidance.

The `triage/accepted` label can be added by org members by writing `/triage accepted` in a comment.


<details>

Instructions for interacting with me using PR comments are available [here](https://git.k8s.io/community/contributors/guide/pull-requests.md).  If you have questions or suggestions related to my behavior, please file an issue against the [kubernetes-sigs/prow](https://github.com/kubernetes-sigs/prow/issues/new?title=Prow%20issue:) repository.
</details>

Fix patch:
diff --git a/pkg/kubelet/volumemanager/cache/actual_state_of_world.go b/pkg/kubelet/volumemanager/cache/actual_state_of_world.go
index 78f8523be436f..746a7163c0795 100644
--- a/pkg/kubelet/volumemanager/cache/actual_state_of_world.go
+++ b/pkg/kubelet/volumemanager/cache/actual_state_of_world.go
@@ -176,6 +176,11 @@ type ActualStateOfWorld interface {
 	// or have a mount/unmount operation pending.
 	GetAttachedVolumes() []AttachedVolume
 
+	// GetAttachedVolume returns the volume that is known to be attached to the node
+	// with the given volume name. If the volume is not found, the second return value
+	// is false.
+	GetAttachedVolume(volumeName v1.UniqueVolumeName) (AttachedVolume, bool)
+
 	// Add the specified volume to ASW as uncertainly attached.
 	AddAttachUncertainReconstructedVolume(volumeName v1.UniqueVolumeName, volumeSpec *volume.Spec, nodeName types.NodeName, devicePath string) error
 
@@ -1160,6 +1165,18 @@ func (asw *actualStateOfWorld) GetAttachedVolumes() []AttachedVolume {
 	return allAttachedVolumes
 }
 
+func (asw *actualStateOfWorld) GetAttachedVolume(volumeName v1.UniqueVolumeName) (AttachedVolume, bool) {
+	asw.RLock()
+	defer asw.RUnlock()
+
+	volumeObj, ok := asw.attachedVolumes[volumeName]
+	if !ok {
+		return AttachedVolume{}, false
+	}
+
+	return asw.newAttachedVolume(&volumeObj), true
+}
+
 func (asw *actualStateOfWorld) GetUnmountedVolumes() []AttachedVolume {
 	asw.RLock()
 	defer asw.RUnlock()
diff --git a/pkg/kubelet/volumemanager/reconciler/reconciler_common.go b/pkg/kubelet/volumemanager/reconciler/reconciler_common.go
index cd22f78597ac9..628fffad8a26d 100644
--- a/pkg/kubelet/volumemanager/reconciler/reconciler_common.go
+++ b/pkg/kubelet/volumemanager/reconciler/reconciler_common.go
@@ -275,6 +275,11 @@ func (rc *reconciler) unmountDetachDevices() {
 		// Check IsOperationPending to avoid marking a volume as detached if it's in the process of mounting.
 		if !rc.desiredStateOfWorld.VolumeExists(attachedVolume.VolumeName, attachedVolume.SELinuxMountContext) &&
 			!rc.operationExecutor.IsOperationPending(attachedVolume.VolumeName, nestedpendingoperations.EmptyUniquePodName, nestedpendingoperations.EmptyNodeName) {
+
+			// Re-read the actual state of the world, maybe the volume got mounted in the meantime.
+			// This is safe, because there is no pending operation (checked above) and no new operation
+			// could start in the meantime. The only goroutine that adds new operations is this reconciler.
+			attachedVolume, _ = rc.actualStateOfWorld.GetAttachedVolume(attachedVolume.VolumeName)
 			if attachedVolume.DeviceMayBeMounted() {
 				// Volume is globally mounted to device, unmount it
 				klog.V(5).InfoS(attachedVolume.GenerateMsgDetailed("Starting operationExecutor.UnmountDevice", ""))


Test patch: