@@ -235,6 +235,7 @@ func Test_Run_Positive_OneDesiredVolumeAttachThenDetachWithMountedVolume(t *test
235
235
volumeSpec := controllervolumetesting .GetTestVolumeSpec (string (volumeName ), volumeName )
236
236
nodeName := k8stypes .NodeName ("node-name" )
237
237
dsw .AddNode (nodeName , false /*keepTerminatedPodVolumes*/ )
238
+
238
239
volumeExists := dsw .VolumeExists (volumeName , nodeName )
239
240
if volumeExists {
240
241
t .Fatalf (
@@ -936,6 +937,102 @@ func Test_Run_OneVolumeDetachOnNoOutOfServiceTaintedNode(t *testing.T) {
936
937
waitForDetachCallCount (t , 0 /* expectedDetachCallCount */ , fakePlugin )
937
938
}
938
939
940
+ // Populates desiredStateOfWorld cache with one node/volume/pod tuple.
941
+ // The node starts as healthy.
942
+ //
943
+ // Calls Run()
944
+ // Verifies there is one attach call and no detach calls.
945
+ // Deletes the pod from desiredStateOfWorld cache without first marking the node/volume as unmounted.
946
+ // Verifies that the volume is NOT detached after maxWaitForUnmountDuration.
947
+ // Marks the node as unhealthy.
948
+ // Verifies that the volume is detached after maxWaitForUnmountDuration.
949
+ func Test_Run_OneVolumeDetachOnUnhealthyNode (t * testing.T ) {
950
+ // Arrange
951
+ volumePluginMgr , fakePlugin := volumetesting .GetTestVolumePluginMgr (t )
952
+ dsw := cache .NewDesiredStateOfWorld (volumePluginMgr )
953
+ asw := cache .NewActualStateOfWorld (volumePluginMgr )
954
+ fakeKubeClient := controllervolumetesting .CreateTestClient ()
955
+ fakeRecorder := & record.FakeRecorder {}
956
+ fakeHandler := volumetesting .NewBlockVolumePathHandler ()
957
+ ad := operationexecutor .NewOperationExecutor (operationexecutor .NewOperationGenerator (
958
+ fakeKubeClient ,
959
+ volumePluginMgr ,
960
+ fakeRecorder ,
961
+ fakeHandler ))
962
+ informerFactory := informers .NewSharedInformerFactory (fakeKubeClient , controller .NoResyncPeriodFunc ())
963
+ nsu := statusupdater .NewFakeNodeStatusUpdater (false /* returnError */ )
964
+ nodeLister := informerFactory .Core ().V1 ().Nodes ().Lister ()
965
+ reconciler := NewReconciler (
966
+ reconcilerLoopPeriod , maxWaitForUnmountDuration , syncLoopPeriod , false , dsw , asw , ad ,
967
+ nsu , nodeLister , fakeRecorder )
968
+ podName1 := "pod-uid1"
969
+ volumeName1 := v1 .UniqueVolumeName ("volume-name1" )
970
+ volumeSpec1 := controllervolumetesting .GetTestVolumeSpec (string (volumeName1 ), volumeName1 )
971
+ nodeName1 := k8stypes .NodeName ("worker-0" )
972
+ node1 := & v1.Node {
973
+ ObjectMeta : metav1.ObjectMeta {Name : string (nodeName1 )},
974
+ Status : v1.NodeStatus {
975
+ Conditions : []v1.NodeCondition {
976
+ {
977
+ Type : v1 .NodeReady ,
978
+ Status : v1 .ConditionTrue ,
979
+ },
980
+ },
981
+ },
982
+ }
983
+ informerFactory .Core ().V1 ().Nodes ().Informer ().GetStore ().Add (node1 )
984
+ dsw .AddNode (nodeName1 , false /*keepTerminatedPodVolumes*/ )
985
+ volumeExists := dsw .VolumeExists (volumeName1 , nodeName1 )
986
+ if volumeExists {
987
+ t .Fatalf (
988
+ "Volume %q/node %q should not exist, but it does." ,
989
+ volumeName1 ,
990
+ nodeName1 )
991
+ }
992
+
993
+ generatedVolumeName , podErr := dsw .AddPod (types .UniquePodName (podName1 ), controllervolumetesting .NewPod (podName1 ,
994
+ podName1 ), volumeSpec1 , nodeName1 )
995
+ if podErr != nil {
996
+ t .Fatalf ("AddPod failed. Expected: <no error> Actual: <%v>" , podErr )
997
+ }
998
+
999
+ // Act
1000
+ ch := make (chan struct {})
1001
+ go reconciler .Run (ch )
1002
+ defer close (ch )
1003
+
1004
+ // Assert
1005
+ waitForNewAttacherCallCount (t , 1 /* expectedCallCount */ , fakePlugin )
1006
+ verifyNewAttacherCallCount (t , false /* expectZeroNewAttacherCallCount */ , fakePlugin )
1007
+ waitForAttachCallCount (t , 1 /* expectedAttachCallCount */ , fakePlugin )
1008
+ verifyNewDetacherCallCount (t , true /* expectZeroNewDetacherCallCount */ , fakePlugin )
1009
+ waitForDetachCallCount (t , 0 /* expectedDetachCallCount */ , fakePlugin )
1010
+
1011
+ // Act
1012
+ // Delete the pod and the volume will be detached even after the maxWaitForUnmountDuration expires as volume is
1013
+ // not unmounted and the node is healthy.
1014
+ dsw .DeletePod (types .UniquePodName (podName1 ), generatedVolumeName , nodeName1 )
1015
+ time .Sleep (maxWaitForUnmountDuration * 5 )
1016
+ // Assert
1017
+ waitForNewDetacherCallCount (t , 0 /* expectedCallCount */ , fakePlugin )
1018
+ verifyNewAttacherCallCount (t , false /* expectZeroNewAttacherCallCount */ , fakePlugin )
1019
+ waitForAttachCallCount (t , 1 /* expectedAttachCallCount */ , fakePlugin )
1020
+ verifyNewDetacherCallCount (t , true /* expectZeroNewDetacherCallCount */ , fakePlugin )
1021
+ waitForDetachCallCount (t , 0 /* expectedDetachCallCount */ , fakePlugin )
1022
+
1023
+ // Act
1024
+ // Mark the node unhealthy
1025
+ node2 := node1 .DeepCopy ()
1026
+ node2 .Status .Conditions [0 ].Status = v1 .ConditionFalse
1027
+ informerFactory .Core ().V1 ().Nodes ().Informer ().GetStore ().Update (node2 )
1028
+ // Assert -- Detach was triggered after maxWaitForUnmountDuration
1029
+ waitForNewDetacherCallCount (t , 1 /* expectedCallCount */ , fakePlugin )
1030
+ verifyNewAttacherCallCount (t , false /* expectZeroNewAttacherCallCount */ , fakePlugin )
1031
+ waitForAttachCallCount (t , 1 /* expectedAttachCallCount */ , fakePlugin )
1032
+ verifyNewDetacherCallCount (t , false /* expectZeroNewDetacherCallCount */ , fakePlugin )
1033
+ waitForDetachCallCount (t , 1 /* expectedDetachCallCount */ , fakePlugin )
1034
+ }
1035
+
939
1036
func Test_ReportMultiAttachError (t * testing.T ) {
940
1037
type nodeWithPods struct {
941
1038
name k8stypes.NodeName
0 commit comments