forked from TrueCloudLab/frostfs-s3-gw
[#488] Sync using oid.ID
Signed-off-by: Denis Kirillov <denis@nspcc.ru>
This commit is contained in:
parent
85c203e157
commit
c88a9842db
18 changed files with 224 additions and 139 deletions
|
@ -75,9 +75,61 @@ func (p *PartInfo) ToHeaderString() string {
|
|||
|
||||
// LockInfo is lock information to create appropriate tree node.
|
||||
type LockInfo struct {
|
||||
ID uint64
|
||||
LegalHoldOID *oid.ID
|
||||
RetentionOID *oid.ID
|
||||
UntilDate string
|
||||
IsCompliance bool
|
||||
id uint64
|
||||
|
||||
legalHoldOID oid.ID
|
||||
setLegalHold 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
|
||||
}
|
||||
|
|
|
@ -120,12 +120,12 @@ func (h *handler) setLockingHeaders(bktInfo *data.BucketInfo, lockInfo *data.Loc
|
|||
legalHold := &data.LegalHold{Status: legalHoldOff}
|
||||
retention := &data.Retention{Mode: governanceMode}
|
||||
|
||||
if lockInfo.LegalHoldOID != nil {
|
||||
if lockInfo.IsLegalHoldSet() {
|
||||
legalHold.Status = legalHoldOn
|
||||
}
|
||||
if lockInfo.RetentionOID != nil {
|
||||
retention.RetainUntilDate = lockInfo.UntilDate
|
||||
if lockInfo.IsCompliance {
|
||||
if lockInfo.IsRetentionSet() {
|
||||
retention.RetainUntilDate = lockInfo.UntilDate()
|
||||
if lockInfo.IsCompliance() {
|
||||
retention.Mode = complianceMode
|
||||
}
|
||||
}
|
||||
|
|
|
@ -174,7 +174,7 @@ func (h *handler) GetObjectLegalHoldHandler(w http.ResponseWriter, r *http.Reque
|
|||
}
|
||||
|
||||
legalHold := &data.LegalHold{Status: legalHoldOff}
|
||||
if lockInfo.LegalHoldOID != nil {
|
||||
if lockInfo.IsLegalHoldSet() {
|
||||
legalHold.Status = legalHoldOn
|
||||
}
|
||||
|
||||
|
@ -248,16 +248,16 @@ func (h *handler) GetObjectRetentionHandler(w http.ResponseWriter, r *http.Reque
|
|||
return
|
||||
}
|
||||
|
||||
if lockInfo.RetentionOID == nil {
|
||||
if !lockInfo.IsRetentionSet() {
|
||||
h.logAndSendError(w, "retention lock isn't set", reqInfo, apiErrors.GetAPIError(apiErrors.ErrNoSuchKey))
|
||||
return
|
||||
}
|
||||
|
||||
retention := &data.Retention{
|
||||
Mode: governanceMode,
|
||||
RetainUntilDate: lockInfo.UntilDate,
|
||||
RetainUntilDate: lockInfo.UntilDate(),
|
||||
}
|
||||
if lockInfo.IsCompliance {
|
||||
if lockInfo.IsCompliance() {
|
||||
retention.Mode = complianceMode
|
||||
}
|
||||
|
||||
|
|
|
@ -49,12 +49,13 @@ func (n *layer) PutBucketCORS(ctx context.Context, p *PutCORSParams) error {
|
|||
}
|
||||
|
||||
objIDToDelete, err := n.treeService.PutBucketCORS(ctx, p.BktInfo.CID, objID)
|
||||
if err != nil {
|
||||
objIDToDeleteNotFound := errorsStd.Is(err, ErrNoNodeToRemove)
|
||||
if err != nil && !objIDToDeleteNotFound {
|
||||
return err
|
||||
}
|
||||
|
||||
if objIDToDelete != nil {
|
||||
if err = n.objectDelete(ctx, p.BktInfo, *objIDToDelete); err != nil {
|
||||
if !objIDToDeleteNotFound {
|
||||
if err = n.objectDelete(ctx, p.BktInfo, objIDToDelete); err != nil {
|
||||
n.log.Error("couldn't delete cors object", zap.Error(err),
|
||||
zap.String("cnrID", p.BktInfo.CID.EncodeToString()),
|
||||
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 {
|
||||
objID, err := n.treeService.DeleteBucketCORS(ctx, bktInfo.CID)
|
||||
if err != nil {
|
||||
objIDNotFound := errorsStd.Is(err, ErrNoNodeToRemove)
|
||||
if err != nil && !objIDNotFound {
|
||||
return err
|
||||
}
|
||||
if objID != nil {
|
||||
if err = n.objectDelete(ctx, bktInfo, *objID); err != nil {
|
||||
if !objIDNotFound {
|
||||
if err = n.objectDelete(ctx, bktInfo, objID); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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{}
|
||||
if _, err := rand.Read(b[:]); err != nil {
|
||||
return nil, err
|
||||
return oid.ID{}, err
|
||||
}
|
||||
|
||||
var objID oid.ID
|
||||
objID.SetSHA256(b)
|
||||
return &objID, nil
|
||||
return objID, nil
|
||||
}
|
||||
|
||||
// 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{
|
||||
BaseNodeVersion: data.BaseNodeVersion{
|
||||
OID: *randOID,
|
||||
OID: randOID,
|
||||
FilePath: obj.Name,
|
||||
},
|
||||
DeleteMarker: &data.DeleteMarkerInfo{
|
||||
|
|
|
@ -36,7 +36,7 @@ func TestObjectLockAttributes(t *testing.T) {
|
|||
foundLock, err := tc.layer.GetLockInfo(tc.ctx, p)
|
||||
require.NoError(t, err)
|
||||
|
||||
lockObj := tc.getObjectByID(*foundLock.RetentionOID)
|
||||
lockObj := tc.getObjectByID(foundLock.Retention())
|
||||
require.NotNil(t, lockObj)
|
||||
|
||||
expEpoch := false
|
||||
|
|
|
@ -198,18 +198,19 @@ func (n *layer) uploadPart(ctx context.Context, multipartInfo *data.MultipartInf
|
|||
Key: p.Info.Key,
|
||||
UploadID: p.Info.UploadID,
|
||||
Number: p.PartNumber,
|
||||
OID: *id,
|
||||
OID: id,
|
||||
Size: p.Size,
|
||||
ETag: hex.EncodeToString(hash),
|
||||
Created: time.Now(),
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
if oldPartID != nil {
|
||||
if err = n.objectDelete(ctx, bktInfo, *oldPartID); err != nil {
|
||||
if !oldPartIDNotFound {
|
||||
if err = n.objectDelete(ctx, bktInfo, oldPartID); err != nil {
|
||||
n.log.Error("couldn't delete old part object", zap.Error(err),
|
||||
zap.String("cnrID", bktInfo.CID.EncodeToString()),
|
||||
zap.String("bucket name", bktInfo.Name),
|
||||
|
@ -218,7 +219,7 @@ func (n *layer) uploadPart(ctx context.Context, multipartInfo *data.MultipartInf
|
|||
}
|
||||
|
||||
objInfo := &data.ObjectInfo{
|
||||
ID: *id,
|
||||
ID: id,
|
||||
CID: bktInfo.CID,
|
||||
|
||||
Owner: bktInfo.Owner,
|
||||
|
|
|
@ -146,7 +146,7 @@ type NeoFS interface {
|
|||
//
|
||||
// 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.
|
||||
CreateContainer(context.Context, PrmContainerCreate) (cid.ID, error)
|
||||
|
||||
|
@ -214,9 +214,9 @@ type NeoFS interface {
|
|||
//
|
||||
// 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.
|
||||
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.
|
||||
// Successful return does not guarantee actual removal.
|
||||
|
|
|
@ -178,10 +178,10 @@ func (t *TestNeoFS) ReadObject(_ context.Context, prm PrmObjectRead) (*ObjectPar
|
|||
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)
|
||||
if _, err := io.ReadFull(rand.Reader, b); err != nil {
|
||||
return nil, err
|
||||
return oid.ID{}, err
|
||||
}
|
||||
var id oid.ID
|
||||
id.SetSHA256(sha256.Sum256(b))
|
||||
|
@ -219,7 +219,7 @@ func (t *TestNeoFS) CreateObject(_ context.Context, prm PrmObjectCreate) (*oid.I
|
|||
if prm.Payload != nil {
|
||||
all, err := io.ReadAll(prm.Payload)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return oid.ID{}, err
|
||||
}
|
||||
obj.SetPayload(all)
|
||||
obj.SetPayloadSize(uint64(len(all)))
|
||||
|
@ -233,7 +233,7 @@ func (t *TestNeoFS) CreateObject(_ context.Context, prm PrmObjectCreate) (*oid.I
|
|||
|
||||
addr := newAddress(cnrID, objID)
|
||||
t.objects[addr.EncodeToString()] = obj
|
||||
return &objID, nil
|
||||
return objID, nil
|
||||
}
|
||||
|
||||
func (t *TestNeoFS) DeleteObject(_ context.Context, prm PrmObjectDelete) error {
|
||||
|
|
|
@ -39,12 +39,13 @@ func (n *layer) PutBucketNotificationConfiguration(ctx context.Context, p *PutBu
|
|||
}
|
||||
|
||||
objIDToDelete, err := n.treeService.PutNotificationConfigurationNode(ctx, p.BktInfo.CID, objID)
|
||||
if err != nil {
|
||||
objIDToDeleteNotFound := errorsStd.Is(err, ErrNoNodeToRemove)
|
||||
if err != nil && !objIDToDeleteNotFound {
|
||||
return err
|
||||
}
|
||||
|
||||
if objIDToDelete != nil {
|
||||
if err = n.objectDelete(ctx, p.BktInfo, *objIDToDelete); err != nil {
|
||||
if !objIDToDeleteNotFound {
|
||||
if err = n.objectDelete(ctx, p.BktInfo, objIDToDelete); err != nil {
|
||||
n.log.Error("couldn't delete notification configuration object", zap.Error(err),
|
||||
zap.String("cnrID", p.BktInfo.CID.EncodeToString()),
|
||||
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)
|
||||
if err != nil && !errorsStd.Is(err, ErrNodeNotFound) {
|
||||
objIDNotFound := errorsStd.Is(err, ErrNodeNotFound)
|
||||
if err != nil && !objIDNotFound {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
conf := &data.NotificationConfiguration{}
|
||||
|
||||
if objID != nil {
|
||||
obj, err := n.objectGet(ctx, bktInfo, *objID)
|
||||
if !objIDNotFound {
|
||||
obj, err := n.objectGet(ctx, bktInfo, objID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
|
|
@ -189,7 +189,7 @@ func (n *layer) PutObject(ctx context.Context, p *PutObjectParams) (*data.Object
|
|||
return nil, err
|
||||
}
|
||||
|
||||
newVersion.OID = *id
|
||||
newVersion.OID = id
|
||||
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)
|
||||
}
|
||||
|
@ -209,7 +209,7 @@ func (n *layer) PutObject(ctx context.Context, p *PutObjectParams) (*data.Object
|
|||
n.listsCache.CleanCacheEntriesContainingObject(p.Object, p.BktInfo.CID)
|
||||
|
||||
objInfo := &data.ObjectInfo{
|
||||
ID: *id,
|
||||
ID: id,
|
||||
CID: p.BktInfo.CID,
|
||||
|
||||
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.
|
||||
// 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)
|
||||
hash := sha256.New()
|
||||
prm.Payload = wrapReader(prm.Payload, 64*1024, func(buf []byte) {
|
||||
|
|
|
@ -36,18 +36,19 @@ func (n *layer) PutLockInfo(ctx context.Context, objVersion *ObjectVersion, newL
|
|||
}
|
||||
|
||||
if newLock.Retention != nil {
|
||||
if lockInfo.RetentionOID != nil {
|
||||
if lockInfo.IsCompliance {
|
||||
if lockInfo.IsRetentionSet() {
|
||||
if lockInfo.IsCompliance() {
|
||||
return fmt.Errorf("you cannot change compliance mode")
|
||||
}
|
||||
if !newLock.Retention.ByPassedGovernance {
|
||||
return fmt.Errorf("you cannot bypass governence mode")
|
||||
}
|
||||
|
||||
if len(lockInfo.UntilDate) > 0 {
|
||||
parsedTime, err := time.Parse(time.RFC3339, lockInfo.UntilDate)
|
||||
untilDate := lockInfo.UntilDate()
|
||||
if len(untilDate) > 0 {
|
||||
parsedTime, err := time.Parse(time.RFC3339, untilDate)
|
||||
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) {
|
||||
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}
|
||||
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
|
||||
}
|
||||
lockInfo.IsCompliance = newLock.Retention.IsCompliance
|
||||
lockInfo.UntilDate = newLock.Retention.Until.UTC().Format(time.RFC3339)
|
||||
lockInfo.SetRetention(retentionOID, newLock.Retention.Until.UTC().Format(time.RFC3339), newLock.Retention.IsCompliance)
|
||||
}
|
||||
|
||||
if newLock.LegalHold != nil {
|
||||
if newLock.LegalHold.Enabled && lockInfo.LegalHoldOID == nil {
|
||||
if newLock.LegalHold.Enabled && !lockInfo.IsLegalHoldSet() {
|
||||
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
|
||||
}
|
||||
} else if !newLock.LegalHold.Enabled && lockInfo.LegalHoldOID != nil {
|
||||
if err = n.objectDelete(ctx, objVersion.BktInfo, *lockInfo.LegalHoldOID); err != nil {
|
||||
return fmt.Errorf("couldn't delete lock object '%s' to remove legal hold: %w", lockInfo.LegalHoldOID.EncodeToString(), err)
|
||||
lockInfo.SetLegalHold(legalHoldOID)
|
||||
} else if !newLock.LegalHold.Enabled && lockInfo.IsLegalHoldSet() {
|
||||
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
|
||||
}
|
||||
|
||||
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{
|
||||
Container: bktInfo.CID,
|
||||
Creator: bktInfo.Owner,
|
||||
|
@ -97,7 +100,7 @@ func (n *layer) putLockObject(ctx context.Context, bktInfo *data.BucketInfo, obj
|
|||
var err error
|
||||
prm.Attributes, err = n.attributesFromLock(ctx, lock)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return oid.ID{}, err
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
objID, err := n.treeService.GetBucketCORS(ctx, bkt.CID)
|
||||
if err != nil {
|
||||
objIDNotFound := errorsStd.Is(err, ErrNodeNotFound)
|
||||
if err != nil && !objIDNotFound {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if objID == nil {
|
||||
if objIDNotFound {
|
||||
return nil, errors.GetAPIError(errors.ErrNoSuchCORSConfiguration)
|
||||
}
|
||||
|
||||
obj, err := n.objectGet(ctx, bkt, *objID)
|
||||
obj, err := n.objectGet(ctx, bkt, objID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
|
|
@ -81,23 +81,23 @@ func (t *TreeServiceMock) GetSettingsNode(_ context.Context, id cid.ID) (*data.B
|
|||
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")
|
||||
}
|
||||
|
||||
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")
|
||||
}
|
||||
|
||||
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")
|
||||
}
|
||||
|
||||
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")
|
||||
}
|
||||
|
||||
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")
|
||||
}
|
||||
|
||||
|
@ -294,14 +294,14 @@ func (t *TreeServiceMock) GetMultipartUpload(_ context.Context, cnrID cid.ID, ob
|
|||
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)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return oid.ID{}, err
|
||||
}
|
||||
|
||||
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]
|
||||
|
@ -312,7 +312,7 @@ func (t *TreeServiceMock) AddPart(ctx context.Context, cnrID cid.ID, multipartNo
|
|||
partsMap[info.Number] = info
|
||||
|
||||
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) {
|
||||
|
|
|
@ -16,19 +16,34 @@ type TreeService interface {
|
|||
|
||||
// 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)
|
||||
|
||||
GetNotificationConfigurationNode(ctx context.Context, cnrID cid.ID) (*oid.ID, error)
|
||||
// PutNotificationConfigurationNode puts a node to a system tree
|
||||
// and returns objectID of a previous notif config which must be deleted in NeoFS
|
||||
PutNotificationConfigurationNode(ctx context.Context, cnrID cid.ID, objID *oid.ID) (*oid.ID, error)
|
||||
// GetNotificationConfigurationNode gets an object id that corresponds to object with bucket CORS.
|
||||
//
|
||||
// If tree node is not found returns ErrNodeNotFound error.
|
||||
GetNotificationConfigurationNode(ctx context.Context, cnrID cid.ID) (oid.ID, 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
|
||||
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
|
||||
DeleteBucketCORS(ctx context.Context, cnrID cid.ID) (*oid.ID, error)
|
||||
// PutNotificationConfigurationNode puts a node to a system tree
|
||||
// and returns objectID of a previous notif config which must be deleted in NeoFS.
|
||||
//
|
||||
// If object id to remove is not found returns ErrNoNodeToRemove 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)
|
||||
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
|
||||
// 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)
|
||||
|
||||
// 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)
|
||||
}
|
||||
|
||||
// ErrNodeNotFound is returned from Tree service in case of not found error.
|
||||
var ErrNodeNotFound = errors.New("not found")
|
||||
var (
|
||||
// ErrNodeNotFound is returned from Tree service in case of not found error.
|
||||
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")
|
||||
)
|
||||
|
|
|
@ -3,7 +3,6 @@ package authmate
|
|||
import (
|
||||
"testing"
|
||||
|
||||
cid "github.com/nspcc-dev/neofs-sdk-go/container/id"
|
||||
"github.com/nspcc-dev/neofs-sdk-go/session"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
@ -29,9 +28,9 @@ func TestContainerSessionRules(t *testing.T) {
|
|||
|
||||
require.Len(t, sessionContext, 3)
|
||||
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.NotNil(t, sessionContext[1].containerID)
|
||||
require.Equal(t, sessionContext[2].verb, session.VerbContainerSetEACL)
|
||||
require.Equal(t, cid.ID{}, sessionContext[2].containerID)
|
||||
require.Zero(t, sessionContext[2].containerID)
|
||||
}
|
||||
|
|
|
@ -55,7 +55,7 @@ type NeoFS interface {
|
|||
//
|
||||
// It returns exactly one non-nil value. It returns any error encountered which
|
||||
// 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
|
||||
// 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
|
||||
addr.SetObject(*idObj)
|
||||
addr.SetObject(idObj)
|
||||
addr.SetContainer(idCnr)
|
||||
|
||||
return &addr, nil
|
||||
|
|
|
@ -215,7 +215,7 @@ func (x *NeoFS) DeleteContainer(ctx context.Context, id cid.ID, token *session.C
|
|||
}
|
||||
|
||||
// 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
|
||||
|
||||
if prm.Filename != "" {
|
||||
|
@ -270,12 +270,12 @@ func (x *NeoFS) CreateObject(ctx context.Context, prm layer.PrmObjectCreate) (*o
|
|||
if err != nil {
|
||||
reason, ok := isErrAccessDenied(err)
|
||||
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.
|
||||
|
@ -570,7 +570,7 @@ func (x *AuthmateNeoFS) ReadObjectPayload(ctx context.Context, addr oid.Address)
|
|||
}
|
||||
|
||||
// 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{
|
||||
Creator: prm.Creator,
|
||||
Container: prm.Container,
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
|
||||
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})
|
||||
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})
|
||||
isErrNotFound := errors.Is(err, layer.ErrNodeNotFound)
|
||||
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)
|
||||
|
@ -321,27 +321,29 @@ func (c *TreeClient) PutNotificationConfigurationNode(ctx context.Context, cnrID
|
|||
meta[oidKV] = objID.EncodeToString()
|
||||
|
||||
if isErrNotFound {
|
||||
_, err = c.addNode(ctx, cnrID, systemTree, 0, meta)
|
||||
return nil, err
|
||||
if _, err = c.addNode(ctx, cnrID, systemTree, 0, meta); err != nil {
|
||||
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})
|
||||
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})
|
||||
isErrNotFound := errors.Is(err, layer.ErrNodeNotFound)
|
||||
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)
|
||||
|
@ -349,24 +351,26 @@ func (c *TreeClient) PutBucketCORS(ctx context.Context, cnrID cid.ID, objID *oid
|
|||
meta[oidKV] = objID.EncodeToString()
|
||||
|
||||
if isErrNotFound {
|
||||
_, err = c.addNode(ctx, cnrID, systemTree, 0, meta)
|
||||
return nil, err
|
||||
if _, err = c.addNode(ctx, cnrID, systemTree, 0, meta); err != nil {
|
||||
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})
|
||||
if err != nil && !errors.Is(err, layer.ErrNodeNotFound) {
|
||||
return nil, err
|
||||
return oid.ID{}, err
|
||||
}
|
||||
|
||||
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) {
|
||||
|
@ -881,10 +885,10 @@ func (c *TreeClient) GetMultipartUpload(ctx context.Context, cnrID cid.ID, objec
|
|||
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)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return oid.ID{}, err
|
||||
}
|
||||
|
||||
meta := map[string]string{
|
||||
|
@ -906,14 +910,16 @@ func (c *TreeClient) AddPart(ctx context.Context, cnrID cid.ID, multipartNodeID
|
|||
}
|
||||
if partInfo.Number == info.Number {
|
||||
foundPartID = part.GetNodeId()
|
||||
oldObjIDToDelete = &partInfo.OID
|
||||
oldObjIDToDelete = partInfo.OID
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if oldObjIDToDelete == nil {
|
||||
_, err = c.addNode(ctx, cnrID, systemTree, multipartNodeID, meta)
|
||||
return nil, err
|
||||
if foundPartID != multipartNodeID {
|
||||
if _, err = c.addNode(ctx, cnrID, systemTree, multipartNodeID, meta); err != nil {
|
||||
return oid.ID{}, err
|
||||
}
|
||||
return oid.ID{}, layer.ErrNoNodeToRemove
|
||||
}
|
||||
|
||||
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 {
|
||||
meta := map[string]string{isLockKV: "true"}
|
||||
|
||||
if lock.LegalHoldOID != nil {
|
||||
meta[legalHoldOIDKV] = lock.LegalHoldOID.EncodeToString()
|
||||
if lock.IsLegalHoldSet() {
|
||||
meta[legalHoldOIDKV] = lock.LegalHold().EncodeToString()
|
||||
}
|
||||
if lock.RetentionOID != nil {
|
||||
meta[retentionOIDKV] = lock.RetentionOID.EncodeToString()
|
||||
meta[untilDateKV] = lock.UntilDate
|
||||
if lock.IsCompliance {
|
||||
if lock.IsRetentionSet() {
|
||||
meta[retentionOIDKV] = lock.Retention().EncodeToString()
|
||||
meta[untilDateKV] = lock.UntilDate()
|
||||
if lock.IsCompliance() {
|
||||
meta[isComplianceKV] = "true"
|
||||
}
|
||||
}
|
||||
|
||||
if lock.ID == 0 {
|
||||
if lock.ID() == 0 {
|
||||
_, err := c.addNode(ctx, cnrID, versionTree, nodeID, meta)
|
||||
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) {
|
||||
|
@ -976,18 +982,17 @@ func (c *TreeClient) GetLock(ctx context.Context, cnrID cid.ID, nodeID uint64) (
|
|||
}
|
||||
|
||||
func getLock(lockNode *TreeNode) (*data.LockInfo, error) {
|
||||
lockInfo := &data.LockInfo{}
|
||||
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 {
|
||||
var legalHoldOID oid.ID
|
||||
if err := legalHoldOID.DecodeString(legalHold); err != nil {
|
||||
return nil, fmt.Errorf("invalid legal hold object id: %w", err)
|
||||
}
|
||||
lockInfo.LegalHoldOID = &legalHoldOID
|
||||
lockInfo.SetLegalHold(legalHoldOID)
|
||||
}
|
||||
|
||||
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 {
|
||||
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
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in a new issue