Skip to content

Commit de0cf01

Browse files
authored
Merge pull request #243 from andyzhangx/read-from-secret
fix: account key list throttling issue
2 parents ece81de + 9504618 commit de0cf01

File tree

200 files changed

+17445
-34
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

200 files changed

+17445
-34
lines changed

charts/latest/blob-csi-driver/templates/rbac-csi-blob-controller.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -133,7 +133,7 @@ metadata:
133133
rules:
134134
- apiGroups: [""]
135135
resources: ["secrets"]
136-
verbs: ["get", "list"]
136+
verbs: ["get", "list", "create"]
137137

138138
---
139139
kind: ClusterRoleBinding

deploy/rbac-csi-blob-controller.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -132,7 +132,7 @@ metadata:
132132
rules:
133133
- apiGroups: [""]
134134
resources: ["secrets"]
135-
verbs: ["get", "list"]
135+
verbs: ["get", "list", "create"]
136136

137137
---
138138
kind: ClusterRoleBinding

pkg/blob/azure.go

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -72,10 +72,14 @@ func GetCloudProvider(kubeconfig string) (*azureprovider.Cloud, error) {
7272
defer f.Close()
7373

7474
klog.V(2).Infof("read cloud config from file: %s successfully", credFile)
75-
return azureprovider.NewCloudWithoutFeatureGates(f)
75+
if az, err = azureprovider.NewCloudWithoutFeatureGates(f); err != nil {
76+
return az, err
77+
}
7678
}
7779

78-
klog.V(2).Infof("read cloud config from secret successfully")
80+
if kubeClient != nil {
81+
az.KubeClient = kubeClient
82+
}
7983
return az, nil
8084
}
8185

pkg/blob/blob.go

