2022-06-06 11:09:09 +00:00
|
|
|
package layer
|
2022-03-04 13:07:27 +00:00
|
|
|
|
|
|
|
import (
|
|
|
|
"bytes"
|
|
|
|
"context"
|
|
|
|
"crypto/rand"
|
|
|
|
"crypto/sha256"
|
2024-11-18 12:31:09 +00:00
|
|
|
"errors"
|
2022-03-04 13:07:27 +00:00
|
|
|
"fmt"
|
|
|
|
"io"
|
2024-04-26 14:32:58 +00:00
|
|
|
"strings"
|
2022-03-05 08:53:01 +00:00
|
|
|
"time"
|
2022-03-04 13:07:27 +00:00
|
|
|
|
2024-09-27 08:14:45 +00:00
|
|
|
"git.frostfs.info/TrueCloudLab/frostfs-s3-gw/api/layer/frostfs"
|
2023-07-05 14:05:45 +00:00
|
|
|
"git.frostfs.info/TrueCloudLab/frostfs-s3-gw/api/middleware"
|
2024-11-27 13:16:45 +00:00
|
|
|
v2container "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/api/container"
|
|
|
|
objectv2 "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/api/object"
|
2023-03-07 14:38:08 +00:00
|
|
|
"git.frostfs.info/TrueCloudLab/frostfs-sdk-go/bearer"
|
|
|
|
"git.frostfs.info/TrueCloudLab/frostfs-sdk-go/checksum"
|
2023-04-14 12:36:22 +00:00
|
|
|
apistatus "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/client/status"
|
2023-03-07 14:38:08 +00:00
|
|
|
"git.frostfs.info/TrueCloudLab/frostfs-sdk-go/container"
|
|
|
|
cid "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/container/id"
|
2024-07-22 09:00:17 +00:00
|
|
|
"git.frostfs.info/TrueCloudLab/frostfs-sdk-go/netmap"
|
2023-03-07 14:38:08 +00:00
|
|
|
"git.frostfs.info/TrueCloudLab/frostfs-sdk-go/object"
|
|
|
|
oid "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/object/id"
|
2024-08-13 15:21:16 +00:00
|
|
|
oidtest "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/object/id/test"
|
2024-11-22 09:32:35 +00:00
|
|
|
"git.frostfs.info/TrueCloudLab/frostfs-sdk-go/object/relations"
|
2023-03-07 14:38:08 +00:00
|
|
|
"git.frostfs.info/TrueCloudLab/frostfs-sdk-go/session"
|
|
|
|
"git.frostfs.info/TrueCloudLab/frostfs-sdk-go/user"
|
2024-11-18 12:31:09 +00:00
|
|
|
"git.frostfs.info/TrueCloudLab/policy-engine/pkg/chain"
|
2023-08-03 12:08:22 +00:00
|
|
|
"github.com/nspcc-dev/neo-go/pkg/crypto/keys"
|
2022-03-04 13:07:27 +00:00
|
|
|
)
|
|
|
|
|
2023-08-21 13:05:16 +00:00
|
|
|
type FeatureSettingsMock struct {
|
2023-10-02 08:52:07 +00:00
|
|
|
clientCut bool
|
|
|
|
md5Enabled bool
|
2023-08-21 13:05:16 +00:00
|
|
|
}
|
|
|
|
|
2024-11-22 09:32:35 +00:00
|
|
|
func (k *FeatureSettingsMock) TombstoneLifetime() uint64 {
|
|
|
|
return 1
|
|
|
|
}
|
|
|
|
|
|
|
|
func (k *FeatureSettingsMock) TombstoneMembersSize() int {
|
|
|
|
return 2
|
|
|
|
}
|
|
|
|
|
2023-08-25 10:06:43 +00:00
|
|
|
func (k *FeatureSettingsMock) BufferMaxSizeForPut() uint64 {
|
|
|
|
return 0
|
|
|
|
}
|
|
|
|
|
2023-08-21 13:05:16 +00:00
|
|
|
func (k *FeatureSettingsMock) ClientCut() bool {
|
|
|
|
return k.clientCut
|
|
|
|
}
|
|
|
|
|
|
|
|
func (k *FeatureSettingsMock) SetClientCut(clientCut bool) {
|
|
|
|
k.clientCut = clientCut
|
|
|
|
}
|
|
|
|
|
2023-10-02 08:52:07 +00:00
|
|
|
func (k *FeatureSettingsMock) MD5Enabled() bool {
|
|
|
|
return k.md5Enabled
|
|
|
|
}
|
|
|
|
|
|
|
|
func (k *FeatureSettingsMock) SetMD5Enabled(md5Enabled bool) {
|
|
|
|
k.md5Enabled = md5Enabled
|
|
|
|
}
|
|
|
|
|
2024-09-03 14:50:51 +00:00
|
|
|
func (k *FeatureSettingsMock) FormContainerZone(ns string) string {
|
2023-11-23 07:44:50 +00:00
|
|
|
if ns == "" {
|
2024-09-03 14:50:51 +00:00
|
|
|
return v2container.SysAttributeZoneDefault
|
2023-11-23 07:44:50 +00:00
|
|
|
}
|
|
|
|
|
2024-09-03 14:50:51 +00:00
|
|
|
return ns + ".ns"
|
2023-11-23 07:44:50 +00:00
|
|
|
}
|
|
|
|
|
2024-11-18 12:31:09 +00:00
|
|
|
var _ frostfs.FrostFS = (*TestFrostFS)(nil)
|
2022-03-04 13:07:27 +00:00
|
|
|
|
2024-11-18 12:31:09 +00:00
|
|
|
type TestFrostFS struct {
|
2023-08-01 14:57:41 +00:00
|
|
|
objects map[string]*object.Object
|
|
|
|
objectErrors map[string]error
|
|
|
|
objectPutErrors map[string]error
|
|
|
|
containers map[string]*container.Container
|
2024-11-18 12:31:09 +00:00
|
|
|
chains map[string][]chain.Chain
|
2023-08-01 14:57:41 +00:00
|
|
|
currentEpoch uint64
|
2023-08-03 12:08:22 +00:00
|
|
|
key *keys.PrivateKey
|
2022-03-04 13:07:27 +00:00
|
|
|
}
|
|
|
|
|
2023-08-03 12:08:22 +00:00
|
|
|
func NewTestFrostFS(key *keys.PrivateKey) *TestFrostFS {
|
2022-12-20 08:38:58 +00:00
|
|
|
return &TestFrostFS{
|
2023-08-01 14:57:41 +00:00
|
|
|
objects: make(map[string]*object.Object),
|
|
|
|
objectErrors: make(map[string]error),
|
|
|
|
objectPutErrors: make(map[string]error),
|
|
|
|
containers: make(map[string]*container.Container),
|
2024-11-18 12:31:09 +00:00
|
|
|
chains: make(map[string][]chain.Chain),
|
2023-08-03 12:08:22 +00:00
|
|
|
key: key,
|
2022-03-04 13:07:27 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-12-20 08:38:58 +00:00
|
|
|
func (t *TestFrostFS) CurrentEpoch() uint64 {
|
2022-03-04 13:07:27 +00:00
|
|
|
return t.currentEpoch
|
|
|
|
}
|
|
|
|
|
2023-04-24 16:40:18 +00:00
|
|
|
func (t *TestFrostFS) SetObjectError(addr oid.Address, err error) {
|
|
|
|
if err == nil {
|
|
|
|
delete(t.objectErrors, addr.EncodeToString())
|
|
|
|
} else {
|
|
|
|
t.objectErrors[addr.EncodeToString()] = err
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-08-01 14:57:41 +00:00
|
|
|
func (t *TestFrostFS) SetObjectPutError(fileName string, err error) {
|
|
|
|
if err == nil {
|
|
|
|
delete(t.objectPutErrors, fileName)
|
|
|
|
} else {
|
|
|
|
t.objectPutErrors[fileName] = err
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-12-20 08:38:58 +00:00
|
|
|
func (t *TestFrostFS) Objects() []*object.Object {
|
2022-03-04 13:07:27 +00:00
|
|
|
res := make([]*object.Object, 0, len(t.objects))
|
|
|
|
|
|
|
|
for _, obj := range t.objects {
|
|
|
|
res = append(res, obj)
|
|
|
|
}
|
|
|
|
|
|
|
|
return res
|
|
|
|
}
|
|
|
|
|
2023-04-14 12:36:22 +00:00
|
|
|
func (t *TestFrostFS) ObjectExists(objID oid.ID) bool {
|
|
|
|
for _, obj := range t.objects {
|
|
|
|
if id, _ := obj.ID(); id.Equals(objID) {
|
|
|
|
return true
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
|
2022-12-20 08:38:58 +00:00
|
|
|
func (t *TestFrostFS) AddObject(key string, obj *object.Object) {
|
2022-03-04 13:07:27 +00:00
|
|
|
t.objects[key] = obj
|
|
|
|
}
|
|
|
|
|
2022-12-20 08:38:58 +00:00
|
|
|
func (t *TestFrostFS) ContainerID(name string) (cid.ID, error) {
|
2022-03-04 13:07:27 +00:00
|
|
|
for id, cnr := range t.containers {
|
2022-06-29 14:43:52 +00:00
|
|
|
if container.Name(*cnr) == name {
|
|
|
|
var cnrID cid.ID
|
2022-06-27 09:08:26 +00:00
|
|
|
return cnrID, cnrID.DecodeString(id)
|
2022-03-04 13:07:27 +00:00
|
|
|
}
|
|
|
|
}
|
2022-06-27 09:08:26 +00:00
|
|
|
return cid.ID{}, fmt.Errorf("not found")
|
2022-03-04 13:07:27 +00:00
|
|
|
}
|
|
|
|
|
2024-02-12 12:28:55 +00:00
|
|
|
func (t *TestFrostFS) SetContainer(cnrID cid.ID, cnr *container.Container) {
|
|
|
|
t.containers[cnrID.EncodeToString()] = cnr
|
|
|
|
}
|
|
|
|
|
2024-09-27 08:14:45 +00:00
|
|
|
func (t *TestFrostFS) CreateContainer(_ context.Context, prm frostfs.PrmContainerCreate) (*frostfs.ContainerCreateResult, error) {
|
2022-06-29 14:43:52 +00:00
|
|
|
var cnr container.Container
|
|
|
|
cnr.Init()
|
|
|
|
cnr.SetOwner(prm.Creator)
|
|
|
|
cnr.SetPlacementPolicy(prm.Policy)
|
2022-11-08 09:12:55 +00:00
|
|
|
|
|
|
|
creationTime := prm.CreationTime
|
|
|
|
if creationTime.IsZero() {
|
|
|
|
creationTime = time.Now()
|
|
|
|
}
|
|
|
|
container.SetCreationTime(&cnr, creationTime)
|
2022-03-04 13:07:27 +00:00
|
|
|
|
|
|
|
if prm.Name != "" {
|
2022-06-30 08:58:51 +00:00
|
|
|
var d container.Domain
|
|
|
|
d.SetName(prm.Name)
|
2023-11-23 07:44:50 +00:00
|
|
|
d.SetZone(prm.Zone)
|
2022-06-30 08:58:51 +00:00
|
|
|
|
|
|
|
container.WriteDomain(&cnr, d)
|
2022-06-29 14:43:52 +00:00
|
|
|
container.SetName(&cnr, prm.Name)
|
2022-03-04 13:07:27 +00:00
|
|
|
}
|
|
|
|
|
2022-06-29 14:43:52 +00:00
|
|
|
for i := range prm.AdditionalAttributes {
|
|
|
|
cnr.SetAttribute(prm.AdditionalAttributes[i][0], prm.AdditionalAttributes[i][1])
|
2022-03-04 13:07:27 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
b := make([]byte, 32)
|
|
|
|
if _, err := io.ReadFull(rand.Reader, b); err != nil {
|
2023-08-25 10:06:43 +00:00
|
|
|
return nil, err
|
2022-03-04 13:07:27 +00:00
|
|
|
}
|
|
|
|
|
2022-04-25 09:57:58 +00:00
|
|
|
var id cid.ID
|
2022-03-04 13:07:27 +00:00
|
|
|
id.SetSHA256(sha256.Sum256(b))
|
2022-06-29 14:43:52 +00:00
|
|
|
t.containers[id.EncodeToString()] = &cnr
|
2024-11-18 12:31:09 +00:00
|
|
|
t.chains[id.EncodeToString()] = []chain.Chain{}
|
2022-03-04 13:07:27 +00:00
|
|
|
|
2024-09-27 08:14:45 +00:00
|
|
|
return &frostfs.ContainerCreateResult{ContainerID: id}, nil
|
2022-03-04 13:07:27 +00:00
|
|
|
}
|
|
|
|
|
2022-12-20 08:38:58 +00:00
|
|
|
func (t *TestFrostFS) DeleteContainer(_ context.Context, cnrID cid.ID, _ *session.Container) error {
|
2022-07-21 09:05:47 +00:00
|
|
|
delete(t.containers, cnrID.EncodeToString())
|
|
|
|
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2024-09-27 08:14:45 +00:00
|
|
|
func (t *TestFrostFS) Container(_ context.Context, prm frostfs.PrmContainer) (*container.Container, error) {
|
2022-03-04 13:07:27 +00:00
|
|
|
for k, v := range t.containers {
|
2024-03-01 14:11:07 +00:00
|
|
|
if k == prm.ContainerID.EncodeToString() {
|
2022-03-04 13:07:27 +00:00
|
|
|
return v, nil
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-03-01 14:11:07 +00:00
|
|
|
return nil, fmt.Errorf("container not found %s", prm.ContainerID)
|
2022-03-04 13:07:27 +00:00
|
|
|
}
|
|
|
|
|
2024-09-27 08:14:45 +00:00
|
|
|
func (t *TestFrostFS) UserContainers(context.Context, frostfs.PrmUserContainers) ([]cid.ID, error) {
|
2022-03-04 13:07:27 +00:00
|
|
|
var res []cid.ID
|
|
|
|
for k := range t.containers {
|
|
|
|
var idCnr cid.ID
|
2022-04-25 09:57:58 +00:00
|
|
|
if err := idCnr.DecodeString(k); err != nil {
|
2022-03-04 13:07:27 +00:00
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
res = append(res, idCnr)
|
|
|
|
}
|
|
|
|
|
|
|
|
return res, nil
|
|
|
|
}
|
|
|
|
|
2024-07-15 12:47:19 +00:00
|
|
|
func (t *TestFrostFS) retrieveObject(ctx context.Context, cnrID cid.ID, objID oid.ID) (*object.Object, error) {
|
2022-05-25 17:25:43 +00:00
|
|
|
var addr oid.Address
|
2024-07-15 12:47:19 +00:00
|
|
|
addr.SetContainer(cnrID)
|
|
|
|
addr.SetObject(objID)
|
2022-03-04 13:07:27 +00:00
|
|
|
|
2022-05-25 17:25:43 +00:00
|
|
|
sAddr := addr.EncodeToString()
|
2022-03-04 13:07:27 +00:00
|
|
|
|
2023-04-24 16:40:18 +00:00
|
|
|
if err := t.objectErrors[sAddr]; err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
2022-03-04 13:07:27 +00:00
|
|
|
if obj, ok := t.objects[sAddr]; ok {
|
2023-08-03 12:08:22 +00:00
|
|
|
owner := getBearerOwner(ctx)
|
2024-07-15 12:47:19 +00:00
|
|
|
if !t.checkAccess(cnrID, owner) {
|
2024-09-27 08:14:45 +00:00
|
|
|
return nil, frostfs.ErrAccessDenied
|
2022-10-04 08:31:09 +00:00
|
|
|
}
|
|
|
|
|
2024-07-15 12:47:19 +00:00
|
|
|
return obj, nil
|
|
|
|
}
|
2022-08-01 16:52:09 +00:00
|
|
|
|
2024-07-15 12:47:19 +00:00
|
|
|
return nil, fmt.Errorf("%w: %s", &apistatus.ObjectNotFound{}, addr)
|
|
|
|
}
|
|
|
|
|
2024-09-27 08:14:45 +00:00
|
|
|
func (t *TestFrostFS) HeadObject(ctx context.Context, prm frostfs.PrmObjectHead) (*object.Object, error) {
|
2024-07-15 12:47:19 +00:00
|
|
|
return t.retrieveObject(ctx, prm.Container, prm.Object)
|
|
|
|
}
|
|
|
|
|
2024-09-27 08:14:45 +00:00
|
|
|
func (t *TestFrostFS) GetObject(ctx context.Context, prm frostfs.PrmObjectGet) (*frostfs.Object, error) {
|
2024-07-15 12:47:19 +00:00
|
|
|
obj, err := t.retrieveObject(ctx, prm.Container, prm.Object)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
2024-09-27 08:14:45 +00:00
|
|
|
return &frostfs.Object{
|
2024-07-15 12:47:19 +00:00
|
|
|
Header: *obj,
|
|
|
|
Payload: io.NopCloser(bytes.NewReader(obj.Payload())),
|
|
|
|
}, nil
|
|
|
|
}
|
2022-08-01 16:52:09 +00:00
|
|
|
|
2024-09-27 08:14:45 +00:00
|
|
|
func (t *TestFrostFS) RangeObject(ctx context.Context, prm frostfs.PrmObjectRange) (io.ReadCloser, error) {
|
2024-07-15 12:47:19 +00:00
|
|
|
obj, err := t.retrieveObject(ctx, prm.Container, prm.Object)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
2022-03-04 13:07:27 +00:00
|
|
|
}
|
|
|
|
|
2024-07-15 12:47:19 +00:00
|
|
|
off := prm.PayloadRange[0]
|
|
|
|
payload := obj.Payload()[off : off+prm.PayloadRange[1]]
|
|
|
|
|
|
|
|
return io.NopCloser(bytes.NewReader(payload)), nil
|
2022-03-04 13:07:27 +00:00
|
|
|
}
|
|
|
|
|
2024-11-22 09:32:35 +00:00
|
|
|
func (t *TestFrostFS) CreateObject(ctx context.Context, prm frostfs.PrmObjectCreate) (*frostfs.CreateObjectResult, error) {
|
|
|
|
if prm.Type == object.TypeTombstone {
|
|
|
|
return t.createTombstone(ctx, prm)
|
|
|
|
}
|
|
|
|
|
2022-04-25 09:57:58 +00:00
|
|
|
b := make([]byte, 32)
|
|
|
|
if _, err := io.ReadFull(rand.Reader, b); err != nil {
|
2024-07-22 09:00:17 +00:00
|
|
|
return nil, err
|
2022-04-25 09:57:58 +00:00
|
|
|
}
|
|
|
|
var id oid.ID
|
|
|
|
id.SetSHA256(sha256.Sum256(b))
|
2022-03-04 13:07:27 +00:00
|
|
|
|
2022-03-17 14:03:06 +00:00
|
|
|
attrs := make([]object.Attribute, 0)
|
2022-03-04 13:07:27 +00:00
|
|
|
|
2023-08-01 14:57:41 +00:00
|
|
|
if err := t.objectPutErrors[prm.Filepath]; err != nil {
|
2024-07-22 09:00:17 +00:00
|
|
|
return nil, err
|
2023-08-01 14:57:41 +00:00
|
|
|
}
|
|
|
|
|
2022-09-07 06:59:24 +00:00
|
|
|
if prm.Filepath != "" {
|
2022-03-04 13:07:27 +00:00
|
|
|
a := object.NewAttribute()
|
2022-09-07 06:59:24 +00:00
|
|
|
a.SetKey(object.AttributeFilePath)
|
|
|
|
a.SetValue(prm.Filepath)
|
2022-03-17 14:03:06 +00:00
|
|
|
attrs = append(attrs, *a)
|
2022-03-04 13:07:27 +00:00
|
|
|
}
|
|
|
|
|
2023-08-21 13:05:16 +00:00
|
|
|
if prm.ClientCut {
|
|
|
|
a := object.NewAttribute()
|
|
|
|
a.SetKey("s3-client-cut")
|
|
|
|
a.SetValue("true")
|
|
|
|
attrs = append(attrs, *a)
|
|
|
|
}
|
|
|
|
|
2022-03-04 13:07:27 +00:00
|
|
|
for i := range prm.Attributes {
|
|
|
|
a := object.NewAttribute()
|
|
|
|
a.SetKey(prm.Attributes[i][0])
|
|
|
|
a.SetValue(prm.Attributes[i][1])
|
2022-03-17 14:03:06 +00:00
|
|
|
attrs = append(attrs, *a)
|
2022-03-04 13:07:27 +00:00
|
|
|
}
|
|
|
|
|
2023-08-03 12:08:22 +00:00
|
|
|
var owner user.ID
|
|
|
|
user.IDFromKey(&owner, t.key.PrivateKey.PublicKey)
|
|
|
|
|
2022-03-04 13:07:27 +00:00
|
|
|
obj := object.New()
|
2022-04-25 09:57:58 +00:00
|
|
|
obj.SetContainerID(prm.Container)
|
2022-03-04 13:07:27 +00:00
|
|
|
obj.SetID(id)
|
|
|
|
obj.SetPayloadSize(prm.PayloadSize)
|
|
|
|
obj.SetAttributes(attrs...)
|
|
|
|
obj.SetCreationEpoch(t.currentEpoch)
|
2024-03-01 14:11:07 +00:00
|
|
|
obj.SetOwnerID(owner)
|
2022-03-04 13:07:27 +00:00
|
|
|
t.currentEpoch++
|
|
|
|
|
2022-03-04 14:36:30 +00:00
|
|
|
if len(prm.Locks) > 0 {
|
|
|
|
lock := new(object.Lock)
|
|
|
|
lock.WriteMembers(prm.Locks)
|
|
|
|
objectv2.WriteLock(obj.ToV2(), (objectv2.Lock)(*lock))
|
|
|
|
}
|
|
|
|
|
2022-03-04 13:07:27 +00:00
|
|
|
if prm.Payload != nil {
|
|
|
|
all, err := io.ReadAll(prm.Payload)
|
|
|
|
if err != nil {
|
2024-07-22 09:00:17 +00:00
|
|
|
return nil, err
|
2022-03-04 13:07:27 +00:00
|
|
|
}
|
|
|
|
obj.SetPayload(all)
|
|
|
|
obj.SetPayloadSize(uint64(len(all)))
|
2022-06-01 14:50:30 +00:00
|
|
|
var hash checksum.Checksum
|
|
|
|
checksum.Calculate(&hash, checksum.SHA256, all)
|
|
|
|
obj.SetPayloadChecksum(hash)
|
2022-03-04 13:07:27 +00:00
|
|
|
}
|
|
|
|
|
2022-04-25 09:57:58 +00:00
|
|
|
cnrID, _ := obj.ContainerID()
|
|
|
|
objID, _ := obj.ID()
|
|
|
|
|
|
|
|
addr := newAddress(cnrID, objID)
|
2022-05-25 17:25:43 +00:00
|
|
|
t.objects[addr.EncodeToString()] = obj
|
2024-09-27 08:14:45 +00:00
|
|
|
return &frostfs.CreateObjectResult{
|
2024-07-22 09:00:17 +00:00
|
|
|
ObjectID: objID,
|
|
|
|
CreationEpoch: t.currentEpoch - 1,
|
|
|
|
}, nil
|
2022-03-04 13:07:27 +00:00
|
|
|
}
|
|
|
|
|
2024-11-22 09:32:35 +00:00
|
|
|
func (t *TestFrostFS) createTombstone(ctx context.Context, prm frostfs.PrmObjectCreate) (*frostfs.CreateObjectResult, error) {
|
|
|
|
payload, err := io.ReadAll(prm.Payload)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
var tomb object.Tombstone
|
|
|
|
err = tomb.Unmarshal(payload)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
for _, objID := range tomb.Members() {
|
|
|
|
prmDelete := frostfs.PrmObjectDelete{
|
|
|
|
PrmAuth: prm.PrmAuth,
|
|
|
|
Container: prm.Container,
|
|
|
|
Object: objID,
|
|
|
|
}
|
|
|
|
|
|
|
|
if err = t.DeleteObject(ctx, prmDelete); err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return &frostfs.CreateObjectResult{
|
|
|
|
CreationEpoch: t.currentEpoch,
|
|
|
|
}, nil
|
|
|
|
}
|
|
|
|
|
2024-09-27 08:14:45 +00:00
|
|
|
func (t *TestFrostFS) DeleteObject(ctx context.Context, prm frostfs.PrmObjectDelete) error {
|
2022-05-25 17:25:43 +00:00
|
|
|
var addr oid.Address
|
|
|
|
addr.SetContainer(prm.Container)
|
|
|
|
addr.SetObject(prm.Object)
|
2022-03-04 13:07:27 +00:00
|
|
|
|
2023-04-24 16:40:18 +00:00
|
|
|
if err := t.objectErrors[addr.EncodeToString()]; err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
2024-05-28 12:50:34 +00:00
|
|
|
if _, ok := t.objects[addr.EncodeToString()]; ok {
|
2023-08-03 12:08:22 +00:00
|
|
|
owner := getBearerOwner(ctx)
|
2024-05-28 12:50:34 +00:00
|
|
|
if !t.checkAccess(prm.Container, owner) {
|
2024-09-27 08:14:45 +00:00
|
|
|
return frostfs.ErrAccessDenied
|
2022-10-04 08:31:09 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
delete(t.objects, addr.EncodeToString())
|
|
|
|
}
|
2022-03-04 13:07:27 +00:00
|
|
|
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2022-12-20 08:38:58 +00:00
|
|
|
func (t *TestFrostFS) TimeToEpoch(_ context.Context, now, futureTime time.Time) (uint64, uint64, error) {
|
2022-11-08 09:12:55 +00:00
|
|
|
return t.currentEpoch, t.currentEpoch + uint64(futureTime.Sub(now).Seconds()), nil
|
2022-03-05 08:53:01 +00:00
|
|
|
}
|
|
|
|
|
2022-12-20 08:38:58 +00:00
|
|
|
func (t *TestFrostFS) AllObjects(cnrID cid.ID) []oid.ID {
|
2022-08-22 08:02:58 +00:00
|
|
|
result := make([]oid.ID, 0, len(t.objects))
|
|
|
|
|
|
|
|
for _, val := range t.objects {
|
|
|
|
objCnrID, _ := val.ContainerID()
|
|
|
|
objObjID, _ := val.ID()
|
|
|
|
if cnrID.Equals(objCnrID) {
|
|
|
|
result = append(result, objObjID)
|
2022-03-04 13:07:27 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-08-22 08:02:58 +00:00
|
|
|
return result
|
2022-03-04 13:07:27 +00:00
|
|
|
}
|
2022-09-06 07:43:04 +00:00
|
|
|
|
2024-09-27 08:14:45 +00:00
|
|
|
func (t *TestFrostFS) SearchObjects(_ context.Context, prm frostfs.PrmObjectSearch) ([]oid.ID, error) {
|
2024-04-26 14:32:58 +00:00
|
|
|
filters := object.NewSearchFilters()
|
|
|
|
filters.AddRootFilter()
|
|
|
|
|
|
|
|
if prm.ExactAttribute[0] != "" {
|
|
|
|
filters.AddFilter(prm.ExactAttribute[0], prm.ExactAttribute[1], object.MatchStringEqual)
|
|
|
|
}
|
|
|
|
|
|
|
|
cidStr := prm.Container.EncodeToString()
|
|
|
|
|
|
|
|
var res []oid.ID
|
|
|
|
|
|
|
|
if len(filters) == 1 {
|
|
|
|
for k, v := range t.objects {
|
|
|
|
if strings.Contains(k, cidStr) {
|
|
|
|
id, _ := v.ID()
|
|
|
|
res = append(res, id)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return res, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
filter := filters[1]
|
|
|
|
if len(filters) != 2 || filter.Operation() != object.MatchStringEqual {
|
|
|
|
return nil, fmt.Errorf("usupported filters")
|
|
|
|
}
|
|
|
|
|
|
|
|
for k, v := range t.objects {
|
|
|
|
if strings.Contains(k, cidStr) && isMatched(v.Attributes(), filter) {
|
|
|
|
id, _ := v.ID()
|
|
|
|
res = append(res, id)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return res, nil
|
|
|
|
}
|
|
|
|
|
2024-07-22 09:00:17 +00:00
|
|
|
func (t *TestFrostFS) NetworkInfo(context.Context) (netmap.NetworkInfo, error) {
|
|
|
|
ni := netmap.NetworkInfo{}
|
|
|
|
ni.SetCurrentEpoch(t.currentEpoch)
|
2024-10-14 14:03:53 +00:00
|
|
|
ni.SetEpochDuration(60)
|
|
|
|
ni.SetMsPerBlock(1000)
|
2024-07-22 09:00:17 +00:00
|
|
|
|
|
|
|
return ni, nil
|
|
|
|
}
|
|
|
|
|
2024-09-27 08:14:45 +00:00
|
|
|
func (t *TestFrostFS) PatchObject(ctx context.Context, prm frostfs.PrmObjectPatch) (oid.ID, error) {
|
2024-08-13 15:21:16 +00:00
|
|
|
obj, err := t.retrieveObject(ctx, prm.Container, prm.Object)
|
|
|
|
if err != nil {
|
|
|
|
return oid.ID{}, err
|
|
|
|
}
|
|
|
|
|
|
|
|
newObj := *obj
|
|
|
|
|
|
|
|
patchBytes, err := io.ReadAll(prm.Payload)
|
|
|
|
if err != nil {
|
|
|
|
return oid.ID{}, err
|
|
|
|
}
|
|
|
|
|
|
|
|
var newPayload []byte
|
2024-08-20 09:35:07 +00:00
|
|
|
if prm.Offset > 0 {
|
|
|
|
newPayload = append(newPayload, obj.Payload()[:prm.Offset]...)
|
2024-08-13 15:21:16 +00:00
|
|
|
}
|
|
|
|
newPayload = append(newPayload, patchBytes...)
|
2024-08-20 09:35:07 +00:00
|
|
|
if prm.Offset+prm.Length < obj.PayloadSize() {
|
|
|
|
newPayload = append(newPayload, obj.Payload()[prm.Offset+prm.Length:]...)
|
2024-08-13 15:21:16 +00:00
|
|
|
}
|
|
|
|
newObj.SetPayload(newPayload)
|
|
|
|
newObj.SetPayloadSize(uint64(len(newPayload)))
|
|
|
|
|
|
|
|
var hash checksum.Checksum
|
|
|
|
checksum.Calculate(&hash, checksum.SHA256, newPayload)
|
|
|
|
newObj.SetPayloadChecksum(hash)
|
|
|
|
|
|
|
|
newID := oidtest.ID()
|
|
|
|
newObj.SetID(newID)
|
|
|
|
|
|
|
|
t.objects[newAddress(prm.Container, newID).EncodeToString()] = &newObj
|
|
|
|
|
|
|
|
return newID, nil
|
|
|
|
}
|
|
|
|
|
2024-11-22 09:32:35 +00:00
|
|
|
func (t *TestFrostFS) Relations() relations.Relations {
|
|
|
|
return &RelationsMock{}
|
|
|
|
}
|
|
|
|
|
2024-11-18 12:31:09 +00:00
|
|
|
func (t *TestFrostFS) AddContainerPolicyChain(_ context.Context, prm frostfs.PrmAddContainerPolicyChain) error {
|
|
|
|
list, ok := t.chains[prm.ContainerID.EncodeToString()]
|
|
|
|
if !ok {
|
|
|
|
return errors.New("container not found")
|
|
|
|
}
|
|
|
|
|
|
|
|
t.chains[prm.ContainerID.EncodeToString()] = append(list, prm.Chain)
|
|
|
|
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2024-05-28 12:50:34 +00:00
|
|
|
func (t *TestFrostFS) checkAccess(cnrID cid.ID, owner user.ID) bool {
|
2023-08-03 12:08:22 +00:00
|
|
|
cnr, ok := t.containers[cnrID.EncodeToString()]
|
2023-02-20 09:18:29 +00:00
|
|
|
if !ok {
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
|
2023-08-03 12:08:22 +00:00
|
|
|
if !cnr.BasicACL().Extendable() {
|
|
|
|
return cnr.Owner().Equals(owner)
|
|
|
|
}
|
|
|
|
|
|
|
|
return true
|
2023-02-20 09:18:29 +00:00
|
|
|
}
|
|
|
|
|
2023-08-03 12:08:22 +00:00
|
|
|
func getBearerOwner(ctx context.Context) user.ID {
|
2023-08-14 15:34:41 +00:00
|
|
|
if bd, err := middleware.GetBoxData(ctx); err == nil && bd.Gate.BearerToken != nil {
|
2022-10-04 08:31:09 +00:00
|
|
|
return bearer.ResolveIssuer(*bd.Gate.BearerToken)
|
|
|
|
}
|
|
|
|
|
|
|
|
return user.ID{}
|
|
|
|
}
|
2024-04-26 14:32:58 +00:00
|
|
|
|
|
|
|
func isMatched(attributes []object.Attribute, filter object.SearchFilter) bool {
|
|
|
|
for _, attr := range attributes {
|
|
|
|
if attr.Key() == filter.Header() && attr.Value() == filter.Value() {
|
|
|
|
return true
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return false
|
|
|
|
}
|
2024-11-22 09:32:35 +00:00
|
|
|
|
|
|
|
type RelationsMock struct{}
|
|
|
|
|
|
|
|
func (r *RelationsMock) GetSplitInfo(context.Context, cid.ID, oid.ID, relations.Tokens) (*object.SplitInfo, error) {
|
|
|
|
return nil, relations.ErrNoSplitInfo
|
|
|
|
}
|
|
|
|
|
|
|
|
func (r *RelationsMock) ListChildrenByLinker(context.Context, cid.ID, oid.ID, relations.Tokens) ([]oid.ID, error) {
|
|
|
|
return nil, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func (r *RelationsMock) GetLeftSibling(context.Context, cid.ID, oid.ID, relations.Tokens) (oid.ID, error) {
|
|
|
|
return oid.ID{}, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func (r *RelationsMock) FindSiblingBySplitID(context.Context, cid.ID, *object.SplitID, relations.Tokens) ([]oid.ID, error) {
|
|
|
|
return nil, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func (r *RelationsMock) FindSiblingByParentID(_ context.Context, _ cid.ID, _ oid.ID, _ relations.Tokens) ([]oid.ID, error) {
|
|
|
|
return nil, nil
|
|
|
|
}
|