[#42] Fix using separate container for lifecycles
All checks were successful
/ DCO (pull_request) Successful in 1m11s
/ Builds (1.21) (pull_request) Successful in 1m23s
/ Builds (1.22) (pull_request) Successful in 1m21s
/ Vulncheck (pull_request) Successful in 4m33s
/ Lint (pull_request) Successful in 5m56s
/ Tests (1.21) (pull_request) Successful in 1m7s
/ Tests (1.22) (pull_request) Successful in 1m22s
All checks were successful
/ DCO (pull_request) Successful in 1m11s
/ Builds (1.21) (pull_request) Successful in 1m23s
/ Builds (1.22) (pull_request) Successful in 1m21s
/ Vulncheck (pull_request) Successful in 4m33s
/ Lint (pull_request) Successful in 5m56s
/ Tests (1.21) (pull_request) Successful in 1m7s
/ Tests (1.22) (pull_request) Successful in 1m22s
Signed-off-by: Marina Biryukova <m.biryukova@yadro.com>
This commit is contained in:
parent
a4a4573b10
commit
5863af4a26
8 changed files with 131 additions and 90 deletions
|
@ -165,9 +165,6 @@ func prepareHandlerContextBase(t *testing.T, cacheCfg *layer.CachesConfig) *hand
|
|||
|
||||
features := &layer.FeatureSettingsMock{}
|
||||
|
||||
res, err := tp.CreateContainer(context.Background(), layer.PrmContainerCreate{Name: ".bucket-lifecycles"})
|
||||
require.NoError(t, err)
|
||||
|
||||
layerCfg := &layer.Config{
|
||||
Cache: layer.NewCache(cacheCfg),
|
||||
AnonKey: layer.AnonymousKey{Key: key},
|
||||
|
@ -175,7 +172,6 @@ func prepareHandlerContextBase(t *testing.T, cacheCfg *layer.CachesConfig) *hand
|
|||
TreeService: treeMock,
|
||||
Features: features,
|
||||
GateOwner: owner,
|
||||
LifecycleCnrInfo: &data.BucketInfo{CID: res.ContainerID},
|
||||
GateKey: key,
|
||||
}
|
||||
|
||||
|
|
|
@ -817,5 +817,20 @@ func (n *Layer) DeleteBucket(ctx context.Context, p *DeleteBucketParams) error {
|
|||
}
|
||||
|
||||
n.cache.DeleteBucket(p.BktInfo)
|
||||
return n.frostFS.DeleteContainer(ctx, p.BktInfo.CID, p.SessionToken)
|
||||
|
||||
lifecycleObj, err := n.treeService.GetBucketLifecycleConfiguration(ctx, p.BktInfo)
|
||||
if err != nil {
|
||||
n.reqLogger(ctx).Error(logs.GetBucketLifecycle, zap.Error(err))
|
||||
}
|
||||
|
||||
err = n.frostFS.DeleteContainer(ctx, p.BktInfo.CID, p.SessionToken)
|
||||
if err != nil {
|
||||
return fmt.Errorf("delete container: %w", err)
|
||||
}
|
||||
|
||||
if !lifecycleObj.Container().Equals(p.BktInfo.CID) {
|
||||
n.deleteLifecycleObject(ctx, p.BktInfo, lifecycleObj)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
|
|
@ -12,7 +12,6 @@ import (
|
|||
"git.frostfs.info/TrueCloudLab/frostfs-s3-gw/api/data"
|
||||
apiErr "git.frostfs.info/TrueCloudLab/frostfs-s3-gw/api/errors"
|
||||
"git.frostfs.info/TrueCloudLab/frostfs-s3-gw/internal/logs"
|
||||
"git.frostfs.info/TrueCloudLab/frostfs-sdk-go/object"
|
||||
oid "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/object/id"
|
||||
"go.uber.org/zap"
|
||||
)
|
||||
|
@ -45,7 +44,7 @@ func (n *Layer) PutBucketLifecycleConfiguration(ctx context.Context, p *PutBucke
|
|||
|
||||
createdObj, err := n.objectPutAndHash(ctx, prm, lifecycleBkt)
|
||||
if err != nil {
|
||||
return err
|
||||
return fmt.Errorf("put lifecycle object: %w", err)
|
||||
}
|
||||
|
||||
hashBytes, err := base64.StdEncoding.DecodeString(p.MD5Hash)
|
||||
|
@ -54,19 +53,19 @@ func (n *Layer) PutBucketLifecycleConfiguration(ctx context.Context, p *PutBucke
|
|||
}
|
||||
|
||||
if !bytes.Equal(hashBytes, createdObj.MD5Sum) {
|
||||
n.deleteLifecycleObject(ctx, lifecycleBkt, createdObj.ID)
|
||||
n.deleteLifecycleObject(ctx, p.BktInfo, newAddress(lifecycleBkt.CID, createdObj.ID))
|
||||
|
||||
return apiErr.GetAPIError(apiErr.ErrInvalidDigest)
|
||||
}
|
||||
|
||||
objIDToDelete, err := n.treeService.PutBucketLifecycleConfiguration(ctx, p.BktInfo, createdObj.ID)
|
||||
objIDToDeleteNotFound := errors.Is(err, ErrNoNodeToRemove)
|
||||
if err != nil && !objIDToDeleteNotFound {
|
||||
objToDelete, err := n.treeService.PutBucketLifecycleConfiguration(ctx, p.BktInfo, newAddress(lifecycleBkt.CID, createdObj.ID))
|
||||
objToDeleteNotFound := errors.Is(err, ErrNoNodeToRemove)
|
||||
if err != nil && !objToDeleteNotFound {
|
||||
return err
|
||||
}
|
||||
|
||||
if !objIDToDeleteNotFound {
|
||||
n.deleteLifecycleObject(ctx, lifecycleBkt, objIDToDelete)
|
||||
if !objToDeleteNotFound {
|
||||
n.deleteLifecycleObject(ctx, p.BktInfo, objToDelete)
|
||||
}
|
||||
|
||||
n.cache.PutLifecycleConfiguration(n.BearerOwner(ctx), p.BktInfo, p.LifecycleCfg)
|
||||
|
@ -75,18 +74,18 @@ func (n *Layer) PutBucketLifecycleConfiguration(ctx context.Context, p *PutBucke
|
|||
}
|
||||
|
||||
// deleteLifecycleObject removes object and logs in case of error.
|
||||
func (n *Layer) deleteLifecycleObject(ctx context.Context, lifecycleBkt *data.BucketInfo, objID oid.ID) {
|
||||
var err error
|
||||
if n.lifecycleCnrInfo == nil {
|
||||
err = n.objectDelete(ctx, lifecycleBkt, objID)
|
||||
} else {
|
||||
err = n.objectDeleteWithAuth(ctx, lifecycleBkt, objID, PrmAuth{PrivateKey: &n.gateKey.PrivateKey})
|
||||
func (n *Layer) deleteLifecycleObject(ctx context.Context, bktInfo *data.BucketInfo, addr oid.Address) {
|
||||
var prmAuth PrmAuth
|
||||
lifecycleBkt := bktInfo
|
||||
if !addr.Container().Equals(bktInfo.CID) {
|
||||
lifecycleBkt = &data.BucketInfo{CID: addr.Container()}
|
||||
prmAuth.PrivateKey = &n.gateKey.PrivateKey
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
if err := n.objectDeleteWithAuth(ctx, lifecycleBkt, addr.Object(), prmAuth); err != nil {
|
||||
n.reqLogger(ctx).Error(logs.CouldntDeleteLifecycleObject, zap.Error(err),
|
||||
zap.String("cid", lifecycleBkt.CID.EncodeToString()),
|
||||
zap.String("oid", objID.EncodeToString()))
|
||||
zap.String("oid", addr.Object().EncodeToString()))
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -96,25 +95,26 @@ func (n *Layer) GetBucketLifecycleConfiguration(ctx context.Context, bktInfo *da
|
|||
return cfg, nil
|
||||
}
|
||||
|
||||
objID, err := n.treeService.GetBucketLifecycleConfiguration(ctx, bktInfo)
|
||||
objIDNotFound := errors.Is(err, ErrNodeNotFound)
|
||||
if err != nil && !objIDNotFound {
|
||||
addr, err := n.treeService.GetBucketLifecycleConfiguration(ctx, bktInfo)
|
||||
objNotFound := errors.Is(err, ErrNodeNotFound)
|
||||
if err != nil && !objNotFound {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if objIDNotFound {
|
||||
if objNotFound {
|
||||
return nil, fmt.Errorf("%w: %s", apiErr.GetAPIError(apiErr.ErrNoSuchLifecycleConfiguration), err.Error())
|
||||
}
|
||||
|
||||
var obj *object.Object
|
||||
if n.lifecycleCnrInfo == nil {
|
||||
if obj, err = n.objectGet(ctx, bktInfo, objID); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
} else {
|
||||
if obj, err = n.objectGetWithAuth(ctx, n.lifecycleCnrInfo, objID, PrmAuth{PrivateKey: &n.gateKey.PrivateKey}); err != nil {
|
||||
return nil, err
|
||||
var prmAuth PrmAuth
|
||||
lifecycleBkt := bktInfo
|
||||
if !addr.Container().Equals(bktInfo.CID) {
|
||||
lifecycleBkt = &data.BucketInfo{CID: addr.Container()}
|
||||
prmAuth.PrivateKey = &n.gateKey.PrivateKey
|
||||
}
|
||||
|
||||
obj, err := n.objectGetWithAuth(ctx, lifecycleBkt, addr.Object(), prmAuth)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("get lifecycle object: %w", err)
|
||||
}
|
||||
|
||||
lifecycleCfg := &data.LifecycleConfiguration{}
|
||||
|
@ -129,20 +129,22 @@ func (n *Layer) GetBucketLifecycleConfiguration(ctx context.Context, bktInfo *da
|
|||
}
|
||||
|
||||
func (n *Layer) DeleteBucketLifecycleConfiguration(ctx context.Context, bktInfo *data.BucketInfo) error {
|
||||
objID, err := n.treeService.DeleteBucketLifecycleConfiguration(ctx, bktInfo)
|
||||
objIDNotFound := errors.Is(err, ErrNoNodeToRemove)
|
||||
if err != nil && !objIDNotFound {
|
||||
obj, err := n.treeService.DeleteBucketLifecycleConfiguration(ctx, bktInfo)
|
||||
objNotFound := errors.Is(err, ErrNoNodeToRemove)
|
||||
if err != nil && !objNotFound {
|
||||
return err
|
||||
}
|
||||
if !objIDNotFound {
|
||||
if n.lifecycleCnrInfo == nil {
|
||||
if err = n.objectDelete(ctx, bktInfo, objID); err != nil {
|
||||
return err
|
||||
}
|
||||
} else {
|
||||
if err = n.objectDeleteWithAuth(ctx, n.lifecycleCnrInfo, objID, PrmAuth{PrivateKey: &n.gateKey.PrivateKey}); err != nil {
|
||||
return err
|
||||
|
||||
if !objNotFound {
|
||||
var prmAuth PrmAuth
|
||||
lifecycleBkt := bktInfo
|
||||
if !obj.Container().Equals(bktInfo.CID) {
|
||||
lifecycleBkt = &data.BucketInfo{CID: obj.Container()}
|
||||
prmAuth.PrivateKey = &n.gateKey.PrivateKey
|
||||
}
|
||||
|
||||
if err = n.objectDeleteWithAuth(ctx, lifecycleBkt, obj.Object(), prmAuth); err != nil {
|
||||
return fmt.Errorf("delete lifecycle object: %w", err)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -392,49 +392,49 @@ LOOP:
|
|||
return result, nil
|
||||
}
|
||||
|
||||
func (t *TreeServiceMock) PutBucketLifecycleConfiguration(_ context.Context, bktInfo *data.BucketInfo, objID oid.ID) (oid.ID, error) {
|
||||
func (t *TreeServiceMock) PutBucketLifecycleConfiguration(_ context.Context, bktInfo *data.BucketInfo, addr oid.Address) (oid.Address, error) {
|
||||
systemMap, ok := t.system[bktInfo.CID.EncodeToString()]
|
||||
if !ok {
|
||||
systemMap = make(map[string]*data.BaseNodeVersion)
|
||||
}
|
||||
|
||||
systemMap["lifecycle"] = &data.BaseNodeVersion{
|
||||
OID: objID,
|
||||
OID: addr.Object(),
|
||||
}
|
||||
|
||||
t.system[bktInfo.CID.EncodeToString()] = systemMap
|
||||
|
||||
return oid.ID{}, ErrNoNodeToRemove
|
||||
return oid.Address{}, ErrNoNodeToRemove
|
||||
}
|
||||
|
||||
func (t *TreeServiceMock) GetBucketLifecycleConfiguration(_ context.Context, bktInfo *data.BucketInfo) (oid.ID, error) {
|
||||
func (t *TreeServiceMock) GetBucketLifecycleConfiguration(_ context.Context, bktInfo *data.BucketInfo) (oid.Address, error) {
|
||||
systemMap, ok := t.system[bktInfo.CID.EncodeToString()]
|
||||
if !ok {
|
||||
return oid.ID{}, ErrNodeNotFound
|
||||
return oid.Address{}, ErrNodeNotFound
|
||||
}
|
||||
|
||||
node, ok := systemMap["lifecycle"]
|
||||
if !ok {
|
||||
return oid.ID{}, ErrNodeNotFound
|
||||
return oid.Address{}, ErrNodeNotFound
|
||||
}
|
||||
|
||||
return node.OID, nil
|
||||
return newAddress(bktInfo.CID, node.OID), nil
|
||||
}
|
||||
|
||||
func (t *TreeServiceMock) DeleteBucketLifecycleConfiguration(_ context.Context, bktInfo *data.BucketInfo) (oid.ID, error) {
|
||||
func (t *TreeServiceMock) DeleteBucketLifecycleConfiguration(_ context.Context, bktInfo *data.BucketInfo) (oid.Address, error) {
|
||||
systemMap, ok := t.system[bktInfo.CID.EncodeToString()]
|
||||
if !ok {
|
||||
return oid.ID{}, ErrNoNodeToRemove
|
||||
return oid.Address{}, ErrNoNodeToRemove
|
||||
}
|
||||
|
||||
node, ok := systemMap["lifecycle"]
|
||||
if !ok {
|
||||
return oid.ID{}, ErrNoNodeToRemove
|
||||
return oid.Address{}, ErrNoNodeToRemove
|
||||
}
|
||||
|
||||
delete(systemMap, "lifecycle")
|
||||
|
||||
return node.OID, nil
|
||||
return newAddress(bktInfo.CID, node.OID), nil
|
||||
}
|
||||
|
||||
func (t *TreeServiceMock) DeleteMultipartUpload(_ context.Context, bktInfo *data.BucketInfo, multipartInfo *data.MultipartInfo) error {
|
||||
|
|
|
@ -63,9 +63,9 @@ type TreeService interface {
|
|||
AddPart(ctx context.Context, bktInfo *data.BucketInfo, multipartNodeID uint64, info *data.PartInfo) (oldObjIDToDelete oid.ID, err error)
|
||||
GetParts(ctx context.Context, bktInfo *data.BucketInfo, multipartNodeID uint64) ([]*data.PartInfo, error)
|
||||
|
||||
PutBucketLifecycleConfiguration(ctx context.Context, bktInfo *data.BucketInfo, objID oid.ID) (oldObjIDToDelete oid.ID, err error)
|
||||
GetBucketLifecycleConfiguration(ctx context.Context, bktInfo *data.BucketInfo) (oid.ID, error)
|
||||
DeleteBucketLifecycleConfiguration(ctx context.Context, bktInfo *data.BucketInfo) (oid.ID, error)
|
||||
PutBucketLifecycleConfiguration(ctx context.Context, bktInfo *data.BucketInfo, addr oid.Address) (oldObjToDelete oid.Address, err error)
|
||||
GetBucketLifecycleConfiguration(ctx context.Context, bktInfo *data.BucketInfo) (oid.Address, error)
|
||||
DeleteBucketLifecycleConfiguration(ctx context.Context, bktInfo *data.BucketInfo) (oid.Address, error)
|
||||
|
||||
// Compound methods for optimizations
|
||||
|
||||
|
|
|
@ -173,7 +173,6 @@ func prepareContext(t *testing.T, cachesConfig ...*CachesConfig) *testContext {
|
|||
TreeService: NewTreeService(),
|
||||
Features: &FeatureSettingsMock{},
|
||||
GateOwner: owner,
|
||||
LifecycleCnrInfo: &data.BucketInfo{CID: res.ContainerID},
|
||||
GateKey: key,
|
||||
}
|
||||
|
||||
|
|
|
@ -153,4 +153,5 @@ const (
|
|||
CouldntCacheLifecycleConfiguration = "couldn't cache lifecycle configuration"
|
||||
CouldNotFetchLifecycleContainerInfo = "couldn't fetch lifecycle container info"
|
||||
FoundSeveralBucketLifecycleNodes = "found several bucket lifecycle nodes, latest be used"
|
||||
GetBucketLifecycle = "get bucket lifecycle"
|
||||
)
|
||||
|
|
|
@ -15,6 +15,7 @@ import (
|
|||
"git.frostfs.info/TrueCloudLab/frostfs-s3-gw/api/layer"
|
||||
"git.frostfs.info/TrueCloudLab/frostfs-s3-gw/api/middleware"
|
||||
"git.frostfs.info/TrueCloudLab/frostfs-s3-gw/internal/logs"
|
||||
cid "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/container/id"
|
||||
oid "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/object/id"
|
||||
"git.frostfs.info/TrueCloudLab/frostfs-sdk-go/user"
|
||||
"github.com/nspcc-dev/neo-go/pkg/crypto/keys"
|
||||
|
@ -84,6 +85,7 @@ const (
|
|||
ownerKeyKV = "ownerKey"
|
||||
lockConfigurationKV = "LockConfiguration"
|
||||
oidKV = "OID"
|
||||
cidKV = "CID"
|
||||
|
||||
isCombinedKV = "IsCombined"
|
||||
isUnversionedKV = "IsUnversioned"
|
||||
|
@ -1374,22 +1376,23 @@ func (c *Tree) GetParts(ctx context.Context, bktInfo *data.BucketInfo, multipart
|
|||
return result, nil
|
||||
}
|
||||
|
||||
func (c *Tree) PutBucketLifecycleConfiguration(ctx context.Context, bktInfo *data.BucketInfo, objID oid.ID) (oid.ID, error) {
|
||||
func (c *Tree) PutBucketLifecycleConfiguration(ctx context.Context, bktInfo *data.BucketInfo, addr oid.Address) (oid.Address, error) {
|
||||
node, err := c.getSystemNode(ctx, bktInfo, []string{bucketLifecycleFilename})
|
||||
isErrNotFound := errors.Is(err, layer.ErrNodeNotFound)
|
||||
if err != nil && !isErrNotFound {
|
||||
return oid.ID{}, fmt.Errorf("couldn't get node: %w", err)
|
||||
return oid.Address{}, fmt.Errorf("couldn't get node: %w", err)
|
||||
}
|
||||
|
||||
meta := make(map[string]string)
|
||||
meta[FileNameKey] = bucketLifecycleFilename
|
||||
meta[oidKV] = objID.EncodeToString()
|
||||
meta[oidKV] = addr.Object().EncodeToString()
|
||||
meta[cidKV] = addr.Container().EncodeToString()
|
||||
|
||||
if isErrNotFound {
|
||||
if _, err = c.service.AddNode(ctx, bktInfo, systemTree, 0, meta); err != nil {
|
||||
return oid.ID{}, err
|
||||
return oid.Address{}, err
|
||||
}
|
||||
return oid.ID{}, layer.ErrNoNodeToRemove
|
||||
return oid.Address{}, layer.ErrNoNodeToRemove
|
||||
}
|
||||
|
||||
ind := node.GetLatestNodeIndex()
|
||||
|
@ -1397,22 +1400,27 @@ func (c *Tree) PutBucketLifecycleConfiguration(ctx context.Context, bktInfo *dat
|
|||
c.reqLogger(ctx).Warn(logs.FoundSeveralBucketLifecycleNodes)
|
||||
}
|
||||
|
||||
return node.ObjID, c.service.MoveNode(ctx, bktInfo, systemTree, node.ID[ind], 0, meta)
|
||||
}
|
||||
|
||||
func (c *Tree) GetBucketLifecycleConfiguration(ctx context.Context, bktInfo *data.BucketInfo) (oid.ID, error) {
|
||||
node, err := c.getSystemNode(ctx, bktInfo, []string{bucketLifecycleFilename})
|
||||
prevAddr, err := getAddress(node)
|
||||
if err != nil {
|
||||
return oid.ID{}, err
|
||||
return oid.Address{}, fmt.Errorf("couldn't get prev lifecycle object addr: %w", err)
|
||||
}
|
||||
|
||||
return node.ObjID, nil
|
||||
return prevAddr, c.service.MoveNode(ctx, bktInfo, systemTree, node.ID[ind], 0, meta)
|
||||
}
|
||||
|
||||
func (c *Tree) DeleteBucketLifecycleConfiguration(ctx context.Context, bktInfo *data.BucketInfo) (oid.ID, error) {
|
||||
func (c *Tree) GetBucketLifecycleConfiguration(ctx context.Context, bktInfo *data.BucketInfo) (oid.Address, error) {
|
||||
node, err := c.getSystemNode(ctx, bktInfo, []string{bucketLifecycleFilename})
|
||||
if err != nil {
|
||||
return oid.Address{}, err
|
||||
}
|
||||
|
||||
return getAddress(node)
|
||||
}
|
||||
|
||||
func (c *Tree) DeleteBucketLifecycleConfiguration(ctx context.Context, bktInfo *data.BucketInfo) (oid.Address, error) {
|
||||
node, err := c.getSystemNode(ctx, bktInfo, []string{bucketLifecycleFilename})
|
||||
if err != nil && !errors.Is(err, layer.ErrNodeNotFound) {
|
||||
return oid.ID{}, err
|
||||
return oid.Address{}, err
|
||||
}
|
||||
|
||||
if node != nil {
|
||||
|
@ -1421,10 +1429,30 @@ func (c *Tree) DeleteBucketLifecycleConfiguration(ctx context.Context, bktInfo *
|
|||
c.reqLogger(ctx).Warn(logs.FoundSeveralBucketLifecycleNodes)
|
||||
}
|
||||
|
||||
return node.ObjID, c.service.RemoveNode(ctx, bktInfo, systemTree, node.ID[ind])
|
||||
addr, err := getAddress(node)
|
||||
if err != nil {
|
||||
return oid.Address{}, fmt.Errorf("couldn't get lifecycle object addr: %w", err)
|
||||
}
|
||||
|
||||
return oid.ID{}, layer.ErrNoNodeToRemove
|
||||
return addr, c.service.RemoveNode(ctx, bktInfo, systemTree, node.ID[ind])
|
||||
}
|
||||
|
||||
return oid.Address{}, layer.ErrNoNodeToRemove
|
||||
}
|
||||
|
||||
func getAddress(node *treeNode) (oid.Address, error) {
|
||||
var addr oid.Address
|
||||
addr.SetObject(node.ObjID)
|
||||
|
||||
if cidStr, ok := node.Get(cidKV); ok {
|
||||
var cnrID cid.ID
|
||||
if err := cnrID.DecodeString(cidStr); err != nil {
|
||||
return oid.Address{}, fmt.Errorf("couldn't decode cid: %w", err)
|
||||
}
|
||||
addr.SetContainer(cnrID)
|
||||
}
|
||||
|
||||
return addr, nil
|
||||
}
|
||||
|
||||
func (c *Tree) DeleteMultipartUpload(ctx context.Context, bktInfo *data.BucketInfo, multipartInfo *data.MultipartInfo) error {
|
||||
|
|
Loading…
Reference in a new issue