Skip to content

Commit 89676a6

Browse files
committed
Assert NodePublishVolume behavior for single node single writer volumes
Ensure that a single node single writer volume cannot be mounted at two different target paths on the same node. See the second table in the NodePublishVolume section of the CSI spec for more details: https://github.com/container-storage-interface/spec/blob/v1.7.0/spec.md#nodepublishvolume
1 parent ed1324b commit 89676a6

File tree

3 files changed

+65
-11
lines changed

3 files changed

+65
-11
lines changed

Makefile

+1-1
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ build-sanity:
3838
$(MAKE) -C cmd/csi-sanity all
3939

4040

41-
TEST_HOSTPATH_VERSION=v1.7.3
41+
TEST_HOSTPATH_VERSION=v1.11.0
4242
TEST_HOSTPATH_SOURCE=bin/hostpath-source
4343
TEST_HOSTPATH_REPO=https://github.com/kubernetes-csi/csi-driver-host-path.git
4444
bin/hostpathplugin:

hack/_apitest2/api_test.go

+4-4
Original file line numberDiff line numberDiff line change
@@ -31,10 +31,10 @@ func TestMyDriverWithCustomTargetPaths(t *testing.T) {
3131
var createTargetDirCalls, createStagingDirCalls,
3232
removeTargetDirCalls, removeStagingDirCalls int
3333

34-
wantCreateTargetCalls := 3
35-
wantCreateStagingCalls := 3
36-
wantRemoveTargetCalls := 3
37-
wantRemoveStagingCalls := 3
34+
wantCreateTargetCalls := 4
35+
wantCreateStagingCalls := 4
36+
wantRemoveTargetCalls := 4
37+
wantRemoveStagingCalls := 4
3838

3939
// tmpPath could be a CO specific directory under which all the target dirs
4040
// are created. For k8s, it could be /var/lib/kubelet/pods under which the

pkg/sanity/node.go

+60-6
Original file line numberDiff line numberDiff line change
@@ -183,12 +183,13 @@ var _ = DescribeSanity("Node Service", func(sc *TestContext) {
183183
var (
184184
r *Resources
185185

186-
providesControllerService bool
187-
controllerPublishSupported bool
188-
nodeStageSupported bool
189-
nodeVolumeStatsSupported bool
190-
nodeExpansionSupported bool
191-
controllerExpansionSupported bool
186+
providesControllerService bool
187+
controllerPublishSupported bool
188+
nodeStageSupported bool
189+
nodeVolumeStatsSupported bool
190+
nodeExpansionSupported bool
191+
controllerExpansionSupported bool
192+
singleNodeMultiWriterSupported bool
192193
)
193194

194195
createVolumeWithCapability := func(volumeName string, cap *csi.VolumeCapability) *csi.CreateVolumeResponse {
@@ -319,6 +320,8 @@ var _ = DescribeSanity("Node Service", func(sc *TestContext) {
319320
nodeVolumeStatsSupported = isNodeCapabilitySupported(n, csi.NodeServiceCapability_RPC_GET_VOLUME_STATS)
320321
nodeExpansionSupported = isNodeCapabilitySupported(n, csi.NodeServiceCapability_RPC_EXPAND_VOLUME)
321322
controllerExpansionSupported = isControllerCapabilitySupported(cl, csi.ControllerServiceCapability_RPC_EXPAND_VOLUME)
323+
singleNodeMultiWriterSupported = isNodeCapabilitySupported(n, csi.NodeServiceCapability_RPC_SINGLE_NODE_MULTI_WRITER)
324+
322325
r = &Resources{
323326
Context: sc,
324327
ControllerClient: cl,
@@ -431,6 +434,57 @@ var _ = DescribeSanity("Node Service", func(sc *TestContext) {
431434
Expect(ok).To(BeTrue())
432435
Expect(serverError.Code()).To(Equal(codes.InvalidArgument), "unexpected error: %s", serverError.Message())
433436
})
437+
438+
Describe("with single node multi writer capability", func() {
439+
BeforeEach(func() {
440+
if !singleNodeMultiWriterSupported {
441+
Skip("Service does not have single node multi writer capability")
442+
}
443+
})
444+
445+
It("should fail when volume with single node single writer access mode is already mounted at a different target path", func() {
446+
By("creating a single node single writer volume")
447+
name := UniqueString("sanity-node-publish-single-node-single-writer")
448+
cap := TestVolumeCapabilityWithAccessType(sc, csi.VolumeCapability_AccessMode_SINGLE_NODE_SINGLE_WRITER)
449+
vol := createVolumeWithCapability(name, cap)
450+
451+
By("Getting a node id")
452+
nid, err := r.NodeGetInfo(
453+
context.Background(),
454+
&csi.NodeGetInfoRequest{})
455+
Expect(err).NotTo(HaveOccurred())
456+
Expect(nid).NotTo(BeNil())
457+
Expect(nid.GetNodeId()).NotTo(BeEmpty())
458+
459+
By("Staging and publishing a volume")
460+
conpubvol := controllerPublishVolumeWithCapability(name, vol, nid, cap)
461+
_ = nodeStageVolumeWithCapability(name, vol, conpubvol, cap)
462+
_ = nodePublishVolumeWithCapability(name, vol, conpubvol, cap)
463+
464+
nodePublishRequest := &csi.NodePublishVolumeRequest{
465+
VolumeId: vol.GetVolume().GetVolumeId(),
466+
TargetPath: sc.TargetPath + "/other_target",
467+
VolumeCapability: cap,
468+
VolumeContext: vol.GetVolume().GetVolumeContext(),
469+
Secrets: sc.Secrets.NodePublishVolumeSecret,
470+
}
471+
if conpubvol != nil {
472+
nodePublishRequest.PublishContext = conpubvol.GetPublishContext()
473+
}
474+
if nodeStageSupported {
475+
nodePublishRequest.StagingTargetPath = sc.StagingPath
476+
}
477+
478+
_, err = r.NodePublishVolume(
479+
context.Background(),
480+
nodePublishRequest)
481+
Expect(err).To(HaveOccurred())
482+
483+
serverError, ok := status.FromError(err)
484+
Expect(ok).To(BeTrue())
485+
Expect(serverError.Code()).To(Equal(codes.FailedPrecondition), "unexpected error: %s", serverError.Message())
486+
})
487+
})
434488
})
435489

436490
Describe("NodeUnpublishVolume", func() {

0 commit comments

Comments
 (0)