@@ -59,8 +59,6 @@ const (
5959 MSI = "MSI"
6060 SPN = "SPN"
6161 authorizationPermissionMismatch = "AuthorizationPermissionMismatch"
62-
63- waitForAzCopyInterval = 2 * time .Second
6462)
6563
6664// CreateVolume provisions a volume
@@ -79,19 +77,12 @@ func (d *Driver) CreateVolume(ctx context.Context, req *csi.CreateVolumeRequest)
7977 return nil , status .Error (codes .InvalidArgument , err .Error ())
8078 }
8179
82- if acquired := d .volumeLocks .TryAcquire (volName ); ! acquired {
83- // logging the job status if it's volume cloning
84- if req .GetVolumeContentSource () != nil {
85- jobState , percent , err := d .azcopy .GetAzcopyJob (volName , []string {})
86- klog .V (2 ).Infof ("azcopy job status: %s, copy percent: %s%%, error: %v" , jobState , percent , err )
87- }
88- return nil , status .Errorf (codes .Aborted , volumeOperationAlreadyExistsFmt , volName )
89- }
90- defer d .volumeLocks .Release (volName )
91-
9280 volSizeBytes := int64 (req .GetCapacityRange ().GetRequiredBytes ())
9381 requestGiB := int (util .RoundUpGiB (volSizeBytes ))
9482
83+ volContentSource := req .GetVolumeContentSource ()
84+ secrets := req .GetSecrets ()
85+
9586 parameters := req .GetParameters ()
9687 if parameters == nil {
9788 parameters = make (map [string ]string )
@@ -341,10 +332,31 @@ func (d *Driver) CreateVolume(ctx context.Context, req *csi.CreateVolumeRequest)
341332 GetLatestAccountKey : getLatestAccountKey ,
342333 }
343334
335+ containerName = replaceWithMap (containerName , containerNameReplaceMap )
336+ validContainerName := containerName
337+ if validContainerName == "" {
338+ validContainerName = volName
339+ if containerNamePrefix != "" {
340+ validContainerName = containerNamePrefix + "-" + volName
341+ }
342+ validContainerName = getValidContainerName (validContainerName , protocol )
343+ setKeyValueInMap (parameters , containerNameField , validContainerName )
344+ }
345+
346+ if acquired := d .volumeLocks .TryAcquire (volName ); ! acquired {
347+ // logging the job status if it's volume cloning
348+ if volContentSource != nil {
349+ jobState , percent , err := d .azcopy .GetAzcopyJob (validContainerName , []string {})
350+ return nil , status .Errorf (codes .Aborted , volumeOperationAlreadyExistsWithAzcopyFmt , volName , jobState , percent , err )
351+ }
352+ return nil , status .Errorf (codes .Aborted , volumeOperationAlreadyExistsFmt , volName )
353+ }
354+ defer d .volumeLocks .Release (volName )
355+
344356 var volumeID string
345357 requestName := "controller_create_volume"
346- if req . GetVolumeContentSource () != nil {
347- switch req . VolumeContentSource .Type .(type ) {
358+ if volContentSource != nil {
359+ switch volContentSource .Type .(type ) {
348360 case * csi.VolumeContentSource_Snapshot :
349361 requestName = "controller_create_volume_from_snapshot"
350362 case * csi.VolumeContentSource_Volume :
@@ -357,9 +369,39 @@ func (d *Driver) CreateVolume(ctx context.Context, req *csi.CreateVolumeRequest)
357369 mc .ObserveOperationWithResult (isOperationSucceeded , VolumeID , volumeID )
358370 }()
359371
372+ var srcAzcopyAuthEnv []string
373+ var srcSubscriptionID , srcResourceGroupName , srcAccountName , srcContainerName , srcPath , srcAccountSASToken string
374+ if volContentSource != nil {
375+ switch volContentSource .Type .(type ) {
376+ case * csi.VolumeContentSource_Snapshot :
377+ return nil , status .Errorf (codes .InvalidArgument , "VolumeContentSource Snapshot is not yet implemented" )
378+ case * csi.VolumeContentSource_Volume :
379+ var srcVolumeID string
380+ if volContentSource .GetVolume () != nil {
381+ srcVolumeID = volContentSource .GetVolume ().GetVolumeId ()
382+ }
383+ srcResourceGroupName , srcAccountName , srcContainerName , _ , srcSubscriptionID , err = GetContainerInfo (srcVolumeID )
384+ if err != nil {
385+ return nil , status .Error (codes .NotFound , err .Error ())
386+ }
387+ srcAccountOptions := & azure.AccountOptions {
388+ Name : srcAccountName ,
389+ SubscriptionID : srcSubscriptionID ,
390+ ResourceGroup : srcResourceGroupName ,
391+ GetLatestAccountKey : getLatestAccountKey ,
392+ }
393+ srcAccountSASToken , srcAzcopyAuthEnv , err = d .getAzcopyAuth (ctx , srcAccountName , "" , storageEndpointSuffix , srcAccountOptions , secrets , secretName , secretNamespace )
394+ if err != nil {
395+ return nil , status .Errorf (codes .Internal , "failed to getAzcopyAuth on account(%s) rg(%s), error: %v" , accountOptions .Name , accountOptions .ResourceGroup , err )
396+ }
397+ srcPath = fmt .Sprintf ("https://%s.blob.%s/%s" , srcAccountName , storageEndpointSuffix , srcContainerName )
398+ default :
399+ return nil , status .Errorf (codes .InvalidArgument , "VolumeContentSource is not recognized: %v" , volContentSource )
400+ }
401+ }
402+
360403 var accountKey string
361404 accountName := account
362- secrets := req .GetSecrets ()
363405 if len (secrets ) == 0 && accountName == "" {
364406 if v , ok := d .volMap .Load (volName ); ok {
365407 accountName = v .(string )
@@ -413,31 +455,26 @@ func (d *Driver) CreateVolume(ctx context.Context, req *csi.CreateVolumeRequest)
413455 secrets = createStorageAccountSecret (accountName , accountKey )
414456 }
415457
416- // replace pv/pvc name namespace metadata in subDir
417- containerName = replaceWithMap ( containerName , containerNameReplaceMap )
418- validContainerName := containerName
419- if validContainerName == "" {
420- validContainerName = volName
421- if containerNamePrefix != "" {
422- validContainerName = containerNamePrefix + "-" + volName
458+ dstAzcopyAuthEnv := srcAzcopyAuthEnv
459+ dstAccountSASToken := srcAccountSASToken
460+ if volContentSource != nil {
461+ if srcSubscriptionID != subsID || srcResourceGroupName != resourceGroup || srcAccountName != accountName {
462+ if dstAccountSASToken , dstAzcopyAuthEnv , err = d . getAzcopyAuth ( ctx , accountName , accountKey , storageEndpointSuffix , accountOptions , secrets , secretName , secretNamespace ); err != nil {
463+ return nil , status . Errorf ( codes . Internal , "failed to getAzcopyAuth on account(%s) rg(%s), error: %v" , accountOptions . Name , accountOptions . ResourceGroup , err )
464+ }
423465 }
424- validContainerName = getValidContainerName (validContainerName , protocol )
425- setKeyValueInMap (parameters , containerNameField , validContainerName )
426466 }
427467
428- if req .GetVolumeContentSource () != nil {
429- accountSASToken , authAzcopyEnv , err := d .getAzcopyAuth (ctx , accountName , accountKey , storageEndpointSuffix , accountOptions , secrets , secretName , secretNamespace )
430- if err != nil {
431- return nil , status .Errorf (codes .Internal , "failed to getAzcopyAuth on account(%s) rg(%s), error: %v" , accountOptions .Name , accountOptions .ResourceGroup , err )
432- }
433- if err := d .copyVolume (req , accountSASToken , authAzcopyEnv , validContainerName , storageEndpointSuffix ); err != nil {
468+ klog .V (2 ).Infof ("begin to create container(%s) on account(%s) type(%s) subsID(%s) rg(%s) location(%s) size(%d)" , validContainerName , accountName , storageAccountType , subsID , resourceGroup , location , requestGiB )
469+ if err := d .CreateBlobContainer (ctx , subsID , resourceGroup , accountName , validContainerName , secrets ); err != nil {
470+ return nil , status .Errorf (codes .Internal , "failed to create container(%s) on account(%s) type(%s) rg(%s) location(%s) size(%d), error: %v" , validContainerName , accountName , storageAccountType , resourceGroup , location , requestGiB , err )
471+ }
472+
473+ if volContentSource != nil {
474+ dstPath := fmt .Sprintf ("https://%s.blob.%s/%s" , accountName , storageEndpointSuffix , validContainerName )
475+ if err := d .copyBlobContainer (dstAzcopyAuthEnv , srcPath , srcAccountSASToken , dstPath , dstAccountSASToken , validContainerName ); err != nil {
434476 return nil , err
435477 }
436- } else {
437- klog .V (2 ).Infof ("begin to create container(%s) on account(%s) type(%s) subsID(%s) rg(%s) location(%s) size(%d)" , validContainerName , accountName , storageAccountType , subsID , resourceGroup , location , requestGiB )
438- if err := d .CreateBlobContainer (ctx , subsID , resourceGroup , accountName , validContainerName , secrets ); err != nil {
439- return nil , status .Errorf (codes .Internal , "failed to create container(%s) on account(%s) type(%s) rg(%s) location(%s) size(%d), error: %v" , validContainerName , accountName , storageAccountType , resourceGroup , location , requestGiB , err )
440- }
441478 }
442479
443480 if storeAccountKey && len (req .GetSecrets ()) == 0 {
@@ -478,7 +515,7 @@ func (d *Driver) CreateVolume(ctx context.Context, req *csi.CreateVolumeRequest)
478515 VolumeId : volumeID ,
479516 CapacityBytes : req .GetCapacityRange ().GetRequiredBytes (),
480517 VolumeContext : parameters ,
481- ContentSource : req . GetVolumeContentSource () ,
518+ ContentSource : volContentSource ,
482519 },
483520 }, nil
484521}
@@ -724,71 +761,45 @@ func (d *Driver) DeleteBlobContainer(ctx context.Context, subsID, resourceGroupN
724761 })
725762}
726763
727- // CopyBlobContainer copies a blob container in the same storage account
728- func (d * Driver ) copyBlobContainer (req * csi.CreateVolumeRequest , accountSasToken string , authAzcopyEnv []string , dstContainerName , storageEndpointSuffix string ) error {
729- var sourceVolumeID string
730- if req .GetVolumeContentSource () != nil && req .GetVolumeContentSource ().GetVolume () != nil {
731- sourceVolumeID = req .GetVolumeContentSource ().GetVolume ().GetVolumeId ()
764+ // copyBlobContainer copies source volume content into a destination volume
765+ func (d * Driver ) copyBlobContainer (authAzcopyEnv []string , srcPath string , srcAccountSASToken string , dstPath string , dstAccountSASToken string , dstContainerName string ) error {
732766
733- }
734- resourceGroupName , accountName , srcContainerName , _ , _ , err := GetContainerInfo (sourceVolumeID ) //nolint:dogsled
735- if err != nil {
736- return status .Error (codes .NotFound , err .Error ())
737- }
738- if srcContainerName == "" || dstContainerName == "" {
739- return fmt .Errorf ("srcContainerName(%s) or dstContainerName(%s) is empty" , srcContainerName , dstContainerName )
767+ if srcPath == "" || dstPath == "" || dstContainerName == "" {
768+ return fmt .Errorf ("srcPath(%s) or dstPath(%s) or dstContainerName(%s) is empty" , srcPath , dstPath , dstContainerName )
740769 }
741770
742- timeAfter := time .After (time .Duration (d .waitForAzCopyTimeoutMinutes ) * time .Minute )
743- timeTick := time .Tick (waitForAzCopyInterval )
744- srcPath := fmt .Sprintf ("https://%s.blob.%s/%s%s" , accountName , storageEndpointSuffix , srcContainerName , accountSasToken )
745- dstPath := fmt .Sprintf ("https://%s.blob.%s/%s%s" , accountName , storageEndpointSuffix , dstContainerName , accountSasToken )
746-
747771 jobState , percent , err := d .azcopy .GetAzcopyJob (dstContainerName , authAzcopyEnv )
748772 klog .V (2 ).Infof ("azcopy job status: %s, copy percent: %s%%, error: %v" , jobState , percent , err )
749- if jobState == util .AzcopyJobError || jobState == util .AzcopyJobCompleted {
773+ switch jobState {
774+ case util .AzcopyJobError , util .AzcopyJobCompleted :
750775 return err
751- }
752- klog .V (2 ).Infof ("begin to copy blob container %s to %s" , srcContainerName , dstContainerName )
753- for {
754- select {
755- case <- timeTick :
756- jobState , percent , err := d .azcopy .GetAzcopyJob (dstContainerName , authAzcopyEnv )
757- klog .V (2 ).Infof ("azcopy job status: %s, copy percent: %s%%, error: %v" , jobState , percent , err )
758- switch jobState {
759- case util .AzcopyJobError , util .AzcopyJobCompleted :
760- return err
761- case util .AzcopyJobNotFound :
762- klog .V (2 ).Infof ("copy blob container %s to %s" , srcContainerName , dstContainerName )
763- cmd := exec .Command ("azcopy" , "copy" , srcPath , dstPath , "--recursive" , "--check-length=false" )
764- if len (authAzcopyEnv ) > 0 {
765- cmd .Env = append (os .Environ (), authAzcopyEnv ... )
766- }
767- out , copyErr := cmd .CombinedOutput ()
768- if copyErr != nil {
769- klog .Warningf ("CopyBlobContainer(%s, %s, %s) failed with error(%v): %v" , resourceGroupName , accountName , dstPath , copyErr , string (out ))
770- } else {
771- klog .V (2 ).Infof ("copied blob container %s to %s successfully" , srcContainerName , dstContainerName )
772- }
773- return copyErr
776+ case util .AzcopyJobRunning :
777+ return fmt .Errorf ("wait for the existing AzCopy job to complete, current copy percentage is %s%%" , percent )
778+ case util .AzcopyJobNotFound :
779+ klog .V (2 ).Infof ("copy blob container %s to %s" , srcPath , dstContainerName )
780+ execFunc := func () error {
781+ cmd := exec .Command ("azcopy" , "copy" , srcPath + srcAccountSASToken , dstPath + dstAccountSASToken , "--recursive" , "--check-length=false" , "--s2s-preserve-access-tier=false" )
782+ if len (authAzcopyEnv ) > 0 {
783+ cmd .Env = append (os .Environ (), authAzcopyEnv ... )
784+ }
785+ if out , err := cmd .CombinedOutput (); err != nil {
786+ return fmt .Errorf ("exec error: %v, output: %v" , err , string (out ))
774787 }
775- case <- timeAfter :
776- return fmt .Errorf ("timeout waiting for copy blob container %s to %s succeed" , srcContainerName , dstContainerName )
788+ return nil
777789 }
790+ timeoutFunc := func () error {
791+ _ , percent , _ := d .azcopy .GetAzcopyJob (dstContainerName , authAzcopyEnv )
792+ return fmt .Errorf ("timeout waiting for copy blob container %s to %s complete, current copy percent: %s%%" , srcPath , dstContainerName , percent )
793+ }
794+ copyErr := util .WaitUntilTimeout (time .Duration (d .waitForAzCopyTimeoutMinutes )* time .Minute , execFunc , timeoutFunc )
795+ if copyErr != nil {
796+ klog .Warningf ("CopyBlobContainer(%s, %s, %s) failed with error: %v" , srcPath , dstPath , dstContainerName , copyErr )
797+ } else {
798+ klog .V (2 ).Infof ("copied blob container %s to %s successfully" , srcPath , dstContainerName )
799+ }
800+ return copyErr
778801 }
779- }
780-
781- // copyVolume copies a volume form volume or snapshot, snapshot is not supported now
782- func (d * Driver ) copyVolume (req * csi.CreateVolumeRequest , accountSASToken string , authAzcopyEnv []string , dstContainerName , storageEndpointSuffix string ) error {
783- vs := req .VolumeContentSource
784- switch vs .Type .(type ) {
785- case * csi.VolumeContentSource_Snapshot :
786- return status .Errorf (codes .InvalidArgument , "copy volume from volumeSnapshot is not supported" )
787- case * csi.VolumeContentSource_Volume :
788- return d .copyBlobContainer (req , accountSASToken , authAzcopyEnv , dstContainerName , storageEndpointSuffix )
789- default :
790- return status .Errorf (codes .InvalidArgument , "%v is not a proper volume source" , vs )
791- }
802+ return err
792803}
793804
794805// authorizeAzcopyWithIdentity returns auth env for azcopy using cluster identity
0 commit comments