Skip to content

Commit 84180e6

Browse files
committed
e2e/storage: test usage of volume in multiple pods at once
This is a special case that both kubelet and the volume driver should support, because users might expect it. One Kubernetes mechanism to deploy pods like this is via pod affinity. However, strictly speaking the CSI spec does not allow this usage mode (see container-storage-interface/spec#150) and there is an on-going debate to enable it (see container-storage-interface/spec#178). Therefore this test gets skipped unless explicitly enabled for a driver. CSI drivers which create a block device for a remote volume in NodePublishVolume fail this test. They have to make the volume available in NodeStageVolume and then in NodePublishVolume merely do a bind mount (as for example in https://github.com/kubernetes-sigs/gcp-compute-persistent-disk-csi-driver/blob/master/pkg/gce-pd-csi-driver/node.go#L150).
1 parent 0bfcf28 commit 84180e6

File tree

3 files changed

+56
-0
lines changed

3 files changed

+56
-0
lines changed

test/e2e/storage/drivers/csi.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,7 @@ func initHostPathCSIDriver(name string, config testsuites.TestConfig, manifests
7171
),
7272
Capabilities: map[testsuites.Capability]bool{
7373
testsuites.CapPersistence: true,
74+
testsuites.CapMultiPODs: true,
7475
},
7576

7677
Config: config,

test/e2e/storage/testsuites/provisioning.go

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ package testsuites
1818

1919
import (
2020
"fmt"
21+
"sync"
2122
"time"
2223

2324
. "github.com/onsi/ginkgo"
@@ -218,6 +219,50 @@ func testProvisioning(input *provisioningTestInput) {
218219
input.pvc.Spec.VolumeMode = &block
219220
TestDynamicProvisioning(input.testCase, input.cs, input.pvc, input.sc)
220221
})
222+
223+
It("should allow concurrent writes on the same node", func() {
224+
if !input.dInfo.Capabilities[CapMultiPODs] {
225+
framework.Skipf("Driver %q does not support multiple concurrent pods - skipping", input.dInfo.Name)
226+
}
227+
input.testCase.PvCheck = func(claim *v1.PersistentVolumeClaim, volume *v1.PersistentVolume) {
228+
// We start two pods concurrently on the same node,
229+
// using the same PVC. Both wait for other to create a
230+
// file before returning. The pods are forced onto the
231+
// same node via pod affinity.
232+
wg := sync.WaitGroup{}
233+
wg.Add(2)
234+
firstPodName := "pvc-tester-first"
235+
secondPodName := "pvc-tester-second"
236+
run := func(podName, command string) {
237+
defer GinkgoRecover()
238+
defer wg.Done()
239+
node := NodeSelection{
240+
Name: input.nodeName,
241+
}
242+
if podName == secondPodName {
243+
node.Affinity = &v1.Affinity{
244+
PodAffinity: &v1.PodAffinity{
245+
RequiredDuringSchedulingIgnoredDuringExecution: []v1.PodAffinityTerm{
246+
{LabelSelector: &metav1.LabelSelector{
247+
MatchLabels: map[string]string{
248+
// Set by RunInPodWithVolume.
249+
"app": firstPodName,
250+
},
251+
},
252+
TopologyKey: "kubernetes.io/hostname",
253+
},
254+
},
255+
},
256+
}
257+
}
258+
RunInPodWithVolume(input.cs, claim.Namespace, claim.Name, podName, command, node)
259+
}
260+
go run(firstPodName, "touch /mnt/test/first && while ! [ -f /mnt/test/second ]; do sleep 1; done")
261+
go run(secondPodName, "touch /mnt/test/second && while ! [ -f /mnt/test/first ]; do sleep 1; done")
262+
wg.Wait()
263+
}
264+
TestDynamicProvisioning(input.testCase, input.cs, input.pvc, input.sc)
265+
})
221266
}
222267

223268
// TestDynamicProvisioning tests dynamic provisioning with specified StorageClassTest and storageClass
@@ -530,6 +575,9 @@ func StartInPodWithVolume(c clientset.Interface, ns, claimName, podName, command
530575
},
531576
ObjectMeta: metav1.ObjectMeta{
532577
GenerateName: podName + "-",
578+
Labels: map[string]string{
579+
"app": podName,
580+
},
533581
},
534582
Spec: v1.PodSpec{
535583
NodeName: node.Name,

test/e2e/storage/testsuites/testdriver.go

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -86,6 +86,13 @@ const (
8686
CapBlock Capability = "block" // raw block mode
8787
CapFsGroup Capability = "fsGroup" // volume ownership via fsGroup
8888
CapExec Capability = "exec" // exec a file in the volume
89+
90+
// multiple pods on a node can use the same volume concurrently;
91+
// for CSI, see:
92+
// - https://github.com/container-storage-interface/spec/pull/150
93+
// - https://github.com/container-storage-interface/spec/issues/178
94+
// - NodeStageVolume in the spec
95+
CapMultiPODs Capability = "multipods"
8996
)
9097

9198
// DriverInfo represents a combination of parameters to be used in implementation of TestDriver

0 commit comments

Comments
 (0)