Skip to content

Commit 126a41d

Browse files
committed
feat: support mount blob storage directory in another subscription
1 parent d7855dd commit 126a41d

File tree

5 files changed

+34
-14
lines changed

5 files changed

+34
-14
lines changed

docs/driver-parameters.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ allowBlobPublicAccess | Allow or disallow public access to all blobs or containe
1919
storageEndpointSuffix | specify Azure storage endpoint suffix | `core.windows.net` | No | if empty, driver will use default storage endpoint suffix according to cloud environment, e.g. `core.windows.net`
2020
tags | [tags](https://docs.microsoft.com/en-us/azure/azure-resource-manager/management/tag-resources) would be created in newly created storage account | tag format: 'foo=aaa,bar=bbb' | No | ""
2121
--- | **Following parameters are only for blobfuse** | --- | --- |
22+
subscriptionID | specify Azure subscription ID in which blob storage directory will be created | Azure subscription ID | No | if not empty, `resourceGroup` must be provided
2223
storeAccountKey | whether store account key to k8s secret <br><br> Note: <br> `false` means driver would leverage kubelet identity to get account key | `true`,`false` | No | `true`
2324
secretName | specify secret name to store account key | | No |
2425
secretNamespace | specify the namespace of secret to store account key | `default`,`kube-system`, etc | No | pvc namespace

pkg/blob/blob.go

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,7 @@ const (
5858
storageAccountField = "storageaccount"
5959
storageAccountTypeField = "storageaccounttype"
6060
skuNameField = "skuname"
61+
subscriptionIDField = "subscriptionid"
6162
resourceGroupField = "resourcegroup"
6263
locationField = "location"
6364
secretNameField = "secretname"
@@ -306,6 +307,7 @@ func (d *Driver) GetAuthEnv(ctx context.Context, volumeID, protocol string, attr
306307
}
307308

308309
var (
310+
subsID string
309311
accountKey string
310312
accountSasToken string
311313
secretName string
@@ -321,6 +323,8 @@ func (d *Driver) GetAuthEnv(ctx context.Context, volumeID, protocol string, attr
321323

322324
for k, v := range attrib {
323325
switch strings.ToLower(k) {
326+
case subscriptionIDField:
327+
subsID = v
324328
case containerNameField:
325329
containerName = v
326330
case keyVaultURLField:
@@ -404,7 +408,7 @@ func (d *Driver) GetAuthEnv(ctx context.Context, volumeID, protocol string, attr
404408
if err != nil && !getAccountKeyFromSecret {
405409
klog.V(2).Infof("get account(%s) key from secret(%s, %s) failed with error: %v, use cluster identity to get account key instead",
406410
accountName, secretNamespace, secretName, err)
407-
accountKey, err = d.cloud.GetStorageAccesskey(ctx, accountName, rgName)
411+
accountKey, err = d.cloud.GetStorageAccesskey(ctx, subsID, accountName, rgName)
408412
if err != nil {
409413
return rgName, accountName, accountKey, containerName, authEnv, fmt.Errorf("no key for storage account(%s) under resource group(%s), err %w", accountName, rgName, err)
410414
}
@@ -453,6 +457,7 @@ func (d *Driver) GetAuthEnv(ctx context.Context, volumeID, protocol string, attr
453457
// only for e2e testing
454458
func (d *Driver) GetStorageAccountAndContainer(ctx context.Context, volumeID string, attrib, secrets map[string]string) (string, string, string, string, error) {
455459
var (
460+
subsID string
456461
accountName string
457462
accountKey string
458463
accountSasToken string
@@ -465,6 +470,8 @@ func (d *Driver) GetStorageAccountAndContainer(ctx context.Context, volumeID str
465470

466471
for k, v := range attrib {
467472
switch strings.ToLower(k) {
473+
case subscriptionIDField:
474+
subsID = v
468475
case containerNameField:
469476
containerName = v
470477
case keyVaultURLField:
@@ -505,7 +512,7 @@ func (d *Driver) GetStorageAccountAndContainer(ctx context.Context, volumeID str
505512
rgName = d.cloud.ResourceGroup
506513
}
507514

508-
accountKey, err = d.cloud.GetStorageAccesskey(ctx, accountName, rgName)
515+
accountKey, err = d.cloud.GetStorageAccesskey(ctx, subsID, accountName, rgName)
509516
if err != nil {
510517
return "", "", "", "", fmt.Errorf("no key for storage account(%s) under resource group(%s), err %w", accountName, rgName, err)
511518
}
@@ -626,7 +633,7 @@ func (d *Driver) GetStorageAccesskey(ctx context.Context, accountOptions *azure.
626633
_, accountKey, err := d.GetStorageAccountFromSecret(secretName, secretNamespace)
627634
if err != nil {
628635
klog.V(2).Infof("could not get account(%s) key from secret(%s) namespace(%s), error: %v, use cluster identity to get account key instead", accountOptions.Name, secretName, secretNamespace, err)
629-
accountKey, err = d.cloud.GetStorageAccesskey(ctx, accountOptions.Name, accountOptions.ResourceGroup)
636+
accountKey, err = d.cloud.GetStorageAccesskey(ctx, accountOptions.SubscriptionID, accountOptions.Name, accountOptions.ResourceGroup)
630637
}
631638
return accountOptions.Name, accountKey, err
632639
}

pkg/blob/blob_test.go

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -460,7 +460,7 @@ func TestGetAuthEnv(t *testing.T) {
460460
rerr := &retry.Error{
461461
RawError: fmt.Errorf("test"),
462462
}
463-
mockStorageAccountsClient.EXPECT().ListKeys(gomock.Any(), gomock.Any(), gomock.Any()).Return(accountListKeysResult, rerr).AnyTimes()
463+
mockStorageAccountsClient.EXPECT().ListKeys(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return(accountListKeysResult, rerr).AnyTimes()
464464
_, _, _, _, _, err := d.GetAuthEnv(context.TODO(), volumeID, "", attrib, secret)
465465
expectedErr := fmt.Errorf("no key for storage account(storageaccountname) under resource group(rg), err Retriable: false, RetryAfter: 0s, HTTPStatusCode: 0, RawError: test")
466466
if !strings.EqualFold(err.Error(), expectedErr.Error()) {
@@ -489,7 +489,7 @@ func TestGetAuthEnv(t *testing.T) {
489489
list := storage.AccountListKeysResult{
490490
Keys: &accountkeylist,
491491
}
492-
mockStorageAccountsClient.EXPECT().ListKeys(gomock.Any(), gomock.Any(), gomock.Any()).Return(list, nil).AnyTimes()
492+
mockStorageAccountsClient.EXPECT().ListKeys(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return(list, nil).AnyTimes()
493493
_, _, _, _, _, err := d.GetAuthEnv(context.TODO(), volumeID, "", attrib, secret)
494494
expectedErr := error(nil)
495495
if !reflect.DeepEqual(err, expectedErr) {
@@ -573,7 +573,7 @@ func TestGetStorageAccountAndContainer(t *testing.T) {
573573
rerr := &retry.Error{
574574
RawError: fmt.Errorf("test"),
575575
}
576-
mockStorageAccountsClient.EXPECT().ListKeys(gomock.Any(), gomock.Any(), gomock.Any()).Return(accountListKeysResult, rerr).AnyTimes()
576+
mockStorageAccountsClient.EXPECT().ListKeys(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return(accountListKeysResult, rerr).AnyTimes()
577577
_, _, _, _, err := d.GetStorageAccountAndContainer(context.TODO(), volumeID, attrib, secret)
578578
expectedErr := fmt.Errorf("no key for storage account(f5713de20cde511e8ba4900) under resource group(rg), err Retriable: false, RetryAfter: 0s, HTTPStatusCode: 0, RawError: test")
579579
if !strings.EqualFold(err.Error(), expectedErr.Error()) {
@@ -602,7 +602,7 @@ func TestGetStorageAccountAndContainer(t *testing.T) {
602602
list := storage.AccountListKeysResult{
603603
Keys: &accountkeylist,
604604
}
605-
mockStorageAccountsClient.EXPECT().ListKeys(gomock.Any(), gomock.Any(), gomock.Any()).Return(list, nil).AnyTimes()
605+
mockStorageAccountsClient.EXPECT().ListKeys(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return(list, nil).AnyTimes()
606606
_, _, _, _, err := d.GetStorageAccountAndContainer(context.TODO(), volumeID, attrib, secret)
607607
expectedErr := error(nil)
608608
if !reflect.DeepEqual(err, expectedErr) {

pkg/blob/controllerserver.go

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -67,7 +67,7 @@ func (d *Driver) CreateVolume(ctx context.Context, req *csi.CreateVolumeRequest)
6767
if parameters == nil {
6868
parameters = make(map[string]string)
6969
}
70-
var storageAccountType, resourceGroup, location, account, containerName, protocol, customTags, secretName, secretNamespace, pvcNamespace string
70+
var storageAccountType, subsID, resourceGroup, location, account, containerName, protocol, customTags, secretName, secretNamespace, pvcNamespace string
7171
var isHnsEnabled *bool
7272
var vnetResourceGroup, vnetName, subnetName string
7373
// set allowBlobPublicAccess as false by default
@@ -88,6 +88,8 @@ func (d *Driver) CreateVolume(ctx context.Context, req *csi.CreateVolumeRequest)
8888
location = v
8989
case storageAccountField:
9090
account = v
91+
case subscriptionIDField:
92+
subsID = v
9193
case resourceGroupField:
9294
resourceGroup = v
9395
case containerNameField:
@@ -140,6 +142,15 @@ func (d *Driver) CreateVolume(ctx context.Context, req *csi.CreateVolumeRequest)
140142
}
141143
}
142144

145+
if subsID != "" && subsID != d.cloud.SubscriptionID {
146+
if protocol == nfs {
147+
return nil, status.Errorf(codes.InvalidArgument, fmt.Sprintf("NFS protocol is not supported in cross subscription(%s)", subsID))
148+
}
149+
if !storeAccountKey {
150+
return nil, status.Errorf(codes.InvalidArgument, fmt.Sprintf("storeAccountKey must set as true in cross subscription(%s)", subsID))
151+
}
152+
}
153+
143154
if resourceGroup == "" {
144155
resourceGroup = d.cloud.ResourceGroup
145156
}
@@ -195,6 +206,7 @@ func (d *Driver) CreateVolume(ctx context.Context, req *csi.CreateVolumeRequest)
195206
Name: account,
196207
Type: storageAccountType,
197208
Kind: accountKind,
209+
SubscriptionID: subsID,
198210
ResourceGroup: resourceGroup,
199211
Location: location,
200212
EnableHTTPSTrafficOnly: enableHTTPSTrafficOnly,
@@ -402,7 +414,7 @@ func (d *Driver) ValidateVolumeCapabilities(ctx context.Context, req *csi.Valida
402414

403415
var accountKey string
404416
if len(req.GetSecrets()) == 0 { // check whether account is provided by secret
405-
accountKey, err = d.cloud.GetStorageAccesskey(ctx, accountName, resourceGroupName)
417+
accountKey, err = d.cloud.GetStorageAccesskey(ctx, "", accountName, resourceGroupName)
406418
if err != nil {
407419
return nil, fmt.Errorf("no key for storage account(%s) under resource group(%s), err %w", accountName, resourceGroupName, err)
408420
}

pkg/blob/controllerserver_test.go

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -197,7 +197,7 @@ func TestCreateVolume(t *testing.T) {
197197
rerr := &retry.Error{
198198
RawError: fmt.Errorf("test"),
199199
}
200-
mockStorageAccountsClient.EXPECT().ListByResourceGroup(gomock.Any(), gomock.Any()).Return(nil, rerr).AnyTimes()
200+
mockStorageAccountsClient.EXPECT().ListByResourceGroup(gomock.Any(), gomock.Any(), gomock.Any()).Return(nil, rerr).AnyTimes()
201201
_, err := d.CreateVolume(context.Background(), req)
202202
expectedErr := status.Errorf(codes.Internal, "ensure storage account failed with could not list storage accounts for account type : Retriable: false, RetryAfter: 0s, HTTPStatusCode: 0, RawError: test")
203203
if !reflect.DeepEqual(err, expectedErr) {
@@ -338,7 +338,7 @@ func TestDeleteVolume(t *testing.T) {
338338
RawError: fmt.Errorf("test"),
339339
}
340340
accountListKeysResult := storage.AccountListKeysResult{}
341-
mockStorageAccountsClient.EXPECT().ListKeys(gomock.Any(), gomock.Any(), gomock.Any()).Return(accountListKeysResult, rerr).AnyTimes()
341+
mockStorageAccountsClient.EXPECT().ListKeys(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return(accountListKeysResult, rerr).AnyTimes()
342342
expectedErr := fmt.Errorf("no key for storage account(test) under resource group(unit), err Retriable: false, RetryAfter: 0s, HTTPStatusCode: 0, RawError: test")
343343
_, err := d.DeleteVolume(context.Background(), req)
344344
if !strings.EqualFold(err.Error(), expectedErr.Error()) {
@@ -370,7 +370,7 @@ func TestDeleteVolume(t *testing.T) {
370370
list := storage.AccountListKeysResult{
371371
Keys: &accountkeylist,
372372
}
373-
mockStorageAccountsClient.EXPECT().ListKeys(gomock.Any(), gomock.Any(), gomock.Any()).Return(list, nil).AnyTimes()
373+
mockStorageAccountsClient.EXPECT().ListKeys(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return(list, nil).AnyTimes()
374374
expectedErr := fmt.Errorf("azure: base storage service url required")
375375
_, err := d.DeleteVolume(context.Background(), req)
376376
if !reflect.DeepEqual(err, expectedErr) {
@@ -467,7 +467,7 @@ func TestValidateVolumeCapabilities(t *testing.T) {
467467
RawError: fmt.Errorf("test"),
468468
}
469469
accountListKeysResult := storage.AccountListKeysResult{}
470-
mockStorageAccountsClient.EXPECT().ListKeys(gomock.Any(), gomock.Any(), gomock.Any()).Return(accountListKeysResult, rerr).AnyTimes()
470+
mockStorageAccountsClient.EXPECT().ListKeys(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return(accountListKeysResult, rerr).AnyTimes()
471471
expectedErr := fmt.Errorf("no key for storage account(test) under resource group(unit), err Retriable: false, RetryAfter: 0s, HTTPStatusCode: 0, RawError: test")
472472
_, err := d.ValidateVolumeCapabilities(context.Background(), req)
473473
if !strings.EqualFold(err.Error(), expectedErr.Error()) {
@@ -500,7 +500,7 @@ func TestValidateVolumeCapabilities(t *testing.T) {
500500
list := storage.AccountListKeysResult{
501501
Keys: &accountkeylist,
502502
}
503-
mockStorageAccountsClient.EXPECT().ListKeys(gomock.Any(), gomock.Any(), gomock.Any()).Return(list, nil).AnyTimes()
503+
mockStorageAccountsClient.EXPECT().ListKeys(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return(list, nil).AnyTimes()
504504
expectedErr := fmt.Errorf("azure: base storage service url required")
505505
_, err := d.ValidateVolumeCapabilities(context.Background(), req)
506506
if !reflect.DeepEqual(err, expectedErr) {

0 commit comments

Comments
 (0)