@@ -20,6 +20,7 @@ import (
20
20
"context"
21
21
"fmt"
22
22
"net/url"
23
+ "os"
23
24
"os/exec"
24
25
"strconv"
25
26
"strings"
@@ -50,6 +51,12 @@ import (
50
51
const (
51
52
privateEndpoint = "privateendpoint"
52
53
54
+ azcopyAutoLoginType = "AZCOPY_AUTO_LOGIN_TYPE"
55
+ azcopySPAApplicationID = "AZCOPY_SPA_APPLICATION_ID"
56
+ azcopySPAClientSecret = "AZCOPY_SPA_CLIENT_SECRET"
57
+ azcopyTenantID = "AZCOPY_TENANT_ID"
58
+ azcopyMSIClientID = "AZCOPY_MSI_CLIENT_ID"
59
+
53
60
waitForCopyInterval = 5 * time .Second
54
61
waitForCopyTimeout = 3 * time .Minute
55
62
)
@@ -412,12 +419,11 @@ func (d *Driver) CreateVolume(ctx context.Context, req *csi.CreateVolumeRequest)
412
419
}
413
420
414
421
if req .GetVolumeContentSource () != nil {
415
- if accountKey == "" {
416
- if _ , accountKey , err = d .GetStorageAccesskey (ctx , accountOptions , secrets , secretName , secretNamespace ); err != nil {
417
- return nil , status .Errorf (codes .Internal , "failed to GetStorageAccesskey on account(%s) rg(%s), error: %v" , accountOptions .Name , accountOptions .ResourceGroup , err )
418
- }
422
+ var accountSASToken string
423
+ if accountSASToken , err = d .useGenerateSASToken (ctx , accountName , accountKey , storageEndpointSuffix , accountOptions , secrets , secretName , secretNamespace ); err != nil {
424
+ return nil , err
419
425
}
420
- if err := d .copyVolume (ctx , req , accountKey , validContainerName , storageEndpointSuffix ); err != nil {
426
+ if err := d .copyVolume (ctx , req , accountSASToken , validContainerName , storageEndpointSuffix ); err != nil {
421
427
return nil , err
422
428
}
423
429
} else {
@@ -712,7 +718,7 @@ func (d *Driver) DeleteBlobContainer(ctx context.Context, subsID, resourceGroupN
712
718
}
713
719
714
720
// CopyBlobContainer copies a blob container in the same storage account
715
- func (d * Driver ) copyBlobContainer (_ context.Context , req * csi.CreateVolumeRequest , accountKey , dstContainerName , storageEndpointSuffix string ) error {
721
+ func (d * Driver ) copyBlobContainer (_ context.Context , req * csi.CreateVolumeRequest , accountSasToken , dstContainerName , storageEndpointSuffix string ) error {
716
722
var sourceVolumeID string
717
723
if req .GetVolumeContentSource () != nil && req .GetVolumeContentSource ().GetVolume () != nil {
718
724
sourceVolumeID = req .GetVolumeContentSource ().GetVolume ().GetVolumeId ()
@@ -726,10 +732,11 @@ func (d *Driver) copyBlobContainer(_ context.Context, req *csi.CreateVolumeReque
726
732
return fmt .Errorf ("srcContainerName(%s) or dstContainerName(%s) is empty" , srcContainerName , dstContainerName )
727
733
}
728
734
729
- klog .V (2 ).Infof ("generate sas token for account(%s)" , accountName )
730
- accountSasToken , genErr := generateSASToken (accountName , accountKey , storageEndpointSuffix , d .sasTokenExpirationMinutes )
731
- if genErr != nil {
732
- return genErr
735
+ if accountSasToken == "" {
736
+ err = d .authorizeAzcopyBySecurityPrincipal ()
737
+ if err != nil {
738
+ return err
739
+ }
733
740
}
734
741
735
742
timeAfter := time .After (waitForCopyTimeout )
@@ -768,18 +775,74 @@ func (d *Driver) copyBlobContainer(_ context.Context, req *csi.CreateVolumeReque
768
775
}
769
776
770
777
// copyVolume copies a volume form volume or snapshot, snapshot is not supported now
771
- func (d * Driver ) copyVolume (ctx context.Context , req * csi.CreateVolumeRequest , accountKey , dstContainerName , storageEndpointSuffix string ) error {
778
+ func (d * Driver ) copyVolume (ctx context.Context , req * csi.CreateVolumeRequest , accountSASToken , dstContainerName , storageEndpointSuffix string ) error {
772
779
vs := req .VolumeContentSource
773
780
switch vs .Type .(type ) {
774
781
case * csi.VolumeContentSource_Snapshot :
775
782
return status .Errorf (codes .InvalidArgument , "copy volume from volumeSnapshot is not supported" )
776
783
case * csi.VolumeContentSource_Volume :
777
- return d .copyBlobContainer (ctx , req , accountKey , dstContainerName , storageEndpointSuffix )
784
+ return d .copyBlobContainer (ctx , req , accountSASToken , dstContainerName , storageEndpointSuffix )
778
785
default :
779
786
return status .Errorf (codes .InvalidArgument , "%v is not a proper volume source" , vs )
780
787
}
781
788
}
782
789
790
+ func (d * Driver ) authorizeAzcopyBySecurityPrincipal () error {
791
+ if len (d .cloud .Config .AzureAuthConfig .AADClientSecret ) > 0 {
792
+ klog .V (2 ).Infof ("use service principal to authorize azcopy" )
793
+ if err := os .Setenv (azcopyAutoLoginType , "SPN" ); err != nil {
794
+ return err
795
+ }
796
+ if d .cloud .Config .AzureAuthConfig .AADClientID == "" {
797
+ return fmt .Errorf ("AADClientID and AADClientSecret must be set when use service principal" )
798
+ }
799
+ if err := os .Setenv (azcopySPAApplicationID , d .cloud .Config .AzureAuthConfig .AADClientID ); err != nil {
800
+ return err
801
+ }
802
+ if err := os .Setenv (azcopySPAClientSecret , d .cloud .Config .AzureAuthConfig .AADClientSecret ); err != nil {
803
+ return err
804
+ }
805
+ if d .cloud .Config .AzureAuthConfig .TenantID != "" {
806
+ if err := os .Setenv (azcopyTenantID , d .cloud .Config .AzureAuthConfig .TenantID ); err != nil {
807
+ return err
808
+ }
809
+ klog .V (2 ).Infof (fmt .Sprintf ("set AZCOPY_TENANT_ID=%s successfully" , d .cloud .Config .AzureAuthConfig .TenantID ))
810
+ }
811
+ return nil
812
+ }
813
+ if d .cloud .Config .AzureAuthConfig .UseManagedIdentityExtension {
814
+ if err := os .Setenv (azcopyAutoLoginType , "MSI" ); err != nil {
815
+ return err
816
+ }
817
+ if len (d .cloud .Config .AzureAuthConfig .UserAssignedIdentityID ) > 0 {
818
+ klog .V (2 ).Infof ("use user assigned managed identity to authorize azcopy" )
819
+ err := os .Setenv (azcopyMSIClientID , d .cloud .Config .AzureAuthConfig .UserAssignedIdentityID )
820
+ return err
821
+ }
822
+ klog .V (2 ).Infof ("use system-assigned managed identity to authorize azcopy" )
823
+ return nil
824
+ }
825
+ return fmt .Errorf ("AADClientSecret shouldn't be nil or useManagedIdentityExtension must be set to true" )
826
+ }
827
+
828
+ func (d * Driver ) useGenerateSASToken (ctx context.Context , accountName , accountKey , storageEndpointSuffix string , accountOptions * azure.AccountOptions , secrets map [string ]string , secretName , secretNamespace string ) (string , error ) {
829
+ if len (secrets ) > 0 && len (d .cloud .Config .AzureAuthConfig .AADClientSecret ) == 0 && ! d .cloud .Config .AzureAuthConfig .UseManagedIdentityExtension {
830
+ var err error
831
+ if accountKey == "" {
832
+ if _ , accountKey , err = d .GetStorageAccesskey (ctx , accountOptions , secrets , secretName , secretNamespace ); err != nil {
833
+ return "" , status .Errorf (codes .Internal , "failed to GetStorageAccesskey on account(%s) rg(%s), error: %v" , accountOptions .Name , accountOptions .ResourceGroup , err )
834
+ }
835
+ }
836
+ klog .V (2 ).Infof ("generate sas token for account(%s)" , accountName )
837
+ accountSASToken , err := generateSASToken (accountName , accountKey , storageEndpointSuffix , d .sasTokenExpirationMinutes )
838
+ if err != nil {
839
+ return "" , err
840
+ }
841
+ return accountSASToken , nil
842
+ }
843
+ return "" , nil
844
+ }
845
+
783
846
// isValidVolumeCapabilities validates the given VolumeCapability array is valid
784
847
func isValidVolumeCapabilities (volCaps []* csi.VolumeCapability ) error {
785
848
if len (volCaps ) == 0 {
0 commit comments