yunion.io/x/cloudmux@v0.3.10-0-alpha.1/pkg/multicloud/azure/storageaccount.go (about)

     1  // Copyright 2019 Yunion
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at
     6  //
     7  //     http://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS,
    11  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  // See the License for the specific language governing permissions and
    13  // limitations under the License.
    14  
    15  package azure
    16  
    17  import (
    18  	"context"
    19  	"encoding/base64"
    20  	"fmt"
    21  	"io"
    22  	"math/rand"
    23  	"net/http"
    24  	"net/url"
    25  	"path"
    26  	"strconv"
    27  	"strings"
    28  	"time"
    29  
    30  	"github.com/Azure/azure-sdk-for-go/storage"
    31  	azureenv "github.com/Azure/go-autorest/autorest/azure"
    32  	"github.com/Microsoft/azure-vhd-utils/vhdcore/common"
    33  	"github.com/Microsoft/azure-vhd-utils/vhdcore/diskstream"
    34  
    35  	"yunion.io/x/jsonutils"
    36  	"yunion.io/x/log"
    37  	"yunion.io/x/pkg/errors"
    38  	"yunion.io/x/pkg/utils"
    39  
    40  	"yunion.io/x/cloudmux/pkg/cloudprovider"
    41  	"yunion.io/x/onecloud/pkg/httperrors"
    42  	"yunion.io/x/cloudmux/pkg/multicloud"
    43  )
    44  
    45  type SContainer struct {
    46  	storageaccount *SStorageAccount
    47  	Name           string
    48  }
    49  
    50  type SSku struct {
    51  	Name         string
    52  	Tier         string
    53  	Kind         string
    54  	ResourceType string
    55  }
    56  
    57  type Identity struct {
    58  	PrincipalID string
    59  	TenantID    string
    60  	Type        string
    61  }
    62  
    63  type SStorageEndpoints struct {
    64  	Blob  string
    65  	Queue string
    66  	Table string
    67  	File  string
    68  }
    69  
    70  type AccountProperties struct {
    71  	//normal
    72  	PrimaryEndpoints   SStorageEndpoints `json:"primaryEndpoints,omitempty"`
    73  	ProvisioningState  string
    74  	PrimaryLocation    string
    75  	SecondaryEndpoints SStorageEndpoints `json:"secondaryEndpoints,omitempty"`
    76  	SecondaryLocation  string
    77  	//CreationTime           time.Time
    78  	AccessTier               string `json:"accessTier,omitempty"`
    79  	EnableHTTPSTrafficOnly   *bool  `json:"supportsHttpsTrafficOnly,omitempty"`
    80  	IsHnsEnabled             bool   `json:"isHnsEnabled,omitempty"`
    81  	AzureFilesAadIntegration bool   `json:"azureFilesAadIntegration,omitempty"`
    82  }
    83  
    84  type SStorageAccount struct {
    85  	multicloud.SBaseBucket
    86  	AzureTags
    87  
    88  	region *SRegion
    89  
    90  	accountKey string
    91  
    92  	Sku      SSku   `json:"sku,omitempty"`
    93  	Kind     string `json:"kind,omitempty"`
    94  	Identity *Identity
    95  	Location string `json:"location,omitempty"`
    96  	ID       string `json:"id,omitempty"`
    97  	Name     string `json:"name,omitempty"`
    98  	Type     string `json:"type,omitempty"`
    99  
   100  	Properties AccountProperties `json:"properties"`
   101  }
   102  
   103  func (self *SRegion) listStorageAccounts() ([]SStorageAccount, error) {
   104  	accounts := []SStorageAccount{}
   105  	err := self.list("Microsoft.Storage/storageAccounts", url.Values{}, &accounts)
   106  	if err != nil {
   107  		return nil, errors.Wrapf(err, "list")
   108  	}
   109  	result := []SStorageAccount{}
   110  	for i := range accounts {
   111  		accounts[i].region = self
   112  		result = append(result, accounts[i])
   113  	}
   114  	return result, nil
   115  }
   116  
   117  func (self *SRegion) ListStorageAccounts() ([]SStorageAccount, error) {
   118  	return self.listStorageAccounts()
   119  }
   120  
   121  func randomString(prefix string, length int) string {
   122  	bytes := []byte("0123456789abcdefghijklmnopqrstuvwxyz")
   123  	result := []byte{}
   124  	r := rand.New(rand.NewSource(time.Now().UnixNano()))
   125  	for i := 0; i < length; i++ {
   126  		result = append(result, bytes[r.Intn(len(bytes))])
   127  	}
   128  	return prefix + string(result)
   129  }
   130  
   131  func (self *SRegion) GetUniqStorageAccountName() (string, error) {
   132  	for i := 0; i < 20; i++ {
   133  		name := randomString("storage", 8)
   134  		exist, err := self.checkStorageAccountNameExist(name)
   135  		if err == nil && !exist {
   136  			return name, nil
   137  		}
   138  	}
   139  	return "", fmt.Errorf("failed to found uniq storage name")
   140  }
   141  
   142  type sNameAvailableOutput struct {
   143  	NameAvailable bool   `json:"nameAvailable"`
   144  	Reason        string `json:"reason"`
   145  	Message       string `json:"message"`
   146  }
   147  
   148  func (self *SRegion) checkStorageAccountNameExist(name string) (bool, error) {
   149  	ok, err := self.client.CheckNameAvailability("Microsoft.Storage/storageAccounts", name)
   150  	if err != nil {
   151  		return false, errors.Wrapf(err, "CheckNameAvailability(%s)", name)
   152  	}
   153  	return !ok, nil
   154  }
   155  
   156  type SStorageAccountSku struct {
   157  	ResourceType string   `json:"resourceType"`
   158  	Name         string   `json:"name"`
   159  	Tier         string   `json:"tier"`
   160  	Kind         string   `json:"kind"`
   161  	Locations    []string `json:"locations"`
   162  	Capabilities []struct {
   163  		Name  string `json:"name"`
   164  		Value string `json:"value"`
   165  	} `json:"capabilities"`
   166  	Restrictions []struct {
   167  		Type       string   `json:"type"`
   168  		Values     []string `json:"values"`
   169  		ReasonCode string   `json:"reasonCode"`
   170  	} `json:"restrictions"`
   171  }
   172  
   173  func (self *SRegion) GetStorageAccountSkus() ([]SStorageAccountSku, error) {
   174  	skus := make([]SStorageAccountSku, 0)
   175  	err := self.client.list("Microsoft.Storage/skus", url.Values{}, &skus)
   176  	if err != nil {
   177  		return nil, errors.Wrap(err, "List")
   178  	}
   179  	ret := make([]SStorageAccountSku, 0)
   180  	for i := range skus {
   181  		if utils.IsInStringArray(self.GetId(), skus[i].Locations) {
   182  			ret = append(ret, skus[i])
   183  		}
   184  	}
   185  	return ret, nil
   186  }
   187  
   188  func (self *SRegion) getStorageAccountSkuByName(name string) (*SStorageAccountSku, error) {
   189  	skus, err := self.GetStorageAccountSkus()
   190  	if err != nil {
   191  		return nil, errors.Wrap(err, "getStorageAccountSkus")
   192  	}
   193  	for _, kind := range []string{
   194  		"StorageV2",
   195  		"Storage",
   196  	} {
   197  		for i := range skus {
   198  			if skus[i].Name == name && skus[i].Kind == kind {
   199  				return &skus[i], nil
   200  			}
   201  		}
   202  	}
   203  	return nil, cloudprovider.ErrNotFound
   204  }
   205  
   206  func (self *SRegion) createStorageAccount(name string, skuName string) (*SStorageAccount, error) {
   207  	storageKind := "Storage"
   208  	if len(skuName) > 0 {
   209  		sku, err := self.getStorageAccountSkuByName(skuName)
   210  		if err != nil {
   211  			return nil, errors.Wrap(err, "getStorageAccountSkuByName")
   212  		}
   213  		skuName = sku.Name
   214  		storageKind = sku.Kind
   215  	} else {
   216  		skuName = "Standard_GRS"
   217  	}
   218  	storageaccount := SStorageAccount{
   219  		region:   self,
   220  		Location: self.Name,
   221  		Sku: SSku{
   222  			Name: skuName,
   223  		},
   224  		Kind: storageKind,
   225  		Properties: AccountProperties{
   226  			IsHnsEnabled:             true,
   227  			AzureFilesAadIntegration: true,
   228  		},
   229  		Name: name,
   230  		Type: "Microsoft.Storage/storageAccounts",
   231  	}
   232  
   233  	err := self.create("", jsonutils.Marshal(storageaccount), &storageaccount)
   234  	if err != nil {
   235  		return nil, errors.Wrap(err, "Create")
   236  	}
   237  	return &storageaccount, nil
   238  }
   239  
   240  func (self *SRegion) CreateStorageAccount(storageAccount string) (*SStorageAccount, error) {
   241  	account, err := self.getStorageAccountID(storageAccount)
   242  	if err == nil {
   243  		return account, nil
   244  	}
   245  	if errors.Cause(err) == cloudprovider.ErrNotFound {
   246  		uniqName, err := self.GetUniqStorageAccountName()
   247  		if err != nil {
   248  			return nil, errors.Wrapf(err, "GetUniqStorageAccountName")
   249  		}
   250  		storageaccount := SStorageAccount{
   251  			region: self,
   252  			Sku: SSku{
   253  				Name: "Standard_GRS",
   254  			},
   255  			Location: self.Name,
   256  			Kind:     "Storage",
   257  			Properties: AccountProperties{
   258  				IsHnsEnabled:             true,
   259  				AzureFilesAadIntegration: true,
   260  			},
   261  			Name: uniqName,
   262  			Type: "Microsoft.Storage/storageAccounts",
   263  		}
   264  		storageaccount.Tags = map[string]string{"id": storageAccount}
   265  		return &storageaccount, self.create("", jsonutils.Marshal(storageaccount), &storageaccount)
   266  	}
   267  	return nil, err
   268  }
   269  
   270  func (self *SRegion) getStorageAccountID(storageAccount string) (*SStorageAccount, error) {
   271  	accounts, err := self.ListStorageAccounts()
   272  	if err != nil {
   273  		return nil, errors.Wrapf(err, "ListStorageAccounts")
   274  	}
   275  	for i := 0; i < len(accounts); i++ {
   276  		for k, v := range accounts[i].Tags {
   277  			if k == "id" && v == storageAccount {
   278  				accounts[i].region = self
   279  				return &accounts[i], nil
   280  			}
   281  		}
   282  	}
   283  	return nil, cloudprovider.ErrNotFound
   284  }
   285  
   286  func (self *SRegion) GetStorageAccountDetail(accountId string) (*SStorageAccount, error) {
   287  	account := SStorageAccount{region: self}
   288  	err := self.get(accountId, url.Values{}, &account)
   289  	if err != nil {
   290  		return nil, err
   291  	}
   292  	return &account, nil
   293  }
   294  
   295  type AccountKeys struct {
   296  	KeyName     string
   297  	Permissions string
   298  	Value       string
   299  }
   300  
   301  func (self *SRegion) GetStorageAccountKey(accountId string) (string, error) {
   302  	body, err := self.perform(accountId, "listKeys", nil)
   303  	if err != nil {
   304  		return "", err
   305  	}
   306  	if body.Contains("keys") {
   307  		keys := []AccountKeys{}
   308  		err = body.Unmarshal(&keys, "keys")
   309  		if err != nil {
   310  			return "", err
   311  		}
   312  		for _, key := range keys {
   313  			if strings.ToLower(key.Permissions) == "full" {
   314  				return key.Value, nil
   315  			}
   316  		}
   317  		return "", fmt.Errorf("not find storageaccount %s key", accountId)
   318  	}
   319  	return body.GetString("primaryKey")
   320  }
   321  
   322  func (self *SRegion) DeleteStorageAccount(accountId string) error {
   323  	return self.del(accountId)
   324  }
   325  
   326  func (self *SRegion) ListClassicStorageAccounts() ([]SStorageAccount, error) {
   327  	accounts := make([]SStorageAccount, 0)
   328  	err := self.list("Microsoft.ClassicStorage/storageAccounts", url.Values{}, &accounts)
   329  	if err != nil {
   330  		return nil, err
   331  	}
   332  	return accounts, nil
   333  }
   334  
   335  func (self *SRegion) GetClassicStorageAccount(id string) (*SStorageAccount, error) {
   336  	account := &SStorageAccount{}
   337  	err := self.get(id, url.Values{}, account)
   338  	if err != nil {
   339  		return nil, errors.Wrapf(err, "get(%s)", id)
   340  	}
   341  	return account, nil
   342  }
   343  
   344  func (self *SStorageAccount) GetAccountKey() (accountKey string, err error) {
   345  	if len(self.accountKey) > 0 {
   346  		return self.accountKey, nil
   347  	}
   348  	self.accountKey, err = self.region.GetStorageAccountKey(self.ID)
   349  	return self.accountKey, err
   350  }
   351  
   352  func (self *SStorageAccount) getBlobServiceClient() (*storage.BlobStorageClient, error) {
   353  	accessKey, err := self.GetAccountKey()
   354  	if err != nil {
   355  		return nil, err
   356  	}
   357  	env, err := azureenv.EnvironmentFromName(self.region.client.envName)
   358  	if err != nil {
   359  		return nil, errors.Wrapf(err, "azureenv.EnvironmentFromName(%s)", self.region.client.envName)
   360  	}
   361  	client, err := storage.NewBasicClientOnSovereignCloud(self.Name, accessKey, env)
   362  	if err != nil {
   363  		return nil, err
   364  	}
   365  	srv := client.GetBlobService()
   366  	return &srv, nil
   367  }
   368  
   369  func (self *SStorageAccount) CreateContainer(containerName string) (*SContainer, error) {
   370  	blobService, err := self.getBlobServiceClient()
   371  	if err != nil {
   372  		return nil, errors.Wrap(err, "getBlobServiceClient")
   373  	}
   374  	containerRef := blobService.GetContainerReference(containerName)
   375  	err = containerRef.Create(&storage.CreateContainerOptions{})
   376  	if err != nil {
   377  		return nil, errors.Wrap(err, "Create")
   378  	}
   379  	container := SContainer{
   380  		storageaccount: self,
   381  		Name:           containerName,
   382  	}
   383  	return &container, nil
   384  }
   385  
   386  func (self *SStorageAccount) GetContainers() ([]SContainer, error) {
   387  	blobService, err := self.getBlobServiceClient()
   388  	if err != nil {
   389  		return nil, errors.Wrap(err, "getBlobServiceClient")
   390  	}
   391  	result, err := blobService.ListContainers(storage.ListContainersParameters{})
   392  	if err != nil {
   393  		return nil, err
   394  	}
   395  	containers := []SContainer{}
   396  	err = jsonutils.Update(&containers, result.Containers)
   397  	if err != nil {
   398  		return nil, err
   399  	}
   400  	for i := 0; i < len(containers); i++ {
   401  		containers[i].storageaccount = self
   402  	}
   403  	return containers, nil
   404  }
   405  
   406  func (self *SStorageAccount) GetContainer(name string) (*SContainer, error) {
   407  	containers, err := self.GetContainers()
   408  	if err != nil {
   409  		return nil, err
   410  	}
   411  	for i := range containers {
   412  		if containers[i].Name == name {
   413  			return &containers[i], nil
   414  		}
   415  	}
   416  	return nil, cloudprovider.ErrNotFound
   417  }
   418  
   419  func (self *SContainer) ListAllFiles(include *storage.IncludeBlobDataset) ([]storage.Blob, error) {
   420  	blobs := make([]storage.Blob, 0)
   421  	var marker string
   422  	for {
   423  		result, err := self.ListFiles("", marker, "", 5000, include)
   424  		if err != nil {
   425  			return nil, errors.Wrap(err, "ListFiles")
   426  		}
   427  		if len(result.Blobs) > 0 {
   428  			blobs = append(blobs, result.Blobs...)
   429  		}
   430  		if len(result.NextMarker) == 0 {
   431  			break
   432  		} else {
   433  			marker = result.NextMarker
   434  		}
   435  	}
   436  	return blobs, nil
   437  }
   438  
   439  func (self *SContainer) ListFiles(prefix string, marker string, delimiter string, maxCount int, include *storage.IncludeBlobDataset) (storage.BlobListResponse, error) {
   440  	var result storage.BlobListResponse
   441  	blobService, err := self.storageaccount.getBlobServiceClient()
   442  	if err != nil {
   443  		return result, errors.Wrap(err, "getBlobServiceClient")
   444  	}
   445  	params := storage.ListBlobsParameters{Include: include}
   446  	if len(prefix) > 0 {
   447  		params.Prefix = prefix
   448  	}
   449  	if len(marker) > 0 {
   450  		params.Marker = marker
   451  	}
   452  	if len(delimiter) > 0 {
   453  		params.Delimiter = delimiter
   454  	}
   455  	if maxCount > 0 {
   456  		params.MaxResults = uint(maxCount)
   457  	}
   458  	result, err = blobService.GetContainerReference(self.Name).ListBlobs(params)
   459  	if err != nil {
   460  		return result, err
   461  	}
   462  	return result, nil
   463  }
   464  
   465  func (self *SContainer) getContainerRef() (*storage.Container, error) {
   466  	blobService, err := self.storageaccount.getBlobServiceClient()
   467  	if err != nil {
   468  		return nil, errors.Wrap(err, "getBlobServiceClient")
   469  	}
   470  	return blobService.GetContainerReference(self.Name), nil
   471  }
   472  
   473  func (self *SContainer) Delete(fileName string) error {
   474  	containerRef, err := self.getContainerRef()
   475  	if err != nil {
   476  		return err
   477  	}
   478  	blobRef := containerRef.GetBlobReference(fileName)
   479  	_, err = blobRef.DeleteIfExists(&storage.DeleteBlobOptions{})
   480  	return err
   481  }
   482  
   483  func (self *SContainer) CopySnapshot(snapshotId, fileName string) (*storage.Blob, error) {
   484  	containerRef, err := self.getContainerRef()
   485  	if err != nil {
   486  		return nil, err
   487  	}
   488  	blobRef := containerRef.GetBlobReference(fileName)
   489  	uri, err := self.storageaccount.region.GrantAccessSnapshot(snapshotId)
   490  	if err != nil {
   491  		return nil, err
   492  	}
   493  	err = blobRef.Copy(uri, &storage.CopyOptions{})
   494  	if err != nil {
   495  		return nil, err
   496  	}
   497  	return blobRef, blobRef.GetProperties(&storage.GetBlobPropertiesOptions{})
   498  }
   499  
   500  func setBlobRefMeta(blobRef *storage.Blob, meta http.Header) (bool, bool) {
   501  	propChanged := false
   502  	metaChanged := false
   503  	for k, v := range meta {
   504  		if len(v) == 0 || len(v[0]) == 0 {
   505  			continue
   506  		}
   507  		switch http.CanonicalHeaderKey(k) {
   508  		case cloudprovider.META_HEADER_CACHE_CONTROL:
   509  			blobRef.Properties.CacheControl = v[0]
   510  			propChanged = true
   511  		case cloudprovider.META_HEADER_CONTENT_TYPE:
   512  			blobRef.Properties.ContentType = v[0]
   513  			propChanged = true
   514  		case cloudprovider.META_HEADER_CONTENT_MD5:
   515  			blobRef.Properties.ContentMD5 = v[0]
   516  			propChanged = true
   517  		case cloudprovider.META_HEADER_CONTENT_ENCODING:
   518  			blobRef.Properties.ContentEncoding = v[0]
   519  			propChanged = true
   520  		case cloudprovider.META_HEADER_CONTENT_LANGUAGE:
   521  			blobRef.Properties.ContentLanguage = v[0]
   522  			propChanged = true
   523  		case cloudprovider.META_HEADER_CONTENT_DISPOSITION:
   524  			blobRef.Properties.ContentDisposition = v[0]
   525  			propChanged = true
   526  		default:
   527  			if blobRef.Metadata == nil {
   528  				blobRef.Metadata = storage.BlobMetadata{}
   529  			}
   530  			blobRef.Metadata[k] = v[0]
   531  			metaChanged = true
   532  		}
   533  	}
   534  	return propChanged, metaChanged
   535  }
   536  
   537  func (self *SContainer) UploadStream(key string, reader io.Reader, meta http.Header) error {
   538  	blobService, err := self.storageaccount.getBlobServiceClient()
   539  	if err != nil {
   540  		return errors.Wrap(err, "getBlobServiceClient")
   541  	}
   542  	containerRef := blobService.GetContainerReference(self.Name)
   543  	blobRef := containerRef.GetBlobReference(key)
   544  	blobRef.Properties.BlobType = storage.BlobTypeBlock
   545  	if meta != nil {
   546  		setBlobRefMeta(blobRef, meta)
   547  	}
   548  	return blobRef.CreateBlockBlobFromReader(reader, &storage.PutBlobOptions{})
   549  }
   550  
   551  func (self *SContainer) SignUrl(method string, key string, expire time.Duration) (string, error) {
   552  	blobService, err := self.storageaccount.getBlobServiceClient()
   553  	if err != nil {
   554  		return "", errors.Wrap(err, "getBlobServiceClient")
   555  	}
   556  	containerRef := blobService.GetContainerReference(self.Name)
   557  	sas := storage.BlobSASOptions{}
   558  	sas.Start = time.Now()
   559  	sas.Expiry = sas.Start.Add(expire)
   560  	sas.UseHTTPS = true
   561  	switch method {
   562  	case "GET":
   563  		sas.Read = true
   564  	case "PUT":
   565  		sas.Read = true
   566  		sas.Add = true
   567  		sas.Create = true
   568  		sas.Write = true
   569  	case "DELETE":
   570  		sas.Read = true
   571  		sas.Write = true
   572  		sas.Delete = true
   573  	default:
   574  		return "", errors.Error("unsupport method")
   575  	}
   576  	blobRef := containerRef.GetBlobReference(key)
   577  	return blobRef.GetSASURI(sas)
   578  }
   579  
   580  func (self *SContainer) UploadFile(filePath string, callback func(progress float32)) (string, error) {
   581  	blobService, err := self.storageaccount.getBlobServiceClient()
   582  	if err != nil {
   583  		return "", errors.Wrap(err, "getBlobServiceClient")
   584  	}
   585  	containerRef := blobService.GetContainerReference(self.Name)
   586  
   587  	err = ensureVHDSanity(filePath)
   588  	if err != nil {
   589  		return "", err
   590  	}
   591  	diskStream, err := diskstream.CreateNewDiskStream(filePath)
   592  	if err != nil {
   593  		return "", err
   594  	}
   595  	defer diskStream.Close()
   596  	blobName := path.Base(filePath)
   597  	blobRef := containerRef.GetBlobReference(blobName)
   598  	blobRef.Properties.ContentLength = diskStream.GetSize()
   599  	err = blobRef.PutPageBlob(&storage.PutBlobOptions{})
   600  	if err != nil {
   601  		return "", err
   602  	}
   603  	var rangesToSkip []*common.IndexRange
   604  	uploadableRanges, err := LocateUploadableRanges(diskStream, rangesToSkip, DefaultReadBlockSize)
   605  	if err != nil {
   606  		return "", err
   607  	}
   608  	uploadableRanges, err = DetectEmptyRanges(diskStream, uploadableRanges)
   609  	if err != nil {
   610  		return "", err
   611  	}
   612  
   613  	cxt := &DiskUploadContext{
   614  		VhdStream:             diskStream,
   615  		UploadableRanges:      uploadableRanges,
   616  		AlreadyProcessedBytes: common.TotalRangeLength(rangesToSkip),
   617  		BlobServiceClient:     *blobService,
   618  		ContainerName:         self.Name,
   619  		BlobName:              blobName,
   620  		Parallelism:           3,
   621  		Resume:                false,
   622  		MD5Hash:               []byte(""), //localMetaData.FileMetaData.MD5Hash,
   623  	}
   624  
   625  	if err := Upload(cxt, callback); err != nil {
   626  		return "", err
   627  	}
   628  	return blobRef.GetURL(), nil
   629  }
   630  
   631  func (self *SContainer) getAcl() cloudprovider.TBucketACLType {
   632  	acl := cloudprovider.ACLPrivate
   633  	containerRef, err := self.getContainerRef()
   634  	if err != nil {
   635  		log.Errorf("getContainerRef fail %s", err)
   636  		return acl
   637  	}
   638  	output, err := containerRef.GetPermissions(nil)
   639  	if err != nil {
   640  		log.Errorf("containerRef.GetPermissions fail %s", err)
   641  		return acl
   642  	}
   643  	switch output.AccessType {
   644  	case storage.ContainerAccessTypePrivate:
   645  		acl = cloudprovider.ACLPrivate
   646  	case storage.ContainerAccessTypeContainer:
   647  		acl = cloudprovider.ACLPublicRead
   648  	}
   649  	return acl
   650  }
   651  
   652  func (self *SContainer) setAcl(aclStr cloudprovider.TBucketACLType) error {
   653  	perm := storage.ContainerPermissions{}
   654  	switch aclStr {
   655  	case cloudprovider.ACLPublicRead:
   656  		perm.AccessType = storage.ContainerAccessTypeContainer
   657  	case cloudprovider.ACLPrivate:
   658  		perm.AccessType = storage.ContainerAccessTypePrivate
   659  	default:
   660  		return errors.Error("unsupported ACL:" + string(aclStr))
   661  	}
   662  	containerRef, err := self.getContainerRef()
   663  	if err != nil {
   664  		return errors.Wrap(err, "getContainerRef")
   665  	}
   666  	err = containerRef.SetPermissions(perm, nil)
   667  	if err != nil {
   668  		return errors.Wrap(err, "SetPermissions")
   669  	}
   670  	return nil
   671  }
   672  
   673  func (self *SStorageAccount) UploadFile(containerName string, filePath string, callback func(progress float32)) (string, error) {
   674  	container, err := self.getOrCreateContainer(containerName, true)
   675  	if err != nil {
   676  		return "", errors.Wrap(err, "getOrCreateContainer")
   677  	}
   678  	return container.UploadFile(filePath, callback)
   679  }
   680  
   681  func (self *SStorageAccount) getOrCreateContainer(containerName string, create bool) (*SContainer, error) {
   682  	containers, err := self.GetContainers()
   683  	if err != nil {
   684  		return nil, errors.Wrap(err, "GetContainers")
   685  	}
   686  	var container *SContainer
   687  	find := false
   688  	for i := 0; i < len(containers); i++ {
   689  		if containers[i].Name == containerName {
   690  			container = &containers[i]
   691  			find = true
   692  			break
   693  		}
   694  	}
   695  	if !find {
   696  		if !create {
   697  			return nil, cloudprovider.ErrNotFound
   698  		}
   699  		container, err = self.CreateContainer(containerName)
   700  		if err != nil {
   701  			return nil, errors.Wrap(err, "CreateContainer")
   702  		}
   703  	}
   704  	return container, nil
   705  }
   706  
   707  func (self *SStorageAccount) UploadStream(containerName string, key string, reader io.Reader, meta http.Header) error {
   708  	container, err := self.getOrCreateContainer(containerName, true)
   709  	if err != nil {
   710  		return errors.Wrap(err, "getOrCreateContainer")
   711  	}
   712  	return container.UploadStream(key, reader, meta)
   713  }
   714  
   715  func (b *SStorageAccount) MaxPartSizeBytes() int64 {
   716  	return 100 * 1000 * 1000
   717  }
   718  
   719  func (b *SStorageAccount) MaxPartCount() int {
   720  	return 50000
   721  }
   722  
   723  func (b *SStorageAccount) GetTags() (map[string]string, error) {
   724  	return b.Tags, nil
   725  }
   726  
   727  func (b *SStorageAccount) GetProjectId() string {
   728  	return getResourceGroup(b.ID)
   729  }
   730  
   731  func (b *SStorageAccount) GetGlobalId() string {
   732  	return b.Name
   733  }
   734  
   735  func (b *SStorageAccount) GetName() string {
   736  	return b.Name
   737  }
   738  
   739  func (b *SStorageAccount) GetLocation() string {
   740  	return b.Location
   741  }
   742  
   743  func (b *SStorageAccount) GetIRegion() cloudprovider.ICloudRegion {
   744  	return b.region
   745  }
   746  
   747  func (b *SStorageAccount) GetCreatedAt() time.Time {
   748  	return time.Time{}
   749  }
   750  
   751  func (b *SStorageAccount) GetStorageClass() string {
   752  	return b.Sku.Tier
   753  }
   754  
   755  // get the common ACL of all containers
   756  func (b *SStorageAccount) GetAcl() cloudprovider.TBucketACLType {
   757  	acl := cloudprovider.ACLPrivate
   758  	containers, err := b.GetContainers()
   759  	if err != nil {
   760  		log.Errorf("GetContainers error %s", err)
   761  		return acl
   762  	}
   763  	for i := range containers {
   764  		aclC := containers[i].getAcl()
   765  		if i == 0 {
   766  			if aclC != acl {
   767  				acl = aclC
   768  			}
   769  		} else if aclC != acl {
   770  			acl = cloudprovider.ACLPrivate
   771  		}
   772  	}
   773  	return acl
   774  }
   775  
   776  func (b *SStorageAccount) SetAcl(aclStr cloudprovider.TBucketACLType) error {
   777  	containers, err := b.GetContainers()
   778  	if err != nil {
   779  		return errors.Wrap(err, "GetContainers")
   780  	}
   781  	for i := range containers {
   782  		err = containers[i].setAcl(aclStr)
   783  		if err != nil {
   784  			return errors.Wrap(err, "containers.setAcl")
   785  		}
   786  	}
   787  	return nil
   788  }
   789  
   790  func getDesc(prefix, name string) string {
   791  	if len(prefix) > 0 {
   792  		return prefix + "-" + name
   793  	} else {
   794  		return name
   795  	}
   796  }
   797  
   798  func (ep SStorageEndpoints) getUrls(prefix string) []cloudprovider.SBucketAccessUrl {
   799  	ret := make([]cloudprovider.SBucketAccessUrl, 0)
   800  	if len(ep.Blob) > 0 {
   801  		ret = append(ret, cloudprovider.SBucketAccessUrl{
   802  			Url:         ep.Blob,
   803  			Description: getDesc(prefix, "blob"),
   804  			Primary:     true,
   805  		})
   806  	}
   807  	if len(ep.Queue) > 0 {
   808  		ret = append(ret, cloudprovider.SBucketAccessUrl{
   809  			Url:         ep.Queue,
   810  			Description: getDesc(prefix, "queue"),
   811  		})
   812  	}
   813  	if len(ep.Table) > 0 {
   814  		ret = append(ret, cloudprovider.SBucketAccessUrl{
   815  			Url:         ep.Table,
   816  			Description: getDesc(prefix, "table"),
   817  		})
   818  	}
   819  	if len(ep.File) > 0 {
   820  		ret = append(ret, cloudprovider.SBucketAccessUrl{
   821  			Url:         ep.File,
   822  			Description: getDesc(prefix, "file"),
   823  		})
   824  	}
   825  	return ret
   826  }
   827  
   828  func (b *SStorageAccount) GetAccessUrls() []cloudprovider.SBucketAccessUrl {
   829  	primary := b.Properties.PrimaryEndpoints.getUrls("")
   830  	secondary := b.Properties.SecondaryEndpoints.getUrls("secondary")
   831  	if len(secondary) > 0 {
   832  		primary = append(primary, secondary...)
   833  	}
   834  	return primary
   835  }
   836  
   837  func (b *SStorageAccount) GetStats() cloudprovider.SBucketStats {
   838  	stats, _ := cloudprovider.GetIBucketStats(b)
   839  	return stats
   840  }
   841  
   842  func getBlobRefMeta(blob *storage.Blob) http.Header {
   843  	meta := http.Header{}
   844  	for k, v := range blob.Metadata {
   845  		meta.Add(k, v)
   846  	}
   847  	if len(blob.Properties.CacheControl) > 0 {
   848  		meta.Set(cloudprovider.META_HEADER_CACHE_CONTROL, blob.Properties.CacheControl)
   849  	}
   850  	if len(blob.Properties.ContentType) > 0 {
   851  		meta.Set(cloudprovider.META_HEADER_CONTENT_TYPE, blob.Properties.ContentType)
   852  	}
   853  	if len(blob.Properties.ContentDisposition) > 0 {
   854  		meta.Set(cloudprovider.META_HEADER_CONTENT_DISPOSITION, blob.Properties.ContentDisposition)
   855  	}
   856  	if len(blob.Properties.ContentLanguage) > 0 {
   857  		meta.Set(cloudprovider.META_HEADER_CONTENT_LANGUAGE, blob.Properties.ContentLanguage)
   858  	}
   859  	if len(blob.Properties.ContentEncoding) > 0 {
   860  		meta.Set(cloudprovider.META_HEADER_CONTENT_ENCODING, blob.Properties.ContentEncoding)
   861  	}
   862  	if len(blob.Properties.ContentMD5) > 0 {
   863  		meta.Set(cloudprovider.META_HEADER_CONTENT_MD5, blob.Properties.ContentMD5)
   864  	}
   865  	return meta
   866  }
   867  
   868  func (b *SStorageAccount) ListObjects(prefix string, marker string, delimiter string, maxCount int) (cloudprovider.SListObjectResult, error) {
   869  	result := cloudprovider.SListObjectResult{}
   870  	containers, err := b.GetContainers()
   871  	if err != nil {
   872  		return result, errors.Wrap(err, "GetContainers")
   873  	}
   874  	result.Objects = make([]cloudprovider.ICloudObject, 0)
   875  	result.CommonPrefixes = make([]cloudprovider.ICloudObject, 0)
   876  	for i := 0; i < len(containers); i += 1 {
   877  		container := containers[i]
   878  		matchLen := len(container.Name)
   879  		if matchLen > len(prefix) {
   880  			matchLen = len(prefix)
   881  		}
   882  		var subMarker string
   883  		if len(marker) < len(container.Name) {
   884  			if marker > container.Name {
   885  				continue
   886  			}
   887  			subMarker = ""
   888  		} else {
   889  			containerMarker := marker[:len(container.Name)]
   890  			if containerMarker > container.Name {
   891  				continue
   892  			}
   893  			subMarker = marker[len(container.Name)+1:]
   894  		}
   895  		if marker > container.Name {
   896  			continue
   897  		}
   898  		if maxCount <= 0 {
   899  			break
   900  		}
   901  		// container name matches prefix
   902  		if container.Name[:matchLen] == prefix[:matchLen] && (len(prefix) <= len(container.Name) || strings.HasPrefix(prefix, container.Name+"/")) {
   903  			if delimiter == "/" && len(prefix) == 0 {
   904  				// populate CommonPrefixes
   905  				o := &SObject{
   906  					container: &container,
   907  					SBaseCloudObject: cloudprovider.SBaseCloudObject{
   908  						Key: container.Name + "/",
   909  					},
   910  				}
   911  				result.CommonPrefixes = append(result.CommonPrefixes, o)
   912  				maxCount -= 1
   913  			} else if delimiter == "/" && prefix == container.Name+delimiter {
   914  				// do nothing
   915  			} else if len(prefix) <= len(container.Name)+1 {
   916  				// returns contain names only
   917  				o := &SObject{
   918  					container: &container,
   919  					SBaseCloudObject: cloudprovider.SBaseCloudObject{
   920  						Key: container.Name + "/",
   921  					},
   922  				}
   923  				result.Objects = append(result.Objects, o)
   924  				maxCount -= 1
   925  			}
   926  			if delimiter == "" || len(prefix) >= len(container.Name)+1 {
   927  				subPrefix := ""
   928  				if len(prefix) >= len(container.Name)+1 {
   929  					subPrefix = prefix[len(container.Name)+1:]
   930  				}
   931  				oResult, err := container.ListFiles(subPrefix, subMarker, delimiter, maxCount, nil)
   932  				if err != nil {
   933  					return result, errors.Wrap(err, "ListFiles")
   934  				}
   935  				for i := range oResult.Blobs {
   936  					blob := oResult.Blobs[i]
   937  					o := &SObject{
   938  						container: &container,
   939  						SBaseCloudObject: cloudprovider.SBaseCloudObject{
   940  							Key:          container.Name + "/" + blob.Name,
   941  							SizeBytes:    blob.Properties.ContentLength,
   942  							StorageClass: "",
   943  							ETag:         blob.Properties.Etag,
   944  							LastModified: time.Time(blob.Properties.LastModified),
   945  						},
   946  					}
   947  					result.Objects = append(result.Objects, o)
   948  					maxCount -= 1
   949  					if maxCount == 0 {
   950  						break
   951  					}
   952  					result.NextMarker = blob.Name
   953  				}
   954  				for i := range oResult.BlobPrefixes {
   955  					o := &SObject{
   956  						container: &container,
   957  						SBaseCloudObject: cloudprovider.SBaseCloudObject{
   958  							Key: container.Name + "/" + oResult.BlobPrefixes[i],
   959  						},
   960  					}
   961  					result.CommonPrefixes = append(result.CommonPrefixes, o)
   962  					maxCount -= 1
   963  				}
   964  				if len(oResult.NextMarker) > 0 {
   965  					result.NextMarker = container.Name + "/" + oResult.NextMarker
   966  					result.IsTruncated = true
   967  					break
   968  				}
   969  			}
   970  		}
   971  	}
   972  	return result, nil
   973  }
   974  
   975  func splitKey(key string) (string, string, error) {
   976  	slashPos := strings.IndexByte(key, '/')
   977  	if slashPos <= 0 {
   978  		return "", "", errors.Wrap(httperrors.ErrForbidden, "cannot put object to root")
   979  	}
   980  	containerName := key[:slashPos]
   981  	key = key[slashPos+1:]
   982  	return containerName, key, nil
   983  }
   984  
   985  func splitKeyAndBlob(path string) (string, string, error) {
   986  	containerName, key, err := splitKey(path)
   987  	if err != nil {
   988  		return "", "", errors.Wrapf(err, "splitKey: %s", path)
   989  	}
   990  	if len(key) == 0 {
   991  		return "", "", errors.Error("empty blob path")
   992  	}
   993  	return containerName, key, nil
   994  }
   995  
   996  func (b *SStorageAccount) PutObject(ctx context.Context, key string, reader io.Reader, sizeBytes int64, cannedAcl cloudprovider.TBucketACLType, storageClassStr string, meta http.Header) error {
   997  	containerName, blob, err := splitKey(key)
   998  	if err != nil {
   999  		return errors.Wrap(err, "splitKey")
  1000  	}
  1001  	if len(blob) > 0 {
  1002  		// put blob
  1003  		err = b.UploadStream(containerName, blob, reader, meta)
  1004  		if err != nil {
  1005  			return errors.Wrap(err, "UploadStream")
  1006  		}
  1007  	} else {
  1008  		// create container
  1009  		if sizeBytes > 0 {
  1010  			return errors.Wrap(httperrors.ErrForbidden, "not allow to create blob outsize of container")
  1011  		}
  1012  		_, err = b.getOrCreateContainer(containerName, true)
  1013  		if err != nil {
  1014  			return errors.Wrap(err, "getOrCreateContainer")
  1015  		}
  1016  	}
  1017  	return nil
  1018  }
  1019  
  1020  func (b *SStorageAccount) NewMultipartUpload(ctx context.Context, key string, cannedAcl cloudprovider.TBucketACLType, storageClassStr string, meta http.Header) (string, error) {
  1021  	containerName, blob, err := splitKeyAndBlob(key)
  1022  	if err != nil {
  1023  		return "", errors.Wrap(err, "splitKey")
  1024  	}
  1025  	container, err := b.getOrCreateContainer(containerName, true)
  1026  	if err != nil {
  1027  		return "", errors.Wrap(err, "getOrCreateContainer")
  1028  	}
  1029  	containerRef, err := container.getContainerRef()
  1030  	if err != nil {
  1031  		return "", errors.Wrap(err, "getContainerRef")
  1032  	}
  1033  	blobRef := containerRef.GetBlobReference(blob)
  1034  	if meta != nil {
  1035  		setBlobRefMeta(blobRef, meta)
  1036  	}
  1037  	err = blobRef.CreateBlockBlob(&storage.PutBlobOptions{})
  1038  	if err != nil {
  1039  		return "", errors.Wrap(err, "CreateBlockBlob")
  1040  	}
  1041  	uploadId, err := blobRef.AcquireLease(-1, "", nil)
  1042  	if err != nil {
  1043  		return "", errors.Wrap(err, "blobRef.AcquireLease")
  1044  	}
  1045  
  1046  	return uploadId, nil
  1047  }
  1048  
  1049  func partIndex2BlockId(partIndex int) string {
  1050  	return base64.URLEncoding.EncodeToString([]byte(strconv.FormatInt(int64(partIndex), 10)))
  1051  }
  1052  
  1053  func (b *SStorageAccount) UploadPart(ctx context.Context, key string, uploadId string, partIndex int, input io.Reader, partSize int64, offset, totalSize int64) (string, error) {
  1054  	containerName, blob, err := splitKeyAndBlob(key)
  1055  	if err != nil {
  1056  		return "", errors.Wrap(err, "splitKey")
  1057  	}
  1058  	container, err := b.getOrCreateContainer(containerName, true)
  1059  	if err != nil {
  1060  		return "", errors.Wrap(err, "getOrCreateContainer")
  1061  	}
  1062  	containerRef, err := container.getContainerRef()
  1063  	if err != nil {
  1064  		return "", errors.Wrap(err, "getContainerRef")
  1065  	}
  1066  	blobRef := containerRef.GetBlobReference(blob)
  1067  
  1068  	opts := &storage.PutBlockOptions{}
  1069  	opts.LeaseID = uploadId
  1070  
  1071  	blockId := partIndex2BlockId(partIndex)
  1072  	err = blobRef.PutBlockWithLength(blockId, uint64(partSize), input, opts)
  1073  	if err != nil {
  1074  		return "", errors.Wrap(err, "PutBlockWithLength")
  1075  	}
  1076  
  1077  	return blockId, nil
  1078  }
  1079  
  1080  func (b *SStorageAccount) CompleteMultipartUpload(ctx context.Context, key string, uploadId string, blockIds []string) error {
  1081  	containerName, blob, err := splitKeyAndBlob(key)
  1082  	if err != nil {
  1083  		return errors.Wrap(err, "splitKey")
  1084  	}
  1085  	container, err := b.getOrCreateContainer(containerName, true)
  1086  	if err != nil {
  1087  		return errors.Wrap(err, "getOrCreateContainer")
  1088  	}
  1089  	containerRef, err := container.getContainerRef()
  1090  	if err != nil {
  1091  		return errors.Wrap(err, "getContainerRef")
  1092  	}
  1093  	blobRef := containerRef.GetBlobReference(blob)
  1094  
  1095  	blocks := make([]storage.Block, len(blockIds))
  1096  	for i := range blockIds {
  1097  		blocks[i] = storage.Block{
  1098  			ID:     blockIds[i],
  1099  			Status: storage.BlockStatusLatest,
  1100  		}
  1101  	}
  1102  	opts := &storage.PutBlockListOptions{}
  1103  	opts.LeaseID = uploadId
  1104  	err = blobRef.PutBlockList(blocks, opts)
  1105  	if err != nil {
  1106  		return errors.Wrap(err, "PutBlockList")
  1107  	}
  1108  
  1109  	err = blobRef.ReleaseLease(uploadId, nil)
  1110  	if err != nil {
  1111  		return errors.Wrap(err, "ReleaseLease")
  1112  	}
  1113  
  1114  	return nil
  1115  }
  1116  
  1117  func (b *SStorageAccount) AbortMultipartUpload(ctx context.Context, key string, uploadId string) error {
  1118  	containerName, blob, err := splitKeyAndBlob(key)
  1119  	if err != nil {
  1120  		return errors.Wrap(err, "splitKey")
  1121  	}
  1122  	container, err := b.getOrCreateContainer(containerName, false)
  1123  	if err != nil {
  1124  		if errors.Cause(err) == cloudprovider.ErrNotFound {
  1125  			return nil
  1126  		}
  1127  		return errors.Wrap(err, "getOrCreateContainer")
  1128  	}
  1129  	containerRef, err := container.getContainerRef()
  1130  	if err != nil {
  1131  		return errors.Wrap(err, "getContainerRef")
  1132  	}
  1133  
  1134  	blobRef := containerRef.GetBlobReference(blob)
  1135  	err = blobRef.ReleaseLease(uploadId, nil)
  1136  	if err != nil {
  1137  		return errors.Wrap(err, "ReleaseLease")
  1138  	}
  1139  
  1140  	opts := &storage.DeleteBlobOptions{}
  1141  	deleteSnapshots := true
  1142  	opts.DeleteSnapshots = &deleteSnapshots
  1143  	_, err = blobRef.DeleteIfExists(opts)
  1144  	if err != nil {
  1145  		return errors.Wrap(err, "DeleteIfExists")
  1146  	}
  1147  
  1148  	return nil
  1149  }
  1150  
  1151  func (b *SStorageAccount) DeleteObject(ctx context.Context, key string) error {
  1152  	containerName, blob, err := splitKey(key)
  1153  	if err != nil {
  1154  		return errors.Wrap(err, "splitKey")
  1155  	}
  1156  	blobService, err := b.getBlobServiceClient()
  1157  	if err != nil {
  1158  		return errors.Wrap(err, "getBlobServiceClient")
  1159  	}
  1160  	containerRef := blobService.GetContainerReference(containerName)
  1161  	if len(blob) > 0 {
  1162  		// delete object
  1163  		blobRef := containerRef.GetBlobReference(blob)
  1164  		_, err = blobRef.DeleteIfExists(nil)
  1165  		if err != nil {
  1166  			return errors.Wrap(err, "blobRef.DeleteIfExists")
  1167  		}
  1168  	} else {
  1169  		// delete container
  1170  		_, err = containerRef.DeleteIfExists(nil)
  1171  		if err != nil {
  1172  			return errors.Wrap(err, "containerRef.DeleteIfExists")
  1173  		}
  1174  	}
  1175  	return nil
  1176  }
  1177  
  1178  func (b *SStorageAccount) GetTempUrl(method string, key string, expire time.Duration) (string, error) {
  1179  	containerName, blob, err := splitKeyAndBlob(key)
  1180  	if err != nil {
  1181  		return "", errors.Wrap(err, "splitKey")
  1182  	}
  1183  	container, err := b.getOrCreateContainer(containerName, false)
  1184  	if err != nil {
  1185  		return "", errors.Wrap(err, "getOrCreateContainer")
  1186  	}
  1187  	return container.SignUrl(method, blob, expire)
  1188  }
  1189  
  1190  func (b *SStorageAccount) CopyObject(ctx context.Context, destKey string, srcBucket, srcKey string, cannedAcl cloudprovider.TBucketACLType, storageClassStr string, meta http.Header) error {
  1191  	srcIBucket, err := b.region.GetIBucketByName(srcBucket)
  1192  	if err != nil {
  1193  		return errors.Wrap(err, "GetIBucketByName")
  1194  	}
  1195  	srcAccount := srcIBucket.(*SStorageAccount)
  1196  	srcContName, srcBlob, err := splitKeyAndBlob(srcKey)
  1197  	if err != nil {
  1198  		return errors.Wrap(err, "src splitKey")
  1199  	}
  1200  	srcCont, err := srcAccount.getOrCreateContainer(srcContName, false)
  1201  	if err != nil {
  1202  		return errors.Wrap(err, "src getOrCreateContainer")
  1203  	}
  1204  	srcContRef, err := srcCont.getContainerRef()
  1205  	if err != nil {
  1206  		return errors.Wrap(err, "src getContainerRef")
  1207  	}
  1208  	srcBlobRef := srcContRef.GetBlobReference(srcBlob)
  1209  
  1210  	containerName, blobName, err := splitKeyAndBlob(destKey)
  1211  	if err != nil {
  1212  		return errors.Wrap(err, "dest splitKey")
  1213  	}
  1214  	container, err := b.getOrCreateContainer(containerName, true)
  1215  	if err != nil {
  1216  		return errors.Wrap(err, "dest getOrCreateContainer")
  1217  	}
  1218  	containerRef, err := container.getContainerRef()
  1219  	if err != nil {
  1220  		return errors.Wrap(err, "dest getContainerRef")
  1221  	}
  1222  	blobRef := containerRef.GetBlobReference(blobName)
  1223  	if meta != nil {
  1224  		setBlobRefMeta(blobRef, meta)
  1225  	}
  1226  	opts := &storage.CopyOptions{}
  1227  	err = blobRef.Copy(srcBlobRef.GetURL(), opts)
  1228  	if err != nil {
  1229  		return errors.Wrap(err, "blboRef.Copy")
  1230  	}
  1231  	return nil
  1232  }
  1233  
  1234  func (b *SStorageAccount) GetObject(ctx context.Context, key string, rangeOpt *cloudprovider.SGetObjectRange) (io.ReadCloser, error) {
  1235  	containerName, blob, err := splitKeyAndBlob(key)
  1236  	if err != nil {
  1237  		return nil, errors.Wrap(err, "splitKey")
  1238  	}
  1239  	container, err := b.getOrCreateContainer(containerName, false)
  1240  	if err != nil {
  1241  		return nil, errors.Wrap(err, "getOrCreateContainer")
  1242  	}
  1243  	containerRef, err := container.getContainerRef()
  1244  	if err != nil {
  1245  		return nil, errors.Wrap(err, "container.getContainerRef")
  1246  	}
  1247  	blobRef := containerRef.GetBlobReference(blob)
  1248  	if rangeOpt != nil {
  1249  		opts := &storage.GetBlobRangeOptions{}
  1250  		opts.Range = &storage.BlobRange{
  1251  			Start: uint64(rangeOpt.Start),
  1252  			End:   uint64(rangeOpt.End),
  1253  		}
  1254  		return blobRef.GetRange(opts)
  1255  	} else {
  1256  		opts := &storage.GetBlobOptions{}
  1257  		return blobRef.Get(opts)
  1258  	}
  1259  }
  1260  
  1261  func (b *SStorageAccount) CopyPart(ctx context.Context, key string, uploadId string, partIndex int, srcBucket string, srcKey string, srcOffset int64, srcLength int64) (string, error) {
  1262  	srcIBucket, err := b.region.GetIBucketByName(srcBucket)
  1263  	if err != nil {
  1264  		return "", errors.Wrap(err, "GetIBucketByName")
  1265  	}
  1266  	srcAccount := srcIBucket.(*SStorageAccount)
  1267  	srcContName, srcBlob, err := splitKeyAndBlob(srcKey)
  1268  	if err != nil {
  1269  		return "", errors.Wrap(err, "src splitKey")
  1270  	}
  1271  	srcCont, err := srcAccount.getOrCreateContainer(srcContName, false)
  1272  	if err != nil {
  1273  		return "", errors.Wrap(err, "src getOrCreateContainer")
  1274  	}
  1275  	srcContRef, err := srcCont.getContainerRef()
  1276  	if err != nil {
  1277  		return "", errors.Wrap(err, "src getContainerRef")
  1278  	}
  1279  	srcBlobRef := srcContRef.GetBlobReference(srcBlob)
  1280  
  1281  	containerName, blob, err := splitKeyAndBlob(key)
  1282  	if err != nil {
  1283  		return "", errors.Wrap(err, "splitKey")
  1284  	}
  1285  	container, err := b.getOrCreateContainer(containerName, true)
  1286  	if err != nil {
  1287  		return "", errors.Wrap(err, "getOrCreateContainer")
  1288  	}
  1289  	containerRef, err := container.getContainerRef()
  1290  	if err != nil {
  1291  		return "", errors.Wrap(err, "getContainerRef")
  1292  	}
  1293  	blobRef := containerRef.GetBlobReference(blob)
  1294  
  1295  	opts := &storage.PutBlockFromURLOptions{}
  1296  	opts.LeaseID = uploadId
  1297  
  1298  	blockId := partIndex2BlockId(partIndex)
  1299  	err = blobRef.PutBlockFromURL(blockId, srcBlobRef.GetURL(), srcOffset, uint64(srcLength), opts)
  1300  	if err != nil {
  1301  		return "", errors.Wrap(err, "PutBlockFromUrl")
  1302  	}
  1303  
  1304  	return blockId, nil
  1305  }