285 lines
6.4 KiB
Go
285 lines
6.4 KiB
Go
package object
|
|
|
|
import (
|
|
"context"
|
|
"crypto/sha256"
|
|
"time"
|
|
|
|
"github.com/nspcc-dev/neofs-api-go/object"
|
|
"github.com/nspcc-dev/neofs-api-go/service"
|
|
"github.com/nspcc-dev/neofs-api-go/session"
|
|
"github.com/nspcc-dev/neofs-node/pkg/services/object_manager/transformer"
|
|
"github.com/nspcc-dev/neofs-node/pkg/services/object_manager/transport"
|
|
"github.com/nspcc-dev/neofs-node/pkg/services/object_manager/transport/storagegroup"
|
|
"github.com/pkg/errors"
|
|
"go.uber.org/zap"
|
|
)
|
|
|
|
type (
|
|
objectRemover interface {
|
|
delete(context.Context, deleteInfo) error
|
|
}
|
|
|
|
coreObjRemover struct {
|
|
delPrep deletePreparer
|
|
straightRem objectRemover
|
|
tokenStore session.PrivateTokenStore
|
|
|
|
// Set of potential deletePreparer errors that won't be converted into errDeletePrepare
|
|
mErr map[error]struct{}
|
|
|
|
log *zap.Logger
|
|
}
|
|
|
|
straightObjRemover struct {
|
|
tombCreator tombstoneCreator
|
|
objStorer objectStorer
|
|
}
|
|
|
|
tombstoneCreator interface {
|
|
createTombstone(context.Context, deleteInfo) *Object
|
|
}
|
|
|
|
coreTombCreator struct{}
|
|
|
|
deletePreparer interface {
|
|
prepare(context.Context, deleteInfo) ([]deleteInfo, error)
|
|
}
|
|
|
|
coreDelPreparer struct {
|
|
timeout time.Duration
|
|
childLister objectChildrenLister
|
|
}
|
|
|
|
deleteInfo interface {
|
|
transport.AddressInfo
|
|
GetOwnerID() OwnerID
|
|
}
|
|
|
|
rawDeleteInfo struct {
|
|
rawAddrInfo
|
|
ownerID OwnerID
|
|
}
|
|
)
|
|
|
|
const emRemovePart = "could not remove object part #%d of #%d"
|
|
|
|
var (
|
|
_ tombstoneCreator = (*coreTombCreator)(nil)
|
|
_ deleteInfo = (*rawDeleteInfo)(nil)
|
|
_ deletePreparer = (*coreDelPreparer)(nil)
|
|
_ objectRemover = (*straightObjRemover)(nil)
|
|
_ objectRemover = (*coreObjRemover)(nil)
|
|
_ deleteInfo = (*transportRequest)(nil)
|
|
|
|
checksumOfEmptyPayload = sha256.Sum256([]byte{})
|
|
)
|
|
|
|
func (s *objectService) Delete(ctx context.Context, req *object.DeleteRequest) (res *object.DeleteResponse, err error) {
|
|
defer func() {
|
|
if r := recover(); r != nil {
|
|
s.log.Error(panicLogMsg,
|
|
zap.Stringer("request", object.RequestDelete),
|
|
zap.Any("reason", r),
|
|
)
|
|
|
|
err = errServerPanic
|
|
}
|
|
|
|
err = s.statusCalculator.make(requestError{
|
|
t: object.RequestDelete,
|
|
e: err,
|
|
})
|
|
}()
|
|
|
|
if _, err = s.requestHandler.handleRequest(ctx, handleRequestParams{
|
|
request: req,
|
|
executor: s,
|
|
}); err != nil {
|
|
return
|
|
}
|
|
|
|
res = makeDeleteResponse()
|
|
err = s.respPreparer.prepareResponse(ctx, req, res)
|
|
|
|
return
|
|
}
|
|
|
|
func (s *coreObjRemover) delete(ctx context.Context, dInfo deleteInfo) error {
|
|
token := dInfo.GetSessionToken()
|
|
if token == nil {
|
|
return errNilToken
|
|
}
|
|
|
|
key := session.PrivateTokenKey{}
|
|
key.SetOwnerID(dInfo.GetOwnerID())
|
|
key.SetTokenID(token.GetID())
|
|
|
|
pToken, err := s.tokenStore.Fetch(key)
|
|
if err != nil {
|
|
return &detailedError{
|
|
error: errTokenRetrieval,
|
|
d: privateTokenRecvDetails(token.GetID(), token.GetOwnerID()),
|
|
}
|
|
}
|
|
|
|
deleteList, err := s.delPrep.prepare(ctx, dInfo)
|
|
if err != nil {
|
|
if _, ok := s.mErr[errors.Cause(err)]; !ok {
|
|
s.log.Error("delete info preparation failure",
|
|
zap.String("error", err.Error()),
|
|
)
|
|
|
|
err = errDeletePrepare
|
|
}
|
|
|
|
return err
|
|
}
|
|
|
|
ctx = contextWithValues(ctx,
|
|
transformer.PrivateSessionToken, pToken,
|
|
transformer.PublicSessionToken, token,
|
|
storagegroup.BearerToken, dInfo.GetBearerToken(),
|
|
storagegroup.ExtendedHeaders, dInfo.ExtendedHeaders(),
|
|
)
|
|
|
|
for i := range deleteList {
|
|
if err := s.straightRem.delete(ctx, deleteList[i]); err != nil {
|
|
return errors.Wrapf(err, emRemovePart, i+1, len(deleteList))
|
|
}
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func (s *coreDelPreparer) prepare(ctx context.Context, src deleteInfo) ([]deleteInfo, error) {
|
|
var (
|
|
ownerID = src.GetOwnerID()
|
|
token = src.GetSessionToken()
|
|
addr = src.GetAddress()
|
|
bearer = src.GetBearerToken()
|
|
extHdrs = src.ExtendedHeaders()
|
|
)
|
|
|
|
dInfo := newRawDeleteInfo()
|
|
dInfo.setOwnerID(ownerID)
|
|
dInfo.setAddress(addr)
|
|
dInfo.setTTL(service.NonForwardingTTL)
|
|
dInfo.setSessionToken(token)
|
|
dInfo.setBearerToken(bearer)
|
|
dInfo.setExtendedHeaders(extHdrs)
|
|
dInfo.setTimeout(s.timeout)
|
|
|
|
ctx = contextWithValues(ctx,
|
|
transformer.PublicSessionToken, src.GetSessionToken(),
|
|
storagegroup.BearerToken, bearer,
|
|
storagegroup.ExtendedHeaders, extHdrs,
|
|
)
|
|
|
|
children := s.childLister.children(ctx, addr)
|
|
|
|
res := make([]deleteInfo, 0, len(children)+1)
|
|
|
|
res = append(res, dInfo)
|
|
|
|
for i := range children {
|
|
dInfo = newRawDeleteInfo()
|
|
dInfo.setOwnerID(ownerID)
|
|
dInfo.setAddress(Address{
|
|
ObjectID: children[i],
|
|
CID: addr.CID,
|
|
})
|
|
dInfo.setTTL(service.NonForwardingTTL)
|
|
dInfo.setSessionToken(token)
|
|
dInfo.setBearerToken(bearer)
|
|
dInfo.setExtendedHeaders(extHdrs)
|
|
dInfo.setTimeout(s.timeout)
|
|
|
|
res = append(res, dInfo)
|
|
}
|
|
|
|
return res, nil
|
|
}
|
|
|
|
func (s *straightObjRemover) delete(ctx context.Context, dInfo deleteInfo) error {
|
|
putInfo := newRawPutInfo()
|
|
putInfo.setHead(
|
|
s.tombCreator.createTombstone(ctx, dInfo),
|
|
)
|
|
putInfo.setSessionToken(dInfo.GetSessionToken())
|
|
putInfo.setBearerToken(dInfo.GetBearerToken())
|
|
putInfo.setExtendedHeaders(dInfo.ExtendedHeaders())
|
|
putInfo.setTTL(dInfo.GetTTL())
|
|
putInfo.setTimeout(dInfo.GetTimeout())
|
|
|
|
_, err := s.objStorer.putObject(ctx, putInfo)
|
|
|
|
return err
|
|
}
|
|
|
|
func (s *coreTombCreator) createTombstone(ctx context.Context, dInfo deleteInfo) *Object {
|
|
addr := dInfo.GetAddress()
|
|
obj := &Object{
|
|
SystemHeader: SystemHeader{
|
|
ID: addr.ObjectID,
|
|
CID: addr.CID,
|
|
OwnerID: dInfo.GetOwnerID(),
|
|
},
|
|
Headers: []Header{
|
|
{
|
|
Value: &object.Header_Tombstone{
|
|
Tombstone: new(object.Tombstone),
|
|
},
|
|
},
|
|
{
|
|
Value: &object.Header_PayloadChecksum{
|
|
PayloadChecksum: checksumOfEmptyPayload[:],
|
|
},
|
|
},
|
|
},
|
|
}
|
|
|
|
return obj
|
|
}
|
|
|
|
func (s *rawDeleteInfo) GetAddress() Address {
|
|
return s.addr
|
|
}
|
|
|
|
func (s *rawDeleteInfo) setAddress(addr Address) {
|
|
s.addr = addr
|
|
}
|
|
|
|
func (s *rawDeleteInfo) GetOwnerID() OwnerID {
|
|
return s.ownerID
|
|
}
|
|
|
|
func (s *rawDeleteInfo) setOwnerID(id OwnerID) {
|
|
s.ownerID = id
|
|
}
|
|
|
|
func (s *rawDeleteInfo) setAddrInfo(v *rawAddrInfo) {
|
|
s.rawAddrInfo = *v
|
|
s.setType(object.RequestDelete)
|
|
}
|
|
|
|
func newRawDeleteInfo() *rawDeleteInfo {
|
|
res := new(rawDeleteInfo)
|
|
|
|
res.setAddrInfo(newRawAddressInfo())
|
|
|
|
return res
|
|
}
|
|
|
|
func (s *transportRequest) GetToken() *session.Token {
|
|
return s.serviceRequest.(*object.DeleteRequest).GetToken()
|
|
}
|
|
func (s *transportRequest) GetHead() *Object {
|
|
return &Object{SystemHeader: SystemHeader{
|
|
ID: s.serviceRequest.(*object.DeleteRequest).Address.ObjectID,
|
|
}}
|
|
}
|
|
|
|
func (s *transportRequest) GetOwnerID() OwnerID {
|
|
return s.serviceRequest.(*object.DeleteRequest).OwnerID
|
|
}
|