Skip to content

Commit

Permalink
*: Remove GetExtendObjectInfo usage where possible
Browse files Browse the repository at this point in the history
Signed-off-by: Evgenii Baidakov <evgenii@nspcc.io>
  • Loading branch information
smallhive committed Feb 21, 2025
1 parent bfb2204 commit acfb3ce
Show file tree
Hide file tree
Showing 5 changed files with 158 additions and 37 deletions.
26 changes: 19 additions & 7 deletions api/handler/copy.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package handler

import (
"errors"
"net/http"
"net/url"
"regexp"
Expand Down Expand Up @@ -85,18 +86,29 @@ func (h *handler) CopyObjectHandler(w http.ResponseWriter, r *http.Request) {
}

if settingsSrc.VersioningEnabled() && srcObjPrm.VersionID == "" {
headObjectPrm := &layer.HeadObjectParams{
BktInfo: srcObjPrm.BktInfo,
Object: srcObject,
shortInfoParams := &layer.ShortInfoParams{
Owner: srcObjPrm.BktInfo.Owner,
CID: srcObjPrm.BktInfo.CID,
Object: srcObject,
}

ei, err := h.obj.GetExtendedObjectInfo(r.Context(), headObjectPrm)
ei, err := h.obj.GetIDForVersioningContainer(r.Context(), shortInfoParams)
if err != nil {
h.logAndSendError(w, "could not find object", reqInfo, err)
return
if !errors.Is(err, layer.ErrNodeNotFound) {
h.logAndSendError(w, "could not find object", reqInfo, err)
return
}

// CopyObject can copy object from versioned container, but it can contain only "null" versions.
// In this case we should find actual one.
shortInfoParams.FindNullVersion = true
if ei, err = h.obj.GetIDForVersioningContainer(r.Context(), shortInfoParams); err != nil {
h.logAndSendError(w, "could not find object", reqInfo, err)
return
}
}

srcObjPrm.VersionID = ei.ObjectInfo.VersionID()
srcObjPrm.VersionID = ei.EncodeToString()
}

dstBktInfo, err := h.getBucketAndCheckOwner(r, reqInfo.BucketName)
Expand Down
33 changes: 18 additions & 15 deletions api/handler/locking.go
Original file line number Diff line number Diff line change
Expand Up @@ -159,18 +159,19 @@ func (h *handler) PutObjectLegalHoldHandler(w http.ResponseWriter, r *http.Reque
}

if settings.VersioningEnabled() && p.ObjVersion.VersionID == "" {
headObjectPrm := &layer.HeadObjectParams{
BktInfo: bktInfo,
Object: reqInfo.ObjectName,
shortInfoParams := &layer.ShortInfoParams{
Owner: bktInfo.Owner,
CID: bktInfo.CID,
Object: reqInfo.ObjectName,
}

ei, err := h.obj.GetExtendedObjectInfo(r.Context(), headObjectPrm)
ei, err := h.obj.GetIDForVersioningContainer(r.Context(), shortInfoParams)
if err != nil {
h.logAndSendError(w, "could not find object", reqInfo, err)
return
}

p.ObjVersion.VersionID = ei.ObjectInfo.VersionID()
p.ObjVersion.VersionID = ei.EncodeToString()
}

if err = h.obj.PutLockInfo(r.Context(), p); err != nil {
Expand Down Expand Up @@ -259,18 +260,19 @@ func (h *handler) PutObjectRetentionHandler(w http.ResponseWriter, r *http.Reque
}

if settings.VersioningEnabled() && p.ObjVersion.VersionID == "" {
headObjectPrm := &layer.HeadObjectParams{
BktInfo: bktInfo,
Object: reqInfo.ObjectName,
shortInfoParams := &layer.ShortInfoParams{
Owner: bktInfo.Owner,
CID: bktInfo.CID,
Object: reqInfo.ObjectName,
}

ei, err := h.obj.GetExtendedObjectInfo(r.Context(), headObjectPrm)
ei, err := h.obj.GetIDForVersioningContainer(r.Context(), shortInfoParams)
if err != nil {
h.logAndSendError(w, "could not find object", reqInfo, err)
return
}

p.ObjVersion.VersionID = ei.ObjectInfo.VersionID()
p.ObjVersion.VersionID = ei.EncodeToString()
}

if err = h.obj.PutLockInfo(r.Context(), p); err != nil {
Expand Down Expand Up @@ -307,18 +309,19 @@ func (h *handler) GetObjectRetentionHandler(w http.ResponseWriter, r *http.Reque
}

if settings.VersioningEnabled() && p.VersionID == "" {
headObjectPrm := &layer.HeadObjectParams{
BktInfo: bktInfo,
Object: reqInfo.ObjectName,
shortInfoParams := &layer.ShortInfoParams{
Owner: bktInfo.Owner,
CID: bktInfo.CID,
Object: reqInfo.ObjectName,
}

ei, err := h.obj.GetExtendedObjectInfo(r.Context(), headObjectPrm)
ei, err := h.obj.GetIDForVersioningContainer(r.Context(), shortInfoParams)
if err != nil {
h.logAndSendError(w, "could not find object", reqInfo, err)
return
}

p.VersionID = ei.ObjectInfo.VersionID()
p.VersionID = ei.EncodeToString()
}

lockInfo, err := h.obj.GetLockInfo(r.Context(), p)
Expand Down
33 changes: 18 additions & 15 deletions api/handler/tagging.go
Original file line number Diff line number Diff line change
Expand Up @@ -55,18 +55,19 @@ func (h *handler) PutObjectTaggingHandler(w http.ResponseWriter, r *http.Request
}

if settings.VersioningEnabled() && tagPrm.ObjectVersion.VersionID == "" {
headObjectPrm := &layer.HeadObjectParams{
BktInfo: bktInfo,
Object: reqInfo.ObjectName,
shortInfoParams := &layer.ShortInfoParams{
Owner: bktInfo.Owner,
CID: bktInfo.CID,
Object: reqInfo.ObjectName,
}

ei, err := h.obj.GetExtendedObjectInfo(r.Context(), headObjectPrm)
ei, err := h.obj.GetIDForVersioningContainer(r.Context(), shortInfoParams)
if err != nil {
h.logAndSendError(w, "could not find object", reqInfo, err)
return
}

tagPrm.ObjectVersion.VersionID = ei.ObjectInfo.VersionID()
tagPrm.ObjectVersion.VersionID = ei.EncodeToString()
}

if err = h.obj.PutObjectTagging(r.Context(), tagPrm); err != nil {
Expand Down Expand Up @@ -113,18 +114,19 @@ func (h *handler) GetObjectTaggingHandler(w http.ResponseWriter, r *http.Request
}

if settings.VersioningEnabled() && tagPrm.ObjectVersion.VersionID == "" {
headObjectPrm := &layer.HeadObjectParams{
BktInfo: bktInfo,
Object: reqInfo.ObjectName,
shortInfoParams := &layer.ShortInfoParams{
Owner: bktInfo.Owner,
CID: bktInfo.CID,
Object: reqInfo.ObjectName,
}

ei, err := h.obj.GetExtendedObjectInfo(r.Context(), headObjectPrm)
ei, err := h.obj.GetIDForVersioningContainer(r.Context(), shortInfoParams)
if err != nil {
h.logAndSendError(w, "could not find object", reqInfo, err)
return
}

tagPrm.ObjectVersion.VersionID = ei.ObjectInfo.VersionID()
tagPrm.ObjectVersion.VersionID = ei.EncodeToString()
}

versionID, tagSet, err := h.obj.GetObjectTagging(r.Context(), tagPrm)
Expand Down Expand Up @@ -163,18 +165,19 @@ func (h *handler) DeleteObjectTaggingHandler(w http.ResponseWriter, r *http.Requ
}

if settings.VersioningEnabled() && p.VersionID == "" {
headObjectPrm := &layer.HeadObjectParams{
BktInfo: bktInfo,
Object: reqInfo.ObjectName,
shortInfoParams := &layer.ShortInfoParams{
Owner: bktInfo.Owner,
CID: bktInfo.CID,
Object: reqInfo.ObjectName,
}

ei, err := h.obj.GetExtendedObjectInfo(r.Context(), headObjectPrm)
ei, err := h.obj.GetIDForVersioningContainer(r.Context(), shortInfoParams)
if err != nil {
h.logAndSendError(w, "could not find object", reqInfo, err)
return
}

p.VersionID = ei.ObjectInfo.VersionID()
p.VersionID = ei.EncodeToString()
}

if err = h.obj.DeleteObjectTagging(r.Context(), p); err != nil {
Expand Down
96 changes: 96 additions & 0 deletions api/layer/layer.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,13 @@ package layer

import (
"bytes"
"cmp"
"context"
"errors"
"fmt"
"io"
"net/url"
"slices"
"strconv"
"strings"
"sync"
Expand All @@ -20,6 +22,7 @@ import (
"github.com/nspcc-dev/neofs-s3-gw/api/s3errors"
"github.com/nspcc-dev/neofs-s3-gw/creds/accessbox"
"github.com/nspcc-dev/neofs-sdk-go/bearer"
"github.com/nspcc-dev/neofs-sdk-go/client"
apistatus "github.com/nspcc-dev/neofs-sdk-go/client/status"
cid "github.com/nspcc-dev/neofs-sdk-go/container/id"
"github.com/nspcc-dev/neofs-sdk-go/eacl"
Expand Down Expand Up @@ -82,6 +85,14 @@ type (
IsBucketVersioningEnabled bool
}

// ShortInfoParams stores necessary info to get actual obj info in versioned container for non versioned request.
ShortInfoParams struct {
CID cid.ID
Owner user.ID
Object string
FindNullVersion bool
}

// ObjectVersion stores object version info.
ObjectVersion struct {
BktInfo *data.BucketInfo
Expand Down Expand Up @@ -208,6 +219,7 @@ type (
GetObject(ctx context.Context, p *GetObjectParams) error
GetObjectInfo(ctx context.Context, p *HeadObjectParams) (*data.ObjectInfo, error)
GetExtendedObjectInfo(ctx context.Context, p *HeadObjectParams) (*data.ExtendedObjectInfo, error)
GetIDForVersioningContainer(ctx context.Context, p *ShortInfoParams) (oid.ID, error)

GetLockInfo(ctx context.Context, obj *ObjectVersion) (*data.LockInfo, error)
PutLockInfo(ctx context.Context, p *PutLockInfoParams) error
Expand Down Expand Up @@ -543,6 +555,90 @@ func (n *layer) GetExtendedObjectInfo(ctx context.Context, p *HeadObjectParams)
return objInfo, nil
}

// GetIDForVersioningContainer returns actual oid.ID for object in versioned container.
func (n *layer) GetIDForVersioningContainer(ctx context.Context, p *ShortInfoParams) (oid.ID, error) {
var (
filters = make(object.SearchFilters, 0, 3)
returningAttributes = []string{
object.AttributeFilePath,
object.FilterCreationEpoch,
object.AttributeTimestamp,
}

opts client.SearchObjectsOptions
)

if bt := bearerTokenFromContext(ctx, p.Owner); bt != nil {
opts.WithBearerToken(*bt)
}

filters.AddFilter(object.AttributeFilePath, p.Object, object.MatchStringEqual)
filters.AddTypeFilter(object.MatchStringEqual, object.TypeRegular)

if !p.FindNullVersion {
filters.AddFilter(attrS3VersioningState, data.VersioningEnabled, object.MatchStringEqual)
}

ids, err := n.neoFS.SearchObjectsV2(ctx, p.CID, filters, returningAttributes, opts)
if err != nil {
if errors.Is(err, apistatus.ErrObjectAccessDenied) {
return oid.ID{}, s3errors.GetAPIError(s3errors.ErrAccessDenied)
}

return oid.ID{}, fmt.Errorf("search objects: %w", err)
}

if len(ids) == 0 {
return oid.ID{}, ErrNodeNotFound
}

var searchResults = make([]idSearchResult, 0, len(ids))

for _, item := range ids {
if len(item.Attributes) != len(returningAttributes) {
return oid.ID{}, fmt.Errorf("invalid attribute count returned, expected %d, got %d", len(returningAttributes), len(item.Attributes))
}

var psr = idSearchResult{
ID: item.ID,
FilePath: item.Attributes[0],
}

if item.Attributes[1] != "" {
psr.CreationEpoch, err = strconv.ParseUint(item.Attributes[1], 10, 64)
if err != nil {
return oid.ID{}, fmt.Errorf("invalid creation epoch %s: %w", item.Attributes[1], err)
}
}

if item.Attributes[2] != "" {
psr.CreationTimestamp, err = strconv.ParseInt(item.Attributes[2], 10, 64)
if err != nil {
return oid.ID{}, fmt.Errorf("invalid creation timestamp %s: %w", item.Attributes[2], err)
}
}

searchResults = append(searchResults, psr)
}

sortFunc := func(a, b idSearchResult) int {
if c := cmp.Compare(b.CreationEpoch, a.CreationEpoch); c != 0 { // reverse order.
return c
}

if c := cmp.Compare(b.CreationTimestamp, a.CreationTimestamp); c != 0 { // reverse order.
return c
}

// It is a temporary decision. We can't figure out what object was first and what the second right now.
return bytes.Compare(b.ID[:], a.ID[:]) // reverse order.
}

slices.SortFunc(searchResults, sortFunc)

return searchResults[0].ID, nil
}

// CopyObject from one bucket into another bucket.
func (n *layer) CopyObject(ctx context.Context, p *CopyObjectParams) (*data.ExtendedObjectInfo, error) {
pr, pw := io.Pipe()
Expand Down
7 changes: 7 additions & 0 deletions api/layer/object.go
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,13 @@ type (
PayloadSize int64
PayloadChecksum string
}

idSearchResult struct {
ID oid.ID
FilePath string
CreationEpoch uint64
CreationTimestamp int64
}
)

const (
Expand Down

0 comments on commit acfb3ce

Please sign in to comment.