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  }