// 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) }
func (dsw *desiredStateOfWorld) AddPodToVolume( podName types.UniquePodName, pod *api.Pod, volumeSpec *volume.Spec, outerVolumeSpecName string, volumeGidValue string) (api.UniqueVolumeName, error) { dsw.Lock() defer dsw.Unlock() volumePlugin, err := dsw.volumePluginMgr.FindPluginBySpec(volumeSpec) if err != nil || volumePlugin == nil { return "", fmt.Errorf( "failed to get Plugin from volumeSpec for volume %q err=%v", volumeSpec.Name(), err) } volumeName, err := volumehelper.GetUniqueVolumeNameFromSpec(volumePlugin, volumeSpec) if err != nil { return "", fmt.Errorf( "failed to GetUniqueVolumeNameFromSpec for volumeSpec %q using volume plugin %q err=%v", volumeSpec.Name(), volumePlugin.GetPluginName(), err) } volumeObj, volumeExists := dsw.volumesToMount[volumeName] if !volumeExists { volumeObj = volumeToMount{ volumeName: volumeName, podsToMount: make(map[types.UniquePodName]podToMount), pluginIsAttachable: dsw.isAttachableVolume(volumeSpec), volumeGidValue: volumeGidValue, reportedInUse: false, } dsw.volumesToMount[volumeName] = volumeObj } // Create new podToMount object. If it already exists, it is refreshed with // updated values (this is required for volumes that require remounting on // pod update, like Downward API volumes). dsw.volumesToMount[volumeName].podsToMount[podName] = podToMount{ podName: podName, pod: pod, spec: volumeSpec, outerVolumeSpecName: outerVolumeSpecName, } return volumeName, nil }
// 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>") } verifyVolumeExistsAsw(t, volumeName, false /* shouldExist */, asw) verifyVolumeDoesntExistInUnmountedVolumes(t, volumeName, asw) verifyVolumeDoesntExistInGloballyMountedVolumes(t, volumeName, asw) verifyPodDoesntExistInVolumeAsw( t, podName, volumeName, false, /* expectVolumeToExist */ asw) }
// processPodVolumes processes the volumes in the given pod and adds them to the // desired state of the world if addVolumes is true, otherwise it removes them. func (adc *attachDetachController) processPodVolumes( pod *api.Pod, addVolumes bool) { if pod == nil { return } if len(pod.Spec.Volumes) <= 0 { return } if !adc.desiredStateOfWorld.NodeExists(pod.Spec.NodeName) { // If the node the pod is scheduled to does not exist in the desired // state of the world data structure, that indicates the node is not // yet managed by the controller. Therefore, ignore the pod. // If the node is added to the list of managed nodes in the future, // future adds and updates to the pod will be processed. glog.V(10).Infof( "Skipping processing of pod %q/%q: it is scheduled to node %q which is not managed by the controller.", pod.Namespace, pod.Name, pod.Spec.NodeName) return } // Process volume spec for each volume defined in pod for _, podVolume := range pod.Spec.Volumes { volumeSpec, err := adc.createVolumeSpec(podVolume, pod.Namespace) if err != nil { glog.V(10).Infof( "Error processing volume %q for pod %q/%q: %v", podVolume.Name, pod.Namespace, pod.Name, err) continue } attachableVolumePlugin, err := adc.volumePluginMgr.FindAttachablePluginBySpec(volumeSpec) if err != nil || attachableVolumePlugin == nil { glog.V(10).Infof( "Skipping volume %q for pod %q/%q: it does not implement attacher interface. err=%v", podVolume.Name, pod.Namespace, pod.Name, err) continue } uniquePodName := volumehelper.GetUniquePodName(pod) if addVolumes { // Add volume to desired state of world _, err := adc.desiredStateOfWorld.AddPod( uniquePodName, pod, volumeSpec, pod.Spec.NodeName) if err != nil { glog.V(10).Infof( "Failed to add volume %q for pod %q/%q to desiredStateOfWorld. %v", podVolume.Name, pod.Namespace, pod.Name, err) } } else { // Remove volume from desired state of world uniqueVolumeName, err := volumehelper.GetUniqueVolumeNameFromSpec( attachableVolumePlugin, volumeSpec) if err != nil { glog.V(10).Infof( "Failed to delete volume %q for pod %q/%q from desiredStateOfWorld. GetUniqueVolumeNameFromSpec failed with %v", podVolume.Name, pod.Namespace, pod.Name, err) continue } adc.desiredStateOfWorld.DeletePod( uniquePodName, uniqueVolumeName, pod.Spec.NodeName) } } return }