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
9edec7d573
commit
6ed29853a5
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,
|
||||
}
|
||||
|
||||
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
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
|
||||
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 {
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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{
|
||||
|
|
Loading…
Add table
Reference in a new issue