# Analysis of kubelet volume manager to understand what resources and their state would be post NodeUnstage is called for a CSI volume
## Reconciler: pkg/kubelet/volumemanager/reconciler/reconciler.go
The volumemanager reconciler invokes 3 routines, in order, to unmount, mountOrAttach, and detach devices. On analysis this is what is found:
- mountOrAttachVolumes creates a VolumeAttachement resource prior to staging the volume on the node via a NodeStageVolume CSI call
- Volumes are unmounted from a pod using CSI NodeUnpublishVolume calls
- Volumes are then detached from the node invoking a NodeUnstageVolume call
- Finally the VolumeAttachement resource is deleted for the volume on the node
As a result, to determine if a deleted PVC is no longer in use, we could:
- Wait for the PVC kubernetes.io/pvc-protection finalizer to be deleted
- Wait for all VolumeAttachements for the PV bound to the PVC
### Questions:
- Does any of this change across RWX and RWO volumes or Block mode volumes?
- Does the understanding look correct as per the analysis below?
## Code references/tracing to establish the above behaviour:
1. unmountVolumes
```
for _, mountedVolume := range rc.actualStateOfWorld.GetAllMountedVolumes() {
if !rc.desiredStateOfWorld.PodExistsInVolume(mountedVolume.PodName, mountedVolume.VolumeName) {
err := rc.operationExecutor.UnmountVolume(mountedVolume.MountedVolume, rc.actualStateOfWorld, rc.kubeletPodsDir)
}
}
```
1. rc.operationExecutor.UnmountVolume ---(invokes)---> GenerateUnmountVolumeFunc
- File: pkg/volume/util/operationexecutor/operation_generator.go
- This generates the following order of operations to execute,
```
volumeUnmounter.TearDown()
actualStateOfWorld.MarkVolumeAsUnmounted(volumeToUnmount.PodName, volumeToUnmount.VolumeName)
```
2. TearDown ---(invokes)---> TearownAt ---(invokes)---> NodeUnpublishVolume
- File: pkg/volume/csi/csi_mounter.go
3. MarkVolumeAsUnmounted ---(invokes)---> DeletePodFromVolume
- File: pkg/kubelet/volumemanager/cache/actual_state_of_world.go
- Remove pod from mountedPods of volume object `asw.attachedVolumes[volumeName]`
2. mountOrAttachVolumes
This basically performs the following in order as needed (conditionally),
- Create VolumeAttachment -> NodeStateVolume -> NodePublishVolume -> NodeUnpublishVolume -> NodeUnstageVolume -> delete VolumeAttachment
3. unmountDetachDevices
This is done in 2 phases, with phase one unstaging the volumes and the next stage deleting the VolumeAttachment:
```
for _, attachedVolume := range rc.actualStateOfWorld.GetUnmountedVolumes() {
if !rc.desiredStateOfWorld.VolumeExists(attachedVolume.VolumeName) &&
!rc.operationExecutor.IsOperationPending(attachedVolume.VolumeName, nestedpendingoperations.EmptyUniquePodName, nestedpendingoperations.EmptyNodeName) {
if attachedVolume.DeviceMayBeMounted() {
err := rc.operationExecutor.UnmountDevice(attachedVolume.AttachedVolume, rc.actualStateOfWorld, rc.hostutil)
} else {
if rc.controllerAttachDetachEnabled || !attachedVolume.PluginIsAttachable {
rc.actualStateOfWorld.MarkVolumeAsDetached(attachedVolume.VolumeName, attachedVolume.NodeName)
} else {
err := rc.operationExecutor.DetachVolume(attachedVolume.AttachedVolume, false /* verifySafeToDetach */, rc.actualStateOfWorld)
}
}
}
}
```
1. UnmountDevice ---(invokes)---> GenerateUnmountDeviceFunc
- This is the first phase to unstage the volume
- This generates the following order of operations to execute,
```
volumeDeviceUnmounter.UnmountDevice(deviceMountPath)
actualStateOfWorld.MarkDeviceAsUnmounted(deviceToDetach.VolumeName)
```
2. volumeDeviceUnmounter.UnmountDevice ---(invokes)---> NodeUnstageVolume
3. MarkDeviceAsUnmounted ---(invokes)---> SetDeviceMountState
- Marks volume object `asw.attachedVolumes[volumeName]` as unmounted, for detach to proceed in the next iteration
4. DetachVolume ---(invokes)---> GenerateDetachVolumeFunc
- This is the second phase to remove the VolumeAttachment
- This generates the following order of operations to execute,
```
volumeDetacher.Detach(volumeName, volumeToDetach.NodeName)
actualStateOfWorld.MarkVolumeAsDetached(volumeToDetach.VolumeName, volumeToDetach.NodeName)
```
5. volumeDetacher.Detach
- `c.k8s.StorageV1().VolumeAttachments().Delete(context.TODO(), attachID, metav1.DeleteOptions{})`
- `c.waitForVolumeDetachmentWithLister(volID, attachID, c.watchTimeout)` (analysis **TBD**)
6. MarkVolumeAsDetached
- Deletes the volume from `delete(asw.attachedVolumes, volumeName)`