Repository: dell/csi-powerstore
Pull request #485: Improvements for Shared NFS scalability
Author: login: lukeatdell
Created: 2025-05-08T13:53:20Z | Merged: 2025-05-16T18:32:51Z

# Description

Improvements to scalability:
- additions to unmount any potential duplicate entries and unmount with 'lazy' option
- Node Stage and Unstage now call the NFS service implementation of the functions to perform update stage and unstage operations.

# GitHub Issues
List the GitHub issues impacted by this PR:

| GitHub Issue # |
| -------------- |
| https://github.com/dell/csm/issues/1742 |

# Checklist:

- [x] I have performed a self-review of my own code to ensure there are no formatting, vetting, linting, or security issues
- [x] I have verified that new and existing unit tests pass locally with my changes
- [x] I have not allowed coverage numbers to degenerate
- [x] I have maintained at least 90% code coverage
- [x] I have commented my code, particularly in hard-to-understand areas
- [ ] I have made corresponding changes to the documentation
- [x] I have added tests that prove my fix is effective or that my feature works
- [x] Backward compatibility is not broken

# How Has This Been Tested?
- OCP e2e: OpenShift CSI extended - SCSI LUN Overflow.
- Provisioning volumes until reaching the PowerStore array limit.
- cert-csi certify 
  - block
  - metro
  - nfs
  - SharedNFS


