From f3127fe25cd7b05db6c4b56dee38598b2d74bd24 Mon Sep 17 00:00:00 2001 From: Serguei Bezverkhi Date: Wed, 30 Aug 2017 03:20:08 -0400 Subject: [PATCH] e2e additional tests for local volume Closes #51418 --- test/e2e/storage/BUILD | 1 + test/e2e/storage/persistent_volumes-local.go | 160 +++++++++++++++++++ 2 files changed, 161 insertions(+) diff --git a/test/e2e/storage/BUILD b/test/e2e/storage/BUILD index 8d148acc95..18e20ddb97 100644 --- a/test/e2e/storage/BUILD +++ b/test/e2e/storage/BUILD @@ -60,6 +60,7 @@ go_library( "//vendor/k8s.io/apimachinery/pkg/api/errors:go_default_library", "//vendor/k8s.io/apimachinery/pkg/api/resource:go_default_library", "//vendor/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library", + "//vendor/k8s.io/apimachinery/pkg/fields:go_default_library", "//vendor/k8s.io/apimachinery/pkg/labels:go_default_library", "//vendor/k8s.io/apimachinery/pkg/runtime/schema:go_default_library", "//vendor/k8s.io/apimachinery/pkg/types:go_default_library", diff --git a/test/e2e/storage/persistent_volumes-local.go b/test/e2e/storage/persistent_volumes-local.go index 49154dbf27..dc0796359a 100644 --- a/test/e2e/storage/persistent_volumes-local.go +++ b/test/e2e/storage/persistent_volumes-local.go @@ -31,6 +31,7 @@ import ( rbacv1beta1 "k8s.io/api/rbac/v1beta1" "k8s.io/apimachinery/pkg/api/resource" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/fields" "k8s.io/apimachinery/pkg/runtime/schema" utilerrors "k8s.io/apimachinery/pkg/util/errors" "k8s.io/apimachinery/pkg/util/uuid" @@ -252,6 +253,80 @@ var _ = SIGDescribe("PersistentVolumes-local [Feature:LocalPersistentVolumes] [S }) }) + Context("when pod using local volume with non-existant path", func() { + ep := &eventPatterns{ + reason: "FailedMount", + pattern: make([]string, 2)} + ep.pattern = append(ep.pattern, "MountVolume.SetUp failed") + ep.pattern = append(ep.pattern, "does not exist") + + It("should not be able to mount", func() { + for _, testVolType := range LocalVolumeTypes { + By(fmt.Sprintf("local-volume-type: %s", testVolType)) + testVol := &localTestVolume{ + node: config.node0, + hostDir: "/non-existent/location/nowhere", + localVolumeType: testVolType, + } + By("Creating local PVC and PV") + createLocalPVCPV(config, testVol) + pod, err := createLocalPod(config, testVol) + Expect(err).To(HaveOccurred()) + checkPodEvents(config, pod.Name, ep) + } + }) + }) + + Context("when pod's node is different from PV's NodeAffinity", func() { + + BeforeEach(func() { + if len(config.nodes.Items) < 2 { + framework.Skipf("Runs only when number of nodes >= 2") + } + }) + + ep := &eventPatterns{ + reason: "FailedScheduling", + pattern: make([]string, 2)} + ep.pattern = append(ep.pattern, "MatchNodeSelector") + ep.pattern = append(ep.pattern, "No nodes are available") + for _, testVolType := range LocalVolumeTypes { + + It("should not be able to mount due to different NodeAffinity", func() { + + testPodWithNodeName(config, testVolType, ep, config.nodes.Items[1].Name, makeLocalPodWithNodeAffinity) + }) + + It("should not be able to mount due to different NodeSelector", func() { + + testPodWithNodeName(config, testVolType, ep, config.nodes.Items[1].Name, makeLocalPodWithNodeSelector) + }) + + } + }) + + Context("when pod's node is different from PV's NodeName", func() { + + BeforeEach(func() { + if len(config.nodes.Items) < 2 { + framework.Skipf("Runs only when number of nodes >= 2") + } + }) + + ep := &eventPatterns{ + reason: "FailedMount", + pattern: make([]string, 2)} + ep.pattern = append(ep.pattern, "NodeSelectorTerm") + ep.pattern = append(ep.pattern, "Storage node affinity check failed") + for _, testVolType := range LocalVolumeTypes { + + It("should not be able to mount due to different NodeName", func() { + + testPodWithNodeName(config, testVolType, ep, config.nodes.Items[1].Name, makeLocalPodWithNodeName) + }) + } + }) + Context("when using local volume provisioner", func() { var ( volumePath string @@ -313,6 +388,44 @@ var _ = SIGDescribe("PersistentVolumes-local [Feature:LocalPersistentVolumes] [S }) }) +type makeLocalPodWith func(config *localTestConfig, volume *localTestVolume, nodeName string) *v1.Pod + +func testPodWithNodeName(config *localTestConfig, testVolType LocalVolumeType, ep *eventPatterns, nodeName string, makeLocalPodFunc makeLocalPodWith) { + var testVol *localTestVolume + By(fmt.Sprintf("local-volume-type: %s", testVolType)) + testVol = setupLocalVolumePVCPV(config, testVolType) + + pod := makeLocalPodFunc(config, testVol, nodeName) + pod, err := config.client.CoreV1().Pods(config.ns).Create(pod) + Expect(err).NotTo(HaveOccurred()) + err = framework.WaitForPodRunningInNamespace(config.client, pod) + Expect(err).To(HaveOccurred()) + checkPodEvents(config, pod.Name, ep) + cleanupLocalVolume(config, testVol) +} + +type eventPatterns struct { + reason string + pattern []string +} + +func checkPodEvents(config *localTestConfig, podName string, ep *eventPatterns) { + var events *v1.EventList + selector := fields.Set{ + "involvedObject.kind": "Pod", + "involvedObject.name": podName, + "involvedObject.namespace": config.ns, + "reason": ep.reason, + }.AsSelector().String() + options := metav1.ListOptions{FieldSelector: selector} + events, err := config.client.Core().Events(config.ns).List(options) + Expect(err).NotTo(HaveOccurred()) + Expect(len(events.Items)).NotTo(Equal(0)) + for _, p := range ep.pattern { + Expect(events.Items[0].Message).To(ContainSubstring(p)) + } +} + // The tests below are run against multiple mount point types // Test two pods at the same time, write from pod1, and read from pod2 @@ -504,6 +617,53 @@ func makeLocalPod(config *localTestConfig, volume *localTestVolume, cmd string) return framework.MakeSecPod(config.ns, []*v1.PersistentVolumeClaim{volume.pvc}, false, cmd, false, false, selinuxLabel) } +func makeLocalPodWithNodeAffinity(config *localTestConfig, volume *localTestVolume, nodeName string) (pod *v1.Pod) { + pod = framework.MakeSecPod(config.ns, []*v1.PersistentVolumeClaim{volume.pvc}, false, "", false, false, selinuxLabel) + if pod == nil { + return + } + affinity := &v1.Affinity{ + NodeAffinity: &v1.NodeAffinity{ + RequiredDuringSchedulingIgnoredDuringExecution: &v1.NodeSelector{ + NodeSelectorTerms: []v1.NodeSelectorTerm{ + { + MatchExpressions: []v1.NodeSelectorRequirement{ + { + Key: "kubernetes.io/hostname", + Operator: v1.NodeSelectorOpIn, + Values: []string{nodeName}, + }, + }, + }, + }, + }, + }, + } + pod.Spec.Affinity = affinity + return +} + +func makeLocalPodWithNodeSelector(config *localTestConfig, volume *localTestVolume, nodeName string) (pod *v1.Pod) { + pod = framework.MakeSecPod(config.ns, []*v1.PersistentVolumeClaim{volume.pvc}, false, "", false, false, selinuxLabel) + if pod == nil { + return + } + ns := map[string]string{ + "kubernetes.io/hostname": nodeName, + } + pod.Spec.NodeSelector = ns + return +} + +func makeLocalPodWithNodeName(config *localTestConfig, volume *localTestVolume, nodeName string) (pod *v1.Pod) { + pod = framework.MakeSecPod(config.ns, []*v1.PersistentVolumeClaim{volume.pvc}, false, "", false, false, selinuxLabel) + if pod == nil { + return + } + pod.Spec.NodeName = nodeName + return +} + // createSecPod should be used when Pod requires non default SELinux labels func createSecPod(config *localTestConfig, volume *localTestVolume, hostIPC bool, hostPID bool, seLinuxLabel *v1.SELinuxOptions) (*v1.Pod, error) { pod, err := framework.CreateSecPod(config.client, config.ns, []*v1.PersistentVolumeClaim{volume.pvc}, false, "", hostIPC, hostPID, seLinuxLabel)