Skip to content

Commit

Permalink
Enhancement: object storage api for object tagging operations
Browse files Browse the repository at this point in the history
Signed-off-by: Mofei Zhang <[email protected]>
  • Loading branch information
mervinkid committed Feb 4, 2020
1 parent aae85b2 commit 0009306
Show file tree
Hide file tree
Showing 7 changed files with 177 additions and 29 deletions.
104 changes: 99 additions & 5 deletions objectnode/api_handler_object.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ package objectnode
import (
"encoding/base64"
"encoding/hex"
"encoding/json"
"fmt"
"io"
"io/ioutil"
Expand All @@ -25,6 +26,8 @@ import (
"strconv"
"strings"

"github.com/chubaofs/chubaofs/proto"

"sync"
"syscall"

Expand Down Expand Up @@ -750,22 +753,113 @@ func (o *ObjectNode) deleteObjectHandler(w http.ResponseWriter, r *http.Request)

// Get object tagging
// API reference: https://docs.aws.amazon.com/AmazonS3/latest/API/API_GetObjectTagging.html
func (o *ObjectNode) getObjectTagging(w http.ResponseWriter, r *http.Request) {
func (o *ObjectNode) getObjectTaggingHandler(w http.ResponseWriter, r *http.Request) {
// TODO: implement handler 'GetObjectTagging'
var err error
var param *RequestParam
if param, err = o.parseRequestParam(r); err != nil {
log.LogErrorf("getObjectTaggingHandler: parse request param fail: requestID(%v) err(%v)", RequestIDFromRequest(r), err)
_ = InvalidArgument.ServeResponse(w, r)
return
}
if param.vol == nil {
_ = NoSuchBucket.ServeResponse(w, r)
return
}

var xattrInfo *proto.XAttrInfo
if xattrInfo, err = param.vol.GetXAttr(param.object, XAttrKeyOSSTagging); err != nil {
log.LogErrorf("getObjectTaggingHandler: volume get XAttr fail: requestID(%v) err(%v)", RequestIDFromRequest(r), err)
_ = InternalError.ServeResponse(w, r)
return
}

ossTaggingData := xattrInfo.Get(XAttrKeyOSSTagging)

var output = NewGetObjectTaggingOutput()
if err = json.Unmarshal(ossTaggingData, output); err != nil {
log.LogErrorf("getObjectTaggingHandler: decode tagging from json fail: requestID(%v) err(%v)", RequestIDFromRequest(r), err)
_ = InternalError.ServeResponse(w, r)
return
}

var encoded []byte
if encoded, err = MarshalXMLEntity(output); err != nil {
log.LogErrorf("getObjectTaggingHandler: encode output fail: requestID(%v) err(%v)", RequestIDFromRequest(r), err)
_ = InternalError.ServeResponse(w, r)
return
}

if _, err = w.Write(encoded); err != nil {
log.LogErrorf("getObjectTaggingHandler: write response fail: requestID(%v) err(%v)", RequestIDFromRequest(r), err)
}

return
}

// Put object tagging
// API reference: https://docs.aws.amazon.com/AmazonS3/latest/API/API_PutObjectTagging.html
func (o *ObjectNode) putObjectTagging(w http.ResponseWriter, r *http.Request) {
// TODO: implement handler 'PutObjectTagging'
func (o *ObjectNode) putObjectTaggingHandler(w http.ResponseWriter, r *http.Request) {
var err error
var param *RequestParam
if param, err = o.parseRequestParam(r); err != nil {
log.LogErrorf("putObjectTaggingHandler: parse request param fail: requestID(%v) err(%v)", RequestIDFromRequest(r), err)
_ = InvalidArgument.ServeResponse(w, r)
return
}
if param.vol == nil {
_ = NoSuchBucket.ServeResponse(w, r)
return
}

var requestBody []byte
if requestBody, err = ioutil.ReadAll(r.Body); err != nil {
log.LogErrorf("putObjectTaggingHandler: read request body data fail: requestID(%v) err(%v)", RequestIDFromRequest(r), err)
_ = InvalidArgument.ServeResponse(w, r)
return
}

var tagging = NewTagging()
if err = UnmarshalXMLEntity(requestBody, tagging); err != nil {
log.LogWarnf("putObjectTaggingHandler: decode request body fail: requestID(%v) err(%v)", RequestIDFromRequest(r), err)
_ = InvalidArgument.ServeResponse(w, r)
return
}

var encoded []byte
if encoded, err = json.Marshal(tagging); err != nil {
log.LogWarnf("putObjectTaggingHandler: encode tagging data fail: requestID(%v) err(%v)", RequestIDFromRequest(r), err)
_ = InternalError.ServeResponse(w, r)
return
}

if err = param.vol.SetXAttr(param.object, XAttrKeyOSSTagging, encoded); err != nil {
_ = InternalError.ServeResponse(w, r)
return
}

return
}

// Delete object tagging
// API reference: https://docs.aws.amazon.com/AmazonS3/latest/API/API_DeleteObjectTagging.html
func (o *ObjectNode) deleteObjectTagging(w http.ResponseWriter, r *http.Request) {
// TODO: implement handler 'DeleteObjectTagging'
func (o *ObjectNode) deleteObjectTaggingHandler(w http.ResponseWriter, r *http.Request) {
var err error
var param *RequestParam
if param, err = o.parseRequestParam(r); err != nil {
log.LogErrorf("deleteObjectTaggingHandler: parse request param fail: requestID(%v) err(%v)", RequestIDFromRequest(r), err)
_ = InvalidArgument.ServeResponse(w, r)
return
}
if param.vol == nil {
_ = NoSuchBucket.ServeResponse(w, r)
return
}

if err = param.vol.DeleteXAttr(param.object, XAttrKeyOSSTagging); err != nil {
_ = InternalError.ServeResponse(w, r)
return
}
return
}

Expand Down
4 changes: 2 additions & 2 deletions objectnode/const.go
Original file line number Diff line number Diff line change
Expand Up @@ -85,8 +85,8 @@ const (
// XAttr keys for ObjectNode compatible feature
const (
XAttrKeyOSSETag = "oss:etag"
XAttrKeyOSSTagging = "oss:tg"
XAttrKeyOSSPolicy = "oss:ply"
XAttrKeyOSSTagging = "oss:tagging"
XAttrKeyOSSPolicy = "oss:policy"
)

const (
Expand Down
10 changes: 5 additions & 5 deletions objectnode/fs_volume.go
Original file line number Diff line number Diff line change
Expand Up @@ -953,14 +953,14 @@ func (v *volume) FileInfo(path string) (info *FSFileInfo, err error) {
logger.Error("FileInfo: meta get xattr fail, inode(%v) path(%v) err(%v)", fileInode, path, err)
return
}
md5Val := xAttrInfo.XAttrs[XAttrKeyOSSETag]
md5Val := xAttrInfo.Get(XAttrKeyOSSETag)

info = &FSFileInfo{
Path: path,
Size: int64(fileInodeInfo.Size),
Mode: os.FileMode(fileInodeInfo.Mode),
ModifyTime: fileInodeInfo.ModifyTime,
ETag: md5Val,
ETag: string(md5Val),
Inode: fileInodeInfo.Inode,
}
return
Expand Down Expand Up @@ -1208,7 +1208,7 @@ func (v *volume) supplyListFileInfo(fileInfos []*FSFileInfo) (err error) {
return
}
for _, xAttrInfo := range batchXAttrInfos {
md5Map[xAttrInfo.Inode] = xAttrInfo.XAttrs[XAttrKeyOSSETag]
md5Map[xAttrInfo.Inode] = string(xAttrInfo.Get(XAttrKeyOSSETag))
}
for _, fileInfo := range fileInfos {
fileInfo.ETag = md5Map[fileInfo.Inode]
Expand Down Expand Up @@ -1347,14 +1347,14 @@ func (v *volume) CopyFile(targetPath, sourcePath string) (info *FSFileInfo, err
log.LogErrorf("CopyFile: meta set xattr fail: inode(%v) err(%v)", sourceFileInode, err)
return nil, err
}
md5Val := xAttrInfo.XAttrs[XAttrKeyOSSETag]
md5Val := xAttrInfo.Get(XAttrKeyOSSETag)

info = &FSFileInfo{
Path: targetPath,
Size: int64(inodeInfo.Size),
Mode: os.FileMode(inodeInfo.Size),
ModifyTime: inodeInfo.ModifyTime,
ETag: md5Val,
ETag: string(md5Val),
Inode: inodeInfo.Inode,
}
return
Expand Down
25 changes: 21 additions & 4 deletions objectnode/result.go
Original file line number Diff line number Diff line change
Expand Up @@ -251,13 +251,30 @@ type ListBucketResultV2 struct {
}

type Tag struct {
Key string `xml:"Key"`
Value string `xml:"Value"`
Key string `xml:"Key",json:"k"`
Value string `xml:"Value",json:"v"`
}

type Tagging struct {
XMLName xml.Name `xml:"Tagging"`
TagSet []*Tag `xml:"TagSet>Tag"`
XMLName xml.Name `json:"-"`
TagSet []*Tag `xml:"TagSet>Tag",json:"ts"`
}

func NewTagging() *Tagging {
return &Tagging{
XMLName: xml.Name{Local: "Tagging"},
}
}

func NewGetObjectTaggingOutput() *Tagging {
return &Tagging{
XMLName: xml.Name{Local: "GetObjectTaggingOutput"},
}
}

type GetObjectTaggingOutput struct {
XMLName xml.Name `xml:"GetObjectTaggingOutput"`
Tagging
}

type GetBucketLocationOutput struct {
Expand Down
45 changes: 35 additions & 10 deletions objectnode/result_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
package objectnode

import (
"encoding/json"
"fmt"
"testing"
"time"
Expand Down Expand Up @@ -193,16 +194,40 @@ func TestUnmarshalDeleteRequest(t *testing.T) {
}

func TestMarshalTagging(t *testing.T) {
tagging := &Tagging{
TagSet: []*Tag{
{
Key: "tag1",
Value: "val1",
},
{
Key: "tag2",
Value: "val2",
},
tagging := NewTagging()
tagging.TagSet = []*Tag{
{
Key: "tag1",
Value: "val1",
},
{
Key: "tag2",
Value: "val2",
},
}
marshaled, err := MarshalXMLEntity(tagging)
if err != nil {
t.Fatalf("marshal tagging fail: err(%v)", err)
}
t.Logf("xml result:\n%v", string(marshaled))

marshaled, err = json.Marshal(tagging)
if err != nil {
t.Fatalf("marshal tagging fail: err(%v)", err)
}
t.Logf("json result:\n%v", string(marshaled))
}

func TestMarshalGetObjectTaggingOutput(t *testing.T) {
tagging := NewGetObjectTaggingOutput()
tagging.TagSet = []*Tag{
{
Key: "tag1",
Value: "val1",
},
{
Key: "tag2",
Value: "val2",
},
}
marshaled, err := MarshalXMLEntity(tagging)
Expand Down
6 changes: 3 additions & 3 deletions objectnode/router.go
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,7 @@ func (o *ObjectNode) registerApiRouters(router *mux.Router) {
// API reference: https://docs.aws.amazon.com/AmazonS3/latest/API/API_GetObjectTagging.html
r.Methods(http.MethodGet).
Path("/{object:.+}").
HandlerFunc(o.policyCheck(o.getObjectTagging, []Action{GetBucketPolicyAction}, Read)).
HandlerFunc(o.policyCheck(o.getObjectTaggingHandler, []Action{GetBucketPolicyAction}, Read)).
Queries("tagging", "")

// Get object XAttr
Expand Down Expand Up @@ -179,7 +179,7 @@ func (o *ObjectNode) registerApiRouters(router *mux.Router) {
// API reference: https://docs.aws.amazon.com/AmazonS3/latest/API/API_PutObjectTagging.html
r.Methods(http.MethodPut).
Path("/{object:.+}").
HandlerFunc(o.policyCheck(o.putObjectTagging, []Action{PutObjectTaggingAction}, Write)).
HandlerFunc(o.policyCheck(o.putObjectTaggingHandler, []Action{PutObjectTaggingAction}, Write)).
Queries("tagging", "")

// Put object xattrs
Expand Down Expand Up @@ -232,7 +232,7 @@ func (o *ObjectNode) registerApiRouters(router *mux.Router) {
// API reference: https://docs.aws.amazon.com/AmazonS3/latest/API/API_DeleteObjectTagging.html
r.Methods(http.MethodDelete).
Path("/{object:.+").
HandlerFunc(o.policyCheck(o.deleteObjectTagging, []Action{PutBucketPolicyAction}, Write)).
HandlerFunc(o.policyCheck(o.deleteObjectTaggingHandler, []Action{PutBucketPolicyAction}, Write)).
Queries("tagging", "")

// Delete object xattrs
Expand Down
12 changes: 12 additions & 0 deletions proto/fs_proto.go
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,18 @@ type XAttrInfo struct {
XAttrs map[string]string
}

func (info XAttrInfo) Get(key string) []byte {
return []byte(info.XAttrs[key])
}

func (info XAttrInfo) VisitAll(visitor func(key string, value []byte) bool) {
for k, v := range info.XAttrs {
if visitor == nil || !visitor(k, []byte(v)) {
return
}
}
}

func (info XAttrInfo) String() string {
builder := strings.Builder{}
for k, v := range info.XAttrs {
Expand Down

0 comments on commit 0009306

Please sign in to comment.