forked from TrueCloudLab/frostfs-s3-gw
[#650] Add Copies Numbers for PostObject operation
Signed-off-by: Pavel Pogodaev <p.pogodaev@yadro.com>
This commit is contained in:
parent
d85e5b10bb
commit
c0c4bdb366
6 changed files with 174 additions and 3 deletions
|
@ -591,6 +591,12 @@ func (h *handler) PostObject(w http.ResponseWriter, r *http.Request) {
|
||||||
Header: metadata,
|
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)
|
extendedObjInfo, err := h.obj.PutObject(ctx, params)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
h.logAndSendError(ctx, w, "could not upload object", reqInfo, err)
|
h.logAndSendError(ctx, w, "could not upload object", reqInfo, err)
|
||||||
|
|
46
api/layer/cors_test.go
Normal file
46
api/layer/cors_test.go
Normal 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
|
||||||
|
}
|
|
@ -41,13 +41,13 @@ func (n *Layer) PutBucketLifecycleConfiguration(ctx context.Context, p *PutBucke
|
||||||
var lifecycleBkt *data.BucketInfo
|
var lifecycleBkt *data.BucketInfo
|
||||||
if n.lifecycleCnrInfo == nil {
|
if n.lifecycleCnrInfo == nil {
|
||||||
lifecycleBkt = p.BktInfo
|
lifecycleBkt = p.BktInfo
|
||||||
prm.CopiesNumber = p.CopiesNumbers
|
|
||||||
} else {
|
} else {
|
||||||
lifecycleBkt = n.lifecycleCnrInfo
|
lifecycleBkt = n.lifecycleCnrInfo
|
||||||
prm.PrmAuth.PrivateKey = &n.gateKey.PrivateKey
|
prm.PrmAuth.PrivateKey = &n.gateKey.PrivateKey
|
||||||
}
|
}
|
||||||
|
|
||||||
prm.Container = lifecycleBkt.CID
|
prm.Container = lifecycleBkt.CID
|
||||||
|
prm.CopiesNumber = p.CopiesNumbers
|
||||||
|
|
||||||
createdObj, err := n.objectPutAndHash(ctx, prm, lifecycleBkt)
|
createdObj, err := n.objectPutAndHash(ctx, prm, lifecycleBkt)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
|
@ -8,6 +8,8 @@ import (
|
||||||
"git.frostfs.info/TrueCloudLab/frostfs-s3-gw/api/data"
|
"git.frostfs.info/TrueCloudLab/frostfs-s3-gw/api/data"
|
||||||
apierr "git.frostfs.info/TrueCloudLab/frostfs-s3-gw/api/errors"
|
apierr "git.frostfs.info/TrueCloudLab/frostfs-s3-gw/api/errors"
|
||||||
frosterr "git.frostfs.info/TrueCloudLab/frostfs-s3-gw/internal/frostfs/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"
|
"github.com/stretchr/testify/require"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -56,6 +58,52 @@ func TestBucketLifecycle(t *testing.T) {
|
||||||
require.Equal(t, apierr.GetAPIError(apierr.ErrNoSuchLifecycleConfiguration), frosterr.UnwrapErr(err))
|
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 {
|
func ptr[T any](t T) *T {
|
||||||
return &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
|
||||||
|
}
|
||||||
|
|
|
@ -138,8 +138,18 @@ func (t *TreeServiceMock) GetAllBucketCORS(ctx context.Context, bktInfo *data.Bu
|
||||||
return []oid.Address{cors}, nil
|
return []oid.Address{cors}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t *TreeServiceMock) DeleteBucketCORS(context.Context, *data.BucketInfo) ([]oid.Address, error) {
|
func (t *TreeServiceMock) DeleteBucketCORS(_ context.Context, bktInfo *data.BucketInfo) ([]oid.Address, error) {
|
||||||
panic("implement me")
|
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) {
|
func (t *TreeServiceMock) GetVersions(_ context.Context, bktInfo *data.BucketInfo, objectName string) ([]*data.NodeVersion, error) {
|
||||||
|
|
|
@ -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) {
|
func TestSimpleVersioning(t *testing.T) {
|
||||||
tc := prepareContext(t)
|
tc := prepareContext(t)
|
||||||
err := tc.layer.PutBucketSettings(tc.ctx, &PutSettingsParams{
|
err := tc.layer.PutBucketSettings(tc.ctx, &PutSettingsParams{
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue