package layer import ( "bytes" "context" "encoding/xml" "errors" "fmt" "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 CopiesNumbers []uint32 } func (n *Layer) PutBucketLifecycleConfiguration(ctx context.Context, p *PutBucketLifecycleParams) error { cfgBytes, err := xml.Marshal(p.LifecycleCfg) if err != nil { return fmt.Errorf("marshal lifecycle configuration: %w", err) } prm := frostfs.PrmObjectCreate{ Payload: bytes.NewReader(cfgBytes), 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) } 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) for i := range lifecycleCfg.Rules { if lifecycleCfg.Rules[i].Expiration != nil { lifecycleCfg.Rules[i].Expiration.Epoch = nil } } 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 }