yunion.io/x/cloudmux@v0.3.10-0-alpha.1/pkg/multicloud/huawei/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 huawei
    16  
    17  import (
    18  	"context"
    19  	"fmt"
    20  	"io"
    21  	"net/http"
    22  	"strconv"
    23  	"strings"
    24  	"time"
    25  
    26  	"yunion.io/x/jsonutils"
    27  	"yunion.io/x/log"
    28  	"yunion.io/x/pkg/errors"
    29  	"yunion.io/x/pkg/utils"
    30  	"yunion.io/x/s3cli"
    31  
    32  	"yunion.io/x/cloudmux/pkg/cloudprovider"
    33  	"yunion.io/x/cloudmux/pkg/multicloud"
    34  	"yunion.io/x/cloudmux/pkg/multicloud/huawei/obs"
    35  )
    36  
    37  type SBucket struct {
    38  	multicloud.SBaseBucket
    39  	HuaweiTags
    40  
    41  	region *SRegion
    42  
    43  	Name         string
    44  	Location     string
    45  	CreationDate time.Time
    46  }
    47  
    48  func (b *SBucket) GetProjectId() string {
    49  	resp, err := b.region.HeadBucket(b.Name)
    50  	if err != nil {
    51  		return ""
    52  	}
    53  	epid, _ := resp.ResponseHeaders["epid"]
    54  	if len(epid) > 0 {
    55  		return epid[0]
    56  	}
    57  	return ""
    58  }
    59  
    60  func (b *SBucket) GetGlobalId() string {
    61  	return b.Name
    62  }
    63  
    64  func (b *SBucket) GetName() string {
    65  	return b.Name
    66  }
    67  
    68  func (b *SBucket) GetLocation() string {
    69  	return b.Location
    70  }
    71  
    72  func (b *SBucket) GetIRegion() cloudprovider.ICloudRegion {
    73  	return b.region
    74  }
    75  
    76  func (b *SBucket) GetCreatedAt() time.Time {
    77  	return b.CreationDate
    78  }
    79  
    80  func (b *SBucket) GetStorageClass() string {
    81  	obscli, err := b.region.getOBSClient()
    82  	if err != nil {
    83  		log.Errorf("b.region.getOBSClient error %s", err)
    84  		return ""
    85  	}
    86  	output, err := obscli.GetBucketStoragePolicy(b.Name)
    87  	if err != nil {
    88  		log.Errorf("obscli.GetBucketStoragePolicy error %s", err)
    89  		return ""
    90  	}
    91  	return output.StorageClass
    92  }
    93  
    94  func obsAcl2CannedAcl(acls []obs.Grant) cloudprovider.TBucketACLType {
    95  	switch {
    96  	case len(acls) == 1:
    97  		if acls[0].Grantee.URI == "" && acls[0].Permission == s3cli.PERMISSION_FULL_CONTROL {
    98  			return cloudprovider.ACLPrivate
    99  		}
   100  	case len(acls) == 2:
   101  		for _, g := range acls {
   102  			if g.Grantee.URI == s3cli.GRANTEE_GROUP_URI_AUTH_USERS && g.Permission == s3cli.PERMISSION_READ {
   103  				return cloudprovider.ACLAuthRead
   104  			}
   105  			if g.Grantee.URI == s3cli.GRANTEE_GROUP_URI_ALL_USERS && g.Permission == s3cli.PERMISSION_READ {
   106  				return cloudprovider.ACLPublicRead
   107  			}
   108  		}
   109  	case len(acls) == 3:
   110  		for _, g := range acls {
   111  			if g.Grantee.URI == s3cli.GRANTEE_GROUP_URI_ALL_USERS && g.Permission == s3cli.PERMISSION_WRITE {
   112  				return cloudprovider.ACLPublicReadWrite
   113  			}
   114  		}
   115  	}
   116  	return cloudprovider.ACLUnknown
   117  }
   118  
   119  func (b *SBucket) GetAcl() cloudprovider.TBucketACLType {
   120  	acl := cloudprovider.ACLPrivate
   121  	obscli, err := b.region.getOBSClient()
   122  	if err != nil {
   123  		log.Errorf("b.region.getOBSClient error %s", err)
   124  		return acl
   125  	}
   126  	output, err := obscli.GetBucketAcl(b.Name)
   127  	if err != nil {
   128  		log.Errorf("obscli.GetBucketAcl error %s", err)
   129  		return acl
   130  	}
   131  	acl = obsAcl2CannedAcl(output.Grants)
   132  	return acl
   133  }
   134  
   135  func (b *SBucket) SetAcl(acl cloudprovider.TBucketACLType) error {
   136  	obscli, err := b.region.getOBSClient()
   137  	if err != nil {
   138  		return errors.Wrap(err, "b.region.getOBSClient")
   139  	}
   140  	input := &obs.SetBucketAclInput{}
   141  	input.Bucket = b.Name
   142  	input.ACL = obs.AclType(string(acl))
   143  	_, err = obscli.SetBucketAcl(input)
   144  	if err != nil {
   145  		return errors.Wrap(err, "obscli.SetBucketAcl")
   146  	}
   147  	return nil
   148  }
   149  
   150  func (b *SBucket) GetAccessUrls() []cloudprovider.SBucketAccessUrl {
   151  	return []cloudprovider.SBucketAccessUrl{
   152  		{
   153  			Url:         fmt.Sprintf("https://%s.%s", b.Name, b.region.getOBSEndpoint()),
   154  			Description: "bucket url",
   155  			Primary:     true,
   156  		},
   157  		{
   158  			Url:         fmt.Sprintf("https://%s/%s", b.region.getOBSEndpoint(), b.Name),
   159  			Description: "obs url",
   160  		},
   161  	}
   162  }
   163  
   164  func (b *SBucket) GetStats() cloudprovider.SBucketStats {
   165  	stats := cloudprovider.SBucketStats{}
   166  	obscli, err := b.region.getOBSClient()
   167  	if err != nil {
   168  		log.Errorf("b.region.getOBSClient error %s", err)
   169  		stats.SizeBytes = -1
   170  		stats.ObjectCount = -1
   171  		return stats
   172  	}
   173  	output, err := obscli.GetBucketStorageInfo(b.Name)
   174  	if err != nil {
   175  		log.Errorf("obscli.GetBucketStorageInfo error %s", err)
   176  		stats.SizeBytes = -1
   177  		stats.ObjectCount = -1
   178  		return stats
   179  	}
   180  	stats.SizeBytes = output.Size
   181  	stats.ObjectCount = output.ObjectNumber
   182  	return stats
   183  }
   184  
   185  func (b *SBucket) ListObjects(prefix string, marker string, delimiter string, maxCount int) (cloudprovider.SListObjectResult, error) {
   186  	result := cloudprovider.SListObjectResult{}
   187  	obscli, err := b.region.getOBSClient()
   188  	if err != nil {
   189  		return result, errors.Wrap(err, "GetOBSClient")
   190  	}
   191  	input := &obs.ListObjectsInput{}
   192  	input.Bucket = b.Name
   193  	if len(prefix) > 0 {
   194  		input.Prefix = prefix
   195  	}
   196  	if len(marker) > 0 {
   197  		input.Marker = marker
   198  	}
   199  	if len(delimiter) > 0 {
   200  		input.Delimiter = delimiter
   201  	}
   202  	if maxCount > 0 {
   203  		input.MaxKeys = maxCount
   204  	}
   205  	oResult, err := obscli.ListObjects(input)
   206  	if err != nil {
   207  		return result, errors.Wrap(err, "ListObjects")
   208  	}
   209  	result.Objects = make([]cloudprovider.ICloudObject, 0)
   210  	for _, object := range oResult.Contents {
   211  		obj := &SObject{
   212  			bucket: b,
   213  			SBaseCloudObject: cloudprovider.SBaseCloudObject{
   214  				StorageClass: string(object.StorageClass),
   215  				Key:          object.Key,
   216  				SizeBytes:    object.Size,
   217  				ETag:         object.ETag,
   218  				LastModified: object.LastModified,
   219  			},
   220  		}
   221  		result.Objects = append(result.Objects, obj)
   222  	}
   223  	if oResult.CommonPrefixes != nil {
   224  		result.CommonPrefixes = make([]cloudprovider.ICloudObject, 0)
   225  		for _, commonPrefix := range oResult.CommonPrefixes {
   226  			obj := &SObject{
   227  				bucket: b,
   228  				SBaseCloudObject: cloudprovider.SBaseCloudObject{
   229  					Key: commonPrefix,
   230  				},
   231  			}
   232  			result.CommonPrefixes = append(result.CommonPrefixes, obj)
   233  		}
   234  	}
   235  	result.IsTruncated = oResult.IsTruncated
   236  	result.NextMarker = oResult.NextMarker
   237  	return result, nil
   238  }
   239  
   240  func (b *SBucket) PutObject(ctx context.Context, key string, reader io.Reader, sizeBytes int64, cannedAcl cloudprovider.TBucketACLType, storageClassStr string, meta http.Header) error {
   241  	obscli, err := b.region.getOBSClient()
   242  	if err != nil {
   243  		return errors.Wrap(err, "GetOBSClient")
   244  	}
   245  	input := &obs.PutObjectInput{}
   246  	input.Bucket = b.Name
   247  	input.Key = key
   248  	input.Body = reader
   249  
   250  	if sizeBytes > 0 {
   251  		input.ContentLength = sizeBytes
   252  	}
   253  	if len(storageClassStr) > 0 {
   254  		input.StorageClass, err = str2StorageClass(storageClassStr)
   255  		if err != nil {
   256  			return err
   257  		}
   258  	}
   259  	if len(cannedAcl) == 0 {
   260  		cannedAcl = b.GetAcl()
   261  	}
   262  	input.ACL = obs.AclType(string(cannedAcl))
   263  	if meta != nil {
   264  		val := meta.Get(cloudprovider.META_HEADER_CONTENT_TYPE)
   265  		if len(val) > 0 {
   266  			input.ContentType = val
   267  		}
   268  		val = meta.Get(cloudprovider.META_HEADER_CONTENT_MD5)
   269  		if len(val) > 0 {
   270  			input.ContentMD5 = val
   271  		}
   272  		extraMeta := make(map[string]string)
   273  		for k, v := range meta {
   274  			if utils.IsInStringArray(k, []string{
   275  				cloudprovider.META_HEADER_CONTENT_TYPE,
   276  				cloudprovider.META_HEADER_CONTENT_MD5,
   277  			}) {
   278  				continue
   279  			}
   280  			if len(v[0]) > 0 {
   281  				extraMeta[k] = v[0]
   282  			}
   283  		}
   284  		input.Metadata = extraMeta
   285  	}
   286  	_, err = obscli.PutObject(input)
   287  	if err != nil {
   288  		return errors.Wrap(err, "PutObject")
   289  	}
   290  	return nil
   291  }
   292  
   293  func (b *SBucket) NewMultipartUpload(ctx context.Context, key string, cannedAcl cloudprovider.TBucketACLType, storageClassStr string, meta http.Header) (string, error) {
   294  	obscli, err := b.region.getOBSClient()
   295  	if err != nil {
   296  		return "", errors.Wrap(err, "GetOBSClient")
   297  	}
   298  
   299  	input := &obs.InitiateMultipartUploadInput{}
   300  	input.Bucket = b.Name
   301  	input.Key = key
   302  	if meta != nil {
   303  		val := meta.Get(cloudprovider.META_HEADER_CONTENT_TYPE)
   304  		if len(val) > 0 {
   305  			input.ContentType = val
   306  		}
   307  		extraMeta := make(map[string]string)
   308  		for k, v := range meta {
   309  			if utils.IsInStringArray(k, []string{
   310  				cloudprovider.META_HEADER_CONTENT_TYPE,
   311  			}) {
   312  				continue
   313  			}
   314  			if len(v[0]) > 0 {
   315  				extraMeta[k] = v[0]
   316  			}
   317  		}
   318  		input.Metadata = extraMeta
   319  	}
   320  	if len(cannedAcl) == 0 {
   321  		cannedAcl = b.GetAcl()
   322  	}
   323  	input.ACL = obs.AclType(string(cannedAcl))
   324  	if len(storageClassStr) > 0 {
   325  		input.StorageClass, err = str2StorageClass(storageClassStr)
   326  		if err != nil {
   327  			return "", errors.Wrap(err, "str2StorageClass")
   328  		}
   329  	}
   330  	output, err := obscli.InitiateMultipartUpload(input)
   331  	if err != nil {
   332  		return "", errors.Wrap(err, "InitiateMultipartUpload")
   333  	}
   334  
   335  	return output.UploadId, nil
   336  }
   337  
   338  func (b *SBucket) UploadPart(ctx context.Context, key string, uploadId string, partIndex int, part io.Reader, partSize int64, offset, totalSize int64) (string, error) {
   339  	obscli, err := b.region.getOBSClient()
   340  	if err != nil {
   341  		return "", errors.Wrap(err, "GetOBSClient")
   342  	}
   343  
   344  	input := &obs.UploadPartInput{}
   345  	input.Bucket = b.Name
   346  	input.Key = key
   347  	input.UploadId = uploadId
   348  	input.PartNumber = partIndex
   349  	input.PartSize = partSize
   350  	input.Body = part
   351  	output, err := obscli.UploadPart(input)
   352  	if err != nil {
   353  		return "", errors.Wrap(err, "UploadPart")
   354  	}
   355  
   356  	return output.ETag, nil
   357  }
   358  
   359  func (b *SBucket) CompleteMultipartUpload(ctx context.Context, key string, uploadId string, partEtags []string) error {
   360  	obscli, err := b.region.getOBSClient()
   361  	if err != nil {
   362  		return errors.Wrap(err, "GetOBSClient")
   363  	}
   364  	input := &obs.CompleteMultipartUploadInput{}
   365  	input.Bucket = b.Name
   366  	input.Key = key
   367  	input.UploadId = uploadId
   368  	parts := make([]obs.Part, len(partEtags))
   369  	for i := range partEtags {
   370  		parts[i] = obs.Part{
   371  			PartNumber: i + 1,
   372  			ETag:       partEtags[i],
   373  		}
   374  	}
   375  	input.Parts = parts
   376  	_, err = obscli.CompleteMultipartUpload(input)
   377  	if err != nil {
   378  		return errors.Wrap(err, "CompleteMultipartUpload")
   379  	}
   380  
   381  	return nil
   382  }
   383  
   384  func (b *SBucket) AbortMultipartUpload(ctx context.Context, key string, uploadId string) error {
   385  	obscli, err := b.region.getOBSClient()
   386  	if err != nil {
   387  		return errors.Wrap(err, "GetOBSClient")
   388  	}
   389  
   390  	input := &obs.AbortMultipartUploadInput{}
   391  	input.Bucket = b.Name
   392  	input.Key = key
   393  	input.UploadId = uploadId
   394  
   395  	_, err = obscli.AbortMultipartUpload(input)
   396  	if err != nil {
   397  		return errors.Wrap(err, "AbortMultipartUpload")
   398  	}
   399  
   400  	return nil
   401  }
   402  
   403  func (b *SBucket) DeleteObject(ctx context.Context, key string) error {
   404  	obscli, err := b.region.getOBSClient()
   405  	if err != nil {
   406  		return errors.Wrap(err, "GetOBSClient")
   407  	}
   408  	input := &obs.DeleteObjectInput{}
   409  	input.Bucket = b.Name
   410  	input.Key = key
   411  	_, err = obscli.DeleteObject(input)
   412  	if err != nil {
   413  		return errors.Wrap(err, "DeleteObject")
   414  	}
   415  	return nil
   416  }
   417  
   418  func (b *SBucket) GetTempUrl(method string, key string, expire time.Duration) (string, error) {
   419  	obscli, err := b.region.getOBSClient()
   420  	if err != nil {
   421  		return "", errors.Wrap(err, "GetOBSClient")
   422  	}
   423  	input := obs.CreateSignedUrlInput{}
   424  	input.Bucket = b.Name
   425  	input.Key = key
   426  	input.Expires = int(expire / time.Second)
   427  	switch method {
   428  	case "GET":
   429  		input.Method = obs.HttpMethodGet
   430  	case "PUT":
   431  		input.Method = obs.HttpMethodPut
   432  	case "DELETE":
   433  		input.Method = obs.HttpMethodDelete
   434  	default:
   435  		return "", errors.Error("unsupported method")
   436  	}
   437  	output, err := obscli.CreateSignedUrl(&input)
   438  	return output.SignedUrl, nil
   439  }
   440  
   441  func (b *SBucket) LimitSupport() cloudprovider.SBucketStats {
   442  	return cloudprovider.SBucketStats{
   443  		SizeBytes:   1,
   444  		ObjectCount: -1,
   445  	}
   446  }
   447  
   448  func (b *SBucket) GetLimit() cloudprovider.SBucketStats {
   449  	stats := cloudprovider.SBucketStats{}
   450  	obscli, err := b.region.getOBSClient()
   451  	if err != nil {
   452  		log.Errorf("getOBSClient error %s", err)
   453  		return stats
   454  	}
   455  	output, err := obscli.GetBucketQuota(b.Name)
   456  	if err != nil {
   457  		return stats
   458  	}
   459  	stats.SizeBytes = output.Quota
   460  	return stats
   461  }
   462  
   463  func (b *SBucket) SetLimit(limit cloudprovider.SBucketStats) error {
   464  	obscli, err := b.region.getOBSClient()
   465  	if err != nil {
   466  		return errors.Wrap(err, "getOBSClient")
   467  	}
   468  	input := &obs.SetBucketQuotaInput{}
   469  	input.Bucket = b.Name
   470  	input.Quota = limit.SizeBytes
   471  	_, err = obscli.SetBucketQuota(input)
   472  	if err != nil {
   473  		return errors.Wrap(err, "SetBucketQuota")
   474  	}
   475  	return nil
   476  }
   477  
   478  func (b *SBucket) CopyObject(ctx context.Context, destKey string, srcBucket, srcKey string, cannedAcl cloudprovider.TBucketACLType, storageClassStr string, meta http.Header) error {
   479  	obscli, err := b.region.getOBSClient()
   480  	if err != nil {
   481  		return errors.Wrap(err, "GetOBSClient")
   482  	}
   483  	input := &obs.CopyObjectInput{}
   484  	input.Bucket = b.Name
   485  	input.Key = destKey
   486  	input.CopySourceBucket = srcBucket
   487  	input.CopySourceKey = srcKey
   488  	if len(storageClassStr) > 0 {
   489  		input.StorageClass, err = str2StorageClass(storageClassStr)
   490  		if err != nil {
   491  			return err
   492  		}
   493  	}
   494  	if len(cannedAcl) == 0 {
   495  		cannedAcl = b.GetAcl()
   496  	}
   497  	input.ACL = obs.AclType(string(cannedAcl))
   498  	if meta != nil {
   499  		val := meta.Get(cloudprovider.META_HEADER_CONTENT_TYPE)
   500  		if len(val) > 0 {
   501  			input.ContentType = val
   502  		}
   503  		extraMeta := make(map[string]string)
   504  		for k, v := range meta {
   505  			if utils.IsInStringArray(k, []string{
   506  				cloudprovider.META_HEADER_CONTENT_TYPE,
   507  			}) {
   508  				continue
   509  			}
   510  			if len(v[0]) > 0 {
   511  				extraMeta[k] = v[0]
   512  			}
   513  		}
   514  		input.Metadata = extraMeta
   515  		input.MetadataDirective = obs.ReplaceMetadata
   516  	} else {
   517  		input.MetadataDirective = obs.CopyMetadata
   518  	}
   519  	_, err = obscli.CopyObject(input)
   520  	if err != nil {
   521  		return errors.Wrap(err, "obscli.CopyObject")
   522  	}
   523  	return nil
   524  }
   525  
   526  func (b *SBucket) GetObject(ctx context.Context, key string, rangeOpt *cloudprovider.SGetObjectRange) (io.ReadCloser, error) {
   527  	obscli, err := b.region.getOBSClient()
   528  	if err != nil {
   529  		return nil, errors.Wrap(err, "GetOBSClient")
   530  	}
   531  	input := &obs.GetObjectInput{}
   532  	input.Bucket = b.Name
   533  	input.Key = key
   534  	if rangeOpt != nil {
   535  		input.RangeStart = rangeOpt.Start
   536  		input.RangeEnd = rangeOpt.End
   537  	}
   538  	output, err := obscli.GetObject(input)
   539  	if err != nil {
   540  		return nil, errors.Wrap(err, "obscli.GetObject")
   541  	}
   542  	return output.Body, nil
   543  }
   544  
   545  func (b *SBucket) CopyPart(ctx context.Context, key string, uploadId string, partIndex int, srcBucket string, srcKey string, srcOffset int64, srcLength int64) (string, error) {
   546  	obscli, err := b.region.getOBSClient()
   547  	if err != nil {
   548  		return "", errors.Wrap(err, "GetOBSClient")
   549  	}
   550  	input := &obs.CopyPartInput{}
   551  	input.Bucket = b.Name
   552  	input.Key = key
   553  	input.UploadId = uploadId
   554  	input.PartNumber = partIndex
   555  	input.CopySourceBucket = srcBucket
   556  	input.CopySourceKey = srcKey
   557  	input.CopySourceRangeStart = srcOffset
   558  	input.CopySourceRangeEnd = srcOffset + srcLength - 1
   559  	output, err := obscli.CopyPart(input)
   560  	if err != nil {
   561  		return "", errors.Wrap(err, "CopyPart")
   562  	}
   563  	return output.ETag, nil
   564  }
   565  
   566  func (b *SBucket) SetWebsite(websitConf cloudprovider.SBucketWebsiteConf) error {
   567  	obscli, err := b.region.getOBSClient()
   568  	if err != nil {
   569  		return errors.Wrap(err, "GetOBSClient")
   570  	}
   571  
   572  	obsWebConf := obs.SetBucketWebsiteConfigurationInput{}
   573  	obsWebConf.Bucket = b.Name
   574  	obsWebConf.BucketWebsiteConfiguration = obs.BucketWebsiteConfiguration{
   575  		IndexDocument: obs.IndexDocument{Suffix: websitConf.Index},
   576  		ErrorDocument: obs.ErrorDocument{Key: websitConf.ErrorDocument},
   577  	}
   578  	_, err = obscli.SetBucketWebsiteConfiguration(&obsWebConf)
   579  	if err != nil {
   580  		return errors.Wrap(err, "obscli.SetBucketWebsiteConfiguration(&obsWebConf)")
   581  	}
   582  	return nil
   583  }
   584  
   585  func (b *SBucket) GetWebsiteConf() (cloudprovider.SBucketWebsiteConf, error) {
   586  	result := cloudprovider.SBucketWebsiteConf{}
   587  	obscli, err := b.region.getOBSClient()
   588  	if err != nil {
   589  		return result, errors.Wrap(err, "GetOBSClient")
   590  	}
   591  	out, err := obscli.GetBucketWebsiteConfiguration(b.Name)
   592  	if out == nil {
   593  		return result, nil
   594  	}
   595  	result.Index = out.IndexDocument.Suffix
   596  	result.ErrorDocument = out.ErrorDocument.Key
   597  	result.Url = fmt.Sprintf("https://%s.obs-website.%s.myhuaweicloud.com", b.Name, b.region.GetId())
   598  	return result, nil
   599  }
   600  
   601  func (b *SBucket) DeleteWebSiteConf() error {
   602  	obscli, err := b.region.getOBSClient()
   603  	if err != nil {
   604  		return errors.Wrap(err, "GetOBSClient")
   605  	}
   606  	_, err = obscli.DeleteBucketWebsiteConfiguration(b.Name)
   607  	if err != nil {
   608  		return errors.Wrapf(err, "obscli.DeleteBucketWebsiteConfiguration(%s)", b.Name)
   609  	}
   610  	return nil
   611  }
   612  
   613  func (b *SBucket) SetCORS(rules []cloudprovider.SBucketCORSRule) error {
   614  	obscli, err := b.region.getOBSClient()
   615  	if err != nil {
   616  		return errors.Wrap(err, "GetOBSClient")
   617  	}
   618  	opts := []obs.CorsRule{}
   619  	for i := range rules {
   620  		opts = append(opts, obs.CorsRule{
   621  			AllowedOrigin: rules[i].AllowedOrigins,
   622  			AllowedMethod: rules[i].AllowedMethods,
   623  			AllowedHeader: rules[i].AllowedHeaders,
   624  			MaxAgeSeconds: rules[i].MaxAgeSeconds,
   625  			ExposeHeader:  rules[i].ExposeHeaders,
   626  		})
   627  	}
   628  
   629  	input := obs.SetBucketCorsInput{}
   630  	input.Bucket = b.Name
   631  	input.BucketCors.CorsRules = opts
   632  	_, err = obscli.SetBucketCors(&input)
   633  	if err != nil {
   634  		return errors.Wrapf(err, "obscli.SetBucketCors(%s)", jsonutils.Marshal(input).String())
   635  	}
   636  	return nil
   637  }
   638  
   639  func (b *SBucket) GetCORSRules() ([]cloudprovider.SBucketCORSRule, error) {
   640  	obscli, err := b.region.getOBSClient()
   641  	if err != nil {
   642  		return nil, errors.Wrap(err, "GetOBSClient")
   643  	}
   644  	conf, err := obscli.GetBucketCors(b.Name)
   645  	if err != nil {
   646  		if !strings.Contains(err.Error(), "NoSuchCORSConfiguration") {
   647  			return nil, errors.Wrapf(err, "obscli.GetBucketCors(%s)", b.Name)
   648  		}
   649  	}
   650  	if conf == nil {
   651  		return nil, nil
   652  	}
   653  	result := []cloudprovider.SBucketCORSRule{}
   654  	for i := range conf.CorsRules {
   655  		result = append(result, cloudprovider.SBucketCORSRule{
   656  			AllowedOrigins: conf.CorsRules[i].AllowedOrigin,
   657  			AllowedMethods: conf.CorsRules[i].AllowedMethod,
   658  			AllowedHeaders: conf.CorsRules[i].AllowedHeader,
   659  			MaxAgeSeconds:  conf.CorsRules[i].MaxAgeSeconds,
   660  			ExposeHeaders:  conf.CorsRules[i].ExposeHeader,
   661  			Id:             strconv.Itoa(i),
   662  		})
   663  	}
   664  	return result, nil
   665  }
   666  
   667  func (b *SBucket) DeleteCORS() error {
   668  	obscli, err := b.region.getOBSClient()
   669  	if err != nil {
   670  		return errors.Wrap(err, "GetOBSClient")
   671  	}
   672  
   673  	_, err = obscli.DeleteBucketCors(b.Name)
   674  	if err != nil {
   675  		return errors.Wrapf(err, "obscli.DeleteBucketCors(%s)", b.Name)
   676  	}
   677  	return nil
   678  }
   679  
   680  func (b *SBucket) GetTags() (map[string]string, error) {
   681  	obscli, err := b.region.getOBSClient()
   682  	if err != nil {
   683  		return nil, errors.Wrap(err, "GetOBSClient")
   684  	}
   685  	tagresult, err := obscli.GetBucketTagging(b.Name)
   686  	if err != nil {
   687  		if strings.Contains(err.Error(), "404") {
   688  			return nil, nil
   689  		}
   690  		return nil, errors.Wrapf(err, "osscli.GetBucketTagging(%s)", b.Name)
   691  	}
   692  	result := map[string]string{}
   693  	for i := range tagresult.Tags {
   694  		result[tagresult.Tags[i].Key] = tagresult.Tags[i].Value
   695  	}
   696  	return result, nil
   697  }
   698  
   699  func (b *SBucket) SetTags(tags map[string]string, replace bool) error {
   700  	obscli, err := b.region.getOBSClient()
   701  	if err != nil {
   702  		return errors.Wrap(err, "GetOBSClient")
   703  	}
   704  
   705  	_, err = obscli.DeleteBucketTagging(b.Name)
   706  	if err != nil {
   707  		return errors.Wrapf(err, "DeleteBucketTagging")
   708  	}
   709  
   710  	if len(tags) == 0 {
   711  		return nil
   712  	}
   713  
   714  	input := obs.SetBucketTaggingInput{BucketTagging: obs.BucketTagging{}}
   715  	input.Bucket = b.Name
   716  	for k, v := range tags {
   717  		input.BucketTagging.Tags = append(input.BucketTagging.Tags, obs.Tag{Key: k, Value: v})
   718  	}
   719  
   720  	_, err = obscli.SetBucketTagging(&input)
   721  	if err != nil {
   722  		return errors.Wrapf(err, "obscli.SetBucketTagging(%s)", jsonutils.Marshal(input).String())
   723  	}
   724  	return nil
   725  }
   726  
   727  func (b *SBucket) ListMultipartUploads() ([]cloudprovider.SBucketMultipartUploads, error) {
   728  	obscli, err := b.region.getOBSClient()
   729  	if err != nil {
   730  		return nil, errors.Wrap(err, "GetOBSClient")
   731  	}
   732  	result := []cloudprovider.SBucketMultipartUploads{}
   733  
   734  	input := obs.ListMultipartUploadsInput{Bucket: b.Name}
   735  	keyMarker := ""
   736  	uploadIDMarker := ""
   737  	for {
   738  		if len(keyMarker) > 0 {
   739  			input.KeyMarker = keyMarker
   740  		}
   741  		if len(uploadIDMarker) > 0 {
   742  			input.UploadIdMarker = uploadIDMarker
   743  		}
   744  
   745  		output, err := obscli.ListMultipartUploads(&input)
   746  		if err != nil {
   747  			return nil, errors.Wrap(err, " coscli.Bucket.ListMultipartUploads(context.Background(), &input)")
   748  		}
   749  		for i := range output.Uploads {
   750  			temp := cloudprovider.SBucketMultipartUploads{
   751  				ObjectName: output.Uploads[i].Key,
   752  				UploadID:   output.Uploads[i].UploadId,
   753  				Initiator:  output.Uploads[i].Initiator.DisplayName,
   754  				Initiated:  output.Uploads[i].Initiated,
   755  			}
   756  			result = append(result, temp)
   757  		}
   758  		keyMarker = output.NextKeyMarker
   759  		uploadIDMarker = output.NextUploadIdMarker
   760  		if !output.IsTruncated {
   761  			break
   762  		}
   763  	}
   764  
   765  	return result, nil
   766  }