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 }