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