// 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, _ := controllervolumetesting.GetTestVolumePluginMgr((t)) dsw := NewDesiredStateOfWorld(volumePluginMgr) nodeName := "node-name" dsw.AddNode(nodeName) podName := "pod-name" volume1Name := api.UniqueDeviceName("volume1-name") volume1Spec := controllervolumetesting.GetTestVolumeSpec(string(volume1Name), volume1Name) generatedVolume1Name, podAddErr := dsw.AddPod(podName, volume1Spec, nodeName) if podAddErr != nil { t.Fatalf( "AddPod failed for pod %q. Expected: <no error> Actual: <%v>", podName, podAddErr) } volume2Name := api.UniqueDeviceName("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, _ := controllervolumetesting.GetTestVolumePluginMgr((t)) asw := NewActualStateOfWorld(volumePluginMgr) volume1Name := api.UniqueDeviceName("volume1-name") volume1Spec := controllervolumetesting.GetTestVolumeSpec(string(volume1Name), volume1Name) node1Name := "node1-name" _, add1Err := asw.AddVolumeNode(volume1Spec, node1Name) if add1Err != nil { t.Fatalf("AddVolumeNode failed. Expected: <no error> Actual: <%v>", add1Err) } volume2Name := api.UniqueDeviceName("volume2-name") volume2Spec := controllervolumetesting.GetTestVolumeSpec(string(volume2Name), volume2Name) node2Name := "node2-name" generatedVolumeName2, add2Err := asw.AddVolumeNode(volume2Spec, node2Name) 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, true /* expectedMountedByNode */, false /* expectNonZeroDetachRequestedTime */) }
// 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, _ := controllervolumetesting.GetTestVolumePluginMgr((t)) dsw := NewDesiredStateOfWorld(volumePluginMgr) node1Name := "node1-name" pod1Name := "pod1-name" volume1Name := api.UniqueDeviceName("volume1-name") volume1Spec := controllervolumetesting.GetTestVolumeSpec(string(volume1Name), volume1Name) dsw.AddNode(node1Name) generatedVolume1Name, podAddErr := dsw.AddPod(pod1Name, volume1Spec, node1Name) if podAddErr != nil { t.Fatalf( "AddPod failed for pod %q. Expected: <no error> Actual: <%v>", pod1Name, podAddErr) } node2Name := "node2-name" pod2Name := "pod2-name" volume2Name := api.UniqueDeviceName("volume2-name") volume2Spec := controllervolumetesting.GetTestVolumeSpec(string(volume2Name), volume2Name) dsw.AddNode(node2Name) generatedVolume2Name, podAddErr := dsw.AddPod(pod2Name, volume2Spec, node2Name) if podAddErr != nil { t.Fatalf( "AddPod failed for pod %q. Expected: <no error> Actual: <%v>", pod2Name, podAddErr) } pod3Name := "pod3-name" dsw.AddPod(pod3Name, volume2Spec, node2Name) _, podAddErr = dsw.AddPod(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 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, _ := controllervolumetesting.GetTestVolumePluginMgr((t)) dsw := NewDesiredStateOfWorld(volumePluginMgr) nodeName := "node-name" dsw.AddNode(nodeName) podName := "pod-name" volumeName := api.UniqueDeviceName("volume-name") volumeSpec := controllervolumetesting.GetTestVolumeSpec(string(volumeName), volumeName) generatedVolumeName, _ := dsw.AddPod(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 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 := controllervolumetesting.GetTestVolumePluginMgr((t)) dsw := cache.NewDesiredStateOfWorld(volumePluginMgr) asw := cache.NewActualStateOfWorld(volumePluginMgr) ad := attacherdetacher.NewAttacherDetacher(volumePluginMgr) reconciler := NewReconciler( reconcilerLoopPeriod, maxWaitForUnmountDuration, dsw, asw, ad) podName := "pod-name" volumeName := api.UniqueDeviceName("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) }
// Populates data struct with one volume/node entry. // Calls SetVolumeMountedByNode twice, first setting mounted to true then false. // Calls AddVolumeNode to readd the same volume/node. // Verifies mountedByNode is false and detachRequestedTime is zero. func Test_SetVolumeMountedByNode_Positive_UnsetWithInitialSetAddVolumeNodeNotReset(t *testing.T) { // Arrange volumePluginMgr, _ := controllervolumetesting.GetTestVolumePluginMgr((t)) asw := NewActualStateOfWorld(volumePluginMgr) volumeName := api.UniqueDeviceName("volume-name") volumeSpec := controllervolumetesting.GetTestVolumeSpec(string(volumeName), volumeName) nodeName := "node-name" generatedVolumeName, addErr := asw.AddVolumeNode(volumeSpec, nodeName) if addErr != nil { t.Fatalf("AddVolumeNode failed. Expected: <no error> Actual: <%v>", addErr) } // Act setVolumeMountedErr1 := asw.SetVolumeMountedByNode(generatedVolumeName, nodeName, true /* mounted */) setVolumeMountedErr2 := asw.SetVolumeMountedByNode(generatedVolumeName, nodeName, false /* mounted */) generatedVolumeName, addErr = asw.AddVolumeNode(volumeSpec, nodeName) // 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) } if addErr != nil { t.Fatalf("AddVolumeNode failed. Expected: <no error> Actual: <%v>", addErr) } attachedVolumes := asw.GetAttachedVolumes() if len(attachedVolumes) != 1 { t.Fatalf("len(attachedVolumes) Expected: <1> Actual: <%v>", len(attachedVolumes)) } verifyAttachedVolume(t, attachedVolumes, generatedVolumeName, string(volumeName), nodeName, false /* expectedMountedByNode */, false /* expectNonZeroDetachRequestedTime */) }
// 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, _ := controllervolumetesting.GetTestVolumePluginMgr((t)) asw := NewActualStateOfWorld(volumePluginMgr) volumeName := api.UniqueDeviceName("volume-name") volumeSpec := controllervolumetesting.GetTestVolumeSpec(string(volumeName), volumeName) nodeName := "node-name" generatedVolumeName, addErr := asw.AddVolumeNode(volumeSpec, nodeName) 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) // 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 */) }
// Populates data struct with two volume/node entries (same volume different node). // Calls GetAttachedVolumes() to get list of entries. // Verifies both volume/node entries are returned. func Test_GetAttachedVolumes_Positive_OneVolumeTwoNodes(t *testing.T) { // Arrange volumePluginMgr, _ := controllervolumetesting.GetTestVolumePluginMgr((t)) asw := NewActualStateOfWorld(volumePluginMgr) volumeName := api.UniqueDeviceName("volume-name") volumeSpec := controllervolumetesting.GetTestVolumeSpec(string(volumeName), volumeName) node1Name := "node1-name" generatedVolumeName1, add1Err := asw.AddVolumeNode(volumeSpec, node1Name) if add1Err != nil { t.Fatalf("AddVolumeNode failed. Expected: <no error> Actual: <%v>", add1Err) } node2Name := "node2-name" generatedVolumeName2, add2Err := asw.AddVolumeNode(volumeSpec, node2Name) 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.GetAttachedVolumes() // Assert if len(attachedVolumes) != 2 { t.Fatalf("len(attachedVolumes) Expected: <2> Actual: <%v>", len(attachedVolumes)) } verifyAttachedVolume(t, attachedVolumes, generatedVolumeName1, string(volumeName), node1Name, true /* expectedMountedByNode */, false /* expectNonZeroDetachRequestedTime */) verifyAttachedVolume(t, attachedVolumes, generatedVolumeName1, string(volumeName), node2Name, true /* expectedMountedByNode */, false /* expectNonZeroDetachRequestedTime */) }
// 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, _ := controllervolumetesting.GetTestVolumePluginMgr((t)) asw := NewActualStateOfWorld(volumePluginMgr) volumeName := api.UniqueDeviceName("volume-name") volumeSpec := controllervolumetesting.GetTestVolumeSpec(string(volumeName), volumeName) node1Name := "node1-name" node2Name := "node2-name" generatedVolumeName, addErr := asw.AddVolumeNode(volumeSpec, node1Name) 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, _ := controllervolumetesting.GetTestVolumePluginMgr((t)) asw := NewActualStateOfWorld(volumePluginMgr) volumeName := api.UniqueDeviceName("volume-name") volumeSpec := controllervolumetesting.GetTestVolumeSpec(string(volumeName), volumeName) nodeName := "node-name" generatedVolumeName, addErr := asw.AddVolumeNode(volumeSpec, nodeName) 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 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, _ := controllervolumetesting.GetTestVolumePluginMgr((t)) dsw := NewDesiredStateOfWorld(volumePluginMgr) pod1Name := "pod1-name" pod2Name := "pod2-name" volumeName := api.UniqueDeviceName("volume-name") volumeSpec := controllervolumetesting.GetTestVolumeSpec(string(volumeName), volumeName) nodeName := "node-name" dsw.AddNode(nodeName) generatedVolumeName1, pod1AddErr := dsw.AddPod(pod1Name, volumeSpec, nodeName) if pod1AddErr != nil { t.Fatalf( "AddPod failed for pod %q. Expected: <no error> Actual: <%v>", pod1Name, pod1AddErr) } generatedVolumeName2, pod2AddErr := dsw.AddPod(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(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)) }
// 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, _ := controllervolumetesting.GetTestVolumePluginMgr((t)) dsw := NewDesiredStateOfWorld(volumePluginMgr) pod1Name := "pod1-name" pod2Name := "pod2-name" volumeName := api.UniqueDeviceName("volume-name") volumeSpec := controllervolumetesting.GetTestVolumeSpec(string(volumeName), volumeName) nodeName := "node-name" dsw.AddNode(nodeName) generatedVolumeName, pod1AddErr := dsw.AddPod(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(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 two volume/node entries the second one using a // different node. // Calls DeleteVolumeNode() to delete first volume/node. // Verifies only second volume/node entry exists. func Test_DeleteVolumeNode_Positive_TwoNodesOneDeleted(t *testing.T) { // Arrange volumePluginMgr, _ := controllervolumetesting.GetTestVolumePluginMgr((t)) asw := NewActualStateOfWorld(volumePluginMgr) volumeName := api.UniqueDeviceName("volume-name") volumeSpec := controllervolumetesting.GetTestVolumeSpec(string(volumeName), volumeName) node1Name := "node1-name" node2Name := "node2-name" generatedVolumeName1, add1Err := asw.AddVolumeNode(volumeSpec, node1Name) if add1Err != nil { t.Fatalf("AddVolumeNode failed. Expected: <no error> Actual: <%v>", add1Err) } generatedVolumeName2, add2Err := asw.AddVolumeNode(volumeSpec, node2Name) 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 asw.DeleteVolumeNode(generatedVolumeName1, node1Name) // Assert volumeNodeComboExists := asw.VolumeNodeExists(generatedVolumeName1, node1Name) if volumeNodeComboExists { t.Fatalf("%q/%q volume/node combo exists, it should not.", generatedVolumeName1, node1Name) } volumeNodeComboExists = asw.VolumeNodeExists(generatedVolumeName1, node2Name) if !volumeNodeComboExists { t.Fatalf("%q/%q volume/node combo does not exist, it should.", generatedVolumeName1, node2Name) } attachedVolumes := asw.GetAttachedVolumes() if len(attachedVolumes) != 1 { t.Fatalf("len(attachedVolumes) Expected: <1> Actual: <%v>", len(attachedVolumes)) } verifyAttachedVolume(t, attachedVolumes, generatedVolumeName1, string(volumeName), node2Name, true /* expectedMountedByNode */, false /* expectNonZeroDetachRequestedTime */) }
// Calls VolumeNodeExists() on empty data struct. // Verifies requested entry does not exist. func Test_VolumeNodeExists_Positive_VolumeAndNodeDontExist(t *testing.T) { // Arrange volumePluginMgr, _ := controllervolumetesting.GetTestVolumePluginMgr((t)) asw := NewActualStateOfWorld(volumePluginMgr) volumeName := api.UniqueDeviceName("volume-name") 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)) } }
// 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, _ := controllervolumetesting.GetTestVolumePluginMgr((t)) dsw := NewDesiredStateOfWorld(volumePluginMgr) nodeName := "node-name" volumeName := api.UniqueDeviceName("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)) } }
// Populates data struct with new pod/volume/node. // Calls DeleteNode() to delete the node. // Verifies call fails because node still contains child volumes. func Test_DeleteNode_Negative_NodeExistsHasChildVolumes(t *testing.T) { // Arrange volumePluginMgr, _ := controllervolumetesting.GetTestVolumePluginMgr((t)) dsw := NewDesiredStateOfWorld(volumePluginMgr) nodeName := "node-name" dsw.AddNode(nodeName) podName := "pod-name" volumeName := api.UniqueDeviceName("volume-name") volumeSpec := controllervolumetesting.GetTestVolumeSpec(string(volumeName), volumeName) generatedVolumeName, podAddErr := dsw.AddPod(podName, volumeSpec, nodeName) if podAddErr != nil { t.Fatalf( "AddPod failed for pod %q. Expected: <no error> Actual: <%v>", podName, podAddErr) } // Act err := dsw.DeleteNode(nodeName) // Assert if err == nil { t.Fatalf("DeleteNode did not fail. Expected: <\"failed to delete node...the node still contains volumes in its list of volumes to attach\"> Actual: <no error>") } nodeExists := dsw.NodeExists(nodeName) if !nodeExists { t.Fatalf("Node %q no longer exists, it should.", 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)) }
// Calls AddPod() with new pod/volume/node on empty data struct. // Verifies call fails because node does not exist. func Test_AddPod_Negative_NewPodNodeDoesntExistVolumeDoesntExist(t *testing.T) { // Arrange volumePluginMgr, _ := controllervolumetesting.GetTestVolumePluginMgr((t)) dsw := NewDesiredStateOfWorld(volumePluginMgr) podName := "pod-name" volumeName := api.UniqueDeviceName("volume-name") volumeSpec := controllervolumetesting.GetTestVolumeSpec(string(volumeName), volumeName) nodeName := "node-name" volumeExists := dsw.VolumeExists(volumeName, nodeName) if volumeExists { t.Fatalf( "Volume %q/node %q should not exist, but it does.", volumeName, nodeName) } // Act _, podErr := dsw.AddPod(podName, volumeSpec, nodeName) // Assert if podErr == nil { t.Fatalf("AddPod did not fail. Expected: <\"failed to add pod...no node with that name exists in the list of managed nodes\"> Actual: <no error>") } volumeExists = dsw.VolumeExists(volumeName, nodeName) if volumeExists { t.Fatalf( "Volume %q/node %q should not exist, but it does.", volumeName, nodeName) } volumesToAttach := dsw.GetVolumesToAttach() if len(volumesToAttach) != 0 { t.Fatalf("len(volumesToAttach) Expected: <0> Actual: <%v>", len(volumesToAttach)) } }
// Populates data struct with two nodes with one volume/pod on one node and two // volume/pod pairs on the other node. // Calls GetVolumesToAttach() // Verifies three volumes to attach. func Test_GetVolumesToAttach_Positive_TwoNodesThreeVolumes(t *testing.T) { // Arrange volumePluginMgr, _ := controllervolumetesting.GetTestVolumePluginMgr((t)) dsw := NewDesiredStateOfWorld(volumePluginMgr) node1Name := "node1-name" pod1Name := "pod1-name" volume1Name := api.UniqueDeviceName("volume1-name") volume1Spec := controllervolumetesting.GetTestVolumeSpec(string(volume1Name), volume1Name) dsw.AddNode(node1Name) generatedVolume1Name, podAddErr := dsw.AddPod(pod1Name, volume1Spec, node1Name) if podAddErr != nil { t.Fatalf( "AddPod failed for pod %q. Expected: <no error> Actual: <%v>", pod1Name, podAddErr) } node2Name := "node2-name" pod2aName := "pod2a-name" volume2Name := api.UniqueDeviceName("volume2-name") volume2Spec := controllervolumetesting.GetTestVolumeSpec(string(volume2Name), volume2Name) dsw.AddNode(node2Name) generatedVolume2Name1, podAddErr := dsw.AddPod(pod2aName, volume2Spec, node2Name) if podAddErr != nil { t.Fatalf( "AddPod failed for pod %q. Expected: <no error> Actual: <%v>", pod2aName, podAddErr) } pod2bName := "pod2b-name" generatedVolume2Name2, podAddErr := dsw.AddPod(pod2bName, volume2Spec, node2Name) if podAddErr != nil { t.Fatalf( "AddPod failed for pod %q. Expected: <no error> Actual: <%v>", pod2bName, podAddErr) } if generatedVolume2Name1 != generatedVolume2Name2 { t.Fatalf( "Generated volume names for the same volume should be the same but they are not: %q and %q", generatedVolume2Name1, generatedVolume2Name2) } pod3Name := "pod3-name" volume3Name := api.UniqueDeviceName("volume3-name") volume3Spec := controllervolumetesting.GetTestVolumeSpec(string(volume3Name), volume3Name) generatedVolume3Name, podAddErr := dsw.AddPod(pod3Name, volume3Spec, node1Name) 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) != 3 { t.Fatalf("len(volumesToAttach) Expected: <3> Actual: <%v>", len(volumesToAttach)) } verifyVolumeToAttach(t, volumesToAttach, node1Name, generatedVolume1Name, string(volume1Name)) verifyVolumeToAttach(t, volumesToAttach, node2Name, generatedVolume2Name1, string(volume2Name)) verifyVolumeToAttach(t, volumesToAttach, node1Name, generatedVolume3Name, string(volume3Name)) }
// GetUniqueDeviceName returns a unique name representing the device with the // spcified deviceName of the pluginName volume type. // The returned name can be used to uniquely reference the device. For example, // to prevent operations (attach/detach) from being triggered on the same volume func GetUniqueDeviceName( pluginName, deviceName string) api.UniqueDeviceName { return api.UniqueDeviceName(fmt.Sprintf("%s/%s", pluginName, deviceName)) }
// mountExternalVolumes mounts the volumes declared in a pod, attaching them // to the host if necessary, and returns a map containing information about // the volumes for the pod or an error. This method is run multiple times, // and requires that implementations of Attach() and SetUp() be idempotent. // // Note, in the future, the attach-detach controller will handle attaching and // detaching volumes; this call site will be maintained for backward- // compatibility with current behavior of static pods and pods created via the // Kubelet's http API. func (kl *Kubelet) mountExternalVolumes(pod *api.Pod) (kubecontainer.VolumeMap, error) { podVolumes := make(kubecontainer.VolumeMap) for i := range pod.Spec.Volumes { var fsGroup *int64 if pod.Spec.SecurityContext != nil && pod.Spec.SecurityContext.FSGroup != nil { fsGroup = pod.Spec.SecurityContext.FSGroup } rootContext, err := kl.getRootDirContext() if err != nil { return nil, err } var volSpec *volume.Spec if pod.Spec.Volumes[i].VolumeSource.PersistentVolumeClaim != nil { claimName := pod.Spec.Volumes[i].PersistentVolumeClaim.ClaimName pv, err := kl.getPersistentVolumeByClaimName(claimName, pod.Namespace) if err != nil { glog.Errorf("Could not find persistentVolume for claim %s err %v", claimName, err) return nil, err } kl.applyPersistentVolumeAnnotations(pv, pod) volSpec = volume.NewSpecFromPersistentVolume(pv, pod.Spec.Volumes[i].PersistentVolumeClaim.ReadOnly) } else { volSpec = volume.NewSpecFromVolume(&pod.Spec.Volumes[i]) } // Try to use a plugin for this volume. mounter, err := kl.newVolumeMounterFromPlugins(volSpec, pod, volume.VolumeOptions{RootContext: rootContext}) if err != nil { glog.Errorf("Could not create volume mounter for pod %s: %v", pod.UID, err) return nil, err } // some volumes require attachment before mounter's setup. // The plugin can be nil, but non-nil errors are legitimate errors. // For non-nil plugins, Attachment to a node is required before Mounter's setup. attacher, attachablePlugin, err := kl.newVolumeAttacherFromPlugins(volSpec, pod) if err != nil { glog.Errorf("Could not create volume attacher for pod %s: %v", pod.UID, err) return nil, err } if attacher != nil { // If the device path is already mounted, avoid an expensive call to the // cloud provider. deviceMountPath := attacher.GetDeviceMountPath(volSpec) notMountPoint, err := kl.mounter.IsLikelyNotMountPoint(deviceMountPath) if err != nil && !os.IsNotExist(err) { return nil, err } if notMountPoint { if !kl.enableControllerAttachDetach { err = attacher.Attach(volSpec, kl.hostname) if err != nil { return nil, err } } devicePath, err := attacher.WaitForAttach(volSpec, maxWaitForVolumeOps) if err != nil { return nil, err } if kl.enableControllerAttachDetach { // Attach/Detach controller is enabled and this volume type // implements an attacher uniqueDeviceName, err := attachdetach.GetUniqueDeviceNameFromSpec( attachablePlugin, volSpec) if err != nil { return nil, err } kl.volumeManager.AddVolumeInUse( api.UniqueDeviceName(uniqueDeviceName)) } if err = attacher.MountDevice(volSpec, devicePath, deviceMountPath, kl.mounter); err != nil { return nil, err } } } err = mounter.SetUp(fsGroup) if err != nil { return nil, err } podVolumes[pod.Spec.Volumes[i].Name] = kubecontainer.VolumeInfo{Mounter: mounter} } return podVolumes, nil }