From f463522f34d7b863a9a96482a8792953dc55dc6d Mon Sep 17 00:00:00 2001 From: Denis Kirillov Date: Tue, 10 Aug 2021 11:58:40 +0300 Subject: [PATCH] [#122] Add versioning put object Signed-off-by: Denis Kirillov --- api/handler/versioning.go | 22 ++++++--------- api/layer/layer.go | 41 +++++++++++++++++++++++---- api/layer/object.go | 58 +++++++++++++++++++++++++++++++-------- 3 files changed, 90 insertions(+), 31 deletions(-) diff --git a/api/handler/versioning.go b/api/handler/versioning.go index 3fc70680..b69dfb43 100644 --- a/api/handler/versioning.go +++ b/api/handler/versioning.go @@ -3,7 +3,6 @@ package handler import ( "encoding/xml" "net/http" - "strconv" "github.com/nspcc-dev/neofs-s3-gw/api" "github.com/nspcc-dev/neofs-s3-gw/api/layer" @@ -20,8 +19,8 @@ func (h *handler) PutBucketVersioningHandler(w http.ResponseWriter, r *http.Requ } p := &layer.PutVersioningParams{ - Bucket: reqInfo.BucketName, - VersioningEnabled: configuration.Status == "Enabled", + Bucket: reqInfo.BucketName, + Settings: &layer.BucketSettings{VersioningEnabled: configuration.Status == "Enabled"}, } if _, err := h.obj.PutBucketVersioning(r.Context(), p); err != nil { @@ -33,7 +32,7 @@ func (h *handler) PutBucketVersioningHandler(w http.ResponseWriter, r *http.Requ func (h *handler) GetBucketVersioningHandler(w http.ResponseWriter, r *http.Request) { reqInfo := api.GetReqInfo(r.Context()) - objInfo, err := h.obj.GetBucketVersioning(r.Context(), reqInfo.BucketName) + settings, err := h.obj.GetBucketVersioning(r.Context(), reqInfo.BucketName) if err != nil { h.log.Warn("couldn't get version settings object: default version settings will be used", zap.String("request_id", reqInfo.RequestID), @@ -42,23 +41,18 @@ func (h *handler) GetBucketVersioningHandler(w http.ResponseWriter, r *http.Requ zap.Error(err)) } - if err = api.EncodeToResponse(w, formVersioningConfiguration(objInfo)); err != nil { + if err = api.EncodeToResponse(w, formVersioningConfiguration(settings)); err != nil { h.logAndSendError(w, "something went wrong", reqInfo, err) } } -func formVersioningConfiguration(inf *layer.ObjectInfo) *VersioningConfiguration { +func formVersioningConfiguration(settings *layer.BucketSettings) *VersioningConfiguration { res := &VersioningConfiguration{Status: "Suspended"} - - if inf == nil { + if settings == nil { return res } - - enabled, ok := inf.Headers["S3-Settings-Versioning-enabled"] - if ok { - if parsed, err := strconv.ParseBool(enabled); err == nil && parsed { - res.Status = "Enabled" - } + if settings.VersioningEnabled { + res.Status = "Enabled" } return res } diff --git a/api/layer/layer.go b/api/layer/layer.go index e7d76af9..420c4088 100644 --- a/api/layer/layer.go +++ b/api/layer/layer.go @@ -74,7 +74,12 @@ type ( // PutVersioningParams stores object copy request parameters. PutVersioningParams struct { - Bucket string + Bucket string + Settings *BucketSettings + } + + // BucketSettings stores settings such as versioning. + BucketSettings struct { VersioningEnabled bool } @@ -125,7 +130,7 @@ type ( NeoFS PutBucketVersioning(ctx context.Context, p *PutVersioningParams) (*ObjectInfo, error) - GetBucketVersioning(ctx context.Context, name string) (*ObjectInfo, error) + GetBucketVersioning(ctx context.Context, name string) (*BucketSettings, error) ListBuckets(ctx context.Context) ([]*BucketInfo, error) GetBucketInfo(ctx context.Context, name string) (*BucketInfo, error) @@ -551,7 +556,7 @@ func (n *layer) PutBucketVersioning(ctx context.Context, p *PutVersioningParams) settingsVersioningEnabled := object.NewAttribute() settingsVersioningEnabled.SetKey("S3-Settings-Versioning-enabled") - settingsVersioningEnabled.SetValue(strconv.FormatBool(p.VersioningEnabled)) + settingsVersioningEnabled.SetValue(strconv.FormatBool(p.Settings.VersioningEnabled)) attributes = append(attributes, filename, createdAt, versioningIgnore, settingsVersioningEnabled) @@ -586,10 +591,36 @@ func (n *layer) PutBucketVersioning(ctx context.Context, p *PutVersioningParams) return objectInfoFromMeta(bucketInfo, meta, "", ""), nil } -func (n *layer) GetBucketVersioning(ctx context.Context, bucketName string) (*ObjectInfo, error) { +func (n *layer) GetBucketVersioning(ctx context.Context, bucketName string) (*BucketSettings, error) { bktInfo, err := n.GetBucketInfo(ctx, bucketName) if err != nil { return nil, err } - return n.getSettingsObjectInfo(ctx, bktInfo) + + return n.getBucketSettings(ctx, bktInfo) +} + +func (n *layer) getBucketSettings(ctx context.Context, bktInfo *BucketInfo) (*BucketSettings, error) { + objInfo, err := n.getSettingsObjectInfo(ctx, bktInfo) + if err != nil { + return nil, err + } + + return objectInfoToBucketSettings(objInfo), nil +} + +func objectInfoToBucketSettings(info *ObjectInfo) *BucketSettings { + res := &BucketSettings{} + + if info == nil { + return res + } + + enabled, ok := info.Headers["S3-Settings-Versioning-enabled"] + if ok { + if parsed, err := strconv.ParseBool(enabled); err == nil { + res.VersioningEnabled = parsed + } + } + return res } diff --git a/api/layer/object.go b/api/layer/object.go index 7ec3d1cf..bd816988 100644 --- a/api/layer/object.go +++ b/api/layer/object.go @@ -62,6 +62,11 @@ type ( } ) +const ( + versionsDelAttr = "S3-Versions-del" + versionsAddAttr = "S3-Versions-add" +) + // objectSearch returns all available objects by search params. func (n *layer) objectSearch(ctx context.Context, p *findParams) ([]*object.ID, error) { var opts object.SearchFilters @@ -131,6 +136,7 @@ func (n *layer) objectPut(ctx context.Context, p *PutObjectParams) (*ObjectInfo, return nil, err } + versioningEnabled := n.isVersioningEnabled(ctx, bkt) lastVersionInfo, err := n.headLastVersion(ctx, bkt, p.Object) if err != nil && !apiErrors.IsS3Error(err, apiErrors.ErrNoSuchKey) { return nil, err @@ -139,17 +145,35 @@ func (n *layer) objectPut(ctx context.Context, p *PutObjectParams) (*ObjectInfo, attributes := make([]*object.Attribute, 0, len(p.Header)+1) var idsToDeleteArr []*object.ID if lastVersionInfo != nil { - versionsDeletedStr := lastVersionInfo.Headers["S3-Versions-del"] - if len(versionsDeletedStr) != 0 { - versionsDeletedStr += "," - } - versionsDeletedStr += lastVersionInfo.ID().String() - deletedVersions := object.NewAttribute() - deletedVersions.SetKey("S3-Versions-del") - deletedVersions.SetValue(versionsDeletedStr) + if versioningEnabled { + versionsAddedStr := lastVersionInfo.Headers[versionsAddAttr] + if len(versionsAddedStr) != 0 { + versionsAddedStr += "," + } + versionsAddedStr += lastVersionInfo.ID().String() + addedVersions := object.NewAttribute() + addedVersions.SetKey(versionsAddAttr) + addedVersions.SetValue(versionsAddedStr) + attributes = append(attributes, addedVersions) + if delVersions := lastVersionInfo.Headers[versionsDelAttr]; len(delVersions) > 0 { + deletedVersions := object.NewAttribute() + deletedVersions.SetKey(versionsDelAttr) + deletedVersions.SetValue(delVersions) + attributes = append(attributes, deletedVersions) + } + } else { + versionsDeletedStr := lastVersionInfo.Headers[versionsDelAttr] + if len(versionsDeletedStr) != 0 { + versionsDeletedStr += "," + } + versionsDeletedStr += lastVersionInfo.ID().String() + deletedVersions := object.NewAttribute() + deletedVersions.SetKey(versionsDelAttr) + deletedVersions.SetValue(versionsDeletedStr) - attributes = append(attributes, deletedVersions) - idsToDeleteArr = append(idsToDeleteArr, lastVersionInfo.ID()) + attributes = append(attributes, deletedVersions) + idsToDeleteArr = append(idsToDeleteArr, lastVersionInfo.ID()) + } } unix := strconv.FormatInt(time.Now().UTC().Unix(), 10) @@ -260,8 +284,8 @@ func (n *layer) headLastVersion(ctx context.Context, bkt *BucketInfo, objectName }) return objectInfoFromMeta(bkt, infos[len(infos)-1], "", ""), nil - //versionsToDeleteStr, ok := lastVersionInfo.Headers["S3-Versions-add"] - //versionsDeletedStr := lastVersionInfo.Headers["S3-Versions-del"] + //versionsToDeleteStr, ok := lastVersionInfo.Headers[versionsAddAttr] + //versionsDeletedStr := lastVersionInfo.Headers[versionsDelAttr] //idsToDeleteArr := []*object.ID{lastVersionInfo.ID()} //if ok { // // for versioning mode only @@ -475,3 +499,13 @@ func (n *layer) listAllObjects(ctx context.Context, p ListObjectsParamsCommon) ( return allObjects, nil } + +func (n *layer) isVersioningEnabled(ctx context.Context, bktInfo *BucketInfo) bool { + settings, err := n.getBucketSettings(ctx, bktInfo) + if err != nil { + n.log.Warn("couldn't get versioning settings object", zap.Error(err)) + return false + } + + return settings.VersioningEnabled +}