mirror of
https://github.com/minio/minio.git
synced 2024-12-24 06:05:55 -05:00
add gateway object tagging support (#9124)
This commit is contained in:
parent
c138272d63
commit
3f6d624c7b
19
cmd/fs-v1.go
19
cmd/fs-v1.go
@ -1218,8 +1218,8 @@ func (fs *FSObjects) ListObjects(ctx context.Context, bucket, prefix, marker, de
|
|||||||
fs.listDirFactory(), fs.getObjectInfo, fs.getObjectInfo)
|
fs.listDirFactory(), fs.getObjectInfo, fs.getObjectInfo)
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetObjectTag - get object tags from an existing object
|
// GetObjectTags - get object tags from an existing object
|
||||||
func (fs *FSObjects) GetObjectTag(ctx context.Context, bucket, object string) (*tags.Tags, error) {
|
func (fs *FSObjects) GetObjectTags(ctx context.Context, bucket, object string) (*tags.Tags, error) {
|
||||||
oi, err := fs.GetObjectInfo(ctx, bucket, object, ObjectOptions{})
|
oi, err := fs.GetObjectInfo(ctx, bucket, object, ObjectOptions{})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
@ -1228,8 +1228,8 @@ func (fs *FSObjects) GetObjectTag(ctx context.Context, bucket, object string) (*
|
|||||||
return tags.ParseObjectTags(oi.UserTags)
|
return tags.ParseObjectTags(oi.UserTags)
|
||||||
}
|
}
|
||||||
|
|
||||||
// PutObjectTag - replace or add tags to an existing object
|
// PutObjectTags - replace or add tags to an existing object
|
||||||
func (fs *FSObjects) PutObjectTag(ctx context.Context, bucket, object string, tags string) error {
|
func (fs *FSObjects) PutObjectTags(ctx context.Context, bucket, object string, tags string) error {
|
||||||
fsMetaPath := pathJoin(fs.fsPath, minioMetaBucket, bucketMetaPrefix, bucket, object, fs.metaJSONFile)
|
fsMetaPath := pathJoin(fs.fsPath, minioMetaBucket, bucketMetaPrefix, bucket, object, fs.metaJSONFile)
|
||||||
fsMeta := fsMetaV1{}
|
fsMeta := fsMetaV1{}
|
||||||
wlk, err := fs.rwPool.Write(fsMetaPath)
|
wlk, err := fs.rwPool.Write(fsMetaPath)
|
||||||
@ -1260,9 +1260,9 @@ func (fs *FSObjects) PutObjectTag(ctx context.Context, bucket, object string, ta
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// DeleteObjectTag - delete object tags from an existing object
|
// DeleteObjectTags - delete object tags from an existing object
|
||||||
func (fs *FSObjects) DeleteObjectTag(ctx context.Context, bucket, object string) error {
|
func (fs *FSObjects) DeleteObjectTags(ctx context.Context, bucket, object string) error {
|
||||||
return fs.PutObjectTag(ctx, bucket, object, "")
|
return fs.PutObjectTags(ctx, bucket, object, "")
|
||||||
}
|
}
|
||||||
|
|
||||||
// ReloadFormat - no-op for fs, Valid only for XL.
|
// ReloadFormat - no-op for fs, Valid only for XL.
|
||||||
@ -1360,6 +1360,11 @@ func (fs *FSObjects) IsCompressionSupported() bool {
|
|||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// IsTaggingSupported returns true, object tagging is supported in fs object layer.
|
||||||
|
func (fs *FSObjects) IsTaggingSupported() bool {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
// IsReady - Check if the backend disk is ready to accept traffic.
|
// IsReady - Check if the backend disk is ready to accept traffic.
|
||||||
func (fs *FSObjects) IsReady(_ context.Context) bool {
|
func (fs *FSObjects) IsReady(_ context.Context) bool {
|
||||||
if _, err := os.Stat(fs.fsPath); err != nil {
|
if _, err := os.Stat(fs.fsPath); err != nil {
|
||||||
|
@ -228,20 +228,20 @@ func (a GatewayUnsupported) DeleteBucketTagging(ctx context.Context, bucket stri
|
|||||||
return NotImplemented{}
|
return NotImplemented{}
|
||||||
}
|
}
|
||||||
|
|
||||||
// PutObjectTag - not implemented.
|
// PutObjectTags - not implemented.
|
||||||
func (a GatewayUnsupported) PutObjectTag(ctx context.Context, bucket, object string, tags string) error {
|
func (a GatewayUnsupported) PutObjectTags(ctx context.Context, bucket, object string, tags string) error {
|
||||||
logger.LogIf(ctx, NotImplemented{})
|
logger.LogIf(ctx, NotImplemented{})
|
||||||
return NotImplemented{}
|
return NotImplemented{}
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetObjectTag - not implemented.
|
// GetObjectTags - not implemented.
|
||||||
func (a GatewayUnsupported) GetObjectTag(ctx context.Context, bucket, object string) (*tags.Tags, error) {
|
func (a GatewayUnsupported) GetObjectTags(ctx context.Context, bucket, object string) (*tags.Tags, error) {
|
||||||
logger.LogIf(ctx, NotImplemented{})
|
logger.LogIf(ctx, NotImplemented{})
|
||||||
return nil, NotImplemented{}
|
return nil, NotImplemented{}
|
||||||
}
|
}
|
||||||
|
|
||||||
// DeleteObjectTag - not implemented.
|
// DeleteObjectTags - not implemented.
|
||||||
func (a GatewayUnsupported) DeleteObjectTag(ctx context.Context, bucket, object string) error {
|
func (a GatewayUnsupported) DeleteObjectTags(ctx context.Context, bucket, object string) error {
|
||||||
logger.LogIf(ctx, NotImplemented{})
|
logger.LogIf(ctx, NotImplemented{})
|
||||||
return NotImplemented{}
|
return NotImplemented{}
|
||||||
}
|
}
|
||||||
@ -261,6 +261,11 @@ func (a GatewayUnsupported) IsEncryptionSupported() bool {
|
|||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// IsTaggingSupported returns whether object tagging is supported or not for this layer.
|
||||||
|
func (a GatewayUnsupported) IsTaggingSupported() bool {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
// IsCompressionSupported returns whether compression is applicable for this layer.
|
// IsCompressionSupported returns whether compression is applicable for this layer.
|
||||||
func (a GatewayUnsupported) IsCompressionSupported() bool {
|
func (a GatewayUnsupported) IsCompressionSupported() bool {
|
||||||
return false
|
return false
|
||||||
|
@ -137,3 +137,7 @@ func (n *nasObjects) IsReady(ctx context.Context) bool {
|
|||||||
sinfo := n.ObjectLayer.StorageInfo(ctx, false)
|
sinfo := n.ObjectLayer.StorageInfo(ctx, false)
|
||||||
return sinfo.Backend.Type == minio.BackendFS
|
return sinfo.Backend.Type == minio.BackendFS
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (n *nasObjects) IsTaggingSupported() bool {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
@ -16,7 +16,9 @@
|
|||||||
|
|
||||||
package s3
|
package s3
|
||||||
|
|
||||||
import minio "github.com/minio/minio/cmd"
|
import (
|
||||||
|
minio "github.com/minio/minio/cmd"
|
||||||
|
)
|
||||||
|
|
||||||
// List of header keys to be filtered, usually
|
// List of header keys to be filtered, usually
|
||||||
// from all S3 API http responses.
|
// from all S3 API http responses.
|
||||||
|
@ -29,10 +29,12 @@ import (
|
|||||||
"github.com/minio/cli"
|
"github.com/minio/cli"
|
||||||
miniogo "github.com/minio/minio-go/v6"
|
miniogo "github.com/minio/minio-go/v6"
|
||||||
"github.com/minio/minio-go/v6/pkg/credentials"
|
"github.com/minio/minio-go/v6/pkg/credentials"
|
||||||
|
"github.com/minio/minio-go/v6/pkg/tags"
|
||||||
minio "github.com/minio/minio/cmd"
|
minio "github.com/minio/minio/cmd"
|
||||||
|
|
||||||
"github.com/minio/minio-go/v6/pkg/encrypt"
|
"github.com/minio/minio-go/v6/pkg/encrypt"
|
||||||
"github.com/minio/minio-go/v6/pkg/s3utils"
|
"github.com/minio/minio-go/v6/pkg/s3utils"
|
||||||
|
xhttp "github.com/minio/minio/cmd/http"
|
||||||
"github.com/minio/minio/cmd/logger"
|
"github.com/minio/minio/cmd/logger"
|
||||||
"github.com/minio/minio/pkg/auth"
|
"github.com/minio/minio/pkg/auth"
|
||||||
"github.com/minio/minio/pkg/bucket/policy"
|
"github.com/minio/minio/pkg/bucket/policy"
|
||||||
@ -459,11 +461,25 @@ func (l *s3Objects) GetObjectInfo(ctx context.Context, bucket string, object str
|
|||||||
// PutObject creates a new object with the incoming data,
|
// PutObject creates a new object with the incoming data,
|
||||||
func (l *s3Objects) PutObject(ctx context.Context, bucket string, object string, r *minio.PutObjReader, opts minio.ObjectOptions) (objInfo minio.ObjectInfo, err error) {
|
func (l *s3Objects) PutObject(ctx context.Context, bucket string, object string, r *minio.PutObjReader, opts minio.ObjectOptions) (objInfo minio.ObjectInfo, err error) {
|
||||||
data := r.Reader
|
data := r.Reader
|
||||||
|
var tagMap map[string]string
|
||||||
oi, err := l.Client.PutObject(bucket, object, data, data.Size(), data.MD5Base64String(), data.SHA256HexString(), minio.ToMinioClientMetadata(opts.UserDefined), opts.ServerSideEncryption)
|
if tagstr, ok := opts.UserDefined[xhttp.AmzObjectTagging]; ok && tagstr != "" {
|
||||||
|
tagObj, err := tags.ParseObjectTags(tagstr)
|
||||||
|
if err != nil {
|
||||||
|
return objInfo, minio.ErrorRespToObjectError(err, bucket, object)
|
||||||
|
}
|
||||||
|
tagMap = tagObj.ToMap()
|
||||||
|
delete(opts.UserDefined, xhttp.AmzObjectTagging)
|
||||||
|
}
|
||||||
|
putOpts := miniogo.PutObjectOptions{
|
||||||
|
UserMetadata: opts.UserDefined,
|
||||||
|
ServerSideEncryption: opts.ServerSideEncryption,
|
||||||
|
UserTags: tagMap,
|
||||||
|
}
|
||||||
|
oi, err := l.Client.PutObject(bucket, object, data, data.Size(), data.MD5Base64String(), data.SHA256HexString(), putOpts)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return objInfo, minio.ErrorRespToObjectError(err, bucket, object)
|
return objInfo, minio.ErrorRespToObjectError(err, bucket, object)
|
||||||
}
|
}
|
||||||
|
|
||||||
// On success, populate the key & metadata so they are present in the notification
|
// On success, populate the key & metadata so they are present in the notification
|
||||||
oi.Key = object
|
oi.Key = object
|
||||||
oi.Metadata = minio.ToMinioClientObjectInfoMetadata(opts.UserDefined)
|
oi.Metadata = minio.ToMinioClientObjectInfoMetadata(opts.UserDefined)
|
||||||
@ -490,6 +506,7 @@ func (l *s3Objects) CopyObject(ctx context.Context, srcBucket string, srcObject
|
|||||||
if dstOpts.ServerSideEncryption != nil {
|
if dstOpts.ServerSideEncryption != nil {
|
||||||
dstOpts.ServerSideEncryption.Marshal(header)
|
dstOpts.ServerSideEncryption.Marshal(header)
|
||||||
}
|
}
|
||||||
|
|
||||||
for k, v := range header {
|
for k, v := range header {
|
||||||
srcInfo.UserDefined[k] = v[0]
|
srcInfo.UserDefined[k] = v[0]
|
||||||
}
|
}
|
||||||
@ -530,8 +547,21 @@ func (l *s3Objects) ListMultipartUploads(ctx context.Context, bucket string, pre
|
|||||||
|
|
||||||
// NewMultipartUpload upload object in multiple parts
|
// NewMultipartUpload upload object in multiple parts
|
||||||
func (l *s3Objects) NewMultipartUpload(ctx context.Context, bucket string, object string, o minio.ObjectOptions) (uploadID string, err error) {
|
func (l *s3Objects) NewMultipartUpload(ctx context.Context, bucket string, object string, o minio.ObjectOptions) (uploadID string, err error) {
|
||||||
|
var tagMap map[string]string
|
||||||
|
if tagStr, ok := o.UserDefined[xhttp.AmzObjectTagging]; ok {
|
||||||
|
tagObj, err := tags.Parse(tagStr, true)
|
||||||
|
if err != nil {
|
||||||
|
return uploadID, minio.ErrorRespToObjectError(err, bucket, object)
|
||||||
|
}
|
||||||
|
tagMap = tagObj.ToMap()
|
||||||
|
delete(o.UserDefined, xhttp.AmzObjectTagging)
|
||||||
|
}
|
||||||
// Create PutObject options
|
// Create PutObject options
|
||||||
opts := miniogo.PutObjectOptions{UserMetadata: o.UserDefined, ServerSideEncryption: o.ServerSideEncryption}
|
opts := miniogo.PutObjectOptions{
|
||||||
|
UserMetadata: o.UserDefined,
|
||||||
|
ServerSideEncryption: o.ServerSideEncryption,
|
||||||
|
UserTags: tagMap,
|
||||||
|
}
|
||||||
uploadID, err = l.Client.NewMultipartUpload(bucket, object, opts)
|
uploadID, err = l.Client.NewMultipartUpload(bucket, object, opts)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return uploadID, minio.ErrorRespToObjectError(err, bucket, object)
|
return uploadID, minio.ErrorRespToObjectError(err, bucket, object)
|
||||||
@ -643,6 +673,47 @@ func (l *s3Objects) DeleteBucketPolicy(ctx context.Context, bucket string) error
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// GetObjectTags gets the tags set on the object
|
||||||
|
func (l *s3Objects) GetObjectTags(ctx context.Context, bucket string, object string) (*tags.Tags, error) {
|
||||||
|
var err error
|
||||||
|
var tagObj *tags.Tags
|
||||||
|
var tagStr string
|
||||||
|
var opts minio.ObjectOptions
|
||||||
|
|
||||||
|
if _, err = l.GetObjectInfo(ctx, bucket, object, opts); err != nil {
|
||||||
|
return nil, minio.ErrorRespToObjectError(err, bucket, object)
|
||||||
|
}
|
||||||
|
|
||||||
|
if tagStr, err = l.Client.GetObjectTagging(bucket, object); err != nil {
|
||||||
|
return nil, minio.ErrorRespToObjectError(err, bucket, object)
|
||||||
|
}
|
||||||
|
|
||||||
|
if tagObj, err = tags.ParseObjectXML(strings.NewReader(tagStr)); err != nil {
|
||||||
|
return nil, minio.ErrorRespToObjectError(err, bucket, object)
|
||||||
|
}
|
||||||
|
return tagObj, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// PutObjectTags attaches the tags to the object
|
||||||
|
func (l *s3Objects) PutObjectTags(ctx context.Context, bucket, object string, tagStr string) error {
|
||||||
|
tagObj, err := tags.Parse(tagStr, true)
|
||||||
|
if err != nil {
|
||||||
|
return minio.ErrorRespToObjectError(err, bucket, object)
|
||||||
|
}
|
||||||
|
if err = l.Client.PutObjectTagging(bucket, object, tagObj.ToMap()); err != nil {
|
||||||
|
return minio.ErrorRespToObjectError(err, bucket, object)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeleteObjectTags removes the tags attached to the object
|
||||||
|
func (l *s3Objects) DeleteObjectTags(ctx context.Context, bucket, object string) error {
|
||||||
|
if err := l.Client.RemoveObjectTagging(bucket, object); err != nil {
|
||||||
|
return minio.ErrorRespToObjectError(err, bucket, object)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
// IsCompressionSupported returns whether compression is applicable for this layer.
|
// IsCompressionSupported returns whether compression is applicable for this layer.
|
||||||
func (l *s3Objects) IsCompressionSupported() bool {
|
func (l *s3Objects) IsCompressionSupported() bool {
|
||||||
return false
|
return false
|
||||||
@ -657,3 +728,7 @@ func (l *s3Objects) IsEncryptionSupported() bool {
|
|||||||
func (l *s3Objects) IsReady(ctx context.Context) bool {
|
func (l *s3Objects) IsReady(ctx context.Context) bool {
|
||||||
return minio.IsBackendOnline(ctx, l.HTTPClient, l.Client.EndpointURL().String())
|
return minio.IsBackendOnline(ctx, l.HTTPClient, l.Client.EndpointURL().String())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (l *s3Objects) IsTaggingSupported() bool {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
@ -58,7 +58,7 @@ const (
|
|||||||
|
|
||||||
// S3 object tagging
|
// S3 object tagging
|
||||||
AmzObjectTagging = "X-Amz-Tagging"
|
AmzObjectTagging = "X-Amz-Tagging"
|
||||||
AmzTagCount = "X-Amz-Tag-Count"
|
AmzTagCount = "X-Amz-Tagging-Count"
|
||||||
AmzTagDirective = "X-Amz-Tagging-Directive"
|
AmzTagDirective = "X-Amz-Tagging-Directive"
|
||||||
|
|
||||||
// S3 extensions
|
// S3 extensions
|
||||||
|
@ -123,8 +123,11 @@ type ObjectLayer interface {
|
|||||||
// Check Readiness
|
// Check Readiness
|
||||||
IsReady(ctx context.Context) bool
|
IsReady(ctx context.Context) bool
|
||||||
|
|
||||||
|
// Object Tagging Support check.
|
||||||
|
IsTaggingSupported() bool
|
||||||
|
|
||||||
// ObjectTagging operations
|
// ObjectTagging operations
|
||||||
PutObjectTag(context.Context, string, string, string) error
|
PutObjectTags(context.Context, string, string, string) error
|
||||||
GetObjectTag(context.Context, string, string) (*tags.Tags, error)
|
GetObjectTags(context.Context, string, string) (*tags.Tags, error)
|
||||||
DeleteObjectTag(context.Context, string, string) error
|
DeleteObjectTags(context.Context, string, string) error
|
||||||
}
|
}
|
||||||
|
@ -1072,6 +1072,7 @@ func (api objectAPIHandlers) CopyObjectHandler(w http.ResponseWriter, r *http.Re
|
|||||||
// If x-amz-tagging-directive header is REPLACE, get passed tags.
|
// If x-amz-tagging-directive header is REPLACE, get passed tags.
|
||||||
if isDirectiveReplace(r.Header.Get(xhttp.AmzTagDirective)) {
|
if isDirectiveReplace(r.Header.Get(xhttp.AmzTagDirective)) {
|
||||||
objTags = r.Header.Get(xhttp.AmzObjectTagging)
|
objTags = r.Header.Get(xhttp.AmzObjectTagging)
|
||||||
|
srcInfo.UserDefined[xhttp.AmzTagDirective] = replaceDirective
|
||||||
if _, err := tags.ParseObjectTags(objTags); err != nil {
|
if _, err := tags.ParseObjectTags(objTags); err != nil {
|
||||||
writeErrorResponse(ctx, w, toAPIError(ctx, err), r.URL, guessIsBrowserReq(r))
|
writeErrorResponse(ctx, w, toAPIError(ctx, err), r.URL, guessIsBrowserReq(r))
|
||||||
return
|
return
|
||||||
@ -1147,8 +1148,18 @@ func (api objectAPIHandlers) CopyObjectHandler(w http.ResponseWriter, r *http.Re
|
|||||||
writeErrorResponse(ctx, w, toAPIError(ctx, rerr), r.URL, guessIsBrowserReq(r))
|
writeErrorResponse(ctx, w, toAPIError(ctx, rerr), r.URL, guessIsBrowserReq(r))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
tag, err := tags.ParseObjectTags(objTags)
|
||||||
|
if err != nil {
|
||||||
|
writeErrorResponse(ctx, w, toAPIError(ctx, err), r.URL, guessIsBrowserReq(r))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
opts := miniogo.PutObjectOptions{
|
||||||
|
UserMetadata: srcInfo.UserDefined,
|
||||||
|
ServerSideEncryption: dstOpts.ServerSideEncryption,
|
||||||
|
UserTags: tag.ToMap(),
|
||||||
|
}
|
||||||
remoteObjInfo, rerr := client.PutObject(dstBucket, dstObject, srcInfo.Reader,
|
remoteObjInfo, rerr := client.PutObject(dstBucket, dstObject, srcInfo.Reader,
|
||||||
srcInfo.Size, "", "", srcInfo.UserDefined, dstOpts.ServerSideEncryption)
|
srcInfo.Size, "", "", opts)
|
||||||
if rerr != nil {
|
if rerr != nil {
|
||||||
writeErrorResponse(ctx, w, toAPIError(ctx, rerr), r.URL, guessIsBrowserReq(r))
|
writeErrorResponse(ctx, w, toAPIError(ctx, rerr), r.URL, guessIsBrowserReq(r))
|
||||||
return
|
return
|
||||||
@ -1284,6 +1295,11 @@ func (api objectAPIHandlers) PutObjectHandler(w http.ResponseWriter, r *http.Req
|
|||||||
}
|
}
|
||||||
|
|
||||||
if objTags := r.Header.Get(xhttp.AmzObjectTagging); objTags != "" {
|
if objTags := r.Header.Get(xhttp.AmzObjectTagging); objTags != "" {
|
||||||
|
if !objectAPI.IsTaggingSupported() {
|
||||||
|
writeErrorResponse(ctx, w, errorCodes.ToAPIErr(ErrNotImplemented), r.URL, guessIsBrowserReq(r))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
if _, err := tags.ParseObjectTags(objTags); err != nil {
|
if _, err := tags.ParseObjectTags(objTags); err != nil {
|
||||||
writeErrorResponse(ctx, w, toAPIError(ctx, err), r.URL, guessIsBrowserReq(r))
|
writeErrorResponse(ctx, w, toAPIError(ctx, err), r.URL, guessIsBrowserReq(r))
|
||||||
return
|
return
|
||||||
@ -2946,6 +2962,10 @@ func (api objectAPIHandlers) GetObjectTaggingHandler(w http.ResponseWriter, r *h
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if !objAPI.IsTaggingSupported() {
|
||||||
|
writeErrorResponse(ctx, w, errorCodes.ToAPIErr(ErrNotImplemented), r.URL, guessIsBrowserReq(r))
|
||||||
|
return
|
||||||
|
}
|
||||||
// Allow getObjectTagging if policy action is set.
|
// Allow getObjectTagging if policy action is set.
|
||||||
if s3Error := checkRequestAuthType(ctx, r, policy.GetObjectTaggingAction, bucket, object); s3Error != ErrNone {
|
if s3Error := checkRequestAuthType(ctx, r, policy.GetObjectTaggingAction, bucket, object); s3Error != ErrNone {
|
||||||
writeErrorResponse(ctx, w, errorCodes.ToAPIErr(s3Error), r.URL, guessIsBrowserReq(r))
|
writeErrorResponse(ctx, w, errorCodes.ToAPIErr(s3Error), r.URL, guessIsBrowserReq(r))
|
||||||
@ -2953,7 +2973,7 @@ func (api objectAPIHandlers) GetObjectTaggingHandler(w http.ResponseWriter, r *h
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Get object tags
|
// Get object tags
|
||||||
tags, err := objAPI.GetObjectTag(ctx, bucket, object)
|
tags, err := objAPI.GetObjectTags(ctx, bucket, object)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
writeErrorResponse(ctx, w, toAPIError(ctx, err), r.URL, guessIsBrowserReq(r))
|
writeErrorResponse(ctx, w, toAPIError(ctx, err), r.URL, guessIsBrowserReq(r))
|
||||||
return
|
return
|
||||||
@ -2980,6 +3000,10 @@ func (api objectAPIHandlers) PutObjectTaggingHandler(w http.ResponseWriter, r *h
|
|||||||
writeErrorResponse(ctx, w, errorCodes.ToAPIErr(ErrServerNotInitialized), r.URL, guessIsBrowserReq(r))
|
writeErrorResponse(ctx, w, errorCodes.ToAPIErr(ErrServerNotInitialized), r.URL, guessIsBrowserReq(r))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
if !objAPI.IsTaggingSupported() {
|
||||||
|
writeErrorResponse(ctx, w, errorCodes.ToAPIErr(ErrNotImplemented), r.URL, guessIsBrowserReq(r))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
// Allow putObjectTagging if policy action is set
|
// Allow putObjectTagging if policy action is set
|
||||||
if s3Error := checkRequestAuthType(ctx, r, policy.PutObjectTaggingAction, bucket, object); s3Error != ErrNone {
|
if s3Error := checkRequestAuthType(ctx, r, policy.PutObjectTaggingAction, bucket, object); s3Error != ErrNone {
|
||||||
@ -2994,7 +3018,7 @@ func (api objectAPIHandlers) PutObjectTaggingHandler(w http.ResponseWriter, r *h
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Put object tags
|
// Put object tags
|
||||||
err = objAPI.PutObjectTag(ctx, bucket, object, tags.String())
|
err = objAPI.PutObjectTags(ctx, bucket, object, tags.String())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
writeErrorResponse(ctx, w, toAPIError(ctx, err), r.URL, guessIsBrowserReq(r))
|
writeErrorResponse(ctx, w, toAPIError(ctx, err), r.URL, guessIsBrowserReq(r))
|
||||||
return
|
return
|
||||||
@ -3013,6 +3037,10 @@ func (api objectAPIHandlers) DeleteObjectTaggingHandler(w http.ResponseWriter, r
|
|||||||
writeErrorResponse(ctx, w, errorCodes.ToAPIErr(ErrServerNotInitialized), r.URL, guessIsBrowserReq(r))
|
writeErrorResponse(ctx, w, errorCodes.ToAPIErr(ErrServerNotInitialized), r.URL, guessIsBrowserReq(r))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
if !objAPI.IsTaggingSupported() {
|
||||||
|
writeErrorResponse(ctx, w, errorCodes.ToAPIErr(ErrNotImplemented), r.URL, guessIsBrowserReq(r))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
vars := mux.Vars(r)
|
vars := mux.Vars(r)
|
||||||
bucket := vars["bucket"]
|
bucket := vars["bucket"]
|
||||||
@ -3029,7 +3057,7 @@ func (api objectAPIHandlers) DeleteObjectTaggingHandler(w http.ResponseWriter, r
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Delete object tags
|
// Delete object tags
|
||||||
if err = objAPI.DeleteObjectTag(ctx, bucket, object); err != nil && err != errConfigNotFound {
|
if err = objAPI.DeleteObjectTags(ctx, bucket, object); err != nil && err != errConfigNotFound {
|
||||||
writeErrorResponse(ctx, w, toAPIError(ctx, err), r.URL, guessIsBrowserReq(r))
|
writeErrorResponse(ctx, w, toAPIError(ctx, err), r.URL, guessIsBrowserReq(r))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@ -588,6 +588,10 @@ func (s *xlSets) IsCompressionSupported() bool {
|
|||||||
return s.getHashedSet("").IsCompressionSupported()
|
return s.getHashedSet("").IsCompressionSupported()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (s *xlSets) IsTaggingSupported() bool {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
// DeleteBucket - deletes a bucket on all sets simultaneously,
|
// DeleteBucket - deletes a bucket on all sets simultaneously,
|
||||||
// even if one of the sets fail to delete buckets, we proceed to
|
// even if one of the sets fail to delete buckets, we proceed to
|
||||||
// undo a successful operation.
|
// undo a successful operation.
|
||||||
@ -1684,19 +1688,19 @@ func (s *xlSets) HealObjects(ctx context.Context, bucket, prefix string, opts ma
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// PutObjectTag - replace or add tags to an existing object
|
// PutObjectTags - replace or add tags to an existing object
|
||||||
func (s *xlSets) PutObjectTag(ctx context.Context, bucket, object string, tags string) error {
|
func (s *xlSets) PutObjectTags(ctx context.Context, bucket, object string, tags string) error {
|
||||||
return s.getHashedSet(object).PutObjectTag(ctx, bucket, object, tags)
|
return s.getHashedSet(object).PutObjectTags(ctx, bucket, object, tags)
|
||||||
}
|
}
|
||||||
|
|
||||||
// DeleteObjectTag - delete object tags from an existing object
|
// DeleteObjectTags - delete object tags from an existing object
|
||||||
func (s *xlSets) DeleteObjectTag(ctx context.Context, bucket, object string) error {
|
func (s *xlSets) DeleteObjectTags(ctx context.Context, bucket, object string) error {
|
||||||
return s.getHashedSet(object).DeleteObjectTag(ctx, bucket, object)
|
return s.getHashedSet(object).DeleteObjectTags(ctx, bucket, object)
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetObjectTag - get object tags from an existing object
|
// GetObjectTags - get object tags from an existing object
|
||||||
func (s *xlSets) GetObjectTag(ctx context.Context, bucket, object string) (*tags.Tags, error) {
|
func (s *xlSets) GetObjectTags(ctx context.Context, bucket, object string) (*tags.Tags, error) {
|
||||||
return s.getHashedSet(object).GetObjectTag(ctx, bucket, object)
|
return s.getHashedSet(object).GetObjectTags(ctx, bucket, object)
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetMetrics - no op
|
// GetMetrics - no op
|
||||||
|
@ -269,3 +269,7 @@ func (xl xlObjects) IsEncryptionSupported() bool {
|
|||||||
func (xl xlObjects) IsCompressionSupported() bool {
|
func (xl xlObjects) IsCompressionSupported() bool {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (xl xlObjects) IsTaggingSupported() bool {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
@ -1009,8 +1009,8 @@ func (xl xlObjects) addPartialUpload(bucket, key string) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// PutObjectTag - replace or add tags to an existing object
|
// PutObjectTags - replace or add tags to an existing object
|
||||||
func (xl xlObjects) PutObjectTag(ctx context.Context, bucket, object string, tags string) error {
|
func (xl xlObjects) PutObjectTags(ctx context.Context, bucket, object string, tags string) error {
|
||||||
disks := xl.getDisks()
|
disks := xl.getDisks()
|
||||||
|
|
||||||
// Read metadata associated with the object from all disks.
|
// Read metadata associated with the object from all disks.
|
||||||
@ -1046,13 +1046,13 @@ func (xl xlObjects) PutObjectTag(ctx context.Context, bucket, object string, tag
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// DeleteObjectTag - delete object tags from an existing object
|
// DeleteObjectTags - delete object tags from an existing object
|
||||||
func (xl xlObjects) DeleteObjectTag(ctx context.Context, bucket, object string) error {
|
func (xl xlObjects) DeleteObjectTags(ctx context.Context, bucket, object string) error {
|
||||||
return xl.PutObjectTag(ctx, bucket, object, "")
|
return xl.PutObjectTags(ctx, bucket, object, "")
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetObjectTag - get object tags from an existing object
|
// GetObjectTags - get object tags from an existing object
|
||||||
func (xl xlObjects) GetObjectTag(ctx context.Context, bucket, object string) (*tags.Tags, error) {
|
func (xl xlObjects) GetObjectTags(ctx context.Context, bucket, object string) (*tags.Tags, error) {
|
||||||
// GetObjectInfo will return tag value as well
|
// GetObjectInfo will return tag value as well
|
||||||
oi, err := xl.GetObjectInfo(ctx, bucket, object, ObjectOptions{})
|
oi, err := xl.GetObjectInfo(ctx, bucket, object, ObjectOptions{})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -1223,6 +1223,10 @@ func (z *xlZones) IsCompressionSupported() bool {
|
|||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (z *xlZones) IsTaggingSupported() bool {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
// DeleteBucket - deletes a bucket on all zones simultaneously,
|
// DeleteBucket - deletes a bucket on all zones simultaneously,
|
||||||
// even if one of the zones fail to delete buckets, we proceed to
|
// even if one of the zones fail to delete buckets, we proceed to
|
||||||
// undo a successful operation.
|
// undo a successful operation.
|
||||||
@ -1538,13 +1542,13 @@ func (z *xlZones) IsReady(ctx context.Context) bool {
|
|||||||
return z.zones[0].IsReady(ctx)
|
return z.zones[0].IsReady(ctx)
|
||||||
}
|
}
|
||||||
|
|
||||||
// PutObjectTag - replace or add tags to an existing object
|
// PutObjectTags - replace or add tags to an existing object
|
||||||
func (z *xlZones) PutObjectTag(ctx context.Context, bucket, object string, tags string) error {
|
func (z *xlZones) PutObjectTags(ctx context.Context, bucket, object string, tags string) error {
|
||||||
if z.SingleZone() {
|
if z.SingleZone() {
|
||||||
return z.zones[0].PutObjectTag(ctx, bucket, object, tags)
|
return z.zones[0].PutObjectTags(ctx, bucket, object, tags)
|
||||||
}
|
}
|
||||||
for _, zone := range z.zones {
|
for _, zone := range z.zones {
|
||||||
err := zone.PutObjectTag(ctx, bucket, object, tags)
|
err := zone.PutObjectTags(ctx, bucket, object, tags)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if isErrBucketNotFound(err) {
|
if isErrBucketNotFound(err) {
|
||||||
continue
|
continue
|
||||||
@ -1558,13 +1562,13 @@ func (z *xlZones) PutObjectTag(ctx context.Context, bucket, object string, tags
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// DeleteObjectTag - delete object tags from an existing object
|
// DeleteObjectTags - delete object tags from an existing object
|
||||||
func (z *xlZones) DeleteObjectTag(ctx context.Context, bucket, object string) error {
|
func (z *xlZones) DeleteObjectTags(ctx context.Context, bucket, object string) error {
|
||||||
if z.SingleZone() {
|
if z.SingleZone() {
|
||||||
return z.zones[0].DeleteObjectTag(ctx, bucket, object)
|
return z.zones[0].DeleteObjectTags(ctx, bucket, object)
|
||||||
}
|
}
|
||||||
for _, zone := range z.zones {
|
for _, zone := range z.zones {
|
||||||
err := zone.DeleteObjectTag(ctx, bucket, object)
|
err := zone.DeleteObjectTags(ctx, bucket, object)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if isErrBucketNotFound(err) {
|
if isErrBucketNotFound(err) {
|
||||||
continue
|
continue
|
||||||
@ -1578,13 +1582,13 @@ func (z *xlZones) DeleteObjectTag(ctx context.Context, bucket, object string) er
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetObjectTag - get object tags from an existing object
|
// GetObjectTags - get object tags from an existing object
|
||||||
func (z *xlZones) GetObjectTag(ctx context.Context, bucket, object string) (*tags.Tags, error) {
|
func (z *xlZones) GetObjectTags(ctx context.Context, bucket, object string) (*tags.Tags, error) {
|
||||||
if z.SingleZone() {
|
if z.SingleZone() {
|
||||||
return z.zones[0].GetObjectTag(ctx, bucket, object)
|
return z.zones[0].GetObjectTags(ctx, bucket, object)
|
||||||
}
|
}
|
||||||
for _, zone := range z.zones {
|
for _, zone := range z.zones {
|
||||||
tags, err := zone.GetObjectTag(ctx, bucket, object)
|
tags, err := zone.GetObjectTags(ctx, bucket, object)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if isErrBucketNotFound(err) {
|
if isErrBucketNotFound(err) {
|
||||||
continue
|
continue
|
||||||
|
2
go.mod
2
go.mod
@ -66,7 +66,7 @@ require (
|
|||||||
github.com/minio/hdfs/v3 v3.0.1
|
github.com/minio/hdfs/v3 v3.0.1
|
||||||
github.com/minio/highwayhash v1.0.0
|
github.com/minio/highwayhash v1.0.0
|
||||||
github.com/minio/lsync v1.0.1
|
github.com/minio/lsync v1.0.1
|
||||||
github.com/minio/minio-go/v6 v6.0.55-0.20200425081427-89eebdef2af0
|
github.com/minio/minio-go/v6 v6.0.56-0.20200522164946-44a5f2e3b76b
|
||||||
github.com/minio/parquet-go v0.0.0-20200414234858-838cfa8aae61
|
github.com/minio/parquet-go v0.0.0-20200414234858-838cfa8aae61
|
||||||
github.com/minio/sha256-simd v0.1.1
|
github.com/minio/sha256-simd v0.1.1
|
||||||
github.com/minio/simdjson-go v0.1.5-0.20200303142138-b17fe061ea37
|
github.com/minio/simdjson-go v0.1.5-0.20200303142138-b17fe061ea37
|
||||||
|
4
go.sum
4
go.sum
@ -267,6 +267,10 @@ github.com/minio/lsync v1.0.1/go.mod h1:tCFzfo0dlvdGl70IT4IAK/5Wtgb0/BrTmo/jE8pA
|
|||||||
github.com/minio/minio-go/v6 v6.0.53/go.mod h1:DIvC/IApeHX8q1BAMVCXSXwpmrmM+I+iBvhvztQorfI=
|
github.com/minio/minio-go/v6 v6.0.53/go.mod h1:DIvC/IApeHX8q1BAMVCXSXwpmrmM+I+iBvhvztQorfI=
|
||||||
github.com/minio/minio-go/v6 v6.0.55-0.20200425081427-89eebdef2af0 h1:PdHKpM9h2vqCDr1AjJdK8e/6tRdOSjUNzIqeNmxu7ak=
|
github.com/minio/minio-go/v6 v6.0.55-0.20200425081427-89eebdef2af0 h1:PdHKpM9h2vqCDr1AjJdK8e/6tRdOSjUNzIqeNmxu7ak=
|
||||||
github.com/minio/minio-go/v6 v6.0.55-0.20200425081427-89eebdef2af0/go.mod h1:KQMM+/44DSlSGSQWSfRrAZ12FVMmpWNuX37i2AX0jfI=
|
github.com/minio/minio-go/v6 v6.0.55-0.20200425081427-89eebdef2af0/go.mod h1:KQMM+/44DSlSGSQWSfRrAZ12FVMmpWNuX37i2AX0jfI=
|
||||||
|
github.com/minio/minio-go/v6 v6.0.56-0.20200522005053-e9bc14bbccf9 h1:GpjSzFSjmNL9+SOzdJaQ6vxdvpFjhJR+xjtoK00Rle4=
|
||||||
|
github.com/minio/minio-go/v6 v6.0.56-0.20200522005053-e9bc14bbccf9/go.mod h1:KQMM+/44DSlSGSQWSfRrAZ12FVMmpWNuX37i2AX0jfI=
|
||||||
|
github.com/minio/minio-go/v6 v6.0.56-0.20200522164946-44a5f2e3b76b h1:hY8tAl7MwUuUB9ZqVDXEnrIqCuEy3dfU1RH0cZ7gu+o=
|
||||||
|
github.com/minio/minio-go/v6 v6.0.56-0.20200522164946-44a5f2e3b76b/go.mod h1:KQMM+/44DSlSGSQWSfRrAZ12FVMmpWNuX37i2AX0jfI=
|
||||||
github.com/minio/parquet-go v0.0.0-20200414234858-838cfa8aae61 h1:pUSI/WKPdd77gcuoJkSzhJ4wdS8OMDOsOu99MtpXEQA=
|
github.com/minio/parquet-go v0.0.0-20200414234858-838cfa8aae61 h1:pUSI/WKPdd77gcuoJkSzhJ4wdS8OMDOsOu99MtpXEQA=
|
||||||
github.com/minio/parquet-go v0.0.0-20200414234858-838cfa8aae61/go.mod h1:4trzEJ7N1nBTd5Tt7OCZT5SEin+WiAXpdJ/WgPkESA8=
|
github.com/minio/parquet-go v0.0.0-20200414234858-838cfa8aae61/go.mod h1:4trzEJ7N1nBTd5Tt7OCZT5SEin+WiAXpdJ/WgPkESA8=
|
||||||
github.com/minio/sha256-simd v0.1.1 h1:5QHSlgo3nt5yKOJrC7W8w7X+NFl8cMPZm96iu8kKUJU=
|
github.com/minio/sha256-simd v0.1.1 h1:5QHSlgo3nt5yKOJrC7W8w7X+NFl8cMPZm96iu8kKUJU=
|
||||||
|
@ -626,7 +626,7 @@ func testObjectTagging(s3Client *s3.S3) {
|
|||||||
|
|
||||||
func testObjectTaggingErrors(s3Client *s3.S3) {
|
func testObjectTaggingErrors(s3Client *s3.S3) {
|
||||||
startTime := time.Now()
|
startTime := time.Now()
|
||||||
function := "testObjectTagging"
|
function := "testObjectTaggingErrors"
|
||||||
bucket := randString(60, rand.NewSource(time.Now().UnixNano()), "aws-sdk-go-test-")
|
bucket := randString(60, rand.NewSource(time.Now().UnixNano()), "aws-sdk-go-test-")
|
||||||
object := randString(60, rand.NewSource(time.Now().UnixNano()), "")
|
object := randString(60, rand.NewSource(time.Now().UnixNano()), "")
|
||||||
args := map[string]interface{}{
|
args := map[string]interface{}{
|
||||||
|
Loading…
Reference in New Issue
Block a user