Skip to content

Commit e05be44

Browse files
authored
Merge pull request kubernetes#74693 from mkimuram/issue/74545
Add e2e tests for multiAttach
2 parents 4277079 + b4c88ac commit e05be44

16 files changed

+638
-180
lines changed

test/e2e/framework/pv_util.go

Lines changed: 52 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -93,6 +93,14 @@ type PersistentVolumeClaimConfig struct {
9393
VolumeMode *v1.PersistentVolumeMode
9494
}
9595

96+
// NodeSelection specifies where to run a pod, using a combination of fixed node name,
97+
// node selector and/or affinity.
98+
type NodeSelection struct {
99+
Name string
100+
Selector map[string]string
101+
Affinity *v1.Affinity
102+
}
103+
96104
// Clean up a pv and pvc in a single pv/pvc test case.
97105
// Note: delete errors are appended to []error so that we can attempt to delete both the pvc and pv.
98106
func PVPVCCleanup(c clientset.Interface, ns string, pv *v1.PersistentVolume, pvc *v1.PersistentVolumeClaim) []error {
@@ -873,14 +881,16 @@ func CreateNginxPod(client clientset.Interface, namespace string, nodeSelector m
873881

874882
// create security pod with given claims
875883
func CreateSecPod(client clientset.Interface, namespace string, pvclaims []*v1.PersistentVolumeClaim, isPrivileged bool, command string, hostIPC bool, hostPID bool, seLinuxLabel *v1.SELinuxOptions, fsGroup *int64, timeout time.Duration) (*v1.Pod, error) {
876-
return CreateSecPodWithNodeName(client, namespace, pvclaims, isPrivileged, command, hostIPC, hostPID, seLinuxLabel, fsGroup, "", timeout)
884+
return CreateSecPodWithNodeSelection(client, namespace, pvclaims, isPrivileged, command, hostIPC, hostPID, seLinuxLabel, fsGroup, NodeSelection{}, timeout)
877885
}
878886

879887
// create security pod with given claims
880-
func CreateSecPodWithNodeName(client clientset.Interface, namespace string, pvclaims []*v1.PersistentVolumeClaim, isPrivileged bool, command string, hostIPC bool, hostPID bool, seLinuxLabel *v1.SELinuxOptions, fsGroup *int64, nodeName string, timeout time.Duration) (*v1.Pod, error) {
888+
func CreateSecPodWithNodeSelection(client clientset.Interface, namespace string, pvclaims []*v1.PersistentVolumeClaim, isPrivileged bool, command string, hostIPC bool, hostPID bool, seLinuxLabel *v1.SELinuxOptions, fsGroup *int64, node NodeSelection, timeout time.Duration) (*v1.Pod, error) {
881889
pod := MakeSecPod(namespace, pvclaims, isPrivileged, command, hostIPC, hostPID, seLinuxLabel, fsGroup)
882-
// Setting nodeName
883-
pod.Spec.NodeName = nodeName
890+
// Setting node
891+
pod.Spec.NodeName = node.Name
892+
pod.Spec.NodeSelector = node.Selector
893+
pod.Spec.Affinity = node.Affinity
884894

885895
pod, err := client.CoreV1().Pods(namespace).Create(pod)
886896
if err != nil {
@@ -900,6 +910,44 @@ func CreateSecPodWithNodeName(client clientset.Interface, namespace string, pvcl
900910
return pod, nil
901911
}
902912

913+
// SetNodeAffinityRequirement sets affinity with specified operator to nodeName to nodeSelection
914+
func SetNodeAffinityRequirement(nodeSelection *NodeSelection, operator v1.NodeSelectorOperator, nodeName string) {
915+
// Add node-anti-affinity.
916+
if nodeSelection.Affinity == nil {
917+
nodeSelection.Affinity = &v1.Affinity{}
918+
}
919+
if nodeSelection.Affinity.NodeAffinity == nil {
920+
nodeSelection.Affinity.NodeAffinity = &v1.NodeAffinity{}
921+
}
922+
if nodeSelection.Affinity.NodeAffinity.RequiredDuringSchedulingIgnoredDuringExecution == nil {
923+
nodeSelection.Affinity.NodeAffinity.RequiredDuringSchedulingIgnoredDuringExecution = &v1.NodeSelector{}
924+
}
925+
nodeSelection.Affinity.NodeAffinity.RequiredDuringSchedulingIgnoredDuringExecution.NodeSelectorTerms = append(nodeSelection.Affinity.NodeAffinity.RequiredDuringSchedulingIgnoredDuringExecution.NodeSelectorTerms,
926+
v1.NodeSelectorTerm{
927+
// https://kubernetes.io/docs/concepts/configuration/assign-pod-node/#affinity-and-anti-affinity warns
928+
// that "the value of kubernetes.io/hostname may be the same as the Node name in some environments and a different value in other environments".
929+
// So this might be cleaner:
930+
// MatchFields: []v1.NodeSelectorRequirement{
931+
// {Key: "name", Operator: v1.NodeSelectorOpNotIn, Values: []string{nodeName}},
932+
// },
933+
// However, "name", "Name", "ObjectMeta.Name" all got rejected with "not a valid field selector key".
934+
935+
MatchExpressions: []v1.NodeSelectorRequirement{
936+
{Key: "kubernetes.io/hostname", Operator: operator, Values: []string{nodeName}},
937+
},
938+
})
939+
}
940+
941+
// SetAffinity sets affinity to nodeName to nodeSelection
942+
func SetAffinity(nodeSelection *NodeSelection, nodeName string) {
943+
SetNodeAffinityRequirement(nodeSelection, v1.NodeSelectorOpIn, nodeName)
944+
}
945+
946+
// SetAntiAffinity sets anti-affinity to nodeName to nodeSelection
947+
func SetAntiAffinity(nodeSelection *NodeSelection, nodeName string) {
948+
SetNodeAffinityRequirement(nodeSelection, v1.NodeSelectorOpNotIn, nodeName)
949+
}
950+
903951
// Define and create a pod with a mounted PV. Pod runs infinite loop until killed.
904952
func CreateClientPod(c clientset.Interface, ns string, pvc *v1.PersistentVolumeClaim) (*v1.Pod, error) {
905953
return CreatePod(c, ns, nil, []*v1.PersistentVolumeClaim{pvc}, true, "")

test/e2e/storage/csi_mock_volume.go

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -153,12 +153,12 @@ var _ = utils.SIGDescribe("CSI mock volume", func() {
153153
scTest.AllowVolumeExpansion = true
154154
}
155155

156-
nodeSelection := testsuites.NodeSelection{
156+
nodeSelection := framework.NodeSelection{
157157
// The mock driver only works when everything runs on a single node.
158158
Name: nodeName,
159159
}
160160
if len(m.nodeLabel) > 0 {
161-
nodeSelection = testsuites.NodeSelection{
161+
nodeSelection = framework.NodeSelection{
162162
Selector: m.nodeLabel,
163163
}
164164
}
@@ -177,11 +177,11 @@ var _ = utils.SIGDescribe("CSI mock volume", func() {
177177

178178
createPodWithPVC := func(pvc *v1.PersistentVolumeClaim) (*v1.Pod, error) {
179179
nodeName := m.config.ClientNodeName
180-
nodeSelection := testsuites.NodeSelection{
180+
nodeSelection := framework.NodeSelection{
181181
Name: nodeName,
182182
}
183183
if len(m.nodeLabel) > 0 {
184-
nodeSelection = testsuites.NodeSelection{
184+
nodeSelection = framework.NodeSelection{
185185
Selector: m.nodeLabel,
186186
}
187187
}
@@ -597,7 +597,7 @@ func checkNodeForLimits(nodeName string, attachKey v1.ResourceName, cs clientset
597597
return int(attachLimit), waitErr
598598
}
599599

600-
func startPausePod(cs clientset.Interface, t testsuites.StorageClassTest, node testsuites.NodeSelection, ns string) (*storagev1.StorageClass, *v1.PersistentVolumeClaim, *v1.Pod) {
600+
func startPausePod(cs clientset.Interface, t testsuites.StorageClassTest, node framework.NodeSelection, ns string) (*storagev1.StorageClass, *v1.PersistentVolumeClaim, *v1.Pod) {
601601
class := newStorageClass(t, ns, "")
602602
var err error
603603
_, err = cs.StorageV1().StorageClasses().Get(class.Name, metav1.GetOptions{})
@@ -659,7 +659,7 @@ func startPausePod(cs clientset.Interface, t testsuites.StorageClassTest, node t
659659
return class, claim, pod
660660
}
661661

662-
func startPausePodWithClaim(cs clientset.Interface, pvc *v1.PersistentVolumeClaim, node testsuites.NodeSelection, ns string) (*v1.Pod, error) {
662+
func startPausePodWithClaim(cs clientset.Interface, pvc *v1.PersistentVolumeClaim, node framework.NodeSelection, ns string) (*v1.Pod, error) {
663663
pod := &v1.Pod{
664664
ObjectMeta: metav1.ObjectMeta{
665665
GenerateName: "pvc-volume-tester-",

test/e2e/storage/csi_volumes.go

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,7 @@ var csiTestSuites = []func() testsuites.TestSuite{
4747
testsuites.InitSubPathTestSuite,
4848
testsuites.InitProvisioningTestSuite,
4949
testsuites.InitSnapshottableTestSuite,
50+
testsuites.InitMultiVolumeTestSuite,
5051
}
5152

5253
// This executes testSuites for csi volumes.
@@ -155,7 +156,7 @@ func testTopologyNegative(cs clientset.Interface, suffix, namespace string, dela
155156
test.PvCheck = func(claim *v1.PersistentVolumeClaim, volume *v1.PersistentVolume) {
156157
// Ensure that a pod cannot be scheduled in an unsuitable zone.
157158
pod := testsuites.StartInPodWithVolume(cs, namespace, claim.Name, "pvc-tester-unschedulable", "sleep 100000",
158-
testsuites.NodeSelection{Selector: nodeSelector})
159+
framework.NodeSelection{Selector: nodeSelector})
159160
defer testsuites.StopPod(cs, pod)
160161
framework.ExpectNoError(framework.WaitForPodNameUnschedulableInNamespace(cs, pod.Name, pod.Namespace), "pod should be unschedulable")
161162
}

test/e2e/storage/drivers/in_tree.go

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -95,6 +95,7 @@ func InitNFSDriver() testsuites.TestDriver {
9595
Capabilities: map[testsuites.Capability]bool{
9696
testsuites.CapPersistence: true,
9797
testsuites.CapExec: true,
98+
testsuites.CapRWX: true,
9899
},
99100
},
100101
}
@@ -232,6 +233,7 @@ func InitGlusterFSDriver() testsuites.TestDriver {
232233
Capabilities: map[testsuites.Capability]bool{
233234
testsuites.CapPersistence: true,
234235
testsuites.CapExec: true,
236+
testsuites.CapRWX: true,
235237
},
236238
},
237239
}
@@ -586,6 +588,7 @@ func InitCephFSDriver() testsuites.TestDriver {
586588
Capabilities: map[testsuites.Capability]bool{
587589
testsuites.CapPersistence: true,
588590
testsuites.CapExec: true,
591+
testsuites.CapRWX: true,
589592
},
590593
},
591594
}

test/e2e/storage/in_tree_volumes.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,7 @@ var testSuites = []func() testsuites.TestSuite{
5555
testsuites.InitVolumeModeTestSuite,
5656
testsuites.InitSubPathTestSuite,
5757
testsuites.InitProvisioningTestSuite,
58+
testsuites.InitMultiVolumeTestSuite,
5859
}
5960

6061
// This executes testSuites for in-tree volumes.

test/e2e/storage/persistent_volumes.go

Lines changed: 7 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ import (
3030
utilerrors "k8s.io/apimachinery/pkg/util/errors"
3131
clientset "k8s.io/client-go/kubernetes"
3232
"k8s.io/kubernetes/test/e2e/framework"
33+
"k8s.io/kubernetes/test/e2e/storage/testsuites"
3334
"k8s.io/kubernetes/test/e2e/storage/utils"
3435
imageutils "k8s.io/kubernetes/test/utils/image"
3536
)
@@ -308,68 +309,22 @@ var _ = utils.SIGDescribe("PersistentVolumes", func() {
308309
Describe("Default StorageClass", func() {
309310
Context("pods that use multiple volumes", func() {
310311

311-
AfterEach(func() {
312-
framework.DeleteAllStatefulSets(c, ns)
313-
})
314-
315312
It("should be reschedulable [Slow]", func() {
316313
// Only run on providers with default storageclass
317314
framework.SkipUnlessProviderIs("openstack", "gce", "gke", "vsphere", "azure")
318315

319316
numVols := 4
320-
ssTester := framework.NewStatefulSetTester(c)
321-
322-
By("Creating a StatefulSet pod to initialize data")
323-
writeCmd := "true"
324-
for i := 0; i < numVols; i++ {
325-
writeCmd += fmt.Sprintf("&& touch %v", getVolumeFile(i))
326-
}
327-
writeCmd += "&& sleep 10000"
328-
329-
probe := &v1.Probe{
330-
Handler: v1.Handler{
331-
Exec: &v1.ExecAction{
332-
// Check that the last file got created
333-
Command: []string{"test", "-f", getVolumeFile(numVols - 1)},
334-
},
335-
},
336-
InitialDelaySeconds: 1,
337-
PeriodSeconds: 1,
338-
}
339317

340-
mounts := []v1.VolumeMount{}
341-
claims := []v1.PersistentVolumeClaim{}
318+
By("Creating pvcs")
319+
claims := []*v1.PersistentVolumeClaim{}
342320
for i := 0; i < numVols; i++ {
343321
pvc := framework.MakePersistentVolumeClaim(framework.PersistentVolumeClaimConfig{}, ns)
344-
pvc.Name = getVolName(i)
345-
mounts = append(mounts, v1.VolumeMount{Name: pvc.Name, MountPath: getMountPath(i)})
346-
claims = append(claims, *pvc)
322+
claims = append(claims, pvc)
347323
}
348324

349-
spec := makeStatefulSetWithPVCs(ns, writeCmd, mounts, claims, probe)
350-
ss, err := c.AppsV1().StatefulSets(ns).Create(spec)
351-
Expect(err).NotTo(HaveOccurred())
352-
ssTester.WaitForRunningAndReady(1, ss)
353-
354-
By("Deleting the StatefulSet but not the volumes")
355-
// Scale down to 0 first so that the Delete is quick
356-
ss, err = ssTester.Scale(ss, 0)
357-
Expect(err).NotTo(HaveOccurred())
358-
ssTester.WaitForStatusReplicas(ss, 0)
359-
err = c.AppsV1().StatefulSets(ns).Delete(ss.Name, &metav1.DeleteOptions{})
360-
Expect(err).NotTo(HaveOccurred())
361-
362-
By("Creating a new Statefulset and validating the data")
363-
validateCmd := "true"
364-
for i := 0; i < numVols; i++ {
365-
validateCmd += fmt.Sprintf("&& test -f %v", getVolumeFile(i))
366-
}
367-
validateCmd += "&& sleep 10000"
368-
369-
spec = makeStatefulSetWithPVCs(ns, validateCmd, mounts, claims, probe)
370-
ss, err = c.AppsV1().StatefulSets(ns).Create(spec)
371-
Expect(err).NotTo(HaveOccurred())
372-
ssTester.WaitForRunningAndReady(1, ss)
325+
By("Testing access to pvcs before and after pod recreation on differetn node")
326+
testsuites.TestAccessMultipleVolumesAcrossPodRecreation(f, c, ns,
327+
framework.NodeSelection{}, claims, false /* sameNode */)
373328
})
374329
})
375330
})

test/e2e/storage/regional_pd.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -115,7 +115,7 @@ func testVolumeProvisioning(c clientset.Interface, ns string) {
115115
err = verifyZonesInPV(volume, sets.NewString(cloudZones...), true /* match */)
116116
Expect(err).NotTo(HaveOccurred(), "verifyZonesInPV")
117117

118-
testsuites.PVWriteReadSingleNodeCheck(c, claim, volume, testsuites.NodeSelection{})
118+
testsuites.PVWriteReadSingleNodeCheck(c, claim, volume, framework.NodeSelection{})
119119
},
120120
},
121121
{
@@ -137,7 +137,7 @@ func testVolumeProvisioning(c clientset.Interface, ns string) {
137137
err = verifyZonesInPV(volume, zones, false /* match */)
138138
Expect(err).NotTo(HaveOccurred(), "verifyZonesInPV")
139139

140-
testsuites.PVWriteReadSingleNodeCheck(c, claim, volume, testsuites.NodeSelection{})
140+
testsuites.PVWriteReadSingleNodeCheck(c, claim, volume, framework.NodeSelection{})
141141
},
142142
},
143143
}

test/e2e/storage/testsuites/BUILD

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ go_library(
55
srcs = [
66
"base.go",
77
"driveroperations.go",
8+
"multivolume.go",
89
"provisioning.go",
910
"snapshottable.go",
1011
"subpath.go",
@@ -28,6 +29,7 @@ go_library(
2829
"//staging/src/k8s.io/apimachinery/pkg/util/rand:go_default_library",
2930
"//staging/src/k8s.io/apimachinery/pkg/util/sets:go_default_library",
3031
"//staging/src/k8s.io/apimachinery/pkg/util/wait:go_default_library",
32+
"//staging/src/k8s.io/apiserver/pkg/storage/names:go_default_library",
3133
"//staging/src/k8s.io/client-go/dynamic:go_default_library",
3234
"//staging/src/k8s.io/client-go/kubernetes:go_default_library",
3335
"//test/e2e/framework:go_default_library",

test/e2e/storage/testsuites/base.go

Lines changed: 12 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -188,7 +188,7 @@ func createGenericVolumeTestResource(driver TestDriver, config *PerTestConfig, p
188188
if pDriver, ok := driver.(PreprovisionedPVTestDriver); ok {
189189
pvSource, volumeNodeAffinity := pDriver.GetPersistentVolumeSource(false, fsType, r.volume)
190190
if pvSource != nil {
191-
r.volSource, r.pv, r.pvc = createVolumeSourceWithPVCPV(f, dInfo.Name, pvSource, volumeNodeAffinity, false)
191+
r.volSource, r.pv, r.pvc = createVolumeSourceWithPVCPV(f, dInfo.Name, pvSource, volumeNodeAffinity, false, pattern.VolMode)
192192
}
193193
r.volType = fmt.Sprintf("%s-preprovisionedPV", dInfo.Name)
194194
}
@@ -205,7 +205,7 @@ func createGenericVolumeTestResource(driver TestDriver, config *PerTestConfig, p
205205

206206
if r.sc != nil {
207207
r.volSource, r.pv, r.pvc = createVolumeSourceWithPVCPVFromDynamicProvisionSC(
208-
f, dInfo.Name, claimSize, r.sc, false, nil)
208+
f, dInfo.Name, claimSize, r.sc, false, pattern.VolMode)
209209
}
210210
r.volType = fmt.Sprintf("%s-dynamicPV", dInfo.Name)
211211
}
@@ -269,17 +269,24 @@ func createVolumeSourceWithPVCPV(
269269
pvSource *v1.PersistentVolumeSource,
270270
volumeNodeAffinity *v1.VolumeNodeAffinity,
271271
readOnly bool,
272+
volMode v1.PersistentVolumeMode,
272273
) (*v1.VolumeSource, *v1.PersistentVolume, *v1.PersistentVolumeClaim) {
273274
pvConfig := framework.PersistentVolumeConfig{
274275
NamePrefix: fmt.Sprintf("%s-", name),
275276
StorageClassName: f.Namespace.Name,
276277
PVSource: *pvSource,
277278
NodeAffinity: volumeNodeAffinity,
278279
}
280+
279281
pvcConfig := framework.PersistentVolumeClaimConfig{
280282
StorageClassName: &f.Namespace.Name,
281283
}
282284

285+
if volMode != "" {
286+
pvConfig.VolumeMode = &volMode
287+
pvcConfig.VolumeMode = &volMode
288+
}
289+
283290
framework.Logf("Creating PVC and PV")
284291
pv, pvc, err := framework.CreatePVCPV(f.ClientSet, pvConfig, pvcConfig, f.Namespace.Name, false)
285292
Expect(err).NotTo(HaveOccurred(), "PVC, PV creation failed")
@@ -302,16 +309,16 @@ func createVolumeSourceWithPVCPVFromDynamicProvisionSC(
302309
claimSize string,
303310
sc *storagev1.StorageClass,
304311
readOnly bool,
305-
volMode *v1.PersistentVolumeMode,
312+
volMode v1.PersistentVolumeMode,
306313
) (*v1.VolumeSource, *v1.PersistentVolume, *v1.PersistentVolumeClaim) {
307314
cs := f.ClientSet
308315
ns := f.Namespace.Name
309316

310317
By("creating a claim")
311318
pvc := getClaim(claimSize, ns)
312319
pvc.Spec.StorageClassName = &sc.Name
313-
if volMode != nil {
314-
pvc.Spec.VolumeMode = volMode
320+
if volMode != "" {
321+
pvc.Spec.VolumeMode = &volMode
315322
}
316323

317324
var err error

test/e2e/storage/testsuites/driveroperations.go

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ import (
2222
storagev1 "k8s.io/api/storage/v1"
2323
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
2424
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
25+
"k8s.io/apiserver/pkg/storage/names"
2526
"k8s.io/kubernetes/test/e2e/framework"
2627
"k8s.io/kubernetes/test/e2e/storage/testpatterns"
2728
)
@@ -71,8 +72,8 @@ func GetStorageClass(
7172
Kind: "StorageClass",
7273
},
7374
ObjectMeta: metav1.ObjectMeta{
74-
// Name must be unique, so let's base it on namespace name
75-
Name: ns + "-" + suffix,
75+
// Name must be unique, so let's base it on namespace name and use GenerateName
76+
Name: names.SimpleNameGenerator.GenerateName(ns + "-" + suffix),
7677
},
7778
Provisioner: provisioner,
7879
Parameters: parameters,

0 commit comments

Comments
 (0)