@@ -20,10 +20,12 @@ import (
2020 "context"
2121 "fmt"
2222 "net/url"
23+ "os/exec"
2324 "strconv"
2425 "strings"
2526 "time"
2627
28+ "sigs.k8s.io/azurefile-csi-driver/pkg/util"
2729 volumehelper "sigs.k8s.io/azurefile-csi-driver/pkg/util"
2830
2931 "github.com/Azure/azure-sdk-for-go/sdk/storage/azfile/sas"
@@ -108,7 +110,8 @@ func (d *Driver) CreateVolume(ctx context.Context, req *csi.CreateVolumeRequest)
108110 if acquired := d .volumeLocks .TryAcquire (volName ); ! acquired {
109111 // logging the job status if it's volume cloning
110112 if req .GetVolumeContentSource () != nil {
111- jobState , percent , err := getAzcopyJob (volName )
113+ aFunc := & util.AzcopyFunc {}
114+ jobState , percent , err := aFunc .GetAzcopyJob (volName )
112115 klog .V (2 ).Infof ("azcopy job status: %s, copy percent: %s%%, error: %v" , jobState , percent , err )
113116 }
114117 return nil , status .Errorf (codes .Aborted , volumeOperationAlreadyExistsFmt , volName )
@@ -575,7 +578,7 @@ func (d *Driver) CreateVolume(ctx context.Context, req *csi.CreateVolumeRequest)
575578 if err != nil {
576579 return nil , status .Errorf (codes .Internal , "failed to GetStorageAccesskey on account(%s) rg(%s), error: %v" , accountOptions .Name , accountOptions .ResourceGroup , err )
577580 }
578- if err := d .copyVolume (ctx , req , accountKeyCopy , shareOptions , storageEndpointSuffix ); err != nil {
581+ if err := d .copyVolume (ctx , accountName , req , accountKeyCopy , shareOptions , storageEndpointSuffix ); err != nil {
579582 return nil , err
580583 }
581584 // storeAccountKey is not needed here since copy volume is only using SAS token
@@ -725,11 +728,11 @@ func (d *Driver) DeleteVolume(ctx context.Context, req *csi.DeleteVolumeRequest)
725728 return & csi.DeleteVolumeResponse {}, nil
726729}
727730
728- func (d * Driver ) copyVolume (ctx context.Context , req * csi.CreateVolumeRequest , accountKey string , shareOptions * fileclient.ShareOptions , storageEndpointSuffix string ) error {
731+ func (d * Driver ) copyVolume (ctx context.Context , accountName string , req * csi.CreateVolumeRequest , accountKey string , shareOptions * fileclient.ShareOptions , storageEndpointSuffix string ) error {
729732 vs := req .VolumeContentSource
730733 switch vs .Type .(type ) {
731734 case * csi.VolumeContentSource_Snapshot :
732- return status . Errorf ( codes . InvalidArgument , "copy volume from volumeSnapshot is not supported" )
735+ return d . restoreSnapshot ( ctx , accountName , req , accountKey , shareOptions , storageEndpointSuffix )
733736 case * csi.VolumeContentSource_Volume :
734737 return d .copyFileShare (ctx , req , accountKey , shareOptions , storageEndpointSuffix )
735738 default :
@@ -1083,6 +1086,91 @@ func (d *Driver) ListSnapshots(ctx context.Context, req *csi.ListSnapshotsReques
10831086 return nil , status .Error (codes .Unimplemented , "" )
10841087}
10851088
1089+ // restoreSnapshot restores from a snapshot
1090+ func (d * Driver ) restoreSnapshot (ctx context.Context , dstAccountName string , req * csi.CreateVolumeRequest , accountKey string , shareOptions * fileclient.ShareOptions , storageEndpointSuffix string ) error {
1091+ if shareOptions .Protocol == storage .EnabledProtocolsNFS {
1092+ return fmt .Errorf ("protocol nfs is not supported for snapshot restore" )
1093+ }
1094+ var sourceSnapshotID string
1095+ if req .GetVolumeContentSource () != nil && req .GetVolumeContentSource ().GetSnapshot () != nil {
1096+ sourceSnapshotID = req .GetVolumeContentSource ().GetSnapshot ().GetSnapshotId ()
1097+ }
1098+ resourceGroupName , srcAccountName , srcFileShareName , _ , _ , _ , err := GetFileShareInfo (sourceSnapshotID ) //nolint:dogsled
1099+ if err != nil {
1100+ return status .Error (codes .NotFound , err .Error ())
1101+ }
1102+ snapshot , err := getSnapshot (sourceSnapshotID )
1103+ if err != nil {
1104+ return status .Error (codes .NotFound , err .Error ())
1105+ }
1106+ dstFileShareName := shareOptions .Name
1107+ if srcFileShareName == "" || dstFileShareName == "" {
1108+ return fmt .Errorf ("srcFileShareName(%s) or dstFileShareName(%s) is empty" , srcFileShareName , dstFileShareName )
1109+ }
1110+
1111+ srcAccountKey := accountKey
1112+ if srcAccountName != dstAccountName {
1113+ srcAccountKey , err = d .cloud .GetStorageAccesskey (ctx , d .cloud .SubscriptionID , srcAccountName , resourceGroupName , true )
1114+ if err != nil {
1115+ return fmt .Errorf ("get src accountkey get err: %v" , err )
1116+ }
1117+ }
1118+
1119+ klog .V (2 ).Infof ("generate sas token for resource group(%s) src account(%s)" , resourceGroupName , srcAccountName )
1120+ srcAccountSasToken , err := generateSASToken (srcAccountName , srcAccountKey , storageEndpointSuffix , d .sasTokenExpirationMinutes )
1121+ if err != nil {
1122+ return err
1123+ }
1124+ klog .V (2 ).Infof ("generate sas token for resource group(%s) dst account(%s)" , resourceGroupName , dstAccountName )
1125+ dstAccountSasToken , err := generateSASToken (dstAccountName , accountKey , storageEndpointSuffix , d .sasTokenExpirationMinutes )
1126+ if err != nil {
1127+ return err
1128+ }
1129+
1130+ srcPath := fmt .Sprintf ("https://%s.file.%s/%s%s&sharesnapshot=%s" , srcAccountName , storageEndpointSuffix , srcFileShareName , srcAccountSasToken , snapshot )
1131+ dstPath := fmt .Sprintf ("https://%s.file.%s/%s%s" , dstAccountName , storageEndpointSuffix , dstFileShareName , dstAccountSasToken )
1132+
1133+ return d .copyFileShareByAzcopy (srcFileShareName , dstFileShareName , srcPath , dstPath , snapshot )
1134+ }
1135+
1136+ func (d * Driver ) copyFileShareByAzcopy (srcFileShareName , dstFileShareName , srcPath , dstPath , snapshot string ) error {
1137+ timeAfter := time .After (waitForCopyTimeout )
1138+ timeTick := time .Tick (waitForCopyInterval )
1139+ if snapshot != "" {
1140+ srcFileShareName = fmt .Sprintf ("%s(snapshot: %s)" , srcFileShareName , snapshot )
1141+ }
1142+
1143+ aFunc := d .azcopyFunc
1144+ jobState , percent , err := aFunc .GetAzcopyJob (dstFileShareName )
1145+ klog .V (2 ).Infof ("azcopy job status: %s, copy percent: %s%%, error: %v" , jobState , percent , err )
1146+ if jobState == util .AzcopyJobError || jobState == util .AzcopyJobCompleted {
1147+ return err
1148+ }
1149+ klog .V (2 ).Infof ("begin to copy fileshare %s to %s" , srcFileShareName , dstFileShareName )
1150+ for {
1151+ select {
1152+ case <- timeTick :
1153+ jobState , percent , err := aFunc .GetAzcopyJob (dstFileShareName )
1154+ klog .V (2 ).Infof ("azcopy job status: %s, copy percent: %s%%, error: %v" , jobState , percent , err )
1155+ switch jobState {
1156+ case util .AzcopyJobError , util .AzcopyJobCompleted :
1157+ return err
1158+ case util .AzcopyJobNotFound :
1159+ klog .V (2 ).Infof ("copy fileshare %s to %s" , srcFileShareName , dstFileShareName )
1160+ out , copyErr := exec .Command ("azcopy" , "copy" , srcPath , dstPath , "--recursive" , "--check-length=false" ).CombinedOutput ()
1161+ if copyErr != nil {
1162+ klog .Warningf ("CopyFileShare(%s) failed with error(%v): %v" , dstFileShareName , copyErr , string (out ))
1163+ } else {
1164+ klog .V (2 ).Infof ("copied fileshare %s to %s successfully" , srcFileShareName , dstFileShareName )
1165+ }
1166+ return copyErr
1167+ }
1168+ case <- timeAfter :
1169+ return fmt .Errorf ("timeout waiting for copy fileshare %s to %s succeed" , srcFileShareName , dstFileShareName )
1170+ }
1171+ }
1172+ }
1173+
10861174// ControllerExpandVolume controller expand volume
10871175func (d * Driver ) ControllerExpandVolume (ctx context.Context , req * csi.ControllerExpandVolumeRequest ) (* csi.ControllerExpandVolumeResponse , error ) {
10881176 volumeID := req .GetVolumeId ()
0 commit comments