yunion.io/x/cloudmux@v0.3.10-0-alpha.1/pkg/multicloud/aws/bucket.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 aws 16 17 import ( 18 "context" 19 "fmt" 20 "io" 21 "net/http" 22 "net/url" 23 "strconv" 24 "strings" 25 "time" 26 27 "github.com/aws/aws-sdk-go/aws/request" 28 "github.com/aws/aws-sdk-go/service/s3" 29 30 "yunion.io/x/jsonutils" 31 "yunion.io/x/log" 32 "yunion.io/x/pkg/errors" 33 "yunion.io/x/s3cli" 34 35 "yunion.io/x/cloudmux/pkg/cloudprovider" 36 "yunion.io/x/cloudmux/pkg/multicloud" 37 "yunion.io/x/onecloud/pkg/util/fileutils2" 38 ) 39 40 type SBucket struct { 41 multicloud.SBaseBucket 42 AwsTags 43 44 region *SRegion 45 46 Name string 47 CreationDate time.Time 48 Location string 49 50 acl cloudprovider.TBucketACLType 51 } 52 53 func (b *SBucket) GetProjectId() string { 54 return "" 55 } 56 57 func (b *SBucket) GetGlobalId() string { 58 return b.Name 59 } 60 61 func (b *SBucket) GetName() string { 62 return b.Name 63 } 64 65 func (b *SBucket) GetLocation() string { 66 return b.Location 67 } 68 69 func (b *SBucket) GetIRegion() cloudprovider.ICloudRegion { 70 return b.region 71 } 72 73 func (b *SBucket) GetCreatedAt() time.Time { 74 return b.CreationDate 75 } 76 77 func (b *SBucket) GetStorageClass() string { 78 return "" 79 } 80 81 func s3ToCannedAcl(acls []*s3.Grant) cloudprovider.TBucketACLType { 82 switch { 83 case len(acls) == 1: 84 if acls[0].Grantee.URI == nil && *acls[0].Permission == s3cli.PERMISSION_FULL_CONTROL { 85 return cloudprovider.ACLPrivate 86 } 87 case len(acls) == 2: 88 for _, g := range acls { 89 if *g.Grantee.Type == s3cli.GRANTEE_TYPE_GROUP && *g.Grantee.URI == s3cli.GRANTEE_GROUP_URI_AUTH_USERS && *g.Permission == s3cli.PERMISSION_READ { 90 return cloudprovider.ACLAuthRead 91 } 92 if *g.Grantee.Type == s3cli.GRANTEE_TYPE_GROUP && *g.Grantee.URI == s3cli.GRANTEE_GROUP_URI_ALL_USERS && *g.Permission == s3cli.PERMISSION_READ { 93 return cloudprovider.ACLPublicRead 94 } 95 } 96 case len(acls) == 3: 97 for _, g := range acls { 98 if *g.Grantee.Type == s3cli.GRANTEE_TYPE_GROUP && *g.Grantee.URI == s3cli.GRANTEE_GROUP_URI_ALL_USERS && *g.Permission == s3cli.PERMISSION_WRITE { 99 return cloudprovider.ACLPublicReadWrite 100 } 101 } 102 } 103 return cloudprovider.ACLUnknown 104 } 105 106 func (b *SBucket) GetAcl() cloudprovider.TBucketACLType { 107 acl := cloudprovider.ACLPrivate 108 s3cli, err := b.region.GetS3Client() 109 if err != nil { 110 log.Errorf("b.region.GetS3Client fail %s", err) 111 return acl 112 } 113 input := &s3.GetBucketAclInput{} 114 input.SetBucket(b.Name) 115 output, err := s3cli.GetBucketAcl(input) 116 if err != nil { 117 log.Errorf("s3cli.GetBucketAcl fail %s", err) 118 return acl 119 } 120 return s3ToCannedAcl(output.Grants) 121 } 122 123 func (b *SBucket) SetAcl(aclStr cloudprovider.TBucketACLType) error { 124 s3cli, err := b.region.GetS3Client() 125 if err != nil { 126 return errors.Wrap(err, "b.region.GetS3Client") 127 } 128 input := &s3.PutBucketAclInput{} 129 input.SetBucket(b.Name) 130 input.SetACL(string(aclStr)) 131 _, err = s3cli.PutBucketAcl(input) 132 if err != nil { 133 return errors.Wrap(err, "PutBucketAcl") 134 } 135 return nil 136 } 137 138 func (b *SBucket) GetAccessUrls() []cloudprovider.SBucketAccessUrl { 139 return []cloudprovider.SBucketAccessUrl{ 140 { 141 Url: fmt.Sprintf("https://%s.%s", b.Name, b.region.getS3Endpoint()), 142 Description: "bucket domain", 143 Primary: true, 144 }, 145 { 146 Url: fmt.Sprintf("https://%s/%s", b.region.getS3Endpoint(), b.Name), 147 Description: "s3 domain", 148 }, 149 } 150 } 151 152 func (b *SBucket) GetWebsiteUrl() string { 153 return fmt.Sprintf("http://%s.%s", b.Name, b.region.getS3WebsiteEndpoint()) 154 } 155 156 func (b *SBucket) GetStats() cloudprovider.SBucketStats { 157 stats, _ := cloudprovider.GetIBucketStats(b) 158 return stats 159 } 160 161 func (b *SBucket) ListObjects(prefix string, marker string, delimiter string, maxCount int) (cloudprovider.SListObjectResult, error) { 162 result := cloudprovider.SListObjectResult{} 163 s3cli, err := b.region.GetS3Client() 164 if err != nil { 165 return result, errors.Wrap(err, "GetS3Client") 166 } 167 input := &s3.ListObjectsInput{} 168 input.SetBucket(b.Name) 169 if len(prefix) > 0 { 170 input.SetPrefix(prefix) 171 } 172 if len(marker) > 0 { 173 input.SetMarker(marker) 174 } 175 if len(delimiter) > 0 { 176 input.SetDelimiter(delimiter) 177 } 178 if maxCount > 0 { 179 input.SetMaxKeys(int64(maxCount)) 180 } 181 oResult, err := s3cli.ListObjects(input) 182 if err != nil { 183 return result, errors.Wrap(err, "ListObjects") 184 } 185 result.Objects = make([]cloudprovider.ICloudObject, 0) 186 for _, object := range oResult.Contents { 187 obj := &SObject{ 188 bucket: b, 189 SBaseCloudObject: cloudprovider.SBaseCloudObject{ 190 StorageClass: *object.StorageClass, 191 Key: *object.Key, 192 SizeBytes: *object.Size, 193 ETag: *object.ETag, 194 LastModified: *object.LastModified, 195 }, 196 } 197 result.Objects = append(result.Objects, obj) 198 } 199 if oResult.CommonPrefixes != nil { 200 result.CommonPrefixes = make([]cloudprovider.ICloudObject, len(oResult.CommonPrefixes)) 201 for i, commPrefix := range oResult.CommonPrefixes { 202 result.CommonPrefixes[i] = &SObject{ 203 bucket: b, 204 SBaseCloudObject: cloudprovider.SBaseCloudObject{Key: *commPrefix.Prefix}, 205 } 206 } 207 } 208 if oResult.IsTruncated != nil { 209 result.IsTruncated = *oResult.IsTruncated 210 } 211 if oResult.NextMarker != nil { 212 result.NextMarker = *oResult.NextMarker 213 } 214 return result, nil 215 } 216 217 func (b *SBucket) PutObject(ctx context.Context, key string, body io.Reader, sizeBytes int64, cannedAcl cloudprovider.TBucketACLType, storageClassStr string, meta http.Header) error { 218 if sizeBytes < 0 { 219 return errors.Error("content length expected") 220 } 221 s3cli, err := b.region.GetS3Client() 222 if err != nil { 223 return errors.Wrap(err, "GetS3Client") 224 } 225 input := &s3.PutObjectInput{} 226 input.SetBucket(b.Name) 227 input.SetKey(key) 228 seeker, err := fileutils2.NewReadSeeker(body, sizeBytes) 229 if err != nil { 230 return errors.Wrap(err, "newFakeSeeker") 231 } 232 defer seeker.Close() 233 input.SetBody(seeker) 234 input.SetContentLength(sizeBytes) 235 if meta != nil { 236 metaHdr := make(map[string]*string) 237 for k, v := range meta { 238 if len(v) == 0 || len(v[0]) == 0 { 239 continue 240 } 241 switch http.CanonicalHeaderKey(k) { 242 case cloudprovider.META_HEADER_CACHE_CONTROL: 243 input.SetCacheControl(v[0]) 244 case cloudprovider.META_HEADER_CONTENT_TYPE: 245 input.SetContentType(v[0]) 246 case cloudprovider.META_HEADER_CONTENT_MD5: 247 input.SetContentMD5(v[0]) 248 case cloudprovider.META_HEADER_CONTENT_LANGUAGE: 249 input.SetContentLanguage(v[0]) 250 case cloudprovider.META_HEADER_CONTENT_ENCODING: 251 input.SetContentEncoding(v[0]) 252 case cloudprovider.META_HEADER_CONTENT_DISPOSITION: 253 input.SetContentDisposition(v[0]) 254 default: 255 metaHdr[k] = &v[0] 256 } 257 } 258 if len(metaHdr) > 0 { 259 input.SetMetadata(metaHdr) 260 } 261 } 262 if len(cannedAcl) == 0 { 263 cannedAcl = b.GetAcl() 264 } 265 input.SetACL(string(cannedAcl)) 266 if len(storageClassStr) > 0 { 267 input.SetStorageClass(storageClassStr) 268 } 269 _, err = s3cli.PutObjectWithContext(ctx, input) 270 if err != nil { 271 return errors.Wrap(err, "PutObjectWithContext") 272 } 273 return nil 274 } 275 276 func (b *SBucket) NewMultipartUpload(ctx context.Context, key string, cannedAcl cloudprovider.TBucketACLType, storageClassStr string, meta http.Header) (string, error) { 277 s3cli, err := b.region.GetS3Client() 278 if err != nil { 279 return "", errors.Wrap(err, "GetS3Client") 280 } 281 input := &s3.CreateMultipartUploadInput{} 282 input.SetBucket(b.Name) 283 input.SetKey(key) 284 if meta != nil { 285 metaHdr := make(map[string]*string) 286 for k, v := range meta { 287 if len(v) == 0 || len(v[0]) == 0 { 288 continue 289 } 290 switch http.CanonicalHeaderKey(k) { 291 case cloudprovider.META_HEADER_CACHE_CONTROL: 292 input.SetCacheControl(v[0]) 293 case cloudprovider.META_HEADER_CONTENT_TYPE: 294 input.SetContentType(v[0]) 295 case cloudprovider.META_HEADER_CONTENT_LANGUAGE: 296 input.SetContentLanguage(v[0]) 297 case cloudprovider.META_HEADER_CONTENT_ENCODING: 298 input.SetContentEncoding(v[0]) 299 case cloudprovider.META_HEADER_CONTENT_DISPOSITION: 300 input.SetContentDisposition(v[0]) 301 default: 302 metaHdr[k] = &v[0] 303 } 304 } 305 if len(metaHdr) > 0 { 306 input.SetMetadata(metaHdr) 307 } 308 } 309 if len(cannedAcl) == 0 { 310 cannedAcl = b.GetAcl() 311 } 312 input.SetACL(string(cannedAcl)) 313 if len(storageClassStr) > 0 { 314 input.SetStorageClass(storageClassStr) 315 } 316 output, err := s3cli.CreateMultipartUploadWithContext(ctx, input) 317 if err != nil { 318 return "", errors.Wrap(err, "CreateMultipartUpload") 319 } 320 return *output.UploadId, nil 321 } 322 323 func (b *SBucket) UploadPart(ctx context.Context, key string, uploadId string, partIndex int, part io.Reader, partSize int64, offset, totalSize int64) (string, error) { 324 s3cli, err := b.region.GetS3Client() 325 if err != nil { 326 return "", errors.Wrap(err, "GetS3Client") 327 } 328 input := &s3.UploadPartInput{} 329 input.SetBucket(b.Name) 330 input.SetKey(key) 331 input.SetUploadId(uploadId) 332 input.SetPartNumber(int64(partIndex)) 333 seeker, err := fileutils2.NewReadSeeker(part, partSize) 334 if err != nil { 335 return "", errors.Wrap(err, "newFakeSeeker") 336 } 337 defer seeker.Close() 338 input.SetBody(seeker) 339 input.SetContentLength(partSize) 340 output, err := s3cli.UploadPartWithContext(ctx, input) 341 if err != nil { 342 return "", errors.Wrap(err, "UploadPartWithContext") 343 } 344 return *output.ETag, nil 345 } 346 347 func (b *SBucket) CompleteMultipartUpload(ctx context.Context, key string, uploadId string, partEtags []string) error { 348 s3cli, err := b.region.GetS3Client() 349 if err != nil { 350 return errors.Wrap(err, "GetS3Client") 351 } 352 input := &s3.CompleteMultipartUploadInput{} 353 input.SetBucket(b.Name) 354 input.SetKey(key) 355 input.SetUploadId(uploadId) 356 uploads := &s3.CompletedMultipartUpload{} 357 parts := make([]*s3.CompletedPart, len(partEtags)) 358 for i := range partEtags { 359 parts[i] = &s3.CompletedPart{} 360 parts[i].SetPartNumber(int64(i + 1)) 361 parts[i].SetETag(partEtags[i]) 362 } 363 uploads.SetParts(parts) 364 input.SetMultipartUpload(uploads) 365 _, err = s3cli.CompleteMultipartUploadWithContext(ctx, input) 366 if err != nil { 367 return errors.Wrap(err, "CompleteMultipartUploadWithContext") 368 } 369 return nil 370 } 371 372 func (b *SBucket) AbortMultipartUpload(ctx context.Context, key string, uploadId string) error { 373 s3cli, err := b.region.GetS3Client() 374 if err != nil { 375 return errors.Wrap(err, "GetS3Client") 376 } 377 input := &s3.AbortMultipartUploadInput{} 378 input.SetBucket(b.Name) 379 input.SetKey(key) 380 input.SetUploadId(uploadId) 381 _, err = s3cli.AbortMultipartUploadWithContext(ctx, input) 382 if err != nil { 383 return errors.Wrap(err, "AbortMultipartUploadWithContext") 384 } 385 return nil 386 } 387 388 func (b *SBucket) DeleteObject(ctx context.Context, key string) error { 389 s3cli, err := b.region.GetS3Client() 390 if err != nil { 391 return errors.Wrap(err, "GetS3Client") 392 } 393 input := &s3.DeleteObjectInput{} 394 input.SetBucket(b.Name) 395 input.SetKey(key) 396 _, err = s3cli.DeleteObjectWithContext(ctx, input) 397 if err != nil { 398 return errors.Wrap(err, "DeleteObject") 399 } 400 return nil 401 } 402 403 func (b *SBucket) GetTempUrl(method string, key string, expire time.Duration) (string, error) { 404 s3cli, err := b.region.GetS3Client() 405 if err != nil { 406 return "", errors.Wrap(err, "GetS3Client") 407 } 408 var request *request.Request 409 switch method { 410 case "GET": 411 input := &s3.GetObjectInput{} 412 input.SetBucket(b.Name) 413 input.SetKey(key) 414 request, _ = s3cli.GetObjectRequest(input) 415 case "PUT": 416 input := &s3.PutObjectInput{} 417 input.SetBucket(b.Name) 418 input.SetKey(key) 419 request, _ = s3cli.PutObjectRequest(input) 420 case "DELETE": 421 input := &s3.DeleteObjectInput{} 422 input.SetBucket(b.Name) 423 input.SetKey(key) 424 request, _ = s3cli.DeleteObjectRequest(input) 425 default: 426 return "", errors.Error("unsupported method") 427 } 428 url, _, err := request.PresignRequest(expire) 429 if err != nil { 430 return "", errors.Wrap(err, "request.PresignRequest") 431 } 432 return url, nil 433 } 434 435 func (b *SBucket) CopyObject(ctx context.Context, destKey string, srcBucket, srcKey string, cannedAcl cloudprovider.TBucketACLType, storageClassStr string, meta http.Header) error { 436 s3cli, err := b.region.GetS3Client() 437 if err != nil { 438 return errors.Wrap(err, "GetS3Client") 439 } 440 log.Debugf("copy from %s/%s to %s/%s", srcBucket, srcKey, b.Name, destKey) 441 input := &s3.CopyObjectInput{} 442 input.SetBucket(b.Name) 443 input.SetKey(destKey) 444 input.SetCopySource(fmt.Sprintf("%s/%s", srcBucket, url.PathEscape(srcKey))) 445 input.SetStorageClass(storageClassStr) 446 if len(cannedAcl) == 0 { 447 cannedAcl = b.GetAcl() 448 } 449 input.SetACL(string(cannedAcl)) 450 var metaDir string 451 if meta != nil { 452 metaHdr := make(map[string]*string) 453 for k, v := range meta { 454 if len(v) == 0 || len(v[0]) == 0 { 455 continue 456 } 457 switch http.CanonicalHeaderKey(k) { 458 case cloudprovider.META_HEADER_CACHE_CONTROL: 459 input.SetCacheControl(v[0]) 460 case cloudprovider.META_HEADER_CONTENT_TYPE: 461 input.SetContentType(v[0]) 462 case cloudprovider.META_HEADER_CONTENT_LANGUAGE: 463 input.SetContentLanguage(v[0]) 464 case cloudprovider.META_HEADER_CONTENT_ENCODING: 465 input.SetContentEncoding(v[0]) 466 case cloudprovider.META_HEADER_CONTENT_DISPOSITION: 467 input.SetContentDisposition(v[0]) 468 default: 469 metaHdr[k] = &v[0] 470 } 471 } 472 if len(metaHdr) > 0 { 473 input.SetMetadata(metaHdr) 474 } 475 metaDir = "REPLACE" 476 } else { 477 metaDir = "COPY" 478 } 479 input.SetMetadataDirective(metaDir) 480 _, err = s3cli.CopyObject(input) 481 if err != nil { 482 return errors.Wrap(err, "CopyObject") 483 } 484 return nil 485 } 486 487 func (b *SBucket) GetObject(ctx context.Context, key string, rangeOpt *cloudprovider.SGetObjectRange) (io.ReadCloser, error) { 488 s3cli, err := b.region.GetS3Client() 489 if err != nil { 490 return nil, errors.Wrap(err, "GetS3Client") 491 } 492 input := &s3.GetObjectInput{} 493 input.SetBucket(b.Name) 494 input.SetKey(key) 495 if rangeOpt != nil { 496 input.SetRange(rangeOpt.String()) 497 } 498 output, err := s3cli.GetObject(input) 499 if err != nil { 500 return nil, errors.Wrap(err, "GetObject") 501 } 502 return output.Body, nil 503 } 504 505 func (b *SBucket) CopyPart(ctx context.Context, key string, uploadId string, partNumber int, srcBucket string, srcKey string, srcOffset int64, srcLength int64) (string, error) { 506 s3cli, err := b.region.GetS3Client() 507 if err != nil { 508 return "", errors.Wrap(err, "GetS3Client") 509 } 510 input := &s3.UploadPartCopyInput{} 511 input.SetBucket(b.Name) 512 input.SetKey(key) 513 input.SetUploadId(uploadId) 514 input.SetPartNumber(int64(partNumber)) 515 input.SetCopySource(fmt.Sprintf("/%s/%s", srcBucket, url.PathEscape(srcKey))) 516 if srcLength > 0 { 517 input.SetCopySourceRange(fmt.Sprintf("bytes=%d-%d", srcOffset, srcOffset+srcLength-1)) 518 } 519 output, err := s3cli.UploadPartCopy(input) 520 if err != nil { 521 return "", errors.Wrap(err, "s3cli.UploadPartCopy") 522 } 523 return *output.CopyPartResult.ETag, nil 524 } 525 526 func (b *SBucket) SetWebsite(websitConf cloudprovider.SBucketWebsiteConf) error { 527 s3cli, err := b.region.GetS3Client() 528 if err != nil { 529 return errors.Wrap(err, "GetS3Client") 530 } 531 s3WebConf := s3.WebsiteConfiguration{} 532 s3WebConf.SetIndexDocument(&s3.IndexDocument{Suffix: &websitConf.Index}) 533 s3WebConf.SetErrorDocument(&s3.ErrorDocument{Key: &websitConf.ErrorDocument}) 534 input := s3.PutBucketWebsiteInput{} 535 input.SetBucket(b.Name) 536 input.SetWebsiteConfiguration(&s3WebConf) 537 _, err = s3cli.PutBucketWebsite(&input) 538 if err != nil { 539 return errors.Wrapf(err, "s3cli.PutBucketWebsite(%s)", jsonutils.Marshal(input).String()) 540 } 541 return nil 542 } 543 544 func (b *SBucket) GetWebsiteConf() (cloudprovider.SBucketWebsiteConf, error) { 545 result := cloudprovider.SBucketWebsiteConf{} 546 s3cli, err := b.region.GetS3Client() 547 if err != nil { 548 return result, errors.Wrap(err, "GetS3Client") 549 } 550 input := s3.GetBucketWebsiteInput{} 551 input.SetBucket(b.Name) 552 webconfResult, err := s3cli.GetBucketWebsite(&input) 553 if err != nil { 554 return result, errors.Wrapf(err, "s3cli.GetBucketWebsite(%s)", b.Name) 555 } 556 557 if webconfResult.IndexDocument != nil && webconfResult.IndexDocument.Suffix != nil { 558 result.Index = *webconfResult.IndexDocument.Suffix 559 } 560 if webconfResult.ErrorDocument != nil && webconfResult.ErrorDocument.Key != nil { 561 result.ErrorDocument = *webconfResult.ErrorDocument.Key 562 } 563 result.Url = b.GetWebsiteUrl() 564 return result, nil 565 } 566 567 func (b *SBucket) DeleteWebSiteConf() error { 568 s3cli, err := b.region.GetS3Client() 569 if err != nil { 570 return errors.Wrap(err, "GetS3Client") 571 } 572 input := s3.DeleteBucketWebsiteInput{} 573 input.SetBucket(b.Name) 574 _, err = s3cli.DeleteBucketWebsite(&input) 575 if err != nil { 576 return errors.Wrapf(err, "s3cli.DeleteBucketWebsite(%s)", b.Name) 577 } 578 return nil 579 } 580 581 func InputToAwsApiSliceString(input []string) []*string { 582 result := []*string{} 583 for i := range input { 584 result = append(result, &input[i]) 585 } 586 return result 587 } 588 589 func InputToAwsApiInt64(input int64) *int64 { 590 return &input 591 } 592 593 func AwsApiSliceStringToOutput(input []*string) []string { 594 result := []string{} 595 for i := range input { 596 if input[i] != nil { 597 result = append(result, *input[i]) 598 } else { 599 result = append(result, "") 600 } 601 } 602 return result 603 } 604 605 func AwsApiInt64ToOutput(input *int64) int64 { 606 if input == nil { 607 return 0 608 } 609 return *input 610 } 611 612 func (b *SBucket) SetCORS(rules []cloudprovider.SBucketCORSRule) error { 613 s3cli, err := b.region.GetS3Client() 614 if err != nil { 615 return errors.Wrap(err, "GetS3Client") 616 } 617 opts := []*s3.CORSRule{} 618 for i := range rules { 619 opts = append(opts, &s3.CORSRule{ 620 AllowedOrigins: InputToAwsApiSliceString(rules[i].AllowedOrigins), 621 AllowedMethods: InputToAwsApiSliceString(rules[i].AllowedMethods), 622 AllowedHeaders: InputToAwsApiSliceString(rules[i].AllowedHeaders), 623 MaxAgeSeconds: InputToAwsApiInt64(int64(rules[i].MaxAgeSeconds)), 624 ExposeHeaders: InputToAwsApiSliceString(rules[i].ExposeHeaders), 625 }) 626 } 627 628 input := s3.PutBucketCorsInput{} 629 input.SetBucket(b.Name) 630 input.SetCORSConfiguration(&s3.CORSConfiguration{CORSRules: opts}) 631 _, err = s3cli.PutBucketCors(&input) 632 if err != nil { 633 return errors.Wrapf(err, "s3cli.PutBucketCors(%s)", input) 634 } 635 return nil 636 } 637 638 func (b *SBucket) GetCORSRules() ([]cloudprovider.SBucketCORSRule, error) { 639 s3cli, err := b.region.GetS3Client() 640 if err != nil { 641 return nil, errors.Wrap(err, "GetS3Client") 642 } 643 input := s3.GetBucketCorsInput{} 644 input.SetBucket(b.Name) 645 conf, err := s3cli.GetBucketCors(&input) 646 if err != nil { 647 if !strings.Contains(err.Error(), "NoSuchCORSConfiguration") { 648 return nil, errors.Wrapf(err, "s3cli.GetBucketCors(%s)", b.Name) 649 } 650 } 651 if conf == nil { 652 return nil, nil 653 } 654 result := []cloudprovider.SBucketCORSRule{} 655 for i := range conf.CORSRules { 656 result = append(result, cloudprovider.SBucketCORSRule{ 657 AllowedOrigins: AwsApiSliceStringToOutput(conf.CORSRules[i].AllowedOrigins), 658 AllowedMethods: AwsApiSliceStringToOutput(conf.CORSRules[i].AllowedMethods), 659 AllowedHeaders: AwsApiSliceStringToOutput(conf.CORSRules[i].AllowedHeaders), 660 MaxAgeSeconds: int(AwsApiInt64ToOutput(conf.CORSRules[i].MaxAgeSeconds)), 661 ExposeHeaders: AwsApiSliceStringToOutput(conf.CORSRules[i].ExposeHeaders), 662 Id: strconv.Itoa(i), 663 }) 664 } 665 return result, nil 666 } 667 668 func (b *SBucket) DeleteCORS() error { 669 s3cli, err := b.region.GetS3Client() 670 if err != nil { 671 return errors.Wrap(err, "GetS3Client") 672 } 673 674 input := s3.DeleteBucketCorsInput{} 675 input.SetBucket(b.Name) 676 _, err = s3cli.DeleteBucketCors(&input) 677 if err != nil { 678 return errors.Wrapf(err, "s3cli.DeleteBucketCors(%s)", b.Name) 679 } 680 return nil 681 } 682 683 func (b *SBucket) GetTags() (map[string]string, error) { 684 s3cli, err := b.region.GetS3Client() 685 if err != nil { 686 return nil, errors.Wrap(err, "GetS3Client") 687 } 688 tagresult, err := s3cli.GetBucketTagging(&s3.GetBucketTaggingInput{Bucket: &b.Name}) 689 if err != nil { 690 if strings.Contains(err.Error(), "NoSuchTagSet") { 691 return nil, nil 692 } 693 return nil, errors.Wrapf(err, "osscli.GetBucketTagging(%s)", b.Name) 694 } 695 if tagresult == nil { 696 return nil, nil 697 } 698 result := map[string]string{} 699 for i := range tagresult.TagSet { 700 if tagresult.TagSet[i].Key != nil && tagresult.TagSet[i].Value != nil { 701 result[*tagresult.TagSet[i].Key] = *tagresult.TagSet[i].Value 702 } 703 704 } 705 return result, nil 706 } 707 708 func (b *SBucket) SetTags(tags map[string]string, replace bool) error { 709 if !replace { 710 return cloudprovider.ErrNotSupported 711 } 712 s3cli, err := b.region.GetS3Client() 713 if err != nil { 714 return errors.Wrap(err, "GetS3Client") 715 } 716 717 _, err = s3cli.DeleteBucketTagging(&s3.DeleteBucketTaggingInput{Bucket: &b.Name}) 718 if err != nil { 719 return errors.Wrapf(err, "DeleteBucketTagging") 720 } 721 722 if len(tags) == 0 { 723 return nil 724 } 725 726 input := s3.PutBucketTaggingInput{Tagging: &s3.Tagging{}} 727 input.Bucket = &b.Name 728 apiTagKeys := []string{} 729 apiTagValues := []string{} 730 for k, v := range tags { 731 apiTagKeys = append(apiTagKeys, k) 732 apiTagValues = append(apiTagValues, v) 733 734 } 735 for i := range apiTagKeys { 736 input.Tagging.TagSet = append(input.Tagging.TagSet, &s3.Tag{Key: &apiTagKeys[i], Value: &apiTagValues[i]}) 737 } 738 739 _, err = s3cli.PutBucketTagging(&input) 740 if err != nil { 741 return errors.Wrapf(err, "obscli.SetBucketTagging(%s)", jsonutils.Marshal(input)) 742 } 743 return nil 744 } 745 746 func (b *SBucket) ListMultipartUploads() ([]cloudprovider.SBucketMultipartUploads, error) { 747 s3cli, err := b.region.GetS3Client() 748 if err != nil { 749 return nil, errors.Wrap(err, "GetS3Client") 750 } 751 result := []cloudprovider.SBucketMultipartUploads{} 752 753 input := s3.ListMultipartUploadsInput{} 754 input.SetBucket(b.Name) 755 keyMarker := "" 756 uploadIDMarker := "" 757 for { 758 if len(keyMarker) > 0 { 759 input.SetKeyMarker(keyMarker) 760 } 761 if len(uploadIDMarker) > 0 { 762 input.SetUploadIdMarker(uploadIDMarker) 763 } 764 output, err := s3cli.ListMultipartUploads(&input) 765 if err != nil { 766 return nil, errors.Wrap(err, " coscli.Bucket.ListMultipartUploads(context.Background(), &input)") 767 } 768 if output == nil { 769 return nil, nil 770 } 771 for i := range output.Uploads { 772 temp := cloudprovider.SBucketMultipartUploads{} 773 if output.Uploads[i].Key != nil { 774 temp.ObjectName = *output.Uploads[i].Key 775 } 776 if output.Uploads[i].Initiator != nil { 777 temp.Initiator = *output.Uploads[i].Initiator.DisplayName 778 } 779 if output.Uploads[i].Initiated != nil { 780 temp.Initiated = *output.Uploads[i].Initiated 781 } 782 if output.Uploads[i].UploadId != nil { 783 temp.UploadID = *output.Uploads[i].UploadId 784 } 785 result = append(result, temp) 786 } 787 if output.NextKeyMarker != nil { 788 keyMarker = *output.NextKeyMarker 789 } 790 if output.NextUploadIdMarker != nil { 791 uploadIDMarker = *output.NextUploadIdMarker 792 } 793 794 if output.IsTruncated == nil || !*output.IsTruncated { 795 break 796 } 797 } 798 799 return result, nil 800 }