@@ -18,6 +18,7 @@ package nfs
18
18
19
19
import (
20
20
"fmt"
21
+ "io/fs"
21
22
"os"
22
23
"os/exec"
23
24
"path/filepath"
@@ -71,10 +72,20 @@ type nfsSnapshot struct {
71
72
baseDir string
72
73
// Snapshot name.
73
74
uuid string
75
+ // Source volume.
76
+ src string
77
+ }
78
+
79
+ func (snap nfsSnapshot ) archiveSubPath () string {
80
+ return snap .uuid
74
81
}
75
82
76
83
func (snap nfsSnapshot ) archiveName () string {
77
- return fmt .Sprintf ("%v.tar.gz" , snap .uuid )
84
+ return fmt .Sprintf ("%v.tar.gz" , snap .src )
85
+ }
86
+
87
+ func (snap nfsSnapshot ) archivePath () string {
88
+ return filepath .Join (snap .archiveSubPath (), snap .archiveName ())
78
89
}
79
90
80
91
// Ordering of elements in the CSI volume id.
@@ -92,14 +103,15 @@ const (
92
103
)
93
104
94
105
// Ordering of elements in the CSI snapshot id.
95
- // ID is of the form {server}/{baseDir}/snapshots .
106
+ // ID is of the form {server}/{baseDir}/{snap_name}/{src_name} .
96
107
// Adding a new element should always go at the end
97
108
// before totalSnapIDElements
98
109
const (
99
110
idSnapServer = iota
100
111
idSnapBaseDir
101
112
idSnapUUID
102
- idSnapArchive
113
+ idSnapArchivePath
114
+ idSnapArchiveName
103
115
totalIDSnapElements // Always last
104
116
)
105
117
@@ -309,6 +321,13 @@ func (cs *ControllerServer) CreateSnapshot(ctx context.Context, req *csi.CreateS
309
321
klog .Warningf ("failed to unmount snapshot nfs server: %v" , err )
310
322
}
311
323
}()
324
+ snapInternalVolPath := filepath .Join (getInternalVolumePath (cs .Driver .workingMountDir , snapVol ), snapshot .archiveSubPath ())
325
+ if err = os .MkdirAll (snapInternalVolPath , 0777 ); err != nil {
326
+ return nil , status .Errorf (codes .Internal , "failed to make subdirectory: %v" , err )
327
+ }
328
+ if err := validateSnapshot (snapInternalVolPath , snapshot ); err != nil {
329
+ return nil , err
330
+ }
312
331
313
332
if err = cs .internalMount (ctx , srcVol , nil , nil ); err != nil {
314
333
return nil , status .Errorf (codes .Internal , "failed to mount src nfs server: %v" , err )
@@ -320,7 +339,7 @@ func (cs *ControllerServer) CreateSnapshot(ctx context.Context, req *csi.CreateS
320
339
}()
321
340
322
341
srcPath := getInternalVolumePath (cs .Driver .workingMountDir , srcVol )
323
- dstPath := filepath .Join (getInternalVolumePath ( cs . Driver . workingMountDir , snapVol ) , snapshot .archiveName ())
342
+ dstPath := filepath .Join (snapInternalVolPath , snapshot .archiveName ())
324
343
klog .V (2 ).Infof ("archiving %v -> %v" , srcPath , dstPath )
325
344
out , err := exec .Command ("tar" , "-C" , srcPath , "-czvf" , dstPath , "." ).CombinedOutput ()
326
345
if err != nil {
@@ -380,7 +399,7 @@ func (cs *ControllerServer) DeleteSnapshot(ctx context.Context, req *csi.DeleteS
380
399
}()
381
400
382
401
// delete snapshot archive
383
- internalVolumePath := filepath .Join (getInternalVolumePath (cs .Driver .workingMountDir , vol ), snap .archiveName ())
402
+ internalVolumePath := filepath .Join (getInternalVolumePath (cs .Driver .workingMountDir , vol ), snap .archiveSubPath ())
384
403
klog .V (2 ).Infof ("Removing snapshot archive at %v" , internalVolumePath )
385
404
if err = os .RemoveAll (internalVolumePath ); err != nil {
386
405
return nil , status .Errorf (codes .Internal , "failed to delete subdirectory: %v" , err .Error ())
@@ -397,17 +416,6 @@ func (cs *ControllerServer) ControllerExpandVolume(ctx context.Context, req *csi
397
416
return nil , status .Error (codes .Unimplemented , "" )
398
417
}
399
418
400
- // Volume for snapshot internal mount/unmount
401
- func volumeFromSnapshot (snap * nfsSnapshot ) * nfsVolume {
402
- return & nfsVolume {
403
- id : snap .id ,
404
- server : snap .server ,
405
- baseDir : snap .baseDir ,
406
- subDir : snap .baseDir ,
407
- uuid : snap .uuid ,
408
- }
409
- }
410
-
411
419
// Mount nfs server at base-dir
412
420
func (cs * ControllerServer ) internalMount (ctx context.Context , vol * nfsVolume , volumeContext map [string ]string , volCap * csi.VolumeCapability ) error {
413
421
if volCap == nil {
@@ -485,7 +493,7 @@ func (cs *ControllerServer) copyFromSnapshot(ctx context.Context, req *csi.Creat
485
493
}()
486
494
487
495
// untar snapshot archive to dst path
488
- snapPath := filepath .Join (getInternalVolumePath (cs .Driver .workingMountDir , snapVol ), snap .archiveName ())
496
+ snapPath := filepath .Join (getInternalVolumePath (cs .Driver .workingMountDir , snapVol ), snap .archivePath ())
489
497
dstPath := getInternalVolumePath (cs .Driver .workingMountDir , dstVol )
490
498
klog .V (2 ).Infof ("copy volume from snapshot %v -> %v" , snapPath , dstPath )
491
499
out , err := exec .Command ("tar" , "-xzvf" , snapPath , "-C" , dstPath ).CombinedOutput ()
@@ -568,6 +576,15 @@ func newNFSSnapshot(name string, params map[string]string, vol *nfsVolume) (*nfs
568
576
baseDir : baseDir ,
569
577
uuid : name ,
570
578
}
579
+ if vol .uuid != "" {
580
+ snapshot .src = vol .uuid
581
+ }
582
+ if vol .subDir != "" {
583
+ snapshot .src = vol .subDir
584
+ }
585
+ if snapshot .src == "" {
586
+ return nil , fmt .Errorf ("missing required source volume name" )
587
+ }
571
588
snapshot .id = getSnapshotIDFromNfsSnapshot (snapshot )
572
589
return snapshot , nil
573
590
}
@@ -656,7 +673,8 @@ func getSnapshotIDFromNfsSnapshot(snap *nfsSnapshot) string {
656
673
idElements [idSnapServer ] = strings .Trim (snap .server , "/" )
657
674
idElements [idSnapBaseDir ] = strings .Trim (snap .baseDir , "/" )
658
675
idElements [idSnapUUID ] = snap .uuid
659
- idElements [idSnapArchive ] = snap .archiveName ()
676
+ idElements [idSnapArchivePath ] = snap .uuid
677
+ idElements [idSnapArchiveName ] = snap .src
660
678
return strings .Join (idElements , separator )
661
679
}
662
680
@@ -702,14 +720,15 @@ func getNfsVolFromID(id string) (*nfsVolume, error) {
702
720
// Given a CSI snapshot ID, return a nfsSnapshot
703
721
// sample snapshot ID:
704
722
//
705
- // nfs-server.default.svc.cluster.local#share#snapshot-016f784f-56f4-44d1-9041-5f59e82dbce1#snapshot-016f784f-56f4-44d1-9041-5f59e82dbce1.tar.gz
723
+ // nfs-server.default.svc.cluster.local#share#snapshot-016f784f-56f4-44d1-9041-5f59e82dbce1#snapshot-016f784f-56f4-44d1-9041-5f59e82dbce1#pvc-4bcbf944-b6f7-4bd0-b50f-3c3dd00efc64
706
724
func getNfsSnapFromID (id string ) (* nfsSnapshot , error ) {
707
725
segments := strings .Split (id , separator )
708
- if len (segments ) == 4 {
726
+ if len (segments ) == totalIDSnapElements {
709
727
return & nfsSnapshot {
710
728
id : id ,
711
729
server : segments [idSnapServer ],
712
730
baseDir : segments [idSnapBaseDir ],
731
+ src : segments [idSnapArchiveName ],
713
732
uuid : segments [idSnapUUID ],
714
733
}, nil
715
734
}
@@ -729,3 +748,32 @@ func isValidVolumeCapabilities(volCaps []*csi.VolumeCapability) error {
729
748
}
730
749
return nil
731
750
}
751
+
752
+ // Validate snapshot after internal mount
753
+ func validateSnapshot (snapInternalVolPath string , snap * nfsSnapshot ) error {
754
+ return filepath .WalkDir (snapInternalVolPath , func (path string , d fs.DirEntry , err error ) error {
755
+ if path == snapInternalVolPath {
756
+ // skip root
757
+ return nil
758
+ }
759
+ if err != nil {
760
+ return err
761
+ }
762
+ if d .Name () != snap .archiveName () {
763
+ // there should be just one archive in the snapshot path and archive name should match
764
+ return status .Errorf (codes .AlreadyExists , "snapshot with the same name but different source volume ID already exists: found %q, desired %q" , d .Name (), snap .archiveName ())
765
+ }
766
+ return nil
767
+ })
768
+ }
769
+
770
+ // Volume for snapshot internal mount/unmount
771
+ func volumeFromSnapshot (snap * nfsSnapshot ) * nfsVolume {
772
+ return & nfsVolume {
773
+ id : snap .id ,
774
+ server : snap .server ,
775
+ baseDir : snap .baseDir ,
776
+ subDir : snap .baseDir ,
777
+ uuid : snap .uuid ,
778
+ }
779
+ }
0 commit comments