// Populates data struct with one volume/node entry. // Calls MarkDesireToDetach() once on volume/node entry. // Calls AddVolumeNode() to re-add the same volume/node entry. // Verifies mountedByNode is true and detachRequestedTime is reset to zero. func Test_MarkDesireToDetach_Positive_MarkedAddVolumeNodeReset(t *testing.T) { // Arrange volumePluginMgr, _ := volumetesting.GetTestVolumePluginMgr(t) asw := NewActualStateOfWorld(volumePluginMgr) volumeName := api.UniqueVolumeName("volume-name") volumeSpec := controllervolumetesting.GetTestVolumeSpec(string(volumeName), volumeName) 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 _, markDesireToDetachErr := asw.MarkDesireToDetach(generatedVolumeName, nodeName) generatedVolumeName, addErr = asw.AddVolumeNode(volumeSpec, nodeName, devicePath) // Assert if markDesireToDetachErr != nil { t.Fatalf("MarkDesireToDetach failed. Expected: <no error> Actual: <%v>", markDesireToDetachErr) } if addErr != nil { t.Fatalf("AddVolumeNode failed. Expected: <no error> Actual: <%v>", addErr) } // 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, true /* expectedMountedByNode */, false /* expectNonZeroDetachRequestedTime */) }
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 */) }
// Calls Run() // Verifies there are no calls to attach or detach. func Test_Run_Positive_DoNothing(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(operationexecutor.NewOperationGenerator(fakeKubeClient, volumePluginMgr, fakeRecorder, false /* checkNodeCapabilitiesBeforeMount */)) nodeInformer := informers.NewNodeInformer( fakeKubeClient, resyncPeriod) nsu := statusupdater.NewNodeStatusUpdater( fakeKubeClient, nodeInformer, asw) reconciler := NewReconciler( reconcilerLoopPeriod, maxWaitForUnmountDuration, syncLoopPeriod, false, dsw, asw, ad, nsu) // Act ch := make(chan struct{}) go reconciler.Run(ch) defer close(ch) // Assert waitForNewAttacherCallCount(t, 0 /* expectedCallCount */, fakePlugin) verifyNewAttacherCallCount(t, true /* expectZeroNewAttacherCallCount */, fakePlugin) verifyNewDetacherCallCount(t, true /* expectZeroNewDetacherCallCount */, fakePlugin) waitForAttachCallCount(t, 0 /* expectedAttachCallCount */, fakePlugin) waitForDetachCallCount(t, 0 /* expectedDetachCallCount */, fakePlugin) }
// Calls Run() // Verifies there are no calls to attach, detach, mount, unmount, etc. func Test_Run_Positive_DoNothing(t *testing.T) { // Arrange nodeName := "myhostname" volumePluginMgr, fakePlugin := volumetesting.GetTestVolumePluginMgr(t) dsw := cache.NewDesiredStateOfWorld(volumePluginMgr) asw := cache.NewActualStateOfWorld(nodeName, volumePluginMgr) oex := operationexecutor.NewOperationExecutor(volumePluginMgr) reconciler := NewReconciler( false, /* controllerAttachDetachEnabled */ reconcilerLoopSleepDuration, waitForAttachTimeout, nodeName, dsw, asw, oex) // Act go reconciler.Run(wait.NeverStop) // Assert assert.NoError(t, volumetesting.VerifyZeroAttachCalls(fakePlugin)) assert.NoError(t, volumetesting.VerifyZeroWaitForAttachCallCount(fakePlugin)) assert.NoError(t, volumetesting.VerifyZeroMountDeviceCallCount(fakePlugin)) assert.NoError(t, volumetesting.VerifyZeroSetUpCallCount(fakePlugin)) assert.NoError(t, volumetesting.VerifyZeroTearDownCallCount(fakePlugin)) assert.NoError(t, volumetesting.VerifyZeroDetachCallCount(fakePlugin)) }
func Test_GetAttachedVolumesForNode_Positive_OneVolumeTwoNodes(t *testing.T) { // Arrange volumePluginMgr, _ := volumetesting.GetTestVolumePluginMgr(t) asw := NewActualStateOfWorld(volumePluginMgr) volumeName := api.UniqueVolumeName("volume-name") volumeSpec := controllervolumetesting.GetTestVolumeSpec(string(volumeName), volumeName) node1Name := "node1-name" devicePath := "fake/device/path" generatedVolumeName1, add1Err := asw.AddVolumeNode(volumeSpec, node1Name, devicePath) if add1Err != nil { t.Fatalf("AddVolumeNode failed. Expected: <no error> Actual: <%v>", add1Err) } node2Name := "node2-name" generatedVolumeName2, add2Err := asw.AddVolumeNode(volumeSpec, node2Name, devicePath) 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) } // Act attachedVolumes := asw.GetAttachedVolumesForNode(node1Name) // Assert if len(attachedVolumes) != 1 { t.Fatalf("len(attachedVolumes) Expected: <1> Actual: <%v>", len(attachedVolumes)) } verifyAttachedVolume(t, attachedVolumes, generatedVolumeName1, string(volumeName), node1Name, true /* expectedMountedByNode */, false /* expectNonZeroDetachRequestedTime */) }
// 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) { // Arrange volumePluginMgr, fakePlugin := volumetesting.GetTestVolumePluginMgr(t) dsw := cache.NewDesiredStateOfWorld(volumePluginMgr) asw := cache.NewActualStateOfWorld(volumePluginMgr) fakeKubeClient := controllervolumetesting.CreateTestClient() ad := operationexecutor.NewOperationExecutor(fakeKubeClient, volumePluginMgr) nsu := statusupdater.NewFakeNodeStatusUpdater(false /* returnError */) reconciler := NewReconciler( reconcilerLoopPeriod, maxWaitForUnmountDuration, dsw, asw, ad, nsu) podName := "pod-uid" volumeName := api.UniqueVolumeName("volume-name") volumeSpec := controllervolumetesting.GetTestVolumeSpec(string(volumeName), volumeName) 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 ch := make(chan struct{}) go reconciler.Run(ch) defer close(ch) // 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 waitForNewDetacherCallCount(t, 1 /* expectedCallCount */, fakePlugin) verifyNewAttacherCallCount(t, false /* expectZeroNewAttacherCallCount */, fakePlugin) waitForAttachCallCount(t, 1 /* expectedAttachCallCount */, fakePlugin) verifyNewDetacherCallCount(t, false /* expectZeroNewDetacherCallCount */, fakePlugin) waitForDetachCallCount(t, 1 /* expectedDetachCallCount */, fakePlugin) }
// Calls Run() // Verifies there are no calls to attach or detach. func Test_Run_Positive_DoNothing(t *testing.T) { // Arrange volumePluginMgr, fakePlugin := volumetesting.GetTestVolumePluginMgr(t) dsw := cache.NewDesiredStateOfWorld(volumePluginMgr) asw := cache.NewActualStateOfWorld(volumePluginMgr) fakeKubeClient := controllervolumetesting.CreateTestClient() ad := operationexecutor.NewOperationExecutor( fakeKubeClient, volumePluginMgr) nodeInformer := informers.CreateSharedNodeIndexInformer( fakeKubeClient, resyncPeriod) nsu := statusupdater.NewNodeStatusUpdater( fakeKubeClient, nodeInformer, asw) reconciler := NewReconciler( reconcilerLoopPeriod, maxWaitForUnmountDuration, dsw, asw, ad, nsu) // Act go reconciler.Run(wait.NeverStop) // Assert waitForNewAttacherCallCount(t, 0 /* expectedCallCount */, fakePlugin) verifyNewAttacherCallCount(t, true /* expectZeroNewAttacherCallCount */, fakePlugin) verifyNewDetacherCallCount(t, true /* expectZeroNewDetacherCallCount */, fakePlugin) waitForAttachCallCount(t, 0 /* expectedAttachCallCount */, fakePlugin) waitForDetachCallCount(t, 0 /* expectedDetachCallCount */, fakePlugin) }
// Calls Run() // Verifies there are no calls to attach, detach, mount, unmount, etc. func Test_Run_Positive_DoNothing(t *testing.T) { // Arrange volumePluginMgr, fakePlugin := volumetesting.GetTestVolumePluginMgr(t) dsw := cache.NewDesiredStateOfWorld(volumePluginMgr) asw := cache.NewActualStateOfWorld(nodeName, volumePluginMgr) kubeClient := createTestClient() oex := operationexecutor.NewOperationExecutor(kubeClient, volumePluginMgr) reconciler := NewReconciler( kubeClient, false, /* controllerAttachDetachEnabled */ reconcilerLoopSleepDuration, reconcilerReconstructSleepPeriod, waitForAttachTimeout, nodeName, dsw, asw, oex, &mount.FakeMounter{}, volumePluginMgr, kubeletPodsDir) // Act runReconciler(reconciler) // Assert assert.NoError(t, volumetesting.VerifyZeroAttachCalls(fakePlugin)) assert.NoError(t, volumetesting.VerifyZeroWaitForAttachCallCount(fakePlugin)) assert.NoError(t, volumetesting.VerifyZeroMountDeviceCallCount(fakePlugin)) assert.NoError(t, volumetesting.VerifyZeroSetUpCallCount(fakePlugin)) assert.NoError(t, volumetesting.VerifyZeroTearDownCallCount(fakePlugin)) assert.NoError(t, volumetesting.VerifyZeroDetachCallCount(fakePlugin)) }
// Populates desiredStateOfWorld cache with one volume/pod. // Enables controllerAttachDetachEnabled. // Calls Run() // Verifies there is one mount call and no unmount calls. // Verifies there are no attach/detach calls. func Test_Run_Positive_VolumeMountControllerAttachEnabled(t *testing.T) { // Arrange volumePluginMgr, fakePlugin := volumetesting.GetTestVolumePluginMgr(t) dsw := cache.NewDesiredStateOfWorld(volumePluginMgr) asw := cache.NewActualStateOfWorld(nodeName, volumePluginMgr) kubeClient := createTestClient() oex := operationexecutor.NewOperationExecutor(kubeClient, volumePluginMgr) reconciler := NewReconciler( kubeClient, true, /* controllerAttachDetachEnabled */ reconcilerLoopSleepDuration, waitForAttachTimeout, nodeName, dsw, asw, oex) pod := &api.Pod{ ObjectMeta: api.ObjectMeta{ Name: "pod1", UID: "pod1uid", }, Spec: api.PodSpec{ Volumes: []api.Volume{ { Name: "volume-name", VolumeSource: api.VolumeSource{ GCEPersistentDisk: &api.GCEPersistentDiskVolumeSource{ PDName: "fake-device1", }, }, }, }, }, } volumeSpec := &volume.Spec{Volume: &pod.Spec.Volumes[0]} podName := volumehelper.GetUniquePodName(pod) _, err := dsw.AddPodToVolume( podName, pod, volumeSpec, volumeSpec.Name(), "" /* volumeGidValue */) // Assert if err != nil { t.Fatalf("AddPodToVolume failed. Expected: <no error> Actual: <%v>", err) } // Act go reconciler.Run(wait.NeverStop) waitForAttach(t, fakePlugin, asw) // Assert assert.NoError(t, volumetesting.VerifyZeroAttachCalls(fakePlugin)) assert.NoError(t, volumetesting.VerifyWaitForAttachCallCount( 1 /* expectedWaitForAttachCallCount */, fakePlugin)) assert.NoError(t, volumetesting.VerifyMountDeviceCallCount( 1 /* expectedMountDeviceCallCount */, fakePlugin)) assert.NoError(t, volumetesting.VerifySetUpCallCount( 1 /* expectedSetUpCallCount */, fakePlugin)) assert.NoError(t, volumetesting.VerifyZeroTearDownCallCount(fakePlugin)) assert.NoError(t, volumetesting.VerifyZeroDetachCallCount(fakePlugin)) }
// Populates data struct with a single node. // Calls DeleteNode() to delete the node. // Verifies node no longer exists, and zero volumes to attach. func Test_DeleteNode_Positive_NodeExists(t *testing.T) { // Arrange volumePluginMgr, _ := volumetesting.GetTestVolumePluginMgr(t) dsw := NewDesiredStateOfWorld(volumePluginMgr) nodeName := k8stypes.NodeName("node-name") dsw.AddNode(nodeName) // Act err := dsw.DeleteNode(nodeName) // Assert if err != nil { t.Fatalf("DeleteNode failed. Expected: <no error> Actual: <%v>", err) } nodeExists := dsw.NodeExists(nodeName) if nodeExists { t.Fatalf("Deleted node %q still exists, it should not.", nodeName) } volumesToAttach := dsw.GetVolumesToAttach() if len(volumesToAttach) != 0 { t.Fatalf("len(volumesToAttach) Expected: <0> Actual: <%v>", len(volumesToAttach)) } }
// 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 */) }
// Calls AddVolume() once to add volume // Verifies newly added volume exists in GetAttachedVolumes() func Test_AddVolume_Positive_NewVolume(t *testing.T) { // Arrange volumePluginMgr, _ := volumetesting.GetTestVolumePluginMgr(t) asw := NewActualStateOfWorld("mynode" /* nodeName */, volumePluginMgr) pod := &api.Pod{ ObjectMeta: api.ObjectMeta{ Name: "pod1", UID: "pod1uid", }, Spec: api.PodSpec{ Volumes: []api.Volume{ { Name: "volume-name", VolumeSource: api.VolumeSource{ GCEPersistentDisk: &api.GCEPersistentDiskVolumeSource{ PDName: "fake-device1", }, }, }, }, }, } volumeSpec := &volume.Spec{Volume: &pod.Spec.Volumes[0]} devicePath := "fake/device/path" // Act generatedVolumeName, err := asw.AddVolume(volumeSpec, devicePath) // Assert if err != nil { t.Fatalf("AddVolume failed. Expected: <no error> Actual: <%v>", err) } verifyVolumeExistsInAttachedVolumes(t, generatedVolumeName, asw) }
// 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 */) }
// 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 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)) }
// 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 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)) }
// Calls AddNode() once. // Verifies node exists. // Calls AddNode() again with the same node. // Verifies node exists, and zero volumes to attach. func Test_AddNode_Positive_ExistingNode(t *testing.T) { // Arrange volumePluginMgr, _ := volumetesting.GetTestVolumePluginMgr(t) dsw := NewDesiredStateOfWorld(volumePluginMgr) nodeName := k8stypes.NodeName("node-name") // Act dsw.AddNode(nodeName) // Assert nodeExists := dsw.NodeExists(nodeName) if !nodeExists { t.Fatalf("Added node %q does not exist, it should.", nodeName) } // Act dsw.AddNode(nodeName) // Assert nodeExists = dsw.NodeExists(nodeName) if !nodeExists { t.Fatalf("Added node %q does not exist, it should.", nodeName) } volumesToAttach := dsw.GetVolumesToAttach() if len(volumesToAttach) != 0 { t.Fatalf("len(volumesToAttach) Expected: <0> Actual: <%v>", len(volumesToAttach)) } }
// 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 a volume // Calls AddPodToVolume() twice to add the same pod to the volume // Verifies volume/pod combo exist using PodExistsInVolume() and the second call // did not fail. func Test_AddPodToVolume_Positive_ExistingVolumeExistingNode(t *testing.T) { // Arrange volumePluginMgr, plugin := volumetesting.GetTestVolumePluginMgr(t) asw := NewActualStateOfWorld("mynode" /* nodeName */, volumePluginMgr) devicePath := "fake/device/path" pod := &api.Pod{ ObjectMeta: api.ObjectMeta{ Name: "pod1", UID: "pod1uid", }, Spec: api.PodSpec{ Volumes: []api.Volume{ { Name: "volume-name", VolumeSource: api.VolumeSource{ GCEPersistentDisk: &api.GCEPersistentDiskVolumeSource{ PDName: "fake-device1", }, }, }, }, }, } volumeSpec := &volume.Spec{Volume: &pod.Spec.Volumes[0]} volumeName, err := volumehelper.GetUniqueVolumeNameFromSpec( plugin, volumeSpec) generatedVolumeName, err := asw.AddVolume(volumeSpec, devicePath) if err != nil { t.Fatalf("AddVolume failed. Expected: <no error> Actual: <%v>", err) } podName := volumehelper.GetUniquePodName(pod) mounter, err := plugin.NewMounter(volumeSpec, pod, volume.VolumeOptions{}) if err != nil { t.Fatalf("NewUnmounter failed. Expected: <no error> Actual: <%v>", err) } err = asw.AddPodToVolume( podName, pod.UID, volumeName, mounter, volumeSpec.Name(), "" /* volumeGidValue */) if err != nil { t.Fatalf("AddPodToVolume failed. Expected: <no error> Actual: <%v>", err) } // Act err = asw.AddPodToVolume( podName, pod.UID, volumeName, mounter, volumeSpec.Name(), "" /* volumeGidValue */) // Assert if err != nil { t.Fatalf("AddPodToVolume failed. Expected: <no error> Actual: <%v>", err) } verifyVolumeExistsAsw(t, generatedVolumeName, true /* shouldExist */, asw) verifyVolumeDoesntExistInUnmountedVolumes(t, generatedVolumeName, asw) verifyVolumeDoesntExistInGloballyMountedVolumes(t, generatedVolumeName, asw) verifyPodExistsInVolumeAsw(t, podName, generatedVolumeName, "fake/device/path" /* expectedDevicePath */, asw) }
// Populates data struct with one volume1/node1 entry. // Calls VolumeNodeExists() with volume1/node2. // Verifies requested entry does not exist, but populated entry does. func Test_VolumeNodeExists_Positive_VolumeExistsNodeDoesntExist(t *testing.T) { // Arrange volumePluginMgr, _ := volumetesting.GetTestVolumePluginMgr(t) asw := NewActualStateOfWorld(volumePluginMgr) volumeName := api.UniqueVolumeName("volume-name") volumeSpec := controllervolumetesting.GetTestVolumeSpec(string(volumeName), volumeName) node1Name := "node1-name" node2Name := "node2-name" devicePath := "fake/device/path" generatedVolumeName, addErr := asw.AddVolumeNode(volumeSpec, node1Name, devicePath) if addErr != nil { t.Fatalf("AddVolumeNode failed. Expected: <no error> Actual: <%v>", addErr) } // Act volumeNodeComboExists := asw.VolumeNodeExists(generatedVolumeName, node2Name) // Assert if volumeNodeComboExists { t.Fatalf("%q/%q volume/node combo exists, it should not.", generatedVolumeName, node2Name) } attachedVolumes := asw.GetAttachedVolumes() if len(attachedVolumes) != 1 { t.Fatalf("len(attachedVolumes) Expected: <1> Actual: <%v>", len(attachedVolumes)) } verifyAttachedVolume(t, attachedVolumes, generatedVolumeName, string(volumeName), node1Name, true /* expectedMountedByNode */, false /* expectNonZeroDetachRequestedTime */) }
// 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)) } }
// 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) { // Arrange volumePluginMgr, fakePlugin := volumetesting.GetTestVolumePluginMgr(t) dsw := cache.NewDesiredStateOfWorld(volumePluginMgr) asw := cache.NewActualStateOfWorld(volumePluginMgr) ad := operationexecutor.NewOperationExecutor(volumePluginMgr) reconciler := NewReconciler( reconcilerLoopPeriod, maxWaitForUnmountDuration, dsw, asw, ad) podName := types.UniquePodName("pod-uid") volumeName := api.UniqueVolumeName("volume-name") volumeSpec := controllervolumetesting.GetTestVolumeSpec(string(volumeName), volumeName) 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) } _, podErr := dsw.AddPod(podName, volumeSpec, nodeName) if podErr != nil { t.Fatalf("AddPod failed. Expected: <no error> Actual: <%v>", podErr) } // Act go reconciler.Run(wait.NeverStop) // Assert waitForNewAttacherCallCount(t, 1 /* expectedCallCount */, fakePlugin) waitForAttachCallCount(t, 1 /* expectedAttachCallCount */, fakePlugin) verifyNewDetacherCallCount(t, true /* expectZeroNewDetacherCallCount */, fakePlugin) }
// Calls AddPodToVolume() to add pod to empty data stuct // Verifies call fails with "volume does not exist" error. func Test_AddPodToVolume_Negative_VolumeDoesntExist(t *testing.T) { // Arrange volumePluginMgr, _ := volumetesting.GetTestVolumePluginMgr(t) asw := NewActualStateOfWorld("mynode" /* nodeName */, volumePluginMgr) pod := &api.Pod{ ObjectMeta: api.ObjectMeta{ Name: "pod1", UID: "pod1uid", }, Spec: api.PodSpec{ Volumes: []api.Volume{ { Name: "volume-name", VolumeSource: api.VolumeSource{ GCEPersistentDisk: &api.GCEPersistentDiskVolumeSource{ PDName: "fake-device1", }, }, }, }, }, } volumeSpec := &volume.Spec{Volume: &pod.Spec.Volumes[0]} plugin, err := volumePluginMgr.FindPluginBySpec(volumeSpec) if err != nil { t.Fatalf( "volumePluginMgr.FindPluginBySpec failed to find volume plugin for %#v with: %v", volumeSpec, err) } volumeName, err := volumehelper.GetUniqueVolumeNameFromSpec( plugin, volumeSpec) podName := volumehelper.GetUniquePodName(pod) mounter, err := plugin.NewMounter(volumeSpec, pod, volume.VolumeOptions{}) if err != nil { t.Fatalf("NewUnmounter failed. Expected: <no error> Actual: <%v>", err) } // Act err = asw.AddPodToVolume( podName, pod.UID, volumeName, mounter, volumeSpec.Name(), "" /* volumeGidValue */) // Assert if err == nil { t.Fatalf("AddPodToVolume did not fail. Expected: <\"no volume with the name ... exists in the list of attached volumes\"> Actual: <no error>") } verifyVolumeDoesntExistInAttachedVolumes(t, volumeName, asw) verifyPodDoesntExistInVolumeAsw( t, podName, volumeName, false, /* expectVolumeToExist */ asw) }
// 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)) }
// Calls GetVolumesToAttach() // Verifies zero volumes to attach. func Test_GetVolumesToAttach_Positive_NoNodes(t *testing.T) { // Arrange volumePluginMgr, _ := volumetesting.GetTestVolumePluginMgr(t) dsw := NewDesiredStateOfWorld(volumePluginMgr) // Act volumesToAttach := dsw.GetVolumesToAttach() // Assert if len(volumesToAttach) > 0 { t.Fatalf("len(volumesToAttach) Expected: <0> Actual: <%v>", len(volumesToAttach)) } }
// Calls GetAttachedVolumes() on empty data struct. // Verifies no volume/node entries are returned. func Test_GetAttachedVolumes_Positive_NoVolumesOrNodes(t *testing.T) { // Arrange volumePluginMgr, _ := volumetesting.GetTestVolumePluginMgr(t) asw := NewActualStateOfWorld(volumePluginMgr) // Act attachedVolumes := asw.GetAttachedVolumes() // Assert if len(attachedVolumes) != 0 { t.Fatalf("len(attachedVolumes) Expected: <0> Actual: <%v>", len(attachedVolumes)) } }
// Populates data struct with pod/volume/node1. // Calls DeleteNode() to delete the pod/volume/node2. // Verifies volume still exists, and one volumes to attach. func Test_DeletePod_Positive_NodeDoesNotExist(t *testing.T) { // Arrange volumePluginMgr, _ := volumetesting.GetTestVolumePluginMgr(t) dsw := NewDesiredStateOfWorld(volumePluginMgr) podName := types.UniquePodName("pod-uid") volumeName := api.UniqueVolumeName("volume-name") volumeSpec := controllervolumetesting.GetTestVolumeSpec(string(volumeName), volumeName) node1Name := "node1-name" dsw.AddNode(node1Name) generatedVolumeName, podAddErr := dsw.AddPod(podName, volumeSpec, node1Name) if podAddErr != nil { t.Fatalf( "AddPod failed for pod %q. Expected: <no error> Actual: <%v>", podName, podAddErr) } volumeExists := dsw.VolumeExists(generatedVolumeName, node1Name) if !volumeExists { t.Fatalf( "Added pod %q to volume %q/node %q. Volume does not exist, it should.", podName, generatedVolumeName, node1Name) } node2Name := "node2-name" // Act dsw.DeletePod(podName, generatedVolumeName, node2Name) // Assert volumeExists = dsw.VolumeExists(generatedVolumeName, node1Name) if !volumeExists { t.Fatalf( "Volume %q/node %q does not exist, it should.", generatedVolumeName, node1Name) } volumeExists = dsw.VolumeExists(generatedVolumeName, node2Name) if volumeExists { t.Fatalf( "node %q exists, it should not.", node2Name) } volumesToAttach := dsw.GetVolumesToAttach() if len(volumesToAttach) != 1 { t.Fatalf("len(volumesToAttach) Expected: <1> Actual: <%v>", len(volumesToAttach)) } verifyVolumeToAttach(t, volumesToAttach, node1Name, generatedVolumeName, string(volumeName)) }
// 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)) }
// Test_SetNodeStatusUpdateNeededError expects the map nodesToUpdateStatusFor // to be empty if the SetNodeStatusUpdateNeeded is called on a node that // does not exist in the actual state of the world func Test_SetNodeStatusUpdateNeededError(t *testing.T) { // Arrange volumePluginMgr, _ := volumetesting.GetTestVolumePluginMgr(t) asw := NewActualStateOfWorld(volumePluginMgr) nodeName := types.NodeName("node-1") // Act asw.SetNodeStatusUpdateNeeded(nodeName) // Assert nodesToUpdateStatusFor := asw.GetNodesToUpdateStatusFor() if len(nodesToUpdateStatusFor) != 0 { t.Fatalf("nodesToUpdateStatusFor should be empty as nodeName does not exist") } }