From 826d2f39e450b0d7a1a155a6045ce2f1a1507f78 Mon Sep 17 00:00:00 2001 From: Jan Wozniak Date: Mon, 20 Mar 2023 15:42:39 +0100 Subject: [PATCH] feat: snapshot path to include src volume name/uuid --- pkg/nfs/controllerserver.go | 88 ++++++++++++++++++++++++++++--------- 1 file changed, 68 insertions(+), 20 deletions(-) diff --git a/pkg/nfs/controllerserver.go b/pkg/nfs/controllerserver.go index 5e1d24eab..4a7544bb8 100644 --- a/pkg/nfs/controllerserver.go +++ b/pkg/nfs/controllerserver.go @@ -18,6 +18,7 @@ package nfs import ( "fmt" + "io/fs" "os" "os/exec" "path/filepath" @@ -71,10 +72,20 @@ type nfsSnapshot struct { baseDir string // Snapshot name. uuid string + // Source volume. + src string +} + +func (snap nfsSnapshot) archiveSubPath() string { + return snap.uuid } func (snap nfsSnapshot) archiveName() string { - return fmt.Sprintf("%v.tar.gz", snap.uuid) + return fmt.Sprintf("%v.tar.gz", snap.src) +} + +func (snap nfsSnapshot) archivePath() string { + return filepath.Join(snap.archiveSubPath(), snap.archiveName()) } // Ordering of elements in the CSI volume id. @@ -92,14 +103,15 @@ const ( ) // Ordering of elements in the CSI snapshot id. -// ID is of the form {server}/{baseDir}/snapshots. +// ID is of the form {server}/{baseDir}/{snap_name}/{src_name}. // Adding a new element should always go at the end // before totalSnapIDElements const ( idSnapServer = iota idSnapBaseDir idSnapUUID - idSnapArchive + idSnapArchivePath + idSnapArchiveName totalIDSnapElements // Always last ) @@ -309,6 +321,13 @@ func (cs *ControllerServer) CreateSnapshot(ctx context.Context, req *csi.CreateS klog.Warningf("failed to unmount snapshot nfs server: %v", err) } }() + snapInternalVolPath := filepath.Join(getInternalVolumePath(cs.Driver.workingMountDir, snapVol), snapshot.archiveSubPath()) + if err = os.MkdirAll(snapInternalVolPath, 0777); err != nil { + return nil, status.Errorf(codes.Internal, "failed to make subdirectory: %v", err) + } + if err := validateSnapshot(snapInternalVolPath, snapshot); err != nil { + return nil, err + } if err = cs.internalMount(ctx, srcVol, nil, nil); err != nil { 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 }() srcPath := getInternalVolumePath(cs.Driver.workingMountDir, srcVol) - dstPath := filepath.Join(getInternalVolumePath(cs.Driver.workingMountDir, snapVol), snapshot.archiveName()) + dstPath := filepath.Join(snapInternalVolPath, snapshot.archiveName()) klog.V(2).Infof("archiving %v -> %v", srcPath, dstPath) out, err := exec.Command("tar", "-C", srcPath, "-czvf", dstPath, ".").CombinedOutput() if err != nil { @@ -380,7 +399,7 @@ func (cs *ControllerServer) DeleteSnapshot(ctx context.Context, req *csi.DeleteS }() // delete snapshot archive - internalVolumePath := filepath.Join(getInternalVolumePath(cs.Driver.workingMountDir, vol), snap.archiveName()) + internalVolumePath := filepath.Join(getInternalVolumePath(cs.Driver.workingMountDir, vol), snap.archiveSubPath()) klog.V(2).Infof("Removing snapshot archive at %v", internalVolumePath) if err = os.RemoveAll(internalVolumePath); err != nil { 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 return nil, status.Error(codes.Unimplemented, "") } -// Volume for snapshot internal mount/unmount -func volumeFromSnapshot(snap *nfsSnapshot) *nfsVolume { - return &nfsVolume{ - id: snap.id, - server: snap.server, - baseDir: snap.baseDir, - subDir: snap.baseDir, - uuid: snap.uuid, - } -} - // Mount nfs server at base-dir func (cs *ControllerServer) internalMount(ctx context.Context, vol *nfsVolume, volumeContext map[string]string, volCap *csi.VolumeCapability) error { if volCap == nil { @@ -485,7 +493,7 @@ func (cs *ControllerServer) copyFromSnapshot(ctx context.Context, req *csi.Creat }() // untar snapshot archive to dst path - snapPath := filepath.Join(getInternalVolumePath(cs.Driver.workingMountDir, snapVol), snap.archiveName()) + snapPath := filepath.Join(getInternalVolumePath(cs.Driver.workingMountDir, snapVol), snap.archivePath()) dstPath := getInternalVolumePath(cs.Driver.workingMountDir, dstVol) klog.V(2).Infof("copy volume from snapshot %v -> %v", snapPath, dstPath) 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 baseDir: baseDir, uuid: name, } + if vol.uuid != "" { + snapshot.src = vol.uuid + } + if vol.subDir != "" { + snapshot.src = vol.subDir + } + if snapshot.src == "" { + return nil, fmt.Errorf("missing required source volume name") + } snapshot.id = getSnapshotIDFromNfsSnapshot(snapshot) return snapshot, nil } @@ -656,7 +673,8 @@ func getSnapshotIDFromNfsSnapshot(snap *nfsSnapshot) string { idElements[idSnapServer] = strings.Trim(snap.server, "/") idElements[idSnapBaseDir] = strings.Trim(snap.baseDir, "/") idElements[idSnapUUID] = snap.uuid - idElements[idSnapArchive] = snap.archiveName() + idElements[idSnapArchivePath] = snap.uuid + idElements[idSnapArchiveName] = snap.src return strings.Join(idElements, separator) } @@ -702,14 +720,15 @@ func getNfsVolFromID(id string) (*nfsVolume, error) { // Given a CSI snapshot ID, return a nfsSnapshot // sample snapshot ID: // -// nfs-server.default.svc.cluster.local#share#snapshot-016f784f-56f4-44d1-9041-5f59e82dbce1#snapshot-016f784f-56f4-44d1-9041-5f59e82dbce1.tar.gz +// 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 func getNfsSnapFromID(id string) (*nfsSnapshot, error) { segments := strings.Split(id, separator) - if len(segments) == 4 { + if len(segments) == totalIDSnapElements { return &nfsSnapshot{ id: id, server: segments[idSnapServer], baseDir: segments[idSnapBaseDir], + src: segments[idSnapArchiveName], uuid: segments[idSnapUUID], }, nil } @@ -729,3 +748,32 @@ func isValidVolumeCapabilities(volCaps []*csi.VolumeCapability) error { } return nil } + +// Validate snapshot after internal mount +func validateSnapshot(snapInternalVolPath string, snap *nfsSnapshot) error { + return filepath.WalkDir(snapInternalVolPath, func(path string, d fs.DirEntry, err error) error { + if path == snapInternalVolPath { + // skip root + return nil + } + if err != nil { + return err + } + if d.Name() != snap.archiveName() { + // there should be just one archive in the snapshot path and archive name should match + 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()) + } + return nil + }) +} + +// Volume for snapshot internal mount/unmount +func volumeFromSnapshot(snap *nfsSnapshot) *nfsVolume { + return &nfsVolume{ + id: snap.id, + server: snap.server, + baseDir: snap.baseDir, + subDir: snap.baseDir, + uuid: snap.uuid, + } +}