Diff:
diff --git a/go.mod b/go.mod
index 2918ab16..005efed1 100644
--- a/go.mod
+++ b/go.mod
@@ -9,7 +9,7 @@ require (
 	github.com/apparentlymart/go-cidr v1.1.0
 	github.com/container-storage-interface/spec v1.6.0
 	github.com/dell/csi-metadata-retriever v1.10.0
-	github.com/dell/csm-sharednfs v0.0.0-20250508054056-fed2cd325687
+	github.com/dell/csm-sharednfs v1.0.0
 	github.com/dell/dell-csi-extensions/common v1.8.0
 	github.com/dell/dell-csi-extensions/podmon v1.8.0
 	github.com/dell/dell-csi-extensions/replication v1.11.0
diff --git a/go.sum b/go.sum
index cc40208d..3d69ffcf 100644
--- a/go.sum
+++ b/go.sum
@@ -101,8 +101,8 @@ github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1
 github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
 github.com/dell/csi-metadata-retriever v1.10.0 h1:OMzbwSJ+CMOTpf4WgcrSbT3j9Ve1txuLmYkfSTkRSBc=
 github.com/dell/csi-metadata-retriever v1.10.0/go.mod h1:ppgtPHlxaRNmNizU+TnxZLz8tNotTAwByZ/Lg32n660=
-github.com/dell/csm-sharednfs v0.0.0-20250508054056-fed2cd325687 h1:5xHGkUDoUDrP9qFVixvD7hwGvWvFV0GbEjT1MceRjCI=
-github.com/dell/csm-sharednfs v0.0.0-20250508054056-fed2cd325687/go.mod h1:+flf+ETu0LkNUDVFEybV7fJtO6MEHedg816CILTrXhs=
+github.com/dell/csm-sharednfs v1.0.0 h1:+sVJSU1q8i9lfHuSxJUYjQeofsWPcLfyY05m3x2CeWE=
+github.com/dell/csm-sharednfs v1.0.0/go.mod h1:+flf+ETu0LkNUDVFEybV7fJtO6MEHedg816CILTrXhs=
 github.com/dell/dell-csi-extensions/common v1.8.0 h1:2mmcdWtGKLQlGYr+ju3TDCtYkP1KU0e1JETL8wUWfLc=
 github.com/dell/dell-csi-extensions/common v1.8.0/go.mod h1:0cJvgNiPOosCJujd60aV9ZTujsHbRJNXX0ARRMDyjfw=
 github.com/dell/dell-csi-extensions/podmon v1.8.0 h1:393bE3EKmV3oYJk1pNwW733rh+j562X6IVV9XnDhbBs=
diff --git a/pkg/service/node.go b/pkg/service/node.go
index aacfc68e..9c4f13bd 100644
--- a/pkg/service/node.go
+++ b/pkg/service/node.go
@@ -25,25 +25,17 @@ import (
 	"os"
 	"path"
 	"path/filepath"
-	"strings"
-	"syscall"
 
 	"github.com/container-storage-interface/spec/lib/go/csi"
 	"github.com/dell/csm-sharednfs/nfs"
-	"github.com/dell/gofsutil"
-	"google.golang.org/grpc/codes"
-	"google.golang.org/grpc/status"
 )
 
-var (
-	osRemove   = os.RemoveAll
-	sysUnmount = syscall.Unmount
-)
+var osRemove = os.RemoveAll
 
 // NodeStageVolume prepares volume to be consumed by node publish by connecting volume to the node
 func (s *service) NodeStageVolume(ctx context.Context, req *csi.NodeStageVolumeRequest) (*csi.NodeStageVolumeResponse, error) {
 	if nfs.IsNFSVolumeID(req.GetVolumeId()) {
-		return &csi.NodeStageVolumeResponse{}, nil
+		return nfssvc.NodeStageVolume(ctx, req)
 	}
 	return nodeSvc.NodeStageVolume(ctx, req)
 }
@@ -51,7 +43,7 @@ func (s *service) NodeStageVolume(ctx context.Context, req *csi.NodeStageVolumeR
 // NodeUnstageVolume reverses steps done in NodeStage by disconnecting volume from the node
 func (s *service) NodeUnstageVolume(ctx context.Context, req *csi.NodeUnstageVolumeRequest) (*csi.NodeUnstageVolumeResponse, error) {
 	if nfs.IsNFSVolumeID(req.GetVolumeId()) {
-		return &csi.NodeUnstageVolumeResponse{}, nil
+		return nfssvc.NodeUnstageVolume(ctx, req)
 	}
 	return nodeSvc.NodeUnstageVolume(ctx, req)
 }
@@ -165,41 +157,20 @@ func (s *service) MountVolume(ctx context.Context, volumeID, fsType, nfsExportDi
 func (s *service) UnmountVolume(ctx context.Context, volumeID, exportPath string, publishContext map[string]string) error {
 	staging := path.Join(exportPath, publishContext[nfs.ServiceName]+"-dev")
 	target := path.Join(exportPath, publishContext[nfs.ServiceName])
-	var err error
 
-	log.Infof("[UnmountVolume] Cleaning the following paths - target: %s, staging: %s", target, staging)
 	target = filepath.Clean(target)
 	staging = filepath.Clean(staging)
 
-	mounts, err := s.retrieveMounts()
-	if err != nil {
-		return err
-	}
-
-	found := false
-	for _, mount := range mounts {
-		if mount.Path == target {
-			log.Infof("[UnmountVolume] Found mount %s, will try to unmount", mount.Path)
-			found = true
-			break
-		}
+	nodeUnpublishReq := &csi.NodeUnpublishVolumeRequest{
+		VolumeId:   volumeID,
+		TargetPath: target,
 	}
 
-	if found {
-		log.Infof("[UnmountVolume] Unmounting %s", target)
-		err = sysUnmount(target, syscall.MNT_FORCE)
-		if err != nil {
-			log.Errorf("Could not Umount the target path: %s %s %s", volumeID, target, err.Error())
-			if !strings.Contains(err.Error(), "no such file") {
-				return err
-			}
-		}
-	}
-
-	err = osRemove(target)
-	if err != nil && !os.IsNotExist(err) {
-		log.Errorf("UnmountVolume %s could not remove directory %s: %s", volumeID, target, err.Error())
-		return err
+	log.Infof("[UnmountVolume] Target: %s", target)
+	// Unpublish the target directory for sharedNFS first. This will properly clean up and /dev/sdX entries associated to the mount.
+	_, err := nodeSvc.NodeUnpublishVolume(ctx, nodeUnpublishReq)
+	if err != nil {
+		return fmt.Errorf("[UnmountVolume] unpublish failed for %s: %e", target, err)
 	}
 
 	nodeUnstageReq := &csi.NodeUnstageVolumeRequest{
@@ -207,28 +178,26 @@ func (s *service) UnmountVolume(ctx context.Context, volumeID, exportPath string
 		StagingTargetPath: staging,
 	}
 
-	log.Infof("UnmountVolume calling NodeUnstageVolume %+v", nodeUnstageReq)
+	// Unmount the staging directory for sharedNFS (-dev) to clean up anything related to the mount.
+	log.Infof("[UnmountVolume] Staging: %s", staging)
 	_, err = nodeSvc.NodeUnstageVolume(ctx, nodeUnstageReq)
 	if err != nil {
-		return fmt.Errorf("UnmountVolume unstaging volume %s failed: %e", volumeID, err)
+		return fmt.Errorf("[UnmountVolume] unstage failed for %s: %e", staging, err)
 	}
-	log.Infof("NodeUnstage %s %s returned successfully", volumeID, exportPath)
 
-	// Remove the staging path.
-	err = osRemove(staging)
+	// After successfully unmounting/unstaging both target and staging, remove both.
+	err = osRemove(target)
 	if err != nil && !os.IsNotExist(err) {
-		log.Infof("UnmountVolume Remove %s staging path %s failed: %s", volumeID, "/noderoot/"+staging, err)
+		log.Errorf("[UnmountVolume] could not remove target path %s: %s", target, err.Error())
+		return err
 	}
-	log.Infof("UnmountVolume %s ALL GOOD", volumeID)
-	return nil
-}
 
-func (s *service) retrieveMounts() ([]gofsutil.Info, error) {
-	mounts, err := s.fs.GetUtil().GetMounts(context.Background())
-	if err != nil {
-		log.Errorf("could not reliably determine existing mount status: %s", err.Error())
-		return nil, status.Errorf(codes.Internal, "could not reliably determine existing mount status: %s", err.Error())
+	err = osRemove(staging)
+	if err != nil && !os.IsNotExist(err) {
+		log.Errorf("[UnmountVolume] could not remove staging path %s: %s", staging, err.Error())
+		return err
 	}
 
-	return mounts, nil
+	log.Infof("[UnmountVolume] Successfully unmounted volume %s", volumeID)
+	return nil
 }
diff --git a/pkg/service/node_test.go b/pkg/service/node_test.go
index aee0a0e6..39fe98cf 100644
--- a/pkg/service/node_test.go
+++ b/pkg/service/node_test.go
@@ -19,12 +19,13 @@ package service
 import (
 	"context"
 	"errors"
+	"fmt"
+	"strings"
 	"testing"
 
 	csi "github.com/container-storage-interface/spec/lib/go/csi"
 	"github.com/dell/csi-powerstore/v2/mocks"
 	nfsmock "github.com/dell/csm-sharednfs/nfs/mocks"
-	"github.com/dell/gofsutil"
 	"github.com/stretchr/testify/assert"
 	"go.uber.org/mock/gomock"
 )
@@ -154,8 +155,12 @@ func TestNodeUnstageVolume(t *testing.T) {
 	PutNodeService(mockNode)
 	t.Run("nfs volume", func(t *testing.T) {
 		req := &csi.NodeUnstageVolumeRequest{
-			VolumeId: "nfs-123",
+			VolumeId: "nfs-aaaa-bbbb-cccc-dddd-eeeeeeeeeeee",
 		}
+		mockNFSSvc := nfsmock.NewMockService(ctrl)
+		mockNFSSvc.EXPECT().NodeUnstageVolume(gomock.Any(), req).AnyTimes().Return(&csi.NodeUnstageVolumeResponse{}, nil)
+		PutNfsService(mockNFSSvc)
+
 		resp, err := svc.NodeUnstageVolume(context.Background(), req)
 		assert.Nil(t, err)
 		assert.Empty(t, resp)
@@ -180,8 +185,12 @@ func TestNodeStageVolume(t *testing.T) {
 	PutNodeService(mockNode)
 	t.Run("nfs volume", func(t *testing.T) {
 		req := &csi.NodeStageVolumeRequest{
-			VolumeId: "nfs-123",
+			VolumeId: "nfs-aaaa-bbbb-cccc-dddd-eeeeeeeeeeee",
 		}
+		mockNFSSvc := nfsmock.NewMockService(ctrl)
+		mockNFSSvc.EXPECT().NodeStageVolume(gomock.Any(), req).AnyTimes().Return(&csi.NodeStageVolumeResponse{}, nil)
+		PutNfsService(mockNFSSvc)
+
 		resp, err := svc.NodeStageVolume(context.Background(), req)
 		assert.Nil(t, err)
 		assert.Empty(t, resp)
@@ -260,67 +269,45 @@ func TestUnmountVolume(t *testing.T) {
 	defer ctrl.Finish()
 	ctx := context.Background()
 
-	t.Run("Umount fail", func(t *testing.T) {
-		utilMock := new(mocks.UtilInterface)
-		fsMock := new(mocks.FsInterface)
+	t.Run("fail: unable to unpublish volume", func(t *testing.T) {
+		mockNode := mocks.NewMockInterface(ctrl)
+		oldRemove := osRemove
 
-		defaultSysUnmount := sysUnmount
-		sysUnmount = func(_ string, _ int) error {
-			return errors.New("operation not permitted")
-		}
 		defer func() {
-			sysUnmount = defaultSysUnmount
+			osRemove = oldRemove
 		}()
 
-		utilMock.On("GetMounts", ctx).Return([]gofsutil.Info{
-			{
-				Path: "/var/lib/dell/nfs/myNfsMount",
-			},
-		}, nil)
-		fsMock.On("GetUtil").Return(utilMock)
-
-		svc := service{
-			fs: fsMock,
+		osRemove = func(_ string) error {
+			return nil
 		}
 
+		svc := service{}
+
+		mockNode.EXPECT().NodeUnpublishVolume(gomock.Any(), gomock.Any()).AnyTimes().Return(nil, fmt.Errorf("failed to unstage volume"))
+		PutNodeService(mockNode)
+
 		err := svc.UnmountVolume(ctx, "", "/var/lib/dell/nfs/", map[string]string{
 			"ServiceName": "myNfsMount",
 		})
 		assert.NotNil(t, err)
 	})
 
-	t.Run("success", func(t *testing.T) {
-		utilMock := new(mocks.UtilInterface)
-		fsMock := new(mocks.FsInterface)
-
+	t.Run("success: unmount volume", func(t *testing.T) {
 		mockNode := mocks.NewMockInterface(ctrl)
 		oldRemove := osRemove
-		oldUnmount := sysUnmount
 		defer func() {
 			osRemove = oldRemove
-			sysUnmount = oldUnmount
 		}()
 
-		utilMock.On("GetMounts", ctx).Return([]gofsutil.Info{
-			{
-				Path: "/var/lib/dell/nfs/myNfsMount",
-			},
-		}, nil)
-		fsMock.On("GetUtil").Return(utilMock)
-
 		osRemove = func(_ string) error {
 			return nil
 		}
-		sysUnmount = func(_ string, _ int) error {
-			return nil
-		}
 
+		mockNode.EXPECT().NodeUnpublishVolume(gomock.Any(), gomock.Any()).AnyTimes().Return(&csi.NodeUnpublishVolumeResponse{}, nil)
 		mockNode.EXPECT().NodeUnstageVolume(gomock.Any(), gomock.Any()).AnyTimes().Return(&csi.NodeUnstageVolumeResponse{}, nil)
 		PutNodeService(mockNode)
 
-		svc := service{
-			fs: fsMock,
-		}
+		svc := service{}
 
 		err := svc.UnmountVolume(ctx, "123", "/var/lib/dell/nfs/", map[string]string{
 			"ServiceName": "myNfsMount",
@@ -328,112 +315,53 @@ func TestUnmountVolume(t *testing.T) {
 		assert.Nil(t, err)
 	})
 
-	t.Run("remove fail", func(t *testing.T) {
-		utilMock := new(mocks.UtilInterface)
-		fsMock := new(mocks.FsInterface)
-
+	t.Run("fail: osRemove error of target", func(t *testing.T) {
 		mockNode := mocks.NewMockInterface(ctrl)
 		oldRemove := osRemove
-		oldUnmount := sysUnmount
 		defer func() {
 			osRemove = oldRemove
-			sysUnmount = oldUnmount
 		}()
 
-		utilMock.On("GetMounts", ctx).Return([]gofsutil.Info{
-			{
-				Path: "/var/lib/dell/nfs/myNfsMount",
-			},
-		}, nil)
-		fsMock.On("GetUtil").Return(utilMock)
-
 		osRemove = func(_ string) error {
-			return errors.New("remove error")
-		}
-		sysUnmount = func(_ string, _ int) error {
-			return nil
+			return errors.New("unable to remove target")
 		}
 
+		mockNode.EXPECT().NodeUnpublishVolume(gomock.Any(), gomock.Any()).AnyTimes().Return(&csi.NodeUnpublishVolumeResponse{}, nil)
 		mockNode.EXPECT().NodeUnstageVolume(gomock.Any(), gomock.Any()).AnyTimes().Return(&csi.NodeUnstageVolumeResponse{}, nil)
 		PutNodeService(mockNode)
 
-		svc := service{
-			fs: fsMock,
-		}
+		svc := service{}
 
 		err := svc.UnmountVolume(ctx, "123", "/var/lib/dell/nfs/", map[string]string{
 			"ServiceName": "myNfsMount",
 		})
-		assert.Contains(t, err.Error(), "remove error")
+		assert.Contains(t, err.Error(), "unable to remove target")
 	})
 
-	t.Run("unstage error", func(t *testing.T) {
-		utilMock := new(mocks.UtilInterface)
-		fsMock := new(mocks.FsInterface)
-
+	t.Run("fail: osRemove error of staging", func(t *testing.T) {
 		mockNode := mocks.NewMockInterface(ctrl)
 		oldRemove := osRemove
-		oldUnmount := sysUnmount
 		defer func() {
 			osRemove = oldRemove
-			sysUnmount = oldUnmount
 		}()
 
-		utilMock.On("GetMounts", ctx).Return([]gofsutil.Info{
-			{
-				Path: "/var/lib/dell/nfs/myNfsMount",
-			},
-		}, nil)
-		fsMock.On("GetUtil").Return(utilMock)
+		osRemove = func(file string) error {
+			if strings.Contains(file, "-dev") {
+				return errors.New("unable to remove staging")
+			}
 
-		osRemove = func(_ string) error {
-			return nil
-		}
-		sysUnmount = func(_ string, _ int) error {
 			return nil
 		}
 
-		mockNode.EXPECT().NodeUnstageVolume(gomock.Any(), gomock.Any()).AnyTimes().Return(&csi.NodeUnstageVolumeResponse{}, errors.New("unstage error"))
+		mockNode.EXPECT().NodeUnpublishVolume(gomock.Any(), gomock.Any()).AnyTimes().Return(&csi.NodeUnpublishVolumeResponse{}, nil)
+		mockNode.EXPECT().NodeUnstageVolume(gomock.Any(), gomock.Any()).AnyTimes().Return(&csi.NodeUnstageVolumeResponse{}, nil)
 		PutNodeService(mockNode)
 
-		svc := service{
-			fs: fsMock,
-		}
-
-		err := svc.UnmountVolume(ctx, "123", "/var/lib/dell/nfs/", map[string]string{
-			"ServiceName": "myNfsMount",
-		})
-		assert.Contains(t, err.Error(), "unstage error")
-	})
-
-	t.Run("unable to determine mounts", func(t *testing.T) {
-		utilMock := new(mocks.UtilInterface)
-		fsMock := new(mocks.FsInterface)
-
-		oldRemove := osRemove
-		oldUnmount := sysUnmount
-		defer func() {
-			osRemove = oldRemove
-			sysUnmount = oldUnmount
-		}()
-
-		utilMock.On("GetMounts", ctx).Return([]gofsutil.Info{}, errors.New("get mounts error"))
-		fsMock.On("GetUtil").Return(utilMock)
-
-		osRemove = func(_ string) error {
-			return nil
-		}
-		sysUnmount = func(_ string, _ int) error {
-			return nil
-		}
-
-		svc := service{
-			fs: fsMock,
-		}
+		svc := service{}
 
 		err := svc.UnmountVolume(ctx, "123", "/var/lib/dell/nfs/", map[string]string{
 			"ServiceName": "myNfsMount",
 		})
-		assert.Contains(t, err.Error(), "get mounts error")
+		assert.Contains(t, err.Error(), "unable to remove staging")
 	})
 }
diff --git a/pkg/service/service.go b/pkg/service/service.go
index 740a1632..e3aebd77 100644
--- a/pkg/service/service.go
+++ b/pkg/service/service.go
@@ -23,13 +23,11 @@ import (
 	"time"
 
 	"github.com/dell/csi-powerstore/v2/pkg/common"
-	"github.com/dell/csi-powerstore/v2/pkg/common/fs"
 	"github.com/dell/csi-powerstore/v2/pkg/controller"
 	"github.com/dell/csi-powerstore/v2/pkg/node"
 	"github.com/dell/csm-sharednfs/nfs"
 	"github.com/dell/gocsi"
 	csictx "github.com/dell/gocsi/context"
-	"github.com/dell/gofsutil"
 	"github.com/fsnotify/fsnotify"
 	"github.com/sirupsen/logrus"
 	"github.com/spf13/viper"
@@ -41,7 +39,6 @@ import (
 var log = logrus.New()
 
 var (
-	syncNodeInfoChan chan bool
 	// DriverConfig driver config
 	DriverConfig string
 	// DriverSecret driver secret
@@ -52,18 +49,14 @@ var (
 	nfssvc        nfs.Service
 
 	mx = sync.Mutex{}
-	px = sync.Mutex{}
 )
 
 type service struct {
 	mode string
-	fs   fs.Interface
 }
 
 func New() nfs.Service {
-	return &service{
-		fs: &fs.Fs{Util: &gofsutil.FS{}},
-	}
+	return &service{}
 }
 
 func (s *service) BeforeServe(ctx context.Context, sp *gocsi.StoragePlugin, lis net.Listener) error {


Reviews:
user: login: xuluna
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-05-15T16:20:34Z
user: login: lukeatdell
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-05-15T16:21:03Z
user: login: bharathsreekanth
state: DISMISSED
author_association: CONTRIBUTOR
submitted_at: 2025-05-15T16:57:05Z
user: login: AronAtDell
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-05-15T17:30:38Z
user: login: xuluna
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-05-15T18:06:38Z
user: login: lukeatdell
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-05-15T18:17:35Z
user: login: lukeatdell
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-05-15T19:49:59Z
user: login: bharathsreekanth
state: DISMISSED
author_association: CONTRIBUTOR
submitted_at: 2025-05-15T20:12:33Z
user: login: xuluna
state: DISMISSED
author_association: CONTRIBUTOR
submitted_at: 2025-05-15T20:22:09Z
user: login: santhoshatdell
state: DISMISSED
author_association: CONTRIBUTOR
submitted_at: 2025-05-15T20:28:38Z
user: login: xuluna
state: DISMISSED
author_association: CONTRIBUTOR
submitted_at: 2025-05-16T13:49:35Z
user: login: tdawe
state: APPROVED
author_association: CONTRIBUTOR
submitted_at: 2025-05-16T18:18:07Z
user: login: ChristianAtDell
state: APPROVED
author_association: CONTRIBUTOR
submitted_at: 2025-05-16T18:27:17Z
user: login: santhoshatdell
state: APPROVED
author_association: CONTRIBUTOR
submitted_at: 2025-05-16T18:32:09Z

Review comments:
diff_hunk: @@ -7,9 +7,9 @@ toolchain go1.24.2
 require (
 	github.com/akutz/gosync v0.1.0
 	github.com/apparentlymart/go-cidr v1.1.0
-	github.com/container-storage-interface/spec v1.6.0
+	github.com/container-storage-interface/spec v1.8.0
path: go.mod
user: login: xuluna
body: @anandrajak1 says we can't change the spec to 1.8.0
created_at: 2025-05-15T16:20:34Z
author_association: CONTRIBUTOR
diff_hunk: @@ -7,9 +7,9 @@ toolchain go1.24.2
 require (
 	github.com/akutz/gosync v0.1.0
 	github.com/apparentlymart/go-cidr v1.1.0
-	github.com/container-storage-interface/spec v1.6.0
+	github.com/container-storage-interface/spec v1.8.0
path: go.mod
user: login: lukeatdell
body: ah yep. that's why we have reviews. 
thank you!
created_at: 2025-05-15T16:21:03Z
author_association: CONTRIBUTOR
diff_hunk: @@ -81,8 +81,8 @@ github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDk
 github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc=
 github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk=
 github.com/container-storage-interface/spec v1.5.0/go.mod h1:8K96oQNkJ7pFcC2R9Z1ynGGBB1I93kcS6PGg3SsOk8s=
-github.com/container-storage-interface/spec v1.6.0 h1:vwN9uCciKygX/a0toYryoYD5+qI9ZFeAMuhEEKO+JBA=
-github.com/container-storage-interface/spec v1.6.0/go.mod h1:8K96oQNkJ7pFcC2R9Z1ynGGBB1I93kcS6PGg3SsOk8s=
+github.com/container-storage-interface/spec v1.8.0 h1:D0vhF3PLIZwlwZEf2eNbpujGCNwspwTYf2idJRJx4xI=
path: go.sum
user: login: AronAtDell
body: See @xuluna 's comment above.
created_at: 2025-05-15T17:30:37Z
author_association: CONTRIBUTOR
diff_hunk: @@ -43,15 +43,15 @@ var (
 // NodeStageVolume prepares volume to be consumed by node publish by connecting volume to the node
 func (s *service) NodeStageVolume(ctx context.Context, req *csi.NodeStageVolumeRequest) (*csi.NodeStageVolumeResponse, error) {
 	if nfs.IsNFSVolumeID(req.GetVolumeId()) {
-		return &csi.NodeStageVolumeResponse{}, nil
+		return nfssvc.NodeStageVolume(ctx, req)
path: pkg/service/node.go
user: login: xuluna
body: Are we doing Stage and Unstage multiple times (in MountVolume as well)?
created_at: 2025-05-15T18:06:38Z
author_association: CONTRIBUTOR
diff_hunk: @@ -81,8 +81,8 @@ github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDk
 github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc=
 github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk=
 github.com/container-storage-interface/spec v1.5.0/go.mod h1:8K96oQNkJ7pFcC2R9Z1ynGGBB1I93kcS6PGg3SsOk8s=
-github.com/container-storage-interface/spec v1.6.0 h1:vwN9uCciKygX/a0toYryoYD5+qI9ZFeAMuhEEKO+JBA=
-github.com/container-storage-interface/spec v1.6.0/go.mod h1:8K96oQNkJ7pFcC2R9Z1ynGGBB1I93kcS6PGg3SsOk8s=
+github.com/container-storage-interface/spec v1.8.0 h1:D0vhF3PLIZwlwZEf2eNbpujGCNwspwTYf2idJRJx4xI=
path: go.sum
user: login: lukeatdell
body: addressed
created_at: 2025-05-15T18:17:35Z
author_association: CONTRIBUTOR
diff_hunk: @@ -43,15 +43,15 @@ var (
 // NodeStageVolume prepares volume to be consumed by node publish by connecting volume to the node
 func (s *service) NodeStageVolume(ctx context.Context, req *csi.NodeStageVolumeRequest) (*csi.NodeStageVolumeResponse, error) {
 	if nfs.IsNFSVolumeID(req.GetVolumeId()) {
-		return &csi.NodeStageVolumeResponse{}, nil
+		return nfssvc.NodeStageVolume(ctx, req)
path: pkg/service/node.go
user: login: lukeatdell
body: I don't think I understand what you're asking here. Can you clarify?
created_at: 2025-05-15T19:49:59Z
author_association: CONTRIBUTOR

PR comments:
user: login: github-actions[bot]
created_at: 2025-05-16T18:03:27Z
body: ### Merging this branch will **not change** overall coverage

| Impacted Packages | Coverage Δ | :robot: |
|-------------------|------------|---------|
| github.com/dell/csi-powerstore/pkg/service | 0.00% (ø) |  |

---

<details>

<summary>Coverage by file</summary>

### Changed files (no unit tests)

| Changed File | Coverage Δ | Total | Covered | Missed | :robot: |
|--------------|------------|-------|---------|--------|---------|
| github.com/dell/csi-powerstore/pkg/service/node.go | 0.00% (ø) | 0 | 0 | 0 |  |
| github.com/dell/csi-powerstore/pkg/service/service.go | 0.00% (ø) | 0 | 0 | 0 |  |

_Please note that the "Total", "Covered", and "Missed" counts above refer to ***code statements*** instead of lines of code. The value in brackets refers to the test coverage of that file in the old version of the code._

### Changed unit test files

- github.com/dell/csi-powerstore/pkg/service/node_test.go

</details>

author_association: CONTRIBUTOR