diff --git a/pkg/kubelet/volumemanager/populator/desired_state_of_world_populator.go b/pkg/kubelet/volumemanager/populator/desired_state_of_world_populator.go index 31c5de4e8ae..4111f73f9a8 100644 --- a/pkg/kubelet/volumemanager/populator/desired_state_of_world_populator.go +++ b/pkg/kubelet/volumemanager/populator/desired_state_of_world_populator.go @@ -296,12 +296,12 @@ func (dswp *desiredStateOfWorldPopulator) processPodVolumes( } allVolumesAdded := true - mountsMap, devicesMap := dswp.makeVolumeMap(pod.Spec.Containers) + mounts, devices := util.GetPodVolumeNames(pod) // Process volume spec for each volume defined in pod for _, podVolume := range pod.Spec.Volumes { pvc, volumeSpec, volumeGidValue, err := - dswp.createVolumeSpec(podVolume, pod.Name, pod.Namespace, mountsMap, devicesMap) + dswp.createVolumeSpec(podVolume, pod.Name, pod.Namespace, mounts, devices) if err != nil { klog.Errorf( "Error processing volume %q for pod %q: %v", @@ -485,7 +485,7 @@ func (dswp *desiredStateOfWorldPopulator) deleteProcessedPod( // specified volume. It dereference any PVC to get PV objects, if needed. // Returns an error if unable to obtain the volume at this time. func (dswp *desiredStateOfWorldPopulator) createVolumeSpec( - podVolume v1.Volume, podName string, podNamespace string, mountsMap map[string]bool, devicesMap map[string]bool) (*v1.PersistentVolumeClaim, *volume.Spec, string, error) { + podVolume v1.Volume, podName string, podNamespace string, mounts, devices sets.String) (*v1.PersistentVolumeClaim, *volume.Spec, string, error) { if pvcSource := podVolume.VolumeSource.PersistentVolumeClaim; pvcSource != nil { klog.V(5).Infof( @@ -538,14 +538,14 @@ func (dswp *desiredStateOfWorldPopulator) createVolumeSpec( return nil, nil, "", err } // Error if a container has volumeMounts but the volumeMode of PVC isn't Filesystem - if mountsMap[podVolume.Name] && volumeMode != v1.PersistentVolumeFilesystem { + if mounts.Has(podVolume.Name) && volumeMode != v1.PersistentVolumeFilesystem { return nil, nil, "", fmt.Errorf( "volume %s has volumeMode %s, but is specified in volumeMounts", podVolume.Name, volumeMode) } // Error if a container has volumeDevices but the volumeMode of PVC isn't Block - if devicesMap[podVolume.Name] && volumeMode != v1.PersistentVolumeBlock { + if devices.Has(podVolume.Name) && volumeMode != v1.PersistentVolumeBlock { return nil, nil, "", fmt.Errorf( "volume %s has volumeMode %s, but is specified in volumeDevices", podVolume.Name, @@ -628,28 +628,6 @@ func (dswp *desiredStateOfWorldPopulator) getPVSpec( return volume.NewSpecFromPersistentVolume(pv, pvcReadOnly), volumeGidValue, nil } -func (dswp *desiredStateOfWorldPopulator) makeVolumeMap(containers []v1.Container) (map[string]bool, map[string]bool) { - volumeDevicesMap := make(map[string]bool) - volumeMountsMap := make(map[string]bool) - - for _, container := range containers { - if container.VolumeMounts != nil { - for _, mount := range container.VolumeMounts { - volumeMountsMap[mount.Name] = true - } - } - // TODO: remove feature gate check after no longer needed - if utilfeature.DefaultFeatureGate.Enabled(features.BlockVolume) && - container.VolumeDevices != nil { - for _, device := range container.VolumeDevices { - volumeDevicesMap[device.Name] = true - } - } - } - - return volumeMountsMap, volumeDevicesMap -} - func getPVVolumeGidAnnotationValue(pv *v1.PersistentVolume) string { if volumeGid, ok := pv.Annotations[util.VolumeGidAnnotationKey]; ok { return volumeGid diff --git a/pkg/kubelet/volumemanager/populator/desired_state_of_world_populator_test.go b/pkg/kubelet/volumemanager/populator/desired_state_of_world_populator_test.go index f2e6b8d567c..7c8d2f112c5 100644 --- a/pkg/kubelet/volumemanager/populator/desired_state_of_world_populator_test.go +++ b/pkg/kubelet/volumemanager/populator/desired_state_of_world_populator_test.go @@ -22,7 +22,7 @@ import ( "fmt" - "k8s.io/api/core/v1" + v1 "k8s.io/api/core/v1" "k8s.io/apimachinery/pkg/api/resource" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/runtime" @@ -410,7 +410,7 @@ func TestCreateVolumeSpec_Valid_File_VolumeMounts(t *testing.T) { pod := createPodWithVolume("dswp-test-pod", "dswp-test-volume-name", "file-bound", containers) fakePodManager.AddPod(pod) - mountsMap, devicesMap := dswp.makeVolumeMap(pod.Spec.Containers) + mountsMap, devicesMap := util.GetPodVolumeNames(pod) _, volumeSpec, _, err := dswp.createVolumeSpec(pod.Spec.Volumes[0], pod.Name, pod.Namespace, mountsMap, devicesMap) @@ -459,7 +459,7 @@ func TestCreateVolumeSpec_Valid_Block_VolumeDevices(t *testing.T) { pod := createPodWithVolume("dswp-test-pod", "dswp-test-volume-name", "block-bound", containers) fakePodManager.AddPod(pod) - mountsMap, devicesMap := dswp.makeVolumeMap(pod.Spec.Containers) + mountsMap, devicesMap := util.GetPodVolumeNames(pod) _, volumeSpec, _, err := dswp.createVolumeSpec(pod.Spec.Volumes[0], pod.Name, pod.Namespace, mountsMap, devicesMap) @@ -508,7 +508,7 @@ func TestCreateVolumeSpec_Invalid_File_VolumeDevices(t *testing.T) { pod := createPodWithVolume("dswp-test-pod", "dswp-test-volume-name", "file-bound", containers) fakePodManager.AddPod(pod) - mountsMap, devicesMap := dswp.makeVolumeMap(pod.Spec.Containers) + mountsMap, devicesMap := util.GetPodVolumeNames(pod) _, volumeSpec, _, err := dswp.createVolumeSpec(pod.Spec.Volumes[0], pod.Name, pod.Namespace, mountsMap, devicesMap) @@ -557,7 +557,7 @@ func TestCreateVolumeSpec_Invalid_Block_VolumeMounts(t *testing.T) { pod := createPodWithVolume("dswp-test-pod", "dswp-test-volume-name", "block-bound", containers) fakePodManager.AddPod(pod) - mountsMap, devicesMap := dswp.makeVolumeMap(pod.Spec.Containers) + mountsMap, devicesMap := util.GetPodVolumeNames(pod) _, volumeSpec, _, err := dswp.createVolumeSpec(pod.Spec.Volumes[0], pod.Name, pod.Namespace, mountsMap, devicesMap) diff --git a/pkg/volume/util/util.go b/pkg/volume/util/util.go index f1a8affb9fb..89dfb21f045 100644 --- a/pkg/volume/util/util.go +++ b/pkg/volume/util/util.go @@ -548,3 +548,31 @@ func IsLocalEphemeralVolume(volume v1.Volume) bool { (volume.EmptyDir != nil && volume.EmptyDir.Medium != v1.StorageMediumMemory) || volume.ConfigMap != nil || volume.DownwardAPI != nil } + +// GetPodVolumeNames returns names of volumes that are used in a pod, +// either as filesystem mount or raw block device. +func GetPodVolumeNames(pod *v1.Pod) (mounts sets.String, devices sets.String) { + mounts = sets.NewString() + devices = sets.NewString() + + addContainerVolumes(pod.Spec.Containers, mounts, devices) + addContainerVolumes(pod.Spec.InitContainers, mounts, devices) + return +} + +func addContainerVolumes(containers []v1.Container, mounts, devices sets.String) { + for _, container := range containers { + if container.VolumeMounts != nil { + for _, mount := range container.VolumeMounts { + mounts.Insert(mount.Name) + } + } + // TODO: remove feature gate check after no longer needed + if utilfeature.DefaultFeatureGate.Enabled(features.BlockVolume) && + container.VolumeDevices != nil { + for _, device := range container.VolumeDevices { + devices.Insert(device.Name) + } + } + } +} diff --git a/pkg/volume/util/util_test.go b/pkg/volume/util/util_test.go index 3ad893f82a7..fea30ce346b 100644 --- a/pkg/volume/util/util_test.go +++ b/pkg/volume/util/util_test.go @@ -24,6 +24,7 @@ import ( v1 "k8s.io/api/core/v1" + "k8s.io/apimachinery/pkg/util/sets" _ "k8s.io/kubernetes/pkg/apis/core/install" "reflect" @@ -658,3 +659,180 @@ func TestMakeAbsolutePath(t *testing.T) { } } } + +func TestGetPodVolumeNames(t *testing.T) { + tests := []struct { + name string + pod *v1.Pod + expectedMounts sets.String + expectedDevices sets.String + }{ + { + name: "empty pod", + pod: &v1.Pod{ + Spec: v1.PodSpec{}, + }, + expectedMounts: sets.NewString(), + expectedDevices: sets.NewString(), + }, + { + name: "pod with volumes", + pod: &v1.Pod{ + Spec: v1.PodSpec{ + Containers: []v1.Container{ + { + Name: "container", + VolumeMounts: []v1.VolumeMount{ + { + Name: "vol1", + }, + { + Name: "vol2", + }, + }, + VolumeDevices: []v1.VolumeDevice{ + { + Name: "vol3", + }, + { + Name: "vol4", + }, + }, + }, + }, + Volumes: []v1.Volume{ + { + Name: "vol1", + }, + { + Name: "vol2", + }, + { + Name: "vol3", + }, + { + Name: "vol4", + }, + }, + }, + }, + expectedMounts: sets.NewString("vol1", "vol2"), + expectedDevices: sets.NewString("vol3", "vol4"), + }, + { + name: "pod with init containers", + pod: &v1.Pod{ + Spec: v1.PodSpec{ + InitContainers: []v1.Container{ + { + Name: "initContainer", + VolumeMounts: []v1.VolumeMount{ + { + Name: "vol1", + }, + { + Name: "vol2", + }, + }, + VolumeDevices: []v1.VolumeDevice{ + { + Name: "vol3", + }, + { + Name: "vol4", + }, + }, + }, + }, + Volumes: []v1.Volume{ + { + Name: "vol1", + }, + { + Name: "vol2", + }, + { + Name: "vol3", + }, + { + Name: "vol4", + }, + }, + }, + }, + expectedMounts: sets.NewString("vol1", "vol2"), + expectedDevices: sets.NewString("vol3", "vol4"), + }, + { + name: "pod with multiple containers", + pod: &v1.Pod{ + Spec: v1.PodSpec{ + InitContainers: []v1.Container{ + { + Name: "initContainer1", + VolumeMounts: []v1.VolumeMount{ + { + Name: "vol1", + }, + }, + }, + { + Name: "initContainer2", + VolumeDevices: []v1.VolumeDevice{ + { + Name: "vol2", + }, + }, + }, + }, + Containers: []v1.Container{ + { + Name: "container1", + VolumeMounts: []v1.VolumeMount{ + { + Name: "vol3", + }, + }, + }, + { + Name: "container2", + VolumeDevices: []v1.VolumeDevice{ + { + Name: "vol4", + }, + }, + }, + }, + Volumes: []v1.Volume{ + { + Name: "vol1", + }, + { + Name: "vol2", + }, + { + Name: "vol3", + }, + { + Name: "vol4", + }, + }, + }, + }, + expectedMounts: sets.NewString("vol1", "vol3"), + expectedDevices: sets.NewString("vol2", "vol4"), + }, + } + + for _, test := range tests { + t.Run(test.name, func(t *testing.T) { + mounts, devices := GetPodVolumeNames(test.pod) + if !mounts.Equal(test.expectedMounts) { + t.Errorf("Expected mounts: %q, got %q", mounts.List(), test.expectedMounts.List()) + } + if !devices.Equal(test.expectedDevices) { + t.Errorf("Expected devices: %q, got %q", devices.List(), test.expectedDevices.List()) + } + }) + } +} diff --git a/test/e2e/framework/endpoints/BUILD b/test/e2e/framework/endpoints/BUILD index 43020ee8a4b..2310197e48c 100644 --- a/test/e2e/framework/endpoints/BUILD +++ b/test/e2e/framework/endpoints/BUILD @@ -2,14 +2,19 @@ load("@io_bazel_rules_go//go:def.bzl", "go_library") go_library( name = "go_default_library", - srcs = ["ports.go"], + srcs = [ + "ports.go", + "wait.go", + ], importpath = "k8s.io/kubernetes/test/e2e/framework/endpoints", visibility = ["//visibility:public"], deps = [ "//staging/src/k8s.io/api/core/v1:go_default_library", + "//staging/src/k8s.io/apimachinery/pkg/api/errors:go_default_library", "//staging/src/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library", "//staging/src/k8s.io/apimachinery/pkg/types:go_default_library", "//staging/src/k8s.io/client-go/kubernetes:go_default_library", + "//test/e2e/framework:go_default_library", "//test/e2e/framework/log:go_default_library", "//vendor/github.com/onsi/ginkgo:go_default_library", ],