[#488] Sync using oid.ID

Signed-off-by: Denis Kirillov <denis@nspcc.ru>
This commit is contained in:
Denis Kirillov 2022-06-27 12:33:36 +03:00 committed by Alex Vanin
parent 85c203e157
commit c88a9842db
18 changed files with 224 additions and 139 deletions

View file

@ -75,9 +75,61 @@ func (p *PartInfo) ToHeaderString() string {
// LockInfo is lock information to create appropriate tree node. // LockInfo is lock information to create appropriate tree node.
type LockInfo struct { type LockInfo struct {
ID uint64 id uint64
LegalHoldOID *oid.ID
RetentionOID *oid.ID legalHoldOID oid.ID
UntilDate string setLegalHold bool
IsCompliance bool
retentionOID oid.ID
setRetention bool
untilDate string
isCompliance bool
}
func NewLockInfo(id uint64) *LockInfo {
return &LockInfo{id: id}
}
func (l LockInfo) ID() uint64 {
return l.id
}
func (l *LockInfo) SetLegalHold(objID oid.ID) {
l.legalHoldOID = objID
l.setLegalHold = true
}
func (l *LockInfo) ResetLegalHold() {
l.setLegalHold = false
}
func (l LockInfo) LegalHold() oid.ID {
return l.legalHoldOID
}
func (l LockInfo) IsLegalHoldSet() bool {
return l.setLegalHold
}
func (l *LockInfo) SetRetention(objID oid.ID, until string, isCompliance bool) {
l.retentionOID = objID
l.setRetention = true
l.untilDate = until
l.isCompliance = isCompliance
}
func (l LockInfo) IsRetentionSet() bool {
return l.setRetention
}
func (l LockInfo) Retention() oid.ID {
return l.retentionOID
}
func (l LockInfo) UntilDate() string {
return l.untilDate
}
func (l LockInfo) IsCompliance() bool {
return l.isCompliance
} }

View file

@ -120,12 +120,12 @@ func (h *handler) setLockingHeaders(bktInfo *data.BucketInfo, lockInfo *data.Loc
legalHold := &data.LegalHold{Status: legalHoldOff} legalHold := &data.LegalHold{Status: legalHoldOff}
retention := &data.Retention{Mode: governanceMode} retention := &data.Retention{Mode: governanceMode}
if lockInfo.LegalHoldOID != nil { if lockInfo.IsLegalHoldSet() {
legalHold.Status = legalHoldOn legalHold.Status = legalHoldOn
} }
if lockInfo.RetentionOID != nil { if lockInfo.IsRetentionSet() {
retention.RetainUntilDate = lockInfo.UntilDate retention.RetainUntilDate = lockInfo.UntilDate()
if lockInfo.IsCompliance { if lockInfo.IsCompliance() {
retention.Mode = complianceMode retention.Mode = complianceMode
} }
} }

View file

@ -174,7 +174,7 @@ func (h *handler) GetObjectLegalHoldHandler(w http.ResponseWriter, r *http.Reque
} }
legalHold := &data.LegalHold{Status: legalHoldOff} legalHold := &data.LegalHold{Status: legalHoldOff}
if lockInfo.LegalHoldOID != nil { if lockInfo.IsLegalHoldSet() {
legalHold.Status = legalHoldOn legalHold.Status = legalHoldOn
} }
@ -248,16 +248,16 @@ func (h *handler) GetObjectRetentionHandler(w http.ResponseWriter, r *http.Reque
return return
} }
if lockInfo.RetentionOID == nil { if !lockInfo.IsRetentionSet() {
h.logAndSendError(w, "retention lock isn't set", reqInfo, apiErrors.GetAPIError(apiErrors.ErrNoSuchKey)) h.logAndSendError(w, "retention lock isn't set", reqInfo, apiErrors.GetAPIError(apiErrors.ErrNoSuchKey))
return return
} }
retention := &data.Retention{ retention := &data.Retention{
Mode: governanceMode, Mode: governanceMode,
RetainUntilDate: lockInfo.UntilDate, RetainUntilDate: lockInfo.UntilDate(),
} }
if lockInfo.IsCompliance { if lockInfo.IsCompliance() {
retention.Mode = complianceMode retention.Mode = complianceMode
} }

View file

@ -49,12 +49,13 @@ func (n *layer) PutBucketCORS(ctx context.Context, p *PutCORSParams) error {
} }
objIDToDelete, err := n.treeService.PutBucketCORS(ctx, p.BktInfo.CID, objID) objIDToDelete, err := n.treeService.PutBucketCORS(ctx, p.BktInfo.CID, objID)
if err != nil { objIDToDeleteNotFound := errorsStd.Is(err, ErrNoNodeToRemove)
if err != nil && !objIDToDeleteNotFound {
return err return err
} }
if objIDToDelete != nil { if !objIDToDeleteNotFound {
if err = n.objectDelete(ctx, p.BktInfo, *objIDToDelete); err != nil { if err = n.objectDelete(ctx, p.BktInfo, objIDToDelete); err != nil {
n.log.Error("couldn't delete cors object", zap.Error(err), n.log.Error("couldn't delete cors object", zap.Error(err),
zap.String("cnrID", p.BktInfo.CID.EncodeToString()), zap.String("cnrID", p.BktInfo.CID.EncodeToString()),
zap.String("bucket name", p.BktInfo.Name), zap.String("bucket name", p.BktInfo.Name),
@ -83,11 +84,12 @@ func (n *layer) GetBucketCORS(ctx context.Context, bktInfo *data.BucketInfo) (*d
func (n *layer) DeleteBucketCORS(ctx context.Context, bktInfo *data.BucketInfo) error { func (n *layer) DeleteBucketCORS(ctx context.Context, bktInfo *data.BucketInfo) error {
objID, err := n.treeService.DeleteBucketCORS(ctx, bktInfo.CID) objID, err := n.treeService.DeleteBucketCORS(ctx, bktInfo.CID)
if err != nil { objIDNotFound := errorsStd.Is(err, ErrNoNodeToRemove)
if err != nil && !objIDNotFound {
return err return err
} }
if objID != nil { if !objIDNotFound {
if err = n.objectDelete(ctx, bktInfo, *objID); err != nil { if err = n.objectDelete(ctx, bktInfo, objID); err != nil {
return err return err
} }
} }

View file

@ -462,15 +462,15 @@ func (n *layer) CopyObject(ctx context.Context, p *CopyObjectParams) (*data.Obje
}) })
} }
func getRandomOID() (*oid.ID, error) { func getRandomOID() (oid.ID, error) {
b := [32]byte{} b := [32]byte{}
if _, err := rand.Read(b[:]); err != nil { if _, err := rand.Read(b[:]); err != nil {
return nil, err return oid.ID{}, err
} }
var objID oid.ID var objID oid.ID
objID.SetSHA256(b) objID.SetSHA256(b)
return &objID, nil return objID, nil
} }
// DeleteObject removes all objects with the passed nice name. // DeleteObject removes all objects with the passed nice name.
@ -505,7 +505,7 @@ func (n *layer) deleteObject(ctx context.Context, bkt *data.BucketInfo, obj *Ver
newVersion := &data.NodeVersion{ newVersion := &data.NodeVersion{
BaseNodeVersion: data.BaseNodeVersion{ BaseNodeVersion: data.BaseNodeVersion{
OID: *randOID, OID: randOID,
FilePath: obj.Name, FilePath: obj.Name,
}, },
DeleteMarker: &data.DeleteMarkerInfo{ DeleteMarker: &data.DeleteMarkerInfo{

View file

@ -36,7 +36,7 @@ func TestObjectLockAttributes(t *testing.T) {
foundLock, err := tc.layer.GetLockInfo(tc.ctx, p) foundLock, err := tc.layer.GetLockInfo(tc.ctx, p)
require.NoError(t, err) require.NoError(t, err)
lockObj := tc.getObjectByID(*foundLock.RetentionOID) lockObj := tc.getObjectByID(foundLock.Retention())
require.NotNil(t, lockObj) require.NotNil(t, lockObj)
expEpoch := false expEpoch := false

View file

@ -198,18 +198,19 @@ func (n *layer) uploadPart(ctx context.Context, multipartInfo *data.MultipartInf
Key: p.Info.Key, Key: p.Info.Key,
UploadID: p.Info.UploadID, UploadID: p.Info.UploadID,
Number: p.PartNumber, Number: p.PartNumber,
OID: *id, OID: id,
Size: p.Size, Size: p.Size,
ETag: hex.EncodeToString(hash), ETag: hex.EncodeToString(hash),
Created: time.Now(), Created: time.Now(),
} }
oldPartID, err := n.treeService.AddPart(ctx, bktInfo.CID, multipartInfo.ID, partInfo) oldPartID, err := n.treeService.AddPart(ctx, bktInfo.CID, multipartInfo.ID, partInfo)
if err != nil { oldPartIDNotFound := stderrors.Is(err, ErrNoNodeToRemove)
if err != nil && !oldPartIDNotFound {
return nil, err return nil, err
} }
if oldPartID != nil { if !oldPartIDNotFound {
if err = n.objectDelete(ctx, bktInfo, *oldPartID); err != nil { if err = n.objectDelete(ctx, bktInfo, oldPartID); err != nil {
n.log.Error("couldn't delete old part object", zap.Error(err), n.log.Error("couldn't delete old part object", zap.Error(err),
zap.String("cnrID", bktInfo.CID.EncodeToString()), zap.String("cnrID", bktInfo.CID.EncodeToString()),
zap.String("bucket name", bktInfo.Name), zap.String("bucket name", bktInfo.Name),
@ -218,7 +219,7 @@ func (n *layer) uploadPart(ctx context.Context, multipartInfo *data.MultipartInf
} }
objInfo := &data.ObjectInfo{ objInfo := &data.ObjectInfo{
ID: *id, ID: id,
CID: bktInfo.CID, CID: bktInfo.CID,
Owner: bktInfo.Owner, Owner: bktInfo.Owner,

View file

@ -146,7 +146,7 @@ type NeoFS interface {
// //
// Created container is public with enabled ACL extension. // Created container is public with enabled ACL extension.
// //
// It returns exactly one non-nil value. It returns any error encountered which // It returns exactly one non-zero value. It returns any error encountered which
// prevented the container from being created. // prevented the container from being created.
CreateContainer(context.Context, PrmContainerCreate) (cid.ID, error) CreateContainer(context.Context, PrmContainerCreate) (cid.ID, error)
@ -214,9 +214,9 @@ type NeoFS interface {
// //
// It returns ErrAccessDenied on write access violation. // It returns ErrAccessDenied on write access violation.
// //
// It returns exactly one non-nil value. It returns any error encountered which // It returns exactly one non-zero value. It returns any error encountered which
// prevented the container from being created. // prevented the container from being created.
CreateObject(context.Context, PrmObjectCreate) (*oid.ID, error) CreateObject(context.Context, PrmObjectCreate) (oid.ID, error)
// DeleteObject marks the object to be removed from the NeoFS container by identifier. // DeleteObject marks the object to be removed from the NeoFS container by identifier.
// Successful return does not guarantee actual removal. // Successful return does not guarantee actual removal.

View file

@ -178,10 +178,10 @@ func (t *TestNeoFS) ReadObject(_ context.Context, prm PrmObjectRead) (*ObjectPar
return nil, fmt.Errorf("object not found %s", addr) return nil, fmt.Errorf("object not found %s", addr)
} }
func (t *TestNeoFS) CreateObject(_ context.Context, prm PrmObjectCreate) (*oid.ID, error) { func (t *TestNeoFS) CreateObject(_ context.Context, prm PrmObjectCreate) (oid.ID, error) {
b := make([]byte, 32) b := make([]byte, 32)
if _, err := io.ReadFull(rand.Reader, b); err != nil { if _, err := io.ReadFull(rand.Reader, b); err != nil {
return nil, err return oid.ID{}, err
} }
var id oid.ID var id oid.ID
id.SetSHA256(sha256.Sum256(b)) id.SetSHA256(sha256.Sum256(b))
@ -219,7 +219,7 @@ func (t *TestNeoFS) CreateObject(_ context.Context, prm PrmObjectCreate) (*oid.I
if prm.Payload != nil { if prm.Payload != nil {
all, err := io.ReadAll(prm.Payload) all, err := io.ReadAll(prm.Payload)
if err != nil { if err != nil {
return nil, err return oid.ID{}, err
} }
obj.SetPayload(all) obj.SetPayload(all)
obj.SetPayloadSize(uint64(len(all))) obj.SetPayloadSize(uint64(len(all)))
@ -233,7 +233,7 @@ func (t *TestNeoFS) CreateObject(_ context.Context, prm PrmObjectCreate) (*oid.I
addr := newAddress(cnrID, objID) addr := newAddress(cnrID, objID)
t.objects[addr.EncodeToString()] = obj t.objects[addr.EncodeToString()] = obj
return &objID, nil return objID, nil
} }
func (t *TestNeoFS) DeleteObject(_ context.Context, prm PrmObjectDelete) error { func (t *TestNeoFS) DeleteObject(_ context.Context, prm PrmObjectDelete) error {

View file

@ -39,12 +39,13 @@ func (n *layer) PutBucketNotificationConfiguration(ctx context.Context, p *PutBu
} }
objIDToDelete, err := n.treeService.PutNotificationConfigurationNode(ctx, p.BktInfo.CID, objID) objIDToDelete, err := n.treeService.PutNotificationConfigurationNode(ctx, p.BktInfo.CID, objID)
if err != nil { objIDToDeleteNotFound := errorsStd.Is(err, ErrNoNodeToRemove)
if err != nil && !objIDToDeleteNotFound {
return err return err
} }
if objIDToDelete != nil { if !objIDToDeleteNotFound {
if err = n.objectDelete(ctx, p.BktInfo, *objIDToDelete); err != nil { if err = n.objectDelete(ctx, p.BktInfo, objIDToDelete); err != nil {
n.log.Error("couldn't delete notification configuration object", zap.Error(err), n.log.Error("couldn't delete notification configuration object", zap.Error(err),
zap.String("cnrID", p.BktInfo.CID.EncodeToString()), zap.String("cnrID", p.BktInfo.CID.EncodeToString()),
zap.String("bucket name", p.BktInfo.Name), zap.String("bucket name", p.BktInfo.Name),
@ -67,14 +68,15 @@ func (n *layer) GetBucketNotificationConfiguration(ctx context.Context, bktInfo
} }
objID, err := n.treeService.GetNotificationConfigurationNode(ctx, bktInfo.CID) objID, err := n.treeService.GetNotificationConfigurationNode(ctx, bktInfo.CID)
if err != nil && !errorsStd.Is(err, ErrNodeNotFound) { objIDNotFound := errorsStd.Is(err, ErrNodeNotFound)
if err != nil && !objIDNotFound {
return nil, err return nil, err
} }
conf := &data.NotificationConfiguration{} conf := &data.NotificationConfiguration{}
if objID != nil { if !objIDNotFound {
obj, err := n.objectGet(ctx, bktInfo, *objID) obj, err := n.objectGet(ctx, bktInfo, objID)
if err != nil { if err != nil {
return nil, err return nil, err
} }

View file

@ -189,7 +189,7 @@ func (n *layer) PutObject(ctx context.Context, p *PutObjectParams) (*data.Object
return nil, err return nil, err
} }
newVersion.OID = *id newVersion.OID = id
if err = n.treeService.AddVersion(ctx, p.BktInfo.CID, newVersion); err != nil { if err = n.treeService.AddVersion(ctx, p.BktInfo.CID, newVersion); err != nil {
return nil, fmt.Errorf("couldn't add new verion to tree service: %w", err) return nil, fmt.Errorf("couldn't add new verion to tree service: %w", err)
} }
@ -209,7 +209,7 @@ func (n *layer) PutObject(ctx context.Context, p *PutObjectParams) (*data.Object
n.listsCache.CleanCacheEntriesContainingObject(p.Object, p.BktInfo.CID) n.listsCache.CleanCacheEntriesContainingObject(p.Object, p.BktInfo.CID)
objInfo := &data.ObjectInfo{ objInfo := &data.ObjectInfo{
ID: *id, ID: id,
CID: p.BktInfo.CID, CID: p.BktInfo.CID,
Owner: own, Owner: own,
@ -344,7 +344,7 @@ func (n *layer) objectDelete(ctx context.Context, bktInfo *data.BucketInfo, idOb
// objectPutAndHash prepare auth parameters and invoke neofs.CreateObject. // objectPutAndHash prepare auth parameters and invoke neofs.CreateObject.
// Returns object ID and payload sha256 hash. // Returns object ID and payload sha256 hash.
func (n *layer) objectPutAndHash(ctx context.Context, prm PrmObjectCreate, bktInfo *data.BucketInfo) (*oid.ID, []byte, error) { func (n *layer) objectPutAndHash(ctx context.Context, prm PrmObjectCreate, bktInfo *data.BucketInfo) (oid.ID, []byte, error) {
n.prepareAuthParameters(ctx, &prm.PrmAuth, bktInfo.Owner) n.prepareAuthParameters(ctx, &prm.PrmAuth, bktInfo.Owner)
hash := sha256.New() hash := sha256.New()
prm.Payload = wrapReader(prm.Payload, 64*1024, func(buf []byte) { prm.Payload = wrapReader(prm.Payload, 64*1024, func(buf []byte) {

View file

@ -36,18 +36,19 @@ func (n *layer) PutLockInfo(ctx context.Context, objVersion *ObjectVersion, newL
} }
if newLock.Retention != nil { if newLock.Retention != nil {
if lockInfo.RetentionOID != nil { if lockInfo.IsRetentionSet() {
if lockInfo.IsCompliance { if lockInfo.IsCompliance() {
return fmt.Errorf("you cannot change compliance mode") return fmt.Errorf("you cannot change compliance mode")
} }
if !newLock.Retention.ByPassedGovernance { if !newLock.Retention.ByPassedGovernance {
return fmt.Errorf("you cannot bypass governence mode") return fmt.Errorf("you cannot bypass governence mode")
} }
if len(lockInfo.UntilDate) > 0 { untilDate := lockInfo.UntilDate()
parsedTime, err := time.Parse(time.RFC3339, lockInfo.UntilDate) if len(untilDate) > 0 {
parsedTime, err := time.Parse(time.RFC3339, untilDate)
if err != nil { if err != nil {
return fmt.Errorf("couldn't parse time '%s': %w", lockInfo.UntilDate, err) return fmt.Errorf("couldn't parse time '%s': %w", untilDate, err)
} }
if parsedTime.After(newLock.Retention.Until) { if parsedTime.After(newLock.Retention.Until) {
return fmt.Errorf("you couldn't short the until date") return fmt.Errorf("you couldn't short the until date")
@ -55,24 +56,26 @@ func (n *layer) PutLockInfo(ctx context.Context, objVersion *ObjectVersion, newL
} }
} }
lock := &data.ObjectLock{Retention: newLock.Retention} lock := &data.ObjectLock{Retention: newLock.Retention}
if lockInfo.RetentionOID, err = n.putLockObject(ctx, objVersion.BktInfo, versionNode.OID, lock); err != nil { retentionOID, err := n.putLockObject(ctx, objVersion.BktInfo, versionNode.OID, lock)
if err != nil {
return err return err
} }
lockInfo.IsCompliance = newLock.Retention.IsCompliance lockInfo.SetRetention(retentionOID, newLock.Retention.Until.UTC().Format(time.RFC3339), newLock.Retention.IsCompliance)
lockInfo.UntilDate = newLock.Retention.Until.UTC().Format(time.RFC3339)
} }
if newLock.LegalHold != nil { if newLock.LegalHold != nil {
if newLock.LegalHold.Enabled && lockInfo.LegalHoldOID == nil { if newLock.LegalHold.Enabled && !lockInfo.IsLegalHoldSet() {
lock := &data.ObjectLock{LegalHold: newLock.LegalHold} lock := &data.ObjectLock{LegalHold: newLock.LegalHold}
if lockInfo.LegalHoldOID, err = n.putLockObject(ctx, objVersion.BktInfo, versionNode.OID, lock); err != nil { legalHoldOID, err := n.putLockObject(ctx, objVersion.BktInfo, versionNode.OID, lock)
if err != nil {
return err return err
} }
} else if !newLock.LegalHold.Enabled && lockInfo.LegalHoldOID != nil { lockInfo.SetLegalHold(legalHoldOID)
if err = n.objectDelete(ctx, objVersion.BktInfo, *lockInfo.LegalHoldOID); err != nil { } else if !newLock.LegalHold.Enabled && lockInfo.IsLegalHoldSet() {
return fmt.Errorf("couldn't delete lock object '%s' to remove legal hold: %w", lockInfo.LegalHoldOID.EncodeToString(), err) if err = n.objectDelete(ctx, objVersion.BktInfo, lockInfo.LegalHold()); err != nil {
return fmt.Errorf("couldn't delete lock object '%s' to remove legal hold: %w", lockInfo.LegalHold().EncodeToString(), err)
} }
lockInfo.LegalHoldOID = nil lockInfo.ResetLegalHold()
} }
} }
@ -87,7 +90,7 @@ func (n *layer) PutLockInfo(ctx context.Context, objVersion *ObjectVersion, newL
return nil return nil
} }
func (n *layer) putLockObject(ctx context.Context, bktInfo *data.BucketInfo, objID oid.ID, lock *data.ObjectLock) (*oid.ID, error) { func (n *layer) putLockObject(ctx context.Context, bktInfo *data.BucketInfo, objID oid.ID, lock *data.ObjectLock) (oid.ID, error) {
prm := PrmObjectCreate{ prm := PrmObjectCreate{
Container: bktInfo.CID, Container: bktInfo.CID,
Creator: bktInfo.Owner, Creator: bktInfo.Owner,
@ -97,7 +100,7 @@ func (n *layer) putLockObject(ctx context.Context, bktInfo *data.BucketInfo, obj
var err error var err error
prm.Attributes, err = n.attributesFromLock(ctx, lock) prm.Attributes, err = n.attributesFromLock(ctx, lock)
if err != nil { if err != nil {
return nil, err return oid.ID{}, err
} }
id, _, err := n.objectPutAndHash(ctx, prm, bktInfo) id, _, err := n.objectPutAndHash(ctx, prm, bktInfo)
@ -134,15 +137,16 @@ func (n *layer) getCORS(ctx context.Context, bkt *data.BucketInfo, sysName strin
return cors, nil return cors, nil
} }
objID, err := n.treeService.GetBucketCORS(ctx, bkt.CID) objID, err := n.treeService.GetBucketCORS(ctx, bkt.CID)
if err != nil { objIDNotFound := errorsStd.Is(err, ErrNodeNotFound)
if err != nil && !objIDNotFound {
return nil, err return nil, err
} }
if objID == nil { if objIDNotFound {
return nil, errors.GetAPIError(errors.ErrNoSuchCORSConfiguration) return nil, errors.GetAPIError(errors.ErrNoSuchCORSConfiguration)
} }
obj, err := n.objectGet(ctx, bkt, *objID) obj, err := n.objectGet(ctx, bkt, objID)
if err != nil { if err != nil {
return nil, err return nil, err
} }

View file

@ -81,23 +81,23 @@ func (t *TreeServiceMock) GetSettingsNode(_ context.Context, id cid.ID) (*data.B
return settings, nil return settings, nil
} }
func (t *TreeServiceMock) GetNotificationConfigurationNode(ctx context.Context, cnrID cid.ID) (*oid.ID, error) { func (t *TreeServiceMock) GetNotificationConfigurationNode(ctx context.Context, cnrID cid.ID) (oid.ID, error) {
panic("implement me") panic("implement me")
} }
func (t *TreeServiceMock) PutNotificationConfigurationNode(ctx context.Context, cnrID cid.ID, objID *oid.ID) (*oid.ID, error) { func (t *TreeServiceMock) PutNotificationConfigurationNode(ctx context.Context, cnrID cid.ID, objID oid.ID) (oid.ID, error) {
panic("implement me") panic("implement me")
} }
func (t *TreeServiceMock) GetBucketCORS(ctx context.Context, cnrID cid.ID) (*oid.ID, error) { func (t *TreeServiceMock) GetBucketCORS(ctx context.Context, cnrID cid.ID) (oid.ID, error) {
panic("implement me") panic("implement me")
} }
func (t *TreeServiceMock) PutBucketCORS(ctx context.Context, cnrID cid.ID, objID *oid.ID) (*oid.ID, error) { func (t *TreeServiceMock) PutBucketCORS(ctx context.Context, cnrID cid.ID, objID oid.ID) (oid.ID, error) {
panic("implement me") panic("implement me")
} }
func (t *TreeServiceMock) DeleteBucketCORS(ctx context.Context, cnrID cid.ID) (*oid.ID, error) { func (t *TreeServiceMock) DeleteBucketCORS(ctx context.Context, cnrID cid.ID) (oid.ID, error) {
panic("implement me") panic("implement me")
} }
@ -294,14 +294,14 @@ func (t *TreeServiceMock) GetMultipartUpload(_ context.Context, cnrID cid.ID, ob
return nil, ErrNodeNotFound return nil, ErrNodeNotFound
} }
func (t *TreeServiceMock) AddPart(ctx context.Context, cnrID cid.ID, multipartNodeID uint64, info *data.PartInfo) (oldObjIDToDelete *oid.ID, err error) { func (t *TreeServiceMock) AddPart(ctx context.Context, cnrID cid.ID, multipartNodeID uint64, info *data.PartInfo) (oldObjIDToDelete oid.ID, err error) {
multipartInfo, err := t.GetMultipartUpload(ctx, cnrID, info.Key, info.UploadID) multipartInfo, err := t.GetMultipartUpload(ctx, cnrID, info.Key, info.UploadID)
if err != nil { if err != nil {
return nil, err return oid.ID{}, err
} }
if multipartInfo.ID != multipartNodeID { if multipartInfo.ID != multipartNodeID {
return nil, fmt.Errorf("invalid multipart info id") return oid.ID{}, fmt.Errorf("invalid multipart info id")
} }
partsMap, ok := t.parts[info.UploadID] partsMap, ok := t.parts[info.UploadID]
@ -312,7 +312,7 @@ func (t *TreeServiceMock) AddPart(ctx context.Context, cnrID cid.ID, multipartNo
partsMap[info.Number] = info partsMap[info.Number] = info
t.parts[info.UploadID] = partsMap t.parts[info.UploadID] = partsMap
return nil, nil return oid.ID{}, nil
} }
func (t *TreeServiceMock) GetParts(_ context.Context, cnrID cid.ID, multipartNodeID uint64) ([]*data.PartInfo, error) { func (t *TreeServiceMock) GetParts(_ context.Context, cnrID cid.ID, multipartNodeID uint64) ([]*data.PartInfo, error) {

View file

@ -16,19 +16,34 @@ type TreeService interface {
// GetSettingsNode retrieves the settings node from the tree service and form data.BucketSettings. // GetSettingsNode retrieves the settings node from the tree service and form data.BucketSettings.
// //
// If node is not found returns ErrNodeNotFound error. // If tree node is not found returns ErrNodeNotFound error.
GetSettingsNode(context.Context, cid.ID) (*data.BucketSettings, error) GetSettingsNode(context.Context, cid.ID) (*data.BucketSettings, error)
GetNotificationConfigurationNode(ctx context.Context, cnrID cid.ID) (*oid.ID, error) // GetNotificationConfigurationNode gets an object id that corresponds to object with bucket CORS.
// PutNotificationConfigurationNode puts a node to a system tree //
// and returns objectID of a previous notif config which must be deleted in NeoFS // If tree node is not found returns ErrNodeNotFound error.
PutNotificationConfigurationNode(ctx context.Context, cnrID cid.ID, objID *oid.ID) (*oid.ID, error) GetNotificationConfigurationNode(ctx context.Context, cnrID cid.ID) (oid.ID, error)
GetBucketCORS(ctx context.Context, cnrID cid.ID) (*oid.ID, error) // PutNotificationConfigurationNode puts a node to a system tree
// PutBucketCORS puts a node to a system tree and returns objectID of a previous cors config which must be deleted in NeoFS // and returns objectID of a previous notif config which must be deleted in NeoFS.
PutBucketCORS(ctx context.Context, cnrID cid.ID, objID *oid.ID) (*oid.ID, error) //
// DeleteBucketCORS removes a node from a system tree and returns objID which must be deleted in NeoFS // If object id to remove is not found returns ErrNoNodeToRemove error.
DeleteBucketCORS(ctx context.Context, cnrID cid.ID) (*oid.ID, error) PutNotificationConfigurationNode(ctx context.Context, cnrID cid.ID, objID oid.ID) (oid.ID, error)
// GetBucketCORS gets an object id that corresponds to object with bucket CORS.
//
// If object id is not found returns ErrNodeNotFound error.
GetBucketCORS(ctx context.Context, cnrID cid.ID) (oid.ID, error)
// PutBucketCORS puts a node to a system tree and returns objectID of a previous cors config which must be deleted in NeoFS.
//
// If object id to remove is not found returns ErrNoNodeToRemove error.
PutBucketCORS(ctx context.Context, cnrID cid.ID, objID oid.ID) (oid.ID, error)
// DeleteBucketCORS removes a node from a system tree and returns objID which must be deleted in NeoFS.
//
// If object id to remove is not found returns ErrNoNodeToRemove error.
DeleteBucketCORS(ctx context.Context, cnrID cid.ID) (oid.ID, error)
GetObjectTagging(ctx context.Context, cnrID cid.ID, objVersion *data.NodeVersion) (map[string]string, error) GetObjectTagging(ctx context.Context, cnrID cid.ID, objVersion *data.NodeVersion) (map[string]string, error)
PutObjectTagging(ctx context.Context, cnrID cid.ID, objVersion *data.NodeVersion, tagSet map[string]string) error PutObjectTagging(ctx context.Context, cnrID cid.ID, objVersion *data.NodeVersion, tagSet map[string]string) error
@ -56,8 +71,9 @@ type TreeService interface {
// AddPart puts a node to a system tree as a child of appropriate multipart upload // AddPart puts a node to a system tree as a child of appropriate multipart upload
// and returns objectID of a previous part which must be deleted in NeoFS. // and returns objectID of a previous part which must be deleted in NeoFS.
// If a part is being added for the first time, the previous part ID will be nil. //
AddPart(ctx context.Context, cnrID cid.ID, multipartNodeID uint64, info *data.PartInfo) (oldObjIDToDelete *oid.ID, err error) // If object id to remove is not found returns ErrNoNodeToRemove error.
AddPart(ctx context.Context, cnrID cid.ID, multipartNodeID uint64, info *data.PartInfo) (oldObjIDToDelete oid.ID, err error)
GetParts(ctx context.Context, cnrID cid.ID, multipartNodeID uint64) ([]*data.PartInfo, error) GetParts(ctx context.Context, cnrID cid.ID, multipartNodeID uint64) ([]*data.PartInfo, error)
// Compound methods for optimizations // Compound methods for optimizations
@ -66,5 +82,10 @@ type TreeService interface {
GetObjectTaggingAndLock(ctx context.Context, cnrID cid.ID, objVersion *data.NodeVersion) (map[string]string, *data.LockInfo, error) GetObjectTaggingAndLock(ctx context.Context, cnrID cid.ID, objVersion *data.NodeVersion) (map[string]string, *data.LockInfo, error)
} }
var (
// ErrNodeNotFound is returned from Tree service in case of not found error. // ErrNodeNotFound is returned from Tree service in case of not found error.
var ErrNodeNotFound = errors.New("not found") ErrNodeNotFound = errors.New("not found")
// ErrNoNodeToRemove is returned from Tree service in case of the lack of node with OID to remove.
ErrNoNodeToRemove = errors.New("no node to remove")
)

View file

@ -3,7 +3,6 @@ package authmate
import ( import (
"testing" "testing"
cid "github.com/nspcc-dev/neofs-sdk-go/container/id"
"github.com/nspcc-dev/neofs-sdk-go/session" "github.com/nspcc-dev/neofs-sdk-go/session"
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
) )
@ -29,9 +28,9 @@ func TestContainerSessionRules(t *testing.T) {
require.Len(t, sessionContext, 3) require.Len(t, sessionContext, 3)
require.Equal(t, sessionContext[0].verb, session.VerbContainerPut) require.Equal(t, sessionContext[0].verb, session.VerbContainerPut)
require.Equal(t, cid.ID{}, sessionContext[0].containerID) require.Zero(t, sessionContext[0].containerID)
require.Equal(t, sessionContext[1].verb, session.VerbContainerDelete) require.Equal(t, sessionContext[1].verb, session.VerbContainerDelete)
require.NotNil(t, sessionContext[1].containerID) require.NotNil(t, sessionContext[1].containerID)
require.Equal(t, sessionContext[2].verb, session.VerbContainerSetEACL) require.Equal(t, sessionContext[2].verb, session.VerbContainerSetEACL)
require.Equal(t, cid.ID{}, sessionContext[2].containerID) require.Zero(t, sessionContext[2].containerID)
} }

View file

@ -55,7 +55,7 @@ type NeoFS interface {
// //
// It returns exactly one non-nil value. It returns any error encountered which // It returns exactly one non-nil value. It returns any error encountered which
// prevented the object from being created. // prevented the object from being created.
CreateObject(context.Context, PrmObjectCreate) (*oid.ID, error) CreateObject(context.Context, PrmObjectCreate) (oid.ID, error)
// ReadObjectPayload reads payload of the object from NeoFS network by address // ReadObjectPayload reads payload of the object from NeoFS network by address
// into memory. // into memory.
@ -140,7 +140,7 @@ func (c *cred) Put(ctx context.Context, idCnr cid.ID, issuer user.ID, box *acces
} }
var addr oid.Address var addr oid.Address
addr.SetObject(*idObj) addr.SetObject(idObj)
addr.SetContainer(idCnr) addr.SetContainer(idCnr)
return &addr, nil return &addr, nil

View file

@ -215,7 +215,7 @@ func (x *NeoFS) DeleteContainer(ctx context.Context, id cid.ID, token *session.C
} }
// CreateObject implements neofs.NeoFS interface method. // CreateObject implements neofs.NeoFS interface method.
func (x *NeoFS) CreateObject(ctx context.Context, prm layer.PrmObjectCreate) (*oid.ID, error) { func (x *NeoFS) CreateObject(ctx context.Context, prm layer.PrmObjectCreate) (oid.ID, error) {
attrNum := len(prm.Attributes) + 1 // + creation time attrNum := len(prm.Attributes) + 1 // + creation time
if prm.Filename != "" { if prm.Filename != "" {
@ -270,12 +270,12 @@ func (x *NeoFS) CreateObject(ctx context.Context, prm layer.PrmObjectCreate) (*o
if err != nil { if err != nil {
reason, ok := isErrAccessDenied(err) reason, ok := isErrAccessDenied(err)
if ok { if ok {
return nil, fmt.Errorf("%w: %s", layer.ErrAccessDenied, reason) return oid.ID{}, fmt.Errorf("%w: %s", layer.ErrAccessDenied, reason)
} }
return nil, fmt.Errorf("save object via connection pool: %w", err) return oid.ID{}, fmt.Errorf("save object via connection pool: %w", err)
} }
return idObj, nil return *idObj, nil
} }
// SelectObjects implements neofs.NeoFS interface method. // SelectObjects implements neofs.NeoFS interface method.
@ -570,7 +570,7 @@ func (x *AuthmateNeoFS) ReadObjectPayload(ctx context.Context, addr oid.Address)
} }
// CreateObject implements authmate.NeoFS interface method. // CreateObject implements authmate.NeoFS interface method.
func (x *AuthmateNeoFS) CreateObject(ctx context.Context, prm tokens.PrmObjectCreate) (*oid.ID, error) { func (x *AuthmateNeoFS) CreateObject(ctx context.Context, prm tokens.PrmObjectCreate) (oid.ID, error) {
return x.neoFS.CreateObject(ctx, layer.PrmObjectCreate{ return x.neoFS.CreateObject(ctx, layer.PrmObjectCreate{
Creator: prm.Creator, Creator: prm.Creator,
Container: prm.Container, Container: prm.Container,

View file

@ -300,20 +300,20 @@ func (c *TreeClient) PutSettingsNode(ctx context.Context, cnrID cid.ID, settings
return c.moveNode(ctx, cnrID, systemTree, node.ID, 0, meta) return c.moveNode(ctx, cnrID, systemTree, node.ID, 0, meta)
} }
func (c *TreeClient) GetNotificationConfigurationNode(ctx context.Context, cnrID cid.ID) (*oid.ID, error) { func (c *TreeClient) GetNotificationConfigurationNode(ctx context.Context, cnrID cid.ID) (oid.ID, error) {
node, err := c.getSystemNode(ctx, cnrID, []string{notifConfFileName}, []string{oidKV}) node, err := c.getSystemNode(ctx, cnrID, []string{notifConfFileName}, []string{oidKV})
if err != nil { if err != nil {
return nil, err return oid.ID{}, err
} }
return &node.ObjID, nil return node.ObjID, nil
} }
func (c *TreeClient) PutNotificationConfigurationNode(ctx context.Context, cnrID cid.ID, objID *oid.ID) (*oid.ID, error) { func (c *TreeClient) PutNotificationConfigurationNode(ctx context.Context, cnrID cid.ID, objID oid.ID) (oid.ID, error) {
node, err := c.getSystemNode(ctx, cnrID, []string{notifConfFileName}, []string{oidKV}) node, err := c.getSystemNode(ctx, cnrID, []string{notifConfFileName}, []string{oidKV})
isErrNotFound := errors.Is(err, layer.ErrNodeNotFound) isErrNotFound := errors.Is(err, layer.ErrNodeNotFound)
if err != nil && !isErrNotFound { if err != nil && !isErrNotFound {
return nil, fmt.Errorf("couldn't get node: %w", err) return oid.ID{}, fmt.Errorf("couldn't get node: %w", err)
} }
meta := make(map[string]string) meta := make(map[string]string)
@ -321,27 +321,29 @@ func (c *TreeClient) PutNotificationConfigurationNode(ctx context.Context, cnrID
meta[oidKV] = objID.EncodeToString() meta[oidKV] = objID.EncodeToString()
if isErrNotFound { if isErrNotFound {
_, err = c.addNode(ctx, cnrID, systemTree, 0, meta) if _, err = c.addNode(ctx, cnrID, systemTree, 0, meta); err != nil {
return nil, err return oid.ID{}, err
}
return oid.ID{}, layer.ErrNoNodeToRemove
} }
return &node.ObjID, c.moveNode(ctx, cnrID, systemTree, node.ID, 0, meta) return node.ObjID, c.moveNode(ctx, cnrID, systemTree, node.ID, 0, meta)
} }
func (c *TreeClient) GetBucketCORS(ctx context.Context, cnrID cid.ID) (*oid.ID, error) { func (c *TreeClient) GetBucketCORS(ctx context.Context, cnrID cid.ID) (oid.ID, error) {
node, err := c.getSystemNode(ctx, cnrID, []string{corsFilename}, []string{oidKV}) node, err := c.getSystemNode(ctx, cnrID, []string{corsFilename}, []string{oidKV})
if err != nil { if err != nil {
return nil, err return oid.ID{}, err
} }
return &node.ObjID, nil return node.ObjID, nil
} }
func (c *TreeClient) PutBucketCORS(ctx context.Context, cnrID cid.ID, objID *oid.ID) (*oid.ID, error) { func (c *TreeClient) PutBucketCORS(ctx context.Context, cnrID cid.ID, objID oid.ID) (oid.ID, error) {
node, err := c.getSystemNode(ctx, cnrID, []string{corsFilename}, []string{oidKV}) node, err := c.getSystemNode(ctx, cnrID, []string{corsFilename}, []string{oidKV})
isErrNotFound := errors.Is(err, layer.ErrNodeNotFound) isErrNotFound := errors.Is(err, layer.ErrNodeNotFound)
if err != nil && !isErrNotFound { if err != nil && !isErrNotFound {
return nil, fmt.Errorf("couldn't get node: %w", err) return oid.ID{}, fmt.Errorf("couldn't get node: %w", err)
} }
meta := make(map[string]string) meta := make(map[string]string)
@ -349,24 +351,26 @@ func (c *TreeClient) PutBucketCORS(ctx context.Context, cnrID cid.ID, objID *oid
meta[oidKV] = objID.EncodeToString() meta[oidKV] = objID.EncodeToString()
if isErrNotFound { if isErrNotFound {
_, err = c.addNode(ctx, cnrID, systemTree, 0, meta) if _, err = c.addNode(ctx, cnrID, systemTree, 0, meta); err != nil {
return nil, err return oid.ID{}, err
}
return oid.ID{}, layer.ErrNoNodeToRemove
} }
return &node.ObjID, c.moveNode(ctx, cnrID, systemTree, node.ID, 0, meta) return node.ObjID, c.moveNode(ctx, cnrID, systemTree, node.ID, 0, meta)
} }
func (c *TreeClient) DeleteBucketCORS(ctx context.Context, cnrID cid.ID) (*oid.ID, error) { func (c *TreeClient) DeleteBucketCORS(ctx context.Context, cnrID cid.ID) (oid.ID, error) {
node, err := c.getSystemNode(ctx, cnrID, []string{corsFilename}, []string{oidKV}) node, err := c.getSystemNode(ctx, cnrID, []string{corsFilename}, []string{oidKV})
if err != nil && !errors.Is(err, layer.ErrNodeNotFound) { if err != nil && !errors.Is(err, layer.ErrNodeNotFound) {
return nil, err return oid.ID{}, err
} }
if node != nil { if node != nil {
return &node.ObjID, c.removeNode(ctx, cnrID, systemTree, node.ID) return node.ObjID, c.removeNode(ctx, cnrID, systemTree, node.ID)
} }
return nil, nil return oid.ID{}, layer.ErrNoNodeToRemove
} }
func (c *TreeClient) GetObjectTagging(ctx context.Context, cnrID cid.ID, objVersion *data.NodeVersion) (map[string]string, error) { func (c *TreeClient) GetObjectTagging(ctx context.Context, cnrID cid.ID, objVersion *data.NodeVersion) (map[string]string, error) {
@ -881,10 +885,10 @@ func (c *TreeClient) GetMultipartUpload(ctx context.Context, cnrID cid.ID, objec
return nil, layer.ErrNodeNotFound return nil, layer.ErrNodeNotFound
} }
func (c *TreeClient) AddPart(ctx context.Context, cnrID cid.ID, multipartNodeID uint64, info *data.PartInfo) (oldObjIDToDelete *oid.ID, err error) { func (c *TreeClient) AddPart(ctx context.Context, cnrID cid.ID, multipartNodeID uint64, info *data.PartInfo) (oldObjIDToDelete oid.ID, err error) {
parts, err := c.getSubTree(ctx, cnrID, systemTree, multipartNodeID, 1) parts, err := c.getSubTree(ctx, cnrID, systemTree, multipartNodeID, 1)
if err != nil { if err != nil {
return nil, err return oid.ID{}, err
} }
meta := map[string]string{ meta := map[string]string{
@ -906,14 +910,16 @@ func (c *TreeClient) AddPart(ctx context.Context, cnrID cid.ID, multipartNodeID
} }
if partInfo.Number == info.Number { if partInfo.Number == info.Number {
foundPartID = part.GetNodeId() foundPartID = part.GetNodeId()
oldObjIDToDelete = &partInfo.OID oldObjIDToDelete = partInfo.OID
break break
} }
} }
if oldObjIDToDelete == nil { if foundPartID != multipartNodeID {
_, err = c.addNode(ctx, cnrID, systemTree, multipartNodeID, meta) if _, err = c.addNode(ctx, cnrID, systemTree, multipartNodeID, meta); err != nil {
return nil, err return oid.ID{}, err
}
return oid.ID{}, layer.ErrNoNodeToRemove
} }
return oldObjIDToDelete, c.moveNode(ctx, cnrID, systemTree, foundPartID, multipartNodeID, meta) return oldObjIDToDelete, c.moveNode(ctx, cnrID, systemTree, foundPartID, multipartNodeID, meta)
@ -947,23 +953,23 @@ func (c *TreeClient) DeleteMultipartUpload(ctx context.Context, cnrID cid.ID, mu
func (c *TreeClient) PutLock(ctx context.Context, cnrID cid.ID, nodeID uint64, lock *data.LockInfo) error { func (c *TreeClient) PutLock(ctx context.Context, cnrID cid.ID, nodeID uint64, lock *data.LockInfo) error {
meta := map[string]string{isLockKV: "true"} meta := map[string]string{isLockKV: "true"}
if lock.LegalHoldOID != nil { if lock.IsLegalHoldSet() {
meta[legalHoldOIDKV] = lock.LegalHoldOID.EncodeToString() meta[legalHoldOIDKV] = lock.LegalHold().EncodeToString()
} }
if lock.RetentionOID != nil { if lock.IsRetentionSet() {
meta[retentionOIDKV] = lock.RetentionOID.EncodeToString() meta[retentionOIDKV] = lock.Retention().EncodeToString()
meta[untilDateKV] = lock.UntilDate meta[untilDateKV] = lock.UntilDate()
if lock.IsCompliance { if lock.IsCompliance() {
meta[isComplianceKV] = "true" meta[isComplianceKV] = "true"
} }
} }
if lock.ID == 0 { if lock.ID() == 0 {
_, err := c.addNode(ctx, cnrID, versionTree, nodeID, meta) _, err := c.addNode(ctx, cnrID, versionTree, nodeID, meta)
return err return err
} }
return c.moveNode(ctx, cnrID, versionTree, lock.ID, nodeID, meta) return c.moveNode(ctx, cnrID, versionTree, lock.ID(), nodeID, meta)
} }
func (c *TreeClient) GetLock(ctx context.Context, cnrID cid.ID, nodeID uint64) (*data.LockInfo, error) { func (c *TreeClient) GetLock(ctx context.Context, cnrID cid.ID, nodeID uint64) (*data.LockInfo, error) {
@ -976,18 +982,17 @@ func (c *TreeClient) GetLock(ctx context.Context, cnrID cid.ID, nodeID uint64) (
} }
func getLock(lockNode *TreeNode) (*data.LockInfo, error) { func getLock(lockNode *TreeNode) (*data.LockInfo, error) {
lockInfo := &data.LockInfo{}
if lockNode == nil { if lockNode == nil {
return lockInfo, nil return &data.LockInfo{}, nil
} }
lockInfo.ID = lockNode.ID lockInfo := data.NewLockInfo(lockNode.ID)
if legalHold, ok := lockNode.Get(legalHoldOIDKV); ok { if legalHold, ok := lockNode.Get(legalHoldOIDKV); ok {
var legalHoldOID oid.ID var legalHoldOID oid.ID
if err := legalHoldOID.DecodeString(legalHold); err != nil { if err := legalHoldOID.DecodeString(legalHold); err != nil {
return nil, fmt.Errorf("invalid legal hold object id: %w", err) return nil, fmt.Errorf("invalid legal hold object id: %w", err)
} }
lockInfo.LegalHoldOID = &legalHoldOID lockInfo.SetLegalHold(legalHoldOID)
} }
if retention, ok := lockNode.Get(retentionOIDKV); ok { if retention, ok := lockNode.Get(retentionOIDKV); ok {
@ -995,12 +1000,11 @@ func getLock(lockNode *TreeNode) (*data.LockInfo, error) {
if err := retentionOID.DecodeString(retention); err != nil { if err := retentionOID.DecodeString(retention); err != nil {
return nil, fmt.Errorf("invalid retention object id: %w", err) return nil, fmt.Errorf("invalid retention object id: %w", err)
} }
lockInfo.RetentionOID = &retentionOID _, isCompliance := lockNode.Get(isComplianceKV)
untilDate, _ := lockNode.Get(untilDateKV)
lockInfo.SetRetention(retentionOID, untilDate, isCompliance)
} }
_, lockInfo.IsCompliance = lockNode.Get(isComplianceKV)
lockInfo.UntilDate, _ = lockNode.Get(untilDateKV)
return lockInfo, nil return lockInfo, nil
} }