Skip to content

Commit b309578

Browse files
andyzhangxk8s-infra-cherrypick-robot
authored andcommitted
test: add unit tests for stale NFS mount detection and remount
- Make os.Lstat injectable via lstatFunc package variable for testability - Add NodePublishVolume test case covering ESTALE detection -> unmount -> remount - Add TestIsStaleFileHandle with coverage for ESTALE errno, wrapped errors, string matching, and negative cases
1 parent d11da49 commit b309578

2 files changed

Lines changed: 45 additions & 1 deletion

File tree

pkg/nfs/nodeserver.go

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,9 @@ import (
3636

3737
const mountTimeoutInSec = 110
3838

39+
// lstatFunc is used for testing to inject stale file handle errors
40+
var lstatFunc = os.Lstat
41+
3942
// NodeServer driver
4043
type NodeServer struct {
4144
Driver *Driver
@@ -134,7 +137,7 @@ func (ns *NodeServer) NodePublishVolume(_ context.Context, req *csi.NodePublishV
134137
}
135138
if !notMnt {
136139
// check if the existing mount is stale (e.g. after NFS server restart)
137-
if _, err := os.Lstat(targetPath); err != nil && os.IsPermission(err) {
140+
if _, err := lstatFunc(targetPath); err != nil && os.IsPermission(err) {
138141
return &csi.NodePublishVolumeResponse{}, nil
139142
} else if err != nil && isStaleFileHandle(err) {
140143
klog.Warningf("NodePublishVolume: detected stale mount at %s, attempting remount", targetPath)

pkg/nfs/nodeserver_test.go

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ import (
2323
"os"
2424
"reflect"
2525
"strings"
26+
"syscall"
2627
"testing"
2728

2829
"github.com/container-storage-interface/spec/lib/go/csi"
@@ -178,6 +179,24 @@ func TestNodePublishVolume(t *testing.T) {
178179
Readonly: true},
179180
expectedErr: status.Error(codes.InvalidArgument, "invalid mountPermissions 07ab"),
180181
},
182+
{
183+
desc: "[Success] Stale mount detected and remounted",
184+
setup: func() {
185+
lstatFunc = func(name string) (os.FileInfo, error) {
186+
return nil, syscall.ESTALE
187+
}
188+
},
189+
req: &csi.NodePublishVolumeRequest{
190+
VolumeContext: params,
191+
VolumeCapability: &csi.VolumeCapability{AccessMode: &volumeCap},
192+
VolumeId: "vol_1",
193+
TargetPath: alreadyMountedTarget,
194+
Readonly: true},
195+
expectedErr: nil,
196+
cleanup: func() {
197+
lstatFunc = os.Lstat
198+
},
199+
},
181200
}
182201

183202
// setup
@@ -372,3 +391,25 @@ func TestNodeGetVolumeStats(t *testing.T) {
372391
err = os.RemoveAll(fakePath)
373392
assert.NoError(t, err)
374393
}
394+
395+
func TestIsStaleFileHandle(t *testing.T) {
396+
tests := []struct {
397+
desc string
398+
err error
399+
expected bool
400+
}{
401+
{"nil error", nil, false},
402+
{"ESTALE errno", syscall.ESTALE, true},
403+
{"other errno", syscall.ENOENT, false},
404+
{"stale NFS file handle string", fmt.Errorf("stale NFS file handle"), true},
405+
{"stale file handle string", fmt.Errorf("stale file handle"), true},
406+
{"unrelated error", fmt.Errorf("something else"), false},
407+
{"wrapped ESTALE", fmt.Errorf("wrap: %w", syscall.ESTALE), true},
408+
}
409+
for _, tc := range tests {
410+
result := isStaleFileHandle(tc.err)
411+
if result != tc.expected {
412+
t.Errorf("isStaleFileHandle(%v): got %v, want %v", tc.desc, result, tc.expected)
413+
}
414+
}
415+
}

0 commit comments

Comments
 (0)