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