Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion charts/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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 | `{}` |
Expand Down
2 changes: 1 addition & 1 deletion docs/driver-parameters.md
Original file line number Diff line number Diff line change
Expand Up @@ -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:
```
Expand Down
29 changes: 23 additions & 6 deletions pkg/nfs/controllerserver.go
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
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 %s --> %s", internalVolumePath, archivedInternalVolumePath)
if err = os.Rename(internalVolumePath, archivedInternalVolumePath); err != nil {
return nil, status.Errorf(codes.Internal, "archive subdirectory(%s, %s) failed with %v", internalVolumePath, archivedInternalVolumePath, err.Error())
}
} else {
// 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, "delete subdirectory(%s) failed with %v", internalVolumePath, 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
Expand Down Expand Up @@ -674,16 +686,21 @@ 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)
idElements[idServer] = strings.Trim(vol.server, "/")
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)
}

Expand Down
46 changes: 34 additions & 12 deletions pkg/nfs/controllerserver_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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##archive"
)

func initTestController(t *testing.T) *ControllerServer {
Expand Down Expand Up @@ -287,6 +288,14 @@ func TestDeleteVolume(t *testing.T) {
expectedErr: nil,
expectedDeleteSubDir: false,
},
{
desc: "Valid request with onDelete:archive",
testOnWindows: false,
req: &csi.DeleteVolumeRequest{VolumeId: newTestVolumeOnDeleteArchive},
resp: &csi.DeleteVolumeResponse{},
expectedErr: nil,
expectedDeleteSubDir: true,
},
}

for _, test := range cases {
Expand Down Expand Up @@ -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: "",
onDelete: "archive",
},
expectErr: false,
},
}

for _, test := range cases {
Expand Down
3 changes: 2 additions & 1 deletion pkg/nfs/utils.go
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down
10 changes: 10 additions & 0 deletions pkg/nfs/utils_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand Down
23 changes: 23 additions & 0 deletions test/e2e/dynamic_provisioning_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -350,4 +350,27 @@ var _ = ginkgo.Describe("Dynamic Provisioning", func() {
}
test.Run(cs, ns)
})

ginkgo.It("should create a volume on demand with archive subdir on delete [nfs.csi.k8s.io]", func() {
pods := []testsuites.PodDetails{
{
Cmd: "echo 'hello world' > /mnt/test-1/data && grep 'hello world' /mnt/test-1/data",
Volumes: []testsuites.VolumeDetails{
{
ClaimSize: "10Gi",
VolumeMount: testsuites.VolumeMountDetails{
NameGenerate: "test-volume-",
MountPathGenerate: "/mnt/test-",
},
},
},
},
}
test := testsuites.DynamicallyProvisionedCmdVolumeTest{
CSIDriver: testDriver,
Pods: pods,
StorageClassParameters: archiveStorageClassParameters,
}
test.Run(cs, ns)
})
})
8 changes: 8 additions & 0 deletions test/e2e/e2e_suite_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
)

Expand Down