Lines changed: 99 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,10 @@ import (
2727
"github.com/container-storage-interface/spec/lib/go/csi"
2828
"github.com/pborman/uuid"
2929
"golang.org/x/net/context"
30+
v1 "k8s.io/api/core/v1"
31+
"k8s.io/apimachinery/pkg/api/errors"
32+
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
33+
"k8s.io/client-go/kubernetes"
3034
"k8s.io/klog/v2"
3135
k8sutil "k8s.io/kubernetes/pkg/volume/util"
3236
"k8s.io/legacy-cloud-providers/azure"
@@ -36,20 +40,27 @@ import (
3640

3741
const (
3842
// DriverName holds the name of the csi-driver
39-
DriverName = "blob.csi.azure.com"
40-
separator = "#"
41-
volumeIDTemplate = "%s#%s#%s"
42-
fileMode = "file_mode"
43-
dirMode = "dir_mode"
44-
vers = "vers"
45-
defaultFileMode = "0777"
46-
defaultDirMode = "0777"
47-
defaultVers = "3.0"
48-
serverNameField = "server"
49-
tagsField = "tags"
50-
protocolField = "protocol"
51-
fuse = "fuse"
52-
nfs = "nfs"
43+
DriverName = "blob.csi.azure.com"
44+
separator = "#"
45+
volumeIDTemplate = "%s#%s#%s"
46+
secretNameTemplate = "azure-storage-account-%s-secret"
47+
fileMode = "file_mode"
48+
dirMode = "dir_mode"
49+
vers = "vers"
50+
defaultFileMode = "0777"
51+
defaultDirMode = "0777"
52+
defaultVers = "3.0"
53+
serverNameField = "server"
54+
tagsField = "tags"
55+
protocolField = "protocol"
56+
secretNamespaceField = "secretnamespace"
57+
storeAccountKeyField = "storeaccountkey"
58+
storeAccountKeyFalse = "false"
59+
defaultSecretAccountName = "azurestorageaccountname"
60+
defaultSecretAccountKey = "azurestorageaccountkey"
61+
defaultSecretNamespace = "default"
62+
fuse = "fuse"
63+
nfs = "nfs"
5364

5465
// See https://docs.microsoft.com/en-us/rest/api/storageservices/naming-and-referencing-containers--blobs--and-metadata#container-names
5566
containerNameMinLength = 3
@@ -296,9 +307,14 @@ func (d *Driver) GetAuthEnv(ctx context.Context, volumeID string, attrib, secret
296307
resourceGroupName = d.cloud.ResourceGroup
297308
}
298309

299-
accountKey, err = d.cloud.GetStorageAccesskey(accountName, resourceGroupName)
310+
// read from k8s secret first
311+
accountKey, err = d.GetStorageAccesskeyFromSecret(accountName, attrib[secretNamespaceField])
300312
if err != nil {
301-
return accountName, containerName, authEnv, fmt.Errorf("no key for storage account(%s) under resource group(%s), err %v", accountName, resourceGroupName, err)
313+
klog.V(2).Infof("could not get account(%s) key from secret, error: %v, use cluster identity to get account key instead", accountName, err)
314+
accountKey, err = d.cloud.GetStorageAccesskey(accountName, resourceGroupName)
315+
if err != nil {
316+
return accountName, containerName, authEnv, fmt.Errorf("no key for storage account(%s) under resource group(%s), err %v", accountName, resourceGroupName, err)
317+
}
302318
}
303319
} else {
304320
for k, v := range secrets {
@@ -468,3 +484,70 @@ func getStorageAccount(secrets map[string]string) (string, string, error) {
468484
klog.V(4).Infof("got storage account(%s) from secret", accountName)
469485
return accountName, accountKey, nil
470486
}
487+
488+
func setAzureCredentials(kubeClient kubernetes.Interface, accountName, accountKey, secretNamespace string) (string, error) {
489+
if kubeClient == nil {
490+
klog.Warningf("could not create secret: kubeClient is nil")
491+
return "", nil
492+
}
493+
if accountName == "" || accountKey == "" {
494+
return "", fmt.Errorf("the account info is not enough, accountName(%v), accountKey(%v)", accountName, accountKey)
495+
}
496+
if secretNamespace == "" {
497+
secretNamespace = defaultSecretNamespace
498+
}
499+
secretName := fmt.Sprintf(secretNameTemplate, accountName)
500+
secret := &v1.Secret{
501+
ObjectMeta: metav1.ObjectMeta{
502+
Namespace: defaultSecretNamespace,
503+
Name: secretName,
504+
},
505+
Data: map[string][]byte{
506+
defaultSecretAccountName: []byte(accountName),
507+
defaultSecretAccountKey: []byte(accountKey),
508+
},
509+
Type: "Opaque",
510+
}
511+
_, err := kubeClient.CoreV1().Secrets(secretNamespace).Create(context.TODO(), secret, metav1.CreateOptions{})
512+
if errors.IsAlreadyExists(err) {
513+
err = nil
514+
}
515+
if err != nil {
516+
return "", fmt.Errorf("couldn't create secret %v", err)
517+
}
518+
return secretName, err
519+
}
520+
521+
// GetStorageAccesskey get Azure storage account key
522+
func (d *Driver) GetStorageAccesskey(accountOptions *azure.AccountOptions, secrets map[string]string, secretNamespace string) (string, error) {
523+
if len(secrets) > 0 {
524+
_, accountKey, err := getStorageAccount(secrets)
525+
return accountKey, err
526+
}
527+
528+
// read from k8s secret first
529+
accountKey, err := d.GetStorageAccesskeyFromSecret(accountOptions.Name, secretNamespace)
530+
if err != nil {
531+
klog.V(2).Infof("could not get account(%s) key from secret, error: %v, use cluster identity to get account key instead", accountOptions.Name, err)
532+
return d.cloud.GetStorageAccesskey(accountOptions.Name, accountOptions.ResourceGroup)
533+
}
534+
return accountKey, err
535+
}
536+
537+
// GetStorageAccesskeyFromSecret get storage account key from k8s secret
538+
func (d *Driver) GetStorageAccesskeyFromSecret(accountName, secretNamespace string) (string, error) {
539+
if d.cloud.KubeClient == nil {
540+
return "", fmt.Errorf("could not get account(%s) key from secret: KubeClient is nil", accountName)
541+
}
542+
543+
secretName := fmt.Sprintf(secretNameTemplate, accountName)
544+
if secretNamespace == "" {
545+
secretNamespace = defaultSecretNamespace
546+
}
547+
secret, err := d.cloud.KubeClient.CoreV1().Secrets(secretNamespace).Get(context.TODO(), secretName, metav1.GetOptions{})
548+
if err != nil {
549+
return "", fmt.Errorf("could not get secret(%v): %v", secretName, err)
550+
}
551+
552+
return string(secret.Data[defaultSecretAccountKey][:]), nil
553+
}

pkg/blob/blob_test.go

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,8 @@ import (
3131
"github.com/golang/mock/gomock"
3232
"github.com/stretchr/testify/assert"
3333

34+
"k8s.io/client-go/kubernetes"
35+
"k8s.io/client-go/kubernetes/fake"
3436
"k8s.io/legacy-cloud-providers/azure"
3537
"k8s.io/legacy-cloud-providers/azure/clients/storageaccountclient/mockstorageaccountclient"
3638
)
@@ -700,3 +702,59 @@ func TestGetStorageAccount(t *testing.T) {
700702
}
701703
}
702704
}
705+
706+
func TestSetAzureCredentials(t *testing.T) {
707+
fakeClient := fake.NewSimpleClientset()
708+
709+
tests := []struct {
710+
desc string
711+
kubeClient kubernetes.Interface
712+
accountName string
713+
accountKey string
714+
secretNamespace string
715+
expectedName string
716+
expectedErr error
717+
}{
718+
{
719+
desc: "[failure] accountName is nil",
720+
kubeClient: fakeClient,
721+
expectedErr: fmt.Errorf("the account info is not enough, accountName(), accountKey()"),
722+
},
723+
{
724+
desc: "[failure] accountKey is nil",
725+
kubeClient: fakeClient,
726+
accountName: "testName",
727+
accountKey: "",
728+
expectedErr: fmt.Errorf("the account info is not enough, accountName(testName), accountKey()"),
729+
},
730+
{
731+
desc: "[success] kubeClient is nil",
732+
kubeClient: nil,
733+
expectedErr: nil,
734+
},
735+
{
736+
desc: "[success] normal scenario",
737+
kubeClient: fakeClient,
738+
accountName: "testName",
739+
accountKey: "testKey",
740+
expectedName: "azure-storage-account-testName-secret",
741+
expectedErr: nil,
742+
},
743+
{
744+
desc: "[success] already exist",
745+
kubeClient: fakeClient,
746+
accountName: "testName",
747+
accountKey: "testKey",
748+
expectedName: "azure-storage-account-testName-secret",
749+
expectedErr: nil,
750+
},
751+
}
752+
753+
for _, test := range tests {
754+
result, err := setAzureCredentials(test.kubeClient, test.accountName, test.accountKey, test.secretNamespace)
755+
if result != test.expectedName || !reflect.DeepEqual(err, test.expectedErr) {
756+
t.Errorf("desc: %s,\n input: kubeClient(%v), accountName(%v), accountKey(%v),\n setAzureCredentials result: %v, expectedName: %v err: %v, expectedErr: %v",
757+
test.desc, test.kubeClient, test.accountName, test.accountKey, result, test.expectedName, err, test.expectedErr)
758+
}
759+
}
760+
}

pkg/blob/controllerserver.go

Lines changed: 25 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,7 @@ func (d *Driver) CreateVolume(ctx context.Context, req *csi.CreateVolumeRequest)
5353
requestGiB := int(util.RoundUpGiB(volSizeBytes))
5454

5555
parameters := req.GetParameters()
56-
var storageAccountType, resourceGroup, location, account, containerName, protocol, customTags string
56+
var storageAccountType, resourceGroup, location, account, containerName, protocol, customTags, storeAccountKey, secretNamespace string
5757

5858
// Apply ProvisionerParameters (case-insensitive). We leave validation of
5959
// the values to the cloud provider.
@@ -75,6 +75,10 @@ func (d *Driver) CreateVolume(ctx context.Context, req *csi.CreateVolumeRequest)
7575
protocol = v
7676
case tagsField:
7777
customTags = v
78+
case secretNamespaceField:
79+
secretNamespace = v
80+
case storeAccountKeyField:
81+
storeAccountKey = v
7882
}
7983
}
8084

