2021-10-04 14:30:38 +00:00
|
|
|
package layer
|
|
|
|
|
|
|
|
import (
|
|
|
|
"context"
|
2021-10-13 18:50:02 +00:00
|
|
|
"encoding/xml"
|
2022-04-22 07:18:21 +00:00
|
|
|
errorsStd "errors"
|
2022-02-28 08:02:05 +00:00
|
|
|
"fmt"
|
2022-09-20 10:30:29 +00:00
|
|
|
"math"
|
2022-03-04 13:07:27 +00:00
|
|
|
"strconv"
|
|
|
|
"time"
|
2021-10-04 14:30:38 +00:00
|
|
|
|
2023-03-22 06:53:14 +00:00
|
|
|
"git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/object"
|
2023-03-07 14:38:08 +00:00
|
|
|
"git.frostfs.info/TrueCloudLab/frostfs-s3-gw/api/data"
|
|
|
|
"git.frostfs.info/TrueCloudLab/frostfs-s3-gw/api/errors"
|
|
|
|
oid "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/object/id"
|
2021-10-04 14:30:38 +00:00
|
|
|
)
|
|
|
|
|
2022-02-28 13:39:04 +00:00
|
|
|
const (
|
2023-03-22 06:53:14 +00:00
|
|
|
AttributeComplianceMode = ".s3-compliance-mode"
|
2022-02-28 13:39:04 +00:00
|
|
|
)
|
|
|
|
|
2022-08-11 22:48:56 +00:00
|
|
|
type PutLockInfoParams struct {
|
2024-04-10 06:41:07 +00:00
|
|
|
ObjVersion *data.ObjectVersion
|
2023-04-24 23:49:12 +00:00
|
|
|
NewLock *data.ObjectLock
|
|
|
|
CopiesNumbers []uint32
|
|
|
|
NodeVersion *data.NodeVersion // optional
|
2022-08-11 22:48:56 +00:00
|
|
|
}
|
|
|
|
|
2022-08-29 11:30:38 +00:00
|
|
|
func (n *layer) PutLockInfo(ctx context.Context, p *PutLockInfoParams) (err error) {
|
2022-09-13 09:44:18 +00:00
|
|
|
newLock := p.NewLock
|
2022-08-29 11:30:38 +00:00
|
|
|
versionNode := p.NodeVersion
|
|
|
|
// sometimes node version can be provided from executing context
|
2022-08-30 12:19:28 +00:00
|
|
|
// if not, then receive node version from tree service
|
2022-08-29 11:30:38 +00:00
|
|
|
if versionNode == nil {
|
2022-12-20 08:38:58 +00:00
|
|
|
versionNode, err = n.getNodeVersionFromCacheOrFrostfs(ctx, p.ObjVersion)
|
2022-10-14 14:36:43 +00:00
|
|
|
if err != nil {
|
|
|
|
return err
|
2022-08-29 11:30:38 +00:00
|
|
|
}
|
2021-10-13 18:50:02 +00:00
|
|
|
}
|
|
|
|
|
2022-09-13 09:44:18 +00:00
|
|
|
lockInfo, err := n.treeService.GetLock(ctx, p.ObjVersion.BktInfo, versionNode.ID)
|
2022-05-26 13:11:14 +00:00
|
|
|
if err != nil && !errorsStd.Is(err, ErrNodeNotFound) {
|
|
|
|
return err
|
2021-10-13 18:50:02 +00:00
|
|
|
}
|
|
|
|
|
2022-05-26 13:11:14 +00:00
|
|
|
if lockInfo == nil {
|
|
|
|
lockInfo = &data.LockInfo{}
|
2021-10-13 18:50:02 +00:00
|
|
|
}
|
|
|
|
|
2022-05-26 13:11:14 +00:00
|
|
|
if newLock.Retention != nil {
|
2022-06-27 09:33:36 +00:00
|
|
|
if lockInfo.IsRetentionSet() {
|
|
|
|
if lockInfo.IsCompliance() {
|
2022-05-26 13:11:14 +00:00
|
|
|
return fmt.Errorf("you cannot change compliance mode")
|
|
|
|
}
|
|
|
|
if !newLock.Retention.ByPassedGovernance {
|
|
|
|
return fmt.Errorf("you cannot bypass governence mode")
|
|
|
|
}
|
|
|
|
|
2022-06-27 09:33:36 +00:00
|
|
|
untilDate := lockInfo.UntilDate()
|
|
|
|
if len(untilDate) > 0 {
|
|
|
|
parsedTime, err := time.Parse(time.RFC3339, untilDate)
|
2022-05-26 13:11:14 +00:00
|
|
|
if err != nil {
|
2022-06-27 09:33:36 +00:00
|
|
|
return fmt.Errorf("couldn't parse time '%s': %w", untilDate, err)
|
2022-05-26 13:11:14 +00:00
|
|
|
}
|
|
|
|
if parsedTime.After(newLock.Retention.Until) {
|
|
|
|
return fmt.Errorf("you couldn't short the until date")
|
|
|
|
}
|
|
|
|
}
|
2022-05-18 07:48:30 +00:00
|
|
|
}
|
2022-05-26 13:11:14 +00:00
|
|
|
lock := &data.ObjectLock{Retention: newLock.Retention}
|
2023-04-24 23:49:12 +00:00
|
|
|
retentionOID, err := n.putLockObject(ctx, p.ObjVersion.BktInfo, versionNode.OID, lock, p.CopiesNumbers)
|
2022-06-27 09:33:36 +00:00
|
|
|
if err != nil {
|
2022-05-26 13:11:14 +00:00
|
|
|
return err
|
|
|
|
}
|
2022-06-27 09:33:36 +00:00
|
|
|
lockInfo.SetRetention(retentionOID, newLock.Retention.Until.UTC().Format(time.RFC3339), newLock.Retention.IsCompliance)
|
2021-10-13 18:50:02 +00:00
|
|
|
}
|
|
|
|
|
2022-05-26 13:11:14 +00:00
|
|
|
if newLock.LegalHold != nil {
|
2022-06-27 09:33:36 +00:00
|
|
|
if newLock.LegalHold.Enabled && !lockInfo.IsLegalHoldSet() {
|
2022-05-26 13:11:14 +00:00
|
|
|
lock := &data.ObjectLock{LegalHold: newLock.LegalHold}
|
2023-04-24 23:49:12 +00:00
|
|
|
legalHoldOID, err := n.putLockObject(ctx, p.ObjVersion.BktInfo, versionNode.OID, lock, p.CopiesNumbers)
|
2022-06-27 09:33:36 +00:00
|
|
|
if err != nil {
|
2022-05-26 13:11:14 +00:00
|
|
|
return err
|
|
|
|
}
|
2022-06-27 09:33:36 +00:00
|
|
|
lockInfo.SetLegalHold(legalHoldOID)
|
|
|
|
} else if !newLock.LegalHold.Enabled && lockInfo.IsLegalHoldSet() {
|
2022-08-11 22:48:56 +00:00
|
|
|
if err = n.objectDelete(ctx, p.ObjVersion.BktInfo, lockInfo.LegalHold()); err != nil {
|
2022-06-27 09:33:36 +00:00
|
|
|
return fmt.Errorf("couldn't delete lock object '%s' to remove legal hold: %w", lockInfo.LegalHold().EncodeToString(), err)
|
2022-05-26 13:11:14 +00:00
|
|
|
}
|
2022-06-27 09:33:36 +00:00
|
|
|
lockInfo.ResetLegalHold()
|
2022-05-26 13:11:14 +00:00
|
|
|
}
|
2022-05-17 14:56:05 +00:00
|
|
|
}
|
|
|
|
|
2022-09-13 09:44:18 +00:00
|
|
|
if err = n.treeService.PutLock(ctx, p.ObjVersion.BktInfo, versionNode.ID, lockInfo); err != nil {
|
2022-05-26 13:11:14 +00:00
|
|
|
return fmt.Errorf("couldn't put lock into tree: %w", err)
|
2021-10-13 18:50:02 +00:00
|
|
|
}
|
|
|
|
|
2023-08-03 12:08:22 +00:00
|
|
|
n.cache.PutLockInfo(n.BearerOwner(ctx), lockObjectKey(p.ObjVersion), lockInfo)
|
2021-10-13 18:50:02 +00:00
|
|
|
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2024-04-10 06:41:07 +00:00
|
|
|
func (n *layer) getNodeVersionFromCacheOrFrostfs(ctx context.Context, objVersion *data.ObjectVersion) (nodeVersion *data.NodeVersion, err error) {
|
2022-10-14 14:36:43 +00:00
|
|
|
// check cache if node version is stored inside extendedObjectVersion
|
2023-08-03 12:08:22 +00:00
|
|
|
nodeVersion = n.getNodeVersionFromCache(n.BearerOwner(ctx), objVersion)
|
2022-10-14 14:36:43 +00:00
|
|
|
if nodeVersion == nil {
|
|
|
|
// else get node version from tree service
|
|
|
|
return n.getNodeVersion(ctx, objVersion)
|
|
|
|
}
|
|
|
|
|
|
|
|
return nodeVersion, nil
|
|
|
|
}
|
|
|
|
|
2023-04-24 23:49:12 +00:00
|
|
|
func (n *layer) putLockObject(ctx context.Context, bktInfo *data.BucketInfo, objID oid.ID, lock *data.ObjectLock, copiesNumber []uint32) (oid.ID, error) {
|
2022-06-06 11:09:09 +00:00
|
|
|
prm := PrmObjectCreate{
|
2022-08-11 22:48:56 +00:00
|
|
|
Container: bktInfo.CID,
|
|
|
|
Locks: []oid.ID{objID},
|
2022-11-08 09:12:55 +00:00
|
|
|
CreationTime: TimeNow(ctx),
|
2022-08-11 22:48:56 +00:00
|
|
|
CopiesNumber: copiesNumber,
|
2022-03-01 19:02:24 +00:00
|
|
|
}
|
2021-10-04 14:30:38 +00:00
|
|
|
|
2022-05-26 13:11:14 +00:00
|
|
|
var err error
|
|
|
|
prm.Attributes, err = n.attributesFromLock(ctx, lock)
|
|
|
|
if err != nil {
|
2022-06-27 09:33:36 +00:00
|
|
|
return oid.ID{}, err
|
2022-05-26 13:11:14 +00:00
|
|
|
}
|
2022-03-05 08:53:01 +00:00
|
|
|
|
2023-10-02 08:52:07 +00:00
|
|
|
_, id, _, _, err := n.objectPutAndHash(ctx, prm, bktInfo)
|
2022-05-26 13:11:14 +00:00
|
|
|
return id, err
|
|
|
|
}
|
2022-02-28 13:39:04 +00:00
|
|
|
|
2024-04-10 06:41:07 +00:00
|
|
|
func (n *layer) GetLockInfo(ctx context.Context, objVersion *data.ObjectVersion) (*data.LockInfo, error) {
|
2023-08-03 12:08:22 +00:00
|
|
|
owner := n.BearerOwner(ctx)
|
2022-10-06 08:46:53 +00:00
|
|
|
if lockInfo := n.cache.GetLockInfo(owner, lockObjectKey(objVersion)); lockInfo != nil {
|
2022-05-26 13:11:14 +00:00
|
|
|
return lockInfo, nil
|
2022-03-01 19:02:24 +00:00
|
|
|
}
|
2021-10-04 14:30:38 +00:00
|
|
|
|
2022-05-26 13:11:14 +00:00
|
|
|
versionNode, err := n.getNodeVersion(ctx, objVersion)
|
2021-10-04 14:30:38 +00:00
|
|
|
if err != nil {
|
2022-04-12 06:52:44 +00:00
|
|
|
return nil, err
|
2021-10-04 14:30:38 +00:00
|
|
|
}
|
|
|
|
|
2022-09-13 09:44:18 +00:00
|
|
|
lockInfo, err := n.treeService.GetLock(ctx, objVersion.BktInfo, versionNode.ID)
|
2022-05-26 13:11:14 +00:00
|
|
|
if err != nil && !errorsStd.Is(err, ErrNodeNotFound) {
|
|
|
|
return nil, err
|
2022-05-17 14:56:05 +00:00
|
|
|
}
|
2022-05-26 13:11:14 +00:00
|
|
|
if lockInfo == nil {
|
|
|
|
lockInfo = &data.LockInfo{}
|
2021-10-04 14:30:38 +00:00
|
|
|
}
|
|
|
|
|
2022-10-06 08:46:53 +00:00
|
|
|
n.cache.PutLockInfo(owner, lockObjectKey(objVersion), lockInfo)
|
2022-05-25 15:59:36 +00:00
|
|
|
|
2022-05-26 13:11:14 +00:00
|
|
|
return lockInfo, nil
|
2021-10-04 14:30:38 +00:00
|
|
|
}
|
|
|
|
|
2022-10-03 14:33:49 +00:00
|
|
|
func (n *layer) getCORS(ctx context.Context, bkt *data.BucketInfo) (*data.CORSConfiguration, error) {
|
2023-08-03 12:08:22 +00:00
|
|
|
owner := n.BearerOwner(ctx)
|
2022-10-06 08:46:53 +00:00
|
|
|
if cors := n.cache.GetCORS(owner, bkt); cors != nil {
|
2021-10-13 18:50:02 +00:00
|
|
|
return cors, nil
|
2021-10-04 14:30:38 +00:00
|
|
|
}
|
2022-10-03 14:33:49 +00:00
|
|
|
|
2022-09-13 09:44:18 +00:00
|
|
|
objID, err := n.treeService.GetBucketCORS(ctx, bkt)
|
2022-06-27 09:33:36 +00:00
|
|
|
objIDNotFound := errorsStd.Is(err, ErrNodeNotFound)
|
|
|
|
if err != nil && !objIDNotFound {
|
2022-05-12 02:33:03 +00:00
|
|
|
return nil, err
|
|
|
|
}
|
2022-05-17 13:52:51 +00:00
|
|
|
|
2022-06-27 09:33:36 +00:00
|
|
|
if objIDNotFound {
|
2023-06-30 09:03:55 +00:00
|
|
|
return nil, fmt.Errorf("%w: %s", errors.GetAPIError(errors.ErrNoSuchCORSConfiguration), err.Error())
|
2022-05-12 02:33:03 +00:00
|
|
|
}
|
2021-10-04 14:30:38 +00:00
|
|
|
|
2022-06-27 09:33:36 +00:00
|
|
|
obj, err := n.objectGet(ctx, bkt, objID)
|
2021-10-04 14:30:38 +00:00
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
2021-10-13 18:50:02 +00:00
|
|
|
cors := &data.CORSConfiguration{}
|
2021-10-04 14:30:38 +00:00
|
|
|
|
2021-10-13 18:50:02 +00:00
|
|
|
if err = xml.Unmarshal(obj.Payload(), &cors); err != nil {
|
2022-06-22 19:40:52 +00:00
|
|
|
return nil, fmt.Errorf("unmarshal cors: %w", err)
|
2021-10-04 14:30:38 +00:00
|
|
|
}
|
|
|
|
|
2022-10-06 08:46:53 +00:00
|
|
|
n.cache.PutCORS(owner, bkt, cors)
|
2021-10-04 14:30:38 +00:00
|
|
|
|
2021-10-13 18:50:02 +00:00
|
|
|
return cors, nil
|
2021-10-04 14:30:38 +00:00
|
|
|
}
|
|
|
|
|
2024-04-10 06:41:07 +00:00
|
|
|
func lockObjectKey(objVersion *data.ObjectVersion) string {
|
2022-05-26 13:11:14 +00:00
|
|
|
// todo reconsider forming name since versionID can be "null" or ""
|
|
|
|
return ".lock." + objVersion.BktInfo.CID.EncodeToString() + "." + objVersion.ObjectName + "." + objVersion.VersionID
|
|
|
|
}
|
|
|
|
|
2022-02-28 08:02:05 +00:00
|
|
|
func (n *layer) GetBucketSettings(ctx context.Context, bktInfo *data.BucketInfo) (*data.BucketSettings, error) {
|
2023-08-03 12:08:22 +00:00
|
|
|
owner := n.BearerOwner(ctx)
|
2022-10-06 08:46:53 +00:00
|
|
|
if settings := n.cache.GetSettings(owner, bktInfo); settings != nil {
|
2022-02-28 08:02:05 +00:00
|
|
|
return settings, nil
|
|
|
|
}
|
|
|
|
|
2022-09-13 09:44:18 +00:00
|
|
|
settings, err := n.treeService.GetSettingsNode(ctx, bktInfo)
|
2022-02-28 08:02:05 +00:00
|
|
|
if err != nil {
|
2022-05-12 01:48:17 +00:00
|
|
|
if !errorsStd.Is(err, ErrNodeNotFound) {
|
2022-05-17 13:03:38 +00:00
|
|
|
return nil, err
|
2022-03-31 16:25:33 +00:00
|
|
|
}
|
2022-07-20 10:30:19 +00:00
|
|
|
settings = &data.BucketSettings{Versioning: data.VersioningUnversioned}
|
2022-02-28 08:02:05 +00:00
|
|
|
}
|
|
|
|
|
2022-10-06 08:46:53 +00:00
|
|
|
n.cache.PutSettings(owner, bktInfo, settings)
|
2022-02-28 08:02:05 +00:00
|
|
|
|
|
|
|
return settings, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func (n *layer) PutBucketSettings(ctx context.Context, p *PutSettingsParams) error {
|
2022-09-13 09:44:18 +00:00
|
|
|
if err := n.treeService.PutSettingsNode(ctx, p.BktInfo, p.Settings); err != nil {
|
2022-04-22 07:18:21 +00:00
|
|
|
return fmt.Errorf("failed to get settings node: %w", err)
|
2022-02-28 08:02:05 +00:00
|
|
|
}
|
|
|
|
|
2023-08-03 12:08:22 +00:00
|
|
|
n.cache.PutSettings(n.BearerOwner(ctx), p.BktInfo, p.Settings)
|
2022-02-28 08:02:05 +00:00
|
|
|
|
|
|
|
return nil
|
|
|
|
}
|
2022-02-28 13:39:04 +00:00
|
|
|
|
2022-03-05 08:53:01 +00:00
|
|
|
func (n *layer) attributesFromLock(ctx context.Context, lock *data.ObjectLock) ([][2]string, error) {
|
2022-09-20 10:30:29 +00:00
|
|
|
var (
|
|
|
|
err error
|
|
|
|
expEpoch uint64
|
|
|
|
result [][2]string
|
|
|
|
)
|
|
|
|
|
|
|
|
if lock.Retention != nil {
|
2022-12-20 08:38:58 +00:00
|
|
|
if _, expEpoch, err = n.frostFS.TimeToEpoch(ctx, TimeNow(ctx), lock.Retention.Until); err != nil {
|
2022-09-20 10:30:29 +00:00
|
|
|
return nil, fmt.Errorf("fetch time to epoch: %w", err)
|
|
|
|
}
|
2022-03-05 08:53:01 +00:00
|
|
|
|
2022-09-20 10:30:29 +00:00
|
|
|
if lock.Retention.IsCompliance {
|
|
|
|
result = append(result, [2]string{AttributeComplianceMode, "true"})
|
|
|
|
}
|
2022-05-26 13:11:14 +00:00
|
|
|
}
|
2022-03-05 08:53:01 +00:00
|
|
|
|
2022-09-20 10:30:29 +00:00
|
|
|
if lock.LegalHold != nil && lock.LegalHold.Enabled {
|
2023-03-07 14:38:08 +00:00
|
|
|
// todo: (@KirillovDenis) reconsider this when FrostFS will support Legal Hold https://git.frostfs.info/TrueCloudLab/frostfs-contract/issues/2
|
2022-09-20 10:30:29 +00:00
|
|
|
// Currently lock object must have an expiration epoch.
|
|
|
|
// Besides we need to override retention expiration epoch since legal hold cannot be deleted yet.
|
|
|
|
expEpoch = math.MaxUint64
|
2022-02-28 13:39:04 +00:00
|
|
|
}
|
|
|
|
|
2022-09-20 10:30:29 +00:00
|
|
|
if expEpoch != 0 {
|
|
|
|
result = append(result, [2]string{
|
2023-03-22 06:53:14 +00:00
|
|
|
object.SysAttributeExpEpoch, strconv.FormatUint(expEpoch, 10),
|
2022-09-20 10:30:29 +00:00
|
|
|
})
|
2022-05-26 13:11:14 +00:00
|
|
|
}
|
2022-09-20 10:30:29 +00:00
|
|
|
|
2022-03-05 08:53:01 +00:00
|
|
|
return result, nil
|
2022-02-28 13:39:04 +00:00
|
|
|
}
|