yunion.io/x/cloudmux@v0.3.10-0-alpha.1/pkg/multicloud/apsara/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 apsara
    16  
    17  import (
    18  	"context"
    19  	"fmt"
    20  	"io"
    21  	"net/http"
    22  	"strconv"
    23  	"time"
    24  
    25  	"github.com/aliyun/aliyun-oss-go-sdk/oss"
    26  
    27  	"yunion.io/x/jsonutils"
    28  	"yunion.io/x/log"
    29  	"yunion.io/x/pkg/errors"
    30  
    31  	"yunion.io/x/cloudmux/pkg/cloudprovider"
    32  	"yunion.io/x/cloudmux/pkg/multicloud"
    33  )
    34  
    35  type SBucket struct {
    36  	multicloud.SBaseBucket
    37  	ApsaraTags
    38  
    39  	region *SRegion
    40  
    41  	Name         string
    42  	Location     string
    43  	CreationDate time.Time
    44  	StorageClass string
    45  
    46  	ExtranetEndpoint string
    47  	IntranetEndpoint string
    48  	DepartmentInfo
    49  }
    50  
    51  func (b *SBucket) GetGlobalId() string {
    52  	return b.Name
    53  }
    54  
    55  func (b *SBucket) GetName() string {
    56  	return b.Name
    57  }
    58  
    59  func (self *SBucket) GetOssClient() (*oss.Client, error) {
    60  	return self.region.GetOssClient()
    61  }
    62  
    63  func (b *SBucket) GetAcl() cloudprovider.TBucketACLType {
    64  	acl := b.region.GetBucketAcl(b.Name)
    65  	return cloudprovider.TBucketACLType(acl)
    66  }
    67  
    68  func (self *SRegion) GetBucketAcl(bucket string) string {
    69  	params := map[string]string{
    70  		"AccountInfo":      "aaa",
    71  		"x-acs-instanceid": bucket,
    72  		"Params":           jsonutils.Marshal(map[string]string{"BucketName": bucket, "acl": "acl"}).String(),
    73  	}
    74  	resp, err := self.ossRequest("GetBucketAcl", params)
    75  	if err != nil {
    76  		return ""
    77  	}
    78  	acl, _ := resp.GetString("Data", "AccessControlPolicy", "AccessControlList", "Grant")
    79  	return acl
    80  }
    81  
    82  func (b *SBucket) GetLocation() string {
    83  	return b.Location
    84  }
    85  
    86  func (b *SBucket) GetIRegion() cloudprovider.ICloudRegion {
    87  	return b.region
    88  }
    89  
    90  func (b *SBucket) GetCreatedAt() time.Time {
    91  	return b.CreationDate
    92  }
    93  
    94  func (b *SBucket) GetStorageClass() string {
    95  	return b.StorageClass
    96  }
    97  
    98  func (b *SBucket) GetAccessUrls() []cloudprovider.SBucketAccessUrl {
    99  	return []cloudprovider.SBucketAccessUrl{
   100  		{
   101  			Url:         fmt.Sprintf("%s.%s", b.Name, b.ExtranetEndpoint),
   102  			Description: "ExtranetEndpoint",
   103  			Primary:     true,
   104  		},
   105  	}
   106  }
   107  
   108  func (self *SRegion) GetBucketSize(bucket string, department int) (int64, error) {
   109  	params := map[string]string{
   110  		"Namespace":  "acs_oss_dashboard",
   111  		"MetricName": "MeteringStorageUtilization",
   112  		"Period":     "3600",
   113  		"Dimensions": jsonutils.Marshal([]map[string]string{
   114  			{"BucketName": bucket},
   115  		}).String(),
   116  		"Department": fmt.Sprintf("%d", department),
   117  		"StartTime":  strconv.FormatInt(time.Now().Add(time.Hour*-24*2).Unix()*1000, 10),
   118  		"EndTime":    strconv.FormatInt(time.Now().Unix()*1000, 10),
   119  	}
   120  	resp, err := self.client.metricsRequest("DescribeMetricList", params)
   121  	if err != nil {
   122  		return 0, nil
   123  	}
   124  	datapoints, err := resp.GetString("Datapoints")
   125  	if err != nil {
   126  		return 0, errors.Wrapf(err, "get datapoints")
   127  	}
   128  	obj, err := jsonutils.ParseString(datapoints)
   129  	if err != nil {
   130  		return 0, errors.Wrapf(err, "ParseString")
   131  	}
   132  	data := []struct {
   133  		Timestamp int64
   134  		Value     int64
   135  	}{}
   136  	obj.Unmarshal(&data)
   137  	for i := range data {
   138  		return data[i].Value, nil
   139  	}
   140  	return 0, fmt.Errorf("no storage metric found")
   141  }
   142  
   143  func (b *SBucket) GetStats() cloudprovider.SBucketStats {
   144  	ret := cloudprovider.SBucketStats{
   145  		SizeBytes:   -1,
   146  		ObjectCount: -1,
   147  	}
   148  	dep, _ := strconv.Atoi(b.Department)
   149  	size, _ := b.region.GetBucketSize(b.Name, dep)
   150  	if size > 0 {
   151  		ret.SizeBytes = size
   152  	}
   153  	return ret
   154  }
   155  
   156  func (self *SRegion) GetBucketCapacity(bucket string, department int) (int64, error) {
   157  	params := map[string]string{
   158  		"Params": jsonutils.Marshal(map[string]string{
   159  			"BucketName": bucket,
   160  			"region":     self.RegionId,
   161  		}).String(),
   162  		// 此参数必传,可以设任意值
   163  		"AccountInfo": "aaa",
   164  		"Department":  fmt.Sprintf("%d", department),
   165  	}
   166  	resp, err := self.ossRequest("GetBucketStorageCapacity", params)
   167  	if err != nil {
   168  		return 0, errors.Wrapf(err, "GetBucketStorageCapacity")
   169  	}
   170  	return resp.Int("Data", "BucketUserQos", "StorageCapacity")
   171  }
   172  
   173  func (b *SBucket) GetLimit() cloudprovider.SBucketStats {
   174  	ret := cloudprovider.SBucketStats{
   175  		SizeBytes:   -1,
   176  		ObjectCount: -1,
   177  	}
   178  	dep, _ := strconv.Atoi(b.Department)
   179  	capa, _ := b.region.GetBucketCapacity(b.Name, dep)
   180  	if capa > 0 {
   181  		ret.SizeBytes = capa * 1024 * 1024 * 1024
   182  	}
   183  	return ret
   184  }
   185  
   186  func (b *SBucket) LimitSupport() cloudprovider.SBucketStats {
   187  	return b.GetLimit()
   188  }
   189  
   190  func (b *SBucket) SetAcl(aclStr cloudprovider.TBucketACLType) error {
   191  	osscli, err := b.GetOssClient()
   192  	if err != nil {
   193  		return errors.Wrap(err, "b.region.GetOssClient")
   194  	}
   195  	acl, err := str2Acl(string(aclStr))
   196  	if err != nil {
   197  		return errors.Wrap(err, "str2Acl")
   198  	}
   199  	err = osscli.SetBucketACL(b.Name, acl)
   200  	if err != nil {
   201  		return errors.Wrap(err, "SetBucketACL")
   202  	}
   203  	return nil
   204  }
   205  
   206  func (b *SBucket) ListObjects(prefix string, marker string, delimiter string, maxCount int) (cloudprovider.SListObjectResult, error) {
   207  	result := cloudprovider.SListObjectResult{}
   208  	osscli, err := b.GetOssClient()
   209  	if err != nil {
   210  		return result, errors.Wrap(err, "GetOssClient")
   211  	}
   212  	bucket, err := osscli.Bucket(b.Name)
   213  	if err != nil {
   214  		return result, errors.Wrap(err, "Bucket")
   215  	}
   216  	opts := make([]oss.Option, 0)
   217  	if len(prefix) > 0 {
   218  		opts = append(opts, oss.Prefix(prefix))
   219  	}
   220  	if len(delimiter) > 0 {
   221  		opts = append(opts, oss.Delimiter(delimiter))
   222  	}
   223  	if len(marker) > 0 {
   224  		opts = append(opts, oss.Marker(marker))
   225  	}
   226  	if maxCount > 0 {
   227  		opts = append(opts, oss.MaxKeys(maxCount))
   228  	}
   229  	oResult, err := bucket.ListObjects(opts...)
   230  	if err != nil {
   231  		return result, errors.Wrap(err, "ListObjects")
   232  	}
   233  	result.Objects = make([]cloudprovider.ICloudObject, 0)
   234  	for _, object := range oResult.Objects {
   235  		obj := &SObject{
   236  			bucket: b,
   237  			SBaseCloudObject: cloudprovider.SBaseCloudObject{
   238  				StorageClass: object.StorageClass,
   239  				Key:          object.Key,
   240  				SizeBytes:    object.Size,
   241  				ETag:         object.ETag,
   242  				LastModified: object.LastModified,
   243  			},
   244  		}
   245  		result.Objects = append(result.Objects, obj)
   246  	}
   247  	if oResult.CommonPrefixes != nil {
   248  		result.CommonPrefixes = make([]cloudprovider.ICloudObject, len(oResult.CommonPrefixes))
   249  		for i, commPrefix := range oResult.CommonPrefixes {
   250  			result.CommonPrefixes[i] = &SObject{
   251  				bucket:           b,
   252  				SBaseCloudObject: cloudprovider.SBaseCloudObject{Key: commPrefix},
   253  			}
   254  		}
   255  	}
   256  	result.IsTruncated = oResult.IsTruncated
   257  	result.NextMarker = oResult.NextMarker
   258  	return result, nil
   259  }
   260  
   261  func metaOpts(opts []oss.Option, meta http.Header) []oss.Option {
   262  	for k, v := range meta {
   263  		if len(v) == 0 {
   264  			continue
   265  		}
   266  		switch http.CanonicalHeaderKey(k) {
   267  		case cloudprovider.META_HEADER_CONTENT_TYPE:
   268  			opts = append(opts, oss.ContentType(v[0]))
   269  		case cloudprovider.META_HEADER_CONTENT_MD5:
   270  			opts = append(opts, oss.ContentMD5(v[0]))
   271  		case cloudprovider.META_HEADER_CONTENT_LANGUAGE:
   272  			opts = append(opts, oss.ContentLanguage(v[0]))
   273  		case cloudprovider.META_HEADER_CONTENT_ENCODING:
   274  			opts = append(opts, oss.ContentEncoding(v[0]))
   275  		case cloudprovider.META_HEADER_CONTENT_DISPOSITION:
   276  			opts = append(opts, oss.ContentDisposition(v[0]))
   277  		case cloudprovider.META_HEADER_CACHE_CONTROL:
   278  			opts = append(opts, oss.CacheControl(v[0]))
   279  		default:
   280  			opts = append(opts, oss.Meta(http.CanonicalHeaderKey(k), v[0]))
   281  		}
   282  	}
   283  	return opts
   284  }
   285  
   286  func (b *SBucket) PutObject(ctx context.Context, key string, input io.Reader, sizeBytes int64, cannedAcl cloudprovider.TBucketACLType, storageClassStr string, meta http.Header) error {
   287  	osscli, err := b.GetOssClient()
   288  	if err != nil {
   289  		return errors.Wrap(err, "GetOssClient")
   290  	}
   291  	bucket, err := osscli.Bucket(b.Name)
   292  	if err != nil {
   293  		return errors.Wrap(err, "Bucket")
   294  	}
   295  	opts := make([]oss.Option, 0)
   296  	if sizeBytes > 0 {
   297  		opts = append(opts, oss.ContentLength(sizeBytes))
   298  	}
   299  	if meta != nil {
   300  		opts = metaOpts(opts, meta)
   301  	}
   302  	if len(cannedAcl) == 0 {
   303  		cannedAcl = b.GetAcl()
   304  	}
   305  	acl, err := str2Acl(string(cannedAcl))
   306  	if err != nil {
   307  		return errors.Wrap(err, "")
   308  	}
   309  	opts = append(opts, oss.ObjectACL(acl))
   310  	if len(storageClassStr) > 0 {
   311  		storageClass, err := str2StorageClass(storageClassStr)
   312  		if err != nil {
   313  			return errors.Wrap(err, "str2StorageClass")
   314  		}
   315  		opts = append(opts, oss.ObjectStorageClass(storageClass))
   316  	}
   317  	return bucket.PutObject(key, input, opts...)
   318  }
   319  
   320  func (b *SBucket) NewMultipartUpload(ctx context.Context, key string, cannedAcl cloudprovider.TBucketACLType, storageClassStr string, meta http.Header) (string, error) {
   321  	osscli, err := b.GetOssClient()
   322  	if err != nil {
   323  		return "", errors.Wrap(err, "GetOssClient")
   324  	}
   325  	bucket, err := osscli.Bucket(b.Name)
   326  	if err != nil {
   327  		return "", errors.Wrap(err, "Bucket")
   328  	}
   329  	opts := make([]oss.Option, 0)
   330  	if meta != nil {
   331  		opts = metaOpts(opts, meta)
   332  	}
   333  	if len(cannedAcl) == 0 {
   334  		cannedAcl = b.GetAcl()
   335  	}
   336  	acl, err := str2Acl(string(cannedAcl))
   337  	if err != nil {
   338  		return "", errors.Wrap(err, "str2Acl")
   339  	}
   340  	opts = append(opts, oss.ObjectACL(acl))
   341  	if len(storageClassStr) > 0 {
   342  		storageClass, err := str2StorageClass(storageClassStr)
   343  		if err != nil {
   344  			return "", errors.Wrap(err, "str2StorageClass")
   345  		}
   346  		opts = append(opts, oss.ObjectStorageClass(storageClass))
   347  	}
   348  	result, err := bucket.InitiateMultipartUpload(key, opts...)
   349  	if err != nil {
   350  		return "", errors.Wrap(err, "bucket.InitiateMultipartUpload")
   351  	}
   352  	return result.UploadID, nil
   353  }
   354  
   355  func (b *SBucket) UploadPart(ctx context.Context, key string, uploadId string, partIndex int, input io.Reader, partSize int64, offset, totalSize int64) (string, error) {
   356  	osscli, err := b.GetOssClient()
   357  	if err != nil {
   358  		return "", errors.Wrap(err, "GetOssClient")
   359  	}
   360  	bucket, err := osscli.Bucket(b.Name)
   361  	if err != nil {
   362  		return "", errors.Wrap(err, "Bucket")
   363  	}
   364  	imur := oss.InitiateMultipartUploadResult{
   365  		Bucket:   b.Name,
   366  		Key:      key,
   367  		UploadID: uploadId,
   368  	}
   369  	part, err := bucket.UploadPart(imur, input, partSize, partIndex)
   370  	if err != nil {
   371  		return "", errors.Wrap(err, "bucket.UploadPart")
   372  	}
   373  	if b.region.client.debug {
   374  		log.Debugf("upload part key:%s uploadId:%s partIndex:%d etag:%s", key, uploadId, partIndex, part.ETag)
   375  	}
   376  	return part.ETag, nil
   377  }
   378  
   379  func (b *SBucket) CompleteMultipartUpload(ctx context.Context, key string, uploadId string, partEtags []string) error {
   380  	osscli, err := b.GetOssClient()
   381  	if err != nil {
   382  		return errors.Wrap(err, "GetOssClient")
   383  	}
   384  	bucket, err := osscli.Bucket(b.Name)
   385  	if err != nil {
   386  		return errors.Wrap(err, "Bucket")
   387  	}
   388  	imur := oss.InitiateMultipartUploadResult{
   389  		Bucket:   b.Name,
   390  		Key:      key,
   391  		UploadID: uploadId,
   392  	}
   393  	parts := make([]oss.UploadPart, len(partEtags))
   394  	for i := range partEtags {
   395  		parts[i] = oss.UploadPart{
   396  			PartNumber: i + 1,
   397  			ETag:       partEtags[i],
   398  		}
   399  	}
   400  	result, err := bucket.CompleteMultipartUpload(imur, parts)
   401  	if err != nil {
   402  		return errors.Wrap(err, "bucket.CompleteMultipartUpload")
   403  	}
   404  	if b.region.client.debug {
   405  		log.Debugf("CompleteMultipartUpload bucket:%s key:%s etag:%s location:%s", result.Bucket, result.Key, result.ETag, result.Location)
   406  	}
   407  	return nil
   408  }
   409  
   410  func (b *SBucket) AbortMultipartUpload(ctx context.Context, key string, uploadId string) error {
   411  	osscli, err := b.GetOssClient()
   412  	if err != nil {
   413  		return errors.Wrap(err, "GetOssClient")
   414  	}
   415  	bucket, err := osscli.Bucket(b.Name)
   416  	if err != nil {
   417  		return errors.Wrap(err, "Bucket")
   418  	}
   419  	imur := oss.InitiateMultipartUploadResult{
   420  		Bucket:   b.Name,
   421  		Key:      key,
   422  		UploadID: uploadId,
   423  	}
   424  	err = bucket.AbortMultipartUpload(imur)
   425  	if err != nil {
   426  		return errors.Wrap(err, "AbortMultipartUpload")
   427  	}
   428  	return nil
   429  }
   430  
   431  func (b *SBucket) DeleteObject(ctx context.Context, key string) error {
   432  	osscli, err := b.GetOssClient()
   433  	if err != nil {
   434  		return errors.Wrap(err, "GetOssClient")
   435  	}
   436  	bucket, err := osscli.Bucket(b.Name)
   437  	if err != nil {
   438  		return errors.Wrap(err, "Bucket")
   439  	}
   440  	err = bucket.DeleteObject(key)
   441  	if err != nil {
   442  		return errors.Wrap(err, "DeleteObject")
   443  	}
   444  	return nil
   445  }
   446  
   447  func (b *SBucket) GetTempUrl(method string, key string, expire time.Duration) (string, error) {
   448  	if method != "GET" && method != "PUT" && method != "DELETE" {
   449  		return "", errors.Error("unsupported method")
   450  	}
   451  	osscli, err := b.GetOssClient()
   452  	if err != nil {
   453  		return "", errors.Wrap(err, "GetOssClient")
   454  	}
   455  	bucket, err := osscli.Bucket(b.Name)
   456  	if err != nil {
   457  		return "", errors.Wrap(err, "Bucket")
   458  	}
   459  	urlStr, err := bucket.SignURL(key, oss.HTTPMethod(method), int64(expire/time.Second))
   460  	if err != nil {
   461  		return "", errors.Wrap(err, "SignURL")
   462  	}
   463  	return urlStr, nil
   464  }
   465  
   466  func (b *SBucket) CopyObject(ctx context.Context, destKey string, srcBucket, srcKey string, cannedAcl cloudprovider.TBucketACLType, storageClassStr string, meta http.Header) error {
   467  	osscli, err := b.GetOssClient()
   468  	if err != nil {
   469  		return errors.Wrap(err, "GetOssClient")
   470  	}
   471  	bucket, err := osscli.Bucket(b.Name)
   472  	if err != nil {
   473  		return errors.Wrap(err, "Bucket")
   474  	}
   475  	opts := make([]oss.Option, 0)
   476  	if meta != nil {
   477  		opts = metaOpts(opts, meta)
   478  	}
   479  	if len(cannedAcl) == 0 {
   480  		cannedAcl = b.GetAcl()
   481  	}
   482  	acl, err := str2Acl(string(cannedAcl))
   483  	if err != nil {
   484  		return errors.Wrap(err, "str2Acl")
   485  	}
   486  	opts = append(opts, oss.ObjectACL(acl))
   487  	if len(storageClassStr) > 0 {
   488  		storageClass, err := str2StorageClass(storageClassStr)
   489  		if err != nil {
   490  			return errors.Wrap(err, "str2StorageClass")
   491  		}
   492  		opts = append(opts, oss.ObjectStorageClass(storageClass))
   493  	}
   494  	_, err = bucket.CopyObjectFrom(srcBucket, srcKey, destKey, opts...)
   495  	if err != nil {
   496  		return errors.Wrap(err, "CopyObjectFrom")
   497  	}
   498  	return nil
   499  }
   500  
   501  func (b *SBucket) GetObject(ctx context.Context, key string, rangeOpt *cloudprovider.SGetObjectRange) (io.ReadCloser, error) {
   502  	osscli, err := b.GetOssClient()
   503  	if err != nil {
   504  		return nil, errors.Wrap(err, "GetOssClient")
   505  	}
   506  	bucket, err := osscli.Bucket(b.Name)
   507  	if err != nil {
   508  		return nil, errors.Wrap(err, "Bucket")
   509  	}
   510  	opts := make([]oss.Option, 0)
   511  	if rangeOpt != nil {
   512  		opts = append(opts, oss.NormalizedRange(rangeOpt.String()))
   513  	}
   514  	output, err := bucket.GetObject(key, opts...)
   515  	if err != nil {
   516  		return nil, errors.Wrap(err, "bucket.GetObject")
   517  	}
   518  	return output, nil
   519  }
   520  
   521  func (b *SBucket) CopyPart(ctx context.Context, key string, uploadId string, partNumber int, srcBucket string, srcKey string, srcOffset int64, srcLength int64) (string, error) {
   522  	osscli, err := b.GetOssClient()
   523  	if err != nil {
   524  		return "", errors.Wrap(err, "GetOssClient")
   525  	}
   526  	bucket, err := osscli.Bucket(b.Name)
   527  	if err != nil {
   528  		return "", errors.Wrap(err, "Bucket")
   529  	}
   530  	imur := oss.InitiateMultipartUploadResult{
   531  		Bucket:   b.Name,
   532  		Key:      key,
   533  		UploadID: uploadId,
   534  	}
   535  	opts := make([]oss.Option, 0)
   536  	part, err := bucket.UploadPartCopy(imur, srcBucket, srcKey, srcOffset, srcLength, partNumber, opts...)
   537  	if err != nil {
   538  		return "", errors.Wrap(err, "bucket.UploadPartCopy")
   539  	}
   540  	return part.ETag, nil
   541  }