@@ -95,6 +99,8 @@ func (d *Driver) CreateVolume(ctx context.Context, req *csi.CreateVolumeRequest)
9599
return nil, status.Errorf(codes.InvalidArgument, "storage account must be specified when provisioning nfs file share")
96100
}
97101
enableHTTPSTrafficOnly = false
102+
// NFS protocol does not need account key
103+
storeAccountKey = storeAccountKeyFalse
98104
}
99105

100106
accountKind := string(storage.StorageV2)
@@ -117,9 +123,10 @@ func (d *Driver) CreateVolume(ctx context.Context, req *csi.CreateVolumeRequest)
117123
Tags: tags,
118124
}
119125

120-
var accountName, accountKey string
121-
if len(req.GetSecrets()) == 0 { // check whether account is provided by secret
122-
lockKey := account + storageAccountType + accountKind + resourceGroup + location
126+
var accountKey string
127+
accountName := account
128+
if len(req.GetSecrets()) == 0 && accountName == "" {
129+
lockKey := storageAccountType + accountKind + resourceGroup + location
123130
d.volLockMap.LockEntry(lockKey)
124131
defer d.volLockMap.UnlockEntry(lockKey)
125132

@@ -135,10 +142,12 @@ func (d *Driver) CreateVolume(ctx context.Context, req *csi.CreateVolumeRequest)
135142
if err != nil {
136143
return nil, status.Errorf(codes.Internal, "failed to ensure storage account: %v", err)
137144
}
138-
} else {
139-
accountName, accountKey, err = getStorageAccount(req.GetSecrets())
140-
if err != nil {
141-
return nil, status.Errorf(codes.Internal, "failed to get storage account from secrets: %v", err)
145+
}
146+
accountOptions.Name = accountName
147+
148+
if accountKey == "" {
149+
if accountKey, err = d.GetStorageAccesskey(accountOptions, req.GetSecrets(), secretNamespace); err != nil {
150+
return nil, fmt.Errorf("failed to GetStorageAccesskey on account(%s) rg(%s), error: %v", accountOptions.Name, accountOptions.ResourceGroup, err)
142151
}
143152
}
144153

@@ -159,15 +168,17 @@ func (d *Driver) CreateVolume(ctx context.Context, req *csi.CreateVolumeRequest)
159168
}
160169

161170
volumeID := fmt.Sprintf(volumeIDTemplate, resourceGroup, accountName, containerName)
171+
klog.V(2).Infof("create container %s on storage account %s successfully", containerName, accountName)
162172

163-
/* todo: snapshot support
164-
if req.GetVolumeContentSource() != nil {
165-
contentSource := req.GetVolumeContentSource()
166-
if contentSource.GetSnapshot() != nil {
173+
if storeAccountKey != storeAccountKeyFalse && len(req.GetSecrets()) == 0 {
174+
secretName, err := setAzureCredentials(d.cloud.KubeClient, accountName, accountKey, secretNamespace)
175+
if err != nil {
176+
return nil, status.Errorf(codes.Internal, "failed to store storage account key: %v", err)
177+
}
178+
if secretName != "" {
179+
klog.V(2).Infof("store account key to k8s secret(%v) in %s namespace", secretName, secretNamespace)
167180
}
168181
}
169-
*/
170-
klog.V(2).Infof("create container %s on storage account %s successfully", containerName, accountName)
171182

172183
return &csi.CreateVolumeResponse{
173184
Volume: &csi.Volume{

0 commit comments

Comments
 (0)