kubelet: more tests for generic ephemeral volumes

This simulates various error scenarios (PVC not created for pod,
feature disabled) and switching between feature disabled and enabled.
This commit is contained in:
Patrick Ohly 2021-02-19 13:51:49 +01:00
parent edb9a8584c
commit 68370c8aa6

View File

@ -22,6 +22,7 @@ import (
"fmt"
"github.com/stretchr/testify/require"
v1 "k8s.io/api/core/v1"
"k8s.io/apimachinery/pkg/api/resource"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
@ -454,6 +455,124 @@ func TestFindAndRemoveNonattachableVolumes(t *testing.T) {
}
}
func TestEphemeralVolumeOwnerCheck(t *testing.T) {
defer featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.GenericEphemeralVolume, true)()
// create dswp
pod, pv, pvc := createEphemeralVolumeObjects("dswp-test-pod", "dswp-test-volume-name", false /* not owned */)
dswp, fakePodManager, _ := createDswpWithVolume(t, pv, pvc)
fakePodManager.AddPod(pod)
podName := util.GetUniquePodName(pod)
dswp.findAndAddNewPods()
if dswp.pods.processedPods[podName] {
t.Fatalf("%s should not have been processed by the populator", podName)
}
require.Equal(t,
[]string{fmt.Sprintf("error processing PVC %s/%s: not the ephemeral PVC for the pod", pvc.Namespace, pvc.Name)},
dswp.desiredStateOfWorld.PopPodErrors(podName),
)
}
func TestEphemeralVolumeEnablement(t *testing.T) {
// create dswp
pod, pv, pvc := createEphemeralVolumeObjects("dswp-test-pod", "dswp-test-volume-name", true /* owned */)
dswp, fakePodManager, fakesDSW := createDswpWithVolume(t, pv, pvc)
fakePodManager.AddPod(pod)
podName := util.GetUniquePodName(pod)
volumeName := pod.Spec.Volumes[0].Name
generatedVolumeName := "fake-plugin/" + volumeName
// Feature disabled -> refuse to process pod.
defer featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.GenericEphemeralVolume, false)()
dswp.findAndAddNewPods()
if dswp.pods.processedPods[podName] {
t.Fatalf("%s should not have been processed by the populator", podName)
}
require.Equal(t,
[]string{fmt.Sprintf("volume %s is a generic ephemeral volume, but that feature is disabled in kubelet", volumeName)},
dswp.desiredStateOfWorld.PopPodErrors(podName),
)
// Enabled -> process pod.
defer featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.GenericEphemeralVolume, true)()
dswp.findAndAddNewPods()
if !dswp.pods.processedPods[podName] {
t.Fatalf("Failed to record that the volumes for the specified pod: %s have been processed by the populator", podName)
}
expectedVolumeName := v1.UniqueVolumeName(generatedVolumeName)
volumeExists := fakesDSW.VolumeExists(expectedVolumeName)
if !volumeExists {
t.Fatalf(
"VolumeExists(%q) failed. Expected: <true> Actual: <%v>",
expectedVolumeName,
volumeExists)
}
if podExistsInVolume := fakesDSW.PodExistsInVolume(
podName, expectedVolumeName); !podExistsInVolume {
t.Fatalf(
"DSW PodExistsInVolume returned incorrect value. Expected: <true> Actual: <%v>",
podExistsInVolume)
}
verifyVolumeExistsInVolumesToMount(
t, v1.UniqueVolumeName(generatedVolumeName), false /* expectReportedInUse */, fakesDSW)
//let the pod be terminated
podGet, exist := fakePodManager.GetPodByName(pod.Namespace, pod.Name)
if !exist {
t.Fatalf("Failed to get pod by pod name: %s and namespace: %s", pod.Name, pod.Namespace)
}
podGet.Status.Phase = v1.PodFailed
// Pretend again that the feature is disabled.
// Removal of the pod and volumes is expected to work.
defer featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.GenericEphemeralVolume, false)()
dswp.findAndRemoveDeletedPods()
// Although Pod status is terminated, pod still exists in pod manager and actual state does not has this pod and volume information
// desired state populator will fail to delete this pod and volume first
volumeExists = fakesDSW.VolumeExists(expectedVolumeName)
if !volumeExists {
t.Fatalf(
"VolumeExists(%q) failed. Expected: <true> Actual: <%v>",
expectedVolumeName,
volumeExists)
}
if podExistsInVolume := fakesDSW.PodExistsInVolume(
podName, expectedVolumeName); !podExistsInVolume {
t.Fatalf(
"DSW PodExistsInVolume returned incorrect value. Expected: <true> Actual: <%v>",
podExistsInVolume)
}
// reconcile with actual state so that volume is added into the actual state
// desired state populator now can successfully delete the pod and volume
fakeASW := dswp.actualStateOfWorld
reconcileASW(fakeASW, fakesDSW, t)
dswp.findAndRemoveDeletedPods()
volumeExists = fakesDSW.VolumeExists(expectedVolumeName)
if volumeExists {
t.Fatalf(
"VolumeExists(%q) failed. Expected: <false> Actual: <%v>",
expectedVolumeName,
volumeExists)
}
if podExistsInVolume := fakesDSW.PodExistsInVolume(
podName, expectedVolumeName); podExistsInVolume {
t.Fatalf(
"DSW PodExistsInVolume returned incorrect value. Expected: <false> Actual: <%v>",
podExistsInVolume)
}
}
func TestFindAndAddNewPods_FindAndRemoveDeletedPods_Valid_Block_VolumeDevices(t *testing.T) {
// create dswp
mode := v1.PersistentVolumeBlock
@ -1076,6 +1195,74 @@ func createPodWithVolume(pod, pv, pvc string, containers []v1.Container) *v1.Pod
}
}
func createEphemeralVolumeObjects(podName, volumeName string, owned bool) (pod *v1.Pod, pv *v1.PersistentVolume, pvc *v1.PersistentVolumeClaim) {
pod = &v1.Pod{
ObjectMeta: metav1.ObjectMeta{
Name: podName,
UID: "dswp-test-pod-uid",
Namespace: "dswp-test",
},
Spec: v1.PodSpec{
Volumes: []v1.Volume{
{
Name: volumeName,
VolumeSource: v1.VolumeSource{
Ephemeral: &v1.EphemeralVolumeSource{
VolumeClaimTemplate: &v1.PersistentVolumeClaimTemplate{},
},
},
},
},
Containers: []v1.Container{
{
VolumeMounts: []v1.VolumeMount{
{
Name: volumeName,
MountPath: "/mnt",
},
},
},
},
},
Status: v1.PodStatus{
Phase: v1.PodPhase("Running"),
},
}
mode := v1.PersistentVolumeFilesystem
pv = &v1.PersistentVolume{
ObjectMeta: metav1.ObjectMeta{
Name: volumeName,
},
Spec: v1.PersistentVolumeSpec{
ClaimRef: &v1.ObjectReference{Namespace: "ns", Name: "file-bound"},
VolumeMode: &mode,
},
}
pvc = &v1.PersistentVolumeClaim{
ObjectMeta: metav1.ObjectMeta{
Name: podName + "-" + volumeName,
Namespace: pod.Namespace,
},
Spec: v1.PersistentVolumeClaimSpec{
VolumeName: "dswp-test-volume-name",
},
Status: v1.PersistentVolumeClaimStatus{
Phase: v1.ClaimBound,
},
}
if owned {
controller := true
pvc.OwnerReferences = []metav1.OwnerReference{
{
UID: pod.UID,
Name: podName,
Controller: &controller,
},
}
}
return
}
func createDswpWithVolume(t *testing.T, pv *v1.PersistentVolume, pvc *v1.PersistentVolumeClaim) (*desiredStateOfWorldPopulator, kubepod.Manager, cache.DesiredStateOfWorld) {
fakeVolumePluginMgr, _ := volumetesting.GetTestKubeletVolumePluginMgr(t)
dswp, fakePodManager, fakesDSW := createDswpWithVolumeWithCustomPluginMgr(t, pv, pvc, fakeVolumePluginMgr)