Skip to content

Commit 5651c85

Browse files
committed
fix: only cleanup empty parent directories during DeleteVolume
fix fix
1 parent bd2fa2f commit 5651c85

File tree

3 files changed

+116
-9
lines changed

3 files changed

+116
-9
lines changed

pkg/nfs/controllerserver.go

Lines changed: 8 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -292,17 +292,16 @@ func (cs *ControllerServer) DeleteVolume(ctx context.Context, req *csi.DeleteVol
292292
}
293293
klog.V(2).Infof("archived subdirectory %s --> %s", internalVolumePath, archivedInternalVolumePath)
294294
} else {
295-
rootDir := getRootDir(nfsVol.subDir)
296-
if rootDir != "" {
297-
rootDir = filepath.Join(getInternalMountPath(cs.Driver.workingMountDir, nfsVol), rootDir)
298-
} else {
299-
rootDir = internalVolumePath
300-
}
301-
// delete subdirectory under base-dir
302-
klog.V(2).Infof("removing subdirectory at %v on internalVolumePath %s", rootDir, internalVolumePath)
303-
if err = os.RemoveAll(rootDir); err != nil {
295+
klog.V(2).Infof("removing subdirectory at %v", internalVolumePath)
296+
if err = os.RemoveAll(internalVolumePath); err != nil {
304297
return nil, status.Errorf(codes.Internal, "delete subdirectory(%s) failed with %v", internalVolumePath, err)
305298
}
299+
300+
parentDir := filepath.Dir(internalVolumePath)
301+
klog.V(2).Infof("DeleteVolume: removing empty directories in %s", parentDir)
302+
if err = removeEmptyDirs(getInternalMountPath(cs.Driver.workingMountDir, nfsVol), parentDir); err != nil {
303+
return nil, status.Errorf(codes.Internal, "failed to remove empty directories: %v", err)
304+
}
306305
}
307306
} else {
308307
klog.V(2).Infof("DeleteVolume: volume(%s) is set to retain, not deleting/archiving subdirectory", volumeID)

pkg/nfs/utils.go

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ package nfs
1919
import (
2020
"fmt"
2121
"os"
22+
"path/filepath"
2223
"strings"
2324
"sync"
2425
"time"
@@ -222,6 +223,52 @@ func getRootDir(path string) string {
222223
return parts[0]
223224
}
224225

226+
// removeEmptyDirs removes empty directories in the given directory dir until the parent directory parentDir
227+
// It will remove all empty directories in the path from the given directory to the parent directory
228+
// It will not remove the parent directory parentDir
229+
func removeEmptyDirs(parentDir, dir string) error {
230+
if parentDir == "" || dir == "" {
231+
return nil
232+
}
233+
234+
absParentDir, err := filepath.Abs(parentDir)
235+
if err != nil {
236+
return err
237+
}
238+
239+
absDir, err := filepath.Abs(dir)
240+
if err != nil {
241+
return err
242+
}
243+
244+
if !strings.HasPrefix(absDir, absParentDir) {
245+
return fmt.Errorf("dir %s is not a subdirectory of parentDir %s", dir, parentDir)
246+
}
247+
248+
var depth int
249+
for absDir != absParentDir {
250+
entries, err := os.ReadDir(absDir)
251+
if err != nil {
252+
return err
253+
}
254+
if len(entries) == 0 {
255+
klog.V(2).Infof("Removing empty directory %s", absDir)
256+
if err := os.Remove(absDir); err != nil {
257+
return err
258+
}
259+
} else {
260+
klog.V(2).Infof("Directory %s is not empty", absDir)
261+
break
262+
}
263+
if depth++; depth > 10 {
264+
return fmt.Errorf("depth of directory %s is too deep", dir)
265+
}
266+
absDir = filepath.Dir(absDir)
267+
}
268+
269+
return nil
270+
}
271+
225272
// ExecFunc returns a exec function's output and error
226273
type ExecFunc func() (err error)
227274

pkg/nfs/utils_test.go

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -429,6 +429,67 @@ func TestGetRootPath(t *testing.T) {
429429
}
430430
}
431431

432+
func TestRemoveEmptyDirs(t *testing.T) {
433+
parentDir, _ := os.Getwd()
434+
emptyDirOneLevel, _ := getWorkDirPath("emptyDir1")
435+
emptyDirTwoLevels, _ := getWorkDirPath("emptyDir2/emptyDir2")
436+
emptyDirThreeLevels, _ := getWorkDirPath("emptyDir3/emptyDir2/emptyDir3")
437+
438+
tests := []struct {
439+
desc string
440+
parentDir string
441+
dir string
442+
expected error
443+
}{
444+
{
445+
desc: "empty path",
446+
parentDir: parentDir,
447+
expected: nil,
448+
},
449+
{
450+
desc: "empty dir with one level",
451+
parentDir: parentDir,
452+
dir: emptyDirOneLevel,
453+
expected: nil,
454+
},
455+
{
456+
desc: "dir is not a subdirectory of parentDir",
457+
parentDir: "/dir1",
458+
dir: "/dir2",
459+
expected: fmt.Errorf("dir /dir2 is not a subdirectory of parentDir /dir1"),
460+
},
461+
{
462+
desc: "empty dir with two levels",
463+
parentDir: parentDir,
464+
dir: emptyDirTwoLevels,
465+
expected: nil,
466+
},
467+
{
468+
desc: "empty dir with three levels",
469+
parentDir: parentDir,
470+
dir: emptyDirThreeLevels,
471+
expected: nil,
472+
},
473+
}
474+
475+
for _, test := range tests {
476+
if strings.Contains(test.dir, "emptyDir") {
477+
_ = makeDir(test.dir)
478+
defer os.RemoveAll(test.dir)
479+
}
480+
err := removeEmptyDirs(test.parentDir, test.dir)
481+
if !reflect.DeepEqual(err, test.expected) {
482+
t.Errorf("test[%s]: unexpected output: %v, expected result: %v", test.desc, err, test.expected)
483+
}
484+
if strings.Contains(test.dir, "emptyDir") {
485+
// directory should be removed
486+
if _, err := os.Stat(emptyDirOneLevel); !os.IsNotExist(err) {
487+
t.Errorf("test[%s]: directory %s should be removed", test.desc, emptyDirOneLevel)
488+
}
489+
}
490+
}
491+
}
492+
432493
func TestWaitUntilTimeout(t *testing.T) {
433494
tests := []struct {
434495
desc string

0 commit comments

Comments
 (0)