[#650] Add Copies Numbers for PostObject operation

Signed-off-by: Pavel Pogodaev <p.pogodaev@yadro.com>
This commit is contained in:
Pavel Pogodaev 2025-02-27 12:19:49 +03:00
parent 9edec7d573
commit 6ed29853a5
6 changed files with 174 additions and 3 deletions

View file

@ -591,6 +591,12 @@ func (h *handler) PostObject(w http.ResponseWriter, r *http.Request) {
Header: metadata,
}
params.CopiesNumbers, err = h.pickCopiesNumbers(metadata, reqInfo.Namespace, bktInfo.LocationConstraint)
if err != nil {
h.logAndSendError(ctx, w, "invalid copies number", reqInfo, err)
return
}
extendedObjInfo, err := h.obj.PutObject(ctx, params)
if err != nil {
h.logAndSendError(ctx, w, "could not upload object", reqInfo, err)

46
api/layer/cors_test.go Normal file
View file

@ -0,0 +1,46 @@
package layer
import (
"encoding/xml"
"io"
"strings"
"testing"
"github.com/stretchr/testify/require"
)
func TestCorsCopiesNumber(t *testing.T) {
tc := prepareCORSContext(t)
body := `
<CORSConfiguration xmlns="http://s3.amazonaws.com/doc/2006-03-01/">
<CORSRule>
<AllowedMethod>GET</AllowedMethod>
<AllowedOrigin>http://www.example.com</AllowedOrigin>
<AllowedHeader>Authorization</AllowedHeader>
<ExposeHeader>x-amz-*</ExposeHeader>
</CORSRule>
</CORSConfiguration>
`
copies := []uint32{2, 0}
err := tc.layer.PutBucketCORS(tc.ctx, &PutCORSParams{
BktInfo: tc.bktInfo,
Reader: strings.NewReader(body),
CopiesNumbers: copies,
UserAgent: "",
NewDecoder: NewXMLDecoder,
})
require.NoError(t, err)
objs := tc.testFrostFS.Objects()
require.Len(t, objs, 1)
require.EqualValues(t, copies, tc.testFrostFS.CopiesNumbers(addrFromObject(objs[0]).EncodeToString()))
}
func NewXMLDecoder(r io.Reader, _ string) *xml.Decoder {
dec := xml.NewDecoder(r)
return dec
}

View file

@ -41,13 +41,13 @@ func (n *Layer) PutBucketLifecycleConfiguration(ctx context.Context, p *PutBucke
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
prm.CopiesNumber = p.CopiesNumbers
createdObj, err := n.objectPutAndHash(ctx, prm, lifecycleBkt)
if err != nil {

View file

@ -8,6 +8,8 @@ import (
"git.frostfs.info/TrueCloudLab/frostfs-s3-gw/api/data"
apierr "git.frostfs.info/TrueCloudLab/frostfs-s3-gw/api/errors"
frosterr "git.frostfs.info/TrueCloudLab/frostfs-s3-gw/internal/frostfs/errors"
"git.frostfs.info/TrueCloudLab/frostfs-sdk-go/object"
oid "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/object/id"
"github.com/stretchr/testify/require"
)
@ -56,6 +58,52 @@ func TestBucketLifecycle(t *testing.T) {
require.Equal(t, apierr.GetAPIError(apierr.ErrNoSuchLifecycleConfiguration), frosterr.UnwrapErr(err))
}
func TestLifecycleCopiesNumber(t *testing.T) {
tc := prepareCORSContext(t)
lifecycle := &data.LifecycleConfiguration{
XMLName: xml.Name{
Space: `http://s3.amazonaws.com/doc/2006-03-01/`,
Local: "LifecycleConfiguration",
},
Rules: []data.LifecycleRule{
{
Status: data.LifecycleStatusEnabled,
Expiration: &data.LifecycleExpiration{
Days: ptr(21),
},
},
},
}
copies := []uint32{2, 0}
err := tc.layer.PutBucketLifecycleConfiguration(tc.ctx, &PutBucketLifecycleParams{
BktInfo: tc.bktInfo,
LifecycleCfg: lifecycle,
CopiesNumbers: copies,
})
require.NoError(t, err)
cfg, err := tc.layer.GetBucketLifecycleConfiguration(tc.ctx, tc.bktInfo)
require.NoError(t, err)
require.Equal(t, *lifecycle, *cfg)
objs := tc.testFrostFS.Objects()
require.Len(t, objs, 1)
require.EqualValues(t, copies, tc.testFrostFS.CopiesNumbers(addrFromObject(objs[0]).EncodeToString()))
}
func ptr[T any](t T) *T {
return &t
}
func addrFromObject(obj *object.Object) oid.Address {
var addr oid.Address
cnrID, _ := obj.ContainerID()
objID, _ := obj.ID()
addr.SetContainer(cnrID)
addr.SetObject(objID)
return addr
}

View file

@ -138,8 +138,18 @@ func (t *TreeServiceMock) GetAllBucketCORS(ctx context.Context, bktInfo *data.Bu
return []oid.Address{cors}, nil
}
func (t *TreeServiceMock) DeleteBucketCORS(context.Context, *data.BucketInfo) ([]oid.Address, error) {
panic("implement me")
func (t *TreeServiceMock) DeleteBucketCORS(_ context.Context, bktInfo *data.BucketInfo) ([]oid.Address, error) {
systemMap, ok := t.system[bktInfo.CID.EncodeToString()]
if !ok {
return []oid.Address{}, tree.ErrNodeNotFound
}
_, ok = systemMap["cors"]
if !ok {
return []oid.Address{}, tree.ErrNodeNotFound
}
return []oid.Address{}, nil
}
func (t *TreeServiceMock) GetVersions(_ context.Context, bktInfo *data.BucketInfo, objectName string) ([]*data.NodeVersion, error) {

View file

@ -193,6 +193,67 @@ func prepareContext(t *testing.T, cachesConfig ...*CachesConfig) *testContext {
}
}
func prepareCORSContext(t *testing.T, cachesConfig ...*CachesConfig) *testContext {
logger := zaptest.NewLogger(t)
key, err := keys.NewPrivateKey()
require.NoError(t, err)
bearerToken := bearertest.Token()
require.NoError(t, bearerToken.Sign(key.PrivateKey))
ctx := middleware.SetBox(context.Background(), &middleware.Box{AccessBox: &accessbox.Box{
Gate: &accessbox.GateData{
BearerToken: &bearerToken,
GateKey: key.PublicKey(),
},
}})
tp := NewTestFrostFS(key)
bktName := "testbucket1"
res, err := tp.CreateContainer(ctx, frostfs.PrmContainerCreate{
Name: bktName,
Policy: getPlacementPolicy(),
})
require.NoError(t, err)
config := DefaultCachesConfigs(logger)
if len(cachesConfig) != 0 {
config = cachesConfig[0]
}
var owner user.ID
user.IDFromKey(&owner, key.PrivateKey.PublicKey)
corsCnrInfo := &data.BucketInfo{Name: "CORS"}
lifecycleCnrInfo := &data.BucketInfo{Name: "Lifecycle"}
layerCfg := &Config{
Cache: NewCache(config),
AnonKey: AnonymousKey{Key: key},
TreeService: NewTreeService(),
Features: &FeatureSettingsMock{},
GateOwner: owner,
CORSCnrInfo: corsCnrInfo,
LifecycleCnrInfo: lifecycleCnrInfo,
GateKey: key,
}
return &testContext{
ctx: ctx,
layer: NewLayer(logger, tp, layerCfg),
bktInfo: &data.BucketInfo{
Name: bktName,
Owner: owner,
CID: res.ContainerID,
HomomorphicHashDisabled: res.HomomorphicHashDisabled,
},
obj: "obj1",
t: t,
testFrostFS: tp,
}
}
func TestSimpleVersioning(t *testing.T) {
tc := prepareContext(t)
err := tc.layer.PutBucketSettings(tc.ctx, &PutSettingsParams{