All checks were successful
Build / Build Components (1.20) (pull_request) Successful in 3m52s
Build / Build Components (1.19) (pull_request) Successful in 4m1s
ci/woodpecker/pr/pre-commit Pipeline was successful
Tests and linters / Tests with -race (pull_request) Successful in 5m36s
Tests and linters / Tests (1.20) (pull_request) Successful in 5m55s
Tests and linters / Lint (pull_request) Successful in 14m40s
Tests and linters / Tests (1.19) (pull_request) Successful in 15m29s
ci/woodpecker/push/pre-commit Pipeline was successful
Standardize the alias of the import frostfs-sdk-go/object as objectSDK. Signed-off-by: Alexander Chuprov <a.chuprov@yadro.com>
250 lines
5.7 KiB
Go
250 lines
5.7 KiB
Go
package v2
|
|
|
|
import (
|
|
"context"
|
|
"errors"
|
|
"fmt"
|
|
|
|
"git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/acl"
|
|
objectV2 "git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/object"
|
|
refsV2 "git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/refs"
|
|
"git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/session"
|
|
cid "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/container/id"
|
|
eaclSDK "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/eacl"
|
|
objectSDK "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/object"
|
|
oid "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/object/id"
|
|
"git.frostfs.info/TrueCloudLab/frostfs-sdk-go/user"
|
|
)
|
|
|
|
type Option func(*cfg)
|
|
|
|
type cfg struct {
|
|
storage ObjectStorage
|
|
|
|
msg xHeaderSource
|
|
|
|
cnr cid.ID
|
|
obj *oid.ID
|
|
}
|
|
|
|
type ObjectStorage interface {
|
|
Head(context.Context, oid.Address) (*objectSDK.Object, error)
|
|
}
|
|
|
|
type Request interface {
|
|
GetMetaHeader() *session.RequestMetaHeader
|
|
}
|
|
|
|
type Response interface {
|
|
GetMetaHeader() *session.ResponseMetaHeader
|
|
}
|
|
|
|
type headerSource struct {
|
|
requestHeaders []eaclSDK.Header
|
|
objectHeaders []eaclSDK.Header
|
|
|
|
incompleteObjectHeaders bool
|
|
}
|
|
|
|
func defaultCfg() *cfg {
|
|
return &cfg{
|
|
storage: new(localStorage),
|
|
}
|
|
}
|
|
|
|
func NewMessageHeaderSource(opts ...Option) (eaclSDK.TypedHeaderSource, error) {
|
|
cfg := defaultCfg()
|
|
|
|
for i := range opts {
|
|
opts[i](cfg)
|
|
}
|
|
|
|
if cfg.msg == nil {
|
|
return nil, errors.New("message is not provided")
|
|
}
|
|
|
|
var res headerSource
|
|
|
|
err := cfg.readObjectHeaders(&res)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
res.requestHeaders = requestHeaders(cfg.msg)
|
|
|
|
return res, nil
|
|
}
|
|
|
|
func (h headerSource) HeadersOfType(typ eaclSDK.FilterHeaderType) ([]eaclSDK.Header, bool) {
|
|
switch typ {
|
|
default:
|
|
return nil, true
|
|
case eaclSDK.HeaderFromRequest:
|
|
return h.requestHeaders, true
|
|
case eaclSDK.HeaderFromObject:
|
|
return h.objectHeaders, !h.incompleteObjectHeaders
|
|
}
|
|
}
|
|
|
|
type xHeader session.XHeader
|
|
|
|
func (x xHeader) Key() string {
|
|
return (*session.XHeader)(&x).GetKey()
|
|
}
|
|
|
|
func (x xHeader) Value() string {
|
|
return (*session.XHeader)(&x).GetValue()
|
|
}
|
|
|
|
func requestHeaders(msg xHeaderSource) []eaclSDK.Header {
|
|
return msg.GetXHeaders()
|
|
}
|
|
|
|
var errMissingOID = errors.New("object ID is missing")
|
|
|
|
func (h *cfg) readObjectHeaders(dst *headerSource) error {
|
|
switch m := h.msg.(type) {
|
|
default:
|
|
panic(fmt.Sprintf("unexpected message type %T", h.msg))
|
|
case requestXHeaderSource:
|
|
return h.readObjectHeadersFromRequestXHeaderSource(m, dst)
|
|
case responseXHeaderSource:
|
|
return h.readObjectHeadersResponseXHeaderSource(m, dst)
|
|
}
|
|
}
|
|
|
|
func (h *cfg) readObjectHeadersFromRequestXHeaderSource(m requestXHeaderSource, dst *headerSource) error {
|
|
switch req := m.req.(type) {
|
|
case
|
|
*objectV2.GetRequest,
|
|
*objectV2.HeadRequest:
|
|
if h.obj == nil {
|
|
return errMissingOID
|
|
}
|
|
|
|
objHeaders, completed := h.localObjectHeaders(h.cnr, h.obj)
|
|
|
|
dst.objectHeaders = objHeaders
|
|
dst.incompleteObjectHeaders = !completed
|
|
case
|
|
*objectV2.GetRangeRequest,
|
|
*objectV2.GetRangeHashRequest,
|
|
*objectV2.DeleteRequest:
|
|
if h.obj == nil {
|
|
return errMissingOID
|
|
}
|
|
|
|
dst.objectHeaders = addressHeaders(h.cnr, h.obj)
|
|
case *objectV2.PutRequest:
|
|
if v, ok := req.GetBody().GetObjectPart().(*objectV2.PutObjectPartInit); ok {
|
|
oV2 := new(objectV2.Object)
|
|
oV2.SetObjectID(v.GetObjectID())
|
|
oV2.SetHeader(v.GetHeader())
|
|
|
|
dst.objectHeaders = headersFromObject(objectSDK.NewFromV2(oV2), h.cnr, h.obj)
|
|
}
|
|
case *objectV2.SearchRequest:
|
|
cnrV2 := req.GetBody().GetContainerID()
|
|
var cnr cid.ID
|
|
|
|
if cnrV2 != nil {
|
|
if err := cnr.ReadFromV2(*cnrV2); err != nil {
|
|
return fmt.Errorf("can't parse container ID: %w", err)
|
|
}
|
|
}
|
|
|
|
dst.objectHeaders = []eaclSDK.Header{cidHeader(cnr)}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func (h *cfg) readObjectHeadersResponseXHeaderSource(m responseXHeaderSource, dst *headerSource) error {
|
|
switch resp := m.resp.(type) {
|
|
default:
|
|
objectHeaders, completed := h.localObjectHeaders(h.cnr, h.obj)
|
|
|
|
dst.objectHeaders = objectHeaders
|
|
dst.incompleteObjectHeaders = !completed
|
|
case *objectV2.GetResponse:
|
|
if v, ok := resp.GetBody().GetObjectPart().(*objectV2.GetObjectPartInit); ok {
|
|
oV2 := new(objectV2.Object)
|
|
oV2.SetObjectID(v.GetObjectID())
|
|
oV2.SetHeader(v.GetHeader())
|
|
|
|
dst.objectHeaders = headersFromObject(objectSDK.NewFromV2(oV2), h.cnr, h.obj)
|
|
}
|
|
case *objectV2.HeadResponse:
|
|
oV2 := new(objectV2.Object)
|
|
|
|
var hdr *objectV2.Header
|
|
|
|
switch v := resp.GetBody().GetHeaderPart().(type) {
|
|
case *objectV2.ShortHeader:
|
|
hdr = new(objectV2.Header)
|
|
|
|
var idV2 refsV2.ContainerID
|
|
h.cnr.WriteToV2(&idV2)
|
|
|
|
hdr.SetContainerID(&idV2)
|
|
hdr.SetVersion(v.GetVersion())
|
|
hdr.SetCreationEpoch(v.GetCreationEpoch())
|
|
hdr.SetOwnerID(v.GetOwnerID())
|
|
hdr.SetObjectType(v.GetObjectType())
|
|
hdr.SetPayloadLength(v.GetPayloadLength())
|
|
case *objectV2.HeaderWithSignature:
|
|
hdr = v.GetHeader()
|
|
}
|
|
|
|
oV2.SetHeader(hdr)
|
|
|
|
dst.objectHeaders = headersFromObject(objectSDK.NewFromV2(oV2), h.cnr, h.obj)
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func (h *cfg) localObjectHeaders(cnr cid.ID, idObj *oid.ID) ([]eaclSDK.Header, bool) {
|
|
if idObj != nil {
|
|
var addr oid.Address
|
|
addr.SetContainer(cnr)
|
|
addr.SetObject(*idObj)
|
|
|
|
obj, err := h.storage.Head(context.TODO(), addr)
|
|
if err == nil {
|
|
return headersFromObject(obj, cnr, idObj), true
|
|
}
|
|
}
|
|
|
|
return addressHeaders(cnr, idObj), false
|
|
}
|
|
|
|
func cidHeader(idCnr cid.ID) sysObjHdr {
|
|
return sysObjHdr{
|
|
k: acl.FilterObjectContainerID,
|
|
v: idCnr.EncodeToString(),
|
|
}
|
|
}
|
|
|
|
func oidHeader(obj oid.ID) sysObjHdr {
|
|
return sysObjHdr{
|
|
k: acl.FilterObjectID,
|
|
v: obj.EncodeToString(),
|
|
}
|
|
}
|
|
|
|
func ownerIDHeader(ownerID user.ID) sysObjHdr {
|
|
return sysObjHdr{
|
|
k: acl.FilterObjectOwnerID,
|
|
v: ownerID.EncodeToString(),
|
|
}
|
|
}
|
|
|
|
func addressHeaders(cnr cid.ID, oid *oid.ID) []eaclSDK.Header {
|
|
hh := make([]eaclSDK.Header, 0, 2)
|
|
hh = append(hh, cidHeader(cnr))
|
|
|
|
if oid != nil {
|
|
hh = append(hh, oidHeader(*oid))
|
|
}
|
|
|
|
return hh
|
|
}
|