// 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)) }
// 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)) }
// Populates desiredStateOfWorld cache with one volume/pod. // Calls Run() // Verifies there is one attach/mount/etc call and no detach calls. // Deletes volume/pod from desired state of world. // Verifies detach/unmount calls are issued. func Test_Run_Positive_VolumeAttachMountUnmountDetach(t *testing.T) { // Arrange volumePluginMgr, fakePlugin := volumetesting.GetTestVolumePluginMgr(t) dsw := cache.NewDesiredStateOfWorld(volumePluginMgr) asw := cache.NewActualStateOfWorld(nodeName, volumePluginMgr) kubeClient := createTestClient() fakeRecorder := &record.FakeRecorder{} oex := operationexecutor.NewOperationExecutor(kubeClient, volumePluginMgr, fakeRecorder) reconciler := NewReconciler( kubeClient, false, /* controllerAttachDetachEnabled */ reconcilerLoopSleepDuration, reconcilerReconstructSleepPeriod, waitForAttachTimeout, nodeName, dsw, asw, oex, &mount.FakeMounter{}, volumePluginMgr, kubeletPodsDir) 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) generatedVolumeName, err := dsw.AddPodToVolume( podName, pod, volumeSpec, volumeSpec.Name(), "" /* volumeGidValue */) // Assert if err != nil { t.Fatalf("AddPodToVolume failed. Expected: <no error> Actual: <%v>", err) } // Act runReconciler(reconciler) waitForMount(t, fakePlugin, generatedVolumeName, asw) // Assert assert.NoError(t, volumetesting.VerifyAttachCallCount( 1 /* expectedAttachCallCount */, 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)) // Act dsw.DeletePodFromVolume(podName, generatedVolumeName) waitForDetach(t, fakePlugin, generatedVolumeName, asw) // Assert assert.NoError(t, volumetesting.VerifyTearDownCallCount( 1 /* expectedTearDownCallCount */, fakePlugin)) assert.NoError(t, volumetesting.VerifyDetachCallCount( 1 /* expectedDetachCallCount */, fakePlugin)) }
func TestVolumeUnmountAndDetachControllerEnabled(t *testing.T) { testKubelet := newTestKubelet(t, true /* controllerAttachDetachEnabled */) kubelet := testKubelet.kubelet kubeClient := testKubelet.fakeKubeClient kubeClient.AddReactor("get", "nodes", func(action core.Action) (bool, runtime.Object, error) { return true, &api.Node{ ObjectMeta: api.ObjectMeta{Name: testKubeletHostname}, Status: api.NodeStatus{ VolumesAttached: []api.AttachedVolume{ { Name: "fake/vol1", DevicePath: "fake/path", }, }}, Spec: api.NodeSpec{ExternalID: testKubeletHostname}, }, nil }) kubeClient.AddReactor("*", "*", func(action core.Action) (bool, runtime.Object, error) { return true, nil, fmt.Errorf("no reaction implemented for %s", action) }) pod := podWithUidNameNsSpec("12345678", "foo", "test", api.PodSpec{ Volumes: []api.Volume{ { Name: "vol1", VolumeSource: api.VolumeSource{ GCEPersistentDisk: &api.GCEPersistentDiskVolumeSource{ PDName: "fake-device", }, }, }, }, }) stopCh := runVolumeManager(kubelet) defer func() { close(stopCh) }() // Add pod kubelet.podManager.SetPods([]*api.Pod{pod}) // Fake node status update go simulateVolumeInUseUpdate( api.UniqueVolumeName("fake/vol1"), stopCh, kubelet.volumeManager) // Verify volumes attached assert.NoError(t, kubelet.volumeManager.WaitForAttachAndMount(pod)) podVolumes := kubelet.volumeManager.GetMountedVolumesForPod( volumehelper.GetUniquePodName(pod)) expectedPodVolumes := []string{"vol1"} assert.Len(t, podVolumes, len(expectedPodVolumes), "Volumes for pod %+v", pod) for _, name := range expectedPodVolumes { assert.Contains(t, podVolumes, name, "Volumes for pod %+v", pod) } assert.True(t, testKubelet.volumePlugin.GetNewAttacherCallCount() >= 1, "Expected plugin NewAttacher to be called at least once") assert.NoError(t, volumetest.VerifyWaitForAttachCallCount( 1 /* expectedWaitForAttachCallCount */, testKubelet.volumePlugin)) assert.NoError(t, volumetest.VerifyZeroAttachCalls(testKubelet.volumePlugin)) assert.NoError(t, volumetest.VerifyMountDeviceCallCount( 1 /* expectedMountDeviceCallCount */, testKubelet.volumePlugin)) assert.NoError(t, volumetest.VerifySetUpCallCount( 1 /* expectedSetUpCallCount */, testKubelet.volumePlugin)) // Remove pod kubelet.podManager.SetPods([]*api.Pod{}) assert.NoError(t, waitForVolumeUnmount(kubelet.volumeManager, pod)) // Verify volumes unmounted podVolumes = kubelet.volumeManager.GetMountedVolumesForPod( volumehelper.GetUniquePodName(pod)) assert.Len(t, podVolumes, 0, "Expected volumes to be unmounted and detached. But some volumes are still mounted: %#v", podVolumes) assert.NoError(t, volumetest.VerifyTearDownCallCount( 1 /* expectedTearDownCallCount */, testKubelet.volumePlugin)) // Verify volumes detached and no longer reported as in use assert.NoError(t, waitForVolumeDetach(api.UniqueVolumeName("fake/vol1"), kubelet.volumeManager)) assert.True(t, testKubelet.volumePlugin.GetNewAttacherCallCount() >= 1, "Expected plugin NewAttacher to be called at least once") assert.NoError(t, volumetest.VerifyZeroDetachCallCount(testKubelet.volumePlugin)) }