Kaynağa Gözat

add api for archive, add restore object, modify put bucket, getbucketinfo

dengwu12 8 yıl önce
ebeveyn
işleme
49d34b7e5d
6 değiştirilmiş dosya ile 231 ekleme ve 21 silme
  1. 24 0
      oss/bucket.go
  2. 70 15
      oss/bucket_test.go
  3. 32 0
      oss/client.go
  4. 82 5
      oss/client_test.go
  5. 14 0
      oss/const.go
  6. 9 1
      oss/type.go

+ 24 - 0
oss/bucket.go

@@ -606,6 +606,9 @@ func (bucket Bucket) GetObjectACL(objectKey string) (GetObjectACLResult, error)
 // 如果试图添加的文件已经存在,并且有访问权限。新添加的文件将覆盖原来的文件。
 // 如果在PutSymlink的时候,携带以x-oss-meta-为前缀的参数,则视为user meta。
 //
+// symObjectKey 要创建的符号链接文件。
+// targetObjectKey 目标文件。
+//
 // error 操作无错误为nil,非nil为错误信息。
 //
 func (bucket Bucket) PutSymlink(symObjectKey string, targetObjectKey string, options ...Option) error {
@@ -642,6 +645,27 @@ func (bucket Bucket) GetSymlink(objectKey string) (http.Header, error) {
 	return resp.Headers, err
 }
 
+//
+// RestoreObject 恢复处于冷冻状态的归档类型Object进入读就绪状态。
+//
+// 如果是针对该Object第一次调用restore接口,则返回成功。
+// 如果已经成功调用过restore接口,且restore没有完全完成,再次调用时返回409,错误码:RestoreAlreadyInProgress。
+// 如果已经成功调用过restore接口,且restore已经完成,再次调用时返回成功,且会将object的可下载时间延长一天,最多延长7天。
+// 如果object不存在,则返回404。
+//
+// objectKey 需要恢复状态的object名称。
+//
+// error 操作无错误为nil,非nil为错误信息。
+//
+func (bucket Bucket) RestoreObject(objectKey string) error {
+	resp, err := bucket.do("POST", objectKey, "restore", "restore", nil, nil, nil)
+	if err != nil {
+		return err
+	}
+	defer resp.Body.Close()
+	return checkRespCode(resp.StatusCode, []int{http.StatusOK, http.StatusAccepted})
+}
+
 // Private
 func (bucket Bucket) do(method, objectName, urlParams, subResource string, options []Option,
 	data io.Reader, listener ProgressListener) (*Response, error) {

+ 70 - 15
oss/bucket_test.go

@@ -21,8 +21,9 @@ import (
 )
 
 type OssBucketSuite struct {
-	client *Client
-	bucket *Bucket
+	client        *Client
+	bucket        *Bucket
+	archiveBucket *Bucket
 }
 
 var _ = Suite(&OssBucketSuite{})
@@ -39,34 +40,45 @@ func (s *OssBucketSuite) SetUpSuite(c *C) {
 	s.client = client
 
 	s.client.CreateBucket(bucketName)
+
+	cbConfig := CreateBucketConfiguration{StorageClass: StorageArchive}
+	err = s.client.DoCreateBucket(archiveBucketName, cbConfig)
+	c.Assert(err, IsNil)
+
 	time.Sleep(5 * time.Second)
 
 	bucket, err := s.client.Bucket(bucketName)
 	c.Assert(err, IsNil)
 	s.bucket = bucket
 
+	archiveBucket, err := s.client.Bucket(archiveBucketName)
+	c.Assert(err, IsNil)
+	s.archiveBucket = archiveBucket
+
 	testLogger.Println("test bucket started")
 }
 
 // Run before each test or benchmark starts running
 func (s *OssBucketSuite) TearDownSuite(c *C) {
-	// Delete Multipart
-	lmu, err := s.bucket.ListMultipartUploads()
-	c.Assert(err, IsNil)
-
-	for _, upload := range lmu.Uploads {
-		imur := InitiateMultipartUploadResult{Bucket: bucketName, Key: upload.Key, UploadID: upload.UploadID}
-		err = s.bucket.AbortMultipartUpload(imur)
+	for _, bucket := range []*Bucket{s.bucket, s.archiveBucket} {
+		// Delete Multipart
+		lmu, err := bucket.ListMultipartUploads()
 		c.Assert(err, IsNil)
-	}
 
-	// Delete Objects
-	lor, err := s.bucket.ListObjects()
-	c.Assert(err, IsNil)
+		for _, upload := range lmu.Uploads {
+			imur := InitiateMultipartUploadResult{Bucket: bucketName, Key: upload.Key, UploadID: upload.UploadID}
+			err = bucket.AbortMultipartUpload(imur)
+			c.Assert(err, IsNil)
+		}
 
-	for _, object := range lor.Objects {
-		err = s.bucket.DeleteObject(object.Key)
+		// Delete Objects
+		lor, err := bucket.ListObjects()
 		c.Assert(err, IsNil)
+
+		for _, object := range lor.Objects {
+			err = bucket.DeleteObject(object.Key)
+			c.Assert(err, IsNil)
+		}
 	}
 
 	testLogger.Println("test bucket completed")
@@ -1590,6 +1602,49 @@ func (s *OssBucketSuite) TestSymlink(c *C) {
 	c.Assert(err, IsNil)
 }
 
+// TestRestoreObject
+func (s *OssBucketSuite) TestRestoreObject(c *C) {
+	objectName := objectNamePrefix + "restore"
+
+	// List Object
+	lor, err := s.archiveBucket.ListObjects()
+	c.Assert(err, IsNil)
+	left := len(lor.Objects)
+
+	// Put three object
+	err = s.archiveBucket.PutObject(objectName, strings.NewReader(""))
+	c.Assert(err, IsNil)
+
+	// List
+	lor, err = s.archiveBucket.ListObjects()
+	c.Assert(err, IsNil)
+	c.Assert(len(lor.Objects), Equals, left+1)
+	for _, object := range lor.Objects {
+		c.Assert(object.StorageClass, Equals, string(StorageArchive))
+	}
+
+	// Head Object
+	meta, err := s.archiveBucket.GetObjectDetailedMeta(objectName)
+	c.Assert(err, IsNil)
+	_, ok := meta["X-Oss-Restore"]
+	c.Assert(ok, Equals, false)
+	c.Assert(meta.Get("X-Oss-Storage-Class"), Equals, "Archive")
+
+	// Error Restore
+	err = s.archiveBucket.RestoreObject("notexistobject")
+	c.Assert(err, NotNil)
+
+	// Restore Object
+	err = s.archiveBucket.RestoreObject(objectName)
+	c.Assert(err, IsNil)
+
+	// Head Object
+	meta, err = s.archiveBucket.GetObjectDetailedMeta(objectName)
+	c.Assert(err, IsNil)
+	c.Assert(meta.Get("X-Oss-Restore"), Equals, "ongoing-request=\"true\"")
+	c.Assert(meta.Get("X-Oss-Storage-Class"), Equals, "Archive")
+}
+
 // private
 func createFileAndWrite(fileName string, data []byte) error {
 	os.Remove(fileName)

+ 32 - 0
oss/client.go

@@ -106,6 +106,38 @@ func (client Client) CreateBucket(bucketName string, options ...Option) error {
 	return checkRespCode(resp.StatusCode, []int{http.StatusOK})
 }
 
+//
+// DoCreateBucket 创建Bucket。
+//
+// bucketName bucket名称,在整个OSS中具有全局唯一性,且不能修改。bucket名称的只能包括小写字母,数字和短横线-,
+// 必须以小写字母或者数字开头,长度必须在3-255字节之间。
+// options  创建bucket的选项。您可以使用选项ACL,指定bucket的访问权限。Bucket有以下三种访问权限,私有读写(ACLPrivate)、
+// 公共读私有写(ACLPublicRead),公共读公共写(ACLPublicReadWrite),默认访问权限是私有读写。
+//
+// error 操作无错误时返回nil,非nil为错误信息。
+//
+func (client Client) DoCreateBucket(bucketName string, cbConfig CreateBucketConfiguration, options ...Option) error {
+	bs, err := xml.Marshal(cbConfig)
+	if err != nil {
+		return err
+	}
+	buffer := new(bytes.Buffer)
+	buffer.Write(bs)
+
+	contentType := http.DetectContentType(buffer.Bytes())
+	headers := make(map[string]string)
+	handleOptions(headers, options)
+	headers[HTTPHeaderContentType] = contentType
+
+	resp, err := client.do("PUT", bucketName, "", "", headers, buffer)
+	if err != nil {
+		return err
+	}
+
+	defer resp.Body.Close()
+	return checkRespCode(resp.StatusCode, []int{http.StatusOK})
+}
+
 //
 // ListBuckets 获取当前用户下的bucket。
 //

+ 82 - 5
oss/client_test.go

@@ -44,7 +44,8 @@ const (
 	// prefix of bucket name for bucket ops test
 	bucketNamePrefix = "go-sdk-test-bucket-xyz-"
 	// bucket name for object ops test
-	bucketName = "go-sdk-test-bucket-xyz-for-object"
+	bucketName        = "go-sdk-test-bucket-xyz-for-object"
+	archiveBucketName = "go-sdk-test-bucket-xyz-for-archive"
 	// object name for object ops test
 	objectNamePrefix = "go-sdk-test-object-xyz-"
 	// sts region is one and only hangzhou
@@ -66,8 +67,7 @@ func (s *OssClientSuite) SetUpSuite(c *C) {
 	c.Assert(err, IsNil)
 
 	for _, bucket := range lbr.Buckets {
-		err = client.DeleteBucket(bucket.Name)
-		c.Assert(err, IsNil)
+		s.deleteBucket(client, bucket.Name, c)
 	}
 
 	testLogger.Println("test client started")
@@ -82,13 +82,41 @@ func (s *OssClientSuite) TearDownSuite(c *C) {
 	c.Assert(err, IsNil)
 
 	for _, bucket := range lbr.Buckets {
-		err = client.DeleteBucket(bucket.Name)
-		c.Assert(err, IsNil)
+		s.deleteBucket(client, bucket.Name, c)
 	}
 
 	testLogger.Println("test client completed")
 }
 
+func (s *OssClientSuite) deleteBucket(client *Client, bucketName string, c *C) {
+	bucket, err := client.Bucket(bucketName)
+	c.Assert(err, IsNil)
+
+	// Delete Object
+	lor, err := bucket.ListObjects()
+	c.Assert(err, IsNil)
+
+	for _, object := range lor.Objects {
+		err = bucket.DeleteObject(object.Key)
+		c.Assert(err, IsNil)
+	}
+
+	// Delete Part
+	lmur, err := bucket.ListMultipartUploads()
+	c.Assert(err, IsNil)
+
+	for _, upload := range lmur.Uploads {
+		var imur = InitiateMultipartUploadResult{Bucket: bucketName,
+			Key: upload.Key, UploadID: upload.UploadID}
+		err = bucket.AbortMultipartUpload(imur)
+		c.Assert(err, IsNil)
+	}
+
+	// Delete Bucket
+	err = client.DeleteBucket(bucketName)
+	c.Assert(err, IsNil)
+}
+
 // Run after each test or benchmark runs
 func (s *OssClientSuite) SetUpTest(c *C) {
 }
@@ -160,6 +188,46 @@ func (s *OssClientSuite) TestCreateBucket(c *C) {
 	// Delete
 	err = client.DeleteBucket(bucketNameTest)
 	c.Assert(err, IsNil)
+
+	// create bucket with config and test get bucket info
+	for _, storage := range []StorageClassType{StorageStandard, StorageIA, StorageArchive} {
+		cbConfig := CreateBucketConfiguration{StorageClass: storage}
+		err = client.DoCreateBucket(bucketNameTest, cbConfig, ACL(ACLPublicRead))
+		c.Assert(err, IsNil)
+
+		res, err := client.GetBucketInfo(bucketNameTest)
+		c.Assert(err, IsNil)
+		c.Assert(res.BucketInfo.Name, Equals, bucketNameTest)
+		c.Assert(res.BucketInfo.StorageClass, Equals, string(storage))
+		c.Assert(res.BucketInfo.ACL, Equals, string(ACLPublicRead))
+
+		// Delete
+		err = client.DeleteBucket(bucketNameTest)
+		c.Assert(err, IsNil)
+	}
+
+	// error put bucket with config
+	cbConfig := CreateBucketConfiguration{StorageClass: StorageArchive}
+	err = client.DoCreateBucket("ERRORBUCKETNAME", cbConfig)
+	c.Assert(err, NotNil)
+
+	// create bucket with config and test list bucket
+	for _, storage := range []StorageClassType{StorageStandard, StorageIA, StorageArchive} {
+		cbConfig := CreateBucketConfiguration{StorageClass: storage}
+		err = client.DoCreateBucket(bucketNameTest, cbConfig)
+		c.Assert(err, IsNil)
+
+		res, err := client.ListBuckets()
+		c.Assert(err, IsNil)
+		exist, b := s.getBucket(res.Buckets, bucketNameTest)
+		c.Assert(exist, Equals, true)
+		c.Assert(b.Name, Equals, bucketNameTest)
+		c.Assert(b.StorageClass, Equals, string(storage))
+
+		// Delete
+		err = client.DeleteBucket(bucketNameTest)
+		c.Assert(err, IsNil)
+	}
 }
 
 // TestCreateBucketNegative
@@ -1379,3 +1447,12 @@ func (s *OssClientSuite) checkBucket(buckets []BucketProperties, bucket string)
 	}
 	return false
 }
+
+func (s *OssClientSuite) getBucket(buckets []BucketProperties, bucket string) (bool, BucketProperties) {
+	for _, v := range buckets {
+		if v.Name == bucket {
+			return true, v
+		}
+	}
+	return false, BucketProperties{}
+}

+ 14 - 0
oss/const.go

@@ -30,6 +30,20 @@ const (
 	MetaReplace MetadataDirectiveType = "REPLACE"
 )
 
+// StorageClassType Bucket的存储类型
+type StorageClassType string
+
+const (
+	// StorageStandard 标准存储模式
+	StorageStandard StorageClassType = "Standard"
+
+	// StorageIA IA存储模式
+	StorageIA StorageClassType = "IA"
+
+	// StorageArchive Archive存储模式
+	StorageArchive StorageClassType = "Archive"
+)
+
 // Http头标签
 const (
 	HTTPHeaderAcceptEncoding     string = "Accept-Encoding"

+ 9 - 1
oss/type.go

@@ -24,6 +24,7 @@ type BucketProperties struct {
 	Name         string    `xml:"Name"`         // Bucket名称
 	Location     string    `xml:"Location"`     // Bucket所在的数据中心
 	CreationDate time.Time `xml:"CreationDate"` // Bucket创建时间
+	StorageClass string    `xml:"StorageClass"` // Bucket的存储方式
 }
 
 // GetBucketACLResult GetBucketACL请求返回的结果
@@ -204,6 +205,7 @@ type BucketInfo struct {
 	IntranetEndpoint string    `xml:"IntranetEndpoint"`        // Bucket访问的内网域名
 	ACL              string    `xml:"AccessControlList>Grant"` // Bucket权限
 	Owner            Owner     `xml:"Owner"`                   // Bucket拥有者信息
+	StorageClass     string    `xml:"StorageClass"`            // Bucket存储类型
 }
 
 // ListObjectsResult ListObjects请求返回结果
@@ -228,7 +230,7 @@ type ObjectProperties struct {
 	ETag         string    `xml:"ETag"`         // 标示Object的内容
 	Owner        Owner     `xml:"Owner"`        // 保存Object拥有者信息的容器
 	LastModified time.Time `xml:"LastModified"` // Object最后修改时间
-	StorageClass string    `xml:"StorageClass"` // Object的存储类型,目前只能是Standard
+	StorageClass string    `xml:"StorageClass"` // Object的存储类型
 }
 
 // Owner Bucket/Object的owner
@@ -440,3 +442,9 @@ func decodeListMultipartUploadResult(result *ListMultipartUploadResult) error {
 	}
 	return nil
 }
+
+// CreateBucketConfiguration 规则的过期属性
+type CreateBucketConfiguration struct {
+	XMLName      xml.Name         `xml:"CreateBucketConfiguration"`
+	StorageClass StorageClassType `xml:"StorageClass,omitempty"`
+}