From c305688cebdb30e0d3707318e44bc6e6c0ef9957 Mon Sep 17 00:00:00 2001 From: tennix Date: Mon, 26 Aug 2024 17:40:19 +0800 Subject: [PATCH 1/3] BR: support Azure blob storage sas token --- docs/api-references/docs.md | 22 ++++ manifests/crd.yaml | 20 ++++ .../federation.pingcap.com_volumebackups.yaml | 4 + ...ion.pingcap.com_volumebackupschedules.yaml | 4 + ...federation.pingcap.com_volumerestores.yaml | 4 + manifests/crd/v1/pingcap.com_backups.yaml | 4 + .../crd/v1/pingcap.com_backupschedules.yaml | 8 ++ manifests/crd/v1/pingcap.com_restores.yaml | 8 ++ manifests/federation-crd.yaml | 12 ++ .../pingcap/v1alpha1/openapi_generated.go | 14 +++ pkg/apis/pingcap/v1alpha1/types.go | 4 + pkg/backup/util/remote.go | 26 ++++- pkg/backup/util/util.go | 109 +++++++++--------- pkg/backup/util/utils_test.go | 33 +++++- 14 files changed, 209 insertions(+), 63 deletions(-) diff --git a/docs/api-references/docs.md b/docs/api-references/docs.md index 7bb15cf2e1d..9b6fcae5024 100644 --- a/docs/api-references/docs.md +++ b/docs/api-references/docs.md @@ -3394,6 +3394,28 @@ azblob service account credentials.

+storageAccount
+ +string + + + +

StorageAccount is the storage account of the azure blob storage

+ + + + +sasToken
+ +string + + + +

SasToken is the sas token of the storage account

