const reconcilerLoopPeriod … const syncLoopPeriod … const maxWaitForUnmountDuration … const maxLongWaitForUnmountDuration … const volumeAttachedCheckTimeout … var registerMetrics … // Calls Run() // Verifies there are no calls to attach or detach. func Test_Run_Positive_DoNothing(t *testing.T) { … } // Populates desiredStateOfWorld cache with one node/volume/pod tuple. // Calls Run() // Verifies there is one attach call and no detach calls. func Test_Run_Positive_OneDesiredVolumeAttach(t *testing.T) { … } // Populates desiredStateOfWorld cache with one node/volume/pod tuple. // Calls Run() // Verifies there is one attach call and no detach calls. // Marks the node/volume as unmounted. // Deletes the node/volume/pod tuple from desiredStateOfWorld cache. // Verifies there is one detach call and no (new) attach calls. func Test_Run_Positive_OneDesiredVolumeAttachThenDetachWithUnmountedVolume(t *testing.T) { … } // Populates desiredStateOfWorld cache with one node/volume/pod tuple. // Calls Run() // Verifies there is one attach call and no detach calls. // Deletes the node/volume/pod tuple from desiredStateOfWorld cache without first marking the node/volume as unmounted. // Verifies there is one detach call and no (new) attach calls. func Test_Run_Positive_OneDesiredVolumeAttachThenDetachWithMountedVolume(t *testing.T) { … } // Populates desiredStateOfWorld cache with one node/volume/pod tuple. // Has node update fail // Calls Run() // Verifies there is one attach call and no detach calls. // Marks the node/volume as unmounted. // Deletes the node/volume/pod tuple from desiredStateOfWorld cache. // Verifies there are NO detach call and no (new) attach calls. func Test_Run_Negative_OneDesiredVolumeAttachThenDetachWithUnmountedVolumeUpdateStatusFail(t *testing.T) { … } // Creates a volume with accessMode ReadWriteMany // Populates desiredStateOfWorld cache with two node/volume/pod tuples pointing to the created volume // Calls Run() // Verifies there are two attach calls and no detach calls. // Deletes the first node/volume/pod tuple from desiredStateOfWorld cache without first marking the node/volume as unmounted. // Verifies there is one detach call and no (new) attach calls. // Deletes the second node/volume/pod tuple from desiredStateOfWorld cache without first marking the node/volume as unmounted. // Verifies there are two detach calls and no (new) attach calls. func Test_Run_OneVolumeAttachAndDetachMultipleNodesWithReadWriteMany(t *testing.T) { … } // Creates a volume with accessMode ReadWriteOnce // Populates desiredStateOfWorld cache with two ode/volume/pod tuples pointing to the created volume // Calls Run() // Verifies there is one attach call and no detach calls. // Deletes the node/volume/pod tuple from desiredStateOfWorld which succeeded in attaching // Verifies there are two attach call and one detach call. func Test_Run_OneVolumeAttachAndDetachMultipleNodesWithReadWriteOnce(t *testing.T) { … } // Creates a volume with accessMode ReadWriteOnce // First create a pod which will try to attach the volume to the a node named "uncertain-node". The attach call for this node will // fail for timeout, but the volume will be actually attached to the node after the call. // Secondly, delete this pod. // Lastly, create a pod scheduled to a normal node which will trigger attach volume to the node. The attach should return successfully. func Test_Run_OneVolumeAttachAndDetachUncertainNodesWithReadWriteOnce(t *testing.T) { … } func Test_Run_UpdateNodeStatusFailBeforeOneVolumeDetachNodeWithReadWriteOnce(t *testing.T) { … } func Test_Run_OneVolumeDetachFailNodeWithReadWriteOnce(t *testing.T) { … } // Creates a volume with accessMode ReadWriteOnce // First create a pod which will try to attach the volume to the a node named "timeout-node". The attach call for this node will // fail for timeout, but the volume will be actually attached to the node after the call. // Secondly, delete the this pod. // Lastly, create a pod scheduled to a normal node which will trigger attach volume to the node. The attach should return successfully. func Test_Run_OneVolumeAttachAndDetachTimeoutNodesWithReadWriteOnce(t *testing.T) { … } // Populates desiredStateOfWorld cache with one node/volume/pod tuple. // The node has node.kubernetes.io/out-of-service taint present. // // The maxWaitForUnmountDuration is longer (in this case it is 4200 * time.Second so that detach does not happen // immediately due to timeout. // // Calls Run() // Verifies there is one attach call and no detach calls. // Deletes the pod from desiredStateOfWorld cache without first marking the node/volume as unmounted. // Verifies there is one detach call and no (new) attach calls. func Test_Run_OneVolumeDetachOnOutOfServiceTaintedNode(t *testing.T) { … } // Populates desiredStateOfWorld cache with one node/volume/pod tuple. // The node does not have the node.kubernetes.io/out-of-service taint present. // // The maxWaitForUnmountDuration is longer (in this case it is 4200 * time.Second so that detach does not happen // immediately due to timeout. // // Calls Run() // Verifies there is one attach call and no detach calls. // Deletes the pod from desiredStateOfWorld cache without first marking the node/volume as unmounted. // Verifies there is no detach call and no (new) attach calls. func Test_Run_OneVolumeDetachOnNoOutOfServiceTaintedNode(t *testing.T) { … } // Populates desiredStateOfWorld cache with one node/volume/pod tuple. // The node starts as healthy. // // Calls Run() // Verifies there is one attach call and no detach calls. // Deletes the pod from desiredStateOfWorld cache without first marking the node/volume as unmounted. // Verifies that the volume is NOT detached after maxWaitForUnmountDuration. // Marks the node as unhealthy. // Verifies that the volume is detached after maxWaitForUnmountDuration. func Test_Run_OneVolumeDetachOnUnhealthyNode(t *testing.T) { … } // Populates desiredStateOfWorld cache with one node/volume/pod tuple. // The node starts as healthy. // // Calls Run() // Verifies there is one attach call and no detach calls. // Deletes the pod from desiredStateOfWorld cache without first marking the node/volume as unmounted. // Verifies that the volume is NOT detached after maxWaitForUnmountDuration. // Marks the node as unhealthy. // Sets forceDetachOnUmountDisabled to true. // Verifies that the volume is not detached after maxWaitForUnmountDuration. // // Then applies the node.kubernetes.io/out-of-service taint. // Verifies that there is still just one attach call. // Verifies there is now one detach call. func Test_Run_OneVolumeDetachOnUnhealthyNodeWithForceDetachOnUnmountDisabled(t *testing.T) { … } func Test_ReportMultiAttachError(t *testing.T) { … } func waitForMultiAttachErrorOnNode( t *testing.T, attachedNode k8stypes.NodeName, dsow cache.DesiredStateOfWorld) { … } func waitForNewAttacherCallCount( t *testing.T, expectedCallCount int, fakePlugin *volumetesting.FakeVolumePlugin) { … } func waitForNewDetacherCallCount( t *testing.T, expectedCallCount int, fakePlugin *volumetesting.FakeVolumePlugin) { … } func waitForAttachCallCount( t *testing.T, expectedAttachCallCount int, fakePlugin *volumetesting.FakeVolumePlugin) { … } func waitForTotalAttachCallCount( t *testing.T, expectedAttachCallCount int, fakePlugin *volumetesting.FakeVolumePlugin) { … } func waitForDetachCallCount( t *testing.T, expectedDetachCallCount int, fakePlugin *volumetesting.FakeVolumePlugin) { … } func waitForTotalDetachCallCount( t *testing.T, expectedDetachCallCount int, fakePlugin *volumetesting.FakeVolumePlugin) { … } func waitForAttachedToNodesCount( t *testing.T, expectedNodeCount int, volumeName v1.UniqueVolumeName, asw cache.ActualStateOfWorld) { … } func verifyNewAttacherCallCount( t *testing.T, expectZeroNewAttacherCallCount bool, fakePlugin *volumetesting.FakeVolumePlugin) { … } func waitForVolumeAttachStateToNode( t *testing.T, volumeName v1.UniqueVolumeName, nodeName k8stypes.NodeName, expectedAttachState cache.AttachState, asw cache.ActualStateOfWorld) { … } func waitForVolumeAddedToNode( t *testing.T, volumeName v1.UniqueVolumeName, nodeName k8stypes.NodeName, asw cache.ActualStateOfWorld) { … } func waitForVolumeRemovedFromNode( t *testing.T, volumeName v1.UniqueVolumeName, nodeName k8stypes.NodeName, asw cache.ActualStateOfWorld) { … } func verifyVolumeAttachedToNode( t *testing.T, volumeName v1.UniqueVolumeName, nodeName k8stypes.NodeName, expectedAttachState cache.AttachState, asw cache.ActualStateOfWorld, ) { … } func verifyVolumeReportedAsAttachedToNode( t *testing.T, logger klog.Logger, volumeName v1.UniqueVolumeName, nodeName k8stypes.NodeName, isAttached bool, asw cache.ActualStateOfWorld, timeout time.Duration, ) { … } func verifyVolumeNoStatusUpdateNeeded( t *testing.T, logger klog.Logger, volumeName v1.UniqueVolumeName, nodeName k8stypes.NodeName, asw cache.ActualStateOfWorld, ) { … } func verifyNewDetacherCallCount( t *testing.T, expectZeroNewDetacherCallCount bool, fakePlugin *volumetesting.FakeVolumePlugin) { … } func retryWithExponentialBackOff(initialDuration time.Duration, fn wait.ConditionFunc) error { … } // verifies the force detach metric with reason func testForceDetachMetric(t *testing.T, inputForceDetachMetricCounter int, reason string) { … }