Skip to content

Commit fb24e2b

Browse files
authored
Merge pull request #381 from chrishenzie/enforce-node-publish
Enforce SINGLE_NODE_SINGLE_WRITER access mode in NodePublishVolume
2 parents 0ba98e7 + bad0711 commit fb24e2b

File tree

1 file changed

+31
-7
lines changed

1 file changed

+31
-7
lines changed

pkg/hostpath/nodeserver.go

Lines changed: 31 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -31,10 +31,13 @@ import (
3131
"k8s.io/utils/mount"
3232
)
3333

34-
const TopologyKeyNode = "topology.hostpath.csi/node"
34+
const (
35+
TopologyKeyNode = "topology.hostpath.csi/node"
3536

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+
)
3739

40+
func (hp *hostPath) NodePublishVolume(ctx context.Context, req *csi.NodePublishVolumeRequest) (*csi.NodePublishVolumeResponse, error) {
3841
// Check arguments
3942
if req.GetVolumeCapability() == nil {
4043
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
6063
hp.mutex.Lock()
6164
defer hp.mutex.Unlock()
6265

66+
mounter := mount.New("")
67+
6368
// if ephemeral is specified, create volume here to avoid errors
6469
if ephemeralVolume {
6570
volID := req.GetVolumeId()
@@ -80,6 +85,10 @@ func (hp *hostPath) NodePublishVolume(ctx context.Context, req *csi.NodePublishV
8085
return nil, status.Error(codes.NotFound, err.Error())
8186
}
8287

88+
if hasSingleNodeSingleWriterAccessMode(req) && isMountedElsewhere(req, vol) {
89+
return nil, status.Error(codes.FailedPrecondition, failedPreconditionAccessModeConflict)
90+
}
91+
8392
if !ephemeralVolume {
8493
if vol.Staged.Empty() {
8594
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
102111
return nil, fmt.Errorf("failed to get the loop device: %w", err)
103112
}
104113

105-
mounter := mount.New("")
106-
107114
// Check if the target path exists. Create if not present.
108115
_, err = os.Lstat(targetPath)
109116
if os.IsNotExist(err) {
@@ -130,15 +137,15 @@ func (hp *hostPath) NodePublishVolume(ctx context.Context, req *csi.NodePublishV
130137
}
131138

132139
options := []string{"bind"}
133-
if err := mount.New("").Mount(loopDevice, targetPath, "", options); err != nil {
140+
if err := mounter.Mount(loopDevice, targetPath, "", options); err != nil {
134141
return nil, fmt.Errorf("failed to mount block device: %s at %s: %w", loopDevice, targetPath, err)
135142
}
136143
} else if req.GetVolumeCapability().GetMount() != nil {
137144
if vol.VolAccessType != state.MountAccess {
138145
return nil, status.Error(codes.InvalidArgument, "cannot publish a non-mount volume as mount volume")
139146
}
140147

141-
notMnt, err := mount.IsNotMountPoint(mount.New(""), targetPath)
148+
notMnt, err := mount.IsNotMountPoint(mounter, targetPath)
142149
if err != nil {
143150
if os.IsNotExist(err) {
144151
if err = os.Mkdir(targetPath, 0750); err != nil {
@@ -173,7 +180,6 @@ func (hp *hostPath) NodePublishVolume(ctx context.Context, req *csi.NodePublishV
173180
if readOnly {
174181
options = append(options, "ro")
175182
}
176-
mounter := mount.New("")
177183
path := hp.getVolumePath(volumeId)
178184

179185
if err := mounter.Mount(path, targetPath, "", options); err != nil {
@@ -522,3 +528,21 @@ func makeFile(pathname string) error {
522528
}
523529
return nil
524530
}
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

Comments
 (0)