diff --git a/charts/README.md b/charts/README.md index 42de31df..ef10152c 100644 --- a/charts/README.md +++ b/charts/README.md @@ -68,7 +68,7 @@ The following table lists the configurable parameters of the latest NFS CSI Driv | `controller.runOnMaster` | run controller on master node(deprecated on k8s 1.25+) |`false` | | `controller.runOnControlPlane` | run controller on control plane node |`false` | | `controller.dnsPolicy` | dnsPolicy of controller driver, available values: `Default`, `ClusterFirstWithHostNet`, `ClusterFirst` | `ClusterFirstWithHostNet` | -| `controller.defaultOnDeletePolicy` | default policy for deleting subdirectory when deleting a volume, available values: `delete`, `retain` | `delete` | +| `controller.defaultOnDeletePolicy` | default policy for deleting subdirectory when deleting a volume, available values: `delete`, `retain`, `archive` | `delete` | | `controller.logLevel` | controller driver log level |`5` | | `controller.workingMountDir` | working directory for provisioner to mount nfs shares temporarily | `/tmp` | | `controller.affinity` | controller pod affinity | `{}` | diff --git a/docs/driver-parameters.md b/docs/driver-parameters.md index d0bb4b01..87609d43 100644 --- a/docs/driver-parameters.md +++ b/docs/driver-parameters.md @@ -10,7 +10,7 @@ server | NFS Server address | domain name `nfs-server.default.svc.cluster.local` share | NFS share path | `/` | Yes | subDir | sub directory under nfs share | | No | if sub directory does not exist, this driver would create a new one mountPermissions | mounted folder permissions. The default is `0`, if set as non-zero, driver will perform `chmod` after mount | | No | -onDelete | when volume is deleted, keep the directory if it's `retain` | `delete`(default), `retain` | No | `delete` +onDelete | when volume is deleted, keep the directory if it's `retain` | `delete`(default), `retain`, `archive` | No | `delete` - VolumeID(`volumeHandle`) is the identifier of the volume handled by the driver, format of VolumeID: ``` diff --git a/pkg/nfs/controllerserver.go b/pkg/nfs/controllerserver.go index 99c2ef8f..4ecfc6c6 100644 --- a/pkg/nfs/controllerserver.go +++ b/pkg/nfs/controllerserver.go @@ -240,15 +240,27 @@ func (cs *ControllerServer) DeleteVolume(ctx context.Context, req *csi.DeleteVol } }() - // delete subdirectory under base-dir internalVolumePath := getInternalVolumePath(cs.Driver.workingMountDir, nfsVol) - klog.V(2).Infof("Removing subdirectory at %v", internalVolumePath) - if err = os.RemoveAll(internalVolumePath); err != nil { - return nil, status.Errorf(codes.Internal, "failed to delete subdirectory: %v", err.Error()) + if !strings.EqualFold(nfsVol.onDelete, archive) { + // delete subdirectory under base-dir + klog.V(2).Infof("Removing subdirectory at %v", internalVolumePath) + if err = os.RemoveAll(internalVolumePath); err != nil { + return nil, status.Errorf(codes.Internal, "failed to delete subdirectory: %v", err.Error()) + } + } else { + archivedNfsVol := *nfsVol + archivedNfsVol.subDir = "archived-" + nfsVol.subDir + archivedInternalVolumePath := getArchivedInternalVolumePath(cs.Driver.workingMountDir, nfsVol, &archivedNfsVol) + + // archive subdirectory under base-dir + klog.V(2).Infof("Archiving subdirectory at %v", internalVolumePath) + if err = os.Rename(internalVolumePath, archivedInternalVolumePath); err != nil { + return nil, status.Errorf(codes.Internal, "failed to archive subdirectory: %v", err.Error()) + } } } else { - klog.V(2).Infof("DeleteVolume: volume(%s) is set to retain, not deleting subdirectory", volumeID) + klog.V(2).Infof("DeleteVolume: volume(%s) is set to retain, not deleting/archiving subdirectory", volumeID) } return &csi.DeleteVolumeResponse{}, nil @@ -674,6 +686,10 @@ func getInternalVolumePath(workingMountDir string, vol *nfsVolume) string { return filepath.Join(getInternalMountPath(workingMountDir, vol), vol.subDir) } +func getArchivedInternalVolumePath(workingMountDir string, vol *nfsVolume, archVol *nfsVolume) string { + return filepath.Join(getInternalMountPath(workingMountDir, vol), archVol.subDir) +} + // Given a nfsVolume, return a CSI volume id func getVolumeIDFromNfsVol(vol *nfsVolume) string { idElements := make([]string, totalIDElements) @@ -681,9 +697,10 @@ func getVolumeIDFromNfsVol(vol *nfsVolume) string { idElements[idBaseDir] = strings.Trim(vol.baseDir, "/") idElements[idSubDir] = strings.Trim(vol.subDir, "/") idElements[idUUID] = vol.uuid - if strings.EqualFold(vol.onDelete, retain) { + if strings.EqualFold(vol.onDelete, retain) || strings.EqualFold(vol.onDelete, archive) { idElements[idOnDelete] = vol.onDelete } + return strings.Join(idElements, separator) } diff --git a/pkg/nfs/controllerserver_test.go b/pkg/nfs/controllerserver_test.go index 6c2c789d..f1694240 100644 --- a/pkg/nfs/controllerserver_test.go +++ b/pkg/nfs/controllerserver_test.go @@ -38,18 +38,19 @@ import ( ) const ( - testServer = "test-server" - testBaseDir = "test-base-dir" - testBaseDirNested = "test/base/dir" - testCSIVolume = "volume-name" - testVolumeID = "test-server/test-base-dir/volume-name" - newTestVolumeID = "test-server#test-base-dir#volume-name##" - newTestVolumeWithVolumeID = "test-server#test-base-dir#volume-name#volume-name#" - testVolumeIDNested = "test-server/test/base/dir/volume-name" - newTestVolumeIDNested = "test-server#test/base/dir#volume-name#" - newTestVolumeIDUUID = "test-server#test-base-dir#volume-name#uuid" - newTestVolumeOnDeleteRetain = "test-server#test-base-dir#volume-name#uuid#retain" - newTestVolumeOnDeleteDelete = "test-server#test-base-dir#volume-name#uuid#delete" + testServer = "test-server" + testBaseDir = "test-base-dir" + testBaseDirNested = "test/base/dir" + testCSIVolume = "volume-name" + testVolumeID = "test-server/test-base-dir/volume-name" + newTestVolumeID = "test-server#test-base-dir#volume-name##" + newTestVolumeWithVolumeID = "test-server#test-base-dir#volume-name#volume-name#" + testVolumeIDNested = "test-server/test/base/dir/volume-name" + newTestVolumeIDNested = "test-server#test/base/dir#volume-name#" + newTestVolumeIDUUID = "test-server#test-base-dir#volume-name#uuid" + newTestVolumeOnDeleteRetain = "test-server#test-base-dir#volume-name#uuid#retain" + newTestVolumeOnDeleteDelete = "test-server#test-base-dir#volume-name#uuid#delete" + newTestVolumeOnDeleteArchive = "test-server#test-base-dir#volume-name#uuid#archive" ) func initTestController(t *testing.T) *ControllerServer { @@ -287,6 +288,14 @@ func TestDeleteVolume(t *testing.T) { expectedErr: nil, expectedDeleteSubDir: false, }, + { + desc: "Valid request with onDelete:archive", + testOnWindows: true, + req: &csi.DeleteVolumeRequest{VolumeId: newTestVolumeOnDeleteArchive}, + resp: &csi.DeleteVolumeResponse{}, + expectedErr: nil, + expectedDeleteSubDir: false, + }, } for _, test := range cases { @@ -493,6 +502,19 @@ func TestNfsVolFromId(t *testing.T) { }, expectErr: false, }, + { + name: "valid request nested ondelete archive", + volumeID: newTestVolumeOnDeleteArchive, + resp: &nfsVolume{ + id: newTestVolumeOnDeleteArchive, + server: testServer, + baseDir: testBaseDir, + subDir: testCSIVolume, + uuid: "uuid", + onDelete: "archive", + }, + expectErr: false, + }, } for _, test := range cases { diff --git a/pkg/nfs/utils.go b/pkg/nfs/utils.go index 262b5a9b..d20bc8e0 100644 --- a/pkg/nfs/utils.go +++ b/pkg/nfs/utils.go @@ -36,9 +36,10 @@ const ( separator = "#" delete = "delete" retain = "retain" + archive = "archive" ) -var supportedOnDeleteValues = []string{"", delete, retain} +var supportedOnDeleteValues = []string{"", delete, retain, archive} func validateOnDeleteValue(onDelete string) error { for _, v := range supportedOnDeleteValues { diff --git a/pkg/nfs/utils_test.go b/pkg/nfs/utils_test.go index 3a31693e..33b74787 100644 --- a/pkg/nfs/utils_test.go +++ b/pkg/nfs/utils_test.go @@ -333,6 +333,16 @@ func TestValidateOnDeleteValue(t *testing.T) { onDelete: "Delete", expected: nil, }, + { + desc: "Archive value", + onDelete: "Archive", + expected: nil, + }, + { + desc: "archive value", + onDelete: "archive", + expected: nil, + }, { desc: "invalid value", onDelete: "invalid", diff --git a/test/e2e/e2e_suite_test.go b/test/e2e/e2e_suite_test.go index 797eec2d..5be8dcb1 100644 --- a/test/e2e/e2e_suite_test.go +++ b/test/e2e/e2e_suite_test.go @@ -75,6 +75,14 @@ var ( "mountPermissions": "0755", "onDelete": "retain", } + archiveStorageClassParameters = map[string]string{ + "server": nfsServerAddress, + "share": nfsShare, + "csi.storage.k8s.io/provisioner-secret-name": "mount-options", + "csi.storage.k8s.io/provisioner-secret-namespace": "default", + "mountPermissions": "0755", + "onDelete": "archive", + } controllerServer *nfs.ControllerServer )