From 1be92fa4befc8f95bf94f5aff39e05c87c57d7f8 Mon Sep 17 00:00:00 2001 From: Nikita Zinkevich Date: Mon, 2 Dec 2024 11:45:30 +0300 Subject: [PATCH] [#166] Fix getting s3 object with the FrostFS OID name Prioritize getting s3 object with the key, which equals to valid FrostFS OID, rather than getting non-existent object with OID via native protocol for GET and HEAD requests Signed-off-by: Nikita Zinkevich --- cmd/http-gw/app.go | 4 +- internal/data/{bucket.go => info.go} | 0 internal/{api => data}/tree.go | 17 ++- internal/handler/browse.go | 2 +- internal/handler/download.go | 49 +++++++-- internal/handler/handler.go | 104 +++++------------- internal/handler/handler_test.go | 33 ++++-- internal/handler/head.go | 34 +++++- internal/handler/utils.go | 11 +- internal/{api => }/layer/tree_service.go | 6 +- internal/logs/logs.go | 5 + .../{pool_wrapper.go => tree_pool_wrapper.go} | 0 tree/tree.go | 52 ++++++--- 13 files changed, 178 insertions(+), 139 deletions(-) rename internal/data/{bucket.go => info.go} (100%) rename internal/{api => data}/tree.go (61%) rename internal/{api => }/layer/tree_service.go (64%) rename internal/service/frostfs/{pool_wrapper.go => tree_pool_wrapper.go} (100%) diff --git a/cmd/http-gw/app.go b/cmd/http-gw/app.go index 9adaec2..23a752a 100644 --- a/cmd/http-gw/app.go +++ b/cmd/http-gw/app.go @@ -508,10 +508,10 @@ func (a *app) Serve() { close(a.webDone) }() - handler := handler.New(a.AppParams(), a.settings, tree.NewTree(frostfs.NewPoolWrapper(a.treePool)), workerPool) + handle := handler.New(a.AppParams(), a.settings, tree.NewTree(frostfs.NewPoolWrapper(a.treePool)), workerPool) // Configure router. - a.configureRouter(handler) + a.configureRouter(handle) a.startServices() a.initServers(a.ctx) diff --git a/internal/data/bucket.go b/internal/data/info.go similarity index 100% rename from internal/data/bucket.go rename to internal/data/info.go diff --git a/internal/api/tree.go b/internal/data/tree.go similarity index 61% rename from internal/api/tree.go rename to internal/data/tree.go index 5b1d608..fcf8add 100644 --- a/internal/api/tree.go +++ b/internal/data/tree.go @@ -1,4 +1,4 @@ -package api +package data import ( oid "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/object/id" @@ -7,12 +7,21 @@ import ( // NodeVersion represent node from tree service. type NodeVersion struct { BaseNodeVersion - DeleteMarker bool - IsPrefixNode bool } // BaseNodeVersion is minimal node info from tree service. // Basically used for "system" object. type BaseNodeVersion struct { - OID oid.ID + ID uint64 + OID oid.ID + IsDeleteMarker bool +} + +type NodeInfo struct { + Meta []NodeMeta +} + +type NodeMeta interface { + GetKey() string + GetValue() []byte } diff --git a/internal/handler/browse.go b/internal/handler/browse.go index c54ab76..2938b5c 100644 --- a/internal/handler/browse.go +++ b/internal/handler/browse.go @@ -170,7 +170,7 @@ func (h *Handler) getDirObjectsS3(ctx context.Context, bucketInfo *data.BucketIn objects: make([]ResponseObject, 0, len(nodes)), } for _, node := range nodes { - meta := node.GetMeta() + meta := node.Meta if meta == nil { continue } diff --git a/internal/handler/download.go b/internal/handler/download.go index cd4e55a..de27fa3 100644 --- a/internal/handler/download.go +++ b/internal/handler/download.go @@ -4,12 +4,14 @@ import ( "archive/zip" "bufio" "context" + "errors" "fmt" "io" "net/http" "net/url" "time" + "git.frostfs.info/TrueCloudLab/frostfs-http-gw/internal/layer" "git.frostfs.info/TrueCloudLab/frostfs-http-gw/internal/logs" "git.frostfs.info/TrueCloudLab/frostfs-http-gw/response" "git.frostfs.info/TrueCloudLab/frostfs-http-gw/utils" @@ -23,21 +25,46 @@ import ( // DownloadByAddressOrBucketName handles download requests using simple cid/oid or bucketname/key format. func (h *Handler) DownloadByAddressOrBucketName(c *fasthttp.RequestCtx) { - oidURLParam := c.UserValue("oid").(string) - downloadQueryParam := c.QueryArgs().GetBool("download") + cidParam := c.UserValue("cid").(string) + oidParam := c.UserValue("oid").(string) + downloadParam := c.QueryArgs().GetBool("download") - switch { - case isObjectID(oidURLParam): - h.byNativeAddress(c, h.receiveFile) - case !isContainerRoot(oidURLParam) && (downloadQueryParam || !isDir(oidURLParam)): - h.byS3Path(c, h.receiveFile) - default: - h.browseIndex(c) + ctx := utils.GetContextFromRequest(c) + log := utils.GetReqLogOrDefault(ctx, h.log).With( + zap.String("cid", cidParam), + zap.String("oid", oidParam), + ) + + bktInfo, err := h.getBucketInfo(ctx, cidParam, log) + if err != nil { + logAndSendBucketError(c, log, err) + return + } + + checkS3Err := h.tree.CheckSettingsNodeExists(ctx, bktInfo) + if checkS3Err != nil && !errors.Is(checkS3Err, layer.ErrNodeNotFound) { + logAndSendBucketError(c, log, checkS3Err) + return + } + + req := h.newRequest(c, log) + + var objID oid.ID + if checkS3Err == nil && shouldDownload(oidParam, downloadParam) { + h.byS3Path(ctx, req, bktInfo.CID, oidParam, h.receiveFile) + } else if err = objID.DecodeString(oidParam); err == nil { + h.byNativeAddress(ctx, req, bktInfo.CID, objID, h.receiveFile) + } else { + h.browseIndex(c, checkS3Err != nil) } } -func (h *Handler) newRequest(ctx *fasthttp.RequestCtx, log *zap.Logger) *request { - return &request{ +func shouldDownload(oidParam string, downloadParam bool) bool { + return !isDir(oidParam) || downloadParam +} + +func (h *Handler) newRequest(ctx *fasthttp.RequestCtx, log *zap.Logger) request { + return request{ RequestCtx: ctx, log: log, } diff --git a/internal/handler/handler.go b/internal/handler/handler.go index 1839bf0..3805c2d 100644 --- a/internal/handler/handler.go +++ b/internal/handler/handler.go @@ -11,9 +11,9 @@ import ( "git.frostfs.info/TrueCloudLab/frostfs-http-gw/internal/cache" "git.frostfs.info/TrueCloudLab/frostfs-http-gw/internal/data" "git.frostfs.info/TrueCloudLab/frostfs-http-gw/internal/handler/middleware" + "git.frostfs.info/TrueCloudLab/frostfs-http-gw/internal/layer" "git.frostfs.info/TrueCloudLab/frostfs-http-gw/internal/logs" "git.frostfs.info/TrueCloudLab/frostfs-http-gw/response" - "git.frostfs.info/TrueCloudLab/frostfs-http-gw/tree" "git.frostfs.info/TrueCloudLab/frostfs-http-gw/utils" "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/bearer" apistatus "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/client/status" @@ -165,7 +165,7 @@ type Handler struct { ownerID *user.ID config Config containerResolver ContainerResolver - tree *tree.Tree + tree layer.TreeService cache *cache.BucketCache workerPool *ants.Pool } @@ -178,7 +178,7 @@ type AppParams struct { Cache *cache.BucketCache } -func New(params *AppParams, config Config, tree *tree.Tree, workerPool *ants.Pool) *Handler { +func New(params *AppParams, config Config, tree layer.TreeService, workerPool *ants.Pool) *Handler { return &Handler{ log: params.Logger, frostfs: params.FrostFS, @@ -193,77 +193,34 @@ func New(params *AppParams, config Config, tree *tree.Tree, workerPool *ants.Poo // byNativeAddress is a wrapper for function (e.g. request.headObject, request.receiveFile) that // prepares request and object address to it. -func (h *Handler) byNativeAddress(c *fasthttp.RequestCtx, f func(context.Context, request, oid.Address)) { - idCnr, _ := c.UserValue("cid").(string) - idObj, _ := url.PathUnescape(c.UserValue("oid").(string)) - - ctx := utils.GetContextFromRequest(c) - reqLog := utils.GetReqLogOrDefault(ctx, h.log) - log := reqLog.With(zap.String("cid", idCnr), zap.String("oid", idObj)) - - bktInfo, err := h.getBucketInfo(ctx, idCnr, log) - if err != nil { - logAndSendBucketError(c, log, err) - return - } - - objID := new(oid.ID) - if err = objID.DecodeString(idObj); err != nil { - log.Error(logs.WrongObjectID, zap.Error(err)) - response.Error(c, "wrong object id", fasthttp.StatusBadRequest) - return - } - - addr := newAddress(bktInfo.CID, *objID) - - f(ctx, *h.newRequest(c, log), addr) +func (h *Handler) byNativeAddress(ctx context.Context, req request, cnrID cid.ID, objID oid.ID, handler func(context.Context, request, oid.Address)) { + addr := newAddress(cnrID, objID) + handler(ctx, req, addr) } // byS3Path is a wrapper for function (e.g. request.headObject, request.receiveFile) that // resolves object address from S3-like path /. -func (h *Handler) byS3Path(c *fasthttp.RequestCtx, f func(context.Context, request, oid.Address)) { - bucketname := c.UserValue("cid").(string) - key := c.UserValue("oid").(string) +func (h *Handler) byS3Path(ctx context.Context, req request, cnrID cid.ID, path string, handler func(context.Context, request, oid.Address)) { + c, log := req.RequestCtx, req.log - ctx := utils.GetContextFromRequest(c) - reqLog := utils.GetReqLogOrDefault(ctx, h.log) - log := reqLog.With(zap.String("bucketname", bucketname), zap.String("key", key)) - - unescapedKey, err := url.QueryUnescape(key) + foundOID, err := h.tree.GetLatestVersion(ctx, &cnrID, path) if err != nil { logAndSendBucketError(c, log, err) return } - - bktInfo, err := h.getBucketInfo(ctx, bucketname, log) - if err != nil { - logAndSendBucketError(c, log, err) - return - } - - foundOid, err := h.tree.GetLatestVersion(ctx, &bktInfo.CID, unescapedKey) - if err != nil { - if errors.Is(err, tree.ErrNodeAccessDenied) { - response.Error(c, "Access Denied", fasthttp.StatusForbidden) - } else { - response.Error(c, "object wasn't found", fasthttp.StatusNotFound) - log.Error(logs.GetLatestObjectVersion, zap.Error(err)) - } - return - } - if foundOid.DeleteMarker { + if foundOID.IsDeleteMarker { log.Error(logs.ObjectWasDeleted) response.Error(c, "object deleted", fasthttp.StatusNotFound) return } - addr := newAddress(bktInfo.CID, foundOid.OID) - f(ctx, *h.newRequest(c, log), addr) + addr := newAddress(cnrID, foundOID.OID) + handler(ctx, h.newRequest(c, log), addr) } // byAttribute is a wrapper similar to byNativeAddress. -func (h *Handler) byAttribute(c *fasthttp.RequestCtx, f func(context.Context, request, oid.Address)) { - scid, _ := c.UserValue("cid").(string) +func (h *Handler) byAttribute(c *fasthttp.RequestCtx, handler func(context.Context, request, oid.Address)) { + cidParam, _ := c.UserValue("cid").(string) key, _ := c.UserValue("attr_key").(string) val, _ := c.UserValue("attr_val").(string) @@ -272,21 +229,21 @@ func (h *Handler) byAttribute(c *fasthttp.RequestCtx, f func(context.Context, re key, err := url.QueryUnescape(key) if err != nil { - log.Error(logs.FailedToUnescapeQuery, zap.String("cid", scid), zap.String("attr_key", key), zap.Error(err)) + log.Error(logs.FailedToUnescapeQuery, zap.String("cid", cidParam), zap.String("attr_key", key), zap.Error(err)) response.Error(c, "could not unescape attr_key: "+err.Error(), fasthttp.StatusBadRequest) return } val, err = url.QueryUnescape(val) if err != nil { - log.Error(logs.FailedToUnescapeQuery, zap.String("cid", scid), zap.String("attr_val", val), zap.Error(err)) + log.Error(logs.FailedToUnescapeQuery, zap.String("cid", cidParam), zap.String("attr_val", val), zap.Error(err)) response.Error(c, "could not unescape attr_val: "+err.Error(), fasthttp.StatusBadRequest) return } - log = log.With(zap.String("cid", scid), zap.String("attr_key", key), zap.String("attr_val", val)) + log = log.With(zap.String("cid", cidParam), zap.String("attr_key", key), zap.String("attr_val", val)) - bktInfo, err := h.getBucketInfo(ctx, scid, log) + bktInfo, err := h.getBucketInfo(ctx, cidParam, log) if err != nil { logAndSendBucketError(c, log, err) return @@ -303,11 +260,11 @@ func (h *Handler) byAttribute(c *fasthttp.RequestCtx, f func(context.Context, re return } - var addrObj oid.Address - addrObj.SetContainer(bktInfo.CID) - addrObj.SetObject(objID) + var addr oid.Address + addr.SetContainer(bktInfo.CID) + addr.SetObject(objID) - f(ctx, *h.newRequest(c, log), addrObj) + handler(ctx, h.newRequest(c, log), addr) } func (h *Handler) findObjectByAttribute(ctx context.Context, log *zap.Logger, cnrID cid.ID, attrKey, attrVal string) (oid.ID, error) { @@ -412,7 +369,7 @@ func (h *Handler) readContainer(ctx context.Context, cnrID cid.ID) (*data.Bucket return bktInfo, err } -func (h *Handler) browseIndex(c *fasthttp.RequestCtx) { +func (h *Handler) browseIndex(c *fasthttp.RequestCtx, isNativeList bool) { if !h.config.IndexPageEnabled() { c.SetStatusCode(fasthttp.StatusNotFound) return @@ -438,18 +395,9 @@ func (h *Handler) browseIndex(c *fasthttp.RequestCtx) { } listFunc := h.getDirObjectsS3 - isNativeList := false - - err = h.tree.CheckSettingsNodeExist(ctx, bktInfo) - if err != nil { - if errors.Is(err, tree.ErrNodeNotFound) { - // tree probe failed, try to use native - listFunc = h.getDirObjectsNative - isNativeList = true - } else { - logAndSendBucketError(c, log, err) - return - } + if isNativeList { + // tree probe failed, trying to use native + listFunc = h.getDirObjectsNative } h.browseObjects(c, browseParams{ diff --git a/internal/handler/handler_test.go b/internal/handler/handler_test.go index 86364d9..14f9c98 100644 --- a/internal/handler/handler_test.go +++ b/internal/handler/handler_test.go @@ -14,8 +14,8 @@ import ( "git.frostfs.info/TrueCloudLab/frostfs-http-gw/internal/cache" "git.frostfs.info/TrueCloudLab/frostfs-http-gw/internal/data" "git.frostfs.info/TrueCloudLab/frostfs-http-gw/internal/handler/middleware" + "git.frostfs.info/TrueCloudLab/frostfs-http-gw/internal/layer" "git.frostfs.info/TrueCloudLab/frostfs-http-gw/resolver" - "git.frostfs.info/TrueCloudLab/frostfs-http-gw/tree" "git.frostfs.info/TrueCloudLab/frostfs-http-gw/utils" "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/container" "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/container/acl" @@ -32,14 +32,29 @@ import ( "go.uber.org/zap" ) -type treeClientMock struct { +type treeServiceMock struct { + system map[string]map[string]*data.BaseNodeVersion } -func (t *treeClientMock) GetNodes(context.Context, *tree.GetNodesParams) ([]tree.NodeResponse, error) { - return nil, nil +func newTreeService() *treeServiceMock { + return &treeServiceMock{ + system: make(map[string]map[string]*data.BaseNodeVersion), + } } -func (t *treeClientMock) GetSubTree(context.Context, *data.BucketInfo, string, []uint64, uint32, bool) ([]tree.NodeResponse, error) { +func (t *treeServiceMock) CheckSettingsNodeExists(context.Context, *data.BucketInfo) error { + _, ok := t.system["bucket-settings"] + if !ok { + return layer.ErrNodeNotFound + } + return nil +} + +func (t *treeServiceMock) GetSubTreeByPrefix(context.Context, *data.BucketInfo, string, bool) ([]data.NodeInfo, string, error) { + return nil, "", nil +} + +func (t *treeServiceMock) GetLatestVersion(context.Context, *cid.ID, string) (*data.NodeVersion, error) { return nil, nil } @@ -89,7 +104,7 @@ type handlerContext struct { h *Handler frostfs *TestFrostFS - tree *treeClientMock + tree *treeServiceMock cfg *configMock } @@ -130,14 +145,14 @@ func prepareHandlerContext() (*handlerContext, error) { }), } - treeMock := &treeClientMock{} + treeMock := newTreeService() cfgMock := &configMock{} - workerPool, err := ants.NewPool(1000) + workerPool, err := ants.NewPool(1) if err != nil { return nil, err } - handler := New(params, cfgMock, tree.NewTree(treeMock), workerPool) + handler := New(params, cfgMock, treeMock, workerPool) return &handlerContext{ key: key, diff --git a/internal/handler/head.go b/internal/handler/head.go index ccd6a91..f2e9f38 100644 --- a/internal/handler/head.go +++ b/internal/handler/head.go @@ -2,11 +2,13 @@ package handler import ( "context" + "errors" "io" "net/http" "strconv" "time" + "git.frostfs.info/TrueCloudLab/frostfs-http-gw/internal/layer" "git.frostfs.info/TrueCloudLab/frostfs-http-gw/internal/logs" "git.frostfs.info/TrueCloudLab/frostfs-http-gw/utils" "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/object" @@ -102,14 +104,36 @@ func idsToResponse(resp *fasthttp.Response, obj *object.Object) { // HeadByAddressOrBucketName handles head requests using simple cid/oid or bucketname/key format. func (h *Handler) HeadByAddressOrBucketName(c *fasthttp.RequestCtx) { - test, _ := c.UserValue("oid").(string) - var id oid.ID + cidParam, _ := c.UserValue("cid").(string) + oidParam, _ := c.UserValue("oid").(string) - err := id.DecodeString(test) + ctx := utils.GetContextFromRequest(c) + log := utils.GetReqLogOrDefault(ctx, h.log).With( + zap.String("cid", cidParam), + zap.String("oid", oidParam), + ) + + bktInfo, err := h.getBucketInfo(ctx, cidParam, log) if err != nil { - h.byS3Path(c, h.headObject) + logAndSendBucketError(c, log, err) + return + } + checkS3Err := h.tree.CheckSettingsNodeExists(ctx, bktInfo) + if checkS3Err != nil && !errors.Is(checkS3Err, layer.ErrNodeNotFound) { + logAndSendBucketError(c, log, checkS3Err) + return + } + + req := h.newRequest(c, log) + + var objID oid.ID + if checkS3Err == nil { + h.byS3Path(ctx, req, bktInfo.CID, oidParam, h.headObject) + } else if err = objID.DecodeString(oidParam); err == nil { + h.byNativeAddress(ctx, req, bktInfo.CID, objID, h.headObject) } else { - h.byNativeAddress(c, h.headObject) + logAndSendBucketError(c, log, checkS3Err) + return } } diff --git a/internal/handler/utils.go b/internal/handler/utils.go index b537d64..d09ed23 100644 --- a/internal/handler/utils.go +++ b/internal/handler/utils.go @@ -42,16 +42,7 @@ func bearerToken(ctx context.Context) *bearer.Token { } func isDir(name string) bool { - return strings.HasSuffix(name, "/") -} - -func isObjectID(s string) bool { - var objID oid.ID - return objID.DecodeString(s) == nil -} - -func isContainerRoot(key string) bool { - return key == "" + return name == "" || strings.HasSuffix(name, "/") } func loadAttributes(attrs []object.Attribute) map[string]string { diff --git a/internal/api/layer/tree_service.go b/internal/layer/tree_service.go similarity index 64% rename from internal/api/layer/tree_service.go rename to internal/layer/tree_service.go index beb1e7a..ff80543 100644 --- a/internal/api/layer/tree_service.go +++ b/internal/layer/tree_service.go @@ -4,13 +4,15 @@ import ( "context" "errors" - "git.frostfs.info/TrueCloudLab/frostfs-http-gw/internal/api" + "git.frostfs.info/TrueCloudLab/frostfs-http-gw/internal/data" cid "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/container/id" ) // TreeService provide interface to interact with tree service using s3 data models. type TreeService interface { - GetLatestVersion(ctx context.Context, cnrID *cid.ID, objectName string) (*api.NodeVersion, error) + GetLatestVersion(ctx context.Context, cnrID *cid.ID, objectName string) (*data.NodeVersion, error) + GetSubTreeByPrefix(ctx context.Context, bktInfo *data.BucketInfo, prefix string, latestOnly bool) ([]data.NodeInfo, string, error) + CheckSettingsNodeExists(ctx context.Context, bktInfo *data.BucketInfo) error } var ( diff --git a/internal/logs/logs.go b/internal/logs/logs.go index 8c8b336..7a04064 100644 --- a/internal/logs/logs.go +++ b/internal/logs/logs.go @@ -79,6 +79,11 @@ const ( InvalidLifetimeUsingDefaultValue = "invalid lifetime, using default value (in seconds)" // Error in ../../cmd/http-gw/settings.go InvalidCacheSizeUsingDefaultValue = "invalid cache size, using default value" // Error in ../../cmd/http-gw/settings.go FailedToUnescapeQuery = "failed to unescape query" + FailedToParseAddressInTreeNode = "failed to parse object addr in tree node" + SettingsNodeInvalidOwnerKey = "settings node: invalid owner key" + SystemNodeHasMultipleIDs = "system node has multiple ids" + FailedToRemoveOldSystemNode = "failed to remove old system node" + BucketSettingsNodeHasMultipleIDs = "bucket settings node has multiple ids" ServerReconnecting = "reconnecting server..." ServerReconnectedSuccessfully = "server reconnected successfully" ServerReconnectFailed = "failed to reconnect server" diff --git a/internal/service/frostfs/pool_wrapper.go b/internal/service/frostfs/tree_pool_wrapper.go similarity index 100% rename from internal/service/frostfs/pool_wrapper.go rename to internal/service/frostfs/tree_pool_wrapper.go diff --git a/tree/tree.go b/tree/tree.go index 40209a5..bf0aff9 100644 --- a/tree/tree.go +++ b/tree/tree.go @@ -6,9 +6,8 @@ import ( "fmt" "strings" - "git.frostfs.info/TrueCloudLab/frostfs-http-gw/internal/api" - "git.frostfs.info/TrueCloudLab/frostfs-http-gw/internal/api/layer" "git.frostfs.info/TrueCloudLab/frostfs-http-gw/internal/data" + "git.frostfs.info/TrueCloudLab/frostfs-http-gw/internal/layer" cid "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/container/id" oid "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/object/id" ) @@ -118,7 +117,7 @@ func (n *treeNode) FileName() (string, bool) { return value, ok } -func newNodeVersion(node NodeResponse) (*api.NodeVersion, error) { +func newNodeVersion(node NodeResponse) (*data.NodeVersion, error) { tNode, err := newTreeNode(node) if err != nil { return nil, fmt.Errorf("invalid tree node: %w", err) @@ -127,20 +126,30 @@ func newNodeVersion(node NodeResponse) (*api.NodeVersion, error) { return newNodeVersionFromTreeNode(tNode), nil } -func newNodeVersionFromTreeNode(treeNode *treeNode) *api.NodeVersion { +func newNodeVersionFromTreeNode(treeNode *treeNode) *data.NodeVersion { _, isDeleteMarker := treeNode.Get(isDeleteMarkerKV) - size, _ := treeNode.Get(sizeKV) - version := &api.NodeVersion{ - BaseNodeVersion: api.BaseNodeVersion{ - OID: treeNode.ObjID, + version := &data.NodeVersion{ + BaseNodeVersion: data.BaseNodeVersion{ + OID: treeNode.ObjID, + IsDeleteMarker: isDeleteMarker, }, - DeleteMarker: isDeleteMarker, - IsPrefixNode: size == "", } return version } +func newNodeInfo(node NodeResponse) data.NodeInfo { + nodeMeta := node.GetMeta() + nodeInfo := data.NodeInfo{ + Meta: make([]data.NodeMeta, 0, len(nodeMeta)), + } + for _, meta := range nodeMeta { + nodeInfo.Meta = append(nodeInfo.Meta, meta) + } + + return nodeInfo +} + func newMultiNode(nodes []NodeResponse) (*multiSystemNode, error) { var ( err error @@ -180,7 +189,7 @@ func (m *multiSystemNode) Old() []*treeNode { return m.nodes[1:] } -func (c *Tree) GetLatestVersion(ctx context.Context, cnrID *cid.ID, objectName string) (*api.NodeVersion, error) { +func (c *Tree) GetLatestVersion(ctx context.Context, cnrID *cid.ID, objectName string) (*data.NodeVersion, error) { nodes, err := c.GetVersions(ctx, cnrID, objectName) if err != nil { return nil, err @@ -210,7 +219,7 @@ func (c *Tree) GetVersions(ctx context.Context, cnrID *cid.ID, objectName string return c.service.GetNodes(ctx, p) } -func (c *Tree) CheckSettingsNodeExist(ctx context.Context, bktInfo *data.BucketInfo) error { +func (c *Tree) CheckSettingsNodeExists(ctx context.Context, bktInfo *data.BucketInfo) error { _, err := c.getSystemNode(ctx, bktInfo, settingsFileName) if err != nil { return err @@ -236,7 +245,7 @@ func (c *Tree) getSystemNode(ctx context.Context, bktInfo *data.BucketInfo, name nodes = filterMultipartNodes(nodes) if len(nodes) == 0 { - return nil, ErrNodeNotFound + return nil, layer.ErrNodeNotFound } return newMultiNode(nodes) @@ -298,14 +307,14 @@ func pathFromName(objectName string) []string { return strings.Split(objectName, separator) } -func (c *Tree) GetSubTreeByPrefix(ctx context.Context, bktInfo *data.BucketInfo, prefix string, latestOnly bool) ([]NodeResponse, string, error) { +func (c *Tree) GetSubTreeByPrefix(ctx context.Context, bktInfo *data.BucketInfo, prefix string, latestOnly bool) ([]data.NodeInfo, string, error) { rootID, tailPrefix, err := c.determinePrefixNode(ctx, bktInfo, versionTree, prefix) if err != nil { return nil, "", err } subTree, err := c.service.GetSubTree(ctx, bktInfo, versionTree, rootID, 2, false) if err != nil { - if errors.Is(err, layer.ErrNodeNotFound) { + if errors.Is(err, ErrNodeNotFound) { return nil, "", nil } return nil, "", err @@ -340,14 +349,23 @@ func (c *Tree) GetSubTreeByPrefix(ctx context.Context, bktInfo *data.BucketInfo, nodesMap[fileName] = nodes } - result := make([]NodeResponse, 0, len(subTree)) + result := make([]data.NodeInfo, 0, len(subTree)) for _, nodes := range nodesMap { - result = append(result, nodes...) + result = append(result, nodeResponseToNodeInfo(nodes)...) } return result, strings.TrimSuffix(prefix, tailPrefix), nil } +func nodeResponseToNodeInfo(nodes []NodeResponse) []data.NodeInfo { + nodesInfo := make([]data.NodeInfo, 0, len(nodes)) + for _, node := range nodes { + nodesInfo = append(nodesInfo, newNodeInfo(node)) + } + + return nodesInfo +} + func (c *Tree) determinePrefixNode(ctx context.Context, bktInfo *data.BucketInfo, treeID, prefix string) ([]uint64, string, error) { rootID := []uint64{0} path := strings.Split(prefix, separator)