+ + + + prefix
string diff --git a/manifests/crd.yaml b/manifests/crd.yaml index 5af06877636..49fed4d885a 100644 --- a/manifests/crd.yaml +++ b/manifests/crd.yaml @@ -1187,8 +1187,12 @@ spec: type: string prefix: type: string + sasToken: + type: string secretName: type: string + storageAccount: + type: string type: object backoffRetryPolicy: properties: @@ -3603,8 +3607,12 @@ spec: type: string prefix: type: string + sasToken: + type: string secretName: type: string + storageAccount: + type: string type: object backoffRetryPolicy: properties: @@ -5832,8 +5840,12 @@ spec: type: string prefix: type: string + sasToken: + type: string secretName: type: string + storageAccount: + type: string type: object backoffRetryPolicy: properties: @@ -17214,8 +17226,12 @@ spec: type: string prefix: type: string + sasToken: + type: string secretName: type: string + storageAccount: + type: string type: object backupType: type: string @@ -18101,8 +18117,12 @@ spec: type: string prefix: type: string + sasToken: + type: string secretName: type: string + storageAccount: + type: string type: object gcs: properties: diff --git a/manifests/crd/federation/v1/federation.pingcap.com_volumebackups.yaml b/manifests/crd/federation/v1/federation.pingcap.com_volumebackups.yaml index 528c603a8a0..3f19f9e50f2 100644 --- a/manifests/crd/federation/v1/federation.pingcap.com_volumebackups.yaml +++ b/manifests/crd/federation/v1/federation.pingcap.com_volumebackups.yaml @@ -805,8 +805,12 @@ spec: type: string prefix: type: string + sasToken: + type: string secretName: type: string + storageAccount: + type: string type: object br: properties: diff --git a/manifests/crd/federation/v1/federation.pingcap.com_volumebackupschedules.yaml b/manifests/crd/federation/v1/federation.pingcap.com_volumebackupschedules.yaml index 6cf487f81a0..2c7e482bcd9 100644 --- a/manifests/crd/federation/v1/federation.pingcap.com_volumebackupschedules.yaml +++ b/manifests/crd/federation/v1/federation.pingcap.com_volumebackupschedules.yaml @@ -810,8 +810,12 @@ spec: type: string prefix: type: string + sasToken: + type: string secretName: type: string + storageAccount: + type: string type: object br: properties: diff --git a/manifests/crd/federation/v1/federation.pingcap.com_volumerestores.yaml b/manifests/crd/federation/v1/federation.pingcap.com_volumerestores.yaml index d2961345965..f31b965a7ab 100644 --- a/manifests/crd/federation/v1/federation.pingcap.com_volumerestores.yaml +++ b/manifests/crd/federation/v1/federation.pingcap.com_volumerestores.yaml @@ -61,8 +61,12 @@ spec: type: string prefix: type: string + sasToken: + type: string secretName: type: string + storageAccount: + type: string type: object gcs: properties: diff --git a/manifests/crd/v1/pingcap.com_backups.yaml b/manifests/crd/v1/pingcap.com_backups.yaml index 8d0d559c3d6..753a1d33caf 100644 --- a/manifests/crd/v1/pingcap.com_backups.yaml +++ b/manifests/crd/v1/pingcap.com_backups.yaml @@ -1187,8 +1187,12 @@ spec: type: string prefix: type: string + sasToken: + type: string secretName: type: string + storageAccount: + type: string type: object backoffRetryPolicy: properties: diff --git a/manifests/crd/v1/pingcap.com_backupschedules.yaml b/manifests/crd/v1/pingcap.com_backupschedules.yaml index 16fdcad49f0..fadc331a7dc 100644 --- a/manifests/crd/v1/pingcap.com_backupschedules.yaml +++ b/manifests/crd/v1/pingcap.com_backupschedules.yaml @@ -1162,8 +1162,12 @@ spec: type: string prefix: type: string + sasToken: + type: string secretName: type: string + storageAccount: + type: string type: object backoffRetryPolicy: properties: @@ -3391,8 +3395,12 @@ spec: type: string prefix: type: string + sasToken: + type: string secretName: type: string + storageAccount: + type: string type: object backoffRetryPolicy: properties: diff --git a/manifests/crd/v1/pingcap.com_restores.yaml b/manifests/crd/v1/pingcap.com_restores.yaml index 4bed2fca5ee..06de50d03b9 100644 --- a/manifests/crd/v1/pingcap.com_restores.yaml +++ b/manifests/crd/v1/pingcap.com_restores.yaml @@ -1160,8 +1160,12 @@ spec: type: string prefix: type: string + sasToken: + type: string secretName: type: string + storageAccount: + type: string type: object backupType: type: string @@ -2047,8 +2051,12 @@ spec: type: string prefix: type: string + sasToken: + type: string secretName: type: string + storageAccount: + type: string type: object gcs: properties: diff --git a/manifests/federation-crd.yaml b/manifests/federation-crd.yaml index fa7726482cd..0a6ae36d687 100644 --- a/manifests/federation-crd.yaml +++ b/manifests/federation-crd.yaml @@ -805,8 +805,12 @@ spec: type: string prefix: type: string + sasToken: + type: string secretName: type: string + storageAccount: + type: string type: object br: properties: @@ -2638,8 +2642,12 @@ spec: type: string prefix: type: string + sasToken: + type: string secretName: type: string + storageAccount: + type: string type: object br: properties: @@ -3674,8 +3682,12 @@ spec: type: string prefix: type: string + sasToken: + type: string secretName: type: string + storageAccount: + type: string type: object gcs: properties: diff --git a/pkg/apis/pingcap/v1alpha1/openapi_generated.go b/pkg/apis/pingcap/v1alpha1/openapi_generated.go index aadde10b971..ca34b428e8d 100644 --- a/pkg/apis/pingcap/v1alpha1/openapi_generated.go +++ b/pkg/apis/pingcap/v1alpha1/openapi_generated.go @@ -580,6 +580,20 @@ func schema_pkg_apis_pingcap_v1alpha1_AzblobStorageProvider(ref common.Reference Format: "", }, }, + "storageAccount": { + SchemaProps: spec.SchemaProps{ + Description: "StorageAccount is the storage account of the azure blob storage", + Type: []string{"string"}, + Format: "", + }, + }, + "sasToken": { + SchemaProps: spec.SchemaProps{ + Description: "SasToken is the sas token of the storage account", + Type: []string{"string"}, + Format: "", + }, + }, "prefix": { SchemaProps: spec.SchemaProps{ Description: "Prefix of the data path.", diff --git a/pkg/apis/pingcap/v1alpha1/types.go b/pkg/apis/pingcap/v1alpha1/types.go index 7f92b46dcb7..bfe6535060d 100644 --- a/pkg/apis/pingcap/v1alpha1/types.go +++ b/pkg/apis/pingcap/v1alpha1/types.go @@ -2012,6 +2012,10 @@ type AzblobStorageProvider struct { // SecretName is the name of secret which stores the // azblob service account credentials. SecretName string `json:"secretName,omitempty"` + // StorageAccount is the storage account of the azure blob storage + StorageAccount string `json:"storageAccount,omitempty"` + // SasToken is the sas token of the storage account + SasToken string `json:"sasToken,omitempty"` // Prefix of the data path. Prefix string `json:"prefix,omitempty"` } diff --git a/pkg/backup/util/remote.go b/pkg/backup/util/remote.go index 6125375d32d..fb983c72afe 100644 --- a/pkg/backup/util/remote.go +++ b/pkg/backup/util/remote.go @@ -18,6 +18,7 @@ import ( "errors" "fmt" "io" + "net/url" "os" "path" "strconv" @@ -83,10 +84,12 @@ type gcsConfig struct { } type azblobConfig struct { - container string - accessTier string - secretName string - prefix string + storageAccount string + sasToken string + container string + accessTier string + secretName string + prefix string } type localConfig struct { @@ -634,6 +637,16 @@ func newGcsStorageOptionForFlag(conf *gcsConfig, flag string) []string { func newAzblobStorageOptionForFlag(conf *azblobConfig, flag string) []string { var azblobOptions []string path := fmt.Sprintf("azure://%s/", path.Join(conf.container, conf.prefix)) + values := url.Values{} + if conf.storageAccount != "" { + values.Add("account-name", conf.storageAccount) + } + if conf.sasToken != "" { + values.Add("sas-token", conf.sasToken) + } + if v := values.Encode(); v != "" { + path = path + "?" + v + } if flag != "" && flag != defaultStorageFlag { // now just set path to special flag azblobOptions = append(azblobOptions, fmt.Sprintf("--%s=%s", flag, path)) @@ -697,7 +710,10 @@ func makeGcsConfig(gcs *v1alpha1.GcsStorageProvider, fakeRegion bool) *gcsConfig // makeAzblobConfig constructs azblobConfig parameters func makeAzblobConfig(azblob *v1alpha1.AzblobStorageProvider) *azblobConfig { - conf := azblobConfig{} + conf := azblobConfig{ + storageAccount: azblob.StorageAccount, + sasToken: azblob.SasToken, + } path := strings.Trim(azblob.Container, "/") + "/" + strings.Trim(azblob.Prefix, "/") fields := strings.SplitN(path, "/", 2) diff --git a/pkg/backup/util/util.go b/pkg/backup/util/util.go index 4aca851e2b5..7fd9f39233e 100644 --- a/pkg/backup/util/util.go +++ b/pkg/backup/util/util.go @@ -184,7 +184,7 @@ func generateGcsCertEnvVar(gcs *v1alpha1.GcsStorageProvider) ([]corev1.EnvVar, s } // generateAzblobCertEnvVar generate the env info in order to access azure blob storage -func generateAzblobCertEnvVar(azblob *v1alpha1.AzblobStorageProvider, useAAD bool) ([]corev1.EnvVar, string, error) { +func generateAzblobCertEnvVar(azblob *v1alpha1.AzblobStorageProvider, secret *corev1.Secret, useSasToken bool) ([]corev1.EnvVar, string, error) { if len(azblob.AccessTier) == 0 { azblob.AccessTier = "Cool" } @@ -193,64 +193,63 @@ func generateAzblobCertEnvVar(azblob *v1alpha1.AzblobStorageProvider, useAAD boo Name: "AZURE_ACCESS_TIER", Value: azblob.AccessTier, }, + { + Name: "AZURE_STORAGE_ACCOUNT", + Value: azblob.StorageAccount, + }, } - if azblob.SecretName != "" { + if useSasToken { + return envVars, "", nil + } + _, exist := CheckAllKeysExistInSecret(secret, constants.AzblobClientID, constants.AzblobClientScrt, constants.AzblobTenantID) + if exist { // using AAD auth envVars = append(envVars, []corev1.EnvVar{ { - Name: "AZURE_STORAGE_ACCOUNT", + Name: "AZURE_CLIENT_ID", ValueFrom: &corev1.EnvVarSource{ SecretKeyRef: &corev1.SecretKeySelector{ LocalObjectReference: corev1.LocalObjectReference{Name: azblob.SecretName}, - Key: constants.AzblobAccountName, + Key: constants.AzblobClientID, }, }, }, - }...) - if useAAD { - envVars = append(envVars, []corev1.EnvVar{ - { - Name: "AZURE_CLIENT_ID", - ValueFrom: &corev1.EnvVarSource{ - SecretKeyRef: &corev1.SecretKeySelector{ - LocalObjectReference: corev1.LocalObjectReference{Name: azblob.SecretName}, - Key: constants.AzblobClientID, - }, - }, - }, - { - Name: "AZURE_CLIENT_SECRET", - ValueFrom: &corev1.EnvVarSource{ - SecretKeyRef: &corev1.SecretKeySelector{ - LocalObjectReference: corev1.LocalObjectReference{Name: azblob.SecretName}, - Key: constants.AzblobClientScrt, - }, + { + Name: "AZURE_CLIENT_SECRET", + ValueFrom: &corev1.EnvVarSource{ + SecretKeyRef: &corev1.SecretKeySelector{ + LocalObjectReference: corev1.LocalObjectReference{Name: azblob.SecretName}, + Key: constants.AzblobClientScrt, }, }, - { - Name: "AZURE_TENANT_ID", - ValueFrom: &corev1.EnvVarSource{ - SecretKeyRef: &corev1.SecretKeySelector{ - LocalObjectReference: corev1.LocalObjectReference{Name: azblob.SecretName}, - Key: constants.AzblobTenantID, - }, + }, + { + Name: "AZURE_TENANT_ID", + ValueFrom: &corev1.EnvVarSource{ + SecretKeyRef: &corev1.SecretKeySelector{ + LocalObjectReference: corev1.LocalObjectReference{Name: azblob.SecretName}, + Key: constants.AzblobTenantID, }, }, - }...) - } else { - envVars = append(envVars, []corev1.EnvVar{ - { - Name: "AZURE_STORAGE_KEY", - ValueFrom: &corev1.EnvVarSource{ - SecretKeyRef: &corev1.SecretKeySelector{ - LocalObjectReference: corev1.LocalObjectReference{Name: azblob.SecretName}, - Key: constants.AzblobAccountKey, - }, + }, + }...) + return envVars, "", nil + } + _, exist = CheckAllKeysExistInSecret(secret, constants.AzblobAccountKey) + if exist { // use access key auth + envVars = append(envVars, []corev1.EnvVar{ + { + Name: "AZURE_STORAGE_KEY", + ValueFrom: &corev1.EnvVarSource{ + SecretKeyRef: &corev1.SecretKeySelector{ + LocalObjectReference: corev1.LocalObjectReference{Name: azblob.SecretName}, + Key: constants.AzblobAccountKey, }, }, - }...) - } + }, + }...) + return envVars, "", nil } - return envVars, "", nil + return nil, "azblobKeyOrAADMissing", fmt.Errorf("secret %s/%s missing some keys", secret.Namespace, secret.Name) } // GenerateStorageCertEnv generate the env info in order to access backend backup storage @@ -303,27 +302,25 @@ func GenerateStorageCertEnv(ns string, useKMS bool, provider v1alpha1.StoragePro return certEnv, reason, err } case v1alpha1.BackupStorageTypeAzblob: - useAAD := true azblobSecretName := provider.Azblob.SecretName + var secret *corev1.Secret if azblobSecretName != "" { - secret, err := secretLister.Secrets(ns).Get(azblobSecretName) + secret, err = secretLister.Secrets(ns).Get(azblobSecretName) if err != nil { err := fmt.Errorf("get azblob secret %s/%s failed, err: %v", ns, azblobSecretName, err) return certEnv, "GetAzblobSecretFailed", err } - - keyStrAAD, exist := CheckAllKeysExistInSecret(secret, constants.AzblobAccountName, constants.AzblobClientID, constants.AzblobClientScrt, constants.AzblobTenantID) - if !exist { - keyStrShared, exist := CheckAllKeysExistInSecret(secret, constants.AzblobAccountName, constants.AzblobAccountKey) - if !exist { - err := fmt.Errorf("the azblob secret %s/%s missing some keys for AAD %s or shared %s", ns, azblobSecretName, keyStrAAD, keyStrShared) - return certEnv, "azblobKeyNotExist", err - } - useAAD = false + } + if provider.Azblob.StorageAccount == "" { // try to get storageAccount from secret + account := string(secret.Data[constants.AzblobAccountName]) + if account == "" { + err := fmt.Errorf("secret %s/%s missing some keys, storage account unspecified: %v", ns, azblobSecretName, secret.Data) + return certEnv, "azblobAccountNotExist", err } + provider.Azblob.StorageAccount = account } - - certEnv, reason, err = generateAzblobCertEnvVar(provider.Azblob, useAAD) + useSasToken := provider.Azblob.SasToken != "" + certEnv, reason, err = generateAzblobCertEnvVar(provider.Azblob, secret, useSasToken) if err != nil { return certEnv, reason, err diff --git a/pkg/backup/util/utils_test.go b/pkg/backup/util/utils_test.go index 7b94ce0c547..37552be011d 100644 --- a/pkg/backup/util/utils_test.go +++ b/pkg/backup/util/utils_test.go @@ -168,13 +168,13 @@ func TestGenerateAzblobCertEnvVar(t *testing.T) { azblob = &v1alpha1.AzblobStorageProvider{ AccessTier: "", } - envs, _, err := generateAzblobCertEnvVar(azblob, true) + envs, _, err := generateAzblobCertEnvVar(azblob, nil, true) g.Expect(err).Should(BeNil()) contains(envs, "AZURE_ACCESS_TIER", "Cool") // test &v1alpha1.AzblobStorageProvider AccessTier set value azblob.AccessTier = "Hot" - envs, _, err = generateAzblobCertEnvVar(azblob, true) + envs, _, err = generateAzblobCertEnvVar(azblob, nil, true) g.Expect(err).Should(BeNil()) contains(envs, "AZURE_ACCESS_TIER", "Hot") } @@ -275,8 +275,36 @@ func TestGenerateStorageCertEnv(t *testing.T) { // test azblob secret with key if test.provider.Azblob != nil && test.provider.Azblob.SecretName != "" { + // test using sas token + test.provider.Azblob.StorageAccount = "dummy" + test.provider.Azblob.SasToken = "dummy" + _, _, err := GenerateStorageCertEnv(ns, false, test.provider, informer.Core().V1().Secrets().Lister()) + g.Expect(err).Should(BeNil()) + + // test using sas token, account from env + test.provider.Azblob.SasToken = "dummy" + s.Data = map[string][]byte{ + constants.TidbPasswordKey: []byte("dummy"), + constants.AzblobAccountName: []byte("dummy"), + } + err = informer.Core().V1().Secrets().Informer().GetIndexer().Update(s) + g.Expect(err).Should(BeNil()) + _, _, err = GenerateStorageCertEnv(ns, false, test.provider, informer.Core().V1().Secrets().Lister()) + g.Expect(err).Should(BeNil()) + + // test using sas token, missing account + test.provider.Azblob.SasToken = "dummy" + test.provider.Azblob.StorageAccount = "" + s.Data = map[string][]byte{ + constants.TidbPasswordKey: []byte("dummy"), + } + err = informer.Core().V1().Secrets().Informer().GetIndexer().Update(s) + g.Expect(err).Should(BeNil()) + _, _, err = GenerateStorageCertEnv(ns, false, test.provider, informer.Core().V1().Secrets().Lister()) + g.Expect(err.Error()).Should(MatchRegexp(".*storage account unspecified")) // test missing some critical key + test.provider.Azblob.SasToken = "" s.Data = map[string][]byte{ constants.TidbPasswordKey: []byte("dummy"), constants.AzblobAccountName: []byte("dummy"), @@ -289,6 +317,7 @@ func TestGenerateStorageCertEnv(t *testing.T) { g.Expect(err.Error()).Should(MatchRegexp(".*missing some keys.*")) // test integrated shared key + test.provider.Azblob.SasToken = "" s.Data = map[string][]byte{ constants.TidbPasswordKey: []byte("dummy"), constants.AzblobAccountName: []byte("dummy"), From db0635871c09ee8c831391c12c0f6017b776277b Mon Sep 17 00:00:00 2001 From: tennix Date: Tue, 27 Aug 2024 16:02:20 +0800 Subject: [PATCH 2/3] Address comment --- docs/api-references/docs.md | 4 +++- pkg/apis/pingcap/v1alpha1/openapi_generated.go | 2 +- pkg/apis/pingcap/v1alpha1/types.go | 2 ++ 3 files changed, 6 insertions(+), 2 deletions(-) diff --git a/docs/api-references/docs.md b/docs/api-references/docs.md index 9b6fcae5024..6a96a6bbcc0 100644 --- a/docs/api-references/docs.md +++ b/docs/api-references/docs.md @@ -3400,7 +3400,9 @@ string -

StorageAccount is the storage account of the azure blob storage

+

StorageAccount is the storage account of the azure blob storage +If this field is set, then use this to set backup-manager env +Otherwise retrieve the storage account from secret

diff --git a/pkg/apis/pingcap/v1alpha1/openapi_generated.go b/pkg/apis/pingcap/v1alpha1/openapi_generated.go index ca34b428e8d..85c0ec12465 100644 --- a/pkg/apis/pingcap/v1alpha1/openapi_generated.go +++ b/pkg/apis/pingcap/v1alpha1/openapi_generated.go @@ -582,7 +582,7 @@ func schema_pkg_apis_pingcap_v1alpha1_AzblobStorageProvider(ref common.Reference }, "storageAccount": { SchemaProps: spec.SchemaProps{ - Description: "StorageAccount is the storage account of the azure blob storage", + Description: "StorageAccount is the storage account of the azure blob storage If this field is set, then use this to set backup-manager env Otherwise retrieve the storage account from secret", Type: []string{"string"}, Format: "", }, diff --git a/pkg/apis/pingcap/v1alpha1/types.go b/pkg/apis/pingcap/v1alpha1/types.go index bfe6535060d..7839858e308 100644 --- a/pkg/apis/pingcap/v1alpha1/types.go +++ b/pkg/apis/pingcap/v1alpha1/types.go @@ -2013,6 +2013,8 @@ type AzblobStorageProvider struct { // azblob service account credentials. SecretName string `json:"secretName,omitempty"` // StorageAccount is the storage account of the azure blob storage + // If this field is set, then use this to set backup-manager env + // Otherwise retrieve the storage account from secret StorageAccount string `json:"storageAccount,omitempty"` // SasToken is the sas token of the storage account SasToken string `json:"sasToken,omitempty"` From 8fc5282b64e52ff5b52e4807d1b1da41c89c7c21 Mon Sep 17 00:00:00 2001 From: tennix Date: Wed, 28 Aug 2024 14:37:17 +0800 Subject: [PATCH 3/3] fix br-manager fetch backup meta --- pkg/backup/util/remote.go | 32 ++++++++++++++++++++------------ 1 file changed, 20 insertions(+), 12 deletions(-) diff --git a/pkg/backup/util/remote.go b/pkg/backup/util/remote.go index fb983c72afe..fdf0e6026df 100644 --- a/pkg/backup/util/remote.go +++ b/pkg/backup/util/remote.go @@ -528,38 +528,46 @@ func newAzblobStorage(conf *azblobConfig) (*blob.Bucket, error) { // Azure shared key with access to the storage account accountKey := os.Getenv("AZURE_STORAGE_KEY") - // check condition for using AAD credentials first - var usingAAD bool - if len(clientID) != 0 && len(clientSecret) != 0 && len(tenantID) != 0 { - usingAAD = true - } else if len(accountKey) != 0 { - usingAAD = false - } else { - return nil, errors.New("Missing necessary key(s) for credentials") - } + // Azure Storage Account Shared Access Signature Token + sasToken := conf.sasToken - // initialize a new azblob storage using AAD or shared key credentials var bucket *blob.Bucket var err error - if usingAAD { + if len(sasToken) != 0 { + bucket, err = newAzblobStorageUsingSasToken(conf, account, sasToken) + } else if len(clientID) != 0 && len(clientSecret) != 0 && len(tenantID) != 0 { bucket, err = newAzblobStorageUsingAAD(conf, &azblobAADCred{ account: account, clientID: clientID, clientSecret: clientSecret, tenantID: tenantID, }) - } else { + } else if len(accountKey) != 0 { bucket, err = newAzblobStorageUsingSharedKey(conf, &azblobSharedKeyCred{ account: account, sharedKey: accountKey, }) + } else { + return nil, errors.New("Missing necessary key(s) for credentials") } + if err != nil { return nil, err } return blob.PrefixedBucket(bucket, strings.Trim(conf.prefix, "/")+"/"), nil } +func newAzblobStorageUsingSasToken(conf *azblobConfig, account, token string) (*blob.Bucket, error) { + // Azure Storage Account. + accountName := azureblob.AccountName(account) + sasToken := azureblob.SASToken(token) + cred := azblob.NewAnonymousCredential() + pipeline := azureblob.NewPipeline(cred, azblob.PipelineOptions{}) + // Create a *blob.Bucket. + ctx := context.Background() + return azureblob.OpenBucket(ctx, pipeline, accountName, conf.container, &azureblob.Options{SASToken: sasToken}) +} + // newAzblobStorageUsingAAD initialize a new azblob storage using AAD credentials func newAzblobStorageUsingAAD(conf *azblobConfig, cred *azblobAADCred) (*blob.Bucket, error) { // Azure Storage Account.