// Populates data struct with new pod/volume1/node. // Calls VolumeExists() on that volume2/node. // Verifies volume2/node does not exist, and one volume to attach. func Test_VolumeExists_Positive_VolumeDoesntExistNodeExists(t *testing.T) { // Arrange volumePluginMgr, _ := volumetesting.GetTestVolumePluginMgr(t) dsw := NewDesiredStateOfWorld(volumePluginMgr) nodeName := k8stypes.NodeName("node-name") dsw.AddNode(nodeName) podName := "pod-uid" volume1Name := v1.UniqueVolumeName("volume1-name") volume1Spec := controllervolumetesting.GetTestVolumeSpec(string(volume1Name), volume1Name) generatedVolume1Name, podAddErr := dsw.AddPod(types.UniquePodName(podName), controllervolumetesting.NewPod(podName, podName), volume1Spec, nodeName) if podAddErr != nil { t.Fatalf( "AddPod failed for pod %q. Expected: <no error> Actual: <%v>", podName, podAddErr) } volume2Name := v1.UniqueVolumeName("volume2-name") // Act volumeExists := dsw.VolumeExists(volume2Name, nodeName) // Assert if volumeExists { t.Fatalf("Volume %q exists, it should not.", volume2Name) } volumesToAttach := dsw.GetVolumesToAttach() if len(volumesToAttach) != 1 { t.Fatalf("len(volumesToAttach) Expected: <1> Actual: <%v>", len(volumesToAttach)) } verifyVolumeToAttach(t, volumesToAttach, nodeName, generatedVolume1Name, string(volume1Name)) }
func Test_GetAttachedVolumesForNode_Positive_TwoVolumeTwoNodes(t *testing.T) { // Arrange volumePluginMgr, _ := volumetesting.GetTestVolumePluginMgr(t) asw := NewActualStateOfWorld(volumePluginMgr) volume1Name := v1.UniqueVolumeName("volume1-name") volume1Spec := controllervolumetesting.GetTestVolumeSpec(string(volume1Name), volume1Name) node1Name := types.NodeName("node1-name") devicePath := "fake/device/path" _, add1Err := asw.AddVolumeNode(volume1Spec, node1Name, devicePath) if add1Err != nil { t.Fatalf("AddVolumeNode failed. Expected: <no error> Actual: <%v>", add1Err) } volume2Name := v1.UniqueVolumeName("volume2-name") volume2Spec := controllervolumetesting.GetTestVolumeSpec(string(volume2Name), volume2Name) node2Name := types.NodeName("node2-name") generatedVolumeName2, add2Err := asw.AddVolumeNode(volume2Spec, node2Name, devicePath) if add2Err != nil { t.Fatalf("AddVolumeNode failed. Expected: <no error> Actual: <%v>", add2Err) } // Act attachedVolumes := asw.GetAttachedVolumesForNode(node2Name) // Assert if len(attachedVolumes) != 1 { t.Fatalf("len(attachedVolumes) Expected: <1> Actual: <%v>", len(attachedVolumes)) } verifyAttachedVolume(t, attachedVolumes, generatedVolumeName2, string(volume2Name), node2Name, devicePath, true /* expectedMountedByNode */, false /* expectNonZeroDetachRequestedTime */) }
func TestOperationExecutor_UnmountVolume_ConcurrentUnmountForAllPlugins(t *testing.T) { // Arrange ch, quit, oe := setup() volumesToUnmount := make([]MountedVolume, numAttachableVolumesToUnmount+numNonAttachableVolumesToUnmount) pdName := "pd-volume" secretName := "secret-volume" // Act for i := 0; i < numNonAttachableVolumesToUnmount+numAttachableVolumesToUnmount; i++ { podName := "pod-" + strconv.Itoa(i+1) if i < numNonAttachableVolumesToUnmount { pod := getTestPodWithSecret(podName, secretName) volumesToUnmount[i] = MountedVolume{ PodName: volumetypes.UniquePodName(podName), VolumeName: v1.UniqueVolumeName(secretName), PodUID: pod.UID, } } else { pod := getTestPodWithGCEPD(podName, pdName) volumesToUnmount[i] = MountedVolume{ PodName: volumetypes.UniquePodName(podName), VolumeName: v1.UniqueVolumeName(pdName), PodUID: pod.UID, } } oe.UnmountVolume(volumesToUnmount[i], nil /* actualStateOfWorldMounterUpdater */) } // Assert if !isOperationRunConcurrently(ch, quit, numNonAttachableVolumesToUnmount+numAttachableVolumesToUnmount) { t.Fatalf("Unable to start unmount operations concurrently for volume plugins") } }
// Populates data struct with two nodes with one volume/pod each and an extra // pod for the second node/volume pair. // Calls GetVolumesToAttach() // Verifies two volumes to attach. func Test_GetVolumesToAttach_Positive_TwoNodesOneVolumeEachExtraPod(t *testing.T) { // Arrange volumePluginMgr, _ := volumetesting.GetTestVolumePluginMgr(t) dsw := NewDesiredStateOfWorld(volumePluginMgr) node1Name := k8stypes.NodeName("node1-name") pod1Name := "pod1-uid" volume1Name := v1.UniqueVolumeName("volume1-name") volume1Spec := controllervolumetesting.GetTestVolumeSpec(string(volume1Name), volume1Name) dsw.AddNode(node1Name) generatedVolume1Name, podAddErr := dsw.AddPod(types.UniquePodName(pod1Name), controllervolumetesting.NewPod(pod1Name, pod1Name), volume1Spec, node1Name) if podAddErr != nil { t.Fatalf( "AddPod failed for pod %q. Expected: <no error> Actual: <%v>", pod1Name, podAddErr) } node2Name := k8stypes.NodeName("node2-name") pod2Name := "pod2-uid" volume2Name := v1.UniqueVolumeName("volume2-name") volume2Spec := controllervolumetesting.GetTestVolumeSpec(string(volume2Name), volume2Name) dsw.AddNode(node2Name) generatedVolume2Name, podAddErr := dsw.AddPod(types.UniquePodName(pod2Name), controllervolumetesting.NewPod(pod2Name, pod2Name), volume2Spec, node2Name) if podAddErr != nil { t.Fatalf( "AddPod failed for pod %q. Expected: <no error> Actual: <%v>", pod2Name, podAddErr) } pod3Name := "pod3-uid" dsw.AddPod(types.UniquePodName(pod3Name), controllervolumetesting.NewPod(pod3Name, pod3Name), volume2Spec, node2Name) _, podAddErr = dsw.AddPod(types.UniquePodName(pod3Name), controllervolumetesting.NewPod(pod3Name, pod3Name), volume2Spec, node2Name) if podAddErr != nil { t.Fatalf( "AddPod failed for pod %q. Expected: <no error> Actual: <%v>", pod3Name, podAddErr) } // Act volumesToAttach := dsw.GetVolumesToAttach() // Assert if len(volumesToAttach) != 2 { t.Fatalf("len(volumesToAttach) Expected: <2> Actual: <%v>", len(volumesToAttach)) } verifyVolumeToAttach(t, volumesToAttach, node1Name, generatedVolume1Name, string(volume1Name)) verifyVolumeToAttach(t, volumesToAttach, node2Name, generatedVolume2Name, string(volume2Name)) }
// Populates data struct with one volume/node entry. // Calls SetVolumeMountedByNode once, setting mounted to false. // Verifies mountedByNode is still true (since there was no SetVolumeMountedByNode to true call first) func Test_SetVolumeMountedByNode_Positive_UnsetWithoutInitialSet(t *testing.T) { // Arrange volumePluginMgr, _ := volumetesting.GetTestVolumePluginMgr(t) asw := NewActualStateOfWorld(volumePluginMgr) volumeName := v1.UniqueVolumeName("volume-name") volumeSpec := controllervolumetesting.GetTestVolumeSpec(string(volumeName), volumeName) nodeName := types.NodeName("node-name") devicePath := "fake/device/path" generatedVolumeName, addErr := asw.AddVolumeNode(volumeSpec, nodeName, devicePath) if addErr != nil { t.Fatalf("AddVolumeNode failed. Expected: <no error> Actual: <%v>", addErr) } // Act setVolumeMountedErr := asw.SetVolumeMountedByNode(generatedVolumeName, nodeName, false /* mounted */) // Assert if setVolumeMountedErr != nil { t.Fatalf("SetVolumeMountedByNode failed. Expected <no error> Actual: <%v>", setVolumeMountedErr) } attachedVolumes := asw.GetAttachedVolumes() if len(attachedVolumes) != 1 { t.Fatalf("len(attachedVolumes) Expected: <1> Actual: <%v>", len(attachedVolumes)) } verifyAttachedVolume(t, attachedVolumes, generatedVolumeName, string(volumeName), nodeName, devicePath, true /* expectedMountedByNode */, false /* expectNonZeroDetachRequestedTime */) }
func Test_NewGoRoutineMap_Positive_SecondOpAfterFirstPanicsWithExpBackoff(t *testing.T) { // Arrange grm := NewNestedPendingOperations(true /* exponentialBackOffOnError */) volumeName := v1.UniqueVolumeName("volume-name") operation1 := generatePanicFunc() err1 := grm.Run(volumeName, "" /* operationSubName */, operation1) if err1 != nil { t.Fatalf("NewGoRoutine failed. Expected: <no error> Actual: <%v>", err1) } operation2 := generateNoopFunc() // Act err2 := retryWithExponentialBackOff( time.Duration(initialOperationWaitTimeLong), // Longer duration to accommodate for backoff func() (bool, error) { err := grm.Run(volumeName, "" /* operationSubName */, operation2) if err != nil { t.Logf("Warning: NewGoRoutine failed with %v. Will retry.", err) return false, nil } return true, nil }, ) // Assert if err2 != nil { t.Fatalf("NewGoRoutine failed. Expected: <no error> Actual: <%v>", err2) } }
func Test_NewGoRoutineMap_Positive_SecondOpAfterFirstCompletesWithExpBackoff(t *testing.T) { // Arrange grm := NewNestedPendingOperations(true /* exponentialBackOffOnError */) volumeName := v1.UniqueVolumeName("volume-name") operation1DoneCh := make(chan interface{}, 0 /* bufferSize */) operation1 := generateCallbackFunc(operation1DoneCh) err1 := grm.Run(volumeName, "" /* operationSubName */, operation1) if err1 != nil { t.Fatalf("NewGoRoutine failed. Expected: <no error> Actual: <%v>", err1) } operation2 := generateNoopFunc() <-operation1DoneCh // Force operation1 to complete // Act err2 := retryWithExponentialBackOff( time.Duration(initialOperationWaitTimeShort), func() (bool, error) { err := grm.Run(volumeName, "" /* operationSubName */, operation2) if err != nil { t.Logf("Warning: NewGoRoutine failed with %v. Will retry.", err) return false, nil } return true, nil }, ) // Assert if err2 != nil { t.Fatalf("NewGoRoutine failed. Expected: <no error> Actual: <%v>", err2) } }
func TestOperationExecutor_MountVolume_ConcurrentMountForAttachablePlugins(t *testing.T) { // Arrange ch, quit, oe := setup() volumesToMount := make([]VolumeToMount, numVolumesToAttach) pdName := "pd-volume" volumeName := v1.UniqueVolumeName(pdName) // Act for i := range volumesToMount { podName := "pod-" + strconv.Itoa((i + 1)) pod := getTestPodWithGCEPD(podName, pdName) volumesToMount[i] = VolumeToMount{ Pod: pod, VolumeName: volumeName, PluginIsAttachable: true, // this field determines whether the plugin is attachable ReportedInUse: true, } oe.MountVolume(0 /* waitForAttachTimeout */, volumesToMount[i], nil /* actualStateOfWorldMounterUpdater */) } // Assert if !isOperationRunSerially(ch, quit) { t.Fatalf("Mount operations should not start concurrently for attachable volumes") } }
func Test_NewGoRoutineMap_Positive_WaitWithExpBackoff(t *testing.T) { // Test that Wait() really blocks until the last operation succeeds // Arrange grm := NewNestedPendingOperations(true /* exponentialBackOffOnError */) volumeName := v1.UniqueVolumeName("volume-name") operation1DoneCh := make(chan interface{}, 0 /* bufferSize */) operation1 := generateWaitFunc(operation1DoneCh) err := grm.Run(volumeName, "" /* operationSubName */, operation1) if err != nil { t.Fatalf("NewGoRoutine failed. Expected: <no error> Actual: <%v>", err) } // Act waitDoneCh := make(chan interface{}, 1) go func() { grm.Wait() waitDoneCh <- true }() // Finish the operation operation1DoneCh <- true // Assert err = waitChannelWithTimeout(waitDoneCh, testTimeout) if err != nil { t.Fatalf("Error waiting for GoRoutineMap.Wait: %v", err) } }
// Calls AddVolumeNode() once. // Verifies a single volume/node entry exists. func Test_AddVolumeNode_Positive_NewVolumeNewNode(t *testing.T) { // Arrange volumePluginMgr, _ := volumetesting.GetTestVolumePluginMgr(t) asw := NewActualStateOfWorld(volumePluginMgr) volumeName := v1.UniqueVolumeName("volume-name") volumeSpec := controllervolumetesting.GetTestVolumeSpec(string(volumeName), volumeName) nodeName := types.NodeName("node-name") devicePath := "fake/device/path" // Act generatedVolumeName, err := asw.AddVolumeNode(volumeSpec, nodeName, devicePath) // Assert if err != nil { t.Fatalf("AddVolumeNode failed. Expected: <no error> Actual: <%v>", err) } volumeNodeComboExists := asw.VolumeNodeExists(generatedVolumeName, nodeName) if !volumeNodeComboExists { t.Fatalf("%q/%q volume/node combo does not exist, it should.", generatedVolumeName, nodeName) } attachedVolumes := asw.GetAttachedVolumes() if len(attachedVolumes) != 1 { t.Fatalf("len(attachedVolumes) Expected: <1> Actual: <%v>", len(attachedVolumes)) } verifyAttachedVolume(t, attachedVolumes, generatedVolumeName, string(volumeName), nodeName, devicePath, true /* expectedMountedByNode */, false /* expectNonZeroDetachRequestedTime */) }
// Populates data struct with one volume/node entry. // Calls RemoveVolumeFromReportAsAttached // Verifyies there is no valume as reported as attached func Test_RemoveVolumeFromReportAsAttached(t *testing.T) { // Arrange volumePluginMgr, _ := volumetesting.GetTestVolumePluginMgr(t) asw := NewActualStateOfWorld(volumePluginMgr) volumeName := v1.UniqueVolumeName("volume-name") volumeSpec := controllervolumetesting.GetTestVolumeSpec(string(volumeName), volumeName) nodeName := types.NodeName("node-name") devicePath := "fake/device/path" generatedVolumeName, addErr := asw.AddVolumeNode(volumeSpec, nodeName, devicePath) if addErr != nil { t.Fatalf("AddVolumeNode failed. Expected: <no error> Actual: <%v>", addErr) } removeVolumeDetachErr := asw.RemoveVolumeFromReportAsAttached(generatedVolumeName, nodeName) if removeVolumeDetachErr != nil { t.Fatalf("RemoveVolumeFromReportAsAttached failed. Expected: <no error> Actual: <%v>", removeVolumeDetachErr) } reportAsAttachedVolumesMap := asw.GetVolumesToReportAttached() volumes, exists := reportAsAttachedVolumesMap[nodeName] if !exists { t.Fatalf("MarkDesireToDetach_UnmarkDesireToDetach failed. Expected: <node %q exist> Actual: <node does not exist in the reportedAsAttached map", nodeName) } if len(volumes) > 0 { t.Fatalf("len(reportAsAttachedVolumes) Expected: <0> Actual: <%v>", len(volumes)) } }
// Populates data struct with one volume/node entry. // Calls DeleteVolumeNode() to delete volume/node. // Verifies no volume/node entries exists. func Test_DeleteVolumeNode_Positive_VolumeExistsNodeExists(t *testing.T) { // Arrange volumePluginMgr, _ := volumetesting.GetTestVolumePluginMgr(t) asw := NewActualStateOfWorld(volumePluginMgr) volumeName := v1.UniqueVolumeName("volume-name") volumeSpec := controllervolumetesting.GetTestVolumeSpec(string(volumeName), volumeName) nodeName := types.NodeName("node-name") devicePath := "fake/device/path" generatedVolumeName, addErr := asw.AddVolumeNode(volumeSpec, nodeName, devicePath) if addErr != nil { t.Fatalf("AddVolumeNode failed. Expected: <no error> Actual: <%v>", addErr) } // Act asw.DeleteVolumeNode(generatedVolumeName, nodeName) // Assert volumeNodeComboExists := asw.VolumeNodeExists(generatedVolumeName, nodeName) if volumeNodeComboExists { t.Fatalf("%q/%q volume/node combo exists, it should not.", generatedVolumeName, nodeName) } attachedVolumes := asw.GetAttachedVolumes() if len(attachedVolumes) != 0 { t.Fatalf("len(attachedVolumes) Expected: <0> Actual: <%v>", len(attachedVolumes)) } }
func TestOperationExecutor_MountVolume_ConcurrentMountForNonAttachablePlugins(t *testing.T) { // Arrange ch, quit, oe := setup() volumesToMount := make([]VolumeToMount, numVolumesToMount) secretName := "secret-volume" volumeName := v1.UniqueVolumeName(secretName) // Act for i := range volumesToMount { podName := "pod-" + strconv.Itoa((i + 1)) pod := getTestPodWithSecret(podName, secretName) volumesToMount[i] = VolumeToMount{ Pod: pod, VolumeName: volumeName, PluginIsAttachable: false, // this field determines whether the plugin is attachable ReportedInUse: true, } oe.MountVolume(0 /* waitForAttachTimeOut */, volumesToMount[i], nil /* actualStateOfWorldMounterUpdater */) } // Assert if !isOperationRunConcurrently(ch, quit, numVolumesToMount) { t.Fatalf("Unable to start mount operations in Concurrent for non-attachable volumes") } }
// Populates data struct with one volume/node entry. // Calls SetDetachRequestTime twice and sleep maxWaitTime (1 second) in between // The elapsed time returned from the first SetDetachRequestTime call should be smaller than maxWaitTime // The elapsed time returned from the second SetDetachRequestTime call should be larger than maxWaitTime func Test_SetDetachRequestTime_Positive(t *testing.T) { // Arrange volumePluginMgr, _ := volumetesting.GetTestVolumePluginMgr(t) asw := NewActualStateOfWorld(volumePluginMgr) volumeName := v1.UniqueVolumeName("volume-name") volumeSpec := controllervolumetesting.GetTestVolumeSpec(string(volumeName), volumeName) nodeName := types.NodeName("node-name") devicePath := "fake/device/path" generatedVolumeName, addErr := asw.AddVolumeNode(volumeSpec, nodeName, devicePath) if addErr != nil { t.Fatalf("AddVolumeNode failed. Expected: <no error> Actual: <%v>", addErr) } maxWaitTime := 1 * time.Second etime, err := asw.SetDetachRequestTime(generatedVolumeName, nodeName) if err != nil { t.Fatalf("SetDetachRequestTime failed. Expected: <no error> Actual: <%v>", err) } if etime >= maxWaitTime { t.Logf("SetDetachRequestTim Expected: <elapsed time %v is smaller than maxWaitTime %v> Actual <elapsed time is larger than maxWaitTime>", etime, maxWaitTime) } // Sleep and call SetDetachRequestTime again time.Sleep(maxWaitTime) etime, err = asw.SetDetachRequestTime(generatedVolumeName, nodeName) if err != nil { t.Fatalf("SetDetachRequestTime failed. Expected: <no error> Actual: <%v>", err) } if etime < maxWaitTime { t.Fatalf("SetDetachRequestTim Expected: <elapsed time %v is larger than maxWaitTime %v> Actual <elapsed time is smaller>", etime, maxWaitTime) } }
// Populates data struct with new pod/volume/node. // Calls VolumeExists() on that volume/node. // Verifies volume/node exists, and one volume to attach. func Test_VolumeExists_Positive_VolumeExistsNodeExists(t *testing.T) { // Arrange volumePluginMgr, _ := volumetesting.GetTestVolumePluginMgr(t) dsw := NewDesiredStateOfWorld(volumePluginMgr) nodeName := k8stypes.NodeName("node-name") dsw.AddNode(nodeName) podName := "pod-uid" volumeName := v1.UniqueVolumeName("volume-name") volumeSpec := controllervolumetesting.GetTestVolumeSpec(string(volumeName), volumeName) generatedVolumeName, _ := dsw.AddPod(types.UniquePodName(podName), controllervolumetesting.NewPod(podName, podName), volumeSpec, nodeName) // Act volumeExists := dsw.VolumeExists(generatedVolumeName, nodeName) // Assert if !volumeExists { t.Fatalf("Volume %q does not exist, it should.", generatedVolumeName) } volumesToAttach := dsw.GetVolumesToAttach() if len(volumesToAttach) != 1 { t.Fatalf("len(volumesToAttach) Expected: <1> Actual: <%v>", len(volumesToAttach)) } verifyVolumeToAttach(t, volumesToAttach, nodeName, generatedVolumeName, string(volumeName)) }
// Populates data struct with one volume/node entry. // Calls RemoveVolumeFromReportAsAttached() once on volume/node entry. // Verifies mountedByNode is true and detachRequestedTime is NOT zero. func Test_RemoveVolumeFromReportAsAttached_Positive_Marked(t *testing.T) { // Arrange volumePluginMgr, _ := volumetesting.GetTestVolumePluginMgr(t) asw := NewActualStateOfWorld(volumePluginMgr) volumeName := v1.UniqueVolumeName("volume-name") volumeSpec := controllervolumetesting.GetTestVolumeSpec(string(volumeName), volumeName) nodeName := types.NodeName("node-name") devicePath := "fake/device/path" generatedVolumeName, addErr := asw.AddVolumeNode(volumeSpec, nodeName, devicePath) if addErr != nil { t.Fatalf("AddVolumeNode failed. Expected: <no error> Actual: <%v>", addErr) } // Act _, err := asw.SetDetachRequestTime(generatedVolumeName, nodeName) if err != nil { t.Fatalf("SetDetachRequestTime failed. Expected: <no error> Actual: <%v>", err) } markDesireToDetachErr := asw.RemoveVolumeFromReportAsAttached(generatedVolumeName, nodeName) if markDesireToDetachErr != nil { t.Fatalf("MarkDesireToDetach failed. Expected: <no error> Actual: <%v>", markDesireToDetachErr) } // Assert attachedVolumes := asw.GetAttachedVolumes() if len(attachedVolumes) != 1 { t.Fatalf("len(attachedVolumes) Expected: <1> Actual: <%v>", len(attachedVolumes)) } verifyAttachedVolume(t, attachedVolumes, generatedVolumeName, string(volumeName), nodeName, devicePath, true /* expectedMountedByNode */, true /* expectNonZeroDetachRequestedTime */) }
// 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) { // Arrange volumePluginMgr, fakePlugin := volumetesting.GetTestVolumePluginMgr(t) dsw := cache.NewDesiredStateOfWorld(volumePluginMgr) asw := cache.NewActualStateOfWorld(volumePluginMgr) fakeKubeClient := controllervolumetesting.CreateTestClient() fakeRecorder := &record.FakeRecorder{} ad := operationexecutor.NewOperationExecutor(fakeKubeClient, volumePluginMgr, fakeRecorder, false) nsu := statusupdater.NewFakeNodeStatusUpdater(true /* returnError */) reconciler := NewReconciler( reconcilerLoopPeriod, maxWaitForUnmountDuration, syncLoopPeriod, dsw, asw, ad, nsu) podName := "pod-uid" volumeName := v1.UniqueVolumeName("volume-name") volumeSpec := controllervolumetesting.GetTestVolumeSpec(string(volumeName), volumeName) nodeName := k8stypes.NodeName("node-name") dsw.AddNode(nodeName) volumeExists := dsw.VolumeExists(volumeName, nodeName) if volumeExists { t.Fatalf( "Volume %q/node %q should not exist, but it does.", volumeName, nodeName) } generatedVolumeName, podAddErr := dsw.AddPod(types.UniquePodName(podName), controllervolumetesting.NewPod(podName, podName), volumeSpec, nodeName) if podAddErr != nil { t.Fatalf("AddPod failed. Expected: <no error> Actual: <%v>", podAddErr) } // Act go reconciler.Run(wait.NeverStop) // Assert waitForNewAttacherCallCount(t, 1 /* expectedCallCount */, fakePlugin) verifyNewAttacherCallCount(t, false /* expectZeroNewAttacherCallCount */, fakePlugin) waitForAttachCallCount(t, 1 /* expectedAttachCallCount */, fakePlugin) verifyNewDetacherCallCount(t, true /* expectZeroNewDetacherCallCount */, fakePlugin) waitForDetachCallCount(t, 0 /* expectedDetachCallCount */, fakePlugin) // Act dsw.DeletePod(types.UniquePodName(podName), generatedVolumeName, nodeName) volumeExists = dsw.VolumeExists(generatedVolumeName, nodeName) if volumeExists { t.Fatalf( "Deleted pod %q from volume %q/node %q. Volume should also be deleted but it still exists.", podName, generatedVolumeName, nodeName) } asw.SetVolumeMountedByNode(generatedVolumeName, nodeName, true /* mounted */) asw.SetVolumeMountedByNode(generatedVolumeName, nodeName, false /* mounted */) // Assert verifyNewDetacherCallCount(t, true /* expectZeroNewDetacherCallCount */, fakePlugin) verifyNewAttacherCallCount(t, false /* expectZeroNewAttacherCallCount */, fakePlugin) waitForAttachCallCount(t, 1 /* expectedAttachCallCount */, fakePlugin) verifyNewDetacherCallCount(t, false /* expectZeroNewDetacherCallCount */, fakePlugin) waitForDetachCallCount(t, 0 /* expectedDetachCallCount */, fakePlugin) }
// Populates data struct with pod1/volume/node and pod2/volume/node. // Calls DeleteNode() to delete the pod1/volume/node. // Verifies volume still exists, and one volumes to attach. func Test_DeletePod_Positive_2PodsExistNodeExistsVolumesExist(t *testing.T) { // Arrange volumePluginMgr, _ := volumetesting.GetTestVolumePluginMgr(t) dsw := NewDesiredStateOfWorld(volumePluginMgr) pod1Name := "pod1-uid" pod2Name := "pod2-uid" volumeName := v1.UniqueVolumeName("volume-name") volumeSpec := controllervolumetesting.GetTestVolumeSpec(string(volumeName), volumeName) nodeName := k8stypes.NodeName("node-name") dsw.AddNode(nodeName) generatedVolumeName1, pod1AddErr := dsw.AddPod(types.UniquePodName(pod1Name), controllervolumetesting.NewPod(pod1Name, pod1Name), volumeSpec, nodeName) if pod1AddErr != nil { t.Fatalf( "AddPod failed for pod %q. Expected: <no error> Actual: <%v>", pod1Name, pod1AddErr) } generatedVolumeName2, pod2AddErr := dsw.AddPod(types.UniquePodName(pod2Name), controllervolumetesting.NewPod(pod2Name, pod2Name), volumeSpec, nodeName) if pod2AddErr != nil { t.Fatalf( "AddPod failed for pod %q. Expected: <no error> Actual: <%v>", pod2Name, pod2AddErr) } if generatedVolumeName1 != generatedVolumeName2 { t.Fatalf( "Generated volume names for the same volume should be the same but they are not: %q and %q", generatedVolumeName1, generatedVolumeName2) } volumeExists := dsw.VolumeExists(generatedVolumeName1, nodeName) if !volumeExists { t.Fatalf( "Volume %q does not exist under node %q, it should.", generatedVolumeName1, nodeName) } // Act dsw.DeletePod(types.UniquePodName(pod1Name), generatedVolumeName1, nodeName) // Assert volumeExists = dsw.VolumeExists(generatedVolumeName1, nodeName) if !volumeExists { t.Fatalf( "Volume %q under node %q should still exist, but it does not.", generatedVolumeName1, nodeName) } volumesToAttach := dsw.GetVolumesToAttach() if len(volumesToAttach) != 1 { t.Fatalf("len(volumesToAttach) Expected: <1> Actual: <%v>", len(volumesToAttach)) } verifyVolumeToAttach(t, volumesToAttach, nodeName, generatedVolumeName1, string(volumeName)) }
func TestGetMountedVolumesForPodAndGetVolumesInUse(t *testing.T) { tmpDir, err := utiltesting.MkTmpdir("volumeManagerTest") if err != nil { t.Fatalf("can't make a temp dir: %v", err) } defer os.RemoveAll(tmpDir) podManager := kubepod.NewBasicPodManager(podtest.NewFakeMirrorClient()) node, pod, pv, claim := createObjects() kubeClient := fake.NewSimpleClientset(node, pod, pv, claim) manager, err := newTestVolumeManager(tmpDir, podManager, kubeClient) if err != nil { t.Fatalf("Failed to initialize volume manager: %v", err) } stopCh := runVolumeManager(manager) defer close(stopCh) podManager.SetPods([]*v1.Pod{pod}) // Fake node status update go simulateVolumeInUseUpdate( v1.UniqueVolumeName(node.Status.VolumesAttached[0].Name), stopCh, manager) err = manager.WaitForAttachAndMount(pod) if err != nil { t.Errorf("Expected success: %v", err) } expectedMounted := pod.Spec.Volumes[0].Name actualMounted := manager.GetMountedVolumesForPod(types.UniquePodName(pod.ObjectMeta.UID)) if _, ok := actualMounted[expectedMounted]; !ok || (len(actualMounted) != 1) { t.Errorf("Expected %v to be mounted to pod but got %v", expectedMounted, actualMounted) } expectedInUse := []v1.UniqueVolumeName{v1.UniqueVolumeName(node.Status.VolumesAttached[0].Name)} actualInUse := manager.GetVolumesInUse() if !reflect.DeepEqual(expectedInUse, actualInUse) { t.Errorf("Expected %v to be in use but got %v", expectedInUse, actualInUse) } }
func Test_NewGoRoutineMap_Positive_TwoOps(t *testing.T) { // Arrange grm := NewNestedPendingOperations(false /* exponentialBackOffOnError */) volume1Name := v1.UniqueVolumeName("volume1-name") volume2Name := v1.UniqueVolumeName("volume2-name") operation := func() error { return nil } // Act err1 := grm.Run(volume1Name, "" /* operationSubName */, operation) err2 := grm.Run(volume2Name, "" /* operationSubName */, operation) // Assert if err1 != nil { t.Fatalf("NewGoRoutine %q failed. Expected: <no error> Actual: <%v>", volume1Name, err1) } if err2 != nil { t.Fatalf("NewGoRoutine %q failed. Expected: <no error> Actual: <%v>", volume2Name, err2) } }
func Test_NewGoRoutineMap_Positive_SingleOpWithExpBackoff(t *testing.T) { // Arrange grm := NewNestedPendingOperations(true /* exponentialBackOffOnError */) volumeName := v1.UniqueVolumeName("volume-name") operation := func() error { return nil } // Act err := grm.Run(volumeName, "" /* operationSubName */, operation) // Assert if err != nil { t.Fatalf("NewGoRoutine failed. Expected: <no error> Actual: <%v>", err) } }
// Calls AddVolumeNode() twice. Second time use a different node name. // Verifies two volume/node entries exist with the same volumeSpec. func Test_AddVolumeNode_Positive_ExistingVolumeNewNode(t *testing.T) { // Arrange volumePluginMgr, _ := volumetesting.GetTestVolumePluginMgr(t) asw := NewActualStateOfWorld(volumePluginMgr) volumeName := v1.UniqueVolumeName("volume-name") volumeSpec := controllervolumetesting.GetTestVolumeSpec(string(volumeName), volumeName) node1Name := types.NodeName("node1-name") node2Name := types.NodeName("node2-name") devicePath := "fake/device/path" // Act generatedVolumeName1, add1Err := asw.AddVolumeNode(volumeSpec, node1Name, devicePath) generatedVolumeName2, add2Err := asw.AddVolumeNode(volumeSpec, node2Name, devicePath) // Assert if add1Err != nil { t.Fatalf("AddVolumeNode failed. Expected: <no error> Actual: <%v>", add1Err) } if add2Err != nil { t.Fatalf("AddVolumeNode failed. Expected: <no error> Actual: <%v>", add2Err) } if generatedVolumeName1 != generatedVolumeName2 { t.Fatalf( "Generated volume names for the same volume should be the same but they are not: %q and %q", generatedVolumeName1, generatedVolumeName2) } volumeNode1ComboExists := asw.VolumeNodeExists(generatedVolumeName1, node1Name) if !volumeNode1ComboExists { t.Fatalf("%q/%q volume/node combo does not exist, it should.", generatedVolumeName1, node1Name) } volumeNode2ComboExists := asw.VolumeNodeExists(generatedVolumeName1, node2Name) if !volumeNode2ComboExists { t.Fatalf("%q/%q volume/node combo does not exist, it should.", generatedVolumeName1, node2Name) } attachedVolumes := asw.GetAttachedVolumes() if len(attachedVolumes) != 2 { t.Fatalf("len(attachedVolumes) Expected: <2> Actual: <%v>", len(attachedVolumes)) } verifyAttachedVolume(t, attachedVolumes, generatedVolumeName1, string(volumeName), node1Name, devicePath, true /* expectedMountedByNode */, false /* expectNonZeroDetachRequestedTime */) verifyAttachedVolume(t, attachedVolumes, generatedVolumeName1, string(volumeName), node2Name, devicePath, true /* expectedMountedByNode */, false /* expectNonZeroDetachRequestedTime */) }
// Populates data struct with pod1/volume/node. // Calls DeleteNode() to delete the pod2/volume/node. // Verifies volume still exists, and one volumes to attach. func Test_DeletePod_Positive_PodDoesNotExist(t *testing.T) { // Arrange volumePluginMgr, _ := volumetesting.GetTestVolumePluginMgr(t) dsw := NewDesiredStateOfWorld(volumePluginMgr) pod1Name := "pod1-uid" pod2Name := "pod2-uid" volumeName := v1.UniqueVolumeName("volume-name") volumeSpec := controllervolumetesting.GetTestVolumeSpec(string(volumeName), volumeName) nodeName := k8stypes.NodeName("node-name") dsw.AddNode(nodeName) generatedVolumeName, pod1AddErr := dsw.AddPod(types.UniquePodName(pod1Name), controllervolumetesting.NewPod(pod1Name, pod1Name), volumeSpec, nodeName) if pod1AddErr != nil { t.Fatalf( "AddPod failed for pod %q. Expected: <no error> Actual: <%v>", pod1Name, pod1AddErr) } volumeExists := dsw.VolumeExists(generatedVolumeName, nodeName) if !volumeExists { t.Fatalf( "Added pod %q to volume %q/node %q. Volume does not exist, it should.", pod1Name, generatedVolumeName, nodeName) } // Act dsw.DeletePod(types.UniquePodName(pod2Name), generatedVolumeName, nodeName) // Assert volumeExists = dsw.VolumeExists(generatedVolumeName, nodeName) if !volumeExists { t.Fatalf( "Volume %q/node %q does not exist, it should.", generatedVolumeName, nodeName) } volumesToAttach := dsw.GetVolumesToAttach() if len(volumesToAttach) != 1 { t.Fatalf("len(volumesToAttach) Expected: <1> Actual: <%v>", len(volumesToAttach)) } verifyVolumeToAttach(t, volumesToAttach, nodeName, generatedVolumeName, string(volumeName)) }
// Populates data struct with one volume/node entry. // Calls RemoveVolumeFromReportAsAttached() once on volume/node entry. // Calls SetVolumeMountedByNode() twice, first setting mounted to true then false. // Verifies mountedByNode is false and detachRequestedTime is NOT zero. func Test_SetVolumeMountedByNode_Positive_UnsetWithInitialSetVerifyDetachRequestedTimePerserved(t *testing.T) { // Arrange volumePluginMgr, _ := volumetesting.GetTestVolumePluginMgr(t) asw := NewActualStateOfWorld(volumePluginMgr) volumeName := v1.UniqueVolumeName("volume-name") volumeSpec := controllervolumetesting.GetTestVolumeSpec(string(volumeName), volumeName) nodeName := types.NodeName("node-name") devicePath := "fake/device/path" generatedVolumeName, addErr := asw.AddVolumeNode(volumeSpec, nodeName, devicePath) if addErr != nil { t.Fatalf("AddVolumeNode failed. Expected: <no error> Actual: <%v>", addErr) } _, err := asw.SetDetachRequestTime(generatedVolumeName, nodeName) if err != nil { t.Fatalf("SetDetachRequestTime failed. Expected: <no error> Actual: <%v>", err) } err = asw.RemoveVolumeFromReportAsAttached(generatedVolumeName, nodeName) if err != nil { t.Fatalf("RemoveVolumeFromReportAsAttached failed. Expected: <no error> Actual: <%v>", err) } expectedDetachRequestedTime := asw.GetAttachedVolumes()[0].DetachRequestedTime // Act setVolumeMountedErr1 := asw.SetVolumeMountedByNode(generatedVolumeName, nodeName, true /* mounted */) setVolumeMountedErr2 := asw.SetVolumeMountedByNode(generatedVolumeName, nodeName, false /* mounted */) // Assert if setVolumeMountedErr1 != nil { t.Fatalf("SetVolumeMountedByNode1 failed. Expected <no error> Actual: <%v>", setVolumeMountedErr1) } if setVolumeMountedErr2 != nil { t.Fatalf("SetVolumeMountedByNode2 failed. Expected <no error> Actual: <%v>", setVolumeMountedErr2) } attachedVolumes := asw.GetAttachedVolumes() if len(attachedVolumes) != 1 { t.Fatalf("len(attachedVolumes) Expected: <1> Actual: <%v>", len(attachedVolumes)) } verifyAttachedVolume(t, attachedVolumes, generatedVolumeName, string(volumeName), nodeName, devicePath, false /* expectedMountedByNode */, true /* expectNonZeroDetachRequestedTime */) if !expectedDetachRequestedTime.Equal(attachedVolumes[0].DetachRequestedTime) { t.Fatalf("DetachRequestedTime changed. Expected: <%v> Actual: <%v>", expectedDetachRequestedTime, attachedVolumes[0].DetachRequestedTime) } }
func Test_NewGoRoutineMap_Positive_ThirdOpAfterFirstCompletesWithExpBackoff(t *testing.T) { // Arrange grm := NewNestedPendingOperations(true /* exponentialBackOffOnError */) volumeName := v1.UniqueVolumeName("volume-name") operation1DoneCh := make(chan interface{}, 0 /* bufferSize */) operation1 := generateWaitFunc(operation1DoneCh) err1 := grm.Run(volumeName, "" /* operationSubName */, operation1) if err1 != nil { t.Fatalf("NewGoRoutine failed. Expected: <no error> Actual: <%v>", err1) } operation2 := generateNoopFunc() operation3 := generateNoopFunc() // Act err2 := grm.Run(volumeName, "" /* operationSubName */, operation2) // Assert if err2 == nil { t.Fatalf("NewGoRoutine did not fail. Expected: <Failed to create operation with name \"%s\". An operation with that name already exists.> Actual: <no error>", volumeName) } if !IsAlreadyExists(err2) { t.Fatalf("NewGoRoutine did not return alreadyExistsError, got: %v", err2) } // Act operation1DoneCh <- true // Force operation1 to complete err3 := retryWithExponentialBackOff( time.Duration(initialOperationWaitTimeShort), func() (bool, error) { err := grm.Run(volumeName, "" /* operationSubName */, operation3) if err != nil { t.Logf("Warning: NewGoRoutine failed with %v. Will retry.", err) return false, nil } return true, nil }, ) // Assert if err3 != nil { t.Fatalf("NewGoRoutine failed. Expected: <no error> Actual: <%v>", err3) } }
func TestOperationExecutor_VerifyControllerAttachedVolumeConcurrently(t *testing.T) { // Arrange ch, quit, oe := setup() volumesToMount := make([]VolumeToMount, numVolumesToVerifyControllerAttached) pdName := "pd-volume" // Act for i := range volumesToMount { volumesToMount[i] = VolumeToMount{ VolumeName: v1.UniqueVolumeName(pdName), } oe.VerifyControllerAttachedVolume(volumesToMount[i], types.NodeName("node-name"), nil /* actualStateOfWorldMounterUpdater */) } // Assert if !isOperationRunSerially(ch, quit) { t.Fatalf("VerifyControllerAttachedVolume should not run concurrently") } }
func TestOperationExecutor_DetachVolumeConcurrently(t *testing.T) { // Arrange ch, quit, oe := setup() attachedVolumes := make([]AttachedVolume, numVolumesToDetach) pdName := "pd-volume" // Act for i := range attachedVolumes { attachedVolumes[i] = AttachedVolume{ VolumeName: v1.UniqueVolumeName(pdName), NodeName: "node", } oe.DetachVolume(attachedVolumes[i], true /* verifySafeToDetach */, nil /* actualStateOfWorldAttacherUpdater */) } // Assert if !isOperationRunSerially(ch, quit) { t.Fatalf("DetachVolume operations should not run concurrently") } }
// Calls VolumeNodeExists() on empty data struct. // Verifies requested entry does not exist. func Test_VolumeNodeExists_Positive_VolumeAndNodeDontExist(t *testing.T) { // Arrange volumePluginMgr, _ := volumetesting.GetTestVolumePluginMgr(t) asw := NewActualStateOfWorld(volumePluginMgr) volumeName := v1.UniqueVolumeName("volume-name") nodeName := types.NodeName("node-name") // Act volumeNodeComboExists := asw.VolumeNodeExists(volumeName, nodeName) // Assert if volumeNodeComboExists { t.Fatalf("%q/%q volume/node combo exists, it should not.", volumeName, nodeName) } attachedVolumes := asw.GetAttachedVolumes() if len(attachedVolumes) != 0 { t.Fatalf("len(attachedVolumes) Expected: <0> Actual: <%v>", len(attachedVolumes)) } }
func TestOperationExecutor_AttachVolumeConcurrently(t *testing.T) { // Arrange ch, quit, oe := setup() volumesToAttach := make([]VolumeToAttach, numVolumesToAttach) pdName := "pd-volume" // Act for i := range volumesToAttach { volumesToAttach[i] = VolumeToAttach{ VolumeName: v1.UniqueVolumeName(pdName), NodeName: "node", } oe.AttachVolume(volumesToAttach[i], nil /* actualStateOfWorldAttacherUpdater */) } // Assert if !isOperationRunSerially(ch, quit) { t.Fatalf("Attach volume operations should not start concurrently") } }
// Calls VolumeExists() on some volume/node. // Verifies volume/node do not exist, and zero volumes to attach. func Test_VolumeExists_Positive_VolumeDoesntExistNodeDoesntExists(t *testing.T) { // Arrange volumePluginMgr, _ := volumetesting.GetTestVolumePluginMgr(t) dsw := NewDesiredStateOfWorld(volumePluginMgr) nodeName := k8stypes.NodeName("node-name") volumeName := v1.UniqueVolumeName("volume-name") // Act volumeExists := dsw.VolumeExists(volumeName, nodeName) // Assert if volumeExists { t.Fatalf("Volume %q exists, it should not.", volumeName) } volumesToAttach := dsw.GetVolumesToAttach() if len(volumesToAttach) != 0 { t.Fatalf("len(volumesToAttach) Expected: <0> Actual: <%v>", len(volumesToAttach)) } }