@@ -31,10 +31,13 @@ import (
31
31
"k8s.io/utils/mount"
32
32
)
33
33
34
- const TopologyKeyNode = "topology.hostpath.csi/node"
34
+ const (
35
+ TopologyKeyNode = "topology.hostpath.csi/node"
35
36
36
- func (hp * hostPath ) NodePublishVolume (ctx context.Context , req * csi.NodePublishVolumeRequest ) (* csi.NodePublishVolumeResponse , error ) {
37
+ failedPreconditionAccessModeConflict = "volume uses SINGLE_NODE_SINGLE_WRITER access mode and is already mounted at a different target path"
38
+ )
37
39
40
+ func (hp * hostPath ) NodePublishVolume (ctx context.Context , req * csi.NodePublishVolumeRequest ) (* csi.NodePublishVolumeResponse , error ) {
38
41
// Check arguments
39
42
if req .GetVolumeCapability () == nil {
40
43
return nil , status .Error (codes .InvalidArgument , "Volume capability missing in request" )
@@ -60,6 +63,8 @@ func (hp *hostPath) NodePublishVolume(ctx context.Context, req *csi.NodePublishV
60
63
hp .mutex .Lock ()
61
64
defer hp .mutex .Unlock ()
62
65
66
+ mounter := mount .New ("" )
67
+
63
68
// if ephemeral is specified, create volume here to avoid errors
64
69
if ephemeralVolume {
65
70
volID := req .GetVolumeId ()
@@ -80,6 +85,10 @@ func (hp *hostPath) NodePublishVolume(ctx context.Context, req *csi.NodePublishV
80
85
return nil , status .Error (codes .NotFound , err .Error ())
81
86
}
82
87
88
+ if hasSingleNodeSingleWriterAccessMode (req ) && isMountedElsewhere (req , vol ) {
89
+ return nil , status .Error (codes .FailedPrecondition , failedPreconditionAccessModeConflict )
90
+ }
91
+
83
92
if ! ephemeralVolume {
84
93
if vol .Staged .Empty () {
85
94
return nil , status .Errorf (codes .FailedPrecondition , "volume %q must be staged before publishing" , vol .VolID )
@@ -102,8 +111,6 @@ func (hp *hostPath) NodePublishVolume(ctx context.Context, req *csi.NodePublishV
102
111
return nil , fmt .Errorf ("failed to get the loop device: %w" , err )
103
112
}
104
113
105
- mounter := mount .New ("" )
106
-
107
114
// Check if the target path exists. Create if not present.
108
115
_ , err = os .Lstat (targetPath )
109
116
if os .IsNotExist (err ) {
@@ -130,15 +137,15 @@ func (hp *hostPath) NodePublishVolume(ctx context.Context, req *csi.NodePublishV
130
137
}
131
138
132
139
options := []string {"bind" }
133
- if err := mount . New ( "" ) .Mount (loopDevice , targetPath , "" , options ); err != nil {
140
+ if err := mounter .Mount (loopDevice , targetPath , "" , options ); err != nil {
134
141
return nil , fmt .Errorf ("failed to mount block device: %s at %s: %w" , loopDevice , targetPath , err )
135
142
}
136
143
} else if req .GetVolumeCapability ().GetMount () != nil {
137
144
if vol .VolAccessType != state .MountAccess {
138
145
return nil , status .Error (codes .InvalidArgument , "cannot publish a non-mount volume as mount volume" )
139
146
}
140
147
141
- notMnt , err := mount .IsNotMountPoint (mount . New ( "" ) , targetPath )
148
+ notMnt , err := mount .IsNotMountPoint (mounter , targetPath )
142
149
if err != nil {
143
150
if os .IsNotExist (err ) {
144
151
if err = os .Mkdir (targetPath , 0750 ); err != nil {
@@ -173,7 +180,6 @@ func (hp *hostPath) NodePublishVolume(ctx context.Context, req *csi.NodePublishV
173
180
if readOnly {
174
181
options = append (options , "ro" )
175
182
}
176
- mounter := mount .New ("" )
177
183
path := hp .getVolumePath (volumeId )
178
184
179
185
if err := mounter .Mount (path , targetPath , "" , options ); err != nil {
@@ -522,3 +528,21 @@ func makeFile(pathname string) error {
522
528
}
523
529
return nil
524
530
}
531
+
532
+ // hasSingleNodeSingleWriterAccessMode checks if the publish request uses the
533
+ // SINGLE_NODE_SINGLE_WRITER access mode.
534
+ func hasSingleNodeSingleWriterAccessMode (req * csi.NodePublishVolumeRequest ) bool {
535
+ accessMode := req .GetVolumeCapability ().GetAccessMode ()
536
+ return accessMode != nil && accessMode .GetMode () == csi .VolumeCapability_AccessMode_SINGLE_NODE_SINGLE_WRITER
537
+ }
538
+
539
+ // isMountedElsewhere checks if the volume to publish is mounted elsewhere on
540
+ // the node.
541
+ func isMountedElsewhere (req * csi.NodePublishVolumeRequest , vol state.Volume ) bool {
542
+ for _ , targetPath := range vol .Published {
543
+ if targetPath != req .GetTargetPath () {
544
+ return true
545
+ }
546
+ }
547
+ return false
548
+ }
0 commit comments