diff --git a/test/e2e/storage/testsuites/BUILD b/test/e2e/storage/testsuites/BUILD index 237e4cceea6..59483bfac09 100644 --- a/test/e2e/storage/testsuites/BUILD +++ b/test/e2e/storage/testsuites/BUILD @@ -20,12 +20,14 @@ go_library( importpath = "k8s.io/kubernetes/test/e2e/storage/testsuites", visibility = ["//visibility:public"], deps = [ + "//pkg/kubelet/events:go_default_library", "//staging/src/k8s.io/api/core/v1:go_default_library", "//staging/src/k8s.io/api/storage/v1:go_default_library", "//staging/src/k8s.io/apimachinery/pkg/api/errors:go_default_library", "//staging/src/k8s.io/apimachinery/pkg/api/resource:go_default_library", "//staging/src/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library", "//staging/src/k8s.io/apimachinery/pkg/apis/meta/v1/unstructured:go_default_library", + "//staging/src/k8s.io/apimachinery/pkg/fields:go_default_library", "//staging/src/k8s.io/apimachinery/pkg/labels:go_default_library", "//staging/src/k8s.io/apimachinery/pkg/runtime/schema:go_default_library", "//staging/src/k8s.io/apimachinery/pkg/util/errors:go_default_library", diff --git a/test/e2e/storage/testsuites/volumemode.go b/test/e2e/storage/testsuites/volumemode.go index 89dadb3ca73..fabd7184113 100644 --- a/test/e2e/storage/testsuites/volumemode.go +++ b/test/e2e/storage/testsuites/volumemode.go @@ -24,9 +24,12 @@ import ( v1 "k8s.io/api/core/v1" storagev1 "k8s.io/api/storage/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/fields" clientset "k8s.io/client-go/kubernetes" + "k8s.io/kubernetes/pkg/kubelet/events" "k8s.io/kubernetes/test/e2e/framework" e2elog "k8s.io/kubernetes/test/e2e/framework/log" + e2epod "k8s.io/kubernetes/test/e2e/framework/pod" "k8s.io/kubernetes/test/e2e/storage/testpatterns" ) @@ -94,6 +97,11 @@ func (t *volumeModeTestSuite) defineTests(driver TestDriver, pattern testpattern // Now do the more expensive test initialization. l.config, l.testCleanup = driver.PrepareTest(f) l.intreeOps, l.migratedOps = getMigrationVolumeOpCounts(f.ClientSet, dInfo.InTreePluginName) + } + + // manualInit initializes l.genericVolumeTestResource without creating the PV & PVC objects. + manualInit := func() { + init() fsType := pattern.FsType volBindMode := storagev1.VolumeBindingImmediate @@ -167,7 +175,7 @@ func (t *volumeModeTestSuite) defineTests(driver TestDriver, pattern testpattern case testpatterns.PreprovisionedPV: if pattern.VolMode == v1.PersistentVolumeBlock && !isBlockSupported { ginkgo.It("should fail to create pod by failing to mount volume [Slow]", func() { - init() + manualInit() defer cleanup() var err error @@ -196,13 +204,12 @@ func (t *volumeModeTestSuite) defineTests(driver TestDriver, pattern testpattern }() framework.ExpectError(err) }) - // TODO(mkimuram): Add more tests } case testpatterns.DynamicPV: if pattern.VolMode == v1.PersistentVolumeBlock && !isBlockSupported { ginkgo.It("should fail in binding dynamic provisioned PV to PVC [Slow]", func() { - init() + manualInit() defer cleanup() var err error @@ -218,12 +225,57 @@ func (t *volumeModeTestSuite) defineTests(driver TestDriver, pattern testpattern err = framework.WaitForPersistentVolumeClaimPhase(v1.ClaimBound, l.cs, l.pvc.Namespace, l.pvc.Name, framework.Poll, framework.ClaimProvisionTimeout) framework.ExpectError(err) }) - // TODO(mkimuram): Add more tests } default: e2elog.Failf("Volume mode test doesn't support volType: %v", pattern.VolType) } + ginkgo.It("should fail to use a volume in a pod with mismatched mode [Slow]", func() { + skipBlockTest(driver) + init() + l.genericVolumeTestResource = *createGenericVolumeTestResource(driver, l.config, pattern) + defer cleanup() + + ginkgo.By("Creating pod") + var err error + pod := framework.MakeSecPod(l.ns.Name, []*v1.PersistentVolumeClaim{l.pvc}, nil, false, "", false, false, framework.SELinuxLabel, nil) + // Change volumeMounts to volumeDevices and the other way around + pod = swapVolumeMode(pod) + + // Run the pod + pod, err = l.cs.CoreV1().Pods(l.ns.Name).Create(pod) + framework.ExpectNoError(err) + defer func() { + framework.ExpectNoError(framework.DeletePodWithWait(f, l.cs, pod)) + }() + + ginkgo.By("Waiting for pod to fail") + // Wait for an event that the pod is invalid. + eventSelector := fields.Set{ + "involvedObject.kind": "Pod", + "involvedObject.name": pod.Name, + "involvedObject.namespace": l.ns.Name, + "reason": events.FailedMountVolume, + }.AsSelector().String() + + var msg string + if pattern.VolMode == v1.PersistentVolumeBlock { + msg = "has volumeMode Block, but is specified in volumeMounts" + } else { + msg = "has volumeMode Filesystem, but is specified in volumeDevices" + } + err = e2epod.WaitTimeoutForPodEvent(l.cs, pod.Name, l.ns.Name, eventSelector, msg, framework.PodStartTimeout) + // Events are unreliable, don't depend on them. They're used only to speed up the test. + if err != nil { + e2elog.Logf("Warning: did not get event about mismatched volume use") + } + + // Check the pod is still not running + p, err := l.cs.CoreV1().Pods(l.ns.Name).Get(pod.Name, metav1.GetOptions{}) + framework.ExpectNoError(err, "could not re-read the pod after event (or timeout)") + framework.ExpectEqual(p.Status.Phase, v1.PodPending) + }) + } func generateConfigsForPreprovisionedPVTest(scName string, volBindMode storagev1.VolumeBindingMode, @@ -254,3 +306,29 @@ func generateConfigsForPreprovisionedPVTest(scName string, volBindMode storagev1 return scConfig, pvConfig, pvcConfig } + +// swapVolumeMode changes volumeMounts to volumeDevices and the other way around +func swapVolumeMode(podTemplate *v1.Pod) *v1.Pod { + pod := podTemplate.DeepCopy() + for c := range pod.Spec.Containers { + container := &pod.Spec.Containers[c] + container.VolumeDevices = []v1.VolumeDevice{} + container.VolumeMounts = []v1.VolumeMount{} + + // Change VolumeMounts to VolumeDevices + for _, volumeMount := range podTemplate.Spec.Containers[c].VolumeMounts { + container.VolumeDevices = append(container.VolumeDevices, v1.VolumeDevice{ + Name: volumeMount.Name, + DevicePath: volumeMount.MountPath, + }) + } + // Change VolumeDevices to VolumeMounts + for _, volumeDevice := range podTemplate.Spec.Containers[c].VolumeDevices { + container.VolumeMounts = append(container.VolumeMounts, v1.VolumeMount{ + Name: volumeDevice.Name, + MountPath: volumeDevice.DevicePath, + }) + } + } + return pod +}