From 5502fb97c3e19c1359dee028bfce137f6ad793c9 Mon Sep 17 00:00:00 2001 From: Denis Kirillov Date: Thu, 19 Aug 2021 15:33:02 +0300 Subject: [PATCH] [#196] Add tagging caching Signed-off-by: Denis Kirillov --- api/cache/buckets.go | 6 +-- api/handler/tagging.go | 29 ++++++++++++++ api/layer/layer.go | 88 ++++++++++++++++++++++------------------- api/layer/versioning.go | 61 +++------------------------- docs/s3_test_results.md | 20 +++++----- 5 files changed, 96 insertions(+), 108 deletions(-) diff --git a/api/cache/buckets.go b/api/cache/buckets.go index 667e3653..c9aedbaf 100644 --- a/api/cache/buckets.go +++ b/api/cache/buckets.go @@ -71,7 +71,7 @@ func (b *BucketInfo) SettingsObjectName() string { return bktVersionSettingsObject } -// SettingsObjectKey is key to use in SystemCache. -func (b *BucketInfo) SettingsObjectKey() string { - return b.Name + bktVersionSettingsObject +// SystemObjectKey is key to use in SystemCache. +func (b *BucketInfo) SystemObjectKey(obj string) string { + return b.Name + obj } diff --git a/api/handler/tagging.go b/api/handler/tagging.go index 7ba84fc9..2dc73a9b 100644 --- a/api/handler/tagging.go +++ b/api/handler/tagging.go @@ -30,6 +30,11 @@ func (h *handler) PutObjectTaggingHandler(w http.ResponseWriter, r *http.Request return } + if err = h.checkBucketOwner(r, reqInfo.BucketName); err != nil { + h.logAndSendError(w, "expected owner doesn't match", reqInfo, err) + return + } + p := &layer.HeadObjectParams{ Bucket: reqInfo.BucketName, Object: reqInfo.ObjectName, @@ -56,6 +61,11 @@ func (h *handler) PutObjectTaggingHandler(w http.ResponseWriter, r *http.Request func (h *handler) GetObjectTaggingHandler(w http.ResponseWriter, r *http.Request) { reqInfo := api.GetReqInfo(r.Context()) + if err := h.checkBucketOwner(r, reqInfo.BucketName); err != nil { + h.logAndSendError(w, "expected owner doesn't match", reqInfo, err) + return + } + p := &layer.HeadObjectParams{ Bucket: reqInfo.BucketName, Object: reqInfo.ObjectName, @@ -89,6 +99,11 @@ func (h *handler) DeleteObjectTaggingHandler(w http.ResponseWriter, r *http.Requ VersionID: reqInfo.URL.Query().Get("versionId"), } + if err := h.checkBucketOwner(r, reqInfo.BucketName); err != nil { + h.logAndSendError(w, "expected owner doesn't match", reqInfo, err) + return + } + objInfo, err := h.obj.GetObjectInfo(r.Context(), p) if err != nil { h.logAndSendError(w, "could not get object info", reqInfo, err) @@ -111,6 +126,11 @@ func (h *handler) PutBucketTaggingHandler(w http.ResponseWriter, r *http.Request return } + if err = h.checkBucketOwner(r, reqInfo.BucketName); err != nil { + h.logAndSendError(w, "expected owner doesn't match", reqInfo, err) + return + } + if err := h.obj.PutBucketTagging(r.Context(), reqInfo.BucketName, tagSet); err != nil { h.logAndSendError(w, "could not put object tagging", reqInfo, err) } @@ -119,6 +139,11 @@ func (h *handler) PutBucketTaggingHandler(w http.ResponseWriter, r *http.Request func (h *handler) GetBucketTaggingHandler(w http.ResponseWriter, r *http.Request) { reqInfo := api.GetReqInfo(r.Context()) + if err := h.checkBucketOwner(r, reqInfo.BucketName); err != nil { + h.logAndSendError(w, "expected owner doesn't match", reqInfo, err) + return + } + tagSet, err := h.obj.GetBucketTagging(r.Context(), reqInfo.BucketName) if err != nil { h.logAndSendError(w, "could not get object tagging", reqInfo, err) @@ -132,6 +157,10 @@ func (h *handler) GetBucketTaggingHandler(w http.ResponseWriter, r *http.Request func (h *handler) DeleteBucketTaggingHandler(w http.ResponseWriter, r *http.Request) { reqInfo := api.GetReqInfo(r.Context()) + if err := h.checkBucketOwner(r, reqInfo.BucketName); err != nil { + h.logAndSendError(w, "expected owner doesn't match", reqInfo, err) + return + } if err := h.obj.DeleteBucketTagging(r.Context(), reqInfo.BucketName); err != nil { h.logAndSendError(w, "could not delete object tagging", reqInfo, err) } diff --git a/api/layer/layer.go b/api/layer/layer.go index 9463075f..60e9b192 100644 --- a/api/layer/layer.go +++ b/api/layer/layer.go @@ -348,28 +348,6 @@ func (n *layer) GetObjectInfo(ctx context.Context, p *HeadObjectParams) (*Object return n.headVersion(ctx, bkt, p.VersionID) } -func (n *layer) getSettingsObjectInfo(ctx context.Context, bkt *cache.BucketInfo) (*ObjectInfo, error) { - if meta := n.systemCache.Get(bkt.SettingsObjectKey()); meta != nil { - return objInfoFromMeta(bkt, meta), nil - } - - oid, err := n.objectFindID(ctx, &findParams{cid: bkt.CID, attr: objectSystemAttributeName, val: bkt.SettingsObjectName()}) - if err != nil { - return nil, err - } - - meta, err := n.objectHead(ctx, bkt.CID, oid) - if err != nil { - n.log.Error("could not fetch object head", zap.Error(err)) - return nil, err - } - if err = n.systemCache.Put(bkt.SettingsObjectKey(), meta); err != nil { - n.log.Error("couldn't cache system object", zap.Error(err)) - } - - return objInfoFromMeta(bkt, meta), nil -} - // PutObject into storage. func (n *layer) PutObject(ctx context.Context, p *PutObjectParams) (*ObjectInfo, error) { bkt, err := n.GetBucketInfo(ctx, p.Bucket) @@ -458,19 +436,30 @@ func (n *layer) PutBucketTagging(ctx context.Context, bucketName string, tagSet // DeleteObjectTagging from storage. func (n *layer) DeleteObjectTagging(ctx context.Context, p *ObjectInfo) error { - return n.deleteSystemObject(ctx, p.CID(), p.TagsObject()) -} - -func (n *layer) deleteSystemObject(ctx context.Context, bktCID *cid.ID, name string) error { - oid, err := n.objectFindID(ctx, &findParams{cid: bktCID, attr: objectSystemAttributeName, val: name}) + bktInfo, err := n.GetBucketInfo(ctx, p.Bucket) if err != nil { - if errors.IsS3Error(err, errors.ErrNoSuchKey) { - return nil - } return err } + return n.deleteSystemObject(ctx, bktInfo, p.TagsObject()) +} - return n.objectDelete(ctx, bktCID, oid) +func (n *layer) deleteSystemObject(ctx context.Context, bktInfo *cache.BucketInfo, name string) error { + var oid *object.ID + if meta := n.systemCache.Get(bktInfo.SystemObjectKey(name)); meta != nil { + oid = meta.ID() + } else { + var err error + oid, err = n.objectFindID(ctx, &findParams{cid: bktInfo.CID, attr: objectSystemAttributeName, val: name}) + if err != nil { + if errors.IsS3Error(err, errors.ErrNoSuchKey) { + return nil + } + return err + } + } + + n.systemCache.Delete(bktInfo.SystemObjectKey(name)) + return n.objectDelete(ctx, bktInfo.CID, oid) } // DeleteBucketTagging from storage. @@ -480,13 +469,21 @@ func (n *layer) DeleteBucketTagging(ctx context.Context, bucketName string) erro return err } - return n.deleteSystemObject(ctx, bktInfo.CID, formBucketTagObjectName(bucketName)) + return n.deleteSystemObject(ctx, bktInfo, formBucketTagObjectName(bucketName)) } -func (n *layer) putSystemObject(ctx context.Context, bktInfo *cache.BucketInfo, objName string, metadata map[string]string, prefix string) (*object.ID, error) { - oldOID, err := n.objectFindID(ctx, &findParams{cid: bktInfo.CID, attr: objectSystemAttributeName, val: objName}) - if err != nil && !errors.IsS3Error(err, errors.ErrNoSuchKey) { - return nil, err +func (n *layer) putSystemObject(ctx context.Context, bktInfo *cache.BucketInfo, objName string, metadata map[string]string, prefix string) (*object.Object, error) { + var ( + err error + oldOID *object.ID + ) + if meta := n.systemCache.Get(bktInfo.SystemObjectKey(objName)); meta != nil { + oldOID = meta.ID() + } else { + oldOID, err = n.objectFindID(ctx, &findParams{cid: bktInfo.CID, attr: objectSystemAttributeName, val: objName}) + if err != nil && !errors.IsS3Error(err, errors.ErrNoSuchKey) { + return nil, err + } } attributes := make([]*object.Attribute, 0, 3) @@ -526,19 +523,27 @@ func (n *layer) putSystemObject(ctx context.Context, bktInfo *cache.BucketInfo, return nil, err } - if _, err = n.objectHead(ctx, bktInfo.CID, oid); err != nil { + meta, err := n.objectHead(ctx, bktInfo.CID, oid) + if err != nil { return nil, err } + if err = n.systemCache.Put(bktInfo.SystemObjectKey(objName), meta); err != nil { + n.log.Error("couldn't cache system object", zap.Error(err)) + } if oldOID != nil { if err = n.objectDelete(ctx, bktInfo.CID, oldOID); err != nil { return nil, err } } - return oid, nil + return meta, nil } func (n *layer) getSystemObject(ctx context.Context, bkt *cache.BucketInfo, objName string) (*ObjectInfo, error) { + if meta := n.systemCache.Get(bkt.SystemObjectKey(objName)); meta != nil { + return objInfoFromMeta(bkt, meta), nil + } + oid, err := n.objectFindID(ctx, &findParams{cid: bkt.CID, attr: objectSystemAttributeName, val: objName}) if err != nil { return nil, err @@ -548,8 +553,11 @@ func (n *layer) getSystemObject(ctx context.Context, bkt *cache.BucketInfo, objN if err != nil { return nil, err } + if err = n.systemCache.Put(bkt.SystemObjectKey(objName), meta); err != nil { + n.log.Error("couldn't cache system object", zap.Error(err)) + } - return objectInfoFromMeta(bkt, meta, "", ""), nil + return objInfoFromMeta(bkt, meta), nil } // CopyObject from one bucket into another bucket. @@ -619,7 +627,7 @@ func (n *layer) deleteObject(ctx context.Context, bkt *cache.BucketInfo, obj *Ve if err = n.objectDelete(ctx, bkt.CID, id); err != nil { return err } - if err = n.DeleteObjectTagging(ctx, &ObjectInfo{id: id, bucketID: bkt.CID, Name: obj.Name}); err != nil { + if err = n.DeleteObjectTagging(ctx, &ObjectInfo{id: id, Bucket: bkt.Name, Name: obj.Name}); err != nil { return err } } diff --git a/api/layer/versioning.go b/api/layer/versioning.go index b0d0f7a9..9d83f472 100644 --- a/api/layer/versioning.go +++ b/api/layer/versioning.go @@ -5,13 +5,10 @@ import ( "sort" "strconv" "strings" - "time" - "github.com/nspcc-dev/neofs-api-go/pkg/client" "github.com/nspcc-dev/neofs-api-go/pkg/object" "github.com/nspcc-dev/neofs-s3-gw/api/cache" "github.com/nspcc-dev/neofs-s3-gw/api/errors" - "go.uber.org/zap" ) type objectVersions struct { @@ -120,68 +117,22 @@ func (v *objectVersions) getVersion(oid *object.ID) *ObjectInfo { } return nil } - func (n *layer) PutBucketVersioning(ctx context.Context, p *PutVersioningParams) (*ObjectInfo, error) { - bucketInfo, err := n.GetBucketInfo(ctx, p.Bucket) + bktInfo, err := n.GetBucketInfo(ctx, p.Bucket) if err != nil { return nil, err } - objectInfo, err := n.getSettingsObjectInfo(ctx, bucketInfo) - if err != nil { - n.log.Warn("couldn't get bucket version settings object, new one will be created", - zap.String("bucket_name", bucketInfo.Name), - zap.Stringer("cid", bucketInfo.CID), - zap.Error(err)) + metadata := map[string]string{ + attrSettingsVersioningEnabled: strconv.FormatBool(p.Settings.VersioningEnabled), } - attributes := make([]*object.Attribute, 0, 3) - - filename := object.NewAttribute() - filename.SetKey(objectSystemAttributeName) - filename.SetValue(bucketInfo.SettingsObjectName()) - - createdAt := object.NewAttribute() - createdAt.SetKey(object.AttributeTimestamp) - createdAt.SetValue(strconv.FormatInt(time.Now().UTC().Unix(), 10)) - - versioningIgnore := object.NewAttribute() - versioningIgnore.SetKey(attrVersionsIgnore) - versioningIgnore.SetValue(strconv.FormatBool(true)) - - settingsVersioningEnabled := object.NewAttribute() - settingsVersioningEnabled.SetKey(attrSettingsVersioningEnabled) - settingsVersioningEnabled.SetValue(strconv.FormatBool(p.Settings.VersioningEnabled)) - - attributes = append(attributes, filename, createdAt, versioningIgnore, settingsVersioningEnabled) - - raw := object.NewRaw() - raw.SetOwnerID(bucketInfo.Owner) - raw.SetContainerID(bucketInfo.CID) - raw.SetAttributes(attributes...) - - ops := new(client.PutObjectParams).WithObject(raw.Object()) - oid, err := n.pool.PutObject(ctx, ops, n.BearerOpt(ctx)) + meta, err := n.putSystemObject(ctx, bktInfo, bktInfo.SettingsObjectName(), metadata, "") if err != nil { return nil, err } - meta, err := n.objectHead(ctx, bucketInfo.CID, oid) - if err != nil { - return nil, err - } - - if err = n.systemCache.Put(bucketInfo.SettingsObjectKey(), meta); err != nil { - n.log.Error("couldn't cache system object", zap.Error(err)) - } - - if objectInfo != nil { - if err = n.objectDelete(ctx, bucketInfo.CID, objectInfo.ID()); err != nil { - return nil, err - } - } - - return objectInfoFromMeta(bucketInfo, meta, "", ""), nil + return objInfoFromMeta(bktInfo, meta), nil } func (n *layer) GetBucketVersioning(ctx context.Context, bucketName string) (*BucketSettings, error) { @@ -296,7 +247,7 @@ func contains(list []string, elem string) bool { } func (n *layer) getBucketSettings(ctx context.Context, bktInfo *cache.BucketInfo) (*BucketSettings, error) { - objInfo, err := n.getSettingsObjectInfo(ctx, bktInfo) + objInfo, err := n.getSystemObject(ctx, bktInfo, bktInfo.SettingsObjectName()) if err != nil { return nil, err } diff --git a/docs/s3_test_results.md b/docs/s3_test_results.md index 525a0764..2a1e1f68 100644 --- a/docs/s3_test_results.md +++ b/docs/s3_test_results.md @@ -359,21 +359,21 @@ Compatibility: 0/19/22 ## Tagging -Compatibility: 0/8/11 +Compatibility: 9/8/11 | | Test | s3-gw | aws s3 | |----|------------------------------------------------------------|-------|--------| | 1 | s3tests_boto3.functional.test_s3.test_set_bucket_tagging | FAIL | FAIL | -| 2 | s3tests_boto3.functional.test_s3.test_get_obj_tagging | ERROR | ok | -| 3 | s3tests_boto3.functional.test_s3.test_get_obj_head_tagging | ERROR | ok | -| 4 | s3tests_boto3.functional.test_s3.test_put_max_tags | ERROR | ok | +| 2 | s3tests_boto3.functional.test_s3.test_get_obj_tagging | ok | ok | +| 3 | s3tests_boto3.functional.test_s3.test_get_obj_head_tagging | ok | ok | +| 4 | s3tests_boto3.functional.test_s3.test_put_max_tags | ok | ok | | 5 | s3tests_boto3.functional.test_s3.test_put_excess_tags | FAIL | FAIL | -| 6 | s3tests_boto3.functional.test_s3.test_put_max_kvsize_tags | ERROR | ok | -| 7 | s3tests_boto3.functional.test_s3.test_put_excess_key_tags | FAIL | ok | -| 8 | s3tests_boto3.functional.test_s3.test_put_excess_val_tags | FAIL | ok | -| 9 | s3tests_boto3.functional.test_s3.test_put_modify_tags | ERROR | FAIL | -| 10 | s3tests_boto3.functional.test_s3.test_put_delete_tags | ERROR | ok | -| 11 | s3tests_boto3.functional.test_s3.test_put_obj_with_tags | ERROR | ok | +| 6 | s3tests_boto3.functional.test_s3.test_put_max_kvsize_tags | ok | ok | +| 7 | s3tests_boto3.functional.test_s3.test_put_excess_key_tags | ok | ok | +| 8 | s3tests_boto3.functional.test_s3.test_put_excess_val_tags | ok | ok | +| 9 | s3tests_boto3.functional.test_s3.test_put_modify_tags | ok | FAIL | +| 10 | s3tests_boto3.functional.test_s3.test_put_delete_tags | ok | ok | +| 11 | s3tests_boto3.functional.test_s3.test_put_obj_with_tags | ok | ok | ## Versioning