forked from TrueCloudLab/frostfs-s3-gw
150 lines
4.4 KiB
Go
150 lines
4.4 KiB
Go
package layer
|
|
|
|
import (
|
|
"bytes"
|
|
"context"
|
|
"encoding/base64"
|
|
"encoding/xml"
|
|
"errors"
|
|
"fmt"
|
|
"io"
|
|
|
|
"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/api/layer/frostfs"
|
|
"git.frostfs.info/TrueCloudLab/frostfs-s3-gw/api/layer/tree"
|
|
"git.frostfs.info/TrueCloudLab/frostfs-s3-gw/internal/logs"
|
|
oid "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/object/id"
|
|
"go.uber.org/zap"
|
|
)
|
|
|
|
type PutBucketLifecycleParams struct {
|
|
BktInfo *data.BucketInfo
|
|
LifecycleCfg *data.LifecycleConfiguration
|
|
LifecycleReader io.Reader
|
|
CopiesNumbers []uint32
|
|
MD5Hash string
|
|
}
|
|
|
|
func (n *Layer) PutBucketLifecycleConfiguration(ctx context.Context, p *PutBucketLifecycleParams) error {
|
|
prm := frostfs.PrmObjectCreate{
|
|
Payload: p.LifecycleReader,
|
|
Filepath: p.BktInfo.LifecycleConfigurationObjectName(),
|
|
CreationTime: TimeNow(ctx),
|
|
}
|
|
|
|
var lifecycleBkt *data.BucketInfo
|
|
if n.lifecycleCnrInfo == nil {
|
|
lifecycleBkt = p.BktInfo
|
|
prm.CopiesNumber = p.CopiesNumbers
|
|
} else {
|
|
lifecycleBkt = n.lifecycleCnrInfo
|
|
prm.PrmAuth.PrivateKey = &n.gateKey.PrivateKey
|
|
}
|
|
|
|
prm.Container = lifecycleBkt.CID
|
|
|
|
createdObj, err := n.objectPutAndHash(ctx, prm, lifecycleBkt)
|
|
if err != nil {
|
|
return fmt.Errorf("put lifecycle object: %w", err)
|
|
}
|
|
|
|
hashBytes, err := base64.StdEncoding.DecodeString(p.MD5Hash)
|
|
if err != nil {
|
|
return apierr.GetAPIError(apierr.ErrInvalidDigest)
|
|
}
|
|
|
|
if !bytes.Equal(hashBytes, createdObj.MD5Sum) {
|
|
n.deleteLifecycleObject(ctx, p.BktInfo, newAddress(lifecycleBkt.CID, createdObj.ID))
|
|
|
|
return apierr.GetAPIError(apierr.ErrInvalidDigest)
|
|
}
|
|
|
|
objsToDelete, err := n.treeService.PutBucketLifecycleConfiguration(ctx, p.BktInfo, newAddress(lifecycleBkt.CID, createdObj.ID))
|
|
objsToDeleteNotFound := errors.Is(err, tree.ErrNoNodeToRemove)
|
|
if err != nil && !objsToDeleteNotFound {
|
|
return err
|
|
}
|
|
|
|
if !objsToDeleteNotFound {
|
|
for _, addr := range objsToDelete {
|
|
n.deleteLifecycleObject(ctx, p.BktInfo, addr)
|
|
}
|
|
}
|
|
|
|
n.cache.PutLifecycleConfiguration(n.BearerOwner(ctx), p.BktInfo, p.LifecycleCfg)
|
|
|
|
return nil
|
|
}
|
|
|
|
// deleteLifecycleObject removes object and logs in case of error.
|
|
func (n *Layer) deleteLifecycleObject(ctx context.Context, bktInfo *data.BucketInfo, addr oid.Address) {
|
|
var prmAuth frostfs.PrmAuth
|
|
lifecycleBkt := bktInfo
|
|
if !addr.Container().Equals(bktInfo.CID) {
|
|
lifecycleBkt = &data.BucketInfo{CID: addr.Container()}
|
|
prmAuth.PrivateKey = &n.gateKey.PrivateKey
|
|
}
|
|
|
|
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", addr.Object().EncodeToString()))
|
|
}
|
|
}
|
|
|
|
func (n *Layer) GetBucketLifecycleConfiguration(ctx context.Context, bktInfo *data.BucketInfo) (*data.LifecycleConfiguration, error) {
|
|
owner := n.BearerOwner(ctx)
|
|
if cfg := n.cache.GetLifecycleConfiguration(owner, bktInfo); cfg != nil {
|
|
return cfg, nil
|
|
}
|
|
|
|
addr, err := n.treeService.GetBucketLifecycleConfiguration(ctx, bktInfo)
|
|
objNotFound := errors.Is(err, tree.ErrNodeNotFound)
|
|
if err != nil && !objNotFound {
|
|
return nil, err
|
|
}
|
|
|
|
if objNotFound {
|
|
return nil, fmt.Errorf("%w: %s", apierr.GetAPIError(apierr.ErrNoSuchLifecycleConfiguration), err.Error())
|
|
}
|
|
|
|
var prmAuth frostfs.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{}
|
|
|
|
if err = xml.NewDecoder(obj.Payload).Decode(&lifecycleCfg); err != nil {
|
|
return nil, fmt.Errorf("unmarshal lifecycle configuration: %w", err)
|
|
}
|
|
|
|
n.cache.PutLifecycleConfiguration(owner, bktInfo, lifecycleCfg)
|
|
|
|
return lifecycleCfg, nil
|
|
}
|
|
|
|
func (n *Layer) DeleteBucketLifecycleConfiguration(ctx context.Context, bktInfo *data.BucketInfo) error {
|
|
objs, err := n.treeService.DeleteBucketLifecycleConfiguration(ctx, bktInfo)
|
|
objsNotFound := errors.Is(err, tree.ErrNoNodeToRemove)
|
|
if err != nil && !objsNotFound {
|
|
return err
|
|
}
|
|
if !objsNotFound {
|
|
for _, addr := range objs {
|
|
n.deleteLifecycleObject(ctx, bktInfo, addr)
|
|
}
|
|
}
|
|
|
|
n.cache.DeleteLifecycleConfiguration(bktInfo)
|
|
|
|
return nil
|
|
}
|